cuda-quantum icon indicating copy to clipboard operation
cuda-quantum copied to clipboard

List of callables as an input to the kernel raises an error

Open poojarao8 opened this issue 4 months ago • 6 comments

Required prerequisites

  • [x] 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.
  • [x] Make sure you've read the documentation. Your issue may be addressed there.
  • [x] 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

I can use a callable as an input to the kernel, but the list of callables raises an error.

RuntimeError: kernel argument type is 'list' but argument provided is not (argument 0, value=module {
  func.func @__nvqpp__mlirgen__X(%arg0: !quake.ref) attributes {"cudaq-kernel"} {
    quake.x %arg0 : (!quake.ref) -> ()
    return
  }
}
, type=<class 'cudaq.kernel.kernel_decorator.PyKernelDecorator'>).

Steps to reproduce the bug

import cudaq
from typing import Callable

@cudaq.kernel
def X(q : cudaq.qubit):
  x(q)

@cudaq.kernel
def kernel(f: list[Callable[[cudaq.qubit], None]]):
    q = cudaq.qvector(2)
    f[0](q[0])
    f[1](q[1])

result = cudaq.sample(kernel, [X,X])
print(result)

Expected behavior

The kernel should be able to accept a list of callables.

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

Not a regression

Environment

  • CUDA-Q version:
  • Python version:
  • C++ compiler:
  • Operating system:

Suggestions

No response

poojarao8 avatar Sep 23 '25 19:09 poojarao8

Related Issue : If you try to do an adjoint operation on such a callable, it gives an error. @schweitzpgi

import cudaq
from typing import Callable

@cudaq.kernel
def X(q : cudaq.qubit):
  x(q)

@cudaq.kernel
def kernel(f: Callable[[cudaq.qubit], None]):
    q = cudaq.qvector(2)
    f(q[0])
    cudaq.adjoint(f, q[1])
    
result = cudaq.sample(kernel, X)

Error:

error: 'quake.apply' op operand #0 must be Proxy for a C++ callable instance in cc, but got '!quake.ref'
RuntimeError: Failure while executing pass pipeline.

During handling of the above exception, another exception occurred:

RuntimeError: could not compile code for 'kernel'.

poojarao8 avatar Sep 23 '25 22:09 poojarao8

Not sure what is going on, but weirdly enough the following code works while the previous one I submitted still doesn't.

import cudaq
import numpy as np
from typing import Callable

@cudaq.kernel
def X(q : cudaq.qubit, q1 : cudaq.qubit):
    x.ctrl(q, q1)

@cudaq.kernel
def CA(q : cudaq.qubit, q1 : cudaq.qubit, f: Callable[[cudaq.qubit, cudaq.qubit], None]):
    f(q, q1)

@cudaq.kernel
def CA2(a : cudaq.qubit, q : cudaq.qvector, f: Callable[[cudaq.qubit, cudaq.qubit], None]):
    f(a, q[0])
    
@cudaq.kernel
def kernel(b: np.ndarray, f: Callable[[cudaq.qubit, cudaq.qubit], None]):
    q = cudaq.qvector(2)
    ancilla = cudaq.qubit()
    f(q[0], q[1])
    CA(ancilla, q[0], f) 
    cudaq.adjoint(CA, ancilla, q[0], f) # Why does this work?
    CA2(ancilla, q, f) 
    cudaq.adjoint(CA2, ancilla, q, f) # Why does this work?

bvec = np.zeros(8)
result = cudaq.sample(kernel, bvec, X)

poojarao8 avatar Sep 30 '25 18:09 poojarao8

Related issue https://github.com/NVIDIA/cuda-quantum/issues/3477

poojarao8 avatar Sep 30 '25 18:09 poojarao8

Investigating this issue.

sacpis avatar Sep 30 '25 18:09 sacpis

Another weird behavior that shows up with a small modifications to the code snippet above - I am now passing two callables, f and g, although the second one doesn't get used. It hangs for a bit and then spits out the following error:

error: number of callable block arguments != number of function names provided.
error: number of callable block arguments != number of function names provided.
Segmentation fault (core dumped)

The corresponding snippet:

import cudaq
import numpy as np
from typing import Callable

@cudaq.kernel
def X(q : cudaq.qubit, q1 : cudaq.qubit):
    x.ctrl(q, q1)

@cudaq.kernel
def CA(q : cudaq.qubit, q1 : cudaq.qubit, f: Callable[[cudaq.qubit, cudaq.qubit], None]):
    f(q, q1)

@cudaq.kernel
def CA2(a : cudaq.qubit, q : cudaq.qvector, f: Callable[[cudaq.qubit, cudaq.qubit], None]):
    f(a, q[0])
    
@cudaq.kernel
def kernel(b: np.ndarray, f: Callable[[cudaq.qubit, cudaq.qubit], None], g: Callable[[cudaq.qubit, cudaq.qubit], None]):
    q = cudaq.qvector(2)
    ancilla = cudaq.qubit()
    f(q[0], q[1])
    CA(ancilla, q[0], f) 
    cudaq.adjoint(CA, ancilla, q[0], f) # FIXME
    CA2(ancilla, q, f) 
    cudaq.adjoint(CA2, ancilla, q, f) # FIXME

bvec = np.zeros(8)
result = cudaq.sample(kernel, bvec, X, X) # two callables, one doesn't get used inside the kernel.

poojarao8 avatar Sep 30 '25 18:09 poojarao8

Seems like we are not handling list[Callable] as a parameter to a kernel in CUDA-Q. We will need to add this feature. In the meantime, I will try to get a workaround.

sacpis avatar Sep 30 '25 22:09 sacpis