y_petri 2.2.4 → 2.3.2

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.
Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +675 -0
  3. data/README.md +6 -3
  4. data/Rakefile +1 -1
  5. data/lib/y_petri/agent/{petri_net_related.rb → petri_net_aspect.rb} +34 -10
  6. data/lib/y_petri/agent/{simulation_related.rb → simulation_aspect.rb} +49 -34
  7. data/lib/y_petri/agent.rb +5 -5
  8. data/lib/y_petri/core/guarded.rb +24 -0
  9. data/lib/y_petri/core/timed/euler.rb +4 -8
  10. data/lib/y_petri/core/timed/gillespie.rb +11 -17
  11. data/lib/y_petri/core/timed/methods.rb +23 -0
  12. data/lib/y_petri/core/timed/pseudo_euler.rb +10 -13
  13. data/lib/y_petri/core/timed/quasi_euler.rb +9 -8
  14. data/lib/y_petri/core/timed/runge_kutta.rb +10 -18
  15. data/lib/y_petri/core/timed.rb +6 -14
  16. data/lib/y_petri/core/timeless/methods.rb +15 -0
  17. data/lib/y_petri/core/timeless/pseudo_euler.rb +4 -8
  18. data/lib/y_petri/core/timeless.rb +9 -4
  19. data/lib/y_petri/core.rb +44 -42
  20. data/lib/y_petri/net/data_set.rb +246 -142
  21. data/lib/y_petri/net/node_access.rb +282 -0
  22. data/lib/y_petri/net/own_state.rb +14 -4
  23. data/lib/y_petri/net/state/feature/assignment.rb +123 -0
  24. data/lib/y_petri/net/state/feature/delta.rb +55 -35
  25. data/lib/y_petri/net/state/feature/firing.rb +68 -25
  26. data/lib/y_petri/net/state/feature/flux.rb +9 -2
  27. data/lib/y_petri/net/state/feature/gradient.rb +36 -19
  28. data/lib/y_petri/net/state/feature/marking.rb +10 -5
  29. data/lib/y_petri/net/state/feature.rb +105 -11
  30. data/lib/y_petri/net/state/features/record.rb +144 -99
  31. data/lib/y_petri/net/state/features.rb +327 -200
  32. data/lib/y_petri/net/state.rb +48 -82
  33. data/lib/y_petri/net/visualization.rb +1 -1
  34. data/lib/y_petri/net.rb +62 -47
  35. data/lib/y_petri/place/arcs.rb +44 -0
  36. data/lib/y_petri/place/features.rb +115 -0
  37. data/lib/y_petri/place.rb +62 -29
  38. data/lib/y_petri/simulation/dependency.rb +31 -67
  39. data/lib/y_petri/simulation/feature_set.rb +1 -1
  40. data/lib/y_petri/simulation/initial_marking/access.rb +42 -26
  41. data/lib/y_petri/simulation/marking_clamps/access.rb +22 -17
  42. data/lib/y_petri/simulation/marking_clamps.rb +0 -2
  43. data/lib/y_petri/simulation/marking_vector/access.rb +102 -40
  44. data/lib/y_petri/simulation/marking_vector.rb +35 -37
  45. data/lib/y_petri/simulation/matrix.rb +1 -1
  46. data/lib/y_petri/simulation/node_representation.rb +25 -0
  47. data/lib/y_petri/simulation/nodes/access.rb +78 -0
  48. data/lib/y_petri/simulation/{elements.rb → nodes.rb} +14 -13
  49. data/lib/y_petri/simulation/place_mapping.rb +2 -2
  50. data/lib/y_petri/simulation/place_representation.rb +8 -7
  51. data/lib/y_petri/simulation/places/access.rb +89 -70
  52. data/lib/y_petri/simulation/places/free.rb +1 -1
  53. data/lib/y_petri/simulation/places/types.rb +20 -22
  54. data/lib/y_petri/simulation/places.rb +23 -18
  55. data/lib/y_petri/simulation/recorder.rb +23 -18
  56. data/lib/y_petri/simulation/timed/recorder.rb +19 -11
  57. data/lib/y_petri/simulation/timed.rb +93 -29
  58. data/lib/y_petri/simulation/timeless/recorder.rb +11 -6
  59. data/lib/y_petri/simulation/timeless.rb +13 -3
  60. data/lib/y_petri/simulation/transition_representation/A.rb +24 -4
  61. data/lib/y_petri/simulation/transition_representation/S.rb +11 -1
  62. data/lib/y_petri/simulation/transition_representation/T.rb +1 -1
  63. data/lib/y_petri/simulation/transition_representation/Ts.rb +1 -1
  64. data/lib/y_petri/simulation/transition_representation/a.rb +1 -1
  65. data/lib/y_petri/simulation/transition_representation/s.rb +12 -1
  66. data/lib/y_petri/simulation/transition_representation/t.rb +1 -1
  67. data/lib/y_petri/simulation/transition_representation/tS.rb +1 -1
  68. data/lib/y_petri/simulation/transition_representation/ts.rb +1 -1
  69. data/lib/y_petri/simulation/transition_representation/types.rb +1 -1
  70. data/lib/y_petri/simulation/transition_representation.rb +4 -11
  71. data/lib/y_petri/simulation/transitions/A.rb +17 -2
  72. data/lib/y_petri/simulation/transitions/S.rb +1 -1
  73. data/lib/y_petri/simulation/transitions/T.rb +1 -1
  74. data/lib/y_petri/simulation/transitions/Ts.rb +6 -5
  75. data/lib/y_petri/simulation/transitions/a.rb +1 -1
  76. data/lib/y_petri/simulation/transitions/access.rb +195 -168
  77. data/lib/y_petri/simulation/transitions/s.rb +1 -1
  78. data/lib/y_petri/simulation/transitions/t.rb +1 -1
  79. data/lib/y_petri/simulation/transitions/tS.rb +1 -1
  80. data/lib/y_petri/simulation/transitions/ts.rb +1 -1
  81. data/lib/y_petri/simulation/transitions/types.rb +1 -1
  82. data/lib/y_petri/simulation/transitions.rb +5 -7
  83. data/lib/y_petri/simulation.rb +84 -90
  84. data/lib/y_petri/transition/A.rb +8 -2
  85. data/lib/y_petri/transition/T.rb +25 -2
  86. data/lib/y_petri/transition/arcs.rb +19 -3
  87. data/lib/y_petri/transition/construction_convenience.rb +11 -10
  88. data/lib/y_petri/transition/t.rb +14 -1
  89. data/lib/y_petri/transition/types.rb +6 -1
  90. data/lib/y_petri/transition.rb +9 -12
  91. data/lib/y_petri/version.rb +1 -1
  92. data/lib/y_petri/world/dependency.rb +3 -3
  93. data/lib/y_petri/world/{petri_net_related.rb → petri_net_aspect.rb} +4 -4
  94. data/lib/y_petri/world/simulation_aspect.rb +352 -0
  95. data/lib/y_petri/world.rb +4 -4
  96. data/lib/y_petri.rb +1 -1
  97. data/test/agent_test.rb +2 -1
  98. data/test/examples/demonstrator.rb +4 -1
  99. data/test/examples/demonstrator_2.rb +5 -0
  100. data/test/examples/demonstrator_4.rb +6 -5
  101. data/test/examples/example_2.rb +2 -0
  102. data/test/examples/manual_examples.rb +4 -4
  103. data/test/net_test.rb +457 -54
  104. data/test/place_test.rb +11 -7
  105. data/test/simulation_test.rb +358 -331
  106. data/test/transition_test.rb +11 -10
  107. data/test/world_test.rb +2 -0
  108. data/test/y_petri_test.rb +2 -1
  109. data/y_petri.gemspec +24 -18
  110. metadata +71 -17
  111. data/LICENSE +0 -22
  112. data/lib/y_petri/net/element_access.rb +0 -239
  113. data/lib/y_petri/simulation/element_representation.rb +0 -20
  114. data/lib/y_petri/simulation/elements/access.rb +0 -57
  115. data/lib/y_petri/transition/type.rb +0 -103
  116. data/lib/y_petri/transition/type_information.rb +0 -103
  117. data/lib/y_petri/world/simulation_related.rb +0 -176
