Skip to content

Add QuditCode.get_destabilizer_ops - #543

Merged
perlinm merged 7 commits into
mainfrom
destab
Jul 22, 2026
Merged

Add QuditCode.get_destabilizer_ops#543
perlinm merged 7 commits into
mainfrom
destab

Conversation

@perlinm

@perlinm perlinm commented Jul 22, 2026

Copy link
Copy Markdown
Collaborator

Example usage: I have a syndrome vector syndrome_bits = (s_0, s_1, ...) from stabilizer measurements. I want the destabilizer that zeros out that syndrome and commutes with all logical operators. Say the code is self-dual and defined by a minimal set of stabilizer generators. I can construct the desired stabilizer with some thing like:

from qldpc import codes

code = codes.SteaneCode()
syndrome_bits = (0, 1, 0)

# X-type stabilizers and their Z-type duals
stabs = code.matrix_x
destab_ops = code.get_destabilizer_ops()[: len(stabs), len(code):]

syndrome_vec = code.field(syndrome_bits)  # convert to a galois.FieldArray
print(syndrome_vec)

net_destabilizer = syndrome_vec @ destab_ops
print(net_destabilizer)

perlinm and others added 7 commits July 22, 2026 13:59
The destabilizer construction moved into QuditCode.get_destabilizer_ops was
only correct over GF(2):

- The logical/gauge projection relied on a GF(2)-specific sign trick.  Replace
  it with a field-general projection using the inverse of the symplectic form.
- The commute-fixing loop subtracted a stabilizer with a hardcoded coefficient;
  scale by the symplectic overlap (and fix its sign) so it cancels over GF(q).

Also fix a cache-rebinding inconsistency: evaluating self.dimension /
self.gauge_dimension canonicalizes (and rebinds) the cached stabilizer
operators, so both get_destabilizer_ops and get_encoding_tableau now compute
the stabilizer count before grabbing stab_ops, keeping the destabilizers dual
to the stabilizers actually reported by get_stabilizer_ops.

Add destabilizer property assertions to test_qudit_ops (100% coverage of
get_destabilizer_ops over GF(2)/GF(3)/GF(4)) and an encoding test exercising
the overcomplete-stabilizer fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
QuditCode.dimension and .gauge_dimension counted stabilizer generators via
get_stabilizer_ops(canonicalized=True), which rebinds the cached stabilizer
operators as a side effect.  Count the independent generators with
get_stabilizer_ops().row_space() instead, which operates on a copy and leaves
the cache intact.

With the rebind gone, get_destabilizer_ops and get_encoding_tableau no longer
need to evaluate the stabilizer count before grabbing stab_ops, so restore the
natural inline condition in both.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… form

The logical and gauge operators are already in standard symplectic form, so
their symplectic dual basis is just the operators reindexed as [Z; -X].  Use
that directly instead of constructing ops @ symplectic_conjugate(ops).T and
inverting it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@perlinm
perlinm merged commit e360a8d into main Jul 22, 2026
3 checks passed
@perlinm
perlinm deleted the destab branch July 22, 2026 20:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant