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/simulation.rb
CHANGED
@@ -50,150 +50,110 @@ class YPetri::Simulation
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
# Currently,
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
|
53
|
+
# Currently, simulation is largely immutable. Net, initial marking, clamps
|
54
|
+
# and simulation settings are set upon initialization, whereupon the instance
|
55
|
+
# forms their "mental image", which remains immune to any subsequent changes
|
56
|
+
# to the original objects. Required parameters are :net, :marking_clamps, and
|
57
|
+
# :initial_marking. Optional is :method (simulation method), and :guarded
|
58
|
+
# (true/false, whether the simulation guards the transition function results).
|
59
|
+
# Guard conditions can be either implicit (guarding against negative values
|
60
|
+
# and against type changes in by transition action), or explicitly associated
|
61
|
+
# with either places, or transition function results.
|
62
|
+
#
|
63
|
+
def initialize( method: default_simulation_method,
|
64
|
+
guarded: false,
|
65
|
+
net: raise( ArgumentError, "Net argument absent!" ),
|
66
|
+
marking_clamps: {},
|
67
|
+
initial_marking: {} )
|
63
68
|
puts "starting to set up Simulation" if YPetri::DEBUG
|
64
|
-
|
65
|
-
|
66
|
-
args.must_have :net do |o| o.class_complies? ::YPetri::Net end
|
67
|
-
args.may_have :place_clamps, syn!: :marking_clamps
|
68
|
-
args.may_have :initial_marking, syn!: :initial_marking_vector
|
69
|
-
|
70
|
-
# ==== Simulation method
|
71
|
-
#
|
72
|
-
@method = args[:method] || default_simulation_method()
|
73
|
-
|
74
|
-
# ==== Net
|
75
|
-
#
|
76
|
-
@net = args[:net].dup # @immutable within the instance
|
77
|
-
@places = @net.places.dup
|
78
|
-
@transitions = @net.transitions.dup
|
79
|
-
|
69
|
+
@method, @guarded, @net = method, guarded, net
|
70
|
+
@places, @transitions = @net.places.dup, @net.transitions.dup
|
80
71
|
self.singleton_class.class_exec {
|
81
72
|
define_method :Place do net.send :Place end
|
82
73
|
define_method :Transition do net.send :Transition end
|
83
74
|
define_method :Net do net.send :Net end
|
84
75
|
private :Place, :Transition, :Net
|
85
|
-
}
|
86
|
-
|
87
|
-
puts "setup of :net mental image complete" if YPetri::DEBUG
|
88
|
-
|
89
|
-
# ==== Simulation parameters
|
90
|
-
#
|
91
|
-
# A simulation distinguishes between free and clamped places. For free
|
92
|
-
# places, initial value has to be specified. For clamped places, clamps
|
93
|
-
# have to be specified. Both initial values and clamps are expected as
|
94
|
-
# hash-type named parameters:
|
95
|
-
@place_clamps = ( args[:place_clamps] || {} ).with_keys { |k| place k }
|
96
|
-
@initial_marking = ( args[:initial_marking] || {} ).with_keys { |k| place k }
|
76
|
+
}; puts "setup of :net mental image complete" if YPetri::DEBUG
|
97
77
|
|
78
|
+
# A simulation distinguishes between free and clamped places. For free
|
79
|
+
# places, initial marking has to be specified. For clamped places, marking
|
80
|
+
# clamps have to be specified. Both come as hashes:
|
81
|
+
@marking_clamps = marking_clamps.with_keys { |k| place k }
|
82
|
+
@initial_marking = initial_marking.with_keys { |k| place k }
|
98
83
|
# Enforce that keys in the hashes must be unique:
|
99
|
-
@
|
84
|
+
@marking_clamps.keys.aT_equal @marking_clamps.keys.uniq
|
100
85
|
@initial_marking.keys.aT_equal @initial_marking.keys.uniq
|
101
|
-
|
102
86
|
puts "setup of clamps and initial marking done" if YPetri::DEBUG
|
103
87
|
|
104
|
-
#
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
# place.aT_not "clamped place #{place}",
|
109
|
-
# "have explicitly specified initial marking" do |place|
|
110
|
-
# @initial_marking.keys.include? place
|
111
|
-
# end
|
112
|
-
# }
|
113
|
-
|
114
|
-
# Each place must be treated: either clamped, or have initial marking
|
115
|
-
places.each { |p|
|
116
|
-
p.aT "place #{p}", "have either clamp or initial marking" do |p|
|
117
|
-
@place_clamps.keys.include?( p ) || @initial_marking.keys.include?( p )
|
88
|
+
# Each place must have either clamp, or initial marking:
|
89
|
+
places.each { |pl|
|
90
|
+
pl.aT "place #{pl}", "have either clamp or initial marking" do |pl|
|
91
|
+
( @marking_clamps.keys + @initial_marking.keys ).include? pl
|
118
92
|
end
|
119
|
-
}
|
93
|
+
}; puts "clamp || initial marking test passed" if YPetri::DEBUG
|
120
94
|
|
121
|
-
|
122
|
-
|
123
|
-
# === Correspondence matrices.
|
124
|
-
|
125
|
-
# Multiplying this matrix by marking vector for free places (ᴍ) gives
|
126
|
-
# ᴍ mapped for all places.
|
95
|
+
# @F2A * ᴍ (marking vector of free places) maps ᴍ to all places.
|
127
96
|
@F2A = Matrix.correspondence_matrix( free_places, places )
|
128
|
-
|
129
|
-
# Multiplying this matrix by marking vector for clamped places maps that
|
130
|
-
# vector to all places.
|
97
|
+
# @C2A * marking_vector_of_clamped_places maps it to all places.
|
131
98
|
@C2A = Matrix.correspondence_matrix( clamped_places, places )
|
132
|
-
|
133
99
|
puts "correspondence matrices set up" if YPetri::DEBUG
|
134
100
|
|
135
|
-
#
|
101
|
+
# Stoichiometry matrices:
|
136
102
|
@S_for_tS = S_for tS_transitions()
|
137
103
|
@S_for_SR = S_for SR_transitions()
|
138
104
|
@S_for_TSr = S_for TSr_transitions()
|
139
|
-
|
140
105
|
puts "stoichiometry matrices set up" if YPetri::DEBUG
|
141
106
|
|
142
|
-
#
|
143
|
-
@Δ
|
107
|
+
# Other assets:
|
108
|
+
@Δ_closures_for_tsa = create_Δ_closures_for_tsa
|
144
109
|
@Δ_closures_for_Tsr = create_Δ_closures_for_Tsr
|
145
110
|
@action_closures_for_tS = create_action_closures_for_tS
|
146
111
|
@action_closures_for_TSr = create_action_closures_for_TSr
|
147
112
|
@rate_closures_for_sR = create_rate_closures_for_sR
|
148
113
|
@rate_closures_for_SR = create_rate_closures_for_SR
|
149
|
-
|
150
114
|
@assignment_closures_for_A = create_assignment_closures_for_A
|
151
|
-
|
152
|
-
puts "other assets set up, about to reset" if YPetri::DEBUG
|
153
|
-
|
154
|
-
# ----------- Reset -------------
|
155
|
-
reset!
|
156
|
-
|
157
115
|
@zero_ᴍ = compute_initial_marking_vector_of_free_places.map { |e| e * 0 }
|
158
116
|
@zero_gradient = @zero_ᴍ.dup
|
117
|
+
puts "other assets set up, about to reset" if YPetri::DEBUG
|
159
118
|
|
160
|
-
puts "reset complete" if YPetri::DEBUG
|
161
|
-
end
|
162
|
-
|
163
|
-
# Returns a new instance of the system at a
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
168
|
-
#
|
169
|
-
#
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
else
|
189
|
-
duplicate.send :set_pm, oo
|
190
|
-
end
|
119
|
+
reset!; puts "reset complete" if YPetri::DEBUG
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns a new instance of the system simulation at a specified state, with
|
123
|
+
# same simulation settings. This state (:marking argument) can be specified
|
124
|
+
# either as marking vector for free or all places, marking array for free or
|
125
|
+
# all places, or marking hash. If vector or array is given, its size must
|
126
|
+
# correspond to the number of either free, or all places. If hash is given,
|
127
|
+
# it is not necessary to specify marking of every place – marking of those
|
128
|
+
# left out will be left same as in the current state.
|
129
|
+
#
|
130
|
+
def at( marking: marking, **oo )
|
131
|
+
err_msg = "Size of supplied marking must match either the number of " +
|
132
|
+
"free places, or the number of all places!"
|
133
|
+
update_method = case marking
|
134
|
+
when Hash then :update_marking_from_a_hash
|
135
|
+
when Matrix then
|
136
|
+
case marking.column_to_a.size
|
137
|
+
when places.size then :set_marking_vector
|
138
|
+
when free_places.size then :set_ᴍ
|
139
|
+
else raise TypeError, err_msg end
|
140
|
+
else # marking assumed to be an array
|
141
|
+
case marking.size
|
142
|
+
when places.size then :set_marking
|
143
|
+
when free_places.size then :set_m
|
144
|
+
else raise TypeError, err_msg end
|
145
|
+
end
|
146
|
+
return dup( **oo ).send( update_method, marking )
|
191
147
|
end
|
192
148
|
|
193
149
|
# Exposing @net.
|
194
150
|
#
|
195
151
|
attr_reader :net
|
196
152
|
|
153
|
+
# Is the simulation guarded?
|
154
|
+
#
|
155
|
+
def guarded?; @guarded end
|
156
|
+
|
197
157
|
# Without arguments or block, it returns simply a list of places. Otherwise,
|
198
158
|
# it returns a has whose keys are the places, and whose values are governed
|
199
159
|
# by the supplied parameters (either another collection, or message to #send
|
@@ -287,7 +247,7 @@ class YPetri::Simulation
|
|
287
247
|
#
|
288
248
|
def clamped_places *aa, &b
|
289
249
|
return zip_to_hash clamped_places, *aa, &b unless aa.empty? && b.nil?
|
290
|
-
kk = @
|
250
|
+
kk = @marking_clamps.keys
|
291
251
|
places.select { |p| kk.include? p }
|
292
252
|
end
|
293
253
|
|
@@ -301,9 +261,10 @@ class YPetri::Simulation
|
|
301
261
|
|
302
262
|
# Place clamp definitions for clamped places (array)
|
303
263
|
#
|
304
|
-
def
|
305
|
-
clamped_places.map { |p| @
|
264
|
+
def marking_clamps
|
265
|
+
clamped_places.map { |p| @marking_clamps[p] }
|
306
266
|
end
|
267
|
+
alias place_clamps marking_clamps
|
307
268
|
|
308
269
|
# Marking array of free places.
|
309
270
|
#
|
@@ -438,6 +399,26 @@ class YPetri::Simulation
|
|
438
399
|
ts_transitions.map { |t| t.name || t }
|
439
400
|
end
|
440
401
|
|
402
|
+
# Assignment transitions (A transition) can be regarded as a special kind
|
403
|
+
# of ts transition (subtracting away the current marking of their domain
|
404
|
+
# and replacing it with the result of their function). But it may often
|
405
|
+
# be useful to exclude A transitions from among the ts transitions, and
|
406
|
+
# such set is called tsa transitions (timeless nonstoichiometric
|
407
|
+
# nonassignment transitions).
|
408
|
+
#
|
409
|
+
def tsa_transitions *aa, &b
|
410
|
+
return zip_to_hash tsa_transitions, *aa, &b unless aa.empty? && b.nil?
|
411
|
+
sift_from_net :tsa_transitions
|
412
|
+
end
|
413
|
+
|
414
|
+
# Like #tsa_transitions, except that transition names are used instead of
|
415
|
+
# instance, whenever possible.
|
416
|
+
#
|
417
|
+
def tsa_tt *aa, &b
|
418
|
+
return zip_to_hash tsa_tt, *aa, &b unless aa.empty? && b.nil?
|
419
|
+
tsa_transitions.map { |t| t.name || t }
|
420
|
+
end
|
421
|
+
|
441
422
|
# ==== 2. Exposing tS transitions
|
442
423
|
|
443
424
|
# Without arguments or block, it returns simply a list of timeless
|
@@ -555,6 +536,7 @@ class YPetri::Simulation
|
|
555
536
|
return zip_to_hash A_transitions(), *aa, &b unless aa.empty? && b.nil?
|
556
537
|
sift_from_net :A_transitions
|
557
538
|
end
|
539
|
+
alias assignment_transitions A_transitions
|
558
540
|
|
559
541
|
# Like #A_transitions, except that transition names are used instead of
|
560
542
|
# instances, whenever possible.
|
@@ -563,6 +545,7 @@ class YPetri::Simulation
|
|
563
545
|
return zip_to_hash A_tt(), *aa, &b unless aa.empty? && b.nil?
|
564
546
|
A_transitions().map { |t| t.name || t }
|
565
547
|
end
|
548
|
+
alias assignment_tt A_tt
|
566
549
|
|
567
550
|
# ==== Stoichiometric transitions of any kind (S transitions)
|
568
551
|
|
@@ -656,14 +639,18 @@ class YPetri::Simulation
|
|
656
639
|
|
657
640
|
# Exposing Δ state closures for ts transitions.
|
658
641
|
#
|
659
|
-
attr_reader :Δ
|
642
|
+
attr_reader :Δ_closures_for_tsa
|
660
643
|
|
661
|
-
# Delta state contribution if
|
662
|
-
# are called in their order, but the state
|
663
|
-
# between the calls (
|
644
|
+
# Delta state contribution if timed nonstoichiometric non-assignment (tsa)
|
645
|
+
# transitions fire once. The closures are called in their order, but the state
|
646
|
+
# update is not performed between the calls (they fire simultaneously).
|
647
|
+
#
|
648
|
+
# Note: 'a' in 'tsa' is needed because A (assignment) transitions can also be
|
649
|
+
# regarded as a special kind of ts transitions, while they obviously do not
|
650
|
+
# act through Δ state, but rather directly enforce marking of their codomain.
|
664
651
|
#
|
665
|
-
def Δ
|
666
|
-
Δ
|
652
|
+
def Δ_if_tsa_fire_once
|
653
|
+
Δ_closures_for_tsa.map( &:call ).reduce( @zero_ᴍ, :+ )
|
667
654
|
end
|
668
655
|
|
669
656
|
# ==== Regarding Tsr transitions
|
@@ -858,7 +845,8 @@ class YPetri::Simulation
|
|
858
845
|
def flux_vector
|
859
846
|
return flux_vector_for_SR if s_transitions.empty? && r_transitions.empty?
|
860
847
|
raise "One may only call this method when all the transitions of the " +
|
861
|
-
"simulation are SR transitions."
|
848
|
+
"simulation are SR transitions. Try #flux_vector_for( *transitions ), " +
|
849
|
+
"#flux_vector_for_SR, #flux_for( *transitions ), or #flux_for_SR"
|
862
850
|
end
|
863
851
|
alias φ flux_vector
|
864
852
|
|
@@ -1154,7 +1142,7 @@ class YPetri::Simulation
|
|
1154
1142
|
def compute_marking_vector_of_clamped_places
|
1155
1143
|
puts "computing the marking vector of clamped places" if YPetri::DEBUG
|
1156
1144
|
results = clamped_places.map { |p|
|
1157
|
-
clamp = @
|
1145
|
+
clamp = @marking_clamps[ p ]
|
1158
1146
|
puts "doing clamped place #{p} with clamp #{clamp}" if YPetri::DEBUG
|
1159
1147
|
# unwrap places / cells
|
1160
1148
|
clamp = case clamp
|
@@ -1179,10 +1167,17 @@ class YPetri::Simulation
|
|
1179
1167
|
@marking_vector += F2A() * Δ_free_places
|
1180
1168
|
end
|
1181
1169
|
|
1170
|
+
# Guards proposed marking delta.
|
1171
|
+
#
|
1172
|
+
def guard_Δ! Δ_free_places
|
1173
|
+
ary = ( marking_vector + F2A() * Δ_free_places ).column_to_a
|
1174
|
+
places.zip( ary ).each { |pl, proposed_m| pl.guard.( proposed_m ) }
|
1175
|
+
end
|
1176
|
+
|
1182
1177
|
# Fires all assignment transitions once.
|
1183
1178
|
#
|
1184
1179
|
def assignment_transitions_all_fire!
|
1185
|
-
assignment_closures_for_A.
|
1180
|
+
assignment_closures_for_A.each_with_index do |closure, i|
|
1186
1181
|
@marking_vector = closure.call # TODO: This offers better algorithm.
|
1187
1182
|
end
|
1188
1183
|
end
|
@@ -1193,33 +1188,68 @@ class YPetri::Simulation
|
|
1193
1188
|
# These instance assets are created at the beginning, so the work
|
1194
1189
|
# needs to be performed only once in the instance lifetime.
|
1195
1190
|
|
1196
|
-
def create_Δ
|
1197
|
-
|
1191
|
+
def create_Δ_closures_for_tsa
|
1192
|
+
tsa_transitions.map { |t|
|
1198
1193
|
p2d = Matrix.correspondence_matrix( places, t.domain )
|
1199
1194
|
c2f = Matrix.correspondence_matrix( t.codomain, free_places )
|
1200
|
-
|
1195
|
+
if guarded? then
|
1196
|
+
-> {
|
1197
|
+
domain_marking = ( p2d * marking_vector ).column_to_a
|
1198
|
+
# I. TODO: t.domain_guard.( domain_marking )
|
1199
|
+
codomain_change = Array t.action_closure.( *domain_marking )
|
1200
|
+
# II. TODO: t.action_guard.( codomain_change )
|
1201
|
+
c2f * codomain_change
|
1202
|
+
}
|
1203
|
+
else
|
1204
|
+
-> { c2f * t.action_closure.( *( p2d * marking_vector ).column_to_a ) }
|
1205
|
+
end
|
1201
1206
|
}
|
1202
1207
|
end
|
1203
1208
|
|
1209
|
+
def blame_tsa( marking_vect )
|
1210
|
+
# If, in spite of passing domain guard and action guard, marking guard
|
1211
|
+
# indicates an exception, the method here serves to find the candidate
|
1212
|
+
# transitions to blame for the exception, given certain place marking.
|
1213
|
+
msg = "Action closure of transition #%{t} with domain #%{dm} and " +
|
1214
|
+
"codomain #%{cdm} returns #%{retval} which, when added to place " +
|
1215
|
+
"#{p}, gives marking that would flunk place's marking guard."
|
1216
|
+
tsa_transitions.each { |t|
|
1217
|
+
p2d = Matrix.correspondence_matrix( places, t.domain )
|
1218
|
+
rslt = Array t.action_closure( *( p2d * marking_vect ).column_to_a )
|
1219
|
+
t.codomain.zip( rslt ).each { |place, Δ|
|
1220
|
+
fields = {
|
1221
|
+
t: t.name || t.object_id, p: place,
|
1222
|
+
dm: Hash[ t.domain_pp.zip domain_marking ],
|
1223
|
+
cdm: Hash[ t.codomain_pp.zip( t.codomain.map { |p| ꜧ[p] } ) ],
|
1224
|
+
retval: Hash[ t.codomain_pp.zip( codomain_change ) ]
|
1225
|
+
}
|
1226
|
+
rslt = ꜧ[place] + Δ
|
1227
|
+
raise TypeError, msg % fields unless place.marking_guard.( rslt )
|
1228
|
+
}
|
1229
|
+
}
|
1230
|
+
# TODO: Here, #blame_tsa simply raises. It would be however more correct
|
1231
|
+
# to gather all blame candidates and present them to the user all.
|
1232
|
+
end
|
1233
|
+
|
1204
1234
|
def create_Δ_closures_for_Tsr
|
1205
1235
|
Tsr_transitions().map { |t|
|
1206
1236
|
p2d = Matrix.correspondence_matrix( places, t.domain )
|
1207
1237
|
c2f = Matrix.correspondence_matrix( t.codomain, free_places )
|
1208
|
-
|
1238
|
+
-> Δt { c2f * t.action_closure.( Δt, *( p2d * marking_vector ).column_to_a ) }
|
1209
1239
|
}
|
1210
1240
|
end
|
1211
1241
|
|
1212
1242
|
def create_action_closures_for_tS
|
1213
1243
|
tS_transitions.map{ |t|
|
1214
1244
|
p2d = Matrix.correspondence_matrix( places, t.domain )
|
1215
|
-
|
1245
|
+
-> { t.action_closure.( *( p2d * marking_vector ).column_to_a ) }
|
1216
1246
|
}
|
1217
1247
|
end
|
1218
1248
|
|
1219
1249
|
def create_action_closures_for_TSr
|
1220
1250
|
TSr_transitions().map{ |t|
|
1221
1251
|
p2d = Matrix.correspondence_matrix( places, t.domain )
|
1222
|
-
|
1252
|
+
-> Δt { t.action_closure.( Δt, *( p2d * marking_vector ).column_to_a ) }
|
1223
1253
|
}
|
1224
1254
|
end
|
1225
1255
|
|
@@ -1227,15 +1257,17 @@ class YPetri::Simulation
|
|
1227
1257
|
sR_transitions.map{ |t|
|
1228
1258
|
p2d = Matrix.correspondence_matrix( places, t.domain )
|
1229
1259
|
c2f = Matrix.correspondence_matrix( t.codomain, free_places )
|
1230
|
-
|
1260
|
+
-> { c2f * t.rate_closure.( *( p2d * marking_vector ).column_to_a ) }
|
1231
1261
|
}
|
1232
1262
|
end
|
1233
1263
|
|
1234
1264
|
def create_rate_closures_for_SR
|
1235
|
-
SR_transitions().map{ |t|
|
1265
|
+
SR_transitions().map { |t|
|
1236
1266
|
p2d = Matrix.correspondence_matrix( places, t.domain )
|
1237
1267
|
puts "Marking is #{pp :marking rescue nil}" if YPetri::DEBUG
|
1238
|
-
|
1268
|
+
-> { t.rate_closure.( *( p2d * marking_vector ).column_to_a )
|
1269
|
+
.tap do |r| fail YPetri::GuardError, "SR #{t.name}!!!!" if r.is_a? Complex end
|
1270
|
+
}
|
1239
1271
|
}
|
1240
1272
|
end
|
1241
1273
|
|
@@ -1248,51 +1280,47 @@ class YPetri::Simulation
|
|
1248
1280
|
probe = Matrix.column_vector( t.codomain.size.times.map { |a| a + 1 } )
|
1249
1281
|
result = ( F2A() * c2f * probe ).column_to_a.map { |n| n == 0 ? nil : n }
|
1250
1282
|
assignment_addresses = probe.column_to_a.map { |i| result.index i }
|
1251
|
-
|
1252
|
-
# puts "result is #{result}"
|
1283
|
+
-> {
|
1253
1284
|
act = Array t.action_closure.( *( p2d * marking_vector ).column_to_a )
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
# puts "assign is #{assign}"
|
1259
|
-
assign = assign.each_with_object nils.dup do |pair, o| o[pair[0]] = pair[1] end
|
1260
|
-
# puts "assign is #{assign}"
|
1261
|
-
@marking_vector.map { |original_marking|
|
1262
|
-
assignment_order = assign.shift
|
1263
|
-
assignment_order ? assignment_order : original_marking
|
1285
|
+
act.each_with_index { |e, i|
|
1286
|
+
fail YPetri::GuardError, "Assignment transition #{t.name} with " +
|
1287
|
+
"domain #{t.domain_pp( domain_marking )} has produced a complex " +
|
1288
|
+
"number at output positon #{i} (output was #{act})!" if e.is_a?( Complex ) || i.is_a?( Complex )
|
1264
1289
|
}
|
1265
|
-
|
1290
|
+
assign = assignment_addresses.zip( act )
|
1291
|
+
.each_with_object nils.dup do |pair, o| o[pair[0]] = pair[1] end
|
1292
|
+
marking_vector.map { |orig_val| assign.shift || orig_val }
|
1293
|
+
}
|
1266
1294
|
} # map
|
1267
|
-
|
1295
|
+
end
|
1268
1296
|
|
1269
|
-
# Set
|
1297
|
+
# Set marking vector (for all places).
|
1270
1298
|
#
|
1271
|
-
def set_marking_vector
|
1272
|
-
@marking_vector =
|
1299
|
+
def set_marking_vector marking_vector
|
1300
|
+
@marking_vector = marking_vector
|
1273
1301
|
return self
|
1274
1302
|
end
|
1275
1303
|
|
1276
|
-
# Set
|
1304
|
+
# Set marking vector, based on marking array of all places.
|
1277
1305
|
#
|
1278
1306
|
def set_marking marking_array
|
1279
1307
|
set_marking_vector Matrix.column_vector( marking_array )
|
1280
1308
|
end
|
1281
1309
|
|
1282
|
-
#
|
1310
|
+
# Update marking vector, based on { place => marking } hash argument.
|
1283
1311
|
#
|
1284
|
-
def
|
1312
|
+
def update_marking_from_a_hash marking_hash
|
1285
1313
|
to_set = place_marking.merge( marking_hash.with_keys do |k| place k end )
|
1286
1314
|
set_marking( places.map { |pl| to_set[ pl ] } )
|
1287
1315
|
end
|
1288
1316
|
|
1289
|
-
# Set
|
1317
|
+
# Set marking vector based on marking array of free places.
|
1290
1318
|
#
|
1291
1319
|
def set_m marking_array_for_free_places
|
1292
|
-
|
1320
|
+
set_marking_from_a_hash( free_places( marking_array_for_free_places ) )
|
1293
1321
|
end
|
1294
1322
|
|
1295
|
-
# Set
|
1323
|
+
# Set marking vector based on marking vector of free places.
|
1296
1324
|
#
|
1297
1325
|
def set_ᴍ marking_vector_for_free_places
|
1298
1326
|
set_m( marking_vector_for_free_places.column_to_a )
|
@@ -1305,28 +1333,23 @@ class YPetri::Simulation
|
|
1305
1333
|
return self
|
1306
1334
|
end
|
1307
1335
|
|
1308
|
-
# Duplicate creation.
|
1336
|
+
# Duplicate creation.
|
1309
1337
|
#
|
1310
|
-
def
|
1311
|
-
|
1312
|
-
|
1313
|
-
|
1314
|
-
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1338
|
+
def dup( **oo )
|
1339
|
+
self.class.new( oo.reverse_merge!( { method: @method,
|
1340
|
+
guarded: @guarded,
|
1341
|
+
net: @net,
|
1342
|
+
marking_clamps: @marking_clamps,
|
1343
|
+
initial_marking: @initial_marking
|
1344
|
+
}.update( simulation_settings ) ) )
|
1345
|
+
.tap { |instance|
|
1346
|
+
instance.send :set_recording, recording
|
1347
|
+
instance.send :set_marking_vector, @marking_vector
|
1348
|
+
}
|
1319
1349
|
end
|
1320
1350
|
|
1321
|
-
# Place, Transition, Net class
|
1322
|
-
#
|
1323
|
-
def Place; YPetri::Place end
|
1324
|
-
def Transition; YPetri::Transition end
|
1325
|
-
|
1326
1351
|
# Instance identification methods.
|
1327
1352
|
#
|
1328
1353
|
def place( which ); Place().instance( which ) end
|
1329
1354
|
def transition( which ); Transition().instance( which ) end
|
1330
|
-
|
1331
|
-
# LATER: Mathods for timeless simulation.
|
1332
1355
|
end # class YPetri::Simulation
|
@@ -1,5 +1,4 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# -*- coding: utf-8 -*-
|
3
2
|
# A descendant class of YPetri::Simulation that introduces timekeeping.
|
4
3
|
#
|
5
4
|
class YPetri::TimedSimulation < YPetri::Simulation
|
@@ -78,17 +77,17 @@ class YPetri::TimedSimulation < YPetri::Simulation
|
|
78
77
|
# (alias :step), :sampling_period (alias :sampling), and :target_time
|
79
78
|
# named arguments.
|
80
79
|
#
|
81
|
-
def initialize
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
@step_size =
|
87
|
-
@sampling_period =
|
88
|
-
@target_time =
|
89
|
-
@initial_time =
|
80
|
+
def initialize( **named_args )
|
81
|
+
named_args.must_have :step_size, syn!: :step
|
82
|
+
named_args.must_have :sampling_period, syn!: :sampling
|
83
|
+
named_args.may_have :target_time
|
84
|
+
named_args.may_have :initial_time
|
85
|
+
@step_size = named_args.delete :step_size
|
86
|
+
@sampling_period = named_args.delete :sampling_period
|
87
|
+
@target_time = named_args.delete :target_time
|
88
|
+
@initial_time = named_args.delete( :initial_time ) ||
|
90
89
|
@target_time.nil? ? nil : @sampling_period * 0 # @target_time.class.zero
|
91
|
-
super
|
90
|
+
super( **named_args )
|
92
91
|
@zero_gradient = @zero_ᴍ.map { |e| step_size.to_f / step_size * e }
|
93
92
|
end
|
94
93
|
# LATER: transition clamps
|
@@ -97,11 +96,8 @@ class YPetri::TimedSimulation < YPetri::Simulation
|
|
97
96
|
# which is set to the required state / time. In addition to the parent class,
|
98
97
|
# this version alseo sets time.
|
99
98
|
#
|
100
|
-
def at
|
101
|
-
oo
|
102
|
-
duplicate = super *args, oo
|
103
|
-
t = oo.may_have( :t, syn!: :ᴛ ) and duplicate.send :set_time, t
|
104
|
-
return duplicate
|
99
|
+
def at( time: ᴛ, **oo )
|
100
|
+
super( **oo ).tap { |duplicate| duplicate.send :set_time, time }
|
105
101
|
end
|
106
102
|
|
107
103
|
# At the moment, near alias for #run_to_arget_time!
|
@@ -157,7 +153,13 @@ class YPetri::TimedSimulation < YPetri::Simulation
|
|
157
153
|
# affected.
|
158
154
|
#
|
159
155
|
def Euler_step!( Δt=@step_size ) # implicit Euler method
|
160
|
-
|
156
|
+
delta = Δ_Euler_for_free_places( Δt )
|
157
|
+
if guarded? then
|
158
|
+
guard_Δ! delta
|
159
|
+
update_marking! delta
|
160
|
+
else
|
161
|
+
update_marking! delta
|
162
|
+
end
|
161
163
|
update_time! Δt
|
162
164
|
end
|
163
165
|
alias euler_step! Euler_step!
|
@@ -166,8 +168,15 @@ class YPetri::TimedSimulation < YPetri::Simulation
|
|
166
168
|
# affected.
|
167
169
|
#
|
168
170
|
def timeless_transitions_all_fire!
|
169
|
-
|
170
|
-
|
171
|
+
try "to update marking" do
|
172
|
+
update_marking!( note( "Δ state if tS transitions fire once",
|
173
|
+
is: Δ_if_tS_fire_once ) +
|
174
|
+
note( "Δ state if tsa transitions fire once",
|
175
|
+
is: Δ_if_tsa_fire_once ) )
|
176
|
+
end
|
177
|
+
try "to fire the assignment transitions" do
|
178
|
+
assignment_transitions_all_fire!
|
179
|
+
end
|
171
180
|
end
|
172
181
|
alias t_all_fire! timeless_transitions_all_fire!
|
173
182
|
|