optimization/
utils.rs

1#[cfg(test)]
2use std::f64::{MIN_POSITIVE, MAX};
3
4
5/// Tests whether we reached a flat area, i.e., tests if all absolute gradient component
6/// lie within the `tolerance`.
7pub fn is_saddle_point(gradient: &[f64], tolerance: f64) -> bool {
8    gradient.iter().all(|dx| dx.abs() <= tolerance)
9}
10
11
12/// Tests whether two floating point numbers are close using the relative error
13/// and handling special cases like infinity etc.
14#[cfg(test)]
15#[allow(clippy::float_cmp)]
16pub fn are_close(a: f64, b: f64, eps: f64) -> bool {
17    assert!(eps.is_finite());
18
19    let d = (a - b).abs();
20
21    // identical, e.g., infinity
22    a == b
23
24    // a or b is zero or both are extremely close to it
25    // relative error is less meaningful here
26    || ((a == 0.0 || b == 0.0 || d < MIN_POSITIVE) &&
27        d < eps * MIN_POSITIVE)
28
29    // finally, use the relative error
30    || d / (a + b).min(MAX) < eps
31}
32
33
34#[cfg(test)]
35mod tests {
36    use std::f64::{INFINITY, NAN};
37
38    use super::{is_saddle_point, are_close};
39
40    #[test]
41    fn test_is_saddle_point() {
42        assert!(is_saddle_point(&[1.0, 2.0], 2.0));
43        assert!(is_saddle_point(&[1.0, -2.0], 2.0));
44        assert!(!is_saddle_point(&[1.0, 2.1], 2.0));
45        assert!(!is_saddle_point(&[1.0, -2.1], 2.0));
46    }
47
48    #[test]
49    fn test_are_close() {
50        assert!(are_close(1.0, 1.0, 0.00001));
51        assert!(are_close(INFINITY, INFINITY, 0.00001));
52        assert!(are_close(1.0e-1000, 0.0, 0.1));
53        assert!(!are_close(1.0e-40, 0.0, 0.000_001));
54        assert!(!are_close(2.0, 1.0, 0.00001));
55        assert!(!are_close(NAN, NAN, 0.00001));
56    }
57}