CUDA-Q QEC Python API
Code
- class cudaq_qec.Code(*args, **kwargs)
Represents a quantum error correction code
- contains_operation
Return true if this code contains the given operation encoding
- get_num_ancilla_qubits
Total number of ancilla qubits required by the code.
- get_num_ancilla_x_qubits
Number of X-type ancilla qubits used for X stabilizer measurements.
- get_num_ancilla_z_qubits
Number of Z-type ancilla qubits used for Z stabilizer measurements.
- get_num_data_qubits
Total number of physical data qubits required by the code.
- get_num_x_stabilizers
Number of X-type stabilizers.
- get_num_z_stabilizers
Number of Z-type stabilizers.
- get_observables_x
Get the Pauli X observables of the code
- get_observables_z
Get the Pauli Z observables of the code
- get_operation_one_qubit
Get a CUDA-Q Python kernel for a one-qubit logical operation.
- Returns:
A valid CUDA-Q Python kernel object.
- get_operation_two_qubit
Get a CUDA-Q Python kernel for a two-qubit logical operation.
- Returns:
A valid CUDA-Q Python kernel object.
- get_parity
Get the parity check matrix of the code
- get_parity_x
Get the X-type parity check matrix of the code
- get_parity_z
Get the Z-type parity check matrix of the code
- get_pauli_observables_matrix
Get a matrix of the Pauli observables of the code
- get_stabilizer_round
Get a CUDA-Q Python kernel for a stabilizer round logical operation.
- Returns:
A valid CUDA-Q Python kernel object.
- get_stabilizer_schedule_x
Get the X-stabilizer schedule matrix passed to the code’s stabilizer_round kernel. Entry 0 = no support, entry k >= 1 = interaction at timestep k; defaults to the X-type parity check matrix (every interaction at timestep 1).
- get_stabilizer_schedule_z
Get the Z-stabilizer schedule matrix passed to the code’s stabilizer_round kernel. Entry 0 = no support, entry k >= 1 = interaction at timestep k; defaults to the Z-type parity check matrix (every interaction at timestep 1).
- get_stabilizers
Get the stabilizer generators of the code
Surface code layout
The rotated surface code exposes a grid helper for stabilizer and data-qubit
indexing. In Python it is available as cudaq_qec.stabilizer_grid (call
cudaq_qec.stabilizer_grid(distance)). The C++ type is
cudaq::qec::surface_code::stabilizer_grid (API).
- class cudaq_qec.stabilizer_grid(*args, **kwargs)
- property data_coords
(self) -> list[cudaq_qec.vec2d]
- property data_indices
(self) -> dict
- property distance
(self) -> int
- format_data_grid
- format_stabilizer_coords
- format_stabilizer_grid
- format_stabilizer_indices
- format_stabilizers
- get_cnot_schedule_pairs_x
Return the X-stabilizer CNOT schedule as a flat list of (stabilizer index, data index) pairs ordered by timestep within each stabilizer.
- get_cnot_schedule_pairs_z
Return the Z-stabilizer CNOT schedule as a flat list of (stabilizer index, data index) pairs ordered by timestep within each stabilizer.
- get_cnot_schedule_x
Return the X-stabilizer CNOT schedule matrix as a numpy array whose rows match the sorted parity-matrix rows. Entry 0 = no support, k >= 1 = CNOT timestep, ordered so that ancilla (hook) errors land perpendicular to the logical operators.
- get_cnot_schedule_z
Return the Z-stabilizer CNOT schedule matrix as a numpy array whose rows match the sorted parity-matrix rows. Entry 0 = no support, k >= 1 = CNOT timestep, ordered so that ancilla (hook) errors land perpendicular to the logical operators.
- get_spin_op_observables
Return the logical observables as [X, Z] cudaq::spin_op_term entries
- get_spin_op_stabilizers
Return the stabilizers as a list of cudaq::spin_op_term
- property grid_length
(self) -> int
- property orientation
(self) -> cudaq_qec.sc_orientation
- property roles
(self) -> list[cudaq_qec.surface_role]
- property x_stab_coords
(self) -> list[cudaq_qec.vec2d]
- property x_stab_indices
(self) -> dict
- property x_stabilizers
(self) -> list[list[int]]
- property z_stab_coords
(self) -> list[cudaq_qec.vec2d]
- property z_stab_indices
(self) -> dict
- property z_stabilizers
(self) -> list[list[int]]
Detector Error Model
- class cudaq_qec.DetectorErrorModel(*args, **kwargs)
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.
- canonicalize_for_rounds
Canonicalize the detector error model for a given number of rounds.
Columns sharing the same detector and observable signature are merged, with rates composed to match the input model. By default, zero-syndrome columns that still flip an observable (undetectable logical errors) are retained so the model’s observable-flip probability is preserved. Set
remove_zero_syndrome_errors=Trueto drop all columns with no detector signature, which is appropriate when the canonicalized DEM is consumed only for round-based decoding.Canonicalization does not preserve cross-column exclusivity structure: each output column is given a fresh unique error id and treated as independent of every other column, so any
error_idscorrelation in the input model is discarded.
- canonicalize_for_rounds_with_boundary
Boundary-aware canonicalization for memory-experiment DEMs whose first and last detector layers (the boundaries) are narrower than the interior layers. The first
num_boundary_syndromesdetector rows form the initial round, each subsequent block ofnum_syndromes_per_roundrows is an interior round, and the trailingnum_boundary_syndromesrows form the final round. This makes the round-based column ordering respect the true rounds even when the boundary width differs from the interior width.remove_zero_syndrome_errorsbehaves as incanonicalize_for_rounds. RaisesValueErrorifnum_syndromes_per_roundis zero ornum_boundary_syndromesexceedsnum_syndromes_per_round.
- property 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.
- property 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.
- property error_rates
The list of weights has length equal to the number of columns of the detector error matrix, which assigns a likelihood to each error mechanism.
- num_detectors
The number of detectors in the detector error model
- num_error_mechanisms
The number of error mechanisms in the detector error model
- num_observables
The number of observables in the detector error model
- property 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.
- class cudaq_qec.DecoderContext
Lazy handle returned by
decoder_context_from_memory_circuit.Stores the raw circuit analysis. Each
*_componentmethod canonicalizes exactly the requested stabilizer type and returns a(dem, m2d, m2o)tuple.- full_component
Canonicalize both stabilizer types with boundary awareness; return (dem, m2d, m2o).
demis the canonicalized DetectorErrorModel,m2dandm2oare lists of lists of measurement indices.
- property num_measurements
Total number of measurements per shot.
- x_component
Canonicalize X-stabilizer detectors; return (dem, m2d, m2o).
demis the canonicalized DetectorErrorModel,m2dandm2oare lists of lists of measurement indices.
- z_component
Canonicalize Z-stabilizer detectors; return (dem, m2d, m2o).
demis the canonicalized DetectorErrorModel,m2dandm2oare lists of lists of measurement indices.
Note
The x_component(), z_component(), and full_component() methods each
return a (dem, m2d, m2o) tuple:
dem(DetectorErrorModel) — canonicalized detector error modelm2d(list[list[int]]) — measurement-to-detector map;m2d[d]lists the measurement indices whose XOR forms detectordm2o(list[list[int]]) — measurement-to-observable map
Pass m2d to d_sparse() to produce the D_sparse vector for a
real-time decoder config.
- cudaq_qec.dem_from_memory_circuit(code: cudaq_qec.Code, op: cudaq_qec.operation, numRounds: int, noise: cudaq.NoiseModel | None = None, decompose_errors: bool = False) cudaq_qec.DetectorErrorModel
Generate a detector error model from a memory circuit.
This function generates a detector error model from a memory circuit. The memory circuit is specified by the code, the initial state preparation operation, and the number of stabilizer measurement rounds. The noise model is required.
- Parameters:
code – The code to generate the detector error model for.
op – The initial state preparation operation.
numRounds – The number of stabilizer measurement rounds.
noise – The noise model to apply to the memory circuit.
decompose_errors – If True, hyperedge error mechanisms are decomposed into pairs of two-detector edges by Stim before returning.
- Returns:
A detector error model.
- cudaq_qec.x_dem_from_memory_circuit(code: cudaq_qec.Code, op: cudaq_qec.operation, numRounds: int, noise: cudaq.NoiseModel | None = None, decompose_errors: bool = False) cudaq_qec.DetectorErrorModel
Generate a detector error model from a memory circuit in the X basis.
This function generates a detector error model from a memory circuit in the X basis. The memory circuit is specified by the code, the initial state preparation operation, and the number of stabilizer measurement rounds. The noise model is required.
- Parameters:
code – The code to generate the detector error model for.
op – The initial state preparation operation.
numRounds – The number of stabilizer measurement rounds.
noise – The noise model to apply to the memory circuit.
decompose_errors – If True, hyperedge error mechanisms are decomposed into pairs of two-detector edges by Stim before returning.
- Returns:
A detector error model.
- cudaq_qec.z_dem_from_memory_circuit(code: cudaq_qec.Code, op: cudaq_qec.operation, numRounds: int, noise: cudaq.NoiseModel | None = None, decompose_errors: bool = False) cudaq_qec.DetectorErrorModel
Generate a detector error model from a memory circuit in the Z basis.
This function generates a detector error model from a memory circuit in the Z basis. The memory circuit is specified by the code, the initial state preparation operation, and the number of stabilizer measurement rounds. The noise model is required.
- Parameters:
code – The code to generate the detector error model for.
op – The initial state preparation operation.
numRounds – The number of stabilizer measurement rounds.
noise – The noise model to apply to the memory circuit.
decompose_errors – If True, hyperedge error mechanisms are decomposed into pairs of two-detector edges by Stim before returning.
- Returns:
A detector error model.
- cudaq_qec.decoder_context_from_memory_circuit(code: cudaq_qec.Code, op: cudaq_qec.operation, numRounds: int, noise: cudaq.NoiseModel | None = None, decompose_errors: bool = False) cudaq_qec.DecoderContext
Run a memory-circuit analysis and return a lazy DecoderContext handle.
Executes
dem_from_kernelonce and stores the raw result. Canonicalization is deferred: callx_component(),z_component(), orfull_component()on the returned handle to canonicalize exactly the stabilizer type needed.- Parameters:
code – The code to characterize.
op – The initial state preparation operation.
numRounds – The number of stabilizer measurement rounds.
noise – The noise model to apply to the memory circuit.
decompose_errors – If True, hyperedge error mechanisms are decomposed into pairs of two-detector edges by Stim before returning.
- Returns:
A DecoderContext handle; call a component method to obtain the canonicalized
(dem, m2d, m2o)tuple.
- cudaq_qec.dem_from_stim_text(dem_text: str, use_decomp_suggestions: bool = False) cudaq_qec.DetectorErrorModel
Parse a Stim detector error model string into a DetectorErrorModel.
- Parameters:
dem_text – A Stim detector error model string.
use_decomp_suggestions – If error mechanism separated by
^are decomposed
- cudaq_qec.d_sparse(m2d: collections.abc.Sequence[collections.abc.Sequence[int]]) list[int]
Flatten a measurement-to-detector map into the -1-terminated sparse vector a realtime decoder config expects for its D_sparse.
m2dis a list of lists:m2d[d]contains the measurement indices whose XOR forms detectord. Each detector’s indices are emitted in order, followed by -1.This is a standalone helper for consumers who hold an m2d map (e.g., extracted from a
decoder_inputsreturned by a component method) and want to build the D_sparse vector without going through aDecoderContext.
Decoder Interfaces
- class cudaq_qec.Decoder(*args, **kwargs)
Represents a decoder for quantum error correction
- decode
Decode the given syndrome to determine the error correction
- decode_async
Asynchronously decode the given syndrome
- decode_batch
Decode multiple syndromes and return the results
- get_block_size
Get the size of the code block
- get_syndrome_size
Get the size of the syndrome
- get_version
Get the version of the decoder
- class cudaq_qec.DecoderResult(*args, **kwargs)
Single-shot decoder result.
Returned by
decoder.decode(...). Carries the convergence flag, the decoded correction chain, and optional decoder-specific metadata.Like
BatchDecoderResult, this is conceptually output-only — user code should not need to construct or mutate one. UnlikeBatchDecoderResult, the no-arg constructor and writable fields are preserved here because Python decoder plugins implementing adecodeoverride use the construct-then-mutate pattern:res = DecoderResult() res.converged = True res.result = np.arange(…) return res
Tightening this construction surface would break every existing Python decoder plugin and is deferred to a future change.
- property converged
Boolean flag indicating if the decoder converged to a solution.
True if the decoder successfully found a valid correction chain, False if the decoder failed to converge or exceeded iteration limits.
- property opt_results
Optional additional results from the decoder stored in a heterogeneous map.
This field may be empty if no additional results are available.
- property result
The decoded correction chain or recovery operation.
Contains the sequence of corrections that should be applied to recover the original quantum state. The format depends on the specific decoder implementation.
Returns a 1-D NumPy array of the configured QEC floating point dtype (float64 in standard wheels). A fresh array is allocated per access — the underlying storage is a
std::vector<float_t>and the data is copied out on read.Accepts any sequence of floats on assignment; this is the path Python decoder plugins use in their
decodeoverrides (see class docstring).
- class cudaq_qec.BatchDecoderResult(*args, **kwargs)
Batched decoder result.
Produced by
decoder.decode_batch(...). This type is output-only: it carries decoder output back to the caller and is not parsed by decoders. User code should not need to construct one — calldecoder.decode_batch(...)and read the result.Python decoder plugins implementing a
decode_batchoverride may use the constructor to produce one.resultshould be a 2-D NumPy array of the configured QEC floating point dtype (float64 in standard wheels);convergedshould be a 1-D NumPy bool array;opt_resultsis a list of per-shot dicts or None entries. The constructor coercesresultandconvergedto C-contiguous storage of the expected dtype (vianp.ascontiguousarray), copying when the input doesn’t already satisfy those invariants. Wrong rank (e.g. 1-Dresult) is rejected with TypeError.An empty batch (zero syndromes) yields
result.shape == (0, 0)andconverged.shape == (0,). The per-shot width is unknown without running a decode and depends on decoder mode, soresult.shape[1]is only meaningful when the batch is non-empty.Access patterns, fastest to slowest:
Vectorized: read
result,converged, oropt_resultsdirectly. The properties return the underlying NumPy arrays and Python list with no copy. This is the recommended path for batch processing.Slicing:
batch[a:b]returns another BatchDecoderResult that shares data with the parent. NumPy basic slicing — including stepped slices likebatch[::2]— returns views, so no data is copied. The opt results list slice creates a new Python list, but its entries are shared references.Integer indexing / iteration:
batch[i]orfor r in batch:yields a DecoderResult copy of one shot. This compatibility surface exists for code written against the previouslist[DecoderResult]return type. Each access copies the row out into a fresh per-shot buffer because DecoderResult’s underlying storage (std::vector<float_t>) cannot alias into the batch’s packed NumPy array — the layouts are incompatible. Avoid in hot loops; prefer pattern 1.
- property converged
A one-dimensional NumPy bool array indicating convergence per shot.
- property opt_results
A list of per-shot optional result dictionaries, or None entries.
- property result
A two-dimensional NumPy array of decoder outputs, with one row per shot.
This is the fast path for batch consumers. Its dtype is the configured QEC floating point type.
- class cudaq_qec.AsyncDecoderResult
A future-like object that holds the result of an asynchronous decoder call. Call get() to block until the result is available.
- get
Return the decoder result (blocking until ready)
- ready
Return True if the asynchronous decoder result is ready, False otherwise
Note
NumPy result arrays — As of 0.7.0, the result field of
cudaq_qec.DecoderResult (and the per-shot results returned by
cudaq_qec.BatchDecoderResult and
cudaq_qec.AsyncDecoderResult) is a 1-D NumPy array rather than a
Python list. Indexing and iteration are unchanged, but code that relied
on the result being a list specifically (for example isinstance(res,
list) or list-only methods) should be updated.
- cudaq_qec.get_decoder(arg0: str, arg1: object, /, **kwargs) object | cudaq_qec.Decoder
Get a decoder by name.
Hmay be:A scipy sparse matrix (CSR, CSC, COO, or any
scipy.sparseformat): the preferred input — no dense allocation occurs, and any format is normalised to CSR internally before building the C++ sparse storage.A dense 2D NumPy
uint8array in row-major order: a full densecudaqx::tensoris built first, then converted to CSC sparse storage. For large PCMs this can allocate as much memory asrows * cols.A Stim detector error model string: native C++ decoders receive the raw DEM text via
decoder_init; Python-registered decoders receive the DEM-derived PCM plusOanderror_rate_vecdefaults.
For Python-registered decoders (
cudaq.qec.decoderdecorator),His passed through to__init__unchanged (NumPy array or scipy sparse matrix). DEM string inputs are parsed first as described above. CallDecoder.__init__(self, H)so nanobind can store the PCM internally without building a denserows x colsallocation.
Note
scipy.sparse interop — cudaq_qec.get_decoder() and
cudaq_qec.Decoder accept a scipy.sparse matrix (CSR, CSC,
COO, or any other scipy.sparse format) as the parity-check matrix
H. This is the preferred form for large PCMs because no dense
rows x cols allocation is made — the matrix is normalised to CSR
internally. Dense NumPy uint8 arrays remain supported.
The PCM utilities cudaq_qec.reorder_pcm_columns(),
cudaq_qec.shuffle_pcm_columns(), and
cudaq_qec.pcm_to_sparse_vec() also accept SciPy sparse matrices
without creating a dense cudaqx::tensor. Reordering and shuffling a
sparse input returns a scipy.sparse.csc_matrix; a dense input continues
to return a NumPy array.
scipy is an optional dependency; if it is not installed, pass a dense
NumPy array instead.
Built-in Decoders
NVIDIA QLDPC Decoder
- class cudaq_qec.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 cudaq_qec.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 cudaq_qec.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.
Tensor Network Decoder
- class cudaq_qec.plugins.decoders.tensor_network_decoder.TensorNetworkDecoder
A general class for tensor network decoders for quantum error correction codes.
This decoder constructs a tensor network representation of a quantum code using its parity check matrix, logical observables, and noise model. The tensor network is based on the Tanner graph of the code and can be contracted to compute the probability that a logical observable has flipped, given a syndrome.
The decoder supports both single-syndrome and batch decoding, and can run on CPU or GPU (using cuTensorNet if available).
The Tensor Network Decoder is a Python-only implementation and it requires Python 3.11 or higher. C++ APIs are not available for this decoder.
Due to the additional dependencies of the Tensor Network Decoder, you must specify the optional pip package when installing CUDA-Q QEC in order to use this decoder. Use
pip install cudaq-qec[tensor-network-decoder]in order to use this decoder.The Tensor Network Decoder has the same GPU support as the Quantum Low-Density Parity-Check Decoder. However, if you are using the V100 GPU (SM70), you will need to pin your cuTensor version to 2.2 by running
pip install cutensor_cu12==2.2. Note that this GPU is not supported by the Tensor Network Decoder.Note
It is recommended to create decoders using the
cudaq_qecplugin API:import cudaq_qec as qec import numpy as np # Example: [3,1] repetition code H = np.array([[1, 1, 0], [0, 1, 1]], dtype=np.uint8) logical_obs = np.array([[1, 1, 1]], dtype=np.uint8) noise_model = [0.1, 0.1, 0.1] decoder = qec.get_decoder("tensor_network_decoder", H, logical_obs=logical_obs, noise_model=noise_model) syndrome = [0.0, 1.0] result = decoder.decode(syndrome)
Tensor Network Structure
The tensor network constructed by this decoder is based on the Tanner graph of the code, extended with noise and logical observable tensors. The structure is illustrated below:
open/output index < logical observable -------- | s1 s2 | s3 < syndromes : product of 2D vectors [1 , 1-2pi] (pi is the probability detector i flipped) | | | | ----| c1 c2 l1 c3 < checks / logical | : delta tensors | / | | \ | | H H H H H H < Hadamard matrices | TANNER (bipartite) GRAPH \ | | / | / | e1 e2 e3 < errors | : delta tensors | | / -----| \ / / P(e1, e2, e3) < noise / error model : classical probability density ci, ej, lk are delta tensors represented sparsely as indices.- Parameters:
H – Parity check matrix (numpy.ndarray), shape (num_checks, num_qubits)
logical_obs – Logical observable matrix (numpy.ndarray), shape (1, num_qubits)
noise_model – Noise model, either a list of probabilities (length = num_qubits) or a quimb.tensor.TensorNetwork
check_inds – (optional) List of check index names
error_inds – (optional) List of error index names
logical_inds – (optional) List of logical index names
logical_tags – (optional) List of logical tags
contract_noise_model – (bool, optional) Whether to contract the noise model at initialization (default: True)
dtype – (str, optional) Data type for tensors (default: “float64”)
device – (str, optional) Device for tensor operations (“cpu”, “cuda”, or “cuda:X”, default: “cuda”)
Methods
- decode(syndrome)
Decode a single syndrome by contracting the tensor network.
- Parameters:
syndrome – List of float values (soft-decision probabilities) for each check.
- Returns:
DecoderResult with the probability that the logical observable flipped.
- decode_batch(syndrome_batch)
Decode a batch of syndromes.
- Parameters:
syndrome_batch – numpy.ndarray of shape (batch_size, num_checks)
- Returns:
List of DecoderResult objects with the probability that the logical observable has flipped for each syndrome.
- optimize_path(optimize=None, batch_size=-1)
Optimize the contraction path for the tensor network.
- Parameters:
optimize – Optimization options or None
batch_size – (int, optional) Batch size for optimization (default: -1, no batching)
- Returns:
Optimizer info object
PyMatching Decoder
- class cudaq_qec.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 cudaq_qec.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 (@cudaq.kernel decorated functions) to interact with real-time decoders.
- cudaq_qec.qec.enqueue_syndromes(decoder_id, syndromes, tag=0)
Enqueue syndrome measurements for decoding.
- Parameters:
decoder_id – Unique identifier for the decoder instance (matches configured decoder ID)
syndromes – List of syndrome measurement results from stabilizer measurements
tag – Optional tag for logging and debugging (default: 0)
Example:
import cudaq import cudaq_qec as qec from cudaq_qec import patch @cudaq.kernel def measure_and_decode(logical: patch, decoder_id: int): syndromes = measure_stabilizers(logical) qec.enqueue_syndromes(decoder_id, syndromes, 0)
- cudaq_qec.qec.get_corrections(decoder_id, return_size, reset=False)
Retrieve calculated corrections from the decoder.
- Parameters:
decoder_id – Unique identifier for the decoder instance
return_size – Number of correction bits to return (typically equals number of logical observables)
reset – Whether to reset accumulated corrections after retrieval (default: False)
- Returns:
List of boolean values indicating detected bit flips for each logical observable
Example:
@cudaq.kernel def apply_corrections(logical: patch, decoder_id: int): corrections = qec.get_corrections(decoder_id, 1, False) if corrections[0]: x(logical.data) # Apply transversal X correction
- cudaq_qec.qec.reset_decoder(decoder_id)
Reset decoder state, clearing all queued syndromes and accumulated corrections.
- Parameters:
decoder_id – Unique identifier for the decoder instance to reset
Example:
@cudaq.kernel def run_experiment(decoder_id: int): qec.reset_decoder(decoder_id) # Reset at start of each shot # ... perform experiment ...
Configuration API
The configuration API enables setting up decoders before circuit execution. Decoders are configured using YAML files or programmatically constructed configuration objects.
Decoder Parameters
Decoder-specific parameters (decoder_config.decoder_custom_args) are
plain dicts. The set of accepted keys, their types, and which are required
are defined by the parameter schema each decoder registers – including
out-of-tree decoder plugins. Use cudaq_qec.decoder_param_schema(name) to
inspect a decoder’s parameters and cudaq_qec.registered_decoder_schemas()
to list all decoders with registered schemas.
For example, the pymatching decoder accepts error_rate_vec
(per-error prior probabilities in the range (0, 0.5], length matching
the decoder block_size) and merge_strategy (one of "disallow",
"independent", "smallest_weight", "keep_original",
"replace"):
config.type = "pymatching"
config.decoder_custom_args = {
"error_rate_vec": [0.1, 0.1, 0.1],
"merge_strategy": "smallest_weight",
}
The trt_decoder accepts onnx_load_path or engine_load_path
(mutually exclusive), engine_save_path, precision (“fp16”, “bf16”,
“int8”, “fp8”, “tf32”, “noTF32”, or “best”), memory_workspace (bytes),
batch_size, use_cuda_graph, and an optional global decoder attached
via global_decoder plus global_decoder_params (a nested dict whose
keys follow the schema of the named global decoder).
- cudaq_qec.decoder_param_schema(decoder_name)
Return the registered parameter schema for a decoder as a list of descriptors (
key,kind,required, and, for nested sections,subschemaordiscriminator), orNonewhen the decoder has not registered one.
- cudaq_qec.registered_decoder_schemas()
Names of all decoders (and nested parameter sections) with registered parameter schemas.
- cudaq_qec.decoder_config_json_schema()
Return a JSON Schema (draft 2020-12) document, as a string, that validates
multi_decoder_configYAML files. Generated from the decoder parameter schemas registered in this installation (including loaded third-party decoder plugins), for use with standard tools such ascheck-jsonschema, the pythonjsonschemapackage, or editor YAML language servers. Schema validation hooks are not representable in JSON Schema, so a passing document may still be rejected when parsed.
- decoder_config.validate_custom_args()
Validate
decoder_custom_argsagainst the parameter schema registered for this decodertype: unknown keys, missing required keys, and the schema’s own validation hook. RaisesRuntimeErroron the first violation. YAML parsing applies the same checks automatically; call this to vet a configuration built programmatically before using it. Also available onmulti_decoder_configto validate every decoder at once.
Deprecated Typed Configuration Classes
The typed configuration classes from earlier releases
(nv_qldpc_decoder_config, trt_decoder_config, pymatching_config,
chromobius_config, multi_error_lut_config, and the
qecrt.config-level single_error_lut_config, sliding_window_config,
and srelay_bp_config) remain available as deprecated compatibility shims.
They emit a DeprecationWarning on construction and will be removed in a
future release; existing code that builds one and passes it to
decoder_config.set_decoder_custom_args (or assigns it to
decoder_config.decoder_custom_args) continues to work unchanged. Note
that reading decoder_custom_args now always returns a plain dict, never
a typed object. New code should assign dicts directly, as shown above.
Configuration Functions
- cudaq_qec.configure_decoders(config)
Configure decoders from a multi_decoder_config object.
- Parameters:
config – multi_decoder_config object containing decoder specifications
- Returns:
0 on success, non-zero error code on failure
- cudaq_qec.configure_decoders_from_file(config_file)
Configure decoders from a YAML file.
- Parameters:
config_file – Path to YAML configuration file
- Returns:
0 on success, non-zero error code on failure
- cudaq_qec.configure_decoders_from_str(config_str)
Configure decoders from a YAML string.
- Parameters:
config_str – YAML configuration as a string
- Returns:
0 on success, non-zero error code on failure
- cudaq_qec.finalize_decoders()
Finalize and clean up decoder resources. Should be called before program exit.
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(pcm)
Convert a parity check matrix (PCM) to sparse vector representation for decoder configuration.
- Parameters:
pcm – Dense binary matrix as numpy array (e.g.,
dem.detector_error_matrixordem.observables_flips_matrix)- Returns:
Sparse vector (list of integers) where -1 separates rows
Usage in real-time decoding:
config.H_sparse = qec.pcm_to_sparse_vec(dem.detector_error_matrix) config.O_sparse = qec.pcm_to_sparse_vec(dem.observables_flips_matrix)
- cudaq_qec.pcm_from_sparse_vec(sparse_vec, num_rows, num_cols)
Convert sparse vector representation back to a dense parity check matrix.
- Parameters:
sparse_vec – Sparse representation (from YAML or decoder config)
num_rows – Number of rows in the output matrix
num_cols – Number of columns in the output matrix
- Returns:
Dense binary matrix as numpy array
- cudaq_qec.d_sparse(m2d)
Flatten a measurement-to-detector map into the
-1-terminated sparse vector a realtime decoder config expects for itsD_sparse.- Parameters:
m2d – List of lists of measurement indices.
m2d[d]contains the measurement indices whose XOR forms detectord. Obtain this from the second element of the tuple returned byDecoderContext.x_component(),DecoderContext.z_component(), orDecoderContext.full_component().- Returns:
-1-terminated sparse vector suitable fordecoder_config.D_sparse
Usage in real-time decoding:
ctx = qec.decoder_context_from_memory_circuit(code, statePrep, num_rounds, noise) dem, m2d, m2o = ctx.z_component() # or x_component() / full_component() config.D_sparse = qec.d_sparse(m2d)
See also Parity Check Matrix Utilities for additional PCM manipulation functions.
Common
- cudaq_qec.sample_memory_circuit(code: cudaq_qec.Code, numShots: int, numRounds: int, noise: cudaq.NoiseModel | None = None) tuple
- cudaq_qec.sample_memory_circuit(code: cudaq_qec.Code, op: cudaq_qec.operation, numShots: int, numRounds: int, noise: cudaq.mlir._mlir_libs._quakeDialects.cudaq_runtime.NoiseModel | None = None) tuple
Overloaded function.
sample_memory_circuit(code: cudaq_qec.Code, numShots: int, numRounds: int, noise: cudaq.NoiseModel | None = None) -> tuple
Sample the memory circuit of the code
sample_memory_circuit(code: cudaq_qec.Code, op: cudaq_qec.operation, numShots: int, numRounds: int, noise: cudaq.NoiseModel | None = None) -> tuple
Sample the memory circuit of the code with a specific initial operation. Returns (syndromes, data): syndromes has numFixed boundary detectors (basis matching op), then one block per inter-round transition, then numFixed more boundary detectors.
- cudaq_qec.x_sample_memory_circuit(code: cudaq_qec.Code, op: cudaq_qec.operation, numShots: int, numRounds: int, noise: cudaq.NoiseModel | None = None) tuple
Sample the memory circuit of the code with a specific initial operation, keeping only the X stabilizer syndromes (same detector layout as sample_memory_circuit, restricted to X stabilizers).
- cudaq_qec.z_sample_memory_circuit(code: cudaq_qec.Code, op: cudaq_qec.operation, numShots: int, numRounds: int, noise: cudaq.NoiseModel | None = None) tuple
Sample the memory circuit of the code with a specific initial operation, keeping only the Z stabilizer syndromes (same detector layout as sample_memory_circuit, restricted to Z stabilizers).
Note
Syndrome measurement layout — sample_memory_circuit returns a tuple
(syndromes, data). The syndromes tensor has shape
(num_shots, num_detectors) with columns laid out as [ B S S … S B ]:
B(boundary block) =code.get_num_z_stabilizers()for Z-basis preparations (prep0/prep1), orcode.get_num_x_stabilizers()for X-basis preparations (prepp/prepm).S(inter-round block) =num_z_stabilizers + num_x_stabilizersdetectors per round transition (num_rounds - 1blocks total).Total:
num_detectors = 2*B + (num_rounds - 1)*S.
The data tensor has shape (num_shots, block_size) and holds the final
data-qubit measurements used to verify logical-state preservation.
Detector Error Model (DEM) Sampling
- cudaq_qec.dem_sampling(check_matrix, num_shots: int, error_probabilities, seed: Optional[int] = None, backend: str = 'auto') Tuple[object, object]
Sample errors and syndromes from a Detector Error Model.
- Parameters:
check_matrix – Binary matrix [num_checks x num_error_mechanisms], as a NumPy uint8 array or PyTorch CUDA tensor.
num_shots – Number of independent Monte-Carlo shots.
error_probabilities – 1-D array of length num_error_mechanisms with independent Bernoulli probabilities for each mechanism. Accepts NumPy float64 array or PyTorch CUDA tensor.
seed – Optional RNG seed for reproducibility.
backend – Backend selection policy: - “auto” (default): try GPU, fall back to CPU. - “cpu”: force CPU implementation. - “gpu”: force GPU implementation and raise if unavailable.
- Returns:
- (syndromes, errors) where
syndromes: uint8 array/tensor [num_shots x num_checks] errors: uint8 array/tensor [num_shots x num_error_mechanisms]
The GPU path uses cuStabilizer for accelerated sampling. When PyTorch CUDA tensors are provided, outputs are returned as CUDA tensors. Otherwise, outputs are NumPy arrays.
PyTorch CPU tensors are moved to CUDA under
backend="gpu"(raising a GPU-unavailable error if there is no GPU) but rejected under"auto"/"cpu"; convert to NumPy first for the CPU path. PyTorch is an optional dependency; install withpip install torch.
Parity Check Matrix Utilities
- cudaq_qec.generate_random_pcm(n_rounds: int, n_errs_per_round: int, n_syndromes_per_round: int, weight: int, seed: int = 0) object
Generate a random parity check matrix.
This function creates a random parity check matrix for quantum error correction with specified parameters controlling the structure and randomness.
- Parameters:
n_rounds – Number of measurement rounds in the error correction protocol
n_errs_per_round – Number of error mechanisms per round
n_syndromes_per_round – Number of syndrome measurements per round
weight – The weight parameter controlling the sparsity of the matrix
seed – Random seed for reproducibility (0 for random seed)
See also
cudaq::qec::generate_random_pcm(): The underlying C++ implementation of this function.- Returns:
A NumPy array containing the generated parity check matrix
- cudaq_qec.generate_timelike_sparse_detector_matrix(num_syndromes_per_round: int, num_rounds: int, include_first_round: bool) list[int]
Generate a sparse detector matrix for a given number of syndromes per round and number of rounds. Time-like here means that each round of syndrome measurement bits are xor’d against the preceding round.
- Parameters:
num_syndromes_per_round – The number of syndrome measurements per round
num_rounds – The number of rounds to generate the sparse detector matrix for
include_first_round – Whether to include the first round of syndrome measurements
- Returns:
The detector matrix format is CSR-like, with -1 values indicating the end of each row.
- cudaq_qec.get_pcm_for_rounds(H: numpy.ndarray[dtype=uint8], num_syndromes_per_round: int, start_round: int, end_round: int, straddle_start_round: bool = False, straddle_end_round: bool = False, num_boundary_syndromes: int = 0) tuple
Get a sub-PCM for a range of rounds.
This function returns a sub-parity check matrix for a range of rounds.
- Parameters:
H – A NumPy array representing the parity check matrix
num_syndromes_per_round – The number of syndrome measurements per round
start_round – The starting round
end_round – The ending round
straddle_start_round – Whether to allow error mechanisms that straddle the start round (i.e. include prior rounds, too). This defaults to false.
straddle_end_round – Whether to allow error mechanisms that straddle the end round (i.e. include future rounds, too). This defaults to false.
num_boundary_syndromes – The number of syndrome measurements in the boundary layers
- Returns:
A tuple containing the sub-parity check matrix and the first and last column indices of the sub-PCM relative to the original PCM.
See also
cudaq::qec::get_pcm_for_rounds(): The underlying C++ implementation of this function.
- cudaq_qec.get_sorted_pcm_column_indices(H: numpy.ndarray[dtype=uint8], num_syndromes_per_round: int = 0) list[int]
Get the sorted column indices of a parity check matrix.
This function returns the column indices of a parity check matrix in topological order.
- Parameters:
H – A NumPy array representing the parity check matrix
num_syndromes_per_round – The number of syndrome measurements per round
- Returns:
A NumPy array containing the sorted column indices
See also
cudaq::qec::get_sorted_pcm_column_indices(): The underlying C++ implementation of this function.
- cudaq_qec.pcm_extend_to_n_rounds(H: numpy.ndarray[dtype=uint8], num_syndromes_per_round: int, n_rounds: int) tuple
Extend a parity check matrix to a given number of rounds.
This function extends a parity check matrix to a given number of rounds.
- Parameters:
H – A NumPy array representing the parity check matrix
num_syndromes_per_round – The number of syndrome measurements per round
n_rounds – The number of rounds to extend the parity check matrix to
- Returns:
A tuple containing the extended parity check matrix and the list of column indices from the original PCM that were used to form the new PCM.
See also
cudaq::qec::pcm_extend_to_n_rounds(): The underlying C++ implementation of this function.
- cudaq_qec.pcm_is_sorted(H: numpy.ndarray[dtype=uint8], num_syndromes_per_round: int = 0) bool
Check if a parity check matrix is sorted.
This function checks if a parity check matrix is sorted in topological order.
- Parameters:
H – A NumPy array representing the parity check matrix
num_syndromes_per_round – The number of syndrome measurements per round
- Returns:
A boolean indicating if the parity check matrix is sorted
See also
cudaq::qec::pcm_is_sorted(): The underlying C++ implementation of this function.
- cudaq_qec.pcm_to_sparse_vec(pcm: object) list[int]
Return a sparse representation of the PCM.
- Parameters:
pcm – A NumPy array or scipy sparse matrix.
- cudaq_qec.reorder_pcm_columns(H: object, column_order: collections.abc.Sequence[int]) object
Reorder the columns of a parity check matrix.
This function reorders the columns of a parity check matrix according to the given column order.
- Parameters:
H – A NumPy array or scipy sparse matrix
column_order – A NumPy array containing the column order
- Returns:
A NumPy array, or a scipy CSC matrix when H is scipy sparse.
See also
cudaq::qec::reorder_pcm_columns(): The underlying C++ implementation of this function.
- cudaq_qec.shuffle_pcm_columns(H: object, seed: int = 0) object
Shuffle the columns of a parity check matrix.
This function shuffles the columns of a parity check matrix.
- Parameters:
H – A NumPy array or scipy sparse matrix
seed – Random seed for reproducibility (0 for random seed)
- Returns:
A NumPy array, or a scipy CSC matrix when H is scipy sparse.
See also
cudaq::qec::shuffle_pcm_columns(): The underlying C++ implementation of this function.
- cudaq_qec.simplify_pcm(H: numpy.ndarray[dtype=uint8], weights: numpy.ndarray[dtype=float64], num_syndromes_per_round: int) tuple
Simplify a parity check matrix.
This function simplifies a parity check matrix by removing duplicate columns and 0-weight columns.
- Parameters:
H – A NumPy array representing the parity check matrix
weights – A NumPy array containing the weights of the columns
num_syndromes_per_round – The number of syndrome measurements per round
- Returns:
A tuple containing the simplified parity check matrix and the weights
See also
cudaq::qec::simplify_pcm(): The underlying C++ implementation of this function.
- cudaq_qec.sort_pcm_columns(H: numpy.ndarray[dtype=uint8], num_syndromes_per_round: int = 0) object
Sort the columns of a parity check matrix.
This function sorts the columns of a parity check matrix in topological order.
- Parameters:
H – A NumPy array representing the parity check matrix
num_syndromes_per_round – The number of syndrome measurements per round
- Returns:
A NumPy array containing the sorted parity check matrix
See also
cudaq::qec::sort_pcm_columns(): The underlying C++ implementation of this function.