Skip to main content

kryst/testkit/
mod.rs

1//! Shared helpers so tests & examples work with S=f64 or S=Complex64.
2
3use crate::algebra::blas::{dot_conj, nrm2};
4use crate::algebra::prelude::*;
5
6/// Default tolerances (tweak to your solver accuracy)
7pub const ATOL: R = 1e-12;
8pub const RTOL: R = 1e-10;
9
10/// Turn an f64 into S (real -> complex with im=0 under `complex`)
11#[inline]
12pub fn s(x: f64) -> S {
13    S::from_real(x)
14}
15
16/// Is a scalar essentially zero? (`|z| < eps`)
17#[inline]
18pub fn is_zero(z: S, eps: R) -> bool {
19    z.abs() < eps
20}
21
22/// Scalar approx equality using |a-b| <= atol + rtol*max(|a|,|b|)
23#[inline]
24pub fn approx_s(a: S, b: S, atol: R, rtol: R) -> bool {
25    let diff = (a - b).abs();
26    let scale = a.abs().max(b.abs());
27    diff <= atol + rtol * scale
28}
29
30/// Assert scalar closeness (pretty message in both modes)
31#[track_caller]
32pub fn assert_s_close(label: &str, a: S, b: S, atol: R, rtol: R) {
33    if !approx_s(a, b, atol, rtol) {
34        panic!(
35            "{label}: |a-b|={} (a={:?}, b={:?}) > atol+rtol*scale (atol={:.3e}, rtol={:.3e})",
36            (a - b).abs(),
37            a,
38            b,
39            atol,
40            rtol
41        );
42    }
43}
44
45/// Vector 2-norm of difference (returns R)
46#[inline]
47pub fn vec_err(a: &[S], b: &[S]) -> R {
48    let mut tmp = Vec::<S>::with_capacity(a.len());
49    unsafe {
50        tmp.set_len(a.len());
51    }
52    for i in 0..a.len() {
53        tmp[i] = a[i] - b[i];
54    }
55    nrm2(&tmp)
56}
57
58/// Conjugate dot product helper (matches BLAS dot for reals)
59#[inline]
60pub fn vec_dot(a: &[S], b: &[S]) -> S {
61    dot_conj(a, b)
62}
63
64/// Assert vector closeness via 2-norm
65#[track_caller]
66pub fn assert_vec_close(label: &str, a: &[S], b: &[S], atol: R, rtol: R) {
67    assert_eq!(
68        a.len(),
69        b.len(),
70        "{label}: length mismatch {} != {}",
71        a.len(),
72        b.len()
73    );
74    let err = vec_err(a, b);
75    let na = nrm2(a);
76    let nb = nrm2(b);
77    let scale = na.max(nb);
78    if err > atol + rtol * scale {
79        panic!(
80            "{label}: ||a-b||2={:.3e} > {:.3e} (atol+rtol*max(||a||,||b||))",
81            err,
82            atol + rtol * scale
83        );
84    }
85}
86
87/// Convenience macro: scalar close with defaults
88#[macro_export]
89macro_rules! assert_s_close {
90    ($label:expr, $a:expr, $b:expr) => {{
91        $crate::testkit::assert_s_close(
92            $label,
93            $a,
94            $b,
95            $crate::testkit::ATOL,
96            $crate::testkit::RTOL,
97        )
98    }};
99}
100
101/// Convenience macro: vector close with defaults
102#[macro_export]
103macro_rules! assert_vec_close {
104    ($label:expr, $a:expr, $b:expr) => {{
105        $crate::testkit::assert_vec_close(
106            $label,
107            $a,
108            $b,
109            $crate::testkit::ATOL,
110            $crate::testkit::RTOL,
111        )
112    }};
113}