Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend functionality for working with codes. #288

Merged
merged 20 commits into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/mqt/qecc/codes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from .bb_codes import construct_bb_code
from .color_code import ColorCode, LatticeType
from .concatenation import ConcatenatedCode, ConcatenatedCSSCode
from .css_code import CSSCode, InvalidCSSCodeError
from .hexagonal_color_code import HexagonalColorCode
from .square_octagon_color_code import SquareOctagonColorCode
Expand All @@ -12,6 +13,8 @@
__all__ = [
"CSSCode",
"ColorCode",
"ConcatenatedCSSCode",
"ConcatenatedCode",
"HexagonalColorCode",
"InvalidCSSCodeError",
"InvalidStabilizerCodeError",
Expand Down
137 changes: 137 additions & 0 deletions src/mqt/qecc/codes/concatenation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"""Concatenated quantum codes."""

from __future__ import annotations

from typing import TYPE_CHECKING

import numpy as np

from .css_code import CSSCode
from .pauli import Pauli
from .stabilizer_code import InvalidStabilizerCodeError, StabilizerCode
from .symplectic import SymplecticVector

if TYPE_CHECKING:
from collections.abc import Sequence

import numpy.typing as npt


class ConcatenatedCode(StabilizerCode):
Fixed Show fixed Hide fixed
"""A concatenated quantum code."""

def __init__(self, outer_code: StabilizerCode, inner_code: StabilizerCode | Sequence[StabilizerCode]) -> None:
"""Initialize a concatenated quantum code.

Args:
outer_code: The outer code.
inner_code: The inner code. If a list of codes is provided, the qubits of the outer code are encoded by the different inner codes in the list.
"""
self.outer_code = outer_code
Fixed Show fixed Hide fixed
if isinstance(inner_code, list):
self.inner_codes = inner_code
Fixed Show fixed Hide fixed
else:
self.inner_codes = [inner_code] * outer_code.n
Fixed Show fixed Hide fixed
if not all(code.k == 1 for code in self.inner_codes):
msg = "The inner codes must be stabilizer codes with a single logical qubit."
raise InvalidStabilizerCodeError(msg)

self.n = sum(code.n for code in self.inner_codes)
Fixed Show fixed Hide fixed
generators = [self._outer_pauli_to_physical(p) for p in outer_code.generators]
if outer_code.x_logicals is not None:
x_logicals = [self._outer_pauli_to_physical(p) for p in outer_code.x_logicals]
if outer_code.z_logicals is not None:
z_logicals = [self._outer_pauli_to_physical(p) for p in outer_code.z_logicals]
d = min(code.distance * outer_code.distance for code in self.inner_codes)
super().__init__(generators, d, x_logicals, z_logicals)
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed

def _outer_pauli_to_physical(self, p: Pauli) -> Pauli:
"""Convert a Pauli operator on the outer code to the operator on the concatenated code.

Args:
p: The Pauli operator.

Returns:
The Pauli operator on the physical qubits.
"""
if len(p) != self.outer_code.n:
msg = "The Pauli operator must have the same number of qubits as the outer code."
raise InvalidStabilizerCodeError(msg)
concatenated = SymplecticVector.zeros(self.n)
phase = 0
offset = 0
for i in range(self.outer_code.n):
c = self.inner_codes[i]
new_offset = offset + c.n
assert c.x_logicals is not None
assert c.z_logicals is not None
if p[i] == "X":
concatenated[offset:new_offset] = c.x_logicals[0].x_part()
concatenated[offset + self.n : new_offset + self.n] = c.x_logicals[0].z_part()
phase += c.x_logicals[0].phase
elif p[i] == "Z":
concatenated[offset:new_offset] = c.z_logicals[0].x_part()
concatenated[offset + self.n : new_offset + self.n] = c.z_logicals[0].z_part()
phase += c.z_logicals[0].phase

elif p[i] == "Y":
concatenated[offset:new_offset] = c.x_logicals[0].x_part ^ c.z_logicals[0].x_part()
concatenated[offset + self.n : new_offset + self.n] = c.x_logicals[0].z_part ^ c.z_logicals[0].z_part()
phase += c.x_logicals[0].phase + c.z_logicals[0].phase

offset = new_offset
return Pauli(concatenated, phase)


# def _valid_logicals(lst: list[StabilizerTableau | None]) -> TypeGuard[list[StabilizerTableau]]:
# return None not in lst
Fixed Show fixed Hide fixed


class ConcatenatedCSSCode(ConcatenatedCode, CSSCode):
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
"""A concatenated CSS code."""

def __init__(self, outer_code: CSSCode, inner_codes: CSSCode | Sequence[CSSCode]) -> None:
"""Initialize a concatenated CSS code.

Args:
outer_code: The outer code.
inner_codes: The inner code. If a list of codes is provided, the qubits of the outer code are encoded by the different inner codes in the list.
"""
self.outer_code = outer_code
if isinstance(inner_codes, list):
self.inner_codes = inner_codes
else:
self.inner_codes = [inner_codes] * outer_code.n
if not all(code.k == 1 for code in self.inner_codes):
msg = "The inner codes must be CSS codes with a single logical qubit."
raise InvalidStabilizerCodeError(msg)

self.n = sum(code.n for code in self.inner_codes)
hx = np.array([self._outer_checks_to_physical(check, "X") for check in outer_code.Hx], dtype=np.int8)
hz = np.array([self._outer_checks_to_physical(check, "Z") for check in outer_code.Hz], dtype=np.int8)
d = min(code.distance * outer_code.distance for code in self.inner_codes)
CSSCode.__init__(self, d, hx, hz)

def _outer_checks_to_physical(self, check: npt.NDArray[np.int8], operator: str) -> npt.NDArray[np.int8]:
"""Convert a check operator on the outer code to the operator on the concatenated code.

Args:
check: The check operator.
operator: The type of operator to be converted. Either 'X' or 'Z'.

Returns:
The check operator on the physical qubits.
"""
if check.shape[0] != self.outer_code.n:
msg = "The check operator must have the same number of qubits as the outer code."
raise InvalidStabilizerCodeError(msg)
concatenated = np.zeros((self.n), dtype=np.int8)
offset = 0
for i in range(self.outer_code.n):
c = self.inner_codes[i]
new_offset = offset + c.n
if check[i] == 1:
logical = c.Lx if operator == "X" else c.Lz
concatenated[offset:new_offset] = logical
offset = new_offset
return concatenated
22 changes: 20 additions & 2 deletions src/mqt/qecc/codes/css_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
import numpy as np
from ldpc import mod2

from .pauli import StabilizerTableau
from .stabilizer_code import StabilizerCode

if TYPE_CHECKING: # pragma: no cover
import numpy.typing as npt


class CSSCode(StabilizerCode):

Check warning

Code scanning / CodeQL

`__eq__` not overridden when adding attributes

The class 'CSSCode' does not override ['__eq__'](1), but adds the new attribute [Hx](2). The class 'CSSCode' does not override ['__eq__'](1), but adds the new attribute [Hz](3). The class 'CSSCode' does not override ['__eq__'](1), but adds the new attribute [Lx](4). The class 'CSSCode' does not override ['__eq__'](1), but adds the new attribute [Lz](5). The class 'CSSCode' does not override ['__eq__'](1), but adds the new attribute [n](6). The class 'CSSCode' does not override ['__eq__'](1), but adds the new attribute [Hx](7). The class 'CSSCode' does not override ['__eq__'](1), but adds the new attribute [Hx](8). The class 'CSSCode' does not override ['__eq__'](1), but adds the new attribute [n](9). The class 'CSSCode' does not override ['__eq__'](1), but adds the new attribute [Hz](10). The class 'CSSCode' does not override ['__eq__'](1), but adds the new attribute [Hz](11). The class 'CSSCode' does not override ['__eq__'](1), but adds the new attribute [distance](12). The class 'CSSCode' does not override ['__eq__'](1), but adds the new attribute [x_distance](13). The class 'CSSCode' does not override ['__eq__'](1), but adds the new attribute [z_distance](14). The class 'CSSCode' does not override ['__eq__'](1), but adds the new attribute [Lx](15). The class 'CSSCode' does not override ['__eq__'](1), but adds the new attribute [Lz](16).
"""A class for representing CSS codes."""

def __init__(
Expand All @@ -24,19 +25,31 @@
Hz: npt.NDArray[np.int8] | None = None, # noqa: N803
x_distance: int | None = None,
z_distance: int | None = None,
n: int | None = None,
) -> None:
"""Initialize the code."""
if Hx is None and Hz is None:
if n is None:
msg = "If no check matrices are provided, the code size must be specified."
raise InvalidCSSCodeError(msg)
self.Hx = np.zeros((0, n), dtype=np.int8)
self.Hz = np.zeros((0, n), dtype=np.int8)
self.Lx = np.eye(n, dtype=np.int8)
self.Lz = np.eye(n, dtype=np.int8)
super().__init__([], 1, n=n)
return

self._check_valid_check_matrices(Hx, Hz)

if Hx is None:
assert Hz is not None
self.n = Hz.shape[1]

Check warning

Code scanning / CodeQL

Overwriting attribute in super-class or sub-class

Assignment overwrites attribute n, which was previously defined in subclass [ConcatenatedCSSCode](1).
self.Hx = np.zeros((0, self.n), dtype=np.int8)
else:
self.Hx = Hx
if Hz is None:
assert Hx is not None
self.n = Hx.shape[1]

Check warning

Code scanning / CodeQL

Overwriting attribute in super-class or sub-class

Assignment overwrites attribute n, which was previously defined in subclass [ConcatenatedCSSCode](1).
self.Hz = np.zeros((0, self.n), dtype=np.int8)
else:
self.Hz = Hz
Expand All @@ -46,8 +59,8 @@

x_padded = np.hstack([self.Hx, z_padding])
z_padded = np.hstack([x_padding, self.Hz])
phases = np.zeros((x_padded.shape[0] + z_padded.shape[0], 1), dtype=np.int8)
super().__init__(np.hstack((np.vstack((x_padded, z_padded)), phases)), distance)
phases = np.zeros((x_padded.shape[0] + z_padded.shape[0]), dtype=np.int8)
super().__init__(StabilizerTableau(np.vstack((x_padded, z_padded)), phases), distance)

self.distance = distance
self.x_distance = x_distance if x_distance is not None else distance
Expand Down Expand Up @@ -157,6 +170,11 @@
msg = "The check matrices must be orthogonal"
raise InvalidCSSCodeError(msg)

@classmethod
def get_trivial_code(cls, n: int) -> CSSCode:
"""Return the trivial code."""
return cls(1, None, None, n=n)

@staticmethod
def from_code_name(code_name: str, distance: int | None = None) -> CSSCode:
r"""Return CSSCode object for a known code.
Expand Down
Loading
Loading