y_petri 2.0.7 → 2.0.14.p1

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.
data/lib/y_petri/net.rb CHANGED
@@ -1,31 +1,26 @@
1
1
  #encoding: utf-8
2
2
 
3
- # Represents a <em>Petri net</em>: A collection of places and
4
- # transitions. The connector arrows – called <em>arcs</em> in
5
- # classical Petri net terminology – are considered a property
6
- # of transitions. In <tt>YPetri</tt>, 'arcs' is a synonym for
7
- # places / transitions connected to a given transition / place.
3
+ require_relative 'dependency_injection'
4
+ require_relative 'net/visualization'
5
+ require_relative 'net/selections'
6
+
7
+ # Represents a _Petri net_: A collection of places and transitions. The
8
+ # connector arrows – called _arcs_ in classical Petri net terminology – can be
9
+ # considered a property of transitions. Therefore in +YPetri+, term 'arcs' is
10
+ # mostly used as a synonym denoting neighboring places / transitions.
8
11
  #
9
12
  class YPetri::Net
10
13
  include NameMagic
14
+ include YPetri::DependencyInjection
11
15
 
12
- def initialize *args; oo = args.extract_options!
13
- @places, @transitions = [], [] # empty arrays
14
- # LATER: let the places/transitions be specified upon init
15
- end
16
-
17
16
  attr_reader :places, :transitions
18
17
 
19
- # Names of places in the net.
20
- #
21
- def pp; places.map &:name end
18
+ def initialize( places: [], transitions: [] )
19
+ @places, @transitions = places, transitions
20
+ end
22
21
 
23
- # Names of transitions in the net.
24
- #
25
- def tt; transitions.map &:name end
26
-
27
- # Includes a place in the net. Returns <em>true</em> if successful,
28
- # <em>false</em> if the place is already included in the net.
22
+ # Includes a place in the net. Returns _true_ if successful, _false_ if the
23
+ # place is already included in the net.
29
24
  #
30
25
  def include_place! place
31
26
  pl = place( place )
@@ -34,10 +29,9 @@ class YPetri::Net
34
29
  return true
35
30
  end
36
31
 
37
- # Includes a transition in the net. Returns <em>true</em> if successful,
38
- # <em>false</em> if the transition is already included in the net. The
39
- # arcs of the transition being included may only connect to the places
40
- # already in the net.
32
+ # Includes a transition in the net. Returns _true_ if successful, _false_ if
33
+ # the transition is already included in the net. The arcs of the transition
34
+ # being included may only connect to the places already in the net.
41
35
  #
42
36
  def include_transition! transition;
43
37
  tr = transition( transition )
@@ -49,10 +43,9 @@ class YPetri::Net
49
43
  return true
50
44
  end
51
45
 
52
- # Excludes a place from the net. Returns <em>true<em> if successful,
53
- # <em>false</em> if the place was not found in the net. A place may
54
- # not be excluded from the net so long as any transitions in the
55
- # net connect to it.
46
+ # Excludes a place from the net. Returns _true_ if successful, _false_ if the
47
+ # place was not found in the net. A place may not be excluded from the net so
48
+ # long as any transitions in the net connect to it.
56
49
  #
57
50
  def exclude_place! place
58
51
  pl = place( place )
@@ -62,8 +55,8 @@ class YPetri::Net
62
55
  return false
63
56
  end
64
57
 
65
- # Excludes a transition from the net. Returns <em>true</em> if successful,
66
- # <em>false</em> if the transition was not found in the net.
58
+ # Excludes a transition from the net. Returns _true_ if successful, _false_ if
59
+ # the transition was not found in the net.
67
60
  #
68
61
  def exclude_transition! transition
69
62
  tr = transition( transition )
@@ -71,9 +64,8 @@ class YPetri::Net
71
64
  return false
72
65
  end
73
66
 
74
- # Includes an object (either place or transition) in the net. Acts by
75
- # calling #include_place! or #include_transition!, as needed, the
76
- # difference being, that errors from bad arguments are swallowed.
67
+ # Includes an object (either place or transition) in the net. Acts by calling
68
+ # +#include_place!+ or +#include_transition!+, as needed, swallowing errors.
77
69
  #
78
70
  def << place_or_transition
79
71
  begin
@@ -82,8 +74,8 @@ class YPetri::Net
82
74
  begin
83
75
  include_transition! place_or_transition
84
76
  rescue NameError
