Skip to content

Commit

Permalink
Support for controlled gates in python (#784)
Browse files Browse the repository at this point in the history
Closes #723
Closes #751

Binds the controlled versions of: r1, rx, ry, rz to python
New python unit tests for existing: cx, cy, cz, ch, cs, ct
New python compiler tests for existing: cx, cy, cz, ch, cs, ct
New python unit tests for r1, rx, ry, rz
New python compiler tests for r1, rx, ry, rz
Update existing python documentation and tests to reflect that we can now pass a register of controls to our c-gates
What this PR doesn't address:

A follow-up PR will handle the controlled SWAP implementation in the C++/python builder

* everything except controlled swaps

Signed-off-by: A.M. Santana <[email protected]>

* forgot the parameter annotations

Signed-off-by: A.M. Santana <[email protected]>

* add keywords back to compiler test

Signed-off-by: A.M. Santana <[email protected]>

* swap implementation

Signed-off-by: A.M. Santana <[email protected]>

* remove swap work for separate pr

Signed-off-by: A.M. Santana <[email protected]>

* format compiler test

Signed-off-by: A.M. Santana <[email protected]>

* incorrect filecheck string

Signed-off-by: A.M. Santana <[email protected]>

* hard coded kernel names in filecheck

Signed-off-by: A.M. Santana <[email protected]>

* forgot to add new gates to api docs

Signed-off-by: A.M. Santana <[email protected]>

* replace references to doubles with floats in python docs

Signed-off-by: A.M. Santana <[email protected]>

---------

Signed-off-by: A.M. Santana <[email protected]>
  • Loading branch information
anthony-santana authored Oct 18, 2023
1 parent 6835a5a commit 03a98d4
Show file tree
Hide file tree
Showing 7 changed files with 717 additions and 137 deletions.
4 changes: 4 additions & 0 deletions docs/sphinx/api/languages/python_api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ Program Construction
.. automethod:: tdg
.. automethod:: ct
.. automethod:: rx
.. automethod:: crx
.. automethod:: ry
.. automethod:: cry
.. automethod:: rz
.. automethod:: crz
.. automethod:: r1
.. automethod:: cr1
.. automethod:: swap
.. automethod:: exp_pauli
.. automethod:: mx
Expand Down
92 changes: 77 additions & 15 deletions python/runtime/cudaq/builder/py_kernel_builder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,16 +212,17 @@ and rotations and return a valid, callable, CUDA Quantum kernel.
}, \
py::arg("control"), py::arg("target"), \
"Apply a controlled-" #NAME " operation" \
" to the given target qubit, with the provided control qubit.\n" \
" to the given target qubit, with the provided control qubit/s.\n" \
"\nArgs:\n" \
" control (:class:`QuakeValue`): The control qubit for the " \
"operation. Must be a single qubit, registers are not a valid " \
"`control` argument.\n" \
" control (:class:`QuakeValue`): The control qubit/s for the " \
"operation.\n" \
" target (:class:`QuakeValue`): The target qubit of the " \
"operation.\n" \
"\n.. code-block:: python\n\n" \
" # Example:\n" \
" kernel = cudaq.make_kernel()\n" \
" # Our `control` may either be a single qubit or a register of " \
"qubits.\n" \
" control = kernel.qalloc()\n" \
" target = kernel.qalloc()\n" \
" # Apply a controlled-" #NAME " between the two qubits.\n" \
Expand Down Expand Up @@ -280,9 +281,9 @@ and rotations and return a valid, callable, CUDA Quantum kernel.
py::arg("parameter"), py::arg("target"), \
"Apply " #NAME \
" to the given target qubit, parameterized by the provided " \
"double value (`parameter`).\n" \
"float value (`parameter`).\n" \
"\nArgs:\n" \
" parameter (float): The double value to " \
" parameter (float): The float value to " \
"parameterize " \
"the " #NAME " gate over.\n" \
" target (:class:`QuakeValue`): The target qubit of the " #NAME \
Expand All @@ -292,7 +293,59 @@ and rotations and return a valid, callable, CUDA Quantum kernel.
" kernel = cudaq.make_kernel() \n" \
" # Apply an " #NAME \
" to the kernel at a concrete parameter value.\n" \
" kernel." #NAME "(parameter=3.14, target=qubit)\n")
" kernel." #NAME "(parameter=3.14, target=qubit)\n") \
.def( \
"c" #NAME, \
[](kernel_builder<> &self, QuakeValue &parameter, \
QuakeValue &control, QuakeValue &target) { \
self.NAME<cudaq::ctrl>(parameter, control, target); \
}, \
py::arg("parameter"), py::arg("control"), py::arg("target"), \
"Apply a controlled-" #NAME " operation" \
" to the given target qubit, with the provided control qubit/s.\n" \
"\nArgs:\n" \
" parameter (:class:`QuakeValue`): The kernel argument to " \
"parameterize the " #NAME " gate over.\n" \
" control (:class:`QuakeValue`): The control qubit/s for the " \
"operation.\n" \
" target (:class:`QuakeValue`): The target qubit of the " \
"operation.\n" \
"\n.. code-block:: python\n\n" \
" # Example:\n" \
" kernel, angle = cudaq.make_kernel(float)\n" \
" # Our `control` may either be a single qubit or a register of " \
"qubits.\n" \
" control = kernel.qalloc()\n" \
" target = kernel.qalloc()\n" \
" # Apply a controlled-" #NAME " between the qubits.\n" \
" kernel.c" #NAME \
"(parameter=angle, control=control, target=target)\n") \
.def( \
"c" #NAME, \
[](kernel_builder<> &self, double &parameter, QuakeValue &control, \
QuakeValue &target) { \
self.NAME<cudaq::ctrl>(parameter, control, target); \
}, \
py::arg("parameter"), py::arg("control"), py::arg("target"), \
"Apply a controlled-" #NAME " operation" \
" to the given target qubit, with the provided control qubit/s.\n" \
"\nArgs:\n" \
" parameter (float): The float value to " \
"parameterize the " #NAME " gate over.\n" \
" control (:class:`QuakeValue`): The control qubit/s for the " \
"operation.\n" \
" target (:class:`QuakeValue`): The target qubit of the " \
"operation.\n" \
"\n.. code-block:: python\n\n" \
" # Example:\n" \
" kernel = cudaq.make_kernel()\n" \
" # Our `control` may either be a single qubit or a register of " \
"qubits.\n" \
" control = kernel.qalloc()\n" \
" target = kernel.qalloc()\n" \
" # Apply a controlled-" #NAME " between the two qubits.\n" \
" kernel.c" #NAME \
"(parameter=3.14, control=control, target=target)\n")

