Add fold-transversal logical S to the logical builder with derived detectors and a Guppy protocol factory - #777
Merged
Merged
Conversation
…tectors, fold-aware observables, and a Guppy protocol factory
ciaranra
force-pushed
the
surface-fold-s-builder
branch
from
September 16, 2026 01:38
7256830 to
51b45c7
Compare
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.
Slice 4b of the surface-code gadget library, stacked on #776: the fold-transversal logical S and S-dagger become first-class
LogicalCircuitBuilderoperations with correct detectors and observables, the Guppy protocol module gains the matching factory, and the fault-distance results are pinned numerically.What changed
add_logical_s(label, *, dagger=False)andadd_logical_sdg(label): a fold round is a one-round segment (LogicalGateType.FOLD_S) that the generator expands intofold_s_round_gadgetin the patch's current orientation. It requires a square rotated patch of distance at least 2 and follows the existing placement rules (after preparation, before final readout). The generator's step emitter acceptsOpType.CZ.SGate(the descriptor tracks sign-free Pauli frames).make_logical_s_experiment(rounds_before, rounds_after, *, dagger=False)in the protocol module, rendered with scoped sidebands and checked against the builder program by measurement-partition agreement.pecos.testinggains a reusable deterministic-parity-space helper (noiseless Stim shots, GF(2) rank) so tests can assert that the emitted detectors and observables span exactly the deterministic parities of a circuit.Evidence
Rank oracle, 2048 noiseless shots per shape, emitted rank equal to the deterministic-parity dimension in every case: Z memory with the fold in the first, middle and last round (25 each at d=3); X preparation with one fold (24), adjacent S and S-dagger (33), separated by two rounds (49); H then fold (41); fold before and after a transversal CX (74 each); at d=5 the X pair adjacent (97) and separated (145). Every emitted detector is deterministic over seeds 0 to 7.
Fault distances by
DetectorErrorModel.from_circuitwith p = 0.001 everywhere: Z memory with one fold 3 at d=3 and 5 at d=5; X preparation with S then S-dagger 2 at d=3 and 4 at d=5. The second pair is the scheme's known reduction (a Y fault before the fold becomes a Z pair on a mirror pair), stated in #776 and now pinned.Mutations pinned: dropping the after partner, dropping the before partner, or swapping the maps fails four rank tests each; letting an X term pass through a fold unchanged fails the observable-count test and the emitted observable samples both 0 and 1.
Goldens: five new builder shapes (fold first, mid, last; X pair; H then fold) and a protocol-module golden, captured after the implementation and documented as drift guards; all 145 pre-existing goldens byte-identical.
Verification
Targeted suites, the QEC surface, Guppy, DEM and reliable-observable suites, the generated guide tests, pre-commit on the changed files, and
just pytest-ci-core.Review round (two blind arms, 2026-09-14)
A physics arm (its own GF(2) and Stim flow tooling) and a structure arm (a 3324-program fold sweep, a 1320-program non-fold sweep against the base branch, 26 mutations) found no soundness bug: every emitted detector and observable is deterministic in every fold program, the emitted rank equals Stim's exact determined-measurement count in every fold shape, the partner maps match independently derived flows at d=2 to 7 in both frames, and the fault distances agree with Stim's exact search and are intrinsic to the circuit (no fuller detector set can raise them).
Changes in the fix round:
SGateboundary gate instead of refusing a descriptor: the descriptor tracks sign-free Pauli frames, in which S and S-dagger act identically, and the DEMs are byte-identical.deterministic_parity_basisand refuses too few shots.Two things this PR documents and pins rather than changes, because each is a decision beyond it:
pecos.testing.simulate_tick_circuitwill see; the observable metadata has no sign field. Tests pin raw parity 1 for S,S and 0 for S,S-dagger.build_decoder,LogicalSubgraphDecoder) skips; at d=5 its logical error rate is 2 to 2.8 times a hypergraph decoder's. The docstring and guide point fold circuits at a hypergraph decoder; a test pins the skipped-hyperedge count (12 with a fold, 0 without at d=3).Re-review of the fix round (2026-09-14)
A fresh reviewer found no correctness defect: the head is byte-identical to the previous commit's output across 1160 random programs (904 with folds, d=2 to 5), and the zero-round final detectors are complete after a CX, on two patches, and after SZ teleportation or T injection, at d=3 and 5. It did find that the observable-sign behaviour is broader than first documented: 36 of those 904 fold programs have noiseless raw parity 1 (for example S,S before a Z readout via H, or S-dagger pairs through a CX), none without a fold, and the public
pecos.qec.surface.extract_detection_events_and_observablesreports them flipped on every shot. The docstring and guide now state the general rule (raw parity is the sign with which the program's net logical Clifford maps the readout Pauli back onto the prepared eigenstate) and name both raw-parity consumers, and tests pin three examples. The structural fix, an expected-parity field in the observable metadata that raw consumers subtract, is a schema change left for a follow-up decision.Also in that round: the matching-route hyperedge counts are pinned exactly (12 with a fold, 0 without, at d=3); the zero-final detector count is asserted beside the guide's prose; an invalid fold variant is rejected at runtime; and six builder goldens from #763 that had no capture recipe now have one, with capture.py reproducing all 166 golden artifacts byte for byte.
Rebuilt onto dev after #776 was squash-merged: the branch is now a single commit holding this slice only, with the same tree as before on every file it owns. Its history was rewritten, so re-fetch before working from it locally. Verified after the rebuild: the surface and Guppy suites including the slow cases, the generated guide tests, pre-commit on the changed files, and
just pytest-ci-core.