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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7785c5d949ae026d98f538426713dbf8ba5c087b
4
- data.tar.gz: ae69a454df83b31f1314dd2935282ab97e12653f
3
+ metadata.gz: f65095d233b7613fda917f4c1b7ef7f430d61f27
4
+ data.tar.gz: d65127c5a68f1e798ca08c418d4884dfdb1c511c
5
5
  SHA512:
6
- metadata.gz: 3a6cb9bc4a188d7de73fd3dd01067772be90ecfbd6f9673fb1adcfee464d590b9d470faeb154d07b89a95e930525ce3c090682817440a23b79829027e5cdf6d8
7
- data.tar.gz: fdaed6c9020950aa17a2345567d8fdece08b2987f237aa3448ace6dc75ef14d8f417951ce39690f7de3fab7b24722f0bfcbfe888d151863771ca5f1328f36127
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 *args, &b; workspace.Place.new *args, &b end
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 *args, &b; workspace.Transition.new *args, &b end
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 *args, &b; workspace.Net.new *args, &b end
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; id.nil? ? @net_point : workspace.net( id ) end
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; net( id ).name end
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; net_point_set( workspace.Net::Top ) end
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; @net_point = workspace.net( id ) end
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 *args
35
- key = identify *args
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( simulation_name=nil, net: nil, cc: nil, imc: nil, ssc: nil )
44
- simulation_name || { net: net, cc: cc, imc: imc, ssc: ssc }
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? and t.nonstoichiometric? }
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? and t.stoichiometric? }
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? and t.nonstoichiometric? and t.rateless? }
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? and t.stoichiometric? and t.rateless? }
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? and t.nonstoichiometric? }
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 transitions_with_explicit_assignment_action
223
+ def assignment_transitions
208
224
  transitions.select { |t| t.assignment_action? }
209
225
  end
210
- alias transitions_with_assignment_action \
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 tt_with_explicit_assignment_action
219
- transitions_with_explicit_assignment_action.map &:name
231
+ def assignment_tt
232
+ assignment_transitions.map &:name
220
233
  end
221
- alias tt_with_assignment_action tt_with_explicit_assignment_action
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 *args
310
- oo = args.extract_options!
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 *args
317
- oo = args.extract_options!
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