open-standards/wai/wai-quantum/tests/hip_wmma.rs
Transaction Science 30b8f23268
Some checks failed
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Has been cancelled
standards workspaces build + test / which workspaces this change touches (pull_request) Has been cancelled
standards workspaces build + test / macOS energy backends (pull_request) Has been cancelled
WAI conformance / does this change touch it (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cargo-class corpora · macos (pull_request) Has been cancelled
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Has been cancelled
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Has been cancelled
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Has been cancelled
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · macos (pull_request) Has been cancelled
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Has been cancelled
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Has been cancelled
WAI energy receipts / does this change touch it (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux (pull_request) Has been cancelled
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Has been cancelled
WAI energy receipts / energy receipts · macos (pull_request) Has been cancelled
WAI JPEG-AI whole decode (pinned inputs, required) / does this change touch it (pull_request) Has been cancelled
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Has been cancelled
WAI neural sink gates / does this change touch it (pull_request) Has been cancelled
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Has been cancelled
wai-pack / wai-pack · does this change touch it (pull_request) Has been cancelled
wai-pack / wai-pack · linux (pull_request) Has been cancelled
wai-pack / wai-pack · linux-arm64 (pull_request) Has been cancelled
wai-pack / wai-pack · macos (pull_request) Has been cancelled
wai-pack / wai-pack · independent verifier (Python) (pull_request) Has been cancelled
wai-pack / wai-pack · the floor through a media prober (pull_request) Has been cancelled
wai-pack / wai-pack · dependency licences (pull_request) Has been cancelled
wai-quantum CUDA member / does this change touch it (pull_request) Has been cancelled
wai-quantum CUDA member / member · CUDA (jetson-hub) (pull_request) Has been cancelled
wai-quantum CUDA member / member · CUDA absent (linux) (pull_request) Has been cancelled
wai-quantum dataflow member / does this change touch it (pull_request) Has been cancelled
wai-quantum dataflow member / member · dataflow device, simulated (pci 1e52:b140) (pull_request) Has been cancelled
wai-quantum dataflow member / member · dataflow device, simulated (pci 1e52:401e) (pull_request) Has been cancelled
wai-quantum dataflow member / member · dataflow device absent (macos) (pull_request) Has been cancelled
wai-quantum signal processor / does this change touch it (pull_request) Has been cancelled
wai-quantum signal processor / processor and member · signal processor, emulated (pull_request) Has been cancelled
wai-quantum FPGA member / does this change touch it (pull_request) Has been cancelled
wai-quantum FPGA member / member · FPGA datapath, simulated (macos) (pull_request) Has been cancelled
wai-quantum FPGA member / member · FPGA simulation absent (linux) (pull_request) Has been cancelled
wai-quantum HIP member / does this change touch it (pull_request) Has been cancelled
wai-quantum HIP member / member · HIP (framework-128gb) (pull_request) Has been cancelled
wai-quantum HIP member / member · HIP absent (linux-arm64) (pull_request) Has been cancelled
wai-quantum Metal member / does this change touch it (pull_request) Has been cancelled
wai-quantum Metal member / member · Metal (macos) (pull_request) Has been cancelled
wai-quantum Metal member / member · Metal absent (linux) (pull_request) Has been cancelled
wai-quantum npu-cores member / does this change touch it (pull_request) Has been cancelled
wai-quantum npu-cores member / member · npu-cores, neural processor (framework-128gb silicon) (pull_request) Has been cancelled
wai-quantum npu-cores member / member · npu-cores absent (linux-arm64) (pull_request) Has been cancelled
wai-quantum neural-processor member / does this change touch it (pull_request) Has been cancelled
wai-quantum neural-processor member / member · neural processor (macos) (pull_request) Has been cancelled
wai-quantum neural-processor member / member · neural processor absent (linux) (pull_request) Has been cancelled
wai-quantum OpenCL member / does this change touch it (pull_request) Has been cancelled
wai-quantum OpenCL member / member · OpenCL GPU and CPU (jetson-hub) (pull_request) Has been cancelled
wai-quantum OpenCL member / member · OpenCL GPU (framework-128gb) (pull_request) Has been cancelled
wai-quantum OpenCL member / member · OpenCL GPU (macos) (pull_request) Has been cancelled
wai-quantum OpenCL member / member · OpenCL absent (linux) (pull_request) Has been cancelled
wai-quantum on every processor / emulated · aarch64-unknown-linux-gnu (pull_request) Has been cancelled
wai-quantum on every processor / emulated · armv7-unknown-linux-gnueabihf (pull_request) Has been cancelled
wai-quantum on every processor / emulated · loongarch64-unknown-linux-gnu (pull_request) Has been cancelled
wai-quantum on every processor / emulated · powerpc64-unknown-linux-gnu (pull_request) Has been cancelled
wai-quantum on every processor / emulated · powerpc64le-unknown-linux-gnu (pull_request) Has been cancelled
wai-quantum on every processor / emulated · riscv64gc-unknown-linux-gnu (pull_request) Has been cancelled
wai-quantum on every processor / emulated · s390x-unknown-linux-gnu (pull_request) Has been cancelled
wai-quantum on every processor / emulated · sparc64-unknown-linux-gnu (pull_request) Has been cancelled
wai-quantum on every processor / emulated · i686-unknown-linux-gnu (pull_request) Has been cancelled
wai-quantum-qpu / does this change touch it (pull_request) Has been cancelled
wai-quantum-qpu / clients against their contracts (linux) (pull_request) Has been cancelled
wai-quantum matrix-extension member / does this change touch it (pull_request) Has been cancelled
wai-quantum matrix-extension member / member · matrix extension, emulated (aarch64 Linux) (pull_request) Has been cancelled
wai-quantum matrix-extension member / member · matrix extension absent (macos) (pull_request) Has been cancelled
wai-quantum tensor-engine member / does this change touch it (pull_request) Has been cancelled
wai-quantum tensor-engine member / member · tensor engine, the kernel toolchain's simulator (linux) (pull_request) Has been cancelled
wai-quantum tensor-engine member / member · tensor engine absent (linux-arm64) (pull_request) Has been cancelled
wai-quantum portable tensor program / does this change touch it (pull_request) Has been cancelled
wai-quantum portable tensor program / tensor program · CPU, mutants, GPU builds (pull_request) Has been cancelled
wai-quantum portable tensor program / tensor program · GPUs (jetson-hub) (pull_request) Has been cancelled
wai-quantum portable tensor program / tensor program · integrated GPU (framework-128gb) (pull_request) Has been cancelled
wai-quantum portable tensor program / tensor program · Metal and the arm64 CPU (macos) (pull_request) Has been cancelled
wai-quantum CPU vector units / does this change touch it (pull_request) Has been cancelled
wai-quantum CPU vector units / member · vector unit, rvv 1024-bit (riscv64, emulated) (pull_request) Has been cancelled
wai-quantum CPU vector units / member · vector unit, sve 128-bit (aarch64, emulated) (pull_request) Has been cancelled
wai-quantum CPU vector units / member · vector unit, rvv 128-bit (riscv64, emulated) (pull_request) Has been cancelled
wai-quantum CPU vector units / member · vector unit, sve 2048-bit (aarch64, emulated) (pull_request) Has been cancelled
wai-quantum CPU vector units / member · vector unit, avx2 (x86_64 silicon) (pull_request) Has been cancelled
wai-quantum CPU vector units / member · vector unit, sve 512-bit (aarch64, emulated) (pull_request) Has been cancelled
wai-quantum CPU vector units / member · vector units, avx2 and avx512 (framework-128gb silicon) (pull_request) Has been cancelled
wai-quantum CPU vector units / member · vector unit absent (macos) (pull_request) Has been cancelled
wai-quantum WebGPU member / does this change touch it (pull_request) Has been cancelled
wai-quantum WebGPU member / shader checks · browser build · wasip2 inertness · MSRV (pull_request) Has been cancelled
wai-quantum WebGPU member / member · software Vulkan (linux) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · Metal (macos) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · discrete GPU, Vulkan (jetson-hub) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · integrated GPU, Vulkan (framework-128gb) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · browser WebGPU (macos) (pull_request) Has been cancelled
wai-quantum WebGPU member / member · DX12 and Vulkan (cbio-windows) (pull_request) Has been cancelled
WAI mbt2018_mean integer decode (regenerated vectors) / does this change touch it (pull_request) Has been cancelled
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Has been cancelled
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Has been cancelled
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Has been cancelled
wai-web wasm32 / does this change touch it (pull_request) Has been cancelled
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Has been cancelled
wai-quantum 0.6.2: the HIP member's matrix-instruction kernel
A second HIP kernel for the gate (wmma_limbs.hip, HipMember::wmma), for
the gfx11 architectures: every product formed by the wavefront-of-32
16x16x16 bf16 matrix instruction, accumulated in f32, in the
instruction's own operand layout; one wavefront takes sixteen pairs in
seven 16-column steps.

The instruction's accumulation is not exact once a negative term enters
a sum. Measured on gfx1151, on the instruction alone: 1*1 + (-1)*0
returns 1 - 2^-24, 1 - 1 returns -2^-24, 128 - 64 returns 64 - 2^-17;
non-negative sums are exact. The CUDA kernel's signed top limb fails the
probe there. So the limbs are offset-binary (x + 2^31, all unsigned) and
the kernel removes the offset in 64-bit integers:
e*a = (e+2^31)(a+2^31) - 2^31(e+a) - 2^62 mod 2^64.

Own implementation id (hip-wmma-limbs8/...), HipError::Unsupported for
other architectures, eight mutants (nine-bit limbs, the result rows in
another layout, a wrong offset correction among them).

Measured on gfx1151 (silicon): hip_wmma 9/9, 66020 probe pairs, all four
pins, 22 qubits, 8 of 8 mutants; hip_member 9/9 and hip_sv 3/3
unchanged. With no HIP, both suites pass over no device. CI: a step of
wai-quantum-hip and its absent leg.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 10:07:26 -04:00

444 lines
21 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! The HIP member's matrix-instruction kernel on the devices of the machine
//! that runs this: every product the gate needs formed by the GPU's
//! 16 × 16 × 16 bf16 matrix instruction, from 8-bit limbs accumulated in f32,
//! held to the same tests as the other kernel (`hip_member`).
//!
//! The machine declares its devices in `WAI_QUANTUM_HIP_EXPECT`, as for
//! `hip_member`: their keys, comma-separated and sorted, or `none`. Every
//! declared device with the instruction (a gfx11 architecture) must run the
//! kernel and pass its probe; one without must be refused as unsupported. Each test prints what it established and, when
//! `WAI_QUANTUM_HIP_REPORT` names a file, appends the same lines to it.
//!
//! ```text
//! WAI_QUANTUM_HIP_EXPECT=gpu-hip-gfx1151 \
//! cargo test --features quantum_fabric_hip --test hip_wmma -- --test-threads=1
//! ```
#![cfg(not(target_arch = "wasm32"))]
use std::io::Write;
use std::sync::OnceLock;
use wai_quantum::ed25519_dalek::SigningKey;
use wai_quantum::quantum::{BaseGate, Circuit, Gate, Gateset, MAX_QUBITS, QuantumError};
use wai_quantum::quantum_fabric::hip::{self, WMMA_MUTANTS};
use wai_quantum::quantum_fabric::lanes::{self, GateWords, Lane};
use wai_quantum::quantum_fabric::plan::{Program, Step};
use wai_quantum::quantum_fabric::wgsl::{Detector, Mutant};
use wai_quantum::quantum_fabric::{
Corpus, CpuReference, FABRIC_DEFAULT, Fabric, FabricClass, FabricReceipt, MemberError, Parity, Presence, REFERENCE, apply_kernel, detect_local, place, ready,
};
use wai_quantum::quantum_fabric_hip::{Discovery, HipError, HipMember, discover};
/// The CPU's corpus digests: random-v1 (seed 7, 200 cases) and fabric-v1
/// (seed 7, 200 + 24 cases), per gateset. A bit-exact member reproduces them.
const PINS: [(Gateset, &str, &str); 2] = [
(Gateset::V1, "d18454c639acfd9c7b66450f4ded1aac4ab46cc10a7bedbcf4958a96e204fb36", "41c726a0ac80f7defef92e15ecf893bdb7a3076b5e7f670fa0b7142e5c033df7"),
(Gateset::V2, "3dbde276c1095bf827faec71d5980e050f8a55a5ba7d97cd3bbdaec3f43a13b2", "9a84b55af68f1a8b07390b234cf562dd8d85cef3185a697ab85690c8790a7809"),
];
const RANDOM_V1: Corpus = Corpus::Random { seed: 7, cases: 200 };
static DISCOVERY: OnceLock<Discovery> = OnceLock::new();
static WMMA: OnceLock<Vec<HipMember>> = OnceLock::new();
fn discovery() -> &'static Discovery {
DISCOVERY.get_or_init(discover)
}
/// Prints `line` and appends it to the file `WAI_QUANTUM_HIP_REPORT` names.
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");
}
}
/// The declared device set, as written, if it is exactly what was found;
/// otherwise why not, with what was found and refused and how to declare it.
fn declared(d: &Discovery) -> Result<String, String> {
let mut found: Vec<&str> = d.found.iter().map(|m| m.key()).collect();
found.sort_unstable();
let found_list = if found.is_empty() { "none".to_string() } else { found.join(",") };
let refused: Vec<String> = d.refused.iter().map(|r| format!("{} ({:?}): {}", r.key, r.device, r.error)).collect();
let refused_list = if refused.is_empty() { "none".to_string() } else { refused.join("; ") };
let unavailable = d.unavailable.as_deref().unwrap_or("no");
let how = format!("found: {found_list}; refused: {refused_list}; unavailable: {unavailable}. Declare the devices this machine must have: WAI_QUANTUM_HIP_EXPECT={found_list}");
let expect = match std::env::var("WAI_QUANTUM_HIP_EXPECT") {
Ok(v) if !v.is_empty() => v,
_ => return Err(format!("WAI_QUANTUM_HIP_EXPECT is not set. {how}")),
};
let keys: Vec<&str> = if expect == "none" { Vec::new() } else { expect.split(',').collect() };
let well_formed = keys.windows(2).all(|w| w[0] <= w[1]) && keys.iter().all(|k| !k.is_empty() && *k != "none" && k.trim() == *k);
if !well_formed {
return Err(format!("WAI_QUANTUM_HIP_EXPECT={expect:?} is not a sorted, comma-separated list of keys, nor none. {how}"));
}
for k in &keys {
if !found.contains(k)
&& let Some(r) = d.refused.iter().find(|r| r.key == *k)
{
return Err(format!("the declared device {k} was refused: {}. {how}", r.error));
}
}
if found != keys {
return Err(format!("the devices found are not the declared ones ({expect}). {how}"));
}
Ok(expect)
}
/// The matrix-instruction members: one per declared device with the instruction, once
/// the declared devices are exactly the ones found.
fn members() -> &'static [HipMember] {
let d = discovery();
if let Err(why) = declared(d) {
panic!("{why}");
}
WMMA.get_or_init(|| {
d.found.iter().filter(|m| m.identity().has_wmma()).map(|m| m.wmma().unwrap_or_else(|e| panic!("{}: the matrix-instruction kernel: {e}", m.key()))).collect()
})
}
fn hex(b: &[u8]) -> String {
b.iter().map(|x| format!("{x:02x}")).collect()
}
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)
}
/// A value in `[-bound, bound]`.
fn within(s: &mut u64, bound: i64) -> i32 {
((next(s) % (2 * bound as u64 + 1)) as i64 - bound) as i32
}
/// A random circuit of `ops` gates on `n` qubits: every base gate, up to three
/// distinct controls, `P(1..=32)`.
fn random_circuit(n: u8, ops: usize, seed: u64, gs: Gateset) -> Circuit {
let bases = [BaseGate::I, BaseGate::X, BaseGate::Y, BaseGate::Z, BaseGate::H, BaseGate::S, BaseGate::Sdg, BaseGate::T, BaseGate::Tdg, BaseGate::P];
let mut s = seed;
let mut c = Circuit::with_gateset(n, gs);
for _ in 0..ops {
let base = bases[(next(&mut s) % bases.len() as u64) as usize];
let target = (next(&mut s) % u64::from(n)) as u8;
let mut controls = Vec::new();
for _ in 0..next(&mut s) % 4 {
let q = (next(&mut s) % u64::from(n)) as u8;
if q != target && !controls.contains(&q) {
controls.push(q);
}
}
let param = if base == BaseGate::P { 1 + (next(&mut s) % 32) as u16 } else { 0 };
c.ops.push(Gate { base, controls, target, param });
}
c
}
/// Five circuits the native simulator refuses, in each gateset.
fn refused_circuits() -> Vec<Circuit> {
let gate = |base, controls: Vec<u8>, target, param| Gate { base, controls, target, param };
let bad = [
(1, vec![gate(BaseGate::H, vec![], 0, 0), gate(BaseGate::P, vec![], 0, 0)]),
(1, vec![gate(BaseGate::P, vec![], 0, 33)]),
(1, vec![gate(BaseGate::X, vec![], 3, 0)]),
(2, vec![gate(BaseGate::X, vec![0], 0, 0)]),
(2, vec![gate(BaseGate::X, vec![5], 1, 0)]),
];
let mut out = Vec::new();
for gs in Gateset::ALL {
for (n, ops) in bad.clone() {
let mut c = Circuit::with_gateset(n, gs);
c.ops = ops;
out.push(c);
}
}
out
}
/// `Z` on qubit 0: on `[(0, 0), (i32::MIN, 0)]` its exact result is `2^31`,
/// which leaves 32 bits.
fn z_words() -> GateWords {
lanes::gate_words(&Gate { base: BaseGate::Z, controls: vec![], target: 0, param: 0 }, Gateset::V2)
}
const Z_STATE: [Lane; 2] = [[0, 0], [i32::MIN, 0]];
#[test]
fn c0_devices_are_declared() {
let d = discovery();
match declared(d) {
Ok(expect) => report(&format!("hip wmma devices: as declared ({expect})")),
Err(why) => panic!("{why}"),
}
for m in &d.found {
if !m.identity().has_wmma() {
assert!(matches!(m.wmma(), Err(HipError::Unsupported(_))), "{}", m.key());
report(&format!("hip wmma unsupported: key={} arch {}", m.key(), m.identity().arch));
}
}
for t in members() {
assert_eq!(t.class(), FabricClass::Gpu);
assert!(t.id().starts_with("local/gpu-hip-"), "{}", t.id());
assert_eq!(t.implementation(), hip::wmma_implementation_id());
assert_ne!(t.implementation(), hip::implementation_id());
assert_eq!(t.identity().member_id(), t.id());
let lanes64 = d.found.iter().find(|m| m.id() == t.id()).expect("the device's member");
assert_eq!((lanes64.implementation(), lanes64.max_qubits()), (hip::implementation_id().as_str(), t.max_qubits()));
assert!(t.max_qubits() >= 20, "{}: holds only {} qubits", t.key(), t.max_qubits());
assert!(t.detail().starts_with(lanes64.detail()) && t.detail().contains("matrix instruction"), "{}", t.detail());
report(&format!("hip wmma member: key={} id={} implementation={} {}", t.key(), t.id(), t.implementation(), t.detail()));
}
}
#[test]
fn c1_probe_matches_lanes() {
for m in members() {
let pairs = lanes::probe_pairs(1 << 16, 0x0cda);
let out = m.probe(&pairs).unwrap_or_else(|e| panic!("{}: {e}", m.key()));
assert_eq!(lanes::first_mismatch(&pairs, &out), None, "{}", m.key());
report(&format!("hip wmma probe: key={} {} pairs agree", m.key(), pairs.len()));
}
}
#[test]
fn c2_kernel_matches_run_words() {
for m in members() {
let mut s = 0x6b65_726e_656c_u64;
let (mut flagged, mut clean) = (0, 0);
for case in 0..2000 {
let n = 1 + (next(&mut s) % 12) as u32;
// Three regimes: the whole 32-bit range (nearly always out of
// range), small states and entries (never), and states and
// entries up to 2^30 (either).
let mode = case % 3;
let value = |s: &mut u64| -> i32 {
match mode {
0 => next(s) as i32,
1 => within(s, 1 << 22),
_ => within(s, 1 << 30),
}
};
let entry = |s: &mut u64| -> u32 {
match mode {
0 => next(s) as u32,
1 => within(s, 1 << 29) as u32,
_ => within(s, 1 << 30) as u32,
}
};
let state: Vec<Lane> = (0..1usize << n).map(|_| [value(&mut s), value(&mut s)]).collect();
let gates: Vec<GateWords> = (0..1 + next(&mut s) % 8)
.map(|_| {
let t = (next(&mut s) % u64::from(n)) as u32;
let ctrl = (0..n).filter(|&b| b != t && next(&mut s).is_multiple_of(4)).fold(0u32, |acc, b| acc | 1 << b);
let mut w = [0u32; 8];
w.iter_mut().for_each(|x| *x = entry(&mut s));
GateWords { m: w, tbit: 1 << t, ctrl }
})
.collect();
let want = lanes::run_words(&state, &gates).expect("valid shapes");
let got = m.run_lanes(&state, &gates).unwrap_or_else(|e| panic!("{}: case {case}: {e}", m.key()));
assert_eq!(got.out_of_range, want.out_of_range, "{}: case {case}, n = {n}", m.key());
assert!(got.amps == want.amps, "{}: case {case}, n = {n}: the lanes differ", m.key());
if want.out_of_range.is_some() {
flagged += 1;
} else {
clean += 1;
}
}
assert!(flagged >= 100 && clean >= 100, "{}: {flagged} flagged, {clean} clean", m.key());
report(&format!("hip wmma kernel: key={} ok", m.key()));
}
}
#[test]
fn c3_certificates_reproduce_the_pins() {
let ms = members();
for (gs, random_pin, fabric_pin) in PINS {
let cpu = lanes::certify_corpus(gs, "local/test", RANDOM_V1);
assert_eq!(hex(&cpu.corpus), random_pin, "{gs:?}");
let other = Corpus::Random { seed: 8, cases: 300 };
let cpu_other = lanes::certify_corpus(gs, "local/test", other);
for m in ms {
for (corpus, cases, pin) in [(RANDOM_V1, 200, random_pin), (FABRIC_DEFAULT, 224, fabric_pin)] {
let c = m.certify(gs, corpus).unwrap_or_else(|e| panic!("{} {gs:?} {}: {e}", m.key(), corpus.name()));
assert_eq!(c.parity, Parity::BitExact, "{} {gs:?} {}", m.key(), corpus.name());
assert_eq!(c.cases, cases);
assert_eq!(hex(&c.corpus), pin, "{} {gs:?} {}", m.key(), corpus.name());
assert_eq!((c.kernel.as_str(), c.implementation.as_str(), c.member.as_str()), (apply_kernel(gs), m.implementation(), m.id()));
assert_eq!(c.generator, corpus.generator(gs));
report(&format!(
"hip wmma certified: key={} member={} kernel={} corpus={} implementation={} parity=bit-exact cases={} digest={}",
m.key(),
m.id(),
c.kernel,
corpus.name(),
c.implementation,
c.cases,
hex(&c.corpus)
));
}
// A corpus with no pin: the member and the CPU agree on it.
let c = m.certify(gs, other).unwrap_or_else(|e| panic!("{} {gs:?} seed 8: {e}", m.key()));
assert_eq!((c.parity, c.cases), (Parity::BitExact, 300), "{} {gs:?}", m.key());
assert_eq!(c.corpus, cpu_other.corpus, "{} {gs:?}", m.key());
}
}
}
#[test]
fn c4_wide_circuits_match_native() {
for m in members() {
let widest = m.max_qubits().min(22);
let mut s = 0x0077_6964_6500_u64;
for (k, n) in [12u8, 16, 18, 20, widest].into_iter().enumerate() {
for gs in Gateset::ALL {
let c = random_circuit(n, 40, 0x1000 + k as u64, gs);
let start = (next(&mut s) as usize) % (1usize << n);
let want = c.simulate_from(start).expect("the native run");
let got = m.simulate_from(&c, start).unwrap_or_else(|e| panic!("{}: n = {n} {gs:?}: {e}", m.key()));
assert!(got == want, "{}: n = {n} {gs:?}: the states differ", m.key());
}
}
// The transforms the conformance corpus carries.
for n in [3u8, 5, 8, 12] {
let qft = Circuit::qft(n);
assert!(m.simulate(&qft).unwrap() == qft.simulate().unwrap(), "{}: QFT-{n}", m.key());
}
report(&format!("hip wmma wide: key={} up to {widest} qubits match native", m.key()));
}
}
#[test]
fn c5_refusals_are_typed() {
for m in members() {
for c in refused_circuits() {
let native = c.simulate_from(0).unwrap_err();
assert_eq!(m.simulate_from(&c, 0).unwrap_err(), HipError::Circuit(native), "{c:?}");
}
assert_eq!(m.simulate(&Circuit::new(MAX_QUBITS + 1)).unwrap_err(), HipError::Circuit(QuantumError::TooManyQubits(MAX_QUBITS + 1)));
let small = m.with_max_qubits(4);
assert_eq!(small.simulate(&Circuit::new(5)).unwrap_err(), HipError::TooWide { n_qubits: 5, max: 4 });
let wide = Program { n_qubits: 5, start: 0, gates: Vec::new(), steps: Vec::new() };
assert_eq!(small.run_program(&wide).unwrap_err(), HipError::TooWide { n_qubits: 5, max: 4 });
// A result that leaves 32 bits is reported at its op, and the lane
// holds its low 32 bits.
let r = m.run_lanes(&Z_STATE, &[z_words()]).unwrap();
assert_eq!(r.out_of_range, Some(0), "{}", m.key());
assert_eq!(r.amps[1][0] as u32, 0x8000_0000);
// Malformed words, a state the host cannot lay out and a tile step are
// host defects, never results.
let bad = GateWords { tbit: 3, ..z_words() };
assert!(matches!(m.run_lanes(&[[0, 0]; 4], &[bad]), Err(HipError::Internal(_))));
assert!(matches!(m.run_lanes(&[[0, 0]; 3], &[z_words()]), Err(HipError::Internal(_))));
let tiled = Program { n_qubits: 2, start: 0, gates: Vec::new(), steps: vec![Step::Tile { first: 0, count: 1, size: 2 }] };
assert!(matches!(m.run_program(&tiled), Err(HipError::Internal(_))));
// The refusals that may still fall back to another member, do.
assert!(MemberError::from(HipError::TooWide { n_qubits: 5, max: 4 }).falls_back());
report(&format!("hip wmma refusals: key={} typed", m.key()));
}
}
/// Whether a member running `mutant`'s text is caught by the mutant's
/// detector.
fn caught(mm: &HipMember, mutant: &Mutant, key: &str) -> bool {
match mutant.detector {
Detector::Probe => {
let edges = lanes::probe_pairs(0, 0);
let out = mm.probe(&edges).expect("the mutant's probe runs");
let differing = edges.iter().zip(&out).filter(|&(&(a, b), o)| *o != (lanes::mul_wide(a, b), lanes::fxmul_wide(a, b))).count();
println!("hip wmma mutant {} on {key}: {differing} of 484 edge pairs differ", mutant.name);
differing >= 1
}
Detector::Certificate => Gateset::ALL.into_iter().all(|gs| match mm.certify(gs, RANDOM_V1) {
Err(e) => {
println!("hip wmma mutant {} on {key}: {gs:?} certification refused: {e}", mutant.name);
true
}
Ok(c) => {
let cpu = lanes::certify_corpus(gs, "local/test", RANDOM_V1);
c.parity != Parity::BitExact || c.corpus != cpu.corpus
}
}),
Detector::Range => mm.run_lanes(&Z_STATE, &[z_words()]).map(|r| r.out_of_range) != Ok(Some(0)),
other => panic!("no matrix-instruction mutant uses the {other:?} detector"),
}
}
#[test]
fn c6_mutants_are_killed() {
assert_eq!(lanes::probe_pairs(0, 0).len(), 484);
for m in members() {
let mut killed = 0;
for mutant in &WMMA_MUTANTS {
let mm = m.with_wmma_source(&hip::wmma_mutate(mutant)).unwrap_or_else(|e| panic!("{}: mutant {} does not compile: {e}", m.key(), mutant.name));
assert_ne!(mm.implementation(), m.implementation());
assert!(caught(&mm, mutant, m.key()), "{}: mutant {} survived", m.key(), mutant.name);
killed += 1;
}
assert_eq!(killed, WMMA_MUTANTS.len());
report(&format!("hip wmma mutants: key={} killed {killed} of {}", m.key(), WMMA_MUTANTS.len()));
}
}
#[test]
fn c7_receipt_placement_and_the_fabric() {
for m in members() {
let member = m.member(FABRIC_DEFAULT).unwrap_or_else(|e| panic!("{}: {e}", m.key()));
assert_eq!((member.id.as_str(), member.class, member.presence), (m.id(), FabricClass::Gpu, Presence::Detected));
assert_eq!(member.kernels.len(), 2);
for (entry, gs) in member.kernels.iter().zip(Gateset::ALL) {
assert_eq!((entry.kernel.as_str(), entry.implementation.as_str()), (apply_kernel(gs), m.implementation()));
assert!(entry.energy.is_none());
assert_eq!(entry.certificate.parity, Parity::BitExact, "{gs:?}");
}
// A receipt over a member's run.
let qft = Circuit::qft(5);
let state = m.simulate(&qft).unwrap();
assert_eq!(state, qft.simulate().unwrap());
let entry = member.kernels.iter().find(|k| k.kernel == apply_kernel(qft.gateset)).unwrap();
let signer = SigningKey::from_bytes(&[7; 32]);
let receipt = FabricReceipt::seal(&signer, "local/test", state.statevector_hash(), &member, entry, 0, None);
assert!(receipt.verify());
assert!(receipt.certificate_matches(&entry.certificate));
// Placement: beside the CPU reference with no energy figures, the
// reference; alone, the member.
let local = detect_local();
for gs in Gateset::ALL {
let both = [local.clone(), member.clone()];
let (placed, k) = place(&both, apply_kernel(gs), &Parity::BitExact).unwrap();
assert_eq!((placed.id.as_str(), k.implementation.as_str()), (local.id.as_str(), REFERENCE));
let (placed, k) = place(std::slice::from_ref(&member), apply_kernel(gs), &Parity::BitExact).unwrap();
assert_eq!((placed.id.as_str(), k.implementation.as_str()), (m.id(), m.implementation()));
}
// On the fabric: the member alone runs the circuit; a pairing with
// another member's entries is refused.
let mut fabric = Fabric::new();
fabric.add(member.clone(), m);
let run = ready(fabric.run(&qft, 0, &Parity::BitExact, &signer, "local/test")).expect("resolves").expect("runs");
assert_eq!(run.state, state);
assert_eq!(run.receipt.member_id, m.id());
assert!(run.receipt.verify());
let mut mismatched = Fabric::new();
mismatched.add(local.clone(), m);
assert!(ready(mismatched.run(&qft, 0, &Parity::BitExact, &signer, "local/test")).expect("resolves").is_err());
let mut both = Fabric::new();
both.add(local.clone(), CpuReference).add(member.clone(), m);
let run = ready(both.run(&qft, 0, &Parity::BitExact, &signer, "local/test")).expect("resolves").expect("runs");
assert_eq!(run.state, state);
report(&format!("hip wmma receipt: key={} ok", m.key()));
}
}
#[test]
fn c8_repeated_runs_agree() {
for m in members() {
let c = random_circuit(16, 60, 0xabc, Gateset::V2);
let first = m.simulate(&c).unwrap();
for _ in 0..5 {
assert!(m.simulate(&c).unwrap() == first, "{}", m.key());
}
report(&format!("hip wmma repeat: key={} ok", m.key()));
}
}