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.
- checksums.yaml +4 -4
- data/lib/y_petri/dependency_injection.rb +45 -0
- data/lib/y_petri/manipulator/petri_net_related_methods.rb +26 -7
- data/lib/y_petri/manipulator/simulation_related_methods.rb +4 -4
- data/lib/y_petri/net.rb +30 -21
- data/lib/y_petri/place/arcs.rb +96 -0
- data/lib/y_petri/place/guard.rb +122 -0
- data/lib/y_petri/place.rb +89 -132
- data/lib/y_petri/simulation.rb +191 -168
- data/lib/y_petri/timed_simulation.rb +29 -20
- data/lib/y_petri/transition/arcs.rb +51 -0
- data/lib/y_petri/transition/cocking.rb +32 -0
- data/lib/y_petri/transition/constructor_syntax.rb +378 -0
- data/lib/y_petri/transition.rb +391 -831
- data/lib/y_petri/version.rb +1 -1
- data/lib/y_petri/workspace/parametrized_subclassing.rb +8 -13
- data/lib/y_petri/workspace/simulation_related_methods.rb +13 -11
- data/lib/y_petri.rb +8 -3
- data/test/place_test.rb +83 -0
- data/test/transition_test.rb +325 -0
- data/test/y_petri_test.rb +15 -410
- metadata +12 -2
data/lib/y_petri/transition.rb
CHANGED
@@ -1,8 +1,26 @@
|
|
1
|
-
#
|
1
|
+
# -*- coding: utf-8 -*-
|
2
2
|
|
3
|
-
|
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
|
-
#
|
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
|
-
#
|
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*
|
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
|
-
#
|
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
|
-
#
|
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.
|
41
|
-
# effect can be achieved by subtracting
|
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
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
#
|
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
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
#
|
106
|
-
#
|
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
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
#
|
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
|
-
|
138
|
-
|
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
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
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
|
-
|
425
|
-
|
426
|
-
|
427
|
-
codomain.map { 0 }
|
428
|
-
end
|
170
|
+
# Stoichiometry (implies that the transition is stoichiometric).
|
171
|
+
#
|
172
|
+
attr_reader :stoichiometry
|
429
173
|
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
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
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
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
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
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
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
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
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
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
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
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
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
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
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
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
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
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
|
-
|
605
|
-
|
606
|
-
|
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
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
649
|
-
|
650
|
-
.delete_if{ |place, coeff| coeff > 0 }.keys
|
311
|
+
rslt = action_closure.( *domain_marking )
|
312
|
+
stoichiometry.map { |coeff| rslt * coeff }
|
651
313
|
else
|
652
|
-
|
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
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
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
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
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
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
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
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
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
|
-
|
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
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
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
|
-
|
843
|
-
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
|
849
|
-
|
850
|
-
|
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
|