@@ -0,0 +1,282 @@
1
+ # Access to nodes (places and transitions) of a Petri net.
2
+ #
3
+ module YPetri::Net::NodeAccess
4
+ # Does the net include a place?
5
+ #
6
+ def include_place? id
7
+ begin
8
+ place( id ) and true
9
+ rescue NameError, TypeError; false end
10
+ end
11
+
12
+ # Does the net include a transition?
13
+ #
14
+ def include_transition? id
15
+ begin; transition( id ) and true; rescue NameError, TypeError; false end
16
+ end
17
+
18
+ # Inquirer whether the net includes a node.
19
+ #
20
+ def include? id
21
+ include_place?( id ) || include_transition?( id )
22
+ end
23
+
24
+ # Returns the net's place identified by the argument.
25
+ #
26
+ def place id
27
+ ( super rescue Place().instance( id ) ).tap do |p|
28
+ fail TypeError, "No place #{id} in the net!" unless places.include? p
29
+ end
30
+ end
31
+
32
+ # Returns the net's transition identified by the argument.
33
+ #
34
+ def transition id
35
+ ( super rescue Transition().instance( id ) ).tap do |t|
36
+ transitions.include? t or fail TypeError, "No transition #{id} in the net!"
37
+ end
38
+ end
39
+
40
+ # Returns the net's node identified by the argument
41
+ #
42
+ def node id
43
+ begin; place( id ); rescue NameError, TypeError
44
+ begin; transition( id ); rescue NameError, TypeError
45
+ puts "Hello from failed #node, id is:"
46
+ puts id
47
+ p id
48
+ raise TypeError, "The net does not include place/transition #{id}!"
49
+ end
50
+ end
51
+ end
52
+
53
+ # Expects an array of nodes (places/transitions) or node ids, and returns an
54
+ # array of corresponding node instances.
55
+ #
56
+ def Nodes array
57
+ array.map &method( :node )
58
+ end
59
+
60
+ # Expects an arbitrary number of nodes (places/transitions) or node ids and
61
+ # returns an array of corresponding node instances. If no arguments are
62
+ # supplied, returns all the nodes.
63
+ #
64
+ def nodes *nodes
65
+ return @places + @transitions if nodes.empty?
66
+ Nodes( nodes )
67
+ end
68
+ alias nn nodes
69
+
70
+ # Expects an array of places or place ids, and returns an array of
71
+ # corresponding place instances.
72
+ #
73
+ def Places array
74
+ array.map &method( :place )
75
+ end
76
+
77
+ # Expects an arbitrary number of places or place ids and returns an array of
78
+ # corresponding place instances. If no arguments are supplied, returns all
79
+ # net's places.
80
+ #
81
+ def places *places
82
+ return @places.dup if places.empty?
83
+ Places( places )
84
+ end
85
+ alias pp places
86
+
87
+ # Expects an array of transitions or transition ids, and returns an array of
88
+ # corresponding transition instances.
89
+ #
90
+ def Transitions array
91
+ array.map &method( :transition )
92
+ end
93
+
94
+ # Expects an arbitrary number of transitions or transition ids and returns
95
+ # an array of corresponding transition instances. If no arguments are supplied,
96
+ # returns all net's transitions.
97
+ #
98
+ def transitions *transitions
99
+ return @transitions.dup if transitions.empty?
100
+ Transitions( transitions )
101
+ end
102
+ alias tt transitions
103
+
104
+ # Expects an array of *ts* transitions or transition ids, and returns an array
105
+ # of corresponding transition instances.
106
+ #
107
+ def ts_Transitions array
108
+ Transitions( array ).aT_all "transition identifiers", "be ts", &:ts?
109
+ end
110
+
111
+ # Expects an arbitrary number of *ts* transitions or transition ids as
112
+ # arguments, and returns an array of corresponding transition instances.
113
+ #
114
+ def ts_transitions *transitions
115
+ return transitions().select &:ts? if transitions.empty?
116
+ ts_Transitions( transitions )
117
+ end
118
+ alias ts_tt ts_transitions
119
+
120
+ # Expects an array of *tS* transitions or transition ids, and returns an array
121
+ # of corresponding transition instances.
122
+ #
123
+ def tS_Transitions array
124
+ Transitions( array ).aT_all "transition identifiers", "be tS", &:tS?
125
+ end
126
+
127
+ # Expects an arbitrary number of *tS* transitions or transition ids as
128
+ # arguments, and returns an array of corresponding transition instances.
129
+ #
130
+ def tS_transitions *transitions
131
+ return transitions().select &:tS? if transitions.empty?
132
+ tS_Transitions( transitions )
133
+ end
134
+ alias tS_tt tS_transitions
135
+
136
+ # Expects an array of *Ts* transitions or transition ids, and returns an array
137
+ # of corresponding transition instances.
138
+ #
139
+ def Ts_Transitions array
140
+ Transitions( array ).aT_all "transition identifiers", "be Ts", &:Ts?
141
+ end
142
+
143
+ # Expects an arbitrary number of *Ts* transitions or transition ids as
144
+ # arguments, and returns an array of corresponding transition instances.
145
+ #
146
+ def Ts_transitions *transitions
147
+ return transitions().select &:Ts? if transitions.empty?
148
+ Ts_Transitions( transitions )
149
+ end
150
+ alias Ts_tt Ts_transitions
151
+
152
+ # Expects an array of *TS* transitions or transition ids, and returns an array
153
+ # of corresponding transition instances.
154
+ #
155
+ def TS_Transitions array
156
+ Transitions( array ).aT_all "transition identifiers", "be TS", &:TS?
157
+ end
158
+
159
+ # Expects an arbitrary number of *TS* transitions or transition ids as
160
+ # arguments, and returns an array of corresponding transition instances.
161
+ #
162
+ def TS_transitions *transitions
163
+ return transitions().select &:TS? if transitions.empty?
164
+ TS_Transitions( transitions )
165
+ end
166
+ alias TS_tt TS_transitions
167
+
168
+ # Expects an array of *A* transitions or transition ids, and returns an array
169
+ # of corresponding transition instances.
170
+ #
171
+ def A_Transitions array
172
+ Transitions( array ).aT_all "transition identifiers", "be A", &:A?
173
+ end
174
+
175
+ # Expects an arbitrary number of *A* transitions or transition ids as
176
+ # arguments, and returns an array of corresponding transition instances.
177
+ #
178
+ def A_transitions *transitions
179
+ return transitions().select &:A? if transitions.empty?
180
+ A_Transitions( transitions )
181
+ end
182
+ alias A_tt A_transitions
183
+
184
+ # Expects an array of *a* transitions or transition ids, and returns an array
185
+ # of corresponding transition instances.
186
+ #
187
+ def a_Transitions array
188
+ Transitions( array ).aT_all "transition identifiers", "be a", &:a?
189
+ end
190
+
191
+ # Expects an arbitrary number of *a* transitions or transition ids as
192
+ # arguments, and returns an array of corresponding transition instances.
193
+ #
194
+ def a_transitions *transitions
195
+ return transitions().select &:a? if transitions.empty?
196
+ a_Transitions( transitions )
197
+ end
198
+ alias a_tt a_transitions
199
+
200
+ # Expects an array of *S* transitions or transition ids, and returns an array
201
+ # of corresponding transition instances.
202
+ #
203
+ def S_Transitions array
204
+ Transitions( array ).aT_all "transition identifiers", "be S", &:S?
205
+ end
206
+
207
+ # Expects an arbitrary number of *S* transitions or transition ids as
208
+ # arguments, and returns an array of corresponding transition instances.
209
+ #
210
+ def S_transitions *transitions
211
+ return transitions().select &:S? if transitions.empty?
212
+ S_Transitions( transitions )
213
+ end
214
+ alias S_tt S_transitions
215
+
216
+ # Expects an array of *s* transitions or transition ids, and returns an array
217
+ # of corresponding transition instances.
218
+ #
219
+ def s_Transitions array
220
+ Transitions( array ).aT_all "transition identifiers", "be s", &:s?
221
+ end
222
+
223
+ # Expects an arbitrary number of *s* transitions or transition ids as
224
+ # arguments, and returns an array of corresponding transition instances.
225
+ #
226
+ def s_transitions *transitions
227
+ return transitions().select &:s? if transitions.empty?
228
+ s_Transitions( transitions )
229
+ end
230
+ alias s_tt s_transitions
231
+
232
+ # Expects an array of *T* transitions or transition ids, and returns an array
233
+ # of corresponding transition instances.
234
+ #
235
+ def T_Transitions array
236
+ Transitions( array ).aT_all "transition identifiers", "be T", &:T?
237
+ end
238
+
239
+ # Expects an arbitrary number of *T* transitions or transition ids as
240
+ # arguments, and returns an array of corresponding transition instances.
241
+ #
242
+ def T_transitions *transitions
243
+ return transitions().select &:T? if transitions.empty?
244
+ T_Transitions( transitions )
245
+ end
246
+ alias T_tt T_transitions
247
+
248
+ # Expects an array of *t* transitions or transition ids, and returns an array
249
+ # of corresponding transition instances.
250
+ #
251
+ def t_Transitions array
252
+ Transitions( array ).aT_all "transition identifiers", "be t", &:t?
253
+ end
254
+
255
+ # Expects an arbitrary number of *t* transitions or transition ids as
256
+ # arguments, and returns an array of corresponding transition instances.
257
+ #
258
+ def t_transitions *transitions
259
+ return transitions().select &:t? if transitions.empty?
260
+ t_Transitions( transitions )
261
+ end
262
+ alias t_tt t_transitions
263
+
264
+ # Name-returning versions of the node access methods.
265
+ #
266
+ chain En: :Elements,
267
+ en: :elements,
268
+ Pn: :Places,
269
+ pn: :places,
270
+ Tn: :Transitions,
271
+ tn: :transitions,
272
+ nts: :ts_transitions,
273
+ ntS: :tS_transitions,
274
+ nTs: :Ts_transitions,
275
+ nTS: :TS_transitions,
276
+ nA: :A_transitions,
277
+ na: :a_transitions,
278
+ nS: :S_transitions,
279
+ ns: :s_transitions,
280
+ nT: :T_transitions,
281
+ nt: :t_transitions do |nodes| nodes.names end
282
+ end # class YPetri::Net::NodeAccess
@@ -1,10 +1,11 @@
1
+ # encoding: utf-8
2
+
1
3
  # A mixin catering to the net's own state (ie. marking owned by the place
