Skip to content

Add chromobius decoder to the decoder plugins#546

Open
wsttiger wants to merge 7 commits into
NVIDIA:mainfrom
wsttiger:add_chromobius_decoder
Open

Add chromobius decoder to the decoder plugins#546
wsttiger wants to merge 7 commits into
NVIDIA:mainfrom
wsttiger:add_chromobius_decoder

Conversation

@wsttiger

Copy link
Copy Markdown
Collaborator

Summary

Adds a CUDA-QX QEC decoder plugin for Chromobius and wires it into the existing decoder factory/plugin flow.

The wrapper accepts a Stim detector error model through decoder params, compiles a Chromobius decoder from that DEM, packs detector syndromes into Chromobius’ expected bit-packed format, and returns predicted observable flips directly.

Changes

  • Add chromobius decoder plugin under libs/qec/lib/decoders/plugins/chromobius
  • Wire the plugin into the QEC library build
  • Fetch/build Chromobius as an internal plugin dependency
  • Support DEM input via dem, dem_str, or dem_path
  • Expose Chromobius decoder options through CUDA-QX decoder params
  • Return observable predictions directly and configure observable plumbing accordingly
  • Add C++ unit tests for construction, decode behavior, DEM path loading, and validation failures
  • Add small upstream-derived Chromobius reference data
  • Add C++ and Python correctness tests for single-shot and batched decode paths
  • Fix plugin registration to use CUDAQ_EXT_PT_REGISTER_TYPE
  • Fix a PyMatching pointer access issue found while building the plugin
  • Keep the GCC 12 GTest workaround private so it does not leak into CUDA test builds

Testing

  • ninja -j16
  • ninja install
  • ninja test_chromobius_correctness
  • ctest -R Chromobius --output-on-failure
  • ctest -R ChromobiusCorrectness --output-on-failure
  • PYTHONPATH=/usr/local/cudaq:/scratch/cudaqx-realtime/build/python pytest -q ../libs/qec/python/tests/test_chromobius.py

wsttiger added 3 commits May 2, 2026 22:55
Add a QEC decoder plugin wrapping Chromobius through the existing decoder
factory API. The wrapper accepts a Stim DEM via params, packs detector
syndromes for Chromobius, and returns predicted observable flips.

Also add Chromobius plugin build wiring and focused unit tests covering
construction, decoding, DEM path loading, and validation failures.

Fix a PyMatching pointer member access that broke the plugin build.

Signed-off-by: Scott Thornton <wsttiger@gmail.com>
- Register the Chromobius decoder with the CUDA-QX extension-point macro
- Add small upstream-derived DEM/reference shot data for observable predictions
- Add C++ and Python tests that validate single-shot and batched Chromobius decode results
- Keep the GCC 12 GTest workaround private so it does not leak into CUDA test builds
- Apply minor formatting cleanups in touched tests/headers

Signed-off-by: Scott Thornton <wsttiger@gmail.com>
@wsttiger wsttiger requested review from bmhowe23, kvmto and melody-ren May 15, 2026 22:15
Signed-off-by: Scott Thornton <wsttiger@gmail.com>
@wsttiger wsttiger force-pushed the add_chromobius_decoder branch from fc04644 to 5587a30 Compare May 18, 2026 04:22
std::string get_dem_text(const cudaqx::heterogeneous_map &params) {
if (params.contains("dem"))
return params.get<std::string>("dem");
if (params.contains("dem_str"))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dem_str is a functional duplicate of dem — same getstd::string lookup, same return, no transformation, and dem wins if both are passed.

Grepping the repo turns up zero callers of dem_stras a parameter key: neither test_chromobius.cpp nor test_chromobius.py exercises it, and there are no examples or docs that pass it.

Suggest dropping dem_str from get_dem_text and keeping just dem / dem_path

}

