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
@@ -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
|