Skip to content

Replace msm execution contexts with cudaq::dem_from_kernel in app examples - #696

Open
bmhowe23 wants to merge 8 commits into
NVIDIA:mainfrom
bmhowe23:bmh/dem-from-kernel-app-examples
Open

Replace msm execution contexts with cudaq::dem_from_kernel in app examples#696
bmhowe23 wants to merge 8 commits into
NVIDIA:mainfrom
bmhowe23:bmh/dem-from-kernel-app-examples

Conversation

@bmhowe23

@bmhowe23 bmhowe23 commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

The surface_code-2 and surface_code-3 C++ app examples built their detector error models by running the demo kernels under the "msm_size"/"msm" execution contexts and hand-assembling the detector and observable matrices
from the raw measurement syndrome matrix. The cudaq::dem_from_kernel API supersedes that: the demo kernels now annotate themselves with cudaq::detector/detectors and cudaq::logical_observable when a declare_detectors flag is set (detectors pair each round against the previous one, with the lock-in round as the reference; prep0's deterministic first-round Z stabilizers are declared as singles where the old code used them), and the hosts parse the returned Stim DEM text with dem_from_stim_text. The runtime D_sparse now comes straight from the analysis' measurements-to-detectors
(m2d) map instead of generate_timelike_sparse_detector_matrix, remapped into each decoder's own enqueue stream for surface_code-3's split Z/X decoders (which run one dem_from_kernel pass per decoder via declare_detectors_z/_x
flags).

Notable constraint baked into the kernels:

  • dem_from_kernel rejects kernels tagged qubitMeasurementFeedback, so the final data measurements are packed branch-free instead of via to_integer(to_bools(...)), whose discriminate-into-call flow sets the tag.

The now-unused compute_msm/construct_mz_table Python bindings (py_decoder.cpp, and their aliases in cudaq_qec/__init__.py) are removed. No other code references them after main's surface_code-1 refactor.

Note on the library API: main now offers decoder_context_from_memory_circuit(...) (with x_component()/z_component()/ full_component()), which surface_code-1 uses. surface_code-2/3 keep the hand-rolled dem_from_kernel path because that library circuit models noise only through the attached noise_model (gate channels), whereas these two examples deliberately demonstrate in-kernel SPAM (apply_noise data-qubit depolarization at p_spam); surface_code-3 additionally applies corrections as in-kernel transversal gates rather than the library's observable-flip model. Migrating them would change the modeled physics and measured logical error rate,
so it is intentionally out of scope here.

bmhowe23 and others added 6 commits July 15, 2026 03:03
…mples

The surface_code-1/2/3 C++ app examples and the surface_code_1.py Python
example built their detector error models by running the kernels under the
"msm_size"/"msm" execution contexts and hand-assembling the detector and
observable matrices from the raw measurement syndrome matrix. The new
cudaq::dem_from_kernel API supersedes that: the demo kernels now annotate
themselves with cudaq::detector/detectors and cudaq::logical_observable
when a declare_detectors flag is set (detectors pair each round against
the previous one, with the lock-in round as the reference; prep0's
deterministic first-round Z stabilizers are declared as singles where the
old code used them), and the hosts parse the returned Stim DEM text with
dem_from_stim_text. The runtime D_sparse now comes straight from the
analysis' measurements-to-detectors (m2d) map instead of
generate_timelike_sparse_detector_matrix, remapped into each decoder's own
enqueue stream for surface_code-3's split Z/X decoders (which run one
dem_from_kernel pass per decoder via declare_detectors_z/_x flags).

Notable constraints baked into the kernels:
- dem_from_kernel rejects kernels tagged qubitMeasurementFeedback, so the
  final data measurements are packed branch-free instead of via
  to_integer(to_bools(...)), whose discriminate-into-call flow sets the tag.
- The Python kernels avoid measure-handle lists crossing kernel-call
  boundaries and avoid iterating returned handle lists: both leave loops
  and heap traffic that do not survive the full loop unrolling the
  adaptive QIR profile (quantinuum target) requires. The DEM slice of
  demo_circuit_qpu is instead a single custom_memory_circuit_stabs call
  covering lock-in plus one decoder window, which is gate-for-gate
  identical to the live path.

The now-unused compute_msm/construct_mz_table Python bindings are removed.

Testing: all 54 app_examples ctest cases pass (local, sliding-window,
cqr in-process and two-process, quantinuum-emulate at d=3/5, prep0/prepp),
and the 12 surface_code-1-test.py pytest cases pass in ~10s (with
/usr/local/cudaq and the built python dir on PYTHONPATH).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Ben Howe <bhowe@nvidia.com>
Signed-off-by: Ben Howe <bhowe@nvidia.com>
…pp-examples

Signed-off-by: Ben Howe <bhowe@nvidia.com>

# Conflicts:
#	libs/qec/unittests/realtime/app_examples/surface_code_1.py
Signed-off-by: Ben Howe <bhowe@nvidia.com>
Comment thread libs/qec/python/bindings/py_decoder.cpp
@bmhowe23
bmhowe23 marked this pull request as ready for review August 18, 2026 18:15

@eliotheinrich eliotheinrich 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.

Looks good!

Removing the declare_detectors flags would be good, since the detector annotations should be automatically removed during compilation.

Comment thread libs/qec/unittests/realtime/app_examples/surface_code-2.cpp Outdated
Comment thread libs/qec/unittests/realtime/app_examples/surface_code-2.cpp Outdated
Comment thread libs/qec/unittests/realtime/app_examples/surface_code-2.cpp Outdated

@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.

Looks good. Thanks! Just some notes about blurbs/stale looking comments

Should these two examples be referenced in the docs too? Currently they are not

Comment thread libs/qec/unittests/realtime/app_examples/surface_code-2.cpp
Comment thread libs/qec/unittests/realtime/app_examples/surface_code-2.cpp
Comment thread libs/qec/unittests/realtime/app_examples/surface_code-3.cpp
- sc-2: always declare detectors (no-op on live path), drop the
  declare_detectors flag; store the lock-in syndrome per logical qubit;
  remove the dead PER_SHOT_DEBUG call that references undefined vars.
- sc-3: store the lock-in syndrome per logical qubit. Its
  declare_detectors_z/_x flags are kept: they partition detectors across
  the two DEM passes, so they are not removable no-ops.
- Add header blurbs to both examples; drop the stale MR24 QIR workaround
  note from both test scripts.

Signed-off-by: Ben Howe <bhowe@nvidia.com>
@bmhowe23

Copy link
Copy Markdown
Collaborator Author

Re: referencing these in the docs — leaving them out for now; they're realtime-decoding test examples rather than user-facing tutorials.

Signed-off-by: Ben Howe <bhowe@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