use rane::mil::{self, OutputDtype};
use rane::{f32_to_fp16, fp16_to_f32, Buffer, Program};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let oc = 64usize;
let seq = 64usize;
println!("ANE matmul โ int output probe");
println!(" hypothesis: int8/int16 cast reads from int32 accumulator (not fp16)\n");
println!("--- v=1, ic=64, expected=64 ---");
test_dtype("fp16", 64, oc, seq, 1.0, 64, OutputDtype::Fp16)?;
test_dtype("int16", 64, oc, seq, 1.0, 64, OutputDtype::Int16)?;
test_dtype("int8", 64, oc, seq, 1.0, 64, OutputDtype::Int8)?;
test_dtype("uint8", 64, oc, seq, 1.0, 64, OutputDtype::UInt8)?;
println!("\n--- v=10, ic=64, expected=6400 ---");
test_dtype("fp16", 64, oc, seq, 10.0, 6400, OutputDtype::Fp16)?;
test_dtype("int16", 64, oc, seq, 10.0, 6400, OutputDtype::Int16)?;
test_dtype("int8", 64, oc, seq, 10.0, 6400, OutputDtype::Int8)?;
println!("\n--- v=23, ic=64, expected=33856 (fp16 should be inf) ---");
test_dtype("fp16", 64, oc, seq, 23.0, 33856, OutputDtype::Fp16)?;
test_dtype("int16", 64, oc, seq, 23.0, 33856, OutputDtype::Int16)?;
test_dtype("int8", 64, oc, seq, 23.0, 33856, OutputDtype::Int8)?;
println!("\n--- v=31, ic=128, expected=123008 ---");
test_dtype("fp16", 128, oc, seq, 31.0, 123008, OutputDtype::Fp16)?;
test_dtype("int16", 128, oc, seq, 31.0, 123008, OutputDtype::Int16)?;
test_dtype("int8", 128, oc, seq, 31.0, 123008, OutputDtype::Int8)?;
Ok(())
}
fn test_dtype(
label: &str,
ic: usize,
oc: usize,
seq: usize,
fill_val: f32,
expected: i32,
dtype: OutputDtype,
) -> Result<(), Box<dyn std::error::Error>> {
let program = mil::matmul_cast(ic, oc, seq, dtype);
let mut model = match Program::compile(&program, &[]) {
Ok(m) => m,
Err(e) => {
println!(
" [{label:6}] compile FAILED: {}",
format!("{e}").chars().take(80).collect::<String>()
);
return Ok(());
}
};
if let Err(e) = model.load() {
println!(
" [{label:6}] load FAILED: {}",
format!("{e}").chars().take(80).collect::<String>()
);
return Ok(());
}
let input = Buffer::new(program.input_size())?;
let output = Buffer::new(program.output_size())?;
let sp = program.input_spatial;
let vf = f32_to_fp16(fill_val);
input.write(|data| {
for d in data.iter_mut() {
*d = 0;
}
for ch in 0..ic {
for s in 0..seq {
data[ch * sp + s] = vf;
}
for o in 0..oc {
data[ch * sp + seq + o] = vf;
}
}
});
if let Err(e) = model.run(&input, &output) {
println!(
" [{label:6}] eval FAILED: {}",
format!("{e}").chars().take(80).collect::<String>()
);
return Ok(());
}
let (got_str, n) = (oc * seq, oc * seq);
let val: String;
let n_check: usize;
match dtype {
OutputDtype::Fp16 => {
(val, n_check) = output.read(|data| {
let v0 = fp16_to_f32(data[0]);
let nc = data[..n]
.iter()
.filter(|&&v| fp16_to_f32(v) as i32 == expected)
.count();
(format!("{v0:.1}"), nc)
});
}
OutputDtype::Int16 => {
(val, n_check) = output.read_i16(|data| {
let v0 = data[0] as i32;
let nc = data[..n].iter().filter(|&&v| v as i32 == expected).count();
(format!("{v0}"), nc)
});
}
OutputDtype::Int8 => {
(val, n_check) = output.read_i8(|data| {
let v0 = data[0] as i32;
let nc = data[..n].iter().filter(|&&v| v as i32 == expected).count();
(format!("{v0}"), nc)
});
}
OutputDtype::UInt8 => {
(val, n_check) = output.read_bytes(|data| {
let v0 = data[0] as i32;
let nc = data[..n]
.iter()
.filter(|&&v| v as i32 == (expected & 0xff))
.count();
(format!("{v0}"), nc)
});
}
_ => {
(val, n_check) = (format!("?"), 0);
}
}
let _ = got_str;
println!(" [{label:6}] got={val:>10} expected={expected:>8} ({n_check}/{n})");
Ok(())
}