y_petri 2.1.3 → 2.1.6
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/agent/petri_net_related.rb +25 -5
- data/lib/y_petri/agent/selection.rb +12 -10
- data/lib/y_petri/agent/simulation_related.rb +14 -58
- data/lib/y_petri/agent.rb +15 -17
- data/lib/y_petri/core/timed/euler.rb +13 -15
- data/lib/y_petri/core/timed/pseudo_euler.rb +22 -24
- data/lib/y_petri/core/timed/quasi_euler.rb +15 -17
- data/lib/y_petri/core/timed.rb +42 -44
- data/lib/y_petri/core/timeless/pseudo_euler.rb +12 -14
- data/lib/y_petri/core/timeless.rb +10 -7
- data/lib/y_petri/core.rb +3 -3
- data/lib/y_petri/dsl.rb +46 -46
- data/lib/y_petri/fixed_assets.rb +8 -0
- data/lib/y_petri/net/data_set.rb +238 -0
- data/lib/y_petri/net/own_state.rb +63 -0
- data/lib/y_petri/net/state/feature/delta.rb +98 -71
- data/lib/y_petri/net/state/feature/firing.rb +51 -54
- data/lib/y_petri/net/state/feature/flux.rb +51 -55
- data/lib/y_petri/net/state/feature/gradient.rb +55 -59
- data/lib/y_petri/net/state/feature/marking.rb +55 -59
- data/lib/y_petri/net/state/feature.rb +65 -67
- data/lib/y_petri/net/state/features/record.rb +150 -43
- data/lib/y_petri/net/state/features.rb +252 -96
- data/lib/y_petri/net/state.rb +114 -106
- data/lib/y_petri/net/visualization.rb +3 -2
- data/lib/y_petri/net.rb +29 -24
- data/lib/y_petri/place/arcs.rb +3 -3
- data/lib/y_petri/place/guard.rb +35 -117
- data/lib/y_petri/place/guarded.rb +86 -0
- data/lib/y_petri/place.rb +6 -3
- data/lib/y_petri/simulation/element_representation.rb +2 -2
- data/lib/y_petri/simulation/elements.rb +3 -1
- data/lib/y_petri/simulation/feature_set.rb +3 -1
- data/lib/y_petri/simulation/marking_vector.rb +3 -1
- data/lib/y_petri/simulation/place_mapping.rb +3 -1
- data/lib/y_petri/simulation/places.rb +1 -1
- data/lib/y_petri/simulation/recorder.rb +60 -54
- data/lib/y_petri/simulation/timed/recorder.rb +12 -1
- data/lib/y_petri/simulation/timed.rb +173 -172
- data/lib/y_petri/simulation/transitions/access.rb +97 -29
- data/lib/y_petri/simulation.rb +11 -9
- data/lib/y_petri/transition/{assignment.rb → A.rb} +2 -2
- data/lib/y_petri/transition/{timed.rb → T.rb} +2 -2
- data/lib/y_petri/transition/arcs.rb +3 -3
- data/lib/y_petri/transition/cocking.rb +3 -3
- data/lib/y_petri/transition/{init.rb → construction_convenience.rb} +6 -53
- data/lib/y_petri/transition/{ordinary_timeless.rb → t.rb} +2 -2
- data/lib/y_petri/transition/type.rb +103 -0
- data/lib/y_petri/transition/type_information.rb +103 -0
- data/lib/y_petri/transition/types.rb +107 -0
- data/lib/y_petri/transition/usable_without_world.rb +14 -0
- data/lib/y_petri/transition.rb +87 -101
- data/lib/y_petri/version.rb +1 -1
- data/lib/y_petri/world/dependency.rb +30 -28
- data/lib/y_petri/world.rb +10 -8
- data/test/acceptance/basic_usage_test.rb +3 -3
- data/test/acceptance/simulation_test.rb +3 -3
- data/test/acceptance/simulation_with_physical_units_test.rb +2 -2
- data/test/acceptance/token_game_test.rb +2 -2
- data/test/acceptance/visualization_test.rb +3 -3
- data/test/acceptance_tests.rb +2 -2
- data/test/agent_test.rb +1 -1
- data/test/net_test.rb +41 -17
- data/test/place_test.rb +1 -1
- data/test/simulation_test.rb +39 -39
- data/test/transition_test.rb +1 -1
- data/test/world_test.rb +1 -1
- data/test/y_petri_test.rb +1 -1
- metadata +13 -8
- data/lib/y_petri/net/state/features/dataset.rb +0 -135
- data/lib/y_petri/transition/construction.rb +0 -311
@@ -1,10 +1,12 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
# Basic elements of a simulation, a mixin intended for YPetri::Simulation.
|
2
4
|
#
|
3
5
|
class YPetri::Simulation
|
4
6
|
# Represents a set of features of a simulation state.
|
5
7
|
#
|
6
8
|
class FeatureSet
|
7
|
-
|
9
|
+
★ DependencyInjection
|
8
10
|
|
9
11
|
attr_reader :marking, :firing, :delta
|
10
12
|
|
@@ -1,54 +1,60 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# A machine that receives alerts during simulation and records a recording
|
4
|
+
# according to its implementation. Alerts are received via +#alert+ method.
|
5
|
+
# The recording bein recorded is stored in @recording instance variable.
|
6
|
+
# This can be reset by +#reset!+ method, which also accepts arguments to
|
7
|
+
# change the recorder settings and/or insert another recording.
|
8
|
+
#
|
9
|
+
class YPetri::Simulation::Recorder
|
10
|
+
★ YPetri::Simulation::Dependency
|
11
|
+
|
12
|
+
SAMPLING_DECIMAL_PLACES = 5
|
13
|
+
|
14
|
+
attr_reader :features, :recording
|
15
|
+
delegate :simulation, to: "self.class"
|
16
|
+
delegate :reconstruct, :reduce, to: :recording
|
17
|
+
|
18
|
+
# Initializes the recorder. Takes 2 arguments: +:features+ expecting the
|
19
|
+
# feature set to record during simulation, and +:recording+, expecting the
|
20
|
+
# initial state of the recording.
|
21
|
+
#
|
22
|
+
def initialize features: net.State.marking( free_pp ),
|
23
|
+
recording: nil,
|
24
|
+
**nn
|
25
|
+
@features = net.State.features( features )
|
26
|
+
if recording then reset! recording: recording else reset! end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Construct a new recording based on +features+.
|
30
|
+
#
|
31
|
+
def new_recording
|
32
|
+
features.new_dataset
|
33
|
+
end
|
34
|
+
|
35
|
+
# Assigns to @recording a new Dataset instance. Without arguments, the new
|
36
|
+
# recording is empty. With +:recording+ named argument supplied, the new
|
37
|
+
# recording is filled with the prescribed contents.
|
38
|
+
#
|
39
|
+
def reset! **nn
|
40
|
+
@features = net.State.features( nn[:features] || @features )
|
41
|
+
@recording = new_recording
|
42
|
+
@recording.update Hash[ nn[:recording] ] if nn[:recording]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Hook to be called by simulators whenever there is a state change. The
|
46
|
+
# decision to sample is then the business of the recorder.
|
47
|
+
#
|
48
|
+
def alert
|
49
|
+
sample! # vanilla recorder samples at every occasion
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# Records the current state as a pair { sampling_event => system_state }.
|
55
|
+
#
|
56
|
+
def sample! event
|
57
|
+
record = simulation.get_features( features )
|
58
|
+
recording[ event ] = record.dump( precision: SAMPLING_DECIMAL_PLACES )
|
59
|
+
end
|
60
|
+
end # class YPetri::Simulation::Recorder
|
@@ -19,6 +19,12 @@ module YPetri::Simulation::Timed
|
|
19
19
|
@next_time = next_time
|
20
20
|
end
|
21
21
|
|
22
|
+
# Construct a new recording based on +features+.
|
23
|
+
#
|
24
|
+
def new_recording
|
25
|
+
features.new_dataset type: :timed
|
26
|
+
end
|
27
|
+
|
22
28
|
# Like +YPetri::Simulation::Recorder#reset+, but allowing for an additional
|
23
29
|
# named argument +:next_time+ that sets the next sampling time, and
|
24
30
|
# +:sampling:, resetting the sampling period.
|
@@ -37,7 +43,12 @@ module YPetri::Simulation::Timed
|
|
37
43
|
t2 = next_time.round( 9 )
|
38
44
|
if t >= t2 then # it's time to sample
|
39
45
|
sample!
|
40
|
-
|
46
|
+
begin
|
47
|
+
@next_time += sampling
|
48
|
+
rescue NoMethodError => err
|
49
|
+
( puts "Here go error #{err}"; Kernel::p @next_time; Kernel::p sampling )
|
50
|
+
end
|
51
|
+
|
41
52
|
end
|
42
53
|
end
|
43
54
|
|
@@ -1,184 +1,185 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
require_relative 'timed/recorder'
|
8
|
-
|
9
|
-
DEFAULT_SETTINGS = -> do { step: 0.1, sampling: 5, time: 0..60 } end
|
10
|
-
|
11
|
-
def self.included receiver
|
12
|
-
receiver.Recording.class_exec { prepend Recording }
|
13
|
-
end
|
14
|
-
|
15
|
-
# True for timed simulations.
|
16
|
-
#
|
17
|
-
def timed?
|
18
|
-
true
|
19
|
-
end
|
20
|
-
|
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
|
45
|
-
|
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 )
|
53
|
-
end
|
54
|
-
|
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 )
|
60
|
-
end
|
61
|
-
|
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
|
69
|
-
|
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
|
95
|
-
|
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
|
3
|
+
# A mixin for timed simulations, used by an +#extend+ call during init.
|
4
|
+
#
|
5
|
+
module YPetri::Simulation::Timed
|
6
|
+
require_relative 'timed/recorder'
|
102
7
|
|
103
|
-
|
104
|
-
#
|
105
|
-
def increment_time! Δt=step
|
106
|
-
@time += Δt
|
107
|
-
recorder.alert
|
108
|
-
end
|
8
|
+
DEFAULT_SETTINGS = -> do { step: 0.1, sampling: 5, time: 0..60 } end
|
109
9
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
10
|
+
# True for timed simulations.
|
11
|
+
#
|
12
|
+
def timed?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :time,
|
17
|
+
:time_unit,
|
18
|
+
:initial_time,
|
19
|
+
:target_time,
|
20
|
+
:step,
|
21
|
+
:default_sampling
|
22
|
+
|
23
|
+
alias starting_time initial_time
|
24
|
+
alias ending_time target_time
|
25
|
+
|
26
|
+
delegate :flux_vector_TS,
|
27
|
+
:gradient_TS,
|
28
|
+
:gradient_Ts,
|
29
|
+
:gradient,
|
30
|
+
:flux_vector,
|
31
|
+
to: :core
|
32
|
+
|
33
|
+
delegate :sampling, to: :recorder
|
34
|
+
|
35
|
+
# Reads the time range (initial_time..target_time) of the simulation.
|
36
|
+
#
|
37
|
+
def time_range
|
38
|
+
initial_time..target_time
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returnst the settings pertaining to the Timed aspect of the simulation,
|
42
|
+
# that is, +:step+, +:sampling+ and +:time+.
|
43
|
+
#
|
44
|
+
def settings all=false
|
45
|
+
super.update( step: step,
|
46
|
+
sampling: sampling,
|
47
|
+
time: time_range )
|
48
|
+
end
|
49
|
+
|
50
|
+
# Same as +#run!+, but guards against run upto infinity.
|
51
|
+
#
|
52
|
+
def run( upto: target_time, final_step: :exact )
|
53
|
+
fail "Upto time equals infinity!" if upto == Float::INFINITY
|
54
|
+
run!( upto: upto, final_step: final_step )
|
55
|
+
end
|
56
|
+
|
57
|
+
# Near alias for +#run_upto+. Accepts +:upto+ named argument, using
|
58
|
+
# @target_time attribute as a default. The second optional argument,
|
59
|
+
# +:final_step+, has the same options as in +#run_upto+ method.
|
60
|
+
#
|
61
|
+
def run!( upto: target_time, final_step: :exact )
|
62
|
+
run_upto( upto, final_step: final_step )
|
63
|
+
end
|
64
|
+
|
65
|
+
# Runs the simulation until the target time. Named argument :final_step has
|
66
|
+
# options :just_before, :just_after and :exact, and tunes the simulation
|
67
|
+
# behavior towards the end of the run.
|
68
|
+
#
|
69
|
+
# just_before: last step has normal size, simulation stops before or just
|
70
|
+
# on the target time
|
71
|
+
# just_after: last step has normal size, simulation stops after or just
|
72
|
+
# on the target time_step
|
73
|
+
# exact: simulation stops exactly on the prescribed time, last step
|
74
|
+
# is shortened if necessary
|
75
|
+
#
|
76
|
+
def run_upto( target_time, final_step: :exact )
|
77
|
+
case final_step
|
78
|
+
when :before then
|
79
|
+
step! while time + step <= target_time
|
80
|
+
when :exact then
|
81
|
+
step! while time + step < target_time
|
82
|
+
step!( target_time - time )
|
83
|
+
@time = target_time
|
84
|
+
when :after then
|
85
|
+
step! while time < target_time
|
86
|
+
else
|
87
|
+
fail ArgumentError, "Unrecognized :final_step option: #{final_step}"
|
115
88
|
end
|
89
|
+
end
|
116
90
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
91
|
+
# String representation of this timed simulation.
|
92
|
+
#
|
93
|
+
def to_s
|
94
|
+
"#<Simulation: time: %s, pp: %s, tt: %s, oid: %s>" %
|
95
|
+
[ time, pp.size, tt.size, object_id ]
|
96
|
+
end
|
97
|
+
|
98
|
+
# Increments the simulation's time and alerts the recorder.
|
99
|
+
#
|
100
|
+
def increment_time! Δt=step
|
101
|
+
@time += Δt
|
102
|
+
recorder.alert
|
103
|
+
end
|
104
|
+
|
105
|
+
# Resets the timed simulation.
|
106
|
+
#
|
107
|
+
def reset! **nn
|
108
|
+
@time = initial_time || time_unit * 0
|
109
|
+
super
|
110
|
+
end
|
111
|
+
|
112
|
+
# Customized dup method that allows to modify the attributes of
|
113
|
+
# the duplicate upon creation.
|
114
|
+
#
|
115
|
+
def dup time: time, **nn
|
116
|
+
super( **nn ).tap { |i| i.reset_time! time }
|
117
|
+
end
|
118
|
+
alias at dup
|
119
|
+
|
120
|
+
# Returns the zero gradient. Optionally, places can be specified, for which
|
121
|
+
# the zero vector is returned.
|
122
|
+
#
|
123
|
+
def zero_gradient places: nil
|
124
|
+
return zero_gradient places: places() if places.nil?
|
125
|
+
places.map { |id|
|
126
|
+
p = place( id )
|
127
|
+
( p.free? ? p.initial_marking : p.clamp ) * 0 / time_unit
|
128
|
+
}.to_column_vector
|
129
|
+
end
|
130
|
+
alias zero_∇ zero_gradient
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
# Initialization subroutine for timed simulations. Expects named arguments
|
135
|
+
# +:time+ (alias +:time_range+), meaning the simulation time range (a Range
|
136
|
+
# of initial_time..target_time), +:step+, meaning time step of the
|
137
|
+
# simulation, and +:sampling+, meaning sampling period of the simulation.
|
138
|
+
#
|
139
|
+
# Initializes the time-related attributes @initial_time, @target_time,
|
140
|
+
# @time_unit and @time (via +#reset_time!+ call). Also sets up the
|
141
|
+
# parametrized subclasses +@Core+ and +@Recorder+, and initializes the
|
142
|
+
# +@recorder+ attribute.
|
143
|
+
#
|
144
|
+
def init **settings
|
145
|
+
if settings.has? :time, syn!: :time_range then # time range given
|
146
|
+
case settings[:time]
|
147
|
+
when Range then
|
151
148
|
time_range = settings[:time]
|
152
149
|
@initial_time, @target_time = time_range.begin, time_range.end
|
153
|
-
@time_unit =
|
150
|
+
@time_unit = initial_time.class.one
|
154
151
|
else
|
155
|
-
|
156
|
-
|
157
|
-
|
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
|
152
|
+
@initial_time = settings[:time]
|
153
|
+
@time_unit = initial_time.class.one
|
154
|
+
@target_time = time_unit * Float::INFINITY
|
161
155
|
end
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
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 } )
|
156
|
+
else
|
157
|
+
anything = settings[:step] || settings[:sampling]
|
158
|
+
msg = "The simulation is timed, but the constructor lacks any of the " +
|
159
|
+
"time-related arguments: :time, :step, or :sampling!"
|
160
|
+
fail ArgumentError, msg unless anything
|
161
|
+
@time_unit = anything.class.one
|
162
|
+
@initial_time, @target_time = time_unit * 0, time_unit * Float::INFINITY
|
176
163
|
end
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
164
|
+
init_core_and_recorder_subclasses
|
165
|
+
reset_time!
|
166
|
+
@step = settings[:step] || time_unit
|
167
|
+
@default_sampling = settings[:sampling] || step
|
168
|
+
@recorder = Recorder().new sampling: settings[:sampling]
|
169
|
+
end
|
170
|
+
|
171
|
+
# Sets up subclasses of +Core+ (the simulator) and +Recorder+ (the sampler)
|
172
|
+
# for timed simulations.
|
173
|
+
#
|
174
|
+
def init_core_and_recorder_subclasses
|
175
|
+
param_class( { Core: YPetri::Core::Timed,
|
176
|
+
Recorder: Recorder },
|
177
|
+
with: { simulation: self } )
|
178
|
+
end
|
179
|
+
|
180
|
+
# Resets the time to initial time, or to the argument (if provided).
|
181
|
+
#
|
182
|
+
def reset_time! time=nil
|
183
|
+
@time = time.nil? ? initial_time : time
|
184
|
+
end
|
184
185
|
end # module YPetri::Simulation::Timed
|