Skip to content

Added bundled decoder_context struct for measurement extraction#671

Merged
eliotheinrich merged 9 commits into
NVIDIA:mainfrom
eliotheinrich:pr-decoder-context-from-mem-circ
Jul 16, 2026
Merged

Added bundled decoder_context struct for measurement extraction#671
eliotheinrich merged 9 commits into
NVIDIA:mainfrom
eliotheinrich:pr-decoder-context-from-mem-circ

Conversation

@eliotheinrich

@eliotheinrich eliotheinrich commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Description

In order to process enqueued syndromes, the decoder needs access to a measurement map so it knows how to form detectors. Right now, this comes from generate_timelike_sparse_detector_matrix, which does not include boundary detectors.

Rather than refactor this function (which would require it to be aware of the stabilizer structure of the code), I include the m2d map calculated by dem_from_kernel, and bundle it into a new struct decoder_context, and provide a new helper function decoder_context_from_memory_circuit with a similar signature to dem_from_memory_circuit, which it essentially supersedes. This new function returns the DEM as well as the measurement map needed by the decoder. The decoder_context struct also provides helper methods for extracting the X/Z components, alleviating the need for helper functions (in the style of *_dem_from_memory_circuit) as well as allowing a single circuit analysis to provide both the X and Z decoder contexts.

The PR also applies some changes to the real_time_complete documentation examples to use this function.

New tests:
test_dem.py::test_decoder_context_from_memory_circuit_requires_noise_model - asserts a throw on the decoder_context_from_memory_circuit if no noise model is provided
test_dem.py::test_decoder_context_and_components - asserts that decoder_context_from_memory_circuit matches output of superceded *_dem_from_memory_circuit
test_dem.py::test_decoder_context_d_sparse_layout() - asserts that .d_sparse() on decoder_context flattens m2d map into correct -1-terminated sparse vector
test_dem.py::test_decoder_context_single_type_code_empty_component - check that empty component (i.e. x-component of a repetition code) is consistent
test_qec_stim.cpp::checkTealtimeDecodeFromMemoryCircuit - end-to-end check of realtime API using decoder_context
test_qec_stim.cpp::checkDecoderContextAndComponents - Verify DEM output by decoder_context_from_memory_circuit matches superseded *_dem_from_memory_circuit, and that extracting x/z components is idempotent and consistent.

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

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

Scope and size

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

Tests

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

Documentation

  • Public-facing APIs have Doxygen docs.
  • User-visible behavior changes have public docs, or a follow-up is
    tracked.

Code style

  • Naming follows the existing convention (snake_case vs camelCase) for
    the area being modified.

Dependencies

  • No new third-party dependencies, or the team has been notified and
    OSRB tickets filed.

Signed-off-by: Eliot Heinrich <eheinrich@nvidia.com>
@eliotheinrich eliotheinrich mentioned this pull request Jul 13, 2026
18 tasks
@eliotheinrich
eliotheinrich marked this pull request as ready for review July 13, 2026 14:23
@eliotheinrich
eliotheinrich force-pushed the pr-decoder-context-from-mem-circ branch from 6cecabe to 224ee96 Compare July 14, 2026 20:40
Signed-off-by: Eliot Heinrich <38039898+eliotheinrich@users.noreply.github.com>
Signed-off-by: Eliot Heinrich <eheinrich@nvidia.com>
@eliotheinrich
eliotheinrich force-pushed the pr-decoder-context-from-mem-circ branch from 224ee96 to 136e54c Compare July 14, 2026 20:56

@melody-ren melody-ren left a comment

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.

Thanks @eliotheinrich! This a great upgrade to the decoder interface. I have some comments regarding API and performance. Please see inline comments.

Probably out of scope for this PR given the timeframe, I think we can consider deprecating the legacy APIs.

Comment thread libs/qec/lib/experiments.cpp
Comment thread libs/qec/lib/experiments.cpp Outdated
Comment thread libs/qec/lib/experiments.cpp Outdated
Comment thread libs/qec/include/cudaq/qec/experiments.h Outdated
Comment thread libs/qec/lib/experiments.cpp
Comment thread libs/qec/lib/experiments.cpp Outdated
Comment thread libs/qec/lib/experiments.cpp Outdated
@eliotheinrich
eliotheinrich force-pushed the pr-decoder-context-from-mem-circ branch from f4c0988 to fea2189 Compare July 15, 2026 22:10
Signed-off-by: Eliot Heinrich <eheinrich@nvidia.com>
@eliotheinrich
eliotheinrich force-pushed the pr-decoder-context-from-mem-circ branch from fea2189 to 88908c1 Compare July 15, 2026 22:14
eliotheinrich and others added 2 commits July 15, 2026 15:38
Signed-off-by: Eliot Heinrich <eheinrich@nvidia.com>
Signed-off-by: Eliot Heinrich <38039898+eliotheinrich@users.noreply.github.com>

@bmhowe23 bmhowe23 left a comment

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.

The PR introduces two related types whose names make the relationship harder to follow than it needs to be in my opinion:

  • decoder_context - the result struct (dem + m2d + m2o), returned by component methods
  • decoder_context_handle - the lazy factory returned by decoder_context_from_memory_circuit; call .x/z/full_component() to get a decoder_context

A few specific issues:

  1. Python class is named DecoderContext, not DecoderContextHandle (py_code.cpp:534)
    In Python, DecoderContext is what C++ calls decoder_context_handle - the lazy factory, not the final result. The C++ decoder_context struct has no Python binding (component methods return tuples). So Python users see DecoderContext as the object with .x/z/full_component() methods, which reads as "this is the context" when it's really "this is the thing that produces contexts." Either name the Python class DecoderContextHandle to match C++, or flip the naming so the more user-facing object (the handle) gets the simpler name.

  2. Stray term memory_circuit_handle in two test comments
    test_qec_stim.cpp:854 and test_dem.py:684 both say "decoder_context_from_memory_circuit returns a memory_circuit_handle" - a type that doesn't exist in the API. Should say decoder_context_handle.

  3. Possible naming inversion to consider
    If the intent is that users mostly interact with the factory object (decoder_context_from_memory_circuit(...)) and rarely hold a decoder_context directly, it might read more naturally to give the factory the simpler name (decoder_context) and add a suffix to the result struct (decoder_inputs, decoder_config, or canonicalized_decoder_context). As written, the prominent user-facing object has the longer name with _handle, while the plain data struct has the cleaner name.

Signed-off-by: Eliot Heinrich <eheinrich@nvidia.com>
@eliotheinrich
eliotheinrich force-pushed the pr-decoder-context-from-mem-circ branch from b95b0d8 to edf1b2c Compare July 16, 2026 14:37
@eliotheinrich

Copy link
Copy Markdown
Collaborator Author

Good points, thank you Ben; I went with calling the factory object the decoder_context, and the returned struct a decoder_input (and fixed the stale comments) in edf1b2c.

@bmhowe23 bmhowe23 left a comment

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.

Thanks, Eliot,

@eliotheinrich
eliotheinrich enabled auto-merge (squash) July 16, 2026 15:36
@eliotheinrich
eliotheinrich merged commit 0859cc0 into NVIDIA:main Jul 16, 2026
21 checks passed
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