Skip to content

Commit

Permalink
Fix qudit surface/toric codes (#197)
Browse files Browse the repository at this point in the history
* fix parity checks for rotated surface/toric codes

* fix tests and coverage
  • Loading branch information
perlinm authored Jan 18, 2025
1 parent a9b0a28 commit f8f734c
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 12 deletions.
2 changes: 1 addition & 1 deletion qldpc/circuits_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def test_finding_circuit(pytestconfig: pytest.Config) -> None:

def test_deformed_decoder() -> None:
"""Deform a code in such a way as to preserve its logicals, but change its stabilizers."""
code = codes.CSSCode([[1] * 6], [[1] * 6])
code = codes.CSSCode([[1] * 6], [[1] * 6], field=2)
code.set_logical_ops_xz(
[
[1, 1, 0, 0, 0, 0],
Expand Down
7 changes: 4 additions & 3 deletions qldpc/codes/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1701,13 +1701,14 @@ def concatenate(
operators of the concatenated code. Otherwise, logical operators of the concatenated code
get recomputed from scratch.
"""
if not isinstance(inner, CSSCode) or not isinstance(outer, CSSCode):
raise TypeError("CSSCode.concatenate requires CSSCode inputs")

# stack copies of the inner and outer codes (if necessary) and permute inner logicals
inner, outer = QuditCode._standardize_concatenation_inputs(
inner, outer, outer_physical_to_inner_logical
)

if not isinstance(inner, CSSCode) or not isinstance(outer, CSSCode):
raise TypeError("CSSCode.concatenate requires CSSCode inputs")
assert isinstance(inner, CSSCode) and isinstance(outer, CSSCode)

"""
Parity checks inherited from the outer code are nominally defined in terms of their support
Expand Down
5 changes: 3 additions & 2 deletions qldpc/codes/common_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ def test_qudit_code() -> None:
[0, 1, 0, 0, 1, 0, 0, 1, 1, 0],
[1, 0, 1, 0, 0, 0, 0, 0, 1, 1],
[0, 1, 0, 1, 0, 1, 0, 0, 0, 1],
]
],
field=2,
)
assert np.array_equal(code.matrix, equiv_code.matrix)

Expand Down Expand Up @@ -439,7 +440,7 @@ def test_css_ops() -> None:
# the 2x2 toric code has redundant stabilizers
code = codes.ToricCode(2)
assert code.num_checks == 4
assert codes.CSSCode.equiv(code, codes.CSSCode([[1, 1, 1, 1]], [[1, 1, 1, 1]]))
assert code.canonicalized().num_checks == 2


def test_distance_css() -> None:
Expand Down
20 changes: 17 additions & 3 deletions qldpc/codes/quantum.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@
import os
from collections.abc import Collection, Sequence

import galois
import networkx as nx
import numpy as np
import numpy.typing as npt
import sympy

from qldpc import abstract
from qldpc.abstract import DEFAULT_FIELD_ORDER
from qldpc.objects import CayleyComplex, ChainComplex, Node, Pauli, QuditOperator

from .classical import HammingCode, RepetitionCode, RingCode, TannerCode
Expand Down Expand Up @@ -998,6 +1000,12 @@ def __init__(
if (row + col) % 2
]

# invert Z-type Pauli on every other qubit
code_field = galois.GF(field or DEFAULT_FIELD_ORDER)
if code_field.order > 2:
matrix_z = code_field(matrix_z)
matrix_z[:, self._default_conjugate] *= -1

else:
# "original" surface code
code_a = RepetitionCode(rows, field)
Expand All @@ -1007,7 +1015,7 @@ def __init__(
matrix_z = code_ab.matrix_z
self._default_conjugate = slice(code_ab.sector_size[0, 0], None)

CSSCode.__init__(self, matrix_x, matrix_z, field=field, validate=False)
CSSCode.__init__(self, matrix_x, matrix_z, field=field)

@classmethod
def get_rotated_checks(
Expand Down Expand Up @@ -1107,6 +1115,12 @@ def __init__(
if (row + col) % 2
]

# invert Z-type Pauli on every other qubit
code_field = galois.GF(field or DEFAULT_FIELD_ORDER)
if code_field.order > 2:
matrix_z = code_field(matrix_z)
matrix_z[:, self._default_conjugate] *= -1

else:
# "original" toric code
code_a = RingCode(rows, field)
Expand All @@ -1116,7 +1130,7 @@ def __init__(
matrix_z = code_ab.matrix_z
self._default_conjugate = slice(code_ab.sector_size[0, 0], None)

CSSCode.__init__(self, matrix_x, matrix_z, field=field, validate=False)
CSSCode.__init__(self, matrix_x, matrix_z, field=field)

@classmethod
def get_rotated_checks(
Expand Down Expand Up @@ -1188,4 +1202,4 @@ def __init__(
matrix_x, matrix_z = chain.op(1), chain.op(2).T
assert not isinstance(matrix_x, abstract.Protograph)
assert not isinstance(matrix_z, abstract.Protograph)
CSSCode.__init__(self, matrix_x, matrix_z, field, validate=False)
CSSCode.__init__(self, matrix_x, matrix_z, field)
6 changes: 3 additions & 3 deletions qldpc/codes/quantum_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,14 +388,14 @@ def test_surface_codes(rows: int = 3, cols: int = 2) -> None:
assert code.conjugated() == codes.HGPCode(*rep_codes).conjugated()

# rotated surface code
code = codes.SurfaceCode(rows, cols, rotated=True)
code = codes.SurfaceCode(rows, cols, rotated=True, field=3)
assert code.dimension == 1
assert code.num_qudits == rows * cols
assert codes.CSSCode.get_distance(code, Pauli.X) == cols
assert codes.CSSCode.get_distance(code, Pauli.Z) == rows

# test that the conjugated rotated surface code is an XZZX code
code = codes.SurfaceCode(max(rows, cols), rotated=True, field=2)
code = codes.SurfaceCode(max(rows, cols), rotated=True)
for row in code.conjugated().matrix:
row_x, row_z = row[: code.num_qudits], row[-code.num_qudits :]
assert np.count_nonzero(row_x) == np.count_nonzero(row_z)
Expand All @@ -420,7 +420,7 @@ def test_toric_codes() -> None:

# rotated toric code
distance = 4
code = codes.ToricCode(distance, rotated=True)
code = codes.ToricCode(distance, rotated=True, field=3)
assert code.dimension == 2
assert code.num_qudits == distance**2
assert codes.CSSCode.get_distance(code) == distance
Expand Down

0 comments on commit f8f734c

Please sign in to comment.