y_petri 2.1.3 → 2.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/lib/y_petri/agent/petri_net_related.rb +25 -5
  3. data/lib/y_petri/agent/selection.rb +12 -10
  4. data/lib/y_petri/agent/simulation_related.rb +14 -58
  5. data/lib/y_petri/agent.rb +15 -17
  6. data/lib/y_petri/core/timed/euler.rb +13 -15
  7. data/lib/y_petri/core/timed/pseudo_euler.rb +22 -24
  8. data/lib/y_petri/core/timed/quasi_euler.rb +15 -17
  9. data/lib/y_petri/core/timed.rb +42 -44
  10. data/lib/y_petri/core/timeless/pseudo_euler.rb +12 -14
  11. data/lib/y_petri/core/timeless.rb +10 -7
  12. data/lib/y_petri/core.rb +3 -3
  13. data/lib/y_petri/dsl.rb +46 -46
  14. data/lib/y_petri/fixed_assets.rb +8 -0
  15. data/lib/y_petri/net/data_set.rb +238 -0
  16. data/lib/y_petri/net/own_state.rb +63 -0
  17. data/lib/y_petri/net/state/feature/delta.rb +98 -71
  18. data/lib/y_petri/net/state/feature/firing.rb +51 -54
  19. data/lib/y_petri/net/state/feature/flux.rb +51 -55
  20. data/lib/y_petri/net/state/feature/gradient.rb +55 -59
  21. data/lib/y_petri/net/state/feature/marking.rb +55 -59
  22. data/lib/y_petri/net/state/feature.rb +65 -67
  23. data/lib/y_petri/net/state/features/record.rb +150 -43
  24. data/lib/y_petri/net/state/features.rb +252 -96
  25. data/lib/y_petri/net/state.rb +114 -106
  26. data/lib/y_petri/net/visualization.rb +3 -2
  27. data/lib/y_petri/net.rb +29 -24
  28. data/lib/y_petri/place/arcs.rb +3 -3
  29. data/lib/y_petri/place/guard.rb +35 -117
  30. data/lib/y_petri/place/guarded.rb +86 -0
  31. data/lib/y_petri/place.rb +6 -3
  32. data/lib/y_petri/simulation/element_representation.rb +2 -2
  33. data/lib/y_petri/simulation/elements.rb +3 -1
  34. data/lib/y_petri/simulation/feature_set.rb +3 -1
  35. data/lib/y_petri/simulation/marking_vector.rb +3 -1
  36. data/lib/y_petri/simulation/place_mapping.rb +3 -1
  37. data/lib/y_petri/simulation/places.rb +1 -1
  38. data/lib/y_petri/simulation/recorder.rb +60 -54
  39. data/lib/y_petri/simulation/timed/recorder.rb +12 -1
  40. data/lib/y_petri/simulation/timed.rb +173 -172
  41. data/lib/y_petri/simulation/transitions/access.rb +97 -29
  42. data/lib/y_petri/simulation.rb +11 -9
  43. data/lib/y_petri/transition/{assignment.rb → A.rb} +2 -2
  44. data/lib/y_petri/transition/{timed.rb → T.rb} +2 -2
  45. data/lib/y_petri/transition/arcs.rb +3 -3
  46. data/lib/y_petri/transition/cocking.rb +3 -3
  47. data/lib/y_petri/transition/{init.rb → construction_convenience.rb} +6 -53
  48. data/lib/y_petri/transition/{ordinary_timeless.rb → t.rb} +2 -2
  49. data/lib/y_petri/transition/type.rb +103 -0
  50. data/lib/y_petri/transition/type_information.rb +103 -0
  51. data/lib/y_petri/transition/types.rb +107 -0
  52. data/lib/y_petri/transition/usable_without_world.rb +14 -0
  53. data/lib/y_petri/transition.rb +87 -101
  54. data/lib/y_petri/version.rb +1 -1
  55. data/lib/y_petri/world/dependency.rb +30 -28
  56. data/lib/y_petri/world.rb +10 -8
  57. data/test/acceptance/basic_usage_test.rb +3 -3
  58. data/test/acceptance/simulation_test.rb +3 -3
  59. data/test/acceptance/simulation_with_physical_units_test.rb +2 -2
  60. data/test/acceptance/token_game_test.rb +2 -2
  61. data/test/acceptance/visualization_test.rb +3 -3
  62. data/test/acceptance_tests.rb +2 -2
  63. data/test/agent_test.rb +1 -1
  64. data/test/net_test.rb +41 -17
  65. data/test/place_test.rb +1 -1
  66. data/test/simulation_test.rb +39 -39
  67. data/test/transition_test.rb +1 -1
  68. data/test/world_test.rb +1 -1
  69. data/test/y_petri_test.rb +1 -1
  70. metadata +13 -8
  71. data/lib/y_petri/net/state/features/dataset.rb +0 -135
  72. data/lib/y_petri/transition/construction.rb +0 -311
