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
@@ -0,0 +1,54 @@
|
|
1
|
+
class YPetri::Simulation
|
2
|
+
# A machine that receives alerts during simulation and records a recording
|
3
|
+
# according to its implementation. Alerts are received via +#alert+ method.
|
4
|
+
# The recording bein recorded is stored in @recording instance variable.
|
5
|
+
# This can be reset by +#reset!+ method, which also accepts arguments to
|
6
|
+
# change the recorder settings and/or insert another recording.
|
7
|
+
#
|
8
|
+
class Recorder
|
9
|
+
include Dependency
|
10
|
+
|
11
|
+
SAMPLING_DECIMAL_PLACES = 5
|
12
|
+
|
13
|
+
attr_reader :features, :recording
|
14
|
+
delegate :simulation, to: "self.class"
|
15
|
+
delegate :reconstruct, :reduce, to: :recording
|
16
|
+
|
17
|
+
# Initializes the recorder. Takes 2 arguments: +:features+ expecting the
|
18
|
+
# feature set to record during simulation, and +:recording+, expecting the
|
19
|
+
# initial state of the recording.
|
20
|
+
#
|
21
|
+
def initialize features: net.State.marking( free_pp ),
|
22
|
+
recording: features.new_dataset,
|
23
|
+
**nn
|
24
|
+
@features = net.State.features( features )
|
25
|
+
reset! recording: recording
|
26
|
+
end
|
27
|
+
|
28
|
+
# Assigns to @recording a new Dataset instance. Without arguments, the new
|
29
|
+
# recording is empty. With +:recording+ named argument supplied, the new
|
30
|
+
# recording is filled with the prescribed contents.
|
31
|
+
#
|
32
|
+
def reset! **nn
|
33
|
+
@features = net.State.features( nn[:features] || @features )
|
34
|
+
@recording = features.new_dataset
|
35
|
+
@recording.update Hash[ nn[:recording] ] if nn[:recording]
|
36
|
+
end
|
37
|
+
|
38
|
+
# Hook to be called by simulators whenever there is a state change. The
|
39
|
+
# decision to sample is then the business of the recorder.
|
40
|
+
#
|
41
|
+
def alert
|
42
|
+
sample! # vanilla recorder samples at every occasion
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# Records the current state as a pair { sampling_event => system_state }.
|
48
|
+
#
|
49
|
+
def sample! event
|
50
|
+
record = simulation.get_features( features )
|
51
|
+
recording[ event ] = record.dump( precision: SAMPLING_DECIMAL_PLACES )
|
52
|
+
end
|
53
|
+
end # class Recorder
|
54
|
+
end # YPetri::Simulation
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module YPetri::Simulation::Timed
|
2
|
+
# Timed aspect of the recorder.
|
3
|
+
#
|
4
|
+
class Recorder < YPetri::Simulation::Recorder
|
5
|
+
TIME_DECIMAL_PLACES = 5
|
6
|
+
|
7
|
+
attr_reader :next_time
|
8
|
+
attr_accessor :sampling
|
9
|
+
delegate :time,
|
10
|
+
:default_sampling,
|
11
|
+
to: :simulation
|
12
|
+
|
13
|
+
# Apart from the vanilla version arguments, timed recorder takes +:sampling+
|
14
|
+
# argument.
|
15
|
+
#
|
16
|
+
def initialize sampling: default_sampling, next_time: time, **nn
|
17
|
+
super
|
18
|
+
@sampling = sampling
|
19
|
+
@next_time = next_time
|
20
|
+
end
|
21
|
+
|
22
|
+
# Like +YPetri::Simulation::Recorder#reset+, but allowing for an additional
|
23
|
+
# named argument +:next_time+ that sets the next sampling time, and
|
24
|
+
# +:sampling:, resetting the sampling period.
|
25
|
+
#
|
26
|
+
def reset! sampling: default_sampling, next_time: time, **nn
|
27
|
+
super
|
28
|
+
@sampling = sampling
|
29
|
+
@next_time = next_time
|
30
|
+
end
|
31
|
+
|
32
|
+
# Hook to be called by simulators whenever the state changes (every time
|
33
|
+
# that simulation +time+ is incremented).
|
34
|
+
#
|
35
|
+
def alert
|
36
|
+
t = time.round( 9 )
|
37
|
+
t2 = next_time.round( 9 )
|
38
|
+
if t >= t2 then # it's time to sample
|
39
|
+
sample!
|
40
|
+
@next_time += sampling
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
# Records the current state as a pair { sampling_time => system_state }.
|
47
|
+
#
|
48
|
+
def sample!
|
49
|
+
sampling_time = time.round( TIME_DECIMAL_PLACES )
|
50
|
+
super sampling_time
|
51
|
+
end
|
52
|
+
end # class Recorder
|
53
|
+
end # YPetri::Simulation
|
@@ -1,284 +1,184 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
#
|
5
|
-
module YPetri::Simulation::Timed
|
6
|
-
SAMPLING_TIME_DECIMAL_PLACES = 5
|
7
|
-
SIMULATION_METHODS =
|
8
|
-
[
|
9
|
-
[ :Euler ],
|
10
|
-
[ :Euler_with_timeless_transitions_firing_after_each_time_tick, :quasi_Euler ],
|
11
|
-
[ :Euler_with_timeless_transitions_firing_after_each_step, :pseudo_Euler ]
|
12
|
-
]
|
13
|
-
DEFAULT_SIMULATION_METHOD = :Euler
|
14
|
-
|
15
|
-
# ==== Exposing time-related global simulation settings
|
16
|
-
|
17
|
-
# Simulation parameter: :initial_time.
|
18
|
-
#
|
19
|
-
attr_reader :initial_time
|
20
|
-
|
21
|
-
# Simulation parameter: :step_size
|
22
|
-
#
|
23
|
-
attr_accessor :step_size
|
24
|
-
|
25
|
-
# Simulation parameter: :sampling_period
|
26
|
-
#
|
27
|
-
attr_accessor :sampling_period
|
28
|
-
|
29
|
-
# Simulation parameter: :target_time
|
30
|
-
#
|
31
|
-
attr_accessor :target_time
|
32
|
-
|
33
|
-
# Reads the sampling rate.
|
3
|
+
class YPetri::Simulation
|
4
|
+
# A mixin for timed simulations, used by an +#extend+ call during init.
|
34
5
|
#
|
35
|
-
|
36
|
-
|
37
|
-
# Reads the time range (initial_time..target_time) of the simulation.
|
38
|
-
#
|
39
|
-
def time_range; initial_time..target_time end
|
40
|
-
|
41
|
-
# Reads simulation settings
|
42
|
-
# (:step_size, :sampling_period and :time_range).
|
43
|
-
#
|
44
|
-
def settings
|
45
|
-
{ step: step_size,
|
46
|
-
sampling: sampling_period,
|
47
|
-
time: time_range }
|
48
|
-
end
|
49
|
-
alias simulation_settings settings
|
50
|
-
|
51
|
-
# Exposing time.
|
52
|
-
#
|
53
|
-
attr_reader :time
|
54
|
-
alias ᴛ time
|
55
|
-
|
56
|
-
# def stop; end # LATER
|
57
|
-
# def continue; end # LATER
|
6
|
+
module Timed
|
7
|
+
require_relative 'timed/recorder'
|
58
8
|
|
59
|
-
|
60
|
-
# def gillespie_step
|
61
|
-
# t, dt = gillespie_select( @net.transitions )
|
62
|
-
# @marking_vector += t.project( @marking_vector, @step_size )
|
63
|
-
# @time += dt
|
64
|
-
# note_state_change
|
65
|
-
# end
|
9
|
+
DEFAULT_SETTINGS = -> do { step: 0.1, sampling: 5, time: 0..60 } end
|
66
10
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
# # the internals of this method were already heavily obsolete
|
71
|
-
# # they can be seen in previous versions, if needed
|
72
|
-
# # so now, I just take use of the Δ_Euler_free
|
73
|
-
# Δ_Euler_free
|
74
|
-
# end
|
75
|
-
|
76
|
-
# Allows to explore the system at different state / time. Creates a double,
|
77
|
-
# which is set to the required state / time. In addition to the parent class,
|
78
|
-
# this version alseo sets time.
|
79
|
-
#
|
80
|
-
def at( time: ᴛ, **oo )
|
81
|
-
super( **oo ).tap { |duplicate| duplicate.send :set_time, time }
|
82
|
-
end
|
11
|
+
def self.included receiver
|
12
|
+
receiver.Recording.class_exec { prepend Recording }
|
13
|
+
end
|
83
14
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
end
|
15
|
+
# True for timed simulations.
|
16
|
+
#
|
17
|
+
def timed?
|
18
|
+
true
|
19
|
+
end
|
90
20
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
21
|
+
attr_reader :time,
|
22
|
+
:time_unit,
|
23
|
+
:initial_time,
|
24
|
+
:target_time,
|
25
|
+
:step,
|
26
|
+
:default_sampling
|
27
|
+
|
28
|
+
alias starting_time initial_time
|
29
|
+
alias ending_time target_time
|
30
|
+
|
31
|
+
delegate :flux_vector_TS,
|
32
|
+
:gradient_TS,
|
33
|
+
:gradient_Ts,
|
34
|
+
:gradient,
|
35
|
+
:flux_vector,
|
36
|
+
to: :core
|
37
|
+
|
38
|
+
delegate :sampling, to: :recorder
|
39
|
+
|
40
|
+
# Reads the time range (initial_time..target_time) of the simulation.
|
41
|
+
#
|
42
|
+
def time_range
|
43
|
+
initial_time..target_time
|
44
|
+
end
|
96
45
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
# after or just on the target time_step
|
105
|
-
# exact: simulation stops exactly on the prescribed time,
|
106
|
-
# to make this possible last step is shortened if necessary
|
107
|
-
#
|
108
|
-
def run_until( target_time, final_step: :exact )
|
109
|
-
case final_step
|
110
|
-
when :before then # step until on or just before the target
|
111
|
-
step! while @time + @step_size <= target_time
|
112
|
-
when :exact then # simulate to exact time
|
113
|
-
step! while @time + @step_size < target_time
|
114
|
-
step!( target_time - @time ) # make a short last step as required
|
115
|
-
@time = target_time # to get exactly on the prescribed time
|
116
|
-
when :after then # step until on or after target
|
117
|
-
step! while @time < target_time
|
118
|
-
else
|
119
|
-
fail ArgumentError, "Unrecognized :final_step option: #{final_step}"
|
46
|
+
# Returnst the settings pertaining to the Timed aspect of the simulation,
|
47
|
+
# that is, +:step+, +:sampling+ and +:time+.
|
48
|
+
#
|
49
|
+
def settings all=false
|
50
|
+
super.update( step: step,
|
51
|
+
sampling: sampling,
|
52
|
+
time: time_range )
|
120
53
|
end
|
121
|
-
end
|
122
54
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
S_SR() * flux_vector_for_SR + g_sR
|
129
|
-
else
|
130
|
-
S_SR() * flux_vector_for_SR
|
55
|
+
# Same as +#run!+, but guards against run upto infinity.
|
56
|
+
#
|
57
|
+
def run( upto: target_time, final_step: :exact )
|
58
|
+
fail "Upto time equals infinity!" if upto == Float::INFINITY
|
59
|
+
run!( upto: upto, final_step: final_step )
|
131
60
|
end
|
132
|
-
end
|
133
61
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
62
|
+
# Near alias for +#run_upto+. Accepts +:upto+ named argument, using
|
63
|
+
# @target_time attribute as a default. The second optional argument,
|
64
|
+
# +:final_step+, has the same options as in +#run_upto+ method.
|
65
|
+
#
|
66
|
+
def run!( upto: target_time, final_step: :exact )
|
67
|
+
run_upto( upto, final_step: final_step )
|
68
|
+
end
|
139
69
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
70
|
+
# Runs the simulation until the target time. Named argument :final_step has
|
71
|
+
# options :just_before, :just_after and :exact, and tunes the simulation
|
72
|
+
# behavior towards the end of the run.
|
73
|
+
#
|
74
|
+
# just_before: last step has normal size, simulation stops before or just
|
75
|
+
# on the target time
|
76
|
+
# just_after: last step has normal size, simulation stops after or just
|
77
|
+
# on the target time_step
|
78
|
+
# exact: simulation stops exactly on the prescribed time, last step
|
79
|
+
# is shortened if necessary
|
80
|
+
#
|
81
|
+
def run_upto( target_time, final_step: :exact )
|
82
|
+
case final_step
|
83
|
+
when :before then
|
84
|
+
step! while time + step <= target_time
|
85
|
+
when :exact then
|
86
|
+
step! while time + step < target_time
|
87
|
+
step!( target_time - time )
|
88
|
+
@time = target_time
|
89
|
+
when :after then
|
90
|
+
step! while time < target_time
|
91
|
+
else
|
92
|
+
fail ArgumentError, "Unrecognized :final_step option: #{final_step}"
|
93
|
+
end
|
94
|
+
end
|
146
95
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
end
|
154
|
-
alias Δ_euler_for_free_places Δ_Euler_for_free_places
|
155
|
-
alias ΔE Δ_Euler_for_free_places
|
96
|
+
# String representation of this timed simulation.
|
97
|
+
#
|
98
|
+
def to_s
|
99
|
+
"#<Simulation: time: %s, pp: %s, tt: %s, oid: %s>" %
|
100
|
+
[ time, pp.size, tt.size, object_id ]
|
101
|
+
end
|
156
102
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
alias Δ_Euler Δ_Euler_for_all_places
|
103
|
+
# Increments the simulation's time and alerts the recorder.
|
104
|
+
#
|
105
|
+
def increment_time! Δt=step
|
106
|
+
@time += Δt
|
107
|
+
recorder.alert
|
108
|
+
end
|
164
109
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
if guarded? then
|
171
|
-
guard_Δ! delta
|
172
|
-
update_marking! delta
|
173
|
-
else
|
174
|
-
update_marking! delta
|
110
|
+
# Resets the timed simulation.
|
111
|
+
#
|
112
|
+
def reset!
|
113
|
+
@time = initial_time || time_unit * 0
|
114
|
+
super
|
175
115
|
end
|
176
|
-
update_time! Δt
|
177
|
-
end
|
178
|
-
alias euler_step! Euler_step!
|
179
116
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
update_marking!( note( "Δ state if tS transitions fire once",
|
186
|
-
is: Δ_if_tS_fire_once ) +
|
187
|
-
note( "Δ state if tsa transitions fire once",
|
188
|
-
is: Δ_if_tsa_fire_once ) )
|
117
|
+
# Customized dup method that allows to modify the attributes of
|
118
|
+
# the duplicate upon creation.
|
119
|
+
#
|
120
|
+
def dup time: time, **nn
|
121
|
+
super( **nn ).tap { |i| i.reset_time! time }
|
189
122
|
end
|
190
|
-
|
191
|
-
|
123
|
+
alias at dup
|
124
|
+
|
125
|
+
# Returns the zero gradient. Optionally, places can be specified, for which
|
126
|
+
# the zero vector is returned.
|
127
|
+
#
|
128
|
+
def zero_gradient places: nil
|
129
|
+
return zero_gradient places: places() if places.nil?
|
130
|
+
places.map { |id|
|
131
|
+
p = place( id )
|
132
|
+
( p.free? ? p.initial_marking : p.clamp ) * 0 / time_unit
|
133
|
+
}.to_column_vector
|
192
134
|
end
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
135
|
+
alias zero_∇ zero_gradient
|
136
|
+
|
137
|
+
private
|
138
|
+
|
139
|
+
# Initialization subroutine for timed simulations. Expects named arguments
|
140
|
+
# +:time+ (alias +:time_range+), meaning the simulation time range (a Range
|
141
|
+
# of initial_time..target_time), +:step+, meaning time step of the
|
142
|
+
# simulation, and +:sampling+, meaning sampling period of the simulation.
|
143
|
+
#
|
144
|
+
# Initializes the time-related attributes @initial_time, @target_time,
|
145
|
+
# @time_unit and @time (via +#reset_time!+ call). Also sets up the
|
146
|
+
# parametrized subclasses +@Core+ and +@Recorder+, and initializes the
|
147
|
+
# +@recorder+ attribute.
|
148
|
+
#
|
149
|
+
def init **settings
|
150
|
+
if settings.has? :time, syn!: :time_range then # time range given
|
151
|
+
time_range = settings[:time]
|
152
|
+
@initial_time, @target_time = time_range.begin, time_range.end
|
153
|
+
@time_unit = target_time / target_time.to_f
|
154
|
+
else
|
155
|
+
anything = settings[:step] || settings[:sampling]
|
156
|
+
msg = "The simulation is timed, but the constructor lacks any of the " +
|
157
|
+
"time-related arguments: :time, :step, or :sampling!"
|
158
|
+
fail ArgumentError, msg unless anything
|
159
|
+
@time_unit = anything / anything.to_f
|
160
|
+
@initial_time, @target_time = time_unit * 0, time_unit * Float::INFINITY
|
161
|
+
end
|
162
|
+
init_core_and_recorder_subclasses
|
163
|
+
reset_time!
|
164
|
+
@step = settings[:step] || time_unit
|
165
|
+
@default_sampling = settings[:sampling] || step
|
166
|
+
@recorder = Recorder().new sampling: settings[:sampling]
|
216
167
|
end
|
217
|
-
return self
|
218
|
-
end
|
219
|
-
|
220
|
-
# Produces the inspect string for this timed simulation.
|
221
|
-
#
|
222
|
-
def inspect
|
223
|
-
"#<Simulation: Time: #{time}, #{pp.size} places, #{tt.size} " +
|
224
|
-
"transitions, object id: #{object_id}>"
|
225
|
-
end
|
226
168
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
@time = initial_time || 0
|
236
|
-
@next_sampling_time = @time
|
237
|
-
super # otherwise same as for timeless cases
|
238
|
-
end
|
239
|
-
|
240
|
-
# Records a sample, now.
|
241
|
-
def sample!
|
242
|
-
print '.'
|
243
|
-
super time.round( SAMPLING_TIME_DECIMAL_PLACES )
|
244
|
-
end
|
245
|
-
|
246
|
-
# Hook to allow Simulation to react to its state changes.
|
247
|
-
def note_state_change!
|
248
|
-
return nil unless @time.round( 9 ) >= @next_sampling_time.round( 9 )
|
249
|
-
sample!
|
250
|
-
@next_sampling_time += @sampling_period
|
251
|
-
end
|
252
|
-
|
253
|
-
def update_time! Δt=step_size
|
254
|
-
@time += Δt
|
255
|
-
end
|
256
|
-
|
257
|
-
def set_time t
|
258
|
-
@time = t
|
259
|
-
end
|
169
|
+
# Sets up subclasses of +Core+ (the simulator) and +Recorder+ (the sampler)
|
170
|
+
# for timed simulations.
|
171
|
+
#
|
172
|
+
def init_core_and_recorder_subclasses
|
173
|
+
param_class( { Core: YPetri::Core::Timed,
|
174
|
+
Recorder: Recorder },
|
175
|
+
with: { simulation: self } )
|
176
|
+
end
|
260
177
|
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
end
|
178
|
+
# Resets the time to initial time, or to the argument (if provided).
|
179
|
+
#
|
180
|
+
def reset_time! time=nil
|
181
|
+
@time = time.nil? ? initial_time : time
|
182
|
+
end
|
183
|
+
end # module Timed
|
268
184
|
end # module YPetri::Simulation::Timed
|
269
|
-
|
270
|
-
# In general, it is not required that all net elements are simulated with the
|
271
|
-
# same method. Practically, ODE systems have many good simulation methods
|
272
|
-
# available.
|
273
|
-
#
|
274
|
-
# (1) ᴍ(t) = ϝ f(ᴍ, t).dt, where f(ᴍ, t) is a known function.
|
275
|
-
#
|
276
|
-
# Many of these methods depend on the Jacobian, but that may not be available
|
277
|
-
# for some places. Therefore, the places, whose marking defines the system
|
278
|
-
# state, are divided into two categories: "A" (accelerated), for which as
|
279
|
-
# common Jacobian can be found, and "E" places, where "E" can stand either for
|
280
|
-
# "External" or "Euler".
|
281
|
-
#
|
282
|
-
# If we apply the definition of "causal orientation" on A and E places, then it
|
283
|
-
# can be said, that only the transitions causally oriented towards "A" places
|
284
|
-
# are allowed for compliance with the equation (1).
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module YPetri::Simulation::Timeless
|
2
|
+
# A timeless recorder.
|
3
|
+
#
|
4
|
+
class Recorder < YPetri::Simulation::Recorder
|
5
|
+
attr_reader :next_event
|
6
|
+
|
7
|
+
# Like +YPetri::Simulation::Recording#reset+, but allowing for additional
|
8
|
+
# named argument +:next_sample+ that sets the event (label, hash key) of
|
9
|
+
# the next sample.
|
10
|
+
#
|
11
|
+
def reset! **nn
|
12
|
+
super
|
13
|
+
@next_event = nn[:next_event] || 0
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# Records the current system state under a numbered sample.
|
19
|
+
#
|
20
|
+
def sample!
|
21
|
+
super next_event
|
22
|
+
@next_event = @next_event.next # "event" shoud implement next method
|
23
|
+
end
|
24
|
+
end # Recorder
|
25
|
+
end # YPetri::Simulation::Timeless
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# A mixin for timeless simulations.
|
4
|
+
#
|
5
|
+
class YPetri::Simulation
|
6
|
+
module Timeless
|
7
|
+
require_relative 'timeless/recorder'
|
8
|
+
|
9
|
+
# False for timeless simulations.
|
10
|
+
#
|
11
|
+
def timed?
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# Initialization subroutine for timeless simulations. Sets up the
|
18
|
+
# parametrized subclasses +@Core+ (the simulator) and +@Recorder+,
|
19
|
+
# and initializes the +@recorder+ attribute.
|
20
|
+
#
|
21
|
+
def init **settings
|
22
|
+
init_core_and_recorder_subclasses
|
23
|
+
@recorder = Recorder().new # init the recorder
|
24
|
+
end
|
25
|
+
|
26
|
+
# Sets up subclasses of +Core+ (the simulator) and +Recorder+ (the sampler)
|
27
|
+
# for timeless simulations.
|
28
|
+
#
|
29
|
+
def init_core_and_recorder_subclasses
|
30
|
+
param_class( { Core: YPetri::Core::Timeless,
|
31
|
+
Recorder: Recorder },
|
32
|
+
with: { simulation: self } )
|
33
|
+
end
|
34
|
+
end # module Timeless
|
35
|
+
end # module YPetri::Simulation
|
@@ -0,0 +1,58 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
# A mixin for A transition representations.
|
4
|
+
#
|
5
|
+
module YPetri::Simulation::TransitionRepresentation::Type_A
|
6
|
+
attr_reader :assignment_closure
|
7
|
+
|
8
|
+
# Assignment action -- true for A transitions.
|
9
|
+
#
|
10
|
+
def A?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
alias assignment_action? A?
|
14
|
+
alias assignment? A?
|
15
|
+
|
16
|
+
# Normal (non-assignment) action -- false for A transitions
|
17
|
+
#
|
18
|
+
def a?
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
# Initialization subroutine.
|
23
|
+
#
|
24
|
+
def init
|
25
|
+
@assignment_closure = to_assignment_closure
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns the assignments, as they would happen if this A transition fired,
|
29
|
+
# as hash places >> action.
|
30
|
+
#
|
31
|
+
def action
|
32
|
+
act.select { |pl, v| pl.free? }
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the assignments to all places, as they would happen if A transition
|
36
|
+
# could change their values.
|
37
|
+
#
|
38
|
+
def act
|
39
|
+
codomain >> Array( function.( *domain_marking ) )
|
40
|
+
end
|
41
|
+
|
42
|
+
# Builds an assignment closure, which, when called, directly affects the
|
43
|
+
# simulation's marking vector (free places only).
|
44
|
+
#
|
45
|
+
def to_assignment_closure
|
46
|
+
mv, ac = simulation.m_vector, source.action_closure
|
47
|
+
λ = if codomain.size == 1 then
|
48
|
+
target = codomain.first
|
49
|
+
return proc {} if target.clamped?
|
50
|
+
i = target.m_vector_index
|
51
|
+
"-> do mv.send :[]=, #{i}, 0, *ac.( %s ) end"
|
52
|
+
else
|
53
|
+
assignment_code = codomain_assignment_code vector: :mv, source: :act
|
54
|
+
"-> do act = ac.( %s )\n#{assignment_code} end"
|
55
|
+
end
|
56
|
+
eval λ % domain_access_code( vector: :mv )
|
57
|
+
end
|
58
|
+
end # class YPetri::Simulation::TransitionRepresentation::Type_A
|