y_petri 2.1.3 → 2.1.6
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/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
|