y_petri 2.3.12 → 2.4.0
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/runge_kutta.rb +103 -49
- data/lib/y_petri/core/timed.rb +26 -8
- data/lib/y_petri/core/timeless.rb +8 -0
- data/lib/y_petri/core.rb +50 -40
- data/lib/y_petri/simulation/dependency.rb +8 -8
- data/lib/y_petri/simulation/marking_vector.rb +20 -6
- data/lib/y_petri/simulation/matrix.rb +11 -1
- data/lib/y_petri/simulation/nodes/access.rb +1 -1
- data/lib/y_petri/simulation/nodes.rb +5 -5
- data/lib/y_petri/simulation/place_mapping.rb +2 -2
- data/lib/y_petri/simulation/places/access.rb +10 -9
- data/lib/y_petri/simulation/places/types.rb +8 -5
- data/lib/y_petri/simulation/places.rb +1 -3
- data/lib/y_petri/simulation/recorder.rb +23 -0
- data/lib/y_petri/simulation/timed.rb +104 -11
- data/lib/y_petri/simulation/transition_representation/types.rb +4 -12
- data/lib/y_petri/simulation/transitions/access.rb +6 -6
- data/lib/y_petri/simulation/transitions/types.rb +20 -30
- data/lib/y_petri/simulation/transitions.rb +3 -1
- data/lib/y_petri/simulation.rb +17 -14
- data/lib/y_petri/version.rb +1 -1
- data/lib/y_petri/world.rb +1 -1
- data/test/simulation_test.rb +211 -50
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ef666cef4bef63ff531f923b5b5c15dbf44f3d01
|
4
|
+
data.tar.gz: 39b9e0478d5fbe58ea0f351d930c307acdeab419
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c8c52cbb6efd249b118a8e9347a478725d2916354c894a63b1ef6da8bc7e517f0af702a0479cc39b3ee334b0579d697ce7926fb2e2de03850c8707aaeebea457
|
7
|
+
data.tar.gz: 8a3ed7e54e271be653a563401abeeebb8409e5c34b76eeba6f8ccc49af59c9e86d3427917840ae0adbab28f4c4f93d92e318acb8776a313ff4f915f856081bad
|
@@ -3,57 +3,111 @@
|
|
3
3
|
# Runge-Kutta method. Like vanilla Euler method, assumes that only T transitions are in the net.
|
4
4
|
#
|
5
5
|
module YPetri::Core::Timed::RungeKutta
|
6
|
+
# Computes delta by Runge-Kutta 4th order method.
|
7
|
+
#
|
6
8
|
def delta Δt
|
7
9
|
# The f below is from the equation state' = f( state )
|
8
|
-
f = lambda
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
55
|
-
#
|
56
|
-
#
|
10
|
+
f = lambda do |mv| # mv is the marking vector of the free places
|
11
|
+
# Delta from s transitions.
|
12
|
+
# TODO: This is only array now. Make it something else. One possibility
|
13
|
+
# would be to use simulation's MarkingVector class, but core should
|
14
|
+
# actually have its own marking vector class, probably parametrized by
|
15
|
+
# the net. It does not matter because alone I won't be able to exhaust
|
16
|
+
# all the possibilities.
|
17
|
+
delta_s = simulation.MarkingVector.zero( simulation.free_pp )
|
18
|
+
# Here, we get the nonstoichiometric transitions of the simulation.
|
19
|
+
nonstoichio_tt = simulation.s_tt
|
20
|
+
# Now, let's get the delta contribution of the nonstoichio. tt.
|
21
|
+
nonstoichio_tt.each { |t|
|
22
|
+
domain, codomain = t.domain, t.codomain # transition's domain
|
23
|
+
function = t.rate_closure # transition's function
|
24
|
+
output = Array function.call( *domain.map { |p| mv.fetch p } )
|
25
|
+
codomain.each_with_index do |p, i|
|
26
|
+
delta_s.set( p, delta_s.fetch( p ) + output[i] )
|
27
|
+
end
|
28
|
+
# TODO: The above code is suboptimal, needlessly computing
|
29
|
+
# MarkingVector#index and #fetch( place ) each time.
|
30
|
+
# The array incrementing might not be the best choice either,
|
31
|
+
# and most of all, the whole thing would need to be compiled
|
32
|
+
# into assembly language or at least FORTRAN.
|
33
|
+
}
|
34
|
+
|
35
|
+
# Delta from S transitions.
|
36
|
+
# TODO: (Same remark as for s transitions, see above.)
|
37
|
+
delta_S = simulation.MarkingVector.zero( simulation.free_pp )
|
38
|
+
# Here, we get the stoichiometric transitions of the simulation
|
39
|
+
stoichio_tt = simulation.S_tt
|
40
|
+
# Now, let's get the delta contribution of the stoichio. tt.
|
41
|
+
stoichio_tt.each { |t|
|
42
|
+
domain, codomain = t.domain, t.codomain # transition's domain
|
43
|
+
function = t.rate_closure # transition's function
|
44
|
+
s = t.stoichiometry
|
45
|
+
flux = function.call( *domain.map { |place| mv.fetch place } )
|
46
|
+
codomain.each_with_index do |p, i|
|
47
|
+
delta_S.set( p, delta_S.fetch( p ) + flux * s[i] )
|
48
|
+
end
|
49
|
+
# TODO: Again, the above code is suboptimal.
|
50
|
+
}
|
51
|
+
|
52
|
+
return delta_s + delta_S
|
53
|
+
end
|
54
|
+
|
55
|
+
y = marking_of_free_places
|
56
|
+
|
57
|
+
k1 = f.( y ) # puts "k1 ( = f( y ) ) is #{k1}"
|
58
|
+
k2 = f.( y + Δt / 2 * k1 ) # puts "k2 is #{k2}"
|
59
|
+
k3 = f.( y + Δt / 2 * k2 ) # puts "k3 is #{k3}"
|
60
|
+
k4 = f.( y + Δt * k3 ) # puts "k4 is #{k4}"
|
61
|
+
|
62
|
+
rslt = Δt / 6 * ( k1 + 2 * k2 + 2 * k3 + k4 ) # puts "rslt is #{rslt}"
|
63
|
+
|
64
|
+
return rslt # Marking vector of free places
|
57
65
|
end
|
58
66
|
alias Δ delta
|
67
|
+
|
68
|
+
def step! Δt=simulation.step
|
69
|
+
# TODO: Thus far, runge_kutta method is an exception in the core in
|
70
|
+
# that it works with core's own state. (Core used to work with
|
71
|
+
# simulation's state before and rely on the simulation to provide
|
72
|
+
# state increment and assign closures.) This is how whole core should
|
73
|
+
# work.
|
74
|
+
increment_marking_of_free_places Δ( Δt )
|
75
|
+
increment_time! Δt
|
76
|
+
alert_user! marking_of_free_places
|
77
|
+
end
|
78
|
+
|
79
|
+
def increment_marking_of_free_places by
|
80
|
+
# TODO: Same remark as above.
|
81
|
+
@marking_of_free_places += by
|
82
|
+
end
|
83
|
+
|
84
|
+
def increment_time! by
|
85
|
+
# TODO: Once other timed methods than runge_kutta are reasonable, this
|
86
|
+
# should be moved to core/timed.rb
|
87
|
+
@time += by
|
88
|
+
end
|
89
|
+
|
90
|
+
def reset_time! to=0.0
|
91
|
+
# TODO: Once other timed methods than runge_kutta are reasonable, this
|
92
|
+
# should be moved to core/timed.rb
|
93
|
+
@time = to
|
94
|
+
end
|
95
|
+
|
96
|
+
def set_user_alert_closure &block
|
97
|
+
# TODO: Core's runge_kutta method is special for now, and even
|
98
|
+
# simulation recognizes that. With runge_kutta method, core uses
|
99
|
+
# single @user_alert_closure which it calls whenever the state
|
100
|
+
# of the core progresses. It is the business of the user to supply,
|
101
|
+
# before using the core, that does what the user wants. It is also
|
102
|
+
# imaginable that different core's modes of operation would have
|
103
|
+
# different sensitivity with regard to alerting the user, but for now,
|
104
|
+
# the user is alerted whenever anything happens at all.
|
105
|
+
@user_alert_closure = block
|
106
|
+
end
|
107
|
+
|
108
|
+
def alert_user! object
|
109
|
+
# TODO: As soon as more core's method begin relying on core's own state,
|
110
|
+
# this method will be moved to Core module.
|
111
|
+
@user_alert_closure.call( object )
|
112
|
+
end
|
59
113
|
end # YPetri::Core::Timed::RungeKutta
|
data/lib/y_petri/core/timed.rb
CHANGED
@@ -5,9 +5,9 @@
|
|
5
5
|
#
|
6
6
|
class YPetri::Core::Timed
|
7
7
|
★ YPetri::Core
|
8
|
-
|
8
|
+
|
9
9
|
require_relative 'timed/basic'
|
10
|
-
require_relative 'timed/ticked'
|
10
|
+
require_relative 'timed/ticked'
|
11
11
|
require_relative 'timed/euler'
|
12
12
|
require_relative 'timed/runge_kutta'
|
13
13
|
require_relative 'timed/gillespie'
|
@@ -20,20 +20,38 @@ class YPetri::Core::Timed
|
|
20
20
|
gillespie: Gillespie # for timed nets only
|
21
21
|
}
|
22
22
|
|
23
|
+
# From now on, core has its own time attribute and selector.
|
24
|
+
attr_reader :time
|
25
|
+
|
26
|
+
# This inquirer (=Boolean selector) is always true for timed cores.
|
27
|
+
#
|
28
|
+
def timed?; true end
|
29
|
+
|
30
|
+
# This inquirer (=Boolean selector) is always false for timed cores.
|
31
|
+
#
|
32
|
+
def timeless?; false end
|
33
|
+
|
23
34
|
def initialize **named_args
|
24
|
-
super
|
35
|
+
super # TODO: Net type checking.
|
36
|
+
@time = 0.0
|
25
37
|
extend METHODS.fetch simulation_method
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
38
|
+
@delta_s = simulation.MarkingVector.zero( @free_pp )
|
39
|
+
@delta_S = simulation.MarkingVector.zero( @free_pp )
|
40
|
+
end
|
41
|
+
|
42
|
+
# Increments core time by Δt.
|
43
|
+
#
|
44
|
+
def increment_time! Δt
|
45
|
+
@time += Δt
|
31
46
|
end
|
32
47
|
|
33
48
|
# Makes a single step by Δt.
|
34
49
|
#
|
35
50
|
def step! Δt=simulation.step
|
51
|
+
# TODO: This one will act directly upon simulation. Subject of potential
|
52
|
+
# change later.
|
36
53
|
increment_marking_vector Δ( Δt )
|
54
|
+
# TODO: The bottom two, obviously, act directly upon simulation.
|
37
55
|
simulation.increment_time! Δt
|
38
56
|
simulation.recorder.alert
|
39
57
|
end
|
@@ -12,6 +12,14 @@ class YPetri::Core::Timeless
|
|
12
12
|
# Note: the reason why Timeless core has distinct basic method is because
|
13
13
|
# without having to consider timed transitions, it can be made simpler.
|
14
14
|
|
15
|
+
# This inquirer (=Boolean selector) is always false for timeless cores.
|
16
|
+
#
|
17
|
+
def timed?; false end
|
18
|
+
|
19
|
+
# This inquirer (=Boolean selector) is always true for timeless cores.
|
20
|
+
#
|
21
|
+
def timeless?; true end
|
22
|
+
|
15
23
|
def initialize **named_args
|
16
24
|
super
|
17
25
|
extend METHODS.fetch simulation_method
|
data/lib/y_petri/core.rb
CHANGED
@@ -11,58 +11,69 @@ module YPetri::Core
|
|
11
11
|
|
12
12
|
DEFAULT_METHOD = :basic
|
13
13
|
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
14
|
+
# Instead of getting the parametrized subclass of Timed/Timeless core
|
15
|
+
# belonging to a simulation, I am passing a simulation instance to the
|
16
|
+
# constructor now in order to gradually begin decoupling in my mind core
|
17
|
+
# simulation. If I ever make the core independent from the simulation,
|
18
|
+
# it will have its own representation of the net and its own capability
|
19
|
+
# to form wiring and apply the required numerical procedures.
|
20
|
+
|
19
21
|
attr_reader :simulation # just a remark:
|
20
22
|
attr_reader :simulation_method # "reader" is "selector" in Landin's language
|
23
|
+
attr_reader :guarded
|
24
|
+
alias guarded? guarded
|
25
|
+
|
26
|
+
attr_reader :free_pp,
|
27
|
+
:clamped_pp,
|
28
|
+
:pp,
|
29
|
+
:marking_of_free_places,
|
30
|
+
:marking_of_clamped_places
|
21
31
|
|
22
32
|
def initialize simulation: nil, method: nil, guarded: false, **named_args
|
23
33
|
@simulation = simulation or fail ArgumentError, "Core requires simulation!"
|
24
34
|
@simulation_method = method || DEFAULT_METHOD
|
25
|
-
|
35
|
+
|
26
36
|
if guarded then # TODO: Guarded is unfinished business.
|
27
37
|
fail NotImplementedMethod, "Guarded core is not implemented yet!"
|
28
38
|
require_relative 'core/guarded' # TODO: Should be replaced with autoload.
|
29
39
|
else @guarded = false end
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
#
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
#
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
# the core now. Machines will be wired only inside the core.
|
40
|
+
|
41
|
+
@free_pp = simulation.free_pp
|
42
|
+
@clamped_pp = simulation.clamped_pp
|
43
|
+
@pp = simulation.pp
|
44
|
+
# TODO: Try to make the lines below simpler. In particular, in the future, core should
|
45
|
+
# not depend on simulation at this level.
|
46
|
+
@marking_of_free_places = simulation.MarkingVector.starting( @free_pp )
|
47
|
+
@marking_of_clamped_places = simulation.MarkingVector.starting( @clamped_pp )
|
48
|
+
|
49
|
+
# TODO: I don't remember how to load this in a simple way.
|
50
|
+
simulation.state.to_hash.each do |place, value|
|
51
|
+
if @marking_of_free_places.annotation.include? place then
|
52
|
+
@marking_of_free_places.set( place, value )
|
53
|
+
elsif @marking_of_clamped_places.annotation.include? place then
|
54
|
+
@marking_of_clamped_places.set( place, value )
|
55
|
+
else
|
56
|
+
fail "Problem loading marking vector to timed core."
|
57
|
+
end
|
58
|
+
end
|
50
59
|
end
|
51
60
|
|
52
|
-
#
|
53
|
-
#
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
61
|
+
# Selector of the core's own state vector.
|
62
|
+
#
|
63
|
+
def state
|
64
|
+
# TODO: Make it more efficient. Later, when core is detached from
|
65
|
+
# simulation, use own assets instead of simulation.MarkingVector
|
66
|
+
simulation.MarkingVector.zero.tap do |mv|
|
67
|
+
@marking_of_free_places.annotation.each do |place|
|
68
|
+
mv.set place, @marking_of_free_places.fetch( place )
|
69
|
+
end
|
70
|
+
@marking_of_clamped_places.annotation.each do |place|
|
71
|
+
mv.set place, @marking_of_clamped_places.fetch( place )
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
63
75
|
|
64
|
-
delegate :alert!,
|
65
|
-
to: :recorder
|
76
|
+
delegate :alert!, to: :recorder
|
66
77
|
|
67
78
|
# Delta for free places from timeless transitions.
|
68
79
|
#
|
@@ -148,4 +159,3 @@ end # module YPetri::Core
|
|
148
159
|
# deterministic (continous) and stochastic (discrete, going quantum by
|
149
160
|
# quantum, or by several quanta, there is more than one stochastic discrete
|
150
161
|
# method in stock) methods for simulation of individual processes.
|
151
|
-
|
@@ -1,18 +1,18 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
# Mixin providing collections of places / transitions to classes parametrized
|
4
|
-
# by an instance of YPetri::Simulation
|
5
|
-
# +#simulation+ method returning the +Simulation+ instance with which
|
6
|
-
# parametrized.
|
4
|
+
# by an instance of +YPetri::Simulation+. Expects the includer classes to
|
5
|
+
# provide +#simulation+ method returning the +Simulation+ instance with which
|
6
|
+
# they are parametrized.
|
7
7
|
#
|
8
8
|
class YPetri::Simulation
|
9
9
|
module Dependency
|
10
|
-
delegate :
|
11
|
-
:
|
10
|
+
delegate :PlacePS,
|
11
|
+
:TransitionPS,
|
12
12
|
:MarkingClamp,
|
13
13
|
:InitialMarkingObject,
|
14
|
-
:
|
15
|
-
:
|
14
|
+
:PlacesPS,
|
15
|
+
:TransitionsPS,
|
16
16
|
:MarkingClamps,
|
17
17
|
:InitialMarking,
|
18
18
|
:net,
|
@@ -37,7 +37,7 @@ class YPetri::Simulation
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
# Necessary to overcome the protected character of the listed methods.
|
42
42
|
#
|
43
43
|
[ :node,
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
class YPetri::Simulation
|
4
|
-
# The class in which places' marking is stored
|
4
|
+
# The class in which places' marking is stored inside a simulation.
|
5
5
|
#
|
6
6
|
class MarkingVector < Matrix
|
7
7
|
★ Dependency
|
@@ -14,6 +14,12 @@ class YPetri::Simulation
|
|
14
14
|
# Constructs a marking vector from a hash places >> values, or from
|
15
15
|
# an array, in which case, it is assumed that the marking vector
|
16
16
|
# corresponds to all the places in the simulation.
|
17
|
+
#
|
18
|
+
# TODO: I don't like having to write MarkingVector[ [ 1, 2, 3 ] ] instead
|
19
|
+
# of MarkingVector[ 1, 2, 3 ], but I accepted and endorsed it long time
|
20
|
+
# ago as a necessary tax for being able to distinguish between the user
|
21
|
+
# meaning to supply no arguments and the user meaning to supply empty
|
22
|
+
# vector.
|
17
23
|
#
|
18
24
|
def [] arg
|
19
25
|
case arg
|
@@ -25,10 +31,10 @@ class YPetri::Simulation
|
|
25
31
|
msg unless arg.size == annotation.size
|
26
32
|
column_vector( arg )
|
27
33
|
else
|
28
|
-
annotated_with( places )[
|
34
|
+
annotated_with( places )[ arg ]
|
29
35
|
end
|
30
36
|
else
|
31
|
-
self[
|
37
|
+
self[ arg.each.to_a ]
|
32
38
|
end
|
33
39
|
end
|
34
40
|
|
@@ -46,7 +52,7 @@ class YPetri::Simulation
|
|
46
52
|
#
|
47
53
|
def starting places=nil
|
48
54
|
if places.nil? then
|
49
|
-
return starting places() if annotation.nil?
|
55
|
+
return starting( places() ) if annotation.nil?
|
50
56
|
self[ annotation.map { |p| p.free? ? p.initial_marking : p.clamp } ]
|
51
57
|
else
|
52
58
|
annotated_with( places ).starting
|
@@ -80,7 +86,7 @@ class YPetri::Simulation
|
|
80
86
|
end
|
81
87
|
end
|
82
88
|
|
83
|
-
# Modifying the vector
|
89
|
+
# Modifying the vector elements.
|
84
90
|
#
|
85
91
|
def set id, value
|
86
92
|
self[ index( id ), 0 ] = value
|
@@ -92,17 +98,25 @@ class YPetri::Simulation
|
|
92
98
|
def reset! arg=self.class.starting
|
93
99
|
case arg
|
94
100
|
when Hash then
|
101
|
+
# Hash is first converted into a PlaceMapping instance (mp).
|
95
102
|
mp = simulation.PlaceMapping().load( arg )
|
103
|
+
# Updated marking vector is constructed using reliable methods
|
104
|
+
# self.class.starting and self#set.
|
96
105
|
updated = mp.each_with_object self.class.starting do |(place, value), mv|
|
97
106
|
mv.set place, value
|
98
107
|
end
|
108
|
+
# Updated marking vector is then converted into an array and #reset! method
|
109
|
+
# is called upon it again to actually perform in-place update of this vector.
|
110
|
+
# TODO: The above is slightly inefficient -- constructing a new vector when
|
111
|
+
# in-place modification seems a better solution. But if it works, it's a
|
112
|
+
# strong reason to not fix it until we are in the optimization stage.
|
99
113
|
reset! updated.column_to_a
|
100
114
|
else # array arg assumed
|
101
115
|
arg.each.to_a.zip( annotation ).map { |value, place| set place, value }
|
102
116
|
end
|
103
117
|
end
|
104
118
|
|
105
|
-
# Access of the vector
|
119
|
+
# Access of the vector elements.
|
106
120
|
#
|
107
121
|
def fetch id
|
108
122
|
self[ index( id ), 0 ]
|
@@ -39,7 +39,7 @@ class Matrix
|
|
39
39
|
end
|
40
40
|
code_lines.compact.join( "\n" ) << "\n"
|
41
41
|
end
|
42
|
-
|
42
|
+
|
43
43
|
# Builds a code string for incrementing a vector at given indices. Source is
|
44
44
|
# an array.
|
45
45
|
#
|
@@ -63,4 +63,14 @@ class Matrix
|
|
63
63
|
indices: indices,
|
64
64
|
source: "delta" )
|
65
65
|
end
|
66
|
+
|
67
|
+
# Builds a closure for assigning to a column at given indices.
|
68
|
+
#
|
69
|
+
def assign_at_indices_closure indices: (fail ArgumentError, "No indices!")
|
70
|
+
v = self
|
71
|
+
eval "-> ass do\n%s\nend" %
|
72
|
+
self.class.column_vector_assignment_code( vector: ?v,
|
73
|
+
indices: indices,
|
74
|
+
source: "ass" )
|
75
|
+
end
|
66
76
|
end
|
@@ -62,7 +62,7 @@ class YPetri::Simulation::Nodes
|
|
62
62
|
# NOTE: At the moment, the Simulation instance does not have a
|
63
63
|
# parametrized subclass of Simulation::Nodes class, the following
|
64
64
|
# statement is thus made to return a plain array of elements.
|
65
|
-
|
65
|
+
NodesPS().load array.map &method( :node )
|
66
66
|
end
|
67
67
|
|
68
68
|
# Without arguments, returns all the nodes (places / transitions) of the
|
@@ -5,7 +5,7 @@ class YPetri::Simulation
|
|
5
5
|
#
|
6
6
|
class Nodes < Array
|
7
7
|
★ Dependency
|
8
|
-
|
8
|
+
|
9
9
|
class << self
|
10
10
|
# New collection constructor
|
11
11
|
#
|
@@ -13,15 +13,15 @@ class YPetri::Simulation
|
|
13
13
|
new.tap { |inst| inst.load collection }
|
14
14
|
end
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
delegate :simulation, to: "self.class"
|
18
|
-
|
18
|
+
|
19
19
|
# Loads nodes to this collection.
|
20
20
|
#
|
21
21
|
def load nodes
|
22
22
|
nodes.each{ |node| push node }
|
23
23
|
end
|
24
|
-
|
24
|
+
|
25
25
|
# Creates a subset of this collection (of the same class).
|
26
26
|
#
|
27
27
|
def subset nodes=nil, &block # TODO: Rename to subarray
|
@@ -37,7 +37,7 @@ class YPetri::Simulation
|
|
37
37
|
self.class.load( nn )
|
38
38
|
end
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
# Returns an array of the node sources (nodes in the underlying net).
|
42
42
|
#
|
43
43
|
def sources
|
@@ -26,7 +26,7 @@ class YPetri::Simulation
|
|
26
26
|
# Returns the initial marking as a column vector.
|
27
27
|
#
|
28
28
|
def vector
|
29
|
-
simulation.MarkingVector[ self ]
|
29
|
+
simulation.MarkingVector[ *self ]
|
30
30
|
end
|
31
31
|
alias to_marking_vector vector
|
32
32
|
|
@@ -60,5 +60,5 @@ class YPetri::Simulation
|
|
60
60
|
def keys_to_source_places
|
61
61
|
with_keys do |key| key.source end
|
62
62
|
end
|
63
|
-
end # class
|
63
|
+
end # class PlaceMapping
|
64
64
|
end # class YPetri::Simulation
|
@@ -66,11 +66,11 @@ class YPetri::Simulation::Places
|
|
66
66
|
# Place instance identification.
|
67
67
|
#
|
68
68
|
def place( place )
|
69
|
-
begin;
|
69
|
+
begin; PlacePS().instance( place ); rescue NameError, TypeError
|
70
70
|
begin
|
71
71
|
place = net.place( place )
|
72
72
|
places.find { |place_rep| place_rep.source == place } ||
|
73
|
-
|
73
|
+
PlacePS().instance( place.name )
|
74
74
|
rescue NameError, TypeError => msg
|
75
75
|
raise # FIXME: This raise needs to be here in order for the current
|
76
76
|
# tests to pass (they expect NameError, while the raise below would
|
@@ -89,16 +89,17 @@ class YPetri::Simulation::Places
|
|
89
89
|
# that even without an argument, it does not fail, but returns the @Places
|
90
90
|
# parametrized subclass itself.
|
91
91
|
#
|
92
|
-
def Places( array )
|
93
|
-
# as protected in real Simulation instances, while
|
94
|
-
# places method just above does.
|
92
|
+
def Places( array )
|
95
93
|
# Kernel.p array
|
96
|
-
|
94
|
+
PlacesPS().load array.map &method( :place )
|
97
95
|
end
|
98
96
|
|
99
|
-
#
|
100
|
-
#
|
101
|
-
# arguments,
|
97
|
+
# This method is overloaded in such way, that when called without arguments,
|
98
|
+
# it acts as a selector of @places property (instance variable) of the
|
99
|
+
# simulation. When called with arguments, all the arguments must be places
|
100
|
+
# or symbols (or strings etc.) identifying a place, and for these arguments,
|
101
|
+
# the method returns an array of corresponding places (more precisely,
|
102
|
+
# objects that represent places in the context of the receiver simulation).
|
102
103
|
#
|
103
104
|
def places( *places )
|
104
105
|
return @places if places.empty?
|
@@ -1,5 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
require_relative 'free'
|
4
|
+
require_relative 'clamped'
|
5
|
+
|
3
6
|
# A mixin with place type selectors.
|
4
7
|
#
|
5
8
|
class YPetri::Simulation::Places
|
@@ -7,16 +10,16 @@ class YPetri::Simulation::Places
|
|
7
10
|
# Subset of free places, if any.
|
8
11
|
#
|
9
12
|
def free
|
10
|
-
( @Type_free ||= Class.new
|
11
|
-
|
13
|
+
( @Type_free ||= Class.new self.class do
|
14
|
+
include Type_free
|
12
15
|
end ).load subset( &:free? )
|
13
16
|
end
|
14
|
-
|
17
|
+
|
15
18
|
# Subset of clamped places, if any.
|
16
19
|
#
|
17
20
|
def clamped
|
18
|
-
( @Type_clamped ||= Class.new
|
19
|
-
|
21
|
+
( @Type_clamped ||= Class.new self.class do
|
22
|
+
include Type_clamped
|
20
23
|
end ).load subset( &:clamped? )
|
21
24
|
end
|
22
25
|
end # Types
|
@@ -4,8 +4,6 @@
|
|
4
4
|
#
|
5
5
|
class YPetri::Simulation::Places < YPetri::Simulation::Nodes
|
6
6
|
require_relative 'places/types'
|
7
|
-
require_relative 'places/free'
|
8
|
-
require_relative 'places/clamped'
|
9
7
|
|
10
8
|
★ Types
|
11
9
|
|
@@ -15,7 +13,7 @@ class YPetri::Simulation::Places < YPetri::Simulation::Nodes
|
|
15
13
|
p = begin; net.place( place ); rescue NameError, TypeError
|
16
14
|
return super place( place )
|
17
15
|
end
|
18
|
-
super p.name ?
|
16
|
+
super p.name ? PlacePS().new( p, name: p.name ) : PlacePS().new( p )
|
19
17
|
end
|
20
18
|
|
21
19
|
# Marking of the place collection in the current simulation.
|