wai-quantum 0.3.25: error correction at circuit level, sampled, modelled and decoded exactly #143

Merged
dcharlot merged 1 commit from wai-quantum-qec into main 2026-10-04 20:39:11 -04:00
20 changed files with 3062 additions and 22 deletions

View file

@ -589,7 +589,7 @@ dependencies = [
[[package]]
name = "wai-quantum"
version = "0.3.24"
version = "0.3.25"
dependencies = [
"blake3",
"ed25519-dalek",

View file

@ -415,7 +415,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wai-quantum"
version = "0.3.24"
version = "0.3.25"
dependencies = [
"blake3",
"ed25519-dalek",

View file

@ -1,6 +1,6 @@
[package]
name = "wai-quantum"
version = "0.3.24"
version = "0.3.25"
edition = "2024"
rust-version = "1.97"
license = "Apache-2.0"
@ -53,6 +53,9 @@ quantum_bptn = ["quantum_spd"]
quantum_resource = []
# Signed, energy-accounted receipts for it, re-checkable bit for bit anywhere.
quantum_resource_receipt = ["quantum_resource", "_signing"]
# Noisy Clifford circuits at QEC scale: bit-packed Pauli-frame sampling, detector
# error models by backward sensitivity, and exact minimum-weight matching.
quantum_frame = []
# Mixed states and the observables that distinguish quantum theory from a classical
# account: density matrices, the partial trace, exact Pauli expectations, CHSH.
quantum_info = ["quantum"]
@ -92,13 +95,21 @@ quantum_phasor_meter = ["_signing"]
# Everything.
full = [
"quantum", "quantum_toolchain", "quantum_mps", "quantum_stabilizer", "quantum_pauli", "quantum_spd", "quantum_spd_receipt", "quantum_bptn", "quantum_resource", "quantum_resource_receipt", "quantum_qir", "quantum_info", "quantum_qudit", "quantum_comm", "quantum_source", "quantum_channel",
"quantum", "quantum_toolchain", "quantum_mps", "quantum_stabilizer", "quantum_pauli", "quantum_spd", "quantum_spd_receipt", "quantum_bptn", "quantum_resource", "quantum_resource_receipt", "quantum_frame", "quantum_qir", "quantum_info", "quantum_qudit", "quantum_comm", "quantum_source", "quantum_channel",
"quantum_receipt", "quantum_ops", "quantum_cal", "quantum_mitigate", "quantum_qec",
"quantum_control", "quantum_noise", "quantum_compile", "quantum_atom", "quantum_qbom",
"quantum_vml", "quantum_phasor", "quantum_qfhrr", "quantum_kernel", "quantum_qdata",
"quantum_phasor_meter",
]
[[example]]
name = "qec_threshold"
required-features = ["quantum_frame"]
[[example]]
name = "qec_frame"
required-features = ["quantum_frame"]
[[example]]
name = "utility"
required-features = ["quantum_spd"]

View file

@ -69,6 +69,7 @@ never creates information.
| `quantum_cal`, `quantum_control`, `quantum_noise` | calibration, filter-function robust control, DD noise spectroscopy, Cycle-Benchmarking noise learning |
| `quantum_mitigate` | zero-noise extrapolation, readout M3, classical shadows |
| `quantum_qec` | CSS codes (distance verified by exhaustive search, not asserted) — **rotated surface** `[[d²,1,d]]`, toric, bivariate-bicycle, and the **`[[7,1,3]]` colour code**, whose self-duality buys a *transversal* logical Hadamard — one round, no ancillas, no surgery — verified on the simulator, with the surface code shown failing the same move — with **two decoders**: union-find (weighted matching, single-shot, space-time with faulty measurement, **and circuit-level with hook errors**; **imports and exports** Stim-syntax detector error models, so other tools' noise models decode here and ours decode there) and Relay-BP (for qLDPC) |
| `quantum_frame` | **noisy Clifford circuits at QEC scale**: reads the ecosystem's circuit text; samples detection events by bit-packed Pauli-frame simulation (64 shots per word); derives the detector error model by backward sensitivity, refusing non-deterministic detectors; generates the rotated surface-code memory experiment byte for byte as the reference layout; and decodes by **exact minimum-weight perfect matching** (Edmonds' blossom on integer weights). Measures logical error rates and fits the code model resource estimates assume |
| `quantum_compile` | Clifford routing + stabilizer-tableau equivalence proof |
| `quantum_atom` | neutral-atom register preparation (Hungarian / LSAP) |
| `quantum_qir` | **QIR export + ingest** — emit and parse QIR (the QIR Alliance's LLVM-based IR); emitted modules validated with `llvm-as`, and 9 of 10 catalog algorithms re-ingest to a bit-identical statevector |
@ -148,6 +149,32 @@ search limits, to the bit). Re-running it anywhere must reproduce every figure,
down to each factory round, and the bytes are the same natively and as a WASI
component.
## Error correction at circuit level
An error-correction experiment is a noisy Clifford circuit, its detectors and
its logical observable. `quantum_frame` samples it, derives its detector error
model, and decodes it exactly:
```sh
wai-quantum qec -d 5 --p 0.003 --shots 100000 # surface-code memory, per-round logical error
wai-quantum qec --file circuit.txt --shots 100000 # any circuit in the ecosystem's text
wai-quantum qec --dem -d 3 # its detector error model
wai-quantum qec --fit # measure a and p* of P(d) = a·(p/p*)^((d+1)/2)
```
The model is checked against an independent implementation:
- the generated circuits are identical, byte for byte, across distances 2–15
and 1–8 rounds;
- the derived error models match mechanism for mechanism, to 10⁻¹² in
probability, across surface codes in both bases (rotated and unrotated) and
repetition codes;
- decoded logical error rates agree point by point within sampling error.
`tests/frame_reference.rs` keeps two of those fixtures in the repository. The
fitted code model comes out at `a ≈ 0.023`, `p* ≈ 0.0100`. That is a measured
value for the constants a resource estimate (`quantum_resource`) otherwise takes
on trust, and it can be fed straight into one.
## Determinism
Simulation is **byte-exact**: amplitudes are dyadic fixed-point, so a circuit

View file

@ -0,0 +1,65 @@
//! Noisy Clifford circuits from the command line.
//!
//! ```text
//! cargo run --release --example qec_frame --features quantum_frame -- gen 5 5 0.001
//! cargo run --release --example qec_frame --features quantum_frame -- dem circuit.txt
//! cargo run --release --example qec_frame --features quantum_frame -- sample circuit.txt 100000 7
//! cargo run --release --example qec_frame --features quantum_frame -- decode circuit.txt events.txt
//! cargo run --release --example qec_frame --features quantum_frame -- rate circuit.txt 100000 7
//! ```
use wai_quantum::quantum_frame::{error_model, parse, sample, surface_code_memory};
use wai_quantum::quantum_match::MatchingGraph;
fn main() {
let a: Vec<String> = std::env::args().collect();
let read = |p: &str| std::fs::read_to_string(p).expect("read circuit");
match a.get(1).map(String::as_str) {
Some("gen") => {
let d: u32 = a[2].parse().unwrap();
let r: u32 = a[3].parse().unwrap();
let p: f64 = a[4].parse().unwrap();
print!("{}", surface_code_memory(d, r, p));
}
Some("dem") => {
let c = parse(&read(&a[2])).expect("parse");
print!("{}", error_model(&c).expect("model").to_text());
}
Some("sample") => {
let c = parse(&read(&a[2])).expect("parse");
let shots: usize = a[3].parse().unwrap();
let seed: u64 = a[4].parse().unwrap();
let s = sample(&c, shots, seed);
for d in 0..s.detectors {
println!("D{d} {}", s.count(d));
}
let flips = (0..shots).filter(|&i| s.flips(i) & 1 == 1).count();
println!("L0 {flips}");
}
// One line per shot of fired detector indices; prints one predicted
// observable mask per line.
Some("decode") => {
let c = parse(&read(&a[2])).expect("parse");
let g = MatchingGraph::from_model(&error_model(&c).expect("model")).expect("graphlike");
let mut out = String::new();
for line in read(&a[3]).lines() {
let fired: Vec<u32> = line.split_whitespace().map(|t| t.parse().unwrap()).collect();
out += &format!("{}\n", g.decode(&fired).expect("decode"));
}
print!("{out}");
}
// Sample and decode: logical failures out of shots, and the time spent.
Some("rate") => {
let c = parse(&read(&a[2])).expect("parse");
let shots: usize = a[3].parse().unwrap();
let seed: u64 = a[4].parse().unwrap();
let g = MatchingGraph::from_model(&error_model(&c).expect("model")).expect("graphlike");
let t0 = std::time::Instant::now();
let s = sample(&c, shots, seed);
let t1 = t0.elapsed();
let f = g.failures(&s).expect("decode");
let t2 = t0.elapsed() - t1;
println!("{f} {shots} sample_s={:.3} decode_s={:.3}", t1.as_secs_f64(), t2.as_secs_f64());
}
_ => eprintln!("usage: qec_frame gen D ROUNDS P | dem FILE | sample FILE SHOTS SEED | decode FILE EVENTS | rate FILE SHOTS SEED"),
}
}

View file

@ -0,0 +1,26 @@
//! Measure the rotated surface code's logical error rate per round, and fit
//! the code model `P(d) = a·(p/p*)^((d+1)/2)` that resource estimates assume.
//!
//! ```text
//! cargo run --release --example qec_threshold --features quantum_frame -- [SHOTS] [SEED]
//! ```
use wai_quantum::quantum_match::{fit_code_model, memory_experiment};
fn main() {
let a: Vec<String> = std::env::args().collect();
let shots: usize = a.get(1).map_or(200_000, |s| s.parse().unwrap());
let seed: u64 = a.get(2).map_or(1, |s| s.parse().unwrap());
let mut pts = Vec::new();
println!("{:>3} {:>7} {:>9} {:>9} {:>12}", "d", "p", "shots", "failures", "per round");
for d in [3u32, 5, 7] {
for p in [0.002, 0.003, 0.004, 0.005] {
// Fewer shots where decoding costs more and failures are plentiful.
let n = if d == 7 { shots / 4 } else { shots };
let t = memory_experiment(d, d, p, n, seed).expect("decode");
println!("{:>3} {:>7} {:>9} {:>9} {:>12.4e}", d, p, t.shots, t.failures, t.per_round());
pts.push(t);
}
}
let f = fit_code_model(&pts).expect("fit");
println!("fit: a = {:.4}, p* = {:.5}", f.prefactor, f.threshold);
}

View file

@ -27,7 +27,8 @@ mod linalg;
feature = "quantum_vml",
feature = "quantum_phasor",
feature = "quantum_kernel",
feature = "quantum_resource"
feature = "quantum_resource",
feature = "quantum_frame"
))]
mod repro;
@ -91,6 +92,12 @@ pub mod quantum_resource;
#[cfg(feature = "quantum_resource_receipt")]
pub mod quantum_resource_receipt;
#[cfg(feature = "quantum_frame")]
pub mod quantum_frame;
#[cfg(feature = "quantum_frame")]
pub mod quantum_match;
#[cfg(feature = "quantum_info")]
pub mod quantum_info;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -30,28 +30,28 @@ pub use trig::sin_cos;
// Natural logarithm
// ---------------------------------------------------------------------------
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame"))]
const LN2_HI: f64 = 6.931_471_803_691_238_164_90e-1;
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame"))]
const LN2_LO: f64 = 1.908_214_929_270_587_700_02e-10;
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame"))]
const LG1: f64 = 6.666_666_666_666_735_130e-1;
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame"))]
const LG2: f64 = 3.999_999_999_940_941_908e-1;
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame"))]
const LG3: f64 = 2.857_142_874_366_239_149e-1;
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame"))]
const LG4: f64 = 2.222_219_843_214_978_396e-1;
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame"))]
const LG5: f64 = 1.818_357_216_161_805_012e-1;
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame"))]
const LG6: f64 = 1.531_383_769_920_937_332e-1;
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame"))]
const LG7: f64 = 1.479_819_860_511_658_591e-1;
/// The natural logarithm, within an ulp of the true value: `−∞` at zero, NaN
/// below it.
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame"))]
pub fn ln(x: f64) -> f64 {
let mut bits = x.to_bits();
let mut hx = (bits >> 32) as u32;
@ -91,6 +91,74 @@ pub fn ln(x: f64) -> f64 {
s * (hfsq + r) + dk * LN2_LO - hfsq + f + dk * LN2_HI
}
// ---------------------------------------------------------------------------
// Exponential
// ---------------------------------------------------------------------------
#[cfg(feature = "quantum_frame")]
const EXP_P: [f64; 5] = [
1.666_666_666_666_660_190_37e-1,
-2.777_777_777_701_559_338_42e-3,
6.613_756_321_437_934_361_17e-5,
-1.653_390_220_546_525_153_90e-6,
4.138_136_797_057_238_460_39e-8,
];
/// `eˣ`, bit-identical on every IEEE-754 platform: fdlibm's algorithm
/// (reduction by `ln 2` in two parts, a rational approximation on
/// `[−ln2/2, ln2/2]`), using only `+ − × ÷`. Within an ulp of the true value.
#[cfg(feature = "quantum_frame")]
pub fn exp(x: f64) -> f64 {
const LN2_HI_E: f64 = 6.931_471_803_691_238_164_90e-1;
const LN2_LO_E: f64 = 1.908_214_929_270_587_700_02e-10;
const INV_LN2: f64 = 1.442_695_040_888_963_387_00;
if x.is_nan() {
return x;
}
if x > 7.097_827_128_933_839_730_96e2 {
return f64::INFINITY;
}
if x < -7.451_332_191_019_411_084_20e2 {
return 0.0;
}
let ax = x.abs();
let (hi, lo, k): (f64, f64, i32);
if ax > 0.5 * core::f64::consts::LN_2 {
let kk = if ax < 1.5 * core::f64::consts::LN_2 {
if x < 0.0 { -1 } else { 1 }
} else {
(INV_LN2 * x + if x < 0.0 { -0.5 } else { 0.5 }) as i32
};
let t = f64::from(kk);
hi = x - t * LN2_HI_E;
lo = t * LN2_LO_E;
k = kk;
} else if ax < 3.725_290_298_461_914e-9 {
// |x| < 2⁻²⁸: 1 + x to the last bit.
return 1.0 + x;
} else {
hi = x;
lo = 0.0;
k = 0;
}
let r = hi - lo;
let t = r * r;
let c = r - t * (EXP_P[0] + t * (EXP_P[1] + t * (EXP_P[2] + t * (EXP_P[3] + t * EXP_P[4]))));
if k == 0 {
return 1.0 - ((r * c) / (c - 2.0) - r);
}
let y = 1.0 - ((lo - (r * c) / (2.0 - c)) - hi);
// y · 2ᵏ, in two steps where 2ᵏ alone would leave the normal range.
let pow2 = |e: i32| f64::from_bits(((e + 1023) as u64) << 52);
if k > 1023 {
y * pow2(1023) * pow2(k - 1023)
} else if k < -1021 {
y * pow2(k + 1000) * pow2(-1000)
} else {
y * pow2(k)
}
}
// ---------------------------------------------------------------------------
// Arctangent
// ---------------------------------------------------------------------------
@ -240,6 +308,27 @@ mod tests {
#[allow(unused_imports)]
use super::*;
#[cfg(feature = "quantum_frame")]
#[test]
fn exp_is_within_an_ulp_of_the_platform() {
let mut s = 0x9e37_79b9_7f4a_7c15u64;
for i in 0..400_000 {
s ^= s << 13;
s ^= s >> 7;
s ^= s << 17;
let u = (s >> 11) as f64 / (1u64 << 53) as f64;
let span = [1e-10, 0.3, 0.7, 2.0, 20.0, 700.0, 740.0][i % 7];
let x = (2.0 * u - 1.0) * span;
assert!(ulps(exp(x), x.exp()) <= 1, "exp({x:e}) = {:e} vs {:e}", exp(x), x.exp());
}
assert_eq!(exp(0.0), 1.0);
assert_eq!(exp(f64::INFINITY), f64::INFINITY);
assert_eq!(exp(f64::NEG_INFINITY), 0.0);
assert!(exp(f64::NAN).is_nan());
assert_eq!(exp(800.0), f64::INFINITY);
assert_eq!(exp(-800.0), 0.0);
}
#[allow(dead_code)]
fn ulps(a: f64, b: f64) -> u64 {
if a == b {
@ -262,7 +351,7 @@ mod tests {
})
}
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame"))]
#[test]
fn ln_is_within_an_ulp_of_the_platform() {
for x in inputs().filter(|&x| x > 0.0) {

View file

@ -15,7 +15,8 @@
feature = "quantum_pauli",
feature = "quantum_spd",
feature = "quantum_bptn",
feature = "quantum_resource"
feature = "quantum_resource",
feature = "quantum_frame"
))]
use wai_quantum::*;
@ -147,3 +148,27 @@ fn resource_estimates() {
assert_eq!(digest(&v), 0xce9d_43b2_d498_f379);
}
/// Detection counts of a sampled surface-code run, the failures its decoding
/// leaves, and the code model fitted from a small grid: the sampler's random
/// stream, its logarithm-driven skips, the matching and the fit's `exp`.
#[test]
fn frame_sampling_and_matching() {
use quantum_frame::{parse, sample, surface_code_memory};
use quantum_match::{fit_code_model, memory_experiment};
let c = parse(&surface_code_memory(3, 3, 0.01)).unwrap();
let s = sample(&c, 10_000, 5);
let mut v: Vec<f64> = (0..s.detectors).map(|d| s.count(d) as f64).collect();
let mut pts = Vec::new();
for d in [3, 5] {
for p in [0.004, 0.008] {
let t = memory_experiment(d, d, p, 4_000, 9).unwrap();
v.push(t.failures as f64);
v.push(t.per_round());
pts.push(t);
}
}
let f = fit_code_model(&pts).unwrap();
v.extend([f.prefactor, f.threshold]);
assert_eq!(digest(&v), 0xcbb3_99f9_6d5c_e8f0);
}

View file

@ -0,0 +1,89 @@
QUBIT_COORDS(1, 1) 1
QUBIT_COORDS(2, 0) 2
QUBIT_COORDS(3, 1) 3
QUBIT_COORDS(5, 1) 5
QUBIT_COORDS(1, 3) 8
QUBIT_COORDS(2, 2) 9
QUBIT_COORDS(3, 3) 10
QUBIT_COORDS(4, 2) 11
QUBIT_COORDS(5, 3) 12
QUBIT_COORDS(6, 2) 13
QUBIT_COORDS(0, 4) 14
QUBIT_COORDS(1, 5) 15
QUBIT_COORDS(2, 4) 16
QUBIT_COORDS(3, 5) 17
QUBIT_COORDS(4, 4) 18
QUBIT_COORDS(5, 5) 19
QUBIT_COORDS(4, 6) 25
RX 1 3 5 8 10 12 15 17 19
Z_ERROR(0.002) 1 3 5 8 10 12 15 17 19
R 2 9 11 13 14 16 18 25
X_ERROR(0.002) 2 9 11 13 14 16 18 25
TICK
DEPOLARIZE1(0.002) 1 3 5 8 10 12 15 17 19
H 2 11 16 25
DEPOLARIZE1(0.002) 2 11 16 25
TICK
CX 2 3 16 17 11 12 15 14 10 9 19 18
DEPOLARIZE2(0.002) 2 3 16 17 11 12 15 14 10 9 19 18
TICK
CX 2 1 16 15 11 10 8 14 3 9 12 18
DEPOLARIZE2(0.002) 2 1 16 15 11 10 8 14 3 9 12 18
TICK
CX 16 10 11 5 25 19 8 9 17 18 12 13
DEPOLARIZE2(0.002) 16 10 11 5 25 19 8 9 17 18 12 13
TICK
CX 16 8 11 3 25 17 1 9 10 18 5 13
DEPOLARIZE2(0.002) 16 8 11 3 25 17 1 9 10 18 5 13
TICK
H 2 11 16 25
DEPOLARIZE1(0.002) 2 11 16 25
TICK
X_ERROR(0.002) 2 9 11 13 14 16 18 25
MR 2 9 11 13 14 16 18 25
X_ERROR(0.002) 2 9 11 13 14 16 18 25
DETECTOR(2, 0, 0) rec[-8]
DETECTOR(2, 4, 0) rec[-3]
DETECTOR(4, 2, 0) rec[-6]
DETECTOR(4, 6, 0) rec[-1]
REPEAT 2 {
TICK
DEPOLARIZE1(0.002) 1 3 5 8 10 12 15 17 19
H 2 11 16 25
DEPOLARIZE1(0.002) 2 11 16 25
TICK
CX 2 3 16 17 11 12 15 14 10 9 19 18
DEPOLARIZE2(0.002) 2 3 16 17 11 12 15 14 10 9 19 18
TICK
CX 2 1 16 15 11 10 8 14 3 9 12 18
DEPOLARIZE2(0.002) 2 1 16 15 11 10 8 14 3 9 12 18
TICK
CX 16 10 11 5 25 19 8 9 17 18 12 13
DEPOLARIZE2(0.002) 16 10 11 5 25 19 8 9 17 18 12 13
TICK
CX 16 8 11 3 25 17 1 9 10 18 5 13
DEPOLARIZE2(0.002) 16 8 11 3 25 17 1 9 10 18 5 13
TICK
H 2 11 16 25
DEPOLARIZE1(0.002) 2 11 16 25
TICK
X_ERROR(0.002) 2 9 11 13 14 16 18 25
MR 2 9 11 13 14 16 18 25
X_ERROR(0.002) 2 9 11 13 14 16 18 25
SHIFT_COORDS(0, 0, 1)
DETECTOR(2, 0, 0) rec[-8] rec[-16]
DETECTOR(2, 2, 0) rec[-7] rec[-15]
DETECTOR(4, 2, 0) rec[-6] rec[-14]
DETECTOR(6, 2, 0) rec[-5] rec[-13]
DETECTOR(0, 4, 0) rec[-4] rec[-12]
DETECTOR(2, 4, 0) rec[-3] rec[-11]
DETECTOR(4, 4, 0) rec[-2] rec[-10]
DETECTOR(4, 6, 0) rec[-1] rec[-9]
}
Z_ERROR(0.002) 1 3 5 8 10 12 15 17 19
MX 1 3 5 8 10 12 15 17 19
DETECTOR(2, 0, 1) rec[-8] rec[-9] rec[-17]
DETECTOR(2, 4, 1) rec[-2] rec[-3] rec[-5] rec[-6] rec[-12]
DETECTOR(4, 2, 1) rec[-4] rec[-5] rec[-7] rec[-8] rec[-15]
DETECTOR(4, 6, 1) rec[-1] rec[-2] rec[-10]
OBSERVABLE_INCLUDE(0) rec[-3] rec[-6] rec[-9]

View file

@ -0,0 +1,221 @@
error(0.003858066319639164) D0 D2
error(0.007686363287824861) D0 D4
error(0.003858066319639164) D0 L0
error(0.004387566933333155) D1 D2
error(0.001066666666666516) D1 D2 D5
error(0.003858066319639164) D1 D3
error(0.000400266975246834) D1 D4
error(0.000400266975246834) D1 D4 D5
error(0.0001334579439585259) D1 D4 D5 D8
error(0.0001334579439585259) D1 D4 D8
error(0.000400266975246834) D1 D5 D6
error(0.0001334579439585259) D1 D5 D6 D8
error(0.000400266975246834) D1 D5 D8 D9
error(0.000400266975246834) D1 D5 D8 L0
error(0.0001334579439585259) D1 D5 D9
error(0.0001334579439585259) D1 D5 L0
error(0.000400266975246834) D1 D6
error(0.0001334579439585259) D1 D6 D8
error(0.000400266975246834) D1 D8 D9
error(0.001466079739032824) D1 D8 L0
error(0.007817769619301565) D1 D9
error(0.008343044294438688) D1 L0
error(0.00821142736622363) D10
error(0.001200017820547319) D10 D11
error(0.0001334579439585259) D10 D11 D14
error(0.0002668802658714406) D10 D11 D14 D18
error(0.000400266975246834) D10 D11 D17
error(0.0001334579439585259) D10 D11 D17 D18
error(0.000400266975246834) D10 D11 D18
error(0.0008002135231907215) D10 D13
error(0.000400266975246834) D10 D13 D14 D17
error(0.0001334579439585259) D10 D14 D16 D17
error(0.0001334579439585259) D10 D14 D17
error(0.000400266975246834) D10 D14 D17 D18
error(0.0001334579439585259) D10 D14 D18
error(0.0002668802658714406) D10 D16
error(0.0001334579439585259) D10 D16 D17 L0
error(0.0001334579439585259) D10 D17 L0
error(0.004785824618421568) D10 D18
error(0.001599324140787608) D11
error(0.0002668802658714406) D11 D14
error(0.0001334579439585259) D11 D14 D18
error(0.0006669335944047278) D11 D17
error(0.000400266975246834) D11 D17 D18
error(0.000533618081590258) D11 D18
error(0.000533618081590258) D11 D18 D19
error(0.007160387030215617) D11 D19
error(0.001200017820547319) D12 D13 D14
error(0.0001334579439585259) D12 D13 D16 L0
error(0.000533618081590258) D12 D13 D20
error(0.001200017820547319) D12 D13 L0
error(0.001200017820547319) D12 D14
error(0.007160387030215617) D12 D20
error(0.001599324140787608) D12 L0
error(0.008211427366223628) D13
error(0.0001334579439585259) D13 D14
error(0.0001334579439585259) D13 D14 D16 D17
error(0.000533618081590258) D13 D14 D17
error(0.001200017820547319) D13 D14 D17 D18
error(0.000533618081590258) D13 D14 D20
error(0.001466257564268764) D13 D16
error(0.001200017820547319) D13 D16 D17 L0
error(0.000533618081590258) D13 D17 D20
error(0.000533618081590258) D13 D17 L0
error(0.001200017820547319) D13 D18
error(0.001066666666666516) D13 D20 L0
error(0.002928215306047978) D14
error(0.001200017820547319) D14 D15
error(0.0001334579439585259) D14 D15 D17
error(0.0001334579439585259) D14 D15 D17 D18
error(0.001200017820547319) D14 D15 D18
error(0.000400266975246834) D14 D15 D18 D22
error(0.000400266975246834) D14 D15 D22
error(0.001865350750555233) D14 D17
error(0.000533618081590258) D14 D17 D18
error(0.0001334579439585259) D14 D18 D22
error(0.001066666666666516) D14 D20
error(0.007817769619301565) D14 D22
error(0.006501598321819228) D15
error(0.0001334579439585259) D15 D17 D18 D22
error(0.0001334579439585259) D15 D17 D22
error(0.001466257564268764) D15 D18
error(0.0001334579439585259) D15 D18 D19
error(0.0001334579439585259) D15 D18 D19 D22
error(0.000400266975246834) D15 D18 D22
error(0.0001334579439585259) D15 D19
error(0.0001334579439585259) D15 D19 D22
error(0.001466079739032824) D15 D22
error(0.005842457790162801) D16
error(0.001599324140787608) D16 D17 L0
error(0.001200017820547319) D17 D18 D19
error(0.0008002135231907215) D17 D18 D21
error(0.000400266975246834) D17 D18 D22
error(0.0002668802658714406) D17 D18 L0
error(0.001200017820547319) D17 D19
error(0.000533618081590258) D17 D20
error(0.007949140876340624) D17 D21
error(0.000400266975246834) D17 D22
error(0.003591243050134198) D17 L0
error(0.008867408007091262) D18
error(0.001599324140787608) D18 D19
error(0.000533618081590258) D18 D19 D21
error(0.000400266975246834) D18 D19 D22
error(0.001066666666666516) D18 D21 D22
error(0.0002668802658714406) D18 D21 L0
error(0.0001334579439585259) D18 D22
error(0.00213123536581817) D19
error(0.001066666666666516) D19 D21
error(0.000400266975246834) D19 D22
error(0.007686363287824861) D19 D23
error(0.007423445364098325) D2
error(0.001066666666666516) D2 D4
error(0.000533618081590258) D2 D4 D5
error(0.0002668802658714406) D2 D5
error(0.0008002135231907215) D2 D5 D6
error(0.007949140876340624) D2 D6
error(0.002531483609263898) D20 D22
error(0.003062399999999851) D20 L0
error(0.003062399999999851) D21 D22
error(0.002531483609263898) D21 D23
error(0.004785824618421568) D21 L0
error(0.005710347764487468) D22
error(0.002531483609263898) D23
error(0.004387566933333155) D3
error(0.001066666666666516) D3 D10
error(0.000533618081590258) D3 D10 D11
error(0.007160387030215617) D3 D11
error(0.000533618081590258) D3 D6
error(0.000533618081590258) D3 D6 D10
error(0.001066666666666516) D3 D9
error(0.000533618081590258) D3 D9 D10
error(0.007160387030215617) D4 D12
error(0.000533618081590258) D4 D5 D12
error(0.001200017820547319) D4 D5 D6
error(0.0001334579439585259) D4 D5 D8 L0
error(0.001599324140787608) D4 D5 L0
error(0.001200017820547319) D4 D6
error(0.0001334579439585259) D4 D8 L0
error(0.00213123536581817) D4 L0
error(0.008867408007091262) D5
error(0.001200017820547319) D5 D10
error(0.000400266975246834) D5 D12 D13 L0
error(0.0001334579439585259) D5 D12 D16 L0
error(0.000533618081590258) D5 D12 L0
error(0.004785824618421568) D5 D13
error(0.000400266975246834) D5 D16
error(0.0002668802658714406) D5 D6
error(0.000400266975246834) D5 D6 D12
error(0.0001334579439585259) D5 D6 D12 D13
error(0.0001334579439585259) D5 D6 D8 D9
error(0.000533618081590258) D5 D6 D9
error(0.001200017820547319) D5 D6 D9 D10
error(0.000400266975246834) D5 D6 D9 D13
error(0.001466257564268764) D5 D8
error(0.001200017820547319) D5 D8 D9 L0
error(0.0001334579439585259) D5 D9 D12
error(0.0002668802658714406) D5 D9 D12 D13
error(0.0001334579439585259) D5 D9 D12 D16
error(0.0001334579439585259) D5 D9 D13 L0
error(0.000400266975246834) D5 D9 D16 L0
error(0.0035912430501342) D6
error(0.000533618081590258) D6 D10
error(0.0001334579439585259) D6 D10 D13 D14
error(0.0006669335944047278) D6 D12
error(0.000400266975246834) D6 D12 D13
error(0.0001334579439585259) D6 D13
error(0.000400266975246834) D6 D13 D14
error(0.007423445364098326) D6 D14
error(0.001599324140787608) D6 D7
error(0.001200017820547319) D6 D7 D10
error(0.000400266975246834) D6 D7 D10 D14
error(0.0001334579439585259) D6 D7 D13
error(0.0002668802658714406) D6 D7 D13 D14
error(0.0001334579439585259) D6 D7 D14
error(0.0001334579439585259) D6 D7 D9 D10
error(0.0001334579439585259) D6 D7 D9 D13
error(0.0001334579439585259) D6 D8 D9
error(0.001865350750555233) D6 D9
error(0.000533618081590258) D6 D9 D10
error(0.000400266975246834) D6 D9 D10 D13
error(0.0001334579439585259) D6 D9 D13
error(0.005842457790162799) D7
error(0.001466257564268764) D7 D10
error(0.0001334579439585259) D7 D10 D11
error(0.0001334579439585259) D7 D10 D11 D14
error(0.000400266975246834) D7 D10 D14
error(0.0001334579439585259) D7 D11 D14 D18
error(0.0001334579439585259) D7 D11 D18
error(0.0002668802658714406) D7 D13
error(0.0001334579439585259) D7 D13 D14
error(0.000400266975246834) D7 D14
error(0.000533618081590258) D7 D14 D15
error(0.000400266975246834) D7 D14 D18
error(0.004521357674826842) D7 D15
error(0.000400266975246834) D7 D18
error(0.0001334579439585259) D7 D9 D10 D14
error(0.0001334579439585259) D7 D9 D13 D14
error(0.006501598321819227) D8
error(0.004521357674826842) D8 D16
error(0.000533618081590258) D8 D9 D16 L0
error(0.001200017820547319) D8 D9 L0
error(0.001200017820547319) D9 D10 D11
error(0.0002668802658714406) D9 D10 D13 D14
error(0.0001334579439585259) D9 D10 D13 D17
error(0.0001334579439585259) D9 D10 D14 D16
error(0.0002668802658714406) D9 D10 D16 D17
error(0.0001334579439585259) D9 D10 D16 L0
error(0.000400266975246834) D9 D10 D17
error(0.0001334579439585259) D9 D10 L0
error(0.001200017820547319) D9 D11
error(0.0002668802658714406) D9 D12
error(0.0001334579439585259) D9 D12 D13
error(0.0001334579439585259) D9 D12 D13 D16
error(0.0001334579439585259) D9 D13 D14 D16
error(0.000400266975246834) D9 D13 D16 D17
error(0.000400266975246834) D9 D13 D16 L0
error(0.0002668802658714406) D9 D14
error(0.0001334579439585259) D9 D16 D17
error(0.000400266975246834) D9 D16 L0
error(0.007423445364098326) D9 D17
error(0.002928215306047977) D9 L0

View file

@ -0,0 +1,153 @@
QUBIT_COORDS(1, 1) 1
QUBIT_COORDS(2, 0) 2
QUBIT_COORDS(3, 1) 3
QUBIT_COORDS(5, 1) 5
QUBIT_COORDS(6, 0) 6
QUBIT_COORDS(7, 1) 7
QUBIT_COORDS(9, 1) 9
QUBIT_COORDS(1, 3) 12
QUBIT_COORDS(2, 2) 13
QUBIT_COORDS(3, 3) 14
QUBIT_COORDS(4, 2) 15
QUBIT_COORDS(5, 3) 16
QUBIT_COORDS(6, 2) 17
QUBIT_COORDS(7, 3) 18
QUBIT_COORDS(8, 2) 19
QUBIT_COORDS(9, 3) 20
QUBIT_COORDS(10, 2) 21
QUBIT_COORDS(0, 4) 22
QUBIT_COORDS(1, 5) 23
QUBIT_COORDS(2, 4) 24
QUBIT_COORDS(3, 5) 25
QUBIT_COORDS(4, 4) 26
QUBIT_COORDS(5, 5) 27
QUBIT_COORDS(6, 4) 28
QUBIT_COORDS(7, 5) 29
QUBIT_COORDS(8, 4) 30
QUBIT_COORDS(9, 5) 31
QUBIT_COORDS(1, 7) 34
QUBIT_COORDS(2, 6) 35
QUBIT_COORDS(3, 7) 36
QUBIT_COORDS(4, 6) 37
QUBIT_COORDS(5, 7) 38
QUBIT_COORDS(6, 6) 39
QUBIT_COORDS(7, 7) 40
QUBIT_COORDS(8, 6) 41
QUBIT_COORDS(9, 7) 42
QUBIT_COORDS(10, 6) 43
QUBIT_COORDS(0, 8) 44
QUBIT_COORDS(1, 9) 45
QUBIT_COORDS(2, 8) 46
QUBIT_COORDS(3, 9) 47
QUBIT_COORDS(4, 8) 48
QUBIT_COORDS(5, 9) 49
QUBIT_COORDS(6, 8) 50
QUBIT_COORDS(7, 9) 51
QUBIT_COORDS(8, 8) 52
QUBIT_COORDS(9, 9) 53
QUBIT_COORDS(4, 10) 59
QUBIT_COORDS(8, 10) 63
R 1 3 5 7 9 12 14 16 18 20 23 25 27 29 31 34 36 38 40 42 45 47 49 51 53
X_ERROR(0.001) 1 3 5 7 9 12 14 16 18 20 23 25 27 29 31 34 36 38 40 42 45 47 49 51 53
R 2 6 13 15 17 19 21 22 24 26 28 30 35 37 39 41 43 44 46 48 50 52 59 63
X_ERROR(0.001) 2 6 13 15 17 19 21 22 24 26 28 30 35 37 39 41 43 44 46 48 50 52 59 63
TICK
DEPOLARIZE1(0.001) 1 3 5 7 9 12 14 16 18 20 23 25 27 29 31 34 36 38 40 42 45 47 49 51 53
H 2 6 15 19 24 28 37 41 46 50 59 63
DEPOLARIZE1(0.001) 2 6 15 19 24 28 37 41 46 50 59 63
TICK
CX 2 3 24 25 46 47 15 16 37 38 6 7 28 29 50 51 19 20 41 42 23 22 45 44 14 13 36 35 27 26 49 48 18 17 40 39 31 30 53 52
DEPOLARIZE2(0.001) 2 3 24 25 46 47 15 16 37 38 6 7 28 29 50 51 19 20 41 42 23 22 45 44 14 13 36 35 27 26 49 48 18 17 40 39 31 30 53 52
TICK
CX 2 1 24 23 46 45 15 14 37 36 6 5 28 27 50 49 19 18 41 40 12 22 34 44 3 13 25 35 16 26 38 48 7 17 29 39 20 30 42 52
DEPOLARIZE2(0.001) 2 1 24 23 46 45 15 14 37 36 6 5 28 27 50 49 19 18 41 40 12 22 34 44 3 13 25 35 16 26 38 48 7 17 29 39 20 30 42 52
TICK
CX 24 14 46 36 15 5 37 27 59 49 28 18 50 40 19 9 41 31 63 53 12 13 34 35 25 26 47 48 16 17 38 39 29 30 51 52 20 21 42 43
DEPOLARIZE2(0.001) 24 14 46 36 15 5 37 27 59 49 28 18 50 40 19 9 41 31 63 53 12 13 34 35 25 26 47 48 16 17 38 39 29 30 51 52 20 21 42 43
TICK
CX 24 12 46 34 15 3 37 25 59 47 28 16 50 38 19 7 41 29 63 51 1 13 23 35 14 26 36 48 5 17 27 39 18 30 40 52 9 21 31 43
DEPOLARIZE2(0.001) 24 12 46 34 15 3 37 25 59 47 28 16 50 38 19 7 41 29 63 51 1 13 23 35 14 26 36 48 5 17 27 39 18 30 40 52 9 21 31 43
TICK
H 2 6 15 19 24 28 37 41 46 50 59 63
DEPOLARIZE1(0.001) 2 6 15 19 24 28 37 41 46 50 59 63
TICK
X_ERROR(0.001) 2 6 13 15 17 19 21 22 24 26 28 30 35 37 39 41 43 44 46 48 50 52 59 63
MR 2 6 13 15 17 19 21 22 24 26 28 30 35 37 39 41 43 44 46 48 50 52 59 63
X_ERROR(0.001) 2 6 13 15 17 19 21 22 24 26 28 30 35 37 39 41 43 44 46 48 50 52 59 63
DETECTOR(0, 4, 0) rec[-17]
DETECTOR(0, 8, 0) rec[-7]
DETECTOR(2, 2, 0) rec[-22]
DETECTOR(2, 6, 0) rec[-12]
DETECTOR(4, 4, 0) rec[-15]
DETECTOR(4, 8, 0) rec[-5]
DETECTOR(6, 2, 0) rec[-20]
DETECTOR(6, 6, 0) rec[-10]
DETECTOR(8, 4, 0) rec[-13]
DETECTOR(8, 8, 0) rec[-3]
DETECTOR(10, 2, 0) rec[-18]
DETECTOR(10, 6, 0) rec[-8]
REPEAT 3 {
TICK
DEPOLARIZE1(0.001) 1 3 5 7 9 12 14 16 18 20 23 25 27 29 31 34 36 38 40 42 45 47 49 51 53
H 2 6 15 19 24 28 37 41 46 50 59 63
DEPOLARIZE1(0.001) 2 6 15 19 24 28 37 41 46 50 59 63
TICK
CX 2 3 24 25 46 47 15 16 37 38 6 7 28 29 50 51 19 20 41 42 23 22 45 44 14 13 36 35 27 26 49 48 18 17 40 39 31 30 53 52
DEPOLARIZE2(0.001) 2 3 24 25 46 47 15 16 37 38 6 7 28 29 50 51 19 20 41 42 23 22 45 44 14 13 36 35 27 26 49 48 18 17 40 39 31 30 53 52
TICK
CX 2 1 24 23 46 45 15 14 37 36 6 5 28 27 50 49 19 18 41 40 12 22 34 44 3 13 25 35 16 26 38 48 7 17 29 39 20 30 42 52
DEPOLARIZE2(0.001) 2 1 24 23 46 45 15 14 37 36 6 5 28 27 50 49 19 18 41 40 12 22 34 44 3 13 25 35 16 26 38 48 7 17 29 39 20 30 42 52
TICK
CX 24 14 46 36 15 5 37 27 59 49 28 18 50 40 19 9 41 31 63 53 12 13 34 35 25 26 47 48 16 17 38 39 29 30 51 52 20 21 42 43
DEPOLARIZE2(0.001) 24 14 46 36 15 5 37 27 59 49 28 18 50 40 19 9 41 31 63 53 12 13 34 35 25 26 47 48 16 17 38 39 29 30 51 52 20 21 42 43
TICK
CX 24 12 46 34 15 3 37 25 59 47 28 16 50 38 19 7 41 29 63 51 1 13 23 35 14 26 36 48 5 17 27 39 18 30 40 52 9 21 31 43
DEPOLARIZE2(0.001) 24 12 46 34 15 3 37 25 59 47 28 16 50 38 19 7 41 29 63 51 1 13 23 35 14 26 36 48 5 17 27 39 18 30 40 52 9 21 31 43
TICK
H 2 6 15 19 24 28 37 41 46 50 59 63
DEPOLARIZE1(0.001) 2 6 15 19 24 28 37 41 46 50 59 63
TICK
X_ERROR(0.001) 2 6 13 15 17 19 21 22 24 26 28 30 35 37 39 41 43 44 46 48 50 52 59 63
MR 2 6 13 15 17 19 21 22 24 26 28 30 35 37 39 41 43 44 46 48 50 52 59 63
X_ERROR(0.001) 2 6 13 15 17 19 21 22 24 26 28 30 35 37 39 41 43 44 46 48 50 52 59 63
SHIFT_COORDS(0, 0, 1)
DETECTOR(2, 0, 0) rec[-24] rec[-48]
DETECTOR(6, 0, 0) rec[-23] rec[-47]
DETECTOR(2, 2, 0) rec[-22] rec[-46]
DETECTOR(4, 2, 0) rec[-21] rec[-45]
DETECTOR(6, 2, 0) rec[-20] rec[-44]
DETECTOR(8, 2, 0) rec[-19] rec[-43]
DETECTOR(10, 2, 0) rec[-18] rec[-42]
DETECTOR(0, 4, 0) rec[-17] rec[-41]
DETECTOR(2, 4, 0) rec[-16] rec[-40]
DETECTOR(4, 4, 0) rec[-15] rec[-39]
DETECTOR(6, 4, 0) rec[-14] rec[-38]
DETECTOR(8, 4, 0) rec[-13] rec[-37]
DETECTOR(2, 6, 0) rec[-12] rec[-36]
DETECTOR(4, 6, 0) rec[-11] rec[-35]
DETECTOR(6, 6, 0) rec[-10] rec[-34]
DETECTOR(8, 6, 0) rec[-9] rec[-33]
DETECTOR(10, 6, 0) rec[-8] rec[-32]
DETECTOR(0, 8, 0) rec[-7] rec[-31]
DETECTOR(2, 8, 0) rec[-6] rec[-30]
DETECTOR(4, 8, 0) rec[-5] rec[-29]
DETECTOR(6, 8, 0) rec[-4] rec[-28]
DETECTOR(8, 8, 0) rec[-3] rec[-27]
DETECTOR(4, 10, 0) rec[-2] rec[-26]
DETECTOR(8, 10, 0) rec[-1] rec[-25]
}
X_ERROR(0.001) 1 3 5 7 9 12 14 16 18 20 23 25 27 29 31 34 36 38 40 42 45 47 49 51 53
M 1 3 5 7 9 12 14 16 18 20 23 25 27 29 31 34 36 38 40 42 45 47 49 51 53
DETECTOR(0, 4, 1) rec[-15] rec[-20] rec[-42]
DETECTOR(0, 8, 1) rec[-5] rec[-10] rec[-32]
DETECTOR(2, 2, 1) rec[-19] rec[-20] rec[-24] rec[-25] rec[-47]
DETECTOR(2, 6, 1) rec[-9] rec[-10] rec[-14] rec[-15] rec[-37]
DETECTOR(4, 4, 1) rec[-13] rec[-14] rec[-18] rec[-19] rec[-40]
DETECTOR(4, 8, 1) rec[-3] rec[-4] rec[-8] rec[-9] rec[-30]
DETECTOR(6, 2, 1) rec[-17] rec[-18] rec[-22] rec[-23] rec[-45]
DETECTOR(6, 6, 1) rec[-7] rec[-8] rec[-12] rec[-13] rec[-35]
DETECTOR(8, 4, 1) rec[-11] rec[-12] rec[-16] rec[-17] rec[-38]
DETECTOR(8, 8, 1) rec[-1] rec[-2] rec[-6] rec[-7] rec[-28]
DETECTOR(10, 2, 1) rec[-16] rec[-21] rec[-43]
DETECTOR(10, 6, 1) rec[-6] rec[-11] rec[-33]
OBSERVABLE_INCLUDE(0) rec[-21] rec[-22] rec[-23] rec[-24] rec[-25]

View file

@ -0,0 +1,46 @@
//! Cross-tool fixtures for noisy Clifford circuits.
//!
//! `data/memory_z_d5.circuit` and `data/memory_x_d3.circuit` were written by
//! an independent circuit generator. `data/memory_x_d3.dem` is that tool's
//! detector error model of the second circuit, flattened, with identical
//! mechanisms XOR-combined. Matching them checks three things against an
//! implementation that shares no code with this one:
//! - the generator, byte for byte;
//! - the parser, including the X-basis resets and measurements our generator
//! never emits;
//! - the backward-sensitivity model, mechanism for mechanism.
#![cfg(feature = "quantum_frame")]
use std::collections::BTreeMap;
use wai_quantum::quantum_frame::{error_model, parse, surface_code_memory};
#[test]
fn the_generator_matches_the_reference_text() {
let want = include_str!("data/memory_z_d5.circuit");
assert_eq!(surface_code_memory(5, 4, 0.001), want);
}
/// `"D0 D4 L0"` → probability.
fn model_map(text: &str) -> BTreeMap<String, f64> {
text.lines()
.filter_map(|l| {
let rest = l.trim().strip_prefix("error(")?;
let (p, syms) = rest.split_once(')')?;
let mut s: Vec<&str> = syms.split_whitespace().collect();
s.sort_by_key(|t| (t.as_bytes()[0], t[1..].parse::<u32>().unwrap()));
Some((s.join(" "), p.parse().unwrap()))
})
.collect()
}
#[test]
fn the_model_matches_the_reference_model() {
let c = parse(include_str!("data/memory_x_d3.circuit")).unwrap();
let ours = model_map(&error_model(&c).unwrap().to_text());
let theirs = model_map(include_str!("data/memory_x_d3.dem"));
assert_eq!(ours.len(), theirs.len());
for (k, p) in &theirs {
let q = ours.get(k).unwrap_or_else(|| panic!("missing mechanism {k}"));
assert!((q / p - 1.0).abs() < 1e-9, "{k}: {q} vs {p}");
}
}

2
wai/wai-rs/Cargo.lock generated
View file

@ -3900,7 +3900,7 @@ dependencies = [
[[package]]
name = "wai-quantum"
version = "0.3.24"
version = "0.3.25"
dependencies = [
"blake3",
"ed25519-dalek",

View file

@ -348,6 +348,9 @@ quantum_bptn = ["quantum_spd", "wai-quantum/quantum_bptn"]
# and its signed receipts.
quantum_resource = ["dep:wai-quantum", "wai-quantum/quantum_resource"]
quantum_resource_receipt = ["quantum_resource", "wai-quantum/quantum_resource_receipt"]
# Noisy Clifford circuits at QEC scale: frame sampling, detector error models,
# exact matching, measured code models.
quantum_frame = ["dep:wai-quantum", "wai-quantum/quantum_frame"]
quantum_qir = ["quantum_toolchain", "wai-quantum/quantum_qir"]
# Variational quantum machine learning (extensions/quantum-sim § VML,
# `wai.quantum.vml`): the interference-based ML the thermodynamic-computing pivot
@ -476,6 +479,7 @@ required-features = [
"quantum_spd_receipt",
"quantum_bptn",
"quantum_resource_receipt",
"quantum_frame",
]
[[bin]]

View file

@ -47,6 +47,16 @@
//! wai-quantum estimate factoring --qubit ns-e4 --receipt | wai-quantum verify
//! ```
//!
//! Error correction itself, at circuit level: sample a noisy Clifford circuit,
//! derive its detector error model, decode it exactly, and measure the code
//! model that estimates otherwise take on trust:
//!
//! ```text
//! wai-quantum qec -d 5 --p 0.003 --shots 100000
//! wai-quantum qec --file circuit.txt
//! wai-quantum qec --fit # then: estimate --prefactor A --threshold P
//! ```
//!
//! The `channel` command prints the Holevo bound before and after, which can only
//! fall: noise never creates information. Two results there are easy to miss —
//! fully damping `|1⟩` yields a *pure* output that still carries nothing (both
@ -145,7 +155,12 @@ fn usage() -> ! {
wai-quantum estimate [factoring|chemistry] [--qubit NAME|all] [--code surface|surface-meas|floquet]\n \
[--budget E] [--slowdown N] [--frontier] [--json] [--receipt]\n \
wai-quantum estimate --qubits N [--ccz N] [--t N] [--rotations N] [--depth N] [--measurements N] ...\n \
Qubits: us-e3, us-e4, ns-e3, ns-e4, meas-e4, meas-e6.\n\n\
Qubits: us-e3, us-e4, ns-e3, ns-e4, meas-e4, meas-e6. A measured code: --prefactor A --threshold P.\n\n\
ERROR CORRECTION AT CIRCUIT LEVEL:\n \
wai-quantum qec [-d D] [--rounds R] [--p P] [--shots N] [--seed S] [--json]\n \
wai-quantum qec --file CIRCUIT [--shots N] (the ecosystem's circuit text)\n \
wai-quantum qec [--circuit | --dem] [-d D | --file CIRCUIT]\n \
wai-quantum qec --fit [--shots N] (measure a and p* of the code model)\n\n\
Channels: identity, bitflip, dephasing, depolarizing, damping.\n\
Run `wai-quantum list` for the algorithm catalog."
);
@ -560,6 +575,20 @@ fn main() {
"floquet" => QecModel::floquet(),
other => die(&format!("unknown code '{other}' (surface, surface-meas, floquet)")),
});
// A code model with given constants, e.g. as `qec --fit` measured them.
let a = opt(args, "--prefactor", "").map(|v| v.parse::<f64>().unwrap_or_else(|_| die("bad --prefactor")));
let t = opt(args, "--threshold", "").map(|v| v.parse::<f64>().unwrap_or_else(|_| die("bad --threshold")));
let code = if a.is_some() || t.is_some() {
let base = code.unwrap_or_else(QecModel::surface_gate);
Some(QecModel {
name: format!("{} (given)", base.name),
prefactor: a.unwrap_or(base.prefactor),
threshold: t.unwrap_or(base.threshold),
..base
})
} else {
code
};
let which = opt(args, "--qubit", "").unwrap_or_else(|| {
if flag(args, "--receipt") || flag(args, "--frontier") { "ns-e4".into() } else { "all".into() }
});
@ -631,13 +660,13 @@ fn main() {
let isa = qr::layout(&counts, budget);
println!("program {} tiles, {} logical steps, {} T states (budget {budget})", isa.tiles, isa.min_steps, isa.t_states);
println!(
"{:<12} {:<13} {:>3} {:>9} {:>8} {:>14} {}",
"{:<12} {:<22} {:>3} {:>9} {:>8} {:>14} {}",
"qubit", "code", "d", "factories", "share", "physical", "runtime"
);
for (q, e) in &estimates {
match e {
Ok(e) => println!(
"{:<12} {:<13} {:>3} {:>9} {:>7.1}% {:>14} {}",
"{:<12} {:<22} {:>3} {:>9} {:>7.1}% {:>14} {}",
e.qubit,
e.code,
e.distance,
@ -651,6 +680,85 @@ fn main() {
}
}
// Error correction at circuit level: sample a noisy Clifford circuit,
// derive its detector error model, decode it exactly.
"qec" => {
use wai::quantum_frame::{error_model, parse, sample, surface_code_memory};
use wai::quantum_match::{fit_code_model, memory_experiment, MatchingGraph};
let shots: usize = num(args, "--shots", "", 100_000);
let seed: u64 = num(args, "--seed", "", 1);
let json = flag(args, "--json");
if flag(args, "--fit") {
// The surface code's logical error per round on a grid, and
// the code model a resource estimate would otherwise assume.
let mut pts = Vec::new();
if !json {
println!("{:>3} {:>7} {:>9} {:>9} {:>11}", "d", "p", "shots", "failures", "per round");
}
for d in [3u32, 5, 7] {
for p in [0.002, 0.003, 0.004, 0.005] {
let n = if d == 7 { shots / 4 } else { shots };
let t = memory_experiment(d, d, p, n, seed).unwrap_or_else(|e| die(&format!("{e:?}")));
if !json {
println!("{:>3} {:>7} {:>9} {:>9} {:>11.3e}", d, p, t.shots, t.failures, t.per_round());
}
pts.push(t);
}
}
let f = fit_code_model(&pts).unwrap_or_else(|| die("too few failures to fit; raise --shots"));
if json {
println!("{{\"prefactor\":{},\"threshold\":{}}}", f.prefactor, f.threshold);
} else {
println!("fit P(d) = a·(p/p*)^((d+1)/2) per round, a = {:.4}, p* = {:.5}", f.prefactor, f.threshold);
println!("estimate wai-quantum estimate factoring --prefactor {:.4} --threshold {:.5}", f.prefactor, f.threshold);
}
return;
}
let d: u32 = num(args, "--distance", "-d", 3);
let rounds: u32 = num(args, "--rounds", "", d);
let p: f64 = num(args, "--p", "", 0.001);
let text = match opt(args, "--file", "") {
Some(path) => std::fs::read_to_string(&path).unwrap_or_else(|e| die(&format!("{path}: {e}"))),
None => {
if d < 2 || rounds < 1 || !(0.0..=0.5).contains(&p) {
die("need -d ≥ 2, --rounds ≥ 1 and 0 ≤ --p ≤ 0.5");
}
surface_code_memory(d, rounds, p)
}
};
if flag(args, "--circuit") {
print!("{text}");
return;
}
let c = parse(&text).unwrap_or_else(|e| die(&format!("circuit: {e}")));
let model = error_model(&c).unwrap_or_else(|e| die(&format!("model: {e}")));
if flag(args, "--dem") {
print!("{}", model.to_text());
return;
}
let g = MatchingGraph::from_model(&model).unwrap_or_else(|e| die(&format!("matching graph: {e:?}")));
let s = sample(&c, shots, seed);
let failures = g.failures(&s).unwrap_or_else(|e| die(&format!("decode: {e:?}")));
let rate = failures as f64 / shots as f64;
if json {
println!(
"{{\"detectors\":{},\"failures\":{failures},\"mechanisms\":{},\"observables\":{},\"qubits\":{},\"seed\":{seed},\"shots\":{shots}}}",
model.detectors,
model.mechanisms.len(),
model.observables,
c.qubits
);
} else {
println!("circuit {} qubits, {} detectors, {} observables", c.qubits, model.detectors, model.observables);
println!("model {} error mechanisms", model.mechanisms.len());
println!("failures {failures} of {shots} shots ({rate:.3e} per shot)");
if opt(args, "--file", "").is_none() {
let per_round = wai::quantum_match::MemoryPoint { distance: d, rounds, p, shots: shots as u64, failures }.per_round();
println!("per round {per_round:.3e} (d = {d}, {rounds} rounds, p = {p})");
}
}
}
"entropy" => {
let c = circuit_from(args, algo);
let sv = c.simulate().unwrap_or_else(|e| die(&format!("simulate: {e:?}")));

View file

@ -618,6 +618,12 @@ pub use wai_quantum::quantum_resource;
#[cfg(feature = "quantum_resource_receipt")]
pub use wai_quantum::quantum_resource_receipt;
#[cfg(feature = "quantum_frame")]
pub use wai_quantum::quantum_frame;
#[cfg(feature = "quantum_frame")]
pub use wai_quantum::quantum_match;
#[cfg(feature = "quantum_qir")]
pub use wai_quantum::quantum_qir;

View file

@ -734,7 +734,7 @@ dependencies = [
[[package]]
name = "wai-quantum"
version = "0.3.24"
version = "0.3.25"
dependencies = [
"blake3",
"ed25519-dalek",