Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
425dcde
Unify decoder construction inputs
melody-ren Jul 31, 2026
7213c2d
Fix decoder result form at construction
melody-ren Aug 1, 2026
45c26c1
Supply the measurement-to-detector map at decoder construction
melody-ren Aug 3, 2026
bbe9fe6
Resolve decoder models before applying a configuration
melody-ren Aug 3, 2026
b6160cb
Prove the decoding-server acceptance cases
melody-ren Aug 3, 2026
86fc902
Document model data as distinct from decoder parameters
melody-ren Aug 3, 2026
95f18f0
Project a Stim DEM straight to sparse, and drop the provenance string
melody-ren Aug 3, 2026
cece030
Own O- and D-derived allocation at construction
melody-ren Aug 3, 2026
608dfd3
WIP design_walkthrough.md
melody-ren Aug 4, 2026
ed867b4
Name decoder_inputs operations for what they do
melody-ren Aug 4, 2026
40481dc
remove stale info from walkthrough
melody-ren Aug 4, 2026
751e6ed
clarify some points in walkthrough
melody-ren Aug 4, 2026
2b78e58
Drive realtime streaming from the installed layer geometry
melody-ren Aug 5, 2026
6477f27
Fix CI build of the hololink bridge and the decoder Doxygen comment
melody-ren Aug 6, 2026
398733c
Resolve the Sphinx directives for the API this branch changed
melody-ren Aug 6, 2026
9092aa6
Remove the sparse helpers left behind by the O/D setters
melody-ren Aug 6, 2026
01a8ff2
Address review: reject empty leading detector rows, drop unused surface
melody-ren Aug 6, 2026
2a91fe5
Drop the design walkthrough and the unused Python output enum
melody-ren Aug 6, 2026
bae441f
Document which column a merged parallel edge reports
melody-ren Aug 6, 2026
268aa17
Merge remote-tracking branch 'upstream/main' into melodyr/enable-chro…
melody-ren Aug 6, 2026
1124859
Restore main's names where the concept did not change
melody-ren Aug 6, 2026
8bf9b11
Name our construction input decoder_init and restore decoder_inputs
melody-ren Aug 7, 2026
f0e8527
Merge remote-tracking branch 'upstream/main' into melodyr/enable-chro…
melody-ren Aug 7, 2026
4c367ac
Address review feedback
melody-ren Aug 7, 2026
73bba87
Name the sparse DEM builder for what it does
melody-ren Aug 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/sphinx/api/qec/cpp_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,10 @@ Decoder Interfaces
.. doxygenstruct:: cudaq::qec::decoder_inputs
:members:

.. doxygenfunction:: cudaq::qec::d_sparse(const cudaq::M2DSparseMatrix &)
.. doxygenclass:: cudaq::qec::decoder_init
:members:

.. doxygentypedef:: cudaq::qec::decoder_init
.. doxygenfunction:: cudaq::qec::d_sparse(const cudaq::M2DSparseMatrix &)

.. doxygenclass:: cudaq::qec::decoder
:members:
Expand Down
3 changes: 2 additions & 1 deletion docs/sphinx/api/qec/cpp_realtime_decoding_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ The configuration API enables setting up decoders before circuit execution. Deco
.. doxygenclass:: cudaq::qec::decoding::config::multi_decoder_config
:members:

.. doxygenfunction:: cudaq::qec::decoding::config::configure_decoders
.. doxygenfunction:: cudaq::qec::decoding::config::configure_decoders(multi_decoder_config &)
.. doxygenfunction:: cudaq::qec::decoding::config::configure_decoders(multi_decoder_config &, const std::filesystem::path &)
.. doxygenfunction:: cudaq::qec::decoding::config::configure_decoders_from_file
.. doxygenfunction:: cudaq::qec::decoding::config::configure_decoders_from_str
.. doxygenfunction:: cudaq::qec::decoding::config::finalize_decoders
Expand Down
212 changes: 98 additions & 114 deletions libs/qec/include/cudaq/qec/decoder.h

Large diffs are not rendered by default.

160 changes: 160 additions & 0 deletions libs/qec/include/cudaq/qec/decoder_init.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
/****************************************************************-*- C++ -*-****
* Copyright (c) 2026 NVIDIA Corporation & Affiliates. *
* All rights reserved. *
* *
* This source code and the accompanying materials are made available under *
* the terms of the Apache License 2.0 which accompanies this distribution. *
******************************************************************************/

#pragma once

#include "cudaq/qec/detector_error_model.h"
#include "cudaq/qec/sparse_binary_matrix.h"
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>

namespace cudaq::qec {

/// @brief Authoritative representation from which a decoder model originates.
///
/// Matrix and Stim sources are implemented. This is the entry point for a
/// compact chunked DEM: that source would be added here with a new enumerator
/// plus its typed constructor and accessor, so a decoder that consumes chunks
/// reads them directly instead of the handle first flattening them into
/// matrices. Adding one changes neither the `decoder_init` object layout nor
/// the decoder factory signature.
enum class decoder_model_source : std::uint8_t {
matrices,
stim_dem,
};

/// @brief Stable, owning input contract shared by offline and server decoders.
///
/// This is a small immutable value handle. Copies share the same model state;
/// the decoder factory takes the handle by value and the decoder base retains
/// it. Source-specific data is authoritative and the common matrix accessors
/// expose the projection stored when the handle is constructed. Model matrices
/// are stored sparsely instead of composing detector_error_model, whose matrix
/// fields are dense tensors.
class decoder_init {
public:
/// @brief Construct an H-only matrix model.
explicit decoder_init(sparse_binary_matrix detector_error_matrix);

/// Raw Stim DEM text enters through from_stim_dem(), which parses and
/// projects it. Deleted so the older spelling fails here rather than
/// through overload resolution somewhere less obvious.
explicit decoder_init(std::string) = delete;

/// @brief Construct a materialized matrix model.
/// @param detector_error_matrix H, with shape detectors x error mechanisms.
/// @param observable_flips_matrix O, with shape observables x error
/// mechanisms. Supplying it establishes an observable model; its row count is
/// retained even when a row has no nonzeros, so a zero-row O is a supplied
/// model rather than an absent one.
/// @param error_rates Optional rate per error mechanism.
/// @param measurement_to_detectors Optional D, with shape detectors x raw
/// measurements.
/// @param error_ids Optional correlation ID per error mechanism.
decoder_init(
sparse_binary_matrix detector_error_matrix,
std::optional<sparse_binary_matrix> observable_flips_matrix,
std::vector<double> error_rates = {},
std::optional<sparse_binary_matrix> measurement_to_detectors =
std::nullopt,
std::optional<std::vector<std::size_t>> error_ids = std::nullopt);

/// @brief Construct from the existing materialized detector-error model.
explicit decoder_init(detector_error_model model,
std::optional<sparse_binary_matrix>
measurement_to_detectors = std::nullopt);

/// @brief Construct from authoritative raw Stim DEM text.
///
/// Matrix accessors expose the common lossy projection produced by
/// `dem_from_stim_text`; DEM-native decoders should consume `stim_dem()`.
static decoder_init
from_stim_dem(std::string stim_dem_text,
std::optional<sparse_binary_matrix> measurement_to_detectors =
std::nullopt);

decoder_init(const decoder_init &) noexcept;
/// @brief Move construction leaves the source valid only for destruction or
/// assignment.
decoder_init(decoder_init &&) noexcept;
decoder_init &operator=(const decoder_init &) noexcept;
/// @brief Move assignment leaves the source valid only for destruction or
/// assignment.
decoder_init &operator=(decoder_init &&) noexcept;
~decoder_init();

/// @brief The authoritative representation. Consumers that only need to
/// know whether raw DEM text is available should ask has_stim_dem(); this
/// discriminator is what a future compact source would extend.
decoder_model_source source() const noexcept;

/// @brief Return the stored common H projection.
const sparse_binary_matrix &detector_error_matrix() const;

/// @brief Whether this model supplies an observable mapping at all.
///
/// Distinct from `num_observables() == 0`: a supplied O with zero rows is an
/// observable model, an H-only input is not. Construction-time validation of
/// an observable-output request depends on this distinction.
bool has_observable_model() const noexcept;

/// @brief Return the stored common O projection.
/// @throws std::logic_error if this model supplies no observable mapping.
const sparse_binary_matrix &observable_flips_matrix() const;

const std::vector<double> &error_rates() const;
const std::optional<std::vector<std::size_t>> &error_ids() const;

/// @brief Return D, or nullptr when input syndromes are already detectors.
const sparse_binary_matrix *measurement_to_detectors() const noexcept;

/// @brief Return the same inputs without D, for a decoder that is fed
/// detectors rather than a raw measurement stream. Everything else,
/// including the authoritative source, is preserved.
decoder_init decoder_init_without_d() const;

/// @brief Return the same inputs with H in GF(2)-canonical CSC form.
///
/// Sorts indices within each compressed group and XOR-merges duplicates,
/// leaving column identity, ordering and dimensions unchanged. O and D are
/// passed through untouched, and the authoritative source is retained.
/// Consumers that need a canonical H should ask for it here rather than
/// rebuilding a matrix-authoritative handle by hand.
decoder_init canonicalize_H() const;

bool has_stim_dem() const noexcept;

/// @throws std::logic_error if the authoritative source is not a Stim DEM.
const std::string &stim_dem() const;

/// Dimensions are stored as source metadata so these accessors never need to
/// request H or O. For matrix sources they intentionally duplicate the O(1)
/// matrix shape values in preparation for compact source alternatives.
std::size_t num_detectors() const noexcept;
std::size_t num_error_mechanisms() const noexcept;
std::size_t num_observables() const noexcept;

private:
struct impl;
static std::shared_ptr<const impl> make_matrix_state(
decoder_model_source source, sparse_binary_matrix detector_error_matrix,
std::optional<sparse_binary_matrix> observable_flips_matrix,
std::vector<double> error_rates,
std::optional<std::vector<std::size_t>> error_ids,
std::optional<sparse_binary_matrix> measurement_to_detectors,
std::optional<std::string> raw_stim_dem = std::nullopt);
explicit decoder_init(std::shared_ptr<const impl> state);
std::shared_ptr<const impl> state_;
};

} // namespace cudaq::qec
5 changes: 5 additions & 0 deletions libs/qec/include/cudaq/qec/experiments.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "cudaq/algorithms/dem.h"
#include "cudaq/qec/code.h"
#include "cudaq/qec/detector_error_model.h"
#include "cudaq/qec/sparse_binary_matrix.h"
#include <cstddef>
#include <vector>

