y_petri 2.0.3 → 2.0.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/y_petri/dependency_injection.rb +45 -0
- data/lib/y_petri/manipulator/petri_net_related_methods.rb +26 -7
- data/lib/y_petri/manipulator/simulation_related_methods.rb +4 -4
- data/lib/y_petri/net.rb +30 -21
- data/lib/y_petri/place/arcs.rb +96 -0
- data/lib/y_petri/place/guard.rb +122 -0
- data/lib/y_petri/place.rb +89 -132
- data/lib/y_petri/simulation.rb +191 -168
- data/lib/y_petri/timed_simulation.rb +29 -20
- data/lib/y_petri/transition/arcs.rb +51 -0
- data/lib/y_petri/transition/cocking.rb +32 -0
- data/lib/y_petri/transition/constructor_syntax.rb +378 -0
- data/lib/y_petri/transition.rb +391 -831
- data/lib/y_petri/version.rb +1 -1
- data/lib/y_petri/workspace/parametrized_subclassing.rb +8 -13
- data/lib/y_petri/workspace/simulation_related_methods.rb +13 -11
- data/lib/y_petri.rb +8 -3
- data/test/place_test.rb +83 -0
- data/test/transition_test.rb +325 -0
- data/test/y_petri_test.rb +15 -410
- metadata +12 -2
data/lib/y_petri/version.rb
CHANGED
@@ -2,25 +2,20 @@
|
|
2
2
|
module YPetri::Workspace::ParametrizedSubclassing
|
3
3
|
def initialize
|
4
4
|
# Parametrized subclasses of Place, Transition and Net.
|
5
|
-
@Place = place_subclass = Class.new YPetri::Place
|
5
|
+
@Place = place_subclass = Class.new( YPetri::Place )
|
6
6
|
@Transition = transition_subclass = Class.new YPetri::Transition
|
7
7
|
@Net = net_subclass = Class.new YPetri::Net
|
8
8
|
|
9
|
-
#
|
10
|
-
[ @Place, @Transition, @Net ].each
|
11
|
-
klass.class_exec
|
12
|
-
# redefine their Place, Transition, Net method
|
9
|
+
# Make them namespaces and inject dependencies:
|
10
|
+
[ @Place, @Transition, @Net ].each do |klass|
|
11
|
+
klass.namespace!.class_exec do # make'em work together
|
13
12
|
define_method :Place do place_subclass end
|
14
13
|
define_method :Transition do transition_subclass end
|
15
14
|
define_method :Net do net_subclass end
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
# declare them private explicitly again after redefining?
|
20
|
-
private :Place, :Transition, :Net
|
21
|
-
}
|
22
|
-
}
|
15
|
+
private :Place, :Transition, :Net # Redeclare private after redef???
|
16
|
+
end
|
17
|
+
end
|
23
18
|
|
24
|
-
super #
|
19
|
+
super # param. subclassing achieved, proceed ahead normally
|
25
20
|
end # def initialize
|
26
21
|
end # module YPetri::Workspace::ParametrizedSubclassing
|
@@ -137,15 +137,18 @@ module YPetri::Workspace::SimulationRelatedMethods
|
|
137
137
|
#
|
138
138
|
# * default_ss = { step_size: 0.1, sampling_period: 5, target_time: 60 }
|
139
139
|
#
|
140
|
-
def new_timed_simulation(
|
141
|
-
net_ɪ = net(
|
142
|
-
|
143
|
-
|
144
|
-
|
140
|
+
def new_timed_simulation( net: Net()::Top, **nn )
|
141
|
+
net_ɪ = net( net )
|
142
|
+
nn.may_have :cc, syn!: :clamp_collection
|
143
|
+
nn.may_have :imc, syn!: :initial_marking_collection
|
144
|
+
nn.may_have :ssc, syn!: :simulation_settings_collection
|
145
|
+
cc_id = nn.delete( :cc ) || :Base
|
146
|
+
imc_id = nn.delete( :imc ) || :Base
|
147
|
+
ssc_id = nn.delete( :ssc ) || :Base
|
145
148
|
|
146
149
|
# simulation key
|
147
|
-
key =
|
148
|
-
{ net: net_ɪ, cc: cc_id, imc: imc_id, ssc: ssc_id } # or constructed
|
150
|
+
key = nn.may_have( :ɴ, syn!: :name ) || # either explicit
|
151
|
+
{ net: net_ɪ, cc: cc_id, imc: imc_id, ssc: ssc_id }.merge( nn ) # or constructed
|
149
152
|
|
150
153
|
# Let's clarify what we got so far.
|
151
154
|
simulation_settings = self.ssc( ssc_id )
|
@@ -173,9 +176,8 @@ module YPetri::Workspace::SimulationRelatedMethods
|
|
173
176
|
else err.( [0, 1], " and #{missing.size-2} more places" ) end
|
174
177
|
|
175
178
|
# Finally, create and return the simulation
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
place_clamps: clamp_hash ) )
|
179
|
+
named_args = simulation_settings.merge( initial_marking: im_hash,
|
180
|
+
marking_clamps: clamp_hash ).merge( nn )
|
181
|
+
@simulations[ key ] = net_ɪ.new_timed_simulation **named_args
|
180
182
|
end # def new_timed_simulation
|
181
183
|
end # module YPetri::Workspace::SimulationRelatedMethods
|
data/lib/y_petri.rb
CHANGED
@@ -7,6 +7,7 @@ require 'y_support/respond_to'
|
|
7
7
|
require 'y_support/name_magic'
|
8
8
|
require 'y_support/unicode'
|
9
9
|
require 'y_support/typing'
|
10
|
+
require 'y_support/conscience'; include Conscience
|
10
11
|
require 'y_support/core_ext/hash'
|
11
12
|
require 'y_support/core_ext/array'
|
12
13
|
require 'y_support/stdlib_ext/matrix'
|
@@ -50,6 +51,8 @@ module YPetri
|
|
50
51
|
target_time: 60 }
|
51
52
|
end
|
52
53
|
|
54
|
+
GuardError = Class.new TypeError
|
55
|
+
|
53
56
|
def self.included( receiver )
|
54
57
|
# receiver.instance_variable_set :@YPetriManipulator, Manipulator.new
|
55
58
|
# puts "included in #{receiver}"
|
@@ -64,7 +67,7 @@ module YPetri
|
|
64
67
|
|
65
68
|
delegate( :workspace, to: :y_petri_manipulator )
|
66
69
|
|
67
|
-
# Petri net
|
70
|
+
# Petri net aspect.
|
68
71
|
delegate( :Place, :Transition, :Net,
|
69
72
|
:place, :transition, :pl, :tr,
|
70
73
|
:places, :transitions, :nets,
|
@@ -76,7 +79,7 @@ module YPetri
|
|
76
79
|
:net_point_set,
|
77
80
|
to: :y_petri_manipulator )
|
78
81
|
|
79
|
-
# Simulation
|
82
|
+
# Simulation aspect.
|
80
83
|
delegate( :simulation_point, :ssc_point, :cc_point, :imc_point,
|
81
84
|
:simulation_selection, :ssc_selection,
|
82
85
|
:cc_selection, :imc_selection,
|
@@ -99,7 +102,9 @@ module YPetri
|
|
99
102
|
:simulation_settings_collection, :ssc,
|
100
103
|
:clamp,
|
101
104
|
:initial_marking,
|
102
|
-
:set_step, :
|
105
|
+
:set_step, :set_step_size,
|
106
|
+
:set_time, :set_target_time,
|
107
|
+
:set_sampling,
|
103
108
|
:set_simulation_method,
|
104
109
|
:new_timed_simulation,
|
105
110
|
:run!,
|
data/test/place_test.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
#! /usr/bin/ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
require 'minitest/spec'
|
5
|
+
require 'minitest/autorun'
|
6
|
+
require_relative '../lib/y_petri' # tested component itself
|
7
|
+
# require 'y_petri'
|
8
|
+
# require 'sy'
|
9
|
+
|
10
|
+
describe YPetri::Place do
|
11
|
+
before do
|
12
|
+
@pç = pç = Class.new YPetri::Place
|
13
|
+
@p = pç.new! default_marking: 3.2,
|
14
|
+
marking: 1.1,
|
15
|
+
quantum: 0.1,
|
16
|
+
name: "P1"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should support #name" do
|
20
|
+
assert_respond_to @p, :name
|
21
|
+
assert_equal @p.name, :P1
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should have marking and related methods" do
|
25
|
+
@p.marking.must_equal 1.1 # Attention, #marking overloaded with guard setup!
|
26
|
+
@p.quantum.must_equal 0.1
|
27
|
+
@p.add 1
|
28
|
+
@p.value.must_equal 2.1 # near-alias of #marking (no guard setup)
|
29
|
+
@p.subtract 0.5
|
30
|
+
@p.m.must_equal 1.6 # alias of #value
|
31
|
+
@p.reset_marking
|
32
|
+
@p.marking.must_equal 3.2
|
33
|
+
@p.marking = 42
|
34
|
+
@p.m.must_equal 42
|
35
|
+
@p.m = 43
|
36
|
+
@p.m.must_equal 43
|
37
|
+
@p.value = 44
|
38
|
+
@p.m.must_equal 44
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should have decent #inspect and #to_s methods" do
|
42
|
+
assert @p.inspect.start_with? "#<Place:"
|
43
|
+
assert @p.to_s.start_with? "#{@p.name}["
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should have arc getter methods" do
|
47
|
+
@p.upstream_arcs.must_equal []
|
48
|
+
@p.upstream_transitions.must_equal [] # alias of #upstream_arcs
|
49
|
+
@p.ϝ.must_equal [] # alias of #upstream_arcs
|
50
|
+
@p.downstream_arcs.must_equal []
|
51
|
+
@p.downstream_transitions.must_equal [] # alias of #downstream_arcs
|
52
|
+
@p.arcs.must_equal [] # all arcs
|
53
|
+
@p.precedents.must_equal []
|
54
|
+
@p.upstream_places.must_equal [] # alias for #precedents
|
55
|
+
@p.dependents.must_equal []
|
56
|
+
@p.downstream_places.must_equal [] # alias for #dependents
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should have convenience methods to fire surrounding transitions" do
|
60
|
+
assert_respond_to @p, :fire_upstream
|
61
|
+
assert_respond_to @p, :fire_upstream!
|
62
|
+
assert_respond_to @p, :fire_downstream
|
63
|
+
assert_respond_to @p, :fire_downstream!
|
64
|
+
assert_respond_to @p, :fire_upstream_recursively
|
65
|
+
assert_respond_to @p, :fire_downstream_recursively
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should have guard mechanics" do
|
69
|
+
@p.guards.size.must_equal 2 # working automatic guard construction
|
70
|
+
g1, g2 = @p.guards
|
71
|
+
g1.assertion.must_include "number"
|
72
|
+
g2.assertion.must_include "complex"
|
73
|
+
begin; g1.validate 11.1; g2.validate 11.1; @p.guard.( 11.1 ); :nothing_raised
|
74
|
+
rescue; :error end.must_equal :nothing_raised
|
75
|
+
-> { g2.validate Complex( 1, 1 ) }.must_raise YPetri::GuardError
|
76
|
+
@p.marking "must be in 0..10" do |m| fail unless ( 0..10 ) === m end
|
77
|
+
@p.guards.size.must_equal 3
|
78
|
+
g = @p.federated_guard_closure
|
79
|
+
-> { g.( 11.1 ) }.must_raise YPetri::GuardError
|
80
|
+
@p.marking = -1.11
|
81
|
+
-> { @p.guard! }.must_raise YPetri::GuardError
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,325 @@
|
|
1
|
+
#! /usr/bin/ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
require 'minitest/spec'
|
5
|
+
require 'minitest/autorun'
|
6
|
+
require 'y_support/typing'
|
7
|
+
|
8
|
+
require_relative '../lib/y_petri' # tested component itself
|
9
|
+
|
10
|
+
# require 'y_petri'
|
11
|
+
# require 'sy'
|
12
|
+
|
13
|
+
# **************************************************************************
|
14
|
+
# Test of Transition class, part I.
|
15
|
+
# **************************************************************************
|
16
|
+
#
|
17
|
+
describe ::YPetri::Transition do
|
18
|
+
before do
|
19
|
+
@ç = ç = Class.new ::YPetri::Transition
|
20
|
+
@pç = pç = Class.new ::YPetri::Place
|
21
|
+
[ ç, pç ].each do |ç|
|
22
|
+
ç.class_exec {
|
23
|
+
define_method :Place do pç end
|
24
|
+
define_method :Transition do ç end
|
25
|
+
private :Place, :Transition
|
26
|
+
}
|
27
|
+
end
|
28
|
+
@p1 = pç.new default_marking: 1.0
|
29
|
+
@p2 = pç.new default_marking: 2.0
|
30
|
+
@p3 = pç.new default_marking: 3.0
|
31
|
+
@p4 = pç.new default_marking: 4.0
|
32
|
+
@p5 = pç.new default_marking: 5.0
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "ts transitions (timeless nonstoichiometric)" do
|
36
|
+
# Note: ts transitions require a function, and thus are always functional
|
37
|
+
before do
|
38
|
+
@t1 = @ç.new codomain: [ @p1, @p3 ], domain: @p2, action: -> a { [ a, a ] }
|
39
|
+
# saying that the transition is timed saves the day here:
|
40
|
+
@t2 = @ç.new codomain: [ @p1, @p3 ], action: -> t { [ t, t ] }, timed: true
|
41
|
+
# Only when the domain is unary, is the closure allowed to be timeless:
|
42
|
+
@t3 = @ç.new codomain: [ @p1, @p3 ], action: -> t { [ t, t ] }, timed: false, domain: [ @p2 ]
|
43
|
+
# With nullary action closure, timeless is implied, so this is allowed
|
44
|
+
@t4 = @ç.new action: -> { [ 0.5, 0.5 ] }, codomain: [ @p1, @p3 ]
|
45
|
+
# ... also for stoichiometric variety
|
46
|
+
@t5 = @ç.new action: -> { 0.5 }, codomain: [ @p1, @p3 ], s: [ 1, 1 ]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should raise errors for bad parameters" do
|
50
|
+
# omitting the domain should raise ArgumentError about too much ambiguity:
|
51
|
+
-> { @ç.new codomain: [ @p1, @p3 ], action: -> t { [ t, t ] } }
|
52
|
+
.must_raise ArgumentError
|
53
|
+
# saying that the transition is timeless points to a conflict:
|
54
|
+
-> { @ç.new codomain: [ @p1, @p3 ], action: -> t { [ t, t ] }, timeless: true }
|
55
|
+
.must_raise ArgumentError
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should initialize and perform" do
|
59
|
+
@t1.domain.must_equal [@p2]
|
60
|
+
@t1.action_arcs.must_equal [@p1, @p3]
|
61
|
+
assert @t1.functional?
|
62
|
+
assert @t1.timeless?
|
63
|
+
assert @t2.timed?
|
64
|
+
assert [@t3, @t4, @t5].all? { |t| t.timeless? }
|
65
|
+
assert @t2.rateless?
|
66
|
+
# Now let's flex them:
|
67
|
+
@t1.fire!
|
68
|
+
[@p1.m, @p3.m].must_equal [3, 5]
|
69
|
+
@t3.fire!
|
70
|
+
[@p1.m, @p3.m].must_equal [5, 7]
|
71
|
+
@t4.fire!
|
72
|
+
[@p1.m, @p3.m].must_equal [5.5, 7.5]
|
73
|
+
@t5.fire!
|
74
|
+
[@p1.m, @p3.m].must_equal [6, 8]
|
75
|
+
# now t2 for firing requires delta time
|
76
|
+
@t2.fire! 1
|
77
|
+
[@p1.m, @p3.m].must_equal [7, 9]
|
78
|
+
@t2.fire! 0.1
|
79
|
+
[@p1.m, @p3.m ].must_equal [7.1, 9.1]
|
80
|
+
# let's change @p2 marking
|
81
|
+
@p2.m = 0.1
|
82
|
+
@t1.fire!
|
83
|
+
assert_in_epsilon 7.2, @p1.marking, 1e-9
|
84
|
+
assert_in_epsilon 9.2, @p3.marking, 1e-9
|
85
|
+
# let's test #domain_marking, #codomain_marking, #zero_action
|
86
|
+
@t1.codomain_marking.must_equal [@p1.m, @p3.m]
|
87
|
+
@t1.domain_marking.must_equal [@p2.m]
|
88
|
+
@t1.zero_action.must_equal [0, 0]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe "Tsr transitions (timed rateless non-stoichiometric)" do
|
93
|
+
#LATER: To save time, I omit the full test suite.
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "tS transitions (timeless stoichiometric)" do
|
97
|
+
describe "functionless tS transitions" do
|
98
|
+
# For functionless tS transitions, stoichiometric vector must be given,
|
99
|
+
# from which the action closure is then generated.
|
100
|
+
|
101
|
+
before do
|
102
|
+
# tS transition with only stoichiometric vector, as hash
|
103
|
+
@ftS1 = @ç.new stoichiometry: { @p1 => 1 }
|
104
|
+
# tS transition with only stoichiometric vector, as array + codomain
|
105
|
+
@ftS2 = @ç.new stoichiometry: 1, codomain: @p1
|
106
|
+
# :stoichiometry keyword is aliased as :s
|
107
|
+
@ftS3 = @ç.new s: 1, codomain: @p1
|
108
|
+
# :codomain is aliased as :action_arcs
|
109
|
+
@ftS4 = @ç.new s: 1, action_arcs: @p1
|
110
|
+
# square brackets (optional for size 1 vectors)
|
111
|
+
@ftS5 = @ç.new s: [ 1 ], downstream: [ @p1 ]
|
112
|
+
# another alias of :codomain is :downstream_places
|
113
|
+
@ftS6 = @ç.new s: [ 1 ], downstream_places: [ @p1 ]
|
114
|
+
# And now, collect all of the above:
|
115
|
+
@tt = @ftS1, @ftS2, @ftS3, @ftS4, @ftS5, @ftS6
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should work" do
|
119
|
+
# ...should be the same, having a single action arc:
|
120
|
+
assert @tt.all? { |t| t.action_arcs == [@p1] }
|
121
|
+
# timeless:
|
122
|
+
assert @tt.all? { |t| t.timeless? }
|
123
|
+
# rateless:
|
124
|
+
assert @tt.all? { |t| t.rateless? }
|
125
|
+
assert @tt.all? { |t| not t.has_rate? }
|
126
|
+
# no assignment action
|
127
|
+
assert @tt.all? { |t| not t.assignment_action? }
|
128
|
+
# not considered functional
|
129
|
+
assert @tt.all? { |t| t.functionless? }
|
130
|
+
assert @tt.all? { |t| not t.functional? }
|
131
|
+
# and having nullary action closure
|
132
|
+
assert @tt.all? { |t| t.action_closure.arity == 0 }
|
133
|
+
# the transitions should be able to #fire!
|
134
|
+
@ftS1.fire!
|
135
|
+
# the difference is apparent: marking of place @p1 jumped to 2:
|
136
|
+
@p1.marking.must_equal 2
|
137
|
+
# but should not #fire (no exclamation mark) unless cocked
|
138
|
+
assert !@ftS1.cocked?
|
139
|
+
@ftS1.fire
|
140
|
+
@p1.marking.must_equal 2
|
141
|
+
# cock it
|
142
|
+
@ftS1.cock
|
143
|
+
assert @ftS1.cocked?
|
144
|
+
# uncock again, just to test cocking
|
145
|
+
@ftS1.uncock
|
146
|
+
assert @ftS1.uncocked?
|
147
|
+
@ftS1.cock
|
148
|
+
assert !@ftS1.uncocked?
|
149
|
+
@ftS1.fire
|
150
|
+
@p1.marking.must_equal 3
|
151
|
+
# enough playing, we'll reset @p1 marking
|
152
|
+
@p1.reset_marking
|
153
|
+
@p1.marking.must_equal 1
|
154
|
+
# #action
|
155
|
+
assert @tt.all? { |t| t.action == [1] }
|
156
|
+
# #zero_action
|
157
|
+
assert @tt.all? { |t| t.zero_action }
|
158
|
+
# #action_after_feasibility_check
|
159
|
+
assert @tt.all? { |t| t.action_after_feasibility_check == [1] }
|
160
|
+
# #domain_marking
|
161
|
+
assert @tt.all? { |t| t.domain_marking == [] }
|
162
|
+
# #codomain_marking
|
163
|
+
assert @tt.all? { |t| t.codomain_marking == [@p1.m] }
|
164
|
+
# #enabled?
|
165
|
+
@p1.m.must_equal 1
|
166
|
+
@p1.guard.( 1 ).must_equal true
|
167
|
+
@tt.each { |t| t.enabled?.must_equal true }
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "functional tS transitions" do
|
172
|
+
# Closure supplied to tS transitions governs their action.
|
173
|
+
|
174
|
+
before do
|
175
|
+
# stoichiometry given as hash
|
176
|
+
@FtS1 = @ç.new action_closure: ->{ 1 }, s: { @p1 => 1 }
|
177
|
+
# :action_closure has alias :action
|
178
|
+
@FtS2 = @ç.new action: ->{ 1 }, s: { @p1 => 1 }
|
179
|
+
# stoichiometry given as array of coefficients + codomain
|
180
|
+
@FtS3 = @ç.new s: 1, codomain: @p1, action: ->{ 1 }
|
181
|
+
# Specifying +timed: false+ as well as +timeless: true+ should be OK.
|
182
|
+
@FtS4 = @ç.new s: { @p1 => 1 }, action: ->{ 1 }, timed: false
|
183
|
+
@FtS5 = @ç.new s: { @p1 => 1 }, action: ->{ 1 }, timeless: true
|
184
|
+
# Even together in one statement:
|
185
|
+
@FtS6 = @ç.new s: { @p1 => 1 }, action: ->{ 1 }, timed: false, timeless: true
|
186
|
+
@tt = @FtS1, @FtS2, @FtS3, @FtS4, @FtS5, @FtS6
|
187
|
+
end
|
188
|
+
|
189
|
+
it "should reject bad parameters" do
|
190
|
+
# # +timed: true+ should raise a complaint:
|
191
|
+
# -> { @ç.new s: { @p1 => 1 }, action: ->{ 1 }, timed: true }
|
192
|
+
# .must_raise ArgumentError # constraint relaxed?
|
193
|
+
end
|
194
|
+
|
195
|
+
it "should init and perform" do
|
196
|
+
assert @tt.all? { |t| t.action_arcs == [ @p1 ] }
|
197
|
+
assert @tt.all? { |t| t.timeless? }
|
198
|
+
assert @tt.all? { |t| not t.has_rate? }
|
199
|
+
assert @tt.all? { |t| t.rateless? }
|
200
|
+
assert @tt.all? { |t| not t.assignment_action? }
|
201
|
+
assert @tt.all? { |t| not t.functionless? }
|
202
|
+
assert @tt.all? { |t| t.functional? }
|
203
|
+
# and having nullary action closure
|
204
|
+
assert @tt.all? { |t| t.action_closure.arity == 0 }
|
205
|
+
# the transitions should be able to #fire!
|
206
|
+
@FtS1.fire!
|
207
|
+
# no need for more testing here
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
describe "TSr transitions (timed rateless stoichiometric)" do
|
213
|
+
# Sr transitions have an action closure, require a function block, and thus
|
214
|
+
# are always functional. Their closure must take Δt as its first argument.
|
215
|
+
|
216
|
+
#LATER: To save time, I omit the tests of TSr transitions for now.
|
217
|
+
end
|
218
|
+
|
219
|
+
describe "sR transitions (nonstoichiometric with rate)" do
|
220
|
+
# Expect a function block with arity equal to their domain size, and output
|
221
|
+
# arity equal to the codomain size.
|
222
|
+
|
223
|
+
#LATER: To save time, I omit the full test suite.
|
224
|
+
end
|
225
|
+
|
226
|
+
describe "SR transitions (stoichiometric with rate)" do
|
227
|
+
before do
|
228
|
+
# This should give standard mass action by magic:
|
229
|
+
@SR1 = @ç.new s: { @p1 => -1, @p2 => -1, @p4 => 1 }, rate: 0.1
|
230
|
+
# While this has custom closure:
|
231
|
+
@SR2 = @ç.new s: { @p1 => -1, @p3 => 1 }, rate: -> a { a * 0.5 }
|
232
|
+
# While this one even has domain explicitly specified:
|
233
|
+
@SR3 = @ç.new s: { @p1 => -1, @p2 => -1, @p4 => 1 },
|
234
|
+
upstream_arcs: @p3, rate: -> a { a * 0.5 }
|
235
|
+
end
|
236
|
+
|
237
|
+
it "should init and work" do
|
238
|
+
@SR1.has_rate?.must_equal true
|
239
|
+
@SR1.upstream_arcs.must_equal [@p1, @p2]
|
240
|
+
@SR1.action_arcs.must_equal [@p1, @p2, @p4]
|
241
|
+
@SR2.domain.must_equal [@p1]
|
242
|
+
@SR2.action_arcs.must_equal [@p1, @p3]
|
243
|
+
@SR3.domain.must_equal [@p3]
|
244
|
+
@SR3.action_arcs.must_equal [@p1, @p2, @p4]
|
245
|
+
# and flex them
|
246
|
+
@SR1.fire! 1.0
|
247
|
+
[@p1, @p2, @p4].map( &:marking ).must_equal [0.8, 1.8, 4.2]
|
248
|
+
@SR2.fire! 1.0
|
249
|
+
[@p1, @p3].map( &:marking ).must_equal [0.4, 3.4]
|
250
|
+
# the action t3 cannot fire with delta time 1.0
|
251
|
+
-> { @SR3.fire! 1.0 }.must_raise RuntimeError
|
252
|
+
[@p1, @p2, @p3, @p4].map( &:marking ).must_equal [0.4, 1.8, 3.4, 4.2]
|
253
|
+
# but it can fire with eg. delta time 0.1
|
254
|
+
@SR3.fire! 0.1
|
255
|
+
assert_in_epsilon 0.23, @p1.marking, 1e-15
|
256
|
+
assert_in_epsilon 1.63, @p2.marking, 1e-15
|
257
|
+
assert_in_epsilon 3.4, @p3.marking, 1e-15
|
258
|
+
assert_in_epsilon 4.37, @p4.marking, 1e-15
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
|
264
|
+
# **************************************************************************
|
265
|
+
# Test of mutual knowedge of upstream/downstream arcs of places/transitions.
|
266
|
+
# **************************************************************************
|
267
|
+
#
|
268
|
+
describe "upstream and downstream reference mτs of places and transitions" do
|
269
|
+
before do
|
270
|
+
# skip "to speed up testing"
|
271
|
+
@tç = tç = Class.new YPetri::Transition
|
272
|
+
@pç = pç = Class.new YPetri::Place
|
273
|
+
[ tç, pç ].each { |ç|
|
274
|
+
ç.class_exec {
|
275
|
+
define_method :Place do pç end
|
276
|
+
define_method :Transition do tç end
|
277
|
+
private :Place, :Transition
|
278
|
+
}
|
279
|
+
}
|
280
|
+
@a = @pç.new( default_marking: 1.0 )
|
281
|
+
@b = @pç.new( default_marking: 2.0 )
|
282
|
+
@c = @pç.new( default_marking: 3.0 )
|
283
|
+
end
|
284
|
+
|
285
|
+
describe "Place" do
|
286
|
+
it "should have #register_ustream/downstream_transition methods" do
|
287
|
+
@t1 = @tç.new s: {}
|
288
|
+
@a.instance_variable_get( :@upstream_arcs ).must_equal []
|
289
|
+
@a.instance_variable_get( :@downstream_arcs ).must_equal []
|
290
|
+
@a.send :register_upstream_transition, @t1
|
291
|
+
@a.instance_variable_get( :@upstream_arcs ).must_equal [ @t1 ]
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
describe "upstream and downstream reference methods" do
|
296
|
+
before do
|
297
|
+
@t1 = @tç.new s: { @a => -1, @b => 1 }, rate: 1
|
298
|
+
end
|
299
|
+
|
300
|
+
it "should show on the referencers" do
|
301
|
+
@a.upstream_arcs.must_equal [@t1]
|
302
|
+
@b.downstream_arcs.must_equal []
|
303
|
+
@b.ϝ.must_equal [@t1]
|
304
|
+
@t1.upstream_arcs.must_equal [@a]
|
305
|
+
@t1.action_arcs.must_equal [@a, @b]
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
describe "assignment action transitions" do
|
310
|
+
before do
|
311
|
+
@p = @pç.new default_marking: 1.0
|
312
|
+
@t = @tç.new codomain: @p, action: -> { 1 }, assignment_action: true
|
313
|
+
end
|
314
|
+
|
315
|
+
it "should work" do
|
316
|
+
@p.marking = 3
|
317
|
+
@p.marking.must_equal 3
|
318
|
+
assert @t.assignment_action?
|
319
|
+
@t.domain.must_equal []
|
320
|
+
@t.action_closure.arity.must_equal 0
|
321
|
+
@t.fire!
|
322
|
+
@p.marking.must_equal 1
|
323
|
+
end
|
324
|
+
end # context assignment action transiotions
|
325
|
+
end
|