TEST(ChromobiusDecoder, checkDemPath) {
const std::string dem_path = "/tmp/cudaq_qec_chromobius_test.dem";

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm generally wary of hardcoded path

@@ -0,0 +1,8 @@
# Reference case from quantumlib/chromobius:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that this file and the .tsv are both straight from the quantumlib/chromobius repo? If so, I think we need to add a readme or a license file/header. @bmhowe23 What's the general rule of thumb on this?

std::fill(packed_detection_events.begin(), packed_detection_events.end(),
uint8_t{0});
for (std::size_t i = 0; i < syndrome.size(); ++i) {
if (syndrome[i] >= 0.5)

@melody-ren melody-ren May 19, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we are doing the comparison directly. I think in the base class header decoder.h, we have helpers that does this. (call cudaq::qec::convert_vec_soft_to_hard(syndrome, hard_buf)). Using the helper will prevent future drift and match the pattern that both the sliding_window and the nv-qldpc decoder use.

I'm aware that there are other instances in our repo currently that still do this manual comparison. I'll make a new issue to track those.

Signed-off-by: Scott Thornton <wsttiger@gmail.com>
chromobius(const cudaqx::tensor<uint8_t> &H,
const cudaqx::heterogeneous_map &params)
: decoder(H), dem(get_dem_text(params)) {
auto H_shape = H.shape();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is H actually used anywhere?

FetchContent_Declare(
chromobius
GIT_REPOSITORY https://github.com/quantumlib/chromobius.git
GIT_TAG efae0325e4a8fc13be981434d38e499aa06b989f # v1.1.1

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make sure to file the OSRB paperwork for this.

vedika-saravanan added a commit that referenced this pull request Jun 8, 2026
## Description

Adds unified decoder initialization from either a parity-check matrix or
raw Stim detector error model text.

This supports both existing PCM-based decoders and DEM-native decoders
needed by #546. PCM-based decoders can parse DEM text into `H`, `O`, and
`error_rate_vec` defaults, while DEM-native decoders can consume the raw
DEM text without going through a lossy matrix conversion.

### API (`cudaq/qec/decoder.h`)

- Adds `decoder_init = std::variant<sparse_binary_matrix, std::string>`
as the decoder registry construction input.
- Keeps existing PCM construction paths:
  - `get_decoder(name, H, options)`
  - `decoder::get(name, H, options)`
- Adds DEM string construction through the same entry points:
  - `get_decoder(name, dem_text, options)`
  - `decoder::get(name, dem_text, options)`
- Adds `dem_from_stim_text(dem_text)` to parse Stim DEM text into
`detector_error_model`.
- Adds helper routing for PCM-based decoders so DEM-derived `O` and
`error_rate_vec` are supplied as defaults when not explicitly provided
by the user.

The DEM-to-detector_error_model parser is lossy: it extracts detector
flips, observable flips, and per-error probabilities, but drops detector
coordinates and separator-encoded correlation structure. DEM-native
decoders should consume the raw string alternative in decoder_init.

Python: `cudaq_qec.get_decoder(...)` now accepts Stim DEM strings.
Python-registered decoders currently receive parsed `H` plus DEM-derived
`O` / `error_rate_vec` defaults, not raw DEM text.

### Dependency / build

QEC now provides its own Stim dependency via `FetchContent` when
`libstim` is not already available, so standalone QEC builds do not
depend on parent CUDA-Q interim build artifacts.

### Tests

C++ gtests and Python tests cover:

- DEM string construction through `get_decoder(...)`
- PCM construction still working
- user-provided options overriding DEM-derived defaults
- DEM parse edge cases
- DEMs without observables
- `stim::DemTarget` category assumptions

### Out of scope / follow-ups

Chromobius plugin integration itself; detector-coordinate storage on
`detector_error_model`; optional PyMatching-specific improvements;
user-facing Sphinx/RST docs.

## Runtime / performance impact

N/A

## Self-review checklist

Please confirm each item before requesting review. Check `[x]` or strike
through and explain.

### Before requesting review
- [x] I reviewed my own full diff in GitHub or my editor.
- [x] PR is in Draft if it is not yet ready for review.
- [x] Temporary / debugging changes have been removed.
- [x] Local test logs reviewed; no unexplained warnings or errors.
- [x] CI logs reviewed; no unexplained warnings or errors.
- [x] Full CI has been run.

### Scope and size
- [x] PR is under ~1000 lines, or an exception is justified in the
description.
- [x] Refactoring-only changes are isolated in their own PR(s).
- [x] No existing tests were disabled or modified just to make this PR
pass
      (if so, an issue has been raised).

### Tests
- [x] New functionality has new tests.
- [x] Tests fail if the new functionality is broken (including crashes),
not
      just when it is missing.
- [x] Negative tests added where exceptions are expected.
- [x] Truth data added where simple `EXPECT_*` / `assert` checks are
      insufficient for algorithmic correctness.
- [x] CI runtime impact considered; team notified if significant.

### Documentation
- [x] Public-facing APIs have Doxygen docs.
- [x] User-visible behavior changes have public docs, or a follow-up is
      tracked.
- [x] User-facing docs for new features are in a **separate PR** held
until
release (the docs site publishes immediately on merge to the default
      branch, so feature docs must not land before the feature ships).

### Code style
- [x] Naming follows the existing convention (`snake_case` vs
`camelCase`) for
      the area being modified.

### Dependencies
- [x] No new third-party dependencies, **or** the team has been notified
and
      OSRB tickets filed.

---------

Signed-off-by: vedika-saravanan <vsaravanan@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants