open-standards/wai/wai-web/src/quantum_qec.rs
Transaction Science 35c11fb6cf
All checks were successful
WAI conformance / cargo-class corpora · linux (push) Successful in 33s
WAI conformance / cross-arch byte-equality · linux (push) Successful in 14s
WAI conformance / cargo-class corpora · linux-arm64 (push) Successful in 1m11s
WAI conformance / cargo-class corpora · windows (push) Successful in 1m36s
WAI conformance / cross-arch byte-equality · linux-arm64 (push) Successful in 45s
WAI conformance / cross-arch byte-equality · windows (push) Successful in 29s
WAI conformance / cargo-class corpora · macos (push) Successful in 8m57s
WAI conformance / cross-arch byte-equality · macos (push) Successful in 2m41s
wai: qLDPC decoding — wai.quantum.decode
A deterministic fixed-point Relay-BP (normalized min-sum) decoder for quantum
LDPC codes, each decode sealing a signed, joule-metered receipt carrying
joules-per-decode — the metric no decoder reports and none signs.

- quantum_qec.rs: GF(2) RREF + row-space membership (logical-triviality test);
  CSS code builders — toric + bivariate-bicycle [[72,12]] "gross" (verified
  n/k + CSS orthogonality); normalized min-sum BP in pure i64 (scale-invariant,
  no float/tanh); Relay-BP — disordered signed per-node memory + warm-start,
  which breaks the degeneracy that traps plain BP on quantum codes; Monte-Carlo
  benchmark (relay vs plain over identical samples).
- quantum_ops.rs: DecodeReceipt binds (check-matrix hash, syndrome hash, decoder
  params) -> (correction hash, converged, work, joules); merkle + Ed25519, joule
  ceiling in verify(). logical_success is NOT a field — a real decoder can't know
  it (needs the true error); the receipt attests what it does know.
- wai-web: ToricDemo / GrossDemo / BenchDemo wasm bindings.

Honest boundary: simulated syndromes (no QPU); browser joules modeled from the
exact message-pass count (a real decoder fills it from a hardware meter). 56
quantum tests green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-08-26 00:42:03 -04:00

214 lines
7 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.

