Some checks failed
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 3s
no-vanity-metrics / WAI public text names no comparison (pull_request) Successful in 3s
no-vanity-metrics / WAI public text names no company or product (pull_request) Successful in 3s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m54s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m26s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m12s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m15s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m39s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 4m28s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 12s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 2m10s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 26s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 48s
WAI energy receipts / energy receipts · linux (pull_request) Failing after 57s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 1m2s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m2s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 3m51s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 1m48s
standards workspaces build + test / aac-rs (pull_request) Has been cancelled
standards workspaces build + test / arl-rs (pull_request) Has been cancelled
standards workspaces build + test / joule-code-rs (pull_request) Has been cancelled
standards workspaces build + test / joule-ui-rs (pull_request) Has been cancelled
standards workspaces build + test / joulecontract-rs (pull_request) Has been cancelled
standards workspaces build + test / jouledesk-rs (pull_request) Has been cancelled
standards workspaces build + test / joulehook-rs (pull_request) Has been cancelled
standards workspaces build + test / map-rs (pull_request) Has been cancelled
standards workspaces build + test / mesh-rs (pull_request) Has been cancelled
standards workspaces build + test / openpay (pull_request) Has been cancelled
standards workspaces build + test / sense-rs (pull_request) Has been cancelled
standards workspaces build + test / macOS energy backends (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 / cross-arch byte-equality · linux-arm64 (pull_request) Has been cancelled
WAI conformance / cross-arch byte-equality · macos (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
standards workspaces build + test / jouleclaw-rs (pull_request) Has been cancelled
A fault-tolerant machine must decode each syndrome round before the next arrives. Exact matching (quantum_match) is the accuracy reference; quantum_uf is the fast decoder over the same matching graph: - Grow: every fired detector starts a cluster; odd clusters (not on the boundary) grow all frontier edges at once, on the graph's integer weights, an edge between two growing clusters at double speed. - Merge: a filled edge joins its clusters; a cluster on the boundary stops. - Peel: each cluster's filled edges as a spanning tree, rooted at the boundary when it holds it, peeled leaves-first into a correction. - Growth is event-driven: each frontier edge carries a fill time, and a merge reschedules only the frontier of a side whose growth changed. Fill times never run backwards, so events sit in a radix heap at amortised O(1). Profiling put 45% of the time in a binary heap before; the radix heap halved the time per shot at d = 21. Time now grows linearly with fired detectors, about 1.1-1.5 us each from d = 9 to d = 21. - A reusable workspace resets in time proportional to the last shot. Checked: - every single edge of d = 3 and d = 5 graphs is corrected exactly; - the event-driven schedule agrees with a round-by-round one (kept in the tests) on 2,000 shots at each of three distances, up to tie order; - on the same 20,000 shots, union-find fails 0.92-1.18x as often as exact matching (d = 3-9, p = 0.003-0.007), 77x faster at d = 11; its threshold sits a little lower (at p = 0.007 its failures stop falling with distance). CLI: wai-quantum qec --decoder uf|exact. Example: uf_bench. Pinned bit for bit natively and under wasm32-wasip2 (17 pins); CLI output byte-identical. Co-Authored-By: Claude Opus 5.5 <[email protected]>
46 lines
2.1 KiB
Rust
46 lines
2.1 KiB
Rust
//! Union-find against exact matching on rotated surface-code memory
|
|
//! experiments: logical failures on the same shots, and time per shot.
|
|
//!
|
|
//! ```text
|
|
//! cargo run --release --example uf_bench --features quantum_uf -- P SHOTS D [D …] [--uf-only]
|
|
//! ```
|
|
use wai_quantum::quantum_frame::{error_model, parse, sample, surface_code_memory};
|
|
use wai_quantum::quantum_match::MatchingGraph;
|
|
use wai_quantum::quantum_uf::UnionFind;
|
|
|
|
fn main() {
|
|
let a: Vec<String> = std::env::args().collect();
|
|
let p: f64 = a[1].parse().unwrap();
|
|
let shots: usize = a[2].parse().unwrap();
|
|
let uf_only = a.iter().any(|x| x == "--uf-only");
|
|
for d in a[3..].iter().filter(|x| !x.starts_with("--")).map(|x| x.parse::<u32>().unwrap()) {
|
|
let c = parse(&surface_code_memory(d, d, p)).unwrap();
|
|
let g = MatchingGraph::from_model(&error_model(&c).unwrap()).unwrap();
|
|
let det = sample(&c, shots, 7);
|
|
let uf = UnionFind::new(&g);
|
|
let mut ws = uf.workspace();
|
|
let shots_fired: Vec<Vec<u32>> = (0..shots).map(|s| det.fired(s)).collect();
|
|
let t0 = std::time::Instant::now();
|
|
let mut uf_fail = 0;
|
|
for (s, f) in shots_fired.iter().enumerate() {
|
|
uf_fail += u64::from(uf.decode(f, &mut ws).unwrap() != det.flips(s));
|
|
}
|
|
let uf_us = t0.elapsed().as_secs_f64() * 1e6 / shots as f64;
|
|
let mean_fired = shots_fired.iter().map(Vec::len).sum::<usize>() as f64 / shots as f64;
|
|
if uf_only {
|
|
println!("d={d:>2} p={p} shots={shots} fired/shot {mean_fired:.1} uf {uf_fail} ({uf_us:.2} us/shot)");
|
|
continue;
|
|
}
|
|
let t1 = std::time::Instant::now();
|
|
let mut mw_fail = 0;
|
|
for (s, f) in shots_fired.iter().enumerate() {
|
|
mw_fail += u64::from(g.decode(f).unwrap() != det.flips(s));
|
|
}
|
|
let mw_us = t1.elapsed().as_secs_f64() * 1e6 / shots as f64;
|
|
println!(
|
|
"d={d:>2} p={p} shots={shots} fired/shot {mean_fired:.1} uf {uf_fail} ({uf_us:.2} us/shot) exact {mw_fail} ({mw_us:.1} us/shot) ratio {:.2} speedup {:.0}x",
|
|
uf_fail as f64 / mw_fail.max(1) as f64,
|
|
mw_us / uf_us
|
|
);
|
|
}
|
|
}
|