CUDA-Q QEC C++ API
Code
-
class code : public cudaqx::extension_point<code, const heterogeneous_map&>
Base class for quantum error correcting codes in CUDA-Q.
This class provides the core interface and functionality for implementing quantum error correcting codes in CUDA-Q. It defines the basic operations that any QEC code must support and provides infrastructure for syndrome measurement and error correction experiments.
To implement a new quantum error correcting code:
Create a new class that inherits from code
Implement the protected virtual methods:
Define quantum kernels for each required logical operation (these are the fault tolerant logical operation implementations)
Register the operations in your constructor using the operation_encodings map on the base class
Register your new code type using CUDAQ_EXT_PT_REGISTER_TYPE
Example implementation:
__qpu__ void x_kernel(patch p); __qpu__ void z_kernel(patch p); class my_code : public qec::code { protected: std::size_t get_num_data_qubits() const override { return 7; } std::size_t get_num_ancilla_qubits() const override { return 6; } std::size_t get_num_ancilla_x_qubits() const override { return 3; } std::size_t get_num_ancilla_z_qubits() const override { return 3; } public: my_code(const heterogeneous_map& options) : code() { // Can use user-specified options, e.g. auto d = options.get<int>("distance"); operation_encodings.insert(std::make_pair(operation::x, x_kernel)); operation_encodings.insert(std::make_pair(operation::z, z_kernel)); // Register other required operations... // Define the default stabilizers! m_stabilizers = fromPauliWords({"XXXX", "ZZZZ"}); } CUDAQ_EXTENSION_CUSTOM_CREATOR_FUNCTION( my_code, static std::unique_ptr<qec::code> create(const heterogeneous_map &options) { return std::make_unique<my_code>(options); } ) }; CUDAQ_EXT_PT_REGISTER_TYPE(my_code)
Supported quantum operations for error correcting codes
Subclassed by cudaq::qec::repetition::repetition, cudaq::qec::steane::steane, cudaq::qec::surface_code::surface_code
Public Types
-
using one_qubit_encoding = cudaq::qkernel<void(patch)>
Type alias for single qubit quantum kernels.
-
using two_qubit_encoding = cudaq::qkernel<void(patch, patch)>
Type alias for two qubit quantum kernels.
-
using stabilizer_round = cudaq::qkernel<std::vector<cudaq::measure_result>(patch, const std::vector<std::size_t>&, const std::vector<std::size_t>&)>
Type alias for stabilizer measurement kernels. The two vector arguments are the flattened X and Z stabilizer schedule matrices (see get_stabilizer_schedule_x/z()): entry 0 = no support, entry k >= 1 = interaction at timestep k.
-
using encoding = std::variant<one_qubit_encoding, two_qubit_encoding, stabilizer_round>
Type alias for quantum operation encodings.
Public Functions
-
virtual std::size_t get_num_data_qubits() const = 0
Get the number of physical data qubits needed for the code.
- Returns:
Number of data qubits
-
virtual std::size_t get_num_ancilla_qubits() const = 0
Get the total number of ancilla qubits needed.
- Returns:
Total number of ancilla qubits
-
virtual std::size_t get_num_ancilla_x_qubits() const = 0
Get number of ancilla qubits needed for X stabilizer measurements.
- Returns:
Number of X-type ancilla qubits
-
virtual std::size_t get_num_ancilla_z_qubits() const = 0
Get number of ancilla qubits needed for Z stabilizer measurements.
- Returns:
Number of Z-type ancilla qubits
-
virtual std::size_t get_num_x_stabilizers() const = 0
Get number of X stabilizer that can be measured.
- Returns:
Number of X-type stabilizers
-
virtual std::size_t get_num_z_stabilizers() const = 0
Get number of Z stabilizer that can be measured.
- Returns:
Number of Z-type stabilizers
-
cudaqx::tensor<uint8_t> get_parity() const
Get the full parity check matrix H = (Hx | Hz)
- Returns:
Tensor representing the parity check matrix
-
cudaqx::tensor<uint8_t> get_parity_x() const
Get the X component of the parity check matrix.
- Returns:
Tensor representing Hx
-
cudaqx::tensor<uint8_t> get_parity_z() const
Get the Z component of the parity check matrix.
- Returns:
Tensor representing Hz
-
inline virtual cudaqx::tensor<uint8_t> get_stabilizer_schedule_x() const
Get the X-stabilizer schedule matrix passed to the code’s stabilizer_round kernel. Same shape and support pattern as get_parity_x(); entry 0 means no support, entry k >= 1 means the ancilla-data interaction executes at timestep k, so a code can encode an optimized (e.g. hook-error-aware) gate order.
- Returns:
Tensor of scheduled interactions; the default implementation returns get_parity_x() unchanged (every interaction at timestep 1, i.e. ascending qubit-index order).
-
inline virtual cudaqx::tensor<uint8_t> get_stabilizer_schedule_z() const
Get the Z-stabilizer schedule matrix passed to the code’s stabilizer_round kernel. See get_stabilizer_schedule_x().
- Returns:
Tensor of scheduled interactions; the default implementation returns get_parity_z() unchanged.
-
cudaqx::tensor<uint8_t> get_pauli_observables_matrix() const
Get Lx stacked on Lz.
- Returns:
Tensor representing pauli observables
-
cudaqx::tensor<uint8_t> get_observables_x() const
Get the Lx observables.
- Returns:
Tensor representing Lx
-
cudaqx::tensor<uint8_t> get_observables_z() const
Get the Lz observables.
- Returns:
Tensor representing Lz
-
inline const std::vector<cudaq::spin_op_term> &get_stabilizers() const
Get the stabilizer generators.
- Returns:
Reference to stabilizers
Public Static Functions
-
static std::unique_ptr<code> get(const std::string &name, const std::vector<cudaq::spin_op_term> &stabilizers, const heterogeneous_map options = {})
Factory method to create a code instance with specified stabilizers.
- Parameters:
name – Name of the code to create
stabilizers – Stabilizer generators for the code
options – Optional code-specific configuration options
- Returns:
Unique pointer to created code instance
-
struct patch
Represents a logical qubit patch for quantum error correction.
This type is for CUDA-Q kernel code only.
This structure defines a patch of qubits used in quantum error correction codes. It consists of data qubits and ancilla qubits for X and Z stabilizer measurements.
-
class repetition : public cudaq::qec::code
Implementation of the repetition quantum error correction code.
Public Functions
-
repetition(const heterogeneous_map&)
Constructs a repetition code instance.
-
repetition(const heterogeneous_map&)
-
class steane : public cudaq::qec::code
Steane code implementation.
Public Functions
-
steane(const heterogeneous_map&)
Constructor for the Steane code.
-
steane(const heterogeneous_map&)
-
enum cudaq::qec::surface_code::surface_role
enumerates the role of a grid site in the surface codes stabilizer grid
Values:
-
enumerator amx
-
enumerator amz
-
enumerator empty
-
enumerator amx
-
enum class cudaq::qec::surface_code::sc_orientation
Surface-code orientation: controls which Pauli type (X or Z) occupies the bulk interior and which boundaries are X-type vs Z-type.
Geometry names follow the NVIDIA/Ising-Decoding naming convention (https://github.com/NVIDIA/Ising-Decoding): Ising’s
code_rotationconvention uses first character = bulk syndrome type, second character = rotated_type. By geometry: XV and ZH place X-type stabilizers on the left/right boundaries (Z on top/bottom); XH and ZV place X-type on top/bottom (Z on left/right). XV vs ZH (and XH vs ZV) differ in the bulk X/Z checkerboard.Observable convention: get_spin_op_observables() returns the valid logical pair for the orientation. XV/ZH use X obs along the top row and Z obs along the left column; XH/ZV swap them (X along the left column, Z along the top row). See get_spin_op_observables() for details.
Values:
-
enumerator XV
-
enumerator XH
-
enumerator ZV
-
enumerator ZH
-
enumerator XV
-
struct vec2d
describes the 2d coordinate on the stabilizer grid
-
class stabilizer_grid
Generates and keeps track of the 2d grid of stabilizers in the rotated surface code. Following same layout convention as in: https://arxiv.org/abs/2311.10687 Grid layout is arranged from left to right, top to bottom (row major storage) grid_length = 4 example:
Each entry on the grid can be an X stabilizer, Z stabilizer, or empty, as is needed on the edges. The diagrams below show the default ZH orientation. Other orientations assign X/Z roles according to sc_orientation. The grid length of 4 corresponds to a distance 3 surface code, which results in:(0,0) (0,1) (0,2) (0,3) (1,0) (1,1) (1,2) (1,3) (2,0) (2,1) (2,2) (2,3) (3,0) (3,1) (3,2) (3,3)
e(0,0) e(0,1) Z(0,2) e(0,3) X(1,0) Z(1,1) X(1,2) e(1,3) e(2,0) X(2,1) Z(2,2) X(2,3) e(3,0) Z(3,1) e(3,2) e(3,3)
This is seen through the
print_stabilizer_grid()member function. To get rid of the empty sites, theprint_stabilizer_coords()function is used:Z(0,2) X(1,0) Z(1,1) X(1,2) X(2,1) Z(2,2) X(2,3) Z(3,1)
and to get the familiar visualization of the distance three surface code, the
print_stabilizer_indicesresults in:Z0 X0 Z1 X1 X2 Z2 X3 Z3
The data qubits are located at the four corners of each of the weight-4 stabilizers. They are also organized with index increasing from left to right, top to bottom:
d0 d1 d2 d3 d4 d5 d6 d7 d8
Public Functions
-
stabilizer_grid(uint32_t distance, sc_orientation orientation = sc_orientation::ZH)
Construct the grid from the code’s distance.
-
stabilizer_grid()
Empty constructor.
-
sc_orientation get_orientation() const
Get the orientation used to construct this stabilizer grid.
-
void print_stabilizer_grid() const
Print a 2d grid of stabilizer roles.
-
void print_stabilizer_coords() const
Print a 2d grid of stabilizer coords.
-
void print_stabilizer_indices() const
Print a 2d grid of stabilizer indices.
-
void print_data_grid() const
Print a 2d grid of data qubit indices.
-
void print_stabilizer_maps() const
Print the coord <–> indices maps.
-
void print_stabilizers() const
Print the stabilizers in sparse pauli format.
-
std::vector<cudaq::spin_op_term> get_spin_op_stabilizers() const
Get the stabilizers as a vector of cudaq::spin_op_terms.
-
cudaqx::tensor<uint8_t> get_cnot_schedule_x() const
Get the CNOT schedule matrix for the X stabilizers.
Note
The per-plaquette CNOT order is chosen per the grid’s orientation so that mid-round ancilla faults (“hook errors”), which propagate onto the data qubits of the remaining CNOTs, land perpendicular to the same-type logical operator instead of along it, following the standard zigzag schedule of https://arxiv.org/abs/1404.3747. Rows are ordered to match the rows of to_parity_matrix() / code::get_parity_x().
- Returns:
Tensor with the same shape and support pattern as the X block of the parity check matrix (num_x_stabilizers x distance^2). Entry 0 means the ancilla does not touch that data qubit; entry k in [1, 4] means the ancilla-data CNOT executes at timestep k of the stabilizer round.
-
cudaqx::tensor<uint8_t> get_cnot_schedule_z() const
Get the CNOT schedule matrix for the Z stabilizers.
Note
See get_cnot_schedule_x() for the hook-error rationale and row ordering.
- Returns:
Tensor with the same shape and support pattern as the Z block of the parity check matrix (num_z_stabilizers x distance^2). Entry 0 means the ancilla does not touch that data qubit; entry k in [1, 4] means the data-ancilla CNOT executes at timestep k of the stabilizer round.
-
std::vector<std::size_t> get_cnot_schedule_pairs_x() const
Get the X-stabilizer CNOT schedule as a flat list of (stabilizer index, data index) pairs, ordered by timestep within each stabilizer — the replay format for kernels that take an explicit CNOT pair list. Stabilizer indices match the rows of get_cnot_schedule_x().
-
std::vector<std::size_t> get_cnot_schedule_pairs_z() const
Z-stabilizer counterpart of get_cnot_schedule_pairs_x().
-
std::vector<cudaq::spin_op_term> get_spin_op_observables() const
Get the observables as a vector of cudaq::spin_op_terms.
Note
Returns the correct logical pair for the grid’s orientation. For XV and ZH the X observable runs along the top row of data qubits and the Z observable along the left column; for XH and ZV the assignment is swapped (X along the left column, Z along the top row) to match their boundary types. The returned X/Z observables commute with the stabilizers and anticommute with each other for every orientation.
- Returns:
The X logical observable first, followed by the Z logical observable.
Public Members
-
uint32_t distance = 0
The distance of the code determines the number of data qubits per dimension.
-
uint32_t grid_length = 0
length of the stabilizer grid for distance = d data qubits, the stabilizer grid has length d+1
-
std::vector<surface_role> roles
flattened vector of the stabilizer grid sites roles’ grid idx -> role stored in row major order
-
std::vector<vec2d> data_coords
data index -> 2d coord data qubits are in an offset 2D coord system from stabilizers
-
std::vector<std::vector<size_t>> x_stabilizers
Each element is an X stabilizer specified by the data qubits it has support on In surface code, can have weight 2 or weight 4 stabs So {x,z}_stabilizer[i].size() == 2 || 4.
-
std::vector<std::vector<size_t>> z_stabilizers
Each element is an Z stabilizer specified by the data qubits it has support on.
-
stabilizer_grid(uint32_t distance, sc_orientation orientation = sc_orientation::ZH)
-
class surface_code : public cudaq::qec::code
surface_code implementation
Public Functions
-
surface_code(const heterogeneous_map&)
Constructor for the surface_code.
-
virtual cudaqx::tensor<uint8_t> get_stabilizer_schedule_x() const override
Get the hook-error-aware X-stabilizer CNOT schedule matrix. See stabilizer_grid::get_cnot_schedule_x().
-
virtual cudaqx::tensor<uint8_t> get_stabilizer_schedule_z() const override
Get the hook-error-aware Z-stabilizer CNOT schedule matrix. See stabilizer_grid::get_cnot_schedule_z().
Public Members
-
stabilizer_grid grid
Extension creator function for the surface_code.
Grid to keep track of topological arrangement of qubits.
-
surface_code(const heterogeneous_map&)
Detector Error Model
-
struct detector_error_model
A detector error model (DEM) for a quantum error correction circuit. A DEM can be created from a QEC circuit and a noise model. It contains information about which errors flip which detectors. This is used by the decoder to help make predictions about observables flips.
Shared size parameters among the matrix types.
detector_error_matrix: num_detectors x num_error_mechanisms [d, e]error_rates: num_error_mechanismsobservables_flips_matrix: num_observables x num_error_mechanisms [k, e]
Note
The C++ API for this class may change in the future. The Python API is more likely to be backwards compatible.
Public Functions
-
std::size_t num_detectors() const
Return the number of rows in the detector_error_matrix.
-
std::size_t num_error_mechanisms() const
Return the number of columns in the detector_error_matrix, error_rates, and observables_flips_matrix.
-
std::size_t num_observables() const
Return the number of rows in the observables_flips_matrix.
-
void canonicalize_for_rounds(uint32_t num_syndromes_per_round, bool remove_zero_syndrome_errors = false)
Put the detector_error_matrix into canonical form for round-based decoding: topologically order the columns and merge columns that share the same detector AND observable signature, composing their rates so the canonicalized model matches the input.
Note
Canonicalization does not preserve cross-column exclusivity structure: each output column is given a fresh unique error id and is treated as independent, so any
error_idscorrelation in the input model is discarded.- Parameters:
num_syndromes_per_round – Number of syndromes per round, used to order columns by the rounds their detectors span; 0 disables the per-round key.
remove_zero_syndrome_errors – If false (default), zero-syndrome columns that still flip an observable (undetectable logical errors) are retained so the observable-flip probability is preserved; if true, all columns with no detector signature are dropped (appropriate when the DEM is consumed only for round-based decoding).
-
void canonicalize_for_rounds_with_boundary(uint32_t num_syndromes_per_round, uint32_t num_boundary_syndromes, bool remove_zero_syndrome_errors)
Boundary-aware variant of canonicalize_for_rounds for memory-experiment DEMs whose first and last detector layers (the boundaries) are narrower than the interior layers.
- Parameters:
num_syndromes_per_round – Interior-layer width (syndromes per interior round).
num_boundary_syndromes – Width of the leading and trailing boundary layers: the first and last
num_boundary_syndromesrows form the boundary rounds and each intermediate block ofnum_syndromes_per_roundrows is an interior round.remove_zero_syndrome_errors – Same meaning as in the scalar overload.
- Throws:
std::invalid_argument – if
num_syndromes_per_roundis 0 ornum_boundary_syndromes>num_syndromes_per_round.
Public Members
-
cudaqx::tensor<uint8_t> detector_error_matrix
The detector error matrix is a specific kind of circuit-level parity-check matrix where each row represents a detector, and each column represents an error mechanism. The entries of this matrix are H[i,j] = 1 if detector i is triggered by error mechanism j, and 0 otherwise.
-
std::vector<double> error_rates
The list of weights has length equal to the number of columns of
detector_error_matrix, which assigns a likelihood to each error mechanism.
-
cudaqx::tensor<uint8_t> observables_flips_matrix
The observables flips matrix is a specific kind of circuit-level parity- check matrix where each row represents a Pauli observable, and each column represents an error mechanism. The entries of this matrix are O[i,j] = 1 if Pauli observable i is flipped by error mechanism j, and 0 otherwise.
-
std::optional<std::vector<std::size_t>> error_ids
Error mechanism ID. From a probability perspective, each error mechanism ID is independent of all other error mechanism ID. For all errors with the same ID, only one of them can happen. That is - the errors containing the same ID are correlated with each other.
-
struct decoder_context
Lazy handle returned by
decoder_context_from_memory_circuit.Stores the raw (uncanonicalized) circuit analysis. Call a component method to canonicalize exactly the stabilizer type needed and obtain a
decoder_inputs:x_component()— X-stabilizer detectors onlyz_component()— Z-stabilizer detectors onlyfull_component()— both stabilizer types, boundary-aware
Public Functions
-
std::size_t num_measurements() const
Total number of measurements per shot (column count of m2d/m2o).
-
decoder_inputs x_component() const
Canonicalize X-stabilizer detectors; return decoder_inputs.
-
decoder_inputs z_component() const
Canonicalize Z-stabilizer detectors; return decoder_inputs.
-
decoder_inputs full_component() const
Canonicalize both stabilizer types with boundary awareness; return decoder_inputs.
-
detector_error_model cudaq::qec::dem_from_memory_circuit(const code &code, operation statePrep, std::size_t numRounds, cudaq::noise_model &noise, bool decompose_errors = false)
Given a memory circuit setup, generate a DEM.
- Parameters:
code – QEC Code to sample
statePrep – Initial state preparation operation
numRounds – Number of stabilizer measurement rounds
noise – Noise model to apply
decompose_errors – If true, hyperedge error mechanisms are decomposed into pairs of two-detector edges by Stim before returning.
- Returns:
Detector error model
-
detector_error_model cudaq::qec::x_dem_from_memory_circuit(const code &code, operation statePrep, std::size_t numRounds, cudaq::noise_model &noise, bool decompose_errors = false)
Given a memory circuit setup, generate a DEM for X stabilizers.
- Parameters:
code – QEC Code to sample
statePrep – Initial state preparation operation
numRounds – Number of stabilizer measurement rounds
noise – Noise model to apply
decompose_errors – If true, hyperedge error mechanisms are decomposed into pairs of two-detector edges by Stim before returning.
- Returns:
Detector error model
-
detector_error_model cudaq::qec::z_dem_from_memory_circuit(const code &code, operation statePrep, std::size_t numRounds, cudaq::noise_model &noise, bool decompose_errors = false)
Given a memory circuit setup, generate a DEM for Z stabilizers.
- Parameters:
code – QEC Code to sample
statePrep – Initial state preparation operation
numRounds – Number of stabilizer measurement rounds
noise – Noise model to apply
decompose_errors – If true, hyperedge error mechanisms are decomposed into pairs of two-detector edges by Stim before returning.
- Returns:
Detector error model
-
decoder_context cudaq::qec::decoder_context_from_memory_circuit(const code &code, operation statePrep, std::size_t numRounds, cudaq::noise_model &noise, bool decompose_errors = false)
Run a memory-circuit analysis and return a lazy handle.
Executes
dem_from_kernelonce and stores the raw result. Callx_component(),z_component(), orfull_component()on the returned handle to canonicalize exactly the stabilizer type needed.
-
detector_error_model cudaq::qec::dem_from_stim_text(const std::string &dem_text, bool use_decomp_suggestions = false)
Parse the Stim DEM string
dem_textinto detector/observable flip matrices and error rates. DEM-native decoders should consume raw DEM text instead. By default (use_decomp_suggestions= false) the ‘^’ separators are ignored and each error instruction produces a single column. Ifuse_decomp_suggestionsis true, error mechanisms that carry an explicit graphlike decomposition (components separated by ‘^’) are expanded into one column per component, each inheriting the probability of the parent instruction.error_idsis always left as nullopt. Note that this is a lossy approximation of the original DEM.
Detector Error Model (DEM) Sampling
CPU sampling (host tensors):
-
std::tuple<cudaqx::tensor<uint8_t>, cudaqx::tensor<uint8_t>> cudaq::qec::dem_sampler::cpu::sample_dem(const cudaqx::tensor<uint8_t> &check_matrix, std::size_t numShots, const std::vector<double> &error_probabilities)
Sample measurements from a check matrix (CPU, per-mechanism probs)
- Parameters:
check_matrix – Binary matrix [num_checks × num_error_mechanisms]
numShots – Number of measurement shots
error_probabilities – Per-error-mechanism probabilities
- Returns:
(checks [numShots × num_checks], errors [numShots × num_mechanisms])
-
std::tuple<cudaqx::tensor<uint8_t>, cudaqx::tensor<uint8_t>> cudaq::qec::dem_sampler::cpu::sample_dem(const cudaqx::tensor<uint8_t> &check_matrix, std::size_t numShots, const std::vector<double> &error_probabilities, unsigned seed)
Sample measurements from a check matrix with seed (CPU)
GPU sampling (device pointers, requires cuStabilizer):
-
bool cudaq::qec::dem_sampler::gpu::sample_dem(const uint8_t *d_check_matrix, size_t num_checks, size_t num_error_mechanisms, const double *d_error_probabilities, size_t num_shots, unsigned seed, uint8_t *d_checks_out, uint8_t *d_errors_out, std::uintptr_t stream_handle = 0)
GPU DEM sampling with caller-provided device pointers.
The function composes (all GPU memory is allocated and freed internally per call):
pack_check_matrix_rowwise (dense uint8 → bitpacked uint32)
custabilizerSampleProbArraySparseCompute (sparse Bernoulli sampling)
custabilizerGF2SparseDenseMatrixMultiply (syndrome = errors * H^T)
unpack_syndromes_gpu (bitpacked uint32 → uint8)
csr_to_dense_fused (CSR errors → dense uint8)
- Parameters:
d_check_matrix – Device pointer [num_checks × num_error_mechanisms]
num_checks – Number of checks (rows of H)
num_error_mechanisms – Number of error mechanisms (columns of H)
d_error_probabilities – Device pointer [num_error_mechanisms]
num_shots – Number of samples
seed – RNG seed
d_checks_out – Device pointer [num_shots × num_checks] (OUTPUT)
d_errors_out – Device pointer [num_shots × num_error_mechanisms] (OUT)
stream_handle – Optional CUDA stream handle (uintptr_t cast), 0 for default stream
- Returns:
true on success, false if cuStabilizer is unavailable
Legacy convenience wrappers (delegate to cpu::sample_dem; prefer the
dem_sampler::cpu::sample_dem overloads above):
-
std::tuple<cudaqx::tensor<uint8_t>, cudaqx::tensor<uint8_t>> cudaq::qec::dem_sampling(const cudaqx::tensor<uint8_t> &check_matrix, std::size_t numShots, const std::vector<double> &error_probabilities)
Sample a detector error model on CPU (legacy API).
- Parameters:
check_matrix – Binary matrix [num_checks x num_error_mechanisms]
numShots – Number of independent Monte-Carlo shots
error_probabilities – Per-error-mechanism Bernoulli probabilities
- Returns:
Tuple of (checks, errors)
-
std::tuple<cudaqx::tensor<uint8_t>, cudaqx::tensor<uint8_t>> cudaq::qec::dem_sampling(const cudaqx::tensor<uint8_t> &check_matrix, std::size_t numShots, const std::vector<double> &error_probabilities, unsigned seed)
Sample a detector error model on CPU (legacy API, seeded).
- Parameters:
check_matrix – Binary matrix [num_checks x num_error_mechanisms]
numShots – Number of independent Monte-Carlo shots
error_probabilities – Per-error-mechanism Bernoulli probabilities
seed – RNG seed for reproducibility
- Returns:
Tuple of (checks, errors)
Decoder Interfaces
-
struct decoder_inputs
Finalized decoder inputs: a canonicalized DEM and measurement maps.
-
std::vector<std::int64_t> cudaq::qec::d_sparse(const cudaq::M2DSparseMatrix &m2d)
Flatten an M2DSparseMatrix into the
-1-terminated sparse vector a realtime decoder config expects for itsD_sparse.
-
using cudaq::qec::decoder_init = std::variant<sparse_binary_matrix, std::string>
Decoder construction input: either a parity-check matrix or raw Stim DEM text.
-
class decoder : public cudaqx::extension_point<decoder, const decoder_init&, const cudaqx::heterogeneous_map&>
The
decoderbase class should be subclassed by specific decoder implementations. Theheterogeneous_mapprovides a placeholder for arbitrary constructor parameters that can be unique to each specific decoder.Public Types
-
enum decode_result_type
Indicates whether decode() returns a full error frame (length block_size) or an already-projected observable frame (length num_observables). Decoders that accept an “O” observable matrix in their constructor params should call set_result_type(decode_to_obs); all others default to decode_to_errs.
Note: even in decode_to_obs mode, set_O_sparse() must still be called so that enqueue_syndrome() knows num_observables and can size the corrections buffer correctly.
Values:
-
enumerator decode_to_errs
result.size() == block_size; enqueue_syndrome projects via O_sparse
-
enumerator decode_to_obs
result.size() == num_observables; enqueue_syndrome uses result directly; set_O_sparse() still required
-
enumerator decode_to_errs
Public Functions
-
decoder(cudaq::qec::sparse_binary_matrix H)
Constructor.
- Parameters:
H – Decoder’s parity check matrix. Taken by value so rvalue arguments are moved into the base member.
-
virtual decoder_result decode(const std::vector<float_t> &syndrome) = 0
Decode a single syndrome.
- Parameters:
syndrome – A vector of syndrome measurements where the floating point value is the probability that the syndrome measurement is a |1>. The length of the syndrome vector should be equal to
syndrome_size.- Returns:
Vector of length
block_sizewith soft probabilities of errors in each index.
-
virtual decoder_result decode(const cudaqx::tensor<uint8_t> &syndrome)
Decode a single syndrome.
- Parameters:
syndrome – An order-1 tensor of syndrome measurements where a 1 bit represents that the syndrome measurement is a |1>. The length of the syndrome vector should be equal to
syndrome_size.- Returns:
Vector of length
block_sizeof errors in each index.
-
virtual std::future<decoder_result> decode_async(const std::vector<float_t> &syndrome)
Decode a single syndrome.
- Parameters:
syndrome – A vector of syndrome measurements where the floating point value is the probability that the syndrome measurement is a |1>.
- Returns:
std::future of a vector of length
block_sizewith soft probabilities of errors in each index.
-
virtual std::vector<decoder_result> decode_batch(const std::vector<std::vector<float_t>> &syndrome)
Decode multiple independent syndromes (may be done in serial or parallel depending on the specific implementation)
- Parameters:
syndrome – A vector of
Nsyndrome measurements where the floating point value is the probability that the syndrome measurement is a |1>.- Returns:
2-D vector of size
Nxblock_sizewith soft probabilities of errors in each index.
-
inline decode_result_type get_result_type() const
Returns the type of result produced by decode(). Defaults to decode_to_errs. Decoders that project to observables internally (i.e., constructed with an “O” param) should call set_result_type(decode_to_obs) in their constructor.
-
uint32_t get_num_msyn_per_decode() const
Get the number of measurement syndromes per decode call. This depends on D_sparse, so you must have called set_D_sparse() first.
-
inline int get_cuda_device_id() const
The CUDA device this decoder was pinned to at construction via the “cuda_device_id” parameter, or -1 when no pin was requested. Construction pins the constructing thread persistently (the thread that creates a decoder is the thread expected to drive its decode calls).
-
void set_O_sparse(const std::vector<std::vector<uint32_t>> &O_sparse)
Set the observable matrix.
-
void set_O_sparse(const std::vector<int64_t> &O_sparse)
Set the observable matrix, using a single long vector with -1 as row terminators.
-
void set_D_sparse(const std::vector<std::vector<uint32_t>> &D_sparse)
Set the D_sparse matrix.
-
void set_D_sparse(const std::vector<int64_t> &D_sparse)
Set the D_sparse matrix, using a single long vector with -1 as row terminators.
-
void set_decoder_id(uint32_t decoder_id)
Set the decoder id.
-
uint32_t get_decoder_id() const
Get the decoder id.
-
virtual bool enqueue_syndrome(const uint8_t *syndrome, std::size_t syndrome_length)
Enqueue a syndrome for decoding (pointer version)
- Returns:
True if enough syndromes have been enqueued to trigger a decode.
-
virtual bool enqueue_syndrome(const std::vector<uint8_t> &syndrome)
Enqueue a syndrome for decoding (vector version)
- Returns:
True if enough syndromes have been enqueued to trigger a decode.
-
virtual const uint8_t *get_obs_corrections() const
Get the current observable corrections.
-
std::size_t get_num_observables() const
Get the number of observables.
-
virtual void clear_corrections()
Clear any stored corrections.
-
virtual void reset_decoder()
Reset the decoder, clearing all per-shot memory and corrections.
-
inline virtual bool supports_graph_dispatch() const
Returns true if this decoder supports graph-based realtime dispatch via capture_decode_graph().
-
inline virtual void *capture_decode_graph(int reserved_sms = 0)
Capture a CUDA graph for realtime dispatch.
Returns a pointer to a cudaq::qec::realtime::graph_resources struct (caller must include realtime/graph_resources.h to interpret it). Returns nullptr if graph dispatch is not supported. The decoder retains ownership of the returned pointer.
-
inline virtual void release_decode_graph(void *graph_resources)
Release graph resources previously returned by capture_decode_graph().
-
virtual ~decoder() = default
Destructor.
-
virtual std::string get_version() const
Get the version of the decoder. Subclasses that are not part of the standard GitHub repo should override this to provide a more tailored version string.
- Returns:
A string containing the version of the decoder
Public Static Functions
-
static std::unique_ptr<decoder> get(const std::string &name, const decoder_init &init, const cudaqx::heterogeneous_map ¶m_map = cudaqx::heterogeneous_map())
Construct a registered decoder by name.
- Parameters:
name – The registered decoder name.
init – A parity-check matrix or raw Stim DEM string.
param_map – Optional decoder-specific parameters.
-
enum decode_result_type
-
struct decoder_result
Decoder results.
Public Members
-
bool converged = false
Whether or not the decoder converged.
-
std::vector<float_t> result
Vector of length
block_sizewith soft probabilities of errors in each index.
-
std::optional<cudaqx::heterogeneous_map> opt_results
Optional additional results from the decoder stored in a heterogeneous map. For equality comparison, this field is treated as a boolean flag - two decoder_results are considered equal only if both have empty opt_results (either std::nullopt or an empty map). If either result has non-empty opt_results, they are considered not equal.
-
bool converged = false
Built-in Decoders
NVIDIA QLDPC Decoder
- class nv_qldpc_decoder
A general purpose Quantum Low-Density Parity-Check Decoder (QLDPC) decoder based on GPU accelerated belief propagation (BP). Since belief propagation is an iterative method, decoding can be improved with a second-stage post-processing step. Optionally, ordered statistics decoding (OSD) can be chosen to perform the second stage of decoding.
An [[n,k,d]] quantum error correction (QEC) code encodes k logical qubits into an n qubit data block, with a code distance d. Quantum low-density parity-check (QLDPC) codes are characterized by sparse parity-check matrices (or Tanner graphs), corresponding to a bounded number of parity checks per data qubit.
Requires a CUDA-Q compatible GPU. See the CUDA-Q GPU Compatibility List for a list of valid GPU configurations.
References: Decoding Across the Quantum LDPC Code Landscape
Note
It is required to create decoders with the
get_decoderAPI from the CUDA-QX extension points API, such asimport cudaq_qec as qec import numpy as np H = np.array([[1, 0, 0, 1, 0, 1, 1], [0, 1, 0, 1, 1, 0, 1], [0, 0, 1, 0, 1, 1, 1]], dtype=np.uint8) # sample 3x7 PCM opts = dict() # see below for options # H may also be a scipy.sparse matrix (CSR, CSC, COO, or any # other scipy.sparse format), which avoids a full dense rows×cols # allocation for large PCMs. Any format is normalised to CSR # internally; no call to .toarray() or .todense() is needed. nvdec = qec.get_decoder('nv-qldpc-decoder', H, **opts)
std::size_t block_size = 7; std::size_t syndrome_size = 3; cudaqx::tensor<uint8_t> H; std::vector<uint8_t> H_vec = {1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1}; H.copy(H_vec.data(), {syndrome_size, block_size}); cudaqx::heterogeneous_map nv_custom_args; nv_custom_args.insert("use_osd", true); // See below for options auto nvdec = cudaq::qec::get_decoder("nv-qldpc-decoder", H, nv_custom_args);
Note
The
"nv-qldpc-decoder"implements thecudaq_qec.Decoderinterface for Python and thecudaq::qec::decoderinterface for C++, so it supports all the methods in those respective classes.- Parameters:
H – Parity check matrix (tensor format)
params –
Heterogeneous map of parameters:
cuda_device_id(int): Zero-based CUDA device ordinal on which to construct the decoder and run every decode. Must be>= 0and less than the number of visible GPUs. When omitted, the decoder is not pinned to a specific device and runs on the default device (GPU 0). Introduced in 0.7.0.use_sparsity(bool): Whether or not to use a sparse matrix solvererror_rate(double): Probability of an error (in 0-1 range) on a block data bit (defaults to 0.001)error_rate_vec(double): Vector of length “block size” containing the probability of an error (in 0-1 range) on a block data bit (defaults to 0.001). This overrideserror_rate.max_iterations(int): Maximum number of BP iterations to perform (defaults to 30)n_threads(int): Number of CUDA threads to use for the GPU decoder (defaults to smart selection based on parity matrix size)use_osd(bool): Whether or not to use an OSD post processor if the initial BP algorithm fails to converge on a solutionosd_method(int): 1=OSD-0, 2=Exhaustive, 3=Combination Sweep (defaults to 1). Ignored unlessuse_osdis true.osd_order(int): OSD postprocessor order (defaults to 0). Ref: Decoding Across the Quantum LDPC Code LandscapeFor
osd_method=2(Exhaustive), the number of possible permutations searched after OSD-0 grows by 2^osd_order.For
osd_method=3(Combination Sweep), this is the λ parameter. All weight 1 permutations and the first λ bits worth of weight 2 permutations are searched after OSD-0. This is (syndrome_length - block_size + λ * (λ - 1) / 2) additional permutations.For other
osd_methodvalues, this is ignored.
bp_batch_size(int): Number of syndromes that will be decoded in parallel for the BP decoder (defaults to 1)osd_batch_size(int): Number of syndromes that will be decoded in parallel for OSD (defaults to the number of concurrent threads supported by the hardware)iter_per_check(int): Number of iterations between BP convergence checks (defaults to 1, and max ismax_iterations). Introduced in 0.4.0.clip_value(float): Value to clip the BP messages to. Should be a non-negative value (defaults to 0.0, which disables clipping). Introduced in 0.4.0.repeatable(bool): Whether to make the BP algorithm (and Relay BP algorithm if enabled) bit-for-bit repeatable. Defaults to False. You must setclip_valueto a non-zero value to use this option. Setting this option to True makes it run approximately 5-10% slower, but you are guaranteed to get repeatable results, which is often useful for both timing and detailed syndrome analysis. Introduced in 0.6.0.bp_method(int): Core BP algorithm to use (defaults to 0). Introduced in 0.4.0, expanded in 0.5.0 and 0.7.0:0: sum-product
1: min-sum (introduced in 0.4.0)
2: min-sum+mem (uniform memory strength, requires
use_sparsity=True. Introduced in 0.5.0)3: min-sum+dmem (disordered memory strength, requires
use_sparsity=True. Introduced in 0.5.0)4: sum-product+mem (uniform memory strength, requires
use_sparsity=True. Introduced in 0.7.0)5: sum-product+dmem (disordered memory strength, requires
use_sparsity=True. Introduced in 0.7.0)
composition(int): Iteration strategy (defaults to 0). Introduced in 0.5.0:0: Standard (single run)
1: Sequential relay (multiple gamma legs). Requires:
bp_method=3(min-sum+dmem) orbp_method=5(sum-product+dmem),use_sparsity=True, andsrelay_config. Support forbp_method=5was added in 0.7.0.
scale_factor(float): The scale factor to use for min-sum. Defaults to 1.0. When set to 0.0, the scale factor is dynamically computed based on the number of iterations. Introduced in 0.4.0.proc_float(string): The processing float type to use. Defaults to “fp64”. Valid values are “fp32” and “fp64”. Introduced in 0.5.0.gamma0(float): Memory strength parameter. Required forbp_method=2(min-sum+mem) andbp_method=4(sum-product+mem), and forcomposition=1(sequential relay). Introduced in 0.5.0; extended in 0.7.0 forbp_method=4.gamma_dist(vector<float>): Gamma distribution interval [min, max] for disordered memory strength. Required forbp_method=3(min-sum+dmem) orbp_method=5(sum-product+dmem) ifexplicit_gammasnot provided. Introduced in 0.5.0; extended in 0.7.0 forbp_method=5.explicit_gammas(vector<vector<float>>): Explicit gamma values for each variable node. Forbp_method=3orbp_method=5withcomposition=0, provide a 2D vector where each row hasblock_sizecolumns. Forcomposition=1(Sequential relay), providenum_setsrows (one per relay leg). Overridesgamma_distif provided. Introduced in 0.5.0; extended in 0.7.0 forbp_method=5.srelay_config(heterogeneous_map): Sequential relay configuration (required forcomposition=1). Contains the following parameters. Introduced in 0.5.0:pre_iter(int): Number of pre-iterations to run before relay legsnum_sets(int): Number of relay sets (legs) to runstopping_criterion(string): When to stop relay legs:”All”: Run all legs
”FirstConv”: Stop relay after first convergence
”NConv”: Stop after N convergences (requires
stop_nconvparameter)
stop_nconv(int): Number of convergences to wait for before stopping (required only whenstopping_criterion="NConv")
Note
Starting in version 0.6.0, convergence during the
pre_iterphase counts as a successful convergence towards the stopping criteria. Prior to 0.6.0, convergence during pre-iterations did not count.bp_seed(int): Seed for random number generation used inbp_method=3orbp_method=5(disordered memory BP), or incomposition=1(sequential relay). Optional parameter, defaults to 42 if not provided. Introduced in 0.5.0.O(tensor<uint8_t>): Optional observables matrix with shape (num_observables, block_size). When provided,decode()anddecode_batch()return observable flips (O * correction (mod 2)) inDecoderResult.resultinstead of the raw decoded correction vector. Mutually exclusive with the realtimeenqueue_syndromepath: use one or the other, not both. Introduced in 0.7.0.opt_results(heterogeneous_map): Optional results to return. This field can be left empty if no additional results are desired. Choices are:bp_llr_history(int): Return the lastbp_llr_historyiterations of the BP LLR history. Minimum value is 0 and maximum value is max_iterations. The actual number of returned iterations might be fewer thanbp_llr_historyif BP converges before the requested number of iterations. Introduced in 0.4.0. Note: Not supported forcomposition=1.num_iter(bool): If true, return the number of BP iterations run. Introduced in 0.5.0.
gamma_ensemble_size(int): Number of parallel gamma trajectories (“lanes”) run per sequential-relay BP iteration. Allowed values are 1, 2, 4, and 8 (defaults to 1, which disables the ensemble). Each lane explores a distinct gamma set drawn fromgamma_dist(orexplicit_gammas). The constructor requiresnum_sets >= gamma_ensemble_sizeso the N per-lane gamma-set offsets stay pairwise distinct. Introduced in 0.7.0.Design semantics (race-to-fastest): the ensemble is optimized for decode latency. The kernel runs the N lanes in parallel and applies the user-supplied
stopping_criterionacross the ensemble: the first lane to satisfy the criterion stops the others. The winning lane is the converged lane with the lowest-weight correction (sum of error-rate LLRs over bits decoded as 1). If no lane converges across allnum_setslegs the decoder falls back to lane 0’s last-leg marginals. The speedups are maximized for the long-tail (p99) syndromes: easy syndromes converge in the first leg either way, while hard syndromes that would otherwise walk through many sequential legs get them raced in parallel instead.Warm-up (`pre_iter`) semantics: with the ensemble, the search diversity comes entirely from the relay legs, because each lane runs its own sequence of gamma sets. The
pre_iterwarm-up is the opposite: every lane would run it with the same uniformgamma0value, so the kernel computes it once (on one lane) and broadcasts the warmed-up marginals to every lane before the legs start. The warm-up helps convergence by settling the messages before the disordered gammas are applied and by letting easy syndromes converge early (which counts toward the stopping criterion), at the cost of delaying the start of the legs by the warm-up iterations. Ensemble speedups are therefore maximized whenpre_iteris small or zero.The gamma ensemble is supported on the sparse GPU single-decode path with
composition=1andbp_method=3(min-sum + dmem) orbp_method=5(sum-product + dmem). Passinggamma_ensemble_size > 1with any otherbp_methodor with the CPU or dense GPU path raisesstd::invalid_argumentat construction. The batched relay path (decode_batch()) and the realtime / graph-dispatch path (capture_decode_graph()) do not yet support ensembles and raise at call time; support forgamma_ensemble_size > 1withcapture_decode_graph()is intended in a future release.
Sliding Window Decoder
- class sliding_window
The Sliding Window Decoder is a wrapper around a standard decoder that introduces two key differences:
1. Sliding Window Decoding: The decoding process is performed incrementally, one window at a time. The window size is specified by the user. This allows decoding to begin before all syndromes have been received, potentially reducing overall latency in multi-round QEC codes.
2. Partial Syndrome Support: Unlike standard decoders, the
decodefunction (and its variants likedecode_batch) can accept partial syndromes. If partial syndromes are provided, the return vector will be empty, the decoder will not complete the processing and remain in an intermediate state, awaiting future syndromes. The return vector is only non-empty once enough data has been provided to match the original syndrome size (calculated from the Parity Check Matrix).Sliding window decoders are advantageous in QEC codes subject to circuit-level noise across multiple syndrome extraction rounds. These decoders permit syndrome processing to begin before the complete syndrome measurement sequence is obtained, potentially reducing the overall decoding latency. However, this approach introduces a trade-off: the reduction in latency typically comes at the cost of increased logical error rates. Therefore, the viability of sliding window decoding depends critically on the specific code parameters, noise model, and latency requirements of the system under consideration.
Sliding window decoding imposes only a single structural constraint on the parity check matrices: each syndrome extraction round must produce a constant number of syndrome measurements. Notably, the decoder makes no assumptions about temporal correlations or periodicity in the underlying noise process.
Streaming Syndrome Interface
For real-time applications, the decoder provides an
enqueue_syndrome()method that accepts syndrome data one round at a time. This allows the host to feed syndrome measurements as they arrive without waiting for all rounds to complete. The decoder automatically manages internal buffering and triggers window decodes at appropriate boundaries.References: Toward Low-latency Iterative Decoding of QLDPC Codes Under Circuit-Level Noise
Note
It is required to create decoders with the
get_decoderAPI from the CUDA-QX extension points API, such asimport cudaq import cudaq_qec as qec import numpy as np cudaq.set_target('stim') num_rounds = 5 code = qec.get_code('surface_code', distance=num_rounds) noise = cudaq.NoiseModel() noise.add_all_qubit_channel("x", cudaq.Depolarization2(0.001), 1) statePrep = qec.operation.prep0 dem = qec.dem_from_memory_circuit(code, statePrep, num_rounds, noise) inner_decoder_params = {'use_osd': True, 'max_iterations': 50} opts = { 'error_rate_vec': np.array(dem.error_rates), 'window_size': 1, 'num_syndromes_per_round': code.get_num_z_stabilizers() + code.get_num_x_stabilizers(), 'num_boundary_syndromes': code.get_num_z_stabilizers(), 'inner_decoder_name': 'single_error_lut', 'inner_decoder_params': inner_decoder_params, } swdec = qec.get_decoder('sliding_window', dem.detector_error_matrix, **opts)
#include "cudaq/qec/code.h" #include "cudaq/qec/decoder.h" #include "cudaq/qec/experiments.h" #include "common/NoiseModel.h" int main() { // Generate a Detector Error Model. int num_rounds = 5; auto code = cudaq::qec::get_code( "surface_code", cudaqx::heterogeneous_map{{"distance", num_rounds}}); cudaq::noise_model noise; noise.add_all_qubit_channel("x", cudaq::depolarization2(0.001), 1); auto statePrep = cudaq::qec::operation::prep0; auto dem = cudaq::qec::dem_from_memory_circuit(*code, statePrep, num_rounds, noise); // Use the DEM to create a sliding window decoder. auto inner_decoder_params = cudaqx::heterogeneous_map{{"use_osd", true}, {"max_iterations", 50}}; auto opts = cudaqx::heterogeneous_map{ {"error_rate_vec", dem.error_rates}, {"window_size", 1}, {"num_syndromes_per_round", code->get_num_z_stabilizers() + code->get_num_x_stabilizers()}, {"num_boundary_syndromes", code->get_num_z_stabilizers()}, {"inner_decoder_name", "single_error_lut"}, {"inner_decoder_params", inner_decoder_params}}; auto swdec = cudaq::qec::get_decoder("sliding_window", dem.detector_error_matrix, opts); return 0; }
Note
The
"sliding_window"decoder implements thecudaq_qec.Decoderinterface for Python and thecudaq::qec::decoderinterface for C++, so it supports all the methods in those respective classes.- Parameters:
H – Parity check matrix (tensor format)
params –
Heterogeneous map of parameters:
error_rate_vec(double): Vector of length “block size” containing the probability of an error (in 0-1 range). This vector is used to populate theerror_rate_vecparameter for the inner decoder (automatically sliced correctly according to each window).window_size(int): The number of rounds of syndrome data in each window. (Defaults to 1.)step_size(int): The number of rounds to advance the window by each time. (Defaults to 1.)num_syndromes_per_round(int): The number of syndromes per round. (Must be provided.)num_boundary_syndromes(int): The number of detectors in the first and last (boundary) round of the memory experiment. (Defaults to 0, meaning all layers havenum_syndromes_per_rounddetectors.) For a single-basis DEM fromz_dem_from_memory_circuit()(respectivelyx_dem_from_memory_circuit()), every layer hascode.get_num_z_stabilizers()(respectivelycode.get_num_x_stabilizers()) detectors, so this may be left at its default. For a full DEM fromdem_from_memory_circuit(),num_syndromes_per_roundiscode.get_num_z_stabilizers() + code.get_num_x_stabilizers()while the boundary rounds only carry the stabilizer type fixed by the state prep, so set this tocode.get_num_z_stabilizers()for Z-basis preps (prep0/prep1) orcode.get_num_x_stabilizers()for X-basis preps (prepp/prepm).straddle_start_round(bool): When forming a window, should error mechanisms that span the start round and any preceding rounds be included? (Defaults to False.)straddle_end_round(bool): When forming a window, should error mechanisms that span the end round and any subsequent rounds be included? (Defaults to True.)inner_decoder_name(string): The name of the inner decoder to use.inner_decoder_params(Python dict or C++heterogeneous_map): A dictionary of parameters to pass to the inner decoder.
TensorRT Decoder
- class trt_decoder
A GPU-accelerated quantum error correction decoder based on NVIDIA TensorRT. This decoder leverages TensorRT’s optimized inference engine to perform fast neural network-based decoding of quantum error correction syndromes.
The TRT decoder supports loading pre-trained neural network models in ONNX format or directly loading pre-built TensorRT engine files for maximum performance. It automatically optimizes the model for the target GPU architecture and supports various precision modes (FP16, BF16, INT8, FP8) to balance accuracy and speed.
Neural network-based decoders can be trained to perform syndrome decoding for specific quantum error correction codes and noise models. The TRT decoder provides a high-performance inference engine for these models, with automatic CUDA graph optimization for reduced latency.
Requires a CUDA-capable GPU and TensorRT installation. See the CUDA-Q GPU Compatibility List for a list of valid GPU configurations.
Note
It is required to create decoders with the
get_decoderAPI from the CUDA-QX extension points API, such asimport cudaq_qec as qec import numpy as np # Create a simple parity check matrix (not used by the TRT decoder) H = np.array([[1, 0, 0, 1, 0, 1, 1], [0, 1, 0, 1, 1, 0, 1], [0, 0, 1, 0, 1, 1, 1]], dtype=np.uint8) # Option 1: Load from ONNX model (builds TRT engine) trt_dec = qec.get_decoder('trt_decoder', H, onnx_load_path='model.onnx', precision='fp16', engine_save_path='model.engine') # Option 2: Load pre-built TRT engine (faster startup) trt_dec = qec.get_decoder('trt_decoder', H, engine_load_path='model.engine')
#include "cudaq/qec/decoder.h" std::size_t block_size = 7; std::size_t syndrome_size = 3; cudaqx::tensor<uint8_t> H; // Create a simple parity check matrix (not used by the TRT decoder) std::vector<uint8_t> H_vec = {1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1}; H.copy(H_vec.data(), {syndrome_size, block_size}); // Option 1: Load from ONNX model (builds TRT engine) cudaqx::heterogeneous_map params1; params1.insert("onnx_load_path", "model.onnx"); params1.insert("precision", "fp16"); params1.insert("engine_save_path", "model.engine"); auto trt_dec1 = cudaq::qec::get_decoder("trt_decoder", H, params1); // Option 2: Load pre-built TRT engine (faster startup) cudaqx::heterogeneous_map params2; params2.insert("engine_load_path", "model.engine"); auto trt_dec2 = cudaq::qec::get_decoder("trt_decoder", H, params2);
Note
The
"trt_decoder"implements thecudaq_qec.Decoderinterface for Python and thecudaq::qec::decoderinterface for C++, so it supports all the methods in those respective classes.Note
The parity check matrix
His not used by the TRT decoder. The neural network model encodes the decoding logic, so the parity check matrix is only required to satisfy the decoder interface. You can pass any valid parity check matrix of appropriate dimensions.Note
Batch Processing: The TRT decoder automatically handles batch size optimization. Models trained with batch_size > 1 will receive zero-padded inputs when using
decode()on a single syndrome. When usingdecode_batch(), provide syndromes in multiples of the model’s batch size for optimal performance.- Parameters:
H – Parity check matrix (tensor format). Note: This parameter is not used by the TRT decoder but is required by the decoder interface.
params –
Heterogeneous map of parameters:
Required (choose one):
onnx_load_path(string): Path to ONNX model file. The decoder will build a TensorRT engine from this model. Cannot be used together withengine_load_path.engine_load_path(string): Path to pre-built TensorRT engine file. Provides faster initialization since the engine is already optimized. Cannot be used together withonnx_load_path.
Optional:
engine_save_path(string): Path to save the built TensorRT engine. Only applicable when usingonnx_load_path. Saving the engine allows for faster initialization in subsequent runs by usingengine_load_path.precision(string): Precision mode for inference (defaults to “best”). Valid options:”fp16”: Use FP16 (half precision) - good balance of speed and accuracy
”bf16”: Use BF16 (bfloat16) - available on newer GPUs (Ampere+)
”int8”: Use INT8 quantization - fastest but requires calibration
”fp8”: Use FP8 precision - available on Hopper GPUs
”tf32”: Use TensorFloat-32 - available on Ampere+ GPUs
”noTF32”: Disable TF32 and use standard FP32
”best”: Let TensorRT automatically choose the best precision (default)
Note: If the requested precision is not supported by the hardware, the decoder will fall back to FP32 with a warning.
memory_workspace(size_t): Memory workspace size in bytes for TensorRT engine building (defaults to 1GB = 1073741824 bytes). Larger workspaces may allow TensorRT to explore more optimization strategies.use_cuda_graph(bool): Enable CUDA graph optimization for improved performance (defaults to True). CUDA graphs capture inference operations and replay them with reduced kernel launch overhead, providing ~20% speedup. The optimization is applied automatically on the first decode call. Automatically disabled for models with dynamic shapes or multiple optimization profiles. Set to False to force traditional execution path.batch_size(automatic): The decoder automatically detects the model’s batch size from the first input dimension. For models with batch_size > 1, thedecode()method automatically zero-pads single syndromes to fill the batch. Thedecode_batch()method requires the number of syndromes to be an integral multiple of the model’s batch size.global_decoder(string): Name of an optional second-stage “global” decoder to chain after the TensorRT model (composite decoding). The TRT model acts as a first-stage predecoder whose output is passed to the named global decoder (for example"pymatching"or"chromobius"). When omitted, the TRT model’s output is returned directly. See the real-time decoding API for configuring composite decoding from YAML. Introduced in 0.7.0.global_decoder_params(map): Parameters forwarded to theglobal_decoder. The accepted keys follow the parameter schema of the named global decoder. When constructing the decoder directly (rather than from YAML), supplyglobal_decoder_params(which may be an empty map) wheneverglobal_decoderis set; otherwise the global stage is skipped. Introduced in 0.7.0.cuda_device_id(int): Zero-based CUDA device ordinal on which to construct the decoder and run every decode. Must be>= 0and less than the number of visible GPUs. When omitted, the decoder is not pinned to a specific device and runs on the default device (GPU 0). Introduced in 0.7.0.
PyMatching Decoder
- class pymatching
A minimum-weight perfect matching (MWPM) decoder for matchable quantum error correction codes (such as the surface code), built on the open-source PyMatching library. It is a CPU decoder: each syndrome bit becomes a detector node, and each error (column of the parity-check matrix) with one or two set entries becomes a (boundary) edge whose weight is derived from the error prior.
Note
To use the decoder, use the
get_decoderAPI with a parity-check matrix as the decoder input:import cudaq_qec as qec import numpy as np # Parity check matrix. Each column (error mechanism) must have one # or two set entries so the graph is matchable. H = np.array([[1, 1, 0], [0, 1, 1]], dtype=np.uint8) dec = qec.get_decoder("pymatching", H, error_rate_vec=[0.1, 0.1, 0.1], merge_strategy="smallest_weight")
#include "cudaq/qec/decoder.h" cudaqx::heterogeneous_map params; params.insert("merge_strategy", std::string("smallest_weight")); auto dec = cudaq::qec::get_decoder("pymatching", H, params);
Note
The
"pymatching"decoder implements thecudaq_qec.Decoderinterface for Python and thecudaq::qec::decoderinterface for C++, so it supports all the methods in those respective classes.- Parameters:
H – Parity check matrix. Each column must have one or two set entries (matchable graph). In Python, a
scipy.sparsematrix or a dense NumPyuint8array may be passed.params –
Heterogeneous map of parameters:
error_rate_vec(vector<double>): Per-error prior probabilities, one per column ofH(lengthblock_size). Each value must lie in(0, 0.5]and sets the matching edge weight-log(p / (1 - p)). When omitted, all edge weights default to1.0.merge_strategy(string): How to combine parallel edges that map to the same pair of detectors. One of"disallow"(default for theH-only path),"independent","smallest_weight","keep_original", or"replace".O(tensor, optional): Anum_observables x block_sizebinary matrix. When provided, the decoder returns predicted observable flips (decode_to_obs) instead of a raw error vector, andmerge_strategydefaults to"independent"to match PyMatching’s detector-error-model construction.
Chromobius Decoder
- class chromobius
A decoder for color codes built on the open-source Chromobius Möbius decoder. Unlike the matrix-based decoders, Chromobius is detector-error-model native: it is constructed directly from Stim detector-error-model (DEM) text and predicts logical observable flips directly.
Note
To use the decoder, use the
get_decoderAPI with the Stim DEM text as the decoder input:import cudaq_qec as qec with open("color_code.dem") as f: dem_text = f.read() dec = qec.get_decoder("chromobius", dem_text) corrections = dec.decode(syndrome) # predicted observable flips
#include "cudaq/qec/decoder.h" std::string dem_text = /* Stim detector error model text */; cudaqx::heterogeneous_map params; auto dec = cudaq::qec::get_decoder("chromobius", dem_text, params);
Note
Chromobius is DEM-native: constructing it from a parity-check matrix is rejected with an error. Use
get_decoder("chromobius", dem_text, params). The DEM must describe a color code whose errors carry the color/basis annotations that Chromobius decomposes.Note
The
"chromobius"decoder implements thecudaq_qec.Decoderinterface for Python and thecudaq::qec::decoderinterface for C++. The wrapper currently returns observable flips as a 64-bit mask, so at most 64 logical observables are supported;decode()returns one bit per observable.- Parameters:
dem_text – Stim detector error model, as text. Detectors become the decoder syndrome bits; observables become the returned
block_sizecorrection bits.params –
- Heterogeneous map of parameters (all optional and boolean),
forwarded to the Chromobius decoder configuration:
drop_mobius_errors_involving_remnant_errors(bool)ignore_decomposition_failures(bool)include_coords_in_mobius_dem(bool)return_weight(bool): When true, the match weight is returned in the decode result’sopt_resultsunder the key"weight".write_mobius_match_to_stderr(bool)
Realtime Decoding
The Realtime Decoding API enables low-latency error correction on quantum hardware by allowing CUDA-Q quantum kernels to interact with decoders during circuit execution. This API is designed for use cases where corrections must be calculated and applied within qubit coherence times.
The real-time decoding system supports simulation environments for local testing and hardware integration (e.g., on Quantinuum’s Helios QPU).
Core Decoding Functions
These functions can be called from within CUDA-Q quantum kernels (__qpu__ functions) to interact with real-time decoders.
-
void cudaq::qec::decoding::enqueue_syndromes(std::uint64_t decoder_id, const std::vector<cudaq::measure_result> &syndromes, std::uint64_t tag = 0)
Enqueue syndromes for decoding.
- Parameters:
decoder_id – The ID of the decoder to use.
syndromes – The syndromes to enqueue.
tag – The tag to use for the syndrome (currently useful for logging only)
-
std::vector<bool> cudaq::qec::decoding::get_corrections(std::uint64_t decoder_id, std::uint64_t return_size, bool reset = false)
Get the corrections for a given decoder.
- Parameters:
decoder_id – The ID of the decoder to use.
return_size – The number of bits to return (in bits). This is expected to match the number of observables in the decoder.
reset – Whether to reset the decoder corrections after retrieving them.
- Returns:
The corrections (detected bit flips) for the given decoder, based on all of the decoded syndromes since the last time any corrections were reset.
Configuration API
The configuration API enables setting up decoders before circuit execution. Decoders are configured using YAML files or programmatically constructed configuration objects.
-
struct graph_resources
Resources returned by decoder::capture_decode_graph().
The decoder plugin captures a CUDA graph internally and populates this struct. The host dispatcher (libcudaq-realtime-host-dispatch) uses graph_exec / stream to launch the graph, and writes per-slot I/O addresses into h_mailbox before each launch. function_id is used by the host dispatcher to route RPC requests to the correct graph worker.
-
enum class cudaq::qec::decoding::config::DecoderTransport
Transport type for a decoder session. cpu_roce: CpuRoceTransceiver / SoftRoCE (dev, CI, no GPU required) gpu_roce: GpuRoceTransceiver / DOCA (production, real ConnectX)
Values:
-
enumerator cpu_roce
-
enumerator gpu_roce
-
enumerator cpu_roce
-
struct decoder_config
Configuration structure for decoder options.
Public Functions
-
cudaqx::heterogeneous_map decoder_custom_args_to_heterogeneous_map() const
Return the parameter map a decoder’s constructor should receive: the stored custom args with schema-declared defaults materialized (see materialize_default_args in cudaq/qec/decoder_config_schema.h) when a schema is registered for
type, so programmatically built configs get the same defaulting the YAML parse path applies.
-
void validate_custom_args() const
Validate
decoder_custom_argsagainst the parameter schema registered fortype: unknown keys, missing required keys, and the schema’s own validate hook (if any). Throws std::runtime_error on the first violation. YAML parsing applies the same checks automatically; call this to vet a configuration built programmatically before using it.
Public Members
-
DecoderTransport transport = DecoderTransport::cpu_roce
Transport used to receive syndromes and send corrections for this decoder. Defaults to cpu_roce. Set to gpu_roce for decoders where syndrome bits are DMA’d directly to GPU VRAM (e.g. nv_qldpc_decoder with RelayBP).
-
std::optional<int> cuda_device_id
CUDA device this decoder is pinned to at construction (see the “cuda_device_id” decoder parameter). Placement knob common to any GPU-accelerated decoder, hence at this level rather than inside the per-decoder custom args. Unset = unpinned.
-
cudaqx::heterogeneous_map decoder_custom_args_to_heterogeneous_map() const
-
class multi_decoder_config
Public Functions
-
void validate_custom_args() const
Validate every decoder’s custom args (see decoder_config::validate_custom_args).
-
void validate_custom_args() const
-
int cudaq::qec::decoding::config::configure_decoders(multi_decoder_config &config)
Configure the decoders (
multi_decoder_configvariant). This function configures both local decoders, and if running on remote target hardware, will submit the configuration to the remote target for further processing.- Parameters:
config – The configuration to use.
- Returns:
0 on success, non-zero on failure.
-
int cudaq::qec::decoding::config::configure_decoders_from_file(const char *config_file)
Configure the decoders from a file. This function configures both local decoders, and if running on remote target hardware, will submit the configuration to the remote target for further processing.
- Parameters:
config_file – The file to read the configuration from.
- Returns:
0 on success, non-zero on failure.
-
int cudaq::qec::decoding::config::configure_decoders_from_str(const char *config_str)
Configure the decoders from a string. This function configures both local decoders, and if running on remote target hardware, will submit the configuration to the remote target for further processing.
- Parameters:
config_str – The string to read the configuration from.
- Returns:
0 on success, non-zero on failure.
Helper Functions
Realtime decoding requires converting matrices to sparse format for efficient decoder configuration. The following utility functions are essential:
cudaq::qec::pcm_to_sparse_vec()for converting a dense PCM to a sparse PCM.cudaq::qec::pcm_from_sparse_vec()for converting a sparse PCM to a dense PCM.cudaq::qec::d_sparse()for converting anM2DSparseMatrix(obtained from acudaq::qec::decoder_inputscomponent) into the-1-terminated sparse vector a decoder config expects forD_sparse.Usage in real-time decoding:
auto ctx = cudaq::qec::decoder_context_from_memory_circuit( code, statePrep, numRounds, noise); auto inputs = ctx.z_component(); // or x_component() / full_component() config.H_sparse = cudaq::qec::pcm_to_sparse_vec(inputs.dem.detector_error_matrix); config.O_sparse = cudaq::qec::pcm_to_sparse_vec(inputs.dem.observables_flips_matrix); config.D_sparse = cudaq::qec::d_sparse(inputs.m2d);
See also Parity Check Matrix Utilities for additional PCM manipulation functions.
Realtime Pipeline API
The realtime pipeline API provides the reusable host-side runtime for
low-latency QEC pipelines that combine GPU inference with optional CPU
post-processing. The published reference is generated from
cudaq/qec/realtime/pipeline.h.
Note
This API is experimental and subject to change.
Configuration
-
struct core_pinning
CPU core affinity settings for pipeline threads.
-
struct pipeline_stage_config
Configuration for a single pipeline stage.
Public Members
-
int num_workers = 8
Number of GPU worker threads (max 64).
-
int num_slots = 32
Number of ring buffer slots.
-
size_t slot_size = 16384
Size of each ring buffer slot in bytes.
-
core_pinning cores
CPU core affinity settings.
-
void *external_ringbuffer = nullptr
When non-null, the pipeline uses this caller-owned ring buffer (cudaq_ringbuffer_t*) instead of allocating its own. The caller is responsible for lifetime. ring_buffer_injector is unavailable in this mode (the FPGA/emulator owns the producer side).
-
int num_workers = 8
GPU Stage
-
struct gpu_worker_resources
Per-worker GPU resources returned by the gpu_stage_factory.
Each worker owns a captured CUDA graph, a dedicated stream, and optional pre/post launch callbacks for DMA staging or result extraction.
Public Members
-
cudaGraphExec_t graph_exec = nullptr
Instantiated CUDA graph for this worker.
-
cudaStream_t stream = nullptr
Dedicated CUDA stream for graph launches.
-
void (*pre_launch_fn)(void *user_data, void *slot_dev, cudaStream_t stream) = nullptr
Optional callback invoked before graph launch (e.g. DMA copy).
-
void *pre_launch_data = nullptr
Opaque user data passed to
pre_launch_fn.
-
void (*post_launch_fn)(void *user_data, void *slot_dev, cudaStream_t stream) = nullptr
Optional callback invoked after graph launch.
-
void *post_launch_data = nullptr
Opaque user data passed to
post_launch_fn.
-
uint32_t function_id = 0
RPC function ID that this worker handles.
-
void *user_context = nullptr
Opaque user context passed to cpu_stage_callback.
-
cudaGraphExec_t graph_exec = nullptr
-
using cudaq::qec::realtime::experimental::gpu_stage_factory = std::function<gpu_worker_resources(int worker_id)>
Factory called once per worker during start().
- Param worker_id:
Zero-based worker index assigned by the pipeline.
- Return:
GPU resources for the given worker. Any handles, callbacks, and user data returned here must remain valid until the pipeline stops.
CPU Stage
-
struct cpu_stage_context
Context passed to the CPU stage callback for each completed GPU workload.
The callback reads
gpu_output, performs post-processing (e.g. MWPM decoding), and writes the result intoresponse_buffer.Public Members
-
int worker_id
Index of the worker thread invoking this callback.
-
int origin_slot
Ring buffer slot that originated this request.
-
const void *gpu_output
Pointer to GPU inference output (nullptr in poll mode).
-
size_t gpu_output_size
Size of GPU output in bytes.
-
void *response_buffer
Destination buffer for the RPC response.
-
size_t max_response_size
Maximum number of bytes that can be written to
response_buffer.
-
void *user_context
Opaque user context from gpu_worker_resources::user_context.
-
int worker_id
-
using cudaq::qec::realtime::experimental::cpu_stage_callback = std::function<size_t(const cpu_stage_context &ctx)>
CPU stage callback type.
- Param ctx:
Poll-mode view of the current worker state and response buffer.
- Return:
Number of bytes written into
ctx.response_buffer. Return 0 if no GPU result is ready yet (poll again). Return DEFERRED_COMPLETION to release the worker immediately while deferring slot completion to a later complete_deferred() call.
-
static constexpr size_t cudaq::qec::realtime::experimental::DEFERRED_COMPLETION = SIZE_MAX
Sentinel return value from cpu_stage_callback: release the worker (idle_mask) but do NOT signal slot completion (tx_flags). The caller is responsible for calling realtime_pipeline::complete_deferred(slot) once the deferred work (e.g. a separate decode thread) finishes.
Note
External rings use the overload that also takes the request ID.
Completion
-
struct completion
Metadata for a completed (or errored) pipeline request.
-
using cudaq::qec::realtime::experimental::completion_callback = std::function<void(const completion &c)>
Callback invoked by the consumer thread for each completed request.
Note
For an external ring, the callback runs synchronously on the CPU worker or deferred-completion thread instead.
- Param c:
Metadata for the completed or errored request.
Ring Buffer Injector
-
class ring_buffer_injector
Writes RPC-framed requests into the pipeline’s ring buffer, simulating FPGA DMA deposits.
Created via realtime_pipeline::create_injector(). The parent realtime_pipeline must outlive the injector. Not available when the pipeline is configured with an external ring buffer.
Public Functions
-
~ring_buffer_injector()
Destroy the injector state.
-
ring_buffer_injector(ring_buffer_injector&&) noexcept
Move-construct an injector.
-
ring_buffer_injector &operator=(ring_buffer_injector&&) noexcept
Move-assign an injector.
-
bool try_submit(uint32_t function_id, const void *payload, size_t payload_size, uint64_t request_id)
Try to submit a request without blocking.
- Parameters:
function_id – RPC function identifier.
payload – Pointer to the payload data.
payload_size – Size of the payload in bytes.
request_id – Caller-assigned request identifier.
- Returns:
True if accepted, false if all slots are busy (backpressure).
-
void submit(uint32_t function_id, const void *payload, size_t payload_size, uint64_t request_id)
Submit a request, spinning until a slot becomes available.
- Parameters:
function_id – RPC function identifier.
payload – Pointer to the payload data.
payload_size – Size of the payload in bytes.
request_id – Caller-assigned request identifier.
-
~ring_buffer_injector()
Pipeline
-
class realtime_pipeline
Orchestrates GPU inference and CPU post-processing for low-latency realtime QEC decoding.
The pipeline manages a ring buffer, a host dispatcher thread, per-worker GPU streams with captured CUDA graphs, optional CPU worker threads for post-processing (e.g. PyMatching), and a consumer thread for completion signaling. It supports both an internal ring buffer (for software testing via ring_buffer_injector) and an external ring buffer (for FPGA RDMA).
Note
External transports own TX flag consumption, so the consumer thread is only created for an internal ring.
Public Functions
-
explicit realtime_pipeline(const pipeline_stage_config &config)
Construct a pipeline and allocate ring buffer resources.
Note
Construction allocates the backing ring buffer or binds the caller-provided external ring so ringbuffer_bases can be queried before start.
- Parameters:
config – Stage configuration (slots, slot size, workers, etc.).
-
~realtime_pipeline()
Stop the pipeline if needed and release owned resources.
-
void set_gpu_stage(gpu_stage_factory factory)
Register the GPU stage factory. Must be called before start().
- Parameters:
factory – Callback that returns gpu_worker_resources per worker.
-
void set_cpu_stage(cpu_stage_callback callback)
Register the CPU worker callback. Must be called before start().
- Parameters:
callback – Function invoked by each worker thread to poll for and process completed GPU workloads. If not set, the pipeline operates in GPU-only mode with completion signaled via cudaLaunchHostFunc.
-
void set_completion_handler(completion_callback handler)
Register the completion callback. Must be called before start().
Note
For an external ring, the handler runs on the CPU worker or deferred-completion thread instead.
- Parameters:
handler – Function invoked by the consumer thread for each completed or errored request.
-
void start()
Allocate resources, build dispatcher config, and spawn all threads.
- Throws:
std::logic_error – If the GPU stage factory was not registered.
std::logic_error – If GPU-only mode is requested with an external ring buffer.
-
void stop()
Signal shutdown, join all threads, free resources.
Note
Safe to call multiple times. Subsequent calls are no-ops once the pipeline has fully stopped.
-
ring_buffer_injector create_injector()
Create a software injector for testing without FPGA hardware.
- Throws:
std::logic_error – if the pipeline uses an external ring buffer.
- Returns:
A ring_buffer_injector bound to this pipeline’s ring buffer.
-
void complete_deferred(int slot)
Signal that deferred processing for a slot is complete.
Call from any thread after the cpu_stage callback returned DEFERRED_COMPLETION and the deferred work has finished writing the response into the slot’s ring buffer area.
Note
This overload is for an internal ring buffer.
- Parameters:
slot – Ring buffer slot index to complete.
- Throws:
std::logic_error – If the pipeline uses an external ring buffer.
std::out_of_range – If
slotis not a valid ring slot.
-
void complete_deferred(int slot, uint64_t request_id)
Signal external-ring deferred processing for a slot is complete.
Publishes the caller-written TX response, accounts the completion, and invokes the registered completion callback synchronously.
- Parameters:
slot – Ring buffer slot index to complete.
request_id – Original RPC request identifier.
- Throws:
std::logic_error – If the pipeline uses an internal ring buffer.
std::out_of_range – If
slotis not a valid ring slot.
-
ring_buffer_bases ringbuffer_bases() const
Return the host and device base addresses of the RX data ring.
Note
In external-ring mode these pointers are the caller-provided ring addresses. In internal mode they refer to the owned mapped ring buffer.
- Returns:
Struct containing both base pointers.
-
struct ring_buffer_bases
Host and device base addresses of the RX data ring.
-
struct Stats
Pipeline throughput and backpressure statistics.
Public Members
-
uint64_t submitted
Total requests submitted to the ring buffer.
Note
External transports own submission, so this remains zero when an external ring buffer is used.
-
uint64_t completed
Total requests that completed (success or error).
-
uint64_t dispatched
Total packets dispatched by the host dispatcher.
-
uint64_t backpressure_stalls
Cumulative producer backpressure stalls.
-
uint64_t submitted
-
explicit realtime_pipeline(const pipeline_stage_config &config)
Parity Check Matrix Utilities
The utilities below create, convert, inspect, and transform parity-check
matrices (PCMs). CUDA-Q QEC supports dense matrices as
cudaqx::tensor<std::uint8_t> and sparse matrices as
sparse_binary_matrix. Decoder entry points accept either representation
and store the PCM internally as a sparse matrix.
sparse_binary_matrix stores a binary matrix in compressed sparse column
(CSC) or compressed sparse row (CSR) layout without storing values for its
nonzero entries. Input indices are preserved as supplied. Use
cudaq::qec::sparse_binary_matrix::canonicalize() when duplicate
indices should be combined over GF(2) and each compressed row or column should
be sorted. Canonicalization preserves the matrix layout. The matrix uses
std::uint32_t indices, so each dimension and the number of stored entries
must fit in that type.
Sparse utility overloads operate without materializing the full input as a
dense tensor. Use
cudaq::qec::generate_random_pcm_sparse() when a generated PCM would
be impractical to allocate densely. The dense generator remains available and
rejects dimensions whose products overflow std::size_t.
-
enum class cudaq::qec::sparse_binary_matrix_layout
Storage layout for the sparse PCM: Compressed Sparse Column (CSC) or Compressed Sparse Row (CSR). All non-zero entries are assumed to be 1; values are not stored.
Values:
-
enumerator csc
-
enumerator csr
-
enumerator csc
-
class sparse_binary_matrix
Sparse parity-check matrix in either CSC or CSR form.
Input index lists are stored as given: not required to be sorted or GF(2)-unique. Consumers that require cuSPARSE-style compressed groups can call
validate_sorted_unique_indices; consumers that need GF(2)-collapsed per-group indices can callcanonicalizeon entry.index_typeisuint32_t, so each dimension andnnzmust fit in~4×10^9.Public Functions
-
sparse_binary_matrix(const cudaqx::tensor<std::uint8_t> &dense, sparse_binary_matrix_layout layout = sparse_binary_matrix_layout::csc)
Construct from a rank-2 dense PCM (any non-zero treated as 1). Intentionally not
explicitso call sites that takesparse_binary_matrixaccept a densecudaqx::tensorunchanged.
-
inline const std::vector<index_type> &ptr() const
For CSC: ptr has length num_cols+1; for CSR: ptr has length num_rows+1.
-
inline const std::vector<index_type> &indices() const
For CSC: row indices; for CSR: column indices.
-
void validate_sorted_unique_indices(const char *context = "sparse_binary_matrix") const
Throw if each compressed column/row does not have strictly increasing indices. This rejects duplicate entries in the stored layout.
-
sparse_binary_matrix canonicalize() const
Return a GF(2)-canonical copy: each compressed column/row has its indices sorted ascending, and duplicate indices are XOR-merged. An index that appears k times is kept iff k is odd. The output has the same layout as the input and is idempotent under further
canonicalizecalls.Use this when a PCM source, such as a DEM decomposition, may emit duplicate indices within a compressed group and the caller wants GF(2) duplicate-collapse semantics before passing the matrix to consumers that require at most one entry per row/column.
-
sparse_binary_matrix to_csc() const
Return a copy of this matrix in CSC layout. No-op if already CSC.
-
sparse_binary_matrix to_csr() const
Return a copy of this matrix in CSR layout. No-op if already CSR.
-
cudaqx::tensor<std::uint8_t> to_dense() const
Convert to a dense PCM tensor (rows x columns). Non-zero entries are set to 1.
-
std::vector<std::vector<index_type>> to_nested_csc() const
Nested CSC: outer vector has size num_cols; inner vector for column j lists row indices of non-zeros in that column.
-
std::vector<std::vector<index_type>> to_nested_csr() const
Nested CSR: outer vector has size num_rows; inner vector for row i lists column indices of non-zeros in that row.
Public Static Functions
-
static sparse_binary_matrix from_csc(index_type num_rows, index_type num_cols, std::vector<index_type> col_ptrs, std::vector<index_type> row_indices)
Construct a sparse PCM in CSC form.
- Parameters:
num_rows – Number of rows.
num_cols – Number of columns.
col_ptrs – Column pointer array (length num_cols + 1); column j has indices in
row_indices[col_ptrs[j] .. col_ptrs[j+1]-1].row_indices – Row indices of non-zeros (length nnz).
-
static sparse_binary_matrix from_csr(index_type num_rows, index_type num_cols, std::vector<index_type> row_ptrs, std::vector<index_type> col_indices)
Construct a sparse PCM in CSR form.
- Parameters:
num_rows – Number of rows.
num_cols – Number of columns.
row_ptrs – Row pointer array (length num_rows + 1); row i has indices in
col_indices[row_ptrs[i] .. row_ptrs[i+1]-1].col_indices – Column indices of non-zeros (length nnz).
-
static sparse_binary_matrix from_nested_csc(index_type num_rows, index_type num_cols, const std::vector<std::vector<index_type>> &nested)
Construct from nested CSC:
nested[j] is the list of row indices for column j;nested.size()must equalnum_cols.
-
static sparse_binary_matrix from_nested_csr(index_type num_rows, index_type num_cols, const std::vector<std::vector<index_type>> &nested)
Construct from nested CSR:
nested[i] is the list of column indices for row i;nested.size()must equalnum_rows.
-
sparse_binary_matrix(const cudaqx::tensor<std::uint8_t> &dense, sparse_binary_matrix_layout layout = sparse_binary_matrix_layout::csc)
-
cudaqx::tensor<uint8_t> cudaq::qec::to_parity_matrix(const std::vector<cudaq::spin_op_term> &stabilizers, stabilizer_type type = stabilizer_type::XZ)
Convert stabilizers to a parity check matrix
- Returns:
Tensor representing the parity check matrix
-
cudaqx::tensor<uint8_t> cudaq::qec::to_parity_matrix(const std::vector<std::string> &words, stabilizer_type type = stabilizer_type::XZ)
-
std::vector<std::vector<std::uint32_t>> cudaq::qec::dense_to_sparse(const cudaqx::tensor<uint8_t> &pcm)
Return a sparse representation of the PCM.
- Parameters:
pcm – The PCM to convert to a sparse representation.
- Returns:
A vector of vectors that sparsely represents the PCM. The size of the outer vector is the number of columns in the PCM, and the i-th element contains an inner vector of the row indices of the non-zero elements in the i-th column of the PCM.
-
cudaqx::tensor<uint8_t> cudaq::qec::generate_random_pcm(std::size_t n_rounds, std::size_t n_errs_per_round, std::size_t n_syndromes_per_round, int weight, std::mt19937_64 &&rng)
Generate a random PCM with the given parameters.
The PCM has shape
(n_rounds * n_syndromes_per_round) × (n_rounds * n_errs_per_round).- Parameters:
n_rounds – The number of rounds in the PCM.
n_errs_per_round – The number of errors per round in the PCM.
n_syndromes_per_round – The number of syndromes per round in the PCM.
weight – The column weight of the PCM.
rng – The random number generator to use (e.g. std::mt19937_64(your_seed))
- Returns:
A random PCM with the given parameters.
-
sparse_binary_matrix cudaq::qec::generate_random_pcm_sparse(std::size_t n_rounds, std::size_t n_errs_per_round, std::size_t n_syndromes_per_round, int weight, std::mt19937_64 &&rng)
Same distribution as generate_random_pcm, but constructs a CSC sparse_binary_matrix directly without allocating a dense rank-2 tensor. Intended for large PCMs whose dense form would be impractical or impossible to allocate.
-
std::vector<std::int64_t> cudaq::qec::generate_timelike_sparse_detector_matrix(std::uint32_t num_syndromes_per_round, std::uint32_t num_rounds, bool include_first_round = false)
Generate a sparse detector matrix for a given number of syndromes per round and number of rounds. Timelike here means that each round of syndrome bits are xor’d against the preceding round.
- Parameters:
num_syndromes_per_round – The number of syndromes per round.
num_rounds – The number of rounds.
include_first_round – Whether to include the first round in the detector matrix.
- Returns:
The detector matrix format is CSR-like, with -1 values indicating the end of a row.
-
std::vector<std::int64_t> cudaq::qec::generate_timelike_sparse_detector_matrix(std::uint32_t num_syndromes_per_round, std::uint32_t num_rounds, std::vector<std::int64_t> first_round_matrix)
Generate a sparse detector matrix for a given number of syndromes per round and number of rounds. Timelike here means that each round of syndrome bits are xor’d against the preceding round. The first round is supplied by the user, to allow for a mixture of detectors and non-detectors.
- Parameters:
num_syndromes_per_round – The number of syndromes per round.
num_rounds – The number of rounds.
first_round_matrix – User specified detector matrix for the first round.
- Returns:
The detector matrix format is CSR-like, with -1 values indicating the end of a row.
-
std::tuple<cudaqx::tensor<uint8_t>, std::uint32_t, std::uint32_t> cudaq::qec::get_pcm_for_rounds(const cudaqx::tensor<uint8_t> &pcm, std::uint32_t num_syndromes_per_round, std::uint32_t start_round, std::uint32_t end_round, bool straddle_start_round = false, bool straddle_end_round = false, std::uint32_t num_boundary_syndromes = 0)
Get a sub-PCM for a range of rounds. It is recommended (but not required) that you call sort_pcm_columns() before calling this function.
- Parameters:
pcm – The PCM to get a sub-PCM for.
num_syndromes_per_round – The number of syndromes per round.
start_round – The start round (0-based).
end_round – The end round (0-based).
straddle_start_round – Whether to include columns that straddle the start_round (defaults to false)
straddle_end_round – Whether to include columns that straddle the end_round (defaults to false)
num_boundary_syndromes – Width of the narrower first/last boundary rounds for a non-uniform [B | K*S | B] detector layout (0 == uniform).
- Returns:
A tuple with the new PCM with the columns in the range [start_round, end_round], the first column included, and the last column included.
-
std::tuple<cudaqx::tensor<uint8_t>, std::uint32_t, std::uint32_t> cudaq::qec::get_pcm_for_rounds(const sparse_binary_matrix &pcm, std::uint32_t num_syndromes_per_round, std::uint32_t start_round, std::uint32_t end_round, bool straddle_start_round = false, bool straddle_end_round = false, bool pcm_is_canonical = false, std::uint32_t num_boundary_syndromes = 0)
Same semantics as the overload taking a dense tensor
pcm, but reads frompcmassparse_binary_matrixso the full dense PCM is not required (only the returned sub-matrix is dense). Parameter meanings match the dense overload.Warning
When
pcm_is_canonicalistruethe precondition is not checked.select_pcm_columns_for_round_rangereads.front()/.back()of each CSC column to derive first/last round; those are only the true min/max row if the column list is sorted-unique. Passing a non-canonical PCM with this flag (e.g. a raw DEM decomposition, a hand-built sparse matrix with duplicate or unsorted indices, or a canonical CSR that the caller forgot to re-canonicalize after conversion) silently produces wrong round assignments. If unsure, leave the flagfalse.- Parameters:
pcm – The PCM to get a sub-PCM for.
num_syndromes_per_round – The number of syndromes per round.
start_round – The start round (0-based).
end_round – The end round (0-based).
straddle_start_round – Whether to include columns that straddle the start_round (defaults to false)
straddle_end_round – Whether to include columns that straddle the end_round (defaults to false)
pcm_is_canonical – If true, the caller asserts
pcmhas sorted-unique per-group indices (i.e. is the output ofsparse_binary_matrix::canonicalizeor was constructed canonically). In that case the per-call canonicalization step is skipped — useful for callers likesliding_windowthat canonicalize once at construction and then call this function in a per-window loop on the same matrix. Defaultfalsepreserves the original “canonicalize on entry” behavior.num_boundary_syndromes – Width of the narrower first/last boundary rounds for a non-uniform [B | K*S | B] detector layout (0 == uniform).
- Returns:
A tuple with the new PCM with the columns in the range [start_round, end_round], the first column included, and the last column included.
-
std::vector<std::uint32_t> cudaq::qec::get_sorted_pcm_column_indices(const std::vector<std::vector<std::uint32_t>> &row_indices, std::uint32_t num_syndromes_per_round = 0)
Return a vector of column indices that would sort the PCM columns in topological order.
This function tries to make a matrix that is close to a block diagonal matrix from its input. Columns are first sorted by the index of the first non-zero entry in the column, and if those match, then they are sorted by the index of the last non-zero entry in the column. This ping pong continues for the indices of the second non-zero element and the second-to-last non-zero element, and so forth.
- Parameters:
row_indices – For each column, a vector of row indices that have a non-zero value in that column.
num_syndromes_per_round – The number of syndromes per round. (Defaults to 0, which means that no secondary per-round sorting will occur.)
-
std::vector<std::uint32_t> cudaq::qec::get_sorted_pcm_column_indices(const std::vector<std::vector<std::uint32_t>> &row_indices, std::uint32_t num_syndromes_per_round, std::uint32_t num_boundary_syndromes)
Boundary-aware overload of the above: the first and last
num_boundary_syndromesrows form the boundary rounds and each interior round spansnum_syndromes_per_roundrows. throws std::invalid_argument ifnum_syndromes_per_roundis zero ornum_boundary_syndromes>num_syndromes_per_round.
-
std::vector<std::uint32_t> cudaq::qec::get_sorted_pcm_column_indices(const cudaqx::tensor<uint8_t> &pcm, std::uint32_t num_syndromes_per_round = 0)
Return a vector of column indices that would sort the PCM columns in topological order.
- Parameters:
pcm – The PCM to sort.
num_syndromes_per_round – The number of syndromes per round. (Defaults to 0, which means that no secondary per-round sorting will occur.)
-
std::pair<cudaqx::tensor<uint8_t>, std::vector<std::uint32_t>> cudaq::qec::pcm_extend_to_n_rounds(const cudaqx::tensor<uint8_t> &pcm, std::size_t num_syndromes_per_round, std::uint32_t n_rounds)
Extend a PCM to the given number of rounds.
- Parameters:
pcm – The PCM to extend.
num_syndromes_per_round – The number of syndromes per round.
n_rounds – The number of rounds to extend the PCM to.
- Returns:
A pair of the new PCM and the list of column indices from the original PCM that were used to form the new PCM.
-
cudaqx::tensor<uint8_t> cudaq::qec::pcm_from_sparse_string(const std::string &sparse_str, std::size_t num_rows, std::size_t num_cols)
Return a PCM from a sparse representation.
- Parameters:
sparse_str – The sparse representation of the PCM.
num_rows – The number of rows in the PCM.
num_cols – The number of columns in the PCM.
- Returns:
A PCM tensor.
-
cudaqx::tensor<uint8_t> cudaq::qec::pcm_from_sparse_vec(const std::vector<std::int64_t> &sparse_vec, std::size_t num_rows, std::size_t num_cols)
Return a PCM from a sparse representation.
- Parameters:
sparse_vec – The sparse representation of the PCM, where -1 separates rows.
num_rows – The number of rows in the PCM.
num_cols – The number of columns in the PCM.
- Returns:
A PCM tensor.
-
bool cudaq::qec::pcm_is_sorted(const cudaqx::tensor<uint8_t> &pcm, std::uint32_t num_syndromes_per_round = 0)
Check if a PCM is sorted.
- Parameters:
pcm – The PCM to check.
num_syndromes_per_round – The number of syndromes per round.
- Returns:
True if the PCM is sorted, false otherwise.
-
bool cudaq::qec::pcm_is_sorted(const std::vector<std::vector<std::uint32_t>> &sparse_pcm, std::uint32_t num_syndromes_per_round, std::uint32_t num_boundary_syndromes)
Boundary-aware overload of pcm_is_sorted: the first and last
num_boundary_syndromesrows form the boundary rounds and each interior round spansnum_syndromes_per_roundrows (a [B | K*S | B] layout).- Parameters:
sparse_pcm – The sparse PCM to check (in the same format as dense_to_sparse())
num_syndromes_per_round – The interior-round width.
num_boundary_syndromes – The width of the first/last boundary rounds.
- Returns:
True if the PCM is sorted for this boundary layout, false otherwise.
-
std::string cudaq::qec::pcm_to_sparse_string(const cudaqx::tensor<uint8_t> &pcm)
Return a sparse representation of the PCM as a string.
- Parameters:
pcm – The PCM to convert to a sparse representation.
- Returns:
A string that represents the PCM in a sparse format.
-
std::string cudaq::qec::pcm_to_sparse_string(const sparse_binary_matrix &pcm)
Sparse overload of pcm_to_sparse_string. Duplicate stored entries are canonicalized with GF(2) cancellation before serialization.
-
std::vector<std::int64_t> cudaq::qec::pcm_to_sparse_vec(const cudaqx::tensor<uint8_t> &pcm)
Return a sparse representation of the PCM.
- Parameters:
pcm – The PCM to convert to a sparse representation.
- Returns:
A vector of integers that represents the PCM in a sparse format, where -1 separates rows.
-
std::vector<std::int64_t> cudaq::qec::pcm_to_sparse_vec(const sparse_binary_matrix &pcm)
Sparse overload of pcm_to_sparse_vec. Duplicate stored entries are canonicalized with GF(2) cancellation before serialization.
-
cudaqx::tensor<uint8_t> cudaq::qec::reorder_pcm_columns(const cudaqx::tensor<uint8_t> &pcm, const std::vector<std::uint32_t> &column_order, uint32_t row_begin = 0, uint32_t row_end = std::numeric_limits<uint32_t>::max())
Reorder the columns of a PCM according to the given column order. Note: this may return a subset of the columns in the original PCM if the
column_orderdoes not contain all of the columns in the original PCM.- Parameters:
pcm – The PCM to reorder.
column_order – The column order to use for reordering.
row_begin – The first row to include in the reordering. Leave at the default value to include all rows.
row_end – The last row to include in the reordering. Leave at the default value to include all rows.
- Returns:
A new PCM with the columns reordered according to the given column order.
-
sparse_binary_matrix cudaq::qec::reorder_pcm_columns(const sparse_binary_matrix &pcm, const std::vector<std::uint32_t> &column_order, uint32_t row_begin = 0, uint32_t row_end = std::numeric_limits<uint32_t>::max())
Sparse overload of reorder_pcm_columns. Returns CSC.
-
cudaqx::tensor<uint8_t> cudaq::qec::shuffle_pcm_columns(const cudaqx::tensor<uint8_t> &pcm, std::mt19937_64 &&rng)
Randomly permute the columns of a PCM.
- Parameters:
pcm – The PCM to permute.
rng – The random number generator to use (e.g. std::mt19937_64(your_seed))
- Returns:
A new PCM with the columns permuted randomly.
-
sparse_binary_matrix cudaq::qec::shuffle_pcm_columns(const sparse_binary_matrix &pcm, std::mt19937_64 &&rng)
Sparse overload of shuffle_pcm_columns.
-
std::pair<cudaqx::tensor<uint8_t>, std::vector<double>> cudaq::qec::simplify_pcm(const cudaqx::tensor<uint8_t> &pcm, const std::vector<double> &weights, std::uint32_t num_syndromes_per_round = 0)
Simplify a PCM by removing duplicate columns and 0-weight columns, and combine the probability weight vectors accordingly.
- Parameters:
pcm – The PCM to simplify.
weights – The probability weight vectors to combine.
num_syndromes_per_round – The number of syndromes per round. (Defaults to 0, which means that no secondary per-round sorting will occur.)
- Returns:
A new PCM with the columns sorted in topological order, and the probability weight vectors combined accordingly.
-
cudaqx::tensor<uint8_t> cudaq::qec::sort_pcm_columns(const cudaqx::tensor<uint8_t> &pcm, std::uint32_t num_syndromes_per_round = 0)
Sort the columns of a PCM in topological order.
- Parameters:
pcm – The PCM to sort.
num_syndromes_per_round – The number of syndromes per round. (Defaults to 0, which means that no secondary per-round sorting will occur.)
- Returns:
A new PCM with the columns sorted in topological order.
Logger
The QEC logger API currently lives in cudaq::qec::detail and is used by
the CUDA_QEC_* macros exposed in cudaq/qec/logger.h.
-
enum class cudaq::qec::detail::log_level
Severity levels supported by the QEC logger.
Values:
-
enumerator trace
-
enumerator debug
-
enumerator info
-
enumerator warn
-
enumerator error
-
enumerator trace
-
enum class cudaq::qec::detail::forward_drop_policy
Queue backpressure policy for forwarded records.
Values:
-
enumerator drop_newest
-
enumerator drop_oldest
-
enumerator drop_newest
-
struct forwarded_log_record
Log payload forwarded to an optional background callback.
-
constexpr std::size_t cudaq::qec::detail::realtime_forwarder_default_message_capacity = 512
Default forwarded message payload capacity in bytes.
-
struct forwarder_config
Runtime configuration for asynchronous forwarding.
Public Members
-
std::function<void(forwarded_log_record&&)> callback
Callback executed on the forwarder worker thread.
The record is passed as an rvalue: the worker owns a fresh, per-record instance that is discarded after the callback returns, so a callback may move out of it (e.g.
std::move(record)to re-queue it) to avoid copying the heap-allocatedfile_name/messagestrings. Read-only callbacks takingconst ForwardedLogRecord &remain valid.
-
std::size_t queue_capacity = 1024
Bounded queue capacity (records), clamped to at least 1.
-
std::size_t message_capacity = realtime_forwarder_default_message_capacity
Max forwarded message bytes copied on producer path.
Values are clamped to [1, realtime_forwarder_max_message_capacity].
-
forward_drop_policy drop_policy = forward_drop_policy::drop_newest
Overflow policy when the forwarder queue is saturated.
-
std::function<void(forwarded_log_record&&)> callback
-
struct forwarder_stats
Runtime counters for asynchronous forwarding behavior.
Public Members
-
std::uint64_t enqueued_records = 0
Records successfully enqueued to the forwarder queue.
-
std::uint64_t dropped_records = 0
Records dropped due to queue saturation/overflow policy.
-
std::uint64_t truncated_records = 0
Records whose message payload was truncated to fit capacity.
-
std::uint64_t forward_failures = 0
Callback invocations that threw exceptions.
-
std::uint64_t enqueued_records = 0
-
bool cudaq::qec::detail::should_log(const log_level level)
Return true if the given level is currently enabled.
-
void cudaq::qec::detail::set_forwarder(forwarder_config config)
Install or replace the optional async forwarding callback.
-
void cudaq::qec::detail::set_forwarder()
Enable forwarding with a default worker callback to stdout/stderr.
-
std::size_t cudaq::qec::detail::get_forwarder_message_capacity()
Return active forwarded-message capacity in bytes.
-
forwarder_stats cudaq::qec::detail::get_forwarder_stats()
Return a snapshot of forwarding counters.
Common
-
enum class cudaq::qec::operation
Enum describing all supported logical operations.
Values:
-
enumerator x
Logical X gate.
-
enumerator y
Logical Y gate.
-
enumerator z
Logical Z gate.
-
enumerator h
Logical Hadamard gate.
-
enumerator s
Logical S gate.
-
enumerator cx
Logical controlled-X gate.
-
enumerator cy
Logical controlled-Y gate.
-
enumerator cz
Logical controlled-Z gate.
-
enumerator stabilizer_round
Stabilizer measurement round.
-
enumerator prep0
Prepare logical |0⟩ state.
-
enumerator prep1
Prepare logical |1⟩ state.
-
enumerator prepp
Prepare logical |+⟩ state.
-
enumerator prepm
Prepare logical |-⟩ state.
-
enumerator x
-
std::tuple<cudaqx::tensor<uint8_t>, cudaqx::tensor<uint8_t>> cudaq::qec::sample_code_capacity(const cudaqx::tensor<uint8_t> &H, std::size_t numShots, double error_probability)
Sample syndrome measurements with code capacity noise.
- Parameters:
H – Parity check matrix of a QEC code
numShots – Number of measurement shots
error_probability – Probability of bit flip on data
- Returns:
Tuple containing syndrome measurements and data qubit measurements
-
std::tuple<cudaqx::tensor<uint8_t>, cudaqx::tensor<uint8_t>> cudaq::qec::sample_code_capacity(const cudaqx::tensor<uint8_t> &H, std::size_t numShots, double error_probability, unsigned seed)
Sample syndrome measurements with code capacity noise.
- Parameters:
H – Parity check matrix of a QEC code
numShots – Number of measurement shots
error_probability – Probability of bit flip on data
seed – RNG seed for reproducible experiments
- Returns:
Tuple containing syndrome measurements and data qubit measurements
-
std::tuple<cudaqx::tensor<uint8_t>, cudaqx::tensor<uint8_t>> cudaq::qec::sample_code_capacity(const code &code, std::size_t numShots, double error_probability)
Sample syndrome measurements with code capacity noise.
- Parameters:
code – QEC Code to sample
numShots – Number of measurement shots
error_probability – Probability of bit flip on data
- Returns:
Tuple containing syndrome measurements and data qubit measurements
-
std::tuple<cudaqx::tensor<uint8_t>, cudaqx::tensor<uint8_t>> cudaq::qec::sample_code_capacity(const code &code, std::size_t numShots, double error_probability, unsigned seed)
Sample syndrome measurements with code capacity noise.
- Parameters:
code – QEC Code to sample
numShots – Number of measurement shots
error_probability – Probability of bit flip on data
seed – RNG seed for reproducible experiments
- Returns:
Tuple containing syndrome measurements and data qubit measurements
-
std::tuple<cudaqx::tensor<uint8_t>, cudaqx::tensor<uint8_t>> cudaq::qec::sample_memory_circuit(const code &code, std::size_t numShots, std::size_t numRounds = 1)
Sample syndrome measurements starting from |0⟩ state.
- Parameters:
code – QEC Code to sample
numShots – Number of measurement shots
numRounds – Number of stabilizer measurement rounds
- Returns:
Tuple of (syndromeTensor, dataResults), equivalent to the
operation::prep0overload ofsample_memory_circuit.
-
std::tuple<cudaqx::tensor<uint8_t>, cudaqx::tensor<uint8_t>> cudaq::qec::sample_memory_circuit(const code &code, std::size_t numShots, std::size_t numRounds, cudaq::noise_model &noise)
Sample syndrome measurements from |0⟩ state with noise.
- Parameters:
code – QEC Code to sample
numShots – Number of measurement shots
numRounds – Number of stabilizer measurement rounds
noise – Noise model to apply
- Returns:
Tuple of (syndromeTensor, dataResults), equivalent to the
operation::prep0overload ofsample_memory_circuit.
-
std::tuple<cudaqx::tensor<uint8_t>, cudaqx::tensor<uint8_t>> cudaq::qec::sample_memory_circuit(const code &code, operation statePrep, std::size_t numShots, std::size_t numRounds = 1)
Sample syndrome measurements from the memory circuit.
- Parameters:
code – QEC Code to sample
statePrep – Initial state preparation operation
numShots – Number of measurement shots
numRounds – Number of stabilizer measurement rounds
- Returns:
Tuple of (syndromeTensor, dataResults), equivalent to the
operation::prep0overload ofsample_memory_circuit.
-
std::tuple<cudaqx::tensor<uint8_t>, cudaqx::tensor<uint8_t>> cudaq::qec::sample_memory_circuit(const code &code, operation statePrep, std::size_t numShots, std::size_t numRounds, cudaq::noise_model &noise)
Sample syndrome measurements with circuit-level noise.
- Parameters:
code – QEC Code to sample
statePrep – Initial state preparation operation
numShots – Number of measurement shots
numRounds – Number of stabilizer measurement rounds
noise – Noise model to apply
- Returns:
Tuple of (syndromeTensor, dataResults).
syndromeTensorhas shape(numShots, numDetectors):numFixedboundary detectors (the stabilizer type matchingstatePrep’s basis, since only that type is deterministic at the circuit’s endpoints), then one detector block per each of thenumRounds - 1inter-round transitions, thennumFixedmore boundary detectors.dataResultshas shape(numShots, numDataQubits).
-
std::tuple<cudaqx::tensor<uint8_t>, cudaqx::tensor<uint8_t>> cudaq::qec::x_sample_memory_circuit(const code &code, operation statePrep, std::size_t numShots, std::size_t numRounds, cudaq::noise_model &noise)
Sample syndrome measurements with circuit-level noise, keeping only the X stabilizer syndromes.
- Parameters:
code – QEC Code to sample
statePrep – Initial state preparation operation
numShots – Number of measurement shots
numRounds – Number of stabilizer measurement rounds
noise – Noise model to apply
- Returns:
Tuple of (syndromeTensor, dataResults), same layout as
sample_memory_circuitbut restricted to X-stabilizer detector values.
-
std::tuple<cudaqx::tensor<uint8_t>, cudaqx::tensor<uint8_t>> cudaq::qec::z_sample_memory_circuit(const code &code, operation statePrep, std::size_t numShots, std::size_t numRounds, cudaq::noise_model &noise)
Sample syndrome measurements with circuit-level noise, keeping only the Z stabilizer syndromes.
- Parameters:
code – QEC Code to sample
statePrep – Initial state preparation operation
numShots – Number of measurement shots
numRounds – Number of stabilizer measurement rounds
noise – Noise model to apply
- Returns:
Tuple of (syndromeTensor, dataResults), same layout as
sample_memory_circuitbut restricted to Z-stabilizer detector values.