y_petri 2.0.15 → 2.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|