Class: YPetri::Net::DataSet

Inherits:
Hash
  • Object
show all
Defined in:
lib/y_petri/net/data_set.rb

Overview

Dataset is a collection of labeled state records.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#settingsObject (readonly)

more like event_type, idea not matured yet



36
37
38
# File 'lib/y_petri/net/data_set.rb', line 36

def settings
  @settings
end

#typeObject (readonly)

more like event_type, idea not matured yet



36
37
38
# File 'lib/y_petri/net/data_set.rb', line 36

def type
  @type
end

Class Method Details

.__new__Object



7
# File 'lib/y_petri/net/data_set.rb', line 7

alias __new__ new

.new(type: nil) ⇒ Object



9
10
11
12
13
14
15
16
17
# File 'lib/y_petri/net/data_set.rb', line 9

def new type: nil
  __new__ do |hsh, missing|
    case missing
    when Float then nil
    else hsh[ missing.to_f ] end
  end.tap { |inst|
    inst.instance_variable_set :@type, type
  }
end

Instance Method Details

#ceiling(event, equal_ok = true) ⇒ Object

Returns the nearest event greater or equal to the supplied event-type argument. The second optional ordered argument, true by default, controls whether equality is accepted. If set to false, then the nearest greater event is sought.



66
67
68
69
# File 'lib/y_petri/net/data_set.rb', line 66

def ceiling( event, equal_ok=true )
  e = events.ascending_ceiling( event, equal_ok )
  e.nil? ? nil : e
end

#delta(*args) ⇒ Object

Returns a subset of this dataset with only the specified delta features identified by the arguments retained. If no arguments are given, all the delta features from the receiver dataset are selected.



242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'lib/y_petri/net/data_set.rb', line 242

def delta *args
  Δt = if args.last.is_a? Hash then
         args.last.may_have( :delta_time, syn!: :Δt )
         args.last.delete( :delta_time )
           .tap { args.delete_at( -1 ) if args.last.empty? }
       end
  if Δt then
    return reduce_features net.State.delta, delta_time: Δt if args.empty?
    reduce_features delta: args, delta_time: Δt
  else
    return reduce_features net.State.delta if args.empty?
    reduce_features delta: args
  end
end

#distance(other) ⇒ Object

Computes the distance to another dataset.



129
130
131
132
133
134
135
136
# File 'lib/y_petri/net/data_set.rb', line 129

def distance( other )
  sum_of_sq = events
    .map { |e| [ e, other.interpolate( e ) ] }
    .map { |rec1, rec2| rec1.euclidean_distance rec2 }
    .map { |dist| dist * dist }
    .reduce( :+ )
  sum_of_sq ** 0.5
end

#firing(*args) ⇒ Object

Returns a subset of this dataset with only the specified firing features identified by the arguments retained. If no arguments are given, all the firing features from the receiver dataset are selected.



205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/y_petri/net/data_set.rb', line 205

def firing *args
  Δt = if args.last.is_a? Hash then
         args.last.may_have( :delta_time, syn!: :Δt )
         args.last.delete( :delta_time )
           .tap { args.delete_at( -1 ) if args.last.empty? }
       end
  if Δt then
    return reduce_features net.State.firing, delta_time: Δt if args.empty?
    reduce_features firing: args.first, delta_time: Δt
  else
    return reduce_features net.State.firing if args.empty?
    reduce_features firing: args.first
  end
end

#floor(event, equal_ok = true) ⇒ Object

Returns the nearest event smaller or equal to the supplied event-type argument. The second optional ordered argument, true by default, controls whether equality is accepted. If set to false, then the nearest smaller event is sought.



56
57
58
59
# File 'lib/y_petri/net/data_set.rb', line 56

def floor( event, equal_ok=true )
  e = events.ascending_floor( event, equal_ok )
  e.nil? ? nil : e
end

#flux(ids = nil) ⇒ Object

Returns a subset of this dataset with only the specified flux features identified by the arguments retained. If no arguments are given, all the flux features from the receiver dataset are selected.



224
225
226
227
# File 'lib/y_petri/net/data_set.rb', line 224

def flux ids=nil
  return reduce_features net.State.flux if ids.nil?
  reduce_features flux: ids
