Distance finding and fault-tolerance tooling: Rust engine, typed inputs, DEM fault distance, docs - #415
Distance finding and fault-tolerance tooling: Rust engine, typed inputs, DEM fault distance, docs#415ciaranra wants to merge 16 commits into
Conversation
…c matrix input for code specification
…agating multiple faults
|
Two fault-tolerance correctness fixes have been folded in (previously #441 and a follow-on), alongside the distance tooling. Multi-fault propagation
Faults are now injected at their own tick and before/after position. The duplicated Pauli-injection mapping that let the two functions drift is now a single shared helper. Single-leg fault enumeration
These two bugs were masking each other. Three tests described injecting a single data-qubit X, the iterator actually produced The DAG path was already correct ( What the enumeration fix surfacedThe omission erred toward false confidence: faults that are never enumerated cannot be found to break a circuit.
Conversely No test containing an actual fault-tolerance assertion fails. Reported counts move widely, as expected when more faults are tested — for example Two follow-ups worth separate attention, deliberately not changed here:
Verification
|
|
Hook-error diagnosis added ( What it reports
A hook error is defined as a fault whose OWN Pauli weight is exactly 1 but whose propagated support on the data qubits has weight at least
This is why circuit fault distance falls below code distance, so the point of the diagnostic is attribution: not "your fault distance is 3 rather than 5", but which gate makes it so. Design notesIt is a readout over existing machinery, reusing
This diagnosis is only meaningful because of the single-leg enumeration fix in this PR: an ancilla-only fault on a CX was previously unreachable, so the analysis would have found nothing and looked correct doing it. Rust-only for now. Verification
|
|
Circuit fault distance and DEM search pruning added. Circuit fault distance as a number, per logical
Added The existing single-weight methods are untouched — they are shipped API with dependents. Mutation-verified: collapsing the weight loop to a single weight turns the discrimination result from DEM cross-validation was investigated and deliberately NOT added: the two fault models are not directly comparable. Connected-cluster pruning for the DEM search
Added
Measured on a 594-mechanism DEM with non-peelable cycle padding, so the numbers isolate the connectivity gain rather than peeling collapsing the input:
About 38x, same answer. At weight 4 the pruned search completes in 0.8 ms; the blind search would have to consider 5.13e9 candidate subsets. A bug this work exposed in the existing codeThe extended property test caught an inconsistency introduced earlier in this PR. Only the randomised generator surfaced this: it needs a DEM with a hyperedge AND a weight-1 detector-free mechanism together, which was case 27 of 512 and which no hand-written fixture had produced. TestsThe seeded property test now generates hyperedge DEMs as well as graphlike ones, and asserts the blind and pruned searches agree on distance and solution existence for every case, with the graphlike method additionally agreeing where applicable. Plus fixtures for peeling reaching a fixpoint without changing the distance, and peeling preserving a witness whose detectors are all shared. Mutation-verified: making peeling over-prune (deactivating a mechanism whose detectors are all shared) fails four tests including the property test and both distance-3 witness fixtures. Verification
|
|
Flag fault-tolerance verification added ( What it checks
The At Scope limitation, stated rather than impliedThe paper's definition also requires that a fault-free run does not flag. That is not checked here, and cannot be: The verdict field is therefore named TestsSix tests, built around a discriminating pair rather than a single circuit: the standard single-flag weight-4 stabilizer measurement satisfies the condition at Also: the stabilizer-equivalence case above, restriction of weights to the caller-supplied data qubits, determinism, and a negative Mutation-verified:
Verification
Python exposure is the outstanding follow-up; |
Distance-finding and fault-tolerance work, consolidated into one PR (previously split as #439 and #440).
1. Expose the Rust distance search and verification workflow
The incremental-weight distance search in
pecos-qecand theStabilizerCodeSpecverification machinery were fully implemented in Rust but had no Python bindings and no callers. This exposes them as a supported replacement for the legacypecos.analysis.VerifyStabilizersdevelopment loop.StabilizerCodeSpecwith a builder:check,logical_z,logical_x, and three build modes —build(),build_verified()(errors name the exact anticommuting generator pair), andbuild_with_discovered_logicals()(derives paired logicals and destabilizers by stabilizer simulation).distance(max_weight=None, css=False, verbose=False)returningDistanceResult;min_weight_logicals();shortest_logicals(delta)for logicals withindeltaof the minimum. The search grows error weight from 1, so cost scales with the distance rather than the qubit count, reaching codes the coset enumeration inStabilizerCode.distance()(capped at k + rank <= 30) cannot.Xs,Ys,Zsmulti-qubit Pauli helpers, so checks read asZs([0, 1]) * Y(2).ParityCheckMatrix(pecos-qec, role-neutral) andSymplecticMatrix(pecos-quantum,[X block | Z block]), with CSS orthogonality validated (Hx * Hz^T = 0, errors naming the offending row pair) and width-bearingzerosconstructors for single-stabilizer-type codes. Phase-dropping conversions are named honestly (to_positive_paulis,from_pauli_sequence_ignoring_phase) because symplectic form carries no sign.StabilizerCodeSpecconstructors now reject linearly dependent stabilizers (DependentStabilizers { rank, count }). Previouslynum_logical_qubits()returnedn - stabilizers.len()while documenting "independent generators", so redundant generators silently corrupted k — which matrix input makes easy to hit.pecos/tools/fault_tolerance_checks.pyandpecos/tools/stabilizer_verification.py, byte-identical dead copies unreachable through the public path (pecos.toolsis a deprecation shim re-exportingpecos.analysis).pecos.analysis.VerifyStabilizersitself is untouched; retiring it is a follow-up now that every capability has a Rust-backed home.2. Consolidate the duplicate searches and parallelize
Two independent implementations of the same weight-increasing search existed. Their predicates were verified equivalent — both test "commutes with every stabilizer generator AND anticommutes with at least one configured logical" — so
StabilizerFlipChecker::{has_undetectable_logical, compute_distance}now delegate to the shared engine via a newhas_logical_error_at_weight. The checker's existing tests are unchanged and act as the regression guard. Thecombinations/pauli_product/build_pauli_stringhelpers are deliberately retained:analyze_weightneeds configurable X/Y/Z subsets the shared iterator cannot express.The per-weight candidate scan now runs on rayon, partitioned over support combinations. Output is bit-identical to serial rather than merely equivalent — reduction is on enumeration index, so the same operator and the same vector order come back, and tests cover both the serial and parallel branch.
PARALLEL_CANDIDATE_THRESHOLD = 65_536candidates at one weight, derived from a measured sweep, not intuition. Below roughly 22k candidates parallelism loses (forcing the toric [[18, 2, 3]] weight-3 tier, 22,032 candidates, parallel made that search 4.6x slower); above roughly 193k it stops engaging where the time is spent (the color [[17, 1, 5]] search is dominated by its weight-4 tier, 192,780 candidates, and a higher gate erased the speedup entirely).shortest_logicalsdelta=1, color [[17,1,5]]This is a trade, not a free win: microsecond-scale searches pay about 10%, while searches long enough to wait on improve 5-13x. Small-code figures were confirmed by an A/B/A run after an initial measurement proved to be machine drift. Adds
benches/modules/code_distance.rs; no distance benchmark existed before.3. Detector-error-model fault distance
Code distance is not circuit distance. A distance-5 code whose syndrome extraction spreads one fault across multiple data qubits can have fault distance 3, so code distance alone can overstate real protection. Nothing in PECOS computed the circuit-level number —
check_undetectable_logical_errorsenumerates failing configurations but never reports a minimum.This adds the DEM level: the minimum number of fault mechanisms whose XOR flips no detector but flips at least one observable. With
Hthe detector-by-mechanism matrix andLthe observable-by-mechanism matrix, that is minimum|e|withH*e = 0andL*e != 0— structurally the same problem as code distance, which is why it lands beside the existing fault-tolerance checkers rather than in a separate silo.graphlike_fault_distanceis exact when every mechanism flips at most two detectors, searching the parity-doubled graph with every detector AND the boundary as a BFS root. Rooting only at the boundary is not exact: a DEM whose minimum cycle avoids the boundary (D0 D1 L0/D1 D2/D0 D2, distance 3) leaves the boundary isolated and finds nothing. That is now a regression test.exhaustive_fault_distance(max_weight)is correct for any DEM including hyperedges;max_weightis required because the cost is combinatorial in the mechanism count.DemMatchingGraphsilently skips hyperedges, so building on it would have returned quietly wrong distances. The implementation readsto_mechanisms()directly and reusesFaultMechanism::{xor, is_graphlike, is_hyperedge}.DistanceResult::min_weight_operator— knowing which faults conspire is the point.Guarded by a seeded property test over 512 random small graphlike DEMs asserting both methods agree on distance and on solution existence. Fixture tests alone shared a blind spot with the original design (every case happened to have a boundary edge); cross-validating an exact special case against a general reference catches the class rather than one instance. Verified by mutation: restricting the roots to boundary-only fails both the boundary-free regression and the property test.
4. Documentation
New
docs/user-guide/stabilizer-code-verification.md, replacing the legacystab_code_verification.rstnarrative on the current API: the builder workflow, the ten-qubit design storyline (anticommuting pair diagnosed, then [[10, 3]] at distance 2, then [[10, 1]] at distance 3), logical-operator exploration, matrix input with its error diagnostics, and a note on choosing between the two distance methods. Ten executable doc tests, no skip markers, wired into the mkdocs nav.Verification
Run on the combined branch:
cargo test -p pecos-qec -p pecos-quantum: no failuresuv run --frozen pytestacross the stabilizer-code binding suites, the fault-distance suite, and the generated doc tests: 40 passedjust build-debugandjust lint(new files staged first, since pre-commit only inspects tracked files): clean