85
- raise NameError,
86
- "Unrecognized place or transition: #{place_or_transition}"
77
+ raise NameError, "Unrecognized place/transition: #{place_or_transition}"
78
+ # TODO: Exceptional Ruby
87
79
  end
88
80
  end
89
81
  return self
@@ -107,213 +99,17 @@ class YPetri::Net
107
99
  return false
108
100
  end
109
101
 
110
- # ----------------------------------------------------------------------
111
- # Methods exposing transition collections acc. to their properties:
112
-
113
- # Array of <em>ts</em> transitions in the net.
114
- #
115
- def timeless_nonstoichiometric_transitions
116
- transitions.select { |t| t.timeless? && t.nonstoichiometric? }
117
- end
118
- alias ts_transitions timeless_nonstoichiometric_transitions
119
-
120
- # Names of <em>ts</em> transitions in the net.
121
- #
122
- def timeless_nonstoichiometric_tt
123
- timeless_nonstoichiometric_transitions.map &:name
124
- end
125
- alias ts_tt timeless_nonstoichiometric_tt
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
-
143
- # Array of <em>tS</em> transitions in the net.
144
- #
145
- def timeless_stoichiometric_transitions
146
- transitions.select { |t| t.timeless? && t.stoichiometric? }
147
- end
148
- alias tS_transitions timeless_stoichiometric_transitions
149
-
150
- # Names of <em>tS</em> transitions in the net.
151
- #
152
- def timeless_stoichiometric_tt
153
- timeless_stoichiometric_transitions.map &:name
154
- end
155
- alias tS_tt timeless_stoichiometric_tt
156
-
157
- # Array of <em>Tsr</em> transitions in the net.
158
- #
159
- def timed_nonstoichiometric_transitions_without_rate
160
- transitions.select { |t| t.timed? && t.nonstoichiometric? && t.rateless? }
161
- end
162
- alias timed_rateless_nonstoichiometric_transitions \
163
- timed_nonstoichiometric_transitions_without_rate
164
- alias Tsr_transitions timed_nonstoichiometric_transitions_without_rate
165
-
166
- # Names of <em>Tsr</em> transitions in the net.
167
- #
168
- def timed_nonstoichiometric_tt_without_rate
169
- timed_nonstoichiometric_transitions_without_rate.map &:name
170
- end
171
- alias timed_rateless_nonstoichiometric_tt \
172
- timed_nonstoichiometric_tt_without_rate
173
- alias Tsr_tt timed_nonstoichiometric_tt_without_rate
174
-
175
- # Array of <em>TSr</em> transitions in the net.
176
- #
177
- def timed_stoichiometric_transitions_without_rate
178
- transitions.select { |t| t.timed? && t.stoichiometric? && t.rateless? }
179
- end
180
- alias timed_rateless_stoichiometric_transitions \
181
- timed_stoichiometric_transitions_without_rate
182
- alias TSr_transitions timed_stoichiometric_transitions_without_rate
183
-
184
- # Names of <em>TSr</em> transitions in the net.
185
- #
186
- def timed_stoichiometric_tt_without_rate
187
- timed_stoichiometric_transitions_without_rate.map &:name
188
- end
189
- alias timed_rateless_stoichiometric_tt timed_stoichiometric_tt_without_rate
190
- alias Tsr_tt timed_stoichiometric_tt_without_rate
191
-
192
- # Array of <em>sR</em> transitions in the net.
193
- #
194
- def nonstoichiometric_transitions_with_rate
195
- transitions.select { |t| t.has_rate? && t.nonstoichiometric? }
196
- end
197
- alias sR_transitions nonstoichiometric_transitions_with_rate
198
-
199
- # Names of <em>sR</em> transitions in the net.
200
- #
201
- def nonstoichiometric_tt_with_rate
202
- nonstoichiometric_transitions_with_rate.map &:name
203
- end
204
- alias sR_tt nonstoichiometric_tt_with_rate
205
-
206
- # Array of <em>SR</em> transitions in the net.
207
- #
208
- def stoichiometric_transitions_with_rate
209
- transitions.select { |t| t.has_rate? and t.stoichiometric? }
210
- end
211
- alias SR_transitions stoichiometric_transitions_with_rate
212
-
213
- # Names of <em>SR</em> transitions in the net.
214
- #
215
- def stoichiometric_tt_with_rate
216
- stoichiometric_transitions_with_rate.map &:name
217
- end
218
- alias SR_tt stoichiometric_tt_with_rate
219
-
220
- # Array of transitions with <em>explicit assignment action</em>
221
- # (<em>A</em> transitions) in the net.
102
+ # Is the net _functional_?
222
103
  #
