Class: SkiftetStatistical::Sampler

Inherits:
Object
  • Object
show all
Defined in:
lib/skiftet_statistical/sampler.rb

Overview

Random sampling used by the stochastic policies (Thompson Sampling, Softmax, Epsilon-Greedy). An injectable RNG (a Random) makes every policy fully deterministic under test — pass rng: Random.new(seed).

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(rng = Random.new) ⇒ Sampler

Returns a new instance of Sampler.



10
11
12
# File 'lib/skiftet_statistical/sampler.rb', line 10

def initialize(rng = Random.new)
  @rng = rng
end

Instance Attribute Details

#rngObject (readonly)

Returns the value of attribute rng.



8
9
10
# File 'lib/skiftet_statistical/sampler.rb', line 8

def rng
  @rng
end

Instance Method Details

#beta(alpha, beta) ⇒ Object

Beta(alpha, beta) drawn as G1 / (G1 + G2) with Gi ~ Gamma(., 1).



42
43
44
45
46
47
# File 'lib/skiftet_statistical/sampler.rb', line 42

def beta(alpha, beta)
  g1 = gamma(alpha)
  g2 = gamma(beta)
  total = g1 + g2
  total.zero? ? 0.5 : g1 / total
end

#gamma(shape) ⇒ Object

Gamma(shape, scale = 1) via Marsaglia–Tsang. Shapes < 1 are handled by the standard boosting identity: Gamma(k) = Gamma(k + 1) * U**(1/k).

Raises:

  • (ArgumentError)


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/skiftet_statistical/sampler.rb', line 23

def gamma(shape)
  raise ArgumentError, "shape must be > 0" unless shape.positive?

  return gamma(shape + 1.0) * (rand_open**(1.0 / shape)) if shape < 1.0

  d = shape - (1.0 / 3.0)
  c = 1.0 / Math.sqrt(9.0 * d)
  loop do
    x = gaussian
    v = (1.0 + (c * x))**3
    next if v <= 0.0

    u = @rng.rand
    return d * v if u < 1.0 - (0.0331 * (x**4))
    return d * v if Math.log(u) < (0.5 * x * x) + (d * (1.0 - v + Math.log(v)))
  end
end

#gaussianObject

Standard normal deviate via Box–Muller.



15
16
17
18
19
# File 'lib/skiftet_statistical/sampler.rb', line 15

def gaussian
  u1 = rand_open
  u2 = @rng.rand
  Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(2.0 * Math::PI * u2)
end