Expand Down Expand Up @@ -207,6 +208,10 @@ struct decoder_context {
/// realtime decoder config expects for its `D_sparse`.
std::vector<std::int64_t> d_sparse(const cudaq::M2DSparseMatrix &m2d);

/// @brief Convert CUDA-Q circuit-analysis M2D output to the QEC-owned sparse
/// matrix used by decoder_init.
sparse_binary_matrix m2d_to_sparse(const cudaq::M2DSparseMatrix &m2d);

/// @brief Given a memory circuit setup, generate a DEM
/// @param code QEC Code to sample
/// @param statePrep Initial state preparation operation
Expand Down
22 changes: 22 additions & 0 deletions libs/qec/include/cudaq/qec/realtime/decoding_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "cuda-qx/core/heterogeneous_map.h"
#include <cstdint>
#include <filesystem>
#include <memory>
#include <optional>
#include <string>
Expand Down Expand Up @@ -70,11 +71,24 @@ struct decoder_config {
/// GPU-accelerated decoder, hence at this level rather than inside the
/// per-decoder custom args. Unset = unpinned.
std::optional<int> cuda_device_id;
/// Path to a Stim detector error model, authoritative when set. Resolved
/// against the configuration file's directory, or the process working
/// directory for a programmatic or raw-string configuration. Mutually
/// exclusive with `H_sparse`, `O_sparse` and `error_rate_vec`, which are the
/// competing matrix representation of the same model; `block_size` and
/// `syndrome_size` remain accepted as checked assertions.
std::string stim_dem_path;
/// Required for a matrix model; derived from the DEM otherwise. Zero means
/// unset.
uint64_t block_size = 0;
uint64_t syndrome_size = 0;
std::vector<std::int64_t> H_sparse;
std::vector<std::int64_t> O_sparse;
/// Maps raw measurements to detectors. Orthogonal to the model source and
/// required by both.
std::vector<std::int64_t> D_sparse;
/// Error probability per H column.
std::vector<double> error_rate_vec;
decoder_custom_args_t decoder_custom_args;

bool operator==(const decoder_config &) const = default;
Expand Down Expand Up @@ -181,6 +195,14 @@ __attribute__((visibility("default"))) std::string decoder_config_json_schema();
__attribute__((visibility("default"))) int
configure_decoders(multi_decoder_config &config);

/// @brief Configure the decoders, resolving relative model paths (such as
/// `stim_dem_path`) against @p base_dir. The overload above uses the process
/// working directory as it stands when resolution starts.
/// @return 0 on success, non-zero on failure.
__attribute__((visibility("default"))) int
configure_decoders(multi_decoder_config &config,
const std::filesystem::path &base_dir);

/// @brief 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.
Expand Down
1 change: 1 addition & 0 deletions libs/qec/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ endif()

set(DECODERS_SOURCES
decoder.cpp
decoder_init.cpp
decoder_config_payload.cpp
decoder_config_schema.cpp
detector_error_model.cpp
Expand Down
Loading