Skip to content

Commit 79b2541

Browse files
committed
Feature-gate approx trait implementations
1 parent 984cf22 commit 79b2541

File tree

5 files changed

+166
-148
lines changed

5 files changed

+166
-148
lines changed

Cargo.toml

+3-2
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,15 @@ bench = false
2828
test = true
2929

3030
[dependencies]
31-
approx = "0.3"
3231
num-integer = "0.1.39"
3332
num-traits = "0.2"
3433
num-complex = "0.2"
3534
itertools = { version = "0.7.0", default-features = false }
3635

3736
rayon = { version = "1.0.3", optional = true }
3837

38+
approx = { version = "0.3", optional = true }
39+
3940
# Use via the `blas` crate feature!
4041
cblas-sys = { version = "0.1.4", optional = true, default-features = false }
4142
blas-src = { version = "0.2.0", optional = true, default-features = false }
@@ -62,7 +63,7 @@ test-blas-openblas-sys = ["blas"]
6263
test = ["test-blas-openblas-sys"]
6364

6465
# This feature is used for docs
65-
docs = ["serde-1", "rayon"]
66+
docs = ["approx", "serde-1", "rayon"]
6667

6768
[profile.release]
6869
[profile.bench]

src/array_approx.rs

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
use crate::imp_prelude::*;
2+
use approx::{AbsDiffEq, RelativeEq, UlpsEq};
3+
4+
/// **Requires crate feature `"approx"`**
5+
impl<A, S, D> AbsDiffEq for ArrayBase<S, D>
6+
where
7+
A: AbsDiffEq,
8+
A::Epsilon: Clone,
9+
S: Data<Elem = A>,
10+
D: Dimension,
11+
{
12+
type Epsilon = A::Epsilon;
13+
14+
fn default_epsilon() -> A::Epsilon {
15+
A::default_epsilon()
16+
}
17+
18+
fn abs_diff_eq(&self, other: &ArrayBase<S, D>, epsilon: A::Epsilon) -> bool {
19+
if self.shape() != other.shape() {
20+
return false;
21+
}
22+
Zip::from(self)
23+
.and(other)
24+
.fold_while(true, |_, a, b| {
25+
if A::abs_diff_ne(a, b, epsilon.clone()) {
26+
FoldWhile::Done(false)
27+
} else {
28+
FoldWhile::Continue(true)
29+
}
30+
})
31+
.into_inner()
32+
}
33+
}
34+
35+
/// **Requires crate feature `"approx"`**
36+
impl<A, S, D> RelativeEq for ArrayBase<S, D>
37+
where
38+
A: RelativeEq,
39+
A::Epsilon: Clone,
40+
S: Data<Elem = A>,
41+
D: Dimension,
42+
{
43+
fn default_max_relative() -> A::Epsilon {
44+
A::default_max_relative()
45+
}
46+
47+
fn relative_eq(
48+
&self,
49+
other: &ArrayBase<S, D>,
50+
epsilon: A::Epsilon,
51+
max_relative: A::Epsilon,
52+
) -> bool {
53+
if self.shape() != other.shape() {
54+
return false;
55+
}
56+
Zip::from(self)
57+
.and(other)
58+
.fold_while(true, |_, a, b| {
59+
if A::relative_ne(a, b, epsilon.clone(), max_relative.clone()) {
60+
FoldWhile::Done(false)
61+
} else {
62+
FoldWhile::Continue(true)
63+
}
64+
})
65+
.into_inner()
66+
}
67+
}
68+
69+
/// **Requires crate feature `"approx"`**
70+
impl<A, S, D> UlpsEq for ArrayBase<S, D>
71+
where
72+
A: UlpsEq,
73+
A::Epsilon: Clone,
74+
S: Data<Elem = A>,
75+
D: Dimension,
76+
{
77+
fn default_max_ulps() -> u32 {
78+
A::default_max_ulps()
79+
}
80+
81+
fn ulps_eq(&self, other: &ArrayBase<S, D>, epsilon: A::Epsilon, max_ulps: u32) -> bool {
82+
if self.shape() != other.shape() {
83+
return false;
84+
}
85+
Zip::from(self)
86+
.and(other)
87+
.fold_while(true, |_, a, b| {
88+
if A::ulps_ne(a, b, epsilon.clone(), max_ulps) {
89+
FoldWhile::Done(false)
90+
} else {
91+
FoldWhile::Continue(true)
92+
}
93+
})
94+
.into_inner()
95+
}
96+
}
97+
98+
#[cfg(test)]
99+
mod tests {
100+
use approx::{
101+
assert_abs_diff_eq, assert_abs_diff_ne, assert_relative_eq, assert_relative_ne,
102+
assert_ulps_eq, assert_ulps_ne,
103+
};
104+
105+
#[test]
106+
fn abs_diff_eq() {
107+
let a: Array2<f32> = array![[0., 2.], [-0.000010001, 100000000.]];
108+
let mut b: Array2<f32> = array![[0., 1.], [-0.000010002, 100000001.]];
109+
assert_abs_diff_ne!(a, b);
110+
b[(0, 1)] = 2.;
111+
assert_abs_diff_eq!(a, b);
112+
113+
// Check epsilon.
114+
assert_abs_diff_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32);
115+
assert_abs_diff_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32);
116+
117+
// Make sure we can compare different shapes without failure.
118+
let c = array![[1., 2.]];
119+
assert_abs_diff_ne!(a, c);
120+
}
121+
122+
#[test]
123+
fn relative_eq() {
124+
let a: Array2<f32> = array![[1., 2.], [-0.000010001, 100000000.]];
125+
let mut b: Array2<f32> = array![[1., 1.], [-0.000010002, 100000001.]];
126+
assert_relative_ne!(a, b);
127+
b[(0, 1)] = 2.;
128+
assert_relative_eq!(a, b);
129+
130+
// Check epsilon.
131+
assert_relative_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32);
132+
assert_relative_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32);
133+
134+
// Make sure we can compare different shapes without failure.
135+
let c = array![[1., 2.]];
136+
assert_relative_ne!(a, c);
137+
}
138+
139+
#[test]
140+
fn ulps_eq() {
141+
let a: Array2<f32> = array![[1., 2.], [-0.000010001, 100000000.]];
142+
let mut b: Array2<f32> = array![[1., 1.], [-0.000010002, 100000001.]];
143+
assert_ulps_ne!(a, b);
144+
b[(0, 1)] = 2.;
145+
assert_ulps_eq!(a, b);
146+
147+
// Check epsilon.
148+
assert_ulps_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32);
149+
assert_ulps_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32);
150+
151+
// Make sure we can compare different shapes without failure.
152+
let c = array![[1., 2.]];
153+
assert_ulps_ne!(a, c);
154+
}
155+
}

