open-standards/wai/wai-quantum/tests/hip_sv.rs
Transaction Science 024ac93411 wai-quantum: the HIP member, the CUDA kernel's text on HIP devices
quantum_fabric_hip (opt-in, Linux): the gate kernel the CUDA member runs,
unchanged, compiled at run time by HIP's runtime compiler for each
device's own architecture (--offload-arch from the device), with
-ffp-contract=off. Both libraries are loaded when first used, so a build
with the feature runs anywhere and finds no member where there is no
HIP. The device's properties are read in the runtime's frozen layout
(hipDeviceProp_tR0600); properties that do not read as that layout are
refused, never guessed at.

quantum_fabric::hip holds what names a member (the kernel text, the
implementation id hip-lanes64/..., distinct from the CUDA one over the
same text, the device identity and the key gpu-hip-<arch>); the mutants
are the CUDA kernel's, the same edits of the same text. With quantum_sv
the dense f64 state vector runs the CUDA kernels' text too
(hip-sv64/..., contraction off).

Measured on gfx1151: hip_member 9/9 (66020 probe pairs, all four pinned
certificates, circuits to 22 qubits, 6 of 6 mutants killed) and hip_sv
3/3 (the state vector's pin b7d129d0..., 16 and 20 qubits bit for bit,
4 of 4 mutants killed).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 05:01:50 -04:00

112 lines
4.7 KiB
Rust

//! The dense f64 state vector on the HIP devices of the machine that runs
//! this: bit for bit the CPU's, or the certificate says not.
//!
//! The machine declares its devices in `WAI_QUANTUM_HIP_EXPECT` (comma-
//! separated keys, or `none`), as for `hip_member`; `WAI_QUANTUM_HIP_REPORT`
//! names a file the established lines are appended to.
#![cfg(not(target_arch = "wasm32"))]
use std::io::Write;
use wai_quantum::gates::Gate;
use wai_quantum::quantum_fabric::sv64::{self, MUTANTS, SV_DEFAULT, SV_DEFAULT_PIN, SvCorpus};
use wai_quantum::quantum_fabric::{Certification, Parity};
use wai_quantum::quantum_fabric_hip::{HipMember, discover};
use wai_quantum::quantum_sv::StateVector;
fn report(line: &str) {
println!("{line}");
if let Some(path) = std::env::var_os("WAI_QUANTUM_HIP_REPORT") {
let mut f = std::fs::OpenOptions::new().create(true).append(true).open(&path).expect("the report file opens");
writeln!(f, "{line}").expect("the report line is written");
}
}
fn members() -> Vec<HipMember> {
let d = discover();
let mut found: Vec<String> = d.found.iter().map(|m| m.key().to_string()).collect();
found.sort();
let found_list = if found.is_empty() { "none".to_string() } else { found.join(",") };
let expect = std::env::var("WAI_QUANTUM_HIP_EXPECT").unwrap_or_else(|_| panic!("WAI_QUANTUM_HIP_EXPECT is not set; found {found_list}"));
assert_eq!(found_list, expect, "the devices found are not the declared ones");
d.found
}
fn hex(b: &[u8]) -> String {
b.iter().map(|x| format!("{x:02x}")).collect()
}
/// The CPU certifying itself on `corpus`: the digest a bit-exact device
/// reproduces.
fn cpu_digest(corpus: SvCorpus) -> String {
let mut cert = Certification::new();
for case in corpus.cases() {
let r = case.reference().unwrap();
cert.add(&case.bytes(), &r, &r);
}
hex(&cert.finish(sv64::KERNEL_SV_RUN, "cpu", "cpu", &corpus.generator()).corpus)
}
#[test]
fn s0_the_state_vector_is_the_cpus_bit_for_bit() {
let cpu = cpu_digest(SV_DEFAULT);
assert_eq!(cpu, SV_DEFAULT_PIN, "this machine's CPU does not reproduce the pin");
for m in members() {
let sv = m.sv().unwrap_or_else(|e| panic!("{}: {e}", m.key()));
let c = sv.certify(SV_DEFAULT).unwrap_or_else(|e| panic!("{}: {e}", m.key()));
assert_eq!(c.parity, Parity::BitExact, "{}", m.key());
assert_eq!(c.cases, SV_DEFAULT.cases as u64);
assert_eq!(hex(&c.corpus), cpu, "{}: the digest is not the CPU's", m.key());
report(&format!("hip sv certified: key={} member={} kernel={} implementation={} parity=bit-exact cases={} digest={}", m.key(), m.id(), c.kernel, c.implementation, c.cases, hex(&c.corpus)));
}
}
#[test]
fn s1_wide_states_match_the_cpu() {
for m in members() {
let sv = m.sv().unwrap();
for (n, width) in [(16u32, 0usize), (20, 3)] {
let mut gates = Vec::new();
let mut a = 0.1f64;
for layer in 0..6u32 {
for q in 0..n {
a += 0.731;
gates.push(match (q + layer) % 5 {
0 => Gate::rx(q, a),
1 => Gate::ry(q, a),
2 => Gate::rz(q, a),
3 => Gate::sqrt_w(q),
_ => Gate::t(q),
});
}
let mut q = layer % 2;
while q + 1 < n {
gates.push(if q % 3 == 0 { Gate::fsim(q, q + 1, a, 0.5 * a) } else { Gate::cz(q, q + 1) });
q += 2;
}
gates.push(Gate::iswap(0, n - 1));
}
let mut cpu = StateVector::zero(n).unwrap();
cpu.run(&gates, width).unwrap();
let (re, im) = sv.run(n, &gates, width).unwrap();
let same = |a: &[f64], b: &[f64]| a.iter().zip(b).all(|(x, y)| x.to_bits() == y.to_bits());
assert!(same(&re, &cpu.re) && same(&im, &cpu.im), "{}: n={n} width={width}", m.key());
}
report(&format!("hip sv wide: key={} 16 and 20 qubits match the CPU bit for bit", m.key()));
}
}
#[test]
fn s2_mutants_are_caught() {
let small = SvCorpus { cases: 18, ..SV_DEFAULT };
let cpu = cpu_digest(small);
for m in members() {
let mut killed = 0;
for mutant in &MUTANTS {
let sv = m.sv_with_source(&sv64::mutate(mutant)).unwrap_or_else(|e| panic!("mutant {}: {e}", mutant.name));
let c = sv.certify(small).unwrap();
assert!(c.parity != Parity::BitExact || hex(&c.corpus) != cpu, "{}: mutant {} survived", m.key(), mutant.name);
killed += 1;
}
report(&format!("hip sv mutants: key={} killed {killed} of {}", m.key(), MUTANTS.len()));
}
}