Replace msm execution contexts with cudaq::dem_from_kernel in app examples - #696
Open
bmhowe23 wants to merge 8 commits into
Open
Replace msm execution contexts with cudaq::dem_from_kernel in app examples#696bmhowe23 wants to merge 8 commits into
bmhowe23 wants to merge 8 commits into
Conversation
…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>
…es' into bmh/dem-from-kernel-app-examples
…pp-examples Signed-off-by: Ben Howe <bhowe@nvidia.com> # Conflicts: # libs/qec/unittests/realtime/app_examples/surface_code_1.py
bmhowe23
commented
Aug 18, 2026
bmhowe23
marked this pull request as ready for review
August 18, 2026 18:15
eliotheinrich
left a comment
Collaborator
There was a problem hiding this comment.
Looks good!
Removing the declare_detectors flags would be good, since the detector annotations should be automatically removed during compilation.
melody-ren
reviewed
Aug 18, 2026
melody-ren
left a comment
Collaborator
There was a problem hiding this comment.
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
- 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>
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>
melody-ren
approved these changes
Aug 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The
surface_code-2andsurface_code-3C++ 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 matricesfrom the raw measurement syndrome matrix. The
cudaq::dem_from_kernelAPI supersedes that: the demo kernels now annotate themselves withcudaq::detector/detectorsandcudaq::logical_observablewhen adeclare_detectorsflag 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 withdem_from_stim_text. The runtimeD_sparsenow comes straight from the analysis' measurements-to-detectors(
m2d) map instead ofgenerate_timelike_sparse_detector_matrix, remapped into each decoder's own enqueue stream forsurface_code-3's split Z/X decoders (which run onedem_from_kernelpass per decoder viadeclare_detectors_z/_xflags).
Notable constraint baked into the kernels:
dem_from_kernelrejects kernels taggedqubitMeasurementFeedback, so the final data measurements are packed branch-free instead of viato_integer(to_bools(...)), whose discriminate-into-call flow sets the tag.The now-unused
compute_msm/construct_mz_tablePython bindings (py_decoder.cpp, and their aliases incudaq_qec/__init__.py) are removed. No other code references them aftermain'ssurface_code-1refactor.Note on the library API:
mainnow offersdecoder_context_from_memory_circuit(...)(withx_component()/z_component()/full_component()), whichsurface_code-1uses.surface_code-2/3keep the hand-rolleddem_from_kernelpath because that library circuit models noise only through the attachednoise_model(gate channels), whereas these two examples deliberately demonstrate in-kernel SPAM (apply_noisedata-qubit depolarization atp_spam);surface_code-3additionally 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.