223
- def assignment_transitions
224
- transitions.select { |t| t.assignment_action? }
104
+ def functional?
105
+ transitions.all? { |t| t.functional? }
225
106
  end
226
- alias A_transitions assignment_transitions
227
-
228
- # Names of transitions with <em>explicit assignment action</em>
229
- # (<em>A</em> transitions) in the net.
230
- #
231
- def assignment_tt
232
- assignment_transitions.map &:name
233
- end
234
- alias A_tt assignment_tt
235
-
236
- # Array of <em>stoichiometric</em> transitions in the net.
237
- #
238
- def stoichiometric_transitions
239
- transitions.select &:stoichiometric?
240
- end
241
- alias S_transitions stoichiometric_transitions
242
-
243
- # Names of <em>stoichiometric</em> transitions in the net.
244
- #
245
- def stoichiometric_tt
246
- stoichiometric_transitions.map &:name
247
- end
248
- alias S_tt stoichiometric_tt
249
-
250
- # Array of <em>nonstoichiometric</em> transitions in the net.
251
- #
252
- def nonstoichiometric_transitions
253
- transitions.select &:nonstoichiometric?
254
- end
255
- alias s_transitions nonstoichiometric_transitions
256
-
257
- # Names of <em>nonstoichimetric</em> transitions in the net.
258
- #
259
- def nonstoichiometric_tt
260
- nonstoichiometric_transitions.map &:name
261
- end
262
- alias s_tt nonstoichiometric_tt
263
-
264
- # Array of <em>timed</em> transitions in the net.
265
- #
266
- def timed_transitions; transitions.select &:timed? end
267
- alias T_transitions timed_transitions
268
-
269
- # Names of <em>timed</em> transitions in the net.
270
- #
271
- def timed_tt; timed_transitions.map &:name end
272
- alias T_tt timed_tt
273
-
274
- # Array of <em>timeless</em> transitions in the net.
275
- #
276
- def timeless_transitions; transitions.select &:timeless? end
277
- alias t_transitions timeless_transitions
278
-
279
- # Names of <em>timeless</em> transitions in the net.
280
- #
281
- def timeless_tt; timeless_transitions.map &:name end
282
- alias t_tt timeless_tt
283
-
284
- # Array of <em>transitions with rate</em> in the net.
285
- #
286
- def transitions_with_rate; transitions.select &:has_rate? end
287
- alias R_transitions transitions_with_rate
288
-
289
- # Names of <em>transitions with rate</em> in the net.
290
- #
291
- def tt_with_rate; transitions_with_rate.map &:name end
292
- alias R_tt tt_with_rate
293
-
294
- # Array of <em>rateless</em> transitions in the net.
295
- #
296
- def rateless_transitions; transitions.select &:rateless? end
297
- alias transitions_without_rate rateless_transitions
298
- alias r_transitions rateless_transitions
299
-
300
- # Names of <em>rateless</em> transitions in the net.
301
- #
302
- def rateless_tt; rateless_transitions.map &:name end
303
- alias tt_without_rate rateless_tt
304
- alias r_tt rateless_tt
305
-
306
- # ==== Inquirer methods about net qualities
307
-
308
- # Is the net <em>functional</em>?
309
- #
310
- def functional?; transitions.all? { |t| t.functional? } end
311
107
 
312
108
  # Is the net <em>timed</em>?
313
109
  #
314
- def timed?; transitions.all? { |t| t.timed? } end
315
-
316
- # ==== Simulation constructors
110
+ def timed?
111
+ transitions.all? { |t| t.timed? }
112
+ end
317
113
 
318
114
  # Creates a new simulation from the net.
319
115
  #
@@ -327,8 +123,6 @@ class YPetri::Net
327
123
  YPetri::TimedSimulation.new **named_args.merge( net: self )
328
124
  end
329
125
 
330
- # ==== Sundry methods
331
-
332
126
  # Networks are equal when their places and transitions are equal.
333
127
  #
334
128
  def == other
@@ -340,82 +134,10 @@ class YPetri::Net
340
134
  #
341
135
  def to_s
342
136
  "#<Net: " + ( name.nil? ? "%s" : "name: #{name}, %s" ) %
