y_petri 2.0.14 → 2.0.15
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/manipulator/simulation_related_methods.rb +10 -5
- data/lib/y_petri/net.rb +4 -4
- data/lib/y_petri/place/guard.rb +17 -12
- data/lib/y_petri/simulation/collections.rb +460 -0
- data/lib/y_petri/{timed_simulation.rb → simulation/timed.rb} +55 -68
- data/lib/y_petri/simulation.rb +192 -724
- data/lib/y_petri/transition/construction.rb +1 -1
- data/lib/y_petri/transition.rb +82 -86
- data/lib/y_petri/version.rb +1 -1
- data/lib/y_petri/workspace/parametrized_subclassing.rb +2 -1
- data/lib/y_petri.rb +1 -2
- data/test/acceptance/basic_usage_test.rb +1 -1
- data/test/manipulator_test.rb +1 -1
- data/test/net_test.rb +2 -1
- data/test/place_test.rb +3 -1
- data/test/simulation_test.rb +14 -13
- data/test/timed_simulation_test.rb +30 -26
- data/test/workspace_test.rb +1 -1
- data/test/y_petri_test.rb +2 -4
- metadata +4 -3
data/lib/y_petri/simulation.rb
CHANGED
@@ -1,15 +1,19 @@
|
|
1
1
|
#encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
3
|
+
require_relative 'simulation/collections'
|
4
|
+
require_relative 'simulation/timed'
|
5
|
+
|
6
|
+
# Represents a simulation of a Petri net, using certain method and settings.
|
7
|
+
# Simulation concerns (simulation method and settings, initial values, marking
|
8
|
+
# clamps, guards...) are separated from those Petri net domain model (existence,
|
9
|
+
# naming, connectivity and function specification of the net). Again, clamps,
|
10
|
+
# guards, initial values etc. <b>do not belong</b> to the model, although for
|
11
|
+
# convenience, places may carry default initial marking, default guards, and
|
12
|
+
# default clamps for use in simulations if none other are specified.
|
11
13
|
#
|
12
14
|
class YPetri::Simulation
|
15
|
+
include Collections
|
16
|
+
|
13
17
|
SAMPLING_DECIMAL_PLACES = 5
|
14
18
|
SIMULATION_METHODS =
|
15
19
|
[
|
@@ -24,626 +28,30 @@ class YPetri::Simulation
|
|
24
28
|
self.class.const_get :DEFAULT_SIMULATION_METHOD
|
25
29
|
end
|
26
30
|
|
27
|
-
|
28
|
-
|
31
|
+
attr_reader :method, :guarded, :timed, :net
|
32
|
+
alias guarded? guarded
|
33
|
+
alias timed? timed
|
34
|
+
attr_reader :marking_vector
|
35
|
+
attr_reader :zero_ᴍ, :zero_gradient
|
29
36
|
attr_reader :recording
|
30
37
|
alias :r :recording
|
31
38
|
|
32
|
-
#
|
33
|
-
#
|
34
|
-
attr_reader :zero_ᴍ
|
35
|
-
|
36
|
-
# Zero gradient.
|
37
|
-
#
|
38
|
-
attr_reader :zero_gradient
|
39
|
-
|
40
|
-
# Simulation settings.
|
41
|
-
#
|
42
|
-
def settings; {} end
|
43
|
-
alias :simulation_settings :settings
|
44
|
-
|
45
|
-
def recording_csv_string
|
46
|
-
CSV.generate do |csv|
|
47
|
-
@recording.keys.zip( @recording.values ).map{ |a, b| [ a ] + b.to_a }
|
48
|
-
.each{ |line| csv << line }
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
# Currently, simulation is largely immutable. Net, initial marking, clamps
|
53
|
-
# and simulation settings are set upon initialization, whereupon the instance
|
54
|
-
# forms their "mental image", which remains immune to any subsequent changes
|
55
|
-
# to the original objects. Required parameters are :net, :marking_clamps, and
|
56
|
-
# :initial_marking. Optional is :method (simulation method), and :guarded
|
57
|
-
# (true/false, whether the simulation guards the transition function results).
|
58
|
-
# Guard conditions can be either implicit (guarding against negative values
|
59
|
-
# and against type changes in by transition action), or explicitly associated
|
60
|
-
# with either places, or transition function results.
|
61
|
-
#
|
62
|
-
def initialize( method: default_simulation_method,
|
63
|
-
guarded: false,
|
64
|
-
net: raise( ArgumentError, "Net argument absent!" ),
|
65
|
-
marking_clamps: {},
|
66
|
-
initial_marking: {} )
|
67
|
-
puts "starting to set up Simulation" if YPetri::DEBUG
|
68
|
-
@method, @guarded, @net = method, guarded, net
|
69
|
-
@places, @transitions = @net.places.dup, @net.transitions.dup
|
70
|
-
self.singleton_class.class_exec {
|
71
|
-
define_method :Place do net.send :Place end
|
72
|
-
define_method :Transition do net.send :Transition end
|
73
|
-
define_method :Net do net.send :Net end
|
74
|
-
private :Place, :Transition, :Net
|
75
|
-
}; puts "setup of :net mental image complete" if YPetri::DEBUG
|
76
|
-
|
77
|
-
# A simulation distinguishes between free and clamped places. For free
|
78
|
-
# places, initial marking has to be specified. For clamped places, marking
|
79
|
-
# clamps have to be specified. Both come as hashes:
|
80
|
-
@marking_clamps = marking_clamps.with_keys { |k| place k }
|
81
|
-
@initial_marking = initial_marking.with_keys { |k| place k }
|
82
|
-
# Enforce that keys in the hashes must be unique:
|
83
|
-
@marking_clamps.keys.aT_equal @marking_clamps.keys.uniq
|
84
|
-
@initial_marking.keys.aT_equal @initial_marking.keys.uniq
|
85
|
-
puts "setup of clamps and initial marking done" if YPetri::DEBUG
|
86
|
-
|
87
|
-
# Each place must have either clamp, or initial marking:
|
88
|
-
places.each { |pl|
|
89
|
-
pl.aT "place #{pl}", "have either clamp or initial marking" do |pl|
|
90
|
-
( @marking_clamps.keys + @initial_marking.keys ).include? pl
|
91
|
-
end
|
92
|
-
}; puts "clamp || initial marking test passed" if YPetri::DEBUG
|
93
|
-
|
94
|
-
# @F2A * ᴍ (marking vector of free places) maps ᴍ to all places.
|
95
|
-
@F2A = Matrix.correspondence_matrix( free_places, places )
|
96
|
-
# @C2A * marking_vector_of_clamped_places maps it to all places.
|
97
|
-
@C2A = Matrix.correspondence_matrix( clamped_places, places )
|
98
|
-
puts "correspondence matrices set up" if YPetri::DEBUG
|
99
|
-
|
100
|
-
# Stoichiometry matrices:
|
101
|
-
@S_for_tS = S_for tS_transitions()
|
102
|
-
@S_for_SR = S_for SR_transitions()
|
103
|
-
@S_for_TSr = S_for TSr_transitions()
|
104
|
-
puts "stoichiometry matrices set up" if YPetri::DEBUG
|
105
|
-
|
106
|
-
# Other assets:
|
107
|
-
@Δ_closures_for_tsa = create_Δ_closures_for_tsa
|
108
|
-
@Δ_closures_for_Tsr = create_Δ_closures_for_Tsr
|
109
|
-
@action_closures_for_tS = create_action_closures_for_tS
|
110
|
-
@action_closures_for_TSr = create_action_closures_for_TSr
|
111
|
-
@rate_closures_for_sR = create_rate_closures_for_sR
|
112
|
-
@rate_closures_for_SR = create_rate_closures_for_SR
|
113
|
-
@assignment_closures_for_A = create_assignment_closures_for_A
|
114
|
-
@zero_ᴍ = compute_initial_marking_vector_of_free_places.map { |e| e * 0 }
|
115
|
-
@zero_gradient = @zero_ᴍ.dup
|
116
|
-
puts "other assets set up, about to reset" if YPetri::DEBUG
|
117
|
-
|
118
|
-
reset!; puts "reset complete" if YPetri::DEBUG
|
119
|
-
end
|
120
|
-
|
121
|
-
# Returns a new instance of the system simulation at a specified state, with
|
122
|
-
# same simulation settings. This state (:marking argument) can be specified
|
123
|
-
# either as marking vector for free or all places, marking array for free or
|
124
|
-
# all places, or marking hash. If vector or array is given, its size must
|
125
|
-
# correspond to the number of either free, or all places. If hash is given,
|
126
|
-
# it is not necessary to specify marking of every place – marking of those
|
127
|
-
# left out will be left same as in the current state.
|
128
|
-
#
|
129
|
-
def at( marking: marking, **oo )
|
130
|
-
err_msg = "Size of supplied marking must match either the number of " +
|
131
|
-
"free places, or the number of all places!"
|
132
|
-
update_method = case marking
|
133
|
-
when Hash then :update_marking_from_a_hash
|
134
|
-
when Matrix then
|
135
|
-
case marking.column_to_a.size
|
136
|
-
when places.size then :set_marking_vector
|
137
|
-
when free_places.size then :set_ᴍ
|
138
|
-
else raise TypeError, err_msg end
|
139
|
-
else # marking assumed to be an array
|
140
|
-
case marking.size
|
141
|
-
when places.size then :set_marking
|
142
|
-
when free_places.size then :set_m
|
143
|
-
else raise TypeError, err_msg end
|
144
|
-
end
|
145
|
-
return dup( **oo ).send( update_method, marking )
|
146
|
-
end
|
147
|
-
|
148
|
-
# Exposing @net.
|
149
|
-
#
|
150
|
-
attr_reader :net
|
151
|
-
|
152
|
-
# Is the simulation guarded?
|
153
|
-
#
|
154
|
-
def guarded?; @guarded end
|
155
|
-
|
156
|
-
# Without arguments or block, it returns simply a list of places. Otherwise,
|
157
|
-
# it returns a has whose keys are the places, and whose values are governed
|
158
|
-
# by the supplied parameters (either another collection, or message to #send
|
159
|
-
# to self to obtain a second collection).
|
39
|
+
# Stoichiometry matrix for *tS* transitions.
|
160
40
|
#
|
161
|
-
|
162
|
-
return @places.dup if aa.empty? && b.nil?
|
163
|
-
zip_to_hash places, *aa, &b
|
164
|
-
end
|
165
|
-
|
166
|
-
# Without arguments or block, it returns simply a list of transitions.
|
167
|
-
# Otherwise, it returns a has whose keys are the places, and whose values are
|
168
|
-
# governed by the supplied parameters (either another collection, or message
|
169
|
-
# to #send to self to obtain a second collection).
|
170
|
-
#
|
171
|
-
def transitions *aa, &b
|
172
|
-
return @transitions.dup if aa.empty? && b.nil?
|
173
|
-
zip_to_hash transitions, *aa, &b
|
174
|
-
end
|
175
|
-
|
176
|
-
# Without arguments or block, it returns simply a list of place names.
|
177
|
-
# Otherwise, it returns a hash whose keys are place names, and whose values
|
178
|
-
# are determined by the supplied argument(s) and/or block (either another
|
179
|
-
# collection, or a message to #send to self to obtain such collection). Unary
|
180
|
-
# block can be supplied to modify these values.
|
181
|
-
#
|
182
|
-
def pp *aa, &b
|
183
|
-
return places.map &:name if aa.empty? && b.nil?
|
184
|
-
zip_to_hash( places.map { |p| p.name || p }, *aa, &b )
|
185
|
-
end
|
186
|
-
|
187
|
-
# Without arguments or block, it returns simply a list of transition names.
|
188
|
-
# Otherwise, it returns a hash whose keys are transition names, and whose
|
189
|
-
# values are determined by the supplied argument(s) and/or block (either
|
190
|
-
# another collection, or a message to #send to self to obtain such collection).
|
191
|
-
# Unary block can be supplied to modify these values.
|
192
|
-
#
|
193
|
-
def tt *aa, &b
|
194
|
-
return transitions.map &:name if aa.empty? && b.nil?
|
195
|
-
zip_to_hash( transitions.map { |t| t.name || t }, *aa, &b )
|
196
|
-
end
|
197
|
-
|
198
|
-
# Without arguments or block, it returns simply a list of free places.
|
199
|
-
# Otherwise, it returns a hash, whose keys are the free places, and whose
|
200
|
-
# values are governed by the supplied parameters (either another collection,
|
201
|
-
# or message to #send to self to obtain a second collection).
|
202
|
-
#
|
203
|
-
def free_places *aa, &b
|
204
|
-
return zip_to_hash free_places, *aa, &b unless aa.empty? && b.nil?
|
205
|
-
kk = @initial_marking.keys
|
206
|
-
places.select { |p| kk.include? p }
|
207
|
-
end
|
41
|
+
attr_reader :S_tS
|
208
42
|
|
209
|
-
#
|
210
|
-
# instances whenever possible.
|
43
|
+
# Stoichiometry matrix for *TSr* transitions.
|
211
44
|
#
|
212
|
-
|
213
|
-
return free_places.map { |p| p.name || p } if aa.empty? && b.nil?
|
214
|
-
zip_to_hash free_pp, *aa, &b
|
215
|
-
end
|
216
|
-
|
217
|
-
# Initial marking definitions for free places (array).
|
218
|
-
#
|
219
|
-
def im
|
220
|
-
free_places.map { |p| @initial_marking[p] }
|
221
|
-
end
|
222
|
-
|
223
|
-
# Marking array of all places as it appears at the beginning of a simulation.
|
224
|
-
#
|
225
|
-
def initial_marking
|
226
|
-
raise # FIXME: "Initial marking" for all places (ie. incl. clamped ones).
|
227
|
-
end
|
228
|
-
|
229
|
-
# Initial marking of free places as a column vector.
|
230
|
-
#
|
231
|
-
def im_vector
|
232
|
-
Matrix.column_vector im
|
233
|
-
end
|
234
|
-
alias iᴍ im_vector
|
235
|
-
|
236
|
-
# Marking of all places at the beginning of a simulation, as a column vector.
|
237
|
-
#
|
238
|
-
def initial_marking_vector
|
239
|
-
Matrix.column_vector initial_marking
|
240
|
-
end
|
45
|
+
attr_reader :S_TSr
|
241
46
|
|
242
|
-
#
|
243
|
-
# Otherwise, it returns a hash, whose keys are the places, and whose values
|
244
|
-
# are governed by the supplied parameters (either another collection, or
|
245
|
-
# message to #send to self to obtain a second collection).
|
47
|
+
# Stoichiometry matrix for *SR* transitions.
|
246
48
|
#
|
247
|
-
|
248
|
-
return zip_to_hash clamped_places, *aa, &b unless aa.empty? && b.nil?
|
249
|
-
kk = @marking_clamps.keys
|
250
|
-
places.select { |p| kk.include? p }
|
251
|
-
end
|
252
|
-
|
253
|
-
# Behaves like #clamped_places, except that it uses place names instead of
|
254
|
-
# instances whenever possible.
|
255
|
-
#
|
256
|
-
def clamped_pp *aa, &b
|
257
|
-
return clamped_places.map { |p| p.name || p } if aa.empty? && b.nil?
|
258
|
-
zip_to_hash clamped_pp, *aa, &b
|
259
|
-
end
|
260
|
-
|
261
|
-
# Place clamp definitions for clamped places (array)
|
262
|
-
#
|
263
|
-
def marking_clamps
|
264
|
-
clamped_places.map { |p| @marking_clamps[p] }
|
265
|
-
end
|
266
|
-
alias place_clamps marking_clamps
|
267
|
-
|
268
|
-
# Marking array of free places.
|
269
|
-
#
|
270
|
-
def m
|
271
|
-
m_vector.column_to_a
|
272
|
-
end
|
273
|
-
|
274
|
-
# Marking hash of free places { name: marking }.
|
275
|
-
#
|
276
|
-
def pm
|
277
|
-
free_pp :m
|
278
|
-
end
|
279
|
-
alias p_m pm
|
280
|
-
|
281
|
-
# Marking hash of free places { place: marking }.
|
282
|
-
#
|
283
|
-
def place_m
|
284
|
-
free_places :m
|
285
|
-
end
|
286
|
-
|
287
|
-
# Marking array of all places.
|
288
|
-
#
|
289
|
-
def marking
|
290
|
-
marking_vector ? marking_vector.column_to_a : nil
|
291
|
-
end
|
292
|
-
|
293
|
-
# Marking hash of all places { name: marking }.
|
294
|
-
#
|
295
|
-
def pmarking
|
296
|
-
pp :marking
|
297
|
-
end
|
298
|
-
alias p_marking pmarking
|
299
|
-
|
300
|
-
# Marking hash of all places { place: marking }.
|
301
|
-
#
|
302
|
-
def place_marking
|
303
|
-
places :marking
|
304
|
-
end
|
305
|
-
|
306
|
-
# Marking of a specified place(s)
|
307
|
-
#
|
308
|
-
def marking_of place_or_collection_of_places
|
309
|
-
if place_or_collection_of_places.respond_to? :each then
|
310
|
-
place_or_collection_of_places.map { |pl| place_marking[ place( pl ) ] }
|
311
|
-
else
|
312
|
-
place_marking[ place( place_or_collection_of_places ) ]
|
313
|
-
end
|
314
|
-
end
|
315
|
-
alias m_of marking_of
|
316
|
-
|
317
|
-
# Marking of free places as a column vector.
|
318
|
-
#
|
319
|
-
def m_vector
|
320
|
-
F2A().t * @marking_vector
|
321
|
-
end
|
322
|
-
alias ᴍ m_vector
|
323
|
-
|
324
|
-
# Marking of clamped places as a column vector.
|
325
|
-
#
|
326
|
-
def marking_vector_of_clamped_places
|
327
|
-
C2A().t * @marking_vector
|
328
|
-
end
|
329
|
-
alias ᴍ_clamped marking_vector_of_clamped_places
|
330
|
-
|
331
|
-
# Marking of clamped places as an array.
|
332
|
-
#
|
333
|
-
def marking_of_clamped_places
|
334
|
-
ᴍ_clamped.column( 0 ).to_a
|
335
|
-
end
|
336
|
-
alias m_clamped marking_of_clamped_places
|
337
|
-
|
338
|
-
# Marking of all places as a column vector.
|
339
|
-
#
|
340
|
-
attr_reader :marking_vector
|
341
|
-
|
342
|
-
# Creation of stoichiometry matrix for an arbitrary array of stoichio.
|
343
|
-
# transitions, that maps (has the number of rows equal to) the free places.
|
344
|
-
#
|
345
|
-
def S_for( array_of_S_transitions )
|
346
|
-
array_of_S_transitions.map { |t| sparse_σ t }
|
347
|
-
.reduce( Matrix.empty( free_places.size, 0 ), :join_right )
|
348
|
-
end
|
349
|
-
|
350
|
-
# Creation of stoichiometry matrix for an arbitrary array of stoichio.
|
351
|
-
# transitions, that maps (has the number of rows equal to) all the places.
|
352
|
-
#
|
353
|
-
def stoichiometry_matrix_for( array_of_S_transitions )
|
354
|
-
array_of_S_transitions.map { |t| sparse_stoichiometry_vector t }
|
355
|
-
.reduce( Matrix.empty( places.size, 0 ), :join_right )
|
356
|
-
end
|
357
|
-
|
358
|
-
# 3. Stoichiometry matrix for timeless stoichiometric transitions.
|
359
|
-
#
|
360
|
-
attr_reader :S_for_tS
|
361
|
-
|
362
|
-
# 4. Stoichiometry matrix for timed rateless stoichiometric transitions.
|
363
|
-
#
|
364
|
-
attr_reader :S_for_TSr
|
365
|
-
|
366
|
-
# 6. Stoichiometry matrix for stoichiometric transitions with rate.
|
367
|
-
#
|
368
|
-
attr_reader :S_for_SR
|
369
|
-
|
370
|
-
# Stoichiometry matrix, with the distinction, that the caller asserts,
|
371
|
-
# that all transitions in this simulation are stoichiometric transitions
|
372
|
-
# with rate (or error).
|
373
|
-
#
|
374
|
-
def S
|
375
|
-
return S_for_SR() if s_transitions.empty? && r_transitions.empty?
|
376
|
-
raise "The simulation contains also non-stoichiometric transitions! " +
|
377
|
-
"Consider using #S_for_SR."
|
378
|
-
end
|
379
|
-
|
380
|
-
# ==== 1. Exposing ts transitions
|
381
|
-
|
382
|
-
# Without arguments or block, it returns simply a list of timeless
|
383
|
-
# nonstoichiometric transitions. Otherwise, it returns a hash, whose keys
|
384
|
-
# are the ts transitions, and values are governed by the supplied parameters
|
385
|
-
# (either another collection, or a message to #send to self to obtain the
|
386
|
-
# collection of values).
|
387
|
-
#
|
388
|
-
def ts_transitions *aa, &b
|
389
|
-
return zip_to_hash ts_transitions, *aa, &b unless aa.empty? && b.nil?
|
390
|
-
sift_from_net :ts_transitions
|
391
|
-
end
|
392
|
-
|
393
|
-
# Like #ts_transitions, except that transition names are used instead of
|
394
|
-
# instances, whenever possible.
|
395
|
-
#
|
396
|
-
def ts_tt *aa, &b
|
397
|
-
return zip_to_hash ts_tt, *aa, &b unless aa.empty? && b.nil?
|
398
|
-
ts_transitions.map { |t| t.name || t }
|
399
|
-
end
|
400
|
-
|
401
|
-
# Assignment transitions (A transition) can be regarded as a special kind
|
402
|
-
# of ts transition (subtracting away the current marking of their domain
|
403
|
-
# and replacing it with the result of their function). But it may often
|
404
|
-
# be useful to exclude A transitions from among the ts transitions, and
|
405
|
-
# such set is called tsa transitions (timeless nonstoichiometric
|
406
|
-
# nonassignment transitions).
|
407
|
-
#
|
408
|
-
def tsa_transitions *aa, &b
|
409
|
-
return zip_to_hash tsa_transitions, *aa, &b unless aa.empty? && b.nil?
|
410
|
-
sift_from_net :tsa_transitions
|
411
|
-
end
|
412
|
-
|
413
|
-
# Like #tsa_transitions, except that transition names are used instead of
|
414
|
-
# instance, whenever possible.
|
415
|
-
#
|
416
|
-
def tsa_tt *aa, &b
|
417
|
-
return zip_to_hash tsa_tt, *aa, &b unless aa.empty? && b.nil?
|
418
|
-
tsa_transitions.map { |t| t.name || t }
|
419
|
-
end
|
420
|
-
|
421
|
-
# ==== 2. Exposing tS transitions
|
422
|
-
|
423
|
-
# Without arguments or block, it returns simply a list of timeless
|
424
|
-
# stoichiometric transitions. Otherwise, it returns a hash, whose keys are
|
425
|
-
# the tS transitions, and values are governed by the supplied parameters
|
426
|
-
# (either another collection, or a message to #send to self to obtain the
|
427
|
-
# collection of values).
|
428
|
-
#
|
429
|
-
def tS_transitions *aa, &b
|
430
|
-
return zip_to_hash tS_transitions, *aa, &b unless aa.empty? && b.nil?
|
431
|
-
sift_from_net :tS_transitions
|
432
|
-
end
|
433
|
-
|
434
|
-
# Like #tS_transitions, except that transition names are used instead of
|
435
|
-
# instances, whenever possible.
|
436
|
-
#
|
437
|
-
def tS_tt *aa, &b
|
438
|
-
return zip_to_hash tS_tt, *aa, &b unless aa.empty? && b.nil?
|
439
|
-
tS_transitions.map { |t| t.name || t }
|
440
|
-
end
|
441
|
-
|
442
|
-
# ==== 3. Exposing Tsr transitions
|
443
|
-
|
444
|
-
# Without arguments or block, it returns simply a list of timed rateless
|
445
|
-
# nonstoichiometric transitions. Otherwise, it returns a hash, whose keys
|
446
|
-
# are the Tsr transitions, and whose values are governed by the supplied
|
447
|
-
# arguments (either an explicit collection of values, or a message to #send
|
448
|
-
# to self to obtain such collection).
|
449
|
-
#
|
450
|
-
def Tsr_transitions *aa, &b
|
451
|
-
return zip_to_hash Tsr_transitions(), *aa, &b unless aa.empty? && b.nil?
|
452
|
-
sift_from_net :Tsr_transitions
|
453
|
-
end
|
454
|
-
|
455
|
-
# Like #Tsr_transitions, except that transition names are used instead of
|
456
|
-
# instances, whenever possible.
|
457
|
-
#
|
458
|
-
def Tsr_tt *aa, &b
|
459
|
-
return zip_to_hash Tsr_tt(), *aa, &b unless aa.empty? && b.nil?
|
460
|
-
Tsr_transitions().map { |t| t.name || t }
|
461
|
-
end
|
462
|
-
|
463
|
-
# ==== 4. Exposing TSr transitions
|
464
|
-
|
465
|
-
# Without arguments or block, it returns simply a list of timed rateless
|
466
|
-
# stoichiometric transitions. Otherwise, it returns a hash, whose keys are
|
467
|
-
# are the TSr transitions, and whose values are governed by the supplied
|
468
|
-
# arguments (either an explicit collection of values, or a message to #send
|
469
|
-
# to self to obtain such collection).
|
470
|
-
#
|
471
|
-
def TSr_transitions *aa, &b
|
472
|
-
return zip_to_hash TSr_transitions(), *aa, &b unless aa.empty? && b.nil?
|
473
|
-
sift_from_net :TSr_transitions
|
474
|
-
end
|
475
|
-
|
476
|
-
# Like #TSr_transitions, except that transition names are used instead of
|
477
|
-
# instances, whenever possible.
|
478
|
-
#
|
479
|
-
def TSr_tt *aa, &b
|
480
|
-
return zip_to_hash TSr_tt(), *aa, &b unless aa.empty? && b.nil?
|
481
|
-
TSr_transitions().map { |t| t.name || t }
|
482
|
-
end
|
483
|
-
|
484
|
-
# ==== 5. Exposing sR transitions
|
485
|
-
|
486
|
-
# Without arguments or block, it returns simply a list of nonstoichiometric
|
487
|
-
# transitions with rate. Otherwise, it returns a hash, whose keys are
|
488
|
-
# are the sR transitions, and whose values are governed by the supplied
|
489
|
-
# arguments (either an explicit collection of values, or a message to #send
|
490
|
-
# to self to obtain such collection).
|
491
|
-
#
|
492
|
-
def sR_transitions *aa, &b
|
493
|
-
return zip_to_hash sR_transitions(), *aa, &b unless aa.empty? && b.nil?
|
494
|
-
sift_from_net :sR_transitions
|
495
|
-
end
|
496
|
-
|
497
|
-
# Like #sR_transitions, except that transition names are used instead of
|
498
|
-
# instances, whenever possible.
|
499
|
-
#
|
500
|
-
def sR_tt *aa, &b
|
501
|
-
return zip_to_hash sR_tt(), *aa, &b unless aa.empty? && b.nil?
|
502
|
-
sR_transitions.map { |t| t.name || t }
|
503
|
-
end
|
504
|
-
|
505
|
-
# ==== 6. Exposing SR transitions
|
506
|
-
|
507
|
-
# Without arguments or block, it returns simply a list of stoichiometric
|
508
|
-
# transitions with rate. Otherwise, it returns a hash, whose keys are
|
509
|
-
# are the SR transitions, and whose values are governed by the supplied
|
510
|
-
# arguments (either an explicit collection of values, or a message to #send
|
511
|
-
# to self to obtain such collection).
|
512
|
-
#
|
513
|
-
def SR_transitions *aa, &b
|
514
|
-
return zip_to_hash SR_transitions(), *aa, &b unless aa.empty? && b.nil?
|
515
|
-
sift_from_net :SR_transitions
|
516
|
-
end
|
517
|
-
|
518
|
-
# Like #SR_transitions, except that transition names are used instead of
|
519
|
-
# instances, whenever possible.
|
520
|
-
#
|
521
|
-
def SR_tt *aa, &b
|
522
|
-
return zip_to_hash SR_tt(), *aa, &b unless aa.empty? && b.nil?
|
523
|
-
SR_transitions().map { |t| t.name || t }
|
524
|
-
end
|
525
|
-
|
526
|
-
# ==== Assignment (A) transitions
|
527
|
-
|
528
|
-
# Without arguments or block, it returns simply a list of assignment
|
529
|
-
# transitions. Otherwise, it returns a hash, whose keys are the A
|
530
|
-
# transitions, and whose values are governed by the supplied arguments
|
531
|
-
# (either an explicit collection of values, or a message to #send
|
532
|
-
# to self to obtain such collection).
|
533
|
-
#
|
534
|
-
def A_transitions *aa, &b
|
535
|
-
return zip_to_hash A_transitions(), *aa, &b unless aa.empty? && b.nil?
|
536
|
-
sift_from_net :A_transitions
|
537
|
-
end
|
538
|
-
alias assignment_transitions A_transitions
|
539
|
-
|
540
|
-
# Like #A_transitions, except that transition names are used instead of
|
541
|
-
# instances, whenever possible.
|
542
|
-
#
|
543
|
-
def A_tt *aa, &b
|
544
|
-
return zip_to_hash A_tt(), *aa, &b unless aa.empty? && b.nil?
|
545
|
-
A_transitions().map { |t| t.name || t }
|
546
|
-
end
|
547
|
-
alias assignment_tt A_tt
|
548
|
-
|
549
|
-
# ==== Stoichiometric transitions of any kind (S transitions)
|
550
|
-
|
551
|
-
# Without arguments or block, it returns simply a list of stoichiometric
|
552
|
-
# transitions. Otherwise, it returns a hash, whose keys are the S
|
553
|
-
# transitions, and whose values are governed by the supplied arguments
|
554
|
-
# (either an explicit collection of values, or a message to #send to
|
555
|
-
# self to obtain such collection).
|
556
|
-
#
|
557
|
-
def S_transitions *aa, &b
|
558
|
-
return zip_to_hash S_transitions(), *aa, &b unless aa.empty? && b.nil?
|
559
|
-
sift_from_net :S_transitions
|
560
|
-
end
|
561
|
-
|
562
|
-
# Like #S_transitions, except that transition names are used instead of
|
563
|
-
# instances, whenever possible.
|
564
|
-
#
|
565
|
-
def S_tt *aa, &b
|
566
|
-
return zip_to_hash S_tt(), *aa, &b unless aa.empty? && b.nil?
|
567
|
-
S_transitions().map { |t| t.name || t }
|
568
|
-
end
|
569
|
-
|
570
|
-
# ==== Nonstoichiometric transitions of any kind (s transitions)
|
571
|
-
|
572
|
-
# Without arguments or block, it returns simply a list of
|
573
|
-
# nonstoichiometric transitions. Otherwise, it returns a hash, whose
|
574
|
-
# keys are the s transitions, and whose values are governed by the
|
575
|
-
# supplied arguments (either an explicit collection of values, or a
|
576
|
-
# message to #send to self to obtain such collection).
|
577
|
-
#
|
578
|
-
def s_transitions *aa, &b
|
579
|
-
return zip_to_hash s_transitions, *aa, &b unless aa.empty? && b.nil?
|
580
|
-
sift_from_net :s_transitions
|
581
|
-
end
|
582
|
-
|
583
|
-
# Like #s_transitions, except that transition names are used instead of
|
584
|
-
# instances, whenever possible.
|
585
|
-
#
|
586
|
-
def s_tt *aa, &b
|
587
|
-
return zip_to_hash s_tt, *aa, &b unless aa.empty? && b.nil?
|
588
|
-
s_transitions.map { |t| t.name || t }
|
589
|
-
end
|
590
|
-
|
591
|
-
# ==== Transitions with rate (R transitions), otherwise of any kind
|
592
|
-
|
593
|
-
# Without arguments or block, it returns simply a list of transitions
|
594
|
-
# with rate. Otherwise, it returns a hash, whose keys are the R
|
595
|
-
# transitions, and whose values are governed by the supplied arguments
|
596
|
-
# (either an explicit collection of values, or a message to #send to
|
597
|
-
# self to obtain such collection).
|
598
|
-
#
|
599
|
-
def R_transitions *aa, &b
|
600
|
-
return zip_to_hash R_transitions(), *aa, &b unless aa.empty? && b.nil?
|
601
|
-
sift_from_net :R_transitions
|
602
|
-
end
|
603
|
-
|
604
|
-
# Like #s_transitions, except that transition names are used instead of
|
605
|
-
# instances, whenever possible.
|
606
|
-
#
|
607
|
-
def R_tt *aa, &b
|
608
|
-
return zip_to_hash R_tt(), *aa, &b unless aa.empty? && b.nil?
|
609
|
-
R_transitions().map { |t| t.name || t }
|
610
|
-
end
|
611
|
-
|
612
|
-
# ==== Rateless transitions (r transitions), otherwise of any kind
|
613
|
-
|
614
|
-
# Without arguments or block, it returns simply a list of rateless
|
615
|
-
# transitions. Otherwise, it returns a hash, whose keys are the r
|
616
|
-
# transitions, and whose values are governed by the supplied arguments
|
617
|
-
# (either an explicit collection of values, or a message to #send to
|
618
|
-
# self to obtain such collection).
|
619
|
-
#
|
620
|
-
def r_transitions *aa, &b
|
621
|
-
return zip_to_hash r_transitions, *aa, &b unless aa.empty? && b.nil?
|
622
|
-
sift_from_net :r_transitions
|
623
|
-
end
|
624
|
-
|
625
|
-
# Like #r_transitions, except that transition names are used instead of
|
626
|
-
# instances, whenever possible.
|
627
|
-
#
|
628
|
-
def r_tt *aa, &b
|
629
|
-
return zip_to_hash r_tt, *aa, &b unless aa.empty? && b.nil?
|
630
|
-
r_transitions.map { |t| t.name || t }
|
631
|
-
end
|
632
|
-
|
633
|
-
# === Methods presenting other simulation assets
|
634
|
-
|
635
|
-
# ==== Regarding ts transitions
|
636
|
-
#
|
637
|
-
# (Their closures supply directly Δ codomain.)
|
49
|
+
attr_reader :S_SR
|
638
50
|
|
639
51
|
# Exposing Δ state closures for ts transitions.
|
640
52
|
#
|
641
53
|
attr_reader :Δ_closures_for_tsa
|
642
54
|
|
643
|
-
# Delta state contribution if timed nonstoichiometric non-assignment (tsa)
|
644
|
-
# transitions fire once. The closures are called in their order, but the state
|
645
|
-
# update is not performed between the calls (they fire simultaneously).
|
646
|
-
#
|
647
55
|
# Note: 'a' in 'tsa' is needed because A (assignment) transitions can also be
|
648
56
|
# regarded as a special kind of ts transitions, while they obviously do not
|
649
57
|
# act through Δ state, but rather directly enforce marking of their codomain.
|
@@ -652,29 +60,16 @@ class YPetri::Simulation
|
|
652
60
|
Δ_closures_for_tsa.map( &:call ).reduce( @zero_ᴍ, :+ )
|
653
61
|
end
|
654
62
|
|
655
|
-
# ==== Regarding Tsr transitions
|
656
|
-
#
|
657
|
-
# (Their closures do take Δt as argument, but do not expose their ∂,
|
658
|
-
# and they might not even have one.)
|
659
|
-
|
660
63
|
# Exposing Δ state closures for Tsr transitions.
|
661
64
|
#
|
662
65
|
attr_reader :Δ_closures_for_Tsr
|
663
66
|
|
664
67
|
# Delta state contribution for Tsr transitions given Δt.
|
665
68
|
#
|
666
|
-
def Δ
|
69
|
+
def Δ_Tsr( Δt )
|
667
70
|
Δ_closures_for_Tsr.map { |cl| cl.( Δt ) }.reduce( @zero_ᴍ, :+ )
|
668
71
|
end
|
669
|
-
|
670
|
-
# ==== Regarding tS transitions
|
671
|
-
#
|
672
|
-
# (These transitions are timeless, but stoichiometric. It means that their
|
673
|
-
# closures do not output Δ state contribution directly, but instead they
|
674
|
-
# output a single number, which is a transition action, and Δ state is then
|
675
|
-
# computed from it by multiplying the the action vector with the
|
676
|
-
# stoichiometry matrix.
|
677
|
-
|
72
|
+
|
678
73
|
# Exposing action closures for tS transitions.
|
679
74
|
#
|
680
75
|
attr_reader :action_closures_for_tS
|
@@ -686,7 +81,7 @@ class YPetri::Simulation
|
|
686
81
|
def action_vector_for_tS
|
687
82
|
Matrix.column_vector action_closures_for_tS.map( &:call )
|
688
83
|
end
|
689
|
-
alias
|
84
|
+
alias ᴀ_tS action_vector_for_tS
|
690
85
|
|
691
86
|
# Action vector if tS transitions fire once, like the previous method.
|
692
87
|
# But by calling this method, the caller asserts that all timeless
|
@@ -698,20 +93,14 @@ class YPetri::Simulation
|
|
698
93
|
"transitions! Consider using #action_vector_for_tS."
|
699
94
|
end
|
700
95
|
alias action_vector_for_t action_vector_for_timeless_transitions
|
701
|
-
alias
|
96
|
+
alias ᴀ_t action_vector_for_timeless_transitions
|
702
97
|
|
703
98
|
# Δ state contribution for tS transitions.
|
704
99
|
#
|
705
100
|
def Δ_if_tS_fire_once
|
706
|
-
|
101
|
+
S_tS() * action_vector_for_tS
|
707
102
|
end
|
708
103
|
|
709
|
-
# ==== Regarding TSr transitions
|
710
|
-
#
|
711
|
-
# (Same as Tsr, but stoichiometric. That is, their closures do not return
|
712
|
-
# Δ contribution, but transition's action, which is to be multiplied by
|
713
|
-
# the its stoichiometry to obtain Δ contribution.)
|
714
|
-
|
715
104
|
# Exposing action closures for TSr transitions.
|
716
105
|
#
|
717
106
|
attr_reader :action_closures_for_TSr
|
@@ -730,7 +119,7 @@ class YPetri::Simulation
|
|
730
119
|
def action_vector_for_TSr( Δt )
|
731
120
|
Matrix.column_vector action_closures_for_TSr.map { |c| c.( Δt ) }
|
732
121
|
end
|
733
|
-
alias
|
122
|
+
alias ᴀ_TSr action_vector_for_TSr
|
734
123
|
|
735
124
|
# Action vector for timed rateless stoichiometric transitions
|
736
125
|
# By calling this method, the caller asserts that all timeless transitions
|
@@ -741,20 +130,14 @@ class YPetri::Simulation
|
|
741
130
|
raise "The simulation also contains nonstoichiometric timed rateless " +
|
742
131
|
"transitions! Consider using #action_vector_for_TSr."
|
743
132
|
end
|
744
|
-
alias
|
133
|
+
alias ᴀ_Tr action_vector_for_Tr
|
745
134
|
|
746
|
-
#
|
135
|
+
# State contribution of TSr transitions for the period Δt.
|
747
136
|
#
|
748
|
-
def Δ
|
749
|
-
|
137
|
+
def Δ_TSr( Δt )
|
138
|
+
S_TSr() * action_vector_for_TSr( Δt )
|
750
139
|
end
|
751
140
|
|
752
|
-
# ==== Regarding sR transitions
|
753
|
-
#
|
754
|
-
# (Whether nonstoichiometric or stoichiometric, transitions with rate
|
755
|
-
# explicitly provide their contribution to the the state differential,
|
756
|
-
# rather than just contribution to the Δ state.)
|
757
|
-
|
758
141
|
# Exposing rate closures for sR transitions.
|
759
142
|
#
|
760
143
|
attr_reader :rate_closures_for_sR
|
@@ -781,20 +164,11 @@ class YPetri::Simulation
|
|
781
164
|
free_pp :gradient_for_sR
|
782
165
|
end
|
783
166
|
|
784
|
-
#
|
785
|
-
# as a conveniece, this method for multiplying the differential by provided
|
786
|
-
# Δt is added.
|
167
|
+
# First-order state contribution of sR transitions during Δt.
|
787
168
|
#
|
788
|
-
def Δ
|
169
|
+
def Δ_sR( Δt )
|
789
170
|
gradient_for_sR * Δt
|
790
171
|
end
|
791
|
-
alias Δ_euler_for_sR Δ_Euler_for_sR
|
792
|
-
|
793
|
-
# ==== Regarding SR_transitions
|
794
|
-
#
|
795
|
-
# (Whether nonstoichiometric or stoichiometric, transitions with rate
|
796
|
-
# explicitly provide their contribution to the the state differential,
|
797
|
-
# rather than just contribution to the Δ state.)
|
798
172
|
|
799
173
|
# Exposing rate closures for SR transitions.
|
800
174
|
#
|
@@ -891,7 +265,7 @@ class YPetri::Simulation
|
|
891
265
|
# State differential for SR transitions.
|
892
266
|
#
|
893
267
|
def gradient_for_SR
|
894
|
-
|
268
|
+
S_SR() * flux_vector_for_SR
|
895
269
|
end
|
896
270
|
|
897
271
|
# State differential for SR transitions as a hash { place_name: ∂ / ∂ᴛ }.
|
@@ -900,44 +274,30 @@ class YPetri::Simulation
|
|
900
274
|
free_pp :gradient_for_SR
|
901
275
|
end
|
902
276
|
|
903
|
-
#
|
904
|
-
# step, whose size is given by the Δt argument.
|
277
|
+
# First-order action vector for SR transitions for the time period Δt.
|
905
278
|
#
|
906
|
-
def
|
279
|
+
def first_order_action_vector_for_SR( Δt )
|
907
280
|
flux_vector_for_SR * Δt
|
908
281
|
end
|
909
|
-
alias
|
910
|
-
alias Euler_α_for_SR Euler_action_vector_for_SR
|
911
|
-
alias euler_α_for_SR Euler_action_vector_for_SR
|
282
|
+
alias ᴀ_SR first_order_action_vector_for_SR
|
912
283
|
|
913
|
-
#
|
284
|
+
# First-order action (as array) for SR for the time period Δt.
|
914
285
|
#
|
915
|
-
def
|
916
|
-
|
286
|
+
def first_order_action_for_SR( Δt )
|
287
|
+
first_order_action_vector_for_SR( Δt ).column( 0 ).to_a
|
917
288
|
end
|
918
|
-
alias euler_action_for_SR Euler_action_for_SR
|
919
289
|
|
920
|
-
#
|
921
|
-
# Euler step with Δt given as argument.
|
290
|
+
# First-order state contribution of SR transitions during Δt.
|
922
291
|
#
|
923
|
-
def Δ
|
292
|
+
def Δ_SR( Δt )
|
924
293
|
gradient_for_SR * Δt
|
925
294
|
end
|
926
|
-
alias Δ_euler_for_SR Δ_Euler_for_SR
|
927
295
|
|
928
|
-
#
|
929
|
-
# returned as an array.
|
296
|
+
# First-order state contribution for SR transitions during Δt (as array).
|
930
297
|
#
|
931
|
-
def Δ
|
298
|
+
def Δ_array_for_SR( Δt )
|
932
299
|
Δ_Euler_for_SR( Δt ).column( 0 ).to_a
|
933
300
|
end
|
934
|
-
alias Δ_euler_array_for_SR Δ_Euler_array_for_SR
|
935
|
-
|
936
|
-
|
937
|
-
# ==== Regarding A transitions
|
938
|
-
#
|
939
|
-
# (Assignment transitions directly replace the values in their codomain
|
940
|
-
# places with their results.)
|
941
301
|
|
942
302
|
# Exposing assignment closures for A transitions.
|
943
303
|
#
|
@@ -977,6 +337,150 @@ class YPetri::Simulation
|
|
977
337
|
# TODO: Assignment action to a clamped place should result in a warning.
|
978
338
|
end
|
979
339
|
|
340
|
+
# Correspondence matrix free places => all places.
|
341
|
+
#
|
342
|
+
attr_reader :F2A
|
343
|
+
|
344
|
+
# Correspondence matrix clamped places => all places.
|
345
|
+
#
|
346
|
+
attr_reader :C2A
|
347
|
+
|
348
|
+
# Simulation settings.
|
349
|
+
#
|
350
|
+
def settings; {} end
|
351
|
+
alias :simulation_settings :settings
|
352
|
+
|
353
|
+
def recording_csv_string
|
354
|
+
CSV.generate do |csv|
|
355
|
+
@recording.keys.zip( @recording.values ).map{ |a, b| [ a ] + b.to_a }
|
356
|
+
.each{ |line| csv << line }
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
# Currently, simulation is largely immutable. Net, initial marking, clamps
|
361
|
+
# and simulation settings are set upon initialization, whereupon the instance
|
362
|
+
# forms their "mental image", which remains immune to any subsequent changes
|
363
|
+
# to the original objects. Required parameters are :net, :marking_clamps, and
|
364
|
+
# :initial_marking. Optional is :method (simulation method), and :guarded
|
365
|
+
# (true/false, whether the simulation guards the transition function results).
|
366
|
+
# Guard conditions can be either implicit (guarding against negative values
|
367
|
+
# and against type changes in by transition action), or explicitly associated
|
368
|
+
# with either places, or transition function results.
|
369
|
+
#
|
370
|
+
# A simulation distinguishes between free and clamped places. For free
|
371
|
+
# places, initial marking has to be specified. For clamped places, marking
|
372
|
+
# clamps have to be specified. Both come as hashes:
|
373
|
+
#
|
374
|
+
# In addition to the arguments required by the regular simulation
|
375
|
+
# constructor, timed simulation constructor also expects :step_size
|
376
|
+
# (alias :step), :sampling_period (alias :sampling), and :target_time
|
377
|
+
# named arguments.
|
378
|
+
#
|
379
|
+
def initialize( method: default_simulation_method,
|
380
|
+
guarded: false,
|
381
|
+
marking_clamps: {},
|
382
|
+
initial_marking: {},
|
383
|
+
**nn )
|
384
|
+
puts "constructing a simulation" if YPetri::DEBUG
|
385
|
+
|
386
|
+
@method, @guarded = method, guarded
|
387
|
+
@net = nn.fetch( :net )
|
388
|
+
@places, @transitions = @net.places.dup, @net.transitions.dup
|
389
|
+
self.singleton_class.class_exec {
|
390
|
+
define_method :Place do net.send :Place end
|
391
|
+
define_method :Transition do net.send :Transition end
|
392
|
+
define_method :Net do net.send :Net end
|
393
|
+
private :Place, :Transition, :Net
|
394
|
+
}; puts "setup of :net mental image complete" if YPetri::DEBUG
|
395
|
+
|
396
|
+
@marking_clamps = marking_clamps.with_keys { |k| place k }
|
397
|
+
@initial_marking = initial_marking.with_keys { |k| place k }
|
398
|
+
# Enforce that keys in the hashes must be unique:
|
399
|
+
@marking_clamps.keys.aT_equal @marking_clamps.keys.uniq
|
400
|
+
@initial_marking.keys.aT_equal @initial_marking.keys.uniq
|
401
|
+
puts "setup of clamps and initial marking done" if YPetri::DEBUG
|
402
|
+
|
403
|
+
places.each { |pl| # each place must have either clamp, or initial marking
|
404
|
+
pl.aT "place #{pl}", "have either clamp or initial marking" do |pl|
|
405
|
+
( @marking_clamps.keys + @initial_marking.keys ).include? pl
|
406
|
+
end
|
407
|
+
}; puts "clamp || initial marking test passed" if YPetri::DEBUG
|
408
|
+
|
409
|
+
# @F2A * ᴍ (marking vector of free places) maps ᴍ to all places.
|
410
|
+
@F2A = Matrix.correspondence_matrix( free_places, places )
|
411
|
+
# @C2A * marking_vector_of_clamped_places maps it to all places.
|
412
|
+
@C2A = Matrix.correspondence_matrix( clamped_places, places )
|
413
|
+
puts "correspondence matrices set up" if YPetri::DEBUG
|
414
|
+
|
415
|
+
# Stoichiometry matrices:
|
416
|
+
@S_tS = S_for tS_transitions()
|
417
|
+
@S_SR = S_for SR_transitions()
|
418
|
+
@S_TSr = S_for TSr_transitions()
|
419
|
+
puts "stoichiometry matrices set up" if YPetri::DEBUG
|
420
|
+
|
421
|
+
# Other assets:
|
422
|
+
@Δ_closures_for_tsa = create_Δ_closures_for_tsa
|
423
|
+
@Δ_closures_for_Tsr = create_Δ_closures_for_Tsr
|
424
|
+
@action_closures_for_tS = create_action_closures_for_tS
|
425
|
+
@action_closures_for_TSr = create_action_closures_for_TSr
|
426
|
+
@rate_closures_for_sR = create_rate_closures_for_sR
|
427
|
+
@rate_closures_for_SR = create_rate_closures_for_SR
|
428
|
+
@assignment_closures_for_A = create_assignment_closures_for_A
|
429
|
+
@zero_ᴍ = compute_initial_marking_vector_of_free_places.map { |e| e * 0 }
|
430
|
+
@zero_gradient = @zero_ᴍ.dup
|
431
|
+
puts "other assets set up" if YPetri::DEBUG
|
432
|
+
|
433
|
+
@timed = if nn.has?( :time ) || nn.has?( :step ) || nn.has?( :sampling )
|
434
|
+
extend Timed
|
435
|
+
true
|
436
|
+
else false end
|
437
|
+
|
438
|
+
if timed? then # we have to set up all the expected variables
|
439
|
+
if nn[:time] then # time range given
|
440
|
+
time_range = nn[:time]
|
441
|
+
@initial_time, @target_time = time_range.begin, time_range.end
|
442
|
+
@step_size = nn[:step] || target_time / target_time.to_f
|
443
|
+
@sampling_period = nn[:sampling] || step_size
|
444
|
+
else
|
445
|
+
anything = nn[:step] || nn[:sampling]
|
446
|
+
@initial_time, @target_time = anything * 0, anything * Float::INFINITY
|
447
|
+
@step_size = nn[:step] || anything / anything.to_f
|
448
|
+
@sampling_period = nn[:sampling] || step_size
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
puts "timedness of the simulation decided" if YPetri::DEBUG
|
453
|
+
|
454
|
+
reset!
|
455
|
+
end
|
456
|
+
|
457
|
+
# Returns a new instance of the system simulation at a specified state, with
|
458
|
+
# same simulation settings. This state (:marking argument) can be specified
|
459
|
+
# either as marking vector for free or all places, marking array for free or
|
460
|
+
# all places, or marking hash. If vector or array is given, its size must
|
461
|
+
# correspond to the number of either free, or all places. If hash is given,
|
462
|
+
# it is not necessary to specify marking of every place – marking of those
|
463
|
+
# left out will be left same as in the current state.
|
464
|
+
#
|
465
|
+
def at( marking: marking, **nn )
|
466
|
+
err_msg = "Size of supplied marking must match either the number of " +
|
467
|
+
"free places, or the number of all places!"
|
468
|
+
update_method = case marking
|
469
|
+
when Hash then :update_marking_from_a_hash
|
470
|
+
when Matrix then
|
471
|
+
case marking.column_to_a.size
|
472
|
+
when places.size then :set_marking_vector
|
473
|
+
when free_places.size then :set_ᴍ
|
474
|
+
else fail TypeError, err_msg end
|
475
|
+
else # marking assumed to be an array
|
476
|
+
case marking.size
|
477
|
+
when places.size then :set_marking
|
478
|
+
when free_places.size then :set_m
|
479
|
+
else fail TypeError, err_msg end
|
480
|
+
end
|
481
|
+
return dup( **nn ).send( update_method, marking )
|
482
|
+
end
|
483
|
+
|
980
484
|
# ==== Sparse stoichiometry vectors for transitions
|
981
485
|
|
982
486
|
# For the transition specified by the argument, this method returns the
|
@@ -1001,14 +505,6 @@ class YPetri::Simulation
|
|
1001
505
|
Matrix.column_vector( instance.stoichiometry )
|
1002
506
|
end
|
1003
507
|
|
1004
|
-
# Correspondence matrix free places => all places.
|
1005
|
-
#
|
1006
|
-
attr_reader :F2A
|
1007
|
-
|
1008
|
-
# Correspondence matrix clamped places => all places.
|
1009
|
-
#
|
1010
|
-
attr_reader :C2A
|
1011
|
-
|
1012
508
|
# Produces the inspect string of the transition.
|
1013
509
|
#
|
1014
510
|
def inspect
|
@@ -1023,34 +519,6 @@ class YPetri::Simulation
|
|
1023
519
|
|
1024
520
|
private
|
1025
521
|
|
1026
|
-
# This helper method takes a collection, a variable number of other arguments
|
1027
|
-
# and an optional block, and returns a hash whose keys are the collection
|
1028
|
-
# members, and whose values are given by the supplied othe arguments and/or
|
1029
|
-
# block in the following way: If there is no additional argument, but a block
|
1030
|
-
# is supplied, this is applied to the collection. If there is exactly one
|
1031
|
-
# other argument, and it is also a collection, it is used as values.
|
1032
|
-
# Otherwise, these other arguments are treated as a message to be sent to
|
1033
|
-
# self (via #send), expecting it to return a collection to be used as hash
|
1034
|
-
# values. Optional block (which is always assumed to be unary) can be used
|
1035
|
-
# to additionally modify the second collection.
|
1036
|
-
#
|
1037
|
-
def zip_to_hash collection, *args, &block
|
1038
|
-
sz = args.size
|
1039
|
-
values = if sz == 0 then collection
|
1040
|
-
elsif sz == 1 && args[0].respond_to?( :each ) then args[0]
|
1041
|
-
else send *args end
|
1042
|
-
Hash[ collection.zip( block ? values.map( &block ) : values ) ]
|
1043
|
-
end
|
1044
|
-
|
1045
|
-
# Chicken approach towards ensuring that transitions in question come in
|
1046
|
-
# the same order as in @transitions local variable. Takes a symbol as the
|
1047
|
-
# argument (:SR, :TSr, :sr etc.)
|
1048
|
-
#
|
1049
|
-
def sift_from_net type_of_transitions
|
1050
|
-
from_net = net.send type_of_transitions
|
1051
|
-
@transitions.select { |t| from_net.include? t }
|
1052
|
-
end
|
1053
|
-
|
1054
522
|
# Resets the simulation
|
1055
523
|
#
|
1056
524
|
def reset!
|
@@ -1334,8 +802,8 @@ end
|
|
1334
802
|
|
1335
803
|
# Duplicate creation.
|
1336
804
|
#
|
1337
|
-
def dup( **
|
1338
|
-
self.class.new(
|
805
|
+
def dup( **nn )
|
806
|
+
self.class.new( nn.reverse_merge!( { method: @method,
|
1339
807
|
guarded: @guarded,
|
1340
808
|
net: @net,
|
1341
809
|
marking_clamps: @marking_clamps,
|