y_petri 2.0.3 → 2.0.7
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/dependency_injection.rb +45 -0
- data/lib/y_petri/manipulator/petri_net_related_methods.rb +26 -7
- data/lib/y_petri/manipulator/simulation_related_methods.rb +4 -4
- data/lib/y_petri/net.rb +30 -21
- data/lib/y_petri/place/arcs.rb +96 -0
- data/lib/y_petri/place/guard.rb +122 -0
- data/lib/y_petri/place.rb +89 -132
- data/lib/y_petri/simulation.rb +191 -168
- data/lib/y_petri/timed_simulation.rb +29 -20
- data/lib/y_petri/transition/arcs.rb +51 -0
- data/lib/y_petri/transition/cocking.rb +32 -0
- data/lib/y_petri/transition/constructor_syntax.rb +378 -0
- data/lib/y_petri/transition.rb +391 -831
- data/lib/y_petri/version.rb +1 -1
- data/lib/y_petri/workspace/parametrized_subclassing.rb +8 -13
- data/lib/y_petri/workspace/simulation_related_methods.rb +13 -11
- data/lib/y_petri.rb +8 -3
- data/test/place_test.rb +83 -0
- data/test/transition_test.rb +325 -0
- data/test/y_petri_test.rb +15 -410
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f65095d233b7613fda917f4c1b7ef7f430d61f27
|
4
|
+
data.tar.gz: d65127c5a68f1e798ca08c418d4884dfdb1c511c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b10bfea43b1e4a9f531ce953cac4e943fd8570d5b441acbc9e84a1f16e2d20a6ab445ff9ffe4c76c4d8ae914f876d0814a8b28d57aa7abdd356e22e19b0755e9
|
7
|
+
data.tar.gz: e2eae4ee3ecb0259979634d3b8f21982d9d93e3b6cc2e82fe658f3561b591f36d5a31af521815a3b4b741d1553796b79332e1ec8c27b564cd88e9d9b1ad3e572
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
# Provides basic skeleton for dependency injection for the triples of the
|
4
|
+
# parametrized subclasses of Place, Transition and Net in different workspaces.
|
5
|
+
#
|
6
|
+
module YPetri::DependencyInjection
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
# Place class -- to be overriden in subclasses for dependency injection.
|
11
|
+
#
|
12
|
+
def Place
|
13
|
+
YPetri::Place
|
14
|
+
end
|
15
|
+
|
16
|
+
# Transition class -- to be overriden in subclasses for dependency injection.
|
17
|
+
#
|
18
|
+
def Transition
|
19
|
+
YPetri::Transition
|
20
|
+
end
|
21
|
+
|
22
|
+
# Net class -- to be overriden in subclasses for dependency injection
|
23
|
+
#
|
24
|
+
def Net
|
25
|
+
YPetri::Net
|
26
|
+
end
|
27
|
+
|
28
|
+
# Place instance identification.
|
29
|
+
#
|
30
|
+
def place id
|
31
|
+
Place().instance( id )
|
32
|
+
end
|
33
|
+
|
34
|
+
# Transition instance identification.
|
35
|
+
#
|
36
|
+
def transition id
|
37
|
+
Transition().instance( id )
|
38
|
+
end
|
39
|
+
|
40
|
+
# Net instance identification.
|
41
|
+
#
|
42
|
+
def net id
|
43
|
+
Net().instance( id )
|
44
|
+
end
|
45
|
+
end # module YPetri::DependencyInjection
|
@@ -26,30 +26,49 @@ module YPetri::Manipulator::PetriNetRelatedMethods
|
|
26
26
|
|
27
27
|
# Place constructor: Creates a new place in the current workspace.
|
28
28
|
#
|
29
|
-
def Place *
|
29
|
+
def Place( *ordered_args, **named_args, &block )
|
30
|
+
fail ArgumentError, "If block is given, :guard named argument " +
|
31
|
+
"must not be given!" if named_args.has? :guard if block
|
32
|
+
named_args.update( guard: block ) if block # use block as a guard
|
33
|
+
named_args.may_have :default_marking, syn!: :m!
|
34
|
+
named_args.may_have :marking, syn!: :m
|
35
|
+
workspace.Place.new *ordered_args, **named_args
|
36
|
+
end
|
30
37
|
|
31
38
|
# Transiton constructor: Creates a new transition in the current workspace.
|
32
39
|
#
|
33
|
-
def Transition *
|
40
|
+
def Transition( *aa, **oo, &b )
|
41
|
+
workspace.Transition.new *aa, **oo, &b
|
42
|
+
end
|
34
43
|
|
35
44
|
# Net constructor: Creates a new Net instance in the current workspace.
|
36
45
|
#
|
37
|
-
def Net *
|
46
|
+
def Net *aa, **oo, &b
|
47
|
+
workspace.Net.new *aa, **oo, &b
|
48
|
+
end
|
38
49
|
|
39
50
|
# Returns the net identified, or the net at point (if no argument given).
|
40
51
|
#
|
41
|
-
def net id=nil
|
52
|
+
def net id=nil
|
53
|
+
id.nil? ? @net_point : workspace.net( id )
|
54
|
+
end
|
42
55
|
|
43
56
|
# Returns the name of the identified net, or of the net at point (if no
|
44
57
|
# argument given).
|
45
58
|
#
|
46
|
-
def ne id=nil
|
59
|
+
def ne id=nil
|
60
|
+
net( id ).name
|
61
|
+
end
|
47
62
|
|
48
63
|
# Sets net point to workspace.Net::Top
|
49
64
|
#
|
50
|
-
def net_point_reset
|
65
|
+
def net_point_reset
|
66
|
+
net_point_set( workspace.Net::Top )
|
67
|
+
end
|
51
68
|
|
52
69
|
# Sets net point to the net identified by the argument (by name or instance).
|
53
70
|
#
|
54
|
-
def net_point_set id
|
71
|
+
def net_point_set id
|
72
|
+
@net_point = workspace.net( id )
|
73
|
+
end
|
55
74
|
end # module YPetri::Manipulator::PetriNetRelatedMethods
|
@@ -31,8 +31,8 @@ module YPetri::Manipulator::SimulationRelatedMethods
|
|
31
31
|
# A simulation is identified either by its name (if named), or by its
|
32
32
|
# parameters and settings (:net, :cc, :imc, :ssc).
|
33
33
|
#
|
34
|
-
def set
|
35
|
-
key = identify
|
34
|
+
def set **nn
|
35
|
+
key = identify **nn
|
36
36
|
@key = if key.nil? then key
|
37
37
|
elsif @hash.has_key? key then key
|
38
38
|
else raise "No simulation identified by #{key}!" end
|
@@ -40,8 +40,8 @@ module YPetri::Manipulator::SimulationRelatedMethods
|
|
40
40
|
|
41
41
|
# Helper method specifying how a simulation is identified by arguments.
|
42
42
|
#
|
43
|
-
def identify(
|
44
|
-
|
43
|
+
def identify( name: nil, net: nil, cc: nil, imc: nil, ssc: nil, **nn )
|
44
|
+
name || { net: net, cc: cc, imc: imc, ssc: ssc }.merge( nn )
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
data/lib/y_petri/net.rb
CHANGED
@@ -113,7 +113,7 @@ class YPetri::Net
|
|
113
113
|
# Array of <em>ts</em> transitions in the net.
|
114
114
|
#
|
115
115
|
def timeless_nonstoichiometric_transitions
|
116
|
-
transitions.select{ |t| t.timeless?
|
116
|
+
transitions.select { |t| t.timeless? && t.nonstoichiometric? }
|
117
117
|
end
|
118
118
|
alias ts_transitions timeless_nonstoichiometric_transitions
|
119
119
|
|
@@ -124,10 +124,26 @@ class YPetri::Net
|
|
124
124
|
end
|
125
125
|
alias ts_tt timeless_nonstoichiometric_tt
|
126
126
|
|
127
|
+
# Array of <em>tsa</em> transitions in the net.
|
128
|
+
#
|
129
|
+
def timeless_nonstoichiometric_nonassignment_transitions
|
130
|
+
transitions.select { |t|
|
131
|
+
t.timeless? && t.nonstoichiometric? && ! t.assignment_action?
|
132
|
+
}
|
133
|
+
end
|
134
|
+
alias tsa_transitions timeless_nonstoichiometric_nonassignment_transitions
|
135
|
+
|
136
|
+
# Names of <em>tsa</em> transitions in the net.
|
137
|
+
#
|
138
|
+
def timeless_nonstoichiometric_nonassignment_tt
|
139
|
+
timeless_nonstoichiometric_nonassignment_transitions.map &:name
|
140
|
+
end
|
141
|
+
alias tsa_tt timeless_nonstoichiometric_nonassignment_tt
|
142
|
+
|
127
143
|
# Array of <em>tS</em> transitions in the net.
|
128
144
|
#
|
129
145
|
def timeless_stoichiometric_transitions
|
130
|
-
transitions.select{ |t| t.timeless?
|
146
|
+
transitions.select { |t| t.timeless? && t.stoichiometric? }
|
131
147
|
end
|
132
148
|
alias tS_transitions timeless_stoichiometric_transitions
|
133
149
|
|
@@ -141,7 +157,7 @@ class YPetri::Net
|
|
141
157
|
# Array of <em>Tsr</em> transitions in the net.
|
142
158
|
#
|
143
159
|
def timed_nonstoichiometric_transitions_without_rate
|
144
|
-
transitions.select{ |t| t.timed?
|
160
|
+
transitions.select { |t| t.timed? && t.nonstoichiometric? && t.rateless? }
|
145
161
|
end
|
146
162
|
alias timed_rateless_nonstoichiometric_transitions \
|
147
163
|
timed_nonstoichiometric_transitions_without_rate
|
@@ -159,7 +175,7 @@ class YPetri::Net
|
|
159
175
|
# Array of <em>TSr</em> transitions in the net.
|
160
176
|
#
|
161
177
|
def timed_stoichiometric_transitions_without_rate
|
162
|
-
transitions.select { |t| t.timed?
|
178
|
+
transitions.select { |t| t.timed? && t.stoichiometric? && t.rateless? }
|
163
179
|
end
|
164
180
|
alias timed_rateless_stoichiometric_transitions \
|
165
181
|
timed_stoichiometric_transitions_without_rate
|
@@ -176,7 +192,7 @@ class YPetri::Net
|
|
176
192
|
# Array of <em>sR</em> transitions in the net.
|
177
193
|
#
|
178
194
|
def nonstoichiometric_transitions_with_rate
|
179
|
-
transitions.select { |t| t.has_rate?
|
195
|
+
transitions.select { |t| t.has_rate? && t.nonstoichiometric? }
|
180
196
|
end
|
181
197
|
alias sR_transitions nonstoichiometric_transitions_with_rate
|
182
198
|
|
@@ -204,23 +220,18 @@ class YPetri::Net
|
|
204
220
|
# Array of transitions with <em>explicit assignment action</em>
|
205
221
|
# (<em>A</em> transitions) in the net.
|
206
222
|
#
|
207
|
-
def
|
223
|
+
def assignment_transitions
|
208
224
|
transitions.select { |t| t.assignment_action? }
|
209
225
|
end
|
210
|
-
alias
|
211
|
-
transitions_with_explicit_assignment_action
|
212
|
-
alias assignment_transitions transitions_with_explicit_assignment_action
|
213
|
-
alias A_transitions transitions_with_explicit_assignment_action
|
226
|
+
alias A_transitions assignment_transitions
|
214
227
|
|
215
228
|
# Names of transitions with <em>explicit assignment action</em>
|
216
229
|
# (<em>A</em> transitions) in the net.
|
217
230
|
#
|
218
|
-
def
|
219
|
-
|
231
|
+
def assignment_tt
|
232
|
+
assignment_transitions.map &:name
|
220
233
|
end
|
221
|
-
alias
|
222
|
-
alias assignment_tt tt_with_assignment_action
|
223
|
-
alias A_tt tt_with_assignment_action
|
234
|
+
alias A_tt assignment_tt
|
224
235
|
|
225
236
|
# Array of <em>stoichiometric</em> transitions in the net.
|
226
237
|
#
|
@@ -306,16 +317,14 @@ class YPetri::Net
|
|
306
317
|
|
307
318
|
# Creates a new simulation from the net.
|
308
319
|
#
|
309
|
-
def new_simulation
|
310
|
-
|
311
|
-
YPetri::Simulation.new *args, oo.merge( net: self )
|
320
|
+
def new_simulation( **named_args )
|
321
|
+
YPetri::Simulation.new **named_args.merge( net: self )
|
312
322
|
end
|
313
323
|
|
314
324
|
# Creates a new timed simulation from the net.
|
315
325
|
#
|
316
|
-
def new_timed_simulation
|
317
|
-
|
318
|
-
YPetri::TimedSimulation.new oo.merge( net: self )
|
326
|
+
def new_timed_simulation( **named_args )
|
327
|
+
YPetri::TimedSimulation.new **named_args.merge( net: self )
|
319
328
|
end
|
320
329
|
|
321
330
|
# ==== Sundry methods
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# Connectivity aspect of a Petri net place.
|
4
|
+
#
|
5
|
+
class YPetri::Place
|
6
|
+
# Transitions that can directly add/remove tokens from this place. Aliased as
|
7
|
+
# +#upstream_transitions+ and +#ϝ+. (Digamma resembles "f", meaning function,
|
8
|
+
# well known from existing spreadsheet software.)
|
9
|
+
#
|
10
|
+
attr_reader :upstream_arcs
|
11
|
+
alias :upstream_transitions :upstream_arcs
|
12
|
+
alias :ϝ :upstream_arcs
|
13
|
+
|
14
|
+
# Transitions whose action directly depends on this place. Aliased as
|
15
|
+
# +#downstream_transitions+.
|
16
|
+
#
|
17
|
+
attr_reader :downstream_arcs
|
18
|
+
alias :downstream_transitions :downstream_arcs
|
19
|
+
|
20
|
+
# All the transitions connected to the place.
|
21
|
+
#
|
22
|
+
def arcs
|
23
|
+
upstream_arcs | downstream_arcs
|
24
|
+
end
|
25
|
+
|
26
|
+
# Union of the domains of the upstream transitions.
|
27
|
+
#
|
28
|
+
def precedents
|
29
|
+
upstream_transitions.map( &:upstream_places ).reduce( [], :| )
|
30
|
+
end
|
31
|
+
alias :upstream_places :precedents
|
32
|
+
|
33
|
+
# Union of the codomains of the downstream transitions.
|
34
|
+
#
|
35
|
+
def dependents
|
36
|
+
downstream_transitions.map( &:downstream_places ).reduce( [], :| )
|
37
|
+
end
|
38
|
+
alias :downstream_places :dependents
|
39
|
+
|
40
|
+
# Fires the upstream transitions.
|
41
|
+
#
|
42
|
+
def fire_upstream
|
43
|
+
upstream_arcs.each &:fire
|
44
|
+
end
|
45
|
+
|
46
|
+
# Fires the upstream transitions regardless of cocking. (Normally, transitions
|
47
|
+
# should be cocked (+#cock+ method) before they are fired (+#fire+ method).)
|
48
|
+
#
|
49
|
+
def fire_upstream!
|
50
|
+
upstream_arcs.each &:fire!
|
51
|
+
end
|
52
|
+
|
53
|
+
# Fires the whole upstream portion of the net. Cocking ensures that the
|
54
|
+
# recursive firing will eventually end.
|
55
|
+
#
|
56
|
+
def fire_upstream_recursively
|
57
|
+
# LATER: This as a global hash { place => fire_list }
|
58
|
+
@upstream_arcs.each &:fire_upstream_recursively
|
59
|
+
end
|
60
|
+
|
61
|
+
# Fires the downstream transitions.
|
62
|
+
#
|
63
|
+
def fire_downstream
|
64
|
+
downstream_arcs.each &:fire
|
65
|
+
end
|
66
|
+
|
67
|
+
# Fires the downstream transitions regardless of cocking. (Normally,
|
68
|
+
# transitions should be cocked (+#cock+ method) before they are fired (+#fire+
|
69
|
+
# method).)
|
70
|
+
#
|
71
|
+
def fire_downstream!
|
72
|
+
@downstream_arcs.each &:fire!
|
73
|
+
end
|
74
|
+
|
75
|
+
# Fires the whole downstream portion of the net. Cocking ensures that the
|
76
|
+
# recursive firing will eventually end.
|
77
|
+
#
|
78
|
+
def fire_downstream_recursively
|
79
|
+
# LATER: This as a global hash { place => fire_list }
|
80
|
+
@downstream_arcs.each &:fire_downstream_recursively
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# Notes a new upstream transition.
|
86
|
+
#
|
87
|
+
def register_upstream_transition( transition )
|
88
|
+
@upstream_arcs << transition
|
89
|
+
end
|
90
|
+
|
91
|
+
# Notes a new downstream transition.
|
92
|
+
#
|
93
|
+
def register_downstream_transition( transition )
|
94
|
+
@downstream_arcs << transition
|
95
|
+
end
|
96
|
+
end # class YPetri::Place
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
# Guard mechanics aspect of a place.
|
4
|
+
#
|
5
|
+
class YPetri::Place
|
6
|
+
# Marking guard.
|
7
|
+
#
|
8
|
+
class Guard
|
9
|
+
ERRMSG = -> m, assert { "Marking #{m}:#{m.class} #{assert}!" }
|
10
|
+
|
11
|
+
attr_reader :assertion, :block
|
12
|
+
|
13
|
+
# Requires a NL guard assertion (used in GuardError messages), and a guard
|
14
|
+
# block expressing the same assertion formally, in code. Attention: *Only
|
15
|
+
# _false_ result is considered a failure! If the block returns _nil_, the
|
16
|
+
# guard has passed!* When +YPetri::Guard+ is in action (typically via its
|
17
|
+
# +#validate+ method), it raises +YPetri::GuardError+ if the guard block
|
18
|
+
# returns _false_. However, the guard block is welcome to raise +GuardError+
|
19
|
+
# on its own, and for this purpose, it is evaluated inside a special "Lab"
|
20
|
+
# object, with +#fail+ method redefined so as to accept no arguments, and
|
21
|
+
# automatically raise appropriately worded +GuardError+. See also:
|
22
|
+
# {+YPetri#guard+ method}[rdoc-ref:YPetri::guard].
|
23
|
+
#
|
24
|
+
def initialize assertion_NL_string, &block
|
25
|
+
@assertion, @block = assertion_NL_string, block
|
26
|
+
@Lab = Class.new BasicObject do
|
27
|
+
def initialize λ; @λ = λ end
|
28
|
+
def fail; @λ.call end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Validates a supplied marking value against the guard block. Raises
|
33
|
+
# +YPetri::GuardError+ if the guard fails, otherwise returns _true_.
|
34
|
+
#
|
35
|
+
def validate( marking_value )
|
36
|
+
λ = __fail__( marking_value, assertion )
|
37
|
+
λ.call if @Lab.new( λ ).instance_exec( marking_value, &block ) == false
|
38
|
+
return true
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# Constructs the fail closure.
|
44
|
+
#
|
45
|
+
def __fail__ marking_value, assertion
|
46
|
+
-> { fail YPetri::GuardError, ERRMSG.( marking_value, assertion ) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Expects a guard assertion in natural language, and a guard block. Guard
|
51
|
+
# block is a unary block capable of validating a marking value. The validation
|
52
|
+
# is considered as having failed if:
|
53
|
+
#
|
54
|
+
# 1. The block returns _false_.
|
55
|
+
# 2. The block raises +YPetri::GuardError+.
|
56
|
+
#
|
57
|
+
# In all other cases, including the block returning _nil_, the validation is
|
58
|
+
# considered as having passed! The block is evaluated in the context of a
|
59
|
+
# special "Lab" object, which has +#fail+ method redefined so that it can
|
60
|
+
# (and must) be called without parameters, and produces an appropriately
|
61
|
+
# worded +GuardError+. (Other exceptions can be still raised using +#raise+
|
62
|
+
# method.)
|
63
|
+
#
|
64
|
+
# As for the NL assertion, apart from self-documenting the code, it is used
|
65
|
+
# for constructing appropriately worded +GuardError+ messages:
|
66
|
+
#
|
67
|
+
# guard "should be a number" do |m| fail unless m.is_a? Numeric end
|
68
|
+
#
|
69
|
+
# Then +guard! :foobar+ raises +GuardError+ with message "Marking foobar:Symbol
|
70
|
+
# should be a number!"
|
71
|
+
#
|
72
|
+
# The method returns the reference to the +YPetri::Guard+ object, that has
|
73
|
+
# been constructed and already included in the collection of this place's
|
74
|
+
# guards.
|
75
|
+
#
|
76
|
+
# Finally, this method is overloaded in such way, that if no block is
|
77
|
+
# given to it, it acts as a frontend for the +#federated_guard_closure+
|
78
|
+
# method: It either applies the federated closure to the marking value given
|
79
|
+
# in the argument, or returns the federated closure itself if no arguemnts
|
80
|
+
# were given (behaving as +#federated_guard_closure+ alias in this case).
|
81
|
+
#
|
82
|
+
def guard *args, &block
|
83
|
+
if block then @guards << Guard.new( *args, &block )
|
84
|
+
elsif args.size == 1 then federated_guard_closure.( args[0] )
|
85
|
+
elsif args.empty? then federated_guard_closure
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns a joint guard closure, composed of all the guards defined for the
|
90
|
+
# place at the moment. Joint closure passes if and only if all the guard
|
91
|
+
# blocks pass for the given marking value.
|
92
|
+
#
|
93
|
+
def federated_guard_closure
|
94
|
+
lineup = guards.dup
|
95
|
+
-> marking_value { lineup.each { |g| g.validate marking_value }; true }
|
96
|
+
end
|
97
|
+
|
98
|
+
# Applies guards on the marking currently owned by the place.
|
99
|
+
#
|
100
|
+
def guard!
|
101
|
+
guard.( marking )
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
# If no guards were specified by the user, this method can make them up in a
|
107
|
+
# standard way, using user-supplied marking / default marking as a type
|
108
|
+
# reference. Numeric types are an exception – they are considered mutually
|
109
|
+
# interchangeable, except complex numbers.
|
110
|
+
#
|
111
|
+
def add_default_guards!( reference_marking )
|
112
|
+
ref_class = reference_marking.class
|
113
|
+
if ref_class < Numeric and not ref_class < Complex then
|
114
|
+
# Note that #marking method is overloaded to act as #guard method when
|
115
|
+
# a block is supplied to it:
|
116
|
+
marking "should be a number" do |m| m.is_a? Numeric end
|
117
|
+
marking "should not be complex" do |m| fail if m.is_a? Complex end
|
118
|
+
else
|
119
|
+
marking "should be a #{ref_class}" do |m| m.is_a? ref_class end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end # class YPetri::Place
|