343
- "#{places.size} places, #{transitions.size} transitions" + " >"
344
- end
345
-
346
- def visualize
347
- require 'graphviz'
348
- γ = GraphViz.new :G
349
- # Add places and transitions.
350
- place_nodes = places.map.with_object Hash.new do |pl, ꜧ|
351
- ꜧ[pl] = γ.add_nodes pl.name.to_s,
352
- fillcolor: 'lightgrey',
353
- color: 'grey',
354
- style: 'filled'
355
- end
356
- transition_nodes = transitions.map.with_object Hash.new do |tr, ꜧ|
357
- ꜧ[tr] = γ.add_nodes tr.name.to_s,
358
- shape: 'box',
359
- fillcolor: if tr.assignment? then 'yellow'
360
- elsif tr.basic_type == :SR then 'lightcyan'
361
- else 'ghostwhite' end,
362
- color: if tr.assignment? then 'goldenrod'
363
- elsif tr.basic_type == :SR then 'cyan'
364
- else 'grey' end,
365
- style: 'filled'
366
- end
367
- # Add Petri net arcs.
368
- transition_nodes.each { |tr, tr_node|
369
- if tr.assignment? then
370
- tr.codomain.each { |pl|
371
- γ.add_edges tr_node, place_nodes[pl], color: 'goldenrod'
372
- }
373
- ( tr.domain - tr.codomain ).each { |pl|
374
- γ.add_edges tr_node, place_nodes[pl], color: 'grey', arrowhead: 'none'
375
- }
376
- elsif tr.basic_type == :SR then
377
- tr.codomain.each { |pl|
378
- if tr.stoichio[pl] > 0 then # producing arc
379
- γ.add_edges tr_node, place_nodes[pl], color: 'cyan'
380
- elsif tr.stoichio[pl] < 0 then # consuming arc
381
- γ.add_edges place_nodes[pl], tr_node, color: 'cyan'
382
- else
383
- γ.add_edges place_nodes[pl], tr_node, color: 'grey', arrowhead: 'none'
384
- end
385
- }
386
- ( tr.domain - tr.codomain ).each { |pl|
387
- γ.add_edges tr_node, place_nodes[pl], color: 'grey', arrowhead: 'none'
388
- }
389
- end
390
- }
391
- # Generate output image.
392
- γ.output png: File.expand_path( "~/y_petri_graph.png" )
393
- # require 'y_support/kde'
394
- YSupport::KDE.show_file_with_kioclient File.expand_path( "~/y_petri_graph.png" )
137
+ "#{places.size} places, #{transitions.size} transitions" + ">"
395
138
  end
396
139
 
397
140
  # Inspect string of the instance.
398
141
  #
399
142
  def inspect; to_s end
400
-
401
- private
402
-
403
- # Display a file with kioclient (KDE).
404
- #
405
- def show_file_with_kioclient( file_name )
406
- system "sleep 0.2; kioclient exec 'file:%s'" %
407
- File.expand_path( '.', file_name )
408
- end
409
-
410
- # Place, Transition, Net classes.
411
- #
412
- def Place; ::YPetri::Place end
413
- def Transition; ::YPetri::Transition end
414
- def Net; ::YPetri::Net end
415
-
416
- # Instance identification methods.
417
- #
418
- def place( which ); Place().instance( which ) end
419
- def transition( which ); Transition().instance( which ) end
420
- def net( which ); Net().instance( which ) end
421
143
  end # class YPetri::Net
@@ -6,7 +6,9 @@ class YPetri::Place
6
6
  # Marking guard.
7
7
  #
8
8
  class Guard
9
- ERRMSG = -> m, assert { "Marking #{m}:#{m.class} #{assert}!" }
9
+ ERRMSG = -> m, pl, assert do
10
+ "Marking #{m}:#{m.class}#{pl ? " of place #{pl}" : ''} #{assert}!"
11
+ end
10
12
 
11
13
  attr_reader :assertion, :block
12
14
 
@@ -32,9 +34,9 @@ class YPetri::Place
32
34
  # Validates a supplied marking value against the guard block. Raises
33
35
  # +YPetri::GuardError+ if the guard fails, otherwise returns _true_.
34
36
  #
35
- def validate( marking_value )
36
- λ = __fail__( marking_value, assertion )
37
- λ.call if @Lab.new( λ ).instance_exec( marking_value, &block ) == false
37
+ def validate( marking, place=nil )
38
+ λ = __fail__( marking, place, assertion )
39
+ λ.call if @Lab.new( λ ).instance_exec( marking, &block ) == false
38
40
  return true
