y_petri 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,458 @@
1
+ #encoding: utf-8
2
+
3
+ # Represents a <em>Petri net</em>: A collection of places and
4
+ # transitions. The connector arrows – called <em>arcs</em> in
5
+ # classical Petri net terminology – are considered a property
6
+ # of transitions. In <tt>YPetri</tt>, 'arcs' is a synonym for
7
+ # places / transitions connected to a given transition / place.
8
+ #
9
+ class YPetri::Net
10
+ include NameMagic
11
+
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
+ attr_reader :places, :transitions
18
+
19
+ # Names of places in the net.
20
+ #
21
+ def pp; places.map &:name end
22
+
23
+ # Names of transitions in the net.
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.
29
+ #
30
+ def include_place! place
31
+ p = place( place )
32
+ return false if @places.include? p
33
+ @places << p
34
+ return true
35
+ end
36
+
37
+ # Includes a transition in the net. Returns <em>true</em> if successful,
38
+ # <em>false</em> if the transition is already included in the net. The
39
+ # arcs of the transition being included may only connect to the places
40
+ # already in the net.
41
+ #
42
+ def include_transition! transition;
43
+ t = transition( transition )
44
+ return false if @transitions.include? t
45
+ raise TypeError, "Unable to include the transition #{t} in #{self}: " +
46
+ "It connects to one or more places outside the net." unless
47
+ t.arcs.all? { |p| include? p }
48
+ @transitions << t
49
+ return true
50
+ end
51
+
52
+ # Excludes a place from the net. Returns <em>true<em> if successful,
53
+ # <em>false</em> if the place was not found in the net. A place may
54
+ # not be excluded from the net so long as any transitions in the
55
+ # net connect to it.
56
+ #
57
+ def exclude_place! place
58
+ p = place( place )
59
+ raise "Unable to exclude #{p} from #{self}: One or more transitions" +
60
+ "depend on it" if transitions.any? { |t| t.arcs.include? p }
61
+ return true if @places.delete p
62
+ return false
63
+ end
64
+
65
+ # Excludes a transition from the net. Returns <em>true</em> if successful,
66
+ # <em>false</em> if the transition was not found in the net.
67
+ #
68
+ def exclude_transition! transition
69
+ t = transition( transition )
70
+ return true if @transitions.delete t
71
+ return false
72
+ end
73
+
74
+ # Includes an object (either place or transition) in the net. Acts by
75
+ # calling #include_place! or #include_transition!, as needed, the
76
+ # difference being, that errors from bad arguments are swallowed.
77
+ #
78
+ def << place_or_transition
79
+ begin
80
+ include_place! place_or_transition
81
+ rescue NameError
82
+ begin
83
+ include_transition! place_or_transition
84
+ rescue NameError
85
+ raise NameError,
86
+ "Unrecognized place or transition: #{place_or_transition}"
87
+ end
88
+ end
89
+ return self
90
+ end
91
+
92
+ # Inquirer whether the net includes a place / transition.
93
+ #
94
+ def include? place_or_transition
95
+ p = begin
96
+ place( place_or_transition )
97
+ rescue NameError
98
+ nil
99
+ end
100
+ return places.include? p if p
101
+ t = begin
102
+ transition( place_or_transition )
103
+ rescue NameError
104
+ nil
105
+ end
106
+ return transitions.include? t if t
107
+ return false
108
+ end
109
+
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? and 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>tS</em> transitions in the net.
128
+ #
129
+ def timeless_stoichiometric_transitions
130
+ transitions.select{ |t| t.timeless? and t.stoichiometric? }
131
+ end
132
+ alias tS_transitions timeless_stoichiometric_transitions
133
+
134
+ # Names of <em>tS</em> transitions in the net.
135
+ #
136
+ def timeless_stoichiometric_tt
137
+ timeless_stoichiometric_transitions.map &:name
138
+ end
139
+ alias tS_tt timeless_stoichiometric_tt
140
+
141
+ # Array of <em>Tsr</em> transitions in the net.
142
+ #
143
+ def timed_nonstoichiometric_transitions_without_rate
144
+ transitions.select{ |t| t.timed? and t.nonstoichiometric? and t.rateless? }
145
+ end
146
+ alias timed_rateless_nonstoichiometric_transitions \
147
+ timed_nonstoichiometric_transitions_without_rate
148
+ alias Tsr_transitions timed_nonstoichiometric_transitions_without_rate
149
+
150
+ # Names of <em>Tsr</em> transitions in the net.
151
+ #
152
+ def timed_nonstoichiometric_tt_without_rate
153
+ timed_nonstoichiometric_transitions_without_rate.map &:name
154
+ end
155
+ alias timed_rateless_nonstoichiometric_tt \
156
+ timed_nonstoichiometric_tt_without_rate
157
+ alias Tsr_tt timed_nonstoichiometric_tt_without_rate
158
+
159
+ # Array of <em>TSr</em> transitions in the net.
160
+ #
161
+ def timed_stoichiometric_transitions_without_rate
162
+ transitions.select { |t| t.timed? and t.stoichiometric? and t.rateless? }
163
+ end
164
+ alias timed_rateless_stoichiometric_transitions \
165
+ timed_stoichiometric_transitions_without_rate
166
+ alias TSr_transitions timed_stoichiometric_transitions_without_rate
167
+
168
+ # Names of <em>TSr</em> transitions in the net.
169
+ #
170
+ def timed_stoichiometric_tt_without_rate
171
+ timed_stoichiometric_transitions_without_rate.map &:name
172
+ end
173
+ alias timed_rateless_stoichiometric_tt timed_stoichiometric_tt_without_rate
174
+ alias Tsr_tt timed_stoichiometric_tt_without_rate
175
+
176
+ # Array of <em>sR</em> transitions in the net.
177
+ #
178
+ def nonstoichiometric_transitions_with_rate
179
+ transitions.select { |t| t.has_rate? and t.nonstoichiometric? }
180
+ end
181
+ alias sR_transitions nonstoichiometric_transitions_with_rate
182
+
183
+ # Names of <em>sR</em> transitions in the net.
184
+ #
185
+ def nonstoichiometric_tt_with_rate
186
+ nonstoichiometric_transitions_with_rate.map &:name
187
+ end
188
+ alias sR_tt nonstoichiometric_tt_with_rate
189
+
190
+ # Array of <em>SR</em> transitions in the net.
191
+ #
192
+ def stoichiometric_transitions_with_rate
193
+ transitions.select { |t| t.has_rate? and t.stoichiometric? }
194
+ end
195
+ alias SR_transitions stoichiometric_transitions_with_rate
196
+
197
+ # Names of <em>SR</em> transitions in the net.
198
+ #
199
+ def stoichiometric_tt_with_rate
200
+ stoichiometric_transitions_with_rate.map &:name
201
+ end
202
+ alias SR_tt stoichiometric_tt_with_rate
203
+
204
+ # Array of transitions with <em>explicit assignment action</em>
205
+ # (<em>A</em> transitions) in the net.
206
+ #
207
+ def transitions_with_explicit_assignment_action
208
+ transitions.select { |t| t.assignment_action? }
209
+ end
210
+ alias transitions_with_assignment_action \
211
+ transitions_with_explicit_assignment_action
212
+ alias assignment_transitions transitions_with_explicit_assignment_action
213
+ alias A_transitions transitions_with_explicit_assignment_action
214
+
215
+ # Names of transitions with <em>explicit assignment action</em>
216
+ # (<em>A</em> transitions) in the net.
217
+ #
218
+ def tt_with_explicit_assignment_action
219
+ transitions_with_explicit_assignment_action.map &:name
220
+ end
221
+ alias tt_with_assignment_action tt_with_explicit_assignment_action
222
+ alias assignment_tt tt_with_assignment_action
223
+ alias A_tt tt_with_assignment_action
224
+
225
+ # Array of <em>stoichiometric</em> transitions in the net.
226
+ #
227
+ def stoichiometric_transitions
228
+ transitions.select &:stoichiometric?
229
+ end
230
+ alias S_transitions stoichiometric_transitions
231
+
232
+ # Names of <em>stoichiometric</em> transitions in the net.
233
+ #
234
+ def stoichiometric_tt
235
+ stoichiometric_transitions.map &:name
236
+ end
237
+ alias S_tt stoichiometric_tt
238
+
239
+ # Array of <em>nonstoichiometric</em> transitions in the net.
240
+ #
241
+ def nonstoichiometric_transitions
242
+ transitions.select &:nonstoichiometric?
243
+ end
244
+ alias s_transitions nonstoichiometric_transitions
245
+
246
+ # Names of <em>nonstoichimetric</em> transitions in the net.
247
+ #
248
+ def nonstoichiometric_tt
249
+ nonstoichiometric_transitions.map &:name
250
+ end
251
+ alias s_tt nonstoichiometric_tt
252
+
253
+ # Array of <em>timed</em> transitions in the net.
254
+ #
255
+ def timed_transitions; transitions.select &:timed? end
256
+ alias T_transitions timed_transitions
257
+
258
+ # Names of <em>timed</em> transitions in the net.
259
+ #
260
+ def timed_tt; timed_transitions.map &:name end
261
+ alias T_tt timed_tt
262
+
263
+ # Array of <em>timeless</em> transitions in the net.
264
+ #
265
+ def timeless_transitions; transitions.select &:timeless? end
266
+ alias t_transitions timeless_transitions
267
+
268
+ # Names of <em>timeless</em> transitions in the net.
269
+ #
270
+ def timeless_tt; timeless_transitions.map &:name end
271
+ alias t_tt timeless_tt
272
+
273
+ # Array of <em>transitions with rate</em> in the net.
274
+ #
275
+ def transitions_with_rate; transitions.select &:has_rate? end
276
+ alias R_transitions transitions_with_rate
277
+
278
+ # Names of <em>transitions with rate</em> in the net.
279
+ #
280
+ def tt_with_rate; transitions_with_rate.map &:name end
281
+ alias R_tt tt_with_rate
282
+
283
+ # Array of <em>rateless</em> transitions in the net.
284
+ #
285
+ def rateless_transitions; transitions.select &:rateless? end
286
+ alias transitions_without_rate rateless_transitions
287
+ alias r_transitions rateless_transitions
288
+
289
+ # Names of <em>rateless</em> transitions in the net.
290
+ #
291
+ def rateless_tt; rateless_transitions.map &:name end
292
+ alias tt_without_rate rateless_tt
293
+ alias r_tt rateless_tt
294
+
295
+ # ==== Inquirer methods about net qualities
296
+
297
+ # Is the net <em>functional</em>?
298
+ #
299
+ def functional?; transitions.all? { |t| t.functional? } end
300
+
301
+ # Is the net <em>timed</em>?
302
+ #
303
+ def timed?; transitions.all? { |t| t.timed? } end
304
+
305
+ # ==== Simulation constructors
306
+
307
+ # Creates a new simulation from the net.
308
+ #
309
+ def new_simulation *args
310
+ oo = args.extract_options!
311
+ YPetri::Simulation.new *args, oo.merge( net: self )
312
+ end
313
+
314
+ # Creates a new timed simulation from the net.
315
+ #
316
+ def new_timed_simulation *args
317
+ oo = args.extract_options!
318
+ YPetri::TimedSimulation.new oo.merge( net: self )
319
+ end
320
+
321
+ # ==== Sundry methods
322
+
323
+ # Networks are equal when their places and transitions are equal.
324
+ #
325
+ def == other
326
+ return false unless other.class_complies?( ç )
327
+ places == other.places && transitions == other.transitions
328
+ end
329
+
330
+ # Returns a string briefly describing the net.
331
+ #
332
+ def to_s
333
+ "#<Net: " + ( name.nil? ? "%s" : "name: #{name}, %s" ) %
334
+ "#{places.size} places, #{transitions.size} transitions" + " >"
335
+ end
336
+
337
+ def visualize
338
+ require 'graphviz'
339
+ γ = GraphViz.new :G # creating a new graph
340
+
341
+ # main = γ.add_nodes( "main", shape: "box" )
342
+ # parse = γ.add_nodes( "parse", fillcolor: "yellow", style: "rounded,filled", shape: "diamond" )
343
+ # execute = γ.add_nodes( "execute", shape: "record", label: "{ a | b | c }", style: "rounded" )
344
+ # init = γ.add_nodes( "init", fillcolor: "yellow", style: "filled" )
345
+
346
+ # # set global node options
347
+ # g.node[:color] = "#ddaa66"
348
+ # g.node[:style] = "filled"
349
+ # g.node[:shape] = "box"
350
+ # g.node[:penwidth] = "1"
351
+ # g.node[:fontname] = "Trebuchet MS"
352
+ # g.node[:fontsize] = "8"
353
+ # g.node[:fillcolor] = "#ffeecc"
354
+ # g.node[:fontcolor] = "#775500"
355
+ # g.node[:margin] = "0.0"
356
+
357
+ # # set global edge options
358
+ # g.edge[:color] = "#999999"
359
+ # g.edge[:weight] = "1"
360
+ # g.edge[:fontsize] = "6"
361
+ # g.edge[:fontcolor] = "#444444"
362
+ # g.edge[:fontname] = "Verdana"
363
+ # g.edge[:dir] = "forward"
364
+ # g.edge[:arrowsize] = "0.5"
365
+
366
+ # add place nodes
367
+ place_nodes =
368
+ Hash[ places.zip places.map { |p|
369
+ γ.add_nodes p.name.to_s, fillcolor: 'lightgrey', color: 'grey', style: 'filled'
370
+ } ]
371
+
372
+ # add transition nodes
373
+ transition_nodes =
374
+ Hash[ transitions.zip transitions.map { |t|
375
+ γ.add_nodes( t.name.to_s,
376
+ shape: 'box',
377
+ fillcolor: if t.assignment? then 'yellow'
378
+ elsif t.basic_type == :SR then 'lightcyan'
379
+ else 'ghostwhite' end,
380
+ color: if t.assignment? then 'goldenrod'
381
+ elsif t.basic_type == :SR then 'cyan'
382
+ else 'grey' end,
383
+ style: 'filled'
384
+ )
385
+
386
+ } ]
387
+
388
+ # add edges
389
+ transition_nodes.each { |t, t_node|
390
+ if t.assignment? then
391
+ t.codomain.each { |p|
392
+ γ.add_edges t_node, place_nodes[p], color: 'goldenrod'
393
+ }
394
+ ( t.domain - t.codomain ).each { |p|
395
+ γ.add_edges t_node, place_nodes[p], color: 'grey', arrowhead: 'none'
396
+ }
397
+ elsif t.basic_type == :SR then
398
+ t.codomain.each { |p|
399
+ if t.stoichio[p] > 0 then # producing arc
400
+ γ.add_edges t_node, place_nodes[p], color: 'cyan'
401
+ elsif t.stoichio[p] < 0 then # consuming arc
402
+ γ.add_edges place_nodes[p], t_node, color: 'cyan'
403
+ else
404
+ γ.add_edges place_nodes[p], t_node, color: 'grey', arrowhead: 'none'
405
+ end
406
+ }
407
+ ( t.domain - t.codomain ).each { |p|
408
+ γ.add_edges t_node, place_nodes[p], color: 'grey', arrowhead: 'none'
409
+ }
410
+ end
411
+ }
412
+
413
+ # place_collection.each { |place_name, place_label|
414
+ # place_instance = place( place_name )
415
+ # place_instance.upstream_places.each { |upstream_place|
416
+ # node = nodes[ place_name ]
417
+ # next unless set_of_places.map { |ɴ, _| ɴ }.include?( upstream_place.name )
418
+ # next if upstream_place == place_instance
419
+ # upstream_node = nodes[ upstream_place.name ]
420
+ # node << upstream_node
421
+ # }
422
+ # }
423
+
424
+ # Generate output image
425
+ γ.output png: "y_petri_graph.png"
426
+ show_file_with_kioclient "y_petri_graph.png"
427
+ end
428
+
429
+ # display it with kioclient
430
+ def show_file_with_kioclient( fɴ )
431
+ system "sleep 0.2; kioclient exec 'file:%s'" % File.expand_path( '.', fɴ )
432
+ end
433
+
434
+ # Inspect string of the instance.
435
+ #
436
+ def inspect; to_s end
437
+
438
+ private
439
+
440
+ # Display a file with kioclient (KDE).
441
+ #
442
+ def show_file_with_kioclient( file_name )
443
+ system "sleep 0.2; kioclient exec 'file:%s'" %
444
+ File.expand_path( '.', file_name )
445
+ end
446
+
447
+ # Place, Transition, Net classes.
448
+ #
449
+ def Place; ::YPetri::Place end
450
+ def Transition; ::YPetri::Transition end
451
+ def Net; ::YPetri::Net end
452
+
453
+ # Instance identification methods.
454
+ #
455
+ def place( which ); Place().instance( which ) end
456
+ def transition( which ); Transition().instance( which ) end
457
+ def net( which ); Net().instance( which ) end
458
+ end # class YPetri::Net
@@ -0,0 +1,189 @@
1
+ #encoding: utf-8
2
+
3
+ # This class represents Petri net places.
4
+ #
5
+ class YPetri::Place
6
+ USE_QUANTUM = false
7
+ include NameMagic
8
+
9
+ attr_reader :quantum
10
+ attr_accessor :default_marking
11
+ attr_accessor :marking # instance-attached marking
12
+ alias :value :marking
13
+ alias :m :marking
14
+
15
+ # Alias for #marking=
16
+ #
17
+ def value=( marking ); self.marking = marking end
18
+
19
+ # Alias for #marking=
20
+ #
21
+ def m=( marking ); self.marking = marking end
22
+
23
+ # Transitions that can directly add/remove tokens from this place.
24
+ # It is aliased as #upstream_transitions and #ϝ. (ϝ (Greek digamma) looks
25
+ # like "function", which we know from spreadsheet software: A collection
26
+ # of transitions directly affecting marking of this place.)
27
+ #
28
+ attr_reader :upstream_arcs
29
+ alias :upstream_transitions :upstream_arcs
30
+ alias :ϝ :upstream_arcs
31
+
32
+ # Transitions whose action directly depends on this place. Aliased
33
+ # as #downstream_transitions.
34
+ #
35
+ attr_reader :downstream_arcs
36
+ alias :downstream_transitions :downstream_arcs
37
+
38
+ # Named parameters supplied upon place initialization may include:
39
+ #
40
+ # * :marking (alias :m)
41
+ # * :default_marking (alias :dflt_m or :m!)
42
+ # * :quantum (alias :q)
43
+ #
44
+ # While 'marking' is a standard Petri net concept, and default marking
45
+ # is self-explanatory, place quantum is the concept by which YPetri
46
+ # reconciles with letter H in the abbreviation HFPN. YPetri places are
47
+ # always considered discrete, and it is true insomuch, as their marking
48
+ # is represented by a finite-digit number. The place quantum feature is
49
+ # not supported yet, but in future, it should enable smooth transition
50
+ # between continuous and stochastic modes of simulation.
51
+ #
52
+ def initialize *aa; oo = aa.extract_options!
53
+ # set domain and codomain of the place empty
54
+ @upstream_arcs = []
55
+ @downstream_arcs = []
56
+ @quantum = oo.may_have( :quantum, syn!: :q ) || 1
57
+ @default_marking = oo.may_have( :default_marking, syn!: [ :dflt_m, :m! ] )
58
+ @marking = oo.may_have( :marking, syn!: :m ) || @default_marking
59
+ end
60
+
61
+ # Returns an array of all the transitions connected to the place.
62
+ #
63
+ def arcs
64
+ upstream_arcs | downstream_arcs
65
+ end
66
+ alias :connectivity :arcs
67
+
68
+ # Returns the union of domains of the transitions associated
69
+ # with the upstream arcs of this place.
70
+ #
71
+ def precedents
72
+ upstream_transitions
73
+ .map( &:upstream_places )
74
+ .reduce( [], :| )
75
+ end
76
+ alias :upstream_places :precedents
77
+
78
+ # Returns the union of codomains of the transitions associated
79
+ # with the downstream arcs originating from this place.
80
+ #
81
+ def dependents
82
+ downstream_transitions
83
+ .map( &:downstream_places )
84
+ .reduce( [], :| )
85
+ end
86
+ alias :downstream_places :dependents
87
+
88
+ # Adds tokens to the place.
89
+ #
90
+ def add( amount_of_tokens )
91
+ @marking += amount_of_tokens
92
+ end
93
+
94
+ # Subtracts tokens from the place.
95
+ #
96
+ def subtract( amount_of_tokens)
97
+ @marking -= amount_of_tokens
98
+ end
99
+
100
+ # Resets place marking back to its default marking.
101
+ #
102
+ def reset_marking
103
+ @marking = @default_marking
104
+ end
105
+
106
+ # Firing of upstream transitions regardless of cocking. (To #fire
107
+ # transitions, they have to be cocked with #cock method; the firing
108
+ # methods with exclamation marks disregard cocking.)
109
+ #
110
+ def fire_upstream!
111
+ @upstream_arcs.each &:fire!
112
+ end
113
+ alias :fire! :fire_upstream!
114
+
115
+ # Fires whole upstream portion of the net.
116
+ #
117
+ def fire_upstream_recursively
118
+ # LATER: so far, implemented without concerns about infinite loops
119
+ # LATER: This as a global hash { place => fire_list }
120
+ @upstream_arcs.each &:fire_upstream_recursively
121
+ end
122
+ alias :fire_upstream! :fire_upstream_recursively
123
+
124
+ # Firing of downstream transitions regardless of cocking. (To #fire
125
+ # transitions, they have to be cocked with #cock method; the firing
126
+ # methods with exclamation marks disregard cocking.)
127
+ #
128
+ def fire_downstream!
129
+ @downstream_arcs.each &:fire!
130
+ end
131
+
132
+ # Fires whole downstream portion of the net.
133
+ #
134
+ def fire_downstream_recursively
135
+ # LATER: so far, implemented withoud concerns about infinite loops
136
+ # LATER: This as a global hash { place => fire_list }
137
+ @downstream_arcs.each &:fire_downstream_recursively
138
+ end
139
+ alias :fire_downstream! :fire_downstream_recursively
140
+
141
+ # Produces the inspect string of the place.
142
+ #
143
+ def inspect
144
+ n, m, d, q = instance_description_strings
145
+ "#<Place: #{ ( USE_QUANTUM ? [n, m, d, q] : [n, m, d] ).join ', ' } >"
146
+ end
147
+
148
+ # Returns a string briefly describing the place.
149
+ #
150
+ def to_s
151
+ n, m = name, marking
152
+ "#{n.nil? ? 'Place' : n}[ #{m.nil? ? 'nil' : m} ]"
153
+ end
154
+
155
+ private
156
+
157
+ # Makes the place notice an upstream transition;
158
+ # to be called from the connecting transitions.
159
+ def register_upstream_transition( transition )
160
+ @upstream_arcs << transition
161
+ end
162
+
163
+ # Makes the place notice a downstream transition;
164
+ # to be called from the connecting transitions.
165
+ def register_downstream_transition( transition )
166
+ @downstream_arcs << transition
167
+ end
168
+
169
+ def instance_description_strings
170
+ m, n, d, q = marking, name, default_marking, quantum
171
+ nς = "name: #{n.nil? ? '∅' : n}"
172
+ mς = "marking: #{m.nil? ? 'nil' : m}"
173
+ dς = "default_marking: #{d.nil? ? '∅' : d}"
174
+ qς = "quantum: #{q.nil? ? '∅' : q}"
175
+ return nς, mς, dς, qς
176
+ end
177
+
178
+ # Place, Transition, Net class
179
+ #
180
+ def Place; ::YPetri::Place end
181
+ def Transition; ::YPetri::Transition end
182
+ def Net; ::YPetri::Net end
183
+
184
+ # Instance identification methods.
185
+ #
186
+ def place( which ); Place().instance( which ) end
187
+ def transition( which ); Transition().instance( which ) end
188
+ def net( which ); Net().instance( which ) end
189
+ end # class YPetri::Place