Skip to content

[python] Crash when cudaq.adjoint is followed by cudaq.control on the same kernel with a constant argument #5490

Description

@iarjunganesh

Required prerequisites

  • Consult the security policy. If reporting a security vulnerability, do not report the bug using this form. Use the process described in the policy to report the issue.
  • Make sure you've read the documentation. Your issue may be addressed there.
  • Search the issue tracker to verify that this hasn't already been reported. +1 or comment there if it has.
  • If possible, make a PR with a failing test to give us a starting point to work on!

Describe the bug

Hi, and thank you for all the work on CUDA-Q! I came across a crash that I don't think has been reported yet, though please let me know if I missed an existing issue.

In a Python kernel, calling cudaq.adjoint(k, ...) followed by cudaq.control(k, ...) on the same kernel aborts the process with a failed assertion when the kernel argument is a compile-time constant, either a literal or a local variable:

/usr/include/c++/12/bits/stl_vector.h:1125: ... std::vector<mlir::BlockArgument>::operator[](size_type) ...: Assertion '__n < this->size()' failed.
Aborted (exit code 134)

From what I could narrow down:

Order in the caller Argument passed Result
adjoint, then control literal (1.5) crash
adjoint, then control local variable (theta = 1.5) crash
adjoint, then control caller's kernel argument (theta: float) works
control, then adjoint literal (1.5) works
each on its own literal works

The equivalent C++ program, compiled with nvq++, runs fine, so this seems specific to the Python frontend.

Steps to reproduce the bug

import cudaq


@cudaq.kernel
def my_func(q: cudaq.qubit, theta: float):
    ry(theta, q)
    rz(theta, q)


@cudaq.kernel
def kernel():
    ancilla = cudaq.qubit()
    q = cudaq.qubit()
    h(ancilla)
    cudaq.adjoint(my_func, q, 1.5)
    cudaq.control(my_func, ancilla, q, 1.5)


cudaq.sample(kernel).dump()  # aborts with the assertion above

Swapping the last two lines of kernel, or making theta an argument of kernel and passing it through, makes it run correctly.

For reference, test_control_then_adjoint in python/tests/kernel/test_kernel_features.py covers the control-then-adjoint order with a runtime argument. I couldn't find a test for the reverse order with a constant argument.

Expected behavior

The kernel compiles and samples normally, as it does in C++ and with the reversed call order. For example: { 00:~250 01:~250 10:~500 }.

Is this a regression? If it is, put the last known working version (or commit) here.

Not sure. A related adjoint/control combination was fixed in #1871 (#1979, #1993), but I haven't found a version where this exact case worked.

Environment

  • CUDA-Q version: 0.16.0 (pip wheel), and the nightly image nvcr.io/nvidia/nightly/cuda-quantum:cu12-latest at commit 934e7065
  • Python version: 3.12
  • C++ compiler: N/A (the C++ equivalent built with nvq++ from the same nightly image works)
  • Operating system: Ubuntu on WSL2 (x86_64) for the wheel; the nightly container for the nightly build

Suggestions

I haven't dug into the root cause yet. Since it only fails for constant arguments and only when adjoint comes first, I wonder whether the argument synthesis or constant propagation for the .adj variant is changing the callee's signature before the .ctrl variant is generated. That's only a guess, though. I'd be happy to contribute a failing regression test, or to help investigate further, if that would be useful.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    stale-notifiedStale notification has already fired for this issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions