use crate::distributions::Distribution;
use num::Complex;
use std::f64::consts::{E, PI};
use RustQuant_error::RustQuantError;
pub struct Binomial {
n: usize,
p: f64,
}
impl Default for Binomial {
fn default() -> Self {
Self { n: 1, p: 0.5 }
}
}
impl Binomial {
#[must_use]
pub fn new(trials: usize, probability: f64) -> Self {
assert!((0.0..=1.0).contains(&probability));
Self {
n: trials,
p: probability,
}
}
}
impl Distribution for Binomial {
fn cf(&self, t: f64) -> Complex<f64> {
assert!((0.0..=1.0).contains(&self.p));
let i: Complex<f64> = Complex::i();
(1.0 - self.p + self.p * (i * t).exp()).powi(self.n as i32)
}
fn pdf(&self, x: f64) -> f64 {
self.pmf(x)
}
fn pmf(&self, k: f64) -> f64 {
assert!(k as usize <= self.n);
assert!((0.0..=1.0).contains(&self.p));
let n_C_k = |n: u32, k: u32| -> u32 {
(1..=n).product::<u32>() / ((1..=k).product::<u32>() * (1..=(n - k)).product::<u32>())
};
n_C_k(self.n as u32, k as u32) as f64
* self.p.powi(k as i32)
* (1.0 - self.p).powi((self.n - k as usize) as i32)
}
fn cdf(&self, k: f64) -> f64 {
statrs::function::beta::beta_reg((self.n - k as usize) as f64, 1_f64 + k, 1_f64 - self.p)
}
fn inv_cdf(&self, p: f64) -> f64 {
assert!((0.0..=1.0).contains(&p));
let mut k = 0.0;
let mut cdf = self.cdf(k);
while cdf < p {
k += 1.0;
cdf = self.cdf(k);
}
k
}
fn mean(&self) -> f64 {
self.n as f64 * self.p
}
fn median(&self) -> f64 {
self.mean().floor()
}
fn mode(&self) -> f64 {
((self.n as f64 + 1.) * self.p).floor()
}
fn variance(&self) -> f64 {
self.n as f64 * self.p * (1. - self.p)
}
fn skewness(&self) -> f64 {
(1. - 2. * self.p) / self.variance().sqrt()
}
fn kurtosis(&self) -> f64 {
(1. - 6. * self.p * (1. - self.p)) / self.variance()
}
fn entropy(&self) -> f64 {
0.5 * (2. * PI * E * self.variance()).ln()
}
fn mgf(&self, t: f64) -> f64 {
((1. - self.p) + self.p * t.exp()).powi(self.n as i32)
}
fn sample(&self, n: usize) -> Result<Vec<f64>, RustQuantError> {
use rand::thread_rng;
use rand_distr::{Binomial, Distribution};
assert!(n > 0);
let mut rng = thread_rng();
let dist = Binomial::new(n as u64, self.p)?;
let mut variates: Vec<f64> = Vec::with_capacity(n);
for _ in 0..variates.capacity() {
variates.push(dist.sample(&mut rng) as f64);
}
Ok(variates)
}
}
#[cfg(test)]
mod tests_binomial_distribution {
use super::*;
use RustQuant_utils::{assert_approx_equal, RUSTQUANT_EPSILON as EPS};
#[test]
fn test_binomial_distribution() {
let dist: Binomial = Binomial::new(2, 0.5);
let cf = dist.cf(1.0);
assert_approx_equal!(cf.re, 0.416_114_443_797_284_35, EPS);
assert_approx_equal!(cf.im, 0.648_059_849_110_368_7, EPS);
let pmf = dist.pmf(1.);
assert_approx_equal!(pmf, 0.5, EPS);
let cdf = dist.cdf(1.);
assert_approx_equal!(cdf, 0.749_999_999_999_999_1, EPS);
}
#[test]
fn test_binomial_functions() {
let binomial = Binomial::new(5, 0.4);
let cf = binomial.cf(1.0);
assert_approx_equal!(cf.re, -0.201_403_389_549_595_36, EPS);
assert_approx_equal!(cf.im, 0.496_934_703_617_956_4, EPS);
let pmf = binomial.pmf(3.0);
assert_approx_equal!(pmf, 0.2304, EPS);
let cdf = binomial.cdf(3.0);
assert_approx_equal!(cdf, 0.91296, EPS);
assert_approx_equal!(binomial.mean(), 2.0, EPS);
assert_approx_equal!(binomial.median(), 2.0, EPS);
assert_approx_equal!(binomial.mode(), 2.0, EPS);
assert_approx_equal!(binomial.variance(), 1.2, 1e-6);
assert_approx_equal!(binomial.skewness(), 0.182_574_185_835_055_33, EPS);
assert_approx_equal!(binomial.kurtosis(), -0.366_666_666_666_666_8, EPS);
assert_approx_equal!(binomial.entropy(), 1.510_099_311_601_65, EPS);
assert_approx_equal!(binomial.mgf(1.0), 13.676_592_816_585_314, EPS);
}
}