Skip to content

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

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

Replace msm execution contexts with cudaq::dem_from_kernel in app examples#696
bmhowe23 wants to merge 4 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/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).

bmhowe23 and others added 4 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>
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.

1 participant