#![cfg(target_os = "macos")]
use nuvec_core::*;
fn gpu() -> bool {
set_default_stream(Stream::default_for(Device::gpu(0)));
let ok = !cfg!(miri) && (&Array::scalar(1.0f32) + 1.0).eval().is_ok();
if !ok {
eprintln!("skipped: no Metal device");
}
ok
}
fn normal(shape: &[usize], dtype: DType, seed: u64) -> Array {
let n: usize = shape.iter().product();
let t = arange(0.0, n as f64, 1.0, DType::F32).unwrap();
let x = sin(&(&t * (0.7381 + seed as f64 * 0.0137))).unwrap();
let x = &x * cos(&(&t * (0.0071 * seed as f64 + 0.013))).unwrap();
let shape: Vec<isize> = shape.iter().map(|&d| d as isize).collect();
x.reshape(&shape).unwrap().astype(dtype).unwrap()
}
fn workload() -> Result<Vec<Array>> {
let (f32_, bf16_) = (DType::F32, DType::BF16);
let mut out = Vec::new();
out.push(matmul(
&normal(&[32, 16384], f32_, 1),
&normal(&[16384, 32], f32_, 2),
)?);
out.push(matmul(
&normal(&[512, 1024], bf16_, 3),
&normal(&[1024, 768], bf16_, 4),
)?);
out.push(matmul(
&normal(&[2048, 256], bf16_, 34),
&normal(&[256, 2048], bf16_, 35),
)?);
let w = normal(&[4096, 4096], bf16_, 5);
out.push(matmul(&normal(&[1, 4096], bf16_, 6), &w.t()?)?);
let (wq, scales, biases) = quantize(&w, 64, 4)?;
for (rows, seed) in [(1, 7), (8, 8), (256, 9)] {
let x = normal(&[rows, 4096], bf16_, seed);
out.push(quantized_matmul(&x, &wq, &scales, &biases, true, 64, 4)?);
}
out.push(quantized_matmul(
&normal(&[1, 4096], bf16_, 10),
&wq,
&scales,
&biases,
false,
64,
4,
)?);
let experts = normal(&[16, 512, 1024], bf16_, 11);
let (eq, es, eb) = quantize(&experts, 64, 4)?;
let ids: Vec<u32> = (0..512u32).map(|i| i * 16 / 512).collect();
let ids = Array::from_slice(&ids, &[512])?;
out.push(gather_qmm(
&normal(&[512, 1, 1024], bf16_, 12),
&eq,
&es,
&eb,
None,
Some(&ids),
true,
64,
4,
true,
)?);
let (k, v) = (
normal(&[1, 4, 8192, 128], bf16_, 13),
normal(&[1, 4, 8192, 128], bf16_, 14),
);
out.push(fast::scaled_dot_product_attention(
&normal(&[1, 16, 1, 128], bf16_, 15),
&k,
&v,
0.088,
fast::AttentionMask::None,
None,
)?);
let (k, v) = (
normal(&[1, 4, 300, 128], bf16_, 16),
normal(&[1, 4, 300, 128], bf16_, 17),
);
out.push(fast::scaled_dot_product_attention(
&normal(&[1, 16, 300, 128], bf16_, 18),
&k,
&v,
0.088,
fast::AttentionMask::Causal,
None,
)?);
out.push(sum(&normal(&[1 << 22], f32_, 19), &[], false)?);
let rows = normal(&[64, 32768], bf16_, 20);
out.push(logsumexp(&rows, &[-1], false)?);
out.push(softmax(&rows, &[-1])?);
out.push(max(&rows, &[0], false)?);
out.push(sum(&rows, &[0], false)?);
let long = normal(&[4, 50000], f32_, 21);
out.push(sort(&long, Some(-1))?);
out.push(argsort(&long, Some(-1))?);
out.push(cumsum(
&normal(&[4, 100000], f32_, 22),
Some(-1),
false,
true,
)?);
let (h, n) = fast::add_rms_norm(
&normal(&[64, 4096], bf16_, 23),
&normal(&[64, 4096], bf16_, 24),
Some(&normal(&[4096], bf16_, 25)),
1e-6,
)?;
out.extend([h, n]);
let rotation = fast::Rotation {
dims: 128,
traditional: false,
base: Some(1e6),
scale: 1.0,
freqs: None,
};
out.push(fast::norm_rope(
&transpose(&normal(&[1, 64, 8, 128], bf16_, 26), &[0, 2, 1, 3])?,
Some((&normal(&[128], bf16_, 27), 1e-6)),
rotation,
40,
Some(&normal(&[1, 8, 256, 128], bf16_, 28)),
)?);
let (y, state) = fast::gated_delta(
&(normal(&[1, 64, 4, 128], bf16_, 29) * 0.05),
&(normal(&[1, 64, 4, 128], bf16_, 30) * 0.05),
&normal(&[1, 64, 8, 128], bf16_, 31),
&sigmoid(&normal(&[1, 64, 8], f32_, 32))?,
&sigmoid(&normal(&[1, 64, 8], bf16_, 33))?,
&zeros(&[1, 8, 128, 128], f32_)?,
)?;
out.extend([y, state]);
Ok(out)
}
fn bits(a: &Array) -> Vec<u64> {
match a.dtype().category() {
Category::Float => a
.astype(DType::F32)
.unwrap()
.to_vec::<f32>()
.unwrap()
.into_iter()
.map(|v| u64::from(v.to_bits()))
.collect(),
_ => a
.astype(DType::I64)
.unwrap()
.to_vec::<i64>()
.unwrap()
.into_iter()
.map(|v| v as u64)
.collect(),
}
}
#[test]
fn results_are_bit_identical_across_runs() {
if !gpu() {
return;
}
let first: Vec<Vec<u64>> = workload().unwrap().iter().map(bits).collect();
let mut pending: Option<Vec<Array>> = None;
for run in 0..5 {
let noise: Vec<Array> = (0..(run + 1) * 3)
.map(|i| normal(&[1000 + 997 * i, 64], DType::F32, 100 + i as u64) * 2.0)
.collect();
async_eval(&noise).unwrap();
let outputs = workload().unwrap();
async_eval(&outputs).unwrap();
drop(noise);
if let Some(previous) = pending.replace(outputs) {
check(&first, &previous, run);
}
}
check(&first, &pending.unwrap(), 5);
}
fn check(first: &[Vec<u64>], outputs: &[Array], run: usize) {
assert_eq!(first.len(), outputs.len());
for (i, (want, got)) in first.iter().zip(outputs).enumerate() {
let got = bits(got);
let differ = want.iter().zip(&got).filter(|(a, b)| a != b).count();
assert_eq!(
differ, 0,
"run {run}: output {i} differs in {differ} elements"
);
}
}