end

#gradient(*args) ⇒ Object

Returns a subset of this dataset with only the specified gradient features identified by the arguments retained. If no arguments are given, all the gradient features from the receiver dataset are selected.



233
234
235
236
# File 'lib/y_petri/net/data_set.rb', line 233

def gradient *args
  return reduce_features net.State.gradient if args.empty?
  reduce_features gradient: args
end

#inspectObject

Inspect string of the instance.



331
332
333
# File 'lib/y_petri/net/data_set.rb', line 331

def inspect
  to_s
end

#interpolate(event) ⇒ Object

Interpolates the recording an the given point (event). Return value is the Record class instance.



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/y_petri/net/data_set.rb', line 93

def interpolate( event )
  begin
    record( event )
  rescue KeyError => msg
    timed? or raise TypeError, "Event #{event} not recorded! (%s)" %
      "simulation type: #{type.nil? ? 'nil' : type}"
    # (Remark: #floor, #ceiling supported by timed datasets only)
    fe = floor( event )
    fail "Event #{event} has no floor!" if fe.nil?
    f = Matrix.column_vector record( fe )
    ce = ceiling( event )
    fail "Event #{event} has no ceiling!" if ce.nil?
    c = Matrix.column_vector record( ce )
    rslt = f + ( c - f ) / ( ce - fe ) * ( event - fe )
    features.load( rslt.column_to_a )
  end
end

#marking(ids = nil) ⇒ Object

Returns a subset of this dataset with only the specified marking features identified by the arguments retained. If no arguments are given, all the marking features from the receiver dataset are selected.



196
197
198
199
# File 'lib/y_petri/net/data_set.rb', line 196

def marking ids=nil
  return reduce_features net.State.marking if ids.nil?
  reduce_features marking: ids
end

#plot(element_ids = nil, time: nil, except: [], **nn) ⇒ Object

Plots the dataset. Takes several optional arguments: The list of elements can be supplied as optional first ordered argument, which are then converted into features using Net::State::Features.infer_from_elements method. Similarly, the features to exclude can be specifies as a list of elements (or a feature-specifying hash) supplied under except: keyword. Otherwise, feature specification can be passed to the method as named arguments. If no feature specification is explicitly provided, it is assumed that all the features of this dataset are meant to be plotted.



272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'lib/y_petri/net/data_set.rb', line 272

def plot( element_ids=nil, time: nil, except: [], **nn )
  time = nn.may_have :time, syn!: :time_range
  events = events()
  if element_ids.nil? then
    nn_ff = nn.slice [ :marking, :flux, :firing, :gradient, :delta ]
    ff = nn_ff.empty? ? features : net.State.features( nn_ff )
  else
    ff = net.State.Features.infer_from_elements( element_ids )
  end
  xff = case except
        when Array then net.State.Features.infer_from_elements( except )
        when Hash then net.State.features( except )
        else
          fail TypeError, "Wrong type of :except argument: #{except.class}"
        end
  ff -= xff
  data_ss = series( ff )
  x_range = if time.nil? then
              from = events.first || 0
              to = events.last && events.last > from ? events.last :
                events.first + 1
              "[#{from}:#{to}]"
            elsif time.is_a? Range then
              "[#{time.begin}:#{time.end}]"
            else
              "[-0:#{SY::Time.magnitude( time ).amount rescue time}]"
            end
  Gnuplot.open do |gp|
    Gnuplot::Plot.new gp do |plot|
      plot.xrange x_range
      if nn.has? :yrange, syn!: :y_range then
        if nn[:yrange].is_a? Range then
          plot.yrange "[#{nn[:yrange].begin}:#{nn[:yrange].end}]"
        else fail TypeError, "Argument :yrange is not a range!" end
      end
      plot.title nn[:title] || "#{net} plot"
      plot.ylabel nn[:ylabel] || "Values"
      plot.xlabel nn[:xlabel] || "Time [s]"
      ff.labels.zip( data_ss ).each do |label, data_array|
        plot.data << Gnuplot::DataSet.new( [ events, data_array ] ) { |ds|
          ds.with = "linespoints"
          ds.title = label
        }
      end
    end
  end
end

Pretty print the dataset. Takes :precision and :distance named arguments, that control the shape of the printed table.



