From b6ab8be91944046befb4943be5f178e1996e4c34 Mon Sep 17 00:00:00 2001 From: "A.M. Santana" Date: Fri, 3 Nov 2023 01:55:00 +0000 Subject: [PATCH 01/10] upper half of implementation (broken lowering ot qir) Signed-off-by: A.M. Santana --- lib/Optimizer/CodeGen/LowerToQIR.cpp | 61 +++++++++++++++---- .../cudaq/builder/py_kernel_builder.cpp | 55 +++++++++++++++++ runtime/cudaq/builder/kernel_builder.h | 15 +++++ unittests/integration/builder_tester.cpp | 60 ++++++++++++------ 4 files changed, 159 insertions(+), 32 deletions(-) diff --git a/lib/Optimizer/CodeGen/LowerToQIR.cpp b/lib/Optimizer/CodeGen/LowerToQIR.cpp index 9bf01c457a..884d0c9c9b 100644 --- a/lib/Optimizer/CodeGen/LowerToQIR.cpp +++ b/lib/Optimizer/CodeGen/LowerToQIR.cpp @@ -727,22 +727,57 @@ class TwoTargetRewrite : public ConvertOpWithControls { ModuleOp parentModule = instOp->template getParentOfType(); auto context = parentModule->getContext(); auto qirFunctionName = std::string(cudaq::opt::QIRQISPrefix) + instName; - auto qubitIndexType = cudaq::opt::getQubitType(context); - SmallVector tmpArgTypes = {qubitIndexType, qubitIndexType}; - FlatSymbolRefAttr symbolRef = cudaq::opt::factory::createLLVMFunctionSymbol( - qirFunctionName, /*return type=*/LLVM::LLVMVoidType::get(context), - std::move(tmpArgTypes), parentModule); - // FIXME: Do we want to use any fast math flags? - // TODO: Looks like bugs in the MLIR cmake files don't install this any - // longer. - // auto fastMathFlags = LLVM::FMFAttr::get(context, {}); - // Create the CallOp for this quantum instruction - rewriter.replaceOpWithNewOp( - instOp, TypeRange{}, symbolRef, - adaptor.getOperands() /*, fastMathFlags*/); + // FIXME: once code works, simplify these conditional checks and their returns + + if (numControls == 0) { + SmallVector tmpArgTypes = {qubitIndexType, qubitIndexType}; + + FlatSymbolRefAttr symbolRef = cudaq::opt::factory::createLLVMFunctionSymbol( + qirFunctionName, /*return type=*/LLVM::LLVMVoidType::get(context), + std::move(tmpArgTypes), parentModule); + + // FIXME: Do we want to use any fast math flags? + // TODO: Looks like bugs in the MLIR cmake files don't install this any + // longer. + // auto fastMathFlags = LLVM::FMFAttr::get(context, {}); + // Create the CallOp for this quantum instruction + rewriter.replaceOpWithNewOp( + instOp, TypeRange{}, symbolRef, + adaptor.getOperands() /*, fastMathFlags*/); + return success(); + } + + // // This will be a controlled-operation. + // qirFunctionName += "__ctl"; + // auto qirArrayType = cudaq::opt::getArrayType(context); + // SmallVector tmpArgTypes = {qirArrayType, qubitIndexType, qubitIndexType}; + + // // // __quantum__qis__NAME__ctl(Array*, Qubit*, Qubit*) Type + // // auto instOpQISFunctionType = LLVM::LLVMFunctionType::get( + // // LLVM::LLVMVoidType::get(context), {qirArrayType, qubitIndexType, qubitIndexType}); + + // // Get the function pointer for the ctrl operation + // auto qirFunctionSymbolRef = cudaq::opt::factory::createLLVMFunctionSymbol( + // qirFunctionName, LLVM::LLVMVoidType::get(context), + // tmpArgTypes, parentModule); + + // // Get the first control's type + // auto control = *instOp.getControls().begin(); + // Type type = control.getType(); + // auto instOperands = adaptor.getOperands(); + // if (numControls == 1 && type.isa()) { + // // if (negatedQubitCtrls) + // // return instOp.emitError("unsupported controlled op " + instName + + // // " with vector of ctrl qubits"); + // // Operands are already an Array* and Qubit*. + // rewriter.replaceOpWithNewOp( + // instOp, TypeRange{}, qirFunctionSymbolRef, instOperands); + // return success(); + // } + return success(); } }; diff --git a/python/runtime/cudaq/builder/py_kernel_builder.cpp b/python/runtime/cudaq/builder/py_kernel_builder.cpp index ec02188b1f..99257c041f 100644 --- a/python/runtime/cudaq/builder/py_kernel_builder.cpp +++ b/python/runtime/cudaq/builder/py_kernel_builder.cpp @@ -658,6 +658,61 @@ target qubit/s. # SWAP their states, resulting in the transformation: `|10> -> |01>`. kernel.swap(first, second))#") + /// @brief Bind the controlled-SWAP gate with the controls provided in a + /// register of qubit/s. + .def( + "cswap", + [](kernel_builder<> &self, QuakeValue control, + const QuakeValue &first, const QuakeValue &second) { + return self.swap(control, first, second); + }, + py::arg("control"), py::arg("first"), py::arg("second"), + R"#(Swap the states of the provided qubits. + +Args: + control (:class:`QuakeValue`): The control qubit for the operation. + first (:class:`QuakeValue`): The target qubit of the operation. + Its state will swap with the `second` qubit, based on the state of the + input `controls`. + second (:class:`QuakeValue`): The target qubit of the operation. + Its state will swap with the `first` qubit, based on the state of the + input `controls`. + +.. code-block:: python + + # Example: + TODO)#") + /// @brief Bind the controlled-SWAP gate with the controls provided in a + /// vector. + .def( + "cswap", + [](kernel_builder<> &self, const std::vector controls, + const QuakeValue &first, const QuakeValue &second) { + return self.swap(controls, first, second); + }, + py::arg("controls"), py::arg("first"), py::arg("second"), + R"#(Swap the states of the provided qubits. + +Args: + controls (list[QuakeValue]): The list of control qubits for the operation. + first (:class:`QuakeValue`): The target qubit of the operation. + Its state will swap with the `second` qubit, based on the state of the + input `controls`. + second (:class:`QuakeValue`): The target qubit of the operation. + Its state will swap with the `first` qubit, based on the state of the + input `controls`. + +.. 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]))#") + /// @brief Allow for conditional statements on measurements. .def( "c_if", diff --git a/runtime/cudaq/builder/kernel_builder.h b/runtime/cudaq/builder/kernel_builder.h index 2744e102aa..418906641d 100644 --- a/runtime/cudaq/builder/kernel_builder.h +++ b/runtime/cudaq/builder/kernel_builder.h @@ -585,6 +585,21 @@ class kernel_builder : public details::kernel_builder_base { const std::vector &qubits{first, second}; details::swap(*opBuilder, empty, qubits); } + /// @brief SWAP operation for performing a Fredkin gate between two qubits, + /// based on the state of an input `control` qubit. + void swap(const QuakeValue &control, const QuakeValue &first, + const QuakeValue &second) { + const std::vector &ctrl{control}; + const std::vector &targets{first, second}; + details::swap(*opBuilder, ctrl, targets); + } + /// @brief SWAP operation for performing a Fredkin gate between two qubits, + /// based on the state of an input set of `controls`. + void swap(const std::vector &controls, const QuakeValue &first, + const QuakeValue &second) { + const std::vector &targets{first, second}; + details::swap(*opBuilder, controls, targets); + } /// @brief Reset the given qubit or qubits. void reset(const QuakeValue &qubit) { details::reset(*opBuilder, qubit); } diff --git a/unittests/integration/builder_tester.cpp b/unittests/integration/builder_tester.cpp index 8b17a41214..e3eaea54c1 100644 --- a/unittests/integration/builder_tester.cpp +++ b/unittests/integration/builder_tester.cpp @@ -432,37 +432,59 @@ CUDAQ_TEST(BuilderTester, checkRotations) { CUDAQ_TEST(BuilderTester, checkSwap) { cudaq::set_random_seed(13); - // Simple two-qubit swap. + // controlled-SWAP { auto kernel = cudaq::make_kernel(); + auto ctrl = kernel.qalloc(); auto q = kernel.qalloc(2); + kernel.x(ctrl); // 0th qubit into the 1-state. kernel.x(q[0]); // Swap their states and measure. - kernel.swap(q[0], q[1]); - // Measure. - kernel.mz(q); + kernel.swap(ctrl, q[0], q[1]); + + std::cout << kernel.to_quake() << "\n"; auto counts = cudaq::sample(kernel); counts.dump(); EXPECT_NEAR(counts.count("01"), 1000, 0); } - // Simple two-qubit swap. - { - auto kernel = cudaq::make_kernel(); - auto q = kernel.qalloc(2); - // 1st qubit into the 1-state. - kernel.x(q[1]); - // Swap their states and measure. - kernel.swap(q[0], q[1]); - // Measure. - kernel.mz(q); - - auto counts = cudaq::sample(kernel); - counts.dump(); - EXPECT_NEAR(counts.count("10"), 1000, 0); - } + // multi-controlled SWAP + + + + // // Simple two-qubit swap. + // { + // auto kernel = cudaq::make_kernel(); + // auto q = kernel.qalloc(2); + // // 0th qubit into the 1-state. + // kernel.x(q[0]); + // // Swap their states and measure. + // kernel.swap(q[0], q[1]); + // // Measure. + // kernel.mz(q); + + // auto counts = cudaq::sample(kernel); + // counts.dump(); + // EXPECT_NEAR(counts.count("01"), 1000, 0); + // } + + // // Simple two-qubit swap. + // { + // auto kernel = cudaq::make_kernel(); + // auto q = kernel.qalloc(2); + // // 1st qubit into the 1-state. + // kernel.x(q[1]); + // // Swap their states and measure. + // kernel.swap(q[0], q[1]); + // // Measure. + // kernel.mz(q); + + // auto counts = cudaq::sample(kernel); + // counts.dump(); + // EXPECT_NEAR(counts.count("10"), 1000, 0); + // } } // Conditional execution on the tensornet backend is slow for a large number of From 8d5a29f3f4f7bbe6e386cb81da3bf27204dc6848 Mon Sep 17 00:00:00 2001 From: "A.M. Santana" Date: Mon, 6 Nov 2023 17:12:47 +0000 Subject: [PATCH 02/10] clean up Signed-off-by: A.M. Santana --- python/runtime/cudaq/builder/py_kernel_builder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/runtime/cudaq/builder/py_kernel_builder.cpp b/python/runtime/cudaq/builder/py_kernel_builder.cpp index 99257c041f..7e3cbb1774 100644 --- a/python/runtime/cudaq/builder/py_kernel_builder.cpp +++ b/python/runtime/cudaq/builder/py_kernel_builder.cpp @@ -658,7 +658,7 @@ target qubit/s. # SWAP their states, resulting in the transformation: `|10> -> |01>`. kernel.swap(first, second))#") - /// @brief Bind the controlled-SWAP gate with the controls provided in a + /// @brief Bind the controlled-SWAP gate with the controls provided in a /// register of qubit/s. .def( "cswap", From 4aa052c425316ed7cb53ddf05e281fc5354d4044 Mon Sep 17 00:00:00 2001 From: "A.M. Santana" Date: Tue, 14 Nov 2023 17:01:09 +0000 Subject: [PATCH 03/10] revert bridge changes Signed-off-by: A.M. Santana --- lib/Optimizer/CodeGen/LowerToQIR.cpp | 61 ++++++---------------------- 1 file changed, 13 insertions(+), 48 deletions(-) diff --git a/lib/Optimizer/CodeGen/LowerToQIR.cpp b/lib/Optimizer/CodeGen/LowerToQIR.cpp index 884d0c9c9b..9bf01c457a 100644 --- a/lib/Optimizer/CodeGen/LowerToQIR.cpp +++ b/lib/Optimizer/CodeGen/LowerToQIR.cpp @@ -727,57 +727,22 @@ class TwoTargetRewrite : public ConvertOpWithControls { ModuleOp parentModule = instOp->template getParentOfType(); auto context = parentModule->getContext(); auto qirFunctionName = std::string(cudaq::opt::QIRQISPrefix) + instName; - auto qubitIndexType = cudaq::opt::getQubitType(context); + auto qubitIndexType = cudaq::opt::getQubitType(context); + SmallVector tmpArgTypes = {qubitIndexType, qubitIndexType}; - // FIXME: once code works, simplify these conditional checks and their returns - - if (numControls == 0) { - SmallVector tmpArgTypes = {qubitIndexType, qubitIndexType}; - - FlatSymbolRefAttr symbolRef = cudaq::opt::factory::createLLVMFunctionSymbol( - qirFunctionName, /*return type=*/LLVM::LLVMVoidType::get(context), - std::move(tmpArgTypes), parentModule); - - // FIXME: Do we want to use any fast math flags? - // TODO: Looks like bugs in the MLIR cmake files don't install this any - // longer. - // auto fastMathFlags = LLVM::FMFAttr::get(context, {}); - // Create the CallOp for this quantum instruction - rewriter.replaceOpWithNewOp( - instOp, TypeRange{}, symbolRef, - adaptor.getOperands() /*, fastMathFlags*/); - return success(); - } - - // // This will be a controlled-operation. - // qirFunctionName += "__ctl"; - // auto qirArrayType = cudaq::opt::getArrayType(context); - // SmallVector tmpArgTypes = {qirArrayType, qubitIndexType, qubitIndexType}; - - // // // __quantum__qis__NAME__ctl(Array*, Qubit*, Qubit*) Type - // // auto instOpQISFunctionType = LLVM::LLVMFunctionType::get( - // // LLVM::LLVMVoidType::get(context), {qirArrayType, qubitIndexType, qubitIndexType}); - - // // Get the function pointer for the ctrl operation - // auto qirFunctionSymbolRef = cudaq::opt::factory::createLLVMFunctionSymbol( - // qirFunctionName, LLVM::LLVMVoidType::get(context), - // tmpArgTypes, parentModule); - - // // Get the first control's type - // auto control = *instOp.getControls().begin(); - // Type type = control.getType(); - // auto instOperands = adaptor.getOperands(); - // if (numControls == 1 && type.isa()) { - // // if (negatedQubitCtrls) - // // return instOp.emitError("unsupported controlled op " + instName + - // // " with vector of ctrl qubits"); - // // Operands are already an Array* and Qubit*. - // rewriter.replaceOpWithNewOp( - // instOp, TypeRange{}, qirFunctionSymbolRef, instOperands); - // return success(); - // } + FlatSymbolRefAttr symbolRef = cudaq::opt::factory::createLLVMFunctionSymbol( + qirFunctionName, /*return type=*/LLVM::LLVMVoidType::get(context), + std::move(tmpArgTypes), parentModule); + // FIXME: Do we want to use any fast math flags? + // TODO: Looks like bugs in the MLIR cmake files don't install this any + // longer. + // auto fastMathFlags = LLVM::FMFAttr::get(context, {}); + // Create the CallOp for this quantum instruction + rewriter.replaceOpWithNewOp( + instOp, TypeRange{}, symbolRef, + adaptor.getOperands() /*, fastMathFlags*/); return success(); } }; From f1ff7161002ad5677a39062a0ae5ad08aa743c24 Mon Sep 17 00:00:00 2001 From: "A.M. Santana" Date: Wed, 15 Nov 2023 21:06:43 +0000 Subject: [PATCH 04/10] finish implementation and tests Signed-off-by: A.M. Santana --- docs/sphinx/api/languages/python_api.rst | 1 + .../cudaq/builder/py_kernel_builder.cpp | 34 +++- python/tests/compiler/ctrl_gates.py | 32 ++++ python/tests/unittests/test_kernel_builder.py | 78 ++++++++- python/tests/unittests/test_qpp_target.py | 1 + python/tests/utils/target_env_var_check.py | 9 +- python/tests/utils/target_env_var_reset.py | 7 +- runtime/cudaq/builder/kernel_builder.h | 24 ++- unittests/integration/builder_tester.cpp | 162 +++++++++++++----- 9 files changed, 291 insertions(+), 57 deletions(-) diff --git a/docs/sphinx/api/languages/python_api.rst b/docs/sphinx/api/languages/python_api.rst index 31524e3541..8cf6804522 100644 --- a/docs/sphinx/api/languages/python_api.rst +++ b/docs/sphinx/api/languages/python_api.rst @@ -37,6 +37,7 @@ Program Construction .. automethod:: r1 .. automethod:: cr1 .. automethod:: swap + .. automethod:: cswap .. automethod:: exp_pauli .. automethod:: mx .. automethod:: my diff --git a/python/runtime/cudaq/builder/py_kernel_builder.cpp b/python/runtime/cudaq/builder/py_kernel_builder.cpp index 7e3cbb1774..a03c1764c2 100644 --- a/python/runtime/cudaq/builder/py_kernel_builder.cpp +++ b/python/runtime/cudaq/builder/py_kernel_builder.cpp @@ -664,13 +664,13 @@ target qubit/s. "cswap", [](kernel_builder<> &self, QuakeValue control, const QuakeValue &first, const QuakeValue &second) { - return self.swap(control, first, second); + return self.swap(control, first, second); }, py::arg("control"), py::arg("first"), py::arg("second"), R"#(Swap the states of the provided qubits. Args: - control (:class:`QuakeValue`): The control qubit for the operation. + control (:class:`QuakeValue`): The control qubit/s for the operation. first (:class:`QuakeValue`): The target qubit of the operation. Its state will swap with the `second` qubit, based on the state of the input `controls`. @@ -681,14 +681,25 @@ target qubit/s. .. code-block:: python # Example: - TODO)#") + kernel = cudaq.make_kernel() + # Allocate control qubit/s to the `kernel`. + controls = kernel.qalloc(2) + # Allocate target qubit/s to the `kernel`. + targets = kernel.qalloc(2) + # Place the 0th qubit in the 1-state. + kernel.x(targets[0]) + # Place our controls in the 1-state. + kernel.x(controls[0]) + kernel.x(controls[1]) + # Swap their states if all `controls` are in the `1-state`. + kernel.swap(controls, targets[0], targets[1]))#") /// @brief Bind the controlled-SWAP gate with the controls provided in a /// vector. .def( "cswap", [](kernel_builder<> &self, const std::vector controls, const QuakeValue &first, const QuakeValue &second) { - return self.swap(controls, first, second); + return self.swap(controls, first, second); }, py::arg("controls"), py::arg("first"), py::arg("second"), R"#(Swap the states of the provided qubits. @@ -706,12 +717,17 @@ target qubit/s. # Example: kernel = cudaq.make_kernel() - # Allocate qubit/s to the `kernel`. - qubits = kernel.qalloc(2) + # Allocate control qubit/s to the `kernel`. + controls = [kernel.qalloc(), kernel.qalloc()] + # Allocate target qubit/s to the `kernel`. + targets = kernel.qalloc(2) # Place the 0th qubit in the 1-state. - kernel.x(qubits[0]) - # Swap their states. - kernel.swap(qubits[0], qubits[1]))#") + kernel.x(targets[0]) + # Place our controls in the 1-state. + kernel.x(controls[0]) + kernel.x(controls[1]) + # Swap their states if all `controls` are in the `1-state`. + kernel.swap(controls, targets[0], targets[1]))#") /// @brief Allow for conditional statements on measurements. .def( diff --git a/python/tests/compiler/ctrl_gates.py b/python/tests/compiler/ctrl_gates.py index 0aaa456222..cc9a83f0d3 100644 --- a/python/tests/compiler/ctrl_gates.py +++ b/python/tests/compiler/ctrl_gates.py @@ -268,6 +268,38 @@ def test_kernel_rotation_ctrl_register(): # CHECK: return # CHECK: } + +def test_ctrl_swap(): + """ + Tests the compilation of the various overloads of `cswap` gates. + """ + kernel = cudaq.make_kernel() + controls_vector = [kernel.qalloc() for _ in range(3)] + controls_register = kernel.qalloc(3) + first = kernel.qalloc() + second = kernel.qalloc() + + kernel.cswap(controls_vector, first, second) + kernel.cswap(controls_register, first, second) + kernel.cswap([controls_vector[0], controls_vector[1], controls_register], + first, second) + + print(kernel) + + +# CHECK-LABEL: func.func @__nvqpp__mlirgen____nvqppBuilderKernel_{{.*}}() attributes {"cudaq-entrypoint"} { +# CHECK: %[[VAL_0:.*]] = quake.alloca !quake.ref +# CHECK: %[[VAL_1:.*]] = quake.alloca !quake.ref +# CHECK: %[[VAL_2:.*]] = quake.alloca !quake.ref +# CHECK: %[[VAL_3:.*]] = quake.alloca !quake.veq<3> +# CHECK: %[[VAL_4:.*]] = quake.alloca !quake.ref +# CHECK: %[[VAL_5:.*]] = quake.alloca !quake.ref +# CHECK: quake.swap {{\[}}%[[VAL_0]], %[[VAL_1]], %[[VAL_2]]] %[[VAL_4]], %[[VAL_5]] : (!quake.ref, !quake.ref, !quake.ref, !quake.ref, !quake.ref) -> () +# CHECK: quake.swap {{\[}}%[[VAL_3]]] %[[VAL_4]], %[[VAL_5]] : (!quake.veq<3>, !quake.ref, !quake.ref) -> () +# CHECK: quake.swap {{\[}}%[[VAL_0]], %[[VAL_1]], %[[VAL_3]]] %[[VAL_4]], %[[VAL_5]] : (!quake.ref, !quake.ref, !quake.veq<3>, !quake.ref, !quake.ref) -> () +# CHECK: return +# CHECK: } + # leave for gdb debugging if __name__ == "__main__": loc = os.path.abspath(__file__) diff --git a/python/tests/unittests/test_kernel_builder.py b/python/tests/unittests/test_kernel_builder.py index bd3b11d014..f6633b4947 100644 --- a/python/tests/unittests/test_kernel_builder.py +++ b/python/tests/unittests/test_kernel_builder.py @@ -10,6 +10,7 @@ # the kernel builder. import pytest +import random import numpy as np import cudaq @@ -579,9 +580,80 @@ def test_crz_gate(): assert counts["0010011"] == 1000 -# FIXME -def test_cswap_gate(): - pass +@pytest.mark.parametrize("control_count", [1, 2, 3]) +def test_cswap_gate_ctrl_list(control_count): + """Tests the controlled-SWAP operation given a vector of control qubits.""" + kernel = cudaq.make_kernel() + controls = [kernel.qalloc() for _ in range(control_count)] + first = kernel.qalloc() + second = kernel.qalloc() + + kernel.x(first) + # All controls in the |0> state, no SWAP should occur. + kernel.cswap(controls, first, second) + # If we have multiple controls, place a random control qubit + # in the |1> state. This check ensures that our controlled + # SWAP's are performed if and only if all controls are in the + # |1> state. + if (len(controls) != 1): + random_index = random.randint(0, control_count - 1) + kernel.x(controls[random_index]) + # Not all controls in the in |1>, no SWAP. + kernel.cswap(controls, first, second) + # Rotate that random control back to |0>. + kernel.x(controls[random_index]) + + # Now place all of the controls in |1>. + for control in controls: + kernel.x(control) + # Should now SWAP our `first` and `second` qubits. + kernel.cswap(controls, first, second) + + counts = cudaq.sample(kernel) + print(counts) + + controls_state = "1" * control_count + want_state = controls_state + "01" + assert counts[want_state] == 1000 + + +def test_cswap_gate_mixed_ctrls(): + """ + Tests the controlled-SWAP gate given a list of a mix of ctrl + qubits and registers. + """ + kernel = cudaq.make_kernel() + controls_vector = [kernel.qalloc() for _ in range(2)] + controls_register = kernel.qalloc(2) + first = kernel.qalloc() + second = kernel.qalloc() + + # `first` in |1> state. + kernel.x(first) + # `controls_register` in |1> state. + kernel.x(controls_register) + + # `controls_vector` in |0>, no SWAP. + kernel.cswap(controls_vector, first, second) + # `controls_register` in |1>, SWAP. + kernel.cswap(controls_register, first, second) + # Pass the vector and register as the controls. The vector is still in |0>, so + # no SWAP. + kernel.cswap([controls_vector[0], controls_vector[1], controls_register], + first, second) + # Place the vector in |1>, should now get a SWAP. + kernel.x(controls_vector[0]) + kernel.x(controls_vector[1]) + kernel.cswap([controls_vector[0], controls_vector[1], controls_register], + first, second) + + counts = cudaq.sample(kernel) + print(counts) + + controls_state = "1111" + # The SWAP's should make the targets end up back in |10>. + want_state = controls_state + "10" + assert counts[want_state] == 1000 def test_crx_control_list(): diff --git a/python/tests/unittests/test_qpp_target.py b/python/tests/unittests/test_qpp_target.py index f2d1f65f69..0486891ef7 100644 --- a/python/tests/unittests/test_qpp_target.py +++ b/python/tests/unittests/test_qpp_target.py @@ -10,6 +10,7 @@ import cudaq + def test_cpu_only_target(): """Tests the QPP-based CPU-only target""" diff --git a/python/tests/utils/target_env_var_check.py b/python/tests/utils/target_env_var_check.py index 3ecae8b150..56d6c82b27 100644 --- a/python/tests/utils/target_env_var_check.py +++ b/python/tests/utils/target_env_var_check.py @@ -9,12 +9,14 @@ # RUN: PYTHONPATH=../../ pytest -rP %s import os + os.environ["CUDAQ_DEFAULT_SIMULATOR"] = "density-matrix-cpu" import pytest import cudaq + def test_default_target(): """Tests the default target set by environment variable""" @@ -31,13 +33,14 @@ def test_default_target(): assert '00' in result assert '11' in result + def test_env_var_with_emulate(): """Tests the target when emulating a hardware backend""" assert ("density-matrix-cpu" == cudaq.get_target().name) cudaq.set_target("quantinuum", emulate=True) assert ("quantinuum" == cudaq.get_target().name) - + kernel = cudaq.make_kernel() qubits = kernel.qalloc(2) kernel.h(qubits[0]) @@ -49,11 +52,12 @@ def test_env_var_with_emulate(): assert '00' in result assert '11' in result + def test_target_override(): """Tests the target set by environment variable is overridden by user setting""" cudaq.set_target("qpp-cpu") - assert("qpp-cpu" == cudaq.get_target().name) + assert ("qpp-cpu" == cudaq.get_target().name) kernel = cudaq.make_kernel() qubits = kernel.qalloc(2) @@ -66,6 +70,7 @@ def test_target_override(): assert '00' in result assert '11' in result + os.environ.pop("CUDAQ_DEFAULT_SIMULATOR") # leave for gdb debugging diff --git a/python/tests/utils/target_env_var_reset.py b/python/tests/utils/target_env_var_reset.py index df6ca43854..5cde81f3bf 100644 --- a/python/tests/utils/target_env_var_reset.py +++ b/python/tests/utils/target_env_var_reset.py @@ -9,20 +9,22 @@ # RUN: PYTHONPATH=../../ pytest -rP %s import os + os.environ["CUDAQ_DEFAULT_SIMULATOR"] = "density-matrix-cpu" import pytest import cudaq + def test_env_var_update(): """Tests that if the environment variable does not take effect on-the-fly""" os.environ["CUDAQ_DEFAULT_SIMULATOR"] = "qpp-cpu" - assert("qpp-cpu" != cudaq.get_target().name) + assert ("qpp-cpu" != cudaq.get_target().name) cudaq.set_target("qpp-cpu") - assert("qpp-cpu" == cudaq.get_target().name) + assert ("qpp-cpu" == cudaq.get_target().name) kernel = cudaq.make_kernel() qubits = kernel.qalloc(2) @@ -38,6 +40,7 @@ def test_env_var_update(): cudaq.reset_target() assert ("density-matrix-cpu" == cudaq.get_target().name) + os.environ.pop("CUDAQ_DEFAULT_SIMULATOR") # leave for gdb debugging diff --git a/runtime/cudaq/builder/kernel_builder.h b/runtime/cudaq/builder/kernel_builder.h index 418906641d..3a108ef865 100644 --- a/runtime/cudaq/builder/kernel_builder.h +++ b/runtime/cudaq/builder/kernel_builder.h @@ -586,7 +586,9 @@ class kernel_builder : public details::kernel_builder_base { details::swap(*opBuilder, empty, qubits); } /// @brief SWAP operation for performing a Fredkin gate between two qubits, - /// based on the state of an input `control` qubit. + /// based on the state of input `control` qubit/s. + template >> void swap(const QuakeValue &control, const QuakeValue &first, const QuakeValue &second) { const std::vector &ctrl{control}; @@ -594,12 +596,30 @@ class kernel_builder : public details::kernel_builder_base { details::swap(*opBuilder, ctrl, targets); } /// @brief SWAP operation for performing a Fredkin gate between two qubits, - /// based on the state of an input set of `controls`. + /// based on the state of an input vector of `controls`. + template >> void swap(const std::vector &controls, const QuakeValue &first, const QuakeValue &second) { const std::vector &targets{first, second}; details::swap(*opBuilder, controls, targets); } + // This final overload should accept variadic qubit args... for the first + // argument, then accept a first and second just like the other ctrl + // functions. This enforces that there will always be 2 final qubits in the + // lsit to SWAP, rather than implicitly pairing them off of the qubit args. + template < + typename mod, typename... QubitValues, + typename = typename std::enable_if_t= 3>, + typename = typename std::enable_if_t>> + void swap(QubitValues... args) { + std::vector values{args...}; + // Up until the last two arguments will be our controls. + const std::vector controls(values.begin(), values.end() - 2); + // The last two args will be the two qubits to swap. + const std::vector targets(values.end() - 2, values.end()); + details::swap(*opBuilder, controls, targets); + } /// @brief Reset the given qubit or qubits. void reset(const QuakeValue &qubit) { details::reset(*opBuilder, qubit); } diff --git a/unittests/integration/builder_tester.cpp b/unittests/integration/builder_tester.cpp index e3eaea54c1..50417e7830 100644 --- a/unittests/integration/builder_tester.cpp +++ b/unittests/integration/builder_tester.cpp @@ -432,7 +432,39 @@ CUDAQ_TEST(BuilderTester, checkRotations) { CUDAQ_TEST(BuilderTester, checkSwap) { cudaq::set_random_seed(13); - // controlled-SWAP + // Simple two-qubit swap. + { + auto kernel = cudaq::make_kernel(); + auto q = kernel.qalloc(2); + // 0th qubit into the 1-state. + kernel.x(q[0]); + // Swap their states and measure. + kernel.swap(q[0], q[1]); + // Measure. + kernel.mz(q); + + auto counts = cudaq::sample(kernel); + counts.dump(); + EXPECT_NEAR(counts.count("01"), 1000, 0); + } + + // Simple two-qubit swap. + { + auto kernel = cudaq::make_kernel(); + auto q = kernel.qalloc(2); + // 1st qubit into the 1-state. + kernel.x(q[1]); + // Swap their states and measure. + kernel.swap(q[0], q[1]); + // Measure. + kernel.mz(q); + + auto counts = cudaq::sample(kernel); + counts.dump(); + EXPECT_NEAR(counts.count("10"), 1000, 0); + } + + // Single qubit controlled-SWAP. { auto kernel = cudaq::make_kernel(); auto ctrl = kernel.qalloc(); @@ -441,50 +473,102 @@ CUDAQ_TEST(BuilderTester, checkSwap) { // 0th qubit into the 1-state. kernel.x(q[0]); // Swap their states and measure. - kernel.swap(ctrl, q[0], q[1]); - - std::cout << kernel.to_quake() << "\n"; + kernel.swap(ctrl, q[0], q[1]); auto counts = cudaq::sample(kernel); counts.dump(); EXPECT_NEAR(counts.count("01"), 1000, 0); } - // multi-controlled SWAP - - - - // // Simple two-qubit swap. - // { - // auto kernel = cudaq::make_kernel(); - // auto q = kernel.qalloc(2); - // // 0th qubit into the 1-state. - // kernel.x(q[0]); - // // Swap their states and measure. - // kernel.swap(q[0], q[1]); - // // Measure. - // kernel.mz(q); - - // auto counts = cudaq::sample(kernel); - // counts.dump(); - // EXPECT_NEAR(counts.count("01"), 1000, 0); - // } - - // // Simple two-qubit swap. - // { - // auto kernel = cudaq::make_kernel(); - // auto q = kernel.qalloc(2); - // // 1st qubit into the 1-state. - // kernel.x(q[1]); - // // Swap their states and measure. - // kernel.swap(q[0], q[1]); - // // Measure. - // kernel.mz(q); - - // auto counts = cudaq::sample(kernel); - // counts.dump(); - // EXPECT_NEAR(counts.count("10"), 1000, 0); - // } + // Multi-controlled SWAP with a ctrl register. + { + auto kernel = cudaq::make_kernel(); + auto ctrls = kernel.qalloc(3); + auto first = kernel.qalloc(); + auto second = kernel.qalloc(); + + // Rotate `first` to |1> state. + kernel.x(first); + + // Only a subset of controls in the |1> state. + kernel.x(ctrls[0]); + // No SWAP should occur. + kernel.swap(ctrls, first, second); + + // Flip the rest of the controls to |1>. + kernel.x(ctrls[1]); + kernel.x(ctrls[2]); + // `first` and `second` should SWAP. + kernel.swap(ctrls, first, second); + + auto counts = cudaq::sample(kernel); + counts.dump(); + auto ctrls_state = "111"; + // `first` is now |0>, `second` is now |1>. + auto want_state = ctrls_state + "01"; + EXPECT_NEAR(counts.count(want_state), 1000, 0); + } + + // Multi-controlled SWAP with a vector of ctrl qubits. + { + auto kernel = cudaq::make_kernel(); + std::vector ctrls{kernel.qalloc(), kernel.qalloc(), + kernel.qalloc()}; + auto first = kernel.qalloc(); + auto second = kernel.qalloc(); + + // Rotate `second` to |1> state. + kernel.x(second); + + // Only a subset of controls in the |1> state. + kernel.x(ctrls[0]); + // No SWAP should occur. + kernel.swap(ctrls, first, second); + + // Flip the rest of the controls to |1>. + kernel.x(ctrls[1]); + kernel.x(ctrls[2]); + // `first` and `second` should SWAP. + kernel.swap(ctrls, first, second); + + auto counts = cudaq::sample(kernel); + counts.dump(); + auto ctrls_state = "111"; + // `first` is now |1>, `second` is now |0>. + auto want_state = ctrls_state + "10"; + EXPECT_NEAR(counts.count(want_state), 1000, 0); + } + + // Multi-controlled SWAP with a variadic list of ctrl qubits. + { + auto kernel = cudaq::make_kernel(); + auto ctrls0 = kernel.qalloc(2); + auto ctrls1 = kernel.qalloc(); + auto ctrls2 = kernel.qalloc(2); + auto first = kernel.qalloc(); + auto second = kernel.qalloc(); + + // Rotate `second` to |1> state. + kernel.x(second); + + // Only a subset of controls in the |1> state. + kernel.x(ctrls0); + // No SWAP should occur. + kernel.swap(ctrls0, ctrls1, ctrls2, first, second); + + // Flip the rest of the controls to |1>. + kernel.x(ctrls1); + kernel.x(ctrls2); + // `first` and `second` should SWAP. + kernel.swap(ctrls0, ctrls1, ctrls2, first, second); + + auto counts = cudaq::sample(kernel); + counts.dump(); + auto ctrls_state = "11111"; + // `first` is now |1>, `second` is now |0>. + auto want_state = ctrls_state + "10"; + EXPECT_NEAR(counts.count(want_state), 1000, 0); + } } // Conditional execution on the tensornet backend is slow for a large number of From 0c68d5cc15645d9070229b0abbc9b1fa199f8f22 Mon Sep 17 00:00:00 2001 From: "A.M. Santana" Date: Wed, 15 Nov 2023 21:10:27 +0000 Subject: [PATCH 05/10] clean up docstring Signed-off-by: A.M. Santana --- runtime/cudaq/builder/kernel_builder.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/cudaq/builder/kernel_builder.h b/runtime/cudaq/builder/kernel_builder.h index 3a108ef865..33f15713da 100644 --- a/runtime/cudaq/builder/kernel_builder.h +++ b/runtime/cudaq/builder/kernel_builder.h @@ -604,10 +604,10 @@ class kernel_builder : public details::kernel_builder_base { const std::vector &targets{first, second}; details::swap(*opBuilder, controls, targets); } - // This final overload should accept variadic qubit args... for the first - // argument, then accept a first and second just like the other ctrl - // functions. This enforces that there will always be 2 final qubits in the - // lsit to SWAP, rather than implicitly pairing them off of the qubit args. + /// @brief SWAP operation for performing a Fredkin gate between two qubits, + /// based on the state of a variadic input of control qubits and registers. + /// Note: the final two qubits in the variadic list will always be the qubits + /// that undergo a SWAP. This requires >=3 qubits in the args. template < typename mod, typename... QubitValues, typename = typename std::enable_if_t= 3>, From 69000e27560ec5b701010f0f6a0f129ace7ef7a6 Mon Sep 17 00:00:00 2001 From: "A.M. Santana" Date: Wed, 15 Nov 2023 23:22:46 +0000 Subject: [PATCH 06/10] fix bugs in tests Signed-off-by: A.M. Santana --- unittests/integration/builder_tester.cpp | 48 ++++++++++++------------ 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/unittests/integration/builder_tester.cpp b/unittests/integration/builder_tester.cpp index 50417e7830..b1b09526b4 100644 --- a/unittests/integration/builder_tester.cpp +++ b/unittests/integration/builder_tester.cpp @@ -435,13 +435,12 @@ CUDAQ_TEST(BuilderTester, checkSwap) { // Simple two-qubit swap. { auto kernel = cudaq::make_kernel(); - auto q = kernel.qalloc(2); - // 0th qubit into the 1-state. - kernel.x(q[0]); + auto first = kernel.qalloc(); + auto second = kernel.qalloc(); + // `first` qubit into the 1-state. + kernel.x(first); // Swap their states and measure. - kernel.swap(q[0], q[1]); - // Measure. - kernel.mz(q); + kernel.swap(first, second); auto counts = cudaq::sample(kernel); counts.dump(); @@ -451,13 +450,12 @@ CUDAQ_TEST(BuilderTester, checkSwap) { // Simple two-qubit swap. { auto kernel = cudaq::make_kernel(); - auto q = kernel.qalloc(2); - // 1st qubit into the 1-state. - kernel.x(q[1]); + auto first = kernel.qalloc(); + auto second = kernel.qalloc(); + // `second` qubit into the 1-state. + kernel.x(second); // Swap their states and measure. - kernel.swap(q[0], q[1]); - // Measure. - kernel.mz(q); + kernel.swap(first, second); auto counts = cudaq::sample(kernel); counts.dump(); @@ -468,16 +466,17 @@ CUDAQ_TEST(BuilderTester, checkSwap) { { auto kernel = cudaq::make_kernel(); auto ctrl = kernel.qalloc(); - auto q = kernel.qalloc(2); + auto first = kernel.qalloc(); + auto second = kernel.qalloc(); + // ctrl and `first` in the 1-state. kernel.x(ctrl); - // 0th qubit into the 1-state. - kernel.x(q[0]); + kernel.x(first); // Swap their states and measure. - kernel.swap(ctrl, q[0], q[1]); + kernel.swap(ctrl, first, second); auto counts = cudaq::sample(kernel); counts.dump(); - EXPECT_NEAR(counts.count("01"), 1000, 0); + EXPECT_NEAR(counts.count("101"), 1000, 0); } // Multi-controlled SWAP with a ctrl register. @@ -503,9 +502,10 @@ CUDAQ_TEST(BuilderTester, checkSwap) { auto counts = cudaq::sample(kernel); counts.dump(); - auto ctrls_state = "111"; + std::string ctrls_state = "111"; // `first` is now |0>, `second` is now |1>. - auto want_state = ctrls_state + "01"; + std::string want_target = "01"; + auto want_state = ctrls_state + want_target; EXPECT_NEAR(counts.count(want_state), 1000, 0); } @@ -533,9 +533,10 @@ CUDAQ_TEST(BuilderTester, checkSwap) { auto counts = cudaq::sample(kernel); counts.dump(); - auto ctrls_state = "111"; + std::string ctrls_state = "111"; // `first` is now |1>, `second` is now |0>. - auto want_state = ctrls_state + "10"; + std::string want_target = "10"; + auto want_state = ctrls_state + want_target; EXPECT_NEAR(counts.count(want_state), 1000, 0); } @@ -564,9 +565,10 @@ CUDAQ_TEST(BuilderTester, checkSwap) { auto counts = cudaq::sample(kernel); counts.dump(); - auto ctrls_state = "11111"; + std::string ctrls_state = "11111"; // `first` is now |1>, `second` is now |0>. - auto want_state = ctrls_state + "10"; + std::string want_target = "10"; + auto want_state = ctrls_state + want_target; EXPECT_NEAR(counts.count(want_state), 1000, 0); } } From b76d8e84ce3be37062390ebc4b05c729153025e4 Mon Sep 17 00:00:00 2001 From: "A.M. Santana" Date: Thu, 16 Nov 2023 17:24:16 +0000 Subject: [PATCH 07/10] pr comments Signed-off-by: A.M. Santana --- .../cudaq/builder/py_kernel_builder.cpp | 2 +- python/tests/compiler/ctrl_gates.py | 34 +++++++++---------- python/tests/unittests/test_kernel_builder.py | 2 +- runtime/cudaq/builder/kernel_builder.h | 9 +++-- 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/python/runtime/cudaq/builder/py_kernel_builder.cpp b/python/runtime/cudaq/builder/py_kernel_builder.cpp index a03c1764c2..5571fca9c7 100644 --- a/python/runtime/cudaq/builder/py_kernel_builder.cpp +++ b/python/runtime/cudaq/builder/py_kernel_builder.cpp @@ -662,7 +662,7 @@ target qubit/s. /// register of qubit/s. .def( "cswap", - [](kernel_builder<> &self, QuakeValue control, + [](kernel_builder<> &self, const QuakeValue &control, const QuakeValue &first, const QuakeValue &second) { return self.swap(control, first, second); }, diff --git a/python/tests/compiler/ctrl_gates.py b/python/tests/compiler/ctrl_gates.py index cc9a83f0d3..bcaa3bd532 100644 --- a/python/tests/compiler/ctrl_gates.py +++ b/python/tests/compiler/ctrl_gates.py @@ -177,8 +177,8 @@ def test_kernel_ctrl_register(): # CHECK-LABEL: func.func @__nvqpp__mlirgen____nvqppBuilderKernel_{{.*}}() attributes {"cudaq-entrypoint"} { -# CHECK: %[[VAL_0:.*]] = quake.alloca !quake.veq<3> -# CHECK: %[[VAL_1:.*]] = quake.alloca !quake.veq<2> +# CHECK-DAG: %[[VAL_0:.*]] = quake.alloca !quake.veq<3> +# CHECK-DAG: %[[VAL_1:.*]] = quake.alloca !quake.veq<2> # CHECK: %[[VAL_2:.*]] = quake.extract_ref %[[VAL_1]][0] : (!quake.veq<2>) -> !quake.ref # CHECK: %[[VAL_3:.*]] = quake.extract_ref %[[VAL_1]][1] : (!quake.veq<2>) -> !quake.ref # CHECK: quake.h {{\[}}%[[VAL_0]]] %[[VAL_2]] : (!quake.veq<3>, !quake.ref) -> () @@ -224,15 +224,15 @@ def test_kernel_rotation_ctrl_register(): # CHECK-LABEL: func.func @__nvqpp__mlirgen____nvqppBuilderKernel_{{.*}}( # CHECK-SAME: %[[VAL_0:.*]]: !cc.stdvec) attributes {"cudaq-entrypoint"} { -# CHECK: %[[VAL_1:.*]] = arith.constant 3 : index -# CHECK: %[[VAL_2:.*]] = arith.constant 3.000000e+00 : f64 -# CHECK: %[[VAL_3:.*]] = arith.constant 2.000000e+00 : f64 -# CHECK: %[[VAL_4:.*]] = arith.constant 1.000000e+00 : f64 -# CHECK: %[[VAL_5:.*]] = arith.constant 0.000000e+00 : f64 -# CHECK: %[[VAL_6:.*]] = arith.constant 1 : index -# CHECK: %[[VAL_7:.*]] = arith.constant 0 : index -# CHECK: %[[VAL_8:.*]] = quake.alloca !quake.veq<3> -# CHECK: %[[VAL_9:.*]] = quake.alloca !quake.veq<2> +# CHECK-DAG: %[[VAL_1:.*]] = arith.constant 3 : index +# CHECK-DAG: %[[VAL_2:.*]] = arith.constant 3.000000e+00 : f64 +# CHECK-DAG: %[[VAL_3:.*]] = arith.constant 2.000000e+00 : f64 +# CHECK-DAG: %[[VAL_4:.*]] = arith.constant 1.000000e+00 : f64 +# CHECK-DAG: %[[VAL_5:.*]] = arith.constant 0.000000e+00 : f64 +# CHECK-DAG: %[[VAL_6:.*]] = arith.constant 1 : index +# CHECK-DAG: %[[VAL_7:.*]] = arith.constant 0 : index +# CHECK-DAG: %[[VAL_8:.*]] = quake.alloca !quake.veq<3> +# CHECK-DAG: %[[VAL_9:.*]] = quake.alloca !quake.veq<2> # CHECK: %[[VAL_10:.*]] = quake.extract_ref %[[VAL_9]][0] : (!quake.veq<2>) -> !quake.ref # CHECK: %[[VAL_11:.*]] = quake.extract_ref %[[VAL_9]][1] : (!quake.veq<2>) -> !quake.ref # CHECK: %[[VAL_12:.*]] = cc.loop while ((%[[VAL_13:.*]] = %[[VAL_7]]) -> (index)) { @@ -288,12 +288,12 @@ def test_ctrl_swap(): # CHECK-LABEL: func.func @__nvqpp__mlirgen____nvqppBuilderKernel_{{.*}}() attributes {"cudaq-entrypoint"} { -# CHECK: %[[VAL_0:.*]] = quake.alloca !quake.ref -# CHECK: %[[VAL_1:.*]] = quake.alloca !quake.ref -# CHECK: %[[VAL_2:.*]] = quake.alloca !quake.ref -# CHECK: %[[VAL_3:.*]] = quake.alloca !quake.veq<3> -# CHECK: %[[VAL_4:.*]] = quake.alloca !quake.ref -# CHECK: %[[VAL_5:.*]] = quake.alloca !quake.ref +# CHECK-DAG: %[[VAL_0:.*]] = quake.alloca !quake.ref +# CHECK-DAG: %[[VAL_1:.*]] = quake.alloca !quake.ref +# CHECK-DAG: %[[VAL_2:.*]] = quake.alloca !quake.ref +# CHECK-DAG: %[[VAL_3:.*]] = quake.alloca !quake.veq<3> +# CHECK-DAG: %[[VAL_4:.*]] = quake.alloca !quake.ref +# CHECK-DAG: %[[VAL_5:.*]] = quake.alloca !quake.ref # CHECK: quake.swap {{\[}}%[[VAL_0]], %[[VAL_1]], %[[VAL_2]]] %[[VAL_4]], %[[VAL_5]] : (!quake.ref, !quake.ref, !quake.ref, !quake.ref, !quake.ref) -> () # CHECK: quake.swap {{\[}}%[[VAL_3]]] %[[VAL_4]], %[[VAL_5]] : (!quake.veq<3>, !quake.ref, !quake.ref) -> () # CHECK: quake.swap {{\[}}%[[VAL_0]], %[[VAL_1]], %[[VAL_3]]] %[[VAL_4]], %[[VAL_5]] : (!quake.ref, !quake.ref, !quake.veq<3>, !quake.ref, !quake.ref) -> () diff --git a/python/tests/unittests/test_kernel_builder.py b/python/tests/unittests/test_kernel_builder.py index f6633b4947..835b5c2205 100644 --- a/python/tests/unittests/test_kernel_builder.py +++ b/python/tests/unittests/test_kernel_builder.py @@ -595,7 +595,7 @@ def test_cswap_gate_ctrl_list(control_count): # in the |1> state. This check ensures that our controlled # SWAP's are performed if and only if all controls are in the # |1> state. - if (len(controls) != 1): + if (len(controls) > 1): random_index = random.randint(0, control_count - 1) kernel.x(controls[random_index]) # Not all controls in the in |1>, no SWAP. diff --git a/runtime/cudaq/builder/kernel_builder.h b/runtime/cudaq/builder/kernel_builder.h index 33f15713da..6acee8bb8b 100644 --- a/runtime/cudaq/builder/kernel_builder.h +++ b/runtime/cudaq/builder/kernel_builder.h @@ -585,25 +585,28 @@ class kernel_builder : public details::kernel_builder_base { const std::vector &qubits{first, second}; details::swap(*opBuilder, empty, qubits); } + /// @brief SWAP operation for performing a Fredkin gate between two qubits, /// based on the state of input `control` qubit/s. template >> void swap(const QuakeValue &control, const QuakeValue &first, const QuakeValue &second) { - const std::vector &ctrl{control}; - const std::vector &targets{first, second}; + const std::vector ctrl{control}; + const std::vector targets{first, second}; details::swap(*opBuilder, ctrl, targets); } + /// @brief SWAP operation for performing a Fredkin gate between two qubits, /// based on the state of an input vector of `controls`. template >> void swap(const std::vector &controls, const QuakeValue &first, const QuakeValue &second) { - const std::vector &targets{first, second}; + const std::vector targets{first, second}; details::swap(*opBuilder, controls, targets); } + /// @brief SWAP operation for performing a Fredkin gate between two qubits, /// based on the state of a variadic input of control qubits and registers. /// Note: the final two qubits in the variadic list will always be the qubits From 5636a07173b13cfa6dec841240e10964ed0795eb Mon Sep 17 00:00:00 2001 From: "A.M. Santana" Date: Thu, 16 Nov 2023 17:30:46 +0000 Subject: [PATCH 08/10] fixes to python docstring Signed-off-by: A.M. Santana --- .../cudaq/builder/py_kernel_builder.cpp | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/python/runtime/cudaq/builder/py_kernel_builder.cpp b/python/runtime/cudaq/builder/py_kernel_builder.cpp index 5571fca9c7..8981a91602 100644 --- a/python/runtime/cudaq/builder/py_kernel_builder.cpp +++ b/python/runtime/cudaq/builder/py_kernel_builder.cpp @@ -667,32 +667,33 @@ target qubit/s. return self.swap(control, first, second); }, py::arg("control"), py::arg("first"), py::arg("second"), - R"#(Swap the states of the provided qubits. + R"#(Perform a controlled-SWAP operation between two qubits, +if and only if the state of the provided `control` qubit/s is 1. Args: control (:class:`QuakeValue`): The control qubit/s for the operation. first (:class:`QuakeValue`): The target qubit of the operation. Its state will swap with the `second` qubit, based on the state of the - input `controls`. + input `control`. second (:class:`QuakeValue`): The target qubit of the operation. Its state will swap with the `first` qubit, based on the state of the - input `controls`. + input `control`. .. code-block:: python # Example: kernel = cudaq.make_kernel() # Allocate control qubit/s to the `kernel`. - controls = kernel.qalloc(2) - # Allocate target qubit/s to the `kernel`. + control = kernel.qalloc(2) + # Allocate target qubits to the `kernel`. targets = kernel.qalloc(2) - # Place the 0th qubit in the 1-state. + # Place the 0th target qubit in the 1-state. kernel.x(targets[0]) - # Place our controls in the 1-state. - kernel.x(controls[0]) - kernel.x(controls[1]) - # Swap their states if all `controls` are in the `1-state`. - kernel.swap(controls, targets[0], targets[1]))#") + # Place our control/s in the 1-state. + kernel.x(control) + # Since the `control` is all in the 1-state, our target + # states should undergo a SWAP. + kernel.cswap(control, targets[0], targets[1]))#") /// @brief Bind the controlled-SWAP gate with the controls provided in a /// vector. .def( @@ -702,7 +703,8 @@ target qubit/s. return self.swap(controls, first, second); }, py::arg("controls"), py::arg("first"), py::arg("second"), - R"#(Swap the states of the provided qubits. + R"#(Perform a controlled-SWAP operation between two qubits, +if and only if the states of all provided `control` qubits are 1. Args: controls (list[QuakeValue]): The list of control qubits for the operation. @@ -717,17 +719,18 @@ target qubit/s. # Example: kernel = cudaq.make_kernel() - # Allocate control qubit/s to the `kernel`. + # Allocate control qubits to the `kernel`. controls = [kernel.qalloc(), kernel.qalloc()] - # Allocate target qubit/s to the `kernel`. + # Allocate target qubits to the `kernel`. targets = kernel.qalloc(2) - # Place the 0th qubit in the 1-state. + # Place the 0th target qubit in the 1-state. kernel.x(targets[0]) # Place our controls in the 1-state. kernel.x(controls[0]) kernel.x(controls[1]) - # Swap their states if all `controls` are in the `1-state`. - kernel.swap(controls, targets[0], targets[1]))#") + # Since the `controls` are all in the 1-state, our target + # states should undergo a SWAP. + kernel.cswap(controls, targets[0], targets[1]))#") /// @brief Allow for conditional statements on measurements. .def( From 2f0e30586ddc53b8a19a20b60c59a66866e8e2fe Mon Sep 17 00:00:00 2001 From: "A.M. Santana" Date: Mon, 11 Dec 2023 23:05:30 +0000 Subject: [PATCH 09/10] spelling fix Signed-off-by: A.M. Santana --- runtime/cudaq/builder/kernel_builder.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/cudaq/builder/kernel_builder.h b/runtime/cudaq/builder/kernel_builder.h index 6acee8bb8b..507ff93f7b 100644 --- a/runtime/cudaq/builder/kernel_builder.h +++ b/runtime/cudaq/builder/kernel_builder.h @@ -610,7 +610,7 @@ class kernel_builder : public details::kernel_builder_base { /// @brief SWAP operation for performing a Fredkin gate between two qubits, /// based on the state of a variadic input of control qubits and registers. /// Note: the final two qubits in the variadic list will always be the qubits - /// that undergo a SWAP. This requires >=3 qubits in the args. + /// that undergo a SWAP. This requires >=3 qubits in the arguments. template < typename mod, typename... QubitValues, typename = typename std::enable_if_t= 3>, From ac1013fcf980d379be19f47e0741a6856b280437 Mon Sep 17 00:00:00 2001 From: "A.M. Santana" Date: Mon, 18 Dec 2023 18:19:03 +0000 Subject: [PATCH 10/10] ignore cudaq ctrl missing in docs Signed-off-by: A.M. Santana --- docs/sphinx/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/sphinx/conf.py b/docs/sphinx/conf.py index e6de899680..30219be29f 100644 --- a/docs/sphinx/conf.py +++ b/docs/sphinx/conf.py @@ -171,6 +171,7 @@ def setup(app): ('cpp:identifier', 'BinarySymplecticForm'), ('cpp:identifier', 'CountsDictionary'), ('cpp:identifier', 'QuakeValueOrNumericType'), + ('cpp:identifier', 'cudaq::ctrl'), ('py:class', 'function'), ('py:class', 'type'), ('py:class', 'cudaq::spin_op'),