y_petri 2.3.11 → 2.3.12
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/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
|