use hanzo_kernel::norm::{rms_norm_ref, rms_norm_run};
use hanzo_kernel::prelude::*;
const ROWS: usize = 37;
const N: usize = 128;
const EPS: f32 = 1e-5;
fn data() -> (Vec<f32>, Vec<f32>) {
let mut s = 0x2545F491_4F6CDD1Du64;
let mut next = || {
s ^= s << 13;
s ^= s >> 7;
s ^= s << 17;
(s % 2000) as f32 / 1000.0 - 1.0
};
let x = (0..ROWS * N).map(|_| next()).collect();
let w = (0..N).map(|_| next() * 0.5 + 1.0).collect();
(x, w)
}
fn max_rel(want: &[f32], got: &[f32]) -> f32 {
want.iter()
.zip(got)
.map(|(a, b)| (a - b).abs() / a.abs().max(1e-6))
.fold(0.0, f32::max)
}
fn run<R: Runtime>(client: &ComputeClient<R>) -> bool {
let (x, w) = data();
let got = rms_norm_run::<R>(client, &x, &w, ROWS, N, EPS);
let want = rms_norm_ref(&x, &w, ROWS, N, EPS);
let rel = max_rel(&want, &got);
let ok = rel < 2e-3;
let checksum: f64 = got.iter().map(|&v| v as f64).sum();
println!(
"target={:?} runtime={} {}x{}",
Target::of(client),
R::name(client),
ROWS,
N
);
println!(" out[0..4] = {:?}", &got[..4]);
println!(" oracle[0..4] = {:?}", &want[..4]);
println!(
" checksum = {checksum:.6} max_rel = {rel:.3e} {}",
if ok { "MATCH" } else { "MISMATCH" }
);
ok
}
fn main() {
let mut ran = 0usize;
let mut ok = true;
#[cfg(feature = "cpu")]
{
use hanzo_kernel::cubecl::cpu::{CpuDevice, CpuRuntime};
ok &= run::<CpuRuntime>(&CpuRuntime::client(&CpuDevice::default()));
ran += 1;
}
#[cfg(feature = "rocm")]
{
use hanzo_cubecl_hip::{AmdDevice, HipRuntime};
ok &= run::<HipRuntime>(&HipRuntime::client(&AmdDevice::default()));
ran += 1;
}
#[cfg(feature = "cuda")]
{
use hanzo_kernel::cubecl::cuda::{CudaDevice, CudaRuntime};
ok &= run::<CudaRuntime>(&CudaRuntime::client(&CudaDevice::default()));
ran += 1;
}
#[cfg(any(feature = "metal", feature = "vulkan"))]
{
use hanzo_kernel::cubecl::wgpu::{WgpuDevice, WgpuRuntime};
ok &= run::<WgpuRuntime>(&WgpuRuntime::client(&WgpuDevice::default()));
ran += 1;
}
println!(
"\n{ran} backend(s) ran from ONE kernel source: {}",
if ok { "all MATCH" } else { "MISMATCH" }
);
if !ok {
std::process::exit(1);
}
}