y_petri 2.0.3 → 2.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,26 @@
1
- #encoding: utf-8
1
+ # -*- coding: utf-8 -*-
2
2
 
3
- # Now is a good time to talk about transition classification:
3
+ require_relative 'dependency_injection'
4
+ require_relative 'transition/arcs'
5
+ require_relative 'transition/cocking'
6
+ require_relative 'transition/constructor_syntax'
7
+
8
+ # A Petri net transition. There are 6 basic types of YPetri transitions:
9
+ #
10
+ # * <b>ts</b> – timeless nonstoichiometric
11
+ # * <b>tS</b> – timeless stoichiometric
12
+ # * <b>Tsr</b> – timed rateless nonstoichiometric
13
+ # * <b>TSr</b> – timed rateless stoichiometric
14
+ # * <b>sR</b> – nonstoichiometric with rate
15
+ # * <b>SR</b> – stoichiometric with rate
4
16
  #
5
- # STOICHIOMETRIC / NON-STOICHIOMETRIC
17
+ # These 6 kinds of YPetri transitions correspond to the vertices of a cube,
18
+ # whose 3 dimensions are:
19
+ #
20
+ # - stoichiometric (S) / nonstoichiometric (s)
21
+ # - timed (T) / timeless (t)
22
+ # - having rate (R) / not having rate (r)
23
+ #
6
24
  # I. For stoichiometric transitions:
7
25
  # 1. Rate vector is computed as rate * stoichiometry vector, or
8
26
  # 2. Δ vector is computed a action * stoichiometry vector.
@@ -12,882 +30,424 @@
12
30
  #
13
31
  # Conclusion: stoichiometricity distinguishes *need to multiply the
14
32
  # rate/action closure result by stoichiometry*.
15
- #
16
- # HAVING / NOT HAVING RATE
33
+ #
17
34
  # I. For transitions with rate, the closure result has to be
18
- # multiplied by the time step duration (delta_t) to get action.
35
+ # multiplied by the time step duration (delta_t) to get action.
19
36
  # II. For rateless transitions, the closure result is used as is.
20
37
  #
21
38
  # Conclusion: has_rate? distinguishes *need to multiply the closure
22
- # result by delta time* - differentiability of action by time.
23
- #
24
- # TIMED / TIMELESS
39
+ # result by delta time* -- differentiability of action by time.
40
+ #
25
41
  # I. For timed transitions, action is time-dependent. Transitions with
42
+ # rate are thus always timed. In rateless transitions, timedness means
43
+ # that the action closure expects time step length (delta_t) as its first
44
+ # argument - its arity is thus codomain size + 1.
45
+ # II. For timeless transitions, action is time-independent. Timeless
46
+ # transitions are necessarily also rateless. Arity of the action closure
47
+ # is expected to match the domain size.
48
+ #
49
+ # Conclusion: Transitions with rate are always timed. In rateless
50
+ # transitions, timedness distinguishes the need to supply time step
51
+ # duration as the first argument to the action closure.
52
+ #
53
+ # Since transitions with rate are always timed, and vice-versa, timeless
54
+ # transitions cannot have rate, there are not 8, but only 6 permissible
55
+ # combinations -- 6 basic transition types listed above.
56
+ #
57
+ # === Domain and codomin
58
+ #
59
+ # Each transition has a domain, or 'upstream places': A collection of places
60
+ # whose marking directly affects the transition's operation. Also, each
61
+ # transition has a codomain, or 'downstream places': A collection of places,
62
+ # whose marking is directly affected by the transition's operation.
63
+ #
64
+ # === Action and action vector
65
+ #
66
+ # Regardless of the type, every transition has <em>action</em>:
67
+ # A prescription of how the transition changes the marking of its codomain
68
+ # when it fires. With respect to the transition's codomain, we can also
69
+ # talk about <em>action vector</em>. For non-stoichiometric transitions,
70
+ # the action vector is directly the output of the action closure or rate
71
+ # closure multiplied by Δtime, while for stoichiometric transitions, this
72
+ # needs to be additionaly multiplied by the transitions stoichiometric
73
+ # vector. Now we are finally equipped to talk about the exact meaning of
74
+ # 3 basic transition properties.
75
+ #
76
+ # === Meaning of the 3 basic transition properties
77
+ #
78
+ # ==== Stoichiometric / non-stoichiometric
79
+ # * For stoichiometric transitions:
80
+ # [Rate vector] is computed as rate * stoichiometry vector, or
81
+ # [Δ vector] is computed a action * stoichiometry vector
82
+ # * For non-stoichiometric transitions:
83
+ # [Rate vector] is obtained as the rate closure result, or
84
+ # [action vector] is obtained as the action closure result.
85
+ #
86
+ # Conclusion: stoichiometricity distinguishes <b>need to multiply the
87
+ # rate/action closure result by stoichiometry</b>.
88
+ #
89
+ # ==== Having / not having rate
90
+ # * For transitions with rate, the closure result has to be
91
+ # multiplied by the time step duration (Δt) to get the action.
92
+ # * For rateless transitions, the closure result is used as is.
93
+ #
94
+ # Conclusion: has_rate? distinguishes <b>the need to multiply the closure
95
+ # result by delta time</b> - differentiability of action by time.
96
+ #
97
+ # ==== Timed / Timeless
98
+ # * For timed transitions, action is time-dependent. Transitions with
26
99
  # rate are thus always timed. In rateless transitions, timedness means
27
100
  # that the action closure expects time step length (delta_t) as its first
28
101
  # argument - its arity is thus codomain size + 1.
29
- # II. For timeless transitions, action is time-independent. Timeless
102
+ # * For timeless transitions, action is time-independent. Timeless
30
103
  # transitions are necessarily also rateless. Arity of the action closure
31
104
  # is expected to match the domain size.
32
105
  #
33
106
  # Conclusion: Transitions with rate are always timed. In rateless
34
- # transitions, timedness distinguishes the need to supply time step
35
- # duration as the first argument to the action closure.
107
+ # transitions, timedness distinguishes <b>the need to supply time step
108
+ # duration as the first argument to the action closure</b>.
109
+ #
110
+ # === Other transition types
36
111
  #
37
- # ASSIGNMENT TRANSITIONS
112
+ # ==== Assignment transitions
38
113
  # Named argument :assignment_action set to true indicates that the
39
114
  # transitions acts by replacing the object stored as place marking by
40
- # the object supplied by the transition. For numeric types, same
41
- # effect can be achieved by subtracting the old number from the place
42
- # and subsequently adding the new value to it.
115
+ # the object supplied by the transition. (Same as in with spreadsheet
116
+ # functions.) For numeric types, same effect can be achieved by subtracting
117
+ # the old number from the place and subsequently adding the new value to it.
43
118
  #
