From d06b9359f62afd666e28bdc0ceb5ddbf679db2db Mon Sep 17 00:00:00 2001 From: Jim Turner Date: Mon, 14 Jan 2019 15:07:45 -0500 Subject: [PATCH] Feature-gate approx trait implementations --- Cargo.toml | 5 +- src/array_approx.rs | 157 ++++++++++++++++++++++++++++++++++++++++++++ src/arraytraits.rs | 86 ------------------------ src/lib.rs | 9 ++- tests/array.rs | 59 ----------------- 5 files changed, 168 insertions(+), 148 deletions(-) create mode 100644 src/array_approx.rs diff --git a/Cargo.toml b/Cargo.toml index ccf4faf04..b1394aa75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,6 @@ bench = false test = true [dependencies] -approx = "0.3" num-integer = "0.1.39" num-traits = "0.2" num-complex = "0.2" @@ -36,6 +35,8 @@ itertools = { version = "0.7.0", default-features = false } rayon = { version = "1.0.3", optional = true } +approx = { version = "0.3", optional = true } + # Use via the `blas` crate feature! cblas-sys = { version = "0.1.4", optional = true, default-features = false } blas-src = { version = "0.2.0", optional = true, default-features = false } @@ -62,7 +63,7 @@ test-blas-openblas-sys = ["blas"] test = ["test-blas-openblas-sys"] # This feature is used for docs -docs = ["serde-1", "rayon"] +docs = ["approx", "serde-1", "rayon"] [profile.release] [profile.bench] diff --git a/src/array_approx.rs b/src/array_approx.rs new file mode 100644 index 000000000..f8adeb09e --- /dev/null +++ b/src/array_approx.rs @@ -0,0 +1,157 @@ +use crate::imp_prelude::*; +use crate::{FoldWhile, Zip}; +use approx::{AbsDiffEq, RelativeEq, UlpsEq}; + +/// **Requires crate feature `"approx"`** +impl AbsDiffEq for ArrayBase +where + A: AbsDiffEq, + A::Epsilon: Clone, + S: Data, + D: Dimension, +{ + type Epsilon = A::Epsilon; + + fn default_epsilon() -> A::Epsilon { + A::default_epsilon() + } + + fn abs_diff_eq(&self, other: &ArrayBase, epsilon: A::Epsilon) -> bool { + if self.shape() != other.shape() { + return false; + } + Zip::from(self) + .and(other) + .fold_while(true, |_, a, b| { + if A::abs_diff_ne(a, b, epsilon.clone()) { + FoldWhile::Done(false) + } else { + FoldWhile::Continue(true) + } + }) + .into_inner() + } +} + +/// **Requires crate feature `"approx"`** +impl RelativeEq for ArrayBase +where + A: RelativeEq, + A::Epsilon: Clone, + S: Data, + D: Dimension, +{ + fn default_max_relative() -> A::Epsilon { + A::default_max_relative() + } + + fn relative_eq( + &self, + other: &ArrayBase, + epsilon: A::Epsilon, + max_relative: A::Epsilon, + ) -> bool { + if self.shape() != other.shape() { + return false; + } + Zip::from(self) + .and(other) + .fold_while(true, |_, a, b| { + if A::relative_ne(a, b, epsilon.clone(), max_relative.clone()) { + FoldWhile::Done(false) + } else { + FoldWhile::Continue(true) + } + }) + .into_inner() + } +} + +/// **Requires crate feature `"approx"`** +impl UlpsEq for ArrayBase +where + A: UlpsEq, + A::Epsilon: Clone, + S: Data, + D: Dimension, +{ + fn default_max_ulps() -> u32 { + A::default_max_ulps() + } + + fn ulps_eq(&self, other: &ArrayBase, epsilon: A::Epsilon, max_ulps: u32) -> bool { + if self.shape() != other.shape() { + return false; + } + Zip::from(self) + .and(other) + .fold_while(true, |_, a, b| { + if A::ulps_ne(a, b, epsilon.clone(), max_ulps) { + FoldWhile::Done(false) + } else { + FoldWhile::Continue(true) + } + }) + .into_inner() + } +} + +#[cfg(test)] +mod tests { + use crate::prelude::*; + use approx::{ + assert_abs_diff_eq, assert_abs_diff_ne, assert_relative_eq, assert_relative_ne, + assert_ulps_eq, assert_ulps_ne, + }; + + #[test] + fn abs_diff_eq() { + let a: Array2 = array![[0., 2.], [-0.000010001, 100000000.]]; + let mut b: Array2 = array![[0., 1.], [-0.000010002, 100000001.]]; + assert_abs_diff_ne!(a, b); + b[(0, 1)] = 2.; + assert_abs_diff_eq!(a, b); + + // Check epsilon. + assert_abs_diff_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32); + assert_abs_diff_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32); + + // Make sure we can compare different shapes without failure. + let c = array![[1., 2.]]; + assert_abs_diff_ne!(a, c); + } + + #[test] + fn relative_eq() { + let a: Array2 = array![[1., 2.], [-0.000010001, 100000000.]]; + let mut b: Array2 = array![[1., 1.], [-0.000010002, 100000001.]]; + assert_relative_ne!(a, b); + b[(0, 1)] = 2.; + assert_relative_eq!(a, b); + + // Check epsilon. + assert_relative_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32); + assert_relative_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32); + + // Make sure we can compare different shapes without failure. + let c = array![[1., 2.]]; + assert_relative_ne!(a, c); + } + + #[test] + fn ulps_eq() { + let a: Array2 = array![[1., 2.], [-0.000010001, 100000000.]]; + let mut b: Array2 = array![[1., 1.], [-0.000010002, 100000001.]]; + assert_ulps_ne!(a, b); + b[(0, 1)] = 2.; + assert_ulps_eq!(a, b); + + // Check epsilon. + assert_ulps_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32); + assert_ulps_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32); + + // Make sure we can compare different shapes without failure. + let c = array![[1., 2.]]; + assert_ulps_ne!(a, c); + } +} diff --git a/src/arraytraits.rs b/src/arraytraits.rs index 91b163ad4..13b5ce6c8 100644 --- a/src/arraytraits.rs +++ b/src/arraytraits.rs @@ -113,92 +113,6 @@ impl Eq for ArrayBase S::Elem: Eq, { } -impl approx::AbsDiffEq for ArrayBase -where - A: approx::AbsDiffEq, - A::Epsilon: Clone, - S: Data, - D: Dimension, -{ - type Epsilon = A::Epsilon; - - fn default_epsilon() -> A::Epsilon { - A::default_epsilon() - } - - fn abs_diff_eq(&self, other: &ArrayBase, epsilon: A::Epsilon) -> bool { - if self.shape() != other.shape() { - return false; - } - Zip::from(self) - .and(other) - .fold_while(true, |_, a, b| { - if A::abs_diff_ne(a, b, epsilon.clone()) { - FoldWhile::Done(false) - } else { - FoldWhile::Continue(true) - } - }) - .into_inner() - } -} - -impl approx::RelativeEq for ArrayBase -where - A: approx::RelativeEq, - A::Epsilon: Clone, - S: Data, - D: Dimension, -{ - fn default_max_relative() -> A::Epsilon { - A::default_max_relative() - } - - fn relative_eq(&self, other: &ArrayBase, epsilon: A::Epsilon, max_relative: A::Epsilon) -> bool { - if self.shape() != other.shape() { - return false; - } - Zip::from(self) - .and(other) - .fold_while(true, |_, a, b| { - if A::relative_ne(a, b, epsilon.clone(), max_relative.clone()) { - FoldWhile::Done(false) - } else { - FoldWhile::Continue(true) - } - }) - .into_inner() - } -} - -impl approx::UlpsEq for ArrayBase -where - A: approx::UlpsEq, - A::Epsilon: Clone, - S: Data, - D: Dimension, -{ - fn default_max_ulps() -> u32 { - A::default_max_ulps() - } - - fn ulps_eq(&self, other: &ArrayBase, epsilon: A::Epsilon, max_ulps: u32) -> bool { - if self.shape() != other.shape() { - return false; - } - Zip::from(self) - .and(other) - .fold_while(true, |_, a, b| { - if A::ulps_ne(a, b, epsilon.clone(), max_ulps) { - FoldWhile::Done(false) - } else { - FoldWhile::Continue(true) - } - }) - .into_inner() - } -} - impl FromIterator for ArrayBase where S: DataOwned { diff --git a/src/lib.rs b/src/lib.rs index 5ab2656b4..9ffab0665 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,6 +68,9 @@ //! - `rayon` //! - Optional, compatible with Rust stable //! - Enables parallel iterators, parallelized methods and [`par_azip!`]. +//! - `approx` +//! - Optional, compatible with Rust stable +//! - Enables implementations of traits from the [`approx`] crate. //! - `blas` //! - Optional and experimental, compatible with Rust stable //! - Enable transparent BLAS support for matrix multiplication. @@ -90,6 +93,9 @@ extern crate serde; #[cfg(feature="rayon")] extern crate rayon; +#[cfg(feature="approx")] +extern crate approx; + #[cfg(feature="blas")] extern crate cblas_sys; #[cfg(feature="blas")] @@ -97,7 +103,6 @@ extern crate blas_src; extern crate matrixmultiply; -extern crate approx; extern crate itertools; extern crate num_traits; extern crate num_complex; @@ -147,6 +152,8 @@ mod aliases; mod arraytraits; #[cfg(feature = "serde-1")] mod array_serde; +#[cfg(feature = "approx")] +mod array_approx; mod arrayformat; mod data_traits; diff --git a/tests/array.rs b/tests/array.rs index 60f28f132..28e2e7fbc 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -1,6 +1,5 @@ #![allow(non_snake_case)] -extern crate approx; extern crate ndarray; extern crate defmac; extern crate itertools; @@ -13,10 +12,6 @@ use ndarray::{ multislice, }; use ndarray::indices; -use approx::{ - assert_abs_diff_eq, assert_abs_diff_ne, assert_relative_eq, assert_relative_ne, assert_ulps_eq, - assert_ulps_ne, -}; use defmac::defmac; use itertools::{enumerate, zip}; @@ -1168,60 +1163,6 @@ fn equality() assert!(a != c); } -#[test] -fn abs_diff_eq() -{ - let a: Array2 = array![[0., 2.], [-0.000010001, 100000000.]]; - let mut b: Array2 = array![[0., 1.], [-0.000010002, 100000001.]]; - assert_abs_diff_ne!(a, b); - b[(0, 1)] = 2.; - assert_abs_diff_eq!(a, b); - - // Check epsilon. - assert_abs_diff_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32); - assert_abs_diff_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32); - - // Make sure we can compare different shapes without failure. - let c = array![[1., 2.]]; - assert_abs_diff_ne!(a, c); -} - -#[test] -fn relative_eq() -{ - let a: Array2 = array![[1., 2.], [-0.000010001, 100000000.]]; - let mut b: Array2 = array![[1., 1.], [-0.000010002, 100000001.]]; - assert_relative_ne!(a, b); - b[(0, 1)] = 2.; - assert_relative_eq!(a, b); - - // Check epsilon. - assert_relative_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32); - assert_relative_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32); - - // Make sure we can compare different shapes without failure. - let c = array![[1., 2.]]; - assert_relative_ne!(a, c); -} - -#[test] -fn ulps_eq() -{ - let a: Array2 = array![[1., 2.], [-0.000010001, 100000000.]]; - let mut b: Array2 = array![[1., 1.], [-0.000010002, 100000001.]]; - assert_ulps_ne!(a, b); - b[(0, 1)] = 2.; - assert_ulps_eq!(a, b); - - // Check epsilon. - assert_ulps_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32); - assert_ulps_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32); - - // Make sure we can compare different shapes without failure. - let c = array![[1., 2.]]; - assert_ulps_ne!(a, c); -} - #[test] fn map1() {