Skip to content

Add Quantinuum WASM adapter for Frontier decoder - #680

Open
natc-brown wants to merge 2 commits into
PECOS-packages:devfrom
natc-brown:codex/frontier-quantinuum-wasm
Open

Add Quantinuum WASM adapter for Frontier decoder#680
natc-brown wants to merge 2 commits into
PECOS-packages:devfrom
natc-brown:codex/frontier-quantinuum-wasm

Conversation

@natc-brown

@natc-brown natc-brown commented Sep 2, 2026

Copy link
Copy Markdown

Summary

  • add a bare-WebAssembly pecos-frontier-wasm cdylib with zero imports and an i32-only ABI compatible with Quantinuum hardware
  • embed a flattened Stim detector error model at build time and support up to 128 detector and observable bits
  • provide asynchronous decode, result/status getters, and per-shot reset functions
  • avoid unsupported clock calls specifically on wasm32-unknown-unknown while preserving timing on native and WASI targets
  • add one cross-platform Python builder and a just build-frontier-wasm [dem] recipe

Exported ABI

  • init() -> ()
  • frontier_decode(i32, i32, i32, i32) -> ()
  • frontier_result_0..3() -> i32
  • frontier_status() -> i32
  • frontier_reset() -> ()

Validation

  • cargo test -p pecos-trellis -p pecos-frontier -p pecos-frontier-wasm (84 passed, 1 ignored)
  • cargo clippy -p pecos-trellis -p pecos-frontier -p pecos-frontier-wasm --all-targets -- -D warnings
  • RUSTFLAGS='--cfg getrandom_backend="unsupported"' cargo clippy --release --target wasm32-unknown-unknown -p pecos-frontier-wasm -- -D warnings
  • uv run --frozen pre-commit run --all-files
  • scripts/dependency-integrity-check.sh
  • just build-frontier-wasm and the direct Python builder with an explicit DEM
  • verified the builder with an overridden CARGO_TARGET_DIR
  • instantiated the release module in a standalone WebAssembly runtime and exercised init/decode/result/reset
  • confirmed the release module has zero imports and is 251,759 bytes

Real-model build/decode latency still needs measurement against the hardware timeout once a production DEM is selected.

@natc-brown
natc-brown requested a review from ciaranra as a code owner September 2, 2026 22:46
@ciaranra

ciaranra commented Sep 2, 2026

Copy link
Copy Markdown
Member

Reviewed the full diff, checked the branch out in a worktree, ran the stated verification, built the wasm, and drove it through wasmtime. The adapter works and the ABI matches Quantinuum's published constraints. One blocker and a few fixes before merge.

Verified

  • cargo test -p pecos-trellis -p pecos-frontier -p pecos-frontier-wasm: 82 pass.
  • Native cargo clippy --all-targets -- -D warnings on the three crates: clean.
  • scripts/build-frontier-wasm.sh: 234 KB module, zero imports, exports memory, init, frontier_decode, frontier_result_0..3, frontier_status, frontier_reset.
  • wasmtime probe on the embedded toy DEM: init -> status 0; D0 -> L0; D1 -> no flip; D0+D1 -> L0 (the joint 0.05 mechanism outweighs two singles, which is correct); frontier_reset clears results and decode works again afterwards.
  • Quantinuum's QEC decoder toolkit docs confirm the ABI shape: functions are void or return exactly one i32 with i32-only args, init() with no params/results is required, in-memory state needs a per-shot reset call, and the async pattern is void calls during the job with i32 reads only when a correction is needed. The exported surface matches all of that.

Blocker

The repo's dependency integrity check pre-commit hook fails on this branch:

==> Rust unsafe boundary allowlist
exp/pecos-frontier-wasm
ERROR: new unsafe/FFI crate roots must be added to scripts/ci/unsafe-allowlist.txt

The crate uses #[unsafe(no_mangle)], so exp/pecos-frontier-wasm needs an entry in scripts/ci/unsafe-allowlist.txt. The pre-commit and dependency-integrity-check CI jobs will fail until it is added. Reproduce locally with:

uv run --frozen pre-commit run --all-files

Should fix

  1. The stated wasm clippy command does not pass on its own. getrandom 0.4 (pulled in via pecos-random -> pecos-decoder-core) refuses wasm32-unknown-unknown without an explicit backend, so cargo clippy --release --target wasm32-unknown-unknown -p pecos-frontier-wasm -- -D warnings only succeeds with RUSTFLAGS='--cfg getrandom_backend="unsupported"' in the environment. The build scripts set it, the PR description's command does not. The runtime side is fine (nothing on the Frontier decode path calls the RNG), but the verification claim should state the flag.

  2. Missing license headers. exp/pecos-frontier-wasm/src/lib.rs and build.rs lack the PECOS Apache header that exp/pecos-frontier and exp/pecos-trellis carry.

  3. Build scripts hardcode the target directory. Both scripts copy from $workspace/target/wasm32-unknown-unknown/release/..., which is wrong whenever CARGO_TARGET_DIR is set. Separately, two parallel scripts (.ps1 and .sh) for one build step is against repo convention; a single just build-frontier-wasm recipe in the Justfile covers both platforms and is where dev recipes live.

  4. Test coverage. There is one test, and it cannot reach the error paths because State::initialize reads the embedded constant. Suggest initialize(&mut self, dem: &str) so tests can feed a DEM wider than 128 bits (STATUS_MODEL_TOO_WIDE) and a malformed DEM (STATUS_MODEL_ERROR). Also worth a test with a detector and an observable at index >= 32 to pin the word/bit packing, since the single test only exercises bit 0 of word 0.

  5. Unreachable guard. The observable >= MAX_BITS check inside State::decode cannot fire: initialize already rejects models with more than 128 observables, and the predicted mask cannot contain bits beyond the model width. Drop it.

  6. Timing stub done two ways. pecos-trellis gets timer_start/timer_seconds helpers while pecos-frontier uses inline #[cfg] blocks with a literal 0.0. Pick one shape for both. The guard is also broader than needed: wasm32-wasip1 has a working Instant, so all(target_arch = "wasm32", target_os = "unknown") is the precise condition.

