qldpc.codes package
Submodules
qldpc.codes.classical module
Classical error-correcting codes
Copyright 2023 The qLDPC Authors and Infleqtion Inc.
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
- class qldpc.codes.classical.BCHCode(bits: int, dimension: int)[source]
Bases:
ClassicalCodeClassical binary BCH code.
Source: https://galois.readthedocs.io/en/v0.3.8/api/galois.BCH/ References: - https://errorcorrectionzoo.org/c/bch - https://www.cs.cmu.edu/~venkatg/teaching/codingtheory/notes/notes6.pdf
- class qldpc.codes.classical.HammingCode(rank: int, field: int | None = None)[source]
Bases:
ClassicalCodeClassical Hamming code.
- class qldpc.codes.classical.ReedMullerCode(order: int, size: int, field: int | None = None)[source]
Bases:
ClassicalCodeClassical Reed-Muller code.
References: - https://errorcorrectionzoo.org/c/reed_muller - https://feog.github.io/10-coding.pdf
- class qldpc.codes.classical.ReedSolomonCode(bits: int, dimension: int)[source]
Bases:
ClassicalCodeClassical Reed-Solomon code.
Source: https://galois.readthedocs.io/en/v0.3.8/api/galois.ReedSolomon/ References: - https://errorcorrectionzoo.org/c/reed_solomon - https://www.cs.cmu.edu/~venkatg/teaching/codingtheory/notes/notes6.pdf
- class qldpc.codes.classical.RepetitionCode(bits: int, field: int | None = None)[source]
Bases:
ClassicalCodeClassical repetition code.
- class qldpc.codes.classical.RingCode(bits: int, field: int | None = None)[source]
Bases:
ClassicalCodeClassical ring code: repetition code with periodic boundary conditions.
- class qldpc.codes.classical.TannerCode(subgraph: Graph, subcode: ClassicalCode)[source]
Bases:
ClassicalCodeClassical Tanner code, as described in DOI:10.1109/TIT.1981.1056404.
A Tanner code T(G,C) is constructed from: [1] A bipartite “half-regular” graph G. That is, a graph…
… with two sets of nodes, V and W. … in which all nodes in V have degree n.
[2] A classical code C on n bits.
For convenience, we make G directed, with edges directed from V to W. The node sets V and W can then be identified, respectively, by the sources and sinks of G.
The Tanner code T(G,C) is defined on |W| bits. A |W|-bit string x is a code word of T(G,C) iff, for every node v in V, the bits of x incident to v are a code word of C.
This construction requires an ordering the edges E(v) adjacent to each vertex v. This class sorts E(v) by the value of the “sort” attribute attached to each edge. If there is no “sort” attribute, its value is treated as corresponding neighbor of v.
Tanner codes can similarly be defined on regular (undirected) graphs G’ = (V’,E’) by placing checks on V’ and bits on E’.
Notes: - If the subcode C has m checks, its parity matrix has shape (m,n). - The code T(G,C) has |W| bits and |V|m checks.
- classmethod as_directed_subgraph(subgraph: Graph) DiGraph[source]
Convert an undirected graph for a Tanner code into a directed graph for the same code.
- subcode: ClassicalCode
- subgraph: DiGraph
qldpc.codes.common module
General error-correcting code classes and methods
Copyright 2023 The qLDPC Authors and Infleqtion Inc.
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
- class qldpc.codes.common.AbstractCode(matrix: AbstractCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], field: int | None = None)[source]
Bases:
ABCTemplate class for error-correcting codes.
- property field: type[FieldArray]
Base field over which this code is defined.
- property field_name: str
The name of the base field of this code.
- property graph: DiGraph
Tanner graph of this code.
- abstract classmethod graph_to_matrix(graph: DiGraph) FieldArray[source]
Convert a Tanner graph into a parity check matrix.
- property matrix: FieldArray
Parity check matrix of this code.
- abstract classmethod matrix_to_graph(matrix: ndarray[Any, dtype[int64]] | Sequence[Sequence[int]]) DiGraph[source]
Convert a parity check matrix into a Tanner graph.
- property name: str
The name of this code.
- property rank: int
Rank of this code’s parity check matrix.
Equivalently, the number of linearly independent parity checks in this code.
- class qldpc.codes.common.CSSCode(code_x: ClassicalCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], code_z: ClassicalCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], field: int | None = None, *, conjugate: slice | Sequence[int] | None = (), promise_balanced_codes: bool = False, skip_validation: bool = False)[source]
Bases:
QuditCodeCSS qudit code, with separate X-type and Z-type parity checks.
In order for the X-type and Z-type parity checks to be “compatible”, the X-type stabilizers must commute with the Z-type stabilizers. Mathematically, this requirement can be written as
H_x @ H_z.T == 0,
where H_x and H_z are, respectively, the parity check matrices of the classical codes that define the X-type and Z-type stabilizers of the CSS code. Note that H_x witnesses Z-type errors and H_z witnesses X-type errors.
The full parity check matrix of a CSSCode is ⌈ 0 , H_z ⌉ ⌊ H_x, 0 ⌋.
- code_x: ClassicalCode
- code_z: ClassicalCode
- property conjugated: slice | Sequence[int]
Which qudits are conjugated? Conjugated qudits swap their X and Z operators.
- property dimension: int
Number of logical qudits encoded by this code.
- get_code_params(*, bound: int | bool | None = None, **decoder_args: object) tuple[int, int, int | float][source]
Compute the parameters of this code: [[n,k,d]].
Here: - n is the number of data qudits - k is the number of encoded (“logical”) qudits - d is the code distance
Keyword arguments are passed to the calculation of code distance.
- get_distance(pauli: Literal[Pauli.X, Pauli.Z] | None = None, *, bound: int | bool | None = None, vector: Sequence[int] | ndarray[Any, dtype[int64]] | None = None, **decoder_args: object) int | float[source]
Compute (or upper bound) the minimal weight of a nontrivial logical operator.
If bound is None, compute an exact code distance by brute force. Otherwise, compute an upper bound using a randomized algorithm described in arXiv:2308.07915, minimizing over bound random trials. For a detailed explanation, see CSSCode.get_one_distance_bound.
If provided a vector, compute the minimum Hamming distance between this vector and a (possibly trivial) X-type or Z-type logical operator, as applicable.
Additional arguments, if applicable, are passed to a decoder in CSSCode.get_one_distance_bound.
- get_distance_bound(pauli: Literal[Pauli.X, Pauli.Z] | None = None, num_trials: int = 1, *, vector: Sequence[int] | ndarray[Any, dtype[int64]] | None = None, **decoder_args: object) int | float[source]
Compute an upper bound on code distance by minimizing many individual upper bounds.
If provided a vector, compute the minimum Hamming distance between this vector and a (possibly trivial) X-type or Z-type logical operator, as applicable.
Additional arguments, if applicable, are passed to a decoder in CSSCode.get_one_distance_bound.
- get_distance_exact(pauli: Literal[Pauli.X, Pauli.Z] | None, *, vector: Sequence[int] | ndarray[Any, dtype[int64]] | None = None) int | float[source]
Compute the minimal weight of a nontrivial code word by brute force.
If provided a vector, compute the minimum Hamming distance between this vector and a (possibly trivial) X-type or Z-type logical operator, as applicable.
- get_logical_ops(pauli: Literal[Pauli.X, Pauli.Z] | None = None) FieldArray[source]
Complete basis of nontrivial X-type and Z-type logical operators for this code.
Logical operators are represented by a three-dimensional array logical_ops with dimensions (2, k, n), where k and n are respectively the numbers of logical and physical qudits in this code. The bitstring logical_ops[0, 4, :], for example, indicates the support (i.e., the physical qudits addressed nontrivially) by the logical Pauli-X operator on logical qudit 4.
If passed a pauli operator (Pauli.X or Pauli.Z), return the two-dimensional array of logical operators of the specified type.
Logical operators are constructed using the method described in Section 4.1 of Gottesman’s thesis (arXiv:9705052), slightly modified for qudits.
- get_one_distance_bound(pauli: Literal[Pauli.X, Pauli.Z] | None = None, *, vector: Sequence[int] | ndarray[Any, dtype[int64]] | None = None, **decoder_args: object) int | float[source]
Compute a single upper bound on code distance.
If provided a vector, compute the minimum Hamming distance between this vector and a (possibly trivial) X-type or Z-type logical operator, as applicable.
Additional arguments, if applicable, are passed to a decoder.
This method uses a randomized algorithm described in arXiv:2308.07915, and also below.
For ease of language, we henceforth assume (without loss of generality) that we are computing an X-distance.
Pick a random Z-type logical operator Z(w_z) whose support is indicated by the bistring w_z. We now wish to find a low-weight Pauli-X string X(w_x) that
has a trivial syndrome, and
anti-commutes with Z(w_z),
which together would imply that X(w_x) is a nontrivial X-type logical operator. Mathematically, these conditions are equivalent to requiring that
H_z @ w_x = 0, and
w_z @ w_x = 1,
where H_z is the parity check matrix of the Z-type subcode that witnesses X-type errors.
- Conditions (a) and (b) can be combined into the single block-matrix equation
⌈ H_z ⌉ ⌈ 0 ⌉ ⌊ w_z.T ⌋ @ w_x = ⌊ 1 ⌋,
where the “0” on the top right is interpreted as a zero vector. This equation can be solved by decoding the syndrome [ 0, 0, …, 0, 1 ].T for the parity check matrix [ H_z.T, w_z ].T.
We solve the above decoding problem with a decoder in decode. If the decoder fails to find a solution, try again with a new random operator Z(w_z). If the decoder succeeds in finding a solution w_x, this solution corresponds to a logical X-type operator X(w_x) – and presumably one of low Hamming weight, since decoders try to find low-weight solutions. Return the Hamming weight |w_x|.
- get_random_logical_op(pauli: Literal[Pauli.X, Pauli.Z], *, ensure_nontrivial: bool = False, seed: int | None = None) FieldArray[source]
Return a random logical operator of a given type.
A random logical operator may be trivial, which is to say that it may be equal to the identity modulo stabilizers. If ensure_nontrivial is True, ensure that the logical operator we return is nontrivial.
- property matrix: FieldArray
Overall parity check matrix.
- property matrix_x: FieldArray
X-type parity checks.
- property matrix_z: FieldArray
Z-type parity checks.
- property num_checks: int
Number of parity checks in this code.
- property num_checks_x: int
Number of X-type parity checks in this code.
- property num_checks_z: int
Number of X-type parity checks in this code.
- property num_qudits: int
Number of data qudits in this code.
- reduce_logical_op(pauli: Literal[Pauli.X, Pauli.Z], logical_index: int, **decoder_args: object) None[source]
Reduce the weight of a logical operator.
A minimal-weight logical operator is found by enforcing that it has a trivial syndrome, and that it commutes with all logical operators except its dual. This is essentially the same method as that used in CSSCode.get_one_distance_bound.
- class qldpc.codes.common.ClassicalCode(matrix: AbstractCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], field: int | None = None)[source]
Bases:
AbstractCodeClassical linear error-correcting code over a finite field F_q.
A classical binary code C = {x} is a set of vectors x (with entries in F_q) called code words. We consider only linear codes, for which any linear combination of code words is also code word.
Operationally, we define a classical code by a parity check matrix H with dimensions (num_checks, num_bits). Each row of H represents a linear constraint (a “check”) that code words must satisfy. A vector x is a code word iff H @ x = 0.
- property dimension: int
The number of logical bits encoded by this code.
- dual() ClassicalCode[source]
Dual to this code.
The dual code ~C is the set of bitstrings orthogonal to C: ~C = { x : x @ y = 0 for all y in C }. The parity check matrix of ~C is equal to the generator of C.
- classmethod equiv(code_a: ClassicalCode, code_b: ClassicalCode) bool[source]
Test equivalence between two classical codes.
Two classical codes are equivalent if they have the same code words. Equivalently, codes C_a and C_b are equivalent if they contain each other, C_a ⊆ C_b and C_b ⊆ C_a.
- classmethod from_generator(generator: ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], field: int | None = None) ClassicalCode[source]
Construct a ClassicalCode from a generator matrix.
- classmethod from_name(name: str) ClassicalCode[source]
Named code in the GAP computer algebra system.
- property generator: FieldArray
a matrix whose rows for a basis for code words.
- Type:
Generator of this code
- get_code_params(*, bound: int | bool | None = None, **decoder_args: object) tuple[int, int, int | float][source]
Compute the parameters of this code: [n,k,d].
Here: - n is the number of data bits - k is the number of encoded (“logical”) bits - d is the code distance
Keyword arguments are passed to the calculation of code distance.
- get_distance(*, bound: int | bool | None = None, vector: Sequence[int] | ndarray[Any, dtype[int64]] | None = None, **decoder_args: object) int | float[source]
Compute (or upper bound) the minimal weight of a nontrivial code word.
If passed a vector, compute the minimal Hamming distance between the vector and a code word.
Additional arguments, if applicable, are passed to a decoder in ClassicalCode.get_one_distance_bound.
- get_distance_bound(num_trials: int = 1, *, vector: Sequence[int] | ndarray[Any, dtype[int64]] | None = None, **decoder_args: object) int | float[source]
Compute an upper bound on code distance by minimizing many individual upper bounds.
If passed a vector, compute the minimal Hamming distance between the vector and a code word.
Additional arguments, if applicable, are passed to a decoder in ClassicalCode.get_one_distance_bound.
- get_distance_exact(*, vector: Sequence[int] | ndarray[Any, dtype[int64]] | None = None) int | float[source]
Compute the minimal weight of a nontrivial code word by brute force.
If passed a vector, compute the minimal Hamming distance between the vector and a code word.
- get_one_distance_bound(*, vector: Sequence[int] | ndarray[Any, dtype[int64]] | None = None, **decoder_args: object) int | float[source]
Compute a single upper bound on code distance.
The code distance is the minimal Hamming distance between two code words, or equivalently the minimal Hamming weight of a nonzero code word. To find a minimal nonzero code word we decode a trivial (all-0) syndrome, but enforce that the code word has nonzero overlap with a random word, which excludes the all-0 word as a candidate.
If passed a vector, compute the minimal Hamming distance between the vector and a code word. Equivalently, we can interpret the given vector as an error, and find a minimal-weight correction from decoding the syndrome induced by this vector.
Additional arguments, if applicable, are passed to a decoder.
- get_random_word(*, seed: int | None = None) FieldArray[source]
Random code word: a sum of all generating words with random field coefficients.
- classmethod graph_to_matrix(graph: DiGraph) FieldArray[source]
Convert a Tanner graph into a parity check matrix.
- classmethod matrix_to_graph(matrix: ndarray[Any, dtype[int64]] | Sequence[Sequence[int]]) DiGraph[source]
Convert a parity check matrix H into a Tanner graph.
The Tanner graph is a bipartite graph with (num_checks, num_bits) vertices, respectively identified with the checks and bits of the code. The check vertex c and the bit vertex b share an edge iff c addresses b; that is, edge (c, b) is in the graph iff H[c, b] != 0.
- property num_bits: int
Number of data bits in this code.
- property num_checks: int
Number of check bits in this code.
- puncture(*bits: int) ClassicalCode[source]
Delete the specified bits from a code.
To delete bits from the code, we remove the corresponding columns from its generator matrix.
- classmethod random(bits: int, checks: int, field: int | None = None, *, seed: int | None = None) ClassicalCode[source]
Construct a random linear code with the given number of bits and checks.
Reject any code with trivial checks or unchecked bits, identified by an all-zero row or column in the code’s parity check matrix.
- shorten(*bits: int) ClassicalCode[source]
Shorten a code to the words that are zero on the specified bits, and delete those bits.
To shorten a code on a given bit, we: - move the bit to the first position, - row-reduce the generator matrix into the form [ identity_matrix, other_stuff ], and - delete the first row and column from the generator matrix.
- classmethod tensor_product(code_a: ClassicalCode, code_b: ClassicalCode) ClassicalCode[source]
Tensor product C_a ⨂ C_b of two codes C_a and C_b.
Let G_a and G_b respectively denote the generators C_a and C_b. Definition: C_a ⨂ C_b is the code whose generators are G_a ⨂ G_b.
Observation: G_a ⨂ G_b is the check matrix of ~(C_a ⨂ C_b). We therefore construct ~(C_a ⨂ C_b) and return its dual ~~(C_a ⨂ C_b) = C_a ⨂ C_b.
- class qldpc.codes.common.QuditCode(matrix: AbstractCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], field: int | None = None, *, conjugate: slice | Sequence[int] | None = ())[source]
Bases:
AbstractCodeQuantum stabilizer code for Galois qudits, with dimension q = p^m for prime p and integer m.
The parity check matrix of a QuditCode has dimensions (num_checks, 2 * num_qudits), and can be written as a block matrix in the form H = [H_x|H_z]. Each block has num_qudits columns.
The entries H_x[c, d] = r_x and H_z[c, d] = r_z iff check c addresses qudit d with the operator X(r_x) * Z(r_z), where r_x, r_z range over the base field, and X(r), Z(r) are generalized Pauli operators. Specifically: - X(r) = sum_{j=0}^{q-1} |j+r><j| is a shift operator, and - Z(r) = sum_{j=0}^{q-1} w^{j r} |j><j| is a phase operator, with w = exp(2 pi i / q).
Warning: here j, r, s, etc. not integers, but elements of the Galois field GF(q), which has different rules for addition and multiplication when q is not a prime number.
Helpful lecture by Gottesman: https://www.youtube.com/watch?v=JWg4zrNAF-g
- classmethod conjugate(matrix: ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], qudits: slice | Sequence[int]) ndarray[Any, dtype[int64]][source]
Apply local Fourier transforms to the given qudits.
This is equivalent to swapping X-type and Z-type operators.
- property dimension: int
The number of logical qudits encoded by this code.
- classmethod from_stabilizers(*stabilizers: str, field: int | None = None) QuditCode[source]
Construct a QuditCode from the provided stabilizers.
- get_logical_ops() FieldArray[source]
Complete basis of nontrivial logical operators for this code.
Logical operators are represented by a three-dimensional array logical_ops with dimensions (2, k, 2 * n), where k and n are respectively the numbers of logical and physical qudits in this code. The first axis is used to keep track of conjugate pairs of logical operators. The last axis is “doubled” to indicate whether a physical qudit is addressed by a physical X-type or Z-type operator.
Specifically, logical_ops[0, :, :] are “logical X-type” operators, which address at least one physical qudit by a physical X-type operator, and may additionally address physical qudits by physical Z-type operators. logical_ops[1, :, :] are logical Z-type operators that only address physical qudits by physical Z-type operators (which is a consequence of the way these operators are constructed here).
For example, if logical_ops[0, r, j] == 1 for j < n (j >= n), then the X-type logical operator for qudit r addresses physical qudit j with an X-type (Z-type) operator. The fact that logical operators come in conjugate pairs means that logical_ops[0, r, :] @ logical_ops[1, s, :] = int(r == s).
Logical operators are constructed using the method described in Section 4.1 of Gottesman’s thesis (arXiv:9705052), slightly modified for qudits.
- classmethod graph_to_matrix(graph: DiGraph) FieldArray[source]
Convert a Tanner graph into a parity check matrix.
- classmethod matrix_to_graph(matrix: ndarray[Any, dtype[int64]] | Sequence[Sequence[int]]) DiGraph[source]
Convert a parity check matrix into a Tanner graph.
- property num_checks: int
Number of parity checks (stabilizers) in this code.
- property num_qubits: int
Number of data qubits in this code.
- property num_qudits: int
Number of data qudits in this code.
- qldpc.codes.common.get_random_array(field: type[~galois.FieldArray], shape: int | tuple[int, ...], *, satisfy: ~collections.abc.Callable[[~galois.FieldArray], bool | ~numpy.bool_] = <function <lambda>>, seed: int | None = None) FieldArray[source]
Get a random array over a given finite field with a given shape.
If passed a condition that the array must satisfy, re-sample until the condition is met.
qldpc.codes.quantum module
Quantum error-correcting codes
Copyright 2023 The qLDPC Authors and Infleqtion Inc.
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
- class qldpc.codes.quantum.BBCode(orders: Sequence[int] | dict[Symbol, int], poly_a: Basic, poly_b: Basic, field: int | None = None, *, conjugate: bool = False)[source]
Bases:
TBCodeBivariate bicycle codes from arXiv:2308.07915.
A bivariate bicycle code is a CSS code with subcode parity check matrices - matrix_x = [A, B], and - matrix_z = [B.T, -A.T], where A = A_{ij} x^i y^j and B = B_{ij} x^i y^j are bivariate polynomials. Here: - A_{ij} and B_{ij} are scalar coefficients (over some finite field), - x generates a group of order R_x, and - y generates a group of order R_y.
A bivariate bicycle code is defined by… [1] two cyclic group orders, and [2] two sympy polynomials in two variables. By default, group orders are associated in lexicographic order with free variables of the polynomials. Group orders can also be assigned to variables explicitly with a dictionary.
- eval(expr: Integer | Symbol | Pow | Mul | Poly) Element[source]
Convert a sympy expression into an element of this code’s group algebra.
- get_check_shifts(plaquette_map: Callable[[int | Literal[Pauli.X, Pauli.Z]], ndarray[Any, dtype[int64]]], torus_shape: tuple[int, int], open_boundaries: bool = False) tuple[set[tuple[int, int]], set[tuple[int, int]]][source]
Get the relative positions of data qubits addressed by X-type and Z-type check qubits.
If open_boundaries=True, “fold” the torus for a qubit layout with open boundaries.
- get_exponents(expr: Integer | Symbol | Pow | Mul) tuple[int, int][source]
Extract the exponents from a term, for example converting x**2 * y**4 into (2, 4).
- classmethod get_qubit_coordinate_maps(sector: int | Literal[Pauli.X, Pauli.Z], torus_shape: tuple[int, int], open_boundaries: bool = False) tuple[ndarray[Any, dtype[int64]], ndarray[Any, dtype[int64]]][source]
Build arrays that map plaquette coordinates to qubit coordinates.
If open_boundaries=True, “fold” the torus for a qubit layout with open boundaries.
- get_toric_checks(plaquette_map: Callable[[int | Literal[Pauli.X, Pauli.Z]], ndarray[Any, dtype[int64]]], torus_shape: tuple[int, int]) tuple[ndarray[Any, dtype[int64]], ndarray[Any, dtype[int64]]][source]
Build X-type and Z-type parity check matrices for a toric layout.
- to_group_member(expr: Integer | Symbol | Pow | Mul) GroupMember[source]
Convert a sympy expression into an associated member of this code’s base group.
- property toric_layouts: Sequence[tuple[Callable[[int | Literal[Pauli.X, Pauli.Z]], ndarray[Any, dtype[int64]]], tuple[int, int]]]
Get a list of all toric layouts of this code.
- All qubits of this code can be organized into plaquettes of four qubits that look like:
L X Z R
where L and R are data qubits, and X and Z are check qubits. More specifically: - L and R data qubits are addressed by the left and right halves of matrix_x (or matrix_z). - X check qubits measure X-type parity checks, and are associated with rows of matrix_x. - Z check qubits measure Z-type parity checks, and are associated with rows of matrix_z. We identify sectors L, R, X, and Z respectively by the “index” 0, 1, Pauli.X, and Pauli.Z.
A toric layout is one in which plaquettes are arranged on a 2D grid with periodic boundary conditions (that is, a torus), and every check addresses all of its neighboring data qubits (and possibly other data qubits as well). Not every code is guaranteed to have a toric layout.
A toric layout is defined by: - a plaquette_map, which maps each qubit to a plaquette on the torus, and - a torus_shape, or the number of plaquettes along each axis of the torus.
The plaquette map is a function that maps a qubit sector (0, 1, Pauli.X, or Pauli.Z) to a 3D tensor. The tensor is populated by integers in such a way that
plaquette_map(sector)[i, j] = [a, b]
where (i, j) is the “bare” index of a qubit in the given sector, and (a, b) correspond to the location of a plaquette on a torus – the plaquette that the qubit is assigned to.
Bare indices are defined as follows. We can reshape (say) matrix_z into a tensor checks_z with shape (Rx, Ry, 2, Rx, Ry), which has the effect of: - expanding every integer row index into two integers that identify a Z-sector check qubit, - splitting the left and right halves of the matrix into a new 0/1 index that identifies an
L/R sector of the data qubits,
- expanding every integer “column” index within each data qubit sector into two integers
that identify a single data qubit.
For example, checks_z[i, j, 0, k, l] is nonzero iff the Z-sector check qubit (i, j) addresses the L-sector data qubit (k, l).
- class qldpc.codes.quantum.FiveQubitCode(*, conjugate: slice | Sequence[int] | None = ())[source]
Bases:
QuditCodeSmallest quantum error-correcting code.
- class qldpc.codes.quantum.GeneralizedSurfaceCode(size: int, dim: int, periodic: bool = False, field: int | None = None, *, conjugate: slice | Sequence[int] | None = ())[source]
Bases:
CSSCodeSurface or toric code defined on a multi-dimensional hypercubic lattice.
Reference: https://errorcorrectionzoo.org/c/higher_dimensional_surface
- class qldpc.codes.quantum.HGPCode(code_a: ClassicalCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], code_b: ClassicalCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]] | None = None, field: int | None = None, *, conjugate: bool = False)[source]
Bases:
CSSCodeHypergraph product (HGP) code.
A hypergraph product code AB is constructed from two classical codes, A and B.
Consider the following: - Code A has 3 data and 2 check bits. - Code B has 4 data and 3 check bits. We represent data bits/qudits by circles (○) and check bits/qudits by squares (□).
Denode the Tanner graph of code C by G_C. The nodes of G_AB can be arranged into a matrix. The rows of this matrix are labeled by nodes of G_A, and columns by nodes of G_B. The matrix of nodes in G_AB can thus be organized into four sectors:
- ――――――――――――――――――――――――――――――――――
- ○ ○ ○ ○ | □ □ □ ← nodes of G_B
――+―――――――――+―――――― ○ | ○ ○ ○ ○ | □ □ □ ○ | ○ ○ ○ ○ | □ □ □ ○ | ○ ○ ○ ○ | □ □ □ ――+―――――――――+―――――― □ | □ □ □ □ | ○ ○ ○ □ | □ □ □ □ | ○ ○ ○ ↑ nodes of G_A ――――――――――――――――――――――――――――――――――
We identify each sector by two bits. In the example above: - sector (0, 0) has 3×4=12 data qudits - sector (0, 1) has 3×3=9 check qudits - sector (1, 0) has 2×4=8 check qudits - sector (1, 1) has 2×3=6 data qudits
Edges in G_AB are inherited across rows/columns from G_A and G_B. For example, if rows r_1 and r_2 share an edge in G_A, then the same is true in every column of G_AB.
By default, the check qudits in sectors (1, 0) of G_AB measure X-type operators. Likewise with sector (0, 1) and Z-type operators. If a HGP is constructed with conjugate==True, then the types of operators addressing the nodes in sector (1, 1) are switched.
This class contains two equivalent constructions of an HGPCode: - A construction based on Tanner graphs (as discussed above). - A construction based on check matrices, taken from arXiv:2202.01702. The latter construction is less intuitive, but more efficient.
References: - https://errorcorrectionzoo.org/c/hypergraph_product - https://arxiv.org/abs/2202.01702 - https://www.youtube.com/watch?v=iehMcUr2saM - https://arxiv.org/abs/0903.0566 - https://arxiv.org/abs/1202.0928
- classmethod get_graph_product(graph_a: DiGraph, graph_b: DiGraph, *, conjugate: bool = False) DiGraph[source]
Hypergraph product of two Tanner graphs.
- classmethod get_matrix_product(matrix_a: ndarray[Any, dtype[int64 | object_]], matrix_b: ndarray[Any, dtype[int64 | object_]]) tuple[ndarray[Any, dtype[int64 | object_]], ndarray[Any, dtype[int64 | object_]]][source]
Hypergraph product of two parity check matrices.
- classmethod get_product_node_map(nodes_a: Collection[Node], nodes_b: Collection[Node]) dict[tuple[Node, Node], Node][source]
Map (dictionary) that re-labels nodes in the hypergraph product of two codes.
- classmethod get_sector(node_a: Node, node_b: Node) tuple[int, int][source]
Get the sector of a node in a graph product.
- sector_size: npt.NDArray[np.int_]
- class qldpc.codes.quantum.LPCode(protograph_a: ndarray[Any, dtype[object_]] | Sequence[Sequence[object]], protograph_b: ndarray[Any, dtype[object_]] | Sequence[Sequence[object]] | None = None, *, conjugate: bool = False)[source]
Bases:
CSSCodeLifted product (LP) code.
A lifted product code is essentially the same as a hypergraph product code, except that the parity check matrices are “protographs”, or matrices whose entries are members of a group algebra over a finite field F_q. Each of these entries can be “lifted” to a representation as orthogonal matrices over F_q, in which case the protograph is interpreted as a block matrix; this is called “lifting” the protograph.
Notes: - A lifted product code with protographs of size 1×1 is a two-block code (more specifically, a
two-block group-algebra code). If the base group of the protographs is a cyclic group, the resulting lifted product code is a generalized bicycle code.
- A lifted product code with protographs whose entries get lifted to 1×1 matrices is a
hypergraph product code built from the lifted protographs.
- One way to get an LPCode: take a classical code with parity check matrix H and multiply it by
a diagonal matrix D = diag(a_1, a_2, … a_n), where all {a_j} are elements of a group algebra. The protograph P = H @ D can then be used for one of the protographs of an LPCode.
References: - https://errorcorrectionzoo.org/c/lifted_product - https://arxiv.org/abs/2202.01702 - https://arxiv.org/abs/2012.04068 - https://arxiv.org/abs/2306.16400
- class qldpc.codes.quantum.QTCode(subset_a: Collection[GroupMember], subset_b: Collection[GroupMember], code_a: ClassicalCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], code_b: ClassicalCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]] | None = None, field: int | None = None, *, bipartite: bool = False, conjugate: slice | Sequence[int] | None = ())[source]
Bases:
CSSCodeQuantum Tanner code: a CSS code for qudits defined on the faces of a Cayley complex.
Altogether, a quantum Tanner code is defined by: - two symmetric (self-inverse) subsets A and B of a group G, and - two classical codes C_A and C_B, respectively with block lengths |A| and |B|.
The qudits of a quantum Tanner code live on the faces of a Cayley complex built out of A and B. Each face of the Cayley complex looks like:
g ―――――――――― gb
f(g,a,b) |ag ――――――――― agb
where (g,a,b) is an element of (G,A,B), and f(g,a,b) = {g, ab, gb, agb}. We define two (directed) subgraphs on the Cayley complex: - subgraph_x with edges ( g, f(g,a,b)), and - subgraph_z with edges (ag, f(g,a,b)).
The X-type parity checks of a quantum Tanner code are then given by the classical Tanner code on subgraph_x with subcode ~(C_A ⨂ C_B), where ~C is the dual code to C. Z-type parity checks are similarly given by the classical Tanner code on subgraph_z with subcode ~(~C_A ⨂ ~C_B).
Notes: - “Good” quantum Tanner code: projective special linear group and random classical codes.
References: - https://errorcorrectionzoo.org/c/quantum_tanner - https://arxiv.org/abs/2206.07571 - https://arxiv.org/abs/2202.13641
- complex: CayleyComplex
- classmethod get_subcodes(cayplex: CayleyComplex, code_a: ClassicalCode, code_b: ClassicalCode) tuple[TannerCode, TannerCode][source]
Get the classical Tanner subcodes of a quantum Tanner code.
- classmethod get_subgraphs(cayplex: CayleyComplex) tuple[DiGraph, DiGraph][source]
Build the subgraphs of the inner (classical) Tanner codes for a quantum Tanner code.
These subgraphs are defined using the faces of a Cayley complex. Each face looks like:
g ―――――――――― gb
f(g,a,b) |ag ――――――――― agb
where f(g,a,b) = {g, ab, gb, agb}. Specifically, the (directed) subgraphs are: - subgraph_x with edges ( g, f(g,a,b)), and - subgraph_z with edges (ag, f(g,a,b)). Classical Tanner codes on these subgraphs are used as to construct quantum Tanner code.
As a matter of practice, defining classical Tanner codes on subgraph_x and subgraph_z requires choosing an ordering on the edges incident to every source node of these graphs. If the group G is equipped with a total order, a natural ordering of edges incident to every source node is induced by assigning the label (a, b) to edge (g, f(g,a,b)). Consistency then requires that edge (ag, f(g,a,b)) has label (a^-1, b), as verified by defining g’ = ag and checking that f(g,a,b) = f(g’,a^-1,b).
- classmethod random(group: Group, code_a: ClassicalCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], code_b: ClassicalCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]] | None = None, field: int | None = None, *, bipartite: bool = False, conjugate: slice | Sequence[int] | None = (), one_subset: bool = False, seed: int | None = None) QTCode[source]
Construct a random quantum Tanner code from a base group and seed code(s).
If only one code C is provided, use its dual ~C for the second code.
- class qldpc.codes.quantum.SteaneCode(*, conjugate: slice | Sequence[int] | None = ())[source]
Bases:
CSSCodeSmallest quantum error-correcting CSS code.
- class qldpc.codes.quantum.SurfaceCode(rows: int, cols: int | None = None, rotated: bool = True, field: int | None = None, *, conjugate: bool = False)[source]
Bases:
CSSCodeThe one and only!
Actually, there are two variants: “ordinary” and “rotated” surface codes. The rotated code is more qubit-efficient.
If constructed with conjugate=True, every other qubit is Hadamard-transformed in a checkerboard pattern. The rotated surface code with conjugate=True is the XZZX code in arXiv:2009.07851.
- classmethod get_rotated_checks(rows: int, cols: int) tuple[ndarray[Any, dtype[int64]], ndarray[Any, dtype[int64]]][source]
Build X-sector and Z-sector parity check matrices.
Example 5x5 rotated surface code layout:
――― ―――
⋅ | | ⋅ |○―――○―――○―――○―――○――― | × | ⋅ | × | ⋅ | × |
―――○―――○―――○―――○―――○―――
× | ⋅ | × | ⋅ | × | ―――○―――○―――○―――○―――○――― | × | ⋅ | × | ⋅ | × | ―――○―――○―――○―――○―――○―――× | ⋅ | × | ⋅ | × | ―――○―――○―――○―――○―――○ | ⋅ | | ⋅ | ――― ―――Here: - Circles (○) denote data qubits (of which there are 5×5 = 25 total). - Tiles with a cross (×) denote X-type parity checks (12 total). - Tiles with a dot (⋅) denote Z-type parity checks (12 total).
- class qldpc.codes.quantum.TBCode(matrix_a: ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], matrix_b: ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], field: int | None = None, *, conjugate: slice | Sequence[int] = (), promise_balanced_codes: bool = False, skip_validation: bool = False)[source]
Bases:
CSSCodeTwo-block (TB) code.
A TBCode code is built out of two matrices A and B, which are combined as - matrix_x = [A, B], and - matrix_z = [B.T, -A.T], to form the parity check matrices of a CSSCode. If A and B commute, the parity check matrices matrix_x and matrix_z satisfy the requirements of a CSSCode.
Two-block codes constructed out of circulant matrices are known as generalized bicycle codes.
- class qldpc.codes.quantum.ToricCode(rows: int, cols: int | None = None, rotated: bool = True, field: int | None = None, *, conjugate: bool = False)[source]
Bases:
CSSCodeSurface code with periodic bounary conditions, encoding two logical qudits.
Reference: https://errorcorrectionzoo.org/c/surface
Module contents
- class qldpc.codes.BBCode(orders: Sequence[int] | dict[Symbol, int], poly_a: Basic, poly_b: Basic, field: int | None = None, *, conjugate: bool = False)[source]
Bases:
TBCodeBivariate bicycle codes from arXiv:2308.07915.
A bivariate bicycle code is a CSS code with subcode parity check matrices - matrix_x = [A, B], and - matrix_z = [B.T, -A.T], where A = A_{ij} x^i y^j and B = B_{ij} x^i y^j are bivariate polynomials. Here: - A_{ij} and B_{ij} are scalar coefficients (over some finite field), - x generates a group of order R_x, and - y generates a group of order R_y.
A bivariate bicycle code is defined by… [1] two cyclic group orders, and [2] two sympy polynomials in two variables. By default, group orders are associated in lexicographic order with free variables of the polynomials. Group orders can also be assigned to variables explicitly with a dictionary.
- eval(expr: Integer | Symbol | Pow | Mul | Poly) Element[source]
Convert a sympy expression into an element of this code’s group algebra.
- get_check_shifts(plaquette_map: Callable[[int | Literal[Pauli.X, Pauli.Z]], ndarray[Any, dtype[int64]]], torus_shape: tuple[int, int], open_boundaries: bool = False) tuple[set[tuple[int, int]], set[tuple[int, int]]][source]
Get the relative positions of data qubits addressed by X-type and Z-type check qubits.
If open_boundaries=True, “fold” the torus for a qubit layout with open boundaries.
- get_exponents(expr: Integer | Symbol | Pow | Mul) tuple[int, int][source]
Extract the exponents from a term, for example converting x**2 * y**4 into (2, 4).
- classmethod get_qubit_coordinate_maps(sector: int | Literal[Pauli.X, Pauli.Z], torus_shape: tuple[int, int], open_boundaries: bool = False) tuple[ndarray[Any, dtype[int64]], ndarray[Any, dtype[int64]]][source]
Build arrays that map plaquette coordinates to qubit coordinates.
If open_boundaries=True, “fold” the torus for a qubit layout with open boundaries.
- get_toric_checks(plaquette_map: Callable[[int | Literal[Pauli.X, Pauli.Z]], ndarray[Any, dtype[int64]]], torus_shape: tuple[int, int]) tuple[ndarray[Any, dtype[int64]], ndarray[Any, dtype[int64]]][source]
Build X-type and Z-type parity check matrices for a toric layout.
- to_group_member(expr: Integer | Symbol | Pow | Mul) GroupMember[source]
Convert a sympy expression into an associated member of this code’s base group.
- property toric_layouts: Sequence[tuple[Callable[[int | Literal[Pauli.X, Pauli.Z]], ndarray[Any, dtype[int64]]], tuple[int, int]]]
Get a list of all toric layouts of this code.
- All qubits of this code can be organized into plaquettes of four qubits that look like:
L X Z R
where L and R are data qubits, and X and Z are check qubits. More specifically: - L and R data qubits are addressed by the left and right halves of matrix_x (or matrix_z). - X check qubits measure X-type parity checks, and are associated with rows of matrix_x. - Z check qubits measure Z-type parity checks, and are associated with rows of matrix_z. We identify sectors L, R, X, and Z respectively by the “index” 0, 1, Pauli.X, and Pauli.Z.
A toric layout is one in which plaquettes are arranged on a 2D grid with periodic boundary conditions (that is, a torus), and every check addresses all of its neighboring data qubits (and possibly other data qubits as well). Not every code is guaranteed to have a toric layout.
A toric layout is defined by: - a plaquette_map, which maps each qubit to a plaquette on the torus, and - a torus_shape, or the number of plaquettes along each axis of the torus.
The plaquette map is a function that maps a qubit sector (0, 1, Pauli.X, or Pauli.Z) to a 3D tensor. The tensor is populated by integers in such a way that
plaquette_map(sector)[i, j] = [a, b]
where (i, j) is the “bare” index of a qubit in the given sector, and (a, b) correspond to the location of a plaquette on a torus – the plaquette that the qubit is assigned to.
Bare indices are defined as follows. We can reshape (say) matrix_z into a tensor checks_z with shape (Rx, Ry, 2, Rx, Ry), which has the effect of: - expanding every integer row index into two integers that identify a Z-sector check qubit, - splitting the left and right halves of the matrix into a new 0/1 index that identifies an
L/R sector of the data qubits,
- expanding every integer “column” index within each data qubit sector into two integers
that identify a single data qubit.
For example, checks_z[i, j, 0, k, l] is nonzero iff the Z-sector check qubit (i, j) addresses the L-sector data qubit (k, l).
- class qldpc.codes.BCHCode(bits: int, dimension: int)[source]
Bases:
ClassicalCodeClassical binary BCH code.
Source: https://galois.readthedocs.io/en/v0.3.8/api/galois.BCH/ References: - https://errorcorrectionzoo.org/c/bch - https://www.cs.cmu.edu/~venkatg/teaching/codingtheory/notes/notes6.pdf
- class qldpc.codes.CSSCode(code_x: ClassicalCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], code_z: ClassicalCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], field: int | None = None, *, conjugate: slice | Sequence[int] | None = (), promise_balanced_codes: bool = False, skip_validation: bool = False)[source]
Bases:
QuditCodeCSS qudit code, with separate X-type and Z-type parity checks.
In order for the X-type and Z-type parity checks to be “compatible”, the X-type stabilizers must commute with the Z-type stabilizers. Mathematically, this requirement can be written as
H_x @ H_z.T == 0,
where H_x and H_z are, respectively, the parity check matrices of the classical codes that define the X-type and Z-type stabilizers of the CSS code. Note that H_x witnesses Z-type errors and H_z witnesses X-type errors.
The full parity check matrix of a CSSCode is ⌈ 0 , H_z ⌉ ⌊ H_x, 0 ⌋.
- code_x: ClassicalCode
- code_z: ClassicalCode
- property conjugated: slice | Sequence[int]
Which qudits are conjugated? Conjugated qudits swap their X and Z operators.
- property dimension: int
Number of logical qudits encoded by this code.
- get_code_params(*, bound: int | bool | None = None, **decoder_args: object) tuple[int, int, int | float][source]
Compute the parameters of this code: [[n,k,d]].
Here: - n is the number of data qudits - k is the number of encoded (“logical”) qudits - d is the code distance
Keyword arguments are passed to the calculation of code distance.
- get_distance(pauli: Literal[Pauli.X, Pauli.Z] | None = None, *, bound: int | bool | None = None, vector: Sequence[int] | ndarray[Any, dtype[int64]] | None = None, **decoder_args: object) int | float[source]
Compute (or upper bound) the minimal weight of a nontrivial logical operator.
If bound is None, compute an exact code distance by brute force. Otherwise, compute an upper bound using a randomized algorithm described in arXiv:2308.07915, minimizing over bound random trials. For a detailed explanation, see CSSCode.get_one_distance_bound.
If provided a vector, compute the minimum Hamming distance between this vector and a (possibly trivial) X-type or Z-type logical operator, as applicable.
Additional arguments, if applicable, are passed to a decoder in CSSCode.get_one_distance_bound.
- get_distance_bound(pauli: Literal[Pauli.X, Pauli.Z] | None = None, num_trials: int = 1, *, vector: Sequence[int] | ndarray[Any, dtype[int64]] | None = None, **decoder_args: object) int | float[source]
Compute an upper bound on code distance by minimizing many individual upper bounds.
If provided a vector, compute the minimum Hamming distance between this vector and a (possibly trivial) X-type or Z-type logical operator, as applicable.
Additional arguments, if applicable, are passed to a decoder in CSSCode.get_one_distance_bound.
- get_distance_exact(pauli: Literal[Pauli.X, Pauli.Z] | None, *, vector: Sequence[int] | ndarray[Any, dtype[int64]] | None = None) int | float[source]
Compute the minimal weight of a nontrivial code word by brute force.
If provided a vector, compute the minimum Hamming distance between this vector and a (possibly trivial) X-type or Z-type logical operator, as applicable.
- get_logical_ops(pauli: Literal[Pauli.X, Pauli.Z] | None = None) FieldArray[source]
Complete basis of nontrivial X-type and Z-type logical operators for this code.
Logical operators are represented by a three-dimensional array logical_ops with dimensions (2, k, n), where k and n are respectively the numbers of logical and physical qudits in this code. The bitstring logical_ops[0, 4, :], for example, indicates the support (i.e., the physical qudits addressed nontrivially) by the logical Pauli-X operator on logical qudit 4.
If passed a pauli operator (Pauli.X or Pauli.Z), return the two-dimensional array of logical operators of the specified type.
Logical operators are constructed using the method described in Section 4.1 of Gottesman’s thesis (arXiv:9705052), slightly modified for qudits.
- get_one_distance_bound(pauli: Literal[Pauli.X, Pauli.Z] | None = None, *, vector: Sequence[int] | ndarray[Any, dtype[int64]] | None = None, **decoder_args: object) int | float[source]
Compute a single upper bound on code distance.
If provided a vector, compute the minimum Hamming distance between this vector and a (possibly trivial) X-type or Z-type logical operator, as applicable.
Additional arguments, if applicable, are passed to a decoder.
This method uses a randomized algorithm described in arXiv:2308.07915, and also below.
For ease of language, we henceforth assume (without loss of generality) that we are computing an X-distance.
Pick a random Z-type logical operator Z(w_z) whose support is indicated by the bistring w_z. We now wish to find a low-weight Pauli-X string X(w_x) that
has a trivial syndrome, and
anti-commutes with Z(w_z),
which together would imply that X(w_x) is a nontrivial X-type logical operator. Mathematically, these conditions are equivalent to requiring that
H_z @ w_x = 0, and
w_z @ w_x = 1,
where H_z is the parity check matrix of the Z-type subcode that witnesses X-type errors.
- Conditions (a) and (b) can be combined into the single block-matrix equation
⌈ H_z ⌉ ⌈ 0 ⌉ ⌊ w_z.T ⌋ @ w_x = ⌊ 1 ⌋,
where the “0” on the top right is interpreted as a zero vector. This equation can be solved by decoding the syndrome [ 0, 0, …, 0, 1 ].T for the parity check matrix [ H_z.T, w_z ].T.
We solve the above decoding problem with a decoder in decode. If the decoder fails to find a solution, try again with a new random operator Z(w_z). If the decoder succeeds in finding a solution w_x, this solution corresponds to a logical X-type operator X(w_x) – and presumably one of low Hamming weight, since decoders try to find low-weight solutions. Return the Hamming weight |w_x|.
- get_random_logical_op(pauli: Literal[Pauli.X, Pauli.Z], *, ensure_nontrivial: bool = False, seed: int | None = None) FieldArray[source]
Return a random logical operator of a given type.
A random logical operator may be trivial, which is to say that it may be equal to the identity modulo stabilizers. If ensure_nontrivial is True, ensure that the logical operator we return is nontrivial.
- property matrix: FieldArray
Overall parity check matrix.
- property matrix_x: FieldArray
X-type parity checks.
- property matrix_z: FieldArray
Z-type parity checks.
- property num_checks: int
Number of parity checks in this code.
- property num_checks_x: int
Number of X-type parity checks in this code.
- property num_checks_z: int
Number of X-type parity checks in this code.
- property num_qudits: int
Number of data qudits in this code.
- reduce_logical_op(pauli: Literal[Pauli.X, Pauli.Z], logical_index: int, **decoder_args: object) None[source]
Reduce the weight of a logical operator.
A minimal-weight logical operator is found by enforcing that it has a trivial syndrome, and that it commutes with all logical operators except its dual. This is essentially the same method as that used in CSSCode.get_one_distance_bound.
- class qldpc.codes.ClassicalCode(matrix: AbstractCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], field: int | None = None)[source]
Bases:
AbstractCodeClassical linear error-correcting code over a finite field F_q.
A classical binary code C = {x} is a set of vectors x (with entries in F_q) called code words. We consider only linear codes, for which any linear combination of code words is also code word.
Operationally, we define a classical code by a parity check matrix H with dimensions (num_checks, num_bits). Each row of H represents a linear constraint (a “check”) that code words must satisfy. A vector x is a code word iff H @ x = 0.
- property dimension: int
The number of logical bits encoded by this code.
- dual() ClassicalCode[source]
Dual to this code.
The dual code ~C is the set of bitstrings orthogonal to C: ~C = { x : x @ y = 0 for all y in C }. The parity check matrix of ~C is equal to the generator of C.
- classmethod equiv(code_a: ClassicalCode, code_b: ClassicalCode) bool[source]
Test equivalence between two classical codes.
Two classical codes are equivalent if they have the same code words. Equivalently, codes C_a and C_b are equivalent if they contain each other, C_a ⊆ C_b and C_b ⊆ C_a.
- classmethod from_generator(generator: ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], field: int | None = None) ClassicalCode[source]
Construct a ClassicalCode from a generator matrix.
- classmethod from_name(name: str) ClassicalCode[source]
Named code in the GAP computer algebra system.
- property generator: FieldArray
a matrix whose rows for a basis for code words.
- Type:
Generator of this code
- get_code_params(*, bound: int | bool | None = None, **decoder_args: object) tuple[int, int, int | float][source]
Compute the parameters of this code: [n,k,d].
Here: - n is the number of data bits - k is the number of encoded (“logical”) bits - d is the code distance
Keyword arguments are passed to the calculation of code distance.
- get_distance(*, bound: int | bool | None = None, vector: Sequence[int] | ndarray[Any, dtype[int64]] | None = None, **decoder_args: object) int | float[source]
Compute (or upper bound) the minimal weight of a nontrivial code word.
If passed a vector, compute the minimal Hamming distance between the vector and a code word.
Additional arguments, if applicable, are passed to a decoder in ClassicalCode.get_one_distance_bound.
- get_distance_bound(num_trials: int = 1, *, vector: Sequence[int] | ndarray[Any, dtype[int64]] | None = None, **decoder_args: object) int | float[source]
Compute an upper bound on code distance by minimizing many individual upper bounds.
If passed a vector, compute the minimal Hamming distance between the vector and a code word.
Additional arguments, if applicable, are passed to a decoder in ClassicalCode.get_one_distance_bound.
- get_distance_exact(*, vector: Sequence[int] | ndarray[Any, dtype[int64]] | None = None) int | float[source]
Compute the minimal weight of a nontrivial code word by brute force.
If passed a vector, compute the minimal Hamming distance between the vector and a code word.
- get_one_distance_bound(*, vector: Sequence[int] | ndarray[Any, dtype[int64]] | None = None, **decoder_args: object) int | float[source]
Compute a single upper bound on code distance.
The code distance is the minimal Hamming distance between two code words, or equivalently the minimal Hamming weight of a nonzero code word. To find a minimal nonzero code word we decode a trivial (all-0) syndrome, but enforce that the code word has nonzero overlap with a random word, which excludes the all-0 word as a candidate.
If passed a vector, compute the minimal Hamming distance between the vector and a code word. Equivalently, we can interpret the given vector as an error, and find a minimal-weight correction from decoding the syndrome induced by this vector.
Additional arguments, if applicable, are passed to a decoder.
- get_random_word(*, seed: int | None = None) FieldArray[source]
Random code word: a sum of all generating words with random field coefficients.
- classmethod graph_to_matrix(graph: DiGraph) FieldArray[source]
Convert a Tanner graph into a parity check matrix.
- classmethod matrix_to_graph(matrix: ndarray[Any, dtype[int64]] | Sequence[Sequence[int]]) DiGraph[source]
Convert a parity check matrix H into a Tanner graph.
The Tanner graph is a bipartite graph with (num_checks, num_bits) vertices, respectively identified with the checks and bits of the code. The check vertex c and the bit vertex b share an edge iff c addresses b; that is, edge (c, b) is in the graph iff H[c, b] != 0.
- property num_bits: int
Number of data bits in this code.
- property num_checks: int
Number of check bits in this code.
- puncture(*bits: int) ClassicalCode[source]
Delete the specified bits from a code.
To delete bits from the code, we remove the corresponding columns from its generator matrix.
- classmethod random(bits: int, checks: int, field: int | None = None, *, seed: int | None = None) ClassicalCode[source]
Construct a random linear code with the given number of bits and checks.
Reject any code with trivial checks or unchecked bits, identified by an all-zero row or column in the code’s parity check matrix.
- shorten(*bits: int) ClassicalCode[source]
Shorten a code to the words that are zero on the specified bits, and delete those bits.
To shorten a code on a given bit, we: - move the bit to the first position, - row-reduce the generator matrix into the form [ identity_matrix, other_stuff ], and - delete the first row and column from the generator matrix.
- classmethod tensor_product(code_a: ClassicalCode, code_b: ClassicalCode) ClassicalCode[source]
Tensor product C_a ⨂ C_b of two codes C_a and C_b.
Let G_a and G_b respectively denote the generators C_a and C_b. Definition: C_a ⨂ C_b is the code whose generators are G_a ⨂ G_b.
Observation: G_a ⨂ G_b is the check matrix of ~(C_a ⨂ C_b). We therefore construct ~(C_a ⨂ C_b) and return its dual ~~(C_a ⨂ C_b) = C_a ⨂ C_b.
- class qldpc.codes.FiveQubitCode(*, conjugate: slice | Sequence[int] | None = ())[source]
Bases:
QuditCodeSmallest quantum error-correcting code.
- class qldpc.codes.GeneralizedSurfaceCode(size: int, dim: int, periodic: bool = False, field: int | None = None, *, conjugate: slice | Sequence[int] | None = ())[source]
Bases:
CSSCodeSurface or toric code defined on a multi-dimensional hypercubic lattice.
Reference: https://errorcorrectionzoo.org/c/higher_dimensional_surface
- class qldpc.codes.HGPCode(code_a: ClassicalCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], code_b: ClassicalCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]] | None = None, field: int | None = None, *, conjugate: bool = False)[source]
Bases:
CSSCodeHypergraph product (HGP) code.
A hypergraph product code AB is constructed from two classical codes, A and B.
Consider the following: - Code A has 3 data and 2 check bits. - Code B has 4 data and 3 check bits. We represent data bits/qudits by circles (○) and check bits/qudits by squares (□).
Denode the Tanner graph of code C by G_C. The nodes of G_AB can be arranged into a matrix. The rows of this matrix are labeled by nodes of G_A, and columns by nodes of G_B. The matrix of nodes in G_AB can thus be organized into four sectors:
- ――――――――――――――――――――――――――――――――――
- ○ ○ ○ ○ | □ □ □ ← nodes of G_B
――+―――――――――+―――――― ○ | ○ ○ ○ ○ | □ □ □ ○ | ○ ○ ○ ○ | □ □ □ ○ | ○ ○ ○ ○ | □ □ □ ――+―――――――――+―――――― □ | □ □ □ □ | ○ ○ ○ □ | □ □ □ □ | ○ ○ ○ ↑ nodes of G_A ――――――――――――――――――――――――――――――――――
We identify each sector by two bits. In the example above: - sector (0, 0) has 3×4=12 data qudits - sector (0, 1) has 3×3=9 check qudits - sector (1, 0) has 2×4=8 check qudits - sector (1, 1) has 2×3=6 data qudits
Edges in G_AB are inherited across rows/columns from G_A and G_B. For example, if rows r_1 and r_2 share an edge in G_A, then the same is true in every column of G_AB.
By default, the check qudits in sectors (1, 0) of G_AB measure X-type operators. Likewise with sector (0, 1) and Z-type operators. If a HGP is constructed with conjugate==True, then the types of operators addressing the nodes in sector (1, 1) are switched.
This class contains two equivalent constructions of an HGPCode: - A construction based on Tanner graphs (as discussed above). - A construction based on check matrices, taken from arXiv:2202.01702. The latter construction is less intuitive, but more efficient.
References: - https://errorcorrectionzoo.org/c/hypergraph_product - https://arxiv.org/abs/2202.01702 - https://www.youtube.com/watch?v=iehMcUr2saM - https://arxiv.org/abs/0903.0566 - https://arxiv.org/abs/1202.0928
- code_x: ClassicalCode
- code_z: ClassicalCode
- classmethod get_graph_product(graph_a: DiGraph, graph_b: DiGraph, *, conjugate: bool = False) DiGraph[source]
Hypergraph product of two Tanner graphs.
- classmethod get_matrix_product(matrix_a: ndarray[Any, dtype[int64 | object_]], matrix_b: ndarray[Any, dtype[int64 | object_]]) tuple[ndarray[Any, dtype[int64 | object_]], ndarray[Any, dtype[int64 | object_]]][source]
Hypergraph product of two parity check matrices.
- classmethod get_product_node_map(nodes_a: Collection[Node], nodes_b: Collection[Node]) dict[tuple[Node, Node], Node][source]
Map (dictionary) that re-labels nodes in the hypergraph product of two codes.
- classmethod get_sector(node_a: Node, node_b: Node) tuple[int, int][source]
Get the sector of a node in a graph product.
- sector_size: npt.NDArray[np.int_]
- class qldpc.codes.HammingCode(rank: int, field: int | None = None)[source]
Bases:
ClassicalCodeClassical Hamming code.
- class qldpc.codes.LPCode(protograph_a: ndarray[Any, dtype[object_]] | Sequence[Sequence[object]], protograph_b: ndarray[Any, dtype[object_]] | Sequence[Sequence[object]] | None = None, *, conjugate: bool = False)[source]
Bases:
CSSCodeLifted product (LP) code.
A lifted product code is essentially the same as a hypergraph product code, except that the parity check matrices are “protographs”, or matrices whose entries are members of a group algebra over a finite field F_q. Each of these entries can be “lifted” to a representation as orthogonal matrices over F_q, in which case the protograph is interpreted as a block matrix; this is called “lifting” the protograph.
Notes: - A lifted product code with protographs of size 1×1 is a two-block code (more specifically, a
two-block group-algebra code). If the base group of the protographs is a cyclic group, the resulting lifted product code is a generalized bicycle code.
- A lifted product code with protographs whose entries get lifted to 1×1 matrices is a
hypergraph product code built from the lifted protographs.
- One way to get an LPCode: take a classical code with parity check matrix H and multiply it by
a diagonal matrix D = diag(a_1, a_2, … a_n), where all {a_j} are elements of a group algebra. The protograph P = H @ D can then be used for one of the protographs of an LPCode.
References: - https://errorcorrectionzoo.org/c/lifted_product - https://arxiv.org/abs/2202.01702 - https://arxiv.org/abs/2012.04068 - https://arxiv.org/abs/2306.16400
- class qldpc.codes.QTCode(subset_a: Collection[GroupMember], subset_b: Collection[GroupMember], code_a: ClassicalCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], code_b: ClassicalCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]] | None = None, field: int | None = None, *, bipartite: bool = False, conjugate: slice | Sequence[int] | None = ())[source]
Bases:
CSSCodeQuantum Tanner code: a CSS code for qudits defined on the faces of a Cayley complex.
Altogether, a quantum Tanner code is defined by: - two symmetric (self-inverse) subsets A and B of a group G, and - two classical codes C_A and C_B, respectively with block lengths |A| and |B|.
The qudits of a quantum Tanner code live on the faces of a Cayley complex built out of A and B. Each face of the Cayley complex looks like:
g ―――――――――― gb
f(g,a,b) |ag ――――――――― agb
where (g,a,b) is an element of (G,A,B), and f(g,a,b) = {g, ab, gb, agb}. We define two (directed) subgraphs on the Cayley complex: - subgraph_x with edges ( g, f(g,a,b)), and - subgraph_z with edges (ag, f(g,a,b)).
The X-type parity checks of a quantum Tanner code are then given by the classical Tanner code on subgraph_x with subcode ~(C_A ⨂ C_B), where ~C is the dual code to C. Z-type parity checks are similarly given by the classical Tanner code on subgraph_z with subcode ~(~C_A ⨂ ~C_B).
Notes: - “Good” quantum Tanner code: projective special linear group and random classical codes.
References: - https://errorcorrectionzoo.org/c/quantum_tanner - https://arxiv.org/abs/2206.07571 - https://arxiv.org/abs/2202.13641
- code_x: ClassicalCode
- code_z: ClassicalCode
- complex: CayleyComplex
- classmethod get_subcodes(cayplex: CayleyComplex, code_a: ClassicalCode, code_b: ClassicalCode) tuple[TannerCode, TannerCode][source]
Get the classical Tanner subcodes of a quantum Tanner code.
- classmethod get_subgraphs(cayplex: CayleyComplex) tuple[DiGraph, DiGraph][source]
Build the subgraphs of the inner (classical) Tanner codes for a quantum Tanner code.
These subgraphs are defined using the faces of a Cayley complex. Each face looks like:
g ―――――――――― gb
f(g,a,b) |ag ――――――――― agb
where f(g,a,b) = {g, ab, gb, agb}. Specifically, the (directed) subgraphs are: - subgraph_x with edges ( g, f(g,a,b)), and - subgraph_z with edges (ag, f(g,a,b)). Classical Tanner codes on these subgraphs are used as to construct quantum Tanner code.
As a matter of practice, defining classical Tanner codes on subgraph_x and subgraph_z requires choosing an ordering on the edges incident to every source node of these graphs. If the group G is equipped with a total order, a natural ordering of edges incident to every source node is induced by assigning the label (a, b) to edge (g, f(g,a,b)). Consistency then requires that edge (ag, f(g,a,b)) has label (a^-1, b), as verified by defining g’ = ag and checking that f(g,a,b) = f(g’,a^-1,b).
- classmethod random(group: Group, code_a: ClassicalCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], code_b: ClassicalCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]] | None = None, field: int | None = None, *, bipartite: bool = False, conjugate: slice | Sequence[int] | None = (), one_subset: bool = False, seed: int | None = None) QTCode[source]
Construct a random quantum Tanner code from a base group and seed code(s).
If only one code C is provided, use its dual ~C for the second code.
- class qldpc.codes.QuditCode(matrix: AbstractCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], field: int | None = None, *, conjugate: slice | Sequence[int] | None = ())[source]
Bases:
AbstractCodeQuantum stabilizer code for Galois qudits, with dimension q = p^m for prime p and integer m.
The parity check matrix of a QuditCode has dimensions (num_checks, 2 * num_qudits), and can be written as a block matrix in the form H = [H_x|H_z]. Each block has num_qudits columns.
The entries H_x[c, d] = r_x and H_z[c, d] = r_z iff check c addresses qudit d with the operator X(r_x) * Z(r_z), where r_x, r_z range over the base field, and X(r), Z(r) are generalized Pauli operators. Specifically: - X(r) = sum_{j=0}^{q-1} |j+r><j| is a shift operator, and - Z(r) = sum_{j=0}^{q-1} w^{j r} |j><j| is a phase operator, with w = exp(2 pi i / q).
Warning: here j, r, s, etc. not integers, but elements of the Galois field GF(q), which has different rules for addition and multiplication when q is not a prime number.
Helpful lecture by Gottesman: https://www.youtube.com/watch?v=JWg4zrNAF-g
- classmethod conjugate(matrix: ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], qudits: slice | Sequence[int]) ndarray[Any, dtype[int64]][source]
Apply local Fourier transforms to the given qudits.
This is equivalent to swapping X-type and Z-type operators.
- property dimension: int
The number of logical qudits encoded by this code.
- classmethod from_stabilizers(*stabilizers: str, field: int | None = None) QuditCode[source]
Construct a QuditCode from the provided stabilizers.
- get_logical_ops() FieldArray[source]
Complete basis of nontrivial logical operators for this code.
Logical operators are represented by a three-dimensional array logical_ops with dimensions (2, k, 2 * n), where k and n are respectively the numbers of logical and physical qudits in this code. The first axis is used to keep track of conjugate pairs of logical operators. The last axis is “doubled” to indicate whether a physical qudit is addressed by a physical X-type or Z-type operator.
Specifically, logical_ops[0, :, :] are “logical X-type” operators, which address at least one physical qudit by a physical X-type operator, and may additionally address physical qudits by physical Z-type operators. logical_ops[1, :, :] are logical Z-type operators that only address physical qudits by physical Z-type operators (which is a consequence of the way these operators are constructed here).
For example, if logical_ops[0, r, j] == 1 for j < n (j >= n), then the X-type logical operator for qudit r addresses physical qudit j with an X-type (Z-type) operator. The fact that logical operators come in conjugate pairs means that logical_ops[0, r, :] @ logical_ops[1, s, :] = int(r == s).
Logical operators are constructed using the method described in Section 4.1 of Gottesman’s thesis (arXiv:9705052), slightly modified for qudits.
- classmethod graph_to_matrix(graph: DiGraph) FieldArray[source]
Convert a Tanner graph into a parity check matrix.
- classmethod matrix_to_graph(matrix: ndarray[Any, dtype[int64]] | Sequence[Sequence[int]]) DiGraph[source]
Convert a parity check matrix into a Tanner graph.
- property num_checks: int
Number of parity checks (stabilizers) in this code.
- property num_qubits: int
Number of data qubits in this code.
- property num_qudits: int
Number of data qudits in this code.
- class qldpc.codes.ReedMullerCode(order: int, size: int, field: int | None = None)[source]
Bases:
ClassicalCodeClassical Reed-Muller code.
References: - https://errorcorrectionzoo.org/c/reed_muller - https://feog.github.io/10-coding.pdf
- class qldpc.codes.ReedSolomonCode(bits: int, dimension: int)[source]
Bases:
ClassicalCodeClassical Reed-Solomon code.
Source: https://galois.readthedocs.io/en/v0.3.8/api/galois.ReedSolomon/ References: - https://errorcorrectionzoo.org/c/reed_solomon - https://www.cs.cmu.edu/~venkatg/teaching/codingtheory/notes/notes6.pdf
- class qldpc.codes.RepetitionCode(bits: int, field: int | None = None)[source]
Bases:
ClassicalCodeClassical repetition code.
- class qldpc.codes.RingCode(bits: int, field: int | None = None)[source]
Bases:
ClassicalCodeClassical ring code: repetition code with periodic boundary conditions.
- class qldpc.codes.SteaneCode(*, conjugate: slice | Sequence[int] | None = ())[source]
Bases:
CSSCodeSmallest quantum error-correcting CSS code.
- class qldpc.codes.SurfaceCode(rows: int, cols: int | None = None, rotated: bool = True, field: int | None = None, *, conjugate: bool = False)[source]
Bases:
CSSCodeThe one and only!
Actually, there are two variants: “ordinary” and “rotated” surface codes. The rotated code is more qubit-efficient.
If constructed with conjugate=True, every other qubit is Hadamard-transformed in a checkerboard pattern. The rotated surface code with conjugate=True is the XZZX code in arXiv:2009.07851.
- classmethod get_rotated_checks(rows: int, cols: int) tuple[ndarray[Any, dtype[int64]], ndarray[Any, dtype[int64]]][source]
Build X-sector and Z-sector parity check matrices.
Example 5x5 rotated surface code layout:
――― ―――
⋅ | | ⋅ |○―――○―――○―――○―――○――― | × | ⋅ | × | ⋅ | × |
―――○―――○―――○―――○―――○―――
× | ⋅ | × | ⋅ | × | ―――○―――○―――○―――○―――○――― | × | ⋅ | × | ⋅ | × | ―――○―――○―――○―――○―――○―――× | ⋅ | × | ⋅ | × | ―――○―――○―――○―――○―――○ | ⋅ | | ⋅ | ――― ―――Here: - Circles (○) denote data qubits (of which there are 5×5 = 25 total). - Tiles with a cross (×) denote X-type parity checks (12 total). - Tiles with a dot (⋅) denote Z-type parity checks (12 total).
- class qldpc.codes.TBCode(matrix_a: ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], matrix_b: ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], field: int | None = None, *, conjugate: slice | Sequence[int] = (), promise_balanced_codes: bool = False, skip_validation: bool = False)[source]
Bases:
CSSCodeTwo-block (TB) code.
A TBCode code is built out of two matrices A and B, which are combined as - matrix_x = [A, B], and - matrix_z = [B.T, -A.T], to form the parity check matrices of a CSSCode. If A and B commute, the parity check matrices matrix_x and matrix_z satisfy the requirements of a CSSCode.
Two-block codes constructed out of circulant matrices are known as generalized bicycle codes.
- class qldpc.codes.TannerCode(subgraph: Graph, subcode: ClassicalCode)[source]
Bases:
ClassicalCodeClassical Tanner code, as described in DOI:10.1109/TIT.1981.1056404.
A Tanner code T(G,C) is constructed from: [1] A bipartite “half-regular” graph G. That is, a graph…
… with two sets of nodes, V and W. … in which all nodes in V have degree n.
[2] A classical code C on n bits.
For convenience, we make G directed, with edges directed from V to W. The node sets V and W can then be identified, respectively, by the sources and sinks of G.
The Tanner code T(G,C) is defined on |W| bits. A |W|-bit string x is a code word of T(G,C) iff, for every node v in V, the bits of x incident to v are a code word of C.
This construction requires an ordering the edges E(v) adjacent to each vertex v. This class sorts E(v) by the value of the “sort” attribute attached to each edge. If there is no “sort” attribute, its value is treated as corresponding neighbor of v.
Tanner codes can similarly be defined on regular (undirected) graphs G’ = (V’,E’) by placing checks on V’ and bits on E’.
Notes: - If the subcode C has m checks, its parity matrix has shape (m,n). - The code T(G,C) has |W| bits and |V|m checks.
- classmethod as_directed_subgraph(subgraph: Graph) DiGraph[source]
Convert an undirected graph for a Tanner code into a directed graph for the same code.
- subcode: ClassicalCode
- subgraph: DiGraph
- class qldpc.codes.ToricCode(rows: int, cols: int | None = None, rotated: bool = True, field: int | None = None, *, conjugate: bool = False)[source]
Bases:
CSSCodeSurface code with periodic bounary conditions, encoding two logical qudits.
Reference: https://errorcorrectionzoo.org/c/surface