y_petri 2.0.7 → 2.0.14.p1
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/net/selections.rb +209 -0
- data/lib/y_petri/net/visualization.rb +67 -0
- data/lib/y_petri/net.rb +33 -311
- data/lib/y_petri/place/guard.rb +20 -13
- data/lib/y_petri/place.rb +12 -7
- data/lib/y_petri/simulation.rb +8 -9
- data/lib/y_petri/transition/assignment.rb +37 -0
- data/lib/y_petri/transition/{constructor_syntax.rb → construction.rb} +1 -0
- data/lib/y_petri/transition/ordinary_timeless.rb +46 -0
- data/lib/y_petri/transition/timed.rb +57 -0
- data/lib/y_petri/transition.rb +103 -220
- data/lib/y_petri/version.rb +1 -1
- data/lib/y_petri.rb +1 -1
- data/test/acceptance/basic_usage_test.rb +30 -0
- data/test/acceptance/simulation_test.rb +132 -0
- data/test/acceptance/simulation_with_physical_units_test.rb +153 -0
- data/test/acceptance/token_game_test.rb +36 -0
- data/test/acceptance/visualization_test.rb +25 -0
- data/test/acceptance_tests.rb +14 -0
- data/test/manipulator_test.rb +100 -0
- data/test/{simple_manual_examples.rb → manual_examples.rb} +0 -0
- data/test/net_test.rb +171 -0
- data/test/place_test.rb +7 -6
- data/test/simulation_test.rb +280 -0
- data/test/timed_simulation_test.rb +149 -0
- data/test/transition_test.rb +2 -4
- data/test/workspace_test.rb +72 -0
- data/test/y_petri_test.rb +16 -1107
- metadata +34 -7
data/lib/y_petri/net.rb
CHANGED
@@ -1,31 +1,26 @@
|
|
1
1
|
#encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
#
|
3
|
+
require_relative 'dependency_injection'
|
4
|
+
require_relative 'net/visualization'
|
5
|
+
require_relative 'net/selections'
|
6
|
+
|
7
|
+
# Represents a _Petri net_: A collection of places and transitions. The
|
8
|
+
# connector arrows – called _arcs_ in classical Petri net terminology – can be
|
9
|
+
# considered a property of transitions. Therefore in +YPetri+, term 'arcs' is
|
10
|
+
# mostly used as a synonym denoting neighboring places / transitions.
|
8
11
|
#
|
9
12
|
class YPetri::Net
|
10
13
|
include NameMagic
|
14
|
+
include YPetri::DependencyInjection
|
11
15
|
|
12
|
-
def initialize *args; oo = args.extract_options!
|
13
|
-
@places, @transitions = [], [] # empty arrays
|
14
|
-
# LATER: let the places/transitions be specified upon init
|
15
|
-
end
|
16
|
-
|
17
16
|
attr_reader :places, :transitions
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
def initialize( places: [], transitions: [] )
|
19
|
+
@places, @transitions = places, transitions
|
20
|
+
end
|
22
21
|
|
23
|
-
#
|
24
|
-
#
|
25
|
-
def tt; transitions.map &:name end
|
26
|
-
|
27
|
-
# Includes a place in the net. Returns <em>true</em> if successful,
|
28
|
-
# <em>false</em> if the place is already included in the net.
|
22
|
+
# Includes a place in the net. Returns _true_ if successful, _false_ if the
|
23
|
+
# place is already included in the net.
|
29
24
|
#
|
30
25
|
def include_place! place
|
31
26
|
pl = place( place )
|
@@ -34,10 +29,9 @@ class YPetri::Net
|
|
34
29
|
return true
|
35
30
|
end
|
36
31
|
|
37
|
-
# Includes a transition in the net. Returns
|
38
|
-
#
|
39
|
-
#
|
40
|
-
# already in the net.
|
32
|
+
# Includes a transition in the net. Returns _true_ if successful, _false_ if
|
33
|
+
# the transition is already included in the net. The arcs of the transition
|
34
|
+
# being included may only connect to the places already in the net.
|
41
35
|
#
|
42
36
|
def include_transition! transition;
|
43
37
|
tr = transition( transition )
|
@@ -49,10 +43,9 @@ class YPetri::Net
|
|
49
43
|
return true
|
50
44
|
end
|
51
45
|
|
52
|
-
# Excludes a place from the net. Returns
|
53
|
-
#
|
54
|
-
#
|
55
|
-
# net connect to it.
|
46
|
+
# Excludes a place from the net. Returns _true_ if successful, _false_ if the
|
47
|
+
# place was not found in the net. A place may not be excluded from the net so
|
48
|
+
# long as any transitions in the net connect to it.
|
56
49
|
#
|
57
50
|
def exclude_place! place
|
58
51
|
pl = place( place )
|
@@ -62,8 +55,8 @@ class YPetri::Net
|
|
62
55
|
return false
|
63
56
|
end
|
64
57
|
|
65
|
-
# Excludes a transition from the net. Returns
|
66
|
-
#
|
58
|
+
# Excludes a transition from the net. Returns _true_ if successful, _false_ if
|
59
|
+
# the transition was not found in the net.
|
67
60
|
#
|
68
61
|
def exclude_transition! transition
|
69
62
|
tr = transition( transition )
|
@@ -71,9 +64,8 @@ class YPetri::Net
|
|
71
64
|
return false
|
72
65
|
end
|
73
66
|
|
74
|
-
# Includes an object (either place or transition) in the net. Acts by
|
75
|
-
#
|
76
|
-
# difference being, that errors from bad arguments are swallowed.
|
67
|
+
# Includes an object (either place or transition) in the net. Acts by calling
|
68
|
+
# +#include_place!+ or +#include_transition!+, as needed, swallowing errors.
|
77
69
|
#
|
78
70
|
def << place_or_transition
|
79
71
|
begin
|
@@ -82,8 +74,8 @@ class YPetri::Net
|
|
82
74
|
begin
|
83
75
|
include_transition! place_or_transition
|
84
76
|
rescue NameError
|
85
|
-
raise NameError,
|
86
|
-
|
77
|
+
raise NameError, "Unrecognized place/transition: #{place_or_transition}"
|
78
|
+
# TODO: Exceptional Ruby
|
87
79
|
end
|
88
80
|
end
|
89
81
|
return self
|
@@ -107,213 +99,17 @@ class YPetri::Net
|
|
107
99
|
return false
|
108
100
|
end
|
109
101
|
|
110
|
-
#
|
111
|
-
# Methods exposing transition collections acc. to their properties:
|
112
|
-
|
113
|
-
# Array of <em>ts</em> transitions in the net.
|
114
|
-
#
|
115
|
-
def timeless_nonstoichiometric_transitions
|
116
|
-
transitions.select { |t| t.timeless? && t.nonstoichiometric? }
|
117
|
-
end
|
118
|
-
alias ts_transitions timeless_nonstoichiometric_transitions
|
119
|
-
|
120
|
-
# Names of <em>ts</em> transitions in the net.
|
121
|
-
#
|
122
|
-
def timeless_nonstoichiometric_tt
|
123
|
-
timeless_nonstoichiometric_transitions.map &:name
|
124
|
-
end
|
125
|
-
alias ts_tt timeless_nonstoichiometric_tt
|
126
|
-
|
127
|
-
# Array of <em>tsa</em> transitions in the net.
|
128
|
-
#
|
129
|
-
def timeless_nonstoichiometric_nonassignment_transitions
|
130
|
-
transitions.select { |t|
|
131
|
-
t.timeless? && t.nonstoichiometric? && ! t.assignment_action?
|
132
|
-
}
|
133
|
-
end
|
134
|
-
alias tsa_transitions timeless_nonstoichiometric_nonassignment_transitions
|
135
|
-
|
136
|
-
# Names of <em>tsa</em> transitions in the net.
|
137
|
-
#
|
138
|
-
def timeless_nonstoichiometric_nonassignment_tt
|
139
|
-
timeless_nonstoichiometric_nonassignment_transitions.map &:name
|
140
|
-
end
|
141
|
-
alias tsa_tt timeless_nonstoichiometric_nonassignment_tt
|
142
|
-
|
143
|
-
# Array of <em>tS</em> transitions in the net.
|
144
|
-
#
|
145
|
-
def timeless_stoichiometric_transitions
|
146
|
-
transitions.select { |t| t.timeless? && t.stoichiometric? }
|
147
|
-
end
|
148
|
-
alias tS_transitions timeless_stoichiometric_transitions
|
149
|
-
|
150
|
-
# Names of <em>tS</em> transitions in the net.
|
151
|
-
#
|
152
|
-
def timeless_stoichiometric_tt
|
153
|
-
timeless_stoichiometric_transitions.map &:name
|
154
|
-
end
|
155
|
-
alias tS_tt timeless_stoichiometric_tt
|
156
|
-
|
157
|
-
# Array of <em>Tsr</em> transitions in the net.
|
158
|
-
#
|
159
|
-
def timed_nonstoichiometric_transitions_without_rate
|
160
|
-
transitions.select { |t| t.timed? && t.nonstoichiometric? && t.rateless? }
|
161
|
-
end
|
162
|
-
alias timed_rateless_nonstoichiometric_transitions \
|
163
|
-
timed_nonstoichiometric_transitions_without_rate
|
164
|
-
alias Tsr_transitions timed_nonstoichiometric_transitions_without_rate
|
165
|
-
|
166
|
-
# Names of <em>Tsr</em> transitions in the net.
|
167
|
-
#
|
168
|
-
def timed_nonstoichiometric_tt_without_rate
|
169
|
-
timed_nonstoichiometric_transitions_without_rate.map &:name
|
170
|
-
end
|
171
|
-
alias timed_rateless_nonstoichiometric_tt \
|
172
|
-
timed_nonstoichiometric_tt_without_rate
|
173
|
-
alias Tsr_tt timed_nonstoichiometric_tt_without_rate
|
174
|
-
|
175
|
-
# Array of <em>TSr</em> transitions in the net.
|
176
|
-
#
|
177
|
-
def timed_stoichiometric_transitions_without_rate
|
178
|
-
transitions.select { |t| t.timed? && t.stoichiometric? && t.rateless? }
|
179
|
-
end
|
180
|
-
alias timed_rateless_stoichiometric_transitions \
|
181
|
-
timed_stoichiometric_transitions_without_rate
|
182
|
-
alias TSr_transitions timed_stoichiometric_transitions_without_rate
|
183
|
-
|
184
|
-
# Names of <em>TSr</em> transitions in the net.
|
185
|
-
#
|
186
|
-
def timed_stoichiometric_tt_without_rate
|
187
|
-
timed_stoichiometric_transitions_without_rate.map &:name
|
188
|
-
end
|
189
|
-
alias timed_rateless_stoichiometric_tt timed_stoichiometric_tt_without_rate
|
190
|
-
alias Tsr_tt timed_stoichiometric_tt_without_rate
|
191
|
-
|
192
|
-
# Array of <em>sR</em> transitions in the net.
|
193
|
-
#
|
194
|
-
def nonstoichiometric_transitions_with_rate
|
195
|
-
transitions.select { |t| t.has_rate? && t.nonstoichiometric? }
|
196
|
-
end
|
197
|
-
alias sR_transitions nonstoichiometric_transitions_with_rate
|
198
|
-
|
199
|
-
# Names of <em>sR</em> transitions in the net.
|
200
|
-
#
|
201
|
-
def nonstoichiometric_tt_with_rate
|
202
|
-
nonstoichiometric_transitions_with_rate.map &:name
|
203
|
-
end
|
204
|
-
alias sR_tt nonstoichiometric_tt_with_rate
|
205
|
-
|
206
|
-
# Array of <em>SR</em> transitions in the net.
|
207
|
-
#
|
208
|
-
def stoichiometric_transitions_with_rate
|
209
|
-
transitions.select { |t| t.has_rate? and t.stoichiometric? }
|
210
|
-
end
|
211
|
-
alias SR_transitions stoichiometric_transitions_with_rate
|
212
|
-
|
213
|
-
# Names of <em>SR</em> transitions in the net.
|
214
|
-
#
|
215
|
-
def stoichiometric_tt_with_rate
|
216
|
-
stoichiometric_transitions_with_rate.map &:name
|
217
|
-
end
|
218
|
-
alias SR_tt stoichiometric_tt_with_rate
|
219
|
-
|
220
|
-
# Array of transitions with <em>explicit assignment action</em>
|
221
|
-
# (<em>A</em> transitions) in the net.
|
102
|
+
# Is the net _functional_?
|
222
103
|
#
|
223
|
-
def
|
224
|
-
transitions.
|
104
|
+
def functional?
|
105
|
+
transitions.all? { |t| t.functional? }
|
225
106
|
end
|
226
|
-
alias A_transitions assignment_transitions
|
227
|
-
|
228
|
-
# Names of transitions with <em>explicit assignment action</em>
|
229
|
-
# (<em>A</em> transitions) in the net.
|
230
|
-
#
|
231
|
-
def assignment_tt
|
232
|
-
assignment_transitions.map &:name
|
233
|
-
end
|
234
|
-
alias A_tt assignment_tt
|
235
|
-
|
236
|
-
# Array of <em>stoichiometric</em> transitions in the net.
|
237
|
-
#
|
238
|
-
def stoichiometric_transitions
|
239
|
-
transitions.select &:stoichiometric?
|
240
|
-
end
|
241
|
-
alias S_transitions stoichiometric_transitions
|
242
|
-
|
243
|
-
# Names of <em>stoichiometric</em> transitions in the net.
|
244
|
-
#
|
245
|
-
def stoichiometric_tt
|
246
|
-
stoichiometric_transitions.map &:name
|
247
|
-
end
|
248
|
-
alias S_tt stoichiometric_tt
|
249
|
-
|
250
|
-
# Array of <em>nonstoichiometric</em> transitions in the net.
|
251
|
-
#
|
252
|
-
def nonstoichiometric_transitions
|
253
|
-
transitions.select &:nonstoichiometric?
|
254
|
-
end
|
255
|
-
alias s_transitions nonstoichiometric_transitions
|
256
|
-
|
257
|
-
# Names of <em>nonstoichimetric</em> transitions in the net.
|
258
|
-
#
|
259
|
-
def nonstoichiometric_tt
|
260
|
-
nonstoichiometric_transitions.map &:name
|
261
|
-
end
|
262
|
-
alias s_tt nonstoichiometric_tt
|
263
|
-
|
264
|
-
# Array of <em>timed</em> transitions in the net.
|
265
|
-
#
|
266
|
-
def timed_transitions; transitions.select &:timed? end
|
267
|
-
alias T_transitions timed_transitions
|
268
|
-
|
269
|
-
# Names of <em>timed</em> transitions in the net.
|
270
|
-
#
|
271
|
-
def timed_tt; timed_transitions.map &:name end
|
272
|
-
alias T_tt timed_tt
|
273
|
-
|
274
|
-
# Array of <em>timeless</em> transitions in the net.
|
275
|
-
#
|
276
|
-
def timeless_transitions; transitions.select &:timeless? end
|
277
|
-
alias t_transitions timeless_transitions
|
278
|
-
|
279
|
-
# Names of <em>timeless</em> transitions in the net.
|
280
|
-
#
|
281
|
-
def timeless_tt; timeless_transitions.map &:name end
|
282
|
-
alias t_tt timeless_tt
|
283
|
-
|
284
|
-
# Array of <em>transitions with rate</em> in the net.
|
285
|
-
#
|
286
|
-
def transitions_with_rate; transitions.select &:has_rate? end
|
287
|
-
alias R_transitions transitions_with_rate
|
288
|
-
|
289
|
-
# Names of <em>transitions with rate</em> in the net.
|
290
|
-
#
|
291
|
-
def tt_with_rate; transitions_with_rate.map &:name end
|
292
|
-
alias R_tt tt_with_rate
|
293
|
-
|
294
|
-
# Array of <em>rateless</em> transitions in the net.
|
295
|
-
#
|
296
|
-
def rateless_transitions; transitions.select &:rateless? end
|
297
|
-
alias transitions_without_rate rateless_transitions
|
298
|
-
alias r_transitions rateless_transitions
|
299
|
-
|
300
|
-
# Names of <em>rateless</em> transitions in the net.
|
301
|
-
#
|
302
|
-
def rateless_tt; rateless_transitions.map &:name end
|
303
|
-
alias tt_without_rate rateless_tt
|
304
|
-
alias r_tt rateless_tt
|
305
|
-
|
306
|
-
# ==== Inquirer methods about net qualities
|
307
|
-
|
308
|
-
# Is the net <em>functional</em>?
|
309
|
-
#
|
310
|
-
def functional?; transitions.all? { |t| t.functional? } end
|
311
107
|
|
312
108
|
# Is the net <em>timed</em>?
|
313
109
|
#
|
314
|
-
def timed
|
315
|
-
|
316
|
-
|
110
|
+
def timed?
|
111
|
+
transitions.all? { |t| t.timed? }
|
112
|
+
end
|
317
113
|
|
318
114
|
# Creates a new simulation from the net.
|
319
115
|
#
|
@@ -327,8 +123,6 @@ class YPetri::Net
|
|
327
123
|
YPetri::TimedSimulation.new **named_args.merge( net: self )
|
328
124
|
end
|
329
125
|
|
330
|
-
# ==== Sundry methods
|
331
|
-
|
332
126
|
# Networks are equal when their places and transitions are equal.
|
333
127
|
#
|
334
128
|
def == other
|
@@ -340,82 +134,10 @@ class YPetri::Net
|
|
340
134
|
#
|
341
135
|
def to_s
|
342
136
|
"#<Net: " + ( name.nil? ? "%s" : "name: #{name}, %s" ) %
|
343
|
-
"#{places.size} places, #{transitions.size} transitions" + "
|
344
|
-
end
|
345
|
-
|
346
|
-
def visualize
|
347
|
-
require 'graphviz'
|
348
|
-
γ = GraphViz.new :G
|
349
|
-
# Add places and transitions.
|
350
|
-
place_nodes = places.map.with_object Hash.new do |pl, ꜧ|
|
351
|
-
ꜧ[pl] = γ.add_nodes pl.name.to_s,
|
352
|
-
fillcolor: 'lightgrey',
|
353
|
-
color: 'grey',
|
354
|
-
style: 'filled'
|
355
|
-
end
|
356
|
-
transition_nodes = transitions.map.with_object Hash.new do |tr, ꜧ|
|
357
|
-
ꜧ[tr] = γ.add_nodes tr.name.to_s,
|
358
|
-
shape: 'box',
|
359
|
-
fillcolor: if tr.assignment? then 'yellow'
|
360
|
-
elsif tr.basic_type == :SR then 'lightcyan'
|
361
|
-
else 'ghostwhite' end,
|
362
|
-
color: if tr.assignment? then 'goldenrod'
|
363
|
-
elsif tr.basic_type == :SR then 'cyan'
|
364
|
-
else 'grey' end,
|
365
|
-
style: 'filled'
|
366
|
-
end
|
367
|
-
# Add Petri net arcs.
|
368
|
-
transition_nodes.each { |tr, tr_node|
|
369
|
-
if tr.assignment? then
|
370
|
-
tr.codomain.each { |pl|
|
371
|
-
γ.add_edges tr_node, place_nodes[pl], color: 'goldenrod'
|
372
|
-
}
|
373
|
-
( tr.domain - tr.codomain ).each { |pl|
|
374
|
-
γ.add_edges tr_node, place_nodes[pl], color: 'grey', arrowhead: 'none'
|
375
|
-
}
|
376
|
-
elsif tr.basic_type == :SR then
|
377
|
-
tr.codomain.each { |pl|
|
378
|
-
if tr.stoichio[pl] > 0 then # producing arc
|
379
|
-
γ.add_edges tr_node, place_nodes[pl], color: 'cyan'
|
380
|
-
elsif tr.stoichio[pl] < 0 then # consuming arc
|
381
|
-
γ.add_edges place_nodes[pl], tr_node, color: 'cyan'
|
382
|
-
else
|
383
|
-
γ.add_edges place_nodes[pl], tr_node, color: 'grey', arrowhead: 'none'
|
384
|
-
end
|
385
|
-
}
|
386
|
-
( tr.domain - tr.codomain ).each { |pl|
|
387
|
-
γ.add_edges tr_node, place_nodes[pl], color: 'grey', arrowhead: 'none'
|
388
|
-
}
|
389
|
-
end
|
390
|
-
}
|
391
|
-
# Generate output image.
|
392
|
-
γ.output png: File.expand_path( "~/y_petri_graph.png" )
|
393
|
-
# require 'y_support/kde'
|
394
|
-
YSupport::KDE.show_file_with_kioclient File.expand_path( "~/y_petri_graph.png" )
|
137
|
+
"#{places.size} places, #{transitions.size} transitions" + ">"
|
395
138
|
end
|
396
139
|
|
397
140
|
# Inspect string of the instance.
|
398
141
|
#
|
399
142
|
def inspect; to_s end
|
400
|
-
|
401
|
-
private
|
402
|
-
|
403
|
-
# Display a file with kioclient (KDE).
|
404
|
-
#
|
405
|
-
def show_file_with_kioclient( file_name )
|
406
|
-
system "sleep 0.2; kioclient exec 'file:%s'" %
|
407
|
-
File.expand_path( '.', file_name )
|
408
|
-
end
|
409
|
-
|
410
|
-
# Place, Transition, Net classes.
|
411
|
-
#
|
412
|
-
def Place; ::YPetri::Place end
|
413
|
-
def Transition; ::YPetri::Transition end
|
414
|
-
def Net; ::YPetri::Net end
|
415
|
-
|
416
|
-
# Instance identification methods.
|
417
|
-
#
|
418
|
-
def place( which ); Place().instance( which ) end
|
419
|
-
def transition( which ); Transition().instance( which ) end
|
420
|
-
def net( which ); Net().instance( which ) end
|
421
143
|
end # class YPetri::Net
|
data/lib/y_petri/place/guard.rb
CHANGED
@@ -6,7 +6,9 @@ class YPetri::Place
|
|
6
6
|
# Marking guard.
|
7
7
|
#
|
8
8
|
class Guard
|
9
|
-
ERRMSG = -> m,
|
9
|
+
ERRMSG = -> m, pl, assert do
|
10
|
+
"Marking #{m}:#{m.class}#{pl ? " of place #{pl}" : ''} #{assert}!"
|
11
|
+
end
|
10
12
|
|
11
13
|
attr_reader :assertion, :block
|
12
14
|
|
@@ -32,9 +34,9 @@ class YPetri::Place
|
|
32
34
|
# Validates a supplied marking value against the guard block. Raises
|
33
35
|
# +YPetri::GuardError+ if the guard fails, otherwise returns _true_.
|
34
36
|
#
|
35
|
-
def validate(
|
36
|
-
λ = __fail__(
|
37
|
-
λ.call if @Lab.new( λ ).instance_exec(
|
37
|
+
def validate( marking, place=nil )
|
38
|
+
λ = __fail__( marking, place, assertion )
|
39
|
+
λ.call if @Lab.new( λ ).instance_exec( marking, &block ) == false
|
38
40
|
return true
|
39
41
|
end
|
40
42
|
|
@@ -42,8 +44,8 @@ class YPetri::Place
|
|
42
44
|
|
43
45
|
# Constructs the fail closure.
|
44
46
|
#
|
45
|
-
def __fail__
|
46
|
-
-> { fail YPetri::GuardError, ERRMSG.(
|
47
|
+
def __fail__ marking, place, assertion
|
48
|
+
-> { fail YPetri::GuardError, ERRMSG.( marking, place, assertion ) }
|
47
49
|
end
|
48
50
|
end
|
49
51
|
|
@@ -92,7 +94,7 @@ class YPetri::Place
|
|
92
94
|
#
|
93
95
|
def federated_guard_closure
|
94
96
|
lineup = guards.dup
|
95
|
-
->
|
97
|
+
-> m { lineup.each { |g| g.validate( m, self ) }; return m }
|
96
98
|
end
|
97
99
|
|
98
100
|
# Applies guards on the marking currently owned by the place.
|
@@ -109,14 +111,19 @@ class YPetri::Place
|
|
109
111
|
# interchangeable, except complex numbers.
|
110
112
|
#
|
111
113
|
def add_default_guards!( reference_marking )
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
marking "should be a number" do |m| m.is_a? Numeric end
|
114
|
+
case reference_marking
|
115
|
+
when Complex then marking "should be Numeric" do |m| m.is_a? Numeric end
|
116
|
+
when Numeric then
|
117
|
+
marking "should be Numeric" do |m| m.is_a? Numeric end
|
117
118
|
marking "should not be complex" do |m| fail if m.is_a? Complex end
|
119
|
+
marking "should not be negative" do |m| m >= 0 end
|
120
|
+
when nil then # no guards
|
121
|
+
when true, false then marking "should be Boolean" do |m| m == !!m end
|
118
122
|
else
|
119
|
-
|
123
|
+
reference_marking.class.tap do |klass|
|
124
|
+
marking "should be a #{klass}" do |m| m.is_a? klass end
|
125
|
+
end
|
120
126
|
end
|
127
|
+
return nil
|
121
128
|
end
|
122
129
|
end # class YPetri::Place
|
data/lib/y_petri/place.rb
CHANGED
@@ -13,7 +13,6 @@ class YPetri::Place
|
|
13
13
|
attr_reader :quantum
|
14
14
|
attr_reader :guards
|
15
15
|
attr_accessor :default_marking
|
16
|
-
attr_writer :marking
|
17
16
|
|
18
17
|
# Named parameters supplied upon place initialization may include:
|
19
18
|
#
|
@@ -55,7 +54,7 @@ class YPetri::Place
|
|
55
54
|
&block
|
56
55
|
@upstream_arcs, @downstream_arcs, @guards = [], [], [] # init to empty
|
57
56
|
@quantum, @default_marking = quantum, default_marking
|
58
|
-
|
57
|
+
self.marking = marking || default_marking
|
59
58
|
|
60
59
|
# Check in :guard named argument and &block.
|
61
60
|
if guard.ℓ? then # guard NL assertion not given, use block or default guards
|
@@ -91,6 +90,12 @@ class YPetri::Place
|
|
91
90
|
guard args[0], &block
|
92
91
|
end
|
93
92
|
|
93
|
+
# Marking setter.
|
94
|
+
#
|
95
|
+
def marking=( new_marking )
|
96
|
+
@marking = guard.( new_marking )
|
97
|
+
end
|
98
|
+
|
94
99
|
# Alias for #marking=
|
95
100
|
#
|
96
101
|
def value=( marking ); self.marking = marking end
|
@@ -101,20 +106,20 @@ class YPetri::Place
|
|
101
106
|
|
102
107
|
# Adds tokens to the place.
|
103
108
|
#
|
104
|
-
def add(
|
105
|
-
@marking
|
109
|
+
def add( amount )
|
110
|
+
@marking = guard.( @marking + amount )
|
106
111
|
end
|
107
112
|
|
108
113
|
# Subtracts tokens from the place.
|
109
114
|
#
|
110
|
-
def subtract(
|
111
|
-
@marking
|
115
|
+
def subtract( amount )
|
116
|
+
@marking = guard.( @marking - amount )
|
112
117
|
end
|
113
118
|
|
114
119
|
# Resets place marking back to its default marking.
|
115
120
|
#
|
116
121
|
def reset_marking
|
117
|
-
@marking = @default_marking
|
122
|
+
@marking = guard.( @default_marking )
|
118
123
|
end
|
119
124
|
|
120
125
|
# Produces the inspect string of the place.
|
data/lib/y_petri/simulation.rb
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
#encoding: utf-8
|
2
2
|
|
3
|
-
#
|
4
|
-
# simulation
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# of the model and only that is used for actual simulation.
|
3
|
+
# The Petri net model is agnostic of simulation settings. Only for the purpose
|
4
|
+
# of simulation, model is combined together with specific simulation settings.
|
5
|
+
# Simulation settings consist of global settings (time step, sampling rate...)
|
6
|
+
# and object specific settings (clamps, constraints...). Again, clamps and
|
7
|
+
# constraints *do not belong* to the model. The Petri net model is also agnostic
|
8
|
+
# of the simulation methods. Simulation is not achieved by calling instance
|
9
|
+
# methods of the model. Instead, Simulation class makes a 'mental image' of the
|
10
|
+
# model, and uses that one in the actual simulation.
|
12
11
|
#
|
13
12
|
class YPetri::Simulation
|
14
13
|
SAMPLING_DECIMAL_PLACES = 5
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# Mixin for the transitions with assignment action.
|
4
|
+
#
|
5
|
+
module YPetri::Transition::Assignment
|
6
|
+
# Transition's action (before validation).
|
7
|
+
#
|
8
|
+
def action
|
9
|
+
action_closure.( *domain_marking )
|
10
|
+
end
|
11
|
+
|
12
|
+
# Applies action to the codomain, honoring cocking. Returns true if the transition
|
13
|
+
# fired, false if it wasn't cocked.
|
14
|
+
#
|
15
|
+
def fire
|
16
|
+
cocked?.tap { |x| ( uncock; fire! ) if x }
|
17
|
+
end
|
18
|
+
|
19
|
+
# Assigns the action closure result to the codomain, regardless of cocking.
|
20
|
+
#
|
21
|
+
def fire!
|
22
|
+
try "to call #fire! method" do
|
23
|
+
act = note "action", is: Array( action )
|
24
|
+
codomain.each_with_index do |codomain_place, i|
|
25
|
+
note "assigning action element no. #{i} to place #{codomain_place}"
|
26
|
+
codomain_place.marking = note "marking to assign", is: act.fetch( i )
|
27
|
+
end
|
28
|
+
end
|
29
|
+
return nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# A transitions are always _enabled_.
|
33
|
+
#
|
34
|
+
def enabled?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end # class YPetri::Transition::Assignment
|
@@ -60,6 +60,7 @@ class YPetri::Transition
|
|
60
60
|
#
|
61
61
|
def initialize *args
|
62
62
|
check_in_arguments *args # the big work of checking in args
|
63
|
+
extend timed? ? Timed : assignment? ? Assignment : OrdinaryTimeless
|
63
64
|
inform_upstream_places # that they have been connected
|
64
65
|
inform_downstream_places # that they have been connected
|
65
66
|
uncock # transitions initialize uncocked
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# Mixin for timed non-assignment timeless Petri net transitions.
|
4
|
+
#
|
5
|
+
module YPetri::Transition::OrdinaryTimeless
|
6
|
+
# Result of the transition's "function", regardless of the #enabled? status.
|
7
|
+
#
|
8
|
+
def action
|
9
|
+
if stoichiometric? then
|
10
|
+
rslt = action_closure.( *domain_marking )
|
11
|
+
stoichiometry.map { |coeff| rslt * coeff }
|
12
|
+
else
|
13
|
+
action_closure.( *domain_marking )
|
14
|
+
end
|
15
|
+
end # action
|
16
|
+
|
17
|
+
# Fires the transition, honoring cocking. Returns true if the transition
|
18
|
+
# fired, false if it wasn't cocked.
|
19
|
+
#
|
20
|
+
def fire
|
21
|
+
cocked?.tap { |x| ( uncock; fire! ) if x }
|
22
|
+
end
|
23
|
+
|
24
|
+
# Fires the transition regardless of cocking.
|
25
|
+
#
|
26
|
+
def fire!
|
27
|
+
try "to call #fire method" do
|
28
|
+
act = note "action", is: Array( action )
|
29
|
+
codomain.each_with_index do |codomain_place, i|
|
30
|
+
note "adding action element no. #{i} to place #{codomain_place}"
|
31
|
+
codomain_place.add( note "marking change", is: act.fetch( i ) )
|
32
|
+
end
|
33
|
+
end
|
34
|
+
return nil
|
35
|
+
end
|
36
|
+
|
37
|
+
# Timeless transition is _enabled_ if its action would result in a legal
|
38
|
+
# codomain marking.
|
39
|
+
#
|
40
|
+
def enabled?
|
41
|
+
codomain.zip( action ).all? do |place, change|
|
42
|
+
begin; place.guard.( place.marking + change )
|
43
|
+
rescue YPetri::GuardError; false end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end # class YPetri::Transition::OrdinaryTimeless
|