//! qLDPC DECODING in the browser (`wai.quantum.decode`). The SAME deterministic
//! fixed-point Relay-BP (normalized min-sum) decoder a native sink runs, compiled
//! to wasm32: sample a syndrome, decode it byte-exactly, watch relay legs recover
//! decodes that plain BP leaves failed on a real quantum LDPC code, and seal a
//! signed, joule-metered `wai.quantum.decode` receipt carrying joules-per-decode —
//! the metric no decoder reports and none signs.
use ed25519_dalek::SigningKey;
use wai::quantum_ops::{DecodeReceipt, GrantRef};
use wai::quantum_qec::{benchmark, decode_once, CssCode, DecodeConfig, DecodeResult};
use wasm_bindgen::prelude::*;
fn signer() -> SigningKey {
SigningKey::from_bytes(&[0x51; 32])
}
fn grant() -> GrantRef {
GrantRef::unbounded("quantum.decode")
}
// Modeled energy per message-pass (µJ). The message count is the exact,
// byte-portable work; a real decoder fills the receipt's joules from a hardware
// meter. Declared constant → modeled joules = messages · this.
const UJ_PER_MSG: u64 = 1;
fn seal(r: &DecodeResult) -> DecodeReceipt {
r.seal(&signer(), "did:key:demo-lab", r.messages * UJ_PER_MSG, grant())
}
// Small accessor shim: DecodeResult fields are public except a few; expose what
// the demo needs via a trait-free helper module is unnecessary — the fields used
// below (error, correction, syndrome, converged, logical_success, legs, iters,
// messages, code_id, n) are all public on DecodeResult.
// ===========================================================================
// 1. Toric code — decode one error, with lattice geometry for the viz
// ===========================================================================
#[wasm_bindgen]
pub struct ToricDemo {
l: u32,
res: DecodeResult,
}
#[wasm_bindgen]
impl ToricDemo {
/// Decode one bit-flip error on the `L×L` toric code at physical rate `p`.
#[wasm_bindgen(constructor)]
pub fn new(l: u32, p: f64, seed: f64) -> ToricDemo {
let code = CssCode::toric(l as usize);
let cfg = DecodeConfig::from_p(p);
ToricDemo { l, res: decode_once(&code, &cfg, seed as u64) }
}
pub fn l(&self) -> u32 {
self.l
}
pub fn n(&self) -> u32 {
self.res.n as u32
}
/// True-error qubit ids (edges). Convention: id < L² is a horizontal edge at
/// (id/L, id%L); else a vertical edge at ((id−L²)/L, (id−L²)%L).
pub fn error_qubits(&self) -> Vec<u32> {
self.res.error.clone()
}
pub fn correction_qubits(&self) -> Vec<u32> {
self.res.correction.clone()
}
/// Unsatisfied plaquette (Z-check) indices; plaquette c is at (c/L, c%L).
pub fn syndrome_checks(&self) -> Vec<u32> {
self.res.syndrome.clone()
}
pub fn converged(&self) -> bool {
self.res.converged
}
pub fn logical_success(&self) -> bool {
self.res.logical_success
}
pub fn legs(&self) -> u32 {
self.res.legs
}
pub fn iters(&self) -> u32 {
self.res.iters
}
pub fn messages(&self) -> u32 {
self.res.messages as u32
}
pub fn error_weight(&self) -> u32 {
self.res.error.len() as u32
}
pub fn receipt_json(&self) -> String {
seal(&self.res).to_json()
}
pub fn receipt_verifies(&self) -> bool {
seal(&self.res).verify()
}
}
// ===========================================================================
// 2. Bivariate-bicycle "gross" qLDPC code — decode dashboard
// ===========================================================================
#[wasm_bindgen]
pub struct GrossDemo {
res: DecodeResult,
k: u32,
}
#[wasm_bindgen]
impl GrossDemo {
/// Decode one error on the [[72,12]] bivariate-bicycle code at rate `p`.
#[wasm_bindgen(constructor)]
pub fn new(p: f64, seed: f64) -> GrossDemo {
let code = CssCode::gross();
let cfg = DecodeConfig::from_p(p);
GrossDemo { k: code.k as u32, res: decode_once(&code, &cfg, seed as u64) }
}
pub fn n(&self) -> u32 {
self.res.n as u32
}
pub fn k(&self) -> u32 {
self.k
}
pub fn code_id(&self) -> String {
self.res.code_id.clone()
}
/// Per-qubit flags for the strip viz: 0 clean, 1 errored, 2 corrected, 3 both.
pub fn qubit_flags(&self) -> Vec<u8> {
let mut f = vec![0u8; self.res.n];
for &q in &self.res.error {
f[q as usize] |= 1;
}
for &q in &self.res.correction {
f[q as usize] |= 2;
}
f
}
pub fn error_weight(&self) -> u32 {
self.res.error.len() as u32
}
pub fn syndrome_weight(&self) -> u32 {
self.res.syndrome.len() as u32
}
pub fn correction_weight(&self) -> u32 {
self.res.correction.len() as u32
}
pub fn converged(&self) -> bool {
self.res.converged
}
pub fn logical_success(&self) -> bool {
self.res.logical_success
}
pub fn legs(&self) -> u32 {
self.res.legs
}
pub fn iters(&self) -> u32 {
self.res.iters
}
pub fn messages(&self) -> u32 {
self.res.messages as u32
}
/// Modeled joules-per-decode (µJ) = messages · declared per-message energy.
pub fn joules_micro(&self) -> u32 {
(self.res.messages * UJ_PER_MSG) as u32
}
pub fn receipt_json(&self) -> String {
seal(&self.res).to_json()
}
pub fn receipt_verifies(&self) -> bool {
seal(&self.res).verify()
}
}
// ===========================================================================
// 3. Relay-BP vs plain BP — Monte-Carlo sweep over the error rate
// ===========================================================================
#[wasm_bindgen]
pub struct BenchDemo {
ps: Vec<f64>,
plain_rate: Vec<f64>,
relay_rate: Vec<f64>,
trials: u32,
}
#[wasm_bindgen]
impl BenchDemo {
/// Sweep the physical error rate on the [[72,12]] gross code, decoding each
/// sampled error with plain BP and with Relay-BP over identical samples, and
/// report the logical error rate of each — the demonstration that relay is
/// what makes BP work on a quantum LDPC code.
#[wasm_bindgen(constructor)]
pub fn new(trials: u32, seed: f64, max_legs: u32) -> BenchDemo {
let code = CssCode::gross();
let ps = vec![0.02, 0.03, 0.04, 0.05, 0.06, 0.07];
let mut plain_rate = Vec::new();
let mut relay_rate = Vec::new();
for (i, &p) in ps.iter().enumerate() {
let b = benchmark(&code, p, trials, seed as u64 + i as u64 * 1009, max_legs);
plain_rate.push(b.plain_logical_failures as f64 / trials as f64);
relay_rate.push(b.relay_logical_failures as f64 / trials as f64);
}
BenchDemo { ps, plain_rate, relay_rate, trials }
}
pub fn ps(&self) -> Vec<f64> {
self.ps.clone()
}
pub fn plain_rate(&self) -> Vec<f64> {
self.plain_rate.clone()
}
pub fn relay_rate(&self) -> Vec<f64> {
self.relay_rate.clone()
}
pub fn trials(&self) -> u32 {
self.trials
}
}