44
- module YPetri
45
-
46
- # Represents a Petri net transition. YPetri transitions come in 6
47
- # basic types
48
- #
49
- # === Basic transition types
50
- #
51
- # * <b>ts</b> – timeless nonstoichiometric
52
- # * <b>tS</b> – timeless stoichiometric
53
- # * <b>Tsr</b> – timed rateless nonstoichiometric
54
- # * <b>TSr</b> – timed rateless stoichiometric
55
- # * <b>sR</b> – nonstoichiometric with rate
56
- # * <b>SR</b> – stoichiometric with rate
57
- #
58
- # These 6 kinds of YPetri transitions correspond to the vertices
59
- # of a cube with 3 dimensions:
60
- #
61
- # - stoichiometric (S) / nonstoichiometric (s)
62
- # - timed (T) / timeless (t)
63
- # - having rate (R) / not having rate (r)
64
- #
65
- # Since transitions with rate are always timed, and vice-versa, timeless
66
- # transitions cannot have rate, there are only 6 permissible combinations,
67
- # mentioned above.
68
- #
69
- # === Domain and codomin
70
- #
71
- # Each transition has a domain, or 'upstream places': A collection of places
72
- # whose marking directly affects the transition's operation. Also, each
73
- # transition has a codomain, or 'downstream places': A collection of places,
74
- # whose marking is directly affected by the transition's operation.
75
- #
76
- # === Action and action vector
77
- #
78
- # Regardless of the type, every transition has <em>action</em>:
79
- # A prescription of how the transition changes the marking of its codomain
80
- # when it fires. With respect to the transition's codomain, we can also
81
- # talk about <em>action vector</em>. For non-stoichiometric transitions,
82
- # the action vector is directly the output of the action closure or rate
83
- # closure multiplied by Δtime, while for stoichiometric transitions, this
84
- # needs to be additionaly multiplied by the transitions stoichiometric
85
- # vector. Now we are finally equipped to talk about the exact meaning of
86
- # 3 basic transition properties.
87
- #
88
- # === Meaning of the 3 basic transition properties
89
- #
90
- # ==== Stoichiometric / non-stoichiometric
91
- # * For stoichiometric transitions:
92
- # [Rate vector] is computed as rate * stoichiometry vector, or
93
- # [Δ vector] is computed a action * stoichiometry vector
94
- # * For non-stoichiometric transitions:
95
- # [Rate vector] is obtained as the rate closure result, or
96
- # [action vector] is obtained as the action closure result.
119
+ # ==== Functional / Functionless transitions
120
+ # Original Petri net definition does not speak about transition "functions",
121
+ # but it more or less assumes timeless action according to the stoichiometry.
122
+ # So in YPetri, stoichiometric transitions with no action / rate closure
123
+ # specified become functionless transitions as meant by Carl Adam Petri.
124
+ #
125
+ class YPetri::Transition
126
+ include NameMagic
127
+ include YPetri::DependencyInjection
128
+
129
+ BASIC_TRANSITION_TYPES = {
130
+ ts: "timeless nonstoichiometric transition",
131
+ tS: "timeless stoichiometric transition",
132
+ Tsr: "timed rateless nonstoichiometric transition",
133
+ TSr: "timed rateless stoichiometric transition",
134
+ sR: "nonstoichiometric transition with rate",
135
+ SR: "stoichiometric transition with rate"
136
+ }
137
+
138
+ # Domain, or 'upstream arcs', is a collection of places, whose marking
139
+ # directly affects the transition's action.
97
140
  #
98
- # Conclusion: stoichiometricity distinguishes <b>need to multiply the
99
- # rate/action closure result by stoichiometry</b>.
100
- #
101
- # ==== Having / not having rate
102
- # * For transitions with rate, the closure result has to be
103
- # multiplied by the time step duration (Δt) to get the action.
104
- # * For rateless transitions, the closure result is used as is.
105
- #
106
- # Conclusion: has_rate? distinguishes <b>the need to multiply the closure
107
- # result by delta time</b> - differentiability of action by time.
108
- #
109
- # ==== Timed / Timeless
110
- # * For timed transitions, action is time-dependent. Transitions with
111
- # rate are thus always timed. In rateless transitions, timedness means
112
- # that the action closure expects time step length (delta_t) as its first
113
- # argument - its arity is thus codomain size + 1.
114
- # * For timeless transitions, action is time-independent. Timeless
115
- # transitions are necessarily also rateless. Arity of the action closure
116
- # is expected to match the domain size.
141
+ attr_reader :domain
142
+ alias :domain_arcs :domain
143
+ alias :domain_places :domain
144
+ alias :upstream :domain
145
+ alias :upstream_arcs :domain
146
+ alias :upstream_places :domain
147
+
148
+ # Codomain, 'downstream arcs', or 'action arcs', is a collection of places,
149
+ # whose marking is directly changed by this transition's firing.
117
150
  #
118
- # Conclusion: Transitions with rate are always timed. In rateless
119
- # transitions, timedness distinguishes <b>the need to supply time step
120
- # duration as the first argument to the action closure</b>.
121
- #
122
- # === Other transition types
123
- #
124
- # ==== Assignment transitions
125
- # Named argument :assignment_action set to true indicates that the
126
- # transitions acts by replacing the object stored as place marking by
127
- # the object supplied by the transition. (Same as in with spreadsheet
128
- # functions.) For numeric types, same effect can be achieved by subtracting
129
- # the old number from the place and subsequently adding the new value to it.
130
- #
131
- # ==== Functional / Functionless transitions
132
- # Original Petri net definition does not speak about transition "functions",
133
- # but it more or less assumes timeless action according to the stoichiometry.
134
- # So in YPetri, stoichiometric transitions with no action / rate closure
135
- # specified become functionless transitions as meant by Carl Adam Petri.
151
+ attr_reader :codomain
152
+ alias :codomain_arcs :codomain
153
+ alias :codomain_places :codomain
154
+ alias :downstream :codomain
155
+ alias :downstream_arcs :codomain
156
+ alias :downstream_places :codomain
157
+ alias :action_arcs :codomain
158
+
159
+ # Is the transition stoichiometric?
136
160
  #
