[go: up one dir, main page]

ndarray/
array_approx.rs

1#[cfg(feature = "approx")]
2mod approx_methods
3{
4    use crate::imp_prelude::*;
5
6    impl<A, D: Dimension> ArrayRef<A, D>
7    {
8        /// A test for equality that uses the elementwise absolute difference to compute the
9        /// approximate equality of two arrays.
10        pub fn abs_diff_eq<B>(&self, other: &ArrayRef<B, D>, epsilon: A::Epsilon) -> bool
11        where
12            A: ::approx::AbsDiffEq<B>,
13            A::Epsilon: Clone,
14        {
15            <Self as ::approx::AbsDiffEq<_>>::abs_diff_eq(self, other, epsilon)
16        }
17
18        /// A test for equality that uses an elementwise relative comparison if the values are far
19        /// apart; and the absolute difference otherwise.
20        pub fn relative_eq<B>(&self, other: &ArrayRef<B, D>, epsilon: A::Epsilon, max_relative: A::Epsilon) -> bool
21        where
22            A: ::approx::RelativeEq<B>,
23            A::Epsilon: Clone,
24        {
25            <Self as ::approx::RelativeEq<_>>::relative_eq(self, other, epsilon, max_relative)
26        }
27    }
28}
29
30macro_rules! impl_approx_traits {
31    ($approx:ident, $doc:expr) => {
32        mod $approx {
33            use crate::imp_prelude::*;
34            use crate::Zip;
35            use $approx::{AbsDiffEq, RelativeEq, UlpsEq};
36
37            #[doc = $doc]
38            impl<A, B, D> AbsDiffEq<ArrayRef<B, D>> for ArrayRef<A, D>
39            where
40                A: AbsDiffEq<B>,
41                A::Epsilon: Clone,
42                D: Dimension,
43            {
44                type Epsilon = A::Epsilon;
45
46                fn default_epsilon() -> A::Epsilon {
47                    A::default_epsilon()
48                }
49
50                fn abs_diff_eq(&self, other: &ArrayRef<B, D>, epsilon: A::Epsilon) -> bool {
51                    if self.shape() != other.shape() {
52                        return false;
53                    }
54
55                    Zip::from(self)
56                        .and(other)
57                        .all(move |a, b| A::abs_diff_eq(a, b, epsilon.clone()))
58                }
59            }
60
61            #[doc = $doc]
62            impl<A, B, S, S2, D> AbsDiffEq<ArrayBase<S2, D>> for ArrayBase<S, D>
63            where
64                A: AbsDiffEq<B>,
65                A::Epsilon: Clone,
66                S: Data<Elem = A>,
67                S2: Data<Elem = B>,
68                D: Dimension,
69            {
70                type Epsilon = A::Epsilon;
71
72                fn default_epsilon() -> A::Epsilon {
73                    A::default_epsilon()
74                }
75
76                fn abs_diff_eq(&self, other: &ArrayBase<S2, D>, epsilon: A::Epsilon) -> bool {
77                    (**self).abs_diff_eq(other, epsilon)
78                }
79            }
80
81            #[doc = $doc]
82            impl<A, B, D> RelativeEq<ArrayRef<B, D>> for ArrayRef<A, D>
83            where
84                A: RelativeEq<B>,
85                A::Epsilon: Clone,
86                D: Dimension,
87            {
88                fn default_max_relative() -> A::Epsilon {
89                    A::default_max_relative()
90                }
91
92                fn relative_eq(
93                    &self,
94                    other: &ArrayRef<B, D>,
95                    epsilon: A::Epsilon,
96                    max_relative: A::Epsilon,
97                ) -> bool {
98                    if self.shape() != other.shape() {
99                        return false;
100                    }
101
102                    Zip::from(self).and(other).all(move |a, b| {
103                        A::relative_eq(a, b, epsilon.clone(), max_relative.clone())
104                    })
105                }
106            }
107
108            #[doc = $doc]
109            impl<A, B, S, S2, D> RelativeEq<ArrayBase<S2, D>> for ArrayBase<S, D>
110            where
111                A: RelativeEq<B>,
112                A::Epsilon: Clone,
113                S: Data<Elem = A>,
114                S2: Data<Elem = B>,
115                D: Dimension,
116            {
117                fn default_max_relative() -> A::Epsilon {
118                    A::default_max_relative()
119                }
120
121                fn relative_eq(
122                    &self,
123                    other: &ArrayBase<S2, D>,
124                    epsilon: A::Epsilon,
125                    max_relative: A::Epsilon,
126                ) -> bool {
127                    (**self).relative_eq(other, epsilon, max_relative)
128                }
129            }
130
131            #[doc = $doc]
132            impl<A, B, D> UlpsEq<ArrayRef<B, D>> for ArrayRef<A, D>
133            where
134                A: UlpsEq<B>,
135                A::Epsilon: Clone,
136                D: Dimension,
137            {
138                fn default_max_ulps() -> u32 {
139                    A::default_max_ulps()
140                }
141
142                fn ulps_eq(
143                    &self,
144                    other: &ArrayRef<B, D>,
145                    epsilon: A::Epsilon,
146                    max_ulps: u32,
147                ) -> bool {
148                    if self.shape() != other.shape() {
149                        return false;
150                    }
151
152                    Zip::from(self)
153                        .and(other)
154                        .all(move |a, b| A::ulps_eq(a, b, epsilon.clone(), max_ulps))
155                }
156            }
157
158            #[doc = $doc]
159            impl<A, B, S, S2, D> UlpsEq<ArrayBase<S2, D>> for ArrayBase<S, D>
160            where
161                A: UlpsEq<B>,
162                A::Epsilon: Clone,
163                S: Data<Elem = A>,
164                S2: Data<Elem = B>,
165                D: Dimension,
166            {
167                fn default_max_ulps() -> u32 {
168                    A::default_max_ulps()
169                }
170
171                fn ulps_eq(
172                    &self,
173                    other: &ArrayBase<S2, D>,
174                    epsilon: A::Epsilon,
175                    max_ulps: u32,
176                ) -> bool {
177                    (**self).ulps_eq(other, epsilon, max_ulps)
178                }
179            }
180
181            #[cfg(test)]
182            mod tests {
183                use crate::prelude::*;
184                use alloc::vec;
185                use $approx::{
186                    assert_abs_diff_eq, assert_abs_diff_ne, assert_relative_eq, assert_relative_ne,
187                    assert_ulps_eq, assert_ulps_ne,
188                };
189
190                #[test]
191                fn abs_diff_eq() {
192                    let a: Array2<f32> = array![[0., 2.], [-0.000010001, 100000000.]];
193                    let mut b: Array2<f32> = array![[0., 1.], [-0.000010002, 100000001.]];
194                    assert_abs_diff_ne!(a, b);
195                    b[(0, 1)] = 2.;
196                    assert_abs_diff_eq!(a, b);
197
198                    // Check epsilon.
199                    assert_abs_diff_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32);
200                    assert_abs_diff_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32);
201
202                    // Make sure we can compare different shapes without failure.
203                    let c = array![[1., 2.]];
204                    assert_abs_diff_ne!(a, c);
205                }
206
207                #[test]
208                fn relative_eq() {
209                    let a: Array2<f32> = array![[1., 2.], [-0.000010001, 100000000.]];
210                    let mut b: Array2<f32> = array![[1., 1.], [-0.000010002, 100000001.]];
211                    assert_relative_ne!(a, b);
212                    b[(0, 1)] = 2.;
213                    assert_relative_eq!(a, b);
214
215                    // Check epsilon.
216                    assert_relative_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32);
217                    assert_relative_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32);
218
219                    // Make sure we can compare different shapes without failure.
220                    let c = array![[1., 2.]];
221                    assert_relative_ne!(a, c);
222                }
223
224                #[test]
225                fn ulps_eq() {
226                    let a: Array2<f32> = array![[1., 2.], [-0.000010001, 100000000.]];
227                    let mut b: Array2<f32> = array![[1., 1.], [-0.000010002, 100000001.]];
228                    assert_ulps_ne!(a, b);
229                    b[(0, 1)] = 2.;
230                    assert_ulps_eq!(a, b);
231
232                    // Check epsilon.
233                    assert_ulps_eq!(array![0.0f32], array![1e-40f32], epsilon = 1e-40f32);
234                    assert_ulps_ne!(array![0.0f32], array![1e-40f32], epsilon = 1e-41f32);
235
236                    // Make sure we can compare different shapes without failure.
237                    let c = array![[1., 2.]];
238                    assert_ulps_ne!(a, c);
239                }
240            }
241        }
242    };
243}
244
245#[cfg(feature = "approx")]
246impl_approx_traits!(approx, "**Requires crate feature `\"approx\"`.**");