@@ -0,0 +1,238 @@
1
+ # encoding: utf-8
2
+
3
+ # Dataset is a collection of labeled state records.
4
+ #
5
+ class YPetri::Net::DataSet < Hash
6
+ class << self
7
+ alias __new__ new
8
+
9
+ def new type: nil
10
+ __new__ do |hsh, missing|
11
+ case missing
12
+ when Float then nil
13
+ else hsh[ missing.to_f ] end
14
+ end.tap { |inst|
15
+ inst.instance_variable_set :@type, type
16
+ }
17
+ end
18
+
19
+ private :__new__
20
+
21
+ delegate :net, to: :features
22
+ delegate :State, to: :net
23
+ delegate :Marking, :Firing, :Flux, :Gradient, :Delta,
24
+ to: "State()"
25
+ end
26
+
27
+ alias events keys
28
+ alias records values
29
+
30
+ delegate :features,
31
+ :net,
32
+ :State,
33
+ :Marking, :Firing, :Flux, :Gradient, :Delta,
34
+ to: "self.class"
35
+
36
+ attr_reader :type # more like event_type, idea not matured yet
37
+
38
+ # Type of the dataset.
39
+ #
40
+ def timed?
41
+ type == :timed
42
+ end
43
+
44
+ # Returns a Record instance corresponding to the given recorded event.
45
+ #
46
+ def record( event )
47
+ features.load( fetch event )
48
+ end
49
+
50
+ # Revives records from values.
51
+ #
52
+ def records
53
+ values.map { |value| features.Record.new( value ) }
54
+ end
55
+
56
+ # Recreates the simulation at a given event label.
57
+ #
58
+ def reconstruct event: (fail "No event given!"),
59
+ **settings # settings include marking clamps
60
+ rec = interpolate( event )
61
+ if timed? then
62
+ rec.reconstruct time: event, **settings
63
+ else
64
+ rec.reconstruct **settings
65
+ end
66
+ end
67
+
68
+ # Interpolates the recording an the given point (event). Return value is the
69
+ # Record class instance.
70
+ #
71
+ def interpolate( event )
72
+ # TODO: This whole interpolation thing is unfinished.
73
+ begin
74
+ record( event )
75
+ rescue KeyError => msg
76
+ timed? or raise TypeError, "Event #{event} does not have a record! (%s)" %
77
+ "simulation type: #{type.nil? ? 'nil' : type}"
78
+ f_time, floor = floor( event ) # timed datasets support floor, ceiling
79
+ c_time, ceiling = ceiling( time )
80
+ floor + ( ceiling - floor ) / ( c_time - f_time ) * ( time - f_time )
81
+ end
82
+ end
83
+
84
+ # Returns the data series for the specified features.
85
+ #
86
+ def series arg=nil
87
+ return records.transpose if arg.nil?
88
+ reduce_features( State().features( arg ) ).series
89
+ end
90
+
91
+ # Expects a hash of features (:marking (alias :state) of places, :firing
92
+ # of tS transitions, :delta of places and/or transitions) and returns the
93
+ # corresponding mapping of the recording.
94
+ #
95
+ def reduce_features *args
96
+ Δt = if args.last.is_a? Hash then
97
+ args.last.may_have( :delta_time, syn!: :Δt )
98
+ args.last.delete( :delta_time )
99
+ .tap { args.delete_at( -1 ) if args.last.empty? }
100
+ end
101
+ reduced_features = net.State.features *args
102
+ rf_Record = reduced_features.Record
103
+ reduced_features.new_dataset( type: type ).tap do |dataset|
104
+ ( events >> records ).each_pair do |event, record|
105
+ absent_features = reduced_features - features()
106
+ if absent_features.empty? then # it is a subset
107
+ line = reduced_features.map { |feature| record.fetch feature }
108
+ else # it will require simulation reconstruction
109
+ sim = reconstruct event: event
110
+ if absent_features.any? { |f| f.timed? rescue false } then
111
+ fail ArgumentError, "Reconstruction of timed features requires " +
112
+ "the named arg :delta_time to be given!" unless Δt
113
+ line = reduced_features.map do |feature|
114
+ if absent_features.include? feature then
115
+ if ( feature.timed? rescue false ) then
116
+ feature.extract_from( sim ).( Δt )
117
+ else
118
+ feature.extract_from( sim )
119
+ end
120
+ else
121
+ record.fetch feature
122
+ end
123
+ end
124
+ else
125
+ line = reduced_features.map do |feature|
126
+ if absent_features.include? feature then
127
+ feature.extract_from( sim )
128
+ else
129
+ record.fetch feature
130
+ end
131
+ end
132
+ end
133
+ end
134
+ dataset.update event => rf_Record.load( line )
135
+ end
136
+ end
137
+ end
138
+
139
+ # Returns a subset of this dataset with only the specified marking features
140
+ # identified by the arguments retained. If no arguments are given, all the
141
+ # marking features from the receiver dataset are selected.
142
+ #
143
+ def marking ids=nil
144
+ return reduce_features net.State.marking if ids.nil?
145
+ reduce_features marking: ids
146
+ end
147
+
148
+ # Returns a subset of this dataset with only the specified firing features
149
+ # identified by the arguments retained. If no arguments are given, all the
150
+ # firing features from the receiver dataset are selected.
151
+ #
152
+ def firing *args
153
+ Δt = if args.last.is_a? Hash then
154
+ args.last.may_have( :delta_time, syn!: :Δt )
155
+ args.last.delete( :delta_time )
156
+ .tap { args.delete_at( -1 ) if args.last.empty? }
157
+ end
158
+ if Δt then
159
+ return reduce_features net.State.firing, delta_time: Δt if args.empty?
160
+ reduce_features firing: args.first, delta_time: Δt
161
+ else
162
+ return reduce_features net.State.firing if args.empty?
163
+ reduce_features firing: args.first
164
+ end
165
+ end
166
+
167
+ # Returns a subset of this dataset with only the specified flux features
168
+ # identified by the arguments retained. If no arguments are given, all the
169
+ # flux features from the receiver dataset are selected.
170
+ #
171
+ def flux ids=nil
172
+ return reduce_features net.State.flux if ids.nil?
173
+ reduce_features flux: ids
174
+ end
175
+
176
+ # Returns a subset of this dataset with only the specified gradient features
177
+ # identified by the arguments retained. If no arguments are given, all the
178
+ # gradient features from the receiver dataset are selected.
179
+ #
180
+ def gradient *args
181
+ return reduce_features net.State.gradient if args.empty?
182
+ reduce_features gradient: args
183
+ end
184
+
185
+ # Returns a subset of this dataset with only the specified delta features
186
+ # identified by the arguments retained. If no arguments are given, all the
187
+ # delta features from the receiver dataset are selected.
188
+ #
189
+ def delta *args
190
+ Δt = if args.last.is_a? Hash then
191
+ args.last.may_have( :delta_time, syn!: :Δt )
192
+ args.last.delete( :delta_time )
193
+ .tap { args.delete_at( -1 ) if args.last.empty? }
194
+ end
195
+ if Δt then
196
+ return reduce_features net.State.delta, delta_time: Δt if args.empty?
197
+ reduce_features delta: args, delta_time: Δt
198
+ else
199
+ return reduce_features net.State.delta if args.empty?
200
+ reduce_features delta: args
201
+ end
202
+ end
203
+
204
+ # Outputs the current recording in CSV format.
205
+ #
206
+ def to_csv
207
+ map { |lbl, rec| [ lbl, *rec ].join ',' }.join "\n"
208
+ end
209
+
210
+ # Plots the dataset.
211
+ #
212
+ def plot( time: nil, **nn )
213
+ events = events()
214
+ data_ss = series
215
+ x_range = if time.is_a? Range then
216
+ "[#{time.begin}:#{time.end}]"
217
+ else
218
+ "[-0:#{SY::Time.magnitude( time ).amount rescue time}]"
219
+ end
220
+
221
+ Gnuplot.open do |gp|
222
+ Gnuplot::Plot.new gp do |plot|
223
+ plot.xrange x_range
224
+ plot.title nn[:title] || "#{net} plot"
225
+ plot.ylabel nn[:ylabel] || "Values"
226
+ plot.xlabel nn[:xlabel] || "Time [s]"
227
+
228
+ features.labels.zip( data_ss )
229
+ .each { |label, data_array|
230
+ plot.data << Gnuplot::DataSet.new( [ events, data_array ] ) { |ds|
231
+ ds.with = "linespoints"
232
+ ds.title = label
233
+ }
234
+ }
235
+ end
236
+ end
237
+ end
238
+ end # YPetri::Net::Dataset
@@ -0,0 +1,63 @@
1
+ # A mixin catering to the net's own state (ie. marking owned by the place
2
+ # instances themselves) and its features.
3
+ #
4
+ 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.
8
+ #
9
+ def state
10
+ State().new( marking )
11
+ end
12
+
13
+ # If no argument is supplied, the method returns the array of the markings
14
+ # owned by the net's places. If an array of place identifiers is supplied,
15
+ # the return value is the array of the markings owned by those places.
16
+ #
17
+ def marking place_ids=nil
18
+ return marking( pp ) if place_ids.nil?
19
+ place_ids.map { |id| place( id ).marking }
20
+ end
21
+
22
+ # Takes an array of tS transition identifiers as an optional argument, and
23
+ # returns the array of their firing under current net state. If no argument
24
+ # is supplied, the net is required to contain no TS transtions, and the
25
+ # method returns the array of firing of all net's tS transitions.
26
+ #
27
+ def firing transition_ids=nil
28
+ if transition_ids.nil? then
29
+ fail TypeError, "Method #firing with no arguments is ambiguous for " +
30
+ "nets with TS transitions!" if timed?
31
+ firing( tS_tt )
32
+ else
33
+ transition_ids.map { |id| tS_transition( id ).firing }
34
+ end
35
+ end
36
+
37
+ # Takes an array of TS transition identifiers as an optional argument, and
38
+ # returns the array of their fluxes under current net state. If no argument
39
+ # is supplied, the array of fluxes of all net's TS transitions is returned.
40
+ #
41
+ def flux transition_ids=nil
42
+ return flux TS_tt() if transition_ids.nil?
43
+ transition_ids.map { |id| TS_transition( id ).flux }
44
+ end
45
+
46
+ # Takes an array of place identifiers, and a named argument +:transitions+,
47
+ # and returns the array of the place gradient contribution by the indicated
48
+ # transitions. The +:transitions+ argument defaults to all the transitions,
49
+ # place identifiers default to all the places. The net must be timed.
50
+ #
51
+ #
52
+ def gradient place_ids=pp, transitions: tt
53
+ fail NotImplementedError
54
+ end
55
+
56
+ # Takes an array of place identifier, and a named argument +:transitions+,
57
+ # and returns the array of the place delta contribution by the indicated
58
+ # transitions.
59
+ #
60
+ def delta place_ids=nil, transitions: tt
61
+ fail NotImplementedError
62
+ end
63
+ end # module YPetri::Net::OwnState
@@ -1,88 +1,115 @@
1
1
  # encoding: utf-8