137
- class Transition
138
- include NameMagic
139
-
140
- BASIC_TRANSITION_TYPES = {
141
- ts: "timeless nonstoichiometric transition",
142
- tS: "timeless stoichiometric transition",
143
- Tsr: "timed rateless nonstoichiometric transition",
144
- TSr: "timed rateless stoichiometric transition",
145
- sR: "nonstoichiometric transition with rate",
146
- SR: "stoichiometric transition with rate"
147
- }
148
-
149
- # Domain, or 'upstream arcs', is a collection of places, whose marking
150
- # directly affects the transition's action.
151
- #
152
- attr_reader :domain
153
- alias :domain_arcs :domain
154
- alias :domain_places :domain
155
- alias :upstream :domain
156
- alias :upstream_arcs :domain
157
- alias :upstream_places :domain
158
-
159
- # Names of upstream places.
160
- #
161
- def domain_pp; domain.map &:name end
162
- alias :upstream_pp :domain_pp
163
-
164
- # Codomain, 'downstream arcs', or 'action arcs', is a collection of places,
165
- # whose marking is directly changed by this transition's firing.
166
- #
167
- attr_reader :codomain
168
- alias :codomain_arcs :codomain
169
- alias :codomain_places :codomain
170
- alias :downstream :codomain
171
- alias :downstream_arcs :codomain
172
- alias :downstream_places :codomain
173
- alias :action_arcs :codomain
174
-
175
- # Names of downstream places.
176
- #
177
- def codomain_pp; codomain.map &:name end
178
- alias :downstream_pp :codomain_pp
179
-
180
- # Union of action arcs and test arcs.
181
- #
182
- def arcs; domain | codomain end
183
-
184
- # Returns names of the (places connected to) the transition's arcs.
185
- #
186
- def aa; arcs.map &:name end
187
-
188
- # Is the transition stoichiometric?
189
- #
190
- def stoichiometric?; @stoichiometric end
191
- alias :s? :stoichiometric?
192
-
193
- # Is the transition nonstoichiometric? (Opposite of #stoichiometric?)
194
- #
195
- def nonstoichiometric?; not stoichiometric? end
196
-
197
- # Stoichiometry (implies that the transition is stoichiometric).
198
- #
199
- attr_reader :stoichiometry
200
-
201
- # Stoichiometry as a hash of pairs:
202
- # { codomain_place_instance => stoichiometric_coefficient }
203
- #
204
- def stoichio; Hash[ codomain.zip( @stoichiometry ) ] end
205
-
206
- # Stoichiometry as a hash of pairs:
207
- # { codomain_place_name_symbol => stoichiometric_coefficient }
208
- #
209
- def s; stoichio.with_keys { |k| k.name.to_sym } end
210
-
211
- # Does the transition have rate?
212
- #
213
- def has_rate?; @has_rate end
214
-
215
- # Is the transition rateless?
216
- #
217
- def rateless?; not has_rate? end
218
-
219
- # The term 'flux' (meaning flow) is associated with continuous transitions,
220
- # while term 'propensity' is used with discrete stochastic transitions.
221
- # By the design of YPetri, distinguishing between discrete and continuous
222
- # computation is the responsibility of the simulation method, considering
223
- # current marking of the transition's connectivity and quanta of its
224
- # codomain. To emphasize unity of 'flux' and 'propensity', term 'rate' is
225
- # used to represent both of them. Rate closure input arguments must
226
- # correspond to the domain places.
227
- #
228
- attr_reader :rate_closure
229
- alias :rate :rate_closure
230
- alias :flux_closure :rate_closure
231
- alias :flux :rate_closure
232
- alias :propensity_closure :rate_closure
233
- alias :propensity :rate_closure
234
-
235
- # For rateless transition, action closure must be present. Action closure
236
- # input arguments must correspond to the domain places, and for timed
237
- # transitions, the first argument of the action closure must be Δtime.
238
- #
239
- attr_reader :action_closure
240
- alias :action :action_closure
241
-
242
- # Does the transition's action depend on delta time?
243
- #
244
- def timed?; @timed end
245
-
246
- # Is the transition timeless? (Opposite of #timed?)
247
- #
248
- def timeless?; not timed? end
249
-
250
- # Is the transition functional?
251
- # Explanation: If rate or action closure is supplied, a transition is always
252
- # considered 'functional'. Otherwise, it is considered not 'functional'.
253
- # Note that even transitions that are not functional still have standard
254
- # action acc. to Petri's definition. Also note that a timed transition is
255
- # necessarily functional.
256
- #
257
- def functional?; @functional end
258
-
259
- # Opposite of #functional?
260
- #
261
- def functionless?; not functional? end
262
-
263
- # Reports transition membership in one of 6 basic types of YPetri transitions:
264
- # 1. ts ..... timeless nonstoichiometric
265
- # 2. tS ..... timeless stoichiometric
266
- # 3. Tsr .... timed rateless nonstoichiometric
267
- # 4. TSr .... timed rateless stoichiometric
268
- # 5. sR ..... nonstoichiometric with rate
269
- # 6. SR ..... stoichiometric with rate
270
- #
271
- def basic_type
272
- if has_rate? then stoichiometric? ? :SR : :sR
273
- elsif timed? then stoichiometric? ? :TSr : :Tsr
274
- else stoichiometric? ? :tS : :ts end
275
- end
276
-
277
- # Reports transition's type (basic type + whether it's an assignment
278
- # transition).
279
- #
280
- def type
281
- assignment_action? ? "A(ts)" : basic_type
282
- end
161
+ def stoichiometric?; @stoichiometric end
162
+ alias :s? :stoichiometric?
283
163
 
