use crate::linear_algebra;
use crate::matrices::Matrix;
use crate::numeric::extra::{Real, RealRef};
use crate::tensors::views::{TensorRef, TensorView};
use crate::tensors::{Dimension, Tensor};
use std::error::Error;
use std::fmt;
#[derive(Clone, Debug)]
pub struct Gaussian<T: Real> {
pub mean: T,
pub variance: T,
}
impl<T: Real> Gaussian<T> {
pub fn new(mean: T, variance: T) -> Gaussian<T> {
Gaussian { mean, variance }
}
pub fn approximating<I>(data: I) -> Gaussian<T>
where
I: Iterator<Item = T>,
{
let mut copy: Vec<T> = data.collect();
Gaussian {
mean: linear_algebra::mean(copy.iter().cloned()),
variance: linear_algebra::variance(copy.drain(..)),
}
}
}
impl<T: Real> Gaussian<T>
where
for<'a> &'a T: RealRef<T>,
{
pub fn probability(&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()?))
}
#[deprecated(since = "1.1.0", note = "renamed to `probability`")]
pub fn map(&self, x: &T) -> T {
self.probability(x)
}
}
#[derive(Clone, Debug)]
pub struct MultivariateGaussian<T: Real> {
mean: Matrix<T>,
covariance: Matrix<T>,
}
impl<T: 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 }
}
pub fn mean(&self) -> &Matrix<T> {
&self.mean
}
pub fn covariance(&self) -> &Matrix<T> {
&self.covariance
}
}
impl<T: Real> MultivariateGaussian<T>
where
for<'a> &'a T: RealRef<T>,
{
pub fn draw<I>(&self, source: &mut I, max_samples: usize) -> Option<Matrix<T>>
where
I: Iterator<Item = T>,
{
use crate::interop::{DimensionNames, RowAndColumn, TensorRefMatrix};
let mean = crate::tensors::views::TensorIndex::from(
TensorRefMatrix::from(&self.mean).ok()?,
[(RowAndColumn.names()[1], 0)],
);
let covariance = TensorRefMatrix::from(&self.covariance).ok()?;
let samples = draw_tensor_samples::<T, _, _, _>(
&mean,
&covariance,
source,
max_samples,
"samples",
"features",
);
samples.map(|tensor| tensor.into_matrix())
}
}
#[derive(Clone, Debug)]
pub struct MultivariateGaussianTensor<T: Real> {
mean: Tensor<T, 1>,
covariance: Tensor<T, 2>,
}
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq)]
pub enum MultivariateGaussianError<T> {
NotCovarianceMatrix {
mean: Tensor<T, 1>,
covariance: Tensor<T, 2>,
},
MeanVectorWrongLength {
mean: Tensor<T, 1>,
covariance: Tensor<T, 2>,
},
}
impl<T: fmt::Debug> fmt::Display for MultivariateGaussianError<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MultivariateGaussianError::NotCovarianceMatrix {
mean: _,
covariance,
} => write!(f, "Covariance matrix is not square: {:?}", covariance,),
MultivariateGaussianError::MeanVectorWrongLength { mean, covariance } => write!(
f,
"Mean vector has a different length {:?} to the covariance matrix size: {:?}",
mean.shape(),
covariance.shape(),
),
}
}
}
impl<T: fmt::Debug> Error for MultivariateGaussianError<T> {}
impl<T: Real> MultivariateGaussianTensor<T> {
pub fn new(
mean: Tensor<T, 1>,
covariance: Tensor<T, 2>,
) -> Result<MultivariateGaussianTensor<T>, Box<MultivariateGaussianError<T>>> {
let covariance_shape = covariance.shape();
if !crate::tensors::dimensions::is_square(&covariance_shape) {
return Err(Box::new(MultivariateGaussianError::NotCovarianceMatrix {
mean,
covariance,
}));
}
let length = covariance_shape[0].1;
if mean.shape()[0].1 != length {
return Err(Box::new(MultivariateGaussianError::MeanVectorWrongLength {
mean,
covariance,
}));
}
Ok(MultivariateGaussianTensor { mean, covariance })
}
pub fn mean(&self) -> &Tensor<T, 1> {
&self.mean
}
pub fn covariance(&self) -> &Tensor<T, 2> {
&self.covariance
}
}
fn draw_tensor_samples<T, S1, S2, I>(
mean: S1,
covariance: S2,
source: &mut I,
max_samples: usize,
samples: Dimension,
features: Dimension,
) -> Option<Tensor<T, 2>>
where
T: Real,
for<'a> &'a T: RealRef<T>,
I: Iterator<Item = T>,
S1: TensorRef<T, 1>,
S2: TensorRef<T, 2>,
{
if samples == features {
return None;
}
let mean = TensorView::from(mean);
let covariance = TensorView::from(covariance);
use linear_algebra::cholesky_decomposition_tensor as cholesky_decomposition;
let normal_distribution = Gaussian::new(T::zero(), T::one());
let mut lower_triangular = cholesky_decomposition::<T, _, _>(&covariance)?;
lower_triangular.rename([samples, features]);
let number_of_samples = max_samples;
let number_of_features = mean.shape()[0].1;
let shape = [(samples, max_samples), (features, number_of_features)];
let mut drawn_samples = Tensor::empty(shape, T::zero());
let column_vector_mean = mean.rename_view([samples]).expand_owned([(1, features)]);
let mut drawn_samples_iterator = drawn_samples.iter_reference_mut();
for _sample_row in 0..number_of_samples {
let standard_normals = normal_distribution.draw(source, number_of_features)?;
let standard_normals = Tensor::from(
[(samples, standard_normals.len()), (features, 1)],
standard_normals,
);
let random_vector = &column_vector_mean + (&lower_triangular * standard_normals);
for x in random_vector.iter() {
*drawn_samples_iterator.next()? = x;
}
}
Some(drawn_samples)
}
impl<T: Real> MultivariateGaussianTensor<T>
where
for<'a> &'a T: RealRef<T>,
{
pub fn draw<I>(
&self,
source: &mut I,
max_samples: usize,
samples: Dimension,
features: Dimension,
) -> Option<Tensor<T, 2>>
where
I: Iterator<Item = T>,
{
draw_tensor_samples::<T, _, _, _>(
&self.mean,
&self.covariance,
source,
max_samples,
samples,
features,
)
}
}