39
41
  end
40
42
 
@@ -42,8 +44,8 @@ class YPetri::Place
42
44
 
43
45
  # Constructs the fail closure.
44
46
  #
45
- def __fail__ marking_value, assertion
46
- -> { fail YPetri::GuardError, ERRMSG.( marking_value, assertion ) }
47
+ def __fail__ marking, place, assertion
48
+ -> { fail YPetri::GuardError, ERRMSG.( marking, place, assertion ) }
47
49
  end
48
50
  end
49
51
 
@@ -92,7 +94,7 @@ class YPetri::Place
92
94
  #
93
95
  def federated_guard_closure
94
96
  lineup = guards.dup
95
- -> marking_value { lineup.each { |g| g.validate marking_value }; true }
97
+ -> m { lineup.each { |g| g.validate( m, self ) }; return m }
96
98
  end
97
99
 
98
100
  # Applies guards on the marking currently owned by the place.
@@ -109,14 +111,19 @@ class YPetri::Place
109
111
  # interchangeable, except complex numbers.
110
112
  #
111
113
  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
114
+ case reference_marking
115
+ when Complex then marking "should be Numeric" do |m| m.is_a? Numeric end
116
+ when Numeric then
117
+ marking "should be Numeric" do |m| m.is_a? Numeric end
117
118
  marking "should not be complex" do |m| fail if m.is_a? Complex end
119
+ marking "should not be negative" do |m| m >= 0 end
120
+ when nil then # no guards
121
+ when true, false then marking "should be Boolean" do |m| m == !!m end
118
122
  else
119
- marking "should be a #{ref_class}" do |m| m.is_a? ref_class end
123
+ reference_marking.class.tap do |klass|
124
+ marking "should be a #{klass}" do |m| m.is_a? klass end
125
+ end
120
126
  end
127
+ return nil
121
128
  end
122
129
  end # class YPetri::Place
data/lib/y_petri/place.rb CHANGED
@@ -13,7 +13,6 @@ class YPetri::Place
13
13
  attr_reader :quantum
14
14
  attr_reader :guards
15
15
  attr_accessor :default_marking
16
- attr_writer :marking
17
16
 
18
17
  # Named parameters supplied upon place initialization may include:
19
18
  #
@@ -55,7 +54,7 @@ class YPetri::Place
55
54
  &block
56
55
  @upstream_arcs, @downstream_arcs, @guards = [], [], [] # init to empty
57
56
  @quantum, @default_marking = quantum, default_marking
58
- @marking = marking || default_marking
57
+ self.marking = marking || default_marking
59
58
 
60
59
  # Check in :guard named argument and &block.
61
60
  if guard.ℓ? then # guard NL assertion not given, use block or default guards
@@ -91,6 +90,12 @@ class YPetri::Place
91
90
  guard args[0], &block
92
91
  end
93
92
 
93
+ # Marking setter.
94
+ #
95
+ def marking=( new_marking )
96
+ @marking = guard.( new_marking )
97
+ end
98
+
94
99
  # Alias for #marking=
95
100
  #
96
101
  def value=( marking ); self.marking = marking end
@@ -101,20 +106,20 @@ class YPetri::Place
101
106
 
102
107
  # Adds tokens to the place.
103
108
  #
104
- def add( amount_of_tokens )
105
- @marking += amount_of_tokens
109
+ def add( amount )
110
+ @marking = guard.( @marking + amount )
106
111
  end
107
112
 
108
113
  # Subtracts tokens from the place.
109
114
  #
110
- def subtract( amount_of_tokens )
111
- @marking -= amount_of_tokens
115
+ def subtract( amount )
116
+ @marking = guard.( @marking - amount )
112
117
  end
113
118
 
114
119
  # Resets place marking back to its default marking.
115
120
  #
116
121
  def reset_marking
117
- @marking = @default_marking
122
+ @marking = guard.( @default_marking )
118
123
  end
119
124
 
120
125
  # Produces the inspect string of the place.
@@ -1,14 +1,13 @@
1
1
  #encoding: utf-8
2
2
 