284
- # Is it an assignment transition?
285
- #
286
- # A transition can be specified to have 'assignment action', in which case
287
- # it completely replaces codomain marking with the objects resulting from
288
- # the transition's action. Note that for numeric marking, specifying
289
- # assignment action is a matter of convenience, not necessity, as it can
290
- # be emulated by fully subtracting the present codomain values and adding
291
- # the numbers computed by the transition to them. Assignment action flag
292
- # is a matter of necessity only when codomain marking involves objects
293
- # not supporting subtraction/addition (which is out of the scope of Petri's
294
- # original specification anyway.)
295
- #
296
- def assignment_action?; @assignment_action end
297
- alias :assignment? :assignment_action?
298
-
299
- # Is the transition cocked?
300
- #
301
- # The transition has to be cocked before #fire method can be called
302
- # successfully. (Can be overriden using #fire! method.)
303
- #
304
- def cocked?; @cocked end
305
-
306
- # Opposite of #cocked?
307
- #
308
- def uncocked?; not cocked? end
309
-
310
- # As you could have noted in the introduction, Transition class encompasses
311
- # all different kinds of Petri net transitions. This is considered a good
312
- # design pattern for cases like this, but it makes the transition class and
313
- # its constructor look a bit complicated. Should you feel that way, please
314
- # remember that you only learn one constructor, but can create many kinds
315
- # of transition – the computer is doing a lot of work behind the scenes for
316
- # you. The type of a transition created depends on the qualities of supplied
317
- # arguments. However, you can also explicitly specify what kind of
318
- # transition do you want, to exclude any ambiguity.
319
- #
320
- # Whatever arguments you supply, the constructor will always need a way to
321
- # determine domain (upstream arcs) and codomain (downstream arcs) of your
322
- # transitions, implicitly or explicitly. Secondly, the constructor must
323
- # have a way to determine the transition's action, although there is more
324
- # than one way of doing so. So enough talking and onto the examples. We
325
- # will imagine having 3 places A, B, C, for which we will create various
326
- # transitions:
327
- #
328
- # ==== Timeless nonstoichiometric (ts) transitions
329
- # Action closure has to be supplied, whose return arity correspons to
330
- # the codomain size.
331
- # <tt>
332
- # Transition.new upstream_arcs: [A, C], downstream_arcs: [A, B],
333
- # action_closure: proc { |m, x|
334
- # if x > 0 then [-(m / 2), (m / 2)]
335
- # else [1, 0] end }
336
- # </tt>
337
- # (This represents a transition connected by arcs to places A, B, C, whose
338
- # operation depends on C in such way, that if C.marking is positive,
339
- # then half of the marking of A is shifted to B, while if C.marking is
340
- # nonpositive, 1 is added to A.)
341
- #
342
- # ==== Timeless stoichiometric (tS) transitions
343
- # Stochiometry has to be supplied, with optional action closure.
344
- # Action closure return arity should be 1 (its result will be multiplied
345
- # by the stoichiometry vector).
346
- #
347
- # If no action closure is given, a <em>functionless</em> transition will
348
- # be created, whose action closure will be by default 1 * stoichiometry
349
- # vector.
350
- #
351
- # ==== Timed rateless nonstoichiometric (Tsr) transitions
352
- # Action closure has to be supplied, whose first argument is Δt, and the
353
- # remaining arguments correspond to the domain size. Return arity of this
354
- # closure should correspond to the codomain size.
355
- #
356
- # ==== Timed rateless stoichiometric (TSr) transitions
357
- # Action closure has to be supplied, whose first argument is Δt, and the
358
- # remaining arguments correspond to the domain size. Return arity of this
359
- # closure should be 1 (to be multiplied by the stoichiometry vector).
360
- #
361
- # ==== Nonstoichiometric transitions with rate (sR)
362
- # Rate closure has to be supplied, whose arity should correspond to the
363
- # domain size (Δt argument is not needed). Return arity of this closure
364
- # should correspond to the codomain size and represents rate of change
365
- # contribution for marking of the codomain places.
366
- #
367
- # ==== Stoichiometric transitions with rate (SR)
368
- #
369
- # Rate closure and stoichiometry has to be supplied, whose arity should
370
- # correspond to the domain size. Return arity of this closure should be 1
371
- # (to be multiplied by the stoichiometry vector, as in all stoichiometric
372
- # transitions).
373
- #
374
- # <tt>Transition( stoichiometry: { A: -1, B: 1 },
375
- # rate: λ { |a| a * 0.5 } )
376
- #
377
- def initialize *args
378
- check_in_arguments *args # the big work of checking in args
379
- inform_upstream_places # that they have been connected
380
- inform_downstream_places # that they have been connected
381
- @cocked = false # transitions initialize uncocked
382
- end
383
-
384
- # Marking of the domain places.
385
- #
386
- def domain_marking; domain.map &:marking end
387
-
388
- # Marking of the codomain places.
389
- #
390
- def codomain_marking; codomain.map &:marking end
391
-
392
- # Result of the transition's "function", regardless of the #enabled? status.
393
- #
394
- def action Δt=nil
395
- raise ArgumentError, "Δtime argument required for timed transitions!" if
396
- timed? and Δt.nil?
397
- # the code here looks awkward, because I was trying to speed it up
398
- if has_rate? then
399
- if stoichiometric? then
400
- rate = rate_closure.( *domain_marking )
401
- stoichiometry.map { |coeff| rate * coeff * Δt }
402
- else # assuming correct return value arity from the rate closure:
403
- rate_closure.( *domain_marking ).map { |e| component * Δt }
404
- end
405
- else # rateless
406
- if timed? then
407
- if stoichiometric? then
408
- rslt = action_closure.( Δt, *domain_marking )
409
- stoichiometry.map { |coeff| rslt * coeff }
410
- else
411
- action_closure.( Δt, *domain_marking ) # caveat result arity!
412
- end
413
- else # timeless
414
- if stoichiometric? then
415
- rslt = action_closure.( *domain_marking )
416
- stoichiometry.map { |coeff| rslt * coeff }
417
- else
418
- action_closure.( *domain_marking ) # caveat result arity!
419
- end
420
- end
421
- end
422
- end # action
164
+ # Is the transition nonstoichiometric? (Opposite of #stoichiometric?)
165
+ #
166
+ def nonstoichiometric?
167
+ not stoichiometric?
168
+ end
423
169
 
424
- # Zero action
425
- #
426
- def zero_action
427
- codomain.map { 0 }
428
- end
170
+ # Stoichiometry (implies that the transition is stoichiometric).
171
+ #
172
+ attr_reader :stoichiometry
429
173
 
430
- # Changes to the marking of codomain, as they would happen if #fire! was
431
- # called right now (ie. honoring #enabled?, but not #cocked? status.
432
- #
433
- def action_after_feasibility_check( Δt=nil )
434
- raise AErr, "Δtime argument required for timed transitions!" if
435
- timed? and Δt.nil?
436
- act = Array( action Δt )
437
- # Assignment actions are always feasible - no need to check:
438
- return act if assignment?
439
- # check if the marking after the action would still be positive
440
- enabled = codomain
441
- .zip( act )
442
- .all? { |place, change| place.marking.to_f >= -change.to_f }
443
- if enabled then act else
444
- raise "firing of #{self}#{ Δt ? ' with Δtime %s' % Δt : '' } " +
445
- "would result in negative marking"
446
- zero_action
447
- end
448
- # LATER: This use of #zip here should be avoided for speed
449
- end
174
+ # Stoichiometry as a hash of pairs:
175
+ # { codomain_place_instance => stoichiometric_coefficient }
176
+ #
177
+ def stoichio
178
+ Hash[ codomain.zip( @stoichiometry ) ]
179
+ end
450
180
 
