open-standards/wai/wai-quantum/examples/qec_memory/data.rs
Transaction Science 86c2a5c393
Some checks failed
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 23s
WAI examples / WAI examples · does this change touch them (push) Successful in 29s
wai-quantum for JavaScript / does this change touch it (push) Successful in 41s
wai-quantum docs, course and locales / does this change touch it (push) Successful in 45s
wai-audit collector and player integration / does this change touch it (push) Successful in 59s
wai-quantum resource estimation / does this change touch it (push) Successful in 46s
wai-quantum docs, course and locales / the Studio's words in every locale · linux (push) Successful in 21s
wai-quantum wasmQ / does this change touch it (push) Successful in 1m19s
WAI operation catalogue / does this change touch it (push) Successful in 1m20s
wai-quantum for Python / does this change touch it (push) Successful in 40s
wai-quantum error correction and mitigation / does this change touch it (push) Successful in 41s
wai-quantum service / does this change touch it (push) Successful in 41s
wai-quantum extension points / does this change touch it (push) Successful in 42s
wai-quantum pulse and dynamics / does this change touch it (push) Successful in 1m41s
wai-quantum editors / does this change touch it (push) Successful in 2m8s
wai-quantum command line / does this change touch it (push) Successful in 2m11s
wai-quantum other formats / does this change touch it (push) Successful in 2m12s
wai-quantum clippy / does this change touch it (push) Successful in 2m12s
wai-quantum contract / does this change touch it (push) Successful in 2m12s
wai-quantum OpenQASM reader / does this change touch it (push) Successful in 2m13s
wai-quantum studio and desktop / does this change touch it (push) Successful in 53s
wai-quantum wasmQ / the reference modules rebuild to their modules of record · linux (push) Successful in 56s
wai-quantum variational and algorithm library / does this change touch it (push) Successful in 53s
wai-quantum verdicts and repairs / does this change touch it (push) Successful in 59s
wai-sink-android native sink library / does this change touch it (push) Successful in 57s
wai-quantum views in agent hosts / does this change touch it (push) Successful in 59s
WAI Studio / does this change touch it (push) Successful in 55s
wai-sink-swift native sink package / does this change touch it (push) Successful in 1m25s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Successful in 2m40s
wai-quantum pulse and dynamics / the same bits · macos (push) Successful in 1m46s
wai-audit collector and player integration / collector · linux (push) Successful in 4m6s
wai-quantum wasmQ / profile, engines, reference modules, adversarial, vectors · macos (push) Successful in 3m38s
wai-quantum for Python / python · macos (push) Successful in 3m48s
wai-quantum pulse and dynamics / the same bits · linux-arm64 (push) Successful in 3m59s
wai-quantum editors / editors · macos (push) Successful in 2m28s
wai-quantum pulse and dynamics / reader, simulator and pulse jobs under wasm32-wasip2 (wasmtime) (push) Successful in 4m52s
wai-quantum docs, course and locales / the course and the command line's languages · linux (push) Successful in 5m56s
wai-quantum service / service energy · linux packages (push) Successful in 5m23s
wai-quantum command line / command line · macos (push) Successful in 3m24s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 8m10s
wai-quantum wasmQ / interpreter and vectors under wasm32-wasip2 (wasmtime) (push) Successful in 7m52s
wai-quantum contract / the policy is linked, and the deprecations page reads (push) Successful in 10s
wai-quantum service / service · macos (push) Successful in 8m1s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (push) Successful in 7m32s
wai-quantum pulse and dynamics / reader, simulator and pulse jobs · linux, release (push) Successful in 8m36s
wai-quantum editors / editors · linux (push) Successful in 8m4s
wai-quantum extension points / the registry under wasm32-wasip2 (wasmtime) (push) Successful in 8m53s
wai-quantum wasmQ / profile, engines, reference modules, adversarial, vectors · linux (push) Successful in 9m7s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Successful in 8m4s
wai-quantum studio and desktop / desktop app · macos (push) Successful in 7m29s
wai-quantum OpenQASM reader / corpus under wasm32-wasip2 (wasmtime) (push) Successful in 1m18s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Failing after 12m8s
wai-quantum contract / API compatibility · linux (push) Successful in 5m16s
wai-quantum for JavaScript / javascript · linux (push) Successful in 11m27s
wai-quantum extension points / the registry and the example · linux-arm64 (push) Successful in 12m6s
WAI examples / WAI examples · android-app · linux-arm64 (push) Successful in 13m41s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Successful in 9m15s
wai-quantum service / service · linux (push) Successful in 12m53s
wai-quantum resource estimation / estimation and calibration · linux, release (push) Successful in 14m1s
wai-quantum verdicts and repairs / verdicts, repairs and the intent set · linux (push) Successful in 3m10s
wai-quantum verdicts and repairs / the intent set and the verdict under wasm32-wasip2 (wasmtime) (push) Successful in 3m22s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux, release (push) Successful in 4m10s
wai-quantum error correction and mitigation / codes, decoders, texts and mitigation · linux, release (push) Successful in 14m45s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Successful in 1m9s
WAI Studio / Studio in Chromium and Firefox · macos (push) Successful in 11m39s
wai-quantum extension points / the registry and the example · linux (push) Successful in 15m22s
wai-quantum for Python / python · linux (push) Successful in 15m45s
wai-quantum other formats / the set under wasm32-wasip2 (wasmtime) (push) Successful in 7m23s
wai-quantum clippy / clippy, warnings denied, every feature set · linux (push) Successful in 11m15s
WAI operation catalogue / the catalogue and every door · linux (push) Successful in 17m27s
wai-quantum other formats / readers, writers and the set · linux (push) Successful in 10m16s
wai-quantum views in agent hosts / views · the page and its engine (push) Successful in 5m18s
wai-quantum OpenQASM reader / reader and corpus · linux (push) Successful in 10m1s
wai-quantum studio and desktop / desktop app · linux (push) Successful in 10m56s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (push) Successful in 21m33s
wai-quantum views in agent hosts / views · a scripted host (push) Successful in 7m1s
wai-quantum studio and desktop / studio · browser (push) Successful in 13m47s
WAI examples / WAI examples · portable · linux (push) Successful in 24m8s
wai-quantum studio and desktop / desktop app · windows (push) Successful in 17m57s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 29m1s
WAI Studio / Studio in WebKit · linux (push) Successful in 16m2s
wai-quantum contract / schemas and the committed tool lists · linux (push) Successful in 25m13s
wai-quantum command line / command line · linux (push) Successful in 28m51s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Successful in 28m15s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (push) Successful in 36m16s
qec_memory: held-out shot windows, every decoder's predictions kept, and paired tests against the published decoders
The surface-code memory example (examples/qec_memory) gains what a paired
comparison with the published decoders needs:

