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 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