451
- # Allows #fire method to succeed. (#fire! disregards cocking.)
452
- #
453
- def cock; @cocked = true end
454
- alias :cock! :cock
455
-
456
- # Uncocks a cocked transition without firing it.
457
- #
458
- def uncock; @cocked = false end
459
- alias :uncock! :uncock
460
-
461
- # If #fire method of a transition applies its action (token adding/taking)
462
- # on its domain, depending on codomain marking. Time step is expected as
463
- # argument if the transition is timed. Only works if the transition has
464
- # been cocked and causes the transition to uncock.
465
- #
466
- def fire( Δt=nil )
467
- raise AErr, "Δtime argument required for timed transitions!" if
468
- timed? and Δt.nil?
469
- return false unless cocked?
470
- uncock
471
- fire! Δt
472
- return true
473
- end
181
+ # Stoichiometry as a hash of pairs:
182
+ # { codomain_place_name_symbol => stoichiometric_coefficient }
183
+ #
184
+ def s
185
+ stoichio.with_keys { |k| k.name || k.object_id }
186
+ end
474
187
 
475
- # Fires the transition regardless of cocked/uncocked status.
476
- #
477
- def fire!( Δt=nil )
478
- raise AErr, "Δtime required for timed transitions!" if timed? && Δt.nil?
479
- if assignment_action? then
480
- act = Array action( Δt )
481
- codomain.each_with_index do |place, i|
482
- place.marking = act[i]
483
- end
484
- else
485
- act = action_after_feasibility_check( Δt )
486
- codomain.each_with_index do |place, i|
487
- place.add act[i]
488
- end
489
- end
490
- return nil
491
- end
188
+ # Does the transition have rate?
189
+ #
190
+ def has_rate?
191
+ @has_rate
192
+ end
492
193
 
493
- # Sanity of execution is ensured by Petri's notion of transitions being
494
- # "enabled" if and only if the intended action can immediately take
495
- # place without getting places into forbidden state (negative marking).
496
- #
497
- def enabled?( Δt=nil )
498
- raise AErr, "Δtime argument required for timed transitions!" if
499
- timed? and Δt.nil?
500
- codomain
501
- .zip( action Δt )
502
- .all? { |place, change| place.marking.to_f >= -change.to_f }
503
- end
194
+ # Is the transition rateless?
195
+ #
196
+ def rateless?
197
+ not has_rate?
198
+ end
199
+
200
+ # The term 'flux' (meaning flow) is associated with continuous transitions,
201
+ # while term 'propensity' is used with discrete stochastic transitions.
202
+ # By the design of YPetri, distinguishing between discrete and continuous
203
+ # computation is the responsibility of the simulation method, considering
204
+ # current marking of the transition's connectivity and quanta of its
205
+ # codomain. To emphasize unity of 'flux' and 'propensity', term 'rate' is
206
+ # used to represent both of them. Rate closure input arguments must
207
+ # correspond to the domain places.
208
+ #
209
+ attr_reader :rate_closure
210
+ alias :rate :rate_closure
211
+ alias :flux_closure :rate_closure
212
+ alias :flux :rate_closure
213
+ alias :propensity_closure :rate_closure
214
+ alias :propensity :rate_closure
215
+
216
+ # For rateless transition, action closure must be present. Action closure
217
+ # input arguments must correspond to the domain places, and for timed
218
+ # transitions, the first argument of the action closure must be Δtime.
219
+ #
220
+ attr_reader :action_closure
221
+ alias :action :action_closure
504
222
 
505
- # Recursive firing of the upstream net portion (honors #cocked?).
506
- #
507
- def fire_upstream_recursively
508
- return false unless cocked?
509
- uncock
510
- upstream_places.each &:fire_upstream_recursively
511
- fire!
512
- return true
513
- end
223
+ # Does the transition's action depend on delta time?
224
+ #
225
+ def timed?
226
+ @timed
227
+ end
514
228
 
515
- # Recursive firing of the downstream net portion (honors #cocked?).
516
- #
517
- def fire_downstream_recursively
518
- return false unless cocked?
519
- uncock
520
- fire!
521
- downstream_places.each &:fire_downstream_recursively
522
- return true
523
- end
229
+ # Is the transition timeless? (Opposite of #timed?)
230
+ #
231
+ def timeless?
232
+ not timed?
233
+ end
234
+
235
+ # Is the transition functional?
236
+ # Explanation: If rate or action closure is supplied, a transition is always
237
+ # considered 'functional'. Otherwise, it is considered not 'functional'.
238
+ # Note that even transitions that are not functional still have standard
239
+ # action acc. to Petri's definition. Also note that a timed transition is
240
+ # necessarily functional.
241
+ #
242
+ def functional?
243
+ @functional
244
+ end
524
245
 
525
- # def lock
526
- # # LATER
527
- # end
528
- # alias :disable! :force_disabled
529
-
530
- # def unlock
531
- # # LATER
532
- # end
533
- # alias :undisable! :remove_force_disabled
534
-
535
- # def force_enabled!( boolean )
536
- # # true - the transition is always regarded as enabled
537
- # # false - the status is removed
538
- # # LATER
539
- # end
540
-
541
- # def clamp
542
- # # LATER
543
- # end
544
-
545
- # def remove_clamp
546
- # # LATER
547
- # end
548
-
549
- # def reset!
550
- # uncock
551
- # remove_force_disabled
552
- # remove_force_enabled
553
- # remove_clamp
554
- # return self
555
- # end
556
-
557
- # Inspect string for a transition.
558
- #
559
- def inspect
560
- to_s
561
- end
246
+ # Opposite of #functional?
247
+ #
248
+ def functionless?
249
+ not functional?
250
+ end
251
+
252
+ # Reports the transition's membership in one of 6 basic types :
253
+ # 1. ts ..... timeless nonstoichiometric
254
+ # 2. tS ..... timeless stoichiometric
255
+ # 3. Tsr .... timed rateless nonstoichiometric
256
+ # 4. TSr .... timed rateless stoichiometric
257
+ # 5. sR ..... nonstoichiometric with rate
258
+ # 6. SR ..... stoichiometric with rate
259
+ #
260
+ def basic_type
261
+ if has_rate? then stoichiometric? ? :SR : :sR
262
+ elsif timed? then stoichiometric? ? :TSr : :Tsr
263
+ else stoichiometric? ? :tS : :ts end
264
+ end
265
+
266
+ # Reports transition's type (basic type + whether it's an assignment
267
+ # transition).
268
+ #
269
+ def type
270
+ assignment_action? ? "A(ts)" : basic_type
271
+ end
562
272
 
