use std::fmt::Write as _;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Constants {
pub round: i64,
pub shift: i64,
pub last_add: bool,
}
pub const CONFORMING: Constants = Constants { round: 1 << 29, shift: 30, last_add: false };
struct Body {
text: String,
next: usize,
n: usize,
}
impl Body {
fn ty(&self) -> String {
format!("tensor<{}xi64>", self.n)
}
fn op(&mut self, rhs: String) -> String {
let v = format!("%v{}", self.next);
self.next += 1;
let _ = writeln!(self.text, " {v} = {rhs}");
v
}
fn constant(&mut self, c: i64) -> String {
let t = self.ty();
self.op(format!("stablehlo.constant dense<{c}> : {t}"))
}
fn binary(&mut self, name: &str, a: &str, b: &str) -> String {
let t = self.ty();
self.op(format!("stablehlo.{name} {a}, {b} : {t}"))
}
fn widen(&mut self, a: &str) -> String {
let (n, t) = (self.n, self.ty());
self.op(format!("stablehlo.convert {a} : (tensor<{n}xi32>) -> {t}"))
}
fn splat(&mut self, scalar: &str) -> String {
let t = self.ty();
self.op(format!("stablehlo.broadcast_in_dim {scalar}, dims = [] : (tensor<i64>) -> {t}"))
}
fn fx(&mut self, e: &str, a: &str, round: &str, shift: &str) -> String {
let p = self.binary("multiply", e, a);
let r = self.binary("add", &p, round);
self.binary("shift_right_arithmetic", &r, shift)
}
}
pub fn module(n: usize, c: Constants) -> String {
let t = format!("tensor<{n}xi64>");
let ti = format!("tensor<{n}xi32>");
let mut out = String::new();
let _ = writeln!(out, "// The gate's rows and the arithmetic probe, exact in 64-bit integers.");
let _ = writeln!(out, "module @wai_quantum_gate {{");
let mut b = Body { text: String::new(), next: 0, n };
let round = b.constant(c.round);
let shift = b.constant(c.shift);
let e: Vec<String> = (0..8).map(|k| b.splat(&format!("%e{k}"))).collect();
let a: Vec<String> = ["%a0r", "%a0i", "%a1r", "%a1i"].iter().map(|x| b.widen(x)).collect();
let last = if c.last_add { "add" } else { "subtract" };
let terms: [[(usize, usize, &str); 4]; 4] = [
[(0, 0, "add"), (1, 1, "subtract"), (2, 2, "add"), (3, 3, last)],
[(0, 1, "add"), (1, 0, "add"), (2, 3, "add"), (3, 2, "add")],
[(4, 0, "add"), (5, 1, "subtract"), (6, 2, "add"), (7, 3, "subtract")],
[(4, 1, "add"), (5, 0, "add"), (6, 3, "add"), (7, 2, "add")],
];
let mut rows = Vec::new();
for row in terms {
let mut acc = b.fx(&e[row[0].0], &a[row[0].1], &round, &shift);
for &(k, op, how) in &row[1..] {
let f = b.fx(&e[k], &a[op], &round, &shift);
acc = b.binary(how, &acc, &f);
}
rows.push(acc);
}
let params = (0..8).map(|k| format!("%e{k}: tensor<i64>")).chain(["%a0r", "%a0i", "%a1r", "%a1i"].iter().map(|x| format!("{x}: {ti}"))).collect::<Vec<_>>().join(", ");
let _ = writeln!(out, " func.func @rows({params}) -> ({t}, {t}, {t}, {t}) {{");
out.push_str(&b.text);
let _ = writeln!(out, " return {} : {t}, {t}, {t}, {t}", rows.join(", "));
let _ = writeln!(out, " }}");
let mut b = Body { text: String::new(), next: 0, n };
let round = b.constant(c.round);
let shift = b.constant(c.shift);
let (wa, wb) = (b.widen("%a"), b.widen("%b"));
let prod = b.binary("multiply", &wa, &wb);
let fx = b.fx(&wb, &wa, &round, &shift);
let _ = writeln!(out, " func.func @probe(%a: {ti}, %b: {ti}) -> ({t}, {t}) {{");
out.push_str(&b.text);
let _ = writeln!(out, " return {prod}, {fx} : {t}, {t}");
let _ = writeln!(out, " }}");
let _ = writeln!(out, "}}");
out
}
pub fn limb_module(n: usize) -> String {
use super::tensor_engine::{K, M};
let (w, a, c) = (format!("tensor<{K}x{M}xbf16>"), format!("tensor<{K}x{n}xbf16>"), format!("tensor<{M}x{n}xf32>"));
format!(
"// The gate's products as limb coefficients: one bf16 matrix multiply, f32 results.\n\
module @wai_quantum_limbs {{\n\
\x20 func.func @coefficients(%w: {w}, %a: {a}) -> {c} {{\n\
\x20 %c = stablehlo.dot_general %w, %a, contracting_dims = [0] x [0] : ({w}, {a}) -> {c}\n\
\x20 return %c : {c}\n\
\x20 }}\n\
}}\n"
)
}
pub fn reference_coefficients(w: &[i32], a: &[i32], n: usize) -> Vec<f32> {
use super::tensor_engine::{K, M};
let mut c = vec![0f32; M * n];
for col in 0..M {
for q in 0..n {
let v: i64 = (0..K).map(|k| i64::from(w[k * M + col]) * i64::from(a[k * n + q])).sum();
c[col * n + q] = v as f32;
}
}
c
}
pub fn reference_rows(m: &[i64; 8], cols: &[Vec<i32>; 4], c: Constants) -> [Vec<i64>; 4] {
let fx = |e: i64, a: i32| (e * i64::from(a) + c.round) >> c.shift;
let last = if c.last_add { 1 } else { -1 };
let mut out: [Vec<i64>; 4] = Default::default();
for (((&a0r, &a0i), &a1r), &a1i) in cols[0].iter().zip(&cols[1]).zip(&cols[2]).zip(&cols[3]) {
out[0].push(fx(m[0], a0r) - fx(m[1], a0i) + fx(m[2], a1r) + last * fx(m[3], a1i));
out[1].push(fx(m[0], a0i) + fx(m[1], a0r) + fx(m[2], a1i) + fx(m[3], a1r));
out[2].push(fx(m[4], a0r) - fx(m[5], a0i) + fx(m[6], a1r) - fx(m[7], a1i));
out[3].push(fx(m[4], a0i) + fx(m[5], a0r) + fx(m[6], a1i) + fx(m[7], a1r));
}
out
}
pub fn npy(descr: &str, shape: &[usize], data: &[u8]) -> Vec<u8> {
let dims = match shape {
[] => "()".to_string(),
[n] => format!("({n},)"),
_ => format!("({})", shape.iter().map(|d| d.to_string()).collect::<Vec<_>>().join(", ")),
};
let mut header = format!("{{'descr': '{descr}', 'fortran_order': False, 'shape': {dims}, }}");
while (10 + header.len() + 1) % 64 != 0 {
header.push(' ');
}
header.push('\n');
let mut out = b"\x93NUMPY\x01\x00".to_vec();
out.extend_from_slice(&(header.len() as u16).to_le_bytes());
out.extend_from_slice(header.as_bytes());
out.extend_from_slice(data);
out
}
pub fn npy_i32(v: &[i32]) -> Vec<u8> {
npy("<i4", &[v.len()], &v.iter().flat_map(|x| x.to_le_bytes()).collect::<Vec<u8>>())
}
pub fn npy_i64(v: &[i64]) -> Vec<u8> {
npy("<i8", &[v.len()], &v.iter().flat_map(|x| x.to_le_bytes()).collect::<Vec<u8>>())
}
pub fn npy_scalar_i64(v: i64) -> Vec<u8> {
npy("<i8", &[], &v.to_le_bytes())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_module_has_both_functions_and_its_mutants_differ() {
let m = module(64, CONFORMING);
assert!(m.contains("func.func @rows(") && m.contains("func.func @probe("));
assert_eq!(m.matches("stablehlo.shift_right_arithmetic").count(), 16 + 1);
let l = limb_module(512);
assert!(l.contains("func.func @coefficients(%w: tensor<16x112xbf16>, %a: tensor<16x512xbf16>) -> tensor<112x512xf32>"), "{l}");
assert!(l.contains("stablehlo.dot_general %w, %a, contracting_dims = [0] x [0]"));
for c in [Constants { round: 1 << 28, ..CONFORMING }, Constants { shift: 29, ..CONFORMING }, Constants { last_add: true, ..CONFORMING }] {
assert_ne!(module(64, c), m);
}
}
#[test]
fn npy_headers_are_aligned_and_reference_rows_are_the_gate() {
for v in [npy_i32(&[1, 2, 3]), npy_i64(&[7; 512]), npy_scalar_i64(-5)] {
let hl = u16::from_le_bytes([v[8], v[9]]) as usize;
assert_eq!((10 + hl) % 64, 0);
assert_eq!(v[10 + hl - 1], b'\n');
}
let one = 1i64 << 30;
let m = [one, 0, 0, 0, 0, 0, one, 0];
let r = reference_rows(&m, &[vec![5], vec![-6], vec![7], vec![-8]], CONFORMING);
assert_eq!(r.map(|x| x[0]), [5, -6, 7, -8]);
}
}