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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/lib/y_petri/agent/petri_net_related.rb +25 -5
  3. data/lib/y_petri/agent/selection.rb +12 -10
  4. data/lib/y_petri/agent/simulation_related.rb +14 -58
  5. data/lib/y_petri/agent.rb +15 -17
  6. data/lib/y_petri/core/timed/euler.rb +13 -15
  7. data/lib/y_petri/core/timed/pseudo_euler.rb +22 -24
  8. data/lib/y_petri/core/timed/quasi_euler.rb +15 -17
  9. data/lib/y_petri/core/timed.rb +42 -44
  10. data/lib/y_petri/core/timeless/pseudo_euler.rb +12 -14
  11. data/lib/y_petri/core/timeless.rb +10 -7
  12. data/lib/y_petri/core.rb +3 -3
  13. data/lib/y_petri/dsl.rb +46 -46
  14. data/lib/y_petri/fixed_assets.rb +8 -0
  15. data/lib/y_petri/net/data_set.rb +238 -0
  16. data/lib/y_petri/net/own_state.rb +63 -0
  17. data/lib/y_petri/net/state/feature/delta.rb +98 -71
  18. data/lib/y_petri/net/state/feature/firing.rb +51 -54
  19. data/lib/y_petri/net/state/feature/flux.rb +51 -55
  20. data/lib/y_petri/net/state/feature/gradient.rb +55 -59
  21. data/lib/y_petri/net/state/feature/marking.rb +55 -59
  22. data/lib/y_petri/net/state/feature.rb +65 -67
  23. data/lib/y_petri/net/state/features/record.rb +150 -43
  24. data/lib/y_petri/net/state/features.rb +252 -96
  25. data/lib/y_petri/net/state.rb +114 -106
  26. data/lib/y_petri/net/visualization.rb +3 -2
  27. data/lib/y_petri/net.rb +29 -24
  28. data/lib/y_petri/place/arcs.rb +3 -3
  29. data/lib/y_petri/place/guard.rb +35 -117
  30. data/lib/y_petri/place/guarded.rb +86 -0
  31. data/lib/y_petri/place.rb +6 -3
  32. data/lib/y_petri/simulation/element_representation.rb +2 -2
  33. data/lib/y_petri/simulation/elements.rb +3 -1
  34. data/lib/y_petri/simulation/feature_set.rb +3 -1
  35. data/lib/y_petri/simulation/marking_vector.rb +3 -1
  36. data/lib/y_petri/simulation/place_mapping.rb +3 -1
  37. data/lib/y_petri/simulation/places.rb +1 -1
  38. data/lib/y_petri/simulation/recorder.rb +60 -54
  39. data/lib/y_petri/simulation/timed/recorder.rb +12 -1
  40. data/lib/y_petri/simulation/timed.rb +173 -172
  41. data/lib/y_petri/simulation/transitions/access.rb +97 -29
  42. data/lib/y_petri/simulation.rb +11 -9
  43. data/lib/y_petri/transition/{assignment.rb → A.rb} +2 -2
  44. data/lib/y_petri/transition/{timed.rb → T.rb} +2 -2
  45. data/lib/y_petri/transition/arcs.rb +3 -3
  46. data/lib/y_petri/transition/cocking.rb +3 -3
  47. data/lib/y_petri/transition/{init.rb → construction_convenience.rb} +6 -53
  48. data/lib/y_petri/transition/{ordinary_timeless.rb → t.rb} +2 -2
  49. data/lib/y_petri/transition/type.rb +103 -0
  50. data/lib/y_petri/transition/type_information.rb +103 -0
  51. data/lib/y_petri/transition/types.rb +107 -0
  52. data/lib/y_petri/transition/usable_without_world.rb +14 -0
  53. data/lib/y_petri/transition.rb +87 -101
  54. data/lib/y_petri/version.rb +1 -1
  55. data/lib/y_petri/world/dependency.rb +30 -28
  56. data/lib/y_petri/world.rb +10 -8
  57. data/test/acceptance/basic_usage_test.rb +3 -3
  58. data/test/acceptance/simulation_test.rb +3 -3
  59. data/test/acceptance/simulation_with_physical_units_test.rb +2 -2
  60. data/test/acceptance/token_game_test.rb +2 -2
  61. data/test/acceptance/visualization_test.rb +3 -3
  62. data/test/acceptance_tests.rb +2 -2
  63. data/test/agent_test.rb +1 -1
  64. data/test/net_test.rb +41 -17
  65. data/test/place_test.rb +1 -1
  66. data/test/simulation_test.rb +39 -39
  67. data/test/transition_test.rb +1 -1
  68. data/test/world_test.rb +1 -1
  69. data/test/y_petri_test.rb +1 -1
  70. metadata +13 -8
  71. data/lib/y_petri/net/state/features/dataset.rb +0 -135
  72. 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