2
4
  # instances themselves) and its features.
3
5
  #
4
6
  module YPetri::Net::OwnState
5
- # State owned by the net. More precisely, an instance of the Net::State class,
6
- # which is an Array subclass, containing the markings owned by the net's
7
- # places as its elements.
7
+ # State owned by the net. This method returns an instance of +Net::State+
8
+ # class (a subclass of Array), containing marking owned by the net's places.
8
9
  #
9
10
  def state
10
11
  State().new( marking )
@@ -28,7 +29,7 @@ module YPetri::Net::OwnState
28
29
  if transition_ids.nil? then
29
30
  fail TypeError, "Method #firing with no arguments is ambiguous for " +
30
31
  "nets with TS transitions!" if timed?
31
- firing( tS_tt )
32
+ firing tS_tt
32
33
  else
33
34
  transition_ids.map { |id| tS_transition( id ).firing }
34
35
  end
@@ -60,4 +61,13 @@ module YPetri::Net::OwnState
60
61
  def delta place_ids=nil, transitions: tt
61
62
  fail NotImplementedError
62
63
  end
64
+
65
+ # Takes an array of A transition identifiers as an optional argument, and
66
+ # returns the array of their actions under current net state. If no argument
67
+ # is supplied, the array of assignments of all net's A transitions is returned.
68
+ #
69
+ def assignment transition_ids=nil
70
+ return assigment A_tt() if transition_ids.nil?
71
+ transition_ids.map { |id| A_transition( id ).action }
72
+ end
63
73
  end # module YPetri::Net::OwnState
@@ -0,0 +1,123 @@
1
+ # encoding: utf-8
2
+
3
+ # Firing of a Petri net A transition.
4
+ #
5
+ class YPetri::Net::State::Feature::Assignment < YPetri::Net::State::Feature
6
+ attr_reader :place, :transition
7
+
8
+ class << self
9
+ # Customization of the Class#parametrize method.
10
+ #
11
+ def parametrize *args
12
+ Class.instance_method( :parametrize ).bind( self ).( *args ).tap do |ç|
13
+ # Prepare the instance registry.
14
+ hsh = Hash.new do |ꜧ, id|
15
+ if id.is_a? self then # missing key "id" is an Assignment PS instance
16
+ ꜧ[ [ id.place, transition: id.transition ] ]
17
+ elsif id.is_a? ç.net.Place then # a single place
18
+ ç.construct_from_a_place_with_single_upstream_A_transition( id )
19
+ elsif id.is_a? Array and id.size == 1 then # single place again
20
+ ç.construct_from_a_place_with_single_upstream_A_transition( id.first )
21
+ elsif id.is_a? Array then
22
+ p = id.fetch( 0 )
23
+ t = id.fetch( 1 ).fetch( :transition )
24
+ if p.is_a? ç.net.Place and t.is_a? ç.net.Transition then
25
+ ꜧ[ id ] = ç.__new__( p, transition: t )
26
+ else
27
+ ꜧ[ [ ç.net.place( p ), transition: ç.net.transition( t ) ] ]
28
+ end
29
+ else
30
+ ç.construct_from_a_place_with_single_upstream_A_transition( id )
31
+ end
32
+ end # Hash.new do
33
+ # And assign it to @instances:
34
+ ç.instance_variable_set :@instances, hsh
35
+ end # tap
36
+ end # def parametrize
37
+
38
+ attr_reader :instances
39
+
40
+ alias __new__ new
41
+
42
+ # Constructor that enables special syntax of constructing
43
+ # Feature::Assignment instance from a single place, as long
44
+ # as this place has exactly 1 upstream A transition.
45
+ #
46
+ def construct_from_a_place_with_single_upstream_A_transition( place )
47
+ pl = net.place( place )
48
+ aa = pl.upstream_arcs.select( &:A? )
49
+ n = aa.size
50
+ fail TypeError, "When constructing Feature::Assignment from a single" +
51
+ "place, its upstream arcs must contain exactly one A transition! " +
52
+ "(place #{pl} has #{n} upstream A transitions)" unless n == 1
53
+ __new__( pl, transition: aa.first )
54
+ end
55
+
56
+ # Constructor #new is redefined to use instance cache.
57
+ #
58
+ def new *args
59
+ return instances[ *args ] if args.size == 1
60
+ instances[ args ]
61
+ end
62
+ alias to new
63
+ end
64
+
65
+ # The constructor of an assignment feature takes 1 ordered and 1 named
66
+ # (+:transition+) argument, which must identify the place and the transitions.
67
+ #
68
+ def initialize place, transition: transition
69
+ @place = net.place( place )
70
+ @transition = net.transition( transition )
71
+ @place_index_in_codomain = @transition.codomain.index( @place ) or
72
+ fail TypeError, "The place (#@place) must belong to the codomain of " +
73
+ "the supplied A transition (#@transition)!"
74
+ end
75
+
76
+ # Extracts the receiver marking feature from the argument. This can be
77
+ # typically a simulation instance.
78
+ #
79
+ def extract_from arg, **nn
80
+ case arg
81
+ when YPetri::Simulation then
82
+ # First, let's identify the relevant transition representation
83
+ t = arg.send( :A_transitions, transition ).first
84
+ # Then, let's get its assignment closure
85
+ closure = t.assignment_closure
86
+ # And finally, the feature extraction
87
+ Array( closure.call )[ @place_index_in_codomain ]
88
+ else
89
+ fail TypeError, "Argument type not supported!"
90
+ end
91
+ end
92
+
93
+ # Type of this feature.
94
+ #
95
+ def type
96
+ :assignment
97
+ end
98
+
99
+ # A string briefly describing the assignment feature.
100
+ #
101
+ def to_s
102
+ label
103
+ end
104
+
105
+ # Label for the firing feature (to use in the graphics etc.)
106
+ #
107
+ def label
108
+ "A:#{place.name}:#{transition.name}"
109
+ end
110
+
111
+ # Inspect string of the firing feature.
112
+ #
113
+ def inspect
114
+ "<Feature::Assignment to #{place.name or place} by #{transition.name or transition}>"
115
+ end
116
+
117
+ # Two assignment features are equal if their place and transition is equal.
118
+ #
119
+ def == other
120
+ other.is_a? net.State.Feature.Assignment and
121
+ place == other.place && transition == other.transition
122
+ end
123
+ end # YPetri::Net::State::Feature::Assignment
@@ -3,44 +3,52 @@
3
3
  # Change of a Petri net place caused by a certain set of transitions.