2
2
 
3
- class YPetri::Net::State
4
- class Feature
5
- # Change of a Petri net place caused by a certain set of transitions.
6
- #
7
- class Delta < Feature
8
- attr_reader :place, :transitions, :step
3
+ # Change of a Petri net place caused by a certain set of transitions.
4
+ #
5
+ class YPetri::Net::State::Feature::Delta < YPetri::Net::State::Feature
6
+ attr_reader :place, :transitions, :step
9
7
 
10
- class << self
11
- def parametrize *args
12
- Class.instance_method( :parametrize ).bind( self ).( *args ).tap do |ç|
13
- # First, prepare the hash of instances.
14
- hsh = Hash.new do |ꜧ, id|
15
- puts "Delta received:"
16
- p id
17
- puts "of class #{id.class}, with ancestors:"
18
- p id.class.ancestors
19
- if id.is_a? Delta then
20
- ꜧ[ [ id.place, transitions: id.transitions.sort( &:object_id ) ] ]
8
+ class << self
9
+ def parametrize *args
10
+ Class.instance_method( :parametrize ).bind( self ).( *args ).tap do |ç|
11
+ # First, prepare the hash of instances.
12
+ hsh = Hash.new do |ꜧ, id|
13
+ if id.is_a? self then # missing key "id" is a Delta instance
14
+ ꜧ[ [ id.place, transitions: id.transitions.sort( &:object_id ) ] ]
15
+ else
16
+ p = id.fetch( 0 )
17
+ tt = id.fetch( 1 ).fetch( :transitions ) # value of :transitions key
18
+ if p.is_a? ç.net.Place and tt.all? { |t| t.is_a? ç.net.Transition }
19
+ if tt == tt.sort then
20
+ # Cache the instance.
21
+ ꜧ[ id ] = if tt.all? &:timed? then
22
+ ç.timed( *id )
23
+ elsif tt.all? &:timeless? then
24
+ ç.timeless( *id )
25
+ else
26
+ fail TypeError, "Net::State::Feature::Delta only " +
27
+ "admits the transition sets that are either " +
28
+ "all timed, or all timeless!"
29
+ end
21
30
  else
