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