use std::convert::TryFrom;
use crate::gaussian::{operations, GaussianError};
use crate::linalg::Float;
use approx::{AbsDiffEq, RelativeEq, UlpsEq};
use ndarray::{array, Array1};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Hash, Debug, Default, Serialize, Deserialize)]
pub struct Gaussian<F: Float> {
mu: F,
sigma: F,
a: F,
}
impl<F: Float> Gaussian<F> {
pub fn new(mu: F, sigma: F, a: F) -> Gaussian<F> {
Gaussian { mu, sigma, a }
}
pub fn mu(&self) -> &F {
&self.mu
}
pub fn mu_mut(&mut self) -> &mut F {
&mut self.mu
}
pub fn sigma(&self) -> &F {
&self.sigma
}
pub fn sigma_mut(&mut self) -> &mut F {
&mut self.sigma
}
pub fn a(&self) -> &F {
&self.a
}
pub fn a_mut(&mut self) -> &mut F {
&mut self.a
}
pub fn as_tuple(&self) -> (&F, &F, &F) {
(&self.mu, &self.sigma, &self.a)
}
pub fn as_mut_tuple(&mut self) -> (&mut F, &mut F, &mut F) {
(&mut self.mu, &mut self.sigma, &mut self.a)
}
pub fn to_tuple(&self) -> (F, F, F) {
(self.mu, self.sigma, self.a)
}
pub fn into_tuple(self) -> (F, F, F) {
(self.mu, self.sigma, self.a)
}
pub fn value(&self, x: F) -> F {
operations::value(x, self.mu, self.sigma, self.a)
}
pub fn values(&self, x_vec: Array1<F>) -> Array1<F> {
operations::values(x_vec, self.mu, self.sigma, self.a)
}
pub fn fit(x_vec: Array1<F>, y_vec: Array1<F>) -> Result<Gaussian<F>, GaussianError> {
let (mu, sigma, a) = operations::fitting_guos(x_vec, y_vec)?;
Ok(Gaussian::<F>::new(mu, sigma, a))
}
}
impl<F: Float> From<(F, F, F)> for Gaussian<F> {
fn from(tuple: (F, F, F)) -> Self {
Gaussian::new(tuple.0, tuple.1, tuple.2)
}
}
impl<F: Float> From<Gaussian<F>> for (F, F, F) {
fn from(gaussian: Gaussian<F>) -> Self {
(gaussian.mu, gaussian.sigma, gaussian.a)
}
}
impl<F: Float> TryFrom<Array1<F>> for Gaussian<F> {
type Error = &'static str;
fn try_from(arr: Array1<F>) -> Result<Self, Self::Error> {
let error = "The index out of bounds.";
Ok(Gaussian::new(
*arr.get(0).ok_or(error)?,
*arr.get(1).ok_or(error)?,
*arr.get(2).ok_or(error)?,
))
}
}
impl<F: Float> From<Gaussian<F>> for Array1<F> {
fn from(gaussian: Gaussian<F>) -> Self {
array![gaussian.mu, gaussian.sigma, gaussian.a]
}
}
impl<F: Float> AbsDiffEq for Gaussian<F>
where
F::Epsilon: Copy,
{
type Epsilon = F::Epsilon;
fn default_epsilon() -> F::Epsilon {
F::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, epsilon: F::Epsilon) -> bool {
F::abs_diff_eq(&self.mu, &other.mu, epsilon)
&& F::abs_diff_eq(&self.sigma, &other.sigma, epsilon)
&& F::abs_diff_eq(&self.a, &other.a, epsilon)
}
}
impl<F: Float> RelativeEq for Gaussian<F>
where
F::Epsilon: Copy,
{
fn default_max_relative() -> F::Epsilon {
F::default_max_relative()
}
fn relative_eq(&self, other: &Self, epsilon: F::Epsilon, max_relative: F::Epsilon) -> bool {
F::relative_eq(&self.mu, &other.mu, epsilon, max_relative)
&& F::relative_eq(&self.sigma, &other.sigma, epsilon, max_relative)
&& F::relative_eq(&self.a, &other.a, epsilon, max_relative)
}
}
impl<F: Float> UlpsEq for Gaussian<F>
where
F::Epsilon: Copy,
{
fn default_max_ulps() -> u32 {
F::default_max_ulps()
}
fn ulps_eq(&self, other: &Self, epsilon: F::Epsilon, max_ulps: u32) -> bool {
F::ulps_eq(&self.mu, &other.mu, epsilon, max_ulps)
&& F::ulps_eq(&self.sigma, &other.sigma, epsilon, max_ulps)
&& F::ulps_eq(&self.a, &other.a, epsilon, max_ulps)
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_abs_diff_eq;
use ndarray::Array;
use std::convert::TryInto;
#[test]
fn constructor() {
let (mu, sigma, a): (f64, f64, f64) = (1., 2., 3.);
let gaussian = Gaussian::new(mu, sigma, a);
assert_eq!(gaussian.mu, mu);
assert_eq!(gaussian.sigma, sigma);
assert_eq!(gaussian.a, a);
}
#[test]
fn getter() {
let (mu, sigma, a): (f64, f64, f64) = (1., 2., 3.);
let gaussian = Gaussian { mu, sigma, a };
assert_eq!(gaussian.mu(), &mu);
assert_eq!(gaussian.sigma(), &sigma);
assert_eq!(gaussian.a(), &a);
}
#[test]
fn setter() {
let (mu, sigma, a): (f64, f64, f64) = (1., 2., 3.);
let mut gaussian = Gaussian { mu, sigma, a };
let (mu, sigma, a): (f64, f64, f64) = (4., 5., 6.);
*gaussian.mu_mut() = mu;
*gaussian.sigma_mut() = sigma;
*gaussian.a_mut() = a;
assert_eq!(gaussian.mu, mu);
assert_eq!(gaussian.sigma, sigma);
assert_eq!(gaussian.a, a);
}
#[test]
fn value() {
let gaussian = Gaussian::new(5., 3., 1.);
let x = 5.;
let y = gaussian.value(x);
assert_eq!(&y, gaussian.a());
}
#[test]
fn values() {
let gaussian = Gaussian::new(5., 3., 1.);
let x_vec: Array1<f64> = Array::range(1., 10., 1.);
let y_vec: Array1<f64> = gaussian.values(x_vec);
let expected_ans = array![
0.41111229050718745,
0.6065306597126334,
0.8007374029168081,
0.9459594689067654,
1.,
0.9459594689067654,
0.8007374029168081,
0.6065306597126334,
0.41111229050718745
];
assert_abs_diff_eq!(&y_vec, &expected_ans, epsilon = 1e-9);
}
#[test]
fn fit() {
let gaussian = Gaussian::new(5., 3., 1.);
let x_vec: Array1<f64> = Array::range(1., 10., 1.);
let y_vec: Array1<f64> = gaussian.values(x_vec.clone());
let estimated = Gaussian::fit(x_vec, y_vec).unwrap();
assert_abs_diff_eq!(gaussian, estimated, epsilon = 1e-9);
}
#[test]
fn fit_with_negative_value() {
let x_vec: Array1<f64> = array![1., 2., 3.];
let y_vec: Array1<f64> = array![-1., 1., -1.];
let err = Gaussian::fit(x_vec, y_vec).unwrap_err();
assert_eq!(err, GaussianError::GivenYVecContainsNegativeValue);
}
#[test]
fn as_tuple() {
let (mu, sigma, a): (f64, f64, f64) = (1., 2., 3.);
let gaussian = Gaussian { mu, sigma, a };
assert_eq!(gaussian.as_tuple(), (&mu, &sigma, &a));
}
#[test]
fn as_mut_tuple() {
let (mu, sigma, a): (f64, f64, f64) = (1., 2., 3.);
let mut gaussian = Gaussian { mu, sigma, a };
let (mu, sigma, a): (f64, f64, f64) = (4., 5., 6.);
let (mu_mut, sigma_mut, a_mut) = gaussian.as_mut_tuple();
*mu_mut = mu;
*sigma_mut = sigma;
*a_mut = a;
assert_eq!(gaussian.mu, mu);
assert_eq!(gaussian.sigma, sigma);
assert_eq!(gaussian.a, a);
}
#[test]
fn to_tuple() {
let (mu, sigma, a): (f64, f64, f64) = (1., 2., 3.);
let gaussian = Gaussian { mu, sigma, a };
assert_eq!(gaussian.to_tuple(), (mu, sigma, a));
}
#[test]
fn into_tuple() {
let (mu, sigma, a): (f64, f64, f64) = (1., 2., 3.);
let gaussian = Gaussian { mu, sigma, a };
assert_eq!(gaussian.into_tuple(), (mu, sigma, a));
}
#[test]
fn from_into_tuple() {
let input: (f64, f64, f64) = (1., 2., 3.);
let gaussian = Gaussian::from(input);
assert_eq!(input, (gaussian.mu, gaussian.sigma, gaussian.a));
let output: (_, _, _) = gaussian.into();
assert_eq!(input, output);
let gaussian: Gaussian<_> = input.into();
assert_eq!(input, (gaussian.mu, gaussian.sigma, gaussian.a));
}
#[test]
fn from_into_ndarray1() {
let input = array![1., 2., 3.];
let gaussian = Gaussian::try_from(input.clone()).unwrap();
assert_eq!(input, array![gaussian.mu, gaussian.sigma, gaussian.a]);
let input_failure = array![1.,];
Gaussian::try_from(input_failure).unwrap_err();
let output: Array1<_> = gaussian.into();
assert_eq!(input, output);
let output = Array1::from(gaussian);
assert_eq!(input, output);
let gaussian: Gaussian<_> = input.clone().try_into().unwrap();
assert_eq!(input, array![gaussian.mu, gaussian.sigma, gaussian.a]);
let input_failure = array![1.,];
let result: Result<Gaussian<_>, _> = input_failure.try_into();
result.unwrap_err();
}
}