Skip to main content

memra_validate/
lib.rs

1//! memra-validate — shared validation-protocol core (Phase D extraction, ARCHITECTURE-H100.md §5).
2//!
3//! Pure host logic, zero engine/CUDA dependency: the pieces every gate bin and bench
4//! duplicated (kernel_check, gdn_bench, fa_sanitize, dtype_gpu_check5, the N=5 bench
5//! protocol). CPU kernel references stay with their kernels; THIS crate owns the
6//! protocol: deterministic test vectors, error measures, tolerance banding, N-rep
7//! medians, and the ALL-GREEN tally contract.
8//!
9//! Extraction law: moved code is verbatim (bit-identical vectors and measures) — the
10//! `pr` generator here is the kernel_check/dtype_gpu_check5 variant; fa_sanitize's
11//! 16-bit variant intentionally stays local to it (different distribution = different
12//! test vectors = a silent gate change).
13
14/// Max absolute elementwise difference (the universal gate measure).
15pub fn maxdiff(a: &[f32], b: &[f32]) -> f32 {
16    a.iter()
17        .zip(b)
18        .map(|(x, y)| (x - y).abs())
19        .fold(0.0, f32::max)
20}
21
22/// Relative error of `d` against the max-|value| scale of `reference` (floored to avoid
23/// zero-division on all-zero references) — the kernel_check GEMM-band convention.
24pub fn rel_of(d: f32, reference: &[f32]) -> f32 {
25    let scale = reference
26        .iter()
27        .map(|v| v.abs())
28        .fold(0.0, f32::max)
29        .max(1e-3);
30    d / scale
31}
32
33/// Deterministic unit-interval-ish test vector generator (Knuth multiplicative hash →
34/// [-1, 1)). Verbatim the kernel_check/dtype_gpu_check5 `pr` — gate vectors must never
35/// drift across crates or sessions.
36pub fn pr(i: usize) -> f32 {
37    let x = (i.wrapping_mul(2654435761) ^ 0x9E3779B9) as u32;
38    ((x >> 8) as f32 / (1u32 << 24) as f32) * 2.0 - 1.0
39}
40
41/// Median of N runs (the repo's N=5 protocol; N is the caller's law, this is the math).
42/// Sorts a copy; even N takes the lower-middle (matches the existing bench bins).
43pub fn median(xs: &[f64]) -> f64 {
44    assert!(!xs.is_empty());
45    let mut v = xs.to_vec();
46    v.sort_by(|a, b| a.partial_cmp(b).unwrap());
47    v[(v.len() - 1) / 2]
48}
49
50/// Run `f` N times, return (median, all runs). The N=5-medians protocol runner.
51pub fn run_n_median<E>(
52    n: usize,
53    mut f: impl FnMut(usize) -> Result<f64, E>,
54) -> Result<(f64, Vec<f64>), E> {
55    let mut runs = Vec::with_capacity(n);
56    for i in 0..n {
57        runs.push(f(i)?);
58    }
59    Ok((median(&runs), runs))
60}
61
62/// ALL-GREEN tally: gates print per-case lines and exit nonzero on any failure.
63/// `check` returns the condition so call sites keep their inline `{ ... "FAIL" }` style
64/// or use it directly.
65#[derive(Default)]
66pub struct GateTally {
67    pub fails: usize,
68}
69
70impl GateTally {
71    pub fn check(&mut self, label: &str, ok: bool) -> bool {
72        println!("{label}: {}", if ok { "OK" } else { "FAIL" });
73        if !ok {
74            self.fails += 1;
75        }
76        ok
77    }
78
79    /// Terminal verdict in the kernel-check contract: prints ALL GREEN or returns Err.
80    pub fn finish(&self, what: &str) -> Result<(), String> {
81        if self.fails == 0 {
82            println!("ALL GREEN: {what}");
83            Ok(())
84        } else {
85            Err(format!("{}: {} gate(s) FAILED", what, self.fails))
86        }
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn median_odd_even() {
96        assert_eq!(median(&[3.0, 1.0, 2.0]), 2.0);
97        assert_eq!(median(&[4.0, 1.0, 3.0, 2.0]), 2.0); // lower-middle
98    }
99
100    #[test]
101    fn pr_deterministic_snapshot() {
102        // Pin the exact vector law — any drift here silently changes every gate.
103        assert_eq!(pr(71), pr(71));
104        let v: Vec<f32> = (0..4).map(pr).collect();
105        assert!(v.iter().all(|x| (-1.0..1.0).contains(x)));
106    }
107
108    #[test]
109    fn tally_contract() {
110        let mut t = GateTally::default();
111        assert!(t.check("a", true));
112        assert!(!t.check("b", false));
113        assert!(t.finish("demo").is_err());
114        assert_eq!(t.fails, 1);
115    }
116}