1212#include " cuda-qx/core/heterogeneous_map.h"
1313#include " cuda-qx/core/tensor.h"
1414#include " sparse_binary_matrix.h"
15+ #include " cudaq/qec/detector_error_model.h"
1516#include < functional>
1617#include < future>
1718#include < memory>
1819#include < optional>
1920#include < string>
21+ #include < string_view>
22+ #include < variant>
2023#include < vector>
2124
2225namespace cudaq ::qec {
@@ -27,6 +30,22 @@ using float_t = CUDAQX_QEC_FLOAT_TYPE;
2730using float_t = double ;
2831#endif
2932
33+ // / @brief Construction input for a decoder: either an explicit parity-check
34+ // / matrix (\c cudaq::qec::sparse_binary_matrix) or a Stim detector error model string
35+ // / (\c std::string_view).
36+ // /
37+ // / Parity-check-matrix-based decoders (LUT, sliding_window, TRT, PyMatching,
38+ // / nv-qldpc, ...) accept either alternative: a DEM string is parsed into a
39+ // / parity-check matrix via \c dem_from_stim_text. Decoders that require the raw
40+ // / DEM (e.g. Chromobius, which needs detector color/basis annotations) require
41+ // / the string alternative and reject a bare matrix.
42+ // /
43+ // / @note The string alternative is non-owning. The referenced buffer must stay
44+ // / alive for the duration of the \c get_decoder / \c decoder::get call;
45+ // / decoders parse it during construction and do not retain the view.
46+ using decoder_init =
47+ std::variant<cudaq::qec::sparse_binary_matrix, std::string_view>;
48+
3049// / @brief Validates that all keys in a heterogeneous map are found in a list of
3150// / acceptable types
3251// / @param config The heterogeneous map to validate
@@ -125,8 +144,7 @@ class async_decoder_result {
125144// / arbitrary constructor parameters that can be unique to each specific
126145// / decoder.
127146class decoder
128- : public cudaqx::extension_point<decoder,
129- const cudaq::qec::sparse_binary_matrix &,
147+ : public cudaqx::extension_point<decoder, const decoder_init &,
130148 const cudaqx::heterogeneous_map &> {
131149private:
132150 struct rt_impl ;
@@ -175,11 +193,35 @@ class decoder
175193 virtual std::vector<decoder_result>
176194 decode_batch (const std::vector<std::vector<float_t >> &syndrome);
177195
178- // / @brief This `get` overload supports default values.
196+ // / @brief Construct a registered decoder by name.
197+ // / @param name The registered decoder name.
198+ // / @param init Either a parity-check matrix or a Stim DEM string (see
199+ // / \c decoder_init). The variant is forwarded to the decoder's creator, so
200+ // / parity-check-matrix-based decoders and DEM-native decoders (Chromobius)
201+ // / share a single entry point.
202+ // / @param param_map Optional decoder-specific parameters.
179203 static std::unique_ptr<decoder>
180- get (const std::string &name, const cudaq::qec::sparse_binary_matrix &H ,
204+ get (const std::string &name, const decoder_init &init ,
181205 const cudaqx::heterogeneous_map ¶m_map = cudaqx::heterogeneous_map());
182206
207+ static std::unique_ptr<decoder>
208+ get (const std::string &name, const cudaq::qec::sparse_binary_matrix &H,
209+ const cudaqx::heterogeneous_map ¶m_map = cudaqx::heterogeneous_map()) {
210+ return get (name, decoder_init{H}, param_map);
211+ }
212+
213+ static std::unique_ptr<decoder>
214+ get (const std::string &name, const cudaqx::tensor<uint8_t > &H,
215+ const cudaqx::heterogeneous_map ¶m_map = cudaqx::heterogeneous_map()) {
216+ return get (name, cudaq::qec::sparse_binary_matrix (H), param_map);
217+ }
218+
219+ static std::unique_ptr<decoder>
220+ get (const std::string &name, std::string_view stim_dem_text,
221+ const cudaqx::heterogeneous_map ¶m_map = cudaqx::heterogeneous_map()) {
222+ return get (name, decoder_init{stim_dem_text}, param_map);
223+ }
224+
183225 std::size_t get_block_size () { return block_size; }
184226 std::size_t get_syndrome_size () { return syndrome_size; }
185227
@@ -428,10 +470,26 @@ inline void convert_vec_hard_to_soft(const std::vector<std::vector<t_hard>> &in,
428470}
429471
430472std::unique_ptr<decoder>
431- get_decoder (const std::string &name, const cudaq::qec::sparse_binary_matrix &H ,
473+ get_decoder (const std::string &name, const decoder_init &init ,
432474 const cudaqx::heterogeneous_map options = {});
433475
434- struct detector_error_model ;
476+ inline std::unique_ptr<decoder>
477+ get_decoder (const std::string &name, const cudaq::qec::sparse_binary_matrix &H,
478+ const cudaqx::heterogeneous_map options = {}) {
479+ return get_decoder (name, decoder_init{H}, options);
480+ }
481+
482+ inline std::unique_ptr<decoder>
483+ get_decoder (const std::string &name, const cudaqx::tensor<uint8_t > &H,
484+ const cudaqx::heterogeneous_map options = {}) {
485+ return get_decoder (name, cudaq::qec::sparse_binary_matrix (H), options);
486+ }
487+
488+ inline std::unique_ptr<decoder>
489+ get_decoder (const std::string &name, std::string_view stim_dem_text,
490+ const cudaqx::heterogeneous_map options = {}) {
491+ return get_decoder (name, decoder_init{stim_dem_text}, options);
492+ }
435493
436494// / @brief DEM-derived defaults; pointers alias into the source `dem`.
437495struct dem_default_values {
@@ -440,26 +498,55 @@ struct dem_default_values {
440498};
441499
442500// / @brief Return DEM defaults for any key not already supplied by the user.
443- // / Shared by `get_decoder_from_stim_dem ` and its Python binding.
501+ // / Shared by `make_pcm_decoder ` and the Python binding.
444502dem_default_values dem_defaults_for_missing_keys (
445503 const std::function<bool (const std::string &)> &contains_user_key,
446504 const detector_error_model &dem);
447505
448- // / @brief Construct a decoder by name from a Stim detector error model text.
506+ // / @brief Extract the Stim DEM text from a \c decoder_init, throwing if it holds
507+ // / a parity-check matrix instead. Use this in the create() function of decoders
508+ // / that require a raw DEM (e.g. Chromobius), which cannot be reconstructed from
509+ // / a bare parity-check matrix.
510+ std::string_view require_dem_text (const decoder_init &init);
511+
512+ // / @brief Build a parity-check-matrix-based decoder from a \c decoder_init.
513+ // /
514+ // / If \p init holds a sparse matrix, it is used directly as the parity-check matrix.
515+ // / If it holds a Stim DEM string, it is parsed via \c dem_from_stim_text and the
516+ // / derived observables (`"O"`) and per-error rates (`"error_rate_vec"`) are
517+ // / injected into \p params unless the user already supplied them (user values
518+ // / win). This is the shared implementation behind the create() function of every
519+ // / parity-check-matrix-based decoder, giving them DEM-string support for free.
449520// /
450- // / Thin wrapper over \c dem_from_stim_text: parses the DEM and forwards to
451- // / the existing H-based \c decoder::get after injecting two derived entries
452- // / into \p options if they are not already present:
453- // / - `"O"` : `cudaqx::tensor<uint8_t>` observables_flips_matrix
454- // / - `"error_rate_vec"` : `std::vector<double>` per-error probabilities
455- // / User-supplied values for either key win over the DEM-derived ones.
521+ // / @note The DEM parse is lossy: detector annotations, decomposition
522+ // / separators, and `error_ids` are dropped. Sufficient for matching-style /
523+ // / parity-check-matrix decoders (LUT, NV, sliding_window, TRT, PyMatching).
524+ // / Decoders that need full DEM metadata (e.g. Chromobius detector color/basis)
525+ // / must consume the string directly via \c require_dem_text.
526+ template <typename DecoderT>
527+ std::unique_ptr<decoder>
528+ make_pcm_decoder (const decoder_init &init,
529+ const cudaqx::heterogeneous_map ¶ms) {
530+ if (const auto *H = std::get_if<cudaq::qec::sparse_binary_matrix>(&init))
531+ return std::make_unique<DecoderT>(*H, params);
532+
533+ const auto dem =
534+ dem_from_stim_text (std::string (std::get<std::string_view>(init)));
535+ cudaqx::heterogeneous_map merged = params;
536+ const auto defaults = dem_defaults_for_missing_keys (
537+ [&](const std::string &key) { return merged.contains (key); }, dem);
538+ if (defaults.O )
539+ merged.insert (" O" , *defaults.O );
540+ if (defaults.error_rate_vec )
541+ merged.insert (" error_rate_vec" , *defaults.error_rate_vec );
542+ return std::make_unique<DecoderT>(
543+ cudaq::qec::sparse_binary_matrix (dem.detector_error_matrix ), merged);
544+ }
545+
546+ // / @brief Construct a decoder by name from a Stim detector error model string.
456547// /
457- // / @note Lossy: detector annotations, decomposition separators, and
458- // / `error_ids` are dropped. Sufficient for matching-style / H-based
459- // / decoders (LUT, NV, sliding_window, TRT, PyMatching). Decoders that
460- // / need full DEM metadata (e.g. Chromobius detector color/basis) require
461- // / the planned \c detector_coords extension on
462- // / \c cudaq::qec::detector_error_model; tracked as a follow-up.
548+ // / @deprecated Prefer \c get_decoder, which now accepts a Stim DEM string
549+ // / directly via \c decoder_init. Retained as a thin convenience alias.
463550std::unique_ptr<decoder>
464551get_decoder_from_stim_dem (const std::string &name,
465552 const std::string &stim_dem_text,
0 commit comments