4
4
  #
5
5
  class YPetri::Net::State::Feature::Delta < YPetri::Net::State::Feature
6
- attr_reader :place, :transitions, :step
6
+ attr_reader :place, :transitions
7
+ alias tt transitions
7
8
 
8
9
  class << self
9
10
  # Customization of the Class#parametrize method.
10
11
  #
11
12
  def parametrize *args
12
13
  Class.instance_method( :parametrize ).bind( self ).( *args ).tap do |ç|
13
- # First, prepare the hash of instances.
14
+ # First, prepare the instance registry.
14
15
  hsh = Hash.new do |ꜧ, id|
15
16
  if id.is_a? self then # missing key "id" is a Delta instance
16
17
  ꜧ[ [ id.place, transitions: id.transitions.sort_by( &:object_id ) ] ]
17
18
  else
18
19
  p = id.fetch( 0 )
19
20
  tt = id.fetch( 1 ).fetch( :transitions ) # value of :transitions key
20
- if p.is_a? ç.net.Place and tt.all? { |t| t.is_a? ç.net.Transition }
21
- if tt == tt.sort_by( &:object_id ) then
22
- # Cache the instance.
23
- ꜧ[ id ] = if tt.all? &:timed? then
24
- ç.timed( *id )
25
- elsif tt.all? &:timeless? then
26
- ç.timeless( *id )
27
- else
28
- fail TypeError, "Net::State::Feature::Delta only " +
29
- "admits the transition sets that are either " +
30
- "all timed, or all timeless!"
31
- end
32
- else
33
- ꜧ[ [ p, transitions: tt.sort_by( &:object_id ) ] ]
21
+ tt_array = Array( tt )
22
+ if tt == tt_array then
23
+ if p.is_a? ç.net.Place and tt.all? { |t| t.is_a? ç.net.Transition }
24
+ if tt == tt.sort_by( &:object_id ) then
25
+ # Cache the instance.
26
+ ꜧ[ id ] = if tt.all? &:timed? then
27
+ ç.timed( *id )
28
+ elsif tt.all? &:timeless? then
29
+ fail TypeError, "Net::State::Feature::Delta does " +
30
+ "not admit A transitions!" if tt.any? &:A?
31
+ ç.timeless( *id )
32
+ else
33
+ fail TypeError, "Net::State::Feature::Delta only " +
34
+ "admits the transition sets that are either " +
35
+ "all timed, or all timeless!"
36
+ end
37
+ else
38
+ ꜧ[ [ p, transitions: tt.sort_by( &:object_id ) ] ]
39
+ end
40
+ else # convert place and transition ids to places and transitions
41
+ ꜧ[ [ ç.net.place( p ), transitions: ç.net.Transitions( tt ) ] ]
34
42
  end
