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: ClassicalCode

Classical 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: ClassicalCode

Classical Hamming code.

class qldpc.codes.classical.ReedMullerCode(order: int, size: int, field: int | None = None)[source]

Bases: ClassicalCode

Classical Reed-Muller code.

References: - https://errorcorrectionzoo.org/c/reed_muller - https://feog.github.io/10-coding.pdf

classmethod get_generator(order: int, size: int) ndarray[Any, dtype[int64]][source]

Get the generator matrix for the specified Reed-Muller code.

class qldpc.codes.classical.ReedSolomonCode(bits: int, dimension: int)[source]

Bases: ClassicalCode

Classical 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: ClassicalCode

Classical repetition code.

class qldpc.codes.classical.RingCode(bits: int, field: int | None = None)[source]

Bases: ClassicalCode

Classical ring code: repetition code with periodic boundary conditions.

class qldpc.codes.classical.TannerCode(subgraph: Graph, subcode: ClassicalCode)[source]

Bases: ClassicalCode

Classical 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: ABC

Template 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: QuditCode

CSS 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

  1. has a trivial syndrome, and

  2. 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

  1. H_z @ w_x = 0, and

  2. 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.

reduce_logical_ops(pauli: Literal[Pauli.X, Pauli.Z] | None = None, **decoder_args: object) None[source]

Reduce the weight of all logical operators.

class qldpc.codes.common.ClassicalCode(matrix: AbstractCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], field: int | None = None)[source]

Bases: AbstractCode

Classical 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.

get_weight() int[source]

Compute the weight of the largest check.

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.

words() FieldArray[source]

Code words of this code.

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: AbstractCode

Quantum 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.

get_stabilizers() list[str][source]

Stabilizers (checks) of this code, represented by strings.

get_weight() int[source]

Compute the weight of the largest check.

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.common.get_scrambled_seed(seed: int) int[source]

Scramble a seed, allowing us to safely increment seeds in repeat-until-success protocols.

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: TBCode

Bivariate 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: QuditCode

Smallest 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: CSSCode

Surface 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: CSSCode

Hypergraph 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: CSSCode

Lifted 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: CSSCode

Quantum 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 load(path: str) QTCode[source]

Load a QTCode from a file.

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.

save(path: str, *headers: str) None[source]

Save the generating data of this code to a file.

class qldpc.codes.quantum.SteaneCode(*, conjugate: slice | Sequence[int] | None = ())[source]

Bases: CSSCode

Smallest 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: CSSCode

The 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).

Reference: https://errorcorrectionzoo.org/c/rotated_surface

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: CSSCode

Two-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: CSSCode

Surface code with periodic bounary conditions, encoding two logical qudits.

Reference: https://errorcorrectionzoo.org/c/surface

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.

Same as in SurfaceCode.get_rotated_checks, but with periodic boundary conditions.

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: TBCode

Bivariate 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: ClassicalCode

Classical 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: QuditCode

CSS 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

  1. has a trivial syndrome, and

  2. 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

  1. H_z @ w_x = 0, and

  2. 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.

reduce_logical_ops(pauli: Literal[Pauli.X, Pauli.Z] | None = None, **decoder_args: object) None[source]

Reduce the weight of all logical operators.

class qldpc.codes.ClassicalCode(matrix: AbstractCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], field: int | None = None)[source]

Bases: AbstractCode

Classical 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.

get_weight() int[source]

Compute the weight of the largest check.

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.

words() FieldArray[source]

Code words of this code.

class qldpc.codes.FiveQubitCode(*, conjugate: slice | Sequence[int] | None = ())[source]

Bases: QuditCode

Smallest 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: CSSCode

Surface 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: CSSCode

Hypergraph 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: ClassicalCode

Classical 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: CSSCode

Lifted 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: CSSCode

Quantum 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 load(path: str) QTCode[source]

Load a QTCode from a file.

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.

save(path: str, *headers: str) None[source]

Save the generating data of this code to a file.

class qldpc.codes.QuditCode(matrix: AbstractCode | ndarray[Any, dtype[int64]] | Sequence[Sequence[int]], field: int | None = None, *, conjugate: slice | Sequence[int] | None = ())[source]

Bases: AbstractCode

Quantum 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.

get_stabilizers() list[str][source]

Stabilizers (checks) of this code, represented by strings.

get_weight() int[source]

Compute the weight of the largest check.

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: ClassicalCode

Classical Reed-Muller code.

References: - https://errorcorrectionzoo.org/c/reed_muller - https://feog.github.io/10-coding.pdf

classmethod get_generator(order: int, size: int) ndarray[Any, dtype[int64]][source]

Get the generator matrix for the specified Reed-Muller code.

class qldpc.codes.ReedSolomonCode(bits: int, dimension: int)[source]

Bases: ClassicalCode

Classical 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: ClassicalCode

Classical repetition code.

class qldpc.codes.RingCode(bits: int, field: int | None = None)[source]

Bases: ClassicalCode

Classical ring code: repetition code with periodic boundary conditions.

class qldpc.codes.SteaneCode(*, conjugate: slice | Sequence[int] | None = ())[source]

Bases: CSSCode

Smallest 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: CSSCode

The 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).

Reference: https://errorcorrectionzoo.org/c/rotated_surface

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: CSSCode

Two-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: ClassicalCode

Classical 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: CSSCode

Surface code with periodic bounary conditions, encoding two logical qudits.

Reference: https://errorcorrectionzoo.org/c/surface

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.

Same as in SurfaceCode.get_rotated_checks, but with periodic boundary conditions.