y_petri 2.0.7 → 2.0.14.p1

Sign up to get free protection for your applications and to get access to all the features.
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