563
- # Conversion to a string.
564
- #
565
- def to_s
566
- "#<Transition: %s >" %
567
- "#{name.nil? ? '' : '%s ' % name }(#{basic_type}%s)%s" %
568
- [ "#{assignment_action? ? ' Assign.' : ''}",
569
- "#{name.nil? ? ' id:%s' % object_id : ''}" ]
570
- end
273
+ # Is it an assignment transition?
274
+ #
275
+ # A transition can be specified to have 'assignment action', in which case
276
+ # it completely replaces codomain marking with the objects resulting from
277
+ # the transition's action. Note that for numeric marking, specifying
278
+ # assignment action is a matter of convenience, not necessity, as it can
279
+ # be emulated by fully subtracting the present codomain values and adding
280
+ # the numbers computed by the transition to them. Assignment action flag
281
+ # is a matter of necessity only when codomain marking involves objects
282
+ # not supporting subtraction/addition (which is out of the scope of Petri's
283
+ # original specification anyway.)
284
+ #
285
+ def assignment_action?; @assignment_action end
286
+ alias :assignment? :assignment_action?
571
287
 
572
- private
573
-
574
- # **********************************************************************
575
- # ARGUMENT CHECK-IN UPON INITIALIZATION
576
- # **********************************************************************
577
-
578
- # Checking in the arguments supplied to #initialize looks like a big job.
579
- # I won't contest to that, but let us not, that it is basically nothing
580
- # else then defining the duck type of the input argument collection.
581
- # TypeError is therefore raised if invalid collection has been supplied.
582
- #
583
- def check_in_arguments *args
584
- oo = args.extract_options!
585
- oo.may_have :stoichiometry, syn!: [ :stoichio, :s ]
586
- oo.may_have :codomain, syn!: [ :codomain_arcs, :codomain_places,
587
- :downstream,
588
- :downstream_arcs, :downstream_places,
589
- :action_arcs ]
590
- oo.may_have :domain, syn!: [ :domain_arcs, :domain_places,
591
- :upstream, :upstream_arcs, :upstream_places ]
592
- oo.may_have :rate, syn!: [ :rate_closure,
593
- :propensity, :propensity_closure ]
594
- oo.may_have :action, syn!: :action_closure
595
- oo.may_have :timed
596
-
597
- @has_rate = oo.has? :rate # was the rate was given?
598
-
599
- # is the transition stoichiometric (S) or nonstoichiometric (s)?
600
- @stoichiometric = oo.has? :stoichiometry
601
-
602
- # downstream description arguments: codomain, stoichiometry (if S)
288
+ # Result of the transition's "function", regardless of the #enabled? status.
289
+ #
290
+ def action Δt=nil
291
+ raise ArgumentError, "Δtime argument required for timed transitions!" if
292
+ timed? and Δt.nil?
293
+ # the code here looks awkward, because I was trying to speed it up
294
+ if has_rate? then
603
295
  if stoichiometric? then
604
- @codomain, @stoichiometry = check_in_downstream_description_for_S( oo )
605
- else # s transitions have no stoichiometry
606
- @codomain = check_in_downstream_description_for_s( oo )
296
+ rate = rate_closure.( *domain_marking )
297
+ stoichiometry.map { |coeff| rate * coeff * Δt }
298
+ else # assuming correct return value arity from the rate closure:
299
+ rate_closure.( *domain_marking ).map { |e| component * Δt }
607
300
  end
608
-
609
- # check in domain first, :missing symbol may appear
610
- @domain = check_in_domain( oo )
611
-
612
- # upstream description arguments; also takes care of :missing domain
613
- if has_rate? then
614
- @domain, @rate_closure, @timed, @functional =
615
- check_in_upstream_description_for_R( oo )
301
+ else # rateless
302
+ if timed? then
303
+ if stoichiometric? then
304
+ rslt = action_closure.( Δt, *domain_marking )
305
+ stoichiometry.map { |coeff| rslt * coeff }
616
306
  else
617
- @domain, @action_closure, @timed, @functional =
618
- check_in_upstream_description_for_r( oo )
619
- end
620
-
621
- # optional assignment action:
622
- @assignment_action = check_in_assignment_action( oo )
623
- end # def check_in_arguments
624
-
625
- # Makes sure that supplied collection consists only of appropriate places.
626
- # Second optional argument customizes the error message.
627
- #
628
- def sanitize_place_collection place_collection, what_is_collection=nil
629
- c = what_is_collection ? what_is_collection.capitalize : "Collection"
630
- Array( place_collection ).map do |pl_id|
631
- begin
632
- place( pl_id )
633
- rescue NameError
634
- raise TypeError, "#{c} member #{pl_id} does not specify a valid place!"
307
+ action_closure.( Δt, *domain_marking ) # caveat result arity!
635
308
  end
636
- end.aT what_is_collection, "not contain duplicate places" do |collection|
637
- collection == collection.uniq
638
- end
639
- end
640
-
641
- # Private method, part of #initialize argument checking-in.
642
- #
643
- def check_in_domain( oo )
644
- if oo.has? :domain then
645
- sanitize_place_collection( oo[:domain], "supplied domain" )
646
- else
309
+ else # timeless
647
310
  if stoichiometric? then
648
- # take arcs with non-positive stoichiometry coefficients
649
- Hash[ [ @codomain, @stoichiometry ].transpose ]
650
- .delete_if{ |place, coeff| coeff > 0 }.keys
311
+ rslt = action_closure.( *domain_marking )
312
+ stoichiometry.map { |coeff| rslt * coeff }
651
313
  else
652
- :missing
653
- # Barring the caller's error, missing domain can mean:
654
- # 1. empty domain
655
- # 2. domain == codomain
656
- # This will be figured later by rate/action closure arity
314
+ action_closure.( *domain_marking ) # caveat result arity!
657
315
  end
658
316
  end
659
317
  end
318
+ end # action
660
319
 
