y_petri 2.0.3 → 2.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|