3
- # Emphasizing separation of concerns, the model is defined as agnostic of
4
- # simulation settings. Only for the purpose of simulation, model is combined
5
- # together with specific simulation settings. Simulation settings consist of
6
- # global settings (eg. time step, sampling rate...) and object specific
7
- # settings (eg. clamps, constraints...). Again, clamps and constraints *do
8
- # not* belong to the model. Simulation methods are also concern of this
9
- # class, not the model class. Thus, simulation is not done by calling
10
- # instance methods of the model. Instead, this class makes a 'mental image'
11
- # of the model and only that is used for actual simulation.
3
+ # The Petri net model is agnostic of simulation settings. Only for the purpose
4
+ # of simulation, model is combined together with specific simulation settings.
5
+ # Simulation settings consist of global settings (time step, sampling rate...)
6
+ # and object specific settings (clamps, constraints...). Again, clamps and
7
+ # constraints *do not belong* to the model. The Petri net model is also agnostic
8
+ # of the simulation methods. Simulation is not achieved by calling instance
9
+ # methods of the model. Instead, Simulation class makes a 'mental image' of the
10
+ # model, and uses that one in the actual simulation.
12
11
  #
13
12
  class YPetri::Simulation
14
13
  SAMPLING_DECIMAL_PLACES = 5
@@ -0,0 +1,37 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Mixin for the transitions with assignment action.
4
+ #
5
+ module YPetri::Transition::Assignment
6
+ # Transition's action (before validation).
7
+ #
8
+ def action
9
+ action_closure.( *domain_marking )
10
+ end
11
+
12
+ # Applies action to the codomain, honoring cocking. Returns true if the transition
13
+ # fired, false if it wasn't cocked.
14
+ #
15
+ def fire
16
+ cocked?.tap { |x| ( uncock; fire! ) if x }
17
+ end
18
+
19
+ # Assigns the action closure result to the codomain, regardless of cocking.
20
+ #
21
+ def fire!
22
+ try "to call #fire! method" do
23
+ act = note "action", is: Array( action )
24
+ codomain.each_with_index do |codomain_place, i|
25
+ note "assigning action element no. #{i} to place #{codomain_place}"
26
+ codomain_place.marking = note "marking to assign", is: act.fetch( i )
27
+ end
28
+ end
29
+ return nil
30
+ end
31
+
32
+ # A transitions are always _enabled_.
33
+ #
34
+ def enabled?
35
+ true
36
+ end
37
+ end # class YPetri::Transition::Assignment
@@ -60,6 +60,7 @@ class YPetri::Transition
60
60
  #
61
61
  def initialize *args
62
62
  check_in_arguments *args # the big work of checking in args
63
+ extend timed? ? Timed : assignment? ? Assignment : OrdinaryTimeless
63
64
  inform_upstream_places # that they have been connected
64
65
  inform_downstream_places # that they have been connected
65
66
  uncock # transitions initialize uncocked
@@ -0,0 +1,46 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # Mixin for timed non-assignment timeless Petri net transitions.
4
+ #
5
+ module YPetri::Transition::OrdinaryTimeless
6
+ # Result of the transition's "function", regardless of the #enabled? status.
7
+ #
8
+ def action
9
+ if stoichiometric? then
10
+ rslt = action_closure.( *domain_marking )
11
+ stoichiometry.map { |coeff| rslt * coeff }
12
+ else
13
+ action_closure.( *domain_marking )
14
+ end
15
+ end # action
16
+
17
+ # Fires the transition, honoring cocking. Returns true if the transition
18
+ # fired, false if it wasn't cocked.
19
+ #
20
+ def fire
21
+ cocked?.tap { |x| ( uncock; fire! ) if x }
22
+ end
23
+
24
+ # Fires the transition regardless of cocking.
25
+ #
26
+ def fire!
27
+ try "to call #fire method" do
28
+ act = note "action", is: Array( action )
29
+ codomain.each_with_index do |codomain_place, i|
30
+ note "adding action element no. #{i} to place #{codomain_place}"
31
+ codomain_place.add( note "marking change", is: act.fetch( i ) )
32
+ end
33
+ end
34
+ return nil
35
+ end
36
+
37
+ # Timeless transition is _enabled_ if its action would result in a legal
38
+ # codomain marking.
39
+ #
40
+ def enabled?
41
+ codomain.zip( action ).all? do |place, change|
42
+ begin; place.guard.( place.marking + change )
43
+ rescue YPetri::GuardError; false end
44
+ end
45
+ end
46
+ end # class YPetri::Transition::OrdinaryTimeless