use super::FabricClass;
pub const BATCH: usize = 16384;
pub const LIMBS: usize = 8;
pub const SHIFTS: usize = 2 * LIMBS - 1;
pub const HOST_LAYOUT: u32 = 1;
pub const IMPLEMENTATION: &str = "npu-limbs4";
const DOMAIN_IMPLEMENTATION: &[u8] = b"wai:quantum-fabric-npu\x01";
pub const CLASS: FabricClass = FabricClass::Npu;
const VARINT: u32 = 0;
const LEN: u32 = 2;
fn varint(o: &mut Vec<u8>, mut v: u64) {
loop {
let b = (v & 0x7f) as u8;
v >>= 7;
if v == 0 {
o.push(b);
return;
}
o.push(b | 0x80);
}
}
fn key(o: &mut Vec<u8>, field: u32, wire: u32) {
varint(o, u64::from(field << 3 | wire));
}
fn uint(o: &mut Vec<u8>, field: u32, v: u64) {
key(o, field, VARINT);
varint(o, v);
}
fn int(o: &mut Vec<u8>, field: u32, v: i64) {
key(o, field, VARINT);
varint(o, v as u64);
}
fn bytes(o: &mut Vec<u8>, field: u32, b: &[u8]) {
key(o, field, LEN);
varint(o, b.len() as u64);
o.extend_from_slice(b);
}
fn msg(o: &mut Vec<u8>, field: u32, f: impl FnOnce(&mut Vec<u8>)) {
let mut inner = Vec::new();
f(&mut inner);
bytes(o, field, &inner);
}
const ARRAY_FLOAT16: u64 = 65552;
const OPSET: &[u8] = b"CoreML8"; const MIL_BOOL: u64 = 1;
const MIL_STRING: u64 = 2;
const MIL_FLOAT16: u64 = 10;
const MIL_INT32: u64 = 23;
fn tensor_type(o: &mut Vec<u8>, dtype: u64, shape: &[u64]) {
msg(o, 1, |t| {
uint(t, 1, dtype); if !shape.is_empty() {
int(t, 2, shape.len() as i64); }
for &d in shape {
msg(t, 3, |dim| msg(dim, 1, |c| uint(c, 1, d)));
}
});
}
fn feature(o: &mut Vec<u8>, field: u32, name: &str, shape: &[u64]) {
msg(o, field, |f| {
bytes(f, 1, name.as_bytes());
msg(f, 3, |t| {
msg(t, 5, |a| {
for &d in shape {
int(a, 1, d as i64);
}
uint(a, 2, ARRAY_FLOAT16);
})
});
});
}
fn named_input(o: &mut Vec<u8>, name: &str, shape: &[u64]) {
msg(o, 1, |n| {
bytes(n, 1, name.as_bytes());
msg(n, 2, |v| tensor_type(v, MIL_FLOAT16, shape));
});
}
fn name_attribute(o: &mut Vec<u8>, name: &str) {
msg(o, 5, |e| {
bytes(e, 1, b"name");
msg(e, 2, |v| {
msg(v, 2, |t| tensor_type(t, MIL_STRING, &[])); msg(v, 3, |iv| msg(iv, 1, |tv| msg(tv, 4, |s| bytes(s, 1, name.as_bytes()))));
});
});
}
fn input_binding(o: &mut Vec<u8>, param: &str, var: &str) {
msg(o, 2, |e| {
bytes(e, 1, param.as_bytes());
msg(e, 2, |arg| msg(arg, 1, |b| bytes(b, 1, var.as_bytes())));
});
}
fn op_output(o: &mut Vec<u8>, name: &str, dtype: u64, shape: &[u64]) {
msg(o, 3, |n| {
bytes(n, 1, name.as_bytes());
msg(n, 2, |v| tensor_type(v, dtype, shape));
});
}
fn const_op(o: &mut Vec<u8>, name: &str, dtype: u64, shape: &[u64], write: impl FnOnce(&mut Vec<u8>)) {
msg(o, 3, |op| {
bytes(op, 1, b"const");
op_output(op, name, dtype, shape);
msg(op, 5, |e| {
bytes(e, 1, b"val");
msg(e, 2, |v| {
msg(v, 2, |t| tensor_type(t, dtype, shape));
msg(v, 3, |iv| msg(iv, 1, write));
});
});
name_attribute(op, name);
});
}
pub fn limb_product_model(batch: u64, shifts: u64, limbs: u64) -> Vec<u8> {
let full = [batch, shifts, limbs];
let out = [batch, shifts];
let mut m = Vec::new();
uint(&mut m, 1, 9); msg(&mut m, 2, |d| {
feature(d, 1, "x", &full);
feature(d, 1, "y", &full);
feature(d, 10, "z", &out);
});
msg(&mut m, 502, |p| {
int(p, 1, 1);
msg(p, 2, |e| {
bytes(e, 1, b"main");
msg(e, 2, |f| {
named_input(f, "x", &full);
named_input(f, "y", &full);
bytes(f, 2, OPSET); msg(f, 3, |e| {
bytes(e, 1, OPSET);
msg(e, 2, |b| {
bytes(b, 2, b"z"); msg(b, 3, |op| {
bytes(op, 1, b"mul");
input_binding(op, "x", "x");
input_binding(op, "y", "y");
op_output(op, "mul_0", MIL_FLOAT16, &full);
name_attribute(op, "mul_0");
});
const_op(b, "z_axes_0", MIL_INT32, &[1], |tv| msg(tv, 2, |ints| int(ints, 1, -1)));
const_op(b, "z_keep_dims_0", MIL_BOOL, &[], |tv| msg(tv, 3, |bools| uint(bools, 1, 0)));
msg(b, 3, |op| {
bytes(op, 1, b"reduce_sum");
input_binding(op, "x", "mul_0");
input_binding(op, "axes", "z_axes_0");
input_binding(op, "keep_dims", "z_keep_dims_0");
op_output(op, "z", MIL_FLOAT16, &out);
name_attribute(op, "z");
});
});
});
});
});
});
m
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Elem {
F16,
I32,
}
const ONNX_FLOAT16: u64 = 10;
const ONNX_INT32: u64 = 6;
const ONNX_INT64: u64 = 7;
const ONNX_ATTR_INT: u64 = 2;
fn onnx_value(o: &mut Vec<u8>, field: u32, name: &str, elem: u64, shape: &[u64]) {
msg(o, field, |v| {
bytes(v, 1, name.as_bytes());
msg(v, 2, |t| {
msg(t, 1, |tt| {
uint(tt, 1, elem);
msg(tt, 2, |sh| {
for &d in shape {
msg(sh, 1, |dim| int(dim, 1, d as i64));
}
});
})
});
});
}
pub fn onnx_limb_model(batch: u64, shifts: u64, limbs: u64, elem: Elem) -> Vec<u8> {
let e = match elem {
Elem::F16 => ONNX_FLOAT16,
Elem::I32 => ONNX_INT32,
};
let full = [batch, shifts, limbs];
let out = [batch, shifts];
let mut m = Vec::new();
int(&mut m, 1, 8); bytes(&mut m, 2, b"wai-quantum"); msg(&mut m, 7, |g| {
msg(g, 1, |n| {
bytes(n, 1, b"x");
bytes(n, 1, b"y");
bytes(n, 2, b"p");
bytes(n, 3, b"mul");
bytes(n, 4, b"Mul");
});
msg(g, 1, |n| {
bytes(n, 1, b"p");
bytes(n, 1, b"axes");
bytes(n, 2, b"z");
bytes(n, 3, b"sum");
bytes(n, 4, b"ReduceSum");
msg(n, 5, |a| {
bytes(a, 1, b"keepdims");
int(a, 3, 0);
uint(a, 20, ONNX_ATTR_INT);
});
});
bytes(g, 2, b"limb_products");
msg(g, 5, |t| {
int(t, 1, 1); uint(t, 2, ONNX_INT64); int(t, 7, -1); bytes(t, 8, b"axes"); });
onnx_value(g, 11, "x", e, &full);
onnx_value(g, 11, "y", e, &full);
onnx_value(g, 12, "z", e, &out);
});
msg(&mut m, 8, |op| {
bytes(op, 1, b"");
int(op, 2, 17);
});
m
}
pub fn expand_i32(pairs: &[(i32, i32)], x: &mut [i32], y: &mut [i32]) {
assert!(pairs.len() <= BATCH && x.len() == BATCH * SHIFTS * LIMBS && y.len() == x.len());
x.fill(0);
y.fill(0);
for (p, &(a, b)) in pairs.iter().enumerate() {
let (la, lb) = (limbs(a), limbs(b));
for s in 0..SHIFTS {
for j in 0..LIMBS {
if s >= j && s - j < LIMBS {
let at = (p * SHIFTS + s) * LIMBS + j;
x[at] = la[j];
y[at] = lb[s - j];
}
}
}
}
}
pub fn implementation_id_of(model: &[u8]) -> String {
let mut o = DOMAIN_IMPLEMENTATION.to_vec();
o.extend_from_slice(&(model.len() as u64).to_be_bytes());
o.extend_from_slice(model);
o.extend_from_slice(&HOST_LAYOUT.to_be_bytes());
let h: String = blake3::hash(&o).as_bytes()[..8].iter().map(|b| format!("{b:02x}")).collect();
format!("{IMPLEMENTATION}/{h}")
}
pub fn model() -> Vec<u8> {
limb_product_model(BATCH as u64, SHIFTS as u64, LIMBS as u64)
}
pub fn implementation_id() -> String {
implementation_id_of(&model())
}
pub fn limbs(v: i32) -> [i32; LIMBS] {
let u = v as u32;
let mut out = [0i32; LIMBS];
for (i, l) in out.iter_mut().enumerate() {
*l = ((u >> (4 * i)) & 15) as i32;
}
if out[LIMBS - 1] >= 8 {
out[LIMBS - 1] -= 16;
}
out
}
pub fn f16_of(v: i32) -> u16 {
debug_assert!(v.abs() <= 2048);
if v == 0 {
return 0;
}
let sign: u16 = if v < 0 { 0x8000 } else { 0 };
let m = v.unsigned_abs();
let e = 31 - m.leading_zeros(); let below = m - (1 << e);
let frac = if e <= 10 { below << (10 - e) } else { below >> (e - 10) } as u16;
sign | (((e + 15) as u16) << 10) | frac
}
pub fn int_of_f16(bits: u16) -> Option<i64> {
let sign = if bits & 0x8000 != 0 { -1 } else { 1 };
let e = i32::from((bits >> 10) & 0x1f);
let frac = i64::from(bits & 0x3ff);
if e == 0 {
return (frac == 0).then_some(0);
}
if e == 31 {
return None;
}
let p = e - 15; if !(0..=11).contains(&p) {
return None;
}
let m = (1024 + frac) << p; (m & 1023 == 0).then_some(sign * (m >> 10))
}
pub fn expand(pairs: &[(i32, i32)], x: &mut [u16], y: &mut [u16]) {
assert!(pairs.len() <= BATCH && x.len() == BATCH * SHIFTS * LIMBS && y.len() == x.len());
x.fill(0);
y.fill(0);
for (p, &(a, b)) in pairs.iter().enumerate() {
let (la, lb) = (limbs(a), limbs(b));
for s in 0..SHIFTS {
for j in 0..LIMBS {
if s >= j && s - j < LIMBS {
let at = (p * SHIFTS + s) * LIMBS + j;
x[at] = f16_of(la[j]);
y[at] = f16_of(lb[s - j]);
}
}
}
}
}
pub fn assemble(z: &[i64]) -> i64 {
z.iter().rev().fold(0i64, |acc, &c| acc.wrapping_mul(16).wrapping_add(c))
}
pub fn replay(x: &[u16], y: &[u16]) -> Vec<i64> {
x.chunks(LIMBS).zip(y.chunks(LIMBS)).map(|(a, b)| a.iter().zip(b).map(|(&p, &q)| int_of_f16(p).unwrap_or(0) * int_of_f16(q).unwrap_or(0)).sum()).collect()
}
pub fn mutate_model(op_from: &str, op_to: &str) -> Vec<u8> {
let m = model();
let (from, to) = (op_from.as_bytes(), op_to.as_bytes());
assert_eq!(from.len(), to.len(), "a mutant keeps the operation name's length");
let at = m.windows(from.len()).position(|w| w == from).expect("the operation is in the model");
let mut out = m;
out[at..at + to.len()].copy_from_slice(to);
out
}
#[cfg(test)]
mod tests {
use super::*;
use crate::quantum_fabric::lanes;
#[test]
fn small_integers_round_trip_through_fp16_exactly() {
for v in -2048..=2048 {
assert_eq!(int_of_f16(f16_of(v)), Some(i64::from(v)), "{v}");
}
assert_eq!(f16_of(1), 0x3c00);
assert_eq!(f16_of(-2), 0xc000);
assert_eq!(f16_of(1800), 0x6708);
assert_eq!(int_of_f16(0x3800), None, "one half is no integer");
assert_eq!(int_of_f16(0x7c00), None, "infinity is no integer");
}
#[test]
fn limbs_reassemble_and_every_value_stays_exact_in_fp16() {
for v in [0, 1, -1, 7, -8, i32::MAX, i32::MIN, 0x7654_3210, -0x0f0f_0f0f] {
let l = limbs(v);
assert_eq!(l.iter().rev().fold(0i64, |a, &x| a * 16 + i64::from(x)), i64::from(v), "{v}");
assert!(l[..LIMBS - 1].iter().all(|x| (0..=15).contains(x)) && (-8..=7).contains(&l[LIMBS - 1]));
}
assert!(LIMBS as i64 * 15 * 15 <= 2048);
}
#[test]
fn a_batch_replayed_assembles_every_product_exactly() {
let mut pairs = lanes::probe_pairs(4096, 0x11);
pairs.truncate(BATCH);
let (mut x, mut y) = (vec![0u16; BATCH * SHIFTS * LIMBS], vec![0u16; BATCH * SHIFTS * LIMBS]);
expand(&pairs, &mut x, &mut y);
let z = replay(&x, &y);
assert!(z.iter().all(|c| c.abs() <= 1800));
for (p, &(a, b)) in pairs.iter().enumerate() {
let got = assemble(&z[p * SHIFTS..(p + 1) * SHIFTS]);
assert_eq!(got, i64::from(a) * i64::from(b), "({a}, {b})");
}
}
#[test]
fn the_portable_model_names_its_program_and_its_element_type() {
for (elem, code) in [(Elem::F16, ONNX_FLOAT16), (Elem::I32, ONNX_INT32)] {
let m = onnx_limb_model(BATCH as u64, SHIFTS as u64, LIMBS as u64, elem);
assert_eq!(&m[..2], &[0x08, 8], "ir_version first");
for op in [&b"Mul"[..], b"ReduceSum", b"keepdims", b"axes"] {
assert!(m.windows(op.len()).any(|w| w == op));
}
assert!(m.windows(2).any(|w| w == [0x08, code as u8]));
}
assert_ne!(onnx_limb_model(4, 15, 8, Elem::F16), onnx_limb_model(4, 15, 8, Elem::I32));
let pairs = [(i32::MIN, -1), (0x7654_3210, 0x0123_4567)];
let n = BATCH * SHIFTS * LIMBS;
let (mut x, mut y) = (vec![0i32; n], vec![0i32; n]);
expand_i32(&pairs, &mut x, &mut y);
for (p, &(a, b)) in pairs.iter().enumerate() {
let z: Vec<i64> = (0..SHIFTS).map(|s| (0..LIMBS).map(|j| i64::from(x[(p * SHIFTS + s) * LIMBS + j]) * i64::from(y[(p * SHIFTS + s) * LIMBS + j])).sum()).collect();
assert_eq!(assemble(&z), i64::from(a) * i64::from(b));
}
}
#[test]
fn the_model_is_the_program_it_names_and_its_mutants_differ() {
let m = model();
assert_eq!(m[0], 0x08, "field 1, specificationVersion, comes first");
assert_eq!(m[1], 9);
for op in [&b"mul"[..], b"reduce_sum", OPSET] {
assert!(m.windows(op.len()).any(|w| w == op));
}
let id = implementation_id();
assert!(id.starts_with("npu-limbs4/") && id.len() == "npu-limbs4/".len() + 16);
assert_ne!(implementation_id_of(&mutate_model("mul", "add")), id);
assert_ne!(implementation_id_of(&mutate_model("reduce_sum", "reduce_max")), id);
}
}