22
- puts "here"
23
- p id
24
- puts "id size is #{id.size}"
25
- p = id.fetch( 0 )
26
- tt = id
27
- .fetch( 1 )
28
- .fetch( :transitions )
29
- if p.is_a? ç.net.Place and tt.all? { |t| t.is_a? ç.net.Transition }
30
- if tt == tt.sort then
31
- ꜧ[ id ] = ç.__new__( *id )
32
- else
33
- ꜧ[ [ p, transitions: tt.sort ] ]
34
- end
35
- else
36
- ꜧ[ [ ç.net.place( p ), transitions: ç.net.transitions( tt ) ] ]
37
- end
31
+ ꜧ[ [ p, transitions: tt.sort ] ]
38
32
  end
33
+ else # convert place and transition ids to places and transitions
34
+ ꜧ[ [ ç.net.place( p ), transitions: ç.net.transitions( tt ) ] ]
39
35
  end
40
- # And then, assign it to the :@instances variable.
41
- ç.instance_variable_set :@instances, hsh
42
36
  end
43
37
  end
38
+ # And then, assign it to the :@instances variable.
39
+ ç.instance_variable_set :@instances, hsh
40
+ end
41
+ end
44
42
 
45
- attr_reader :instances
43
+ attr_reader :instances
46
44
 
