use crate::numeric::{Numeric, NumericRef};
use crate::numeric::extra::{Real, RealRef};
use crate::matrices::Matrix;
use crate::linear_algebra;
pub struct Gaussian<T: Numeric + Real> {
pub mean: T,
pub variance: T
}
impl <T: Numeric + Real> Gaussian<T> {
pub fn new(mean: T, variance: T) -> Gaussian<T> {
Gaussian {
mean,
variance,
}
}
}
impl <T: Numeric + Real> Gaussian<T>
where for<'a> &'a T: NumericRef<T> + RealRef<T> {
pub fn map(&self, x: &T) -> T {
let standard_deviation = self.variance.clone().sqrt();
let two = T::one() + T::one();
let two_pi = &two * T::pi();
let fraction = T::one() / (standard_deviation * (&two_pi.sqrt()));
let exponent = (- T::one() / &two) * ((x - &self.mean) / &self.variance).pow(&two);
fraction * exponent.exp()
}
pub fn draw<I>(&self, source: &mut I, max_samples: usize) -> Option<Vec<T>>
where I: Iterator<Item = T> {
let two = T::one() + T::one();
let minus_two = - &two;
let two_pi = &two * T::pi();
let mut samples = Vec::with_capacity(max_samples);
let standard_deviation = self.variance.clone().sqrt();
while samples.len() < max_samples {
let (u, v) = self.generate_pair(source)?;
let z1 = (&minus_two * u.clone().ln()).sqrt() * ((&two_pi * &v).cos());
let z2 = (&minus_two * u.clone().ln()).sqrt() * ((&two_pi * &v).sin());
let sample1 = (z1 * &standard_deviation) + &self.mean;
let sample2 = (z2 * &standard_deviation) + &self.mean;
samples.push(sample1);
samples.push(sample2);
}
if samples.len() > max_samples {
samples.pop();
return Some(samples);
}
Some(samples)
}
fn generate_pair<I>(&self, source: &mut I) -> Option<(T, T)>
where I: Iterator<Item = T> {
Some((source.next()?, source.next()?))
}
}
pub struct MultivariateGaussian<T: Numeric + Real> {
pub mean: Matrix<T>,
pub covariance: Matrix<T>
}
impl <T: Numeric + Real> MultivariateGaussian<T> {
pub fn new(mean: Matrix<T>, covariance: Matrix<T>) -> MultivariateGaussian<T> {
assert!(mean.columns() == 1, "Mean must be a column vector");
assert!(covariance.rows() == covariance.columns(), "Supplied 'covariance' matrix is not square");
assert!(mean.rows() == covariance.rows(), "Means must be same length as covariance matrix");
MultivariateGaussian {
mean,
covariance,
}
}
}
impl <T: Numeric + Real> MultivariateGaussian<T>
where for<'a> &'a T: NumericRef<T> + RealRef<T> {
pub fn draw<I>(&self, source: &mut I, max_samples: usize) -> Option<Matrix<T>>
where I: Iterator<Item = T> {
let normal_distribution = Gaussian::new(T::zero(), T::one());
let lower_triangular = linear_algebra::cholesky_decomposition(&self.covariance)?;
let mut samples = Matrix::empty(T::zero(), (max_samples, self.mean.rows()));
for row in 0..samples.rows() {
let standard_normals = normal_distribution.draw(source, self.mean.rows())?;
let random_vector = &self.mean + (&lower_triangular * Matrix::column(standard_normals));
for x in 0..random_vector.rows() {
samples.set(row, x, random_vector.get(x, 0));
}
}
Some(samples)
}
}