use alloc::vec::Vec;
pub const FP_BINARY_THRESHOLD: f32 = 2048.0;
pub const F32_ADD_WGSL: &str = r#"
@group(0) @binding(0) var<storage, read> a: array<f32>;
@group(0) @binding(1) var<storage, read> b: array<f32>;
@group(0) @binding(2) var<storage, read_write> out: array<f32>;
@compute @workgroup_size(256)
fn vadd(@builtin(global_invocation_id) gid: vec3<u32>) {
let i = gid.x;
if (i < arrayLength(&out)) {
out[i] = a[i] + b[i];
}
}
"#;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct VerifyConfig {
pub threshold: f32,
pub trials: u32,
pub p_zero: f32,
}
impl Default for VerifyConfig {
fn default() -> Self {
Self {
threshold: FP_BINARY_THRESHOLD,
trials: 16,
p_zero: 0.7,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct VerifyResult {
pub passed: bool,
pub trials: u32,
pub positions_checked: usize,
pub positions_exact: usize,
pub max_diff: f32,
}
impl VerifyResult {
pub fn empty_aggregator() -> Self {
Self {
passed: true,
trials: 0,
positions_checked: 0,
positions_exact: 0,
max_diff: 0.0,
}
}
pub fn fold_trial(&mut self, trial: VerifyResult) {
self.trials += trial.trials;
self.positions_checked += trial.positions_checked;
self.positions_exact += trial.positions_exact;
if trial.max_diff > self.max_diff {
self.max_diff = trial.max_diff;
}
if !trial.passed {
self.passed = false;
}
}
}
pub fn compare_outputs(gpu_output: &[f32], reference: &[f32], threshold: f32) -> VerifyResult {
let mut positions_checked = 0usize;
let mut positions_exact = 0usize;
let mut max_diff = 0.0f32;
for (gpu_val, ref_val) in gpu_output.iter().zip(reference.iter()) {
if *ref_val <= threshold {
positions_checked += 1;
let diff = (*gpu_val - ref_val).abs();
if diff == 0.0 {
positions_exact += 1;
}
if diff > max_diff {
max_diff = diff;
}
}
}
let passed = positions_checked > 0 && positions_exact == positions_checked;
VerifyResult {
passed,
trials: 1,
positions_checked,
positions_exact,
max_diff,
}
}
pub fn f32_add_reference(a: &[f32], b: &[f32]) -> Vec<f32> {
a.iter().zip(b.iter()).map(|(&x, &y)| x + y).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_config_uses_fp16_threshold() {
let cfg = VerifyConfig::default();
assert_eq!(cfg.threshold, FP_BINARY_THRESHOLD);
assert_eq!(cfg.threshold, 2048.0);
assert!(cfg.trials >= 1);
assert!(cfg.p_zero > 0.0 && cfg.p_zero < 1.0);
}
#[test]
fn compare_outputs_exact_match_passes() {
let gpu = [1.0_f32, 2.0, 3.0, 4.0];
let r#ref = [1.0_f32, 2.0, 3.0, 4.0];
let result = compare_outputs(&gpu, &r#ref, 2048.0);
assert!(result.passed);
assert_eq!(result.positions_checked, 4);
assert_eq!(result.positions_exact, 4);
assert_eq!(result.max_diff, 0.0);
}
#[test]
fn compare_outputs_mismatch_detected() {
let gpu = [1.0_f32, 2.0, 3.0, 5.0]; let r#ref = [1.0_f32, 2.0, 3.0, 4.0];
let result = compare_outputs(&gpu, &r#ref, 2048.0);
assert!(!result.passed);
assert_eq!(result.positions_checked, 4);
assert_eq!(result.positions_exact, 3);
assert_eq!(result.max_diff, 1.0);
}
#[test]
fn compare_outputs_ignores_positions_above_threshold() {
let gpu = [1.0_f32, 2.0, 3000.0];
let r#ref = [1.0_f32, 2.0, 2049.0];
let result = compare_outputs(&gpu, &r#ref, 2048.0);
assert!(result.passed);
assert_eq!(result.positions_checked, 2);
assert_eq!(result.positions_exact, 2);
}
#[test]
fn compare_outputs_threshold_boundary_inclusive() {
let gpu = [2048.0_f32];
let r#ref = [2048.0_f32];
let result = compare_outputs(&gpu, &r#ref, 2048.0);
assert!(result.passed);
assert_eq!(result.positions_checked, 1);
}
#[test]
fn compare_outputs_all_above_threshold_is_not_a_pass() {
let gpu = [5000.0_f32, 6000.0];
let r#ref = [5000.0_f32, 6000.0];
let result = compare_outputs(&gpu, &r#ref, 2048.0);
assert!(!result.passed, "vacuous pass hides lack of evidence");
assert_eq!(result.positions_checked, 0);
}
#[test]
fn compare_outputs_unequal_lengths_compare_only_overlap() {
let gpu = [1.0_f32, 2.0, 3.0];
let r#ref = [1.0_f32, 2.0];
let result = compare_outputs(&gpu, &r#ref, 2048.0);
assert!(result.passed);
assert_eq!(result.positions_checked, 2);
}
#[test]
fn f32_add_reference_sums_elementwise() {
let out = f32_add_reference(&[0.0, 1.0, 1.0, 0.0], &[1.0, 0.0, 1.0, 0.0]);
assert_eq!(out, [1.0, 1.0, 2.0, 0.0]);
}
#[test]
fn f32_add_reference_binary_outputs_stay_below_threshold() {
let a = [0.0_f32, 1.0, 1.0];
let b = [1.0_f32, 1.0, 0.0];
let out = f32_add_reference(&a, &b);
assert!(out.iter().all(|&v| v <= FP_BINARY_THRESHOLD));
}
#[test]
fn aggregator_folds_passing_trials_into_pass() {
let mut agg = VerifyResult::empty_aggregator();
agg.fold_trial(compare_outputs(&[1.0], &[1.0], 2048.0));
agg.fold_trial(compare_outputs(&[2.0, 3.0], &[2.0, 3.0], 2048.0));
assert!(agg.passed);
assert_eq!(agg.trials, 2);
assert_eq!(agg.positions_checked, 3);
assert_eq!(agg.positions_exact, 3);
}
#[test]
fn aggregator_folds_any_failing_trial_into_fail() {
let mut agg = VerifyResult::empty_aggregator();
agg.fold_trial(compare_outputs(&[1.0], &[1.0], 2048.0));
agg.fold_trial(compare_outputs(&[9.0], &[1.0], 2048.0)); assert!(!agg.passed);
assert_eq!(agg.trials, 2);
assert_eq!(agg.positions_checked, 2);
assert_eq!(agg.positions_exact, 1, "only the matching trial counted");
assert_eq!(agg.max_diff, 8.0);
}
}