338
339
340
341
342
343
344
345
# File 'lib/y_petri/net/data_set.rb', line 338

def print precision: 4, distance: precision + 4
  features.labels.print_as_line precision: precision, distance: distance
  puts '-' * features.size * distance
  records.each { |record|
    record.print_as_line precision: precision, distance: distance
  }
  return nil
end

#reconstruct(event: (fail "No event given!"), **settings) ⇒ Object

Recreates the simulation at a given event label.



79
80
81
82
83
84
85
86
87
88
# File 'lib/y_petri/net/data_set.rb', line 79

def reconstruct event: (fail "No event given!"), **settings
  # settings may include marking clamps, marking, inital marking...
  rec = interpolate( event )
  settings = settings().merge settings if settings()
  if timed? then
    rec.reconstruct time: event, **settings
  else
    rec.reconstruct **settings
  end
end

#record(event) ⇒ Object

Returns the Record instance corresponding to the given recorded event.



47
48
49
# File 'lib/y_petri/net/data_set.rb', line 47

def record( event )
  features.load( fetch event )
end

#reduce_features(*args) ⇒ Object

Expects a hash of features (:marking (alias :state) of places, :firing of tS transitions, :delta of places and/or transitions) and returns the corresponding mapping of the recording.



150
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
187
188
189
190
# File 'lib/y_petri/net/data_set.rb', line 150

def reduce_features *args
  Δt = if args.last.is_a? Hash then
         args.last.may_have( :delta_time, syn!: :Δt )
         args.last.delete( :delta_time )
           .tap { args.delete_at( -1 ) if args.last.empty? }
       end
  reduced_features = net.State.features *args
  rf_Record = reduced_features.Record
  reduced_features.new_dataset( type: type ).tap { |dataset|
    ( events >> records ).each_pair { |event, record|
      absent_features = reduced_features - features()
      if absent_features.empty? then # it is a subset
        line = reduced_features.map { |feature| record.fetch feature }
      else # it will require simulation reconstruction
        sim = reconstruct event: event
        if absent_features.any? { |f| f.timed? rescue false } then
          fail ArgumentError, "Reconstruction of timed features requires " +
            "the named arg :delta_time to be given!" unless Δt
          line = reduced_features.map do |feature|
            if absent_features.include? feature then
              if ( feature.timed? rescue false ) then
                feature.extract_from( sim ).( Δt )
              else
                feature.extract_from( sim )
              end
            else
              record.fetch feature
            end
          end
        else
          line = reduced_features.map do |feat|
            if absent_features.include? feat then
              feat.extract_from( sim )
            else record.fetch( feat ) end
          end
        end
      end
      dataset.update event => rf_Record.load( line )
    }
  }
end

#resample(**nn) ⇒ Object

Resamples the recording.



113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/y_petri/net/data_set.rb', line 113

def resample **nn
  time_range = nn.may_have( :time_range, syn!: :time ) ||
    events.first .. events.last
  sampling = nn.must_have :sampling
  t0, target_time = time_range.begin, time_range.end
  t = t0
  o = self.class.new type: type
  loop do
    o.update t => interpolate( t )
    t += sampling
    return o if t > target_time
  end
end

#series(arg = nil) ⇒ Object

Returns the data series for the specified features.



140
141
142
143
144
# File 'lib/y_petri/net/data_set.rb', line 140

def series arg=nil
  
  return records.transpose if arg.nil?
  reduce_features( State().features( arg ) ).series
end

#timed?Boolean

Type of the dataset.

Returns:

  • (Boolean)


41
42
43
# File 'lib/y_petri/net/data_set.rb', line 41

def timed?
  type == :timed
end

#to_csvObject

Outputs the current recording in CSV format.



259
260
261
# File 'lib/y_petri/net/data_set.rb', line 259

def to_csv
  map { |lbl, rec| [ lbl, *rec ].join ',' }.join "\n"
end

#to_sObject

Returns a string briefly discribing the dataset.



322
323
324
325
326
327
# File 'lib/y_petri/net/data_set.rb', line 322

def to_s
  "#<DataSet: " +
    "#{keys.size} records, " +
    "features: #{features}" +
    ">"
end