Fix multi-fault propagation: inject each fault at its own tick and before/after position - #441
Closed
ciaranra wants to merge 1 commit into
Closed
Fix multi-fault propagation: inject each fault at its own tick and before/after position#441ciaranra wants to merge 1 commit into
ciaranra wants to merge 1 commit into
Conversation
…agating multiple faults
Member
Author
|
Folded into #415 — the commit is now on |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Correctness fix in
pecos-qecfault-tolerance analysis. Independent of #415.The bug
propagate_faultsXORed every fault into the initialPauliPropand then propagated frommin_tickonward. Two consequences:min_tick, so gates between those ticks acted on a Pauli that should not have existed yet. Gate order matters, so any weight >= 2 result was untrustworthy.propagate_faulteven at weight 1.propagate_faulthonoursbefore(forbefore=falseit skips ticks <=fault_tick);propagate_faultsignoredbeforeentirely and applied gates atmin_tickinclusive. A singlebefore=falsefault whose own tick contains a gate on its qubit therefore produced different answers from the two functions.The doc comment ("Faults are combined (XORed) and then propagated") described the implementation rather than justifying it, and
PauliFaultIteratorfeeds this function location combinations spanning arbitrary ticks.The fix
Walk every tick in order, injecting each fault at its own tick and
beforeposition: before-faults, then the tick's gates, then after-faults. This reduces exactly topropagate_faultfor a single fault in bothbeforecases, which is the property the new tests pin.Walking ticks earlier than the first fault is deliberately not skipped.
apply_gate_flip_ledgermaintains no dense measurement counter — MZ acts only whenprop.contains_z, PZ/QAlloc/MPZ clear, and Clifford gates map identity to identity — so an empty frame passes through untouched. Amin_tickskip is what caused the divergence in the first place.The Pauli-injection mapping was duplicated between the two functions, which is how they drifted apart. It is now a single
inject_faulthelper thatinit_pauli_prop_with_faultdelegates to.Tests
Four new tests, three of which fail against the old implementation (the
before=trueequivalence case already agreed, because of the old inclusivemin_tick):propagate_faultandpropagate_faults, for bothbeforevaluesHVerified by mutation: injecting after-faults before the tick's gates fails both the after-tick equivalence test and the property test.
A second, separate bug this exposed
Three pre-existing tests changed outcome, because two bugs had been cancelling each other out.
PauliFaultIteratorbuilds its Pauli set from X/Y/Z with no identity (fault_tolerance.rs:353), so at a multi-qubit gate location every qubit always receives a non-identity Pauli — the single-leg faultsIX/XI/IY/YI/IZ/ZIat a CX location are structurally unreachable. Those tests' comments described a single data-qubit X, the iterator actually producedXX, and the buggy propagation pushedXXthrough the CX a second time, cancelling one leg and accidentally reproducing the intended single-qubit effect.Rather than weaken those tests, they now construct the intended single-leg fault directly as a
PauliFault(representable even though the iterator will not generate one), and their original assertions are unchanged — including that naive three-qubit syndrome extraction is not 1-fault tolerant. Each carries a comment explaining why the fault is built directly.The enumerator gap itself is not fixed here; it is a distinct bug and deserves its own change. It matters because it errs toward false confidence — unenumerated faults make a circuit look more fault-tolerant than it is — and because a hook error is exactly a single-leg fault on one arm of a two-qubit gate. Note the DAG path is unaffected:
possible_faults(propagator/dag.rs:1170) offers the identity option per qubit and so does enumerate single-leg faults.Several diagnostic-only tests report different aggregate counts now that
before=falseis honoured; all still pass. Some of them compute an FT verdict without asserting it, which is worth tightening separately.Verification
cargo test -p pecos-qec: 802 passed, 0 failed (636 unit, 108 integration, 58 doctests)uv run --frozen pytest python/quantum-pecos/tests/qec -q: 1138 passed, 1 skipped, 1 xfailedjust build-debug,just lint: clean