47
- alias __new__ new
45
+ alias __new__ new
48
46
 
49
- def new *args
50
- return instances[ *args ] if args.size == 1
51
- instances[ args ]
52
- end
47
+ # Timed delta feature constructor. Takes a place, and an array of timed
48
+ # transition identifiers supplied as +:transitions: parameter.
49
+ #
50
+ def timed place, transitions: net.T_tt
51
+ __new__( place, transitions: net.T_tt( transitions ) )
52
+ .tap { |inst| inst.instance_variable_set :@timed, true }
53
+ end
53
54
 
54
- def of *args
55
- new *args
56
- end
57
- end
55
+ # Timeless delta feature constructor. Takes a place, and an array of
56
+ # timeless transition identifiers as +:transitions: parameter.
57
+ #
58
+ def timeless place, transitions: net.t_tt
59
+ __new__( place, transitions: net.t_tt( transitions ) )
60
+ .tap { |inst| inst.instance_variable_set :@timed, false }
61
+ end
58
62
 
59
- def initialize place, transitions: net.tt
60
- @place = net.place( place )
61
- @transitions = net.transitions( transitions )
62
- end
63
+ # Constructor #new is redefined to use instance cache.
64
+ #
65
+ def new *args
66
+ return instances[ *args ] if args.size == 1
67
+ instances[ args ]
68
+ end
69
+ alias of new
70
+ end
63
71
 
64
- def extract_from arg, **nn
65
- # **nn is here because of timed / timeless possibility, where
66
- # **nn would contain :step named argument.
67
- case arg
68
- when YPetri::Simulation then
69
- _T = arg.send( :T_transitions, transitions )
70
- _t = arg.send( :t_transitions, transitions )
71
- if _T.empty? then _t.delta.fetch( place ) else # time step is required
72
- _t.delta.fetch( place ) + _T.delta( nn[:step] ).fetch( place )
73
- end
74
- else
75
- fail TypeError, "Argument type not supported!"
76
- end
77
- end
72
+ def initialize place, transitions: net.tt
73
+ @place = net.place( place )
74
+ @transitions = net.transitions( transitions )
75
+ end
78
76
 
