y_petri 2.1.3 → 2.1.6
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/agent/petri_net_related.rb +25 -5
- data/lib/y_petri/agent/selection.rb +12 -10
- data/lib/y_petri/agent/simulation_related.rb +14 -58
- data/lib/y_petri/agent.rb +15 -17
- data/lib/y_petri/core/timed/euler.rb +13 -15
- data/lib/y_petri/core/timed/pseudo_euler.rb +22 -24
- data/lib/y_petri/core/timed/quasi_euler.rb +15 -17
- data/lib/y_petri/core/timed.rb +42 -44
- data/lib/y_petri/core/timeless/pseudo_euler.rb +12 -14
- data/lib/y_petri/core/timeless.rb +10 -7
- data/lib/y_petri/core.rb +3 -3
- data/lib/y_petri/dsl.rb +46 -46
- data/lib/y_petri/fixed_assets.rb +8 -0
- data/lib/y_petri/net/data_set.rb +238 -0
- data/lib/y_petri/net/own_state.rb +63 -0
- data/lib/y_petri/net/state/feature/delta.rb +98 -71
- data/lib/y_petri/net/state/feature/firing.rb +51 -54
- data/lib/y_petri/net/state/feature/flux.rb +51 -55
- data/lib/y_petri/net/state/feature/gradient.rb +55 -59
- data/lib/y_petri/net/state/feature/marking.rb +55 -59
- data/lib/y_petri/net/state/feature.rb +65 -67
- data/lib/y_petri/net/state/features/record.rb +150 -43
- data/lib/y_petri/net/state/features.rb +252 -96
- data/lib/y_petri/net/state.rb +114 -106
- data/lib/y_petri/net/visualization.rb +3 -2
- data/lib/y_petri/net.rb +29 -24
- data/lib/y_petri/place/arcs.rb +3 -3
- data/lib/y_petri/place/guard.rb +35 -117
- data/lib/y_petri/place/guarded.rb +86 -0
- data/lib/y_petri/place.rb +6 -3
- data/lib/y_petri/simulation/element_representation.rb +2 -2
- data/lib/y_petri/simulation/elements.rb +3 -1
- data/lib/y_petri/simulation/feature_set.rb +3 -1
- data/lib/y_petri/simulation/marking_vector.rb +3 -1
- data/lib/y_petri/simulation/place_mapping.rb +3 -1
- data/lib/y_petri/simulation/places.rb +1 -1
- data/lib/y_petri/simulation/recorder.rb +60 -54
- data/lib/y_petri/simulation/timed/recorder.rb +12 -1
- data/lib/y_petri/simulation/timed.rb +173 -172
- data/lib/y_petri/simulation/transitions/access.rb +97 -29
- data/lib/y_petri/simulation.rb +11 -9
- data/lib/y_petri/transition/{assignment.rb → A.rb} +2 -2
- data/lib/y_petri/transition/{timed.rb → T.rb} +2 -2
- data/lib/y_petri/transition/arcs.rb +3 -3
- data/lib/y_petri/transition/cocking.rb +3 -3
- data/lib/y_petri/transition/{init.rb → construction_convenience.rb} +6 -53
- data/lib/y_petri/transition/{ordinary_timeless.rb → t.rb} +2 -2
- data/lib/y_petri/transition/type.rb +103 -0
- data/lib/y_petri/transition/type_information.rb +103 -0
- data/lib/y_petri/transition/types.rb +107 -0
- data/lib/y_petri/transition/usable_without_world.rb +14 -0
- data/lib/y_petri/transition.rb +87 -101
- data/lib/y_petri/version.rb +1 -1
- data/lib/y_petri/world/dependency.rb +30 -28
- data/lib/y_petri/world.rb +10 -8
- data/test/acceptance/basic_usage_test.rb +3 -3
- data/test/acceptance/simulation_test.rb +3 -3
- data/test/acceptance/simulation_with_physical_units_test.rb +2 -2
- data/test/acceptance/token_game_test.rb +2 -2
- data/test/acceptance/visualization_test.rb +3 -3
- data/test/acceptance_tests.rb +2 -2
- data/test/agent_test.rb +1 -1
- data/test/net_test.rb +41 -17
- data/test/place_test.rb +1 -1
- data/test/simulation_test.rb +39 -39
- data/test/transition_test.rb +1 -1
- data/test/world_test.rb +1 -1
- data/test/y_petri_test.rb +1 -1
- metadata +13 -8
- data/lib/y_petri/net/state/features/dataset.rb +0 -135
- data/lib/y_petri/transition/construction.rb +0 -311
@@ -1,311 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
|
3
|
-
# Constructor syntax aspect of a transition. Large part of the functionality
|
4
|
-
# of the Transition class is the convenient constructor syntax.
|
5
|
-
#
|
6
|
-
class YPetri::Transition
|
7
|
-
# Transition class represents many different kinds of Petri net transitions.
|
8
|
-
# It makes the constructor syntax a bit more polymorphic. The type of the
|
9
|
-
# transition to construct is mostly inferred from the constructor arguments.
|
10
|
-
#
|
11
|
-
# Mandatorily, the constructor will always need a way to determine the domain
|
12
|
-
# (upstream arcs) and codomain (downstream arcs) of the transition. Also, the
|
13
|
-
# constructor must have a way to determine the transition's action. This is
|
14
|
-
# best explained by examples -- let us have 3 places A, B, C, for whe we will
|
15
|
-
# create different kinds of transitions:
|
16
|
-
#
|
17
|
-
#
|
18
|
-
# ==== TS (timed stoichiometric)
|
19
|
-
#
|
20
|
-
# Rate closure and stoichiometry has to be supplied. Rate closure arity should
|
21
|
-
# correspond to the domain size. Return arity should be 1 (to be multiplied by
|
22
|
-
# the stoichiometry vector, as in all other stoichiometric transitions).
|
23
|
-
#
|
24
|
-
# Transition.new stoichiometry: { A: -1, B: 1 },
|
25
|
-
# rate: -> a { a * 0.5 }
|
26
|
-
#
|
27
|
-
#
|
28
|
-
# ==== Ts (timed nonstoichiometric)
|
29
|
-
#
|
30
|
-
# Rate closure has to be supplied, whose arity should match the domain, and
|
31
|
-
# output arity codomain.
|
32
|
-
#
|
33
|
-
# ==== tS (timeless stoichiometric)
|
34
|
-
#
|
35
|
-
# Stoichiometry has to be supplied, action closure is optional. If supplied,
|
36
|
-
# its return arity should be 1 (to be multiplied by the stoichiometry vector).
|
37
|
-
#
|
38
|
-
# ==== ts transitions (timeless nonstoichiometric)
|
39
|
-
#
|
40
|
-
# Action closure is expected with return arity equal to the codomain size:
|
41
|
-
#
|
42
|
-
# Transition.new upstream_arcs: [A, C], downstream_arcs: [A, B],
|
43
|
-
# action_closure: proc { |m, x|
|
44
|
-
# if x > 0 then [-(m / 2), (m / 2)]
|
45
|
-
# else [1, 0] end
|
46
|
-
# }
|
47
|
-
#
|
48
|
-
def initialize *args, &block
|
49
|
-
check_in_arguments *args, &block # the big job
|
50
|
-
extend timed? ? Timed : assignment? ? Assignment : OrdinaryTimeless
|
51
|
-
inform_upstream_places # that they have been connected
|
52
|
-
inform_downstream_places # that they have been connected
|
53
|
-
uncock # transitions initialize uncocked
|
54
|
-
end
|
55
|
-
|
56
|
-
private
|
57
|
-
|
58
|
-
# Checking in the arguments supplied to #initialize looks like a big job.
|
59
|
-
# I won't contest to that, but let us not, that it is basically nothing
|
60
|
-
# else then defining the duck type of the input argument collection.
|
61
|
-
# TypeError is therefore raised if invalid collection has been supplied.
|
62
|
-
#
|
63
|
-
def check_in_arguments **nn, &block
|
64
|
-
nn.update( action: block ) if block_given?
|
65
|
-
nn.may_have :domain, syn!: [ :domain_arcs, :domain_places,
|
66
|
-
:upstream, :upstream_arcs, :upstream_places ]
|
67
|
-
nn.may_have :codomain, syn!: [ :codomain_arcs, :codomain_places,
|
68
|
-
:downstream,
|
69
|
-
:downstream_arcs, :downstream_places,
|
70
|
-
:action_arcs ]
|
71
|
-
nn.may_have :rate, syn!: [ :rate_closure, :propensity,
|
72
|
-
:propensity_closure ]
|
73
|
-
nn.may_have :action, syn!: :action_closure
|
74
|
-
nn.may_have :stoichiometry, syn!: [ :stoichio, :s ]
|
75
|
-
nn.may_have :domain_guard
|
76
|
-
nn.may_have :codomain_guard
|
77
|
-
|
78
|
-
# If the rate was given, the transition is timed:
|
79
|
-
@timed = nn.has? :rate
|
80
|
-
|
81
|
-
# If stoichiometry was given, the transition is stoichiometric:
|
82
|
-
@stoichiometric = nn.has? :stoichiometry
|
83
|
-
|
84
|
-
# Downstream description involves the codomain, and the stochiometry
|
85
|
-
# (for stoichiometric transitions only):
|
86
|
-
if stoichiometric? then
|
87
|
-
@codomain, @stoichiometry = __downstream_for_S__( **nn )
|
88
|
-
else
|
89
|
-
@codomain = __downstream_for_s__( **nn )
|
90
|
-
end
|
91
|
-
|
92
|
-
# Check in the domain first, :missing symbol may be returned if the user
|
93
|
-
# has not supplied the domaing (the constructor will attempt to guessf it
|
94
|
-
# automatically).
|
95
|
-
@domain = __domain__( **nn )
|
96
|
-
|
97
|
-
# Upstream description involves the domain and the rate/action closure.
|
98
|
-
# Also, :missing domain is taken care of here.
|
99
|
-
if timed? then
|
100
|
-
@domain, @rate_closure, @functional = __upstream_for_T__( **nn )
|
101
|
-
else
|
102
|
-
@domain, @action_closure, @functional = __upstream_for_t__( **nn )
|
103
|
-
end
|
104
|
-
|
105
|
-
# Optional assignment action:
|
106
|
-
@assignment_action = __assignment_action__( **nn )
|
107
|
-
|
108
|
-
# Optional type guards for domain / codomain:
|
109
|
-
@domain_guard, @codomain_guard = __guards__( **nn )
|
110
|
-
end
|
111
|
-
|
112
|
-
# Validates that the supplied collection consists only of places of
|
113
|
-
# correct type. Second optional argument customizes the error message.
|
114
|
-
#
|
115
|
-
def sanitize_place_collection place_collection, what_is_collection=nil
|
116
|
-
c = what_is_collection ? what_is_collection.capitalize : "Collection"
|
117
|
-
Array( place_collection ).map do |pl_id|
|
118
|
-
begin
|
119
|
-
place( pl_id )
|
120
|
-
rescue NameError
|
121
|
-
raise TypeError, "#{c} member #{pl_id} does not specify a valid place!"
|
122
|
-
end
|
123
|
-
end.aT what_is_collection, "not contain duplicate places" do |coll|
|
124
|
-
coll == coll.uniq
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
# Private method, part of #initialize argument checking-in.
|
129
|
-
#
|
130
|
-
def __domain__( **oo )
|
131
|
-
if oo.has? :domain then
|
132
|
-
sanitize_place_collection( oo[:domain], "supplied domain" )
|
133
|
-
else
|
134
|
-
if stoichiometric? then
|
135
|
-
# take arcs with non-positive stoichiometry coefficients
|
136
|
-
Hash[ [ @codomain, @stoichiometry ].transpose ]
|
137
|
-
.delete_if{ |_place, coeff| coeff > 0 }.keys
|
138
|
-
else
|
139
|
-
:missing
|
140
|
-
# Barring the caller's error, missing domain can mean:
|
141
|
-
# 1. empty domain
|
142
|
-
# 2. domain == codomain
|
143
|
-
# This will be figured later by the rate/action closure arity.
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
# Private method, part of the init process for timed transitions. Also takes
|
149
|
-
# care for :missing domain, if :missing.
|
150
|
-
#
|
151
|
-
def __upstream_for_T__( **oo )
|
152
|
-
_domain = domain # this method may modify domain
|
153
|
-
_functional = true # T transitions are implicitly functional
|
154
|
-
fail ArgumentError, "Rate and action collision!" if oo.has? :action
|
155
|
-
# Let's figure the rate closure now.
|
156
|
-
λ = oo[:rate]
|
157
|
-
if λ.is_a? Proc then
|
158
|
-
# Solve :missing domain:
|
159
|
-
_domain = λ.arity == 0 ? [] : codomain if domain == :missing
|
160
|
-
# Validate arity:
|
161
|
-
msg = "Rate closure arity (#{λ.arity}) > domain (#{domain.size})!"
|
162
|
-
fail TypeError, msg if λ.arity.abs > domain.size
|
163
|
-
else # not a Proc, must guess user's intent
|
164
|
-
λ = if stoichiometric? then # standard mass action
|
165
|
-
msg = "With numeric rate, domain must not be given!"
|
166
|
-
fail TypeError, msg if oo.has? :domain
|
167
|
-
__standard_mass_action__( λ )
|
168
|
-
else # constant closure
|
169
|
-
msg = "With numeric rate and no stoichio., codomain size must be 1!"
|
170
|
-
fail TypeError, msg unless codomain.size == 1
|
171
|
-
_domain = [] if domain == :missing # Missing domain is OK here,
|
172
|
-
# But in case it was supplied explicitly, it must be empty.
|
173
|
-
msg = "Rate is a number, but domain is non-empty!"
|
174
|
-
fail TypeError, msg unless domain.empty? if oo.has? :domain
|
175
|
-
-> { λ } # the closure itself
|
176
|
-
end
|
177
|
-
end
|
178
|
-
return _domain, λ, _functional
|
179
|
-
end
|
180
|
-
|
181
|
-
# Private method, part of the init process when :rate is not given. Also
|
182
|
-
# takes care for missing domain (@domain == :missing).
|
183
|
-
#
|
184
|
-
def __upstream_for_t__( **oo )
|
185
|
-
_domain = domain # this method may modify domain
|
186
|
-
_functional = true
|
187
|
-
# Was action given explicitly?
|
188
|
-
if oo.has? :action then
|
189
|
-
λ = oo[:action].aT_is_a Proc, "supplied action named argument"
|
190
|
-
# Time to worry about the domain_missing, guess the user's intention:
|
191
|
-
if domain == :missing then
|
192
|
-
_domain = ( λ.arity == 0 ? [] : codomain )
|
193
|
-
else
|
194
|
-
msg = "Action closure arity (#{λ.arity}) > domain size (#{domain.size})!"
|
195
|
-
fail TypeError, msg if λ.arity.abs > domain.size
|
196
|
-
end
|
197
|
-
else # functionless transition
|
198
|
-
_functional = false
|
199
|
-
λ = -> { 1 }
|
200
|
-
msg = "Stoichiometry is compulsory, if no rate/action was supplied."
|
201
|
-
fail ArgumentError, msg unless S?
|
202
|
-
_domain = [] # in any case, the domain is empty
|
203
|
-
end
|
204
|
-
return _domain, λ, _functional
|
205
|
-
end
|
206
|
-
|
207
|
-
# Default rate closure for SR transitions whose rate is hinted as a number.
|
208
|
-
#
|
209
|
-
def __standard_mass_action__( num )
|
210
|
-
# assume standard mass-action law
|
211
|
-
nonpositive_coeffs = stoichiometry.select { |coeff| coeff <= 0 }
|
212
|
-
# the closure takes markings of the domain as its arguments
|
213
|
-
-> *markings do
|
214
|
-
nonpositive_coeffs.size.times.reduce num do |acc, i|
|
215
|
-
marking, coeff = markings[ i ], nonpositive_coeffs[ i ]
|
216
|
-
# Stoichiometry coefficients equal to zero are taken to indicate
|
217
|
-
# plain factors, assuming that if these places were not involved
|
218
|
-
# in the transition at all, the user would not be mentioning them.
|
219
|
-
case coeff
|
220
|
-
when 0, -1 then marking * acc
|
221
|
-
else marking ** -coeff end
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
# Private method, checking in downstream specification from the argument
|
227
|
-
# field for stoichiometric transition.
|
228
|
-
#
|
229
|
-
def __downstream_for_S__( **oo )
|
230
|
-
codomain, stoichio =
|
231
|
-
case oo[:stoichiometry]
|
232
|
-
when Hash then
|
233
|
-
# contains pairs { codomain place => stoichiometry coefficient }
|
234
|
-
msg = "With hash-type stoichiometry, :codomain must not be given!"
|
235
|
-
fail ArgumentError, msg if oo.has? :codomain
|
236
|
-
oo[:stoichiometry].each_with_object [[], []] do |(cd_pl, coeff), memo|
|
237
|
-
memo[0] << cd_pl
|
238
|
-
memo[1] << coeff
|
239
|
-
end
|
240
|
-
else
|
241
|
-
# array of stoichiometry coefficients
|
242
|
-
msg = "With array-type stoichiometry, :codomain must be given!"
|
243
|
-
fail ArgumentError unless oo.has? :codomain
|
244
|
-
[ oo[:codomain], Array( oo[:stoichiometry] ) ]
|
245
|
-
end
|
246
|
-
# enforce that stoichiometry is a collection of numbers
|
247
|
-
return sanitize_place_collection( codomain, "supplied codomain" ),
|
248
|
-
stoichio.aT_all_numeric( "supplied stoichiometry" )
|
249
|
-
end
|
250
|
-
|
251
|
-
# Private method, checking in downstream specification from the argument
|
252
|
-
# field for nonstoichiometric transition.
|
253
|
-
#
|
254
|
-
def __downstream_for_s__( **oo )
|
255
|
-
# codomain must be explicitly given - no way around it:
|
256
|
-
fail ArgumentError, "For non-stoichiometric transitions, :codomain " +
|
257
|
-
"argument is compulsory." unless oo.has? :codomain
|
258
|
-
return sanitize_place_collection( oo[:codomain], "supplied codomain" )
|
259
|
-
end
|
260
|
-
|
261
|
-
# Private method, part of #initialize argument checking-in.
|
262
|
-
#
|
263
|
-
def __assignment_action__( **oo )
|
264
|
-
if oo.has? :assignment_action, syn!: [ :assignment, :assign, :A ] then
|
265
|
-
if timed? then
|
266
|
-
false.tap do
|
267
|
-
msg = "Timed transitions may not have assignment action!"
|
268
|
-
raise TypeError, msg if oo[:assignment_action]
|
269
|
-
end
|
270
|
-
else oo[:assignment_action] end # only timeless transitions are eligible
|
271
|
-
else false end # the default value
|
272
|
-
end
|
273
|
-
|
274
|
-
# Private method, part of #initialize argument checking-in
|
275
|
-
#
|
276
|
-
def __guards__( **oo )
|
277
|
-
if oo.has? :domain_guard then
|
278
|
-
oo[:domain_guard].aT_is_a Proc, "supplied domain guard"
|
279
|
-
else
|
280
|
-
place_guards = domain_places.map &:guard
|
281
|
-
-> dm do # constructing the default domain guard
|
282
|
-
fails = [domain, dm, place_guards].transpose.map { |pl, m, guard|
|
283
|
-
[ pl, m, begin; guard.( m ); true; rescue YPetri::GuardError; false end ]
|
284
|
-
}.reduce [] do |memo, triple| memo << triple unless triple[2] end
|
285
|
-
# TODO: Watch "Exceptional Ruby" video by Avdi Grimm.
|
286
|
-
unless fails.size == 0
|
287
|
-
fail YPetri::GuardError, "Domain guard of #{self} rejects marking " +
|
288
|
-
if fails.size == 1 then
|
289
|
-
p, m, _ = fails[0]
|
290
|
-
"#{m} of place #{p.name || p.object_id}!"
|
291
|
-
else
|
292
|
-
"of the following places: %s!" %
|
293
|
-
Hash[ fails.map { |pl, m, _| [pl.name || pl.object_id, m] } ]
|
294
|
-
end
|
295
|
-
end
|
296
|
-
end
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
# Informs upstream places that they have been connected to this transition.
|
301
|
-
#
|
302
|
-
def inform_upstream_places
|
303
|
-
upstream_places.each { |p| p.send :register_downstream_transition, self }
|
304
|
-
end
|
305
|
-
|
306
|
-
# Informs downstream places that they are connected to this transition.
|
307
|
-
#
|
308
|
-
def inform_downstream_places
|
309
|
-
downstream_places.each { |p| p.send :register_upstream_transition, self }
|
310
|
-
end
|
311
|
-
end # class YPetri::Transition
|