-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Extend functionality for working with codes. (#288)
## Description This PR extends QECCs capabilities for working with stabilizer codes. The following features are planned: - [x] Refactor Pauli operator and symplectic vector functionality - [ ] Add more codes and unify their creation - [x] Quantum Hamming Codes - [ ] Bring Code - [x] Many-Hypercube Code - [x] Add support for code concatenation ## Checklist: <!--- This checklist serves as a reminder of a couple of things that ensure your pull request will be merged swiftly. --> - [x] The pull request only contains commits that are related to it. - [x] I have added appropriate tests and documentation. - [x] I have made sure that all CI jobs on GitHub pass. - [x] The pull request introduces no new warnings and follows the project's style guidelines. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
- Loading branch information
1 parent
ef25257
commit 0880e37
Showing
10 changed files
with
958 additions
and
208 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
"""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): | ||
"""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 | ||
if isinstance(inner_code, list): | ||
self.inner_codes = inner_code | ||
else: | ||
self.inner_codes = [inner_code] * outer_code.n | ||
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) | ||
generators = [self._outer_pauli_to_physical(p) for p in outer_code.generators] | ||
|
||
x_logicals = None | ||
z_logicals = None | ||
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) | ||
StabilizerCode.__init__(self, generators, d, x_logicals, z_logicals) | ||
|
||
def __eq__(self, other: object) -> bool: | ||
"""Check if two concatenated codes are equal.""" | ||
if not isinstance(other, ConcatenatedCode): | ||
return NotImplemented | ||
return self.outer_code == other.outer_code and all( | ||
c1 == c2 for c1, c2 in zip(self.inner_codes, other.inner_codes) | ||
) | ||
|
||
def __hash__(self) -> int: | ||
"""Compute the hash of the concatenated code.""" | ||
return hash((self.outer_code, tuple(self.inner_codes))) | ||
|
||
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 | ||
|
||
|
||
class ConcatenatedCSSCode(ConcatenatedCode, CSSCode): | ||
"""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, CSSCode): | ||
inner_codes = [inner_codes] * outer_code.n | ||
|
||
if not all(code.k == 1 for code in inner_codes): | ||
msg = "The inner codes must be CSS codes with a single logical qubit." | ||
raise InvalidStabilizerCodeError(msg) | ||
|
||
ConcatenatedCode.__init__(self, outer_code, 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 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 |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
"""Constructions of various known stabilizer codes.""" | ||
|
||
from __future__ import annotations | ||
|
||
from typing import TYPE_CHECKING | ||
|
||
import numpy as np | ||
|
||
from .css_code import CSSCode | ||
|
||
if TYPE_CHECKING: | ||
import numpy.typing as npt | ||
|
||
|
||
def construct_quantum_hamming_code(r: int) -> CSSCode: | ||
"""Return the [[2^r, 2^r-r-1, 3]] quantum Hamming code.""" | ||
h = _hamming_code_checks(r) | ||
return CSSCode(3, h, h) | ||
|
||
|
||
def construct_iceberg_code(m: int) -> CSSCode: | ||
"""Return the [[2m, 2m-2, 2]] Iceberg code. | ||
The Iceberg code is a CSS code with stabilizer generators X^2m and Z^2m. | ||
https://errorcorrectionzoo.org/c/iceberg | ||
""" | ||
n = 2 * m | ||
h = np.array([[1] * n], dtype=np.int8) | ||
return CSSCode(2, h, h) | ||
|
||
|
||
def construct_many_hypercube_code(level: int) -> CSSCode: | ||
"""Return the [[6^l, 4^l, 2^l]] level l many-hypercube code (https://arxiv.org/abs/2403.16054). | ||
This code is obtained by (l-1)-fold concatenation of the [[6,4,2]] iceberg code with itself. | ||
""" | ||
code = construct_iceberg_code(3) | ||
|
||
for _ in range(1, level): | ||
sx = np.hstack([code.Lx] * 6, dtype=np.int8) | ||
sx_rem = np.kron(np.eye(6, dtype=np.int8), code.Hx) | ||
sx = np.vstack((sx, sx_rem), dtype=np.int8) | ||
sz = sx | ||
code = CSSCode(code.distance * 2, sx, sz) | ||
return code | ||
|
||
|
||
def _hamming_code_checks(r: int) -> npt.NDArray[np.int8]: | ||
"""Return the check matrix for the [2^r-1, 2^r-r-1, 3] Hamming code.""" | ||
n = 2**r - 1 | ||
h = np.zeros((r, n), dtype=int) | ||
# columns are all binary strings up to 2^r | ||
for i in range(1, n + 1): | ||
h[:, i - 1] = np.array([int(x) for x in f"{i:b}".zfill(r)]) | ||
|
||
return h |
This file contains 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
File renamed without changes.
File renamed without changes.
Oops, something went wrong.