Notes, not blocking

  • Quantinuum's docs say a Wasm call must complete within roughly 250 ms or the system can enter maintenance mode. The toy DEM decodes instantly; Frontier build and decode time on a realistic embedded DEM has not been measured against that cap. Module size is well within the 1.3 MB guidance / 6 MB hard limit.
  • The 128-detector / 128-observable cap follows from four i32 arguments. That is enough for d=3 with many rounds or d=5 with about five rounds. Fine for an exp/ crate, but the limit belongs in the crate docs alongside the packing rule.
  • rlib in crate-type is not needed; cargo builds the unit tests from source regardless. Harmless either way.

@ciaranra

ciaranra commented Sep 2, 2026

Copy link
Copy Markdown
Member

One more request on framing, separate from the items above.

The module itself is not runtime-specific. It has zero imports, targets wasm32-unknown-unknown, and needs no clock or threads; I loaded and exercised it in wasmtime, not WAVM. Nothing in the code depends on WAVM, and WAVM is not mentioned anywhere else in the repo. The only things that make this crate read as a Quantinuum/WAVM artifact are prose: the Cargo.toml description, the README title and two section headings, and the module doc in lib.rs.

What is Quantinuum-shaped is the ABI: i32-only arguments, at most one i32 return, an init() with no parameters, four separate i32 syndrome words instead of a pointer into linear memory, and a per-shot reset call. Generic WebAssembly allows i64, multi-value returns, and memory pointers. Those choices exist because Quantinuum's runtime is the most constrained target, and that is the one fact a reader needs in order to understand the 128-bit cap and the four-argument shape.

Suggested framing:

  • Describe the crate as a bare-WebAssembly adapter with no imports and i32-only exports, and state those constraints as the crate's own contract (README title, lib.rs module doc, Cargo.toml description).
  • Keep a single sentence explaining why: the ABI is the lowest common denominator so the same module runs on Quantinuum hardware, which accepts only i32 arguments and single i32 returns.
  • Drop "WAVM" entirely. It names the runtime implementation, not the contract, and the repo avoids naming third-party runtimes.
  • Keep init as the export name and keep frontier_reset. Both are harmless on any runtime and required on the target that motivated the crate.

@ciaranra

ciaranra commented Sep 2, 2026

Copy link
Copy Markdown
Member

On the build scripts specifically: please replace the PowerShell + shell pair with one script that runs the same way on Linux, macOS, and Windows.

Two copies of the same logic in two languages drift, and the repo already avoids this. scripts/ currently holds sixteen Python scripts, eight shell scripts, and exactly one .ps1, which is the Windows MSVC bootstrap and has no portable alternative. Every other cross-platform task is a Python script run through uv, or a Justfile recipe (the Justfile runs bash on all platforms, including Git Bash on Windows).

Suggested shape:

  • One scripts/build_frontier_wasm.py invoked as uv run --frozen python scripts/build_frontier_wasm.py [flattened-model.dem]. It sets FRONTIER_DEM_PATH and the getrandom_backend="unsupported" cfg in the child environment, runs cargo build --release --target wasm32-unknown-unknown -p pecos-frontier-wasm, reads target_directory from cargo metadata instead of hardcoding target/ (this also fixes the CARGO_TARGET_DIR bug noted above), and copies the module to dist/.
  • A just build-frontier-wasm [dem] recipe that calls it, so it sits next to the other build recipes.

If the wasm build is expected to become a supported deliverable rather than an experiment, the other option is a subcommand in pecos-cli (crates/pecos-cli/src/cli/rust_cmd.rs already wraps cargo invocations), which needs no shell or interpreter at all. Either is fine; the Python script is the smaller change.

@natc-brown
natc-brown force-pushed the codex/frontier-quantinuum-wasm branch from d8d4f14 to c960e16 Compare September 3, 2026 14:43
@natc-brown

natc-brown commented Sep 3, 2026

Copy link
Copy Markdown
Author

Addressed the review in e590a72 and rebased onto current dev:

  • added exp/pecos-frontier-wasm to the unsafe/FFI allowlist
  • corrected the Wasm Clippy command to include the unsupported getrandom backend
  • added PECOS Apache headers
  • replaced the PowerShell/shell pair with one cross-platform Python builder plus a Just recipe
  • read Cargo's configured target directory and verified an overridden CARGO_TARGET_DIR
  • added malformed-model, detector/observable width, and cross-word packing tests
  • removed the unreachable decode guard and unnecessary rlib
  • made both timing stubs use the same helper shape and limited them to wasm32-unknown-unknown
  • reframed the crate as generic bare WebAssembly while explaining the Quantinuum-compatible ABI

Validation now passes: 84 tests (1 ignored), native and Wasm-target Clippy with warnings denied, full pre-commit, dependency integrity, both builder entry paths, zero-import inspection, and runtime init/decode/result/reset.

@natc-brown
natc-brown force-pushed the codex/frontier-quantinuum-wasm branch from c960e16 to e590a72 Compare September 3, 2026 14:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants