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.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/lib/y_petri/{manipulator → agent}/hash_key_pointer.rb +2 -2
  3. data/lib/y_petri/agent/petri_net_related.rb +115 -0
  4. data/lib/y_petri/{manipulator → agent}/selection.rb +2 -1
  5. data/lib/y_petri/{manipulator/simulation_related_methods.rb → agent/simulation_related.rb} +93 -110
  6. data/lib/y_petri/agent.rb +22 -0
  7. data/lib/y_petri/core/timed/euler.rb +20 -0
  8. data/lib/y_petri/core/timed/pseudo_euler.rb +31 -0
  9. data/lib/y_petri/core/timed/quasi_euler.rb +23 -0
  10. data/lib/y_petri/core/timed.rb +70 -0
  11. data/lib/y_petri/core/timeless/pseudo_euler.rb +20 -0
  12. data/lib/y_petri/core/timeless.rb +12 -0
  13. data/lib/y_petri/core.rb +100 -0
  14. data/lib/y_petri/dsl.rb +66 -0
  15. data/lib/y_petri/fixed_assets.rb +7 -0
  16. data/lib/y_petri/net/element_access.rb +239 -0
  17. data/lib/y_petri/net/state/feature/delta.rb +88 -0
  18. data/lib/y_petri/net/state/feature/firing.rb +57 -0
  19. data/lib/y_petri/net/state/feature/flux.rb +58 -0
  20. data/lib/y_petri/net/state/feature/gradient.rb +75 -0
  21. data/lib/y_petri/net/state/feature/marking.rb +62 -0
  22. data/lib/y_petri/net/state/feature.rb +79 -0
  23. data/lib/y_petri/net/state/features/dataset.rb +135 -0
  24. data/lib/y_petri/net/state/features/record.rb +50 -0
  25. data/lib/y_petri/net/state/features.rb +126 -0
  26. data/lib/y_petri/net/state.rb +121 -0
  27. data/lib/y_petri/net/timed.rb +8 -0
  28. data/lib/y_petri/net/visualization.rb +3 -3
  29. data/lib/y_petri/net.rb +73 -77
  30. data/lib/y_petri/place.rb +8 -3
  31. data/lib/y_petri/simulation/dependency.rb +107 -0
  32. data/lib/y_petri/simulation/element_representation.rb +20 -0
  33. data/lib/y_petri/simulation/elements/access.rb +57 -0
  34. data/lib/y_petri/simulation/elements.rb +45 -0
  35. data/lib/y_petri/simulation/feature_set.rb +21 -0
  36. data/lib/y_petri/simulation/initial_marking/access.rb +55 -0
  37. data/lib/y_petri/simulation/initial_marking.rb +15 -0
  38. data/lib/y_petri/simulation/marking_clamps/access.rb +34 -0
  39. data/lib/y_petri/simulation/marking_clamps.rb +18 -0
  40. data/lib/y_petri/simulation/marking_vector/access.rb +106 -0
  41. data/lib/y_petri/simulation/marking_vector.rb +156 -0
  42. data/lib/y_petri/simulation/matrix.rb +64 -0
  43. data/lib/y_petri/simulation/place_mapping.rb +62 -0
  44. data/lib/y_petri/simulation/place_representation.rb +74 -0
  45. data/lib/y_petri/simulation/places/access.rb +121 -0
  46. data/lib/y_petri/simulation/places/clamped.rb +8 -0
  47. data/lib/y_petri/simulation/places/free.rb +8 -0
  48. data/lib/y_petri/simulation/places/types.rb +25 -0
  49. data/lib/y_petri/simulation/places.rb +41 -0
  50. data/lib/y_petri/simulation/recorder.rb +54 -0
  51. data/lib/y_petri/simulation/timed/recorder.rb +53 -0
  52. data/lib/y_petri/simulation/timed.rb +161 -261
  53. data/lib/y_petri/simulation/timeless/recorder.rb +25 -0
  54. data/lib/y_petri/simulation/timeless.rb +35 -0
  55. data/lib/y_petri/simulation/transition_representation/A.rb +58 -0
  56. data/lib/y_petri/simulation/transition_representation/S.rb +45 -0
  57. data/lib/y_petri/simulation/transition_representation/T.rb +80 -0
  58. data/lib/y_petri/simulation/transition_representation/TS.rb +46 -0
  59. data/lib/y_petri/simulation/transition_representation/Ts.rb +32 -0
  60. data/lib/y_petri/simulation/transition_representation/a.rb +30 -0
  61. data/lib/y_petri/simulation/transition_representation/s.rb +29 -0
  62. data/lib/y_petri/simulation/transition_representation/t.rb +37 -0
  63. data/lib/y_petri/simulation/transition_representation/tS.rb +38 -0
  64. data/lib/y_petri/simulation/transition_representation/ts.rb +32 -0
  65. data/lib/y_petri/simulation/transition_representation/types.rb +62 -0
  66. data/lib/y_petri/simulation/transition_representation.rb +79 -0
  67. data/lib/y_petri/simulation/transitions/A.rb +40 -0
  68. data/lib/y_petri/simulation/transitions/S.rb +24 -0
  69. data/lib/y_petri/simulation/transitions/T.rb +34 -0
  70. data/lib/y_petri/simulation/transitions/TS.rb +57 -0
  71. data/lib/y_petri/simulation/transitions/Ts.rb +60 -0
  72. data/lib/y_petri/simulation/transitions/a.rb +8 -0
  73. data/lib/y_petri/simulation/transitions/access.rb +186 -0
  74. data/lib/y_petri/simulation/transitions/s.rb +9 -0
  75. data/lib/y_petri/simulation/transitions/t.rb +22 -0
  76. data/lib/y_petri/simulation/transitions/tS.rb +55 -0
  77. data/lib/y_petri/simulation/transitions/ts.rb +58 -0
  78. data/lib/y_petri/simulation/transitions/types.rb +98 -0
  79. data/lib/y_petri/simulation/transitions.rb +21 -0
  80. data/lib/y_petri/simulation.rb +176 -781
  81. data/lib/y_petri/transition/assignment.rb +7 -5
  82. data/lib/y_petri/transition/construction.rb +119 -187
  83. data/lib/y_petri/transition/init.rb +311 -0
  84. data/lib/y_petri/transition/ordinary_timeless.rb +8 -6
  85. data/lib/y_petri/transition/timed.rb +11 -18
  86. data/lib/y_petri/transition.rb +104 -132
  87. data/lib/y_petri/version.rb +1 -1
  88. data/lib/y_petri/world/dependency.rb +40 -0
  89. data/lib/y_petri/world/petri_net_related.rb +61 -0
  90. data/lib/y_petri/{workspace/simulation_related_methods.rb → world/simulation_related.rb} +42 -49
  91. data/lib/y_petri/world.rb +27 -0
  92. data/lib/y_petri.rb +47 -99
  93. data/test/{manipulator_test.rb → agent_test.rb} +19 -17
  94. data/{lib/y_petri → test/examples}/demonstrator.rb +0 -0
  95. data/{lib/y_petri → test/examples}/demonstrator_2.rb +1 -0
  96. data/{lib/y_petri → test/examples}/demonstrator_3.rb +0 -0
  97. data/{lib/y_petri → test/examples}/demonstrator_4.rb +0 -0
  98. data/test/examples/example_2.rb +16 -0
  99. data/test/{manual_examples.rb → examples/manual_examples.rb} +0 -0
  100. data/test/net_test.rb +126 -121
  101. data/test/place_test.rb +1 -1
  102. data/test/sim_test +565 -0
  103. data/test/simulation_test.rb +338 -264
  104. data/test/transition_test.rb +77 -174
  105. data/test/world_mock.rb +12 -0
  106. data/test/{workspace_test.rb → world_test.rb} +19 -20
  107. data/test/y_petri_test.rb +4 -5
  108. metadata +101 -26
  109. data/lib/y_petri/dependency_injection.rb +0 -45
  110. data/lib/y_petri/manipulator/petri_net_related_methods.rb +0 -74
  111. data/lib/y_petri/manipulator.rb +0 -20
  112. data/lib/y_petri/net/selections.rb +0 -209
  113. data/lib/y_petri/simulation/collections.rb +0 -460
  114. data/lib/y_petri/workspace/parametrized_subclassing.rb +0 -22
  115. data/lib/y_petri/workspace/petri_net_related_methods.rb +0 -88
  116. data/lib/y_petri/workspace.rb +0 -16
  117. 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