#define ADD_BUILDER_PARAM_TWO_QUBIT_LIB_GATE(NAME, CUDAQ_FUNC) \
.def( \
Expand Down Expand Up @@ -326,9 +379,9 @@ and rotations and return a valid, callable, CUDA Quantum kernel.
py::arg("parameter"), py::arg("q0"), py::arg("q1"), \
"Apply " #NAME \
" to the given target qubits, parameterized by the provided " \
"double value (`parameter`).\n" \
"float value (`parameter`).\n" \
"\nArgs:\n" \
" parameter (float): The double value to " \
" parameter (float): The float value to " \
"parameterize " \
"the " #NAME " gate over.\n" \
" q0 (:class:`QuakeValue`): The first qubit operand of the " #NAME \
Expand Down Expand Up @@ -532,16 +585,25 @@ target qubit/s.
py::arg("first"), py::arg("second"),
R"#(Swap the states of the provided qubits.
Args:
first (:class:`QuakeValue`): The target qubit of the operation.
Its state will be swapped with the `second` qubit.
second (:class:`QuakeValue`): The target qubit of the operation.
Its state will be swapped with the `first` qubit.
.. code-block:: python
# Example:
kernel = cudaq.make_kernel()
# Allocate qubit/s to the `kernel`.
qubits = kernel.qalloc(2)
# Place the 0th qubit in the 1-state.
kernel.x(qubits[0])
# Swap their states.
kernel.swap(qubits[0], qubits[1]))#")
first = kernel.qalloc()
second = kernel.qalloc()
# Place `first` in the |1> state, and leave `second` in |0>.
kernel.x(first)
# SWAP their states, resulting in the transformation: `|10> -> |01>`.
kernel.swap(first, second))#")

/// @brief Allow for conditional statements on measurements.
.def(
"c_if",
Expand Down
Loading

0 comments on commit 03a98d4

Please sign in to comment.