661
- def check_in_upstream_description_for_R( oo )
662
- _domain = domain # this method may modify domain
663
- # check against colliding :action argument
664
- raise TErr, "Rate & action are mutually exclusive!" if oo.has? :action
665
- # lets figure the rate closure
666
- rate_λ = case rate_arg = oo[:rate]
667
- when Proc then # We received the closure directly,
668
- # but we've to be concerned about missing domain.
669
- if domain == :missing then # we've to figure user's intent
670
- _domain = if rate_arg.arity == 0 then
671
- [] # user meant empty domain
672
- else
673
- codomain # user meant domain same as codomain
674
- end
675
- else # domain not missing
676
- raise TErr, "Rate closure arity (#{rate_arg.arity}) " +
677
- "greater than domain size (#{domain.size})!" unless
678
- rate_arg.arity.abs <= domain.size
679
- end
680
- rate_arg
681
- else # We received something else,
682
- # we must make assumption user's intent.
683
- if stoichiometric? then # user's intent was mass action
684
- raise TErr, "When a number is supplied as rate, domain " +
685
- "must not be given!" if oo.has? :domain
686
- construct_standard_mass_action( rate_arg )
687
- else # user's intent was constant closure
688
- raise TErr, "When rate is a number and no stoichiometry " +
689
- "is supplied, codomain size must be 1!" unless
690
- codomain.size == 1
691
- # Missing domain is OK here,
692
- _domain = [] if domain == :missing
693
- # but if it was supplied explicitly, it must be empty.
694
- raise TErr, "Rate is a number, but non-empty domain was " +
695
- "supplied!" unless domain.empty? if oo.has?( :domain )
696
- lambda { rate_arg }
697
- end
698
- end
699
- # R transitions are implicitly timed
700
- _timed = true
701
- # check against colliding :timed argument
702
- oo[:timed].tE :timed, "not be false if rate given" if oo.has? :timed
703
- # R transitions are implicitly functional
704
- _functional = true
705
- return _domain, rate_λ, _timed, _functional
706
- end
320
+ # Zero action
321
+ #
322
+ def zero_action
323
+ codomain.map { 0 }
324
+ end
707
325
 
708
- def check_in_upstream_description_for_r( oo )
709
- _domain = domain # this method may modify domain
710
- _functional = true
711
- # was action closure was given explicitly?
712
- if oo.has? :action then
713
- action_λ = oo[:action].aT_is_a Proc, "supplied action named argument"
714
- if oo.has? :timed then
715
- _timed = oo[:timed]
716
- # Time to worry about the domain_missing
717
- if domain == :missing then # figure user's intent from closure arity
718
- _domain = if action_λ.arity == ( _timed ? 1 : 0 ) then
719
- [] # user meant empty domain
720
- else
721
- codomain # user meant domain same as codomain
722
- end
723
- else # domain not missing
724
- raise TErr, "Rate closure arity (#{rate_arg.arity}) > domain " +
725
- "size (#{domain.size})!" if action_λ.arity.abs > domain.size
726
- end
727
- else # :timed argument not supplied
728
- if domain == :missing then
729
- # If no domain was supplied, there is no way to reasonably figure
730
- # out the user's intent, except when arity is 0:
731
- _domain = case action_λ.arity
732
- when 0 then
733
- _timed = false
734
- [] # empty domain is implied
735
- else # no deduction of user intent possible
736
- raise AErr, "Too much ambiguity: Neither domain nor " +
737
- "timedness of the rateless transition was specified."
738
- end
739
- else # domain not missing
740
- # Even if the user did not bother to inform us explicitly about
741
- # timedness, we can use closure arity as a clue. If it equals the
742
- # domain size, leaving no room for Δtime argument, the user intent
743
- # was to create timeless transition. If it equals domain size + 1,
744
- # theu user intended to create a timed transition.
745
- _timed = case action_λ.arity
746
- when domain.size then false
747
- when domain.size + 1 then true
748
- else # no deduction of user intent possible
749
- raise AErr, "Timedness was not specified, and the " +
750
- "arity of the action supplied action closure " +
751
- "(#{action_λ.arity}) does not give clear hint on it."
752
- end
753
- end
754
- end
755
- else # rateless cases with no action closure specified
756
- # Assumption must be made on transition's action. In particular,
757
- # lambda { 1 } action closure will be assumed,
758
- action_λ = lambda { 1 }
759
- # and it will be required that the transition be stoichiometric and
760
- # timeless. Domain will thus be required empty.
761
- raise AErr, "Stoichiometry is compulsory, if rate/action was " +
762
- "not supplied." unless stoichiometric?
763
- # With this, we can drop worries about missing domain.
764
- raise AErr, "When no rate/action is supplied, the transition can't " +
765
- "be declared timed." if oo[:timed] if oo.has? :timed
766
- _timed = false
767
- _domain = []
768
- _functional = false # the transition is considered functionless
769
- end
770
- return _domain, action_λ, _timed, _functional
326
+ # Changes to the marking of codomain, as they would happen if #fire! was
327
+ # called right now (ie. honoring #enabled?, but not #cocked? status.
328
+ #
329
+ def action_after_feasibility_check( Δt=nil )
330
+ raise AErr, "Δtime argument required for timed transitions!" if
331
+ timed? and Δt.nil?
332
+ act = Array( action Δt )
333
+ # Assignment actions are always feasible - no need to check:
334
+ return act if assignment?
335
+ # check if the marking after the action would still be positive
336
+ enabled = codomain
337
+ .zip( act )
338
+ .all? { |place, change| place.marking.to_f >= -change.to_f }
339
+ if enabled then act else
340
+ raise "firing of #{self}#{ Δt ? ' with Δtime %s' % Δt : '' } " +
341
+ "would result in negative marking"
342
+ zero_action
771
343
  end
772
-
773
- def construct_standard_mass_action( num )
774
- # assume standard mass-action law
775
- nonpositive_coeffs = stoichiometry.select { |coeff| coeff <= 0 }
776
- # the closure takes markings of the domain as its arguments
777
- lambda { |*markings|
778
- nonpositive_coeffs.size.times.reduce num do |acc, i|
779
- marking, coeff = markings[ i ], nonpositive_coeffs[ i ]
780
- # Stoichiometry coefficients equal to zero are taken to indicate
781
- # plain factors, assuming that if these places were not involved
782
- # in the transition at all, the user would not be mentioning them.
783
- case coeff
784
- when 0, -1 then marking * acc
785
- else marking ** -coeff end
344
+ # LATER: This use of #zip here should be avoided for speed
345
+ end
346
+
347
+ # Applies transition's action (adding/taking tokens) on its downstream
348
+ # places (aka. domain places). If the transition is timed, delta time has
349
+ # to be supplied as argument. In order for this method to work, the
350
+ # transition has to be cocked (#cock method), and firing uncocks the
351
+ # transition, so it has to be cocked again before it can be fired for
352
+ # the second time. If the transition is not cocked, this method has no
353
+ # effect.
354
+ #
355
+ def fire( Δt=nil )
356
+ raise ArgumentError, "Δtime argument required for timed transitions!" if
357
+ timed? and Δt.nil?
358
+ return false unless cocked?
359
+ uncock
360
+ fire! Δt
361
+ return true
362
+ end
363
+
364
+ # Fires the transition just like #fire method, but disregards the cocked /
365
+ # uncocked state of the transition.
366
+ #
367
+ def fire!( Δt=nil )
368
+ raise ArgumentError, "Δt required for timed transitions!" if
369
+ Δt.nil? if timed?
370
+ try "to fire" do
371
+ if assignment_action? then
372
+ note has: "assignment action"
373
+ act = note "action", is: Array( action( Δt ) )
374
+ codomain.each_with_index do |place, i|
375
+ "place #{place}".try "to assign marking #{i}" do
376
+ place.marking = act[i]
377
+ end
786
378
  end
787
- }
788
- end
789
-
790
- # Private method, checking in downstream specification from the argument
791
- # field for stoichiometric transition.
792
- #
793
- def check_in_downstream_description_for_S( oo )
794
- codomain, stoichio =
795
- case oo[:stoichiometry]
796
- when Hash then
797
- # contains pairs { codomain place => stoichiometry coefficient }
798
- raise AErr, "With hash-type stoichiometry, :codomain named " +
799
- "argument must not be supplied." if oo.has? :codomain
800
- oo[:stoichiometry].each_with_object [[], []] do |pair, memo|
801
- codomain_place, stoichio_coeff = pair
802
- memo[0] << codomain_place
803
- memo[1] << stoichio_coeff
379
+ else
380
+ act = note "action", is: action_after_feasibility_check( Δt )
381
+ codomain.each_with_index do |place, i|
382
+ "place #{place}".try "to assign marking #{i}" do
383
+ place.add act[i]
804
384
  end
