Research Preview: cudaq-pulse (reopened) - #5244
Open
anthony-santana wants to merge 50 commits into
Open
Conversation
Introduce the pulse package at the repository root as explicitly opt-in, non-production research software. Keep it out of CUDAQ_ALL_PROJECTS while supporting both pulse-only and combined builds through CUDAQ_ENABLE_PROJECTS. This supersedes the initial experimental/pulse drop by relocating the package to pulse/ and adopting the hardened integration. - Add the Python frontend, pulse kernel compiler, MLIR dialects, lowering pipelines, nanobind bindings, targets, visualization, and cuDensityMat runtime. - Integrate CUDA and cuDensityMat discovery with automatic GPU-test availability detection and numerical validation for driven evolution, relaxation, coupling, and the public compile/JIT path. - Add unit, property, semantic, workload, and MLIR regression suites together with examples, benchmarks, API documentation, source-build guidance, and a turnkey development container. - Add scoped CPU, documentation, license, and trusted GPU CI using CUDA-Q's prebuilt LLVM environment instead of rebuilding LLVM. - Generalize CI routing through a central research-preview registry so future previews can avoid stable Linux and macOS builds, CodeQL, and spelling while retaining formatting and DCO checks. - Apply CUDA-Q licensing and formatting throughout and document that APIs, behavior, and numerical support may evolve without compatibility guarantees. Co-authored-by: Alex McCaskey <amccaskey@nvidia.com> Signed-off-by: A.M. Santana <anthonys@nvidia.com>
Port the adaptive-timestep integrator from the experimental pulse GPU runtime into the mainlined cuDensityMat integrator family. The integrator uses the embedded Dormand-Prince RK5(4) pair on top of the existing CuDensityMatTimeStepper, with an error-controlled step-size selector suited to stiff, high-frequency driven (transmon) dynamics. - Declare cudaq::integrators::dopri5 in cudaq/algorithms/integrator.h and implement it in runtime/nvqir/cudensitymat/AdaptiveIntegrator.cpp using the shared CuDensityMatIntegratorHelper (setState/getState/ensureStepper). - Expose it through nanobind (integrators.dopri5) and add a DoPri5Integrator Python wrapper alongside RungeKuttaIntegrator. - Add unittests/dynamics/test_AdaptiveIntegrator.cpp covering Rabi accuracy, adaptive-step statistics, parity with runge_kutta(order=4), clone reproducibility, and open-system T1 relaxation. Signed-off-by: A.M. Santana <anthonys@nvidia.com>
Port the performance-oriented, commutator-free Magnus path from the
experimental pulse GPU runtime as a new add-on integrator alongside the
existing simple midpoint magnus_expansion.
- Declare cudaq::integrators::magnus_cf4 in cudaq/algorithms/integrator.h
(PImpl so the header stays CUDA-free) and implement it in
runtime/nvqir/cudensitymat/MagnusHighOrderIntegrator.cpp. For closed-system
density-matrix evolution it materializes the dense Hamiltonian at the two
Gauss-Legendre nodes (CF4, Blanes-Moan), forms exact unitary propagators via
a GPU matrix exponential, and applies rho <- U rho U^dagger. Propagators are
cached (LRU) keyed by a quantized Hamiltonian signature so repeated PWC
slices reuse a single matrix exponential.
- For open systems (collapse operators / super-operator) or state-vector
evolution it transparently falls back to magnus_expansion for parity.
- Add shared support infra under runtime/nvqir/cudensitymat/support/:
matrix_exp.{h,cu} (scaling-and-squaring Pade[13/13]), propagator_cache.h,
hamiltonian_cache.h, and trimmed cuda_check.h / cuda_memory.h. matrix_exp.cu
compiles as CUDA; the library now links CUDA::cublas and CUDA::cusolver.
- Expose it through nanobind (integrators.magnus_cf4) and add a
MagnusCF4Integrator Python wrapper.
- Add unittests/dynamics/test_MagnusHighOrderIntegrator.cpp covering Rabi
accuracy + trace preservation, propagator-cache reuse, parity with
magnus_expansion, clone reproducibility, and open-system T1 fallback.
Signed-off-by: A.M. Santana <anthonys@nvidia.com>
Expose the magnus (Taylor-series midpoint) and crank_nicolson (predictor-corrector) integrators through the pulse frontend, mirroring the mainlined cudaq::integrators algorithms of the same name. Both are driven through the existing cuDensityMat Liouvillian-action primitives in cudm-runtime, so the RK4 pulse-boundary sampling fix is factored into a shared boundarySafeSampleTime helper and reused by crank_nicolson. The adaptive dopri5 and dense-Hamiltonian magnus_cf4 integrators are not expressible through the operator-action C ABI (no dense H, no adaptive error control at this layer) and remain available via the cudaq.dynamics API only; cudm_evolve rejects them explicitly. - Add IntegratorKind::Magnus (5) and CrankNicolson (6) dialect cases. - Whitelist "magnus"/"crank_nicolson" in evolve.py with updated docstring. - Add cudm-to-llvm lit coverage and pytest coverage (MLIR emission + GPU Rabi parity across rk4/magnus/crank_nicolson).
Translate the research-preview qpu/physics C++ engine tests into Python regressions under pulse/tests/runtime, exercising the dialect-routed cuDensityMat runtime through the pulse frontend: - decoherence: T1 damping, monotonicity, ground-state fixed point, trace preservation, independent multi-qubit decay - idle_evolution: exponential T1 decay across idle sweeps, split-idle equivalence, minimal short-idle perturbation - 8qubit: ladder connectivity/degree structure + single-excitation evolution in the full 256-dim register - physics_validation: free drift, Rabi pi/2 and pi pulses, selective two-qubit drive, XX-coupling excitation transfer - frame_operations: phase-shift no-ops, opposite-phase cancellation, same-phase composition - iq_modulation: pure-I (X) and pure-Q (Y) rotations, IQ symmetry and X/Y orthogonality - quantum_algorithms: single-qubit X/Y rotations, half rotations, gate-sequence identity; Bell/GHZ kept as MLIR-structure checks Multi-qubit population assertions are written to be independent of the state-vector qubit-ordering convention. Files are collected automatically by the existing directory-based pytest runners (check-pulse for CPU structure checks, check-pulse-gpu for GPU cases). The offline Google surface-code decoder-analysis script is intentionally not ported: it needs external experiment data plus stim/pymatching and is not a pulse-runtime example.
Update the research-preview docs to reflect the magnus and crank_nicolson integrators now available on the pulse cuDensityMat runtime path: - gpu_execution.rst: describe all five frontend integrators (rk1/rk2/rk4/magnus/crank_nicolson), note that the adaptive dopri5 and dense-Hamiltonian magnus_cf4 schemes live in cudaq.dynamics, and expand the GPU test-coverage summary to include the new physics regressions. - README.md: update the integrator list in the limitations section. The api/evolve.rst page is autodoc-driven and already reflects the updated evolve() docstring. The pulse.yml GPU job runs check-pulse-gpu (ctest -L gpu -> pytest tests/runtime -m gpu), which collects the new GPU regressions by directory, so no workflow change is required.
The GPU register is now sized by the qudits a kernel allocates (its arguments), not merely the ones it drives. N allocated qudits yield a predictable 2**N register with idle qudits held in |0>. Adds an 8-qubit ladder regression exercising a single-qubit excitation in the full 256-dimensional space.
…ixes - Fix an operators-API portability bug in the dopri5/magnus_cf4 unit tests: matrix_handler::number(0) has no scalar operator*, so use the op-returning factory sum_op<matrix_handler>::number(0). The tests now compile and pass in a full cudaq.dynamics build (11 GPU tests). - Remove the unused support/hamiltonian_cache.h (never wired into the Magnus path, which uses the propagator cache). - Drop an unconditional std::cout (folded into the gtest failure message) and the now-unused <iostream>. - De-duplicate set_system in builtin_integrators.py via a shared _build_system_and_schedule helper for the ported integrators. - clang-format (v22) across authored C++; yapf across Python.
The high-order Magnus integrator (magnus_cf4) links cuBLAS + cuSOLVER for its GPU dense matrix-exponential path. Both ship with the CUDA Toolkit, but minimal/runtime-only toolkit layouts can omit the cuSOLVER dev component, which otherwise surfaces as a cryptic "target CUDA::cusolver not found" at CMake generate time. Emit an actionable diagnostic instead.
The compiler/simulation benchmark scripts are development-only tooling and are not needed in the merged research preview.
# Conflicts: # CMakeLists.txt
Per reviewer feedback (#4834), the dopri5 / magnus_cf4 GPU integrators are being contributed to CUDA-Q core as their own PR for targeted review. To keep the experimental pulse stack free of cross-dependencies on core cudaq (so it can be versioned and, if needed, removed as one atomic unit), this reverts all core-directory changes back to main and retains a non-public, source-only copy of the integrator sources under pulse/core/runtime/cudm/integrators/ (not wired into any build target). The pulse frontend continues to expose only rk1/rk2/rk4/ magnus/crank_nicolson; dopri5/magnus_cf4 remain rejected there. Public docs no longer advertise them as pulse features.
Mirror the cudaq-realtime packaging so pulse builds either from the source tree or as a standalone project consumable via find_package(cudaq-pulse CONFIG): - Root CMake: fold 'pulse' into CUDAQ_ALL_PROJECTS (drop the separate CUDAQ_RESEARCH_PROJECTS variable) and add a CUDAQ_PULSE_DIR installed-package path mirroring CUDAQ_REALTIME_DIR. Keeps naming clean (no 'experimental' token) so promotion to first-class needs no renames. - pulse/CMakeLists.txt: detect standalone vs integrated build, define project()/build-type/install-prefix when standalone, and emit + install a cudaq-pulse package config plus the cudaq-pulse-targets export set. - Export cudaq::cudm_runtime through the package; the config re-discovers cuDensityMat/CUDAToolkit via find_dependency. - Document that CudaqPulseDependencies.cmake is build-time discovery, distinct from the installed cudaq-pulse-config.cmake.in.
- research-preview-paths.txt: rewrite the header to describe it accurately as an ignore-list consumed by the stable CI workflows via paths-ignore-file, and as the single source of truth for a preview's scoped workflow. - pulse.yml: derive the authoritative changed-path filter in the 'changes' job from research-preview-paths.txt (plus the shared build files) instead of duplicating the prefix list; note the trigger-level paths are a coarse pre-gate only. - PulseCanonicalize.cpp: the greedy driver ran with an always-empty pattern set (a no-op). Populate it with the canonicalization patterns every loaded op registers (the same collection the built-in canonicalizer performs) so pulse ops with hasCanonicalizer actually fold. - bindings.cpp: document the PulseModule/PassManager simplification as a tracked follow-up (deep change threading through the Python frontend).
install(EXPORT cudaq-pulse-targets) rejects a target whose INTERFACE_INCLUDE_DIRECTORIES points at a raw source path. Wrap the cudm_runtime include dir in $<BUILD_INTERFACE:>/$<INSTALL_INTERFACE:> generator expressions and install the public C ABI header so the installed package resolves it.
Addresses review feedback on the (non-public, source-only) relocated integrator copy: use a single cudaq::detail namespace for the CUDA / cuBLAS / cuSOLVER helpers instead of cudaq::pulse and the internal __dynamics_support name. These files are not built or linked by pulse; the built integrators live in the separate cudaq.dynamics core PR.
- Ignore py.typed (PEP 561 marker) and pyrightconfig.json (JSON config) in .licenserc.yaml; neither can carry a comment license header, and a header on the JSON would break the check-json hook. - Drop the stale `benchmarks` execution environment from pulse/pyrightconfig.json (that folder was removed). - Reformat pulse runtime tests with yapf (Google style). - Add new pulse domain terms to the spelling allowlist (LC_ALL=C sorted). Signed-off-by: A.M. Santana <anthonys@nvidia.com>
Signed-off-by: A.M. Santana <anthonys@nvidia.com>
I, A.M. Santana <anthonys@nvidia.com>, hereby add my Signed-off-by to this commit: b74788d I, A.M. Santana <anthonys@nvidia.com>, hereby add my Signed-off-by to this commit: 9e96b35 I, A.M. Santana <anthonys@nvidia.com>, hereby add my Signed-off-by to this commit: c91271f I, A.M. Santana <anthonys@nvidia.com>, hereby add my Signed-off-by to this commit: 6f902a6 I, A.M. Santana <anthonys@nvidia.com>, hereby add my Signed-off-by to this commit: c1cb2ab I, A.M. Santana <anthonys@nvidia.com>, hereby add my Signed-off-by to this commit: d2fa250 I, A.M. Santana <anthonys@nvidia.com>, hereby add my Signed-off-by to this commit: 6ef6939 I, A.M. Santana <anthonys@nvidia.com>, hereby add my Signed-off-by to this commit: 388eb0f I, A.M. Santana <anthonys@nvidia.com>, hereby add my Signed-off-by to this commit: b541d18 I, A.M. Santana <anthonys@nvidia.com>, hereby add my Signed-off-by to this commit: 2e14d56 I, A.M. Santana <anthonys@nvidia.com>, hereby add my Signed-off-by to this commit: ebc1b91 I, A.M. Santana <anthonys@nvidia.com>, hereby add my Signed-off-by to this commit: 82602b9 I, A.M. Santana <anthonys@nvidia.com>, hereby add my Signed-off-by to this commit: c39cca9 Signed-off-by: A.M. Santana <anthonys@nvidia.com>
The test-and-docs and gpu-validation jobs pull the private NVIDIA cuda-quantum-devcontainer image but never authenticated, so the pull failed with "manifest unknown". Grant `packages: read` and supply container credentials (github.actor / github.token), mirroring the pattern already used by integration_tests.yml and python_metapackages.yml. Signed-off-by: A.M. Santana <anthonys@nvidia.com>
GitHub auto-authenticates ghcr.io container jobs with GITHUB_TOKEN, which is not authorized for the cross-repo cuda-quantum-devcontainer package and returns "manifest unknown" even though the image is anonymously pullable. Prefer an optional GHCR_PULL_TOKEN secret (read:packages) for the pull and fall back to GITHUB_TOKEN so the workflow works once the secret is added or the package grants this repository read access. Signed-off-by: A.M. Santana <anthonys@nvidia.com>
Match every other CUDA-Q ghcr consumer by authenticating the devcontainer container pull with github.actor / github.token (plus packages: read), rather than a bespoke PAT secret. The cuda-quantum-devcontainer package is built and pushed from this repo by docker_images.yml, so the remaining requirement is that the package grant NVIDIA/cuda-quantum read access. Signed-off-by: A.M. Santana <anthonys@nvidia.com>
… run The job-level `container:` made GitHub auto-authenticate to ghcr.io with GITHUB_TOKEN, which is not authorized for the cuda-quantum-devcontainer package and failed with "manifest unknown" even though the image is public. Drop `container:` and instead `docker pull` the image (anonymous, no creds) and run the build/test/docs inside it with `docker run`. This makes the pulse build self-contained: no PAT secret and no package-access change. Signed-off-by: A.M. Santana <anthonys@nvidia.com>
The cuda-quantum-devcontainer image is a PRIVATE ghcr.io package, so the anonymous docker-run approach cannot fetch its blobs (child manifest 404s without package-read). Restore the canonical job-level container: and pull with `secrets.GHCR_PULL_TOKEN || github.token` so the build resolves as soon as either a read:packages PAT secret is added or the package grants this repo read access under "Manage Actions access". Signed-off-by: A.M. Santana <anthonys@nvidia.com>
The pulse jobs referenced ghcr.io/nvidia/cuda-quantum-devcontainer: amd64-cu12.6-llvm-main, which failed with "manifest unknown": the *-llvm-* tags belong to the cuda-quantum-devdeps image, and the amd64-/arm64- prefixed tags are per-arch build/cache artifacts whose child blobs get cleaned up. Use the canonical multi-arch devcontainer tag cu12.6-gcc12-main (public, intact) and pull it with the standard GITHUB_TOKEN credentials. Signed-off-by: A.M. Santana <anthonys@nvidia.com>
The devcontainer runs as a non-root user while the checked-out workspace is owned by the runner UID, so the manual `git submodule update` step failed with "detected dubious ownership". actions/checkout only scopes its safe.directory exception to its own step, so add a global safe.directory for the workspace before initializing submodules (persists for later build steps in the job). Signed-off-by: A.M. Santana <anthonys@nvidia.com>
Match the non-public integrator snapshot to the versions being contributed in the standalone integrators PR, addressing the review comments in place: consolidate the CUDA/cuBLAS/cuSOLVER error checks into a single cuda_check.h (cuda_memory.h now only holds the RAII device-memory wrapper), and collapse the nested cudaq/integrators namespaces to cudaq::integrators. Signed-off-by: A.M. Santana <anthonys@nvidia.com>
Signed-off-by: A.M. Santana <anthonys@nvidia.com>
I, Luca Mondada <luca@mondada.net>, hereby add my Signed-off-by to this commit: b1824f4 Signed-off-by: Luca Mondada <luca@mondada.net>
anthony-santana
force-pushed
the
research_preview/pulse
branch
from
August 24, 2026 23:58
19b3c97 to
52d463f
Compare
Collaborator
Author
Command Bot: Processing... |
CI Summary (
|
| Job | Result |
|---|---|
binaries |
⏩ skipped |
build_and_test |
✅ success |
changes |
✅ success |
config_devdeps |
✅ success |
config_source_build |
⏩ skipped |
config_wheeldeps |
✅ success |
devdeps |
✅ success |
docker_image |
⏩ skipped |
gen_code_coverage |
⏩ skipped |
metadata |
✅ success |
python_metapackages |
⏩ skipped |
python_wheels |
⏩ skipped |
source_build |
⏩ skipped |
wheeldeps |
✅ success |
⏩ Skipped jobs (7) — intentionally skipped on PR builds; run on merge_group / workflow_dispatch
| Job |
|---|
binaries |
config_source_build |
docker_image |
gen_code_coverage |
python_metapackages |
python_wheels |
source_build |
All sub-jobs (43) — every matrix leg, with links
| Job | Status | Link |
|---|---|---|
| Build and test (amd64, gcc12, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, gcc12, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (amd64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| CI Summary | ❔ in_progress | view |
| Check for stable CUDA-Q changes | ✅ success | view |
| Configure build (devdeps) | ✅ success | view |
| Configure build (source_build) | ⏩ skipped | view |
| Configure build (wheeldeps) | ✅ success | view |
| Create CUDA Quantum installer | ⏩ skipped | view |
| Create Docker images | ⏩ skipped | view |
| Create Python metapackages | ⏩ skipped | view |
| Create Python wheels | ⏩ skipped | view |
| Gen code coverage | ⏩ skipped | view |
| Load dependencies (amd64, gcc12) / Caching | ✅ success | view |
| Load dependencies (amd64, gcc12) / Finalize | ✅ success | view |
| Load dependencies (amd64, gcc12) / Metadata | ✅ success | view |
| Load dependencies (amd64, llvm) / Caching | ✅ success | view |
| Load dependencies (amd64, llvm) / Finalize | ✅ success | view |
| Load dependencies (amd64, llvm) / Metadata | ✅ success | view |
| Load dependencies (arm64, gcc12) / Caching | ✅ success | view |
| Load dependencies (arm64, gcc12) / Finalize | ✅ success | view |
| Load dependencies (arm64, gcc12) / Metadata | ✅ success | view |
| Load dependencies (arm64, llvm) / Caching | ✅ success | view |
| Load dependencies (arm64, llvm) / Finalize | ✅ success | view |
| Load dependencies (arm64, llvm) / Metadata | ✅ success | view |
| Load source build cache | ⏩ skipped | view |
| Load wheel dependencies (amd64, 12.6) / Caching | ✅ success | view |
| Load wheel dependencies (amd64, 12.6) / Finalize | ✅ success | view |
| Load wheel dependencies (amd64, 12.6) / Metadata | ✅ success | view |
| Load wheel dependencies (amd64, 13.0) / Caching | ✅ success | view |
| Load wheel dependencies (amd64, 13.0) / Finalize | ✅ success | view |
| Load wheel dependencies (amd64, 13.0) / Metadata | ✅ success | view |
| Load wheel dependencies (arm64, 12.6) / Caching | ✅ success | view |
| Load wheel dependencies (arm64, 12.6) / Finalize | ✅ success | view |
| Load wheel dependencies (arm64, 12.6) / Metadata | ✅ success | view |
| Load wheel dependencies (arm64, 13.0) / Caching | ✅ success | view |
| Load wheel dependencies (arm64, 13.0) / Finalize | ✅ success | view |
| Load wheel dependencies (arm64, 13.0) / Metadata | ✅ success | view |
| Prepare cache clean-up | ✅ success | view |
| Retrieve PR info | ✅ success | view |
✅ Required checks (6/6) — declared in .github/required-checks.yml for push
| Required check | Status | Link |
|---|---|---|
| Build and test (amd64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (amd64, gcc12, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, gcc12, openmpi) / Dev environment (Python) | ✅ success | view |
Signed-off-by: A.M. Santana <anthonys@nvidia.com>
Signed-off-by: A.M. Santana <anthonys@nvidia.com>
Signed-off-by: A.M. Santana <anthonys@nvidia.com>
Collaborator
Author
Command Bot: Processing... |
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.
Reopening this PR as a miscommunication let it get removed after merging the first time.
This PR provides a research preview of the CUDA-Q Pulse MLIR dialect, as well as auxillary infrastructure for writing, scheduling, and lowering pulse kernels from Python to GPU simulation via cuDynamics.
What this PR introduces / supports
What is not yet supported
Requirements
Getting started & docs