y_petri 2.0.15 → 2.1.3
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/{manipulator → agent}/hash_key_pointer.rb +2 -2
- data/lib/y_petri/agent/petri_net_related.rb +115 -0
- data/lib/y_petri/{manipulator → agent}/selection.rb +2 -1
- data/lib/y_petri/{manipulator/simulation_related_methods.rb → agent/simulation_related.rb} +93 -110
- data/lib/y_petri/agent.rb +22 -0
- data/lib/y_petri/core/timed/euler.rb +20 -0
- data/lib/y_petri/core/timed/pseudo_euler.rb +31 -0
- data/lib/y_petri/core/timed/quasi_euler.rb +23 -0
- data/lib/y_petri/core/timed.rb +70 -0
- data/lib/y_petri/core/timeless/pseudo_euler.rb +20 -0
- data/lib/y_petri/core/timeless.rb +12 -0
- data/lib/y_petri/core.rb +100 -0
- data/lib/y_petri/dsl.rb +66 -0
- data/lib/y_petri/fixed_assets.rb +7 -0
- data/lib/y_petri/net/element_access.rb +239 -0
- data/lib/y_petri/net/state/feature/delta.rb +88 -0
- data/lib/y_petri/net/state/feature/firing.rb +57 -0
- data/lib/y_petri/net/state/feature/flux.rb +58 -0
- data/lib/y_petri/net/state/feature/gradient.rb +75 -0
- data/lib/y_petri/net/state/feature/marking.rb +62 -0
- data/lib/y_petri/net/state/feature.rb +79 -0
- data/lib/y_petri/net/state/features/dataset.rb +135 -0
- data/lib/y_petri/net/state/features/record.rb +50 -0
- data/lib/y_petri/net/state/features.rb +126 -0
- data/lib/y_petri/net/state.rb +121 -0
- data/lib/y_petri/net/timed.rb +8 -0
- data/lib/y_petri/net/visualization.rb +3 -3
- data/lib/y_petri/net.rb +73 -77
- data/lib/y_petri/place.rb +8 -3
- data/lib/y_petri/simulation/dependency.rb +107 -0
- data/lib/y_petri/simulation/element_representation.rb +20 -0
- data/lib/y_petri/simulation/elements/access.rb +57 -0
- data/lib/y_petri/simulation/elements.rb +45 -0
- data/lib/y_petri/simulation/feature_set.rb +21 -0
- data/lib/y_petri/simulation/initial_marking/access.rb +55 -0
- data/lib/y_petri/simulation/initial_marking.rb +15 -0
- data/lib/y_petri/simulation/marking_clamps/access.rb +34 -0
- data/lib/y_petri/simulation/marking_clamps.rb +18 -0
- data/lib/y_petri/simulation/marking_vector/access.rb +106 -0
- data/lib/y_petri/simulation/marking_vector.rb +156 -0
- data/lib/y_petri/simulation/matrix.rb +64 -0
- data/lib/y_petri/simulation/place_mapping.rb +62 -0
- data/lib/y_petri/simulation/place_representation.rb +74 -0
- data/lib/y_petri/simulation/places/access.rb +121 -0
- data/lib/y_petri/simulation/places/clamped.rb +8 -0
- data/lib/y_petri/simulation/places/free.rb +8 -0
- data/lib/y_petri/simulation/places/types.rb +25 -0
- data/lib/y_petri/simulation/places.rb +41 -0
- data/lib/y_petri/simulation/recorder.rb +54 -0
- data/lib/y_petri/simulation/timed/recorder.rb +53 -0
- data/lib/y_petri/simulation/timed.rb +161 -261
- data/lib/y_petri/simulation/timeless/recorder.rb +25 -0
- data/lib/y_petri/simulation/timeless.rb +35 -0
- data/lib/y_petri/simulation/transition_representation/A.rb +58 -0
- data/lib/y_petri/simulation/transition_representation/S.rb +45 -0
- data/lib/y_petri/simulation/transition_representation/T.rb +80 -0
- data/lib/y_petri/simulation/transition_representation/TS.rb +46 -0
- data/lib/y_petri/simulation/transition_representation/Ts.rb +32 -0
- data/lib/y_petri/simulation/transition_representation/a.rb +30 -0
- data/lib/y_petri/simulation/transition_representation/s.rb +29 -0
- data/lib/y_petri/simulation/transition_representation/t.rb +37 -0
- data/lib/y_petri/simulation/transition_representation/tS.rb +38 -0
- data/lib/y_petri/simulation/transition_representation/ts.rb +32 -0
- data/lib/y_petri/simulation/transition_representation/types.rb +62 -0
- data/lib/y_petri/simulation/transition_representation.rb +79 -0
- data/lib/y_petri/simulation/transitions/A.rb +40 -0
- data/lib/y_petri/simulation/transitions/S.rb +24 -0
- data/lib/y_petri/simulation/transitions/T.rb +34 -0
- data/lib/y_petri/simulation/transitions/TS.rb +57 -0
- data/lib/y_petri/simulation/transitions/Ts.rb +60 -0
- data/lib/y_petri/simulation/transitions/a.rb +8 -0
- data/lib/y_petri/simulation/transitions/access.rb +186 -0
- data/lib/y_petri/simulation/transitions/s.rb +9 -0
- data/lib/y_petri/simulation/transitions/t.rb +22 -0
- data/lib/y_petri/simulation/transitions/tS.rb +55 -0
- data/lib/y_petri/simulation/transitions/ts.rb +58 -0
- data/lib/y_petri/simulation/transitions/types.rb +98 -0
- data/lib/y_petri/simulation/transitions.rb +21 -0
- data/lib/y_petri/simulation.rb +176 -781
- data/lib/y_petri/transition/assignment.rb +7 -5
- data/lib/y_petri/transition/construction.rb +119 -187
- data/lib/y_petri/transition/init.rb +311 -0
- data/lib/y_petri/transition/ordinary_timeless.rb +8 -6
- data/lib/y_petri/transition/timed.rb +11 -18
- data/lib/y_petri/transition.rb +104 -132
- data/lib/y_petri/version.rb +1 -1
- data/lib/y_petri/world/dependency.rb +40 -0
- data/lib/y_petri/world/petri_net_related.rb +61 -0
- data/lib/y_petri/{workspace/simulation_related_methods.rb → world/simulation_related.rb} +42 -49
- data/lib/y_petri/world.rb +27 -0
- data/lib/y_petri.rb +47 -99
- data/test/{manipulator_test.rb → agent_test.rb} +19 -17
- data/{lib/y_petri → test/examples}/demonstrator.rb +0 -0
- data/{lib/y_petri → test/examples}/demonstrator_2.rb +1 -0
- data/{lib/y_petri → test/examples}/demonstrator_3.rb +0 -0
- data/{lib/y_petri → test/examples}/demonstrator_4.rb +0 -0
- data/test/examples/example_2.rb +16 -0
- data/test/{manual_examples.rb → examples/manual_examples.rb} +0 -0
- data/test/net_test.rb +126 -121
- data/test/place_test.rb +1 -1
- data/test/sim_test +565 -0
- data/test/simulation_test.rb +338 -264
- data/test/transition_test.rb +77 -174
- data/test/world_mock.rb +12 -0
- data/test/{workspace_test.rb → world_test.rb} +19 -20
- data/test/y_petri_test.rb +4 -5
- metadata +101 -26
- data/lib/y_petri/dependency_injection.rb +0 -45
- data/lib/y_petri/manipulator/petri_net_related_methods.rb +0 -74
- data/lib/y_petri/manipulator.rb +0 -20
- data/lib/y_petri/net/selections.rb +0 -209
- data/lib/y_petri/simulation/collections.rb +0 -460
- data/lib/y_petri/workspace/parametrized_subclassing.rb +0 -22
- data/lib/y_petri/workspace/petri_net_related_methods.rb +0 -88
- data/lib/y_petri/workspace.rb +0 -16
- data/test/timed_simulation_test.rb +0 -153
data/lib/y_petri/simulation.rb
CHANGED
@@ -1,637 +1,205 @@
|
|
1
1
|
#encoding: utf-8
|
2
2
|
|
3
|
-
require_relative 'simulation/
|
3
|
+
require_relative 'simulation/matrix'
|
4
|
+
require_relative 'simulation/dependency'
|
5
|
+
require_relative 'simulation/element_representation'
|
6
|
+
require_relative 'simulation/elements'
|
7
|
+
require_relative 'simulation/elements/access'
|
8
|
+
require_relative 'simulation/place_representation'
|
9
|
+
require_relative 'simulation/places'
|
10
|
+
require_relative 'simulation/places/access'
|
11
|
+
require_relative 'simulation/transition_representation'
|
12
|
+
require_relative 'simulation/transitions'
|
13
|
+
require_relative 'simulation/transitions/access'
|
14
|
+
require_relative 'simulation/place_mapping'
|
15
|
+
require_relative 'simulation/marking_clamps'
|
16
|
+
require_relative 'simulation/marking_clamps/access'
|
17
|
+
require_relative 'simulation/initial_marking'
|
18
|
+
require_relative 'simulation/initial_marking/access'
|
19
|
+
require_relative 'simulation/marking_vector'
|
20
|
+
require_relative 'simulation/marking_vector/access'
|
21
|
+
require_relative 'simulation/recorder'
|
22
|
+
require_relative 'simulation/timeless'
|
4
23
|
require_relative 'simulation/timed'
|
5
24
|
|
6
|
-
# Represents a
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
25
|
+
# Represents a Petri net simulation, concerning are the simulation method and
|
26
|
+
# settings, initial values, marking clamps, guards and similar. Its concerns are
|
27
|
+
# are separated from those of the Petri net domain model (existence, naming,
|
28
|
+
# connectivity, functions...). Clamps, guards, initial values etc. <b>do not
|
29
|
+
# belong</b> to the model, although for convenience, places may carry default
|
30
|
+
# initial marking, guards, and clamps for use in the token game. Simulation
|
31
|
+
# instance can also use these if none other are specified.
|
32
|
+
#
|
33
|
+
# A simulation distinguishes between free and clamped places. For free places,
|
34
|
+
# initial marking has to be specified. For clamped places, marking clamps have
|
35
|
+
# to be specified. Both come as hashes:
|
36
|
+
#
|
14
37
|
class YPetri::Simulation
|
15
|
-
include
|
38
|
+
include Places::Access
|
39
|
+
include Transitions::Access
|
40
|
+
include Elements::Access
|
41
|
+
include InitialMarking::Access
|
42
|
+
include MarkingClamps::Access
|
43
|
+
include MarkingVector::Access
|
16
44
|
|
17
|
-
|
18
|
-
SIMULATION_METHODS =
|
19
|
-
[
|
20
|
-
[:pseudo_Euler] # pseudo-timed simulation (like in Cell Illustrator)
|
21
|
-
]
|
22
|
-
DEFAULT_SIMULATION_METHOD = :pseudo_Euler
|
45
|
+
DEFAULT_SETTINGS = -> do { method: :pseudo_euler, guarded: false } end
|
23
46
|
|
24
|
-
|
25
|
-
|
26
|
-
#
|
27
|
-
def default_simulation_method
|
28
|
-
self.class.const_get :DEFAULT_SIMULATION_METHOD
|
29
|
-
end
|
30
|
-
|
31
|
-
attr_reader :method, :guarded, :timed, :net
|
32
|
-
alias guarded? guarded
|
33
|
-
alias timed? timed
|
34
|
-
attr_reader :marking_vector
|
35
|
-
attr_reader :zero_ᴍ, :zero_gradient
|
36
|
-
attr_reader :recording
|
37
|
-
alias :r :recording
|
38
|
-
|
39
|
-
# Stoichiometry matrix for *tS* transitions.
|
40
|
-
#
|
41
|
-
attr_reader :S_tS
|
42
|
-
|
43
|
-
# Stoichiometry matrix for *TSr* transitions.
|
44
|
-
#
|
45
|
-
attr_reader :S_TSr
|
46
|
-
|
47
|
-
# Stoichiometry matrix for *SR* transitions.
|
48
|
-
#
|
49
|
-
attr_reader :S_SR
|
50
|
-
|
51
|
-
# Exposing Δ state closures for ts transitions.
|
52
|
-
#
|
53
|
-
attr_reader :Δ_closures_for_tsa
|
54
|
-
|
55
|
-
# Note: 'a' in 'tsa' is needed because A (assignment) transitions can also be
|
56
|
-
# regarded as a special kind of ts transitions, while they obviously do not
|
57
|
-
# act through Δ state, but rather directly enforce marking of their codomain.
|
58
|
-
#
|
59
|
-
def Δ_if_tsa_fire_once
|
60
|
-
Δ_closures_for_tsa.map( &:call ).reduce( @zero_ᴍ, :+ )
|
61
|
-
end
|
62
|
-
|
63
|
-
# Exposing Δ state closures for Tsr transitions.
|
64
|
-
#
|
65
|
-
attr_reader :Δ_closures_for_Tsr
|
66
|
-
|
67
|
-
# Delta state contribution for Tsr transitions given Δt.
|
68
|
-
#
|
69
|
-
def Δ_Tsr( Δt )
|
70
|
-
Δ_closures_for_Tsr.map { |cl| cl.( Δt ) }.reduce( @zero_ᴍ, :+ )
|
71
|
-
end
|
72
|
-
|
73
|
-
# Exposing action closures for tS transitions.
|
74
|
-
#
|
75
|
-
attr_reader :action_closures_for_tS
|
76
|
-
|
77
|
-
# Action vector for if tS transitions fire once. The closures are called
|
78
|
-
# in their order, but the state update is not performed between the
|
79
|
-
# calls (ie. they fire "simultaneously").
|
80
|
-
#
|
81
|
-
def action_vector_for_tS
|
82
|
-
Matrix.column_vector action_closures_for_tS.map( &:call )
|
83
|
-
end
|
84
|
-
alias ᴀ_tS action_vector_for_tS
|
85
|
-
|
86
|
-
# Action vector if tS transitions fire once, like the previous method.
|
87
|
-
# But by calling this method, the caller asserts that all timeless
|
88
|
-
# transitions in this simulation are stoichiometric (or error is raised).
|
89
|
-
#
|
90
|
-
def action_vector_for_timeless_transitions
|
91
|
-
return action_vector_for_tS if ts_transitions.empty?
|
92
|
-
raise "The simulation also contains nonstoichiometric timeless " +
|
93
|
-
"transitions! Consider using #action_vector_for_tS."
|
94
|
-
end
|
95
|
-
alias action_vector_for_t action_vector_for_timeless_transitions
|
96
|
-
alias ᴀ_t action_vector_for_timeless_transitions
|
97
|
-
|
98
|
-
# Δ state contribution for tS transitions.
|
99
|
-
#
|
100
|
-
def Δ_if_tS_fire_once
|
101
|
-
S_tS() * action_vector_for_tS
|
102
|
-
end
|
103
|
-
|
104
|
-
# Exposing action closures for TSr transitions.
|
105
|
-
#
|
106
|
-
attr_reader :action_closures_for_TSr
|
107
|
-
|
108
|
-
# By calling this method, the caller asserts that all timeless transitions
|
109
|
-
# in this simulation are stoichiometric (or error is raised).
|
110
|
-
#
|
111
|
-
def action_closures_for_Tr
|
112
|
-
return action_closures_for_TSr if self.TSr_transitions.empty?
|
113
|
-
raise "The simulation also contains nonstoichiometric timed rateless " +
|
114
|
-
"transitions! Consider using #action_closures_for_TSr."
|
115
|
-
end
|
116
|
-
|
117
|
-
# Action vector for timed rateless stoichiometric transitions.
|
118
|
-
#
|
119
|
-
def action_vector_for_TSr( Δt )
|
120
|
-
Matrix.column_vector action_closures_for_TSr.map { |c| c.( Δt ) }
|
121
|
-
end
|
122
|
-
alias ᴀ_TSr action_vector_for_TSr
|
123
|
-
|
124
|
-
# Action vector for timed rateless stoichiometric transitions
|
125
|
-
# By calling this method, the caller asserts that all timeless transitions
|
126
|
-
# in this simulation are stoichiometric (or error is raised).
|
127
|
-
#
|
128
|
-
def action_vector_for_Tr( Δt )
|
129
|
-
return action_vector_for_TSr( Δt ) if TSr_transitions().empty?
|
130
|
-
raise "The simulation also contains nonstoichiometric timed rateless " +
|
131
|
-
"transitions! Consider using #action_vector_for_TSr."
|
132
|
-
end
|
133
|
-
alias ᴀ_Tr action_vector_for_Tr
|
134
|
-
|
135
|
-
# State contribution of TSr transitions for the period Δt.
|
136
|
-
#
|
137
|
-
def Δ_TSr( Δt )
|
138
|
-
S_TSr() * action_vector_for_TSr( Δt )
|
139
|
-
end
|
140
|
-
|
141
|
-
# Exposing rate closures for sR transitions.
|
142
|
-
#
|
143
|
-
attr_reader :rate_closures_for_sR
|
144
|
-
|
145
|
-
# By calling this method, the caller asserts that there are no rateless
|
146
|
-
# transitions in the simulation (or error is raised).
|
147
|
-
#
|
148
|
-
def rate_closures_for_nonstoichiometric_transitions
|
149
|
-
return rate_closures_for_sR if r_transitions.empty?
|
150
|
-
raise "The simulation also contains rateless transitions! Consider " +
|
151
|
-
"using #rate_closures_for_sR."
|
152
|
-
end
|
153
|
-
alias rate_closures_for_s rate_closures_for_nonstoichiometric_transitions
|
154
|
-
|
155
|
-
# State differential for sR transitions.
|
156
|
-
#
|
157
|
-
def gradient_for_sR
|
158
|
-
rate_closures_for_sR.map( &:call ).reduce( @zero_gradient, :+ )
|
159
|
-
end
|
160
|
-
|
161
|
-
# State differential for sR transitions as a hash { place_name: ∂ / ∂ᴛ }.
|
162
|
-
#
|
163
|
-
def ∂_sR
|
164
|
-
free_pp :gradient_for_sR
|
165
|
-
end
|
166
|
-
|
167
|
-
# First-order state contribution of sR transitions during Δt.
|
168
|
-
#
|
169
|
-
def Δ_sR( Δt )
|
170
|
-
gradient_for_sR * Δt
|
171
|
-
end
|
172
|
-
|
173
|
-
# Exposing rate closures for SR transitions.
|
174
|
-
#
|
175
|
-
attr_reader :rate_closures_for_SR
|
176
|
-
|
177
|
-
# Rate closures for SR transitions. By calling this method, the caller
|
178
|
-
# asserts that there are no rateless transitions in the simulation
|
179
|
-
# (or error).
|
180
|
-
#
|
181
|
-
def rate_closures_for_stoichiometric_transitions
|
182
|
-
return rate_closures_for_SR if r_transitions.empty?
|
183
|
-
raise "The simulation also contains rateless transitions! Consider " +
|
184
|
-
"using #rate_closures_for_SR"
|
185
|
-
end
|
186
|
-
alias rate_closures_for_S rate_closures_for_stoichiometric_transitions
|
187
|
-
|
188
|
-
# Rate closures for SR transitions. By calling this method, the caller
|
189
|
-
# asserts that there are only SR transitions in the simulation (or error).
|
190
|
-
#
|
191
|
-
def rate_closures
|
192
|
-
return rate_closures_for_S if s_transitions.empty?
|
193
|
-
raise "The simulation contains also nonstoichiometric transitions! " +
|
194
|
-
"Consider using #rate_closures_for_S."
|
195
|
-
end
|
196
|
-
|
197
|
-
# While rateless stoichiometric transitions provide transition's action as
|
198
|
-
# their closure output, SR transitions' closures return flux, which is
|
199
|
-
# ∂action / ∂t. This methods return flux for SR transitions as a column
|
200
|
-
# vector.
|
201
|
-
#
|
202
|
-
def flux_vector_for_SR
|
203
|
-
Matrix.column_vector rate_closures_for_SR.map( &:call )
|
204
|
-
end
|
205
|
-
alias φ_for_SR flux_vector_for_SR
|
206
|
-
|
207
|
-
# Flux vector for a selected collection of SR transitions.
|
208
|
-
#
|
209
|
-
def flux_vector_for *transitions
|
210
|
-
# TODO
|
211
|
-
end
|
212
|
-
alias φ_for flux_vector_for
|
213
|
-
|
214
|
-
# Flux vector for SR transitions. Same as the previous method, but the
|
215
|
-
# caller asserts that there are only SR transitions in the simulation
|
216
|
-
# (or error).
|
217
|
-
#
|
218
|
-
def flux_vector
|
219
|
-
return flux_vector_for_SR if s_transitions.empty? && r_transitions.empty?
|
220
|
-
raise "One may only call this method when all the transitions of the " +
|
221
|
-
"simulation are SR transitions. Try #flux_vector_for( *transitions ), " +
|
222
|
-
"#flux_vector_for_SR, #flux_for( *transitions ), or #flux_for_SR"
|
223
|
-
end
|
224
|
-
alias φ flux_vector
|
225
|
-
|
226
|
-
# Flux of SR transitions as an array.
|
227
|
-
#
|
228
|
-
def flux_for_SR
|
229
|
-
flux_vector_for_SR.column( 0 ).to_a
|
230
|
-
end
|
231
|
-
|
232
|
-
# Flux for a selected collection of SR transitions.
|
233
|
-
#
|
234
|
-
def flux_for *transitions
|
235
|
-
all = SR_transitions :flux_for_SR
|
236
|
-
transitions.map { |t| transition t }.map { |e| all[e] }
|
237
|
-
end
|
238
|
-
|
239
|
-
# Same as #flux_for_SR, but with caller asserting that there are none but
|
240
|
-
# SR transitions in the simulation (or error).
|
241
|
-
#
|
242
|
-
def flux
|
243
|
-
flux_vector.column( 0 ).to_a
|
244
|
-
end
|
245
|
-
|
246
|
-
# Flux of SR transitions as a hash { name: flux }.
|
247
|
-
#
|
248
|
-
def f_SR
|
249
|
-
SR_tt :flux_for_SR
|
250
|
-
end
|
251
|
-
|
252
|
-
# Flux for a selected collection of SR transition as hash { key => flux }.
|
253
|
-
#
|
254
|
-
def f_for *transitions
|
255
|
-
Hash[ transitions.zip( flux_for *transitions ) ]
|
256
|
-
end
|
257
|
-
|
258
|
-
# Same as #f_SR, but with caller asserting that there are none but SR
|
259
|
-
# transitions in the simulation (or error).
|
260
|
-
#
|
261
|
-
def f
|
262
|
-
SR_tt :flux
|
263
|
-
end
|
264
|
-
|
265
|
-
# State differential for SR transitions.
|
266
|
-
#
|
267
|
-
def gradient_for_SR
|
268
|
-
S_SR() * flux_vector_for_SR
|
269
|
-
end
|
270
|
-
|
271
|
-
# State differential for SR transitions as a hash { place_name: ∂ / ∂ᴛ }.
|
272
|
-
#
|
273
|
-
def ∂_SR
|
274
|
-
free_pp :gradient_for_SR
|
275
|
-
end
|
276
|
-
|
277
|
-
# First-order action vector for SR transitions for the time period Δt.
|
278
|
-
#
|
279
|
-
def first_order_action_vector_for_SR( Δt )
|
280
|
-
flux_vector_for_SR * Δt
|
281
|
-
end
|
282
|
-
alias ᴀ_SR first_order_action_vector_for_SR
|
283
|
-
|
284
|
-
# First-order action (as array) for SR for the time period Δt.
|
285
|
-
#
|
286
|
-
def first_order_action_for_SR( Δt )
|
287
|
-
first_order_action_vector_for_SR( Δt ).column( 0 ).to_a
|
288
|
-
end
|
289
|
-
|
290
|
-
# First-order state contribution of SR transitions during Δt.
|
291
|
-
#
|
292
|
-
def Δ_SR( Δt )
|
293
|
-
gradient_for_SR * Δt
|
294
|
-
end
|
295
|
-
|
296
|
-
# First-order state contribution for SR transitions during Δt (as array).
|
297
|
-
#
|
298
|
-
def Δ_array_for_SR( Δt )
|
299
|
-
Δ_Euler_for_SR( Δt ).column( 0 ).to_a
|
300
|
-
end
|
47
|
+
class << self
|
48
|
+
alias __new__ new
|
301
49
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
# Returns the array of places to which the assignment transitions assign.
|
307
|
-
#
|
308
|
-
def A_target_places
|
309
|
-
# TODO
|
310
|
-
end
|
311
|
-
|
312
|
-
# Like #A_target_places, but returns place names.
|
313
|
-
#
|
314
|
-
def A_target_pp
|
315
|
-
# TODO
|
316
|
-
end
|
317
|
-
|
318
|
-
# Returns the assignments as they would if all A transitions fired now,
|
319
|
-
# as a hash { place => assignment }.
|
320
|
-
#
|
321
|
-
def assignments
|
322
|
-
# TODO
|
323
|
-
end
|
324
|
-
|
325
|
-
# Like #assignments, but place names are used instead { name: assignment }.
|
326
|
-
#
|
327
|
-
def a
|
328
|
-
# TODO
|
329
|
-
end
|
330
|
-
|
331
|
-
# Returns the assignments as a column vector.
|
332
|
-
#
|
333
|
-
def A_action
|
334
|
-
Matrix.column_vector( assignments.reduce( free_places { nil } ) do |α, p|
|
335
|
-
α[p] = marking
|
336
|
-
end )
|
337
|
-
# TODO: Assignment action to a clamped place should result in a warning.
|
338
|
-
end
|
339
|
-
|
340
|
-
# Correspondence matrix free places => all places.
|
341
|
-
#
|
342
|
-
attr_reader :F2A
|
343
|
-
|
344
|
-
# Correspondence matrix clamped places => all places.
|
345
|
-
#
|
346
|
-
attr_reader :C2A
|
347
|
-
|
348
|
-
# Simulation settings.
|
349
|
-
#
|
350
|
-
def settings; {} end
|
351
|
-
alias :simulation_settings :settings
|
352
|
-
|
353
|
-
def recording_csv_string
|
354
|
-
CSV.generate do |csv|
|
355
|
-
@recording.keys.zip( @recording.values ).map{ |a, b| [ a ] + b.to_a }
|
356
|
-
.each{ |line| csv << line }
|
357
|
-
end
|
50
|
+
def new net: (fail ArgumentError, "No net supplied!"), **settings
|
51
|
+
net.simulation **settings
|
52
|
+
end
|
358
53
|
end
|
359
54
|
|
360
|
-
#
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
# In addition to the arguments required by the regular simulation
|
375
|
-
# constructor, timed simulation constructor also expects :step_size
|
376
|
-
# (alias :step), :sampling_period (alias :sampling), and :target_time
|
377
|
-
# named arguments.
|
378
|
-
#
|
379
|
-
def initialize( method: default_simulation_method,
|
380
|
-
guarded: false,
|
381
|
-
marking_clamps: {},
|
382
|
-
initial_marking: {},
|
383
|
-
**nn )
|
384
|
-
puts "constructing a simulation" if YPetri::DEBUG
|
385
|
-
|
386
|
-
@method, @guarded = method, guarded
|
387
|
-
@net = nn.fetch( :net )
|
388
|
-
@places, @transitions = @net.places.dup, @net.transitions.dup
|
389
|
-
self.singleton_class.class_exec {
|
390
|
-
define_method :Place do net.send :Place end
|
391
|
-
define_method :Transition do net.send :Transition end
|
392
|
-
define_method :Net do net.send :Net end
|
393
|
-
private :Place, :Transition, :Net
|
394
|
-
}; puts "setup of :net mental image complete" if YPetri::DEBUG
|
55
|
+
# Parametrized subclasses:
|
56
|
+
attr_reader :core,
|
57
|
+
:recorder,
|
58
|
+
:guarded,
|
59
|
+
:tS_stoichiometry_matrix,
|
60
|
+
:TS_stoichiometry_matrix,
|
61
|
+
:tS_SM,
|
62
|
+
:TS_SM,
|
63
|
+
:ts_delta_closure,
|
64
|
+
:Ts_gradient_closure,
|
65
|
+
:tS_firing_closure,
|
66
|
+
:TS_rate_closure,
|
67
|
+
:A_assignment_closure,
|
68
|
+
:increment_marking_vector_closure
|
395
69
|
|
396
|
-
|
397
|
-
@initial_marking = initial_marking.with_keys { |k| place k }
|
398
|
-
# Enforce that keys in the hashes must be unique:
|
399
|
-
@marking_clamps.keys.aT_equal @marking_clamps.keys.uniq
|
400
|
-
@initial_marking.keys.aT_equal @initial_marking.keys.uniq
|
401
|
-
puts "setup of clamps and initial marking done" if YPetri::DEBUG
|
402
|
-
|
403
|
-
places.each { |pl| # each place must have either clamp, or initial marking
|
404
|
-
pl.aT "place #{pl}", "have either clamp or initial marking" do |pl|
|
405
|
-
( @marking_clamps.keys + @initial_marking.keys ).include? pl
|
406
|
-
end
|
407
|
-
}; puts "clamp || initial marking test passed" if YPetri::DEBUG
|
408
|
-
|
409
|
-
# @F2A * ᴍ (marking vector of free places) maps ᴍ to all places.
|
410
|
-
@F2A = Matrix.correspondence_matrix( free_places, places )
|
411
|
-
# @C2A * marking_vector_of_clamped_places maps it to all places.
|
412
|
-
@C2A = Matrix.correspondence_matrix( clamped_places, places )
|
413
|
-
puts "correspondence matrices set up" if YPetri::DEBUG
|
414
|
-
|
415
|
-
# Stoichiometry matrices:
|
416
|
-
@S_tS = S_for tS_transitions()
|
417
|
-
@S_SR = S_for SR_transitions()
|
418
|
-
@S_TSr = S_for TSr_transitions()
|
419
|
-
puts "stoichiometry matrices set up" if YPetri::DEBUG
|
420
|
-
|
421
|
-
# Other assets:
|
422
|
-
@Δ_closures_for_tsa = create_Δ_closures_for_tsa
|
423
|
-
@Δ_closures_for_Tsr = create_Δ_closures_for_Tsr
|
424
|
-
@action_closures_for_tS = create_action_closures_for_tS
|
425
|
-
@action_closures_for_TSr = create_action_closures_for_TSr
|
426
|
-
@rate_closures_for_sR = create_rate_closures_for_sR
|
427
|
-
@rate_closures_for_SR = create_rate_closures_for_SR
|
428
|
-
@assignment_closures_for_A = create_assignment_closures_for_A
|
429
|
-
@zero_ᴍ = compute_initial_marking_vector_of_free_places.map { |e| e * 0 }
|
430
|
-
@zero_gradient = @zero_ᴍ.dup
|
431
|
-
puts "other assets set up" if YPetri::DEBUG
|
432
|
-
|
433
|
-
@timed = if nn.has?( :time ) || nn.has?( :step ) || nn.has?( :sampling )
|
434
|
-
extend Timed
|
435
|
-
true
|
436
|
-
else false end
|
70
|
+
alias guarded? guarded
|
437
71
|
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
72
|
+
delegate :net, to: "self.class"
|
73
|
+
|
74
|
+
delegate :simulation_method,
|
75
|
+
:guarded?,
|
76
|
+
:step!,
|
77
|
+
to: :core
|
78
|
+
|
79
|
+
delegate :recording,
|
80
|
+
to: :recorder
|
81
|
+
|
82
|
+
# The basic simulation parameter is :net – +YPetri::Net+ instance which to
|
83
|
+
# simulate. Net implies the collection of places and transitions. Other
|
84
|
+
# required attributes are marking clamps and initial marking. These can be
|
85
|
+
# extracted from the Place and Transition instances if not given explicitly.
|
86
|
+
# Simulation method is controlled by the :method argument, guarding is
|
87
|
+
# switched on and off by the :guarded argument (true/false). If timed
|
88
|
+
# transitions are present, the simulation is considered timed. Timed
|
89
|
+
# simulation constructor has additional arguments :time, establishing time
|
90
|
+
# range, :step, controlling the simulation step size, and :sampling,
|
91
|
+
# controlling the sampling frequency.
|
92
|
+
#
|
93
|
+
def initialize **settings
|
94
|
+
method = settings[:method] # the simulation method
|
95
|
+
@guarded = settings[:guarded] # guarding on / off
|
96
|
+
m_clamps = settings[:marking_clamps] || {}
|
97
|
+
init_m = settings[:initial_marking] || {}
|
98
|
+
use_default_marking = if settings.has? :use_default_marking then
|
99
|
+
settings[:use_default_marking]
|
100
|
+
else true end
|
101
|
+
# Time-independent simulation settings received, constructing param. classes
|
102
|
+
param_class!( { Place: PlaceRepresentation,
|
103
|
+
Places: Places,
|
104
|
+
Transition: TransitionRepresentation,
|
105
|
+
Transitions: Transitions,
|
106
|
+
PlaceMapping: PlaceMapping,
|
107
|
+
InitialMarking: InitialMarking,
|
108
|
+
MarkingClamps: MarkingClamps,
|
109
|
+
MarkingVector: MarkingVector }, with: { simulation: self } )
|
110
|
+
# Place and transition representation classes are their own namespaces.
|
111
|
+
Place().namespace!
|
112
|
+
Transition().namespace!
|
113
|
+
# Set up the places collection.
|
114
|
+
@places = Places().load( net.places )
|
115
|
+
# Clamped places' mapping to the clamp values.
|
116
|
+
@marking_clamps = MarkingClamps().load( m_clamps )
|
117
|
+
# Free places' mapping to the initial marking values.
|
118
|
+
@initial_marking = InitialMarking().load( init_m )
|
119
|
+
# Set up the place and transition collections.
|
120
|
+
@places.complete_initial_marking( use_default_marking: use_default_marking )
|
121
|
+
# Correspondence matrix free --> all
|
122
|
+
@f2a = free_places.correspondence_matrix( places )
|
123
|
+
# Correspondence matrix clamped --> all
|
124
|
+
@c2a = clamped_places.correspondence_matrix( places )
|
125
|
+
# Conditionally extend self depending on net's timedness.
|
126
|
+
extend( settings[:time] || settings[:step] || settings[:sampling] ?
|
127
|
+
Timed : Timeless )
|
128
|
+
# Initialize the marking vector.
|
129
|
+
@m_vector = MarkingVector().zero
|
130
|
+
# Set up the transitions collection.
|
131
|
+
@transitions = Transitions().load( net.transitions )
|
132
|
+
# Set up stoichiometry matrices relative to free places.
|
133
|
+
@tS_stoichiometry_matrix = transitions.tS.stoichiometry_matrix
|
134
|
+
@TS_stoichiometry_matrix = transitions.TS.stoichiometry_matrix
|
135
|
+
# Set up stoichiometry matrices relative to all places.
|
136
|
+
@tS_SM = transitions.tS.SM
|
137
|
+
@TS_SM = transitions.TS.SM
|
138
|
+
# Call timedness-dependent initialization.
|
139
|
+
init **settings
|
140
|
+
# Make timeless closures.
|
141
|
+
@ts_delta_closure = transitions.ts.delta_closure
|
142
|
+
@tS_firing_closure = transitions.tS.firing_closure
|
143
|
+
@A_assignment_closure = transitions.A.assignment_closure
|
144
|
+
@increment_marking_vector_closure = m_vector.increment_closure
|
145
|
+
# Make timed closures.
|
146
|
+
if timed? then
|
147
|
+
@Ts_gradient_closure = transitions.Ts.gradient_closure
|
148
|
+
@TS_rate_closure = transitions.TS.rate_closure
|
450
149
|
end
|
451
|
-
|
452
|
-
|
453
|
-
|
150
|
+
# Init the core.
|
151
|
+
@core = Core().new( method: method, guarded: guarded )
|
152
|
+
# Reset.
|
454
153
|
reset!
|
455
154
|
end
|
456
155
|
|
457
|
-
#
|
458
|
-
# same simulation settings. This state (:marking argument) can be specified
|
459
|
-
# either as marking vector for free or all places, marking array for free or
|
460
|
-
# all places, or marking hash. If vector or array is given, its size must
|
461
|
-
# correspond to the number of either free, or all places. If hash is given,
|
462
|
-
# it is not necessary to specify marking of every place – marking of those
|
463
|
-
# left out will be left same as in the current state.
|
464
|
-
#
|
465
|
-
def at( marking: marking, **nn )
|
466
|
-
err_msg = "Size of supplied marking must match either the number of " +
|
467
|
-
"free places, or the number of all places!"
|
468
|
-
update_method = case marking
|
469
|
-
when Hash then :update_marking_from_a_hash
|
470
|
-
when Matrix then
|
471
|
-
case marking.column_to_a.size
|
472
|
-
when places.size then :set_marking_vector
|
473
|
-
when free_places.size then :set_ᴍ
|
474
|
-
else fail TypeError, err_msg end
|
475
|
-
else # marking assumed to be an array
|
476
|
-
case marking.size
|
477
|
-
when places.size then :set_marking
|
478
|
-
when free_places.size then :set_m
|
479
|
-
else fail TypeError, err_msg end
|
480
|
-
end
|
481
|
-
return dup( **nn ).send( update_method, marking )
|
482
|
-
end
|
483
|
-
|
484
|
-
# ==== Sparse stoichiometry vectors for transitions
|
485
|
-
|
486
|
-
# For the transition specified by the argument, this method returns the
|
487
|
-
# sparse stoichiometry vector corresponding to the free places.
|
488
|
-
#
|
489
|
-
def sparse_σ transition
|
490
|
-
instance = transition( transition )
|
491
|
-
raise AE, "Transition #{transition} not stoichiometric!" unless
|
492
|
-
instance.stoichiometric?
|
493
|
-
Matrix.correspondence_matrix( instance.codomain, free_places ) *
|
494
|
-
Matrix.column_vector( instance.stoichiometry )
|
495
|
-
end
|
496
|
-
|
497
|
-
# For the transition specified by the argument, this method returns the
|
498
|
-
# sparse stoichiometry vector mapped to all the places of the simulation.
|
156
|
+
# Simulation settings.
|
499
157
|
#
|
500
|
-
def
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
158
|
+
def settings all=false
|
159
|
+
return { method: simulation_method, guarded: guarded? } unless all == true
|
160
|
+
settings( false )
|
161
|
+
.update( net: net,
|
162
|
+
marking_clamps: marking_clamps.keys_to_source_places,
|
163
|
+
initial_marking: initial_marking.keys_to_source_places )
|
164
|
+
end
|
165
|
+
|
166
|
+
# Returns a new simulation instance. Unless modified by arguments, the state
|
167
|
+
# of the new instance is the same as the creator's. Arguments can partially or
|
168
|
+
# wholly modify the attributes of the duplicate.
|
169
|
+
#
|
170
|
+
def dup( marking: marking, recording: recording, **nn )
|
171
|
+
self.class.new( nn.reverse_merge! settings( true ) ).tap do |dup|
|
172
|
+
dup.recording.reset! recording: recording
|
173
|
+
dup.m_vector.reset! case marking
|
174
|
+
when Hash then
|
175
|
+
m_vector.to_hash_with_source_places
|
176
|
+
.update( PlaceMapping().load( marking ) )
|
177
|
+
.to_marking_vector
|
178
|
+
when Matrix, Array then marking
|
179
|
+
else marking.each.to_a end
|
180
|
+
end
|
506
181
|
end
|
507
182
|
|
508
|
-
#
|
183
|
+
# Inspect string for this simulation.
|
509
184
|
#
|
510
185
|
def inspect
|
511
|
-
|
186
|
+
to_s
|
512
187
|
end
|
513
188
|
|
514
|
-
#
|
189
|
+
# String representation of this simulation.
|
515
190
|
#
|
516
191
|
def to_s
|
517
|
-
"Simulation[
|
192
|
+
"#<Simulation: pp: %s, tt: %s, oid: %s>" % [ pp.size, tt.size, object_id ]
|
518
193
|
end
|
519
194
|
|
520
|
-
private
|
521
|
-
|
522
195
|
# Resets the simulation
|
523
196
|
#
|
524
|
-
def reset!
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
puts "clamped component of marking vector prepared:\n#{clamped_component}" if YPetri::DEBUG
|
531
|
-
|
532
|
-
mv_free = compute_initial_marking_vector_of_free_places
|
533
|
-
puts "#reset! obtained initial marking vector of free places" if YPetri::DEBUG
|
534
|
-
free_component = F2A() * mv_free
|
535
|
-
puts "free component of marking vector prepared:\n#{free_component}" if YPetri::DEBUG
|
536
|
-
|
537
|
-
# zero_vector = Matrix.column_vector( places.map { SY::ZERO rescue 0 } ) # Float zeros
|
538
|
-
zero_vector = Matrix.column_vector( places.map { 0 } ) # Float zeros
|
539
|
-
puts "zero vector prepared: #{zero_vector}" if YPetri::DEBUG
|
540
|
-
|
541
|
-
free_component.aT { |v|
|
542
|
-
qnt = v.first.quantity rescue :no_quantity
|
543
|
-
unless qnt == :no_quantity
|
544
|
-
v.all? { |e| e.quantity == qnt }
|
545
|
-
else true end
|
546
|
-
} if YPetri::DEBUG
|
547
|
-
puts "free component of marking vector checked" if YPetri::DEBUG
|
548
|
-
|
549
|
-
@marking_vector = free_component + clamped_component
|
550
|
-
# Matrix
|
551
|
-
# .column_vector( places.map.with_index do |_, i|
|
552
|
-
# clamped_component[i, 0] || free_component[i, 0]
|
553
|
-
# end )
|
554
|
-
|
555
|
-
puts "marking vector assembled\n#{m}\n, about to reset recording" if YPetri::DEBUG
|
556
|
-
reset_recording!
|
557
|
-
puts "reset recording done, about to initiate sampling process" if YPetri::DEBUG
|
558
|
-
note_state_change!
|
559
|
-
puts "sampling process initiated, #reset! done" if YPetri::DEBUG
|
560
|
-
return self
|
561
|
-
end
|
562
|
-
|
563
|
-
# Resets the recording.
|
564
|
-
#
|
565
|
-
def reset_recording!
|
566
|
-
@recording = {}
|
567
|
-
end
|
568
|
-
|
569
|
-
# To be called whenever the state changes. The method will cogitate, whether
|
570
|
-
# the observed state change warrants calling #sample!
|
571
|
-
#
|
572
|
-
def note_state_change!
|
573
|
-
sample! # default for vanilla Simulation: sample! at every occasion
|
574
|
-
end
|
575
|
-
|
576
|
-
# Performs sampling. A snapshot of the current simulation state is recorded
|
577
|
-
# into @recording hash as a pair { sampling_event => simulation state }.
|
578
|
-
#
|
579
|
-
def sample! key=L!(:sample!)
|
580
|
-
@sample_number = @sample_number + 1 rescue 0
|
581
|
-
@recording[ key.ℓ?(:sample!) ? @sample_number : key ] =
|
582
|
-
marking.map { |n| n.round SAMPLING_DECIMAL_PLACES }
|
583
|
-
end
|
584
|
-
|
585
|
-
# Called upon initialzation
|
586
|
-
#
|
587
|
-
def compute_initial_marking_vector_of_free_places
|
588
|
-
puts "computing the marking vector of free places" if YPetri::DEBUG
|
589
|
-
results = free_places.map { |p|
|
590
|
-
im = @initial_marking[ p ]
|
591
|
-
puts "doing free place #{p} with init. marking #{im}" if YPetri::DEBUG
|
592
|
-
# unwrap places / cells
|
593
|
-
im = case im
|
594
|
-
when YPetri::Place then im.marking
|
595
|
-
else im end
|
596
|
-
case im
|
597
|
-
when Proc then im.call
|
598
|
-
else im end
|
599
|
-
}
|
600
|
-
# and create the matrix out of the results
|
601
|
-
puts "about to create the column vector" if YPetri::DEBUG
|
602
|
-
cv = Matrix.column_vector results
|
603
|
-
puts "column vector #{cv} prepared" if YPetri::DEBUG
|
604
|
-
return cv
|
605
|
-
end
|
606
|
-
|
607
|
-
# Called upon initialization
|
608
|
-
#
|
609
|
-
def compute_marking_vector_of_clamped_places
|
610
|
-
puts "computing the marking vector of clamped places" if YPetri::DEBUG
|
611
|
-
results = clamped_places.map { |p|
|
612
|
-
clamp = @marking_clamps[ p ]
|
613
|
-
puts "doing clamped place #{p} with clamp #{clamp}" if YPetri::DEBUG
|
614
|
-
# unwrap places / cells
|
615
|
-
clamp = case clamp
|
616
|
-
when YPetri::Place then clamp.marking
|
617
|
-
else clamp end
|
618
|
-
# unwrap closure by calling it
|
619
|
-
case clamp
|
620
|
-
when Proc then clamp.call
|
621
|
-
else clamp end
|
622
|
-
}
|
623
|
-
# and create the matrix out of the results
|
624
|
-
puts "about to create the column vector" if YPetri::DEBUG
|
625
|
-
cv = Matrix.column_vector results
|
626
|
-
puts "column vector #{cv} prepared" if YPetri::DEBUG
|
627
|
-
return cv
|
628
|
-
end
|
629
|
-
|
630
|
-
# Expects a Δ marking vector for free places and performs the specified
|
631
|
-
# change on the marking vector for all places.
|
632
|
-
#
|
633
|
-
def update_marking! Δ_free_places
|
634
|
-
@marking_vector += F2A() * Δ_free_places
|
197
|
+
def reset! **settings
|
198
|
+
tap do
|
199
|
+
m_vector.reset!
|
200
|
+
recorder.reset!
|
201
|
+
recorder.alert
|
202
|
+
end
|
635
203
|
end
|
636
204
|
|
637
205
|
# Guards proposed marking delta.
|
@@ -641,182 +209,9 @@ class YPetri::Simulation
|
|
641
209
|
places.zip( ary ).each { |pl, proposed_m| pl.guard.( proposed_m ) }
|
642
210
|
end
|
643
211
|
|
644
|
-
#
|
645
|
-
#
|
646
|
-
def assignment_transitions_all_fire!
|
647
|
-
assignment_closures_for_A.each_with_index do |closure, i|
|
648
|
-
@marking_vector = closure.call # TODO: This offers better algorithm.
|
649
|
-
end
|
650
|
-
end
|
651
|
-
alias A_all_fire! assignment_transitions_all_fire!
|
652
|
-
|
653
|
-
# ----------------------------------------------------------------------
|
654
|
-
# Methods to create other instance assets upon initialization.
|
655
|
-
# These instance assets are created at the beginning, so the work
|
656
|
-
# needs to be performed only once in the instance lifetime.
|
657
|
-
|
658
|
-
def create_Δ_closures_for_tsa
|
659
|
-
tsa_transitions.map { |t|
|
660
|
-
p2d = Matrix.correspondence_matrix( places, t.domain )
|
661
|
-
c2f = Matrix.correspondence_matrix( t.codomain, free_places )
|
662
|
-
if guarded? then
|
663
|
-
-> {
|
664
|
-
domain_marking = ( p2d * marking_vector ).column_to_a
|
665
|
-
# I. TODO: t.domain_guard.( domain_marking )
|
666
|
-
codomain_change = Array t.action_closure.( *domain_marking )
|
667
|
-
# II. TODO: t.action_guard.( codomain_change )
|
668
|
-
c2f * codomain_change
|
669
|
-
}
|
670
|
-
else
|
671
|
-
-> { c2f * t.action_closure.( *( p2d * marking_vector ).column_to_a ) }
|
672
|
-
end
|
673
|
-
}
|
674
|
-
end
|
675
|
-
|
676
|
-
def blame_tsa( marking_vect )
|
677
|
-
# If, in spite of passing domain guard and action guard, marking guard
|
678
|
-
# indicates an exception, the method here serves to find the candidate
|
679
|
-
# transitions to blame for the exception, given certain place marking.
|
680
|
-
msg = "Action closure of transition #%{t} with domain #%{dm} and " +
|
681
|
-
"codomain #%{cdm} returns #%{retval} which, when added to place " +
|
682
|
-
"#{p}, gives marking that would flunk place's marking guard."
|
683
|
-
tsa_transitions.each { |t|
|
684
|
-
p2d = Matrix.correspondence_matrix( places, t.domain )
|
685
|
-
rslt = Array t.action_closure( *( p2d * marking_vect ).column_to_a )
|
686
|
-
t.codomain.zip( rslt ).each { |place, Δ|
|
687
|
-
fields = {
|
688
|
-
t: t.name || t.object_id, p: place,
|
689
|
-
dm: Hash[ t.domain_pp.zip domain_marking ],
|
690
|
-
cdm: Hash[ t.codomain_pp.zip( t.codomain.map { |p| ꜧ[p] } ) ],
|
691
|
-
retval: Hash[ t.codomain_pp.zip( codomain_change ) ]
|
692
|
-
}
|
693
|
-
rslt = ꜧ[place] + Δ
|
694
|
-
raise TypeError, msg % fields unless place.marking_guard.( rslt )
|
695
|
-
}
|
696
|
-
}
|
697
|
-
# TODO: Here, #blame_tsa simply raises. It would be however more correct
|
698
|
-
# to gather all blame candidates and present them to the user all.
|
699
|
-
end
|
700
|
-
|
701
|
-
def create_Δ_closures_for_Tsr
|
702
|
-
Tsr_transitions().map { |t|
|
703
|
-
p2d = Matrix.correspondence_matrix( places, t.domain )
|
704
|
-
c2f = Matrix.correspondence_matrix( t.codomain, free_places )
|
705
|
-
-> Δt { c2f * t.action_closure.( Δt, *( p2d * marking_vector ).column_to_a ) }
|
706
|
-
}
|
707
|
-
end
|
708
|
-
|
709
|
-
def create_action_closures_for_tS
|
710
|
-
tS_transitions.map{ |t|
|
711
|
-
p2d = Matrix.correspondence_matrix( places, t.domain )
|
712
|
-
-> { t.action_closure.( *( p2d * marking_vector ).column_to_a ) }
|
713
|
-
}
|
714
|
-
end
|
715
|
-
|
716
|
-
def create_action_closures_for_TSr
|
717
|
-
TSr_transitions().map{ |t|
|
718
|
-
p2d = Matrix.correspondence_matrix( places, t.domain )
|
719
|
-
-> Δt { t.action_closure.( Δt, *( p2d * marking_vector ).column_to_a ) }
|
720
|
-
}
|
721
|
-
end
|
722
|
-
|
723
|
-
def create_rate_closures_for_sR
|
724
|
-
sR_transitions.map{ |t|
|
725
|
-
p2d = Matrix.correspondence_matrix( places, t.domain )
|
726
|
-
c2f = Matrix.correspondence_matrix( t.codomain, free_places )
|
727
|
-
-> { c2f * t.rate_closure.( *( p2d * marking_vector ).column_to_a ) }
|
728
|
-
}
|
729
|
-
end
|
730
|
-
|
731
|
-
def create_rate_closures_for_SR
|
732
|
-
SR_transitions().map { |t|
|
733
|
-
p2d = Matrix.correspondence_matrix( places, t.domain )
|
734
|
-
puts "Marking is #{pp :marking rescue nil}" if YPetri::DEBUG
|
735
|
-
-> { t.rate_closure.( *( p2d * marking_vector ).column_to_a )
|
736
|
-
.tap do |r| fail YPetri::GuardError, "SR #{t.name}!!!!" if r.is_a? Complex end
|
737
|
-
}
|
738
|
-
}
|
739
|
-
end
|
740
|
-
|
741
|
-
def create_assignment_closures_for_A
|
742
|
-
nils = places.map { nil }
|
743
|
-
A_transitions().map { |t|
|
744
|
-
p2d = Matrix.correspondence_matrix( places, t.domain )
|
745
|
-
c2f = Matrix.correspondence_matrix( t.codomain, free_places )
|
746
|
-
zero_vector = Matrix.column_vector( places.map { 0 } )
|
747
|
-
probe = Matrix.column_vector( t.codomain.size.times.map { |a| a + 1 } )
|
748
|
-
result = ( F2A() * c2f * probe ).column_to_a.map { |n| n == 0 ? nil : n }
|
749
|
-
assignment_addresses = probe.column_to_a.map { |i| result.index i }
|
750
|
-
-> {
|
751
|
-
act = Array t.action_closure.( *( p2d * marking_vector ).column_to_a )
|
752
|
-
act.each_with_index { |e, i|
|
753
|
-
fail YPetri::GuardError, "Assignment transition #{t.name} with " +
|
754
|
-
"domain #{t.domain_pp( domain_marking )} has produced a complex " +
|
755
|
-
"number at output positon #{i} (output was #{act})!" if e.is_a?( Complex ) || i.is_a?( Complex )
|
756
|
-
}
|
757
|
-
assign = assignment_addresses.zip( act )
|
758
|
-
.each_with_object nils.dup do |pair, o| o[pair[0]] = pair[1] end
|
759
|
-
marking_vector.map { |orig_val| assign.shift || orig_val }
|
760
|
-
}
|
761
|
-
} # map
|
762
|
-
end
|
763
|
-
|
764
|
-
# Set marking vector (for all places).
|
212
|
+
# Extract a prescribed set of features.
|
765
213
|
#
|
766
|
-
def
|
767
|
-
|
768
|
-
return self
|
214
|
+
def get_features arg
|
215
|
+
net.State.features( arg ).extract_from( self )
|
769
216
|
end
|
770
|
-
|
771
|
-
# Set marking vector, based on marking array of all places.
|
772
|
-
#
|
773
|
-
def set_marking marking_array
|
774
|
-
set_marking_vector Matrix.column_vector( marking_array )
|
775
|
-
end
|
776
|
-
|
777
|
-
# Update marking vector, based on { place => marking } hash argument.
|
778
|
-
#
|
779
|
-
def update_marking_from_a_hash marking_hash
|
780
|
-
to_set = place_marking.merge( marking_hash.with_keys do |k| place k end )
|
781
|
-
set_marking( places.map { |pl| to_set[ pl ] } )
|
782
|
-
end
|
783
|
-
|
784
|
-
# Set marking vector based on marking array of free places.
|
785
|
-
#
|
786
|
-
def set_m marking_array_for_free_places
|
787
|
-
set_marking_from_a_hash( free_places( marking_array_for_free_places ) )
|
788
|
-
end
|
789
|
-
|
790
|
-
# Set marking vector based on marking vector of free places.
|
791
|
-
#
|
792
|
-
def set_ᴍ marking_vector_for_free_places
|
793
|
-
set_m( marking_vector_for_free_places.column_to_a )
|
794
|
-
end
|
795
|
-
|
796
|
-
# Private method for resetting recording.
|
797
|
-
#
|
798
|
-
def set_recording rec
|
799
|
-
@recording = Hash[ rec ]
|
800
|
-
return self
|
801
|
-
end
|
802
|
-
|
803
|
-
# Duplicate creation.
|
804
|
-
#
|
805
|
-
def dup( **nn )
|
806
|
-
self.class.new( nn.reverse_merge!( { method: @method,
|
807
|
-
guarded: @guarded,
|
808
|
-
net: @net,
|
809
|
-
marking_clamps: @marking_clamps,
|
810
|
-
initial_marking: @initial_marking
|
811
|
-
}.update( simulation_settings ) ) )
|
812
|
-
.tap { |instance|
|
813
|
-
instance.send :set_recording, recording
|
814
|
-
instance.send :set_marking_vector, @marking_vector
|
815
|
-
}
|
816
|
-
end
|
817
|
-
|
818
|
-
# Instance identification methods.
|
819
|
-
#
|
820
|
-
def place( which ); Place().instance( which ) end
|
821
|
-
def transition( which ); Transition().instance( which ) end
|
822
217
|
end # class YPetri::Simulation
|