use self::GammaRepr::*;
use self::ChiSquaredRepr::*;
use Rng;
use distributions::normal::StandardNormal;
use distributions::{Distribution, Exp, Open01};
#[derive(Clone, Copy, Debug)]
pub struct Gamma {
repr: GammaRepr,
}
#[derive(Clone, Copy, Debug)]
enum GammaRepr {
Large(GammaLargeShape),
One(Exp),
Small(GammaSmallShape)
}
#[derive(Clone, Copy, Debug)]
struct GammaSmallShape {
inv_shape: f64,
large_shape: GammaLargeShape
}
#[derive(Clone, Copy, Debug)]
struct GammaLargeShape {
scale: f64,
c: f64,
d: f64
}
impl Gamma {
#[inline]
pub fn new(shape: f64, scale: f64) -> Gamma {
assert!(shape > 0.0, "Gamma::new called with shape <= 0");
assert!(scale > 0.0, "Gamma::new called with scale <= 0");
let repr = if shape == 1.0 {
One(Exp::new(1.0 / scale))
} else if shape < 1.0 {
Small(GammaSmallShape::new_raw(shape, scale))
} else {
Large(GammaLargeShape::new_raw(shape, scale))
};
Gamma { repr }
}
}
impl GammaSmallShape {
fn new_raw(shape: f64, scale: f64) -> GammaSmallShape {
GammaSmallShape {
inv_shape: 1. / shape,
large_shape: GammaLargeShape::new_raw(shape + 1.0, scale)
}
}
}
impl GammaLargeShape {
fn new_raw(shape: f64, scale: f64) -> GammaLargeShape {
let d = shape - 1. / 3.;
GammaLargeShape {
scale,
c: 1. / (9. * d).sqrt(),
d
}
}
}
impl Distribution<f64> for Gamma {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
match self.repr {
Small(ref g) => g.sample(rng),
One(ref g) => g.sample(rng),
Large(ref g) => g.sample(rng),
}
}
}
impl Distribution<f64> for GammaSmallShape {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
let u: f64 = rng.sample(Open01);
self.large_shape.sample(rng) * u.powf(self.inv_shape)
}
}
impl Distribution<f64> for GammaLargeShape {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
loop {
let x = rng.sample(StandardNormal);
let v_cbrt = 1.0 + self.c * x;
if v_cbrt <= 0.0 { continue
}
let v = v_cbrt * v_cbrt * v_cbrt;
let u: f64 = rng.sample(Open01);
let x_sqr = x * x;
if u < 1.0 - 0.0331 * x_sqr * x_sqr ||
u.ln() < 0.5 * x_sqr + self.d * (1.0 - v + v.ln()) {
return self.d * v * self.scale
}
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct ChiSquared {
repr: ChiSquaredRepr,
}
#[derive(Clone, Copy, Debug)]
enum ChiSquaredRepr {
DoFExactlyOne,
DoFAnythingElse(Gamma),
}
impl ChiSquared {
pub fn new(k: f64) -> ChiSquared {
let repr = if k == 1.0 {
DoFExactlyOne
} else {
assert!(k > 0.0, "ChiSquared::new called with `k` < 0");
DoFAnythingElse(Gamma::new(0.5 * k, 2.0))
};
ChiSquared { repr }
}
}
impl Distribution<f64> for ChiSquared {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
match self.repr {
DoFExactlyOne => {
let norm = rng.sample(StandardNormal);
norm * norm
}
DoFAnythingElse(ref g) => g.sample(rng)
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct FisherF {
numer: ChiSquared,
denom: ChiSquared,
dof_ratio: f64,
}
impl FisherF {
pub fn new(m: f64, n: f64) -> FisherF {
assert!(m > 0.0, "FisherF::new called with `m < 0`");
assert!(n > 0.0, "FisherF::new called with `n < 0`");
FisherF {
numer: ChiSquared::new(m),
denom: ChiSquared::new(n),
dof_ratio: n / m
}
}
}
impl Distribution<f64> for FisherF {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
self.numer.sample(rng) / self.denom.sample(rng) * self.dof_ratio
}
}
#[derive(Clone, Copy, Debug)]
pub struct StudentT {
chi: ChiSquared,
dof: f64
}
impl StudentT {
pub fn new(n: f64) -> StudentT {
assert!(n > 0.0, "StudentT::new called with `n <= 0`");
StudentT {
chi: ChiSquared::new(n),
dof: n
}
}
}
impl Distribution<f64> for StudentT {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
let norm = rng.sample(StandardNormal);
norm * (self.dof / self.chi.sample(rng)).sqrt()
}
}
#[derive(Clone, Copy, Debug)]
pub struct Beta {
gamma_a: Gamma,
gamma_b: Gamma,
}
impl Beta {
pub fn new(alpha: f64, beta: f64) -> Beta {
assert!((alpha > 0.) & (beta > 0.));
Beta {
gamma_a: Gamma::new(alpha, 1.),
gamma_b: Gamma::new(beta, 1.),
}
}
}
impl Distribution<f64> for Beta {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 {
let x = self.gamma_a.sample(rng);
let y = self.gamma_b.sample(rng);
x / (x + y)
}
}
#[cfg(test)]
mod test {
use distributions::Distribution;
use super::{Beta, ChiSquared, StudentT, FisherF};
#[test]
fn test_chi_squared_one() {
let chi = ChiSquared::new(1.0);
let mut rng = ::test::rng(201);
for _ in 0..1000 {
chi.sample(&mut rng);
}
}
#[test]
fn test_chi_squared_small() {
let chi = ChiSquared::new(0.5);
let mut rng = ::test::rng(202);
for _ in 0..1000 {
chi.sample(&mut rng);
}
}
#[test]
fn test_chi_squared_large() {
let chi = ChiSquared::new(30.0);
let mut rng = ::test::rng(203);
for _ in 0..1000 {
chi.sample(&mut rng);
}
}
#[test]
#[should_panic]
fn test_chi_squared_invalid_dof() {
ChiSquared::new(-1.0);
}
#[test]
fn test_f() {
let f = FisherF::new(2.0, 32.0);
let mut rng = ::test::rng(204);
for _ in 0..1000 {
f.sample(&mut rng);
}
}
#[test]
fn test_t() {
let t = StudentT::new(11.0);
let mut rng = ::test::rng(205);
for _ in 0..1000 {
t.sample(&mut rng);
}
}
#[test]
fn test_beta() {
let beta = Beta::new(1.0, 2.0);
let mut rng = ::test::rng(201);
for _ in 0..1000 {
beta.sample(&mut rng);
}
}
#[test]
#[should_panic]
fn test_beta_invalid_dof() {
Beta::new(0., 0.);
}
}