y_petri 2.0.3 → 2.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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