src/arraytraits.rs

-86
Original file line numberDiff line numberDiff line change
@@ -113,92 +113,6 @@ impl<S, D> Eq for ArrayBase<S, D>
113113
S::Elem: Eq,
114114
{ }
115115

116-
impl<A, S, D> approx::AbsDiffEq for ArrayBase<S, D>
117-
where
118-
A: approx::AbsDiffEq,
119-
A::Epsilon: Clone,
120-
S: Data<Elem = A>,
121-
D: Dimension,
122-
{
123-
type Epsilon = A::Epsilon;
124-
125-
fn default_epsilon() -> A::Epsilon {
126-
A::default_epsilon()
127-
}
128-
129-
fn abs_diff_eq(&self, other: &ArrayBase<S, D>, epsilon: A::Epsilon) -> bool {
130-
if self.shape() != other.shape() {
131-
return false;
132-
}
133-
Zip::from(self)
134-
.and(other)
135-
.fold_while(true, |_, a, b| {
136-
if A::abs_diff_ne(a, b, epsilon.clone()) {
137-
FoldWhile::Done(false)
138-
} else {
139-
FoldWhile::Continue(true)
140-
}
141-
})
142-
.into_inner()
143-
}
144-
}
145-
146-
impl<A, S, D> approx::RelativeEq for ArrayBase<S, D>
147-
where
148-
A: approx::RelativeEq,
149-
A::Epsilon: Clone,
150-
S: Data<Elem = A>,
151-
D: Dimension,
152-
{
153-
fn default_max_relative() -> A::Epsilon {
154-
A::default_max_relative()
155-
}
156-
157-
fn relative_eq(&self, other: &ArrayBase<S, D>, epsilon: A::Epsilon, max_relative: A::Epsilon) -> bool {
158-
if self.shape() != other.shape() {
159-
return false;
160-
}
161-
Zip::from(self)
162-
.and(other)
163-
.fold_while(true, |_, a, b| {
164-
if A::relative_ne(a, b, epsilon.clone(), max_relative.clone()) {
165-
FoldWhile::Done(false)
166-
} else {
167-
FoldWhile::Continue(true)
168-
}
169-
})
170-
.into_inner()
171-
}
172-
}
173-
174-
impl<A, S, D> approx::UlpsEq for ArrayBase<S, D>
175-
where
176-
A: approx::UlpsEq,
177-
A::Epsilon: Clone,
178-
S: Data<Elem = A>,
179-
D: Dimension,
180-
{
181-
fn default_max_ulps() -> u32 {
182-
A::default_max_ulps()
183-
}
184-
185-
fn ulps_eq(&self, other: &ArrayBase<S, D>, epsilon: A::Epsilon, max_ulps: u32) -> bool {
186-
if self.shape() != other.shape() {
187-
return false;
188-
}
189-
Zip::from(self)
190-
.and(other)
191-
.fold_while(true, |_, a, b| {
192-
if A::ulps_ne(a, b, epsilon.clone(), max_ulps) {
193-
FoldWhile::Done(false)
194-
} else {
195-
FoldWhile::Continue(true)
196-
}
197-
})
198-
.into_inner()
199-
}
200-
}
201-
202116
impl<A, S> FromIterator<A> for ArrayBase<S, Ix1>
203117
where S: DataOwned<Elem=A>
204118
{

src/lib.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@
6868
//! - `rayon`
6969
//! - Optional, compatible with Rust stable
7070
//! - Enables parallel iterators, parallelized methods and [`par_azip!`].
71+
//! - `approx`
72+
//! - Optional, compatible with Rust stable
73+
//! - Enables implementations of traits from the [`approx`] crate.
7174
//! - `blas`
7275
//! - Optional and experimental, compatible with Rust stable
7376
//! - Enable transparent BLAS support for matrix multiplication.
@@ -90,14 +93,16 @@ extern crate serde;
9093
#[cfg(feature="rayon")]
9194
extern crate rayon;
9295

96+
#[cfg(feature="approx")]
97+
extern crate approx;
98+
9399
#[cfg(feature="blas")]
94100
extern crate cblas_sys;
95101
#[cfg(feature="blas")]
96102
extern crate blas_src;
97103

98104
extern crate matrixmultiply;
99105

100-
extern crate approx;
101106
extern crate itertools;
102107
extern crate num_traits;
103108
extern crate num_complex;
@@ -147,6 +152,8 @@ mod aliases;
147152
mod arraytraits;
148153
#[cfg(feature = "serde-1")]
149154
mod array_serde;
155+
#[cfg(feature = "approx")]
156+
mod array_approx;
150157
mod arrayformat;
151158
mod data_traits;
152159

tests/array.rs

-59
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#![allow(non_snake_case)]
22

3-
extern crate approx;
43
extern crate ndarray;
54
extern crate defmac;
65
extern crate itertools;
@@ -13,10 +12,6 @@ use ndarray::{
1312
multislice,
1413
};
1514
use ndarray::indices;
16-
use approx::{
17-
assert_abs_diff_eq, assert_abs_diff_ne, assert_relative_eq, assert_relative_ne, assert_ulps_eq,
18-
assert_ulps_ne,
19-
};
2015
use defmac::defmac;
2116
use itertools::{enumerate, zip};
2217

@@ -1168,60 +1163,6 @@ fn equality()
11681163
assert!(a != c);
11691164
}
11701165

