Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ pecos-eeg = { version = "0.2.0-dev.0", path = "exp/pecos-eeg" }
pecos-stab-tn = { version = "0.2.0-dev.0", path = "exp/pecos-stab-tn" }
pecos-experimental = { version = "0.2.0-dev.0", path = "exp/pecos-experimental" }
pecos-foreign = { version = "0.2.0-dev.0", path = "crates/pecos-foreign" }
pecos-frontier = { version = "0.2.0-dev.0", path = "exp/pecos-frontier" }
pecos-fusion-blossom = { version = "0.2.0-dev.0", path = "crates/pecos-fusion-blossom" }
pecos-gpu-sims = { version = "0.2.0-dev.0", path = "crates/pecos-gpu-sims" }
pecos-hugr = { version = "0.2.0-dev.0", path = "crates/pecos-hugr" }
Expand Down
28 changes: 28 additions & 0 deletions exp/pecos-frontier/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
[package]
name = "pecos-frontier"
version.workspace = true
edition.workspace = true
readme = "README.md"
authors.workspace = true
homepage.workspace = true
repository.workspace = true
license.workspace = true
keywords.workspace = true
categories.workspace = true
description = "Frontier approximate logical maximum-likelihood decoder for PECOS"
publish = false

[dependencies]
pecos-decoder-core.workspace = true

[lib]
name = "pecos_frontier"

[dev-dependencies]
rand.workspace = true
rand_xoshiro.workspace = true
serde.workspace = true
serde_json.workspace = true

[lints]
workspace = true
18 changes: 18 additions & 0 deletions exp/pecos-frontier/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# PECOS Frontier Decoder

Native Rust implementation of the Frontier approximate logical maximum-likelihood
decoder (Leverrier & Urbanke, arXiv:2606.20513). Not a wrap of the upstream
`frontier` package; the upstream implementation is used as a verification oracle.

**Experimental** (`exp/`): the algorithm core is enumeration- and upstream-verified
(per-shot parity on matched models), but the crate has not yet accumulated real-user
mileage. Graduation to `crates/` and registration in the `pecos-decoders` meta-crate
are planned once it has been exercised more broadly (larger code families, Python
bindings, human users).

Pruning ranks accumulated prefix log mass plus a `score_alpha`-weighted
suffix-compatibility estimate. Unpruned results are exact and upstream-verified.

Deterministic ordering and tie-breaking are bitwise reproducible for a fixed
build and platform. The platform's `ln` and `exp` implementations may differ
across platforms.
106 changes: 106 additions & 0 deletions exp/pecos-frontier/examples/bridge_ab.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright 2026 The PECOS Developers
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
// in compliance with the License. You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the License
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
// or implied. See the License for the specific language governing permissions and limitations under
// the License.

//! Cross-implementation A/B harness: decode upstream-frontier sample shots
//! with `FrontierDecoder` on the identical model and column order.
//!
//! Input JSON (produced by an external extraction script from the upstream
//! `frontier` package): `{num_detectors, num_observables, mechanisms:
//! [[p, [detectors], [observables]], ...], shots: [{syndrome, truth_logical}]}`
//! where mechanism order IS the processing order and `syndrome` packs detector
//! `i` into bit `i`.
//!
//! Usage: `bridge_ab <model.json> <k> <delta> <score_alpha>`
//! Prints one `shot,predicted,truth,status` line per shot plus a summary line.

use pecos_decoder_core::dem::SparseDem;
use pecos_frontier::{FrontierConfig, FrontierDecoder};
use serde::Deserialize;
use std::collections::BTreeMap;

#[derive(Deserialize)]
struct BridgeModel {
num_detectors: usize,
num_observables: usize,
mechanisms: Vec<(f64, Vec<u32>, Vec<u32>)>,
shots: Vec<Shot>,
}

#[derive(Deserialize)]
struct Shot {
syndrome: u128,
truth_logical: u128,
}

fn main() {
let mut args = std::env::args().skip(1);
let path = args
.next()
.expect("usage: bridge_ab <model.json> <k> <delta> <score_alpha>");
let k: usize = args.next().expect("missing k").parse().expect("k");
let delta: f64 = args.next().expect("missing delta").parse().expect("delta");
let score_alpha: f64 = args
.next()
.expect("missing score_alpha")
.parse()
.expect("score_alpha");

let model: BridgeModel =
serde_json::from_str(&std::fs::read_to_string(&path).expect("read model json"))
.expect("parse model json");
let dem = SparseDem {
mechanisms: model.mechanisms,
detector_coords: BTreeMap::new(),
num_detectors: model.num_detectors,
num_observables: model.num_observables,
};
let config = FrontierConfig {
k,
delta,
score_alpha,
column_order: None,
};
let mut decoder = FrontierDecoder::from_sparse_dem(&dem, config).expect("build decoder");

let mut failures = 0_u32;
let mut no_path = 0_u32;
let started = std::time::Instant::now();
for (shot, entry) in model.shots.iter().enumerate() {
let syndrome: Vec<u8> = (0..model.num_detectors)
.map(|bit| u8::from(entry.syndrome & (1_u128 << bit) != 0))
.collect();
if let Ok(result) = decoder.decode(&syndrome) {
let words = result.predicted.words();
assert!(words.iter().skip(2).all(|&w| w == 0), "label fits u128");
let predicted = u128::from(words.first().copied().unwrap_or(0))
| (u128::from(words.get(1).copied().unwrap_or(0)) << 64);
let status = if predicted == entry.truth_logical {
"ok"
} else {
failures += 1;
"logical_fail"
};
println!("{shot},{predicted},{},{status}", entry.truth_logical);
} else {
failures += 1;
no_path += 1;
println!("{shot},,{},no_path", entry.truth_logical);
}
}
let elapsed = started.elapsed().as_secs_f64();
let trials = u32::try_from(model.shots.len()).expect("shot count fits u32");
println!(
"SUMMARY trials={trials} fail={failures} no_path={no_path} fer={} k={k} delta={delta} alpha={score_alpha} decode_s_mean={}",
f64::from(failures) / f64::from(trials),
elapsed / f64::from(trials),
);
}
Loading
Loading