79
- def to_s
80
- place.name
77
+ # Extracts the value of this feature from the supplied target
78
+ # (eg. a simulation).
79
+ #
80
+ def extract_from arg, **nn
81
+ # **nn is here because of timed / timeless possibility, where
82
+ # **nn would contain :step named argument.
83
+ case arg
84
+ when YPetri::Simulation then
85
+ if timed? then
86
+ tt = arg.send( :T_transitions, transitions )
87
+ -> Δt { tt.delta( Δt ).fetch( place ) }
88
+ else
89
+ arg.send( :t_transitions, transitions ).delta.fetch( place )
81
90
  end
91
+ else
92
+ fail TypeError, "Argument type not supported!"
93
+ end
94
+ end
82
95
 
83
- def label
84
- "∂:#{place.name}:#{transitions.size}tt"
85
- end
86
- end # class Delta
87
- end # class Feature
88
- end # YPetri::Net::State
96
+ # Is the delta feature timed?
97
+ #
98
+ def timed?
99
+ @timed
100
+ end
101
+
102
+ # Opposite of +#timed?+.
103
+ #
104
+ def timeless?
105
+ ! timed?
106
+ end
107
+
108
+ def to_s
109
+ place.name
110
+ end
111
+
112
+ def label
113
+ "∂:#{place.name}:#{transitions.size}tt"
114
+ end
115
+ end # class YPetri::Net::State::Feature::Delta
@@ -1,57 +1,54 @@
1
1
  # encoding: utf-8
2
- class YPetri::Net::State
3
- class Feature
4
- # Firing of a Petri net tS transition.
5
- #
6
- class Firing < Feature
7
- attr_reader :transition
8
-
9
- class << self
10
- def parametrize *args
11
- Class.instance_method( :parametrize ).bind( self ).( *args ).tap do |ç|
12
- ç.instance_variable_set( :@instances,
13
- Hash.new do |hsh, id|
14
- case id
15
- when Firing then
16
- hsh[ id.transition ]
17
- when ç.net.Transition then
18
- hsh[ id ] = ç.__new__( id )
19
- else
20
- hsh[ ç.net.transition( id ) ]
21
- end
22
- end )
23
- end
24
- end
25
-
26
- attr_reader :instances
27
-
28
- alias __new__ new
29
-
30
- def new id
31
- instances[ id ]
32
- end
33
-
34
- def of id
35
- new id
36
- end
37
- end
38
-
39
- def initialize transition
40
- @transition = net.transition( transition )
41
- end
42
-
43
- def extract_from arg, **nn
44
- case arg
45
- when YPetri::Simulation then
46
- arg.send( :tS_transitions, [ transition ] ).firing.first
47
- else
48
- fail TypeError, "Argument type not supported!"
49
- end
50
- end
51
2
 
52
- def label
53
- "f:#{transition.name}"
3
+ # Firing of a Petri net tS transition.
4
+ #
5
+ class YPetri::Net::State::Feature::Firing < YPetri::Net::State::Feature
6
+ attr_reader :transition
7
+
8
+ class << self
9
+ def parametrize *args
10
+ Class.instance_method( :parametrize ).bind( self ).( *args ).tap do |ç|
11
+ ç.instance_variable_set( :@instances,
12
+ Hash.new do |hsh, id|
13
+ case id
14
+ when self then
15
+ hsh[ id.transition ]
16
+ when ç.net.Transition then
17
+ hsh[ id ] = ç.__new__( id )
18
+ else
19
+ hsh[ ç.net.transition( id ) ]
20
+ end
21
+ end )
54
22
  end
55
- end # class Firing
56
- end # class Feature
57
- end # YPetri::Net::State
23
+ end
24
+
25
+ attr_reader :instances
26
+
27
+ alias __new__ new
28
+
29
+ def new id
30
+ instances[ id ]
31
+ end
32
+
33
+ def of id
34
+ new id
35
+ end
36
+ end
37
+
38
+ def initialize transition
39
+ @transition = net.transition( transition )
40
+ end
41
+
42
+ def extract_from arg, **nn
43
+ case arg
44
+ when YPetri::Simulation then
45
+ arg.send( :tS_transitions, [ transition ] ).firing.first
46
+ else
47
+ fail TypeError, "Argument type not supported!"
48
+ end
49
+ end
50
+
51
+ def label
52
+ "f:#{transition.name}"
53
+ end
54
+ end # YPetri::Net::State::Feature::Firing