css_4_1_2
[[4,1,2]] skip done[[4,1,2]] Leung-Nielsen-Chuang-Yamamoto (LNCY) code
Description
A four-qubit CSS stabilizer code that is the only qubit CSS code with such parameters. A stabilizer tableau for the code is given by \cite[ID 6]{preset:qiskit} \begin{align} \begin{array}{cccc} X & X & I & I \\ I & I & X & X \\ Z & Z & Z & …
Introduced in: arxiv:quant-ph/9704002
Rationale
Already formalized: QEC/Stabilizer/Codes/Small/CSS_4_1_2.lean (PR #32, merged 2026-05-23). 505 LoC, 26 sorries closed in two stages. The [[4,1,2]] LNCY code, the unique 4-qubit CSS detection code. Direct instance of existing CSS / StabilizerGroup machinery; distance-2 proof by enumeration. Notable: T19 demonstrated a generalizable pattern for CSS distance-2 proofs with multiple Z-generators covering disjoint qubit subsets — promoted to CLAUDE.md. Previous rationale: [[4,1,2]] LNCY code, the unique 4-qubit CSS detection code with these parameters; many hardware demos. Generators are four-qubit Pauli strings; distance bound by exhaustion. Direct fit for current CSS infrastructure.
Scoring composite -1.00
Attempt
Result: css_4_1_2 — [[4,1,2]] LNCY code
Status
pr-ready — all 26 sorries closed, lake build clean repo-wide, zero
linter warnings on the new file, zero BLOCKED markers.
Sorries closed
26 of 26 (21 of 21 theorem statements).
| ID | Theorem | Closing tactic |
|---|---|---|
| T1 | ZGenerators_are_ZType |
rcases hg + fin_cases i + simp [PauliOperator.IsZType, S_Z{1,2}, .set, .identity] |
| T2 | XGenerators_are_XType |
rcases hg + fin_cases i + simp [PauliOperator.IsXType, S_X1, .set] |
| T3 | S_Z1_comm_S_X1 |
pauli_comm_even_anticommutes, filter = {0, 1} |
| T4 | S_Z2_comm_S_X1 |
pauli_comm_even_anticommutes, filter = {2, 3} |
| T5 | generators_commute |
rcases + dispatch to ZType_commutes/cross/XType_commutes |
| T6 | negIdentity_not_mem |
CSS.negIdentity_not_mem_closure_union one-liner |
| T7 | listToSet_generatorsList |
simp only [...] + ext + simp only [..., or_assoc] |
| T8 | AllPhaseZero_generatorsList |
nested AllPhaseZero_cons.mpr ⟨rfl, …⟩ × 3 |
| T9 | rowsLinearIndependent_generatorsList |
by decide |
| T10 | GeneratorsIndependent_4_generatorsList |
(already a term in the skeleton; no sorry) |
| T11 | stabilizerGroup_toSubgroup_eq |
simp only [...] + rw [listToSet_generatorsList] |
| T12 | logicalX_anticommutes_logicalZ |
pauli_anticomm_odd_anticommutes, filter = {0} |
| T13a | logicalX_commutes_S_Z1 |
pauli_comm_even_anticommutes, filter = {0, 1} |
| T13b | logicalX_commutes_S_Z2 |
pauli_comm_componentwise [logicalX, S_Z2] (no overlap) |
| T13c | logicalX_commutes_S_X1 |
pauli_comm_componentwise [logicalX, S_X1] (both X) |
| T14a | logicalZ_commutes_S_Z1 |
pauli_comm_componentwise [logicalZ, S_Z1] (both Z) |
| T14b | logicalZ_commutes_S_Z2 |
pauli_comm_componentwise [logicalZ, S_Z2] (both Z) |
| T14c | logicalZ_commutes_S_X1 |
pauli_comm_even_anticommutes, filter = {0, 2} |
| T15 | logicalX_mem_centralizer |
mem_centralizer_iff + forall_comm_closure_iff + rcases |
| T16 | logicalZ_mem_centralizer |
same shape |
| T17 | stabilizerCode |
(structure literal in skeleton; non-sorry) |
| T18 | stabilizerCode_toSubgroup_eq |
change + rw [listToSet_generatorsList] |
| T19a | weightOneAt_anticomm_S_Z1 |
filter = {i} with rcases hi <;> rcases hP inside the filter-eq proof |
| T19b | weightOneAt_anticomm_S_Z2 |
same |
| T19c | weightOneAt_Z_anticomm_S_X1 |
filter = {i}, no hi constraint |
| T19 | weight_one_anticomm_witness |
match P, hP + hi_dichotomy reusable across X/Y branches |
| T20 | code_has_distance_two |
hasCodeDistance_of + no_weight_one_mem_centralizer_of_anticommute_witness |
| T21 | stabilizerCodeWithDistance |
(structure literal in skeleton; non-sorry) |
Blocked sorries
None. Per gap_audit.md's prediction, zero blockers materialized.
Lines of Lean produced
Final LoC: 505 (original estimate: 300; final is 68% above estimate).
The 200-line over-shoot is concentrated in:
- T1 (24 lines for the 2-generator Z-type proof vs ~12 in
FourQubit_4_2_2.lean's 1-generator version), - T19a / T19b (12 lines × 2 = 24 lines of helper machinery for the new
Z-generator dichotomy —
FourQubit_4_2_2.leanhad only one Z-gen so needed neither a Z1 nor Z2 helper), - T19 witness (16 lines vs ~8 in
FourQubit_4_2_2.leanbecause of thehi_dichotomycase-split betweenS_Z1andS_Z2).
The remaining proofs are essentially line-for-line ports from the [[4,2,2]] template.
Time spent
| Phase | Wall-clock |
|---|---|
| Setup (symlink verify, read metadata, skeleton parse check) | ~5 min |
| Batch 1 (T1–T16) drafting + iteration | ~20 min |
Batch 1 build + fix (T1, T2 missing identity; T7 or_assoc) |
~8 min |
| Batch 2 (T18–T20) drafting | ~10 min |
| Build + cleanup (T19 simplify, linter fixes, doc-comment update) | ~10 min |
| Whole-repo verification + write-up | ~5 min |
| Total | ~1 hour |
Well under the 8-hour budget; the proofs are unusually mechanical because this is essentially the [[4,2,2]] code with one extra Z-generator.
Patterns discovered
These are candidates for the next CLAUDE.md update (v4.30 patterns or
the Linter-clean idioms section). None are surprising in isolation,
but several are first-time occurrences in the repo.
-
Multi-Z-generator anti-witness functions: when a CSS code has more than one Z-stabilizer, the weight-1 X/Y anti-witness function must dispatch on which qubits the local Z-stab covers. The clean idiom is
have hi_dichotomy : (i = 0 ∨ i = 1) ∨ (i = 2 ∨ i = 3) := by fin_cases i <;> tautore-used across the
P = XandP = Ybranches, vs. doingby_casesinline in each branch (which duplicates the dichotomy case-split). This generalizes immediately to larger CSS codes (e.g. the next iceberg-family code). -
The
hi-conditional filter-equality pattern (T19a, T19b): when a helper lemma is universally quantified overi : Fin nbut constrained toi ∈ Sfor some subsetS(here,{0, 1}or{2, 3}), the filter equalityhave hfilter : (Finset.univ.filter (anticommutesAt ...)) = ({i} : Finset (Fin 4)) := by ext j; rcases hi with rfl | rfl <;> rcases hP with rfl | rfl <;> fin_cases j <;> simp [...]does the
rcasesoverhiandhPinside theextproof, beforefin_cases j. This avoids having to specialize the lemma statement (which would require a non-universali, defeating the purpose). The case multiplication (2 hi-cases × 2 hP-cases × 4 j-cases = 16) is dispatched cleanly bysimpat the leaves. -
or_associn 3-element list-to-set proofs: thelistToSet generatorsList = generatorsproof works forlen ≤ 2lists via the stock simp set[Set.mem_insert_iff, Set.mem_union, …], but atlen = 3(or more, when the set is structured as{Z1, Z2} ∪ {X1}), the left-associated(a ∨ b) ∨ cfromSet.mem_insert_iffdoesn't match the right-associateda ∨ (b ∨ c)fromSet.mem_union. Addingor_assocto the simp set bridges them. Worth adding to CLAUDE.md if a third example surfaces. -
Linter-clean:
linter.unusedSimpArgsonNQubitPauliOperator.identity: when a.setchain fully covers allFin npositions (e.g.S_X1sets all 4 qubits to X), the underlyingNQubitPauliOperator.identitynever appears in the residual goals afterfin_cases— and the linter correctly flags it as an unused simp argument. For partial coverage (e.g.S_Z1sets only qubits 0 and 1),identityis needed for the un-set positions. Rule of thumb: dropidentityfrom the simp set when every qubit is set; keep it otherwise. The linter tells you which.
Suggested follow-ups
-
Consider lifting a
CSS.distance_two_of_anticomm_witnesshelper. The T20 closing pattern (hasCodeDistance_of+interval_cases w+no_weight_one_mem_centralizer_of_anticommute_witness) is now duplicated verbatim betweenFourQubit_4_2_2.leanandCSS_4_1_2.lean. If a third small distance-2 CSS code lands (the iceberg family[[2m, 2m-2, 2]]is queued), promoting this five-line ritual to a one-call helper inFramework/Core/CSS/CSSDistance.leanwould save ~15 lines per code. Not blocking — flagged bygap_audit.mdand confirmed during this attempt. -
The 2-Z anti-witness helper pattern (T19a-c + T19) is a candidate for a generic CSS-distance toolkit. For an arbitrary CSS code with multiple Z-generators whose supports partition
Fin n, the "weight-1 X/Y at qubitianticommutes with whichever Z-generator coversi" lemma is purely a function of the support partition. A generic helperlemma weightOneAt_XY_anticomm_some_Zgen (n) (Zgens : List (NQubitPauliGroupElement n)) (cover : ∀ i, ∃ g ∈ Zgens, IsZType g ∧ g.operators i ≠ I) : ∀ i, ∀ P ∈ {X, Y}, ∃ g ∈ Zgens, Anticommute (weightOneAt i P) gwould centralize this. Not justified by one new code; revisit at the third instance.
-
Move the
[[4,1,2]]entry to the catalog'sDoneset. The pipeline dashboard readspipeline/queue.mdand the per-codestate.yaml; flippingstatus: skeleton-review → pr-readyhere is sufficient. Catalog re-score (advisory) at the next batch. -
No catalog gaps to file. Every dependency was available; no mathlib API drift was hit; no v4.30 idioms were broken. The [[4,1,2]] LNCY is exactly the "second exerciser" of the CSS-detection path that
gap_audit.mdpredicted, with no surprises.
Informal spec: [[4,1,2]] LNCY code
Summary
The Leung-Nielsen-Chuang-Yamamoto (LNCY) [[4,1,2]] code is a four-qubit
CSS stabilizer code encoding one logical qubit with code distance 2. It
detects (but does not correct) a single arbitrary Pauli error and is the
unique four-qubit qubit-CSS code with these parameters. The code was
introduced in Leung, Nielsen, Chuang, Yamamoto, Approximate quantum
error correction can lead to better codes [arxiv:quant-ph/9704002], where
it is also shown to approximately correct a single amplitude-damping
error with recovery fidelity 1 − 5γ² + O(γ³). We formalize the standard
distance-2 stabilizer-detection structure here; the AD-recovery result is
out of scope for this skeleton.
Parameters
- Physical qubits: n = 4
- Logical qubits: k = 1
- Distance: d = 2
- Family: CSS (two Z-type stabilizers, one X-type stabilizer)
- Originally introduced in: Leung-Nielsen-Chuang-Yamamoto 1997 [arxiv:quant-ph/9704002 §II, Eqs. 5–6]
- EC Zoo entry:
css_4_1_2("[[4,1,2]] LNCY code")
Codeword basis (LNCY convention)
From [arxiv:quant-ph/9704002 §II, Eqs. 5–6]:
|0_L⟩ = (|0000⟩ + |1111⟩)/√2
|1_L⟩ = (|0011⟩ + |1100⟩)/√2
Qubit indexing in this skeleton is 0-based (so the four qubits are indexed 0, 1, 2, 3). The kets above are written in qubit-0-leftmost convention.
Stabilizer generators
Three independent generators (n − k = 3), in canonical Z-then-X order. The paper presents the code by its codewords, not by stabilizers; we use the standard stabilizer presentation that stabilizes the LNCY codewords:
| Name | Pauli string (qubits 0,1,2,3) | Type |
|---|---|---|
S_Z1 |
Z Z I I |
Z-type |
S_Z2 |
I I Z Z |
Z-type |
S_X1 |
X X X X |
X-type |
(2 Z-type + 1 X-type, totalling n − k = 3 independent generators.)
Verification that these stabilize the LNCY codewords:
S_Z1 = ZZII: eigenvalue +1 on|0000⟩(q₀q₁=00),|1111⟩(q₀q₁=11),|0011⟩(q₀q₁=00),|1100⟩(q₀q₁=11). So +1 on|0_L⟩and|1_L⟩. ✓S_Z2 = IIZZ: same reasoning on q₂q₃. +1 on both codewords. ✓S_X1 = XXXX: maps|0000⟩ ↔ |1111⟩and|0011⟩ ↔ |1100⟩, so|0_L⟩ → |0_L⟩and|1_L⟩ → |1_L⟩. ✓
Alternative tableau in EC Zoo description. The EC Zoo entry quotes
the Qiskit preset tableau (XXII, IIXX, ZZZZ). That tableau does not
stabilize the LNCY codewords above — instead, XXII and IIXX are
logical X representatives (each takes |0_L⟩ ↦ |1_L⟩). The two
presentations are equivalent up to a swap of the X-side and Z-side roles,
but they describe different codes-as-subspaces unless the codeword
conventions are also swapped. We use the LNCY paper's codeword
convention as the ground truth; the stabilizer triple
(ZZII, IIZZ, XXXX) is the canonical stabilizer for those codewords.
This is the same convention used implicitly by the parent code
[[4,2,2]] (FourQubit_4_2_2.lean), where XXXX and ZZZZ are the
stabilizers and IIZZ, IZIZ etc. become logical operators.
Logical operators
(One logical qubit ⇒ a single logicalX and logicalZ.)
| Name | Pauli string | Weight | Role |
|---|---|---|---|
logicalX |
X X I I |
2 | Maps ` |
logicalZ |
Z I Z I |
2 | `+ |
Verification:
logicalX = XXII: maps|0000⟩ ↔ |1100⟩,|1111⟩ ↔ |0011⟩. So|0_L⟩ = (|0000⟩+|1111⟩)/√2 ↦ (|1100⟩+|0011⟩)/√2 = |1_L⟩. ✓logicalZ = ZIZI: eigenvalues —|0000⟩ → +,|1111⟩ → +1·−1 = +1(Z·I·Z·I = +1 on bits q₀,q₂ = 0,0; +1 on q₀,q₂=1,1). Wait, recompute: ZIZI on|q₀q₁q₂q₃⟩gives(-1)^(q₀+q₂). On|0000⟩:(-1)^0 = +1. On|1111⟩:(-1)^2 = +1. So|0_L⟩ → +|0_L⟩✓. On|0011⟩:(-1)^(0+1) = -1. On|1100⟩:(-1)^(1+0) = -1. So|1_L⟩ → -|1_L⟩✓.
Commutation/anticommutation table (✓ = commute, ✗ = anticommute):
S_Z1=ZZII |
S_Z2=IIZZ |
S_X1=XXXX |
|
|---|---|---|---|
logicalX = XXII |
✓ (anti at q0,q1: 2 = even) | ✓ (no overlap) | ✓ (both X-type) |
logicalZ = ZIZI |
✓ (both Z-type) | ✓ (both Z-type) | ✓ (anti at q0,q2: 2 = even) |
logicalX vs logicalZ |
✗ — anti at q0 only (1 = odd) → anticommute |
Codespace
The codespace is 2^k = 2-dimensional, spanned by |0_L⟩ and |1_L⟩
as above. The stabilizer projector is
P_C = (1/8)(I + S_Z1)(I + S_Z2)(I + S_X1)
which projects onto this 2-d subspace. We do not formalize P_C
explicitly in this skeleton — it follows from the abstract
Codespace framework once the StabilizerGroup 4 is in hand.
Theorems to formalize
The skeleton mirrors Steane7.lean adapted for (n,k,d) = (4,1,2) and
the 2-Z / 1-X CSS structure. With three Z×X cross-commutation pairs only
two are needed (since there is one X-generator and two Z-generators):
S_Z1·S_X1 and S_Z2·S_X1.
T1: each Z-generator is Z-type (operators in {I, Z})
Statement. ∀ g ∈ ZGenerators, IsZTypeElement g.
Reference: standard CSS predicate from
Framework/Core/CSS/CSSPredicates.lean. No paper citation; this is a
verification of the generator definitions.
T2: the X-generator is X-type (operators in {I, X})
Statement. ∀ g ∈ XGenerators, IsXTypeElement g.
Reference: same as T1.
T3: cross-commutation S_Z1 · S_X1 = S_X1 · S_Z1
Statement. ZZII and XXXX overlap (anticommute pairwise) at qubits
0 and 1, total 2 anticommuting positions (even) → operators commute.
Reference: paper's stabilizer structure (implicit in Eqs. 5–6); standard
CSS argument via commutes_iff_even_anticommutes.
T4: cross-commutation S_Z2 · S_X1 = S_X1 · S_Z2
Statement. IIZZ and XXXX anticommute pairwise at qubits 2 and 3,
total 2 anticommuting positions (even) → commute. Same reference as T3.
T5: all-pair generator commutation
Statement. ∀ g h ∈ generators, g * h = h * g. Follows from T1–T4
plus the CSS shortcuts ZType_commutes, XType_commutes.
Reference: implicit in [LNCY §II] (any stabilizer code requires this).
T6: −I ∉ stabilizer subgroup
Statement. negIdentity 4 ∉ Subgroup.closure generators. Standard
CSS proof via CSS.negIdentity_not_mem_closure_union.
T7: generator list ↔ generator set equality
Statement. listToSet generatorsList = generators where
generatorsList = [S_Z1, S_Z2, S_X1].
T8: all generators have phase 0
Statement. AllPhaseZero generatorsList.
T9: rows of the check matrix are linearly independent
Statement. rowsLinearIndependent generatorsList. The check matrix
has 3 rows over GF(2)^8 and is verifiable by decide for n = 4.
T10: generator independence
Statement. GeneratorsIndependent 4 generatorsList. Follows from T9.
T11: stabilizerGroup.toSubgroup = subgroup
Statement. The bundled StabilizerGroup 4 built from
generatorsList has the same underlying subgroup as
Subgroup.closure generators.
T12: logical X and logical Z anticommute
Statement. Anticommute logicalX logicalZ. XXII · ZIZI
anticommute pairwise only at qubit 0 (X·Z, then X·I, then I·Z, then I·I);
exactly 1 anticommuting position (odd) → anticommute.
T13: logical X commutes with each stabilizer generator
Statement. XXII commutes with each of ZZII, IIZZ, XXXX:
- vs
ZZII: anticommuting at qubits 0, 1; total 2 (even) → commute. - vs
IIZZ: no overlap → commute. - vs
XXXX: both X-type → commute.
T14: logical Z commutes with each stabilizer generator
Statement. ZIZI commutes with each of ZZII, IIZZ, XXXX:
- vs
ZZII: both Z-type → commute. - vs
IIZZ: both Z-type → commute. - vs
XXXX: anticommuting at qubits 0, 2; total 2 (even) → commute.
T15: logicalX ∈ centralizer stabilizerGroup
Statement. Follows from T13 via Subgroup.forall_comm_closure_iff.
T16: logicalZ ∈ centralizer stabilizerGroup
Statement. Follows from T14 via the same idiom.
T17: StabilizerCode 4 1 packaging
Statement. The bundled StabilizerCode 4 1 stabilizerCode is
constructed from generatorsList with logicalOps : Fin 1 → LogicalQubitOps 4 stabilizerGroup
built from T15, T16, T12. The k = 1 case uses
logical_commute_cross := fun ℓ ℓ' h => (h (Subsingleton.elim ℓ ℓ')).elim.
T18: stabilizerCode.toStabilizerGroup.toSubgroup = Subgroup.closure generators
Statement. Bridge between the bundled stabilizer-code view and the
raw Subgroup.closure generators view. Used by the distance proof.
T19: every weight-1 single-qubit Pauli anticommutes with some generator
Statement. ∀ i : Fin 4, ∀ P ∈ {X, Y, Z}, ∃ g ∈ generators, Anticommute (weightOneAt i P) g.
Proof outline (mirroring FourQubit_4_2_2.lean):
P = X: anticommutes with one of the Z-stabilizers (S_Z1ifi ∈ {0,1},S_Z2ifi ∈ {2,3}).P = Y: same (Y locally anticommutes with Z, like X does).P = Z: anticommutes withS_X1 = XXXX.
T20: code distance = 2 (HasCodeDistance stabilizerCode 2)
Statement.
HasCodeDistance stabilizerCode 2
i.e. (i) distance ≥ 1 [decide], (ii) logicalX = XXII is a nontrivial
logical operator of weight exactly 2 [witness], and (iii) no weight-1
Pauli is a nontrivial logical operator [from T19, via
no_weight_one_mem_centralizer_of_anticommute_witness].
Method: finite enumeration (n = 4 is small); the helper
hasCodeDistance_of from Framework/Core/Logical/CodeDistance.lean
reduces the proof to T19 plus the witness logicalX.
Reference: distance-2 claim is from [LNCY §II] — "the code corrects for a single amplitude damping error" (under the approximate-EC criterion; the distance-2 detection property is the projective version of that). The EC Zoo entry states: "Detects a single-qubit error or single erasure as a distance-two code".
T21: bundled StabilizerCodeWithDistance 4 1 2
Statement. Package T17 with T20 into the StabilizerCodeWithDistance
structure for downstream use.
Edge cases / convention notes
-
Qubit indexing is 0-based. Throughout the skeleton, qubits are
Fin 4 = {0, 1, 2, 3}. The LNCY paper writes|n₁ n₂ n₃ n₄⟩with 1-based indices; under our 0-based convention,n₁ ↔ q₀,n₂ ↔ q₁,n₃ ↔ q₂,n₄ ↔ q₃. The codeword strings (0000,1111,0011,1100) are identical under either indexing — only the labels change. -
Codeword convention is locked. Stage-2 review must check the LNCY codewords (Eqs. 5–6) vs. the chosen stabilizer
(ZZII, IIZZ, XXXX). The chosen stabilizers stabilize exactly the LNCY codewords as derived above. If the reviewer prefers the EC Zoo "Qiskit ID 6" tableau(XXII, IIXX, ZZZZ), thenlogicalXshould beZIZIorZZZZ-type andlogicalZshould beXXII-type — but this corresponds to a different 2-d subspace of the four-qubit Hilbert space. We use the LNCY codeword convention as the gold standard. -
No nontrivial phases on stabilizer generators. All three generators have
phasePower = 0. Noi-factor or−1-factor appears. -
No logical Y in this skeleton. For
k = 1codes the optionallogicalYcan be derived asi · X̄ · Z̄, but the LNCY paper does not single outlogicalY, and it is not required for any downstream theorem. We omit it; a Stage-4 follow-up could add it following the Steane7 pattern. -
HasCodeDistancevs. amplitude-damping recovery. The paper's main result (Eq. 20, fidelity1 − 5γ² + O(γ³)under AD) uses approximate error correction criteria. OurHasCodeDistance 2captures only the projective distance-2 detection property — it does not claim approximate AD correction. The AD result would require a separate framework not currently in the repo (continuous-parameter noise channels, residue-norm bounds, etc.) and is explicitly out-of-scope for this skeleton. -
Uniqueness claim. EC Zoo describes this as "the only qubit CSS code with such parameters". We do not formalize this uniqueness in this skeleton — it would require classifying all
[[4,1,2]]CSS codes up to equivalence, which is a substantial separate theorem. The skeleton instantiates one such code and proves its distance; uniqueness is left as future work.
Formalization plan: [[4,1,2]] LNCY code
Strategy summary
The [[4,1,2]] LNCY code is a CSS code with 2 Z-stabilizers + 1 X-stabilizer
on 4 qubits, encoding 1 logical qubit. The structure is exceptionally close
to the existing FourQubit_4_2_2.lean (same n = 4, same CSS detection
shape), differing only in:
- Stabilizer split: LNCY has
(ZZII, IIZZ, XXXX)(2 Z + 1 X) vs.[[4,2,2]]'s(ZZZZ, XXXX)(1 Z + 1 X); - Logical structure: k = 1 instead of k = 2, so the
Subsingleton.elim-shortcut closeslogical_commute_cross(no need for the off-diagonal pairwise commutation theorems); - Logical operators:
logicalX = XXII,logicalZ = ZIZI(specific Paulis on weight-2 supports) instead of the four logicalsIXIX/IIXX/IIZZ/IZIZof the [[4,2,2]] code.
The distance proof reuses the existing
no_weight_one_mem_centralizer_of_anticommute_witness helper
(Framework/Core/CSS/CSSDistance.lean) — essentially the same proof
shape as FourQubit_4_2_2.lean's code_has_distance_two, adjusted for
the new generator partition.
Distance method: finite enumeration via the standard CSS helper. No
homological framework needed; n = 4 is small enough that decide on
weight-1 anti-witness suffices.
Theorem dependency graph
T1 (Z-type) T3 (S_Z1·S_X1 = comm) T8 (phase 0)
T2 (X-type) T4 (S_Z2·S_X1 = comm) T9 (rows indep)
\ /
T5 (gen commute) T7 (list ↔ set) T10 (gens indep, ← T9)
\ /
T6 (-I ∉ subgroup) T11 (stabGroup.toSubgroup = subgroup)
\ /
T8 + T10 + T6 → T17 partial setup
|
T12 (X̄ ⊥ Z̄) |
T13 (X̄ ⊥ stabs) T14 (Z̄ ⊥ stabs)
\ /
T15, T16 (logicals in centralizer)
\ /
T17 (StabilizerCode 4 1) ← needs T7, T8, T10, T5, T6, T12, T15, T16
|
T18 (subgroup bridge)
|
T19 (weight-1 anticomm witness)
|
T20 (HasCodeDistance 2) ← needs T17, T18, T19, T12 (logicalX nontrivial)
|
T21 (StabilizerCodeWithDistance)
Per-theorem proof sketch
T1: ZGenerators are Z-type
- Approach:
rcases hg with rfl | rfl; then for each generator,fin_cases i <;> simp [...]on the per-qubit predicate. - Reuse: copy
FourQubit_4_2_2.ZGenerators_are_ZTypeverbatim, expand to 2 generators instead of 1. - Difficulty: trivial.
T2: XGenerators are X-type
- Approach: same as T1, with only 1 X-generator (matching
FourQubit_4_2_2.XGenerators_are_XType). - Difficulty: trivial.
T3: S_Z1 * S_X1 = S_X1 * S_Z1 (i.e. ZZII and XXXX)
- Approach:
pauli_comm_even_anticommutes; the anticommute-filter Finset is{0, 1}(size 2 = even). - Reuse: line-for-line analog of
Z1_comm_X1inFourQubit_4_2_2.lean:114. - Difficulty: trivial (standard CSS pattern).
T4: S_Z2 * S_X1 = S_X1 * S_Z2 (i.e. IIZZ and XXXX)
- Approach: same; anticommute-filter Finset is
{2, 3}(size 2 = even). - Difficulty: trivial.
T5: all-pair generator commutation
- Approach: standard CSS structural argument; reuse
CSSCommutationLemmas.ZType_commutesandXType_commutes. Case-split on(g, h) ∈ (Z ∪ X) × (Z ∪ X). - Reuse: copy
FourQubit_4_2_2.generators_commuteverbatim. - Difficulty: trivial.
T6: -I ∉ subgroup
- Approach: one-shot via
CSS.negIdentity_not_mem_closure_union, fed T1, T2, and the cross-commutation T3+T4 packaged asZGenerators_commute_XGenerators. - Reuse: copy
FourQubit_4_2_2.negIdentity_not_memverbatim. - Difficulty: trivial.
T7: listToSet generatorsList = generators
- Approach:
ext g; simpafter unfolding. Three elements in the list, three in the set (split as 2-Z + 1-X union). - Reuse: minor adaptation of
FourQubit_4_2_2.listToSet_generatorsList. - Difficulty: trivial.
T8: AllPhaseZero generatorsList
- Approach: chain of
AllPhaseZero_cons.mpr ⟨rfl, …⟩per element. Three elements ⇒ three nested⟨rfl, …⟩. - Reuse: pattern from any small code (Steane7 has 6, [[4,2,2]] has 2).
- Difficulty: trivial.
T9: rows linearly independent
- Approach:
by decide. Three rows in(GF(2))^8; concrete and tiny. - Difficulty: trivial.
T10: generators independent
- Approach: one-line consequence of T9 via
GeneratorsIndependent_of_rowsLinearIndependent. - Difficulty: trivial.
T11: stabilizerGroup.toSubgroup = subgroup
- Approach: unfold
mkStabilizerFromGenerators+ apply T7. - Reuse: copy
FourQubit_4_2_2.stabilizerGroup_toSubgroup_eqverbatim. - Difficulty: trivial.
T12: Anticommute logicalX logicalZ (i.e. XXII vs ZIZI)
- Approach:
pauli_anticomm_odd_anticommutes; anticommute-filter Finset is{0}(size 1 = odd). Q1 (X·I), Q2 (I·Z), Q3 (I·I) all commute. - Reuse: pattern from
FourQubit_4_2_2.logicalX_1_anticommutes_logicalZ_1. - Difficulty: trivial.
T13: logicalX commutes with each generator (3 lemmas)
- Approach: per generator,
pauli_comm_even_anticommutes+ explicit Finset. Cases:- vs
S_Z1=ZZII: filter{0, 1}(size 2). - vs
S_Z2=IIZZ: filter{}(size 0). - vs
S_X1=XXXX:pauli_comm_componentwise(both X-type).
- vs
- Reuse: combinations of
FourQubit_4_2_2.logicalX_i_commutes_*patterns. - Difficulty: trivial.
T14: logicalZ commutes with each generator (3 lemmas)
- Approach: dual of T13. Cases:
- vs
S_Z1=ZZII:pauli_comm_componentwise(both Z-type). - vs
S_Z2=IIZZ:pauli_comm_componentwise(both Z-type). - vs
S_X1=XXXX: filter{0, 2}(size 2).
- vs
- Difficulty: trivial.
T15: logicalX ∈ centralizer stabilizerGroup
- Approach: standard idiom (used in
FourQubit_4_2_2.lean):rw [StabilizerGroup.mem_centralizer_iff, stabilizerGroup_toSubgroup_eq, subgroup] rw [Subgroup.forall_comm_closure_iff] intro s hs ... - Reuse: copy
FourQubit_4_2_2.logicalX_1_mem_centralizerverbatim (modulo generator-set unfolding). - Difficulty: trivial.
T16: logicalZ ∈ centralizer stabilizerGroup
- Approach: same as T15.
- Difficulty: trivial.
T17: StabilizerCode 4 1 packaging
- Approach: structure literal with fields populated from T7–T16.
logical_commute_cross := fun ℓ ℓ' h => (h (Subsingleton.elim ℓ ℓ')).elimcloses the cross-commute field trivially in thek = 1case. - Reuse: copy
Steane7.stabilizerCodeshape (k = 1case). - Difficulty: trivial.
T18: stabilizerCode.toStabilizerGroup.toSubgroup = Subgroup.closure generators
- Approach: unfold + apply T7.
- Reuse: pattern from
FourQubit_4_2_2.stabilizerCode_toSubgroup_eq(it'sprivatethere; we'll need to re-prove or mirror it). - Difficulty: trivial.
T19: weight-1 anticommute witness
- Approach: backtracking dispatch by
(i, P)cases:
Two helper lemmas needed:match P, hP with | .X, _ | .Y, _ => ⟨S_Z_(i covers), ..., weight_anticomm_Z_proof⟩ | .Z, _ => ⟨S_X1, ..., weight_anticomm_X1_proof⟩ | .I, hP => exact (hP rfl).elimweightOneAt_anticomm_S_Z (i : Fin 4) (P ∈ {X, Y}): for the Z-side, pickS_Z1ifi ∈ {0,1},S_Z2ifi ∈ {2,3}. Internallyfin_cases isplits.weightOneAt_Z_anticomm_S_X1 (i : Fin 4): weight-1 Z at any qubit anticommutes withS_X1 = XXXX.
- Reuse: very close to
FourQubit_4_2_2.weight_one_anticomm_witness, with the twist that we need to pick which Z-generator to use (the [[4,2,2]] code had only one Z, so no branching). Use a sub-case oni < 2vsi ≥ 2. - Difficulty: standard (slightly more cases than [[4,2,2]]).
T20: HasCodeDistance stabilizerCode 2
- Approach:
refine hasCodeDistance_of stabilizerCode 2 (by decide) ⟨logicalX, (logicalOps 0).xOp_nontrivial, by decide⟩ ?_ intro w hw_pos hw_lt g hg_weight h_nontrivial interval_cases w -- w = 1 case: use T19 + helper rcases (IsNontrivialLogicalOperator_iff _ _).mp h_nontrivial with ⟨h_cent, _, _⟩ exact no_weight_one_mem_centralizer_of_anticommute_witness stabilizerCode.toStabilizerGroup generators T18 T19 g hg_weight h_cent - Reuse: copy
FourQubit_4_2_2.code_has_distance_two(line 534). - Difficulty: standard.
T21: StabilizerCodeWithDistance 4 1 2
- Approach: one-line structure literal bundling T17 + T20.
- Difficulty: trivial.
Risk register
-
Tableau-vs-codewords confusion. The EC Zoo description quotes a Qiskit tableau
(XXII, IIXX, ZZZZ)that is the dual of our chosen stabilizer(ZZII, IIZZ, XXXX)(X and Z roles flipped, same up to CSS swap). If a reviewer interprets the EC Zoo tableau as the ground-truth stabilizer, they would mark ourlogicalX = XXIIas wrong (it would be a stabilizer under their convention) and demandlogicalX = ZZZZorZZII. We document this clearly ininformal_spec.md§ "Stabilizer generators" — the chosen stabilizers are dictated by the LNCY paper's codewords (Eqs. 5–6), not by the Qiskit preset. Mitigation already in place. -
Logical Z support choice ambiguity.
logicalZ = ZIZIis one valid choice;logicalZ' = IZIZdiffers from it byZZZZ, andZZZZ = S_Z1 · S_Z2lies in the stabilizer. SoZIZIandIZIZare the same logical Z modulo the stabilizer. The choice is arbitrary; we pickZIZIfor symmetry withlogicalX = XXII. No theorem statement depends on this choice except T14 (the support of the explicit Finset would change). -
hasCodeDistance_ofstep forw = 0lower bound. The helper signature inCodeDistance.lean:52takes∀ w, 1 ≤ w → w < d → ..., sow = 0is excluded. Withd = 2, onlyw = 1needs handling, making this very clean. -
Worktree symlink. Done at setup —
lake buildshould reuse main's mathlib. If a futurelake buildfails with "Mathlib not found", check.lake/packagessymlink integrity.
Estimated effort
- Lines of code: ~320 (slightly above the catalog's 300 estimate; the extra Z-generator + one extra cross-commutation lemma vs. the [[4,2,2]] file account for the bulk).
- Hours: ~2–3 for an experienced agent following the
FourQubit_4_2_2.leantemplate. - Longest theorem: T19 (weight-1 anticommute witness) with ~8 cases. Should close in one attempt if the helper lemmas are written carefully.
- Total sorry count to close: 21 (one per theorem; some bundle
several lemmas into one
sorryblock for atomicity, see the Lean skeleton'sTODO(css_4_1_2-T<n>)markers).
Reuse audit: [[4,1,2]] LNCY code
This code is exceptionally close in structure to FourQubit_4_2_2.lean
(same n = 4, same CSS detection structure, same distance proof method)
and Steane7.lean (canonical k = 1 CSS instantiation). Reuse is
correspondingly very high.
Directly applicable (use as-is)
Core types and structures
NQubitPauliGroupElement 4— physical Pauli group on 4 qubits (QEC/Stabilizer/Foundations/PauliGroup/NQubitElement.lean)NQubitPauliOperator.identity 4— the all-IoperatorNQubitPauliOperator.set i P— set qubitito local PauliPSubgroup.closure,StabilizerGroup 4,mkStabilizerFromGeneratorsIsXTypeElement,IsZTypeElementfromFramework/Core/CSS/CSSPredicates.leanLogicalQubitOps 4 stabilizerGroup— bundled logical-qubit data (Framework/Core/Logical/LogicalOperators.lean:189)StabilizerCode 4 1,StabilizerCodeWithDistance 4 1 2(Framework/Core/Stabilizer/StabilizerCode.lean,Framework/Core/Logical/CodeDistance.lean:81)HasCodeDistancepredicate (Framework/Core/Logical/CodeDistance.lean:28)centralizer,StabilizerGroup.mem_centralizer_iff,Subgroup.forall_comm_closure_iff(Framework/Core/Stabilizer/Centralizer.lean)
CSS shortcuts (all directly applicable — this is a CSS code)
CSS.negIdentity_not_mem_closure_union(Framework/Core/CSS/CSSNoNegI.lean) — closes-I ∉ closure(Z ∪ X)given the type predicates and the cross-commutation hypothesis.CSSCommutationLemmas.ZType_commutesandXType_commutes(Framework/Core/CSS/CSSCommutationLemmas.lean) — two Z-type elements always commute, same for X-type.weightOneAt,no_weight_one_mem_centralizer_of_anticommute_witness(Framework/Core/CSS/CSSDistance.lean:25,:31) — the distance proof is one application of this lemma + a weight-1 anti-witness function (T19).
Tactics
pauli_comm_componentwise [...]— closesg * h = h * gwhen no two qubits anticommute (e.g. both Z-type) (Foundations/PauliGroup/CommutationTactics.lean:36).pauli_comm_even_anticommutes— convertsg * h = h * gto an "even number of anticommuting qubits" goal (Foundations/PauliGroup/CommutationTactics.lean:54).pauli_anticomm_odd_anticommutes— same forAnticommute g h(Foundations/PauliGroup/CommutationTactics.lean:66).
Independence machinery
rowsLinearIndependent(decidable on small n) (Foundations/BinarySymplectic/CheckMatrixDecidable.lean)GeneratorsIndependent_of_rowsLinearIndependent(Framework/Symplectic/IndependentEquiv.lean)AllPhaseZero_cons,AllPhaseZero_nil(Foundations/PauliGroup/NQubitElement.lean)
Helpers used in the distance proof
hasCodeDistance_of(Framework/Core/Logical/CodeDistance.lean:54)IsNontrivialLogicalOperator_iff(Framework/Core/Logical/LogicalOperators.lean:176)LogicalQubitOps.xOp_nontrivial(Framework/Core/Logical/LogicalOperators.lean:326)
Lightly adapted (existing pattern, new instance)
Top-level file structure
Mirror FourQubit_4_2_2.lean (Codes/Small/FourQubit_4_2_2.lean) at
the section level (§1 generators → §14 distance). The differences:
- §1 (generators):
[[4,2,2]]has{ZZZZ, XXXX}; LNCY has{ZZII, IIZZ, XXXX}. Add one extra Z-generator definition (S_Z2). - §3, §4 (typing/cross-commutation): 2×1 = 2 cross pairs instead of 1×1 = 1. Pattern unchanged, just one more lemma.
- §9 (independence):
decideruns on a 3-row matrix instead of 2-row. Still trivially decidable. - §10–§13 (logicals): only one logical qubit (
logicalX,logicalZ) instead of two; theSubsingleton.elimshortcut fromSteane7.lean:510dischargeslogical_commute_cross. Pattern from Steane7, not [[4,2,2]]. - §14 (distance): T19's anti-witness function dispatches over
which Z-generator to use (
S_Z1fori ∈ {0, 1},S_Z2fori ∈ {2, 3}). [[4,2,2]] had only one Z-generator so no such split.
Specific copy-paste sites (with adaptation)
Z1,Z2(S_Z1,S_Z2): adapt fromSteane7.lean:49-65(per-qubit.setchain) for support{0, 1}and{2, 3}respectively.X1(S_X1 = XXXX): directly reuse the definition fromFourQubit_4_2_2.lean:64(XXXX).ZGenerators,XGenerators,generators,subgroup:Steane7.lean:85-98pattern.ZGenerators_are_ZType:Steane7.lean:105pattern for 2 Z generators (rcases withrfl | rfl).XGenerators_are_XType:FourQubit_4_2_2.lean:99pattern (1 X generator).- Cross-commutation
S_Z1_comm_S_X1,S_Z2_comm_S_X1: copyFourQubit_4_2_2.Z1_comm_X1(line 114), adjustingFinsetfor{0, 1}and{2, 3}. ZGenerators_commute_XGenerators:Steane7.lean:227pattern (2×1 cases instead of 3×3).generators_commute,negIdentity_not_mem,generatorsList,listToSet_generatorsList: all line-for-line analogs ofFourQubit_4_2_2.lean:149,:165,:175,:179.AllPhaseZero_generatorsList: nested⟨rfl, ...⟩for 3 elements (extendFourQubit_4_2_2.lean:188from 2 to 3).rowsLinearIndependent_generatorsList,GeneratorsIndependent_4_generatorsList: copyFourQubit_4_2_2.lean:195-202.stabilizerGroup,stabilizerGroup_toSubgroup_eq: copyFourQubit_4_2_2.lean:207-215.logicalX,logicalZ: define directly (XXIIandZIZIper the spec). Use the.setpattern fromFourQubit_4_2_2.lean:227-240.logicalX_anticommutes_logicalZ:FourQubit_4_2_2.lean:245pattern with filter{0}(size 1).- Per-generator logical-commutation lemmas
(
logicalX_commutes_S_Z1etc.): 6 lemmas total (2 logicals × 3 generators). Each followsFourQubit_4_2_2.lean:318or:331(pauli_comm_componentwisefor same-type-type commute,pauli_comm_even_anticommutes+ Finset for cross-type commute). logicalX_mem_centralizer,logicalZ_mem_centralizer:FourQubit_4_2_2.lean:384-407pattern, simplified to one logical per centralizer (no k = 2 partitioning).stabilizerCode,logicalOps:Steane7.lean:496-510pattern (k = 1 case withSubsingleton.elimcross-commute).stabilizerCode_toSubgroup_eq:FourQubit_4_2_2.lean:472pattern (private bridge lemma).weightOneAt_anticomm_S_Z,weightOneAt_Z_anticomm_S_X1: adaptFourQubit_4_2_2.lean:480and:499patterns; the Z-side helper needs an extra parameter for which Z-generator (or two separate lemmas: one forS_Z1, one forS_Z2).weight_one_anticomm_witness:FourQubit_4_2_2.lean:517pattern, with an innerfin_cases ito dispatch betweenS_Z1andS_Z2.code_has_distance_two:FourQubit_4_2_2.lean:534pattern.stabilizerCodeWithDistance:FourQubit_4_2_2.lean:546pattern.
New per-code definitions
Nothing genuinely new — every definition has a 1-to-1 analog in
FourQubit_4_2_2.lean or Steane7.lean. The only structural novelty
is the 2-Z + 1-X split (everything else in the repo has either
balanced splits or single-generator splits per Pauli type), but no
new abstraction is required to express it.
Umbrella file update
Add the new module to the Codes/Small.lean umbrella:
QEC/Stabilizer/Codes/Small.lean currently imports 6 small codes
(Shor9, Steane7, Steane7TransversalGates, FourQubit_4_2_2,
QuantumHamming, FiveQubit_5_1_3). Append:
import QEC.Stabilizer.Codes.Small.CSS_4_1_2
and update the doc comment to mention the LNCY [[4,1,2]] code.
Gap audit: [[4,1,2]] LNCY code
Repo gaps (this code surfaces / requires new abstractions)
None. This code is the cleanest possible engineering-track CSS formalization. It exercises the standard CSS path end-to-end:
- Z/X type predicates → already in
Framework/Core/CSS/CSSPredicates.lean - CSS no-
-Ishortcut →Framework/Core/CSS/CSSNoNegI.lean - CSS commutation lemmas (ZType×ZType, XType×XType) →
Framework/Core/CSS/CSSCommutationLemmas.lean - Weight-1 anticomm-witness distance helper →
Framework/Core/CSS/CSSDistance.lean k = 1packaging viaSubsingleton.elimcross-commute shortcut → used inSteane7.lean:510StabilizerCodeWithDistancebundling → standard- Distance proof via
hasCodeDistance_of+weight_one_anticomm_witness→ exactly the [[4,2,2]] pattern, with the only twist being a 2-way case-split oni < 2vsi ≥ 2for the Z-generator choice.
The skeleton is essentially a CSS-detection-code "second exerciser"
of the same path that FourQubit_4_2_2.lean walked. No new abstraction
is justified by this one code; if a third CSS detection code is added
(e.g. iceberg / [[2m, 2m-2, 2]] next in the queue), a small lemma
CSS.distance_two_of_anticomm_witness that bundles
hasCodeDistance_of + weight_one_anticomm_witness + IsNontrivialLogicalOperator_iff
into one call would deduplicate this distance-2 pattern. Not blocking
for this code.
Mathlib gaps (lemmas not in mathlib v4.30)
None anticipated. Everything needed is either project-local (the
pauli_* tactics, weightOneAt, etc.) or in standard mathlib v4.30
(Subgroup.closure, Finset.filter, decide, Fin.cases,
Subsingleton.elim).
Likely "BLOCKED(<reason>)" sorries
None expected. Stage 4 should close all 21 sorries in one or two
attempt rounds, with the only realistic source of friction being the
mechanical idioms documented in CLAUDE.md (most relevantly:
_root_.mul_assoc qualification when open NQubitPauliGroupElement
is in scope, change step before rw [one_mul, mul_one] in
closure_induction proofs — but those don't even arise here since the
centralizer proofs use Subgroup.forall_comm_closure_iff rather than
closure_induction).
Potential mechanical friction points (not blockers, just heads-up)
-
EC Zoo tableau confusion. Stage-4 agents might be tempted to "verify" the stabilizer choice against the EC Zoo description, which quotes
(XXII, IIXX, ZZZZ). As documented ininformal_spec.md§ "Stabilizer generators" — this is the dual of our chosen(ZZII, IIZZ, XXXX). The choice is dictated by the LNCY codewords (Eqs. 5–6) and the requirement that the chosen stabilizers stabilize those codewords. We have verified this in writing. -
logicalZ = ZIZIvsIZIZ. Both are valid logical Z representatives (differ byZZZZ = S_Z1·S_Z2which is in the stabilizer). We pickZIZIfor symmetry withlogicalX = XXII(both have support{0, 2}-style: actuallylogicalXhas support{0, 1}andlogicalZhas support{0, 2}; the only shared support qubit is qubit 0, where they anticommute, hence the anticommute-filter{0}). Stage 4 should not silently switch choices; if the skeleton's pinned choice doesn't close T14 or T19 cleanly, the right fix is to investigate why, not to change the logical operator. -
Filter cardinalities for cross-type commutations (T13, T14):
logicalX = XXIIvsS_Z1 = ZZII: anticommute filter{0, 1}(card 2).logicalX = XXIIvsS_Z2 = IIZZ: anticommute filter{}(card 0).logicalZ = ZIZIvsS_X1 = XXXX: anticommute filter{0, 2}(card 2).- All three are even cardinality → commute. The empty-filter case
is well-supported by
pauli_comm_componentwisesince when neither operator has X overlapping the other's Z, they commute componentwise.
-
Distance-2 lower bound only requires w = 1. The interval-case
interval_cases wwith1 ≤ w < 2reduces to the single casew = 1, whichno_weight_one_mem_centralizer_of_anticommute_witnesscloses. There is now = 2lower-bound case (the bound is< d, and2 ≮ 2). Less work than the [[5,1,3]] distance-3 case.
Progress log: [[4,1,2]] LNCY code (css_4_1_2)
Session 1 (2026-05-23)
- Starting sorries: 26 across 21 theorem statements (T1–T21).
- Budget: 8h, per-sorry 15min autoprove + 25min sorry-filler-deep.
- Plan order: T1 → T21 (per
plan.mddependency graph). - Reference template:
Codes/Small/FourQubit_4_2_2.lean(same n, same CSS shape).
Setup
- Worktree mathlib symlink verified.
- Skeleton
lake buildconfirmed: 26declaration uses sorrywarnings, no errors. - Proof approach: direct edits driven by the FourQubit_4_2_2 template (mechanical adaptation, k=1 vs k=2; 2 Z-generators vs 1).
Batch 1 — T1 through T16 (typing, commutation, centralizer)
- T1 (
ZGenerators_are_ZType): closed byrcases hg with rfl | rfl, per-generatorfin_cases i <;> simp. First attempt missedNQubitPauliOperator.identityin the simp set — Lean reportedNQubitPauliOperator.identity 4 2 = PauliOperator.I ∨ …was unsolved. AddedNQubitPauliOperator.identityto the simp lemmas; closed. - T2 (
XGenerators_are_XType): same fix. (Note: later cleaned out the extraNQubitPauliOperator.identityhere —linter.unusedSimpArgsflagged it because for X1 specifically all positions get overwritten by.set, so identity never appears in the residual goal.) - T3 (
S_Z1_comm_S_X1):pauli_comm_even_anticommutes+ filter{0, 1}. Decided viadecide. - T4 (
S_Z2_comm_S_X1): filter{2, 3}. Same pattern. - T5 (
generators_commute): standard rcases dispatch, 4 cases (Z×Z, Z×X, X×Z, X×X) routing toZType_commutes,cross,XType_commutes.symm. Verbatim from [[4,2,2]] exceptOr.inl/Or.inron the 2-Z generator list. - T6 (
negIdentity_not_mem): one-shotCSS.negIdentity_not_mem_closure_union. - T7 (
listToSet_generatorsList):simp only [...]+ext+ finalsimp only. First attempt failed because the list has 3 elements andSet.mem_insert_iffunfolds left-associated ((a ∨ b) ∨ c), whereasSet.mem_union (ZGenerators ∪ XGenerators)afterSet.mem_insert_iffunfolds right-associated (a ∨ (b ∨ c)). Addedor_assocto the final simp set to close. - T8 (
AllPhaseZero_generatorsList): nestedAllPhaseZero_cons.mpr ⟨rfl, ...⟩× 3. Used the explicitrw + refine ⟨rfl, ?_⟩chain for clarity over thesimpashortcut. - T9 (
rowsLinearIndependent_generatorsList):by decide. - T10 (
GeneratorsIndependent_4_generatorsList): non-sorry — a term that uses T9 viaGeneratorsIndependent_of_rowsLinearIndependent(in the skeleton already; nosorryto close). - T11 (
stabilizerGroup_toSubgroup_eq):simp only [...]+rw. - T12 (
logicalX_anticommutes_logicalZ): filter{0},pauli_anticomm_odd_anticommutes. - T13a-c (
logicalX_commutes_S_{Z1,Z2,X1}):- vs S_Z1: filter
{0, 1}(even). - vs S_Z2:
pauli_comm_componentwise(no overlap with anticommute). - vs S_X1:
pauli_comm_componentwise(both X-type).
- vs S_Z1: filter
- T14a-c (
logicalZ_commutes_S_{Z1,Z2,X1}):- vs S_Z1:
pauli_comm_componentwise(both Z-type). - vs S_Z2:
pauli_comm_componentwise(both Z-type). - vs S_X1: filter
{0, 2}(even).
- vs S_Z1:
- T15, T16 (
logicalX/Z_mem_centralizer):StabilizerGroup.mem_centralizer_iff + forall_comm_closure_iff + rcases.
Status after batch 1: 16/26 closed, 10 remaining. Commit 5a4238a.
Batch 2 — T17/T18 + T19 helpers + T19 witness + T20 + T21
- T17 (
stabilizerCode): non-sorry — structure literal already populated in the skeleton; thelogical_commute_crossfield usesSubsingleton.elimdirectly. Nothing to close here. - T18 (
stabilizerCode_toSubgroup_eq):change+rw. Same as the [[4,2,2]] template, verbatim. - T19a (
weightOneAt_anticomm_S_Z1): the trick is that the filter equality has to incorporate thehi : i = 0 ∨ i = 1case-split inside theextproof — at the call site we can't pick a specific concrete value forisince the lemma is universally quantified overi. Solution: do thercases hi <;> rcases hPinside the filter-equality proof, betweenext jandfin_cases j. The resulting case explosion (2 × 2 × 4 = 16 cases) is handled cleanly bysimp. - T19b (
weightOneAt_anticomm_S_Z2): same shape, mirror of T19a. - T19c (
weightOneAt_Z_anticomm_S_X1): nohiconstraint (Z·X anticommutes at every qubit), so the [[4,2,2]] template applies verbatim. - T19 (
weight_one_anticomm_witness): match onP, hP:P = X/P = Y: pick S_Z1 or S_Z2 byi ∈ {0,1}vsi ∈ {2,3}dichotomy. Initial draft usedby_cases hi : i = 0 ∨ i = 1per branch; a small refactor pulled out the dichotomy as a singlehave hi_dichotomy : (i = 0 ∨ i = 1) ∨ (i = 2 ∨ i = 3)(closed byfin_cases i <;> tauto) reused acrossXandYbranches.P = Z: pick S_X1.P = I: contradiction fromhP : P ≠ I.
- T20 (
code_has_distance_two): verbatim from [[4,2,2]] usinghasCodeDistance_of+IsNontrivialLogicalOperator_iff+no_weight_one_mem_centralizer_of_anticommute_witness. The(logicalOpsCSS_4_1_2 0).xOp_nontrivialinvocation gives the weight-2 logicalX witness directly. - T21 (
stabilizerCodeWithDistance): non-sorry — structure literal bundling T17 + T20.
Cleanup pass: two linter.unusedSimpArgs warnings flagged the
unused NQubitPauliOperator.identity simp argument in T2 and T19c
(the simp set normalizes via .set chains, which fully overwrite the
identity for those theorems). Removed both arguments — file now compiles
with zero warnings.
Final whole-repo lake build: clean, all 3422 jobs succeed. No new
warnings introduced anywhere.
Status after batch 2: 26/26 sorries closed (21/21 theorem statements). 0 BLOCKED. Final LoC = 505. Ready for PR.