- # -*- coding: utf-8 -*-
1
+ # encoding: utf-8
2
2
 
3
- # A mixin for timed simulations.
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
- def sampling_rate; 1 / sampling_period end
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
- # # Makes one Gillespie step
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
- # # Return projection of Δᴍ by mysode-ing the interior.
68
- # def project_mysode_interior( Δt )
69
- # # So far, no interior
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
- # Near alias for #run!, checks against infinite run.
85
- #
86
- def run( until_time=target_time, final_step: :exact )
87
- fail "Target time equals infinity!" if target_time = Float::INFINITY
88
- run! until_time, final_step: final_step
89
- end
15
+ # True for timed simulations.
16
+ #
17
+ def timed?
18
+ true
19
+ end
90
20
 
91
- # Near alias for #run_until, uses @target_time as :until_time by default.
92
- #
93
- def run!( until_time=target_time, final_step: :exact )
94
- run_until until_time, final_step: final_step
95
- end
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
- # Runs the simulation until the target time, using step! method. The second
98
- # optional parameter tunes the behavior towards the end of the run, with
99
- # alternatives :just_before, :just_after and :exact (default).
100
- #
101
- # just_before: all steps have normal size, simulation stops
102
- # before or just on the target time
103
- # just_after: all steps have normal size, simulation stops
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
- # Scalar field gradient for free places.
124
- #
125
- def gradient_for_free_places
126
- g_sR = gradient_for_sR
127
- if g_sR then
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
- # Gradient for free places as a hash { place_name: ∂ / ∂ᴛ }.
135
- #
136
- def
137
- free_places :gradient_for_free_places
138
- end
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
- # Scalar field gradient for all places.
141
- #
142
- def gradient_for_all_places
143
- F2A() * gradient_for_free_places
144
- end
145
- alias gradient gradient_for_all_places
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
- # Δ state of free places that would happen by a single Euler step Δt.
148
- #
149
- def Δ_Euler_for_free_places( Δt=step_size )
150
- # Here, represents all R transitions, to which TSr and Tsr are added:
151
- delta_free = gradient_for_free_places * Δt
152
- delta_free + Δ_TSr( Δt ) + Δ_Tsr( Δt )
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
- # Δ state of all places that would happen by a single Euler step Δt.
158
- #
159
- def Δ_Euler_for_all_places( Δt=step_size )
160
- F2A() * ΔE( Δt )
161
- end
162
- alias Δ_euler_for_all_places Δ_Euler_for_all_places
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
- # Makes one Euler step with T transitions. Timeless transitions are not
166
- # affected.
167
- #
168
- def Euler_step!( Δt=@step_size ) # implicit Euler method
169
- delta = Δ_Euler_for_free_places( Δt )
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
- # Fires timeless transitions once. Time and timed transitions are not
181
- # affected.
182
- #
183
- def timeless_transitions_all_fire!
184
- try "to update marking" do
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
- try "to fire the assignment transitions" do
191
- assignment_transitions_all_fire!
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
- end
194
- alias t_all_fire! timeless_transitions_all_fire!
195
-
196
- # At the moment, near alias of #euler_step!
197
- #
198
- def step! Δt=step_size
199
- case @method
200
- when :Euler then
201
- Euler_step! Δt
202
- note_state_change!
203
- when :Euler_with_timeless_transitions_firing_after_each_step,
204
- :pseudo_Euler then
205
- Euler_step! Δt
206
- timeless_transitions_all_fire!
207
- note_state_change!
208
- when :Euler_with_timeless_transitions_firing_after_each_time_tick,
209
- :quasi_Euler then
210
- raise # FIXME: quasi_Euler doesn't work yet
211
- Euler_step! Δt
212
- # if time tick has elapsed, call #timeless_transitions_all_fire!
213
- note_state_change!
214
- else
215
- raise "Unrecognized simulation method: #@method !!!"
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
- # Produces a string brief
228
- def to_s # :nodoc:
229
- "Simulation[T: #{time}, pp: #{pp.size}, tt: #{tt.size}]"
230
- end
231
-
232
- private
233
-
234
- def reset!
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
- # Duplicate creation.
262
- #
263
- def dup
264
- instance = super
265
- instance.send :set_time, time
266
- return instance
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