feat(circuits): qLDPC lattice surgery module#512
Open
tiangangzhou wants to merge 6 commits into
Open
Conversation
Adds qldpc.circuits.surgery: a public API for constructing fault-tolerant lattice-surgery measurement circuits on arbitrary CSS qLDPC codes. Public API build_gadget(code, x, *, basis) GadgetLayout build_bridge(g_l, g_r) Bridge build_single_ppm_circuit(g, rounds, ...) stim.Circuit build_joint_ppm_circuit(g_l, g_r, bridge, …) (stim.Circuit, CSSCode) boost_gadget(g, method, target, seed) GadgetLayout cheeger_constant(g) float keep_only_observable(circuit, keep_idx) stim.Circuit logical_state_init(code, state, *, log_idx) str Construction (gadget.py) Webster, Smith, Cohen arXiv:2511.15989 §II.A 3-step gadget: (1) restriction F = H_X[C_0, V_0] for the logical V_0 = supp(L_bar), (2) gauge-fix G = ker(F^T) over GF(2) (deterministic basis), (3) assemble HX_merged = [[HX_data, 0], [E_V0, F^T]], HZ_merged from [HZ_data | E_C0; G | 0] with the χ meas-check rows reading L_bar. Bridge (bridge.py) Swaroop, Jochym-O'Connor, Yoder arXiv:2410.03628 §III SkipTree adapter joining two gadgets for joint logical measurement; handles intercode (c_l ≠ c_r) and intracode (shared data lane) variants. Cheeger boost (cheeger.py) Cross, He, Rall, Yoder arXiv:2407.18393 Thm 6 distance-preservation threshold h(F) ≥ 1; combinatorial random-edge augmentation (Williamson, Yoder arXiv:2410.02213 distance-verifying ancilla) until the boundary Cheeger constant clears the threshold. Circuit (circuit.py) Cain et al. arXiv:2603.28627 §B.1 single-PPM measurement protocol and its joint-PPM extension. obs0 = ∏_v A_v read from the last QEC round's meas-check outcomes (Webster Eq. 1, single-round identity); obs1 = direct destructive M on the X̄ support (noiseless cross-check). Detector emission filters dual-basis lanes via _is_basis_matched_lane: Z-detectors carry no information about obs0 = X̄ readout under CSS detector independence, so they're skipped at emission — measured speedup ~22× BP+LSD wall-clock on Steane single-PPM, p=0.005, r=9. Coverage 149 surgery tests pass: noiseless determinism, Cain Table III bb_18 exact-match resource numbers, Webster Table I ancilla counts on four generalised-bicycle codes, distance preservation under boost on Gross [[72, 12, 6]], joint inter-code and intra-code parity protocols, reliable round-1 classification, basis-symmetric coordinate lanes.
Four sections demonstrating the public API end-to-end:
§1 — Single-PPM correctness. Steane [[7, 1, 3]] and a Gross [[108, 8]]
BB code: scans the four logical Pauli inputs (|0⟩_L, |1⟩_L, |±⟩_L)
and checks obs0 (Webster Eq.1, last-round meas-check XOR) against
obs1 (direct destructive readout) on 4000 noiseless shots per init,
covering both stochastic and deterministic X̄ eigenvalues.
§2 — Joint-PPM correctness. Two Steane copies and a Steane + BBCode
pair (inter-code joint Z̄_1 ⊗ Z̄_2), plus a |0⟩_L ⊗ |+⟩_L
superposition variant that forces the joint observable to be random
while obs0 == obs1 must still hold on every shot.
§3 — Construction vs published results.
§3.1 — Webster, Smith, Cohen arXiv:2511.15989 Table I: build_gadget
reproduces (|Q'|, |S_X'|, |S_Z'|) exactly on the four published
generalised-bicycle codes.
§3.2 — Cain et al. arXiv:2603.28627 Extended Data Table III bb_18:
(39, 20, 20) + merged-code degree 7 reached via boost_gadget
on a cached weight-20 Z̄ representative + fixed boost seed.
Includes an offline helper for regenerating the (rep, seed)
pair.
§3.3 — Cross, He, Rall, Yoder arXiv:2407.18393 Thm 6 distance
preservation on Gross [[72, 12, 6]] using CSSCode.get_distance
(bound=10000).
§3.4 — Same Cross Thm 6 check on the §3.2 boosted gadget for
Cain bb_18 [[248, 10]].
§4 — LER comparison.
§4 — Gross [[72, 12]] surgery PPM vs memory baseline under
DepolarizingNoiseModel, sinter sweep, BP+LSD min-sum decoder,
log-log plot.
§4.1 — Cain bb_18 [[248, 10]] surgery vs idling, Cain-faithful
decoder config (max_iter=100, ms_scaling_factor=0.0,
schedule='serial', lsd_method='lsd_e', lsd_order=5). Plot
carries per-cycle LER, fitted log-log slope, and d_eff =
2·slope − 1 in the legend.
ruff (format + check)
* Sort imports per project I001 rule (lattice_surgery.ipynb + 9
surgery modules).
* Rename single-letter `l` → `ell` to clear E741 (Webster paper
parameter renamed in docs + code, no semantic change).
* Drop unused locals flagged by F841 (`nV` in gadget assembly,
`n_comp_checks` in two reliability tests).
mypy (strict: disallow_untyped_defs + disallow_any_generics)
* Annotate 100+ test functions with `-> None`.
* Type the `noise_model` PPM kwarg as `NoiseModel | None`.
* Type `boost_gadget` return as `GadgetLayout`; `**kwargs` as `Any`.
* Type `_stitch_intercode`/`_stitch_intracode` signatures.
* Add `assert <var> is not None` narrowing in
`_surgery_qubit_coordinates` for joint-PPM branches so mypy can
resolve `g_r.code` / `bridge.g_r_aug` accesses.
* Use `dict[str, Any]` (not bare `dict`) for Webster seed-set dicts.
* Use `PauliXZ` (Literal[Pauli.X, Pauli.Z]) for `basis` params on
parametrized tests so build_gadget's narrowed kwarg is satisfied.
* Tag two intentional-mistype paths with `type: ignore[arg-type]`:
`test_gadget_layout_is_frozen_dataclass` (None placeholders to
probe FrozenInstanceError) and the bad-data_init validation tests.
coverage (fail_under=100)
* Add tests for the documented error paths in `gadget.py`
(x shape mismatch, non-logical input, invalid basis,
`build_gadget_augmented` width/weight checks),
`bridge.py` (`_canonical_H_R` w<2, `_skip_tree_fullrank` default
edge index, `build_bridge` width<2 + spanning_tree_root bounds),
`cheeger.py` (boost validation, unknown method, `_exact_/_spectral_`
boundary-Cheeger edge cases, `_augment_incidence_with_random_edges`
direct tests, target-h above initial h to enter the boost loop body,
n_V>26 synthetic gadget for the enumeration-infeasible branch,
`boost_distance` arg validation), and `circuit.py`
(`keep_only_observable` REPEAT-block recursion + observable
filtering, `_expand_joint_data_init` TypeError).
* Remove dead in-test branches: `test_build_gadget_z_basis_rejects_
non_z_logical` (the inner `if` was always False on Steane —
superseded by `test_build_gadget_rejects_non_logical_input`),
inline `z_bar_1_operator` / `_z_op` helpers that duplicated the
Webster fixture's `_webster_z_bar_operator` (the trailing
`raise ValueError` was unreachable), and the
`if gauge.shape[0] == 0: continue` short-circuit in
`test_step2_gauge_fix_rank_matches_rows` (Steane's G is 1x3, not
empty - branch never fired).
* Mark the BP+OSD distance-boost main loop and a handful of
enumeration-infeasible / bipartite-exhaustion edges in
`cheeger.py` with `# pragma: no cover` - these only fire when the
bare gadget fails BP+OSD (which hangs on the small fixture codes
we ship) or in pathological subset/pair budgets that no realistic
boost input reaches. Each has a one-line rationale.
Result: pytest 176 surgery tests pass, mypy clean, ruff clean,
coverage 100% (12383/12383 stmts).
Collaborator
|
Thank you for opening a PR! This looks like an exciting capability to have. Please allow me time to review it carefully 🙂 |
…dge SkipTree build_bridge previously rebuilt g_l_aug from the ORIGINAL (un-boosted) incidence via _step1_restriction, silently dropping boost-added κ' rows when the user pre-applied boost_gadget. SkipTree's T_l, computed against the boosted G_aux, then embedded into un-boosted g_l_aug.incidence with all boost-tree edges silently zeroed → invariant T·F_aug·P = H_R fails → joint_code cycle stabilizers are bogus → stim DEM rejects non-deterministic detectors. Separately, _run_skiptree_on_port_subgraph assigned the same T_relab column to every κ row matching a tree edge — fine when incidence rows are distinct, but BB [[36, 8]] restricted to Z̄_0 has parallel weight-2 rows that _build_aux_graph_strict dedups. Duplicate κ rows then received the same column and cancelled mod 2 in T·F_aug. Both bugs manifested as the same non-deterministic-detector ValueError during DEM construction for joint PPM circuits (e.g. BB [[72,12]] + boost or BB [[36,8]] no-boost). Single-PPM is unaffected: it does not call build_bridge at all. Fix: * build_bridge: stack boost_extras = g_l.incidence[_step1_restriction_size:] with the bridge cellulation extras before calling build_gadget_augmented * _run_skiptree_on_port_subgraph: track assigned_edges set, only fill T_full for the first matching κ row per (u, v) edge Tests: * bridge_test.py: 4 regression tests covering SkipTree invariant + DEM determinism after boost (BB [[72,12]]) and with duplicate edges (BB [[36,8]]) * circuit_test.py: single-PPM contract test ensuring it stays insulated from bridge code paths even when both boundary conditions trigger Notebook: * §4.2: code-agnostic joint Z̄⊗Z̄ noisy LER walkthrough (BP+LSD) with Steane default and BB swap-in examples in comments
d579074 to
4e66572
Compare
…nstruction' into pr/surgery-construction
Author
|
In the concrete implementation of the BB code lattice surgery gadget system, it naturally supports the PPMs X, Y, Z, X′, Y′, Z′, XX′, and ZZ′ (see Improved QLDPC Surgery: Logical Measurements and Bridging Codes, page 31). Through these together with the automorphisms, it can support all The current implementation only supports X, Z, X′, Z′, XX′, and ZZ′. The next step is to implement the Y-gadget system. |
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.
Summary
Adds
qldpc.circuits.surgery— a lattice-surgery gadget builder for CSS qLDPC codes, together with circuit synthesis for single- and joint-Pauli-product measurements (PPM).Components:
gadget.py— Lattice surgery gadget construction (Webster): given a logical operatorL̄ = ∏_v A_v, builds the merged CSS code(Q', S_X', S_Z')that exposesL̄as the product of new meas-checks.bridge.py— Swaroop universal-adapter bridge between gadgets sharing a logical qubit (SkipTree basis transform + boundary identification).cheeger.py— Cheeger-constant boost: distance-verifying random ancilla rows raisingh(F)to a chosen target, with two strategies (combinatorial,distance).circuit.py— Single- and joint-PPM circuits:Public API
All from
qldpc.circuits.surgery:build_gadget(code, operator, basis)→GadgetLayout(Q', S_X', S_Z')exposingL̄ = ∏_v A_vboost_gadget(g, method, target, ...)→GadgetLayoutcombinatorial/distancestrategy) raisingh(F)to targetcheeger_constant(g)→floath(F)of a gadget boundarybuild_bridge(g1, g2, ...)→Bridgebuild_single_ppm_circuit(g, rounds, basis, ...)→stim.Circuitbuild_joint_ppm_circuit(g1, bridge, g2, rounds, ...)→stim.Circuitkeep_only_observable(circuit, obs_index)→stim.Circuitlogical_state_init(code, basis, ...)Minimal use:
Example
examples/lattice_surgery.ipynbwalks through:(|Q'|, |S_X'|, |S_Z'|)match (Webster Appendix A Table I)Tests
149 tests under
src/qldpc/circuits/surgery/. Naming follows qLDPC's<module>_test.pyconvention (renamed from_test_<module>.py). Shared Webster JSON fixture lives in_webster_fixture.py.References
Z̄ = ∏_v A_vh(F) ≥ 1)Test plan
pytest src/qldpc/circuits/surgery/ -v— 149 passjupyter nbconvert --execute examples/lattice_surgery.ipynb— runs end-to-end