diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index 5bd163d..0000000 --- a/src/error.rs +++ /dev/null @@ -1,14 +0,0 @@ -use thiserror::Error; - -//#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)] -#[derive(Error, Debug, Eq, PartialEq)] -pub enum Error { - #[error("None error")] - Optional, - #[error("Line Algebra error: Equations have no solutions")] - LinalgSolveNoSolutions, - #[error("Line Algebra error: Equations have infinite solutions")] - LinalgSolveInfSolutions, - #[error("Fitting error")] - Fitting, -} diff --git a/src/gaussian.rs b/src/gaussian.rs index e486c05..0dacfcb 100644 --- a/src/gaussian.rs +++ b/src/gaussian.rs @@ -1,4 +1,5 @@ mod deprecated; +mod error; mod gaussian; mod operations; @@ -7,3 +8,6 @@ pub use self::deprecated::*; #[doc(inline)] pub use self::gaussian::Gaussian; + +#[doc(inline)] +pub use self::error::GaussianError; diff --git a/src/gaussian/deprecated.rs b/src/gaussian/deprecated.rs index e2f32b9..aba08cd 100644 --- a/src/gaussian/deprecated.rs +++ b/src/gaussian/deprecated.rs @@ -1,5 +1,5 @@ -use crate::error::Error; use crate::gaussian::operations; +use crate::gaussian::GaussianError; use crate::linalg::Float; use ndarray::Array1; @@ -80,6 +80,6 @@ pub fn val(x: F, mu: F, sigma: F, a: F) -> F { since = "0.4.0", note = "Please use the Gaussian::fit function instead" )] -pub fn fit(x_vec: Array1, y_vec: Array1) -> Result<(F, F, F), Error> { +pub fn fit(x_vec: Array1, y_vec: Array1) -> Result<(F, F, F), GaussianError> { operations::fitting_guos(x_vec, y_vec) } diff --git a/src/gaussian/error.rs b/src/gaussian/error.rs new file mode 100644 index 0000000..50337b6 --- /dev/null +++ b/src/gaussian/error.rs @@ -0,0 +1,16 @@ +use crate::linalg::LinalgError; +use thiserror::Error; + +#[derive(Error, Debug, Eq, PartialEq)] +pub enum GaussianError { + /// Given y_vec contains a negative value + #[error("Given y_vec contains a negative value")] + GivenYVecContainsNegativeValue, + /// Given y_vec contains a negative value + #[error("Given x_vec has no element")] + /// Given x_vec has no element + GivenXVecHasNoElement, + /// Error from [`crate::linalg::LinalgError`] + #[error("Linalg error: {0:?}")] + Linalg(#[from] LinalgError), +} diff --git a/src/gaussian/gaussian.rs b/src/gaussian/gaussian.rs index 9c3c725..8cf5c3f 100644 --- a/src/gaussian/gaussian.rs +++ b/src/gaussian/gaussian.rs @@ -1,7 +1,6 @@ use std::convert::TryFrom; -use crate::error::Error; -use crate::gaussian::operations; +use crate::gaussian::{operations, GaussianError}; use crate::linalg::Float; use approx::{AbsDiffEq, RelativeEq, UlpsEq}; use ndarray::{array, Array1}; @@ -149,7 +148,7 @@ impl Gaussian { /// /// # References /// \[1\] [E. Pastuchov ́a and M. Z ́akopˇcan, ”Comparison of Algorithms for Fitting a Gaussian Function used in Testing Smart Sensors”, Journal of Electrical Engineering, vol. 66, no. 3, pp. 178-181, 2015.](https://www.researchgate.net/publication/281907940_Comparison_of_Algorithms_For_Fitting_a_Gaussian_Function_Used_in_Testing_Smart_Sensors) - pub fn fit(x_vec: Array1, y_vec: Array1) -> Result, Error> { + pub fn fit(x_vec: Array1, y_vec: Array1) -> Result, GaussianError> { let (mu, sigma, a) = operations::fitting_guos(x_vec, y_vec)?; Ok(Gaussian::::new(mu, sigma, a)) } @@ -307,6 +306,15 @@ mod tests { assert_abs_diff_eq!(gaussian, estimated, epsilon = 1e-9); } + #[test] + fn fit_with_negative_value() { + let x_vec: Array1 = array![1., 2., 3.]; + let y_vec: Array1 = 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.); diff --git a/src/gaussian/operations.rs b/src/gaussian/operations.rs index c12ce52..eb5c823 100644 --- a/src/gaussian/operations.rs +++ b/src/gaussian/operations.rs @@ -1,4 +1,4 @@ -use crate::error::Error; +use crate::gaussian::GaussianError; use crate::linalg; use crate::linalg::Float; use ndarray::{array, Array1}; @@ -12,8 +12,11 @@ pub fn values(x_vec: Array1, mu: F, sigma: F, a: F) -> Array1 { } #[allow(dead_code)] -pub fn fitting_caruanas(x_vec: Array1, y_vec: Array1) -> Result<(F, F, F), Error> { - let len_x_vec = F::from(x_vec.len()).ok_or(Error::Optional)?; +pub fn fitting_caruanas( + x_vec: Array1, + y_vec: Array1, +) -> Result<(F, F, F), GaussianError> { + let len_x_vec = F::from(x_vec.len()).ok_or(GaussianError::GivenXVecHasNoElement)?; let sum_x = x_vec.sum(); let sum_x_pow2 = x_vec.iter().map(|x| x.powi(2)).sum(); let sum_x_pow3 = x_vec.iter().map(|x| x.powi(3)).sum(); @@ -47,7 +50,14 @@ pub fn fitting_caruanas(x_vec: Array1, y_vec: Array1) -> Result< Ok((mu, sigma, a)) } -pub fn fitting_guos(x_vec: Array1, y_vec: Array1) -> Result<(F, F, F), Error> { +pub fn fitting_guos( + x_vec: Array1, + y_vec: Array1, +) -> Result<(F, F, F), GaussianError> { + // Guo's algorithm doesn't support negative value in y[] + if y_vec.iter().any(|y| y.is_sign_negative()) { + return Err(GaussianError::GivenYVecContainsNegativeValue); + } let sum_y_pow2: F = y_vec.iter().map(|y| y.powi(2)).sum(); let sum_x_y_pow2 = y_vec .iter() @@ -161,4 +171,14 @@ mod tests { epsilon = 1e-9 ); } + + #[test] + fn gaussian_fit_guos_y_vec_contains_negative_value() { + let (mu, sigma, a): (f64, f64, f64) = (5., 3., 1.); + let x_vec: Array1 = Array::range(1., 10., 1.); + let y_vec: Array1 = values(x_vec.clone(), mu, sigma, a).map(|y| y - 0.5); + + let err = fitting_guos(x_vec, y_vec).unwrap_err(); + assert_eq!(err, GaussianError::GivenYVecContainsNegativeValue); + } } diff --git a/src/lib.rs b/src/lib.rs index 0fe0010..2be15d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,7 +45,6 @@ //! ``` //! -pub mod error; pub mod gaussian; pub mod linalg; diff --git a/src/linalg.rs b/src/linalg.rs index 0f6f7e4..be2c78d 100644 --- a/src/linalg.rs +++ b/src/linalg.rs @@ -1,4 +1,3 @@ -use crate::error::Error; use approx::{abs_diff_eq, abs_diff_ne}; use ndarray::{s, Array1, Array2, Axis}; @@ -9,6 +8,18 @@ use std::iter::Sum; pub trait Float: NdFloat + Sum + AbsDiffEq + RelativeEq + UlpsEq {} impl Float for F {} +use thiserror::Error; + +#[derive(Error, Debug, Eq, PartialEq)] +pub enum LinalgError { + /// Equations have no solutions + #[error("Equations have no solutions")] + EquationsHaveNoSolutions, + /// Equations have infinite solutions + #[error("Equations have infinite solutions")] + EquationsHaveInfSolutions, +} + /// Solves a system of linear equations. /// /// This function implements the Gaussian elimination. @@ -25,7 +36,7 @@ impl Float for F {} /// let x = linalg::solve(a, b).unwrap(); /// assert_abs_diff_eq!(x, array![1., -2., -2.], epsilon = 1e-9); /// ``` -pub fn solve(a: Array2, b: Array1) -> Result, Error> { +pub fn solve(a: Array2, b: Array1) -> Result, LinalgError> { let mut a = a; let mut b = b; @@ -87,12 +98,12 @@ pub fn solve(a: Array2, b: Array1) -> Result, Error> { // no solutions if rank_coef != rank_aug { - return Err(Error::LinalgSolveNoSolutions); + return Err(LinalgError::EquationsHaveNoSolutions); } // infinite solutions if rank_coef != a.ncols() { - return Err(Error::LinalgSolveInfSolutions); + return Err(LinalgError::EquationsHaveInfSolutions); } // backward substitution @@ -179,7 +190,7 @@ mod tests { let a = array![[2., 1., -3., -2.], [2., -1., -1., 3.], [1., -1., -2., 2.]]; let b = array![4., 1., -3.]; let err = solve(a, b).unwrap_err(); //panic - assert_eq!(err, Error::LinalgSolveInfSolutions); + assert_eq!(err, LinalgError::EquationsHaveInfSolutions); } #[test] @@ -195,7 +206,7 @@ mod tests { ]; let b = array![2., -6. / 5., -1., 1.]; let err = solve(a, b).unwrap_err(); //panic - assert_eq!(err, Error::LinalgSolveInfSolutions); + assert_eq!(err, LinalgError::EquationsHaveInfSolutions); } #[test] @@ -206,7 +217,7 @@ mod tests { let a = array![[-2., 3.], [4., 1.], [1., -3.],]; let b = array![1., 5., -1.]; let err = solve(a, b).unwrap_err(); //panic - assert_eq!(err, Error::LinalgSolveNoSolutions); + assert_eq!(err, LinalgError::EquationsHaveNoSolutions); } #[test] @@ -217,6 +228,6 @@ mod tests { let a = array![[1., 3., -2.], [-1., 2., -3.], [2., -1., 3.],]; let b = array![2., -2., 3.]; let err = solve(a, b).unwrap_err(); //panic - assert_eq!(err, Error::LinalgSolveNoSolutions); + assert_eq!(err, LinalgError::EquationsHaveNoSolutions); } }