- survey --skip K decodes shots K to K + N of each experiment (K even, so
  every shot keeps its parity and the model that parity picks), and scores
  the published decoders on the same shots: shots a decoder's settings were
  never chosen on.
- survey --predictions DIR now also writes the published decoders'
  predictions on the same shots and the record's own flips, one byte a
  shot, beside this crate's.
- Every receipt records its first shot, and per experiment, for each of
  this crate's decoders against each published decoder, the shots only one
  of the two got wrong.
- qec_memory pairs SET KEY@RECEIPT... tests those counts: the exact
  two-sided McNemar test per distance and over every distance given, the
  fitted rates and Lambda of both, and a paired bootstrap (every fitted
  experiment's shots drawn again with the two decoders' outcomes on a shot
  kept together, each replicate from its own seed) of each distance's
  rate ratio and of the difference in Lambda.

No receipt changes here; the runs that use it follow.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-13 07:31:16 -05:00

230 lines
12 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 two published record sets, read as they unpack.
use std::path::{Path, PathBuf};
use wai_quantum::quantum_frame::ErrorModel;
use wai_quantum::quantum_memory::{Shots, fit_priors, read_01, read_b8};
use wai_quantum::quantum_qec_text::parse_dem;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Set {
/// doi:10.5281/zenodo.6804040: distance 3 (four patches) and 5, rounds
/// 1 to 25, 50,000 shots an experiment.
Y2023,
/// doi:10.5281/zenodo.13273331, the 105-qubit set: distance 3 (nine
/// patches), 5 (four) and 7 (one), rounds 1 to 250.
Y2024,
}
impl Set {
pub fn parse(s: &str) -> Option<Set> {
match s {
"2023" => Some(Set::Y2023),
"2024" => Some(Set::Y2024),
_ => None,
}
}
pub fn name(self) -> &'static str {
match self {
Set::Y2023 => "2023",
Set::Y2024 => "2024",
}
}
/// The experiment, as cited.
pub fn citation(self) -> &'static str {
match self {
Set::Y2023 => "Google Quantum AI, Suppressing quantum errors by scaling a surface code logical qubit, Nature 614, 676 (2023), doi:10.1038/s41586-022-05434-1 (allow-name: the citation and CC BY 4.0 credit the record's licence requires)",
Set::Y2024 => "Google Quantum AI and Collaborators, Quantum error correction below the surface code threshold, Nature 638, 920 (2025), doi:10.1038/s41586-024-08449-y (allow-name: the citation and CC BY 4.0 credit the record's licence requires)",
}
}
/// The record, pinned.
pub fn source(self) -> serde_json::Value {
match self {
Set::Y2023 => serde_json::json!({
"doi": "10.5281/zenodo.6804040",
"url": "https://zenodo.org/api/records/6804040/files/google_qec3v5_experiment_data.zip/content", // allow-name: the record's own file name and its CC BY 4.0 credit
"file": "google_qec3v5_experiment_data.zip", "bytes": 315_490_804u64, // allow-name: the record's own file name and its CC BY 4.0 credit
"md5": "a7fd8b481c3087090093106382dc217d",
"sha256": "ef569742126b305e163cc7fccbf6275e03bdd54d090dffb1aef12a78af0a74c4",
"licence": "CC BY 4.0", "by": "Google Quantum AI", // allow-name: the record's own file name and its CC BY 4.0 credit
}),
Set::Y2024 => serde_json::json!({
"doi": "10.5281/zenodo.13273331",
"url": "https://zenodo.org/api/records/13273331/files/google_105Q_surface_code_d3_d5_d7.zip/content", // allow-name: the record's own file name and its CC BY 4.0 credit
"file": "google_105Q_surface_code_d3_d5_d7.zip", "bytes": 5_716_907_033u64, // allow-name: the record's own file name and its CC BY 4.0 credit
"md5": "21fa6ad35b395d838ebcdbc92e364a12",
"sha256": "1e8e3b4f5f35ba4fd9b4a8448473f37a7090b404fd6eff0c00188092876070dd",
"licence": "CC BY 4.0", "by": "Google Quantum AI", // allow-name: the record's own file name and its CC BY 4.0 credit
}),
}
}
/// The fit, as the receipt states it.
pub fn fit_rule(self) -> &'static str {
match self {
Set::Y2023 => "per patch and basis, a weighted least-squares line through ln(1 − 2p) over rounds 3 to 25 (binomial weights; σ scaled by √(χ²/dof) when above one); ε averaged over patches and bases; Λ = ε₃/ε₅",
Set::Y2024 => "per patch and basis, a weighted least-squares line through ln(1 − 2p) over rounds 10 to 250 less the 13-round calibration experiments (binomial weights; σ scaled by √(χ²/dof) when above one); ε averaged over patches and bases; Λ from a weighted line through ln ε_d against (d + 1)/2",
}
}
/// Whether a publication's fits use an experiment of `rounds` rounds:
/// from round 3 (2023); from round 10 (2024), less the 13-round
/// experiments, which calibrated the optimized prior and carry no
/// ensembled-synthesis predictions.
pub fn fitted(self, rounds: u32) -> bool {
match self {
Set::Y2023 => rounds >= 3,
Set::Y2024 => rounds >= 10 && rounds != 13,
}
}
}
/// One experiment of a set.
#[derive(Clone, Debug)]
pub struct Experiment {
pub name: String,
pub dir: PathBuf,
pub distance: u32,
pub basis: String,
pub rounds: u32,
pub patch: String,
pub shots: usize,
pub detectors: usize,
}
fn kv(text: &str, key: &str) -> Option<String> {
text.lines().find_map(|l| l.strip_prefix(key).and_then(|r| r.strip_prefix(':')).map(|v| v.trim().to_string()))
}
/// Every surface-code experiment of the set under `dir`, in name order.
pub fn list(set: Set, dir: &Path) -> Vec<Experiment> {
let mut out = Vec::new();
match set {
Set::Y2023 => {
let mut names: Vec<String> = std::fs::read_dir(dir).unwrap().map(|e| e.unwrap().file_name().into_string().unwrap()).filter(|n| n.starts_with("surface_code_")).collect();
names.sort();
for n in names {
let d = dir.join(&n);
let p = std::fs::read_to_string(d.join("properties.yml")).unwrap();
let num = |k: &str| kv(&p, k).unwrap_or_else(|| panic!("{n}: no {k}")).parse::<usize>().unwrap();
let (row, col) = (num("center_data_qubit_row"), num("center_data_qubit_col"));
out.push(Experiment {
name: n.clone(),
dir: d.clone(),
distance: num("distance") as u32,
basis: kv(&p, "basis").unwrap(),
rounds: num("rounds") as u32,
patch: format!("{row}_{col}"),
shots: num("shots"),
detectors: num("circuit_detectors"),
});
}
}
Set::Y2024 => {
let root = dir.join("google_105Q_surface_code_d3_d5_d7"); // allow-name: the record's own file name and its CC BY 4.0 credit
let mut patches: Vec<String> = std::fs::read_dir(&root).unwrap().map(|e| e.unwrap().file_name().into_string().unwrap()).filter(|n| n.starts_with('d') && n.contains("_at_")).collect();
patches.sort();
for patch in patches {
for basis in ["X", "Z"] {
let bdir = root.join(&patch).join(basis);
if !bdir.is_dir() {
continue;
}
let mut rs: Vec<String> = std::fs::read_dir(&bdir).unwrap().map(|e| e.unwrap().file_name().into_string().unwrap()).filter(|n| n.starts_with('r')).collect();
rs.sort();
for r in rs {
let d = bdir.join(&r);
let meta: serde_json::Value = serde_json::from_str(&std::fs::read_to_string(d.join("metadata.json")).unwrap()).unwrap();
let get = |k: &str| meta.get(k).and_then(serde_json::Value::as_u64).unwrap_or_else(|| panic!("{}: metadata has no {k}: {meta}", d.display())) as usize;
// The circuit is written out round by round, one
// DETECTOR line a detector.
let circuit = std::fs::read_to_string(d.join("circuit_ideal.stim")).unwrap();
assert!(!circuit.contains("REPEAT"), "{}: a circuit with blocks", d.display());
let detectors = circuit.lines().filter(|l| l.starts_with("DETECTOR")).count();
out.push(Experiment {
name: format!("{patch}/{basis}/{r}"),
dir: d.clone(),
distance: patch[1..].split('_').next().unwrap().parse().unwrap(),
basis: basis.to_string(),
rounds: r[1..].parse().unwrap(),
patch: patch.split("_at_").nth(1).unwrap().to_string(),
shots: get("shots"),
detectors,
});
}
}
}
}
}
out
}
/// The experiment's detection events and observable flips.
pub fn shots(set: Set, e: &Experiment) -> Shots {
let det = std::fs::read(e.dir.join("detection_events.b8")).unwrap();
let obs = match set {
Set::Y2023 => read_01(&std::fs::read_to_string(e.dir.join("obs_flips_actual.01")).unwrap(), 1, e.shots).unwrap(),
Set::Y2024 => read_b8(&std::fs::read(e.dir.join("obs_flips_actual.b8")).unwrap(), 1, e.shots).unwrap(),
};
Shots::from_b8(det, e.detectors, obs, 1).unwrap_or_else(|err| panic!("{}: {err}", e.name))
}
/// The published decoders' predictions for the experiment's shots `skip` to
/// `skip + n`: each decoder's name and its predictions on those shots, shot
/// `skip` first.
pub fn published(set: Set, e: &Experiment, skip: usize, n: usize) -> Vec<(String, Vec<u64>)> {
let mut out = Vec::new();
let cut = |pred: Vec<u64>| -> Vec<u64> { pred[skip..skip + n].to_vec() };
match set {
Set::Y2023 => {
for name in ["pymatching", "correlated_matching", "belief_matching", "tensor_network_contraction"] {
let f = e.dir.join(format!("obs_flips_predicted_by_{name}.01"));
if let Ok(t) = std::fs::read_to_string(&f) {
out.push((name.to_string(), cut(read_01(&t, 1, e.shots).unwrap())));
}
}
}
Set::Y2024 => {
let dr = e.dir.join("decoding_results");
if let Ok(rd) = std::fs::read_dir(&dr) {
let mut names: Vec<String> = rd.map(|x| x.unwrap().file_name().into_string().unwrap()).collect();
names.sort();
for name in names {
let f = dr.join(&name).join("obs_flips_predicted.b8");
if let Ok(b) = std::fs::read(&f) {
out.push((name, cut(read_b8(&b, 1, e.shots).unwrap())));
}
}
}
}
}
out
}
fn read_model(path: &Path) -> ErrorModel {
let t = std::fs::read_to_string(path).unwrap_or_else(|e| panic!("{}: {e}", path.display()));
parse_dem(&t).unwrap_or_else(|e| panic!("{}: {e}", path.display())).to_model().unwrap_or_else(|e| panic!("{}: {e}", path.display()))
}
/// The models a prior names, one per shot residue: shot `s` is decoded with
/// model `s mod len`.
pub fn models(set: Set, e: &Experiment, prior: &str, shots: &Shots) -> Vec<ErrorModel> {
// This crate's fit, over a published model's structure: even shots
// decoded with the model fitted to the odd ones, and odd with even.
let fitted = |structure: ErrorModel| -> Vec<ErrorModel> {
let (for_even, a) = fit_priors(&structure, &shots.select(|s| s % 2 == 1), true, 200);
let (for_odd, b) = fit_priors(&structure, &shots.select(|s| s % 2 == 0), true, 200);
eprintln!("{}: fitted {} symptom sets to {} parities ({} and {} sweeps, residual {:.3e} → {:.3e})", e.name, a.unknowns, a.equations, a.sweeps, b.sweeps, a.residual.0, a.residual.1);
vec![for_even, for_odd]
};
match (set, prior) {
(Set::Y2023, "fit") => fitted(read_model(&e.dir.join("circuit_detector_error_model.dem"))),
(Set::Y2023, "fitpij") => fitted(read_model(&e.dir.join("pij_from_even_for_odd.dem"))),
(Set::Y2024, "fit") => fitted(read_model(&e.dir.join("decoding_results/correlated_matching_decoder_with_si1000_prior/error_model.dem"))),
(Set::Y2024, "fitrl") => fitted(read_model(&e.dir.join("decoding_results/correlated_matching_decoder_with_rl_optimized_prior/error_model.dem"))),
// Even shots with the model fitted to the odd ones, and odd shots
// with the model fitted to the even ones.
(Set::Y2023, "pij") => vec![read_model(&e.dir.join("pij_from_odd_for_even.dem")), read_model(&e.dir.join("pij_from_even_for_odd.dem"))],
(Set::Y2023, "circuit") => vec![read_model(&e.dir.join("circuit_detector_error_model.dem"))],
(Set::Y2024, "si1000") => vec![read_model(&e.dir.join("decoding_results/correlated_matching_decoder_with_si1000_prior/error_model.dem"))],
(Set::Y2024, "rl") => vec![read_model(&e.dir.join("decoding_results/correlated_matching_decoder_with_rl_optimized_prior/error_model.dem"))],
_ => panic!("no prior {prior} for set {}", set.name()),
}
}