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.
- checksums.yaml +4 -4
- data/LICENSE.txt +675 -0
- data/README.md +6 -3
- data/Rakefile +1 -1
- data/lib/y_petri/agent/{petri_net_related.rb → petri_net_aspect.rb} +34 -10
- data/lib/y_petri/agent/{simulation_related.rb → simulation_aspect.rb} +49 -34
- data/lib/y_petri/agent.rb +5 -5
- data/lib/y_petri/core/guarded.rb +24 -0
- data/lib/y_petri/core/timed/euler.rb +4 -8
- data/lib/y_petri/core/timed/gillespie.rb +11 -17
- data/lib/y_petri/core/timed/methods.rb +23 -0
- data/lib/y_petri/core/timed/pseudo_euler.rb +10 -13
- data/lib/y_petri/core/timed/quasi_euler.rb +9 -8
- data/lib/y_petri/core/timed/runge_kutta.rb +10 -18
- data/lib/y_petri/core/timed.rb +6 -14
- data/lib/y_petri/core/timeless/methods.rb +15 -0
- data/lib/y_petri/core/timeless/pseudo_euler.rb +4 -8
- data/lib/y_petri/core/timeless.rb +9 -4
- data/lib/y_petri/core.rb +44 -42
- data/lib/y_petri/net/data_set.rb +246 -142
- data/lib/y_petri/net/node_access.rb +282 -0
- data/lib/y_petri/net/own_state.rb +14 -4
- data/lib/y_petri/net/state/feature/assignment.rb +123 -0
- data/lib/y_petri/net/state/feature/delta.rb +55 -35
- data/lib/y_petri/net/state/feature/firing.rb +68 -25
- data/lib/y_petri/net/state/feature/flux.rb +9 -2
- data/lib/y_petri/net/state/feature/gradient.rb +36 -19
- data/lib/y_petri/net/state/feature/marking.rb +10 -5
- data/lib/y_petri/net/state/feature.rb +105 -11
- data/lib/y_petri/net/state/features/record.rb +144 -99
- data/lib/y_petri/net/state/features.rb +327 -200
- data/lib/y_petri/net/state.rb +48 -82
- data/lib/y_petri/net/visualization.rb +1 -1
- data/lib/y_petri/net.rb +62 -47
- data/lib/y_petri/place/arcs.rb +44 -0
- data/lib/y_petri/place/features.rb +115 -0
- data/lib/y_petri/place.rb +62 -29
- data/lib/y_petri/simulation/dependency.rb +31 -67
- data/lib/y_petri/simulation/feature_set.rb +1 -1
- data/lib/y_petri/simulation/initial_marking/access.rb +42 -26
- data/lib/y_petri/simulation/marking_clamps/access.rb +22 -17
- data/lib/y_petri/simulation/marking_clamps.rb +0 -2
- data/lib/y_petri/simulation/marking_vector/access.rb +102 -40
- data/lib/y_petri/simulation/marking_vector.rb +35 -37
- data/lib/y_petri/simulation/matrix.rb +1 -1
- data/lib/y_petri/simulation/node_representation.rb +25 -0
- data/lib/y_petri/simulation/nodes/access.rb +78 -0
- data/lib/y_petri/simulation/{elements.rb → nodes.rb} +14 -13
- data/lib/y_petri/simulation/place_mapping.rb +2 -2
- data/lib/y_petri/simulation/place_representation.rb +8 -7
- data/lib/y_petri/simulation/places/access.rb +89 -70
- data/lib/y_petri/simulation/places/free.rb +1 -1
- data/lib/y_petri/simulation/places/types.rb +20 -22
- data/lib/y_petri/simulation/places.rb +23 -18
- data/lib/y_petri/simulation/recorder.rb +23 -18
- data/lib/y_petri/simulation/timed/recorder.rb +19 -11
- data/lib/y_petri/simulation/timed.rb +93 -29
- data/lib/y_petri/simulation/timeless/recorder.rb +11 -6
- data/lib/y_petri/simulation/timeless.rb +13 -3
- data/lib/y_petri/simulation/transition_representation/A.rb +24 -4
- data/lib/y_petri/simulation/transition_representation/S.rb +11 -1
- data/lib/y_petri/simulation/transition_representation/T.rb +1 -1
- data/lib/y_petri/simulation/transition_representation/Ts.rb +1 -1
- data/lib/y_petri/simulation/transition_representation/a.rb +1 -1
- data/lib/y_petri/simulation/transition_representation/s.rb +12 -1
- data/lib/y_petri/simulation/transition_representation/t.rb +1 -1
- data/lib/y_petri/simulation/transition_representation/tS.rb +1 -1
- data/lib/y_petri/simulation/transition_representation/ts.rb +1 -1
- data/lib/y_petri/simulation/transition_representation/types.rb +1 -1
- data/lib/y_petri/simulation/transition_representation.rb +4 -11
- data/lib/y_petri/simulation/transitions/A.rb +17 -2
- data/lib/y_petri/simulation/transitions/S.rb +1 -1
- data/lib/y_petri/simulation/transitions/T.rb +1 -1
- data/lib/y_petri/simulation/transitions/Ts.rb +6 -5
- data/lib/y_petri/simulation/transitions/a.rb +1 -1
- data/lib/y_petri/simulation/transitions/access.rb +195 -168
- data/lib/y_petri/simulation/transitions/s.rb +1 -1
- data/lib/y_petri/simulation/transitions/t.rb +1 -1
- data/lib/y_petri/simulation/transitions/tS.rb +1 -1
- data/lib/y_petri/simulation/transitions/ts.rb +1 -1
- data/lib/y_petri/simulation/transitions/types.rb +1 -1
- data/lib/y_petri/simulation/transitions.rb +5 -7
- data/lib/y_petri/simulation.rb +84 -90
- data/lib/y_petri/transition/A.rb +8 -2
- data/lib/y_petri/transition/T.rb +25 -2
- data/lib/y_petri/transition/arcs.rb +19 -3
- data/lib/y_petri/transition/construction_convenience.rb +11 -10
- data/lib/y_petri/transition/t.rb +14 -1
- data/lib/y_petri/transition/types.rb +6 -1
- data/lib/y_petri/transition.rb +9 -12
- data/lib/y_petri/version.rb +1 -1
- data/lib/y_petri/world/dependency.rb +3 -3
- data/lib/y_petri/world/{petri_net_related.rb → petri_net_aspect.rb} +4 -4
- data/lib/y_petri/world/simulation_aspect.rb +352 -0
- data/lib/y_petri/world.rb +4 -4
- data/lib/y_petri.rb +1 -1
- data/test/agent_test.rb +2 -1
- data/test/examples/demonstrator.rb +4 -1
- data/test/examples/demonstrator_2.rb +5 -0
- data/test/examples/demonstrator_4.rb +6 -5
- data/test/examples/example_2.rb +2 -0
- data/test/examples/manual_examples.rb +4 -4
- data/test/net_test.rb +457 -54
- data/test/place_test.rb +11 -7
- data/test/simulation_test.rb +358 -331
- data/test/transition_test.rb +11 -10
- data/test/world_test.rb +2 -0
- data/test/y_petri_test.rb +2 -1
- data/y_petri.gemspec +24 -18
- metadata +71 -17
- data/LICENSE +0 -22
- data/lib/y_petri/net/element_access.rb +0 -239
- data/lib/y_petri/simulation/element_representation.rb +0 -20
- data/lib/y_petri/simulation/elements/access.rb +0 -57
- data/lib/y_petri/transition/type.rb +0 -103
- data/lib/y_petri/transition/type_information.rb +0 -103
- data/lib/y_petri/world/simulation_related.rb +0 -176
data/lib/y_petri/core.rb
CHANGED
@@ -1,64 +1,66 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
# This class represents a simulator.
|
4
|
+
#
|
6
5
|
class YPetri::Core
|
6
|
+
require_relative 'core/timed'
|
7
|
+
require_relative 'core/timeless'
|
8
|
+
require_relative 'core/guarded'
|
9
|
+
|
10
|
+
★ YPetri::Simulation::Dependency
|
11
|
+
|
7
12
|
DEFAULT_METHOD = :pseudo_euler
|
8
13
|
|
9
|
-
|
10
|
-
#
|
14
|
+
class << self
|
15
|
+
# Timed subclass of self.
|
11
16
|
#
|
12
|
-
def
|
13
|
-
|
17
|
+
def timed
|
18
|
+
Class.new self do
|
19
|
+
include Timed
|
20
|
+
def timed?; true end
|
21
|
+
def timeless?; false end
|
22
|
+
end
|
14
23
|
end
|
15
24
|
|
16
|
-
#
|
25
|
+
# Timeless subclass of self.
|
17
26
|
#
|
18
|
-
def
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
is: Δ_if_tsa_fire_once ) )
|
27
|
+
def timeless
|
28
|
+
Class.new self do
|
29
|
+
include Timeless
|
30
|
+
def timed?; false end
|
31
|
+
def timeless?; true end
|
24
32
|
end
|
25
33
|
end
|
26
34
|
|
27
|
-
#
|
35
|
+
# Vanilla simulator is not guarded.
|
36
|
+
#
|
37
|
+
def guarded?; false end
|
38
|
+
|
39
|
+
# Guarded subclass of self (not working yet).
|
28
40
|
#
|
29
|
-
def
|
30
|
-
|
31
|
-
|
41
|
+
def guarded
|
42
|
+
Class.new self do
|
43
|
+
include Guarded
|
44
|
+
def guarded?; true end
|
32
45
|
end
|
33
46
|
end
|
34
47
|
end
|
35
48
|
|
36
|
-
|
37
|
-
|
38
|
-
delegate :simulation, to: "self.class"
|
39
|
-
delegate :alert, to: :recorder
|
49
|
+
attr_reader :simulation_method
|
40
50
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
def new( method: nil, guarded: false, **nn )
|
45
|
-
# Alow for named arg. alias :simulation_method
|
46
|
-
sm = method || nn[:simulation_method] || DEFAULT_METHOD
|
47
|
-
using_simulation_method( sm, guarded: guarded ).__new__
|
48
|
-
end
|
49
|
-
|
50
|
-
def using_simulation_method symbol, guarded: false
|
51
|
-
simulation_method_module = const_get( symbol.to_s.camelize )
|
52
|
-
# TODO: "guarded" argument not handled yet
|
53
|
-
Class.new self do prepend( simulation_method_module ) end
|
54
|
-
end
|
51
|
+
def initialize method: nil, guarded: false, **named_args
|
52
|
+
@simulation_method = method || DEFAULT_METHOD
|
53
|
+
method_init # defined in Timed::Methods and Timeless::Methods
|
55
54
|
end
|
56
55
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
56
|
+
delegate :simulation,
|
57
|
+
:timed?,
|
58
|
+
:timeless?,
|
59
|
+
:guarded?,
|
60
|
+
to: "self.class"
|
61
|
+
|
62
|
+
delegate :alert!,
|
63
|
+
to: :recorder
|
62
64
|
|
63
65
|
# Delta for free places from timeless transitions.
|
64
66
|
#
|
@@ -95,6 +97,6 @@ class YPetri::Core
|
|
95
97
|
# Fires assignment transitions.
|
96
98
|
#
|
97
99
|
def assignment_transitions_all_fire!
|
98
|
-
simulation.
|
100
|
+
simulation.A_direct_assignment_closure.call
|
99
101
|
end
|
100
102
|
end # class YPetri::Core
|
data/lib/y_petri/net/data_set.rb
CHANGED
@@ -1,6 +1,29 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
#
|
3
|
+
# +DataSet+ is a collection of labeled state records. It is a subclass of +Hash+
|
4
|
+
# class, whose keys are known as _events_, and values are data points (arrays)
|
5
|
+
# that correspond to saved records (+YPetri::Net::State::Features::Record+) under
|
6
|
+
# a given feature set (+YPetri::Net::State::Features+). +DataSet+ class is
|
7
|
+
# intended to be parametrized with a specific feature set. Apart from the methods
|
8
|
+
# inherited from +Hash+, +YPetri::Net::DataSet+ can load a record at a given
|
9
|
+
# event (+#record+ method), reconstruct a simulation at a given event
|
10
|
+
# (+#reconstruct+ method), return columns corresponding to features (+#series+
|
11
|
+
# method) and perform feature selection (+#marking+, +#firing+, +#flux+,
|
12
|
+
# +#gradient+, +#delta+, +#assignment+, and +#reduced_features+ for mixed feature
|
13
|
+
# sets). Apart from standard inspection methods, +DataSet+ has methods +#print+
|
14
|
+
# and +#plot+ for visual presentation. Also, +DataSet+ has methods specially
|
15
|
+
# geared towards records of timed simulations, whose events are points in time.
|
16
|
+
# Method +#interpolate+ uses linear interpolation to find the approximate state
|
17
|
+
# of the system at some exact time using linear interpolation between the nearest
|
18
|
+
# earlier and later data points (which can be accessed respectively by +#floor+
|
19
|
+
# and +#ceiling+ methods). Interpolation is used for resampling the set
|
20
|
+
# (+#resample+ method).
|
21
|
+
#
|
22
|
+
# Finally, it is possible that especially professional statisticians have
|
23
|
+
# written, or are planning to write, a +DataSet+ class better than this one.
|
24
|
+
# If I discover a good +DataSet+ class in the future, I would like to inherit
|
25
|
+
# from it or otherwise integrate with it for the purposes of
|
26
|
+
# +YPetri::Net::DataSet+.
|
4
27
|
#
|
5
28
|
class YPetri::Net::DataSet < Hash
|
6
29
|
class << self
|
@@ -19,18 +42,12 @@ class YPetri::Net::DataSet < Hash
|
|
19
42
|
private :__new__
|
20
43
|
|
21
44
|
delegate :net, to: :features
|
22
|
-
delegate :State, to: :net
|
23
|
-
delegate :Marking, :Firing, :Flux, :Gradient, :Delta,
|
24
|
-
to: "State()"
|
25
45
|
end
|
26
46
|
|
27
47
|
alias events keys
|
28
|
-
alias records values
|
29
48
|
|
30
49
|
delegate :features,
|
31
50
|
:net,
|
32
|
-
:State,
|
33
|
-
:Marking, :Firing, :Flux, :Gradient, :Delta,
|
34
51
|
to: "self.class"
|
35
52
|
|
36
53
|
attr_reader :type, # more like event_type, idea not matured yet
|
@@ -78,16 +95,16 @@ class YPetri::Net::DataSet < Hash
|
|
78
95
|
#
|
79
96
|
def reconstruct at: (fail "No event given!"), **settings
|
80
97
|
# settings may include marking clamps, marking, inital marking...
|
81
|
-
|
98
|
+
record = interpolate( at )
|
82
99
|
settings = settings().merge settings if settings()
|
83
100
|
if timed? then
|
84
|
-
|
101
|
+
record.reconstruct time: at, **settings
|
85
102
|
else
|
86
|
-
|
103
|
+
record.reconstruct **settings
|
87
104
|
end
|
88
105
|
end
|
89
106
|
|
90
|
-
# Interpolates the recording
|
107
|
+
# Interpolates the recording at the given point (event). Return value is the
|
91
108
|
# Record class instance.
|
92
109
|
#
|
93
110
|
def interpolate( event )
|
@@ -97,23 +114,24 @@ class YPetri::Net::DataSet < Hash
|
|
97
114
|
timed? or raise TypeError, "Event #{event} not recorded! (%s)" %
|
98
115
|
"simulation type: #{type.nil? ? 'nil' : type}"
|
99
116
|
# (Remark: #floor, #ceiling supported by timed datasets only)
|
100
|
-
|
101
|
-
fail "Event #{event} has no floor!" if
|
102
|
-
|
103
|
-
|
104
|
-
fail "Event #{event} has no ceiling!" if
|
105
|
-
|
106
|
-
rslt =
|
117
|
+
floor = floor( event )
|
118
|
+
fail "Event #{event} has no floor!" if floor.nil?
|
119
|
+
fl = Matrix.column_vector record( floor )
|
120
|
+
ceiling = ceiling( event )
|
121
|
+
fail "Event #{event} has no ceiling!" if ceiling.nil?
|
122
|
+
ce = Matrix.column_vector record( ceiling )
|
123
|
+
rslt = fl + ( ce - fl ) / ( ceiling - floor ) * ( event - floor )
|
107
124
|
features.load( rslt.column_to_a )
|
108
125
|
end
|
109
126
|
end
|
127
|
+
alias at interpolate
|
110
128
|
|
111
129
|
# Resamples the recording.
|
112
130
|
#
|
113
|
-
def resample **
|
114
|
-
time_range =
|
131
|
+
def resample **settings
|
132
|
+
time_range = settings.may_have( :time_range, syn!: :time ) ||
|
115
133
|
events.first .. events.last
|
116
|
-
sampling =
|
134
|
+
sampling = settings.must_have :sampling
|
117
135
|
t0, target_time = time_range.begin, time_range.end
|
118
136
|
t = t0
|
119
137
|
o = self.class.new type: type
|
@@ -137,165 +155,238 @@ class YPetri::Net::DataSet < Hash
|
|
137
155
|
|
138
156
|
# Returns the data series for the specified features.
|
139
157
|
#
|
140
|
-
def series
|
141
|
-
|
142
|
-
|
143
|
-
reduce_features( State().features( arg ) ).series
|
158
|
+
def series array=nil
|
159
|
+
return records.transpose if array.nil?
|
160
|
+
reduce_features( net.State.Features array ).series
|
144
161
|
end
|
145
162
|
|
146
163
|
# Expects a hash of features (:marking (alias :state) of places, :firing
|
147
|
-
# of tS transitions, :delta of places and/or transitions
|
148
|
-
# corresponding mapping of the recording.
|
164
|
+
# of tS transitions, :delta of places and/or transitions, :assignment of
|
165
|
+
# A transitions) and returns the corresponding mapping of the recording.
|
149
166
|
#
|
150
|
-
def reduce_features
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
end
|
187
|
-
dataset.update event => rf_Record.load( line )
|
188
|
-
}
|
189
|
-
}
|
167
|
+
def reduce_features array=nil, **named_args
|
168
|
+
delta_time_given = named_args.has? :delta_time, syn!: :Δt
|
169
|
+
Δt = named_args.delete :delta_time
|
170
|
+
ff = net.State.Features[ *array, **named_args ] # reduced feature set
|
171
|
+
absent = ff - features() # features absent from the current set
|
172
|
+
present = ff - absent # features present in the current set
|
173
|
+
timedness = true if absent.any? { |f| f.timed? rescue false }
|
174
|
+
fail ArgumentError, "Reconstruction of timed features requires Δt to be" +
|
175
|
+
"supplied!" unless delta_time_given if timedness
|
176
|
+
present_ii =
|
177
|
+
present.each_with_object( {} ) { |f, ꜧ| ꜧ[f] = features().index f }
|
178
|
+
ds = ff.DataSet.new type: type
|
179
|
+
if absent.empty? then # no reconstruction
|
180
|
+
( events >> records ).each_with_object ds do |(event, record), dataset|
|
181
|
+
line = record.values_at *ff.map( &present_ii.method( :[] ) )
|
182
|
+
dataset.update event => ff.load( line )
|
183
|
+
end
|
184
|
+
else
|
185
|
+
( events >> records ).each_with_object ds do |(event, record), dataset|
|
186
|
+
reconstructed_sim = reconstruct at: event
|
187
|
+
line = if timedness then
|
188
|
+
ff.map { |f|
|
189
|
+
i = present_ii[ f ]
|
190
|
+
break record[ i ] if i
|
191
|
+
f.extract_from( reconstructed_sim, Δt: Δt )
|
192
|
+
}
|
193
|
+
else
|
194
|
+
ff.map { |f|
|
195
|
+
i = present_ii[ f ]
|
196
|
+
break record[ i ] if i
|
197
|
+
f.extract_from( reconstructed_sim, Δt: Δt )
|
198
|
+
}
|
199
|
+
end
|
200
|
+
dataset.update event => ff.load( line )
|
201
|
+
end
|
202
|
+
end
|
190
203
|
end
|
191
204
|
|
192
|
-
#
|
193
|
-
#
|
194
|
-
# marking features from the receiver dataset are selected.
|
205
|
+
# Expects an array of marking feature identifiers, and returns a subset of
|
206
|
+
# this dataset with only the specified marking features retained.
|
195
207
|
#
|
196
|
-
def
|
197
|
-
|
198
|
-
reduce_features marking: ids
|
208
|
+
def Marking array
|
209
|
+
reduce_features marking: array
|
199
210
|
end
|
200
211
|
|
201
|
-
#
|
202
|
-
#
|
203
|
-
#
|
212
|
+
# Expects an arbitrary number of marking feature identifiers, and returns a
|
213
|
+
# subset of this dataset with only the specified marking features retained.
|
214
|
+
# If no arguments are given, all the marking features are assumed.
|
204
215
|
#
|
205
|
-
def
|
206
|
-
|
207
|
-
|
208
|
-
args.last.delete( :delta_time )
|
209
|
-
.tap { args.delete_at( -1 ) if args.last.empty? }
|
210
|
-
end
|
211
|
-
if Δt then
|
212
|
-
return reduce_features net.State.firing, delta_time: Δt if args.empty?
|
213
|
-
reduce_features firing: args.first, delta_time: Δt
|
214
|
-
else
|
215
|
-
return reduce_features net.State.firing if args.empty?
|
216
|
-
reduce_features firing: args.first
|
217
|
-
end
|
216
|
+
def marking *ids
|
217
|
+
return Marking net.State.Features.marking if ids.empty?
|
218
|
+
Marking ids
|
218
219
|
end
|
219
220
|
|
220
|
-
#
|
221
|
-
#
|
222
|
-
#
|
221
|
+
# Expects an array of firing feature identifiers, and returns a subset of
|
222
|
+
# this dataset with only the specified firing features retained. Named
|
223
|
+
# arguments may include +:delta_time+, alias +:Δt+ (for firing of timed
|
224
|
+
# transitions).
|
225
|
+
#
|
226
|
+
def Firing array, **named_args
|
227
|
+
reduce_features firing: array, **named_args
|
228
|
+
end
|
229
|
+
|
230
|
+
# Expects an arbitrary number of firing feature identifiers and returns
|
231
|
+
# a subset of this dataset with only the specified firing features retained.
|
232
|
+
# Named arguments may include +:delta_time+, alias +:Δt+ (for firing of
|
233
|
+
# timed transitions).
|
223
234
|
#
|
224
|
-
def
|
225
|
-
return
|
226
|
-
|
235
|
+
def firing *ids, **named_args
|
236
|
+
return Firing net.State.Features.firing, **named_args if ids.empty?
|
237
|
+
Firing ids, **named_args
|
238
|
+
end
|
239
|
+
|
240
|
+
# Expects an array of flux feature identifiers, and returns a subset of
|
241
|
+
# this dataset with only the specified flux features retained.
|
242
|
+
#
|
243
|
+
def Flux array
|
244
|
+
reduce_features flux: array
|
245
|
+
end
|
246
|
+
|
247
|
+
# Expects an arbitrary number of flux feature identifiers, and returns
|
248
|
+
# a subset of this dataset, with only the specified flux features retained.
|
249
|
+
# If no aruments are given, full set of flux features is assumed.
|
250
|
+
#
|
251
|
+
def flux *ids
|
252
|
+
return Flux net.State.Features.flux if ids.empty?
|
253
|
+
Flux ids
|
254
|
+
end
|
255
|
+
|
256
|
+
# Expects an array of gradient feature identifiers, optionally qualified by
|
257
|
+
# the +:transitions+ named argument, defaulting to all T transitions in the
|
258
|
+
# net.
|
259
|
+
#
|
260
|
+
def Gradient array, transitions: nil
|
261
|
+
if transitions.nil? then
|
262
|
+
reduce_features gradient: array
|
263
|
+
else
|
264
|
+
reduce_features gradient: [ *array, transitions: transitions ]
|
265
|
+
end
|
227
266
|
end
|
228
267
|
|
229
268
|
# Returns a subset of this dataset with only the specified gradient features
|
230
269
|
# identified by the arguments retained. If no arguments are given, all the
|
231
270
|
# gradient features from the receiver dataset are selected.
|
232
271
|
#
|
233
|
-
def gradient *
|
234
|
-
return
|
235
|
-
|
272
|
+
def gradient *ids, transitions: nil
|
273
|
+
return Gradient net.State.Features.gradient, transitions: transitions if
|
274
|
+
ids.empty?
|
275
|
+
Gradient ids, transitions: transitions
|
236
276
|
end
|
237
277
|
|
238
|
-
#
|
239
|
-
#
|
240
|
-
#
|
278
|
+
# Expects an array of delta feature identifiers, optionally qualified by
|
279
|
+
# the +:transitions+ named argument, defaulting to all the transitions in
|
280
|
+
# the net.
|
241
281
|
#
|
242
|
-
def
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
reduce_features delta: args, delta_time: Δt
|
282
|
+
def Delta array, transitions: nil, **named_args
|
283
|
+
if named_args.has? :delta_time, syn!: :Δt then
|
284
|
+
Δt = named_args.delete( :delta_time )
|
285
|
+
if transitions.nil? then
|
286
|
+
reduce_features delta: array, Δt: Δt
|
287
|
+
else
|
288
|
+
reduce_features delta: [ *array, transitions: transitions ], Δt: Δt
|
289
|
+
end
|
251
290
|
else
|
252
|
-
|
253
|
-
|
291
|
+
if transitions.nil? then
|
292
|
+
reduce_features delta: array
|
293
|
+
else
|
294
|
+
reduce_features delta: [ *array, transitions: transitions ]
|
295
|
+
end
|
254
296
|
end
|
255
297
|
end
|
256
298
|
|
299
|
+
# Expects an arbitrary number of ordered arguments identifying delta
|
300
|
+
# features, optionally qualified by the +:transitions+ named argument,
|
301
|
+
# defaulting to all the transitions in the net.
|
302
|
+
#
|
303
|
+
def delta *ordered_args, transitions: nil, **named_args
|
304
|
+
return Delta( ordered_args, transitions: transitions, **named_args ) unless
|
305
|
+
ordered_args.empty?
|
306
|
+
return Delta( net.places, **named_args ) if transitions.nil?
|
307
|
+
Delta( net.places, transitions: transitions, **named_args )
|
308
|
+
end
|
309
|
+
|
310
|
+
def delta_timed *ordered_args, **named_args
|
311
|
+
delta *ordered_args, transitions: net.T_transitions, **named_args
|
312
|
+
end
|
313
|
+
|
314
|
+
def delta_timeless *ordered_args, **named_args
|
315
|
+
delta *ordered_args, transitions: net.t_transitions, **named_args
|
316
|
+
end
|
317
|
+
|
318
|
+
# Expects an array of assignment feature identifiers. Returns a subset of this
|
319
|
+
# dataset with only the specified assignment features retained.
|
320
|
+
#
|
321
|
+
def Assignment array
|
322
|
+
reduce_features assignment: array
|
323
|
+
end
|
324
|
+
|
325
|
+
# Expects an arbitrary number of assignment feature identifiers as arguments,
|
326
|
+
# and returns a subset of this dataset with only the specified assignment
|
327
|
+
# features retained. If no arguments are given, all the assignment features
|
328
|
+
# are assumed.
|
329
|
+
#
|
330
|
+
def assignment *ids
|
331
|
+
return reduce_features net.State.Features.assignment if args.empty?
|
332
|
+
reduce_features assignment: ids
|
333
|
+
end
|
334
|
+
|
257
335
|
# Outputs the current recording in CSV format.
|
258
336
|
#
|
259
337
|
def to_csv
|
260
|
-
|
338
|
+
require 'csv'
|
339
|
+
[ ":event", *features.labels.map( &:to_s ) ].join( ',' ) + "\n" +
|
340
|
+
map { |lbl, rec| [ lbl, *rec ].join ',' }.join( "\n" )
|
261
341
|
end
|
262
342
|
|
263
|
-
# Plots the dataset. Takes several optional arguments: The list of
|
264
|
-
#
|
265
|
-
#
|
266
|
-
#
|
267
|
-
#
|
268
|
-
#
|
269
|
-
#
|
270
|
-
#
|
343
|
+
# Plots the dataset. Takes several optional arguments: The list of nodes can be
|
344
|
+
# supplied as optional first ordered argument, which are then converted into
|
345
|
+
# features using +Net::State::Features.infer_from_nodes+ method. Similarly,
|
346
|
+
# the features to exclude can be specifies as a list of nodes (or a
|
347
|
+
# feature-specifying hash) supplied under +except:+ keyword. Otherwise, feature
|
348
|
+
# specification can be passed to the method as named arguments. If no feature
|
349
|
+
# specification is explicitly provided, it is assumed that all the features of
|
350
|
+
# this dataset are meant to be plotted.
|
271
351
|
#
|
272
|
-
def plot(
|
352
|
+
def plot( nodes=nil, except: [], **named_args )
|
353
|
+
puts "Hello from plot!"
|
354
|
+
nn = named_args
|
273
355
|
time = nn.may_have :time, syn!: :time_range
|
274
356
|
events = events()
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
357
|
+
# Figure out features.
|
358
|
+
ff = if nodes.nil? then
|
359
|
+
nn_ff = nn.slice [ :marking, :flux, :firing,
|
360
|
+
:gradient, :delta, :assignment ]
|
361
|
+
nn_ff.empty? ? features : net.State.Features( nn_ff )
|
362
|
+
else
|
363
|
+
net.State.Features.infer_from_nodes( nodes )
|
364
|
+
end
|
365
|
+
# Figure out the features not to plot ("except" features).
|
281
366
|
xff = case except
|
282
|
-
when Array then net.State.Features.
|
283
|
-
when Hash then net.State.
|
367
|
+
when Array then net.State.Features.infer_from_nodes( except )
|
368
|
+
when Hash then net.State.Features( except )
|
284
369
|
else
|
285
370
|
fail TypeError, "Wrong type of :except argument: #{except.class}"
|
286
371
|
end
|
372
|
+
# Subtract the "except" features from features to plot.
|
287
373
|
ff -= xff
|
288
|
-
|
289
|
-
|
374
|
+
# Convert the feature set into a set of data arrays.
|
375
|
+
data_arrays = series( ff )
|
376
|
+
# Figure out the x axis range for plotting.
|
377
|
+
x_range = if nn.has? :time then
|
378
|
+
if time.is_a? Range then
|
379
|
+
"[#{time.begin}:#{time.end}]"
|
380
|
+
else
|
381
|
+
"[-0:#{SY::Time.magnitude( time ).amount rescue time}]"
|
382
|
+
end
|
383
|
+
else
|
290
384
|
from = events.first || 0
|
291
|
-
to = events.last
|
292
|
-
|
385
|
+
to = if events.last and events.last > from then events.last
|
386
|
+
else events.first + 1 end
|
293
387
|
"[#{from}:#{to}]"
|
294
|
-
elsif time.is_a? Range then
|
295
|
-
"[#{time.begin}:#{time.end}]"
|
296
|
-
else
|
297
|
-
"[-0:#{SY::Time.magnitude( time ).amount rescue time}]"
|
298
388
|
end
|
389
|
+
# Invoke Gnuplot.
|
299
390
|
Gnuplot.open do |gp|
|
300
391
|
Gnuplot::Plot.new gp do |plot|
|
301
392
|
plot.xrange x_range
|
@@ -307,10 +398,23 @@ class YPetri::Net::DataSet < Hash
|
|
307
398
|
plot.title nn[:title] || "#{net} plot"
|
308
399
|
plot.ylabel nn[:ylabel] || "Values"
|
309
400
|
plot.xlabel nn[:xlabel] || "Time [s]"
|
310
|
-
ff.labels.zip(
|
311
|
-
|
312
|
-
|
313
|
-
|
401
|
+
ff.labels.zip( data_arrays ).each do |label, array|
|
402
|
+
# Replace NaN and Infinity with 0.0 and warn about it.
|
403
|
+
nan, inf = 0, 0
|
404
|
+
array = array.map { |v|
|
405
|
+
if v.to_f.infinite? then inf += 1; 0.0
|
406
|
+
elsif v.to_f.nan? then nan += 1; 0.0
|
407
|
+
else v end
|
408
|
+
}
|
409
|
+
# Warn.
|
410
|
+
nan = nan > 0 ? "#{nan} NaN values" : nil
|
411
|
+
inf = inf > 0 ? "#{inf} infinite values" : nil
|
412
|
+
msg = "Warning: column #{label} contains %s plotted as 0!"
|
413
|
+
warn msg % [ nan, inf ].compact.join( ' and ' ) if nan or inf
|
414
|
+
# Finally, plot.
|
415
|
+
plot.data << Gnuplot::DataSet.new( [ events, array ] ) { |set|
|
416
|
+
set.with = "linespoints"
|
417
|
+
set.title = label
|
314
418
|
}
|
315
419
|
end
|
316
420
|
end
|