Skip to main content

sim_lib_interference_solve/
verification.rs

1//! Public measurements and diagnostics for reference verification.
2
3use std::fmt;
4
5use crate::ReferenceSolveError;
6
7/// Largest relative error admitted for the closed-form analytic checks.
8pub const MAX_ANALYTIC_RELATIVE_ERROR: f64 = 1.0e-12;
9
10/// Largest relative error admitted for a metamorphic field law.
11pub const MAX_METAMORPHIC_RELATIVE_ERROR: f64 = 1.0e-12;
12
13/// Complete measurements from the deterministic reference verification suite.
14///
15/// The Helmholtz value is the matrix member farthest from ideal second-order
16/// convergence. Every matrix member is checked before a report is returned.
17#[derive(Clone, Copy, Debug, PartialEq)]
18pub struct VerificationReport {
19    /// Largest relative error across the analytic identities and time-sign check.
20    pub analytic_max_error: f64,
21    /// Relative point Green-function reciprocity error.
22    pub reciprocity_relative: f64,
23    /// Largest relative superposition error.
24    pub linearity_relative: f64,
25    /// Largest relative error after a shared rotation and translation.
26    pub rigid_motion_relative: f64,
27    /// Largest relative error after rotating every phasor by one phase.
28    pub global_phase_relative: f64,
29    /// Whether every source input permutation produced identical component bits.
30    pub source_permutation_identical: bool,
31    /// Observed residual order farthest from ideal order two in the fixture matrix.
32    pub helmholtz_observed_order: f64,
33}
34
35/// A deterministic verification suite failed before producing a passing report.
36#[derive(Clone, Debug, PartialEq)]
37pub enum VerificationError {
38    /// A checked fixture or reference solve was refused.
39    Reference {
40        /// Exact reference-solver diagnostic.
41        cause: Box<ReferenceSolveError>,
42    },
43    /// A measured analytic or metamorphic error exceeded its declared limit.
44    RelativeErrorExceeded {
45        /// Stable verification check name.
46        check: &'static str,
47        /// Measured relative error.
48        measured: f64,
49        /// Largest accepted relative error.
50        limit: f64,
51    },
52    /// Canonical source permutations changed the field's component bits.
53    SourcePermutationChanged,
54    /// One named Helmholtz fixture did not exhibit second-order convergence.
55    HelmholtzOrderOutOfRange {
56        /// Stable fixture name.
57        fixture: &'static str,
58        /// Measured convergence order.
59        observed: f64,
60        /// Inclusive lower bound.
61        minimum: f64,
62        /// Inclusive upper bound.
63        maximum: f64,
64    },
65}
66
67impl From<ReferenceSolveError> for VerificationError {
68    fn from(cause: ReferenceSolveError) -> Self {
69        Self::Reference {
70            cause: Box::new(cause),
71        }
72    }
73}
74
75impl fmt::Display for VerificationError {
76    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
77        match self {
78            Self::Reference { cause } => write!(formatter, "verification fixture failed: {cause}"),
79            Self::RelativeErrorExceeded {
80                check,
81                measured,
82                limit,
83            } => write!(
84                formatter,
85                "verification check `{check}` measured relative error {measured:e}, \
86                 exceeding {limit:e}"
87            ),
88            Self::SourcePermutationChanged => {
89                formatter.write_str("canonical source permutations changed field component bits")
90            }
91            Self::HelmholtzOrderOutOfRange {
92                fixture,
93                observed,
94                minimum,
95                maximum,
96            } => write!(
97                formatter,
98                "Helmholtz fixture `{fixture}` observed order {observed}, outside \
99                 [{minimum}, {maximum}]"
100            ),
101        }
102    }
103}
104
105impl std::error::Error for VerificationError {
106    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
107        match self {
108            Self::Reference { cause } => Some(cause.as_ref()),
109            Self::RelativeErrorExceeded { .. }
110            | Self::SourcePermutationChanged
111            | Self::HelmholtzOrderOutOfRange { .. } => None,
112        }
113    }
114}