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}