NVIDIA/cuda-quantum

How to add multi-controlled gates when using `cudaq.make_kernel()` approach

Closed this issue · 2 comments

Greetings there,

Hope all are well. I am trying to wrap cuda-quantum in my package, and I would like to follow a qiskit UI. I saw the closest to that UI is to use cudaq.make_kernel() and add gates as methods. However, I am having some difficulty doing this for multi-controlled gates.

Here is a toy class example to demonstrate the issue:

import cudaq


class CUDA:
    def __init__(self,
                 num_qubits: int) -> None:
        self.num_qubits = num_qubits
        self.circuit = cudaq.make_kernel()
        self.qr = self.circuit.qalloc(self.num_qubits)

    def X(self,
          qubit_index: int) -> None:
        self.circuit.x(self.qr[qubit_index])

    def H(self,
          qubit_index: int) -> None:
        self.circuit.h(self.qr[qubit_index])

    def CX(self,
           control_index: int,
           target_index: int) -> None:
        # Create a Controlled-X gate
        target_kernel, qubit = cudaq.make_kernel(cudaq.qubit)
        target_kernel.x(qubit)
        # Apply the CX gate to the circuit at the specified control and target qubits
        self.circuit.control(target_kernel, self.qr[control_index], self.qr[target_index])

    def CCX(self,
            control_index1: int,
            control_index2: int,
            target_index: int) -> None:
        # Create a Controlled-X gate
        target_kernel, qubit = cudaq.make_kernel(cudaq.qubit)
        target_kernel.x(qubit)
        # Apply the CX gate to the circuit at the specified control and target qubits
        self.circuit.control(target_kernel, [self.qr[control_index1], self.qr[control_index2]], self.qr[target_index])

    def draw(self) -> None:
        print(cudaq.draw(self.circuit))

qc = CUDA(3)
qc.CCX(0, 1, 2)
qc.draw()

And the error I am getting is

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[11], [line 39](vscode-notebook-cell:?execution_count=11&line=39)
     [36](vscode-notebook-cell:?execution_count=11&line=36)         print(cudaq.draw(self.circuit))
     [38](vscode-notebook-cell:?execution_count=11&line=38) qc = CUDA(3)
---> [39](vscode-notebook-cell:?execution_count=11&line=39) qc.CCX(0, 1, 2)
     [40](vscode-notebook-cell:?execution_count=11&line=40) qc.draw()

Cell In[11], [line 33](vscode-notebook-cell:?execution_count=11&line=33)
     [31](vscode-notebook-cell:?execution_count=11&line=31) target_kernel.x(qubit)
     [32](vscode-notebook-cell:?execution_count=11&line=32) # Apply the CX gate to the circuit at the specified control and target qubits
---> [33](vscode-notebook-cell:?execution_count=11&line=33) self.circuit.control(target_kernel, [self.qr[control_index1], self.qr[control_index2]], self.qr[target_index])

File /mnt/c/Users/A.C.EA/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/cudaq/kernel/kernel_builder.py:871, in PyKernel.control(self, target, control, *target_arguments)
    [837](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/mnt/c/Users/A.C.EA/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/cudaq/kernel/kernel_builder.py:837) def control(self, target, control, *target_arguments):
    [838](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/mnt/c/Users/A.C.EA/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/cudaq/kernel/kernel_builder.py:838)     """
    [839](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/mnt/c/Users/A.C.EA/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/cudaq/kernel/kernel_builder.py:839)     Apply the `target` kernel as a controlled operation in-place to 
    [840](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/mnt/c/Users/A.C.EA/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/cudaq/kernel/kernel_builder.py:840)     `self`.Uses the provided `control` as control qubit/s for the operation.
   (...)
    [869](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/mnt/c/Users/A.C.EA/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/cudaq/kernel/kernel_builder.py:869)     ```
    [870](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/mnt/c/Users/A.C.EA/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/cudaq/kernel/kernel_builder.py:870)     """
--> [871](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/mnt/c/Users/A.C.EA/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/cudaq/kernel/kernel_builder.py:871)     self.__applyControlOrAdjoint(target, False, [control.mlirValue],
    [872](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/mnt/c/Users/A.C.EA/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/cudaq/kernel/kernel_builder.py:872)                                  *target_arguments)
    [873](https://vscode-remote+wsl-002bubuntu.vscode-resource.vscode-cdn.net/mnt/c/Users/A.C.EA/Documents/GitHub/QICKIT/.venv/lib/python3.11/site-packages/cudaq/kernel/kernel_builder.py:873)     return

AttributeError: 'list' object has no attribute 'mlirValue'

I am actively going through the docs and notebooks you have provided, but I'd appreciate your kind feedback on the best way of doing this as well.

Hi @ACE07-Sev,

Looking at your example, I think you can achieve it with:

import cudaq

class CUDA:
    def __init__(self,
                 num_qubits: int) -> None:
        self.num_qubits = num_qubits
        self.circuit = cudaq.make_kernel()
        self.qr = self.circuit.qalloc(self.num_qubits)

    def X(self,
          qubit_index: int) -> None:
        self.circuit.x(self.qr[qubit_index])

    def H(self,
          qubit_index: int) -> None:
        self.circuit.h(self.qr[qubit_index])

    def CX(self,
           control_index: int,
           target_index: int) -> None:
        self.circuit.cx(self.qr[control_index], self.qr[target_index])

    def CCX(self,
            control_index1: int,
            control_index2: int,
            target_index: int) -> None:
        self.circuit.cx([self.qr[control_index1], self.qr[control_index2]], self.qr[target_index])

    def draw(self) -> None:
        print(cudaq.draw(self.circuit))

qc = CUDA(3)
qc.H(1)
qc.CX(1, 2)
qc.CCX(0, 1, 2)
qc.draw()

Ooh I see, thank you very much!