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
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]>
214 lines
7 KiB
Rust
214 lines
7 KiB
Rust
//! 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
|
||
}
|
||
}
|