Conventions
The pre-built cudaq-qec codes and decoders follow a common set of conventions for how errors, syndromes, and logical observables are laid out. This page documents them; the decoders, examples, and API reference all build on these conventions.
To address vectors of qubits (cudaq::qvector), CUDA-Q indexing starts from 0, and 0 corresponds
to the leftmost position when working with Pauli strings (cudaq::spin_op). For example, applying a Pauli X operator
to qubit 1 out of 7 would be X_1 = IXIIIII.
While implementing your own codes and decoders, you are free to follow any convention that is convenient to you. However,
to interact with the pre-built QEC codes and decoders within this library, the following conventions are used. All of these codes
are CSS codes, and so we separate \(X\)-type and \(Z\)-type errors. For example, an error vector for 3 qubits will
have 6 entries, 3 bits representing the presence of a bit-flip on each qubit, and 3 bits representing a phase-flip on each qubit.
An error vector representing a bit-flip on qubit 0, and a phase-flip on qubit 1 would look like E = 100010. This means that this
error vector is just two error vectors (E_X, E_Z) concatenated together (E = E_X | E_Z).
These errors are detected by stabilizers. \(Z\)-stabilizers detect \(X\)-type errors and vice versa. Thus we write our CSS parity check matrices as
so that when we generate a syndrome vector by multiplying the parity check matrix by an error vector we get
This means that for the concatenated syndrome vector S = S_X | S_Z, the first part, S_X, are syndrome bits triggered by Z
stabilizers detecting X errors. This is because the Z stabilizers like ZZI and IZZ anti-commute with X errors like
IXI.
The decoder prediction as to what error happened is D = D_X | D_Z. A successful error decoding does not require that D = E,
but that D + E is not a logical operator. There are a couple ways to check this.
For bitflip errors, we check that the residual error R = D_X + E_X is not L_X. Since X anticommutes
with Z, we can check that L_Z(D_X + E_X) = 0. This is because we just need to check if they have mutual support on an even
or odd number of qubits. We could also check that R is not a stabilizer.
Similar to the parity check matrix, the logical observables are also stored in a matrix as
so that when determining logical errors, we can do matrix multiplication
Here we’re using P as this can be stored in a Pauli frame tracker to track observable flips.
Each logical qubit has logical observables associated with it. Depending on what basis the data qubits are measured in, either the
X or Z logical observables can be measured. The data qubits which support the logical observables are contained in the qec::code class as well.
To do a logical Z(X) measurement, measure out all of the data qubits in the Z(X) basis. Then check support on the appropriate
Z(x) observable.