y_petri 2.3.11 → 2.3.12
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/core/timed/{pseudo_euler.rb → basic.rb} +4 -4
- data/lib/y_petri/core/timed/runge_kutta.rb +1 -3
- data/lib/y_petri/core/timed/{quasi_euler.rb → ticked.rb} +2 -2
- data/lib/y_petri/core/timed.rb +34 -8
- data/lib/y_petri/core/timeless/basic.rb +20 -0
- data/lib/y_petri/core/timeless.rb +18 -6
- data/lib/y_petri/core.rb +94 -68
- data/lib/y_petri/fixed_assets.rb +1 -1
- data/lib/y_petri/simulation/timed.rb +4 -12
- data/lib/y_petri/simulation/timeless.rb +27 -35
- data/lib/y_petri/simulation.rb +1 -1
- data/lib/y_petri/version.rb +1 -1
- data/test/simulation_test.rb +5 -5
- metadata +5 -7
- data/lib/y_petri/core/timed/methods.rb +0 -23
- data/lib/y_petri/core/timeless/methods.rb +0 -15
- data/lib/y_petri/core/timeless/pseudo_euler.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3fe2aaae8033f7806b851cd4293f555d15beb5a5
|
4
|
+
data.tar.gz: 79271fa571273448d6716b547785f66805b5d5b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 14a6aba15f89a662ee2e13e16dc09695c1d00ccbe60ce7f969b9ae2a348f9bc8c98f51a2090643671b568d06f36419d162dda8ef5005fd34f014ec28aa75a855
|
7
|
+
data.tar.gz: 66681f89b12fc840eabc2d225af8e137492870c61c8d844b8fcb73cd78ef712bfe46c497882be534a98e5287bb76e7aa97a726f032bd16346cb79a735d4069e9
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
#
|
4
|
-
#
|
3
|
+
# Basic Petri net execution method for timed and hybrid nets. Works also for
|
4
|
+
# timeless nets, but for those, Core::Timeless::Basic is slightly more efficient.
|
5
5
|
#
|
6
|
-
module YPetri::Core::Timed::
|
6
|
+
module YPetri::Core::Timed::Basic
|
7
7
|
# Computes Δ for the period of Δt. Its result is a sum of the contribution of
|
8
8
|
# timed transitions over the period Δt and the contribution of timeless
|
9
9
|
# transitions as if each fired once.
|
@@ -23,4 +23,4 @@ module YPetri::Core::Timed::PseudoEuler
|
|
23
23
|
simulation.increment_time! Δt
|
24
24
|
alert! # alerts the sampler that the system has changed
|
25
25
|
end
|
26
|
-
end # YPetri::Core::Timed::
|
26
|
+
end # YPetri::Core::Timed::Basic
|
@@ -4,9 +4,7 @@
|
|
4
4
|
#
|
5
5
|
module YPetri::Core::Timed::RungeKutta
|
6
6
|
def delta Δt
|
7
|
-
|
8
|
-
|
9
|
-
# f = simulation.method :gradient, parameter: :state
|
7
|
+
# The f below is from the equation state' = f( state )
|
10
8
|
f = lambda { |mv| # mv is the marking vector of the free places
|
11
9
|
result = "make hash from free places of the simulation to zeros"
|
12
10
|
nonstoichiometric_transitions.each { |t|
|
@@ -4,7 +4,7 @@
|
|
4
4
|
# and assignment transitions. Unlike +pseudo_euler+, which fires every step,
|
5
5
|
# +quasi_euler+ fires every time tick. Not implemented yet.
|
6
6
|
#
|
7
|
-
module YPetri::Core::Timed::
|
7
|
+
module YPetri::Core::Timed::Ticked
|
8
8
|
# Computes Δ for the period of Δt. Not mplemented yet.
|
9
9
|
#
|
10
10
|
def delta Δt
|
@@ -19,4 +19,4 @@ module YPetri::Core::Timed::QuasiEuler
|
|
19
19
|
# Now one would have to compare whichever comes first, time tick or the
|
20
20
|
# end of Δt, and then again and again, until Δt is fired...
|
21
21
|
end
|
22
|
-
end # YPetri::Core::Timed::
|
22
|
+
end # YPetri::Core::Timed::Ticked
|
data/lib/y_petri/core/timed.rb
CHANGED
@@ -1,10 +1,34 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
# Timed simulation core.
|
3
|
+
# Timed simulation core. Knows several simulation methods applicable to
|
4
|
+
# timed nets.
|
4
5
|
#
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
class YPetri::Core::Timed
|
7
|
+
★ YPetri::Core
|
8
|
+
|
9
|
+
require_relative 'timed/basic'
|
10
|
+
require_relative 'timed/ticked' #
|
11
|
+
require_relative 'timed/euler'
|
12
|
+
require_relative 'timed/runge_kutta'
|
13
|
+
require_relative 'timed/gillespie'
|
14
|
+
|
15
|
+
METHODS = {
|
16
|
+
basic: Basic, # simple PN execution, timeless tt fire after each step
|
17
|
+
ticked: Ticked, # like basic, but timeless tt fire at every time tick
|
18
|
+
euler: Euler, # for timed nets only
|
19
|
+
runge_kutta: RungeKutta, # for timed nets only
|
20
|
+
gillespie: Gillespie # for timed nets only
|
21
|
+
}
|
22
|
+
|
23
|
+
def initialize **named_args
|
24
|
+
super
|
25
|
+
extend METHODS.fetch simulation_method
|
26
|
+
# look in the Core#initialize method for the closures and parameters
|
27
|
+
# and such. Here, we could define:
|
28
|
+
@Ts_gradient_closure = simulation.Ts_gradient_closure
|
29
|
+
# which would remind us that this machine needs to be
|
30
|
+
# actually defined internal to Core instance
|
31
|
+
end
|
8
32
|
|
9
33
|
# Makes a single step by Δt.
|
10
34
|
#
|
@@ -25,6 +49,8 @@ module YPetri::Core::Timed
|
|
25
49
|
#
|
26
50
|
def gradient_Ts
|
27
51
|
simulation.Ts_gradient_closure.call
|
52
|
+
# this could be
|
53
|
+
# @Ts_gradient_closure.call
|
28
54
|
end
|
29
55
|
|
30
56
|
# Gradient contribution by TS transitions.
|
@@ -51,11 +77,9 @@ module YPetri::Core::Timed
|
|
51
77
|
alias propensity_vector_TS flux_vector_TS
|
52
78
|
end # module YPetri::Core::Timed
|
53
79
|
|
54
|
-
#
|
55
|
-
# same method. Practically, ODE systems have many good simulation methods
|
56
|
-
# available.
|
80
|
+
# Textbook simulation methods ODE systems have many good simulation methods available.
|
57
81
|
#
|
58
|
-
# (1)
|
82
|
+
# (1) mv' = ϝ f(mv, t).dt, where f(m, t) is a known function.
|
59
83
|
#
|
60
84
|
# Many of these methods depend on the Jacobian, but that may not be available
|
61
85
|
# for some places. Therefore, the places, whose marking defines the system
|
@@ -66,3 +90,5 @@ end # module YPetri::Core::Timed
|
|
66
90
|
# If we apply the definition of "causal orientation" on A and E places, then it
|
67
91
|
# can be said, that only the transitions causally oriented towards "A" places
|
68
92
|
# are allowed for compliance with the equation (1).
|
93
|
+
|
94
|
+
# In general, it is not required that all net nodes are simulated with the same method.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# The basic simulation method in YPetri is simple Petri net (PN) execution. While
|
4
|
+
# in principle applicable to any PN type, it can be made slightly more efficient
|
5
|
+
# if it is known in advance that no no timed transitions will be in the net.
|
6
|
+
#
|
7
|
+
module YPetri::Core::Timeless::Basic
|
8
|
+
# Peforms a single step of the basic method.
|
9
|
+
#
|
10
|
+
def step!
|
11
|
+
# Compute the sum of the contribution of ts and tS transitions, and
|
12
|
+
# increment the free marking vector by it.
|
13
|
+
increment_free_vector by: delta_ts + delta_tS
|
14
|
+
# Fire all the assignment transitions in their order.
|
15
|
+
fire_all_assignment_transitions!
|
16
|
+
# before: assignment_transitions_all_fire!
|
17
|
+
# Alert the recorder(s) that the system has changed.
|
18
|
+
alert!
|
19
|
+
end
|
20
|
+
end # module YPetri::Core::Timeless::Basic
|
@@ -1,15 +1,27 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
# Timeless simulator
|
3
|
+
# Timeless simulator core. Knows thus far only one, but potentially several
|
4
|
+
# methods applicable to timeless systems simulations.
|
4
5
|
#
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
class YPetri::Core::Timeless
|
7
|
+
★ YPetri::Core
|
8
|
+
|
9
|
+
require_relative 'timeless/basic'
|
10
|
+
|
11
|
+
METHODS = { basic: Basic } # basic PN execution
|
12
|
+
# Note: the reason why Timeless core has distinct basic method is because
|
13
|
+
# without having to consider timed transitions, it can be made simpler.
|
8
14
|
|
9
|
-
|
15
|
+
def initialize **named_args
|
16
|
+
super
|
17
|
+
extend METHODS.fetch simulation_method
|
18
|
+
end
|
19
|
+
|
20
|
+
# Computes the system state delta.
|
10
21
|
#
|
11
22
|
def delta
|
12
|
-
delta_timeless
|
23
|
+
delta_timeless # this method was taken from core.rb
|
24
|
+
# delta_ts + delta_tS # this is the contents of delta_timeless method
|
13
25
|
end
|
14
26
|
|
15
27
|
# Computes the system state delta.
|
data/lib/y_petri/core.rb
CHANGED
@@ -1,83 +1,62 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
# This
|
3
|
+
# This module represents a simulation core (execution machine), which can be
|
4
|
+
# either timed (class Core::Timed) or timeless (class Core::Timeless).
|
4
5
|
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# into the simulation's state. What needs to be done is a simulation
|
9
|
-
# that at least hints the process of core recruitment for the requested
|
10
|
-
# operation (be it step, step backwards, run forward aso.) and then
|
11
|
-
# imprints the core with its current marking vector, tells the core
|
12
|
-
# what to do, and then reads the result and updates its marking vector
|
13
|
-
# accordingly. There are multiple possibilities, such as constructing
|
14
|
-
# a new core for each operation, or keeping the same core for all the
|
15
|
-
# operations using a given method. A simulation method (like euler,
|
16
|
-
# gillespie, or runge-kutta) should be associated not so much with
|
17
|
-
# the simulation object, as it should be associated with the core
|
18
|
-
# object. A core object should be more or less one-trick pony. While
|
19
|
-
# later, it is possible for a simulation to have broader simulation
|
20
|
-
# strategy, or "method" in the broader sense. But simulation should
|
21
|
-
# also avoid doing too much, because above it, there is Agent class,
|
22
|
-
# and this class can be taught to do the more complicated things
|
23
|
-
# such as parameter optimization or computation of control coefficients
|
24
|
-
# and such. It is also possible to construct more specialized agent-like
|
25
|
-
# classes for these more specialized tasks, since the main purpose
|
26
|
-
# of Agent class, as I saw it, was to represent the user (represent
|
27
|
-
# what the user means), to provide the user interface.
|
28
|
-
|
6
|
+
module YPetri::Core
|
7
|
+
★ YPetri::Simulation::Dependency # dependency stays for now
|
8
|
+
|
29
9
|
require_relative 'core/timed'
|
30
10
|
require_relative 'core/timeless'
|
31
|
-
require_relative 'core/guarded'
|
32
11
|
|
33
|
-
|
12
|
+
DEFAULT_METHOD = :basic
|
34
13
|
|
35
|
-
|
14
|
+
# I'm doing it this way in order to gradually begin decoupling in my mind
|
15
|
+
# core from simulation. The constructed core will have to be assigned the
|
16
|
+
# simulation object on which it will depend before core is made completely
|
17
|
+
# independend on simulation. (Not gonna happen any soon.)
|
18
|
+
#
|
19
|
+
attr_reader :simulation # just a remark:
|
20
|
+
attr_reader :simulation_method # "reader" is "selector" in Landin's language
|
36
21
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
#
|
49
|
-
#
|
50
|
-
def timeless
|
51
|
-
Class.new self do
|
52
|
-
include Timeless
|
53
|
-
def timed?; false end
|
54
|
-
def timeless?; true end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# Vanilla simulator is not guarded.
|
22
|
+
def initialize simulation: nil, method: nil, guarded: false, **named_args
|
23
|
+
@simulation = simulation or fail ArgumentError, "Core requires simulation!"
|
24
|
+
@simulation_method = method || DEFAULT_METHOD
|
25
|
+
|
26
|
+
if guarded then # TODO: Guarded is unfinished business.
|
27
|
+
fail NotImplementedMethod, "Guarded core is not implemented yet!"
|
28
|
+
require_relative 'core/guarded' # TODO: Should be replaced with autoload.
|
29
|
+
else @guarded = false end
|
30
|
+
|
31
|
+
# Dependent on Simulation, this machine returns "delta contribution for ts
|
32
|
+
# (timeless nonstoichiometric) transitions", which smells like a vector of
|
33
|
+
# size corresponding to the number of free places.
|
59
34
|
#
|
60
|
-
|
35
|
+
@delta_closure_for_ts_transitions = simulation.ts_delta_closure
|
61
36
|
|
62
|
-
#
|
37
|
+
# This one is slightly different in that it returns so-called "firing vector",
|
38
|
+
# from which delta vector is computed by multiplying it with tS stoichiometry
|
39
|
+
# matrix.
|
63
40
|
#
|
64
|
-
|
65
|
-
Class.new self do
|
66
|
-
include Guarded
|
67
|
-
def guarded?; true end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
41
|
+
@firing_closure_for_tS_transitions = simulation.tS_firing_closure
|
71
42
|
|
72
|
-
|
43
|
+
# This machine is special in that it directly modifies the marking vector,
|
44
|
+
# firing the assignment transitions one by one (or so I think).
|
45
|
+
#
|
46
|
+
@assignment_closure_for_A_transitions = simulation.A_direct_assignment_closure
|
73
47
|
|
74
|
-
|
75
|
-
|
76
|
-
method_init # defined in Timed::Methods and Timeless::Methods
|
48
|
+
# We're gonna change all of the above. The marking vectors will be owned by
|
49
|
+
# the core now. Machines will be wired only inside the core.
|
77
50
|
end
|
78
51
|
|
79
|
-
|
80
|
-
|
52
|
+
# TODO: this delegation below is not completely right.
|
53
|
+
# 1. There is no subclassing and Timed/Timeless module inclusion, so there
|
54
|
+
# is no need to delegate timed?/timeless? to the class, if only the modules
|
55
|
+
# provide those inquirer methods (predicates in Landin's language).
|
56
|
+
# 2. Same goes for guarded? predicate, which might be the business of
|
57
|
+
# Core::Guarded module (or not)
|
58
|
+
#
|
59
|
+
delegate :timed?,
|
81
60
|
:timeless?,
|
82
61
|
:guarded?,
|
83
62
|
to: "self.class"
|
@@ -102,24 +81,71 @@ class YPetri::Core
|
|
102
81
|
#
|
103
82
|
def delta_ts
|
104
83
|
simulation.ts_delta_closure.call
|
84
|
+
# @delta_closure_for_ts_transitions.call
|
105
85
|
end
|
106
86
|
|
107
87
|
# Firing vector of tS transitions.
|
108
88
|
#
|
109
89
|
def firing_vector_tS
|
110
90
|
simulation.tS_firing_closure.call
|
91
|
+
# @firing_closure_for_tS_transitions.call
|
111
92
|
end
|
112
93
|
|
113
94
|
# Increments the marking vector by a given delta.
|
114
95
|
#
|
115
96
|
def increment_marking_vector( delta )
|
116
97
|
print '.'
|
98
|
+
# TODO: From now on, this won't touch the simulation's property
|
99
|
+
# at all. It will be left to the simulation to ask for the results,
|
100
|
+
# or to rig the core to message back when done.
|
117
101
|
simulation.increment_marking_vector_closure.( delta )
|
118
102
|
end
|
119
103
|
|
120
|
-
# Fires assignment transitions.
|
104
|
+
# Fires all the assignment transitions.
|
121
105
|
#
|
122
|
-
def
|
106
|
+
def fire_all_assignment_transitions!
|
123
107
|
simulation.A_direct_assignment_closure.call
|
108
|
+
# @assignment_closure_for_A_transitions.call
|
124
109
|
end
|
125
|
-
|
110
|
+
alias assignment_transitions_all_fire! fire_all_assignment_transitions!
|
111
|
+
end # module YPetri::Core
|
112
|
+
|
113
|
+
# TODO: Decouple Core and Simulation classes. It still looks like one
|
114
|
+
# simulation will use only one core (or one for each type of simulation
|
115
|
+
# trick), but I don't want the core to be parametrized by a Simulation
|
116
|
+
# instance. There should be at least a hint of core recruitment, sending
|
117
|
+
# the state to the core, asking the core to perform its trick on it, and
|
118
|
+
# asking back the results (or rigging the core to send them back as soon
|
119
|
+
# as done). A core should be more or less a one-trick pony. But simulation
|
120
|
+
# should also avoid doing too much, because above it, other classes (Agent
|
121
|
+
# and co.) may exist, doing things like optimization, parameter inferences
|
122
|
+
# aso.
|
123
|
+
|
124
|
+
# TODO: Regarding guarded cores, many kinds of PNs prohibit places from
|
125
|
+
# acquiring negative marking, or impose other restrictions. If the system
|
126
|
+
# state somehow makes it out of this safe envelope, the transitions may
|
127
|
+
# start behaving unpredictably. So there is a question of who is responsible
|
128
|
+
# for keeping the system sane (in the safe state envelope). The simple thing
|
129
|
+
# is for the core not to test anything and leave it up to the user not to
|
130
|
+
# define systems that are not sane. Simple is good, but there must be agreement,
|
131
|
+
# a requirement that the system specification behaves. If there is no such
|
132
|
+
# agreement, the core has no excuse from the need to guard against transitions
|
133
|
+
# trying to fire when they should properly be disabled (at least not if the
|
134
|
+
# core knows that the PN in question is classical). I did not decide what exactly
|
135
|
+
# should happen if the sanity is broken (raise an error? warn and try minor
|
136
|
+
# repair measures yourself?), but something should.
|
137
|
+
#
|
138
|
+
# Another example, in chemical systems, negative markings (concentrations)
|
139
|
+
# also ordinarily make no sense. But insensitive combination of functions
|
140
|
+
# and simulation method may lead to unsafe state with relatively late detection
|
141
|
+
# of error, leaving the modeller wondering when exactly the system state went
|
142
|
+
# haywire. Again, I'm not sure how exactly should the guarded core react to
|
143
|
+
# the treat of insane situation, but it somehow should.
|
144
|
+
#
|
145
|
+
# I feel it is necessary for the core to have awareness of the quality of
|
146
|
+
# the momentary system's state (at least to be able to tell whether it's
|
147
|
+
# meaningful at all), because later I want to let the core choose between
|
148
|
+
# deterministic (continous) and stochastic (discrete, going quantum by
|
149
|
+
# quantum, or by several quanta, there is more than one stochastic discrete
|
150
|
+
# method in stock) methods for simulation of individual processes.
|
151
|
+
|
data/lib/y_petri/fixed_assets.rb
CHANGED
@@ -254,14 +254,15 @@ module YPetri::Simulation::Timed
|
|
254
254
|
@time_unit = anything.class.one
|
255
255
|
@initial_time, @target_time = time_unit * 0, time_unit * Float::INFINITY
|
256
256
|
end
|
257
|
-
|
257
|
+
# Set up a parametrized subclas of the sampler for timed simulation.
|
258
|
+
param_class( { Recorder: Recorder }, with: { simulation: self } )
|
258
259
|
reset_time!
|
259
260
|
@step = settings[:step] || time_unit
|
260
261
|
@default_sampling = settings[:sampling] || step
|
261
262
|
@core = if @guarded then
|
262
|
-
Core
|
263
|
+
YPetri::Core::Timed.new( simulation: self, method: method, guarded: true )
|
263
264
|
else
|
264
|
-
Core
|
265
|
+
YPetri::Core::Timed.new( simulation: self, method: method, guarded: false )
|
265
266
|
end
|
266
267
|
@recorder = if features_to_record then
|
267
268
|
# we'll have to figure out features
|
@@ -277,13 +278,4 @@ module YPetri::Simulation::Timed
|
|
277
278
|
Recorder().new( sampling: settings[:sampling] )
|
278
279
|
end
|
279
280
|
end
|
280
|
-
|
281
|
-
# Sets up subclasses of +Core+ (the simulator) and +Recorder+ (the sampler)
|
282
|
-
# for timed simulations.
|
283
|
-
#
|
284
|
-
def init_core_and_recorder_subclasses
|
285
|
-
param_class( { Core: YPetri::Core.timed,
|
286
|
-
Recorder: Recorder },
|
287
|
-
with: { simulation: self } )
|
288
|
-
end
|
289
281
|
end # module YPetri::Simulation::Timed
|
@@ -2,36 +2,38 @@
|
|
2
2
|
|
3
3
|
# A mixin for timeless simulations.
|
4
4
|
#
|
5
|
-
|
6
|
-
|
7
|
-
require_relative 'timeless/recorder'
|
5
|
+
module YPetri::Simulation::Timeless
|
6
|
+
require_relative 'timeless/recorder'
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
8
|
+
# False for timeless simulations.
|
9
|
+
#
|
10
|
+
def timed?
|
11
|
+
false
|
12
|
+
end
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
# Changing the simulation method on the fly not supported.
|
15
|
+
#
|
16
|
+
def set_simulation_method
|
17
|
+
fail NoMethodError, "Changing simulation method on the fly not supported!"
|
18
|
+
end
|
20
19
|
|
21
|
-
|
20
|
+
private
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
22
|
+
# Initialization subroutine for timeless simulations. Sets up the
|
23
|
+
# parametrized subclasses +@Core+ (the simulator) and +@Recorder+,
|
24
|
+
# and initializes the +@recorder+ attribute.
|
25
|
+
#
|
26
|
+
def init **settings
|
27
|
+
method = settings[:method] # the simulation method
|
28
|
+
features_to_record = settings[:record]
|
29
|
+
# Sets up a parametrized subclass of the sampler for timeless simulation.
|
30
|
+
param_class( { Recorder: Recorder }, with: { simulation: self } )
|
31
31
|
@core = if @guarded then
|
32
|
-
Core
|
32
|
+
YPetri::Core::Timeless
|
33
|
+
.new( simulation: self, method: method, guarded: true )
|
33
34
|
else
|
34
|
-
Core
|
35
|
+
YPetri::Core::Timeless
|
36
|
+
.new( simulation: self, method: method, guarded: false )
|
35
37
|
end
|
36
38
|
@recorder = if features_to_record then
|
37
39
|
# we'll have to figure out features
|
@@ -47,14 +49,4 @@ class YPetri::Simulation
|
|
47
49
|
Recorder().new # init the recorder
|
48
50
|
end
|
49
51
|
end
|
50
|
-
|
51
|
-
# Sets up subclasses of +Core+ (the simulator) and +Recorder+ (the sampler)
|
52
|
-
# for timeless simulations.
|
53
|
-
#
|
54
|
-
def init_core_and_recorder_subclasses
|
55
|
-
param_class( { Core: YPetri::Core.timeless,
|
56
|
-
Recorder: Recorder },
|
57
|
-
with: { simulation: self } )
|
58
|
-
end
|
59
|
-
end # module Timeless
|
60
|
-
end # module YPetri::Simulation
|
52
|
+
end # module YPetri::Simulation::Timeless
|
data/lib/y_petri/simulation.rb
CHANGED
@@ -42,7 +42,7 @@ class YPetri::Simulation
|
|
42
42
|
★ MarkingClamps::Access
|
43
43
|
★ MarkingVector::Access
|
44
44
|
|
45
|
-
DEFAULT_SETTINGS = -> do { method: :
|
45
|
+
DEFAULT_SETTINGS = -> do { method: :basic, guarded: false } end
|
46
46
|
|
47
47
|
class << self
|
48
48
|
alias __new__ new
|
data/lib/y_petri/version.rb
CHANGED
data/test/simulation_test.rb
CHANGED
@@ -123,7 +123,7 @@ require_relative '../lib/y_petri' # tested component itself
|
|
123
123
|
# @sim.m.must_equal [1, 2]
|
124
124
|
# @sim.p_m.must_equal( { A: 1, B: 2 } )
|
125
125
|
# @sim.recording.must_equal( { 0 => [1, 2]} )
|
126
|
-
# @sim.simulation_method.must_equal :
|
126
|
+
# @sim.simulation_method.must_equal :basic
|
127
127
|
# @sim.core.must_be_kind_of YPetri::Core
|
128
128
|
# @sim.ts_tt.first.domain.must_equal []
|
129
129
|
# @sim.send( :ts_transitions ).first.domain_access_code.must_equal ''
|
@@ -195,7 +195,7 @@ require_relative '../lib/y_petri' # tested component itself
|
|
195
195
|
|
196
196
|
# it "should behave" do
|
197
197
|
# @sim.timed?.must_equal true
|
198
|
-
# @sim.simulation_method.must_equal :
|
198
|
+
# @sim.simulation_method.must_equal :basic
|
199
199
|
# @sim.Ts_tt.size.must_equal 1
|
200
200
|
# @sim.send( :transitions ).Ts.first.gradient_closure.call.must_equal [1]
|
201
201
|
# @sim.Ts_tt.first.codomain.names.must_equal [:A]
|
@@ -212,7 +212,7 @@ require_relative '../lib/y_petri' # tested component itself
|
|
212
212
|
|
213
213
|
# it "should behave" do
|
214
214
|
# @sim.send( :transitions ).Ts.first.codomain.names.must_equal [:A]
|
215
|
-
# @sim.simulation_method.must_equal :
|
215
|
+
# @sim.simulation_method.must_equal :basic
|
216
216
|
# @sim.timed?.must_equal true
|
217
217
|
# @sim.core.timed?.must_equal true
|
218
218
|
# @sim.reset!
|
@@ -289,7 +289,7 @@ require_relative '../lib/y_petri' # tested component itself
|
|
289
289
|
# it "should behave" do
|
290
290
|
# s = simulation
|
291
291
|
# assert ! s.timed?
|
292
|
-
# s.core.
|
292
|
+
# s.core.ancestors.must_include YPetri::Core::Timeless::Basic
|
293
293
|
# ds = s.recording
|
294
294
|
# ds.size.must_equal 6
|
295
295
|
# ds.events.must_equal [0, 1, 2, 3, 4, 5]
|
@@ -334,7 +334,7 @@ describe "timed simulation" do
|
|
334
334
|
it "should behave" do
|
335
335
|
places.map( &:marking ).must_equal [0.5, 0.5] # marking unaffected
|
336
336
|
s = simulation
|
337
|
-
s.settings.must_equal( { method: :
|
337
|
+
s.settings.must_equal( { method: :basic, guarded: false,
|
338
338
|
step: 0.1, sampling: 5, time: 0..60 } )
|
339
339
|
assert s.recording.to_csv.start_with?( ":event,:A,:B\n" +
|
340
340
|
"0.0,0.5,0.5\n" +
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: y_petri
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.3.
|
4
|
+
version: 2.3.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- boris
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-10-
|
11
|
+
date: 2015-10-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -145,15 +145,13 @@ files:
|
|
145
145
|
- lib/y_petri/core.rb
|
146
146
|
- lib/y_petri/core/guarded.rb
|
147
147
|
- lib/y_petri/core/timed.rb
|
148
|
+
- lib/y_petri/core/timed/basic.rb
|
148
149
|
- lib/y_petri/core/timed/euler.rb
|
149
150
|
- lib/y_petri/core/timed/gillespie.rb
|
150
|
-
- lib/y_petri/core/timed/methods.rb
|
151
|
-
- lib/y_petri/core/timed/pseudo_euler.rb
|
152
|
-
- lib/y_petri/core/timed/quasi_euler.rb
|
153
151
|
- lib/y_petri/core/timed/runge_kutta.rb
|
152
|
+
- lib/y_petri/core/timed/ticked.rb
|
154
153
|
- lib/y_petri/core/timeless.rb
|
155
|
-
- lib/y_petri/core/timeless/
|
156
|
-
- lib/y_petri/core/timeless/pseudo_euler.rb
|
154
|
+
- lib/y_petri/core/timeless/basic.rb
|
157
155
|
- lib/y_petri/dsl.rb
|
158
156
|
- lib/y_petri/fixed_assets.rb
|
159
157
|
- lib/y_petri/net.rb
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
# Timed simulation core.
|
4
|
-
#
|
5
|
-
module YPetri::Core::Timed
|
6
|
-
require_relative 'euler'
|
7
|
-
require_relative 'pseudo_euler' # t transitions firing after each step
|
8
|
-
require_relative 'quasi_euler' # t transitions firing after each time tick
|
9
|
-
require_relative 'gillespie'
|
10
|
-
require_relative 'runge_kutta'
|
11
|
-
|
12
|
-
module Methods
|
13
|
-
def method_init
|
14
|
-
extend case simulation_method
|
15
|
-
when :euler then Euler
|
16
|
-
when :pseudo_euler then PseudoEuler
|
17
|
-
when :quasi_euler then QuasiEuler
|
18
|
-
when :gillespie then Gillespie
|
19
|
-
when :runge_kutta then RungeKutta
|
20
|
-
else fail TypeError, "Unknown timed simulation method: #{method}!" end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
# Timed simulation core.
|
4
|
-
#
|
5
|
-
module YPetri::Core::Timeless
|
6
|
-
require_relative 'pseudo_euler'
|
7
|
-
|
8
|
-
module Methods
|
9
|
-
def method_init
|
10
|
-
extend case simulation_method
|
11
|
-
when :pseudo_euler then PseudoEuler
|
12
|
-
else fail TypeError, "Unknown timeless simulation method: #{method}!" end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
# Implicit Euler for timeless nets.
|
4
|
-
#
|
5
|
-
module YPetri::Core::Timeless::PseudoEuler
|
6
|
-
# Method #step! for timeless +pseudo_euler+ method. Simply, timeless
|
7
|
-
# transitions fire simultaneously, after which, A transitions (if any) fire.
|
8
|
-
#
|
9
|
-
def step!
|
10
|
-
increment_marking_vector Δ
|
11
|
-
assignment_transitions_all_fire!
|
12
|
-
alert!
|
13
|
-
end
|
14
|
-
end # module YPetri::Core::Timeless::PseudoEuler
|