35
- else # convert place and transition ids to places and transitions
36
- ꜧ[ [ ç.net.place( p ), transitions: ç.net.transitions( tt ) ] ]
43
+ else
44
+ ꜧ[ [ p, transitions: tt_array ] ]
37
45
  end
38
46
  end
39
47
  end
40
- # And then, assign it to the :@instances variable.
48
+ # Then, assign it to the :@instances variable.
41
49
  ç.instance_variable_set :@instances, hsh
42
- end
43
- end
50
+ end # tap
51
+ end # def parametrize
44
52
 
45
53
  attr_reader :instances
46
54
 
@@ -51,7 +59,7 @@ class YPetri::Net::State::Feature::Delta < YPetri::Net::State::Feature
51
59
  #
52
60
  def timed place, transitions: net.T_tt
53
61
  tt = begin
54
- net.T_tt( transitions )
62
+ net.T_Transitions( transitions )
55
63
  rescue TypeError => err
56
64
  msg = "Transitions #{transitions} not recognized as timed " +
57
65
  "transitions in #{net}! (%s)"
@@ -66,13 +74,13 @@ class YPetri::Net::State::Feature::Delta < YPetri::Net::State::Feature
66
74
  #
67
75
  def timeless place, transitions: net.t_tt
68
76
  tt = begin
