Class: Drone::ExponentiallyDecayingSample

Inherits:
Object
  • Object
show all
Defined in:
lib/drone/utils/exponentially_decaying_sample.rb

Overview

An exponentially-decaying random sample

Constant Summary collapse

RESCALE_THRESHOLD =

1 hour

(1 * 60 * 60).freeze

Instance Method Summary collapse

Constructor Details

#initialize(id, reservoir_size, alpha) ⇒ ExponentiallyDecayingSample

Create a new dataset, if the decay factor is too big the flt ruby library is used for internal computations to allow greater precision, the performance impact should be minimal.

Parameters:

  • id (String)

    A unique id representing this dataset.

  • reservoir_size (Integer)

    the number of samples to keep.

  • alpha (Number)

    the decay factor, the higher this number, the more biased the sample will be towards newer values.



30
31
32
33
34
35
36
37
38
39
# File 'lib/drone/utils/exponentially_decaying_sample.rb', line 30

def initialize(id, reservoir_size, alpha)
  @id = id
  @values = Drone::request_hash("#{@id}:values")
  @count = Drone::request_number("#{@id}:count", 0)
  @start_time = Drone::request_number("#{@id}:start_time", current_time())
  @next_scale_time = Drone::request_number("#{@id}:next_scale_time", current_time() + RESCALE_THRESHOLD)
  
  @alpha = alpha
  @reservoir_size = reservoir_size
end

Instance Method Details

#clearObject



41
42
43
44
45
46
# File 'lib/drone/utils/exponentially_decaying_sample.rb', line 41

def clear
  @values.clear()
  @count.set(0)
  @start_time.set(current_time())
  @next_scale_time.set( current_time() + RESCALE_THRESHOLD )
end

#rescale(now, next_scale) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/drone/utils/exponentially_decaying_sample.rb', line 84

def rescale(now, next_scale)
  if @next_scale_time.compare_and_set(next_scale, now + RESCALE_THRESHOLD)
    new_start = current_time()
    old_start = @start_time.get_and_set( new_start )
    time_diff = new_start - old_start
    
    coeff = math_exp(-@alpha * time_diff)
    
    @values = Hash[ @values.map{ |k,v|
        [k * coeff, v]
      }]
    
  end
end

#sizeObject



48
49
50
51
# File 'lib/drone/utils/exponentially_decaying_sample.rb', line 48

def size
  count =  @count.get
  (@values.size < count) ? @values.size : count
end

#update(val, time = current_time) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/drone/utils/exponentially_decaying_sample.rb', line 53

def update(val, time = current_time)
  priority = weight(time - @start_time.get) / generate_random()
  new_count = @count.inc
  
  if new_count <= @reservoir_size
    @values[priority] = val
  else
    first = @values.keys[0]
    if first < priority
      old_val, @values[priority] = @values[priority], val
      unless old_val
        while @values.delete(first) == nil
          first = @values.keys[0]
        end
      end
    end
  end

  now = current_time()
  next_scale = @next_scale_time.get
  if now >= next_scale
    rescale(now, next_scale)
  end
end

#valuesObject



78
79
80
81
82
# File 'lib/drone/utils/exponentially_decaying_sample.rb', line 78

def values
  @values.keys.sort.inject([]) do |buff, key|
    buff << @values[key]
  end
end