y_petri 2.0.3 → 2.0.7
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/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
|