1171-
#[test]
1172-
fn abs_diff_eq()
1173-
{
1174-
let a: Array2<f32> = array![[0., 2.], [-0.000010001, 100000000.]];
1175-
let mut b: Array2<f32> = array![[0., 1.], [-0.000010002, 100000001.]];
1176-
assert_abs_diff_ne!(a, b);
1177-
b[(0, 1)] = 2.;
1178-
assert_abs_diff_eq!(a, b);
1179-
1180-
// Check epsilon.
1181-
assert_abs_diff_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32);
1182-
assert_abs_diff_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32);
1183-
1184-
// Make sure we can compare different shapes without failure.
1185-
let c = array![[1., 2.]];
1186-
assert_abs_diff_ne!(a, c);
1187-
}
1188-
1189-
#[test]
1190-
fn relative_eq()
1191-
{
1192-
let a: Array2<f32> = array![[1., 2.], [-0.000010001, 100000000.]];
1193-
let mut b: Array2<f32> = array![[1., 1.], [-0.000010002, 100000001.]];
1194-
assert_relative_ne!(a, b);
1195-
b[(0, 1)] = 2.;
1196-
assert_relative_eq!(a, b);
1197-
1198-
// Check epsilon.
1199-
assert_relative_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32);
1200-
assert_relative_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32);
1201-
1202-
// Make sure we can compare different shapes without failure.
1203-
let c = array![[1., 2.]];
1204-
assert_relative_ne!(a, c);
1205-
}
1206-
1207-
#[test]
1208-
fn ulps_eq()
1209-
{
1210-
let a: Array2<f32> = array![[1., 2.], [-0.000010001, 100000000.]];
1211-
let mut b: Array2<f32> = array![[1., 1.], [-0.000010002, 100000001.]];
1212-
assert_ulps_ne!(a, b);
1213-
b[(0, 1)] = 2.;
1214-
assert_ulps_eq!(a, b);
1215-
1216-
// Check epsilon.
1217-
assert_ulps_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32);
1218-
assert_ulps_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32);
1219-
1220-
// Make sure we can compare different shapes without failure.
1221-
let c = array![[1., 2.]];
1222-
assert_ulps_ne!(a, c);
1223-
}
1224-
12251166
#[test]
12261167
fn map1()
12271168
{

0 commit comments

Comments
 (0)