open-standards/wai/wai-quantum/tests/qec_text_conformance.rs
Transaction Science 2615e98cd3 wai-quantum: the error-correction circuit and model text read as written and written back, with tags, Pauli-target observables and decompositions
A reader and writer for the circuit text and the detector error model text
that keep every instruction's tag, its arguments as written and every kind of
target (records, Pauli targets, inverted results, sweep bits, products),
blocks as blocks. The frame simulator now reads through it: tags reach the
mechanisms their noise makes, observables take Pauli targets in sampling and
in the model, detectors keep their coordinates. A model is written with its
coordinates, tags and each decomposed mechanism's parts, and read back to the
same model, blocks unrolled and detector shifts applied. A conformance set of
circuits and models the crate writes and of hand-written ones, each held to
its counts, its model and a byte-for-byte round trip, with the refusals.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-28 08:42:53 -04:00

176 lines
8 KiB
Rust

//! The error-correction text conformance set (`wai/quantum-conformance/qec-text/`).
//!
//! Every circuit and model there is in the writer's layout, so each must come
//! back from the reader and the writer byte for byte. Each circuit's counts,
//! tags and model are pinned in `vectors.json`, and its model's text is the
//! file the vector names. The crate's own circuits are regenerated and must be
//! the files as committed. Each file under `refuse/` must be refused, on the
//! line and with the words the vector gives.
//!
//! `WAI_QUANTUM_BLESS=1` rewrites the generated files and `vectors.json` from
//! the code, then checks them like any other run.
#![cfg(all(feature = "quantum_frame", feature = "quantum_ldpc"))]
use serde_json::{Value, json};
use std::path::{Path, PathBuf};
use wai_quantum::quantum_frame::{Basis, error_model, lower, parse, surface_code_memory};
use wai_quantum::quantum_ldpc::{BbCode, memory_circuit};
use wai_quantum::quantum_qec_text::{parse_circuit, parse_dem};
fn root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("../quantum-conformance/qec-text")
}
fn hex(b: &[u8]) -> String {
blake3::hash(b).as_bytes().iter().map(|x| format!("{x:02x}")).collect()
}
/// The circuits the crate writes, by file and the call that writes them.
fn generated() -> Vec<(&'static str, &'static str, String)> {
vec![
("circuits/surface_z_d3_r3.circuit", "quantum_frame::surface_code_memory(3, 3, 0.001)", surface_code_memory(3, 3, 0.001)),
("circuits/surface_z_d5_r1.circuit", "quantum_frame::surface_code_memory(5, 1, 0.002)", surface_code_memory(5, 1, 0.002)),
("circuits/bb72_z_c2.circuit", "quantum_ldpc::memory_circuit(&BbCode::bb72(), 2, 0.003, Basis::Z)", memory_circuit(&BbCode::bb72(), 2, 0.003, Basis::Z)),
("circuits/bb72_x_c1.circuit", "quantum_ldpc::memory_circuit(&BbCode::bb72(), 1, 0.003, Basis::X)", memory_circuit(&BbCode::bb72(), 1, 0.003, Basis::X)),
]
}
/// The hand-written circuits and models, kept as committed.
const WRITTEN_CIRCUITS: [&str; 1] = ["circuits/tagged.circuit"];
const WRITTEN_MODELS: [&str; 1] = ["models/decomposed.dem"];
/// What each refused file must be refused for: (file, line, words).
const REFUSED: [(&str, usize, &str); 7] = [
("refuse/unsupported.circuit", 3, "unsupported instruction 'T'"),
("refuse/random_observable.circuit", 0, "not deterministic"),
("refuse/record.circuit", 3, "measurement record out of range"),
("refuse/open_tag.circuit", 2, "tag's '[' is not closed"),
("refuse/product.circuit", 2, "unsupported instruction 'MPP'"),
("refuse/empty_part.dem", 1, "empty"),
("refuse/unsupported.dem", 2, "unsupported model instruction 'flip'"),
];
fn model_file(circuit: &str) -> String {
circuit.replace("circuits/", "models/").replace(".circuit", ".dem")
}
fn circuit_vector(file: &str, text: &str, generator: Option<&str>) -> Value {
let c = parse(text).unwrap_or_else(|e| panic!("{file}: {e}"));
let m = error_model(&c).unwrap_or_else(|e| panic!("{file}: {e}"));
let dem = m.to_dem_text();
let mut v = json!({
"file": file,
"qubits": c.qubits,
"measurements": c.measurements,
"detectors": c.detectors.len(),
"observables": c.observables.len(),
"tags": c.tags,
"text_blake3": hex(text.as_bytes()),
"model": model_file(file),
"model_blake3": hex(dem.as_bytes()),
"mechanisms": m.mechanisms.len(),
});
if let Some(g) = generator {
v["generator"] = json!(g);
}
v
}
fn model_vector(file: &str, text: &str) -> Value {
let m = parse_dem(text).and_then(|d| d.to_model()).unwrap_or_else(|e| panic!("{file}: {e}"));
json!({
"file": file,
"detectors": m.detectors,
"observables": m.observables,
"mechanisms": m.mechanisms.len(),
"decomposed": m.mechanisms.iter().filter(|x| x.parts.len() > 1).count(),
"tagged": m.mechanisms.iter().filter(|x| x.tag.is_some()).count(),
"text_blake3": hex(text.as_bytes()),
})
}
fn bless() {
let r = root();
let mut circuits = Vec::new();
for (file, generator, text) in generated() {
std::fs::write(r.join(file), &text).unwrap();
circuits.push(circuit_vector(file, &text, Some(generator)));
}
for file in WRITTEN_CIRCUITS {
let text = std::fs::read_to_string(r.join(file)).unwrap();
circuits.push(circuit_vector(file, &text, None));
}
let mut models = Vec::new();
for c in &circuits {
let text = std::fs::read_to_string(r.join(c["file"].as_str().unwrap())).unwrap();
let dem = error_model(&parse(&text).unwrap()).unwrap().to_dem_text();
let file = c["model"].as_str().unwrap();
std::fs::write(r.join(file), &dem).unwrap();
models.push(model_vector(file, &dem));
}
for file in WRITTEN_MODELS {
let text = std::fs::read_to_string(r.join(file)).unwrap();
models.push(model_vector(file, &text));
}
let refuse: Vec<Value> = REFUSED.iter().map(|(f, line, words)| json!({ "file": f, "line": line, "contains": words })).collect();
let doc = json!({ "format": "wai.quantum.qec_text.vectors/1", "circuits": circuits, "models": models, "refuse": refuse });
std::fs::write(r.join("vectors.json"), serde_json::to_string_pretty(&doc).unwrap() + "\n").unwrap();
}
#[test]
fn the_conformance_set_holds() {
if std::env::var("WAI_QUANTUM_BLESS").is_ok_and(|v| v == "1") {
bless();
}
let r = root();
let doc: Value = serde_json::from_str(&std::fs::read_to_string(r.join("vectors.json")).unwrap()).unwrap();
assert_eq!(doc["format"], "wai.quantum.qec_text.vectors/1");
let gens = generated();
let circuits = doc["circuits"].as_array().unwrap();
assert_eq!(circuits.len(), gens.len() + WRITTEN_CIRCUITS.len());
for v in circuits {
let file = v["file"].as_str().unwrap();
let text = std::fs::read_to_string(r.join(file)).unwrap();
// The crate's own circuits are what the code writes today.
if let Some((_, _, now)) = gens.iter().find(|g| g.0 == file) {
assert_eq!(&text, now, "{file}: the generator no longer writes this file");
}
let prog = parse_circuit(&text).unwrap();
assert_eq!(prog.to_text(), text, "{file}: not in the writer's layout");
let c = lower(&prog).unwrap();
let expect = circuit_vector(file, &text, v.get("generator").and_then(Value::as_str));
assert_eq!(&expect, v, "{file}");
let dem = error_model(&c).unwrap().to_dem_text();
assert_eq!(dem, std::fs::read_to_string(r.join(v["model"].as_str().unwrap())).unwrap(), "{file}: its model");
}
for v in doc["models"].as_array().unwrap() {
let file = v["file"].as_str().unwrap();
let text = std::fs::read_to_string(r.join(file)).unwrap();
let d = parse_dem(&text).unwrap();
assert_eq!(d.to_text(), text, "{file}: not in the writer's layout");
let m = d.to_model().unwrap();
assert_eq!(&model_vector(file, &text), v, "{file}");
// A model written and read again is the same model.
assert_eq!(parse_dem(&m.to_dem_text()).unwrap().to_model().unwrap(), m, "{file}");
}
let refuse = doc["refuse"].as_array().unwrap();
assert_eq!(refuse.len(), REFUSED.len());
for v in refuse {
let file = v["file"].as_str().unwrap();
let text = std::fs::read_to_string(r.join(file)).unwrap();
let (line, words) = (v["line"].as_u64().unwrap() as usize, v["contains"].as_str().unwrap());
let message = if file.ends_with(".dem") {
let e = parse_dem(&text).and_then(|d| d.to_model()).expect_err(file);
assert_eq!(e.line, line, "{file}: {e}");
e.to_string()
} else {
let e = parse(&text).and_then(|c| error_model(&c).map(|_| ())).expect_err(file);
let shown = e.to_string();
if line > 0 {
assert!(shown.starts_with(&format!("line {line}:")), "{file}: {shown}");
}
shown
};
assert!(message.contains(words), "{file}: {message}");
}
}