69
- net.t_tt( transitions )
77
+ net.t_Transitions( transitions )
70
78
  rescue TypeError => err
71
79
  msg = "Transitions #{transitions} not recognized as timed " +
72
80
  "transitions in #{net}! (%s)"
73
81
  raise TypeError, msg % err
74
82
  end
75
- __new__( place, transitions: net.t_tt( transitions ) )
83
+ __new__( place, transitions: net.t_Transitions( transitions ) )
76
84
  .tap { |inst| inst.instance_variable_set :@timed, false }
77
85
  end
78
86
 
@@ -92,22 +100,21 @@ class YPetri::Net::State::Feature::Delta < YPetri::Net::State::Feature
92
100
  #
93
101
  def initialize place, transitions: net.tt
94
102
  @place = net.place( place )
95
- @transitions = net.transitions( transitions )
103
+ @transitions = net.Transitions( transitions )
96
104
  end
97
105
 
98
- # Extracts the value of this feature from the supplied target
99
- # (eg. a simulation).
106
+ # Extracts the value of this feature from the target (eg. a simulation).
107
+ # If the receiver delta feature is timed, this method requires an additional
108
+ # named argument +:delta_time+, alias +:Δt+.
100
109
  #
101
- def extract_from arg, **nn
102
- # **nn is here because of timed / timeless possibility, where
103
- # **nn would contain :step named argument.
110
+ def extract_from arg, **named_args
104
111
  case arg