805
- else
806
- # array of stoichiometry coefficients
807
- raise AErr, "With array-type stoichiometry, :codomain named " +
808
- "argument must be supplied." unless oo.has? :codomain
809
- [ oo[:codomain], Array( oo[:stoichiometry] ) ]
810
385
  end
811
- # enforce that stoichiometry is a collection of numbers
812
- return sanitize_place_collection( codomain, "supplied codomain" ),
813
- stoichio.aT_all_numeric( "supplied stoichiometry" )
814
- end
815
-
816
- # Private method, checking in downstream specification from the argument
817
- # field for nonstoichiometric transition.
818
- #
819
- def check_in_downstream_description_for_s( oo )
820
- # codomain must be explicitly given - no way around it:
821
- raise AErr, "For non-stoichiometric transitions, :codomain named " +
822
- "argument is compulsory." unless oo.has? :codomain
823
- return sanitize_place_collection( oo[:codomain], "supplied codomain" )
386
+ end
824
387
  end
388
+ return nil
389
+ end
825
390
 
826
- # Private method, part of #initialize argument checking-in.
827
- #
828
- def check_in_assignment_action( oo )
829
- if oo.has? :assignment_action, syn!: [ :assignment, :assign, :A ] then
830
- if timed? then
831
- msg = "Timed transitions may not have assignment action!"
832
- raise TypeError, msg if oo[:assignment_action]
833
- false
834
- else # timeless transitions are eligible for assignment action
835
- oo[:assignment_action]
836
- end
837
- else # if assignment action is not specified, false is
391
+ # Sanity of execution is ensured by Petri's notion of transitions being
392
+ # "enabled" if and only if the intended action can immediately take
393
+ # place without getting places into forbidden state (negative marking).
394
+ #
395
+ def enabled?( Δt=nil )
396
+ fail ArgumentError, "Δtime argument compulsory for timed transitions!" if
397
+ timed? && Δt.nil?
398
+ codomain.zip( action Δt ).all? do |place, change|
399
+ begin
400
+ place.guard.( place.marking + change )
401
+ rescue YPetri::GuardError
838
402
  false
839
403
  end
840
404
  end
405
+ end
406
+
407
+ # def lock
408
+ # # LATER
409
+ # end
410
+ # alias :disable! :force_disabled
411
+
412
+ # def unlock
413
+ # # LATER
414
+ # end
415
+ # alias :undisable! :remove_force_disabled
416
+
417
+ # def force_enabled!( boolean )
418
+ # # true - the transition is always regarded as enabled
419
+ # # false - the status is removed
420
+ # # LATER
421
+ # end
422
+
423
+ # def clamp
424
+ # # LATER
425
+ # end
426
+
427
+ # def remove_clamp
428
+ # # LATER
429
+ # end
430
+
431
+ # def reset!
432
+ # uncock
433
+ # remove_force_disabled
434
+ # remove_force_enabled
435
+ # remove_clamp
436
+ # return self
437
+ # end
438
+
439
+ # Inspect string for a transition.
440
+ #
441
+ def inspect
442
+ to_s
443
+ end
841
444
 
842
- # Informs upstream places that they are connected to this transition.
843
- #
844
- def inform_upstream_places
845
- upstream_places.each { |p| p.send :register_downstream_transition, self }
846
- end
847
-
848
- # Informs downstream places that they are connected to this transition.
849
- #
850
- def inform_downstream_places
851
- downstream_places.each { |p| p.send :register_upstream_transition, self }
852
- end
853
-
854
- # Place class pertinent herein. Provided for the purpose of parametrized
855
- # subclassing; expected to be overriden in the subclasses.
856
- #
857
- def Place
858
- ::YPetri::Place
859
- end
860
-
861
- # Transition class pertinent herein. Provided for the purpose of
862
- # parametrized subclassing; expected to be overriden in the subclasses.
863
- #
864
- def Transition
865
- ::YPetri::Transition
866
- end
867
-
868
- # Net class pertinent herein. Provided for the purpose of parametrized
869
- # subclassing; expected to be overriden in the subclasses.
870
- #
871
- def Net
872
- ::YPetri::Net
873
- end
874
-
875
- # Presents Place instance specified by the argument.
876
- #
877
- def place instance_identifier
878
- Place().instance( instance_identifier )
879
- end
880
-
881
- # Presents Transition instance specified by the argument.
882
- #
883
- def transition instance_identifier
884
- Transition().instance( instance_identifier )
885
- end
886
-
887
- # Presents Net instance specified by the argument.
888
- #
889
- def net instance_identifier
890
- Net().instance( instance_identifier )
891
- end
892
- end # class Transition
893
- end # module YPetri
445
+ # Conversion to a string.
446
+ #
447
+ def to_s
448
+ "#<Transition: %s >" %
449
+ "#{name.nil? ? '' : '%s ' % name }(#{basic_type}%s)%s" %
450
+ [ "#{assignment_action? ? ' Assign.' : ''}",
451
+ "#{name.nil? ? ' id:%s' % object_id : ''}" ]
452
+ end
453
+ end # class YPetri::Transition