use crate::gates::Gate;
use crate::linalg::C;
use crate::quantum_fabric::Output;
use crate::quantum_sv::{StateVector, SvError, fuse};
pub const KERNEL_SV_RUN: &str = "wai.quantum.sv/run";
pub const SOURCE: &str = include_str!("sv64.cu");
pub const IMPLEMENTATION: &str = "cuda-sv64";
pub const HOST_LAYOUT: u32 = 1;
pub const MAX_D: usize = 64;
pub const COMPILE_OPTIONS: [&str; 2] = ["--std=c++17", "--fmad=false"];
const DOMAIN_IMPLEMENTATION: &[u8] = b"wai:quantum-fabric-sv64\x01";
fn put(o: &mut Vec<u8>, bytes: &[u8]) {
o.extend_from_slice(&(bytes.len() as u64).to_be_bytes());
o.extend_from_slice(bytes);
}
pub fn implementation_id_of(source: &str) -> String {
let mut o = DOMAIN_IMPLEMENTATION.to_vec();
put(&mut o, source.as_bytes());
for option in COMPILE_OPTIONS {
put(&mut o, option.as_bytes());
}
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 implementation_id() -> String {
implementation_id_of(SOURCE)
}
pub const CL_SOURCE: &str = include_str!("sv64.cl");
pub const CL_IMPLEMENTATION: &str = "opencl-sv64";
pub const CL_BUILD_OPTIONS: &str = "-cl-std=CL1.2";
const DOMAIN_CL_IMPLEMENTATION: &[u8] = b"wai:quantum-fabric-sv64-opencl\x01";
pub fn cl_implementation_id_of(source: &str) -> String {
let mut o = DOMAIN_CL_IMPLEMENTATION.to_vec();
put(&mut o, source.as_bytes());
put(&mut o, CL_BUILD_OPTIONS.as_bytes());
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!("{CL_IMPLEMENTATION}/{h}")
}
pub fn cl_implementation_id() -> String {
cl_implementation_id_of(CL_SOURCE)
}
#[derive(Clone, Debug, PartialEq)]
pub enum Launch {
Diag { bitpos: Vec<u32>, diag: Vec<C> },
One { s: u64, m: [C; 4] },
Many { sorted: Vec<u32>, offsets: Vec<u64>, m: Vec<C> },
}
pub fn plan(n: u32, gates: &[Gate], width: usize) -> Result<Vec<Launch>, SvError> {
for (i, g) in gates.iter().enumerate() {
let k = g.qubits.len();
let mut q = g.qubits.clone();
q.sort_unstable();
q.dedup();
if k == 0 || q.len() != k || g.qubits.iter().any(|&x| x >= n) || g.matrix.len() != 1 << (2 * k) {
return Err(SvError::BadGate(i));
}
}
let fused;
let list = if width >= 2 {
fused = fuse(gates, width);
&fused
} else {
gates
};
let mut out = Vec::with_capacity(list.len());
for (i, g) in list.iter().enumerate() {
let k = g.qubits.len();
if 1usize << k > MAX_D {
return Err(SvError::BadGate(i));
}
let bitpos: Vec<u32> = g.qubits.iter().map(|&q| n - 1 - q).collect();
let d = 1usize << k;
if g.is_diagonal() {
out.push(Launch::Diag { diag: (0..d).map(|i| g.matrix[i * d + i]).collect(), bitpos });
continue;
}
let offsets: Vec<u64> = (0..d).map(|local| bitpos.iter().enumerate().fold(0u64, |o, (i, &p)| o | ((((local >> (k - 1 - i)) & 1) as u64) << p))).collect();
if k == 1 {
out.push(Launch::One { s: offsets[1], m: [g.matrix[0], g.matrix[1], g.matrix[2], g.matrix[3]] });
} else {
let mut sorted = bitpos;
sorted.sort_unstable();
out.push(Launch::Many { sorted, offsets, m: g.matrix.clone() });
}
}
Ok(out)
}
fn spread(mut t: u64, pos: &[u32]) -> u64 {
for &p in pos {
let low = t & ((1u64 << p) - 1);
t = ((t >> p) << (p + 1)) | low;
}
t
}
pub fn replay(re: &mut [f64], im: &mut [f64], launches: &[Launch]) {
let len = re.len() as u64;
for l in launches {
match l {
Launch::Diag { bitpos, diag } => {
for x in 0..len {
let local = bitpos.iter().fold(0u64, |a, &p| (a << 1) | ((x >> p) & 1));
let c = diag[local as usize];
if c.re == 1.0 && c.im == 0.0 {
continue;
}
let (ar, ai) = (re[x as usize], im[x as usize]);
re[x as usize] = c.re * ar - c.im * ai;
im[x as usize] = c.re * ai + c.im * ar;
}
}
Launch::One { s, m } => {
let [m00, m01, m10, m11] = *m;
for p in 0..len / 2 {
let low = p & (s - 1);
let i = (((p - low) << 1) | low) as usize;
let j = i + *s as usize;
let (ar, ai, br, bi) = (re[i], im[i], re[j], im[j]);
re[i] = m00.re * ar - m00.im * ai + m01.re * br - m01.im * bi;
im[i] = m00.re * ai + m00.im * ar + m01.re * bi + m01.im * br;
re[j] = m10.re * ar - m10.im * ai + m11.re * br - m11.im * bi;
im[j] = m10.re * ai + m10.im * ar + m11.re * bi + m11.im * br;
}
}
Launch::Many { sorted, offsets, m } => {
let d = offsets.len();
let (mut vr, mut vi) = (vec![0.0; d], vec![0.0; d]);
for t in 0..len >> sorted.len() {
let base = spread(t, sorted);
for (j, &o) in offsets.iter().enumerate() {
vr[j] = re[(base | o) as usize];
vi[j] = im[(base | o) as usize];
}
for (r, &o) in offsets.iter().enumerate() {
let (mut sr, mut si) = (0.0, 0.0);
for c in 0..d {
let e = m[r * d + c];
sr += e.re * vr[c] - e.im * vi[c];
si += e.re * vi[c] + e.im * vr[c];
}
re[(base | o) as usize] = sr;
im[(base | o) as usize] = si;
}
}
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SvCorpus {
pub seed: u64,
pub cases: usize,
pub max_qubits: u32,
pub depth: usize,
}
pub const SV_DEFAULT: SvCorpus = SvCorpus { seed: 7, cases: 48, max_qubits: 10, depth: 30 };
pub const SV_DEFAULT_PIN: &str = "b7d129d0593adf70dc164477664970c5c0b8c09f79f1a0573036bd60888dbcd1";
#[derive(Clone, Debug)]
pub struct SvCase {
pub n: u32,
pub width: usize,
pub gates: Vec<Gate>,
}
fn next(s: &mut u64) -> u64 {
*s = s.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = *s;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
impl SvCorpus {
pub fn generator(&self) -> String {
format!("sv64-v1 seed={} cases={} qubits={} depth={}", self.seed, self.cases, self.max_qubits, self.depth)
}
pub fn cases(&self) -> Vec<SvCase> {
let mut s = self.seed;
(0..self.cases)
.map(|i| {
let n = 2 + (next(&mut s) % u64::from(self.max_qubits.max(2) - 1)) as u32;
let width = [0usize, 2, 3][i % 3];
let mut gates = Vec::with_capacity(self.depth);
for _ in 0..self.depth {
let a = (next(&mut s) % u64::from(n)) as u32;
let mut b = (next(&mut s) % u64::from(n)) as u32;
if b == a {
b = (a + 1) % n;
}
let angle = (next(&mut s) >> 11) as f64 / (1u64 << 53) as f64 * 6.5 - 0.1;
gates.push(match next(&mut s) % 9 {
0 => Gate::rx(a, angle),
1 => Gate::ry(a, angle),
2 => Gate::rz(a, angle),
3 => Gate::sqrt_w(a),
4 => Gate::t(a),
5 => Gate::h(a),
6 => Gate::fsim(a, b, angle, 0.5 * angle),
7 => Gate::cz(a, b),
_ => Gate::iswap(a, b),
});
}
SvCase { n, width, gates }
})
.collect()
}
}
impl SvCase {
pub fn bytes(&self) -> Vec<u8> {
let mut o = Vec::new();
o.extend_from_slice(&self.n.to_le_bytes());
o.extend_from_slice(&(self.width as u64).to_le_bytes());
for g in &self.gates {
o.extend_from_slice(&(g.qubits.len() as u64).to_le_bytes());
for q in &g.qubits {
o.extend_from_slice(&q.to_le_bytes());
}
for c in &g.matrix {
o.extend_from_slice(&c.re.to_bits().to_le_bytes());
o.extend_from_slice(&c.im.to_bits().to_le_bytes());
}
}
o
}
pub fn reference(&self) -> Result<Output, SvError> {
let mut sv = StateVector::zero(self.n)?;
sv.run(&self.gates, self.width)?;
Ok(state_output(&sv.re, &sv.im))
}
}
pub fn state_output(re: &[f64], im: &[f64]) -> Output {
Output::Float(re.iter().zip(im).flat_map(|(&a, &b)| [a, b]).collect())
}
pub const MUTANTS: [crate::quantum_fabric::wgsl::Mutant; 4] = [
crate::quantum_fabric::wgsl::Mutant { name: "S1", find: " if (cr == 1.0 && ci == 0.0) {\n continue;\n }\n", replace: "", detector: crate::quantum_fabric::wgsl::Detector::Certificate },
crate::quantum_fabric::wgsl::Mutant { name: "S2", find: "re[i] = m00r * ar - m00i * ai + m01r * br - m01i * bi;", replace: "re[i] = (m00r * ar - m00i * ai) + (m01r * br - m01i * bi);", detector: crate::quantum_fabric::wgsl::Detector::Certificate },
crate::quantum_fabric::wgsl::Mutant { name: "S3", find: "sr += er * vr[c] - ei * vi[c];", replace: "sr += er * vr[c];\n sr -= ei * vi[c];", detector: crate::quantum_fabric::wgsl::Detector::Certificate },
crate::quantum_fabric::wgsl::Mutant { name: "S4", find: "si += er * vi[c] + ei * vr[c];", replace: "si += er * vi[c] - ei * vr[c];", detector: crate::quantum_fabric::wgsl::Detector::Certificate },
];
pub fn mutate(m: &crate::quantum_fabric::wgsl::Mutant) -> String {
assert_eq!(SOURCE.matches(m.find).count(), 1, "mutant {}", m.name);
SOURCE.replacen(m.find, m.replace, 1)
}
pub fn mutate_cl(m: &crate::quantum_fabric::wgsl::Mutant) -> String {
assert_eq!(CL_SOURCE.matches(m.find).count(), 1, "mutant {}", m.name);
CL_SOURCE.replacen(m.find, m.replace, 1)
}
#[cfg(test)]
mod tests {
use super::*;
fn bits(o: &Output) -> Vec<u64> {
match o {
Output::Float(v) => v.iter().map(|x| x.to_bits()).collect(),
_ => unreachable!(),
}
}
#[test]
fn the_replayed_plan_is_the_cpu_run_bit_for_bit() {
let mut kinds = [0usize; 3];
for case in SV_DEFAULT.cases() {
let launches = plan(case.n, &case.gates, case.width).unwrap();
for l in &launches {
kinds[match l {
Launch::Diag { .. } => 0,
Launch::One { .. } => 1,
Launch::Many { .. } => 2,
}] += 1;
}
let dim = 1usize << case.n;
let (mut re, mut im) = (vec![0.0; dim], vec![0.0; dim]);
re[0] = 1.0;
replay(&mut re, &mut im, &launches);
assert_eq!(bits(&state_output(&re, &im)), bits(&case.reference().unwrap()), "n={} width={}", case.n, case.width);
}
assert!(kinds.iter().all(|&k| k > 10), "every launch kind is exercised: {kinds:?}");
}
#[test]
fn the_cpu_reproduces_the_pinned_digest() {
let mut cert = crate::quantum_fabric::Certification::new();
for case in SV_DEFAULT.cases() {
let r = case.reference().unwrap();
cert.add(&case.bytes(), &r, &r);
}
let c = cert.finish(KERNEL_SV_RUN, "cpu", "cpu", &SV_DEFAULT.generator());
let hex: String = c.corpus.iter().map(|b| format!("{b:02x}")).collect();
assert_eq!(hex, SV_DEFAULT_PIN);
}
#[test]
fn a_replay_with_a_formula_changed_is_caught() {
let differs = SV_DEFAULT.cases().iter().any(|case| {
let launches = plan(case.n, &case.gates, case.width).unwrap();
let dim = 1usize << case.n;
let (mut re, mut im) = (vec![0.0; dim], vec![0.0; dim]);
re[0] = 1.0;
for l in &launches {
if let Launch::One { s, m } = l {
let [m00, m01, m10, m11] = *m;
for p in 0..dim as u64 / 2 {
let low = p & (s - 1);
let i = (((p - low) << 1) | low) as usize;
let j = i + *s as usize;
let (ar, ai, br, bi) = (re[i], im[i], re[j], im[j]);
re[i] = (m00.re * ar - m00.im * ai) + (m01.re * br - m01.im * bi);
im[i] = m00.re * ai + m00.im * ar + m01.re * bi + m01.im * br;
re[j] = m10.re * ar - m10.im * ai + m11.re * br - m11.im * bi;
im[j] = m10.re * ai + m10.im * ar + m11.re * bi + m11.im * br;
}
} else {
replay(&mut re, &mut im, std::slice::from_ref(l));
}
}
bits(&state_output(&re, &im)) != bits(&case.reference().unwrap())
});
assert!(differs, "the corpus cannot tell a reassociated formula from the reference");
}
#[test]
fn plans_refuse_what_the_state_vector_refuses() {
let bad = Gate { qubits: vec![0, 0], matrix: vec![C { re: 1.0, im: 0.0 }; 16] };
assert_eq!(plan(2, &[bad], 0), Err(SvError::BadGate(0)));
assert_eq!(plan(1, &[Gate::h(1)], 0), Err(SvError::BadGate(0)));
}
#[test]
fn the_implementation_id_follows_the_kernel_and_every_mutant_edits_once() {
let id = implementation_id();
assert!(id.starts_with("cuda-sv64/") && id.len() == "cuda-sv64/".len() + 16);
for m in &MUTANTS {
assert_ne!(implementation_id_of(&mutate(m)), id, "{}", m.name);
}
assert!(SOURCE.contains("#define SV_MAX_D 64") && MAX_D == 64);
let cl = cl_implementation_id();
assert!(cl.starts_with("opencl-sv64/") && cl != id);
for m in &MUTANTS {
assert_ne!(cl_implementation_id_of(&mutate_cl(m)), cl, "{}", m.name);
}
assert!(CL_SOURCE.contains("#pragma OPENCL FP_CONTRACT OFF") && CL_SOURCE.contains("#define SV_MAX_D 64"));
}
}