105
112
  when YPetri::Simulation then
106
113
  if timed? then
107
- tt = arg.send( :T_transitions, transitions )
108
- -> Δt { tt.delta( Δt ).fetch( place ) }
114
+ arg.send( :T_Transitions, transitions )
115
+ .delta( named_args.must_have :delta_time, syn!: :Δt ).fetch( place )
109
116
  else
110
- arg.send( :t_transitions, transitions ).delta.fetch( place )
117
+ arg.send( :t_Transitions, transitions ).delta.fetch( place )
111
118
  end
112
119
  else
113
120
  fail TypeError, "Argument type not supported!"
@@ -135,13 +142,18 @@ class YPetri::Net::State::Feature::Delta < YPetri::Net::State::Feature
135
142
  # A string briefly describing this delta feature.
136
143
  #
137
144
  def to_s
138
- place.name
145
+ label
139
146
  end
140
147
 
141
148
  # Label for the delta feature (to use in graphics etc.)
142
149
  #
143
150
  def label
144
- "Δ:#{place.name}:#{transitions.size}tt"
151
+ "Δ:#{place.name}:%s" %
152
+ if transitions.size == 1 then
153
+ transitions.first.name || transitions.first
154
+ else
155
+ "#{transitions.size}tt"
156
+ end
145
157
  end
146
158
 
147
159
  # Inspect string of the delta feature.
@@ -150,4 +162,12 @@ class YPetri::Net::State::Feature::Delta < YPetri::Net::State::Feature
150
162
  "<Feature::Delta Δ:#{place.name || place}:[%s]>" %
151
163
  transitions.names( true ).join( ', ' )
152
164
  end
165
+
166
+ # Delta features are equal if they are of equal PS and refer to
167
+ # the same place and transition set.
168
+ #
169
+ def == other
170
+ other.is_a? net.State.Feature.Delta and
171
+ place == other.place && transitions == other.transitions
172
+ end
153
173
  end # class YPetri::Net::State::Feature::Delta