From fd9df9f88381fb3a0dc3dbab988ee736bfe35579 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Fri, 18 Oct 2024 15:07:46 -0400 Subject: [PATCH 01/53] initial commit From 942c84185e6104b2517dc2bd55ff569709524cb8 Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Fri, 18 Oct 2024 19:39:13 +0000 Subject: [PATCH 02/53] Auto update version from '0.39.0-dev46' to '0.39.0-dev47' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 37f7ba1bbf..de59ceba06 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.39.0-dev46" +__version__ = "0.39.0-dev47" From 035bea0faa8f34112c3d6ced9d55d2300ac14608 Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Fri, 18 Oct 2024 23:26:06 +0000 Subject: [PATCH 03/53] Auto update version from '0.39.0-dev47' to '0.39.0-dev48' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index de59ceba06..6f841927f6 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.39.0-dev47" +__version__ = "0.39.0-dev48" From 78d4f7bcaf1133c30d8784c925fe2f20e024161a Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Thu, 24 Oct 2024 13:03:27 +0000 Subject: [PATCH 04/53] Auto update version from '0.39.0-dev48' to '0.39.0-dev49' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 6f841927f6..eba0279f93 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.39.0-dev48" +__version__ = "0.39.0-dev49" From a964795cde2ebcec238c23d8e4a36b76f9c2f68a Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Fri, 8 Nov 2024 14:51:53 -0500 Subject: [PATCH 05/53] merge with master --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 45d213d59b..8432c4f824 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev4" \ No newline at end of file +__version__ = "0.40.0-dev4" From 0f5beeed0b9c36c9595f481f080519b71a3198a9 Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Fri, 8 Nov 2024 19:59:44 +0000 Subject: [PATCH 06/53] Auto update version from '0.40.0-dev4' to '0.40.0-dev5' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 8432c4f824..36daa70a97 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev4" +__version__ = "0.40.0-dev5" From 6536bb60a7d2fed065fb89576d4cb7ad7b5cdab8 Mon Sep 17 00:00:00 2001 From: Joseph Lee <40768758+josephleekl@users.noreply.github.com> Date: Fri, 8 Nov 2024 18:32:28 -0500 Subject: [PATCH 07/53] (LK-C-1) Add controlled 1-qubit named-gate (e.g. NCPauliX) support to Lightning Kokkos (#952) ### Before submitting Please complete the following checklist when submitting a PR: - [ ] All new features must include a unit test. If you've fixed a bug or added code that should be tested, add a test to the [`tests`](../tests) directory! - [ ] All new functions and code must be clearly commented and documented. If you do make documentation changes, make sure that the docs build and render correctly by running `make docs`. - [ ] Ensure that the test suite passes, by running `make test`. - [ ] Add a new entry to the `.github/CHANGELOG.md` file, summarizing the change, and including a link back to the PR. - [ ] Ensure that code is properly formatted by running `make format`. When all the above are checked, delete everything above the dashed line and fill in the pull request template. ------------------------------------------------------------------------------------------------------------ **Context:** **Description of the Change:** This PR adds support for named controlled 1-qubit gates (e.g. PauliX / RX / PhaseShift / Hadamard / Rot / GlobalPhase). These are defined in `BasicGateFunctors.hpp`, and are applied through `applyNC1Functor` defined in the same file. **Benefits:** Performance benchmarks for gates are shown here: https://www.notion.so/xanaduai/Lightning-Kokkos-Native-Controlled-Operation-Gate-Benchmarks-12ebc6bd17648017a2dcd237748b24fe **Possible Drawbacks:** **Related GitHub Issues:** [sc-76773] --- .../lightning_kokkos/StateVectorKokkos.hpp | 83 ++- .../bindings/LKokkosBindings.hpp | 29 +- .../catalyst/LightningKokkosSimulator.cpp | 6 +- .../gates/BasicGateFunctors.hpp | 625 ++++++++++++++---- .../gates/BasicGeneratorFunctors.hpp | 11 +- .../gates/MatrixGateFunctors.hpp | 17 - .../tests/Test_StateVectorKokkos_NonParam.cpp | 334 +++++++++- .../tests/Test_StateVectorKokkos_Param.cpp | 414 +++++++++++- .../tests/Test_StateVectorLKokkos.cpp | 13 +- .../lightning_kokkos/utils/UtilKokkos.hpp | 124 +++- pennylane_lightning/core/src/utils/Util.hpp | 2 +- .../lightning_kokkos/_state_vector.py | 41 +- .../lightning_kokkos/lightning_kokkos.py | 12 + .../lightning_kokkos/lightning_kokkos.toml | 26 +- tests/test_gates.py | 6 +- 15 files changed, 1469 insertions(+), 274 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp index de47777ed2..0f99da5b47 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp @@ -389,32 +389,6 @@ class StateVectorKokkos final }); } - /** - * @brief Apply a single gate to the state vector. - * - * @param opName Name of gate to apply. - * @param controlled_wires Control wires. - * @param controlled_values Control values (false or true). - * @param wires Wires to apply gate to. - * @param inverse Indicates whether to use adjoint of gate. - * @param params Optional parameter list for parametric gates. - * @param gate_matrix Optional std gate matrix if opName doesn't exist. - */ - void applyOperation(const std::string &opName, - const std::vector &controlled_wires, - const std::vector &controlled_values, - const std::vector &wires, - bool inverse = false, - const std::vector ¶ms = {}, - const std::vector &gate_matrix = {}) { - PL_ABORT_IF_NOT(controlled_wires.empty(), - "Controlled kernels not implemented."); - PL_ABORT_IF_NOT(controlled_wires.size() == controlled_values.size(), - "`controlled_wires` must have the same size as " - "`controlled_values`."); - applyOperation(opName, wires, inverse, params, gate_matrix); - } - /** * @brief Apply a multi qubit operator to the state vector using a matrix * @@ -474,6 +448,46 @@ class StateVectorKokkos final } } + /** + * @brief Apply a controlled-single gate to the state vector. + * + * @param opName Name of gate to apply. + * @param controlled_wires Control wires. + * @param controlled_values Control values (false or true). + * @param wires Wires to apply gate to. + * @param inverse Indicates whether to use adjoint of gate. Default to false + * @param params Optional parameter list for parametric gates. + * @param gate_matrix Optional unitary gate matrix if opName doesn't exist. + */ + void applyOperation(const std::string &opName, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + bool inverse = false, + const std::vector ¶ms = {}, + const std::vector &gate_matrix = {}) { + PL_ABORT_IF_NOT( + areVecsDisjoint(controlled_wires, wires), + "`controlled_wires` and target wires must be disjoint."); + PL_ABORT_IF_NOT(controlled_wires.size() == controlled_values.size(), + "`controlled_wires` must have the same size as " + "`controlled_values`."); + PL_ABORT_IF_NOT( + array_contains(controlled_gate_names, std::string_view{opName}), + "Controlled matrix operation not yet supported."); + + if (controlled_wires.empty()) { + return applyOperation(opName, wires, inverse, params, gate_matrix); + } + + const std::size_t num_qubits = this->getNumQubits(); + const ControlledGateOperation gateop = + reverse_lookup(controlled_gate_names, std::string_view{opName}); + applyNCNamedOperation( + gateop, *data_, num_qubits, controlled_wires, controlled_values, + wires, inverse, params); + } + /** * @brief Apply a given matrix directly to the statevector using a * raw matrix pointer vector. @@ -491,23 +505,6 @@ class StateVectorKokkos final applyMultiQubitOp(matrix_, wires, inverse); } - /** - * @brief Apply a given matrix directly to the statevector. - * - * @param matrix Matrix data (in row-major format). - * @param wires Wires to apply gate to. - * @param inverse Indicate whether inverse should be taken. - */ - inline void applyMatrix(std::vector &matrix, - const std::vector &wires, - bool inverse = false) { - PL_ABORT_IF(wires.empty(), "Number of wires must be larger than 0"); - PL_ABORT_IF(matrix.size() != exp2(2 * wires.size()), - "The size of matrix does not match with the given " - "number of wires"); - applyMatrix(matrix.data(), wires, inverse); - } - /** * @brief Apply a given matrix directly to the statevector using a * raw matrix pointer vector. diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/bindings/LKokkosBindings.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/bindings/LKokkosBindings.hpp index 3ff65add44..a77df459c6 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/bindings/LKokkosBindings.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/bindings/LKokkosBindings.hpp @@ -48,6 +48,33 @@ using StateVectorBackends = Pennylane::Util::TypeList, StateVectorKokkos, void>; +template +void registerControlledGate(PyClass &pyclass) { + using ParamT = typename StateVectorT::PrecisionT; + + using Pennylane::Gates::ControlledGateOperation; + using Pennylane::Util::for_each_enum; + namespace Constant = Pennylane::Gates::Constant; + + for_each_enum( + [&pyclass](ControlledGateOperation gate_op) { + using Pennylane::Util::lookup; + const auto gate_name = + std::string(lookup(Constant::controlled_gate_names, gate_op)); + const std::string doc = "Apply the " + gate_name + " gate."; + auto func = [gate_name = gate_name]( + StateVectorT &sv, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, bool inverse, + const std::vector ¶ms) { + sv.applyOperation(gate_name, controlled_wires, + controlled_values, wires, inverse, params); + }; + pyclass.def(gate_name.c_str(), func, doc.c_str()); + }); +} + /** * @brief Get a gate kernel map for a statevector. */ @@ -61,7 +88,7 @@ void registerBackendClassSpecificBindings(PyClass &pyclass) { py::array::c_style | py::array::forcecast>; registerGatesForStateVector(pyclass); - + registerControlledGate(pyclass); pyclass.def( "applyPauliRot", [](StateVectorT &sv, const std::vector &wires, diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/LightningKokkosSimulator.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/LightningKokkosSimulator.cpp index cfe3a9c5d0..04fab62ac5 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/LightningKokkosSimulator.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/LightningKokkosSimulator.cpp @@ -190,15 +190,11 @@ void LightningKokkosSimulator::MatrixOperation( const std::vector> &matrix, const std::vector &wires, bool inverse, const std::vector &controlled_wires, - const std::vector &controlled_values) { + [[maybe_unused]] const std::vector &controlled_values) { using UnmanagedComplexHostView = Kokkos::View *, Kokkos::HostSpace, Kokkos::MemoryTraits>; - // TODO: Remove when controlled wires API is supported - RT_FAIL_IF( - !controlled_wires.empty() || !controlled_values.empty(), - "LightningKokkos device does not support native quantum control."); RT_FAIL_IF(!isValidQubits(wires), "Given wires do not refer to qubits"); RT_FAIL_IF(!isValidQubits(controlled_wires), "Given controlled wires do not refer to qubits"); diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp index 3586147b43..4297f83b11 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp @@ -27,13 +27,64 @@ namespace { using namespace Pennylane::Util; using Kokkos::kokkos_swap; using Pennylane::Gates::GateOperation; +using Pennylane::LightningKokkos::Util::ControlBitPatterns; +using Pennylane::LightningKokkos::Util::generateBitPatterns; +using Pennylane::LightningKokkos::Util::parity_2_offset; +using Pennylane::LightningKokkos::Util::reverseWires; using Pennylane::LightningKokkos::Util::vector2view; } // namespace /// @endcond namespace Pennylane::LightningKokkos::Functors { -template class applyNC1Functor { +template +class applyNC1Functor {}; + +template +class applyNC1Functor { + using KokkosIntVector = Kokkos::View; + + Kokkos::View *> arr; + const FuncT core_function; + KokkosIntVector indices; + KokkosIntVector parity; + KokkosIntVector rev_wires; + KokkosIntVector rev_wire_shifts; + + public: + template + applyNC1Functor([[maybe_unused]] ExecutionSpace exec, + Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, FuncT core_function_) + : arr(arr_), core_function(core_function_) { + + std::tie(parity, rev_wires) = + reverseWires(num_qubits, wires, controlled_wires); + std::vector indices_ = + generateBitPatterns(wires, num_qubits); + ControlBitPatterns(indices_, num_qubits, controlled_wires, + controlled_values); + indices = vector2view(indices_); + Kokkos::parallel_for( + Kokkos::RangePolicy( + 0, exp2(num_qubits - controlled_wires.size() - wires.size())), + *this); + } + KOKKOS_FUNCTION void operator()(const std::size_t k) const { + const std::size_t offset = parity_2_offset(parity, k); + const std::size_t i0 = indices(0B00); + const std::size_t i1 = indices(0B01); + + core_function(arr, i0 + offset, i1 + offset); + } +}; + +template +class applyNC1Functor { + Kokkos::View *> arr; const FuncT core_function; const std::size_t rev_wire; @@ -64,69 +115,175 @@ template class applyNC1Functor { } }; +template +void applyNCPauliX( + Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] const bool inverse = false, + [[maybe_unused]] const std::vector ¶ms = {}) { + auto core_function = + KOKKOS_LAMBDA(Kokkos::View *> arr, + const std::size_t i0, const std::size_t i1) { + kokkos_swap(arr(i0), arr(i1)); + }; + if (controlled_wires.empty()) { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } +} + template void applyPauliX(Kokkos::View *> arr_, const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC1Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + applyNCPauliX(arr_, num_qubits, {}, {}, wires, + inverse); +} + +template +void applyNCPauliY( + Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] const bool inverse = false, + [[maybe_unused]] const std::vector ¶ms = {}) { + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0, - const std::size_t i1) { kokkos_swap(arr(i0), arr(i1)); }); + const std::size_t i0, const std::size_t i1) { + const auto v0 = arr(i0); + const auto v1 = arr(i1); + arr(i0) = Kokkos::complex{imag(v1), -real(v1)}; + arr(i1) = Kokkos::complex{-imag(v0), real(v0)}; + }; + if (controlled_wires.empty()) { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template void applyPauliY(Kokkos::View *> arr_, const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC1Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + applyNCPauliY(arr_, num_qubits, {}, {}, wires, + inverse); +} + +template +void applyNCPauliZ( + Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] const bool inverse = false, + [[maybe_unused]] const std::vector ¶ms = {}) { + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0, const std::size_t i1) { - const auto v0 = arr(i0); - const auto v1 = arr(i1); - arr(i0) = Kokkos::complex{imag(v1), -real(v1)}; - arr(i1) = Kokkos::complex{-imag(v0), real(v0)}; - }); + [[maybe_unused]] const auto i0_ = i0; + // Note: this is to avoid Clang complain [[maybe_unused]] + // attribute for lambda function arguments + arr(i1) *= -1.0; + }; + if (controlled_wires.empty()) { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template void applyPauliZ(Kokkos::View *> arr_, const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC1Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + applyNCPauliZ(arr_, num_qubits, {}, {}, wires, + inverse); +} + +template +void applyNCHadamard( + Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] const bool inverse = false, + [[maybe_unused]] const std::vector ¶ms = {}) { + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0, const std::size_t i1) { - [[maybe_unused]] const auto i0_ = i0; - arr(i1) *= -1.0; - }); + const Kokkos::complex v0 = arr(i0); + const Kokkos::complex v1 = arr(i1); + arr(i0) = M_SQRT1_2 * (v0 + v1); // M_SQRT1_2 * v0 + M_SQRT1_2 * v1 + arr(i1) = M_SQRT1_2 * (v0 - v1); // M_SQRT1_2 * v0 - M_SQRT1_2 * v1 + }; + if (controlled_wires.empty()) { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template void applyHadamard( Kokkos::View *> arr_, const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC1Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + applyNCHadamard(arr_, num_qubits, {}, {}, wires, + inverse); +} + +template +void applyNCS(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, const bool inverse = false, + [[maybe_unused]] const std::vector ¶ms = {}) { + const Kokkos::complex shift = + (inverse) ? Kokkos::complex{0.0, -1.0} + : Kokkos::complex{0.0, 1.0}; + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0, const std::size_t i1) { - [[maybe_unused]] const auto i0_ = i0; - const Kokkos::complex v0 = arr(i0); - const Kokkos::complex v1 = arr(i1); - arr(i0) = M_SQRT1_2 * v0 + - M_SQRT1_2 * v1; // NOLINT(readability-magic-numbers) - arr(i1) = M_SQRT1_2 * v0 + - -M_SQRT1_2 * v1; // NOLINT(readability-magic-numbers) - }); + [[maybe_unused]] const auto i0_ = i0; + arr(i1) *= shift; + }; + if (controlled_wires.empty()) { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -134,16 +291,36 @@ void applyS(Kokkos::View *> arr_, const std::size_t num_qubits, const std::vector &wires, const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { + applyNCS(arr_, num_qubits, {}, {}, wires, + inverse); +} + +template +void applyNCT(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, const bool inverse = false, + [[maybe_unused]] const std::vector ¶ms = {}) { const Kokkos::complex shift = - (inverse) ? Kokkos::complex{0.0, -1.0} - : Kokkos::complex{0.0, 1.0}; - applyNC1Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + (inverse) ? Kokkos::conj(Kokkos::exp(Kokkos::complex( + 0, static_cast(M_PI_4)))) + : Kokkos::exp(Kokkos::complex( + 0, static_cast(M_PI_4))); + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0, const std::size_t i1) { - [[maybe_unused]] const auto i0_ = i0; - arr(i1) *= shift; - }); + [[maybe_unused]] const auto i0_ = i0; + arr(i1) *= shift; + }; + if (controlled_wires.empty()) { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -151,18 +328,36 @@ void applyT(Kokkos::View *> arr_, const std::size_t num_qubits, const std::vector &wires, const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { + applyNCT(arr_, num_qubits, {}, {}, wires, + inverse); +} + +template +void applyNCPhaseShift(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + const PrecisionT &angle = params[0]; const Kokkos::complex shift = - (inverse) ? conj(exp(Kokkos::complex( - 0, static_cast(M_PI / 4)))) - : exp(Kokkos::complex( - 0, static_cast(M_PI / 4))); - applyNC1Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + (inverse) ? Kokkos::exp(-Kokkos::complex(0, angle)) + : Kokkos::exp(Kokkos::complex(0, angle)); + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0, const std::size_t i1) { - [[maybe_unused]] const auto i0_ = i0; - arr(i1) *= shift; - }); + [[maybe_unused]] const auto i0_ = i0; + arr(i1) *= shift; + }; + if (controlled_wires.empty()) { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -171,17 +366,41 @@ void applyPhaseShift(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { + applyNCPhaseShift(arr_, num_qubits, {}, {}, + wires, inverse, params); +} + +template +void applyNCRX(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; - const Kokkos::complex shift = - (inverse) ? exp(-Kokkos::complex(0, angle)) - : exp(Kokkos::complex(0, angle)); - applyNC1Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + const PrecisionT c = std::cos(angle * static_cast(0.5)); + const PrecisionT s = (inverse) + ? std::sin(angle * static_cast(0.5)) + : std::sin(-angle * static_cast(0.5)); + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0, const std::size_t i1) { - [[maybe_unused]] const auto i0_ = i0; - arr(i1) *= shift; - }); + const auto v0 = arr(i0); + const auto v1 = arr(i1); + arr(i0) = + c * v0 + Kokkos::complex{-imag(v1) * s, real(v1) * s}; + arr(i1) = + Kokkos::complex{-imag(v0) * s, real(v0) * s} + c * v1; + }; + if (controlled_wires.empty()) { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -189,21 +408,41 @@ void applyRX(Kokkos::View *> arr_, const std::size_t num_qubits, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { + applyNCRX(arr_, num_qubits, {}, {}, wires, + inverse, params); +} + +template +void applyNCRY(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; - const PrecisionT c = cos(angle * static_cast(0.5)); - const PrecisionT s = (inverse) ? sin(angle * static_cast(0.5)) - : sin(-angle * static_cast(0.5)); - applyNC1Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + const PrecisionT c = std::cos(angle * static_cast(0.5)); + const PrecisionT s = (inverse) + ? -std::sin(angle * static_cast(0.5)) + : std::sin(angle * static_cast(0.5)); + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0, const std::size_t i1) { - const auto v0 = arr(i0); - const auto v1 = arr(i1); - arr(i0) = c * v0 + - Kokkos::complex{-imag(v1) * s, real(v1) * s}; - arr(i1) = Kokkos::complex{-imag(v0) * s, real(v0) * s} + - c * v1; - }); + const auto v0 = arr(i0); + const auto v1 = arr(i1); + arr(i0) = Kokkos::complex{c * real(v0) - s * real(v1), + c * imag(v0) - s * imag(v1)}; + arr(i1) = Kokkos::complex{s * real(v0) + c * real(v1), + s * imag(v0) + c * imag(v1)}; + }; + if (controlled_wires.empty()) { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -211,21 +450,38 @@ void applyRY(Kokkos::View *> arr_, const std::size_t num_qubits, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { + applyNCRY(arr_, num_qubits, {}, {}, wires, + inverse, params); +} + +template +void applyNCRZ(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; - const PrecisionT c = cos(angle * static_cast(0.5)); - const PrecisionT s = (inverse) ? -sin(angle * static_cast(0.5)) - : sin(angle * static_cast(0.5)); - applyNC1Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + const PrecisionT cos_angle = std::cos(angle * static_cast(0.5)); + const PrecisionT sin_angle = std::sin(angle * static_cast(0.5)); + const Kokkos::complex shift_0{ + cos_angle, (inverse) ? sin_angle : -sin_angle}; + const Kokkos::complex shift_1 = Kokkos::conj(shift_0); + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0, const std::size_t i1) { - const auto v0 = arr(i0); - const auto v1 = arr(i1); - arr(i0) = Kokkos::complex{c * real(v0) - s * real(v1), - c * imag(v0) - s * imag(v1)}; - arr(i1) = Kokkos::complex{s * real(v0) + c * real(v1), - s * imag(v0) + c * imag(v1)}; - }); + arr(i0) *= shift_0; + arr(i1) *= shift_1; + }; + if (controlled_wires.empty()) { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -233,26 +489,18 @@ void applyRZ(Kokkos::View *> arr_, const std::size_t num_qubits, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; - const PrecisionT cos_angle = cos(angle * static_cast(0.5)); - const PrecisionT sin_angle = sin(angle * static_cast(0.5)); - const Kokkos::complex shift_0{ - cos_angle, (inverse) ? sin_angle : -sin_angle}; - const Kokkos::complex shift_1 = Kokkos::conj(shift_0); - applyNC1Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0, const std::size_t i1) { - arr(i0) *= shift_0; - arr(i1) *= shift_1; - }); + applyNCRZ(arr_, num_qubits, {}, {}, wires, + inverse, params); } template -void applyRot(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, const bool inverse = false, - const std::vector ¶ms = {}) { +void applyNCRot(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { const PrecisionT phi = (inverse) ? -params[2] : params[0]; const PrecisionT theta = (inverse) ? -params[1] : params[1]; const PrecisionT omega = (inverse) ? -params[0] : params[2]; @@ -262,15 +510,77 @@ void applyRot(Kokkos::View *> arr_, const Kokkos::complex mat_0b01 = mat[0b01]; const Kokkos::complex mat_0b10 = mat[0b10]; const Kokkos::complex mat_0b11 = mat[0b11]; - applyNC1Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0, const std::size_t i1) { - const Kokkos::complex v0 = arr(i0); - const Kokkos::complex v1 = arr(i1); - arr(i0) = mat_0b00 * v0 + mat_0b01 * v1; - arr(i1) = mat_0b10 * v0 + mat_0b11 * v1; - }); + const Kokkos::complex v0 = arr(i0); + const Kokkos::complex v1 = arr(i1); + arr(i0) = mat_0b00 * v0 + mat_0b01 * v1; + arr(i1) = mat_0b10 * v0 + mat_0b11 * v1; + }; + if (controlled_wires.empty()) { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } +} + +template +void applyRot(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &wires, const bool inverse = false, + const std::vector ¶ms = {}) { + applyNCRot(arr_, num_qubits, {}, {}, wires, + inverse, params); +} + +template +void applyNCGlobalPhase(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + [[maybe_unused]] const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + const Kokkos::complex phase = Kokkos::exp( + Kokkos::complex{0, (inverse) ? params[0] : -params[0]}); + auto core_function = + KOKKOS_LAMBDA(Kokkos::View *> arr, + const std::size_t i0, const std::size_t i1) { + arr(i1) *= phase; + arr(i0) *= phase; + }; + std::size_t target{0U}; + if (!controlled_wires.empty()) { + for (std::size_t i = 0; i < num_qubits; i++) { + if (std::find(controlled_wires.begin(), controlled_wires.end(), + i) == controlled_wires.end()) { + target = i; + break; + } + } + } + if (controlled_wires.empty()) { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, {target}, core_function); + } else { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, {target}, core_function); + } +} + +template +void applyGlobalPhase(Kokkos::View *> arr_, + const std::size_t num_qubits, + [[maybe_unused]] const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + applyNCGlobalPhase(arr_, num_qubits, {}, {}, + wires, inverse, params); } template class applyNC2Functor { @@ -399,8 +709,8 @@ void applyControlledPhaseShift(Kokkos::View *> arr_, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const Kokkos::complex s = - (inverse) ? exp(-Kokkos::complex(0, angle)) - : exp(Kokkos::complex(0, angle)); + (inverse) ? Kokkos::exp(-Kokkos::complex(0, angle)) + : Kokkos::exp(Kokkos::complex(0, angle)); applyNC2Functor( ExecutionSpace{}, arr_, num_qubits, wires, KOKKOS_LAMBDA(Kokkos::View *> arr, @@ -466,8 +776,8 @@ void applyCRZ(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; - const PrecisionT cos_angle = cos(angle * static_cast(0.5)); - const PrecisionT sin_angle = sin(angle * static_cast(0.5)); + const PrecisionT cos_angle = std::cos(angle * static_cast(0.5)); + const PrecisionT sin_angle = std::sin(angle * static_cast(0.5)); const Kokkos::complex shift_0{ cos_angle, (inverse) ? sin_angle : -sin_angle}; const Kokkos::complex shift_1 = Kokkos::conj(shift_0); @@ -621,7 +931,7 @@ void applyIsingZZ(Kokkos::View *> arr_, const Kokkos::complex shift_0 = Kokkos::complex{ std::cos(angle / 2), (inverse) ? std::sin(angle / 2) : -std::sin(angle / 2)}; - const Kokkos::complex shift_1 = conj(shift_0); + const Kokkos::complex shift_1 = Kokkos::conj(shift_0); applyNC2Functor( ExecutionSpace{}, arr_, num_qubits, wires, KOKKOS_LAMBDA(Kokkos::View *> arr, @@ -669,8 +979,8 @@ void applySingleExcitationMinus( const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); const Kokkos::complex e = - (inverse) ? exp(Kokkos::complex(0, angle / 2)) - : exp(Kokkos::complex(0, -angle / 2)); + (inverse) ? Kokkos::exp(Kokkos::complex(0, angle / 2)) + : Kokkos::exp(Kokkos::complex(0, -angle / 2)); applyNC2Functor( ExecutionSpace{}, arr_, num_qubits, wires, KOKKOS_LAMBDA(Kokkos::View *> arr, @@ -696,8 +1006,8 @@ void applySingleExcitationPlus(Kokkos::View *> arr_, const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); const Kokkos::complex e = - (inverse) ? exp(Kokkos::complex(0, -angle / 2)) - : exp(Kokkos::complex(0, angle / 2)); + (inverse) ? Kokkos::exp(Kokkos::complex(0, -angle / 2)) + : Kokkos::exp(Kokkos::complex(0, angle / 2)); applyNC2Functor( ExecutionSpace{}, arr_, num_qubits, wires, KOKKOS_LAMBDA(Kokkos::View *> arr, @@ -992,8 +1302,8 @@ void applyDoubleExcitationMinus( const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = inverse ? -std::sin(angle / 2) : std::sin(angle / 2); const Kokkos::complex e = - inverse ? exp(Kokkos::complex(0, angle / 2)) - : exp(Kokkos::complex(0, -angle / 2)); + inverse ? Kokkos::exp(Kokkos::complex(0, angle / 2)) + : Kokkos::exp(Kokkos::complex(0, -angle / 2)); applyNC4Functor( ExecutionSpace{}, arr_, num_qubits, wires, KOKKOS_LAMBDA(Kokkos::View *> arr, @@ -1036,8 +1346,8 @@ void applyDoubleExcitationPlus(Kokkos::View *> arr_, const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = inverse ? -std::sin(angle / 2) : std::sin(angle / 2); const Kokkos::complex e = - inverse ? exp(Kokkos::complex(0, -angle / 2)) - : exp(Kokkos::complex(0, angle / 2)); + inverse ? Kokkos::exp(Kokkos::complex(0, -angle / 2)) + : Kokkos::exp(Kokkos::complex(0, angle / 2)); applyNC4Functor( ExecutionSpace{}, arr_, num_qubits, wires, KOKKOS_LAMBDA(Kokkos::View *> arr, @@ -1080,7 +1390,7 @@ void applyMultiRZ(Kokkos::View *> arr_, const Kokkos::complex shift_0 = Kokkos::complex{ std::cos(angle / 2), (inverse) ? std::sin(angle / 2) : -std::sin(angle / 2)}; - const Kokkos::complex shift_1 = conj(shift_0); + const Kokkos::complex shift_1 = Kokkos::conj(shift_0); std::size_t wires_parity = 0U; for (std::size_t wire : wires) { wires_parity |= @@ -1148,19 +1458,6 @@ void applyPauliRot(Kokkos::View *> arr_, }); } -template -void applyGlobalPhase(Kokkos::View *> arr_, - const std::size_t num_qubits, - [[maybe_unused]] const std::vector &wires, - const bool inverse = false, - const std::vector ¶ms = {}) { - const Kokkos::complex phase = Kokkos::exp( - Kokkos::complex{0, (inverse) ? params[0] : -params[0]}); - Kokkos::parallel_for( - Kokkos::RangePolicy(0, exp2(num_qubits)), - KOKKOS_LAMBDA(const std::size_t k) { arr_(k) *= phase; }); -} - template void applyNamedOperation(const GateOperation gateop, Kokkos::View *> arr_, @@ -1285,4 +1582,72 @@ void applyNamedOperation(const GateOperation gateop, } } +template +void applyNCNamedOperation(const ControlledGateOperation gateop, + Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + switch (gateop) { + case ControlledGateOperation::PauliX: + applyNCPauliX(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, + params); + return; + case ControlledGateOperation::PauliY: + applyNCPauliY(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, + params); + return; + case ControlledGateOperation::PauliZ: + applyNCPauliZ(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, + params); + return; + case ControlledGateOperation::Hadamard: + applyNCHadamard(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, + params); + return; + case ControlledGateOperation::S: + applyNCS(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, params); + return; + case ControlledGateOperation::T: + applyNCT(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, params); + return; + case ControlledGateOperation::PhaseShift: + applyNCPhaseShift(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, + params); + return; + case ControlledGateOperation::RX: + applyNCRX(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, params); + return; + case ControlledGateOperation::RY: + applyNCRY(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, params); + return; + case ControlledGateOperation::RZ: + applyNCRZ(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, params); + return; + case ControlledGateOperation::Rot: + applyNCRot(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, params); + return; + case ControlledGateOperation::GlobalPhase: + applyNCGlobalPhase(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, + params); + return; + default: + PL_ABORT("Controlled gate operation does not exist."); + } +} } // namespace Pennylane::LightningKokkos::Functors diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp index af461e5b88..e35b75b698 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp @@ -36,13 +36,14 @@ void applyGenPhaseShift( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC1Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0, const std::size_t i1) { - [[maybe_unused]] auto i1_ = i1; - arr(i0) = 0.0; - }); + [[maybe_unused]] auto i1_ = i1; + arr(i0) = 0.0; + }; + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/MatrixGateFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/MatrixGateFunctors.hpp index 60e4259386..fb3e70cbcf 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/MatrixGateFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/MatrixGateFunctors.hpp @@ -111,8 +111,6 @@ template struct apply1QubitOpFunctor { KokkosComplexVector arr; KokkosComplexVector matrix; - const std::size_t n_wires = 1; - const std::size_t dim = one << n_wires; std::size_t num_qubits; std::size_t rev_wire; std::size_t rev_wire_shift; @@ -144,7 +142,6 @@ template struct apply1QubitOpFunctor { arr(i1) = matrix(0B10) * v0 + matrix(0B11) * v1; } }; - template struct apply2QubitOpFunctor { using ComplexT = Kokkos::complex; using KokkosComplexVector = Kokkos::View; @@ -152,8 +149,6 @@ template struct apply2QubitOpFunctor { KokkosComplexVector arr; KokkosComplexVector matrix; - const std::size_t n_wires = 2; - const std::size_t dim = one << n_wires; std::size_t num_qubits; std::size_t rev_wire0; std::size_t rev_wire1; @@ -223,17 +218,13 @@ template struct apply3QubitOpFunctor { KokkosComplexVector arr; KokkosComplexVector matrix; - KokkosIntVector wires; KokkosIntVector parity; KokkosIntVector rev_wire_shifts; - const std::size_t n_wires = 3; - const std::size_t dim = one << n_wires; std::size_t num_qubits; apply3QubitOpFunctor(KokkosComplexVector arr_, std::size_t num_qubits_, const KokkosComplexVector &matrix_, const std::vector &wires_) { - wires = vector2view(wires_); arr = arr_; matrix = matrix_; num_qubits = num_qubits_; @@ -293,17 +284,13 @@ template struct apply4QubitOpFunctor { KokkosComplexVector arr; KokkosComplexVector matrix; - KokkosIntVector wires; KokkosIntVector parity; KokkosIntVector rev_wire_shifts; - const std::size_t n_wires = 4; - const std::size_t dim = one << n_wires; std::size_t num_qubits; apply4QubitOpFunctor(KokkosComplexVector arr_, std::size_t num_qubits_, const KokkosComplexVector &matrix_, const std::vector &wires_) { - wires = vector2view(wires_); arr = arr_; matrix = matrix_; num_qubits = num_qubits_; @@ -399,17 +386,13 @@ template struct apply5QubitOpFunctor { KokkosComplexVector arr; KokkosComplexVector matrix; - KokkosIntVector wires; KokkosIntVector parity; KokkosIntVector rev_wire_shifts; - const std::size_t n_wires = 5; - const std::size_t dim = one << n_wires; std::size_t num_qubits; apply5QubitOpFunctor(KokkosComplexVector arr_, std::size_t num_qubits_, const KokkosComplexVector &matrix_, const std::vector &wires_) { - wires = vector2view(wires_); arr = arr_; matrix = matrix_; num_qubits = num_qubits_; diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp index 93c9c44239..303e60720b 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp @@ -80,6 +80,10 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation", PL_REQUIRE_THROWS_MATCHES( state_vector.applyOperation("XXX", {0}), LightningException, "Operation does not exist for XXX and no matrix provided."); + PL_REQUIRE_THROWS_MATCHES( + state_vector.applyOperation("XXX", {0}, {true}, {1}), + LightningException, + "Operation does not exist for XXX and no matrix provided."); } } @@ -661,6 +665,45 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyToffoli", } } +TEMPLATE_TEST_CASE("StateVectorKokkos::applyCSWAP", + "[StateVectorKokkos_Nonparam]", float, double) { + { + using ComplexT = StateVectorKokkos::ComplexT; + const std::size_t num_qubits = 3; + + StateVectorKokkos kokkos_sv{num_qubits}; + + kokkos_sv.applyOperations({{"Hadamard"}, {"PauliX"}}, {{0}, {1}}, + {{false}, {false}}); + + auto ini_sv = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, + kokkos_sv.getView()); + + auto z = ComplexT{ZERO()}; + auto i = ComplexT{INVSQRT2()}; + + SECTION("Apply using dispatcher") { + SECTION("CSWAP [0,1,2]|+10> -> |010> + |101>") { + const std::vector expected_results = {z, z, i, z, + z, i, z, z}; + + StateVectorKokkos svdat012{num_qubits}; + Kokkos::deep_copy(svdat012.getView(), ini_sv); + + svdat012.applyOperation("CSWAP", {0, 1, 2}, false); + + auto sv012 = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, svdat012.getView()); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(expected_results[j]) == Approx(imag(sv012[j]))); + CHECK(real(expected_results[j]) == Approx(real(sv012[j]))); + } + } + } + } +} + TEMPLATE_TEST_CASE("StateVectorKokkos::applyMultiQubitOp", "[StateVectorKokkos_Nonparam][Inverse]", float, double) { const bool inverse = GENERATE(true, false); @@ -758,45 +801,288 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyMultiQubitOp", } } -TEMPLATE_TEST_CASE("StateVectorKokkos::applyCSWAP", +TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation Controlled", "[StateVectorKokkos_Nonparam]", float, double) { - { - using ComplexT = StateVectorKokkos::ComplexT; - const std::size_t num_qubits = 3; - StateVectorKokkos kokkos_sv{num_qubits}; + using StateVectorT = StateVectorKokkos; + using PrecisionT = StateVectorT::PrecisionT; + const std::size_t num_qubits = 3; + StateVectorKokkos state_vector{num_qubits}; + auto matrix = getIdentity(); + PL_REQUIRE_THROWS_MATCHES( + state_vector.applyOperation("XXX", {0}, {true}, {1}, false, {}, matrix), + LightningException, "Controlled matrix operation not yet supported"); +} - kokkos_sv.applyOperations({{"Hadamard"}, {"PauliX"}}, {{0}, {1}}, - {{false}, {false}}); +TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " + "one-qubit with controls", + "[StateVectorKokkos_NonParam]", float, double) { + const bool inverse = GENERATE(true, false); + const std::size_t num_qubits = 4; + const std::size_t control = GENERATE(0, 1, 2, 3); + const std::size_t wire = GENERATE(0, 1, 2, 3); + StateVectorKokkos kokkos_sv{num_qubits}; - auto ini_sv = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, - kokkos_sv.getView()); + kokkos_sv.applyOperations( + {{"Hadamard"}, {"Hadamard"}, {"Hadamard"}, {"Hadamard"}}, + {{0}, {1}, {2}, {3}}, {{false}, {false}, {false}, {false}}); - auto z = ComplexT{ZERO()}; - auto i = ComplexT{INVSQRT2()}; + auto ini_sv = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, + kokkos_sv.getView()); + StateVectorKokkos sv_gate{num_qubits}; + StateVectorKokkos sv_control{num_qubits}; - SECTION("Apply using dispatcher") { - SECTION("CSWAP [0,1,2]|+10> -> |010> + |101>") { - const std::vector expected_results = {z, z, i, z, - z, i, z, z}; + SECTION("N-controlled PauliX ") { - StateVectorKokkos svdat012{num_qubits}; - Kokkos::deep_copy(svdat012.getView(), ini_sv); + if (control == wire) { + Kokkos::deep_copy(sv_control.getView(), ini_sv); - svdat012.applyOperation("CSWAP", {0, 1, 2}, false); + REQUIRE_THROWS_AS(sv_control.applyOperation( + "PauliX", std::vector{control}, + std::vector{true}, + std::vector{wire}), + LightningException); + } - auto sv012 = Kokkos::create_mirror_view_and_copy( - Kokkos::HostSpace{}, svdat012.getView()); + if (control != wire) { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + Kokkos::deep_copy(sv_control.getView(), ini_sv); + + sv_gate.applyOperation("CNOT", {control, wire}, inverse); + sv_control.applyOperation( + "PauliX", std::vector{control}, + std::vector{true}, std::vector{wire}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + auto sv_control_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_control.getView()); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == + Approx(imag(sv_control_host[j]))); + CHECK(real(sv_gate_host[j]) == + Approx(real(sv_control_host[j]))); + } + } - for (std::size_t j = 0; j < exp2(num_qubits); j++) { - CHECK(imag(expected_results[j]) == Approx(imag(sv012[j]))); - CHECK(real(expected_results[j]) == Approx(real(sv012[j]))); - } + if (control != 0 && wire != 0 && control != wire) { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + Kokkos::deep_copy(sv_control.getView(), ini_sv); + + sv_gate.applyOperation("Toffoli", {0, control, wire}, inverse); + sv_control.applyOperation( + "PauliX", std::vector{0, control}, + std::vector{true, true}, std::vector{wire}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + auto sv_control_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_control.getView()); + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == + Approx(imag(sv_control_host[j]))); + CHECK(real(sv_gate_host[j]) == + Approx(real(sv_control_host[j]))); + } + + sv_gate.applyOperation("Toffoli", {control, 0, wire}); + sv_control.applyOperation( + "PauliX", std::vector{control, 0}, + std::vector{true, true}, std::vector{wire}); + Kokkos::deep_copy(sv_gate_host, sv_gate.getView()); + Kokkos::deep_copy(sv_control_host, sv_control.getView()); + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == + Approx(imag(sv_control_host[j]))); + CHECK(real(sv_gate_host[j]) == + Approx(real(sv_control_host[j]))); + } + } + } + + SECTION("N-controlled PauliY ") { + if (control != wire) { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + Kokkos::deep_copy(sv_control.getView(), ini_sv); + + sv_gate.applyOperation("CY", {control, wire}, inverse); + sv_control.applyOperation( + "PauliY", std::vector{control}, + std::vector{true}, std::vector{wire}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + auto sv_control_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_control.getView()); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == + Approx(imag(sv_control_host[j]))); + CHECK(real(sv_gate_host[j]) == + Approx(real(sv_control_host[j]))); + } + } + } + + SECTION("N-controlled PauliZ ") { + if (control != wire) { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + Kokkos::deep_copy(sv_control.getView(), ini_sv); + + sv_gate.applyOperation("CZ", {control, wire}, inverse); + sv_control.applyOperation( + "PauliZ", std::vector{control}, + std::vector{true}, std::vector{wire}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + auto sv_control_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_control.getView()); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == + Approx(imag(sv_control_host[j]))); + CHECK(real(sv_gate_host[j]) == + Approx(real(sv_control_host[j]))); } } } } +TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " + "one-qubit with multiple-controls", + "[StateVectorKokkos_NonParam]", float, double) { + using ComplexT = StateVectorKokkos::ComplexT; + const bool inverse = false; + const std::size_t num_qubits = 3; + StateVectorKokkos kokkos_sv{num_qubits}; + + kokkos_sv.applyOperations({{"Hadamard"}, {"Hadamard"}, {"Hadamard"}}, + {{0}, {1}, {2}}, {{false}, {false}, {false}}); + + auto ini_sv = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, + kokkos_sv.getView()); + StateVectorKokkos sv_gate{num_qubits}; + StateVectorKokkos expected_result{num_qubits}; + + SECTION("2-controlled PauliY") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1}; + sv_gate.applyOperation("PauliY", control_wires, control_values, + target_wire, inverse); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{ + // Generated using Pennylane + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.0, -0.35355339}, ComplexT{0.35355339, 0.0}, + ComplexT{0.0, 0.35355339}, ComplexT{0.35355339, 0.0}, + }; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled PauliZ") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1}; + sv_gate.applyOperation("PauliZ", control_wires, control_values, + target_wire, inverse); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{ + // Generated using Pennylane + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{-0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + }; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled Hadamard") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1}; + sv_gate.applyOperation("Hadamard", control_wires, control_values, + target_wire, inverse); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{ + // Generated using Pennylane + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.5, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.0, 0.0}, ComplexT{0.35355339, 0.0}, + }; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled S") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1}; + sv_gate.applyOperation("S", control_wires, control_values, target_wire, + inverse); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{ + // Generated using Pennylane + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.0, 0.35355339}, ComplexT{0.35355339, 0.0}, + }; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled T") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1}; + sv_gate.applyOperation("T", control_wires, control_values, target_wire, + inverse); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{ + // Generated using Pennylane + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.25, 0.25}, ComplexT{0.35355339, 0.0}, + }; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } +} + TEMPLATE_TEST_CASE("StateVectorKokkos::SetStateVector", "[StateVectorKokkos_Nonparam]", float, double) { using PrecisionT = TestType; diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp index dda5e51cdc..a48a40db7d 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp @@ -116,6 +116,380 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyMatrix/Operation", } } +TEMPLATE_TEST_CASE( + "StateVectorKokkos::applyOperation param one-qubit with controls", + "[StateVectorKokkos_Operation]", float, double) { + using StateVectorT = StateVectorKokkos; + using PrecisionT = StateVectorT::PrecisionT; + + const std::size_t num_qubits = 4; + const TestType EP = 1e-4; + const TestType param = 0.12342; + auto ini_st = createNonTrivialState(num_qubits); + + const bool inverse = GENERATE(false, true); + const std::size_t control = GENERATE(0, 1, 2, 3); + const std::size_t wire = GENERATE(0, 1, 2, 3); + SECTION("N-controlled RX") { + if (control != wire) { + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_cop{ini_st.data(), ini_st.size()}; + + kokkos_sv_cop.applyOperation("CRX", {control, wire}, inverse, + std::vector{param}); + kokkos_sv_op.applyOperation("RX", std::vector{control}, + std::vector{true}, + std::vector{wire}, inverse, + std::vector{param}); + + auto result_op = kokkos_sv_op.getDataVector(); + auto result_cop = kokkos_sv_cop.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_cop[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_cop[j])).margin(EP)); + } + } + } + + SECTION("N-controlled RY") { + if (control != wire) { + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_cop{ini_st.data(), ini_st.size()}; + + kokkos_sv_cop.applyOperation("CRY", {control, wire}, inverse, + std::vector{param}); + kokkos_sv_op.applyOperation("RY", std::vector{control}, + std::vector{true}, + std::vector{wire}, inverse, + std::vector{param}); + + auto result_op = kokkos_sv_op.getDataVector(); + auto result_cop = kokkos_sv_cop.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_cop[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_cop[j])).margin(EP)); + } + } + } + + SECTION("N-controlled RZ") { + if (control != wire) { + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_cop{ini_st.data(), ini_st.size()}; + + kokkos_sv_cop.applyOperation("CRZ", {control, wire}, inverse, + std::vector{param}); + kokkos_sv_op.applyOperation("RZ", std::vector{control}, + std::vector{true}, + std::vector{wire}, inverse, + std::vector{param}); + + auto result_op = kokkos_sv_op.getDataVector(); + auto result_cop = kokkos_sv_cop.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_cop[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_cop[j])).margin(EP)); + } + } + } + + SECTION("N-controlled Rot") { + if (control != wire) { + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_cop{ini_st.data(), ini_st.size()}; + + kokkos_sv_cop.applyOperation( + "CRot", {control, wire}, inverse, + std::vector{param, param, param}); + kokkos_sv_op.applyOperation( + "Rot", std::vector{control}, + std::vector{true}, std::vector{wire}, + inverse, std::vector{param, param, param}); + + auto result_op = kokkos_sv_op.getDataVector(); + auto result_cop = kokkos_sv_cop.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_cop[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_cop[j])).margin(EP)); + } + } + } + + SECTION("N-controlled PhaseShift") { + if (control != wire) { + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_cop{ini_st.data(), ini_st.size()}; + + kokkos_sv_cop.applyOperation("ControlledPhaseShift", + {control, wire}, inverse, + std::vector{param}); + kokkos_sv_op.applyOperation( + "PhaseShift", std::vector{control}, + std::vector{true}, std::vector{wire}, + inverse, std::vector{param}); + + auto result_op = kokkos_sv_op.getDataVector(); + auto result_cop = kokkos_sv_cop.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_cop[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_cop[j])).margin(EP)); + } + } + } + + SECTION("N-controlled S") { + if (control != wire) { + const TestType pi2 = TestType(M_PI_2); + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_cop{ini_st.data(), ini_st.size()}; + + kokkos_sv_cop.applyOperation("ControlledPhaseShift", + {control, wire}, inverse, + std::vector{pi2}); + kokkos_sv_op.applyOperation( + "S", std::vector{control}, std::vector{true}, + std::vector{wire}, inverse); + + auto result_op = kokkos_sv_op.getDataVector(); + auto result_cop = kokkos_sv_cop.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_cop[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_cop[j])).margin(EP)); + } + } + } + + SECTION("N-controlled T") { + if (control != wire) { + const TestType pi4 = TestType(M_PI_4); + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_cop{ini_st.data(), ini_st.size()}; + + kokkos_sv_cop.applyOperation("ControlledPhaseShift", + {control, wire}, inverse, + std::vector{pi4}); + kokkos_sv_op.applyOperation( + "T", std::vector{control}, std::vector{true}, + std::vector{wire}, inverse); + + auto result_op = kokkos_sv_op.getDataVector(); + auto result_cop = kokkos_sv_cop.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_cop[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_cop[j])).margin(EP)); + } + } + } + + SECTION("N-controlled Hadamard") { + if (control != wire) { + const TestType pi2 = TestType(M_PI_2); + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_cop{ini_st.data(), ini_st.size()}; + + kokkos_sv_cop.applyOperation("CRY", {control, wire}, false, + std::vector{pi2}); + kokkos_sv_cop.applyOperation("CNOT", {control, wire}, false); + kokkos_sv_op.applyOperation( + "Hadamard", std::vector{control}, + std::vector{true}, std::vector{wire}, false); + + auto result_op = kokkos_sv_op.getDataVector(); + auto result_cop = kokkos_sv_cop.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_cop[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_cop[j])).margin(EP)); + } + } + } +} + +TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation param " + "one-qubit with multiple-controls", + "[StateVectorKokkos_Param]", float, double) { + using ComplexT = StateVectorKokkos::ComplexT; + const bool inverse = false; + const std::size_t num_qubits = 3; + StateVectorKokkos kokkos_sv{num_qubits}; + + kokkos_sv.applyOperations({{"Hadamard"}, {"Hadamard"}, {"Hadamard"}}, + {{0}, {1}, {2}}, {{false}, {false}, {false}}); + + auto ini_sv = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, + kokkos_sv.getView()); + StateVectorKokkos sv_gate{num_qubits}; + StateVectorKokkos expected_result{num_qubits}; + + SECTION("2-controlled PhaseShift") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1}; + const TestType param = 0.234; + sv_gate.applyOperation("PhaseShift", control_wires, control_values, + target_wire, inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{ + // Generated using Pennylane + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.34391789, 0.08197855}, ComplexT{0.35355339, 0.0}, + }; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled RX") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1}; + const TestType param = 0.234; + sv_gate.applyOperation("RX", control_wires, control_values, target_wire, + inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{ + // Generated using Pennylane + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35113625, -0.04127144}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35113625, -0.04127144}, ComplexT{0.35355339, 0.0}, + }; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled RY") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1}; + const TestType param = 0.234; + sv_gate.applyOperation("RY", control_wires, control_values, target_wire, + inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{ + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.30986482, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.39240769, 0.0}, ComplexT{0.35355339, 0.0}, + }; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled RZ") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1}; + const TestType param = 0.234; + sv_gate.applyOperation("RZ", control_wires, control_values, target_wire, + inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{ + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35113625, -0.04127144}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35113625, 0.04127144}, ComplexT{0.35355339, 0.0}}; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled Rot") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1}; + const TestType param = 0.234; + sv_gate.applyOperation("Rot", control_wires, control_values, + target_wire, inverse, {param, param, param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{ + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.30029525, -0.08141809}, ComplexT{0.35355339, 0.0}, + ComplexT{0.38283807, 0.08141809}, ComplexT{0.35355339, 0.0}, + }; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled GlobalPhase") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1}; + const TestType param = 0.234; + sv_gate.applyOperation("GlobalPhase", control_wires, control_values, + target_wire, inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{ + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, + ComplexT{0.34391789, -0.08197855}, ComplexT{0.35355339, 0.0}, + ComplexT{0.34391789, -0.08197855}, ComplexT{0.35355339, 0.0}, + }; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } +} + TEMPLATE_TEST_CASE("StateVectorKokkosManaged::applyIsingXY", "[StateVectorKokkosManaged_Param]", float, double) { { @@ -410,16 +784,34 @@ TEMPLATE_TEST_CASE("StateVectorKokkosManaged::applyControlledGlobalPhase", {0.0, 1.}, {1.0, 0.}, {1.0, 0.}, {1.0, 0.}, {1.0, 0.}}; - auto sv_data = createRandomStateVectorData(re, num_qubits); - StateVectorKokkos kokkos_sv( - reinterpret_cast(sv_data.data()), sv_data.size()); - kokkos_sv.applyOperation("C(GlobalPhase)", {index}, inverse, {}, phase); - auto result_sv = kokkos_sv.getDataVector(); - for (std::size_t j = 0; j < exp2(num_qubits); j++) { - ComplexT tmp = (inverse) ? conj(phase[j]) : phase[j]; - tmp *= ComplexT(sv_data[j]); - CHECK((real(result_sv[j])) == Approx(real(tmp))); - CHECK((imag(result_sv[j])) == Approx(imag(tmp))); + SECTION("C(GlobalPhase)") { + auto sv_data = createRandomStateVectorData(re, num_qubits); + StateVectorKokkos kokkos_sv( + reinterpret_cast(sv_data.data()), sv_data.size()); + kokkos_sv.applyOperation("C(GlobalPhase)", {index}, inverse, {}, phase); + auto result_sv = kokkos_sv.getDataVector(); + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + ComplexT tmp = (inverse) ? conj(phase[j]) : phase[j]; + tmp *= ComplexT(sv_data[j]); + CHECK((real(result_sv[j])) == Approx(real(tmp))); + CHECK((imag(result_sv[j])) == Approx(imag(tmp))); + } + } + + SECTION("Controlled GlobalPhase") { + const TestType pi2 = 1.5707963267948966; + auto sv_data = createRandomStateVectorData(re, num_qubits); + StateVectorKokkos kokkos_sv( + reinterpret_cast(sv_data.data()), sv_data.size()); + kokkos_sv.applyOperation("GlobalPhase", {0, 1}, {0, 1}, {2}, inverse, + {-pi2}); + auto result_sv = kokkos_sv.getDataVector(); + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + ComplexT tmp = (inverse) ? conj(phase[j]) : phase[j]; + tmp *= ComplexT(sv_data[j]); + CHECK((real(result_sv[j])) == Approx(real(tmp))); + CHECK((imag(result_sv[j])) == Approx(imag(tmp))); + } } } @@ -1417,4 +1809,4 @@ TEMPLATE_TEST_CASE("Test NQubit gate versus expectation value", CHECK(expected == Approx(res)); } -} \ No newline at end of file +} diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp index 0d2161ebde..acd71d6372 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp @@ -229,12 +229,6 @@ TEMPLATE_PRODUCT_TEST_CASE("StateVectorKokkos::applyOperations", state_vector.applyOperations({"PauliX", "PauliY"}, {{0}, {1}}, {false}), LightningException, "must all be equal"); // invalid inverse - PL_REQUIRE_THROWS_MATCHES( - state_vector.applyOperation("PauliX", std::vector{0}, - std::vector{false}, - std::vector{1}), - LightningException, - "Controlled kernels not implemented."); // invalid controlled_wires PL_REQUIRE_THROWS_MATCHES( state_vector.applyOperation("PauliX", {}, std::vector{false}, std::vector{1}, false, @@ -242,6 +236,13 @@ TEMPLATE_PRODUCT_TEST_CASE("StateVectorKokkos::applyOperations", LightningException, "`controlled_wires` must have the same size " "as"); // invalid controlled_wires + PL_REQUIRE_THROWS_MATCHES( + state_vector.applyOperation( + "PauliX", std::vector{0, 1}, + std::vector{true, true}, std::vector{1, 2}, + false, {}), + LightningException, + "`controlled_wires` and target wires must be disjoint."); } SECTION("Test invalid arguments with parameters") { diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp index dac6411fa6..005a2f50bc 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp @@ -17,9 +17,9 @@ * Defines utility functions for Bitwise operations. */ #pragma once -#include - #include "BitUtil.hpp" +#include "Util.hpp" +#include /// @cond DEV namespace { @@ -115,4 +115,122 @@ inline auto wires2Parity(const std::size_t num_qubits, return {parity, rev_wire_shifts}; } -} // namespace Pennylane::LightningKokkos::Util \ No newline at end of file +/** + * @brief Compute parity and reverse wires for multi-qubit control operations + * + * @param num_qubits Number of qubits in the state vector. + * @param wires List of target wires. + * @param controlled_wires List of control wires. + * @return std::pair Parities and reverse + * wires for control multi-qubit operations + */ +inline auto reverseWires(const std::size_t num_qubits, + const std::vector &wires, + const std::vector &controlled_wires) + -> std::pair { + KokkosIntVector parity; + KokkosIntVector rev_wires; + + const std::size_t n_contr = controlled_wires.size(); + const std::size_t n_wires = wires.size(); + const std::size_t nw_tot = n_contr + n_wires; + std::vector all_wires; + all_wires.reserve(nw_tot); + std::copy(wires.begin(), wires.end(), std::back_inserter(all_wires)); + std::copy(controlled_wires.begin(), controlled_wires.end(), + std::back_inserter(all_wires)); + + std::vector rev_wires_(nw_tot, (num_qubits - 1)); + std::transform(rev_wires_.begin(), rev_wires_.end(), all_wires.rbegin(), + rev_wires_.begin(), std::minus<>{}); + const std::vector parity_ = revWireParity(rev_wires_); + + Kokkos::View> + rev_wires_host(rev_wires_.data(), rev_wires_.size()); + Kokkos::resize(rev_wires, rev_wires_host.size()); + Kokkos::deep_copy(rev_wires, rev_wires_host); + + Kokkos::View> + parity_host(parity_.data(), parity_.size()); + Kokkos::resize(parity, parity_host.size()); + Kokkos::deep_copy(parity, parity_host); + + return {parity, rev_wires}; +} + +/** + * @brief Generate bit patterns for multi-qubit operations + * + * @param wires List of target wires. + * @param num_qubits + * @return std::vector List of indices containing control bit patterns + */ +inline auto generateBitPatterns(const std::vector &wires, + const std::size_t num_qubits) + -> std::vector { + std::vector indices; + indices.reserve(Pennylane::Util::exp2(wires.size())); + indices.emplace_back(0); + + for (auto index_it = wires.rbegin(); index_it != wires.rend(); index_it++) { + const std::size_t value = + Pennylane::Util::maxDecimalForQubit(*index_it, num_qubits); + const std::size_t currentSize = indices.size(); + for (std::size_t j = 0; j < currentSize; j++) { + indices.emplace_back(indices[j] + value); + } + } + return indices; +} + +/** + * @brief Introduce quantum controls in indices generated by + * generateBitPatterns. + * + * @param indices Indices for the operation. + * @param num_qubits Number of qubits in register. + * @param controlled_wires Control wires. + * @param controlled_values Control values (false or true). + */ +inline void ControlBitPatterns(std::vector &indices, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values) { + if (controlled_wires.empty()) { + return; + } + std::vector controlled_values_i(controlled_values.size()); + std::transform(controlled_values.begin(), controlled_values.end(), + controlled_values_i.begin(), + [](const bool v) { return static_cast(v); }); + std::for_each( + indices.begin(), indices.end(), + [num_qubits, &controlled_wires, &controlled_values_i](std::size_t &i) { + for (std::size_t k = 0; k < controlled_wires.size(); k++) { + const std::size_t rev_wire = + (num_qubits - 1) - controlled_wires[k]; + const std::size_t value = controlled_values_i[k]; + i = (i & ~(one << rev_wire)) | (value << rev_wire); + } + }); +} + +/** + * @brief Compute index offset from parity for control operations + * + * @param parity List of parities for control operation. + * @param k Iteration index for applying control operation. + * @return std::size_t Index offset. + */ +KOKKOS_INLINE_FUNCTION std::size_t +parity_2_offset(const KokkosIntVector &parity, const std::size_t k) { + std::size_t offset{0U}; + for (std::size_t i = 0; i < parity.size(); i++) { + offset |= ((k << i) & parity(i)); + } + return offset; +} + +} // namespace Pennylane::LightningKokkos::Util diff --git a/pennylane_lightning/core/src/utils/Util.hpp b/pennylane_lightning/core/src/utils/Util.hpp index 6f512ac40d..34ad5b0697 100644 --- a/pennylane_lightning/core/src/utils/Util.hpp +++ b/pennylane_lightning/core/src/utils/Util.hpp @@ -592,4 +592,4 @@ bool areVecsDisjoint(const std::vector &v1, const std::vector &v2) { } return true; } -} // namespace Pennylane::Util +} // namespace Pennylane::Util \ No newline at end of file diff --git a/pennylane_lightning/lightning_kokkos/_state_vector.py b/pennylane_lightning/lightning_kokkos/_state_vector.py index 9073a9dd8f..4704b829a6 100644 --- a/pennylane_lightning/lightning_kokkos/_state_vector.py +++ b/pennylane_lightning/lightning_kokkos/_state_vector.py @@ -38,7 +38,6 @@ from pennylane.wires import Wires # pylint: disable=ungrouped-imports -from pennylane_lightning.core._serialize import global_phase_diagonal from pennylane_lightning.core._state_vector_base import LightningBaseStateVector from ._measurements import LightningKokkosMeasurements @@ -193,15 +192,19 @@ def _apply_lightning_controlled(self, operation): """ state = self.state_vector + basename = operation.base.name + method = getattr(state, f"{basename}", None) control_wires = list(operation.control_wires) control_values = operation.control_values - name = operation.name - # Apply GlobalPhase - inv = False - param = operation.parameters[0] - wires = self.wires.indices(operation.wires) - matrix = global_phase_diagonal(param, self.wires, control_wires, control_values) - state.apply(name, wires, inv, [[param]], matrix) + target_wires = list(operation.target_wires) + if method is not None: # apply n-controlled specialized gate + inv = False + param = operation.parameters + method(control_wires, control_values, target_wires, inv, param) + else: + raise qml.DeviceError( + "No gate operation supplied and controlled matrix not yet supported" + ) def _apply_lightning_midmeasure( self, operation: MidMeasureMP, mid_measurements: dict, postselect_mode: str @@ -281,10 +284,26 @@ def _apply_lightning( elif method is not None: # apply specialized gate param = operation.parameters method(wires, invert_param, param) - elif isinstance(operation, qml.ops.Controlled) and isinstance( - operation.base, qml.GlobalPhase + elif isinstance(operation, qml.ops.Controlled) and ( + isinstance( + operation.base, + ( + qml.GlobalPhase, + qml.PauliX, + qml.PauliY, + qml.PauliZ, + qml.Hadamard, + qml.S, + qml.T, + qml.PhaseShift, + qml.RX, + qml.RY, + qml.RZ, + qml.Rot, + ), + ) ): # apply n-controlled gate - # Kokkos do not support the controlled gates except for GlobalPhase + # Kokkos does not support controlled gates except for GlobalPhase and single-qubit self._apply_lightning_controlled(operation) else: # apply gate as a matrix # Inverse can be set to False since qml.matrix(operation) is already in diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 42a27e969e..2d6def1255 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -106,6 +106,18 @@ "CRX", "CRY", "CRZ", + "C(PauliX)", + "C(PauliY)", + "C(PauliZ)", + "C(Hadamard)", + "C(S)", + "C(T)", + "C(PhaseShift)", + "C(RX)", + "C(RY)", + "C(RZ)", + "C(Rot)", + "C(GlobalPhase)", "CRot", "IsingXX", "IsingYY", diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml b/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml index 3676feb996..7c1e55473b 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml @@ -16,30 +16,30 @@ CZ = { properties = [ "invertible", "differe DoubleExcitationMinus = { properties = [ "invertible", "differentiable" ] } DoubleExcitationPlus = { properties = [ "invertible", "differentiable" ] } DoubleExcitation = { properties = [ "invertible", "differentiable" ] } -GlobalPhase = { properties = [ "invertible", "differentiable" ] } -Hadamard = { properties = [ "invertible", "differentiable" ] } +GlobalPhase = { properties = [ "invertible", "controllable", "differentiable" ] } +Hadamard = { properties = [ "invertible", "controllable", "differentiable" ] } Identity = { properties = [ "invertible", "differentiable" ] } IsingXX = { properties = [ "invertible", "differentiable" ] } IsingXY = { properties = [ "invertible", "differentiable" ] } IsingYY = { properties = [ "invertible", "differentiable" ] } IsingZZ = { properties = [ "invertible", "differentiable" ] } MultiRZ = { properties = [ "invertible", "differentiable" ] } -PauliX = { properties = [ "invertible", "differentiable" ] } -PauliY = { properties = [ "invertible", "differentiable" ] } -PauliZ = { properties = [ "invertible", "differentiable" ] } -PhaseShift = { properties = [ "invertible", "differentiable" ] } +PauliX = { properties = [ "invertible", "controllable", "differentiable" ] } +PauliY = { properties = [ "invertible", "controllable", "differentiable" ] } +PauliZ = { properties = [ "invertible", "controllable", "differentiable" ] } +PhaseShift = { properties = [ "invertible", "controllable", "differentiable" ] } QubitUnitary = { properties = [ "invertible", ] } -Rot = { properties = [ "invertible", ] } -RX = { properties = [ "invertible", "differentiable" ] } -RY = { properties = [ "invertible", "differentiable" ] } -RZ = { properties = [ "invertible", "differentiable" ] } -SingleExcitationMinus = { properties = [ "invertible", "differentiable" ] } +Rot = { properties = [ "invertible", "controllable" ] } +RX = { properties = [ "invertible", "controllable", "differentiable" ] } +RY = { properties = [ "invertible", "controllable", "differentiable" ] } +RZ = { properties = [ "invertible", "controllable", "differentiable" ] } +SingleExcitationMinus = { properties = [ "invertible", "controllable", "differentiable" ] } SingleExcitationPlus = { properties = [ "invertible", "differentiable" ] } SingleExcitation = { properties = [ "invertible", "differentiable" ] } S = { properties = [ "invertible", "differentiable" ] } -SWAP = { properties = [ "invertible", "differentiable" ] } +SWAP = { properties = [ "invertible", "controllable", "differentiable" ] } Toffoli = { properties = [ "invertible", "differentiable" ] } -T = { properties = [ "invertible", "differentiable" ] } +T = { properties = [ "invertible", "controllable", "differentiable" ] } # Operators that should be decomposed according to the algorithm used # by PennyLane's device API. diff --git a/tests/test_gates.py b/tests/test_gates.py index bc586cb9d4..8ae5302f58 100644 --- a/tests/test_gates.py +++ b/tests/test_gates.py @@ -450,10 +450,6 @@ def circuit(): assert np.allclose(circ(), circ_def(), tol) -@pytest.mark.skipif( - device_name in ("lightning.kokkos"), - reason="N-controlled operations only implemented in lightning.qubit and lightning.tensor.", -) @pytest.mark.parametrize( "operation", [ @@ -491,6 +487,8 @@ def test_controlled_qubit_gates(operation, n_qubits, control_value, tol): dev = qml.device(device_name, wires=n_qubits) threshold = 5 if device_name == "lightning.tensor" else 250 num_wires = max(operation.num_wires, 1) + if device_name == "lightning.kokkos" and num_wires > 1: + pytest.skip("lightning.kokkos only supports single qubit controlled gates.") for n_wires in range(num_wires + 1, num_wires + 4): wire_lists = list(itertools.permutations(range(0, n_qubits), n_wires)) From e89065fffd67f647eafef2db5e60370793aaf6d2 Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Fri, 8 Nov 2024 23:32:46 +0000 Subject: [PATCH 08/53] Auto update version from '0.40.0-dev5' to '0.40.0-dev6' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 36daa70a97..84b4957c22 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev5" +__version__ = "0.40.0-dev6" From 1979bd7c543dba39468930b152875b172f2996eb Mon Sep 17 00:00:00 2001 From: Joseph Lee <40768758+josephleekl@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:20:59 -0500 Subject: [PATCH 09/53] (LK-C-2) Add controlled named 2/4-qubit gate (e.g. NCIsingXX) support to Lightning Kokkos (#953) ### Before submitting Please complete the following checklist when submitting a PR: - [ ] All new features must include a unit test. If you've fixed a bug or added code that should be tested, add a test to the [`tests`](../tests) directory! - [ ] All new functions and code must be clearly commented and documented. If you do make documentation changes, make sure that the docs build and render correctly by running `make docs`. - [ ] Ensure that the test suite passes, by running `make test`. - [ ] Add a new entry to the `.github/CHANGELOG.md` file, summarizing the change, and including a link back to the PR. - [ ] Ensure that code is properly formatted by running `make format`. When all the above are checked, delete everything above the dashed line and fill in the pull request template. ------------------------------------------------------------------------------------------------------------ **Context:** **Description of the Change:** This PR adds support for named controlled 2/4-qubit gates (e.g. Swap / IsingXX / SingleExcitation / Double Excitation). These are defined in `BasicGateFunctor.hpp`, and are applied through `applyNC2Functor` and `applyNC4Functor` defined in the same file. **Benefits:** Performance benchmarks for gates are shown here: https://www.notion.so/xanaduai/Lightning-Kokkos-Native-Controlled-Operation-Gate-Benchmarks-12ebc6bd17648017a2dcd237748b24fe **Possible Drawbacks:** **Related GitHub Issues:** [sc-76774] --- pennylane_lightning/core/_version.py | 2 +- .../gates/BasicGateFunctors.hpp | 983 ++++++++++++------ .../gates/BasicGeneratorFunctors.hpp | 350 +++---- .../tests/Test_StateVectorKokkos_NonParam.cpp | 96 ++ .../tests/Test_StateVectorKokkos_Param.cpp | 716 ++++++++++++- .../lightning_kokkos/_state_vector.py | 20 +- .../lightning_kokkos/lightning_kokkos.py | 11 + .../lightning_kokkos/lightning_kokkos.toml | 20 +- tests/test_gates.py | 4 +- 9 files changed, 1686 insertions(+), 516 deletions(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 84b4957c22..8432c4f824 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev6" +__version__ = "0.40.0-dev4" diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp index 4297f83b11..ae9996defb 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp @@ -60,9 +60,9 @@ class applyNC1Functor { const std::vector &controlled_values, const std::vector &wires, FuncT core_function_) : arr(arr_), core_function(core_function_) { - - std::tie(parity, rev_wires) = + const auto &[parity_, rev_wires_] = reverseWires(num_qubits, wires, controlled_wires); + parity = parity_; std::vector indices_ = generateBitPatterns(wires, num_qubits); ControlBitPatterns(indices_, num_qubits, controlled_wires, @@ -74,7 +74,7 @@ class applyNC1Functor { *this); } KOKKOS_FUNCTION void operator()(const std::size_t k) const { - const std::size_t offset = parity_2_offset(parity, k); + const std::size_t offset = Util::parity_2_offset(parity, k); const std::size_t i0 = indices(0B00); const std::size_t i1 = indices(0B01); @@ -583,8 +583,59 @@ void applyGlobalPhase(Kokkos::View *> arr_, wires, inverse, params); } -template class applyNC2Functor { +template +class applyNC2Functor {}; + +template +class applyNC2Functor { + using KokkosIntVector = Kokkos::View; + Kokkos::View *> arr; + const FuncT core_function; + KokkosIntVector indices; + KokkosIntVector parity; + KokkosIntVector rev_wires; + KokkosIntVector rev_wire_shifts; + + public: + template + applyNC2Functor([[maybe_unused]] ExecutionSpace exec, + Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, FuncT core_function_) + : arr(arr_), core_function(core_function_) { + const auto &[parity_, rev_wires_] = + reverseWires(num_qubits, wires, controlled_wires); + parity = parity_; + std::vector indices_ = + generateBitPatterns(wires, num_qubits); + ControlBitPatterns(indices_, num_qubits, controlled_wires, + controlled_values); + indices = vector2view(indices_); + Kokkos::parallel_for( + Kokkos::RangePolicy( + 0, exp2(num_qubits - controlled_wires.size() - wires.size())), + *this); + } + KOKKOS_FUNCTION void operator()(const std::size_t k) const { + const std::size_t offset = Util::parity_2_offset(parity, k); + const std::size_t i00 = indices(0B00); + const std::size_t i01 = indices(0B01); + const std::size_t i10 = indices(0B10); + const std::size_t i11 = indices(0B11); + + core_function(arr, i00 + offset, i01 + offset, i10 + offset, + i11 + offset); + } +}; + +template +class applyNC2Functor { + + Kokkos::View *> arr; + const FuncT core_function; const std::size_t rev_wire0; const std::size_t rev_wire1; @@ -633,16 +684,17 @@ void applyCNOT(Kokkos::View *> arr_, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i01_ = i01; + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i01_ = i01; - kokkos_swap(arr(i10), arr(i11)); - }); + kokkos_swap(arr(i10), arr(i11)); + }; + + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -651,18 +703,17 @@ void applyCY(Kokkos::View *> arr_, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i01_ = i01; - Kokkos::complex v10 = arr(i10); - arr(i10) = - Kokkos::complex{imag(arr(i11)), -real(arr(i11))}; - arr(i11) = Kokkos::complex{-imag(v10), real(v10)}; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i01_ = i01; + Kokkos::complex v10 = arr(i10); + arr(i10) = Kokkos::complex{imag(arr(i11)), -real(arr(i11))}; + arr(i11) = Kokkos::complex{-imag(v10), real(v10)}; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -671,34 +722,52 @@ void applyCZ(Kokkos::View *> arr_, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i01_ = i01; - [[maybe_unused]] const auto i10_ = i10; - arr(i11) *= -1; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i01_ = i01; + [[maybe_unused]] const auto i10_ = i10; + arr(i11) *= -1; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); +} + +template +void applyNCSWAP(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] const bool inverse = false, + [[maybe_unused]] const std::vector ¶ms = {}) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i11_ = i11; + + kokkos_swap(arr(i10), arr(i01)); + }; + if (controlled_wires.empty()) { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template void applySWAP(Kokkos::View *> arr_, const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i11_ = i11; - - kokkos_swap(arr(i10), arr(i01)); - }); + applyNCSWAP(arr_, num_qubits, {}, {}, wires, + inverse); } template @@ -711,16 +780,16 @@ void applyControlledPhaseShift(Kokkos::View *> arr_, const Kokkos::complex s = (inverse) ? Kokkos::exp(-Kokkos::complex(0, angle)) : Kokkos::exp(Kokkos::complex(0, angle)); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i01_ = i01; - [[maybe_unused]] const auto i10_ = i10; - arr(i11) *= s; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i01_ = i01; + [[maybe_unused]] const auto i10_ = i10; + arr(i11) *= s; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -732,20 +801,20 @@ void applyCRX(Kokkos::View *> arr_, const PrecisionT c = std::cos(angle / 2); const PrecisionT js = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i01_ = i01; - const Kokkos::complex v10 = arr(i10); - const Kokkos::complex v11 = arr(i11); - arr(i10) = Kokkos::complex{ - c * real(v10) + js * imag(v11), c * imag(v10) - js * real(v11)}; - arr(i11) = Kokkos::complex{ - c * real(v11) + js * imag(v10), c * imag(v11) - js * real(v10)}; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i01_ = i01; + const Kokkos::complex v10 = arr(i10); + const Kokkos::complex v11 = arr(i11); + arr(i10) = Kokkos::complex{c * real(v10) + js * imag(v11), + c * imag(v10) - js * real(v11)}; + arr(i11) = Kokkos::complex{c * real(v11) + js * imag(v10), + c * imag(v11) - js * real(v10)}; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -756,18 +825,18 @@ void applyCRY(Kokkos::View *> arr_, const PrecisionT &angle = params[0]; const PrecisionT c = std::cos(angle / 2); const PrecisionT s = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i01_ = i01; - const Kokkos::complex v10 = arr(i10); - const Kokkos::complex v11 = arr(i11); - arr(i10) = c * v10 - s * v11; - arr(i11) = s * v10 + c * v11; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i01_ = i01; + const Kokkos::complex v10 = arr(i10); + const Kokkos::complex v11 = arr(i11); + arr(i10) = c * v10 - s * v11; + arr(i11) = s * v10 + c * v11; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -781,17 +850,17 @@ void applyCRZ(Kokkos::View *> arr_, const Kokkos::complex shift_0{ cos_angle, (inverse) ? sin_angle : -sin_angle}; const Kokkos::complex shift_1 = Kokkos::conj(shift_0); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i01_ = i01; - - arr(i10) *= shift_0; - arr(i11) *= shift_1; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i01_ = i01; + + arr(i10) *= shift_0; + arr(i11) *= shift_1; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -809,18 +878,57 @@ void applyCRot(Kokkos::View *> arr_, const Kokkos::complex mat_0b01 = mat[0b01]; const Kokkos::complex mat_0b10 = mat[0b10]; const Kokkos::complex mat_0b11 = mat[0b11]; - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i01_ = i01; - const Kokkos::complex v0 = arr(i10); - const Kokkos::complex v1 = arr(i11); - arr(i10) = mat_0b00 * v0 + mat_0b01 * v1; - arr(i11) = mat_0b10 * v0 + mat_0b11 * v1; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i01_ = i01; + const Kokkos::complex v0 = arr(i10); + const Kokkos::complex v1 = arr(i11); + arr(i10) = mat_0b00 * v0 + mat_0b01 * v1; + arr(i11) = mat_0b10 * v0 + mat_0b11 * v1; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); +} + +template +void applyNCIsingXX(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + + const PrecisionT &angle = params[0]; + const PrecisionT cr = std::cos(angle / 2); + const PrecisionT sj = + (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + const Kokkos::complex v00 = arr(i00); + const Kokkos::complex v01 = arr(i01); + const Kokkos::complex v10 = arr(i10); + const Kokkos::complex v11 = arr(i11); + arr(i00) = Kokkos::complex{cr * real(v00) + sj * imag(v11), + cr * imag(v00) - sj * real(v11)}; + arr(i01) = Kokkos::complex{cr * real(v01) + sj * imag(v10), + cr * imag(v01) - sj * real(v10)}; + arr(i10) = Kokkos::complex{cr * real(v10) + sj * imag(v01), + cr * imag(v10) - sj * real(v01)}; + arr(i11) = Kokkos::complex{cr * real(v11) + sj * imag(v00), + cr * imag(v11) - sj * real(v00)}; + }; + if (controlled_wires.empty()) { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -829,32 +937,44 @@ void applyIsingXX(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { + applyNCIsingXX(arr_, num_qubits, {}, {}, wires, + inverse, params); +} +template +void applyNCIsingXY(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - const Kokkos::complex v00 = arr(i00); - const Kokkos::complex v01 = arr(i01); - const Kokkos::complex v10 = arr(i10); - const Kokkos::complex v11 = arr(i11); - arr(i00) = - Kokkos::complex{cr * real(v00) + sj * imag(v11), - cr * imag(v00) - sj * real(v11)}; - arr(i01) = - Kokkos::complex{cr * real(v01) + sj * imag(v10), - cr * imag(v01) - sj * real(v10)}; - arr(i10) = - Kokkos::complex{cr * real(v10) + sj * imag(v01), - cr * imag(v10) - sj * real(v01)}; - arr(i11) = - Kokkos::complex{cr * real(v11) + sj * imag(v00), - cr * imag(v11) - sj * real(v00)}; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + const Kokkos::complex v00 = arr(i00); + const Kokkos::complex v01 = arr(i01); + const Kokkos::complex v10 = arr(i10); + const Kokkos::complex v11 = arr(i11); + arr(i00) = Kokkos::complex{real(v00), imag(v00)}; + arr(i01) = Kokkos::complex{cr * real(v01) - sj * imag(v10), + cr * imag(v01) + sj * real(v10)}; + arr(i10) = Kokkos::complex{cr * real(v10) - sj * imag(v01), + cr * imag(v10) + sj * real(v01)}; + arr(i11) = Kokkos::complex{real(v11), imag(v11)}; + }; + if (controlled_wires.empty()) { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -863,28 +983,47 @@ void applyIsingXY(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { + applyNCIsingXY(arr_, num_qubits, {}, {}, wires, + inverse, params); +} + +template +void applyNCIsingYY(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - const Kokkos::complex v00 = arr(i00); - const Kokkos::complex v01 = arr(i01); - const Kokkos::complex v10 = arr(i10); - const Kokkos::complex v11 = arr(i11); - arr(i00) = Kokkos::complex{real(v00), imag(v00)}; - arr(i01) = - Kokkos::complex{cr * real(v01) - sj * imag(v10), - cr * imag(v01) + sj * real(v10)}; - arr(i10) = - Kokkos::complex{cr * real(v10) - sj * imag(v01), - cr * imag(v10) + sj * real(v01)}; - arr(i11) = Kokkos::complex{real(v11), imag(v11)}; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + const Kokkos::complex v00 = arr(i00); + const Kokkos::complex v01 = arr(i01); + const Kokkos::complex v10 = arr(i10); + const Kokkos::complex v11 = arr(i11); + arr(i00) = Kokkos::complex{cr * real(v00) - sj * imag(v11), + cr * imag(v00) + sj * real(v11)}; + arr(i01) = Kokkos::complex{cr * real(v01) + sj * imag(v10), + cr * imag(v01) - sj * real(v10)}; + arr(i10) = Kokkos::complex{cr * real(v10) + sj * imag(v01), + cr * imag(v10) - sj * real(v01)}; + arr(i11) = Kokkos::complex{cr * real(v11) - sj * imag(v00), + cr * imag(v11) + sj * real(v00)}; + }; + if (controlled_wires.empty()) { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -893,32 +1032,40 @@ void applyIsingYY(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { + applyNCIsingYY(arr_, num_qubits, {}, {}, wires, + inverse, params); +} + +template +void applyNCIsingZZ(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + const PrecisionT &angle = params[0]; - const PrecisionT cr = std::cos(angle / 2); - const PrecisionT sj = - (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - const Kokkos::complex v00 = arr(i00); - const Kokkos::complex v01 = arr(i01); - const Kokkos::complex v10 = arr(i10); - const Kokkos::complex v11 = arr(i11); - arr(i00) = - Kokkos::complex{cr * real(v00) - sj * imag(v11), - cr * imag(v00) + sj * real(v11)}; - arr(i01) = - Kokkos::complex{cr * real(v01) + sj * imag(v10), - cr * imag(v01) - sj * real(v10)}; - arr(i10) = - Kokkos::complex{cr * real(v10) + sj * imag(v01), - cr * imag(v10) - sj * real(v01)}; - arr(i11) = - Kokkos::complex{cr * real(v11) - sj * imag(v00), - cr * imag(v11) + sj * real(v00)}; - }); + const Kokkos::complex shift_0 = Kokkos::complex{ + std::cos(angle / 2), + (inverse) ? std::sin(angle / 2) : -std::sin(angle / 2)}; + const Kokkos::complex shift_1 = Kokkos::conj(shift_0); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + arr(i00) *= shift_0; + arr(i01) *= shift_1; + arr(i10) *= shift_1; + arr(i11) *= shift_0; + }; + if (controlled_wires.empty()) { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -927,21 +1074,41 @@ void applyIsingZZ(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { + applyNCIsingZZ(arr_, num_qubits, {}, {}, wires, + inverse, params); +} + +template +void applyNCSingleExcitation(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; - const Kokkos::complex shift_0 = Kokkos::complex{ - std::cos(angle / 2), - (inverse) ? std::sin(angle / 2) : -std::sin(angle / 2)}; - const Kokkos::complex shift_1 = Kokkos::conj(shift_0); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - arr(i00) *= shift_0; - arr(i01) *= shift_1; - arr(i10) *= shift_1; - arr(i11) *= shift_0; - }); + const PrecisionT cr = std::cos(angle / 2); + const PrecisionT sj = + (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i11_ = i11; + + const Kokkos::complex v01 = arr(i01); + const Kokkos::complex v10 = arr(i10); + arr(i01) = cr * v01 - sj * v10; + arr(i10) = sj * v01 + cr * v10; + }; + if (controlled_wires.empty()) { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -950,23 +1117,43 @@ void applySingleExcitation(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { + applyNCSingleExcitation( + arr_, num_qubits, {}, {}, wires, inverse, params); +} + +template +void applyNCSingleExcitationMinus( + Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, const bool inverse = false, + const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i11_ = i11; - - const Kokkos::complex v01 = arr(i01); - const Kokkos::complex v10 = arr(i10); - arr(i01) = cr * v01 - sj * v10; - arr(i10) = sj * v01 + cr * v10; - }); + const Kokkos::complex e = + (inverse) ? Kokkos::exp(Kokkos::complex(0, angle / 2)) + : Kokkos::exp(Kokkos::complex(0, -angle / 2)); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + const Kokkos::complex v01 = arr(i01); + const Kokkos::complex v10 = arr(i10); + arr(i00) *= e; + arr(i01) = cr * v01 - sj * v10; + arr(i10) = sj * v01 + cr * v10; + arr(i11) *= e; + }; + if (controlled_wires.empty()) { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -974,25 +1161,46 @@ void applySingleExcitationMinus( Kokkos::View *> arr_, const std::size_t num_qubits, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { + applyNCSingleExcitationMinus( + arr_, num_qubits, {}, {}, wires, inverse, params); +} + +template +void applyNCSingleExcitationPlus( + Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, const bool inverse = false, + const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); const Kokkos::complex e = - (inverse) ? Kokkos::exp(Kokkos::complex(0, angle / 2)) - : Kokkos::exp(Kokkos::complex(0, -angle / 2)); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - const Kokkos::complex v01 = arr(i01); - const Kokkos::complex v10 = arr(i10); - arr(i00) *= e; - arr(i01) = cr * v01 - sj * v10; - arr(i10) = sj * v01 + cr * v10; - arr(i11) *= e; - }); + (inverse) ? Kokkos::exp(Kokkos::complex(0, -angle / 2)) + : Kokkos::exp(Kokkos::complex(0, angle / 2)); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i11_ = i11; + + const Kokkos::complex v01 = arr(i01); + const Kokkos::complex v10 = arr(i10); + arr(i00) *= e; + arr(i01) = cr * v01 - sj * v10; + arr(i10) = sj * v01 + cr * v10; + arr(i11) *= e; + }; + if (controlled_wires.empty()) { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -1001,28 +1209,8 @@ void applySingleExcitationPlus(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; - const PrecisionT cr = std::cos(angle / 2); - const PrecisionT sj = - (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - const Kokkos::complex e = - (inverse) ? Kokkos::exp(Kokkos::complex(0, -angle / 2)) - : Kokkos::exp(Kokkos::complex(0, angle / 2)); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i11_ = i11; - - const Kokkos::complex v01 = arr(i01); - const Kokkos::complex v10 = arr(i10); - arr(i00) *= e; - arr(i01) = cr * v01 - sj * v10; - arr(i10) = sj * v01 + cr * v10; - arr(i11) *= e; - }); + applyNCSingleExcitationPlus( + arr_, num_qubits, {}, {}, wires, inverse, params); } template class applyNC3Functor { @@ -1138,7 +1326,71 @@ void applyToffoli(Kokkos::View *> arr_, }); } -template class applyNC4Functor { +template +class applyNC4Functor {}; + +template +class applyNC4Functor { + using KokkosIntVector = Kokkos::View; + + Kokkos::View *> arr; + const FuncT core_function; + KokkosIntVector indices; + KokkosIntVector parity; + KokkosIntVector rev_wires; + KokkosIntVector rev_wire_shifts; + + public: + template + applyNC4Functor([[maybe_unused]] ExecutionSpace exec, + Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, FuncT core_function_) + : arr(arr_), core_function(core_function_) { + const auto &[parity_, rev_wires_] = + reverseWires(num_qubits, wires, controlled_wires); + parity = parity_; + std::vector indices_ = + generateBitPatterns(wires, num_qubits); + ControlBitPatterns(indices_, num_qubits, controlled_wires, + controlled_values); + indices = vector2view(indices_); + Kokkos::parallel_for( + Kokkos::RangePolicy( + 0, exp2(num_qubits - controlled_wires.size() - wires.size())), + *this); + } + KOKKOS_FUNCTION void operator()(const std::size_t k) const { + const std::size_t offset = Util::parity_2_offset(parity, k); + const std::size_t i0000 = indices(0B0000); + const std::size_t i0001 = indices(0B0001); + const std::size_t i0010 = indices(0B0010); + const std::size_t i0011 = indices(0B0011); + const std::size_t i0100 = indices(0B0100); + const std::size_t i0101 = indices(0B0101); + const std::size_t i0110 = indices(0B0110); + const std::size_t i0111 = indices(0B0111); + const std::size_t i1000 = indices(0B1000); + const std::size_t i1001 = indices(0B1001); + const std::size_t i1010 = indices(0B1010); + const std::size_t i1011 = indices(0B1011); + const std::size_t i1100 = indices(0B1100); + const std::size_t i1101 = indices(0B1101); + const std::size_t i1110 = indices(0B1110); + const std::size_t i1111 = indices(0B1111); + + core_function( + arr, i0000 + offset, i0001 + offset, i0010 + offset, i0011 + offset, + i0100 + offset, i0101 + offset, i0110 + offset, i0111 + offset, + i1000 + offset, i1001 + offset, i1010 + offset, i1011 + offset, + i1100 + offset, i1101 + offset, i1110 + offset, i1111 + offset); + } +}; + +template +class applyNC4Functor { Kokkos::View *> arr; const FuncT core_function; const std::size_t rev_wire0; @@ -1252,17 +1504,18 @@ template class applyNC4Functor { }; template -void applyDoubleExcitation(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - const bool inverse = false, - const std::vector ¶ms = {}) { +void applyNCDoubleExcitation(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - applyNC4Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0000, const std::size_t i0001, const std::size_t i0010, const std::size_t i0011, @@ -1272,40 +1525,60 @@ void applyDoubleExcitation(Kokkos::View *> arr_, const std::size_t i1010, const std::size_t i1011, const std::size_t i1100, const std::size_t i1101, const std::size_t i1110, const std::size_t i1111) { - [[maybe_unused]] const auto i0000_ = i0000; - [[maybe_unused]] const auto i0001_ = i0001; - [[maybe_unused]] const auto i0010_ = i0010; - [[maybe_unused]] const auto i0100_ = i0100; - [[maybe_unused]] const auto i0101_ = i0101; - [[maybe_unused]] const auto i0110_ = i0110; - [[maybe_unused]] const auto i0111_ = i0111; - [[maybe_unused]] const auto i1000_ = i1000; - [[maybe_unused]] const auto i1001_ = i1001; - [[maybe_unused]] const auto i1010_ = i1010; - [[maybe_unused]] const auto i1011_ = i1011; - [[maybe_unused]] const auto i1101_ = i1101; - [[maybe_unused]] const auto i1110_ = i1110; - [[maybe_unused]] const auto i1111_ = i1111; - const Kokkos::complex v3 = arr(i0011); - const Kokkos::complex v12 = arr(i1100); - arr(i0011) = cr * v3 - sj * v12; - arr(i1100) = sj * v3 + cr * v12; - }); + [[maybe_unused]] const auto i0000_ = i0000; + [[maybe_unused]] const auto i0001_ = i0001; + [[maybe_unused]] const auto i0010_ = i0010; + [[maybe_unused]] const auto i0100_ = i0100; + [[maybe_unused]] const auto i0101_ = i0101; + [[maybe_unused]] const auto i0110_ = i0110; + [[maybe_unused]] const auto i0111_ = i0111; + [[maybe_unused]] const auto i1000_ = i1000; + [[maybe_unused]] const auto i1001_ = i1001; + [[maybe_unused]] const auto i1010_ = i1010; + [[maybe_unused]] const auto i1011_ = i1011; + [[maybe_unused]] const auto i1101_ = i1101; + [[maybe_unused]] const auto i1110_ = i1110; + [[maybe_unused]] const auto i1111_ = i1111; + const Kokkos::complex v3 = arr(i0011); + const Kokkos::complex v12 = arr(i1100); + arr(i0011) = cr * v3 - sj * v12; + arr(i1100) = sj * v3 + cr * v12; + }; + if (controlled_wires.empty()) { + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template -void applyDoubleExcitationMinus( +void applyDoubleExcitation(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + applyNCDoubleExcitation( + arr_, num_qubits, {}, {}, wires, inverse, params); +} + +template +void applyNCDoubleExcitationMinus( Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - const bool inverse = false, const std::vector ¶ms = {}) { + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, const bool inverse = false, + const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = inverse ? -std::sin(angle / 2) : std::sin(angle / 2); const Kokkos::complex e = inverse ? Kokkos::exp(Kokkos::complex(0, angle / 2)) : Kokkos::exp(Kokkos::complex(0, -angle / 2)); - applyNC4Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0000, const std::size_t i0001, const std::size_t i0010, const std::size_t i0011, @@ -1315,41 +1588,59 @@ void applyDoubleExcitationMinus( const std::size_t i1010, const std::size_t i1011, const std::size_t i1100, const std::size_t i1101, const std::size_t i1110, const std::size_t i1111) { - const Kokkos::complex v3 = arr(i0011); - const Kokkos::complex v12 = arr(i1100); - arr(i0000) *= e; - arr(i0001) *= e; - arr(i0010) *= e; - arr(i0011) = cr * v3 - sj * v12; - arr(i0100) *= e; - arr(i0101) *= e; - arr(i0110) *= e; - arr(i0111) *= e; - arr(i1000) *= e; - arr(i1001) *= e; - arr(i1010) *= e; - arr(i1011) *= e; - arr(i1100) = sj * v3 + cr * v12; - arr(i1101) *= e; - arr(i1110) *= e; - arr(i1111) *= e; - }); + const Kokkos::complex v3 = arr(i0011); + const Kokkos::complex v12 = arr(i1100); + arr(i0000) *= e; + arr(i0001) *= e; + arr(i0010) *= e; + arr(i0011) = cr * v3 - sj * v12; + arr(i0100) *= e; + arr(i0101) *= e; + arr(i0110) *= e; + arr(i0111) *= e; + arr(i1000) *= e; + arr(i1001) *= e; + arr(i1010) *= e; + arr(i1011) *= e; + arr(i1100) = sj * v3 + cr * v12; + arr(i1101) *= e; + arr(i1110) *= e; + arr(i1111) *= e; + }; + if (controlled_wires.empty()) { + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template -void applyDoubleExcitationPlus(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - const bool inverse = false, - const std::vector ¶ms = {}) { +void applyDoubleExcitationMinus( + Kokkos::View *> arr_, + const std::size_t num_qubits, const std::vector &wires, + const bool inverse = false, const std::vector ¶ms = {}) { + applyNCDoubleExcitationMinus( + arr_, num_qubits, {}, {}, wires, inverse, params); +} + +template +void applyNCDoubleExcitationPlus( + Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, const bool inverse = false, + const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = inverse ? -std::sin(angle / 2) : std::sin(angle / 2); const Kokkos::complex e = inverse ? Kokkos::exp(Kokkos::complex(0, -angle / 2)) : Kokkos::exp(Kokkos::complex(0, angle / 2)); - applyNC4Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0000, const std::size_t i0001, const std::size_t i0010, const std::size_t i0011, @@ -1359,25 +1650,43 @@ void applyDoubleExcitationPlus(Kokkos::View *> arr_, const std::size_t i1010, const std::size_t i1011, const std::size_t i1100, const std::size_t i1101, const std::size_t i1110, const std::size_t i1111) { - const Kokkos::complex v3 = arr(i0011); - const Kokkos::complex v12 = arr(i1100); - arr(i0000) *= e; - arr(i0001) *= e; - arr(i0010) *= e; - arr(i0011) = cr * v3 - sj * v12; - arr(i0100) *= e; - arr(i0101) *= e; - arr(i0110) *= e; - arr(i0111) *= e; - arr(i1000) *= e; - arr(i1001) *= e; - arr(i1010) *= e; - arr(i1011) *= e; - arr(i1100) = sj * v3 + cr * v12; - arr(i1101) *= e; - arr(i1110) *= e; - arr(i1111) *= e; - }); + const Kokkos::complex v3 = arr(i0011); + const Kokkos::complex v12 = arr(i1100); + arr(i0000) *= e; + arr(i0001) *= e; + arr(i0010) *= e; + arr(i0011) = cr * v3 - sj * v12; + arr(i0100) *= e; + arr(i0101) *= e; + arr(i0110) *= e; + arr(i0111) *= e; + arr(i1000) *= e; + arr(i1001) *= e; + arr(i1010) *= e; + arr(i1011) *= e; + arr(i1100) = sj * v3 + cr * v12; + arr(i1101) *= e; + arr(i1110) *= e; + arr(i1111) *= e; + }; + if (controlled_wires.empty()) { + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } +} + +template +void applyDoubleExcitationPlus(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + applyNCDoubleExcitationPlus( + arr_, num_qubits, {}, {}, wires, inverse, params); } template @@ -1641,6 +1950,60 @@ void applyNCNamedOperation(const ControlledGateOperation gateop, applyNCRot(arr_, num_qubits, controlled_wires, controlled_values, wires, inverse, params); return; + case ControlledGateOperation::SWAP: + applyNCSWAP(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, params); + return; + case ControlledGateOperation::IsingXX: + applyNCIsingXX(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, + params); + return; + case ControlledGateOperation::IsingXY: + applyNCIsingXY(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, + params); + return; + case ControlledGateOperation::IsingYY: + applyNCIsingYY(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, + params); + return; + case ControlledGateOperation::IsingZZ: + applyNCIsingZZ(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, + params); + return; + case ControlledGateOperation::SingleExcitation: + applyNCSingleExcitation( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse, params); + return; + case ControlledGateOperation::SingleExcitationMinus: + applyNCSingleExcitationMinus( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse, params); + return; + case ControlledGateOperation::SingleExcitationPlus: + applyNCSingleExcitationPlus( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse, params); + return; + case ControlledGateOperation::DoubleExcitation: + applyNCDoubleExcitation( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse, params); + return; + case ControlledGateOperation::DoubleExcitationMinus: + applyNCDoubleExcitationMinus( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse, params); + return; + case ControlledGateOperation::DoubleExcitationPlus: + applyNCDoubleExcitationPlus( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse, params); + return; case ControlledGateOperation::GlobalPhase: applyNCGlobalPhase(arr_, num_qubits, controlled_wires, controlled_values, wires, inverse, diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp index e35b75b698..43cc5807dd 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp @@ -52,17 +52,17 @@ void applyGenControlledPhaseShift( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i11_ = i11; + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i11_ = i11; - arr(i00) = 0.0; - arr(i01) = 0.0; - arr(i10) = 0.0; - }); + arr(i00) = 0.0; + arr(i01) = 0.0; + arr(i10) = 0.0; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -71,15 +71,15 @@ void applyGenCRX(Kokkos::View *> arr_, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - arr(i00) = 0.0; - arr(i01) = 0.0; - kokkos_swap(arr(i10), arr(i11)); - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + arr(i00) = 0.0; + arr(i01) = 0.0; + kokkos_swap(arr(i10), arr(i11)); + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -88,18 +88,17 @@ void applyGenCRY(Kokkos::View *> arr_, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - arr(i00) = 0.0; - arr(i01) = 0.0; - const auto v0 = arr(i10); - arr(i10) = - Kokkos::complex{imag(arr(i11)), -real(arr(i11))}; - arr(i11) = Kokkos::complex{-imag(v0), real(v0)}; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + arr(i00) = 0.0; + arr(i01) = 0.0; + const auto v0 = arr(i10); + arr(i10) = Kokkos::complex{imag(arr(i11)), -real(arr(i11))}; + arr(i11) = Kokkos::complex{-imag(v0), real(v0)}; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -108,16 +107,16 @@ void applyGenCRZ(Kokkos::View *> arr_, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i10_ = i10; - arr(i00) = 0.0; - arr(i01) = 0.0; - arr(i11) *= -1; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i10_ = i10; + arr(i00) = 0.0; + arr(i01) = 0.0; + arr(i11) *= -1; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -126,14 +125,14 @@ void applyGenIsingXX( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - kokkos_swap(arr(i00), arr(i11)); - kokkos_swap(arr(i10), arr(i01)); - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + kokkos_swap(arr(i00), arr(i11)); + kokkos_swap(arr(i10), arr(i01)); + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -142,15 +141,15 @@ void applyGenIsingXY( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - kokkos_swap(arr(i10), arr(i01)); - arr(i00) = 0.0; - arr(i11) = 0.0; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + kokkos_swap(arr(i10), arr(i01)); + arr(i00) = 0.0; + arr(i11) = 0.0; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -159,16 +158,16 @@ void applyGenIsingYY( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - const auto v00 = arr(i00); - arr(i00) = -arr(i11); - arr(i11) = -v00; - kokkos_swap(arr(i10), arr(i01)); - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + const auto v00 = arr(i00); + arr(i00) = -arr(i11); + arr(i11) = -v00; + kokkos_swap(arr(i10), arr(i01)); + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -177,17 +176,17 @@ void applyGenIsingZZ( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i11_ = i11; + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i11_ = i11; - arr(i10) *= -1; - arr(i01) *= -1; - }); + arr(i10) *= -1; + arr(i01) *= -1; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -196,17 +195,17 @@ void applyGenSingleExcitation( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - arr(i00) = 0.0; - arr(i01) *= Kokkos::complex{0.0, 1.0}; - arr(i10) *= Kokkos::complex{0.0, -1.0}; - arr(i11) = 0.0; - kokkos_swap(arr(i10), arr(i01)); - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + arr(i00) = 0.0; + arr(i01) *= Kokkos::complex{0.0, 1.0}; + arr(i10) *= Kokkos::complex{0.0, -1.0}; + arr(i11) = 0.0; + kokkos_swap(arr(i10), arr(i01)); + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -215,18 +214,18 @@ void applyGenSingleExcitationMinus( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i11_ = i11; + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i11_ = i11; - arr(i01) *= Kokkos::complex{0.0, 1.0}; - arr(i10) *= Kokkos::complex{0.0, -1.0}; - kokkos_swap(arr(i10), arr(i01)); - }); + arr(i01) *= Kokkos::complex{0.0, 1.0}; + arr(i10) *= Kokkos::complex{0.0, -1.0}; + kokkos_swap(arr(i10), arr(i01)); + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -235,20 +234,20 @@ void applyGenSingleExcitationPlus( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i11_ = i11; + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i11_ = i11; - arr(i00) *= -1; - arr(i01) *= Kokkos::complex{0.0, 1.0}; - arr(i10) *= Kokkos::complex{0.0, -1.0}; - arr(i11) *= -1; - kokkos_swap(arr(i10), arr(i01)); - }); + arr(i00) *= -1; + arr(i01) *= Kokkos::complex{0.0, 1.0}; + arr(i10) *= Kokkos::complex{0.0, -1.0}; + arr(i11) *= -1; + kokkos_swap(arr(i10), arr(i01)); + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -257,8 +256,7 @@ void applyGenDoubleExcitation( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC4Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0000, const std::size_t i0001, const std::size_t i0010, const std::size_t i0011, @@ -268,25 +266,27 @@ void applyGenDoubleExcitation( const std::size_t i1010, const std::size_t i1011, const std::size_t i1100, const std::size_t i1101, const std::size_t i1110, const std::size_t i1111) { - const Kokkos::complex v3 = arr(i0011); - const Kokkos::complex v12 = arr(i1100); - arr(i0000) = 0.0; - arr(i0001) = 0.0; - arr(i0010) = 0.0; - arr(i0011) = v12 * Kokkos::complex{0.0, -1.0}; - arr(i0100) = 0.0; - arr(i0101) = 0.0; - arr(i0110) = 0.0; - arr(i0111) = 0.0; - arr(i1000) = 0.0; - arr(i1001) = 0.0; - arr(i1010) = 0.0; - arr(i1011) = 0.0; - arr(i1100) = v3 * Kokkos::complex{0.0, 1.0}; - arr(i1101) = 0.0; - arr(i1110) = 0.0; - arr(i1111) = 0.0; - }); + const Kokkos::complex v0011 = arr(i0011); + const Kokkos::complex v1100 = arr(i1100); + arr(i0000) = 0.0; + arr(i0001) = 0.0; + arr(i0010) = 0.0; + arr(i0011) = v1100 * Kokkos::complex{0.0, -1.0}; + arr(i0100) = 0.0; + arr(i0101) = 0.0; + arr(i0110) = 0.0; + arr(i0111) = 0.0; + arr(i1000) = 0.0; + arr(i1001) = 0.0; + arr(i1010) = 0.0; + arr(i1011) = 0.0; + arr(i1100) = v0011 * Kokkos::complex{0.0, 1.0}; + arr(i1101) = 0.0; + arr(i1110) = 0.0; + arr(i1111) = 0.0; + }; + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -295,8 +295,7 @@ void applyGenDoubleExcitationMinus( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC4Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0000, const std::size_t i0001, const std::size_t i0010, const std::size_t i0011, @@ -306,24 +305,26 @@ void applyGenDoubleExcitationMinus( const std::size_t i1010, const std::size_t i1011, const std::size_t i1100, const std::size_t i1101, const std::size_t i1110, const std::size_t i1111) { - [[maybe_unused]] const auto i0000_ = i0000; - [[maybe_unused]] const auto i0001_ = i0001; - [[maybe_unused]] const auto i0010_ = i0010; - [[maybe_unused]] const auto i0100_ = i0100; - [[maybe_unused]] const auto i0101_ = i0101; - [[maybe_unused]] const auto i0110_ = i0110; - [[maybe_unused]] const auto i0111_ = i0111; - [[maybe_unused]] const auto i1000_ = i1000; - [[maybe_unused]] const auto i1001_ = i1001; - [[maybe_unused]] const auto i1010_ = i1010; - [[maybe_unused]] const auto i1011_ = i1011; - [[maybe_unused]] const auto i1101_ = i1101; - [[maybe_unused]] const auto i1110_ = i1110; - [[maybe_unused]] const auto i1111_ = i1111; - arr(i0011) *= Kokkos::complex{0.0, 1.0}; - arr(i1100) *= Kokkos::complex{0.0, -1.0}; - kokkos_swap(arr(i1100), arr(i0011)); - }); + [[maybe_unused]] const auto i0000_ = i0000; + [[maybe_unused]] const auto i0001_ = i0001; + [[maybe_unused]] const auto i0010_ = i0010; + [[maybe_unused]] const auto i0100_ = i0100; + [[maybe_unused]] const auto i0101_ = i0101; + [[maybe_unused]] const auto i0110_ = i0110; + [[maybe_unused]] const auto i0111_ = i0111; + [[maybe_unused]] const auto i1000_ = i1000; + [[maybe_unused]] const auto i1001_ = i1001; + [[maybe_unused]] const auto i1010_ = i1010; + [[maybe_unused]] const auto i1011_ = i1011; + [[maybe_unused]] const auto i1101_ = i1101; + [[maybe_unused]] const auto i1110_ = i1110; + [[maybe_unused]] const auto i1111_ = i1111; + arr(i0011) *= Kokkos::complex{0.0, 1.0}; + arr(i1100) *= Kokkos::complex{0.0, -1.0}; + kokkos_swap(arr(i1100), arr(i0011)); + }; + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -332,8 +333,7 @@ void applyGenDoubleExcitationPlus( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC4Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0000, const std::size_t i0001, const std::size_t i0010, const std::size_t i0011, @@ -343,24 +343,26 @@ void applyGenDoubleExcitationPlus( const std::size_t i1010, const std::size_t i1011, const std::size_t i1100, const std::size_t i1101, const std::size_t i1110, const std::size_t i1111) { - [[maybe_unused]] const auto i0000_ = i0000; - [[maybe_unused]] const auto i0001_ = i0001; - [[maybe_unused]] const auto i0010_ = i0010; - [[maybe_unused]] const auto i0100_ = i0100; - [[maybe_unused]] const auto i0101_ = i0101; - [[maybe_unused]] const auto i0110_ = i0110; - [[maybe_unused]] const auto i0111_ = i0111; - [[maybe_unused]] const auto i1000_ = i1000; - [[maybe_unused]] const auto i1001_ = i1001; - [[maybe_unused]] const auto i1010_ = i1010; - [[maybe_unused]] const auto i1011_ = i1011; - [[maybe_unused]] const auto i1101_ = i1101; - [[maybe_unused]] const auto i1110_ = i1110; - [[maybe_unused]] const auto i1111_ = i1111; - arr(i0011) *= Kokkos::complex{0.0, -1.0}; - arr(i1100) *= Kokkos::complex{0.0, 1.0}; - kokkos_swap(arr(i1100), arr(i0011)); - }); + [[maybe_unused]] const auto i0000_ = i0000; + [[maybe_unused]] const auto i0001_ = i0001; + [[maybe_unused]] const auto i0010_ = i0010; + [[maybe_unused]] const auto i0100_ = i0100; + [[maybe_unused]] const auto i0101_ = i0101; + [[maybe_unused]] const auto i0110_ = i0110; + [[maybe_unused]] const auto i0111_ = i0111; + [[maybe_unused]] const auto i1000_ = i1000; + [[maybe_unused]] const auto i1001_ = i1001; + [[maybe_unused]] const auto i1010_ = i1010; + [[maybe_unused]] const auto i1011_ = i1011; + [[maybe_unused]] const auto i1101_ = i1101; + [[maybe_unused]] const auto i1110_ = i1110; + [[maybe_unused]] const auto i1111_ = i1111; + arr(i0011) *= Kokkos::complex{0.0, -1.0}; + arr(i1100) *= Kokkos::complex{0.0, 1.0}; + kokkos_swap(arr(i1100), arr(i0011)); + }; + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp index 303e60720b..af9e318659 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp @@ -1083,6 +1083,102 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " } } +TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " + "two-qubit with controls", + "[StateVectorKokkos_NonParam]", float, double) { + using StateVectorT = StateVectorKokkos; + + const TestType EP = 1e-4; + const std::size_t num_qubits = 4; + const bool inverse = GENERATE(true, false); + const std::size_t control = GENERATE(0, 1, 2, 3); + const std::size_t wire0 = GENERATE(0, 1, 2, 3); + const std::size_t wire1 = GENERATE(0, 1, 2, 3); + + auto ini_st = createNonTrivialState(num_qubits); + + SECTION("N-controlled SWAP") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + + StateVectorT kokkos_sv0{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv1{ini_st.data(), ini_st.size()}; + kokkos_sv0.applyOperation("CSWAP", {control, wire0, wire1}, + inverse); + auto matrix = getSWAP(); + kokkos_sv1.applyOperation("SWAP", std::vector{control}, + std::vector{true}, + std::vector{wire0, wire1}, + inverse); + auto result_sv0 = kokkos_sv0.getDataVector(); + auto result_sv1 = kokkos_sv1.getDataVector(); + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_sv0[j]) == + Approx(real(result_sv1[j])).margin(EP)); + CHECK(imag(result_sv0[j]) == + Approx(imag(result_sv1[j])).margin(EP)); + } + } + } +} + +TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " + "two-qubit with multiple-controls", + "[StateVectorKokkos_NonParam]", float, double) { + SECTION("2-controlled SWAP") { + using ComplexT = StateVectorKokkos::ComplexT; + const std::size_t num_qubits = 4; + + std::vector ini_st{ComplexT{0.33377149, 0.2511291}, + ComplexT{0.33013572, -0.14778699}, + ComplexT{0.17288832, -0.041744}, + ComplexT{0.11004883, -0.15105962}, + ComplexT{0.17120967, 0.04376507}, + ComplexT{0.33010645, 0.20088901}, + ComplexT{-0.12770624, -0.17329647}, + ComplexT{-0.34301996, 0.11944278}, + ComplexT{0.0195779, -0.03687076}, + ComplexT{0.34155068, 0.07464519}, + ComplexT{0.00730597, 0.03670807}, + ComplexT{0.08876188, -0.18019018}, + ComplexT{-0.04946055, -0.10383813}, + ComplexT{0.0715367, 0.04895361}, + ComplexT{-0.12377521, -0.04781011}, + ComplexT{-0.14509767, 0.2102171}}; + + std::vector expected{ComplexT{0.33377149, 0.2511291}, + ComplexT{0.33013572, -0.14778699}, + ComplexT{0.17288832, -0.041744}, + ComplexT{0.11004883, -0.15105962}, + ComplexT{0.17120967, 0.04376507}, + ComplexT{0.33010645, 0.20088901}, + ComplexT{-0.12770624, -0.17329647}, + ComplexT{-0.34301996, 0.11944278}, + ComplexT{0.0195779, -0.03687076}, + ComplexT{-0.04946055, -0.10383813}, + ComplexT{0.00730597, 0.03670807}, + ComplexT{0.08876188, -0.18019018}, + ComplexT{0.34155068, 0.07464519}, + ComplexT{0.0715367, 0.04895361}, + ComplexT{-0.12377521, -0.04781011}, + ComplexT{-0.14509767, 0.2102171}}; + + SECTION("Apply using dispatcher") { + StateVectorKokkos kokkos_sv{num_qubits}; + std::vector result_sv(kokkos_sv.getLength(), {0, 0}); + + kokkos_sv.HostToDevice(ini_st.data(), ini_st.size()); + kokkos_sv.applyOperation("SWAP", {0, 2}, {true, false}, {1, 3}, + false); + kokkos_sv.DeviceToHost(result_sv.data(), result_sv.size()); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(expected[j]) == Approx(imag(result_sv[j]))); + CHECK(real(expected[j]) == Approx(real(result_sv[j]))); + } + } + } +} + TEMPLATE_TEST_CASE("StateVectorKokkos::SetStateVector", "[StateVectorKokkos_Nonparam]", float, double) { using PrecisionT = TestType; diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp index a48a40db7d..5af07e784d 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp @@ -357,7 +357,7 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation param " Kokkos::HostSpace{}, sv_gate.getView()); std::vector expected_result{ - // Generated using Pennylane + // Generated using Pennylane (qml.ctrl) ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, ComplexT{0.35355339, 0.0}, @@ -490,6 +490,720 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation param " } } +TEMPLATE_TEST_CASE( + "StateVectorKokkos::applyOperation param two-qubit with controls", + "[StateVectorKokkos_Operation]", float, double) { + using StateVectorT = StateVectorKokkos; + using PrecisionT = StateVectorT::PrecisionT; + using ComplexT = Kokkos::complex; + + const std::size_t num_qubits = 4; + const TestType EP = 1e-4; + auto ini_st = createNonTrivialState(num_qubits); + + const bool inverse = GENERATE(false, true); + const std::size_t control = GENERATE(0, 1, 2, 3); + const std::size_t wire0 = GENERATE(0, 1, 2, 3); + const std::size_t wire1 = GENERATE(0, 1, 2, 3); + const PrecisionT param = GENERATE(-1.5, -0.5, 0, 0.5, 1.5); + + auto getControlledGate = [](std::vector matrix) { + std::vector cmatrix(matrix.size() * 4); + for (std::size_t i = 0; i < 4; i++) { + cmatrix[i * 8 + i] = ComplexT{1.0}; + } + for (std::size_t i = 0; i < 4; i++) { + for (std::size_t j = 0; j < 4; j++) { + cmatrix[(i + 4) * 8 + j + 4] = matrix[i * 4 + j]; + } + } + return cmatrix; + }; + + SECTION("N-controlled IsingXX") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = getIsingXX(param); + std::vector cmatrix = getControlledGate(matrix); + kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1}, + inverse); + kokkos_sv_op.applyOperation( + "IsingXX", std::vector{control}, + std::vector{true}, std::vector{wire0, wire1}, + inverse, {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } + + SECTION("N-controlled IsingXY") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = getIsingXY(param); + std::vector cmatrix = getControlledGate(matrix); + kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1}, + inverse); + kokkos_sv_op.applyOperation( + "IsingXY", std::vector{control}, + std::vector{true}, std::vector{wire0, wire1}, + inverse, {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } + + SECTION("N-controlled IsingYY") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = getIsingYY(param); + std::vector cmatrix = getControlledGate(matrix); + kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1}, + inverse); + kokkos_sv_op.applyOperation( + "IsingYY", std::vector{control}, + std::vector{true}, std::vector{wire0, wire1}, + inverse, {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } + + SECTION("N-controlled IsingZZ") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = getIsingZZ(param); + std::vector cmatrix = getControlledGate(matrix); + kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1}, + inverse); + kokkos_sv_op.applyOperation( + "IsingZZ", std::vector{control}, + std::vector{true}, std::vector{wire0, wire1}, + inverse, {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } + + SECTION("N-controlled SingleExcitation") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = + getSingleExcitation(param); + std::vector cmatrix = getControlledGate(matrix); + kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1}, + inverse); + kokkos_sv_op.applyOperation( + "SingleExcitation", std::vector{control}, + std::vector{true}, std::vector{wire0, wire1}, + inverse, {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } + + SECTION("N-controlled SingleExcitationMinus") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = + getSingleExcitationMinus(param); + std::vector cmatrix = getControlledGate(matrix); + kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1}, + inverse); + kokkos_sv_op.applyOperation( + "SingleExcitationMinus", std::vector{control}, + std::vector{true}, std::vector{wire0, wire1}, + inverse, {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } + + SECTION("N-controlled SingleExcitationPlus") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = + getSingleExcitationPlus(param); + std::vector cmatrix = getControlledGate(matrix); + kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1}, + inverse); + kokkos_sv_op.applyOperation( + "SingleExcitationPlus", std::vector{control}, + std::vector{true}, std::vector{wire0, wire1}, + inverse, {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } +} + +TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation param " + "two-qubit with multiple-controls", + "[StateVectorKokkos_Param]", float, double) { + using ComplexT = StateVectorKokkos::ComplexT; + const bool inverse = false; + const std::size_t num_qubits = 4; + StateVectorKokkos kokkos_sv{num_qubits}; + + kokkos_sv.applyOperations( + {{"Hadamard"}, {"Hadamard"}, {"Hadamard"}, {"Hadamard"}}, + {{0}, {1}, {2}, {3}}, {{false}, {false}, {false}, {false}}); + + auto ini_sv = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, + kokkos_sv.getView()); + StateVectorKokkos sv_gate{num_qubits}; + StateVectorKokkos expected_result{num_qubits}; + + SECTION("2-controlled IsingXX") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1, 3}; + const TestType param = 0.234; + sv_gate.applyOperation("IsingXX", control_wires, control_values, + target_wire, inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{// Generated using Pennylane + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.24829083, -0.02918331}, + ComplexT{0.24829083, -0.02918331}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.24829083, -0.02918331}, + ComplexT{0.24829083, -0.02918331}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}}; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled IsingXY") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1, 3}; + const TestType param = 0.234; + sv_gate.applyOperation("IsingXY", control_wires, control_values, + target_wire, inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{// Generated using Pennylane + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.24829083, 0.02918331}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.24829083, 0.02918331}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}}; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled IsingYY") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1, 3}; + const TestType param = 0.234; + sv_gate.applyOperation("IsingYY", control_wires, control_values, + target_wire, inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{// Generated using Pennylane + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.24829083, 0.02918331}, + ComplexT{0.24829083, -0.02918331}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.24829083, -0.02918331}, + ComplexT{0.24829083, 0.02918331}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}}; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled IsingZZ") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1, 3}; + const TestType param = 0.234; + sv_gate.applyOperation("IsingZZ", control_wires, control_values, + target_wire, inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{// Generated using Pennylane + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.24829083, -0.02918331}, + ComplexT{0.24829083, 0.02918331}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.24829083, 0.02918331}, + ComplexT{0.24829083, -0.02918331}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}}; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled SingleExcitation") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1, 3}; + const TestType param = 0.234; + sv_gate.applyOperation("SingleExcitation", control_wires, + control_values, target_wire, inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{ + // Generated using Pennylane + ComplexT{0.25, 0.0}, ComplexT{0.25, 0.0}, ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, ComplexT{0.25, 0.0}, ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, ComplexT{0.25, 0.0}, ComplexT{0.25, 0.0}, + ComplexT{0.21910751, 0.0}, ComplexT{0.25, 0.0}, ComplexT{0.25, 0.0}, + ComplexT{0.27747414, 0.0}, ComplexT{0.25, 0.0}, ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}}; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled SingleExcitationPlus") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1, 3}; + const TestType param = 0.234; + sv_gate.applyOperation("SingleExcitationPlus", control_wires, + control_values, target_wire, inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{// Generated using Pennylane + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.24829083, 0.02918331}, + ComplexT{0.21910751, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.27747414, 0.0}, + ComplexT{0.24829083, 0.02918331}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}}; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled SingleExcitationMinus") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1, 3}; + const TestType param = 0.234; + sv_gate.applyOperation("SingleExcitationMinus", control_wires, + control_values, target_wire, inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{// Generated using Pennylane + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.24829083, -0.02918331}, + ComplexT{0.21910751, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.27747414, 0.0}, + ComplexT{0.24829083, -0.02918331}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}}; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } +} + +TEMPLATE_TEST_CASE( + "StateVectorKokkos::applyOperation param four-qubit with controls", + "[StateVectorKokkos_Operation]", float, double) { + using StateVectorT = StateVectorKokkos; + using PrecisionT = StateVectorT::PrecisionT; + using ComplexT = Kokkos::complex; + + const std::size_t num_qubits = 5; + const TestType EP = 1e-4; + auto ini_st = createNonTrivialState(num_qubits); + + const bool inverse = GENERATE(false, true); + const std::size_t control = GENERATE(0, 1, 2, 3, 4); + const std::size_t wire0 = GENERATE(0, 1, 2, 3, 4); + const std::size_t wire1 = GENERATE(0, 1, 2, 3, 4); + const std::size_t wire2 = GENERATE(0, 1, 2, 3, 4); + const std::size_t wire3 = GENERATE(0, 1, 2, 3, 4); + const PrecisionT param = GENERATE(-1.5, -0.5, 0, 0.5, 1.5); + + auto getControlledGate = [](std::vector matrix) { + std::vector cmatrix(matrix.size() * 4); + for (std::size_t i = 0; i < 16; i++) { + cmatrix[i * 32 + i] = ComplexT{1.0}; + } + for (std::size_t i = 0; i < 16; i++) { + for (std::size_t j = 0; j < 16; j++) { + cmatrix[(i + 16) * 32 + j + 16] = matrix[i * 16 + j]; + } + } + return cmatrix; + }; + + SECTION("N-controlled DoubleExcitation") { + std::vector wires = {control, wire0, wire1, wire2, wire3}; + std::sort(wires.begin(), wires.end()); + if (std::adjacent_find(wires.begin(), wires.end()) == wires.end()) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = + getDoubleExcitation(param); + std::vector cmatrix = getControlledGate(matrix); + + kokkos_sv_mat.applyMatrix( + cmatrix, {control, wire0, wire1, wire2, wire3}, inverse); + kokkos_sv_op.applyOperation( + "DoubleExcitation", std::vector{control}, + std::vector{true}, + std::vector{wire0, wire1, wire2, wire3}, inverse, + {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } + + SECTION("N-controlled DoubleExcitationPlus") { + std::vector wires = {control, wire0, wire1, wire2, wire3}; + std::sort(wires.begin(), wires.end()); + if (std::adjacent_find(wires.begin(), wires.end()) == wires.end()) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = + getDoubleExcitationPlus(param); + std::vector cmatrix = getControlledGate(matrix); + + kokkos_sv_mat.applyMatrix( + cmatrix, {control, wire0, wire1, wire2, wire3}, inverse); + kokkos_sv_op.applyOperation( + "DoubleExcitationPlus", std::vector{control}, + std::vector{true}, + std::vector{wire0, wire1, wire2, wire3}, inverse, + {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } + + SECTION("N-controlled DoubleExcitationMinus") { + std::vector wires = {control, wire0, wire1, wire2, wire3}; + std::sort(wires.begin(), wires.end()); + if (std::adjacent_find(wires.begin(), wires.end()) == wires.end()) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = + getDoubleExcitationMinus(param); + std::vector cmatrix = getControlledGate(matrix); + + kokkos_sv_mat.applyMatrix( + cmatrix, {control, wire0, wire1, wire2, wire3}, inverse); + kokkos_sv_op.applyOperation( + "DoubleExcitationMinus", std::vector{control}, + std::vector{true}, + std::vector{wire0, wire1, wire2, wire3}, inverse, + {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } +} + +TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation param " + "four-qubit with multiple-controls", + "[StateVectorKokkos_Param]", float, double) { + using ComplexT = StateVectorKokkos::ComplexT; + const bool inverse = false; + const std::size_t num_qubits = 6; + StateVectorKokkos kokkos_sv{num_qubits}; + + kokkos_sv.applyOperations( + {{"Hadamard"}, + {"Hadamard"}, + {"Hadamard"}, + {"Hadamard"}, + {"Hadamard"}, + {"Hadamard"}}, + {{0}, {1}, {2}, {3}, {4}, {5}}, + {{false}, {false}, {false}, {false}, {false}, {false}}); + + auto ini_sv = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, + kokkos_sv.getView()); + StateVectorKokkos sv_gate{num_qubits}; + StateVectorKokkos expected_result{num_qubits}; + + SECTION("2-controlled DoubleExcitation") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 3}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1, 2, 4, 5}; + const TestType param = 0.234; + sv_gate.applyOperation("DoubleExcitation", control_wires, + control_values, target_wire, inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result( + 64, ComplexT{0.125, 0.0}); // Generated using Pennylane + expected_result[35] = ComplexT{0.10955376, 0.0}; + expected_result[56] = ComplexT{0.13873707, 0.0}; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled DoubleExcitationPlus") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 3}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1, 2, 4, 5}; + const TestType param = 0.234; + sv_gate.applyOperation("DoubleExcitationPlus", control_wires, + control_values, target_wire, inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result( + 64, ComplexT{0.125, 0.0}); // Generated using Pennylane + expected_result[32] = ComplexT{0.12414541, 0.01459166}; + expected_result[33] = ComplexT{0.12414541, 0.01459166}; + expected_result[34] = ComplexT{0.12414541, 0.01459166}; + expected_result[35] = ComplexT{0.10955376, 0.0}; + expected_result[40] = ComplexT{0.12414541, 0.01459166}; + expected_result[41] = ComplexT{0.12414541, 0.01459166}; + expected_result[42] = ComplexT{0.12414541, 0.01459166}; + expected_result[43] = ComplexT{0.12414541, 0.01459166}; + expected_result[48] = ComplexT{0.12414541, 0.01459166}; + expected_result[49] = ComplexT{0.12414541, 0.01459166}; + expected_result[50] = ComplexT{0.12414541, 0.01459166}; + expected_result[51] = ComplexT{0.12414541, 0.01459166}; + expected_result[56] = ComplexT{0.13873707, 0.0}; + expected_result[57] = ComplexT{0.12414541, 0.01459166}; + expected_result[58] = ComplexT{0.12414541, 0.01459166}; + expected_result[59] = ComplexT{0.12414541, 0.01459166}; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("2-controlled DoubleExcitationMinus") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 3}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1, 2, 4, 5}; + const TestType param = 0.234; + sv_gate.applyOperation("DoubleExcitationMinus", control_wires, + control_values, target_wire, inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result( + 64, ComplexT{0.125, 0.0}); // Generated using Pennylane + expected_result[32] = ComplexT{0.12414541, -0.01459166}; + expected_result[33] = ComplexT{0.12414541, -0.01459166}; + expected_result[34] = ComplexT{0.12414541, -0.01459166}; + expected_result[35] = ComplexT{0.10955376, 0.0}; + expected_result[40] = ComplexT{0.12414541, -0.01459166}; + expected_result[41] = ComplexT{0.12414541, -0.01459166}; + expected_result[42] = ComplexT{0.12414541, -0.01459166}; + expected_result[43] = ComplexT{0.12414541, -0.01459166}; + expected_result[48] = ComplexT{0.12414541, -0.01459166}; + expected_result[49] = ComplexT{0.12414541, -0.01459166}; + expected_result[50] = ComplexT{0.12414541, -0.01459166}; + expected_result[51] = ComplexT{0.12414541, -0.01459166}; + expected_result[56] = ComplexT{0.13873707, 0.0}; + expected_result[57] = ComplexT{0.12414541, -0.01459166}; + expected_result[58] = ComplexT{0.12414541, -0.01459166}; + expected_result[59] = ComplexT{0.12414541, -0.01459166}; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } +} + TEMPLATE_TEST_CASE("StateVectorKokkosManaged::applyIsingXY", "[StateVectorKokkosManaged_Param]", float, double) { { diff --git a/pennylane_lightning/lightning_kokkos/_state_vector.py b/pennylane_lightning/lightning_kokkos/_state_vector.py index 4704b829a6..23fb1d626f 100644 --- a/pennylane_lightning/lightning_kokkos/_state_vector.py +++ b/pennylane_lightning/lightning_kokkos/_state_vector.py @@ -284,24 +284,8 @@ def _apply_lightning( elif method is not None: # apply specialized gate param = operation.parameters method(wires, invert_param, param) - elif isinstance(operation, qml.ops.Controlled) and ( - isinstance( - operation.base, - ( - qml.GlobalPhase, - qml.PauliX, - qml.PauliY, - qml.PauliZ, - qml.Hadamard, - qml.S, - qml.T, - qml.PhaseShift, - qml.RX, - qml.RY, - qml.RZ, - qml.Rot, - ), - ) + elif isinstance(operation, qml.ops.Controlled) and not isinstance( + operation.base, (qml.QubitUnitary, qml.BlockEncode, qml.MultiRZ) ): # apply n-controlled gate # Kokkos does not support controlled gates except for GlobalPhase and single-qubit self._apply_lightning_controlled(operation) diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 2d6def1255..3f3c59d4e2 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -117,6 +117,17 @@ "C(RY)", "C(RZ)", "C(Rot)", + "C(SWAP)", + "C(IsingXX)", + "C(IsingXY)", + "C(IsingYY)", + "C(IsingZZ)", + "C(SingleExcitation)", + "C(SingleExcitationMinus)", + "C(SingleExcitationPlus)", + "C(DoubleExcitation)", + "C(DoubleExcitationMinus)", + "C(DoubleExcitationPlus)", "C(GlobalPhase)", "CRot", "IsingXX", diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml b/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml index 7c1e55473b..db6b22ab7b 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml @@ -13,16 +13,16 @@ CRZ = { properties = [ "invertible", "differe CSWAP = { properties = [ "invertible", "differentiable" ] } CY = { properties = [ "invertible", "differentiable" ] } CZ = { properties = [ "invertible", "differentiable" ] } -DoubleExcitationMinus = { properties = [ "invertible", "differentiable" ] } -DoubleExcitationPlus = { properties = [ "invertible", "differentiable" ] } -DoubleExcitation = { properties = [ "invertible", "differentiable" ] } +DoubleExcitationMinus = { properties = [ "invertible", "controllable", "differentiable" ] } +DoubleExcitationPlus = { properties = [ "invertible", "controllable", "differentiable" ] } +DoubleExcitation = { properties = [ "invertible", "controllable", "differentiable" ] } GlobalPhase = { properties = [ "invertible", "controllable", "differentiable" ] } Hadamard = { properties = [ "invertible", "controllable", "differentiable" ] } Identity = { properties = [ "invertible", "differentiable" ] } -IsingXX = { properties = [ "invertible", "differentiable" ] } -IsingXY = { properties = [ "invertible", "differentiable" ] } -IsingYY = { properties = [ "invertible", "differentiable" ] } -IsingZZ = { properties = [ "invertible", "differentiable" ] } +IsingXX = { properties = [ "invertible", "controllable", "differentiable" ] } +IsingXY = { properties = [ "invertible", "controllable", "differentiable" ] } +IsingYY = { properties = [ "invertible", "controllable", "differentiable" ] } +IsingZZ = { properties = [ "invertible", "controllable", "differentiable" ] } MultiRZ = { properties = [ "invertible", "differentiable" ] } PauliX = { properties = [ "invertible", "controllable", "differentiable" ] } PauliY = { properties = [ "invertible", "controllable", "differentiable" ] } @@ -34,9 +34,9 @@ RX = { properties = [ "invertible", "controllable", "differe RY = { properties = [ "invertible", "controllable", "differentiable" ] } RZ = { properties = [ "invertible", "controllable", "differentiable" ] } SingleExcitationMinus = { properties = [ "invertible", "controllable", "differentiable" ] } -SingleExcitationPlus = { properties = [ "invertible", "differentiable" ] } -SingleExcitation = { properties = [ "invertible", "differentiable" ] } -S = { properties = [ "invertible", "differentiable" ] } +SingleExcitationPlus = { properties = [ "invertible", "controllable", "differentiable" ] } +SingleExcitation = { properties = [ "invertible", "controllable", "differentiable" ] } +S = { properties = [ "invertible", "controllable", "differentiable" ] } SWAP = { properties = [ "invertible", "controllable", "differentiable" ] } Toffoli = { properties = [ "invertible", "differentiable" ] } T = { properties = [ "invertible", "controllable", "differentiable" ] } diff --git a/tests/test_gates.py b/tests/test_gates.py index 8ae5302f58..1496a2fec1 100644 --- a/tests/test_gates.py +++ b/tests/test_gates.py @@ -487,8 +487,8 @@ def test_controlled_qubit_gates(operation, n_qubits, control_value, tol): dev = qml.device(device_name, wires=n_qubits) threshold = 5 if device_name == "lightning.tensor" else 250 num_wires = max(operation.num_wires, 1) - if device_name == "lightning.kokkos" and num_wires > 1: - pytest.skip("lightning.kokkos only supports single qubit controlled gates.") + if device_name == "lightning.kokkos" and isinstance(operation, qml.MultiRZ): + pytest.skip("lightning.kokkos does not support controlled-multiRZ") for n_wires in range(num_wires + 1, num_wires + 4): wire_lists = list(itertools.permutations(range(0, n_qubits), n_wires)) From 0acc148e5c517c2dbf9708206ce07a333cd022e0 Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Tue, 12 Nov 2024 18:21:15 +0000 Subject: [PATCH 10/53] Auto update version from '0.40.0-dev4' to '0.40.0-dev7' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 8432c4f824..c625ad3410 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev4" +__version__ = "0.40.0-dev7" From d7aecd724e4f87b5e4cece1f50dd9598ea815f0a Mon Sep 17 00:00:00 2001 From: Joseph Lee <40768758+josephleekl@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:36:10 -0500 Subject: [PATCH 11/53] (LK-C-3) Add controlled MultiRZ support to Lightning Kokkos (#954) ### Before submitting Please complete the following checklist when submitting a PR: - [ ] All new features must include a unit test. If you've fixed a bug or added code that should be tested, add a test to the [`tests`](../tests) directory! - [ ] All new functions and code must be clearly commented and documented. If you do make documentation changes, make sure that the docs build and render correctly by running `make docs`. - [ ] Ensure that the test suite passes, by running `make test`. - [ ] Add a new entry to the `.github/CHANGELOG.md` file, summarizing the change, and including a link back to the PR. - [ ] Ensure that code is properly formatted by running `make format`. When all the above are checked, delete everything above the dashed line and fill in the pull request template. ------------------------------------------------------------------------------------------------------------ **Context:** **Description of the Change:** This PR adds support for controlled multiRZ for Lightning Kokkos. This is defined in `BasicGateFunctor.hpp`, and is applied through `applyNCNFunctor` defined in the same file. **Benefits:** Performance benchmarks for gates are shown here: https://www.notion.so/xanaduai/Lightning-Kokkos-Native-Controlled-Operation-Gate-Benchmarks-12ebc6bd17648017a2dcd237748b24fe **Possible Drawbacks:** **Related GitHub Issues:** [sc-76775] --- .../gates/BasicGateFunctors.hpp | 86 +++++++++++++++++++ .../tests/Test_StateVectorKokkos_Param.cpp | 70 +++++++++++++++ .../lightning_kokkos/_state_vector.py | 2 +- .../lightning_kokkos/lightning_kokkos.py | 1 + .../lightning_kokkos/lightning_kokkos.toml | 2 +- tests/test_gates.py | 2 - 6 files changed, 159 insertions(+), 4 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp index ae9996defb..9594a2e816 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp @@ -36,6 +36,53 @@ using Pennylane::LightningKokkos::Util::vector2view; /// @endcond namespace Pennylane::LightningKokkos::Functors { +template class applyNCNFunctor { + using KokkosComplexVector = Kokkos::View *>; + using KokkosIntVector = Kokkos::View; + using MemberType = Kokkos::TeamPolicy<>::member_type; + + Kokkos::View *> arr; + const FuncT core_function; + KokkosIntVector indices; + KokkosIntVector parity; + KokkosIntVector rev_wires; + KokkosIntVector rev_wire_shifts; + std::size_t dim; + + public: + template + applyNCNFunctor([[maybe_unused]] ExecutionSpace exec, + Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, FuncT core_function_) + : arr(arr_), core_function(core_function_) { + + std::size_t two2N = + std::exp2(num_qubits - wires.size() - controlled_wires.size()); + dim = std::exp2(wires.size()); + const auto &[parity_, rev_wires_] = + reverseWires(num_qubits, wires, controlled_wires); + parity = parity_; + std::vector indices_ = + generateBitPatterns(wires, num_qubits); + ControlBitPatterns(indices_, num_qubits, controlled_wires, + controlled_values); + indices = vector2view(indices_); + Kokkos::parallel_for(Kokkos::TeamPolicy(two2N, Kokkos::AUTO, dim), + *this); + } + // TODO: Runtime selection for copying indices to scratch level 0/shmem + KOKKOS_FUNCTION void operator()(const MemberType &teamMember) const { + const std::size_t k = teamMember.league_rank(); + const std::size_t offset = Util::parity_2_offset(parity, k); + Kokkos::parallel_for(Kokkos::TeamThreadRange(teamMember, dim), + [&](const std::size_t i) { + core_function(arr, i, indices, offset); + }); + } +}; template class applyNC1Functor {}; @@ -1714,6 +1761,40 @@ void applyMultiRZ(Kokkos::View *> arr_, }); } +template +void applyNCMultiRZ(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + const PrecisionT &angle = params[0]; + const Kokkos::complex shift_0 = Kokkos::complex{ + std::cos(angle / 2), + (inverse) ? std::sin(angle / 2) : -std::sin(angle / 2)}; + const Kokkos::complex shift_1 = Kokkos::conj(shift_0); + std::size_t wires_parity = 0U; + wires_parity = + std::accumulate(wires.begin(), wires.end(), std::size_t{0}, + [num_qubits](std::size_t acc, std::size_t wire) { + return acc | (static_cast(1U) + << (num_qubits - wire - 1)); + }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i, + Kokkos::View indices, std::size_t offset) { + const std::size_t index = indices(i); + arr(index + offset) *= + (Kokkos::Impl::bit_count((index + offset) & wires_parity) % 2 == 0) + ? shift_0 + : shift_1; + }; + + applyNCNFunctor(ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); +} + template void applyPauliRot(Kokkos::View *> arr_, const std::size_t num_qubits, @@ -2009,6 +2090,11 @@ void applyNCNamedOperation(const ControlledGateOperation gateop, controlled_values, wires, inverse, params); return; + case ControlledGateOperation::MultiRZ: + applyNCMultiRZ(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, + params); + return; default: PL_ABORT("Controlled gate operation does not exist."); } diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp index 5af07e784d..75336510dc 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp @@ -958,6 +958,76 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation param " CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); } } + + SECTION("2-controlled MultiRZ - c{0,2}") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 2}; + const std::vector control_values = {true, false}; + const std::vector target_wire = {1, 3}; + const TestType param = 0.234; + sv_gate.applyOperation("MultiRZ", control_wires, control_values, + target_wire, inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{// Generated using Pennylane + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.24829083, -0.02918331}, + ComplexT{0.24829083, +0.02918331}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.24829083, +0.02918331}, + ComplexT{0.24829083, -0.02918331}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}}; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } + + SECTION("3-controlled MultiRZ - c{0,1,2}") { + Kokkos::deep_copy(sv_gate.getView(), ini_sv); + + const std::vector control_wires = {0, 1, 2}; + const std::vector control_values = {false, true, false}; + const std::vector target_wire = {3}; + const TestType param = 0.234; + sv_gate.applyOperation("MultiRZ", control_wires, control_values, + target_wire, inverse, {param}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + + std::vector expected_result{// Generated using Pennylane + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.24829083, -0.02918331}, + ComplexT{0.24829083, +0.02918331}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}, + ComplexT{0.25, 0.0}}; + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_gate_host[j]) == Approx(imag(expected_result[j]))); + CHECK(real(sv_gate_host[j]) == Approx(real(expected_result[j]))); + } + } } TEMPLATE_TEST_CASE( diff --git a/pennylane_lightning/lightning_kokkos/_state_vector.py b/pennylane_lightning/lightning_kokkos/_state_vector.py index 23fb1d626f..9bfd1fa920 100644 --- a/pennylane_lightning/lightning_kokkos/_state_vector.py +++ b/pennylane_lightning/lightning_kokkos/_state_vector.py @@ -285,7 +285,7 @@ def _apply_lightning( param = operation.parameters method(wires, invert_param, param) elif isinstance(operation, qml.ops.Controlled) and not isinstance( - operation.base, (qml.QubitUnitary, qml.BlockEncode, qml.MultiRZ) + operation.base, (qml.QubitUnitary, qml.BlockEncode) ): # apply n-controlled gate # Kokkos does not support controlled gates except for GlobalPhase and single-qubit self._apply_lightning_controlled(operation) diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 3f3c59d4e2..532202d015 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -128,6 +128,7 @@ "C(DoubleExcitation)", "C(DoubleExcitationMinus)", "C(DoubleExcitationPlus)", + "C(MultiRZ)", "C(GlobalPhase)", "CRot", "IsingXX", diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml b/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml index db6b22ab7b..fd4dafda3e 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml @@ -23,7 +23,7 @@ IsingXX = { properties = [ "invertible", "controllable", "differe IsingXY = { properties = [ "invertible", "controllable", "differentiable" ] } IsingYY = { properties = [ "invertible", "controllable", "differentiable" ] } IsingZZ = { properties = [ "invertible", "controllable", "differentiable" ] } -MultiRZ = { properties = [ "invertible", "differentiable" ] } +MultiRZ = { properties = [ "invertible", "controllable", "differentiable" ] } PauliX = { properties = [ "invertible", "controllable", "differentiable" ] } PauliY = { properties = [ "invertible", "controllable", "differentiable" ] } PauliZ = { properties = [ "invertible", "controllable", "differentiable" ] } diff --git a/tests/test_gates.py b/tests/test_gates.py index 1496a2fec1..747bd243bf 100644 --- a/tests/test_gates.py +++ b/tests/test_gates.py @@ -487,8 +487,6 @@ def test_controlled_qubit_gates(operation, n_qubits, control_value, tol): dev = qml.device(device_name, wires=n_qubits) threshold = 5 if device_name == "lightning.tensor" else 250 num_wires = max(operation.num_wires, 1) - if device_name == "lightning.kokkos" and isinstance(operation, qml.MultiRZ): - pytest.skip("lightning.kokkos does not support controlled-multiRZ") for n_wires in range(num_wires + 1, num_wires + 4): wire_lists = list(itertools.permutations(range(0, n_qubits), n_wires)) From 7ea92963d98f59457314e3c631304a7adc09e8b5 Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Wed, 13 Nov 2024 21:47:42 +0000 Subject: [PATCH 12/53] Auto update version from '0.40.0-dev7' to '0.40.0-dev9' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index c625ad3410..8fa6ae201f 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev7" +__version__ = "0.40.0-dev9" From eb2ac82a7a462cf9a3e69e8af469b9a278a28355 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Fri, 15 Nov 2024 21:23:24 +0000 Subject: [PATCH 13/53] format --- .../simulators/lightning_kokkos/gates/BasicGateFunctors.hpp | 5 ----- .../gates/tests/Test_StateVectorKokkos_NonParam.cpp | 3 --- pennylane_lightning/lightning_kokkos/lightning_kokkos.py | 1 - 3 files changed, 9 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp index 9594a2e816..a1b1e2eeb7 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp @@ -58,7 +58,6 @@ template class applyNCNFunctor { const std::vector &controlled_values, const std::vector &wires, FuncT core_function_) : arr(arr_), core_function(core_function_) { - std::size_t two2N = std::exp2(num_qubits - wires.size() - controlled_wires.size()); dim = std::exp2(wires.size()); @@ -902,7 +901,6 @@ void applyCRZ(Kokkos::View *> arr_, const std::size_t i01, const std::size_t i10, const std::size_t i11) { [[maybe_unused]] const auto i00_ = i00; [[maybe_unused]] const auto i01_ = i01; - arr(i10) *= shift_0; arr(i11) *= shift_1; }; @@ -947,7 +945,6 @@ void applyNCIsingXX(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = @@ -1042,7 +1039,6 @@ void applyNCIsingYY(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = @@ -1091,7 +1087,6 @@ void applyNCIsingZZ(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; const Kokkos::complex shift_0 = Kokkos::complex{ std::cos(angle / 2), diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp index af9e318659..a949ae1523 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp @@ -803,7 +803,6 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyMultiQubitOp", TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation Controlled", "[StateVectorKokkos_Nonparam]", float, double) { - using StateVectorT = StateVectorKokkos; using PrecisionT = StateVectorT::PrecisionT; const std::size_t num_qubits = 3; @@ -833,7 +832,6 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " StateVectorKokkos sv_control{num_qubits}; SECTION("N-controlled PauliX ") { - if (control == wire) { Kokkos::deep_copy(sv_control.getView(), ini_sv); @@ -1099,7 +1097,6 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " SECTION("N-controlled SWAP") { if (control != wire0 && control != wire1 && wire0 != wire1) { - StateVectorT kokkos_sv0{ini_st.data(), ini_st.size()}; StateVectorT kokkos_sv1{ini_st.data(), ini_st.size()}; kokkos_sv0.applyOperation("CSWAP", {control, wire0, wire1}, diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 57705df1de..7d592ac352 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -76,7 +76,6 @@ "PauliZ", "MultiRZ", "GlobalPhase", - "C(GlobalPhase)", "Hadamard", "S", "Adjoint(S)", From 8e578b14de9e2ca9993172ecfa26c89e6b2bf36f Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Fri, 15 Nov 2024 21:27:34 +0000 Subject: [PATCH 14/53] fix code factor --- .../src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp index a1b1e2eeb7..4f97c62d22 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp @@ -130,7 +130,6 @@ class applyNC1Functor { template class applyNC1Functor { - Kokkos::View *> arr; const FuncT core_function; const std::size_t rev_wire; From 7b5713ec15708e4c0917542da7331fc6c8c1dd61 Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Fri, 15 Nov 2024 21:29:33 +0000 Subject: [PATCH 15/53] Auto update version from '0.40.0-dev9' to '0.40.0-dev10' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 8fa6ae201f..8704f0fbf1 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev9" +__version__ = "0.40.0-dev10" From 5ab8064ae398c8bd6534c994f0ae6de91c4f5e35 Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Tue, 19 Nov 2024 18:50:50 +0000 Subject: [PATCH 16/53] Auto update version from '0.40.0-dev11' to '0.40.0-dev12' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 1af4b208f4..8637df0642 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev11" +__version__ = "0.40.0-dev12" From bde61072f78f2fd59d77da52dbcc6affc383f25d Mon Sep 17 00:00:00 2001 From: Joseph Lee <40768758+josephleekl@users.noreply.github.com> Date: Tue, 19 Nov 2024 15:11:44 -0500 Subject: [PATCH 17/53] (LK-C-4) Add controlled QubitUnitary support to Lightning Kokkos (#955) ### Before submitting Please complete the following checklist when submitting a PR: - [ ] All new features must include a unit test. If you've fixed a bug or added code that should be tested, add a test to the [`tests`](../tests) directory! - [ ] All new functions and code must be clearly commented and documented. If you do make documentation changes, make sure that the docs build and render correctly by running `make docs`. - [ ] Ensure that the test suite passes, by running `make test`. - [ ] Add a new entry to the `.github/CHANGELOG.md` file, summarizing the change, and including a link back to the PR. - [ ] Ensure that code is properly formatted by running `make format`. When all the above are checked, delete everything above the dashed line and fill in the pull request template. ------------------------------------------------------------------------------------------------------------ **Context:** **Description of the Change:** This PR adds support for controlled QubitUnitary for Lightning Kokkos. There are specialized implementations for 1/2/3-qubits (`applyNC1/2/3QubitOpFunctor`) and a general case implementation (`NCMultiQubitOpFunctor`). These functors are defined in `MatrixGateFunctors.hpp`. These are called by `applyControlledMatrix` or `applyOperation`/`applyNCMultiQubitOp` in `StateVectorKokkos.hpp` **Benefits:** Performance benchmarks for gates are shown here: https://www.notion.so/xanaduai/Lightning-Kokkos-Native-Controlled-Operation-Gate-Benchmarks-12ebc6bd17648017a2dcd237748b24fe **Possible Drawbacks:** **Related GitHub Issues:** [sc-76776] --- .../lightning_kokkos/StateVectorKokkos.hpp | 196 +++++- .../bindings/LKokkosBindings.hpp | 23 +- .../gates/BasicGateFunctors.hpp | 593 ++++++++---------- .../gates/BasicGeneratorFunctors.hpp | 199 +++--- .../gates/MatrixGateFunctors.hpp | 281 ++++++++- .../tests/Test_StateVectorKokkos_NonParam.cpp | 372 ++++++++++- .../tests/Test_StateVectorKokkos_Param.cpp | 164 +++++ .../measurements/MeasurementsKokkos.hpp | 6 +- .../tests/Test_StateVectorLKokkos.cpp | 181 +++++- .../lightning_kokkos/_state_vector.py | 18 +- .../lightning_kokkos/lightning_kokkos.py | 1 + .../lightning_kokkos/lightning_kokkos.toml | 2 +- .../test_measurements_class.py | 4 - tests/test_gates.py | 12 - 14 files changed, 1540 insertions(+), 512 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp index 0f99da5b47..0e99918203 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp @@ -134,11 +134,11 @@ class StateVectorKokkos final * * @param index Index of the target element. */ - void setBasisState(const std::size_t index) { + void setBasisState(std::size_t index) { KokkosVector sv_view = getView(); // circumvent error capturing this with KOKKOS_LAMBDA Kokkos::parallel_for( - sv_view.size(), KOKKOS_LAMBDA(const std::size_t i) { + sv_view.size(), KOKKOS_LAMBDA(std::size_t i) { sv_view(i) = (i == index) ? ComplexT{1.0, 0.0} : ComplexT{0.0, 0.0}; }); @@ -197,7 +197,7 @@ class StateVectorKokkos final KokkosVector sv_view = getView(); // circumvent error capturing this with KOKKOS_LAMBDA Kokkos::parallel_for( - indices.size(), KOKKOS_LAMBDA(const std::size_t i) { + indices.size(), KOKKOS_LAMBDA(std::size_t i) { sv_view(d_indices[i]) = d_values[i]; }); } @@ -237,7 +237,7 @@ class StateVectorKokkos final auto d_wires = vector2view(wires); initZeros(); Kokkos::parallel_for( - num_state, KOKKOS_LAMBDA(const std::size_t i) { + num_state, KOKKOS_LAMBDA(std::size_t i) { std::size_t index{0U}; for (std::size_t w = 0; w < d_wires.size(); w++) { const std::size_t bit = (i & (one << w)) >> w; @@ -367,8 +367,7 @@ class StateVectorKokkos final * @param params Rotation angle. * @param word A Pauli word (e.g. "XYYX"). */ - void applyPauliRot(const std::vector &wires, - const bool inverse, + void applyPauliRot(const std::vector &wires, bool inverse, const std::vector ¶ms, const std::string &word) { PL_ABORT_IF_NOT(wires.size() == word.size(), @@ -384,8 +383,9 @@ class StateVectorKokkos final auto two2N = BaseType::getLength(); auto dataview = getView(); Kokkos::parallel_for( - two2N, KOKKOS_LAMBDA(const std::size_t i) { - dataview(i) *= (inverse) ? conj(diagonal_(i)) : diagonal_(i); + two2N, KOKKOS_LAMBDA(std::size_t i) { + dataview(i) *= + (inverse) ? Kokkos::conj(diagonal_(i)) : diagonal_(i); }); } @@ -399,17 +399,17 @@ class StateVectorKokkos final void applyMultiQubitOp(const KokkosVector matrix, const std::vector &wires, bool inverse = false) { - auto &&num_qubits = this->getNumQubits(); - std::size_t two2N = std::exp2(num_qubits - wires.size()); - std::size_t dim = std::exp2(wires.size()); + const std::size_t num_qubits = this->getNumQubits(); + const std::size_t two2N = exp2(num_qubits - wires.size()); + const std::size_t dim = exp2(wires.size()); KokkosVector matrix_trans("matrix_trans", matrix.size()); if (inverse) { Kokkos::MDRangePolicy policy_2d({0, 0}, {dim, dim}); Kokkos::parallel_for( - policy_2d, - KOKKOS_LAMBDA(const std::size_t i, const std::size_t j) { - matrix_trans(i + j * dim) = conj(matrix(i * dim + j)); + policy_2d, KOKKOS_LAMBDA(std::size_t i, std::size_t j) { + matrix_trans(i + j * dim) = + Kokkos::conj(matrix(i * dim + j)); }); } else { matrix_trans = matrix; @@ -436,12 +436,13 @@ class StateVectorKokkos final matrix_trans, wires)); break; default: + // TODO: explore runtime determine scratch space level (L0 vs L1) std::size_t scratch_size = ScratchViewComplex::shmem_size(dim) + ScratchViewSizeT::shmem_size(dim); Kokkos::parallel_for( "multiQubitOpFunctor", TeamPolicy(two2N, Kokkos::AUTO, dim) - .set_scratch_size(0, Kokkos::PerTeam(scratch_size)), + .set_scratch_size(1, Kokkos::PerTeam(scratch_size)), multiQubitOpFunctor(*data_, num_qubits, matrix_trans, wires)); break; @@ -472,20 +473,93 @@ class StateVectorKokkos final PL_ABORT_IF_NOT(controlled_wires.size() == controlled_values.size(), "`controlled_wires` must have the same size as " "`controlled_values`."); - PL_ABORT_IF_NOT( - array_contains(controlled_gate_names, std::string_view{opName}), - "Controlled matrix operation not yet supported."); if (controlled_wires.empty()) { return applyOperation(opName, wires, inverse, params, gate_matrix); } + if (array_contains(controlled_gate_names, std::string_view{opName})) { + const std::size_t num_qubits = this->getNumQubits(); + const ControlledGateOperation gateop = + reverse_lookup(controlled_gate_names, std::string_view{opName}); + applyNCNamedOperation( + gateop, *data_, num_qubits, controlled_wires, controlled_values, + wires, inverse, params); + } else { + PL_ABORT_IF(gate_matrix.empty(), + std::string("Operation does not exist for ") + opName + + std::string(" and no matrix provided.")); + return applyNCMultiQubitOp(vector2view(gate_matrix), + controlled_wires, controlled_values, + wires, inverse); + } + } + + /** + * @brief Apply a controlled-multi qubit operator to the state vector using + * a matrix + * + * @param matrix Kokkos gate matrix in the device space. + * @param controlled_wires Control wires. + * @param controlled_values Control values (true or false). + * @param wires Wires to apply gate to. + * @param inverse Indicates whether to use adjoint of gate. (Default to + * false) + */ + void applyNCMultiQubitOp(const KokkosVector matrix, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + bool inverse = false) { const std::size_t num_qubits = this->getNumQubits(); - const ControlledGateOperation gateop = - reverse_lookup(controlled_gate_names, std::string_view{opName}); - applyNCNamedOperation( - gateop, *data_, num_qubits, controlled_wires, controlled_values, - wires, inverse, params); + const std::size_t two2N = + exp2(num_qubits - wires.size() - controlled_wires.size()); + const std::size_t dim = exp2(wires.size()); + KokkosVector matrix_trans("matrix_trans", matrix.size()); + + if (inverse) { + Kokkos::MDRangePolicy policy_2d({0, 0}, {dim, dim}); + Kokkos::parallel_for( + policy_2d, KOKKOS_LAMBDA(std::size_t i, std::size_t j) { + matrix_trans(i + j * dim) = + Kokkos::conj(matrix(i * dim + j)); + }); + } else { + matrix_trans = matrix; + } + + switch (wires.size()) { + case 1: + Kokkos::parallel_for(two2N, applyNC1QubitOpFunctor( + *data_, num_qubits, matrix_trans, + controlled_wires, controlled_values, + wires)); + break; + case 2: + Kokkos::parallel_for(two2N, applyNC2QubitOpFunctor( + *data_, num_qubits, matrix_trans, + controlled_wires, controlled_values, + wires)); + break; + case 3: + Kokkos::parallel_for(two2N, applyNC3QubitOpFunctor( + *data_, num_qubits, matrix_trans, + controlled_wires, controlled_values, + wires)); + break; + default: + // TODO: explore runtime determine scratch space level (L0 vs L1) + std::size_t scratch_size = ScratchViewComplex::shmem_size(dim) + + ScratchViewSizeT::shmem_size(dim); + Kokkos::parallel_for( + "multiNCQubitOpFunctor", + TeamPolicy(two2N, Kokkos::AUTO, dim) + .set_scratch_size(1, Kokkos::PerTeam(scratch_size)), + NCMultiQubitOpFunctor( + *data_, num_qubits, matrix_trans, controlled_wires, + controlled_values, wires)); + break; + } } /** @@ -500,8 +574,8 @@ class StateVectorKokkos final const std::vector &wires, bool inverse = false) { PL_ABORT_IF(wires.empty(), "Number of wires must be larger than 0"); - std::size_t n = static_cast(1U) << wires.size(); - KokkosVector matrix_(matrix, n * n); + const std::size_t n2 = exp2(wires.size() * 2); + KokkosVector matrix_(matrix, n2); applyMultiQubitOp(matrix_, wires, inverse); } @@ -517,8 +591,7 @@ class StateVectorKokkos final const std::vector &wires, bool inverse = false) { PL_ABORT_IF(wires.empty(), "Number of wires must be larger than 0"); - std::size_t n = static_cast(1U) << wires.size(); - std::size_t n2 = n * n; + const std::size_t n2 = exp2(wires.size() * 2); KokkosVector matrix_("matrix_", n2); Kokkos::deep_copy(matrix_, UnmanagedConstComplexHostView(matrix, n2)); applyMultiQubitOp(matrix_, wires, inverse); @@ -541,6 +614,75 @@ class StateVectorKokkos final applyMatrix(matrix.data(), wires, inverse); } + /** + * @brief Apply a given matrix for controlled operations directly to the + * statevector using a raw matrix pointer vector. + * + * @param matrix Pointer to the array data (in row-major format). + * @param controlled_wires Controlled wires + * @param controlled_values Controlled values (true or false) + * @param wires Wires to apply gate to. + * @param inverse Indicate whether inverse should be taken. + */ + inline void applyControlledMatrix( + ComplexT *matrix, const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, bool inverse = false) { + PL_ABORT_IF(wires.empty(), "Number of wires must be larger than 0"); + const std::size_t n2 = exp2(wires.size() * 2); + KokkosVector matrix_(matrix, n2); + applyNCMultiQubitOp(matrix_, controlled_wires, controlled_values, wires, + inverse); + } + + /** + * @brief Apply a given matrix directly to the statevector using a + * raw matrix pointer vector. + * + * @param matrix Pointer to the array data (in row-major format). + * @param controlled_wires Controlled wires + * @param controlled_values Controlled values (true or false) + * @param wires Wires to apply gate to. + * @param inverse Indicate whether inverse should be taken. + */ + inline void + applyControlledMatrix(const ComplexT *matrix, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + bool inverse = false) { + PL_ABORT_IF(wires.empty(), "Number of wires must be larger than 0"); + const std::size_t n2 = exp2(wires.size() * 2); + KokkosVector matrix_("matrix_", n2); + Kokkos::deep_copy(matrix_, UnmanagedConstComplexHostView(matrix, n2)); + applyNCMultiQubitOp(matrix_, controlled_wires, controlled_values, wires, + inverse); + } + + /** + * @brief Apply a given controlled-matrix directly to the statevector. + * + * @param matrix Vector containing the statevector data (in row-major + * format). + * @param controlled_wires Control wires. + * @param controlled_values Control values (false or true). + * @param wires Wires to apply gate to. + * @param inverse Indicate whether inverse should be taken. + */ + inline void + applyControlledMatrix(const std::vector &matrix, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + bool inverse = false) { + PL_ABORT_IF(wires.empty(), "Number of wires must be larger than 0"); + PL_ABORT_IF(matrix.size() != exp2(2 * wires.size()), + "The size of matrix does not match with the given " + "number of wires"); + applyControlledMatrix(matrix.data(), controlled_wires, + controlled_values, wires, inverse); + } + /** * @brief Apply a single generator to the state vector using the given * kernel. diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/bindings/LKokkosBindings.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/bindings/LKokkosBindings.hpp index a77df459c6..e39320a950 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/bindings/LKokkosBindings.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/bindings/LKokkosBindings.hpp @@ -47,7 +47,26 @@ namespace Pennylane::LightningKokkos { using StateVectorBackends = Pennylane::Util::TypeList, StateVectorKokkos, void>; +/** + * @brief Register controlled matrix kernel. + */ +template +void applyControlledMatrix( + StateVectorT &st, + const py::array_t, + py::array::c_style | py::array::forcecast> &matrix, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, bool inverse = false) { + using ComplexT = typename StateVectorT::ComplexT; + st.applyControlledMatrix( + static_cast(matrix.request().ptr), controlled_wires, + controlled_values, wires, inverse); +} +/** + * @brief Register controlled gates. + */ template void registerControlledGate(PyClass &pyclass) { using ParamT = typename StateVectorT::PrecisionT; @@ -170,7 +189,9 @@ void registerBackendClassSpecificBindings(PyClass &pyclass) { .def("collapse", &StateVectorT::collapse, "Collapse the statevector onto the 0 or 1 branch of a given wire.") .def("normalize", &StateVectorT::normalize, - "Normalize the statevector to norm 1."); + "Normalize the statevector to norm 1.") + .def("applyControlledMatrix", &applyControlledMatrix, + "Apply controlled operation"); } /** diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp index 4f97c62d22..f98cbaf00f 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp @@ -59,8 +59,8 @@ template class applyNCNFunctor { const std::vector &wires, FuncT core_function_) : arr(arr_), core_function(core_function_) { std::size_t two2N = - std::exp2(num_qubits - wires.size() - controlled_wires.size()); - dim = std::exp2(wires.size()); + exp2(num_qubits - wires.size() - controlled_wires.size()); + dim = exp2(wires.size()); const auto &[parity_, rev_wires_] = reverseWires(num_qubits, wires, controlled_wires); parity = parity_; @@ -76,10 +76,9 @@ template class applyNCNFunctor { KOKKOS_FUNCTION void operator()(const MemberType &teamMember) const { const std::size_t k = teamMember.league_rank(); const std::size_t offset = Util::parity_2_offset(parity, k); - Kokkos::parallel_for(Kokkos::TeamThreadRange(teamMember, dim), - [&](const std::size_t i) { - core_function(arr, i, indices, offset); - }); + Kokkos::parallel_for( + Kokkos::TeamThreadRange(teamMember, dim), + [&](std::size_t i) { core_function(arr, i, indices, offset); }); } }; @@ -119,10 +118,10 @@ class applyNC1Functor { 0, exp2(num_qubits - controlled_wires.size() - wires.size())), *this); } - KOKKOS_FUNCTION void operator()(const std::size_t k) const { + KOKKOS_FUNCTION void operator()(std::size_t k) const { const std::size_t offset = Util::parity_2_offset(parity, k); - const std::size_t i0 = indices(0B00); - const std::size_t i1 = indices(0B01); + std::size_t i0 = indices(0B00); + std::size_t i1 = indices(0B01); core_function(arr, i0 + offset, i1 + offset); } @@ -152,26 +151,24 @@ class applyNC1Functor { Kokkos::RangePolicy(0, exp2(num_qubits - 1)), *this); } - KOKKOS_FUNCTION void operator()(const std::size_t k) const { - const std::size_t i0 = - ((k << 1U) & wire_parity_inv) | (wire_parity & k); - const std::size_t i1 = i0 | rev_wire_shift; + KOKKOS_FUNCTION void operator()(std::size_t k) const { + std::size_t i0 = ((k << 1U) & wire_parity_inv) | (wire_parity & k); + std::size_t i1 = i0 | rev_wire_shift; core_function(arr, i0, i1); } }; template void applyNCPauliX( - Kokkos::View *> arr_, - const std::size_t num_qubits, + Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0, const std::size_t i1) { + std::size_t i0, std::size_t i1) { kokkos_swap(arr(i0), arr(i1)); }; if (controlled_wires.empty()) { @@ -186,9 +183,8 @@ void applyNCPauliX( template void applyPauliX(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { applyNCPauliX(arr_, num_qubits, {}, {}, wires, inverse); @@ -196,16 +192,15 @@ void applyPauliX(Kokkos::View *> arr_, template void applyNCPauliY( - Kokkos::View *> arr_, - const std::size_t num_qubits, + Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0, const std::size_t i1) { + std::size_t i0, std::size_t i1) { const auto v0 = arr(i0); const auto v1 = arr(i1); arr(i0) = Kokkos::complex{imag(v1), -real(v1)}; @@ -223,9 +218,8 @@ void applyNCPauliY( template void applyPauliY(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { applyNCPauliY(arr_, num_qubits, {}, {}, wires, inverse); @@ -233,16 +227,15 @@ void applyPauliY(Kokkos::View *> arr_, template void applyNCPauliZ( - Kokkos::View *> arr_, - const std::size_t num_qubits, + Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0, const std::size_t i1) { + std::size_t i0, std::size_t i1) { [[maybe_unused]] const auto i0_ = i0; // Note: this is to avoid Clang complain [[maybe_unused]] // attribute for lambda function arguments @@ -260,9 +253,8 @@ void applyNCPauliZ( template void applyPauliZ(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { applyNCPauliZ(arr_, num_qubits, {}, {}, wires, inverse); @@ -270,16 +262,15 @@ void applyPauliZ(Kokkos::View *> arr_, template void applyNCHadamard( - Kokkos::View *> arr_, - const std::size_t num_qubits, + Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0, const std::size_t i1) { + std::size_t i0, std::size_t i1) { const Kokkos::complex v0 = arr(i0); const Kokkos::complex v1 = arr(i1); arr(i0) = M_SQRT1_2 * (v0 + v1); // M_SQRT1_2 * v0 + M_SQRT1_2 * v1 @@ -297,9 +288,8 @@ void applyNCHadamard( template void applyHadamard( - Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - const bool inverse = false, + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &wires, bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { applyNCHadamard(arr_, num_qubits, {}, {}, wires, inverse); @@ -307,17 +297,17 @@ void applyHadamard( template void applyNCS(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, - const std::vector &wires, const bool inverse = false, + const std::vector &wires, bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { const Kokkos::complex shift = (inverse) ? Kokkos::complex{0.0, -1.0} : Kokkos::complex{0.0, 1.0}; auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0, const std::size_t i1) { + std::size_t i0, std::size_t i1) { [[maybe_unused]] const auto i0_ = i0; arr(i1) *= shift; }; @@ -333,8 +323,8 @@ void applyNCS(Kokkos::View *> arr_, template void applyS(Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { applyNCS(arr_, num_qubits, {}, {}, wires, inverse); @@ -342,10 +332,10 @@ void applyS(Kokkos::View *> arr_, template void applyNCT(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, - const std::vector &wires, const bool inverse = false, + const std::vector &wires, bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { const Kokkos::complex shift = (inverse) ? Kokkos::conj(Kokkos::exp(Kokkos::complex( @@ -354,7 +344,7 @@ void applyNCT(Kokkos::View *> arr_, 0, static_cast(M_PI_4))); auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0, const std::size_t i1) { + std::size_t i0, std::size_t i1) { [[maybe_unused]] const auto i0_ = i0; arr(i1) *= shift; }; @@ -370,8 +360,8 @@ void applyNCT(Kokkos::View *> arr_, template void applyT(Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { applyNCT(arr_, num_qubits, {}, {}, wires, inverse); @@ -379,11 +369,11 @@ void applyT(Kokkos::View *> arr_, template void applyNCPhaseShift(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, const std::vector &wires, - const bool inverse = false, + bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const Kokkos::complex shift = @@ -391,7 +381,7 @@ void applyNCPhaseShift(Kokkos::View *> arr_, : Kokkos::exp(Kokkos::complex(0, angle)); auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0, const std::size_t i1) { + std::size_t i0, std::size_t i1) { [[maybe_unused]] const auto i0_ = i0; arr(i1) *= shift; }; @@ -407,9 +397,9 @@ void applyNCPhaseShift(Kokkos::View *> arr_, template void applyPhaseShift(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &wires, - const bool inverse = false, + bool inverse = false, const std::vector ¶ms = {}) { applyNCPhaseShift(arr_, num_qubits, {}, {}, wires, inverse, params); @@ -417,11 +407,10 @@ void applyPhaseShift(Kokkos::View *> arr_, template void applyNCRX(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, - const std::vector &wires, - const bool inverse = false, + const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT c = std::cos(angle * static_cast(0.5)); @@ -430,7 +419,7 @@ void applyNCRX(Kokkos::View *> arr_, : std::sin(-angle * static_cast(0.5)); auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0, const std::size_t i1) { + std::size_t i0, std::size_t i1) { const auto v0 = arr(i0); const auto v1 = arr(i1); arr(i0) = @@ -450,20 +439,18 @@ void applyNCRX(Kokkos::View *> arr_, template void applyRX(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, const bool inverse = false, - const std::vector ¶ms = {}) { + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, const std::vector ¶ms = {}) { applyNCRX(arr_, num_qubits, {}, {}, wires, inverse, params); } template void applyNCRY(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, - const std::vector &wires, - const bool inverse = false, + const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT c = std::cos(angle * static_cast(0.5)); @@ -472,7 +459,7 @@ void applyNCRY(Kokkos::View *> arr_, : std::sin(angle * static_cast(0.5)); auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0, const std::size_t i1) { + std::size_t i0, std::size_t i1) { const auto v0 = arr(i0); const auto v1 = arr(i1); arr(i0) = Kokkos::complex{c * real(v0) - s * real(v1), @@ -492,20 +479,18 @@ void applyNCRY(Kokkos::View *> arr_, template void applyRY(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, const bool inverse = false, - const std::vector ¶ms = {}) { + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, const std::vector ¶ms = {}) { applyNCRY(arr_, num_qubits, {}, {}, wires, inverse, params); } template void applyNCRZ(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, - const std::vector &wires, - const bool inverse = false, + const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cos_angle = std::cos(angle * static_cast(0.5)); @@ -515,7 +500,7 @@ void applyNCRZ(Kokkos::View *> arr_, const Kokkos::complex shift_1 = Kokkos::conj(shift_0); auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0, const std::size_t i1) { + std::size_t i0, std::size_t i1) { arr(i0) *= shift_0; arr(i1) *= shift_1; }; @@ -531,20 +516,18 @@ void applyNCRZ(Kokkos::View *> arr_, template void applyRZ(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, const bool inverse = false, - const std::vector ¶ms = {}) { + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, const std::vector ¶ms = {}) { applyNCRZ(arr_, num_qubits, {}, {}, wires, inverse, params); } template void applyNCRot(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, - const std::vector &wires, - const bool inverse = false, + const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT phi = (inverse) ? -params[2] : params[0]; const PrecisionT theta = (inverse) ? -params[1] : params[1]; @@ -557,7 +540,7 @@ void applyNCRot(Kokkos::View *> arr_, const Kokkos::complex mat_0b11 = mat[0b11]; auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0, const std::size_t i1) { + std::size_t i0, std::size_t i1) { const Kokkos::complex v0 = arr(i0); const Kokkos::complex v1 = arr(i1); arr(i0) = mat_0b00 * v0 + mat_0b01 * v1; @@ -575,8 +558,8 @@ void applyNCRot(Kokkos::View *> arr_, template void applyRot(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, const std::vector ¶ms = {}) { applyNCRot(arr_, num_qubits, {}, {}, wires, inverse, params); @@ -584,17 +567,17 @@ void applyRot(Kokkos::View *> arr_, template void applyNCGlobalPhase(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, [[maybe_unused]] const std::vector &wires, - const bool inverse = false, + bool inverse = false, const std::vector ¶ms = {}) { const Kokkos::complex phase = Kokkos::exp( Kokkos::complex{0, (inverse) ? params[0] : -params[0]}); auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0, const std::size_t i1) { + std::size_t i0, std::size_t i1) { arr(i1) *= phase; arr(i0) *= phase; }; @@ -620,9 +603,9 @@ void applyNCGlobalPhase(Kokkos::View *> arr_, template void applyGlobalPhase(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, [[maybe_unused]] const std::vector &wires, - const bool inverse = false, + bool inverse = false, const std::vector ¶ms = {}) { applyNCGlobalPhase(arr_, num_qubits, {}, {}, wires, inverse, params); @@ -646,7 +629,7 @@ class applyNC2Functor { template applyNC2Functor([[maybe_unused]] ExecutionSpace exec, Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, const std::vector &wires, FuncT core_function_) @@ -664,12 +647,12 @@ class applyNC2Functor { 0, exp2(num_qubits - controlled_wires.size() - wires.size())), *this); } - KOKKOS_FUNCTION void operator()(const std::size_t k) const { + KOKKOS_FUNCTION void operator()(std::size_t k) const { const std::size_t offset = Util::parity_2_offset(parity, k); - const std::size_t i00 = indices(0B00); - const std::size_t i01 = indices(0B01); - const std::size_t i10 = indices(0B10); - const std::size_t i11 = indices(0B11); + std::size_t i00 = indices(0B00); + std::size_t i01 = indices(0B01); + std::size_t i10 = indices(0B10); + std::size_t i11 = indices(0B11); core_function(arr, i00 + offset, i01 + offset, i10 + offset, i11 + offset); @@ -713,25 +696,24 @@ class applyNC2Functor { Kokkos::RangePolicy(0, exp2(num_qubits - 2)), *this); } - KOKKOS_FUNCTION void operator()(const std::size_t k) const { - const std::size_t i00 = ((k << 2U) & parity_high) | - ((k << 1U) & parity_middle) | (k & parity_low); - const std::size_t i01 = i00 | rev_wire0_shift; - const std::size_t i10 = i00 | rev_wire1_shift; - const std::size_t i11 = i00 | rev_wire0_shift | rev_wire1_shift; + KOKKOS_FUNCTION void operator()(std::size_t k) const { + std::size_t i00 = ((k << 2U) & parity_high) | + ((k << 1U) & parity_middle) | (k & parity_low); + std::size_t i01 = i00 | rev_wire0_shift; + std::size_t i10 = i00 | rev_wire1_shift; + std::size_t i11 = i00 | rev_wire0_shift | rev_wire1_shift; core_function(arr, i00, i01, i10, i11); } }; template void applyCNOT(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i00_ = i00; [[maybe_unused]] const auto i01_ = i01; @@ -744,13 +726,12 @@ void applyCNOT(Kokkos::View *> arr_, template void applyCY(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i00_ = i00; [[maybe_unused]] const auto i01_ = i01; Kokkos::complex v10 = arr(i10); @@ -763,13 +744,12 @@ void applyCY(Kokkos::View *> arr_, template void applyCZ(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i00_ = i00; [[maybe_unused]] const auto i01_ = i01; [[maybe_unused]] const auto i10_ = i10; @@ -781,15 +761,15 @@ void applyCZ(Kokkos::View *> arr_, template void applyNCSWAP(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i00_ = i00; [[maybe_unused]] const auto i11_ = i11; @@ -807,9 +787,8 @@ void applyNCSWAP(Kokkos::View *> arr_, template void applySWAP(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { applyNCSWAP(arr_, num_qubits, {}, {}, wires, inverse); @@ -817,17 +796,17 @@ void applySWAP(Kokkos::View *> arr_, template void applyControlledPhaseShift(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &wires, - const bool inverse = false, + bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const Kokkos::complex s = (inverse) ? Kokkos::exp(-Kokkos::complex(0, angle)) : Kokkos::exp(Kokkos::complex(0, angle)); auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i00_ = i00; [[maybe_unused]] const auto i01_ = i01; [[maybe_unused]] const auto i10_ = i10; @@ -839,16 +818,16 @@ void applyControlledPhaseShift(Kokkos::View *> arr_, template void applyCRX(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT c = std::cos(angle / 2); const PrecisionT js = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i00_ = i00; [[maybe_unused]] const auto i01_ = i01; const Kokkos::complex v10 = arr(i10); @@ -864,15 +843,15 @@ void applyCRX(Kokkos::View *> arr_, template void applyCRY(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT c = std::cos(angle / 2); const PrecisionT s = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i00_ = i00; [[maybe_unused]] const auto i01_ = i01; const Kokkos::complex v10 = arr(i10); @@ -886,8 +865,8 @@ void applyCRY(Kokkos::View *> arr_, template void applyCRZ(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cos_angle = std::cos(angle * static_cast(0.5)); @@ -896,8 +875,8 @@ void applyCRZ(Kokkos::View *> arr_, cos_angle, (inverse) ? sin_angle : -sin_angle}; const Kokkos::complex shift_1 = Kokkos::conj(shift_0); auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i00_ = i00; [[maybe_unused]] const auto i01_ = i01; arr(i10) *= shift_0; @@ -909,9 +888,8 @@ void applyCRZ(Kokkos::View *> arr_, template void applyCRot(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT phi = (inverse) ? -params[2] : params[0]; const PrecisionT theta = (inverse) ? -params[1] : params[1]; @@ -923,8 +901,8 @@ void applyCRot(Kokkos::View *> arr_, const Kokkos::complex mat_0b10 = mat[0b10]; const Kokkos::complex mat_0b11 = mat[0b11]; auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i00_ = i00; [[maybe_unused]] const auto i01_ = i01; const Kokkos::complex v0 = arr(i10); @@ -938,19 +916,18 @@ void applyCRot(Kokkos::View *> arr_, template void applyNCIsingXX(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, - const std::vector &wires, - const bool inverse = false, + const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { const Kokkos::complex v00 = arr(i00); const Kokkos::complex v01 = arr(i01); const Kokkos::complex v10 = arr(i10); @@ -976,20 +953,18 @@ void applyNCIsingXX(Kokkos::View *> arr_, template void applyIsingXX(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, const std::vector ¶ms = {}) { applyNCIsingXX(arr_, num_qubits, {}, {}, wires, inverse, params); } template void applyNCIsingXY(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, - const std::vector &wires, - const bool inverse = false, + const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; @@ -997,8 +972,8 @@ void applyNCIsingXY(Kokkos::View *> arr_, const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { const Kokkos::complex v00 = arr(i00); const Kokkos::complex v01 = arr(i01); const Kokkos::complex v10 = arr(i10); @@ -1022,9 +997,8 @@ void applyNCIsingXY(Kokkos::View *> arr_, template void applyIsingXY(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, const std::vector ¶ms = {}) { applyNCIsingXY(arr_, num_qubits, {}, {}, wires, inverse, params); @@ -1032,19 +1006,18 @@ void applyIsingXY(Kokkos::View *> arr_, template void applyNCIsingYY(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, - const std::vector &wires, - const bool inverse = false, + const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { const Kokkos::complex v00 = arr(i00); const Kokkos::complex v01 = arr(i01); const Kokkos::complex v10 = arr(i10); @@ -1070,9 +1043,8 @@ void applyNCIsingYY(Kokkos::View *> arr_, template void applyIsingYY(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, const std::vector ¶ms = {}) { applyNCIsingYY(arr_, num_qubits, {}, {}, wires, inverse, params); @@ -1080,11 +1052,10 @@ void applyIsingYY(Kokkos::View *> arr_, template void applyNCIsingZZ(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, - const std::vector &wires, - const bool inverse = false, + const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const Kokkos::complex shift_0 = Kokkos::complex{ @@ -1092,8 +1063,8 @@ void applyNCIsingZZ(Kokkos::View *> arr_, (inverse) ? std::sin(angle / 2) : -std::sin(angle / 2)}; const Kokkos::complex shift_1 = Kokkos::conj(shift_0); auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { arr(i00) *= shift_0; arr(i01) *= shift_1; arr(i10) *= shift_1; @@ -1111,9 +1082,8 @@ void applyNCIsingZZ(Kokkos::View *> arr_, template void applyIsingZZ(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, const std::vector ¶ms = {}) { applyNCIsingZZ(arr_, num_qubits, {}, {}, wires, inverse, params); @@ -1121,19 +1091,19 @@ void applyIsingZZ(Kokkos::View *> arr_, template void applyNCSingleExcitation(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, const std::vector &wires, - const bool inverse = false, + bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i00_ = i00; [[maybe_unused]] const auto i11_ = i11; @@ -1154,9 +1124,9 @@ void applyNCSingleExcitation(Kokkos::View *> arr_, template void applySingleExcitation(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &wires, - const bool inverse = false, + bool inverse = false, const std::vector ¶ms = {}) { applyNCSingleExcitation( arr_, num_qubits, {}, {}, wires, inverse, params); @@ -1164,11 +1134,10 @@ void applySingleExcitation(Kokkos::View *> arr_, template void applyNCSingleExcitationMinus( - Kokkos::View *> arr_, - const std::size_t num_qubits, + Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, - const std::vector &wires, const bool inverse = false, + const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); @@ -1178,8 +1147,8 @@ void applyNCSingleExcitationMinus( (inverse) ? Kokkos::exp(Kokkos::complex(0, angle / 2)) : Kokkos::exp(Kokkos::complex(0, -angle / 2)); auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { const Kokkos::complex v01 = arr(i01); const Kokkos::complex v10 = arr(i10); arr(i00) *= e; @@ -1199,20 +1168,19 @@ void applyNCSingleExcitationMinus( template void applySingleExcitationMinus( - Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - const bool inverse = false, const std::vector ¶ms = {}) { + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &wires, bool inverse = false, + const std::vector ¶ms = {}) { applyNCSingleExcitationMinus( arr_, num_qubits, {}, {}, wires, inverse, params); } template void applyNCSingleExcitationPlus( - Kokkos::View *> arr_, - const std::size_t num_qubits, + Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, - const std::vector &wires, const bool inverse = false, + const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); @@ -1222,8 +1190,8 @@ void applyNCSingleExcitationPlus( (inverse) ? Kokkos::exp(Kokkos::complex(0, -angle / 2)) : Kokkos::exp(Kokkos::complex(0, angle / 2)); auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i00_ = i00; [[maybe_unused]] const auto i11_ = i11; @@ -1246,9 +1214,9 @@ void applyNCSingleExcitationPlus( template void applySingleExcitationPlus(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &wires, - const bool inverse = false, + bool inverse = false, const std::vector ¶ms = {}) { applyNCSingleExcitationPlus( arr_, num_qubits, {}, {}, wires, inverse, params); @@ -1303,17 +1271,17 @@ template class applyNC3Functor { Kokkos::RangePolicy(0, exp2(num_qubits - 3)), *this); } - KOKKOS_FUNCTION void operator()(const std::size_t k) const { - const std::size_t i000 = - ((k << 3U) & parity_high) | ((k << 2U) & parity_hmiddle) | - ((k << 1U) & parity_lmiddle) | (k & parity_low); - const std::size_t i001 = i000 | rev_wire0_shift; - const std::size_t i010 = i000 | rev_wire1_shift; - const std::size_t i011 = i000 | rev_wire1_shift | rev_wire0_shift; - const std::size_t i100 = i000 | rev_wire2_shift; - const std::size_t i101 = i000 | rev_wire2_shift | rev_wire0_shift; - const std::size_t i110 = i000 | rev_wire2_shift | rev_wire1_shift; - const std::size_t i111 = + KOKKOS_FUNCTION void operator()(std::size_t k) const { + std::size_t i000 = ((k << 3U) & parity_high) | + ((k << 2U) & parity_hmiddle) | + ((k << 1U) & parity_lmiddle) | (k & parity_low); + std::size_t i001 = i000 | rev_wire0_shift; + std::size_t i010 = i000 | rev_wire1_shift; + std::size_t i011 = i000 | rev_wire1_shift | rev_wire0_shift; + std::size_t i100 = i000 | rev_wire2_shift; + std::size_t i101 = i000 | rev_wire2_shift | rev_wire0_shift; + std::size_t i110 = i000 | rev_wire2_shift | rev_wire1_shift; + std::size_t i111 = i000 | rev_wire2_shift | rev_wire1_shift | rev_wire0_shift; core_function(arr, i000, i001, i010, i011, i100, i101, i110, i111); } @@ -1321,17 +1289,15 @@ template class applyNC3Functor { template void applyCSWAP(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { applyNC3Functor( ExecutionSpace{}, arr_, num_qubits, wires, KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i000, const std::size_t i001, - const std::size_t i010, const std::size_t i011, - const std::size_t i100, const std::size_t i101, - const std::size_t i110, const std::size_t i111) { + std::size_t i000, std::size_t i001, std::size_t i010, + std::size_t i011, std::size_t i100, std::size_t i101, + std::size_t i110, std::size_t i111) { [[maybe_unused]] const auto i000_ = i000; [[maybe_unused]] const auto i001_ = i001; [[maybe_unused]] const auto i010_ = i010; @@ -1345,17 +1311,15 @@ void applyCSWAP(Kokkos::View *> arr_, template void applyToffoli(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { applyNC3Functor( ExecutionSpace{}, arr_, num_qubits, wires, KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i000, const std::size_t i001, - const std::size_t i010, const std::size_t i011, - const std::size_t i100, const std::size_t i101, - const std::size_t i110, const std::size_t i111) { + std::size_t i000, std::size_t i001, std::size_t i010, + std::size_t i011, std::size_t i100, std::size_t i101, + std::size_t i110, std::size_t i111) { [[maybe_unused]] const auto i000_ = i000; [[maybe_unused]] const auto i001_ = i001; [[maybe_unused]] const auto i010_ = i010; @@ -1403,24 +1367,24 @@ class applyNC4Functor { 0, exp2(num_qubits - controlled_wires.size() - wires.size())), *this); } - KOKKOS_FUNCTION void operator()(const std::size_t k) const { + KOKKOS_FUNCTION void operator()(std::size_t k) const { const std::size_t offset = Util::parity_2_offset(parity, k); - const std::size_t i0000 = indices(0B0000); - const std::size_t i0001 = indices(0B0001); - const std::size_t i0010 = indices(0B0010); - const std::size_t i0011 = indices(0B0011); - const std::size_t i0100 = indices(0B0100); - const std::size_t i0101 = indices(0B0101); - const std::size_t i0110 = indices(0B0110); - const std::size_t i0111 = indices(0B0111); - const std::size_t i1000 = indices(0B1000); - const std::size_t i1001 = indices(0B1001); - const std::size_t i1010 = indices(0B1010); - const std::size_t i1011 = indices(0B1011); - const std::size_t i1100 = indices(0B1100); - const std::size_t i1101 = indices(0B1101); - const std::size_t i1110 = indices(0B1110); - const std::size_t i1111 = indices(0B1111); + std::size_t i0000 = indices(0B0000); + std::size_t i0001 = indices(0B0001); + std::size_t i0010 = indices(0B0010); + std::size_t i0011 = indices(0B0011); + std::size_t i0100 = indices(0B0100); + std::size_t i0101 = indices(0B0101); + std::size_t i0110 = indices(0B0110); + std::size_t i0111 = indices(0B0111); + std::size_t i1000 = indices(0B1000); + std::size_t i1001 = indices(0B1001); + std::size_t i1010 = indices(0B1010); + std::size_t i1011 = indices(0B1011); + std::size_t i1100 = indices(0B1100); + std::size_t i1101 = indices(0B1101); + std::size_t i1110 = indices(0B1110); + std::size_t i1111 = indices(0B1111); core_function( arr, i0000 + offset, i0001 + offset, i0010 + offset, i0011 + offset, @@ -1513,31 +1477,31 @@ class applyNC4Functor { Kokkos::RangePolicy(0, exp2(num_qubits - 4)), *this); } - KOKKOS_FUNCTION void operator()(const std::size_t k) const { - const std::size_t i0000 = - ((k << 4U) & parity_high) | ((k << 3U) & parity_hmiddle) | - ((k << 2U) & parity_middle) | ((k << 1U) & parity_lmiddle) | - (k & parity_low); - const std::size_t i0001 = i0000 | rev_wire0_shift; - const std::size_t i0010 = i0000 | rev_wire1_shift; - const std::size_t i0011 = i0000 | rev_wire1_shift | rev_wire0_shift; - const std::size_t i0100 = i0000 | rev_wire2_shift; - const std::size_t i0101 = i0000 | rev_wire2_shift | rev_wire0_shift; - const std::size_t i0110 = i0000 | rev_wire2_shift | rev_wire1_shift; - const std::size_t i0111 = + KOKKOS_FUNCTION void operator()(std::size_t k) const { + std::size_t i0000 = ((k << 4U) & parity_high) | + ((k << 3U) & parity_hmiddle) | + ((k << 2U) & parity_middle) | + ((k << 1U) & parity_lmiddle) | (k & parity_low); + std::size_t i0001 = i0000 | rev_wire0_shift; + std::size_t i0010 = i0000 | rev_wire1_shift; + std::size_t i0011 = i0000 | rev_wire1_shift | rev_wire0_shift; + std::size_t i0100 = i0000 | rev_wire2_shift; + std::size_t i0101 = i0000 | rev_wire2_shift | rev_wire0_shift; + std::size_t i0110 = i0000 | rev_wire2_shift | rev_wire1_shift; + std::size_t i0111 = i0000 | rev_wire2_shift | rev_wire1_shift | rev_wire0_shift; - const std::size_t i1000 = i0000 | rev_wire3_shift; - const std::size_t i1001 = i0000 | rev_wire3_shift | rev_wire0_shift; - const std::size_t i1010 = i0000 | rev_wire3_shift | rev_wire1_shift; - const std::size_t i1011 = + std::size_t i1000 = i0000 | rev_wire3_shift; + std::size_t i1001 = i0000 | rev_wire3_shift | rev_wire0_shift; + std::size_t i1010 = i0000 | rev_wire3_shift | rev_wire1_shift; + std::size_t i1011 = i0000 | rev_wire3_shift | rev_wire1_shift | rev_wire0_shift; - const std::size_t i1100 = i0000 | rev_wire3_shift | rev_wire2_shift; - const std::size_t i1101 = + std::size_t i1100 = i0000 | rev_wire3_shift | rev_wire2_shift; + std::size_t i1101 = i0000 | rev_wire3_shift | rev_wire2_shift | rev_wire0_shift; - const std::size_t i1110 = + std::size_t i1110 = i0000 | rev_wire3_shift | rev_wire2_shift | rev_wire1_shift; - const std::size_t i1111 = i0000 | rev_wire3_shift | rev_wire2_shift | - rev_wire1_shift | rev_wire0_shift; + std::size_t i1111 = i0000 | rev_wire3_shift | rev_wire2_shift | + rev_wire1_shift | rev_wire0_shift; core_function(arr, i0000, i0001, i0010, i0011, i0100, i0101, i0110, i0111, i1000, i1001, i1010, i1011, i1100, i1101, i1110, i1111); @@ -1546,26 +1510,23 @@ class applyNC4Functor { template void applyNCDoubleExcitation(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, const std::vector &wires, - const bool inverse = false, + bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - auto core_function = - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0000, const std::size_t i0001, - const std::size_t i0010, const std::size_t i0011, - const std::size_t i0100, const std::size_t i0101, - const std::size_t i0110, const std::size_t i0111, - const std::size_t i1000, const std::size_t i1001, - const std::size_t i1010, const std::size_t i1011, - const std::size_t i1100, const std::size_t i1101, - const std::size_t i1110, const std::size_t i1111) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, std::size_t i0000, + std::size_t i0001, std::size_t i0010, std::size_t i0011, + std::size_t i0100, std::size_t i0101, std::size_t i0110, + std::size_t i0111, std::size_t i1000, std::size_t i1001, + std::size_t i1010, std::size_t i1011, std::size_t i1100, + std::size_t i1101, std::size_t i1110, std::size_t i1111) { [[maybe_unused]] const auto i0000_ = i0000; [[maybe_unused]] const auto i0001_ = i0001; [[maybe_unused]] const auto i0010_ = i0010; @@ -1597,9 +1558,9 @@ void applyNCDoubleExcitation(Kokkos::View *> arr_, template void applyDoubleExcitation(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &wires, - const bool inverse = false, + bool inverse = false, const std::vector ¶ms = {}) { applyNCDoubleExcitation( arr_, num_qubits, {}, {}, wires, inverse, params); @@ -1607,11 +1568,10 @@ void applyDoubleExcitation(Kokkos::View *> arr_, template void applyNCDoubleExcitationMinus( - Kokkos::View *> arr_, - const std::size_t num_qubits, + Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, - const std::vector &wires, const bool inverse = false, + const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); @@ -1619,16 +1579,13 @@ void applyNCDoubleExcitationMinus( const Kokkos::complex e = inverse ? Kokkos::exp(Kokkos::complex(0, angle / 2)) : Kokkos::exp(Kokkos::complex(0, -angle / 2)); - auto core_function = - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0000, const std::size_t i0001, - const std::size_t i0010, const std::size_t i0011, - const std::size_t i0100, const std::size_t i0101, - const std::size_t i0110, const std::size_t i0111, - const std::size_t i1000, const std::size_t i1001, - const std::size_t i1010, const std::size_t i1011, - const std::size_t i1100, const std::size_t i1101, - const std::size_t i1110, const std::size_t i1111) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, std::size_t i0000, + std::size_t i0001, std::size_t i0010, std::size_t i0011, + std::size_t i0100, std::size_t i0101, std::size_t i0110, + std::size_t i0111, std::size_t i1000, std::size_t i1001, + std::size_t i1010, std::size_t i1011, std::size_t i1100, + std::size_t i1101, std::size_t i1110, std::size_t i1111) { const Kokkos::complex v3 = arr(i0011); const Kokkos::complex v12 = arr(i1100); arr(i0000) *= e; @@ -1660,20 +1617,19 @@ void applyNCDoubleExcitationMinus( template void applyDoubleExcitationMinus( - Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - const bool inverse = false, const std::vector ¶ms = {}) { + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &wires, bool inverse = false, + const std::vector ¶ms = {}) { applyNCDoubleExcitationMinus( arr_, num_qubits, {}, {}, wires, inverse, params); } template void applyNCDoubleExcitationPlus( - Kokkos::View *> arr_, - const std::size_t num_qubits, + Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, - const std::vector &wires, const bool inverse = false, + const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); @@ -1681,16 +1637,13 @@ void applyNCDoubleExcitationPlus( const Kokkos::complex e = inverse ? Kokkos::exp(Kokkos::complex(0, -angle / 2)) : Kokkos::exp(Kokkos::complex(0, angle / 2)); - auto core_function = - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0000, const std::size_t i0001, - const std::size_t i0010, const std::size_t i0011, - const std::size_t i0100, const std::size_t i0101, - const std::size_t i0110, const std::size_t i0111, - const std::size_t i1000, const std::size_t i1001, - const std::size_t i1010, const std::size_t i1011, - const std::size_t i1100, const std::size_t i1101, - const std::size_t i1110, const std::size_t i1111) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, std::size_t i0000, + std::size_t i0001, std::size_t i0010, std::size_t i0011, + std::size_t i0100, std::size_t i0101, std::size_t i0110, + std::size_t i0111, std::size_t i1000, std::size_t i1001, + std::size_t i1010, std::size_t i1011, std::size_t i1100, + std::size_t i1101, std::size_t i1110, std::size_t i1111) { const Kokkos::complex v3 = arr(i0011); const Kokkos::complex v12 = arr(i1100); arr(i0000) *= e; @@ -1722,9 +1675,9 @@ void applyNCDoubleExcitationPlus( template void applyDoubleExcitationPlus(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &wires, - const bool inverse = false, + bool inverse = false, const std::vector ¶ms = {}) { applyNCDoubleExcitationPlus( arr_, num_qubits, {}, {}, wires, inverse, params); @@ -1732,9 +1685,8 @@ void applyDoubleExcitationPlus(Kokkos::View *> arr_, template void applyMultiRZ(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const Kokkos::complex shift_0 = Kokkos::complex{ @@ -1748,7 +1700,7 @@ void applyMultiRZ(Kokkos::View *> arr_, } Kokkos::parallel_for( Kokkos::RangePolicy(0, exp2(num_qubits)), - KOKKOS_LAMBDA(const std::size_t k) { + KOKKOS_LAMBDA(std::size_t k) { arr_(k) *= (Kokkos::Impl::bit_count(k & wires_parity) % 2 == 0) ? shift_0 : shift_1; @@ -1757,11 +1709,10 @@ void applyMultiRZ(Kokkos::View *> arr_, template void applyNCMultiRZ(Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, - const std::vector &wires, - const bool inverse = false, + const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const Kokkos::complex shift_0 = Kokkos::complex{ @@ -1776,9 +1727,9 @@ void applyNCMultiRZ(Kokkos::View *> arr_, << (num_qubits - wire - 1)); }); auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i, + Kokkos::View *> arr, std::size_t i, Kokkos::View indices, std::size_t offset) { - const std::size_t index = indices(i); + std::size_t index = indices(i); arr(index + offset) *= (Kokkos::Impl::bit_count((index + offset) & wires_parity) % 2 == 0) ? shift_0 @@ -1791,8 +1742,8 @@ void applyNCMultiRZ(Kokkos::View *> arr_, template void applyPauliRot(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, const bool inverse, + std::size_t num_qubits, + const std::vector &wires, bool inverse, const PrecisionT angle, const std::string &word) { using ComplexT = Kokkos::complex; constexpr auto IMAG = Pennylane::Util::IMAG(); @@ -1827,8 +1778,8 @@ void applyPauliRot(Kokkos::View *> arr_, const auto count_mask_y = std::popcount(mask_y); Kokkos::parallel_for( Kokkos::RangePolicy(0, exp2(num_qubits)), - KOKKOS_LAMBDA(const std::size_t i0) { - const std::size_t i1 = i0 ^ mask_xy; + KOKKOS_LAMBDA(std::size_t i0) { + std::size_t i1 = i0 ^ mask_xy; if (i0 <= i1) { const auto count_y = Kokkos::Impl::bit_count(i0 & mask_y) * 2; const auto count_z = Kokkos::Impl::bit_count(i0 & mask_z) * 2; @@ -1845,9 +1796,9 @@ void applyPauliRot(Kokkos::View *> arr_, template void applyNamedOperation(const GateOperation gateop, Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &wires, - const bool inverse = false, + bool inverse = false, const std::vector ¶ms = {}) { switch (gateop) { case GateOperation::PauliX: @@ -1969,11 +1920,11 @@ void applyNamedOperation(const GateOperation gateop, template void applyNCNamedOperation(const ControlledGateOperation gateop, Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values, const std::vector &wires, - const bool inverse = false, + bool inverse = false, const std::vector ¶ms = {}) { switch (gateop) { case ControlledGateOperation::PauliX: diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp index 43cc5807dd..076ca88487 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp @@ -32,13 +32,13 @@ namespace Pennylane::LightningKokkos::Functors { template void applyGenPhaseShift( - Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0, const std::size_t i1) { + std::size_t i0, std::size_t i1) { [[maybe_unused]] auto i1_ = i1; arr(i0) = 0.0; }; @@ -48,13 +48,13 @@ void applyGenPhaseShift( template void applyGenControlledPhaseShift( - Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i11_ = i11; arr(i00) = 0.0; @@ -67,13 +67,12 @@ void applyGenControlledPhaseShift( template void applyGenCRX(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { arr(i00) = 0.0; arr(i01) = 0.0; kokkos_swap(arr(i10), arr(i11)); @@ -84,13 +83,12 @@ void applyGenCRX(Kokkos::View *> arr_, template void applyGenCRY(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { arr(i00) = 0.0; arr(i01) = 0.0; const auto v0 = arr(i10); @@ -103,13 +101,12 @@ void applyGenCRY(Kokkos::View *> arr_, template void applyGenCRZ(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + std::size_t num_qubits, const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i10_ = i10; arr(i00) = 0.0; arr(i01) = 0.0; @@ -121,13 +118,13 @@ void applyGenCRZ(Kokkos::View *> arr_, template void applyGenIsingXX( - Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { kokkos_swap(arr(i00), arr(i11)); kokkos_swap(arr(i10), arr(i01)); }; @@ -137,13 +134,13 @@ void applyGenIsingXX( template void applyGenIsingXY( - Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { kokkos_swap(arr(i10), arr(i01)); arr(i00) = 0.0; arr(i11) = 0.0; @@ -154,13 +151,13 @@ void applyGenIsingXY( template void applyGenIsingYY( - Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { const auto v00 = arr(i00); arr(i00) = -arr(i11); arr(i11) = -v00; @@ -172,13 +169,13 @@ void applyGenIsingYY( template void applyGenIsingZZ( - Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i00_ = i00; [[maybe_unused]] const auto i11_ = i11; @@ -191,13 +188,13 @@ void applyGenIsingZZ( template void applyGenSingleExcitation( - Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { arr(i00) = 0.0; arr(i01) *= Kokkos::complex{0.0, 1.0}; arr(i10) *= Kokkos::complex{0.0, -1.0}; @@ -210,13 +207,13 @@ void applyGenSingleExcitation( template void applyGenSingleExcitationMinus( - Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i00_ = i00; [[maybe_unused]] const auto i11_ = i11; @@ -230,13 +227,13 @@ void applyGenSingleExcitationMinus( template void applyGenSingleExcitationPlus( - Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { auto core_function = KOKKOS_LAMBDA( - Kokkos::View *> arr, const std::size_t i00, - const std::size_t i01, const std::size_t i10, const std::size_t i11) { + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i00_ = i00; [[maybe_unused]] const auto i11_ = i11; @@ -252,20 +249,17 @@ void applyGenSingleExcitationPlus( template void applyGenDoubleExcitation( - Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - auto core_function = - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0000, const std::size_t i0001, - const std::size_t i0010, const std::size_t i0011, - const std::size_t i0100, const std::size_t i0101, - const std::size_t i0110, const std::size_t i0111, - const std::size_t i1000, const std::size_t i1001, - const std::size_t i1010, const std::size_t i1011, - const std::size_t i1100, const std::size_t i1101, - const std::size_t i1110, const std::size_t i1111) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, std::size_t i0000, + std::size_t i0001, std::size_t i0010, std::size_t i0011, + std::size_t i0100, std::size_t i0101, std::size_t i0110, + std::size_t i0111, std::size_t i1000, std::size_t i1001, + std::size_t i1010, std::size_t i1011, std::size_t i1100, + std::size_t i1101, std::size_t i1110, std::size_t i1111) { const Kokkos::complex v0011 = arr(i0011); const Kokkos::complex v1100 = arr(i1100); arr(i0000) = 0.0; @@ -291,20 +285,17 @@ void applyGenDoubleExcitation( template void applyGenDoubleExcitationMinus( - Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - auto core_function = - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0000, const std::size_t i0001, - const std::size_t i0010, const std::size_t i0011, - const std::size_t i0100, const std::size_t i0101, - const std::size_t i0110, const std::size_t i0111, - const std::size_t i1000, const std::size_t i1001, - const std::size_t i1010, const std::size_t i1011, - const std::size_t i1100, const std::size_t i1101, - const std::size_t i1110, const std::size_t i1111) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, std::size_t i0000, + std::size_t i0001, std::size_t i0010, std::size_t i0011, + std::size_t i0100, std::size_t i0101, std::size_t i0110, + std::size_t i0111, std::size_t i1000, std::size_t i1001, + std::size_t i1010, std::size_t i1011, std::size_t i1100, + std::size_t i1101, std::size_t i1110, std::size_t i1111) { [[maybe_unused]] const auto i0000_ = i0000; [[maybe_unused]] const auto i0001_ = i0001; [[maybe_unused]] const auto i0010_ = i0010; @@ -329,20 +320,17 @@ void applyGenDoubleExcitationMinus( template void applyGenDoubleExcitationPlus( - Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - auto core_function = - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0000, const std::size_t i0001, - const std::size_t i0010, const std::size_t i0011, - const std::size_t i0100, const std::size_t i0101, - const std::size_t i0110, const std::size_t i0111, - const std::size_t i1000, const std::size_t i1001, - const std::size_t i1010, const std::size_t i1011, - const std::size_t i1100, const std::size_t i1101, - const std::size_t i1110, const std::size_t i1111) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, std::size_t i0000, + std::size_t i0001, std::size_t i0010, std::size_t i0011, + std::size_t i0100, std::size_t i0101, std::size_t i0110, + std::size_t i0111, std::size_t i1000, std::size_t i1001, + std::size_t i1010, std::size_t i1011, std::size_t i1100, + std::size_t i1101, std::size_t i1110, std::size_t i1111) { [[maybe_unused]] const auto i0000_ = i0000; [[maybe_unused]] const auto i0001_ = i0001; [[maybe_unused]] const auto i0010_ = i0010; @@ -367,18 +355,17 @@ void applyGenDoubleExcitationPlus( template void applyGenMultiRZ( - Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] const bool inverse = false, + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { std::size_t wires_parity = static_cast(0U); for (std::size_t wire : wires) { - wires_parity |= - (static_cast(1U) << (num_qubits - wire - 1)); + wires_parity |= exp2(num_qubits - wire - 1); } Kokkos::parallel_for( Kokkos::RangePolicy(0, exp2(num_qubits)), - KOKKOS_LAMBDA(const std::size_t k) { + KOKKOS_LAMBDA(std::size_t k) { arr_(k) *= static_cast( 1 - 2 * int(Kokkos::Impl::bit_count(k & wires_parity) % 2)); }); @@ -387,9 +374,9 @@ void applyGenMultiRZ( template PrecisionT applyNamedGenerator(const GeneratorOperation generator_op, Kokkos::View *> arr_, - const std::size_t num_qubits, + std::size_t num_qubits, const std::vector &wires, - const bool inverse = false, + bool inverse = false, const std::vector ¶ms = {}) { switch (generator_op) { case GeneratorOperation::RX: diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/MatrixGateFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/MatrixGateFunctors.hpp index fb3e70cbcf..19efbccb52 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/MatrixGateFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/MatrixGateFunctors.hpp @@ -23,10 +23,13 @@ namespace { using namespace Pennylane::Util; using Kokkos::Experimental::swap; +using Pennylane::LightningKokkos::Util::ControlBitPatterns; +using Pennylane::LightningKokkos::Util::generateBitPatterns; using Pennylane::LightningKokkos::Util::one; +using Pennylane::LightningKokkos::Util::parity_2_offset; +using Pennylane::LightningKokkos::Util::reverseWires; using Pennylane::LightningKokkos::Util::vector2view; using Pennylane::LightningKokkos::Util::wires2Parity; -using std::size_t; } // namespace /// @endcond @@ -57,19 +60,22 @@ template struct multiQubitOpFunctor { const KokkosComplexVector &matrix_, const std::vector &wires_) { wires = vector2view(wires_); - dim = one << wires_.size(); + dim = exp2(wires_.size()); num_qubits = num_qubits_; arr = arr_; matrix = matrix_; - std::tie(parity, rev_wire_shifts) = wires2Parity(num_qubits_, wires_); + const auto &[parity_, rev_wire_shifts_] = + wires2Parity(num_qubits_, wires_); + parity = parity_; + rev_wire_shifts = rev_wire_shifts_; } KOKKOS_INLINE_FUNCTION void operator()(const MemberType &teamMember) const { const std::size_t k = teamMember.league_rank(); - ScratchViewComplex coeffs_in(teamMember.team_scratch(0), dim); - ScratchViewSizeT indices(teamMember.team_scratch(0), dim); - if (teamMember.team_rank() == 0) { + ScratchViewComplex coeffs_in(teamMember.team_scratch(1), dim); + ScratchViewSizeT indices(teamMember.team_scratch(1), dim); + if (!teamMember.team_rank()) { std::size_t idx = (k & parity(0)); for (std::size_t i = 1; i < parity.size(); i++) { idx |= ((k << i) & parity(i)); @@ -78,7 +84,7 @@ template struct multiQubitOpFunctor { coeffs_in(0) = arr(idx); Kokkos::parallel_for(Kokkos::ThreadVectorRange(teamMember, 1, dim), - [&](const std::size_t inner_idx) { + [&](std::size_t inner_idx) { std::size_t index = indices(0); for (std::size_t i = 0; i < wires.size(); i++) { @@ -92,7 +98,7 @@ template struct multiQubitOpFunctor { } teamMember.team_barrier(); Kokkos::parallel_for( - Kokkos::TeamThreadRange(teamMember, dim), [&](const std::size_t i) { + Kokkos::TeamThreadRange(teamMember, dim), [&](std::size_t i) { const auto idx = indices(i); arr(idx) = 0.0; const std::size_t base_idx = i * dim; @@ -104,6 +110,77 @@ template struct multiQubitOpFunctor { } }; +template struct NCMultiQubitOpFunctor { + using KokkosComplexVector = Kokkos::View *>; + using KokkosIntVector = Kokkos::View; + using ScratchViewComplex = + Kokkos::View *, + Kokkos::DefaultExecutionSpace::scratch_memory_space, + Kokkos::MemoryTraits>; + using ScratchViewSizeT = + Kokkos::View>; + using MemberType = Kokkos::TeamPolicy<>::member_type; + + KokkosComplexVector arr; + KokkosComplexVector matrix; + KokkosIntVector indices; + KokkosIntVector parity; + KokkosIntVector rev_wires; + KokkosIntVector rev_wire_shifts; + std::size_t dim; + std::size_t num_qubits; + + NCMultiQubitOpFunctor(KokkosComplexVector arr_, std::size_t num_qubits_, + const KokkosComplexVector &matrix_, + const std::vector &controlled_wires_, + const std::vector &controlled_values_, + const std::vector &wires_) { + dim = exp2(wires_.size()); + arr = arr_; + matrix = matrix_; + num_qubits = num_qubits_; + const auto &[parity_, rev_wires_] = + reverseWires(num_qubits_, wires_, controlled_wires_); + parity = parity_; + std::vector indices_ = + generateBitPatterns(wires_, num_qubits_); + ControlBitPatterns(indices_, num_qubits_, controlled_wires_, + controlled_values_); + indices = vector2view(indices_); + } + + KOKKOS_INLINE_FUNCTION + void operator()(const MemberType &teamMember) const { + const std::size_t k = teamMember.league_rank(); + ScratchViewComplex coeffs_in(teamMember.team_scratch(1), dim); + ScratchViewSizeT indices_scratch(teamMember.team_scratch(1), dim); + const std::size_t offset = parity_2_offset(parity, k); + + if (!teamMember.team_rank()) { + Kokkos::parallel_for(Kokkos::ThreadVectorRange(teamMember, dim), + [&](std::size_t inner_idx) { + coeffs_in(inner_idx) = + arr(indices(inner_idx) + offset); + indices_scratch(inner_idx) = + indices(inner_idx); + }); + } + teamMember.team_barrier(); + Kokkos::parallel_for( + Kokkos::TeamThreadRange(teamMember, dim), [&](std::size_t i) { + const auto idx = indices_scratch(i) + offset; + arr(idx) = 0.0; + const std::size_t base_idx = i * dim; + + for (std::size_t j = 0; j < dim; j++) { + arr(idx) += matrix(base_idx + j) * coeffs_in(j); + } + }); + } +}; + template struct apply1QubitOpFunctor { using ComplexT = Kokkos::complex; using KokkosComplexVector = Kokkos::View; @@ -125,13 +202,13 @@ template struct apply1QubitOpFunctor { num_qubits = num_qubits_; rev_wire = num_qubits - wires_[0] - 1; - rev_wire_shift = (static_cast(1U) << rev_wire); + rev_wire_shift = (exp2(rev_wire)); wire_parity = fillTrailingOnes(rev_wire); wire_parity_inv = fillLeadingOnes(rev_wire + 1); } KOKKOS_INLINE_FUNCTION - void operator()(const std::size_t k) const { + void operator()(std::size_t k) const { const std::size_t i0 = ((k << 1U) & wire_parity_inv) | (wire_parity & k); const std::size_t i1 = i0 | rev_wire_shift; @@ -142,6 +219,51 @@ template struct apply1QubitOpFunctor { arr(i1) = matrix(0B10) * v0 + matrix(0B11) * v1; } }; + +template struct applyNC1QubitOpFunctor { + using ComplexT = Kokkos::complex; + using KokkosComplexVector = Kokkos::View; + using KokkosIntVector = Kokkos::View; + + KokkosComplexVector arr; + KokkosComplexVector matrix; + KokkosIntVector indices; + KokkosIntVector parity; + KokkosIntVector rev_wires; + KokkosIntVector rev_wire_shifts; + std::size_t num_qubits; + + applyNC1QubitOpFunctor(KokkosComplexVector arr_, std::size_t num_qubits_, + const KokkosComplexVector &matrix_, + const std::vector &controlled_wires_, + const std::vector &controlled_values_, + const std::vector &wires_) { + arr = arr_; + matrix = matrix_; + num_qubits = num_qubits_; + const auto &[parity_, rev_wires_] = + reverseWires(num_qubits_, wires_, controlled_wires_); + parity = parity_; + std::vector indices_ = + generateBitPatterns(wires_, num_qubits_); + ControlBitPatterns(indices_, num_qubits_, controlled_wires_, + controlled_values_); + indices = vector2view(indices_); + } + + KOKKOS_INLINE_FUNCTION + void operator()(std::size_t k) const { + const std::size_t offset = parity_2_offset(parity, k); + std::size_t i0 = indices(0B00); + std::size_t i1 = indices(0B01); + ComplexT v0 = arr(i0 + offset); + ComplexT v1 = arr(i1 + offset); + + arr(i0 + offset) = matrix(0B00) * v0 + matrix(0B01) * v1; + arr(i1 + offset) = matrix(0B10) * v0 + matrix(0B11) * v1; + } +}; + template struct apply2QubitOpFunctor { using ComplexT = Kokkos::complex; using KokkosComplexVector = Kokkos::View; @@ -180,7 +302,7 @@ template struct apply2QubitOpFunctor { } KOKKOS_INLINE_FUNCTION - void operator()(const std::size_t k) const { + void operator()(std::size_t k) const { const std::size_t i00 = ((k << 2U) & parity_high) | ((k << 1U) & parity_middle) | (k & parity_low); const std::size_t i10 = i00 | rev_wire1_shift; @@ -203,6 +325,60 @@ template struct apply2QubitOpFunctor { } }; +template struct applyNC2QubitOpFunctor { + using ComplexT = Kokkos::complex; + using KokkosComplexVector = Kokkos::View; + using KokkosIntVector = Kokkos::View; + + KokkosComplexVector arr; + KokkosComplexVector matrix; + KokkosIntVector indices; + KokkosIntVector parity; + KokkosIntVector rev_wires; + KokkosIntVector rev_wire_shifts; + std::size_t num_qubits; + + applyNC2QubitOpFunctor(KokkosComplexVector arr_, std::size_t num_qubits_, + const KokkosComplexVector &matrix_, + const std::vector &controlled_wires_, + const std::vector &controlled_values_, + const std::vector &wires_) { + arr = arr_; + matrix = matrix_; + num_qubits = num_qubits_; + const auto &[parity_, rev_wires_] = + reverseWires(num_qubits_, wires_, controlled_wires_); + parity = parity_; + std::vector indices_ = + generateBitPatterns(wires_, num_qubits_); + ControlBitPatterns(indices_, num_qubits_, controlled_wires_, + controlled_values_); + indices = vector2view(indices_); + } + + KOKKOS_INLINE_FUNCTION + void operator()(std::size_t k) const { + const std::size_t offset = parity_2_offset(parity, k); + std::size_t i00 = indices(0B00); + std::size_t i01 = indices(0B01); + std::size_t i10 = indices(0B10); + std::size_t i11 = indices(0B11); + ComplexT v00 = arr(i00 + offset); + ComplexT v01 = arr(i01 + offset); + ComplexT v10 = arr(i10 + offset); + ComplexT v11 = arr(i11 + offset); + + arr(i00 + offset) = matrix(0B0000) * v00 + matrix(0B0001) * v01 + + matrix(0B0010) * v10 + matrix(0B0011) * v11; + arr(i01 + offset) = matrix(0B0100) * v00 + matrix(0B0101) * v01 + + matrix(0B0110) * v10 + matrix(0B0111) * v11; + arr(i10 + offset) = matrix(0B1000) * v00 + matrix(0B1001) * v01 + + matrix(0B1010) * v10 + matrix(0B1011) * v11; + arr(i11 + offset) = matrix(0B1100) * v00 + matrix(0B1101) * v01 + + matrix(0B1110) * v10 + matrix(0B1111) * v11; + } +}; + #define GATEENTRY3(xx, yy) xx << 3 | yy #define GATETERM3(xx, yy, vyy) matrix(GATEENTRY3(xx, yy)) * vyy #define GATESUM3(xx) \ @@ -228,11 +404,14 @@ template struct apply3QubitOpFunctor { arr = arr_; matrix = matrix_; num_qubits = num_qubits_; - std::tie(parity, rev_wire_shifts) = wires2Parity(num_qubits_, wires_); + const auto &[parity_, rev_wire_shifts_] = + wires2Parity(num_qubits_, wires_); + parity = parity_; + rev_wire_shifts = rev_wire_shifts_; } KOKKOS_INLINE_FUNCTION - void operator()(const std::size_t k) const { + void operator()(std::size_t k) const { std::size_t i000 = (k & parity(0)); for (std::size_t i = 1; i < parity.size(); i++) { i000 |= ((k << i) & parity(i)); @@ -265,6 +444,68 @@ template struct apply3QubitOpFunctor { } }; +template struct applyNC3QubitOpFunctor { + using ComplexT = Kokkos::complex; + using KokkosComplexVector = Kokkos::View; + using KokkosIntVector = Kokkos::View; + + KokkosComplexVector arr; + KokkosComplexVector matrix; + KokkosIntVector indices; + KokkosIntVector parity; + KokkosIntVector rev_wires; + KokkosIntVector rev_wire_shifts; + std::size_t num_qubits; + + applyNC3QubitOpFunctor(KokkosComplexVector arr_, std::size_t num_qubits_, + const KokkosComplexVector &matrix_, + const std::vector &controlled_wires_, + const std::vector &controlled_values_, + const std::vector &wires_) { + arr = arr_; + matrix = matrix_; + num_qubits = num_qubits_; + const auto &[parity_, rev_wires_] = + reverseWires(num_qubits_, wires_, controlled_wires_); + parity = parity_; + std::vector indices_ = + generateBitPatterns(wires_, num_qubits_); + ControlBitPatterns(indices_, num_qubits_, controlled_wires_, + controlled_values_); + indices = vector2view(indices_); + } + + KOKKOS_INLINE_FUNCTION + void operator()(std::size_t k) const { + const std::size_t offset = parity_2_offset(parity, k); + std::size_t i000 = indices(0B000); + std::size_t i001 = indices(0B001); + std::size_t i010 = indices(0B010); + std::size_t i011 = indices(0B011); + std::size_t i100 = indices(0B100); + std::size_t i101 = indices(0B101); + std::size_t i110 = indices(0B110); + std::size_t i111 = indices(0B111); + ComplexT v000 = arr(i000 + offset); + ComplexT v001 = arr(i001 + offset); + ComplexT v010 = arr(i010 + offset); + ComplexT v011 = arr(i011 + offset); + ComplexT v100 = arr(i100 + offset); + ComplexT v101 = arr(i101 + offset); + ComplexT v110 = arr(i110 + offset); + ComplexT v111 = arr(i111 + offset); + + arr(i000 + offset) = GATESUM3(0B000); + arr(i001 + offset) = GATESUM3(0B001); + arr(i010 + offset) = GATESUM3(0B010); + arr(i011 + offset) = GATESUM3(0B011); + arr(i100 + offset) = GATESUM3(0B100); + arr(i101 + offset) = GATESUM3(0B101); + arr(i110 + offset) = GATESUM3(0B110); + arr(i111 + offset) = GATESUM3(0B111); + } +}; + #define GATEENTRY4(xx, yy) xx << 4 | yy #define GATETERM4(xx, yy, vyy) matrix(GATEENTRY4(xx, yy)) * vyy #define GATESUM4(xx) \ @@ -294,11 +535,14 @@ template struct apply4QubitOpFunctor { arr = arr_; matrix = matrix_; num_qubits = num_qubits_; - std::tie(parity, rev_wire_shifts) = wires2Parity(num_qubits_, wires_); + const auto &[parity_, rev_wire_shifts_] = + wires2Parity(num_qubits_, wires_); + parity = parity_; + rev_wire_shifts = rev_wire_shifts_; } KOKKOS_INLINE_FUNCTION - void operator()(const std::size_t k) const { + void operator()(std::size_t k) const { std::size_t i0000 = (k & parity(0)); for (std::size_t i = 1; i < parity.size(); i++) { i0000 |= ((k << i) & parity(i)); @@ -396,11 +640,14 @@ template struct apply5QubitOpFunctor { arr = arr_; matrix = matrix_; num_qubits = num_qubits_; - std::tie(parity, rev_wire_shifts) = wires2Parity(num_qubits_, wires_); + const auto &[parity_, rev_wire_shifts_] = + wires2Parity(num_qubits_, wires_); + parity = parity_; + rev_wire_shifts = rev_wire_shifts_; } KOKKOS_INLINE_FUNCTION - void operator()(const std::size_t k) const { + void operator()(std::size_t k) const { std::size_t i00000 = (k & parity(0)); for (std::size_t i = 1; i < parity.size(); i++) { i00000 |= ((k << i) & parity(i)); diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp index a949ae1523..0e3dcfbdac 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp @@ -704,6 +704,241 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyCSWAP", } } +TEMPLATE_TEST_CASE("StateVectorKokkos::applyMatrix/Controlled-Operation", + "[StateVectorKokkos_Nonparam]", float, double) { + using StateVectorT = StateVectorKokkos; + using PrecisionT = StateVectorT::PrecisionT; + + const std::size_t num_qubits = 7; + const TestType EP = 1e-4; + auto ini_st = createNonTrivialState(num_qubits); + + std::unordered_map str_to_gates_{}; + for (const auto &[gate_op, gate_name] : Constant::gate_names) { + str_to_gates_.emplace(gate_name, gate_op); + } + + std::unordered_map + str_to_controlled_gates_{}; + for (const auto &[gate_op, controlled_gate_name] : + Constant::controlled_gate_names) { + str_to_controlled_gates_.emplace(controlled_gate_name, gate_op); + } + + const bool inverse = GENERATE(false, true); + const std::string gate_name = + GENERATE("PauliX", "PauliY", "PauliZ", "Hadamard", "S", "T", "SWAP"); + DYNAMIC_SECTION("1-controlled Matrix - Gate = " + << gate_name << " Inverse = " << inverse) { + auto gate_matrix = getMatrix( + str_to_gates_.at(gate_name), {}, false); + + std::vector controlled_wires = {4}; + std::vector controlled_values = {true}; + + StateVectorT kokkos_sv_ops{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + + const auto wires = + createWires(str_to_controlled_gates_.at(gate_name), num_qubits); + kokkos_sv_ops.applyOperation(gate_name, controlled_wires, + controlled_values, wires, inverse, {}); + kokkos_sv_mat.applyOperation("Matrix", controlled_wires, + controlled_values, wires, inverse, {}, + gate_matrix); + + auto result_ops = kokkos_sv_ops.getDataVector(); + auto result_mat = kokkos_sv_mat.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_ops[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_ops[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + + DYNAMIC_SECTION("2-controlled Matrix (c {4, 5})- Gate = " + << gate_name << " Inverse = " << inverse) { + auto gate_matrix = getMatrix( + str_to_gates_.at(gate_name), {}, false); + + std::vector controlled_wires = {4, 5}; + std::vector controlled_values = {true, false}; + + StateVectorT kokkos_sv_ops{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + + const auto wires = + createWires(str_to_controlled_gates_.at(gate_name), num_qubits); + kokkos_sv_ops.applyOperation(gate_name, controlled_wires, + controlled_values, wires, inverse, {}); + kokkos_sv_mat.applyOperation("Matrix", controlled_wires, + controlled_values, wires, inverse, {}, + gate_matrix); + + auto result_ops = kokkos_sv_ops.getDataVector(); + auto result_mat = kokkos_sv_mat.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_ops[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_ops[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + + DYNAMIC_SECTION("2-controlled Matrix (c {4, 6})- Gate = " + << gate_name << " Inverse = " << inverse) { + auto gate_matrix = getMatrix( + str_to_gates_.at(gate_name), {}, false); + + std::vector controlled_wires = {4, 6}; + std::vector controlled_values = {true, false}; + + StateVectorT kokkos_sv_ops{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + + const auto wires = + createWires(str_to_controlled_gates_.at(gate_name), num_qubits); + kokkos_sv_ops.applyOperation(gate_name, controlled_wires, + controlled_values, wires, inverse, {}); + kokkos_sv_mat.applyOperation("Matrix", controlled_wires, + controlled_values, wires, inverse, {}, + gate_matrix); + + auto result_ops = kokkos_sv_ops.getDataVector(); + auto result_mat = kokkos_sv_mat.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_ops[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_ops[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + + DYNAMIC_SECTION("3-controlled Matrix (c {4, 5, 6})- Gate = " + << gate_name << " Inverse = " << inverse) { + auto gate_matrix = getMatrix( + str_to_gates_.at(gate_name), {}, false); + + std::vector controlled_wires = {4, 5, 6}; + std::vector controlled_values = {true, true, false}; + + StateVectorT kokkos_sv_ops{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + + const auto wires = + createWires(str_to_controlled_gates_.at(gate_name), num_qubits); + kokkos_sv_ops.applyOperation(gate_name, controlled_wires, + controlled_values, wires, inverse, {}); + kokkos_sv_mat.applyOperation("Matrix", controlled_wires, + controlled_values, wires, inverse, {}, + gate_matrix); + + auto result_ops = kokkos_sv_ops.getDataVector(); + auto result_mat = kokkos_sv_mat.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_ops[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_ops[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + + DYNAMIC_SECTION("3-controlled Matrix (c {2, 4, 6})- Gate = " + << gate_name << " Inverse = " << inverse) { + auto gate_matrix = getMatrix( + str_to_gates_.at(gate_name), {}, false); + + std::vector controlled_wires = {2, 4, 6}; + std::vector controlled_values = {true, true, false}; + + StateVectorT kokkos_sv_ops{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + + const auto wires = + createWires(str_to_controlled_gates_.at(gate_name), num_qubits); + kokkos_sv_ops.applyOperation(gate_name, controlled_wires, + controlled_values, wires, inverse, {}); + kokkos_sv_mat.applyOperation("Matrix", controlled_wires, + controlled_values, wires, inverse, {}, + gate_matrix); + + auto result_ops = kokkos_sv_ops.getDataVector(); + auto result_mat = kokkos_sv_mat.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_ops[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_ops[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + + DYNAMIC_SECTION("4-controlled Matrix (c {2, 3, 4, 5})- Gate = " + << gate_name << " Inverse = " << inverse) { + auto gate_matrix = getMatrix( + str_to_gates_.at(gate_name), {}, false); + + std::vector controlled_wires = {2, 3, 4, 5}; + std::vector controlled_values = {true, true, false, false}; + + StateVectorT kokkos_sv_ops{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + + const auto wires = + createWires(str_to_controlled_gates_.at(gate_name), num_qubits); + kokkos_sv_ops.applyOperation(gate_name, controlled_wires, + controlled_values, wires, inverse, {}); + kokkos_sv_mat.applyOperation("Matrix", controlled_wires, + controlled_values, wires, inverse, {}, + gate_matrix); + + auto result_ops = kokkos_sv_ops.getDataVector(); + auto result_mat = kokkos_sv_mat.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_ops[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_ops[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + + DYNAMIC_SECTION("4-controlled Matrix (c {2, 3, 5, 6})- Gate = " + << gate_name << " Inverse = " << inverse) { + auto gate_matrix = getMatrix( + str_to_gates_.at(gate_name), {}, false); + + std::vector controlled_wires = {2, 3, 5, 6}; + std::vector controlled_values = {true, true, false, false}; + + StateVectorT kokkos_sv_ops{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + + const auto wires = + createWires(str_to_controlled_gates_.at(gate_name), num_qubits); + kokkos_sv_ops.applyOperation(gate_name, controlled_wires, + controlled_values, wires, inverse, {}); + kokkos_sv_mat.applyOperation("Matrix", controlled_wires, + controlled_values, wires, inverse, {}, + gate_matrix); + + auto result_ops = kokkos_sv_ops.getDataVector(); + auto result_mat = kokkos_sv_mat.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_ops[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_ops[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } +} + TEMPLATE_TEST_CASE("StateVectorKokkos::applyMultiQubitOp", "[StateVectorKokkos_Nonparam][Inverse]", float, double) { const bool inverse = GENERATE(true, false); @@ -721,7 +956,7 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyMultiQubitOp", auto sv_normal_host = Kokkos::create_mirror_view_and_copy( Kokkos::HostSpace{}, sv_normal.getView()); - sv_mq.applyOperation("XXXXXXXX", wires, inverse, {}, matrix); + sv_mq.applyOperation("MatrixHadamard", wires, inverse, {}, matrix); auto sv_mq_host = Kokkos::create_mirror_view_and_copy( Kokkos::HostSpace{}, sv_mq.getView()); @@ -801,16 +1036,82 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyMultiQubitOp", } } -TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation Controlled", - "[StateVectorKokkos_Nonparam]", float, double) { - using StateVectorT = StateVectorKokkos; - using PrecisionT = StateVectorT::PrecisionT; - const std::size_t num_qubits = 3; - StateVectorKokkos state_vector{num_qubits}; - auto matrix = getIdentity(); - PL_REQUIRE_THROWS_MATCHES( - state_vector.applyOperation("XXX", {0}, {true}, {1}, false, {}, matrix), - LightningException, "Controlled matrix operation not yet supported"); +TEMPLATE_TEST_CASE("StateVectorKokkos::applyNCMultiQubitOp", + "[StateVectorKokkos_Nonparam][Inverse]", float, double) { + const bool inverse = GENERATE(true, false); + std::size_t num_qubits = 3; + StateVectorKokkos sv_normal{num_qubits}; + StateVectorKokkos sv_mq{num_qubits}; + using UnmanagedComplexHostView = + Kokkos::View *, Kokkos::HostSpace, + Kokkos::MemoryTraits>; + + SECTION("0 Controlled Single Qubit via applyOperation") { + std::vector wires = {0}; + sv_normal.applyOperation("PauliX", wires, inverse); + auto sv_normal_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_normal.getView()); + + std::vector controlled_wire = {}; + std::vector controlled_value = {}; + std::vector wire = {0}; + auto matrix = getPauliX(); + sv_mq.applyOperation("MatrixPauliX", controlled_wire, controlled_value, + wire, inverse, {}, matrix); + auto sv_mq_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_mq.getView()); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_normal_host[j]) == Approx(imag(sv_mq_host[j]))); + CHECK(real(sv_normal_host[j]) == Approx(real(sv_mq_host[j]))); + } + } + + SECTION("Single Qubit via applyNCMultiQubitOp") { + std::vector wires = {0, 1}; + sv_normal.applyOperation("CNOT", wires, inverse); + auto sv_normal_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_normal.getView()); + + std::vector controlled_wire = {0}; + std::vector controlled_value = {true}; + std::vector wire = {1}; + auto matrix = getPauliX(); + Kokkos::View *> device_matrix("device_matrix", + matrix.size()); + Kokkos::deep_copy(device_matrix, UnmanagedComplexHostView( + matrix.data(), matrix.size())); + sv_mq.applyNCMultiQubitOp(device_matrix, controlled_wire, + controlled_value, wire, inverse); + auto sv_mq_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_mq.getView()); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_normal_host[j]) == Approx(imag(sv_mq_host[j]))); + CHECK(real(sv_normal_host[j]) == Approx(real(sv_mq_host[j]))); + } + } + + SECTION("Controlled Single Qubit via applyOperation") { + std::vector wires = {0, 1}; + sv_normal.applyOperation("CNOT", wires, inverse); + auto sv_normal_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_normal.getView()); + + std::vector controlled_wire = {0}; + std::vector controlled_value = {true}; + std::vector wire = {1}; + auto matrix = getPauliX(); + sv_mq.applyOperation("MatrixCNOT", controlled_wire, controlled_value, + wire, inverse, {}, matrix); + auto sv_mq_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_mq.getView()); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(sv_normal_host[j]) == Approx(imag(sv_mq_host[j]))); + CHECK(real(sv_normal_host[j]) == Approx(real(sv_mq_host[j]))); + } + } } TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " @@ -1116,6 +1417,55 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " } } } + + SECTION("N-controlled SWAP with matrix") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + StateVectorT kokkos_sv0{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv1{ini_st.data(), ini_st.size()}; + kokkos_sv0.applyOperation("CSWAP", {control, wire0, wire1}, + inverse); + auto matrix = getSWAP(); + kokkos_sv1.applyOperation( + "MatrixCSWAP", std::vector{control}, + std::vector{true}, std::vector{wire0, wire1}, + inverse, {}, matrix); + auto result_sv0 = kokkos_sv0.getDataVector(); + auto result_sv1 = kokkos_sv1.getDataVector(); + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_sv0[j]) == + Approx(real(result_sv1[j])).margin(EP)); + CHECK(imag(result_sv0[j]) == + Approx(imag(result_sv1[j])).margin(EP)); + } + } + } +} + +TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation controlled Toffoli", + "[StateVectorKokkos_NonParam]", float, double) { + using StateVectorT = StateVectorKokkos; + + const TestType EP = 1e-4; + const std::size_t num_qubits = 6; + const bool inverse = GENERATE(true, false); + const std::size_t control = GENERATE(0, 1, 2); + + auto ini_st = createNonTrivialState(num_qubits); + StateVectorT kokkos_sv0{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv1{ini_st.data(), ini_st.size()}; + auto matrix = getToffoli(); + kokkos_sv0.applyOperation( + "Matrix", std::vector{control}, std::vector{true}, + std::vector{3, 4, 5}, inverse, {}, matrix); + kokkos_sv1.applyOperation("PauliX", std::vector{control, 3, 4}, + std::vector{true, true, true}, + std::vector{5}, inverse); + auto result_sv0 = kokkos_sv0.getDataVector(); + auto result_sv1 = kokkos_sv1.getDataVector(); + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_sv0[j]) == Approx(real(result_sv1[j])).margin(EP)); + CHECK(imag(result_sv0[j]) == Approx(imag(result_sv1[j])).margin(EP)); + } } TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp index 75336510dc..97f38c60aa 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp @@ -116,6 +116,137 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyMatrix/Operation", } } +TEMPLATE_TEST_CASE("StateVectorKokkos::applyMatrix/Controlled Operation", + "[StateVectorKokkos_Operation]", float, double) { + using StateVectorT = StateVectorKokkos; + using PrecisionT = StateVectorT::PrecisionT; + + const std::size_t num_qubits = 7; + const TestType EP = 1e-4; + const TestType param = 0.12342; + auto ini_st = createNonTrivialState(num_qubits); + + std::unordered_map str_to_gates_{}; + for (const auto &[gate_op, gate_name] : Constant::gate_names) { + str_to_gates_.emplace(gate_name, gate_op); + } + + std::unordered_map + str_to_controlled_gates_{}; + for (const auto &[gate_op, controlled_gate_name] : + Constant::controlled_gate_names) { + str_to_controlled_gates_.emplace(controlled_gate_name, gate_op); + } + + const bool inverse = GENERATE(false, true); + const std::string gate_name = GENERATE( + "PhaseShift", "RX", "RY", "RZ", "Rot", "IsingXX", "IsingXY", "IsingYY", + "IsingZZ", "SingleExcitation", "SingleExcitationMinus", + "SingleExcitationPlus", "DoubleExcitation", "DoubleExcitationMinus", + "DoubleExcitationPlus"); + DYNAMIC_SECTION("1-controlled Matrix - Gate = " + << gate_name << " Inverse = " << inverse) { + auto gate_op = + reverse_lookup(Constant::gate_names, std::string_view{gate_name}); + auto num_params = lookup(Constant::gate_num_params, gate_op); + auto params = std::vector(num_params, param); + auto gate_matrix = getMatrix( + str_to_gates_.at(gate_name), params, inverse); + + StateVectorT kokkos_sv_ops{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + + std::vector controlled_wires = {4}; + std::vector controlled_values = {true}; + + const auto wires = + createWires(str_to_controlled_gates_.at(gate_name), num_qubits); + kokkos_sv_ops.applyOperation(gate_name, controlled_wires, + controlled_values, wires, inverse, params); + kokkos_sv_mat.applyOperation("Matrix", controlled_wires, + controlled_values, wires, false, {}, + gate_matrix); + + auto result_ops = kokkos_sv_ops.getDataVector(); + auto result_mat = kokkos_sv_mat.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_ops[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_ops[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + + DYNAMIC_SECTION("2-controlled Matrix (c {4, 5}) - Gate = " + << gate_name << " Inverse = " << inverse) { + auto gate_op = + reverse_lookup(Constant::gate_names, std::string_view{gate_name}); + auto num_params = lookup(Constant::gate_num_params, gate_op); + auto params = std::vector(num_params, param); + auto gate_matrix = getMatrix( + str_to_gates_.at(gate_name), params, inverse); + + StateVectorT kokkos_sv_ops{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + + std::vector controlled_wires = {4, 5}; + std::vector controlled_values = {true, false}; + + const auto wires = + createWires(str_to_controlled_gates_.at(gate_name), num_qubits); + kokkos_sv_ops.applyOperation(gate_name, controlled_wires, + controlled_values, wires, inverse, params); + kokkos_sv_mat.applyOperation("Matrix", controlled_wires, + controlled_values, wires, false, {}, + gate_matrix); + + auto result_ops = kokkos_sv_ops.getDataVector(); + auto result_mat = kokkos_sv_mat.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_ops[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_ops[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + + DYNAMIC_SECTION("2-controlled Matrix (c {4, 6}) - Gate = " + << gate_name << " Inverse = " << inverse) { + auto gate_op = + reverse_lookup(Constant::gate_names, std::string_view{gate_name}); + auto num_params = lookup(Constant::gate_num_params, gate_op); + auto params = std::vector(num_params, param); + auto gate_matrix = getMatrix( + str_to_gates_.at(gate_name), params, inverse); + + StateVectorT kokkos_sv_ops{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + + std::vector controlled_wires = {4, 6}; + std::vector controlled_values = {true, false}; + + const auto wires = + createWires(str_to_controlled_gates_.at(gate_name), num_qubits); + kokkos_sv_ops.applyOperation(gate_name, controlled_wires, + controlled_values, wires, inverse, params); + kokkos_sv_mat.applyOperation("Matrix", controlled_wires, + controlled_values, wires, false, {}, + gate_matrix); + + auto result_ops = kokkos_sv_ops.getDataVector(); + auto result_mat = kokkos_sv_mat.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_ops[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_ops[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } +} + TEMPLATE_TEST_CASE( "StateVectorKokkos::applyOperation param one-qubit with controls", "[StateVectorKokkos_Operation]", float, double) { @@ -1154,6 +1285,39 @@ TEMPLATE_TEST_CASE( } } } + + SECTION("N-controlled MultiRZ") { + std::vector wires = {control, wire0, wire1, wire2, wire3}; + std::sort(wires.begin(), wires.end()); + const ComplexT e = Kokkos::exp(ComplexT{0, -0.5} * param); + std::vector matrix(16, 0.0); + matrix[0] = e; + matrix[5] = conj(e); + matrix[10] = conj(e); + matrix[15] = e; + if (std::adjacent_find(wires.begin(), wires.end()) == wires.end()) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + kokkos_sv_mat.applyControlledMatrix( + matrix, {control, wire0, wire1}, + std::vector{true, false, true}, {wire2, wire3}, inverse); + kokkos_sv_op.applyOperation( + "MultiRZ", std::vector{control, wire0, wire1}, + std::vector{true, false, true}, + std::vector{wire2, wire3}, inverse, {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } } TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation param " diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/measurements/MeasurementsKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/measurements/MeasurementsKokkos.hpp index ee8684e814..0186c8a525 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/measurements/MeasurementsKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/measurements/MeasurementsKokkos.hpp @@ -135,9 +135,9 @@ class Measurements final */ auto getExpValMatrix(const KokkosVector matrix, const std::vector &wires) -> PrecisionT { - std::size_t num_qubits = this->_statevector.getNumQubits(); - std::size_t two2N = std::exp2(num_qubits - wires.size()); - std::size_t dim = std::exp2(wires.size()); + const std::size_t num_qubits = this->_statevector.getNumQubits(); + const std::size_t two2N = exp2(num_qubits - wires.size()); + const std::size_t dim = exp2(wires.size()); const KokkosVector arr_data = this->_statevector.getView(); PrecisionT expval = 0.0; diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp index acd71d6372..cb7217e89f 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp @@ -107,6 +107,39 @@ TEMPLATE_PRODUCT_TEST_CASE("StateVectorKokkos::applyMatrix with a std::vector", Catch::Contains( "The size of matrix does not match with the given")); } + + SECTION("Test with different number of wires") { + using KokkosVector = typename StateVectorT::KokkosVector; + const std::size_t num_qubits = 5; + for (std::size_t num_wires = 1; num_wires < num_qubits; num_wires++) { + VectorT st_data_1 = + createRandomStateVectorData(re, num_qubits); + VectorT st_data_2 = st_data_1; + StateVectorT state_vector_1( + reinterpret_cast(st_data_1.data()), + st_data_1.size()); + StateVectorT state_vector_2( + reinterpret_cast(st_data_2.data()), + st_data_2.size()); + + std::vector wires(num_wires); + std::iota(wires.begin(), wires.end(), 0); + + auto m = randomUnitary(re, num_wires); + std::vector mkvec(reinterpret_cast(m.data()), + reinterpret_cast(m.data()) + + m.size()); + KokkosVector mkview(reinterpret_cast(m.data()), + m.size()); + state_vector_1.applyMatrix(mkvec, wires); + state_vector_2.applyMultiQubitOp(mkview, wires); + + PrecisionT eps = std::numeric_limits::epsilon() * 10E3; + REQUIRE(isApproxEqual( + state_vector_1.getData(), state_vector_1.getLength(), + state_vector_2.getData(), state_vector_2.getLength(), eps)); + } + } } TEMPLATE_PRODUCT_TEST_CASE("StateVectorKokkos::setState", "[errors]", @@ -193,7 +226,7 @@ TEMPLATE_PRODUCT_TEST_CASE("StateVectorKokkos::applyMatrix with a pointer", m.size()); KokkosVector mkview(reinterpret_cast(m.data()), m.size()); - state_vector_1.applyMatrix(mkvec, wires); + state_vector_1.applyMatrix(mkvec.data(), wires); state_vector_2.applyMultiQubitOp(mkview, wires); PrecisionT eps = std::numeric_limits::epsilon() * 10E3; @@ -204,6 +237,146 @@ TEMPLATE_PRODUCT_TEST_CASE("StateVectorKokkos::applyMatrix with a pointer", } } +TEMPLATE_PRODUCT_TEST_CASE( + "StateVectorKokkos::applyControlledMatrix with a std::vector", + "[applyControlledMatrix]", (StateVectorKokkos), (float, double)) { + using StateVectorT = TestType; + using PrecisionT = typename StateVectorT::PrecisionT; + using ComplexT = typename StateVectorT::ComplexT; + using VectorT = TestVector>; + std::mt19937_64 re{1337}; + + SECTION("Test wrong matrix size") { + std::vector m(7, 0.0); + const std::size_t num_qubits = 4; + VectorT st_data = + createRandomStateVectorData(re, num_qubits); + StateVectorT state_vector(reinterpret_cast(st_data.data()), + st_data.size()); + REQUIRE_THROWS_WITH( + state_vector.applyControlledMatrix(m, {2}, {true}, {0, 1}), + Catch::Contains( + "The size of matrix does not match with the given")); + } + + SECTION("Test wrong number of wires") { + std::vector m(8, 0.0); + const std::size_t num_qubits = 4; + VectorT st_data = + createRandomStateVectorData(re, num_qubits); + + StateVectorT state_vector(reinterpret_cast(st_data.data()), + st_data.size()); + REQUIRE_THROWS_WITH( + state_vector.applyControlledMatrix(m, {2}, {true}, {0}), + Catch::Contains( + "The size of matrix does not match with the given")); + } + + SECTION("Test with different number of wires") { + using KokkosVector = typename StateVectorT::KokkosVector; + const std::size_t num_qubits = 5; + for (std::size_t num_wires = 1; num_wires < (num_qubits - 1); + num_wires++) { + VectorT st_data_1 = + createRandomStateVectorData(re, num_qubits); + VectorT st_data_2 = st_data_1; + StateVectorT state_vector_1( + reinterpret_cast(st_data_1.data()), + st_data_1.size()); + StateVectorT state_vector_2( + reinterpret_cast(st_data_2.data()), + st_data_2.size()); + + std::vector wires(num_wires); + std::iota(wires.begin(), wires.end(), 0); + std::vector controlled_wires(num_qubits - num_wires); + std::iota(controlled_wires.begin(), controlled_wires.end(), + num_wires); + std::vector controlled_values(num_qubits - num_wires, true); + + auto m = randomUnitary(re, num_wires); + std::vector mkvec(reinterpret_cast(m.data()), + reinterpret_cast(m.data()) + + m.size()); + KokkosVector mkview(reinterpret_cast(m.data()), + m.size()); + state_vector_1.applyControlledMatrix(mkvec, controlled_wires, + controlled_values, wires); + state_vector_2.applyNCMultiQubitOp(mkview, controlled_wires, + controlled_values, wires); + + PrecisionT eps = std::numeric_limits::epsilon() * 10E3; + REQUIRE(isApproxEqual( + state_vector_1.getData(), state_vector_1.getLength(), + state_vector_2.getData(), state_vector_2.getLength(), eps)); + } + } +} + +TEMPLATE_PRODUCT_TEST_CASE( + "StateVectorKokkos::applyControlledMatrix with a pointer", + "[applyControlledMatrix]", (StateVectorKokkos), (float, double)) { + using StateVectorT = TestType; + using PrecisionT = typename StateVectorT::PrecisionT; + using ComplexT = typename StateVectorT::ComplexT; + using VectorT = TestVector>; + std::mt19937_64 re{1337}; + + SECTION("Test wrong matrix") { + std::vector m(8, 0.0); + const std::size_t num_qubits = 4; + VectorT st_data = + createRandomStateVectorData(re, num_qubits); + + StateVectorT state_vector(reinterpret_cast(st_data.data()), + st_data.size()); + REQUIRE_THROWS_WITH( + state_vector.applyControlledMatrix(m.data(), {0}, {true}, {}), + Catch::Contains("must be larger than 0")); + } + + SECTION("Test with different number of wires") { + using KokkosVector = typename StateVectorT::KokkosVector; + const std::size_t num_qubits = 5; + for (std::size_t num_wires = 1; num_wires < (num_qubits - 1); + num_wires++) { + VectorT st_data_1 = + createRandomStateVectorData(re, num_qubits); + VectorT st_data_2 = st_data_1; + StateVectorT state_vector_1( + reinterpret_cast(st_data_1.data()), + st_data_1.size()); + StateVectorT state_vector_2( + reinterpret_cast(st_data_2.data()), + st_data_2.size()); + + std::vector wires(num_wires); + std::iota(wires.begin(), wires.end(), 0); + std::vector controlled_wires(num_qubits - num_wires); + std::iota(controlled_wires.begin(), controlled_wires.end(), + num_wires); + std::vector controlled_values(num_qubits - num_wires, true); + + auto m = randomUnitary(re, num_wires); + std::vector mkvec(reinterpret_cast(m.data()), + reinterpret_cast(m.data()) + + m.size()); + KokkosVector mkview(reinterpret_cast(m.data()), + m.size()); + state_vector_1.applyControlledMatrix(mkvec.data(), controlled_wires, + controlled_values, wires); + state_vector_2.applyNCMultiQubitOp(mkview, controlled_wires, + controlled_values, wires); + + PrecisionT eps = std::numeric_limits::epsilon() * 10E3; + REQUIRE(isApproxEqual( + state_vector_1.getData(), state_vector_1.getLength(), + state_vector_2.getData(), state_vector_2.getLength(), eps)); + } + } +} + TEMPLATE_PRODUCT_TEST_CASE("StateVectorKokkos::applyOperations", "[applyOperations invalid arguments]", (StateVectorKokkos), (float, double)) { @@ -279,6 +452,12 @@ TEMPLATE_PRODUCT_TEST_CASE("StateVectorKokkos::applyOperations", state_vector.applyOperation("GlobalPhaseShift", {0}, false, {0.0}), LightningException, "Operation does not exist for GlobalPhaseShift"); + + PL_REQUIRE_THROWS_MATCHES( + state_vector.applyOperation("GlobalPhaseShift", {0}, {false}, {1}, + false, {0.0}), + LightningException, + "Operation does not exist for GlobalPhaseShift"); } } diff --git a/pennylane_lightning/lightning_kokkos/_state_vector.py b/pennylane_lightning/lightning_kokkos/_state_vector.py index 9bfd1fa920..9bfb9879f5 100644 --- a/pennylane_lightning/lightning_kokkos/_state_vector.py +++ b/pennylane_lightning/lightning_kokkos/_state_vector.py @@ -197,13 +197,18 @@ def _apply_lightning_controlled(self, operation): control_wires = list(operation.control_wires) control_values = operation.control_values target_wires = list(operation.target_wires) + inv = False # TODO: update to use adjoint in C++ instead of in Python if method is not None: # apply n-controlled specialized gate - inv = False param = operation.parameters method(control_wires, control_values, target_wires, inv, param) - else: - raise qml.DeviceError( - "No gate operation supplied and controlled matrix not yet supported" + else: # apply gate as an n-controlled matrix + method = getattr(state, "applyControlledMatrix") + method( + qml.matrix(operation.base), + control_wires, + control_values, + target_wires, + inv, ) def _apply_lightning_midmeasure( @@ -284,10 +289,7 @@ def _apply_lightning( elif method is not None: # apply specialized gate param = operation.parameters method(wires, invert_param, param) - elif isinstance(operation, qml.ops.Controlled) and not isinstance( - operation.base, (qml.QubitUnitary, qml.BlockEncode) - ): # apply n-controlled gate - # Kokkos does not support controlled gates except for GlobalPhase and single-qubit + elif isinstance(operation, qml.ops.Controlled): # apply n-controlled gate self._apply_lightning_controlled(operation) else: # apply gate as a matrix # Inverse can be set to False since qml.matrix(operation) is already in diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 9d209bd68a..09d1b873f4 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -127,6 +127,7 @@ "C(DoubleExcitationMinus)", "C(DoubleExcitationPlus)", "C(MultiRZ)", + "C(QubitUnitary)", "C(GlobalPhase)", "CRot", "IsingXX", diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml b/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml index 9e805232a1..dd26df3455 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml @@ -28,7 +28,7 @@ PauliX = { properties = [ "invertible", "controllable", "differe PauliY = { properties = [ "invertible", "controllable", "differentiable" ] } PauliZ = { properties = [ "invertible", "controllable", "differentiable" ] } PhaseShift = { properties = [ "invertible", "controllable", "differentiable" ] } -QubitUnitary = { properties = [ "invertible", ] } +QubitUnitary = { properties = [ "invertible", "controllable" ] } Rot = { properties = [ "invertible", "controllable" ] } RX = { properties = [ "invertible", "controllable", "differentiable" ] } RY = { properties = [ "invertible", "controllable", "differentiable" ] } diff --git a/tests/lightning_qubit/test_measurements_class.py b/tests/lightning_qubit/test_measurements_class.py index b1532c5451..b2d073601f 100644 --- a/tests/lightning_qubit/test_measurements_class.py +++ b/tests/lightning_qubit/test_measurements_class.py @@ -798,10 +798,6 @@ def test_controlled_qubit_gates(self, operation, n_qubits, control_value, tol, l else: assert np.allclose(result, expected, tol * 10) - @pytest.mark.skipif( - device_name in ("lightning.kokkos"), - reason="N-controlled operations are not implemented in lightning.kokkos.", - ) def test_controlled_qubit_unitary_from_op(self, tol, lightning_sv): n_qubits = 10 par = 0.1234 diff --git a/tests/test_gates.py b/tests/test_gates.py index aca34f73b6..3f6451a178 100644 --- a/tests/test_gates.py +++ b/tests/test_gates.py @@ -405,10 +405,6 @@ def test_state_prep(n_targets, tol): assert np.allclose(res.ravel(), ref.ravel(), tol) -@pytest.mark.skipif( - device_name in ("lightning.kokkos"), - reason="N-controlled operations only implemented in lightning.qubit and lightning.gpu.", -) @pytest.mark.parametrize("control_value", [False, True]) @pytest.mark.parametrize("n_qubits", list(range(2, 8))) def test_controlled_qubit_unitary(n_qubits, control_value, tol): @@ -528,10 +524,6 @@ def circuit(): assert np.allclose(circ(), circ_def(), tol) -@pytest.mark.skipif( - device_name in ("lightning.kokkos"), - reason="N-controlled operations only implemented in lightning.qubit and lightning.gpu.", -) def test_controlled_qubit_unitary_from_op(tol): n_qubits = 10 dev_def = qml.device("default.qubit", wires=n_qubits) @@ -589,10 +581,6 @@ def test_paulirot(n_wires, n_targets, tol): assert np.allclose(dev.execute(tape1), dev.execute(tape0), tol) -@pytest.mark.skipif( - device_name in ("lightning.kokkos"), - reason="N-controlled operations are not implemented in lightning.kokkos.", -) @pytest.mark.parametrize("control_wires", range(4)) @pytest.mark.parametrize("target_wires", range(4)) def test_cnot_controlled_qubit_unitary(control_wires, target_wires, tol): From 8834735fd1392a9af9ee616841cd9505b8d2170f Mon Sep 17 00:00:00 2001 From: Joseph Lee <40768758+josephleekl@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:55:57 -0500 Subject: [PATCH 18/53] (LK-C-5) Add controlled generator (1-qubit) support for Lightning Kokkos (#956) ### Before submitting Please complete the following checklist when submitting a PR: - [ ] All new features must include a unit test. If you've fixed a bug or added code that should be tested, add a test to the [`tests`](../tests) directory! - [ ] All new functions and code must be clearly commented and documented. If you do make documentation changes, make sure that the docs build and render correctly by running `make docs`. - [ ] Ensure that the test suite passes, by running `make test`. - [ ] Add a new entry to the `.github/CHANGELOG.md` file, summarizing the change, and including a link back to the PR. - [ ] Ensure that code is properly formatted by running `make format`. When all the above are checked, delete everything above the dashed line and fill in the pull request template. ------------------------------------------------------------------------------------------------------------ **Context:** This PR adds support for 1-qubit controlled generator (RX/RY/RZ/PhaseShift/GlobalPhase) to Lightning Kokkos. **Description of the Change:** **Benefits:** **Possible Drawbacks:** **Related GitHub Issues:** [sc-76777] --- .../lightning_kokkos/StateVectorKokkos.hpp | 62 ++- .../gates/BasicGeneratorFunctors.hpp | 349 ++++++++++++----- .../Test_StateVectorKokkos_Generator.cpp | 359 ++++++++++++++++++ 3 files changed, 680 insertions(+), 90 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp index 0e99918203..882af5251d 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp @@ -631,6 +631,7 @@ class StateVectorKokkos final PL_ABORT_IF(wires.empty(), "Number of wires must be larger than 0"); const std::size_t n2 = exp2(wires.size() * 2); KokkosVector matrix_(matrix, n2); + Kokkos::deep_copy(matrix_, UnmanagedComplexHostView(matrix, n2)); applyNCMultiQubitOp(matrix_, controlled_wires, controlled_values, wires, inverse); } @@ -690,17 +691,68 @@ class StateVectorKokkos final * @param opName Name of gate to apply. * @param wires Wires to apply gate to. * @param inverse Indicates whether to use adjoint of gate. - * @param params Optional parameter list for parametric gates. */ auto applyGenerator(const std::string &opName, const std::vector &wires, - bool inverse = false, - const std::vector ¶ms = {}) -> fp_t { + bool inverse = false) -> PrecisionT { const std::size_t num_qubits = this->getNumQubits(); const GeneratorOperation generator_op = reverse_lookup(generator_names, std::string_view{opName}); - return applyNamedGenerator( - generator_op, *data_, num_qubits, wires, inverse, params); + return applyNamedGenerator(generator_op, *data_, + num_qubits, wires, inverse); + } + + /** + * @brief Apply a single controlled generator to the state vector using the + * given kernel. + * + * @param opName Name of gate to apply. + * @param controlled_wires Control wires. + * @param controlled_values Control values (true or false). + * @param wires Wires to apply gate to. + * @param inverse Indicates whether to use adjoint of gate. + */ + auto + applyControlledGenerator(const std::string &opName, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + bool inverse = false) -> PrecisionT { + const std::size_t num_qubits = this->getNumQubits(); + const ControlledGeneratorOperation generator_op = reverse_lookup( + controlled_generator_names, std::string_view{opName}); + return applyNCNamedGenerator( + generator_op, *data_, num_qubits, controlled_wires, + controlled_values, wires, inverse); + } + + /** + * @brief Apply a single generator to the state vector using the given + * kernel. + * + * @param opName Name of gate to apply. + * @param controlled_wires Control wires. + * @param controlled_values Control values (true or false). + * @param wires Wires to apply gate to. + * @param inverse Indicates whether to use adjoint of gate. + */ + auto applyGenerator(const std::string &opName, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + bool inverse = false) -> PrecisionT { + PL_ABORT_IF_NOT( + areVecsDisjoint(controlled_wires, wires), + "`controlled_wires` and `target wires` must be disjoint."); + PL_ABORT_IF_NOT(controlled_wires.size() == controlled_values.size(), + "`controlled_wires` must have the same size as " + "`controlled_values`."); + if (controlled_wires.empty()) { + return applyGenerator(opName, wires, inverse); + } else { + return applyControlledGenerator(opName, controlled_wires, + controlled_values, wires, inverse); + } } /** diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp index 076ca88487..de7fea6d18 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp @@ -31,11 +31,10 @@ using Pennylane::Gates::GeneratorOperation; namespace Pennylane::LightningKokkos::Functors { template -void applyGenPhaseShift( - Kokkos::View *> arr_, std::size_t num_qubits, - const std::vector &wires, - [[maybe_unused]] bool inverse = false, - [[maybe_unused]] const std::vector ¶ms = {}) { +void applyGenPhaseShift(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, std::size_t i0, std::size_t i1) { @@ -50,8 +49,7 @@ template void applyGenControlledPhaseShift( Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] bool inverse = false, - [[maybe_unused]] const std::vector ¶ms = {}) { + [[maybe_unused]] bool inverse = false) { auto core_function = KOKKOS_LAMBDA( Kokkos::View *> arr, std::size_t i00, std::size_t i01, std::size_t i10, std::size_t i11) { @@ -68,8 +66,7 @@ void applyGenControlledPhaseShift( template void applyGenCRX(Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] bool inverse = false, - [[maybe_unused]] const std::vector ¶ms = {}) { + [[maybe_unused]] bool inverse = false) { auto core_function = KOKKOS_LAMBDA( Kokkos::View *> arr, std::size_t i00, std::size_t i01, std::size_t i10, std::size_t i11) { @@ -84,8 +81,7 @@ void applyGenCRX(Kokkos::View *> arr_, template void applyGenCRY(Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] bool inverse = false, - [[maybe_unused]] const std::vector ¶ms = {}) { + [[maybe_unused]] bool inverse = false) { auto core_function = KOKKOS_LAMBDA( Kokkos::View *> arr, std::size_t i00, std::size_t i01, std::size_t i10, std::size_t i11) { @@ -102,8 +98,7 @@ void applyGenCRY(Kokkos::View *> arr_, template void applyGenCRZ(Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] bool inverse = false, - [[maybe_unused]] const std::vector ¶ms = {}) { + [[maybe_unused]] bool inverse = false) { auto core_function = KOKKOS_LAMBDA( Kokkos::View *> arr, std::size_t i00, std::size_t i01, std::size_t i10, std::size_t i11) { @@ -117,11 +112,10 @@ void applyGenCRZ(Kokkos::View *> arr_, } template -void applyGenIsingXX( - Kokkos::View *> arr_, std::size_t num_qubits, - const std::vector &wires, - [[maybe_unused]] bool inverse = false, - [[maybe_unused]] const std::vector ¶ms = {}) { +void applyGenIsingXX(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { auto core_function = KOKKOS_LAMBDA( Kokkos::View *> arr, std::size_t i00, std::size_t i01, std::size_t i10, std::size_t i11) { @@ -133,11 +127,10 @@ void applyGenIsingXX( } template -void applyGenIsingXY( - Kokkos::View *> arr_, std::size_t num_qubits, - const std::vector &wires, - [[maybe_unused]] bool inverse = false, - [[maybe_unused]] const std::vector ¶ms = {}) { +void applyGenIsingXY(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { auto core_function = KOKKOS_LAMBDA( Kokkos::View *> arr, std::size_t i00, std::size_t i01, std::size_t i10, std::size_t i11) { @@ -150,11 +143,10 @@ void applyGenIsingXY( } template -void applyGenIsingYY( - Kokkos::View *> arr_, std::size_t num_qubits, - const std::vector &wires, - [[maybe_unused]] bool inverse = false, - [[maybe_unused]] const std::vector ¶ms = {}) { +void applyGenIsingYY(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { auto core_function = KOKKOS_LAMBDA( Kokkos::View *> arr, std::size_t i00, std::size_t i01, std::size_t i10, std::size_t i11) { @@ -168,11 +160,10 @@ void applyGenIsingYY( } template -void applyGenIsingZZ( - Kokkos::View *> arr_, std::size_t num_qubits, - const std::vector &wires, - [[maybe_unused]] bool inverse = false, - [[maybe_unused]] const std::vector ¶ms = {}) { +void applyGenIsingZZ(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { auto core_function = KOKKOS_LAMBDA( Kokkos::View *> arr, std::size_t i00, std::size_t i01, std::size_t i10, std::size_t i11) { @@ -187,11 +178,10 @@ void applyGenIsingZZ( } template -void applyGenSingleExcitation( - Kokkos::View *> arr_, std::size_t num_qubits, - const std::vector &wires, - [[maybe_unused]] bool inverse = false, - [[maybe_unused]] const std::vector ¶ms = {}) { +void applyGenSingleExcitation(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { auto core_function = KOKKOS_LAMBDA( Kokkos::View *> arr, std::size_t i00, std::size_t i01, std::size_t i10, std::size_t i11) { @@ -209,8 +199,7 @@ template void applyGenSingleExcitationMinus( Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] bool inverse = false, - [[maybe_unused]] const std::vector ¶ms = {}) { + [[maybe_unused]] bool inverse = false) { auto core_function = KOKKOS_LAMBDA( Kokkos::View *> arr, std::size_t i00, std::size_t i01, std::size_t i10, std::size_t i11) { @@ -229,8 +218,7 @@ template void applyGenSingleExcitationPlus( Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] bool inverse = false, - [[maybe_unused]] const std::vector ¶ms = {}) { + [[maybe_unused]] bool inverse = false) { auto core_function = KOKKOS_LAMBDA( Kokkos::View *> arr, std::size_t i00, std::size_t i01, std::size_t i10, std::size_t i11) { @@ -248,11 +236,10 @@ void applyGenSingleExcitationPlus( } template -void applyGenDoubleExcitation( - Kokkos::View *> arr_, std::size_t num_qubits, - const std::vector &wires, - [[maybe_unused]] bool inverse = false, - [[maybe_unused]] const std::vector ¶ms = {}) { +void applyGenDoubleExcitation(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { auto core_function = KOKKOS_LAMBDA( Kokkos::View *> arr, std::size_t i0000, std::size_t i0001, std::size_t i0010, std::size_t i0011, @@ -287,8 +274,7 @@ template void applyGenDoubleExcitationMinus( Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] bool inverse = false, - [[maybe_unused]] const std::vector ¶ms = {}) { + [[maybe_unused]] bool inverse = false) { auto core_function = KOKKOS_LAMBDA( Kokkos::View *> arr, std::size_t i0000, std::size_t i0001, std::size_t i0010, std::size_t i0011, @@ -322,8 +308,7 @@ template void applyGenDoubleExcitationPlus( Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &wires, - [[maybe_unused]] bool inverse = false, - [[maybe_unused]] const std::vector ¶ms = {}) { + [[maybe_unused]] bool inverse = false) { auto core_function = KOKKOS_LAMBDA( Kokkos::View *> arr, std::size_t i0000, std::size_t i0001, std::size_t i0010, std::size_t i0011, @@ -354,11 +339,10 @@ void applyGenDoubleExcitationPlus( } template -void applyGenMultiRZ( - Kokkos::View *> arr_, std::size_t num_qubits, - const std::vector &wires, - [[maybe_unused]] bool inverse = false, - [[maybe_unused]] const std::vector ¶ms = {}) { +void applyGenMultiRZ(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { std::size_t wires_parity = static_cast(0U); for (std::size_t wire : wires) { wires_parity |= exp2(num_qubits - wire - 1); @@ -371,93 +355,288 @@ void applyGenMultiRZ( }); } +template class applyNCGenerator1Functor { + using KokkosIntVector = Kokkos::View; + + Kokkos::View *> arr; + const FuncT core_function; + KokkosIntVector indices; + KokkosIntVector parity; + KokkosIntVector rev_wires; + KokkosIntVector rev_wire_shifts; + std::size_t mask{0U}; + static constexpr std::size_t one{1U}; + std::size_t i0; + std::size_t i1; + + public: + template + applyNCGenerator1Functor([[maybe_unused]] ExecutionSpace exec, + Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + FuncT core_function_) + : arr(arr_), core_function(core_function_) { + const std::size_t n_wires = wires.size(); + const std::size_t nw_tot = + controlled_wires.size() + n_wires; // n_contr + n_wires + PL_ASSERT(n_wires == 1); + PL_ASSERT(num_qubits >= nw_tot); + + std::vector all_wires; + all_wires.reserve(nw_tot); + all_wires.insert(all_wires.begin(), wires.begin(), wires.end()); + all_wires.insert(all_wires.begin(), controlled_wires.begin(), + controlled_wires.end()); + + const auto &[parity_, rev_wires_] = + reverseWires(num_qubits, wires, controlled_wires); + parity = parity_; + const std::vector indices_ = + generateBitPatterns(all_wires, num_qubits); + + std::size_t k = 0; + mask = std::accumulate( + controlled_values.rbegin(), controlled_values.rend(), + std::size_t{0}, [&k](std::size_t acc, std::size_t value) { + return acc | (static_cast(value) << k++); + }); + + i0 = indices_[mask << one]; + i1 = indices_[(mask << one) | one]; + indices = vector2view(indices_); + Kokkos::parallel_for( + Kokkos::RangePolicy( + 0, exp2(num_qubits - controlled_wires.size() - wires.size())), + *this); + } + KOKKOS_FUNCTION void operator()(std::size_t k) const { + const std::size_t offset = parity_2_offset(parity, k); + for (std::size_t i = 0; i < indices.size(); i++) { + if ((i >> one) == mask) { + continue; + } + arr(indices(i) + offset) = 0.0; + } + core_function(arr, i0 + offset, i1 + offset); + } +}; + +template +void applyNCGenRX(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { + auto core_function = + KOKKOS_LAMBDA(Kokkos::View *> arr, + std::size_t i0, std::size_t i1) { + kokkos_swap(arr(i0), arr(i1)); + }; + applyNCGenerator1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, controlled_values, + wires, core_function); +} + +template +void applyNCGenRY(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { + auto core_function = + KOKKOS_LAMBDA(Kokkos::View *> arr, + std::size_t i0, std::size_t i1) { + const auto v0 = arr(i0); + const auto v1 = arr(i1); + arr(i0) = Kokkos::complex{imag(v1), -real(v1)}; + arr(i1) = Kokkos::complex{-imag(v0), real(v0)}; + }; + applyNCGenerator1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, controlled_values, + wires, core_function); +} + +template +void applyNCGenRZ(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { + auto core_function = + KOKKOS_LAMBDA(Kokkos::View *> arr, + std::size_t i0, std::size_t i1) { + [[maybe_unused]] const auto i0_ = i0; + arr(i1) *= -1; + }; + applyNCGenerator1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, controlled_values, + wires, core_function); +} + +template +void applyNCGenPhaseShift(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { + auto core_function = + KOKKOS_LAMBDA(Kokkos::View *> arr, + std::size_t i0, std::size_t i1) { + [[maybe_unused]] const auto i1_ = i1; + arr[i0] = 0.0; + }; + applyNCGenerator1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, controlled_values, + wires, core_function); +} + +template +void applyNCGenGlobalPhase( + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + [[maybe_unused]] const std::vector &wires, + [[maybe_unused]] bool inverse = false) { + auto core_function = KOKKOS_LAMBDA( + [[maybe_unused]] Kokkos::View *> arr, + std::size_t i0, std::size_t i1) { + [[maybe_unused]] const auto i0_ = i0; + [[maybe_unused]] const auto i1_ = i1; + }; + std::size_t target{0U}; + if (!controlled_wires.empty()) { + for (std::size_t i = 0; i < num_qubits; i++) { + if (std::find(controlled_wires.begin(), controlled_wires.end(), + i) == controlled_wires.end()) { + target = i; + break; + } + } + } + applyNCGenerator1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, controlled_values, + {target}, core_function); +} + template PrecisionT applyNamedGenerator(const GeneratorOperation generator_op, Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &wires, - bool inverse = false, - const std::vector ¶ms = {}) { + bool inverse = false) { switch (generator_op) { case GeneratorOperation::RX: applyNamedOperation(GateOperation::PauliX, arr_, - num_qubits, wires, inverse, params); + num_qubits, wires, inverse); return -static_cast(0.5); case GeneratorOperation::RY: applyNamedOperation(GateOperation::PauliY, arr_, - num_qubits, wires, inverse, params); + num_qubits, wires, inverse); return -static_cast(0.5); case GeneratorOperation::RZ: applyNamedOperation(GateOperation::PauliZ, arr_, - num_qubits, wires, inverse, params); + num_qubits, wires, inverse); return -static_cast(0.5); case GeneratorOperation::PhaseShift: - applyGenPhaseShift(arr_, num_qubits, wires, inverse, - params); + applyGenPhaseShift(arr_, num_qubits, wires, inverse); return static_cast(1.0); case GeneratorOperation::ControlledPhaseShift: applyGenControlledPhaseShift(arr_, num_qubits, wires, - inverse, params); + inverse); return static_cast(1); case GeneratorOperation::CRX: - applyGenCRX(arr_, num_qubits, wires, inverse, params); + applyGenCRX(arr_, num_qubits, wires, inverse); return -static_cast(0.5); case GeneratorOperation::CRY: - applyGenCRY(arr_, num_qubits, wires, inverse, params); + applyGenCRY(arr_, num_qubits, wires, inverse); return -static_cast(0.5); case GeneratorOperation::CRZ: - applyGenCRZ(arr_, num_qubits, wires, inverse, params); + applyGenCRZ(arr_, num_qubits, wires, inverse); return -static_cast(0.5); case GeneratorOperation::IsingXX: - applyGenIsingXX(arr_, num_qubits, wires, inverse, - params); + applyGenIsingXX(arr_, num_qubits, wires, inverse); return -static_cast(0.5); case GeneratorOperation::IsingXY: - applyGenIsingXY(arr_, num_qubits, wires, inverse, - params); + applyGenIsingXY(arr_, num_qubits, wires, inverse); return static_cast(0.5); case GeneratorOperation::IsingYY: - applyGenIsingYY(arr_, num_qubits, wires, inverse, - params); + applyGenIsingYY(arr_, num_qubits, wires, inverse); return -static_cast(0.5); case GeneratorOperation::IsingZZ: - applyGenIsingZZ(arr_, num_qubits, wires, inverse, - params); + applyGenIsingZZ(arr_, num_qubits, wires, inverse); return -static_cast(0.5); case GeneratorOperation::SingleExcitation: applyGenSingleExcitation(arr_, num_qubits, wires, - inverse, params); + inverse); return -static_cast(0.5); case GeneratorOperation::SingleExcitationMinus: applyGenSingleExcitationMinus(arr_, num_qubits, wires, - inverse, params); + inverse); return -static_cast(0.5); case GeneratorOperation::SingleExcitationPlus: applyGenSingleExcitationPlus(arr_, num_qubits, wires, - inverse, params); + inverse); return -static_cast(0.5); case GeneratorOperation::DoubleExcitation: applyGenDoubleExcitation(arr_, num_qubits, wires, - inverse, params); + inverse); return -static_cast(0.5); case GeneratorOperation::DoubleExcitationMinus: applyGenDoubleExcitationMinus(arr_, num_qubits, wires, - inverse, params); + inverse); return -static_cast(0.5); case GeneratorOperation::DoubleExcitationPlus: applyGenDoubleExcitationPlus(arr_, num_qubits, wires, - inverse, params); + inverse); return static_cast(0.5); case GeneratorOperation::MultiRZ: - applyGenMultiRZ(arr_, num_qubits, wires, inverse, - params); + applyGenMultiRZ(arr_, num_qubits, wires, inverse); return -static_cast(0.5); case GeneratorOperation::GlobalPhase: return static_cast(-1.0); - /// LCOV_EXCL_START default: PL_ABORT("Generator operation does not exist."); - /// LCOV_EXCL_STOP + } +} + +template +PrecisionT applyNCNamedGenerator( + const ControlledGeneratorOperation generator_op, + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, bool inverse = false) { + switch (generator_op) { + case ControlledGeneratorOperation::RX: + applyNCGenRX(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse); + return -static_cast(0.5); + case ControlledGeneratorOperation::RY: + applyNCGenRY(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse); + return -static_cast(0.5); + case ControlledGeneratorOperation::RZ: + applyNCGenRZ(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse); + return -static_cast(0.5); + case ControlledGeneratorOperation::PhaseShift: + applyNCGenPhaseShift(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse); + return static_cast(1.0); + case ControlledGeneratorOperation::GlobalPhase: + applyNCGenGlobalPhase( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse); + return static_cast(-1.0); + default: + PL_ABORT("Controlled generator operation does not exist."); } } diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Generator.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Generator.cpp index 8546ee8514..832713179c 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Generator.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Generator.cpp @@ -52,7 +52,16 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyGenerator - errors", PL_REQUIRE_THROWS_MATCHES(state_vector.applyGenerator("XXX", {0}), LightningException, "The given value does not exist."); + PL_REQUIRE_THROWS_MATCHES( + state_vector.applyGenerator("XXX", {1}, {true}, {0}), + LightningException, "The given value does not exist."); + PL_REQUIRE_THROWS_MATCHES( + state_vector.applyGenerator("PauliX", {}, {false}, {1}, false), + LightningException, + "`controlled_wires` must have the same size " + "as"); // invalid controlled_wires } + SECTION("namedGeneratorFactor") { using StateVectorT = StateVectorKokkos; using KokkosVector = StateVectorT::KokkosVector; @@ -64,6 +73,19 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyGenerator - errors", LightningException, "Generator operation does not exist."); } + + SECTION("NCnamedGeneratorFactor") { + using StateVectorT = StateVectorKokkos; + using KokkosVector = StateVectorT::KokkosVector; + using ExecutionSpace = StateVectorT::KokkosExecSpace; + [[maybe_unused]] StateVectorT sv{2}; + KokkosVector arr_("arr_", 4); + PL_REQUIRE_THROWS_MATCHES( + applyNCNamedGenerator( + ControlledGeneratorOperation::END, arr_, 2, {1}, {true}, {0}), + LightningException, + "Controlled generator operation does not exist."); + } } TEMPLATE_TEST_CASE("StateVectorKokkos::applyGenerator", @@ -412,3 +434,340 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyGeneratorDoubleExcitationPlus", } } } + +TEMPLATE_TEST_CASE("StateVectorKokkos::applyControlledGenerator", + "[StateVectorKokkos_Generator]", float, double) { + using StateVectorT = StateVectorKokkos; + const std::size_t num_qubits = 6; + const TestType ep_deriv = 1e-3; + const TestType ep_margin = 1e-4; + + auto ini_st = createNonTrivialState(num_qubits); + + std::unordered_map + str_to_controlled_gates_{}; + for (const auto &[gate_op, controlled_gate_name] : + Constant::controlled_gate_names) { + str_to_controlled_gates_.emplace(controlled_gate_name, gate_op); + } + + const bool inverse = GENERATE(true, false); + const std::string controlled_gate_name = + GENERATE("RX", "RY", "RZ", "PhaseShift", "GlobalPhase"); + + SECTION("1-control: c{5}") { + StateVectorKokkos kokkos_gntr_sv{ini_st.data(), + ini_st.size()}; + StateVectorKokkos kokkos_gate_svp{ini_st.data(), + ini_st.size()}; + StateVectorKokkos kokkos_gate_svm{ini_st.data(), + ini_st.size()}; + + const auto wires = createWires( + str_to_controlled_gates_.at(controlled_gate_name), num_qubits); + const std::vector control_wires = {5}; + const std::vector control_values = {true}; + auto scale = + kokkos_gntr_sv.applyGenerator(controlled_gate_name, control_wires, + control_values, wires, inverse); + auto h = static_cast(((inverse) ? -1.0 : 1.0) * ep_deriv); + kokkos_gate_svp.applyOperation(controlled_gate_name, control_wires, + control_values, wires, inverse, {h}); + kokkos_gate_svm.applyOperation(controlled_gate_name, control_wires, + control_values, wires, inverse, {-h}); + + auto result_gntr_sv = kokkos_gntr_sv.getDataVector(); + auto result_gate_svp = kokkos_gate_svp.getDataVector(); + auto result_gate_svm = kokkos_gate_svm.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(-scale * imag(result_gntr_sv[j]) == + Approx(0.5 * + (real(result_gate_svp[j]) - real(result_gate_svm[j])) / + ep_deriv) + .margin(ep_margin)); + CHECK(scale * real(result_gntr_sv[j]) == + Approx(0.5 * + (imag(result_gate_svp[j]) - imag(result_gate_svm[j])) / + ep_deriv) + .margin(ep_margin)); + } + } + + SECTION("2-control: c{4,5}") { + StateVectorKokkos kokkos_gntr_sv{ini_st.data(), + ini_st.size()}; + StateVectorKokkos kokkos_gate_svp{ini_st.data(), + ini_st.size()}; + StateVectorKokkos kokkos_gate_svm{ini_st.data(), + ini_st.size()}; + + const auto wires = createWires( + str_to_controlled_gates_.at(controlled_gate_name), num_qubits); + const std::vector control_wires = {4, 5}; + const std::vector control_values = {true, false}; + auto scale = + kokkos_gntr_sv.applyGenerator(controlled_gate_name, control_wires, + control_values, wires, inverse); + auto h = static_cast(((inverse) ? -1.0 : 1.0) * ep_deriv); + kokkos_gate_svp.applyOperation(controlled_gate_name, control_wires, + control_values, wires, inverse, {h}); + kokkos_gate_svm.applyOperation(controlled_gate_name, control_wires, + control_values, wires, inverse, {-h}); + + auto result_gntr_sv = kokkos_gntr_sv.getDataVector(); + auto result_gate_svp = kokkos_gate_svp.getDataVector(); + auto result_gate_svm = kokkos_gate_svm.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(-scale * imag(result_gntr_sv[j]) == + Approx(0.5 * + (real(result_gate_svp[j]) - real(result_gate_svm[j])) / + ep_deriv) + .margin(ep_margin)); + CHECK(scale * real(result_gntr_sv[j]) == + Approx(0.5 * + (imag(result_gate_svp[j]) - imag(result_gate_svm[j])) / + ep_deriv) + .margin(ep_margin)); + } + } + + SECTION("2-control: c{3,5}") { + StateVectorKokkos kokkos_gntr_sv{ini_st.data(), + ini_st.size()}; + StateVectorKokkos kokkos_gate_svp{ini_st.data(), + ini_st.size()}; + StateVectorKokkos kokkos_gate_svm{ini_st.data(), + ini_st.size()}; + + const auto wires = createWires( + str_to_controlled_gates_.at(controlled_gate_name), num_qubits); + const std::vector control_wires = {3, 5}; + const std::vector control_values = {true, false}; + auto scale = + kokkos_gntr_sv.applyGenerator(controlled_gate_name, control_wires, + control_values, wires, inverse); + auto h = static_cast(((inverse) ? -1.0 : 1.0) * ep_deriv); + kokkos_gate_svp.applyOperation(controlled_gate_name, control_wires, + control_values, wires, inverse, {h}); + kokkos_gate_svm.applyOperation(controlled_gate_name, control_wires, + control_values, wires, inverse, {-h}); + + auto result_gntr_sv = kokkos_gntr_sv.getDataVector(); + auto result_gate_svp = kokkos_gate_svp.getDataVector(); + auto result_gate_svm = kokkos_gate_svm.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(-scale * imag(result_gntr_sv[j]) == + Approx(0.5 * + (real(result_gate_svp[j]) - real(result_gate_svm[j])) / + ep_deriv) + .margin(ep_margin)); + CHECK(scale * real(result_gntr_sv[j]) == + Approx(0.5 * + (imag(result_gate_svp[j]) - imag(result_gate_svm[j])) / + ep_deriv) + .margin(ep_margin)); + } + } + + SECTION("3-control: c{3,4,5}") { + StateVectorKokkos kokkos_gntr_sv{ini_st.data(), + ini_st.size()}; + StateVectorKokkos kokkos_gate_svp{ini_st.data(), + ini_st.size()}; + StateVectorKokkos kokkos_gate_svm{ini_st.data(), + ini_st.size()}; + + const auto wires = createWires( + str_to_controlled_gates_.at(controlled_gate_name), num_qubits); + const std::vector control_wires = {3, 4, 5}; + const std::vector control_values = {true, false, true}; + auto scale = + kokkos_gntr_sv.applyGenerator(controlled_gate_name, control_wires, + control_values, wires, inverse); + auto h = static_cast(((inverse) ? -1.0 : 1.0) * ep_deriv); + kokkos_gate_svp.applyOperation(controlled_gate_name, control_wires, + control_values, wires, inverse, {h}); + kokkos_gate_svm.applyOperation(controlled_gate_name, control_wires, + control_values, wires, inverse, {-h}); + + auto result_gntr_sv = kokkos_gntr_sv.getDataVector(); + auto result_gate_svp = kokkos_gate_svp.getDataVector(); + auto result_gate_svm = kokkos_gate_svm.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(-scale * imag(result_gntr_sv[j]) == + Approx(0.5 * + (real(result_gate_svp[j]) - real(result_gate_svm[j])) / + ep_deriv) + .margin(ep_margin)); + CHECK(scale * real(result_gntr_sv[j]) == + Approx(0.5 * + (imag(result_gate_svp[j]) - imag(result_gate_svm[j])) / + ep_deriv) + .margin(ep_margin)); + } + } +} + +TEMPLATE_TEST_CASE("StateVectorKokkos::applyControlledGenerator empty control", + "[StateVectorKokkos_Generator]", float, double) { + using StateVectorT = StateVectorKokkos; + const std::size_t num_qubits = 5; + const TestType ep_deriv = 1e-3; + const TestType ep_margin = 1e-4; + + auto ini_st = createNonTrivialState(num_qubits); + + std::unordered_map + str_to_controlled_gates_{}; + for (const auto &[gate_op, controlled_gate_name] : + Constant::controlled_gate_names) { + str_to_controlled_gates_.emplace(controlled_gate_name, gate_op); + } + + const bool inverse = GENERATE(true, false); + const std::string controlled_gate_name = + GENERATE("RX", "RY", "RZ", "PhaseShift", "GlobalPhase"); + { + StateVectorKokkos kokkos_gntr_sv{ini_st.data(), + ini_st.size()}; + StateVectorKokkos kokkos_gate_svp{ini_st.data(), + ini_st.size()}; + StateVectorKokkos kokkos_gate_svm{ini_st.data(), + ini_st.size()}; + + const auto wires = createWires( + str_to_controlled_gates_.at(controlled_gate_name), num_qubits); + const std::vector control_wires = {}; + const std::vector control_values = {}; + auto scale = + kokkos_gntr_sv.applyGenerator(controlled_gate_name, control_wires, + control_values, wires, inverse); + auto h = static_cast(((inverse) ? -1.0 : 1.0) * ep_deriv); + kokkos_gate_svp.applyOperation(controlled_gate_name, control_wires, + control_values, wires, inverse, {h}); + kokkos_gate_svm.applyOperation(controlled_gate_name, control_wires, + control_values, wires, inverse, {-h}); + + auto result_gntr_sv = kokkos_gntr_sv.getDataVector(); + auto result_gate_svp = kokkos_gate_svp.getDataVector(); + auto result_gate_svm = kokkos_gate_svm.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(-scale * imag(result_gntr_sv[j]) == + Approx(0.5 * + (real(result_gate_svp[j]) - real(result_gate_svm[j])) / + ep_deriv) + .margin(ep_margin)); + CHECK(scale * real(result_gntr_sv[j]) == + Approx(0.5 * + (imag(result_gate_svp[j]) - imag(result_gate_svm[j])) / + ep_deriv) + .margin(ep_margin)); + } + } +} + +TEMPLATE_TEST_CASE("StateVectorKokkos::applyControlledGenerator CRX/Y/Z", + "[StateVectorKokkos_Generator]", float, double) { + using StateVectorT = StateVectorKokkos; + const std::size_t num_qubits = 3; + const TestType ep_margin = 1e-4; + + auto ini_st = createNonTrivialState(num_qubits); + const bool inverse = GENERATE(true, false); + const std::size_t control_wire = GENERATE(0, 1, 2); + const std::size_t wire = GENERATE(0, 1, 2); + SECTION("CRX") { + StateVectorKokkos kokkos_gntr_sv_cr{ini_st.data(), + ini_st.size()}; + StateVectorKokkos kokkos_gntr_sv_r{ini_st.data(), + ini_st.size()}; + + if (control_wire == wire) { + PL_REQUIRE_THROWS_MATCHES( + kokkos_gntr_sv_r.applyGenerator("RX", {control_wire}, {true}, + {wire}, inverse), + LightningException, + "`controlled_wires` and `target wires` must be disjoint."); + } else { + kokkos_gntr_sv_cr.applyGenerator("CRX", {control_wire, wire}, + inverse); + kokkos_gntr_sv_r.applyGenerator("RX", {control_wire}, {true}, + {wire}, inverse); + + auto result_gntr_sv_cr = kokkos_gntr_sv_cr.getDataVector(); + auto result_gntr_sv_r = kokkos_gntr_sv_r.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(result_gntr_sv_cr[j]) == + Approx(imag(result_gntr_sv_r[j])).margin(ep_margin)); + CHECK(real(result_gntr_sv_cr[j]) == + Approx(real(result_gntr_sv_r[j])).margin(ep_margin)); + } + } + } + + SECTION("CRY") { + StateVectorKokkos kokkos_gntr_sv_cr{ini_st.data(), + ini_st.size()}; + StateVectorKokkos kokkos_gntr_sv_r{ini_st.data(), + ini_st.size()}; + + if (control_wire == wire) { + PL_REQUIRE_THROWS_MATCHES( + kokkos_gntr_sv_r.applyGenerator("RX", {control_wire}, {true}, + {wire}, inverse), + LightningException, + "`controlled_wires` and `target wires` must be disjoint."); + } else { + kokkos_gntr_sv_cr.applyGenerator("CRY", {control_wire, wire}, + inverse); + kokkos_gntr_sv_r.applyGenerator("RY", {control_wire}, {true}, + {wire}, inverse); + + auto result_gntr_sv_cr = kokkos_gntr_sv_cr.getDataVector(); + auto result_gntr_sv_r = kokkos_gntr_sv_r.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(result_gntr_sv_cr[j]) == + Approx(imag(result_gntr_sv_r[j])).margin(ep_margin)); + CHECK(real(result_gntr_sv_cr[j]) == + Approx(real(result_gntr_sv_r[j])).margin(ep_margin)); + } + } + } + + SECTION("CRZ") { + StateVectorKokkos kokkos_gntr_sv_cr{ini_st.data(), + ini_st.size()}; + StateVectorKokkos kokkos_gntr_sv_r{ini_st.data(), + ini_st.size()}; + + if (control_wire == wire) { + PL_REQUIRE_THROWS_MATCHES( + kokkos_gntr_sv_r.applyGenerator("RX", {control_wire}, {true}, + {wire}, inverse), + LightningException, + "`controlled_wires` and `target wires` must be disjoint."); + } else { + kokkos_gntr_sv_cr.applyGenerator("CRZ", {control_wire, wire}, + inverse); + kokkos_gntr_sv_r.applyGenerator("RZ", {control_wire}, {true}, + {wire}, inverse); + + auto result_gntr_sv_cr = kokkos_gntr_sv_cr.getDataVector(); + auto result_gntr_sv_r = kokkos_gntr_sv_r.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(result_gntr_sv_cr[j]) == + Approx(imag(result_gntr_sv_r[j])).margin(ep_margin)); + CHECK(real(result_gntr_sv_cr[j]) == + Approx(real(result_gntr_sv_r[j])).margin(ep_margin)); + } + } + } +} From 2fd51fe3494e4fc7363b6c85a568ae55c4b26aec Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Fri, 22 Nov 2024 15:56:14 +0000 Subject: [PATCH 19/53] Auto update version from '0.40.0-dev12' to '0.40.0-dev14' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 8637df0642..46600effac 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev12" +__version__ = "0.40.0-dev14" From 873c6fbf3678d06374a4d4ba79fd962cb62b382f Mon Sep 17 00:00:00 2001 From: Joseph Lee <40768758+josephleekl@users.noreply.github.com> Date: Fri, 22 Nov 2024 13:21:41 -0500 Subject: [PATCH 20/53] (LK-C-6) Add controlled generator (multi-qubit) support for Lightning Kokkos (#958) ### Before submitting Please complete the following checklist when submitting a PR: - [ ] All new features must include a unit test. If you've fixed a bug or added code that should be tested, add a test to the [`tests`](../tests) directory! - [ ] All new functions and code must be clearly commented and documented. If you do make documentation changes, make sure that the docs build and render correctly by running `make docs`. - [ ] Ensure that the test suite passes, by running `make test`. - [ ] Add a new entry to the `.github/CHANGELOG.md` file, summarizing the change, and including a link back to the PR. - [ ] Ensure that code is properly formatted by running `make format`. When all the above are checked, delete everything above the dashed line and fill in the pull request template. ------------------------------------------------------------------------------------------------------------ **Context:** This PR adds support for 2/4-qubit controlled generator (e.g. IsingXX / SingleExcitation / DoubleExcitation) and MultiRZ to Lightning Kokkos. **Description of the Change:** **Benefits:** **Possible Drawbacks:** **Related GitHub Issues:** [sc-76778] --- .../gates/BasicGeneratorFunctors.hpp | 488 +++++++++++++++++- .../Test_StateVectorKokkos_Generator.cpp | 56 +- 2 files changed, 502 insertions(+), 42 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp index de7fea6d18..0fd2c166ee 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp @@ -186,10 +186,11 @@ void applyGenSingleExcitation(Kokkos::View *> arr_, Kokkos::View *> arr, std::size_t i00, std::size_t i01, std::size_t i10, std::size_t i11) { arr(i00) = 0.0; - arr(i01) *= Kokkos::complex{0.0, 1.0}; - arr(i10) *= Kokkos::complex{0.0, -1.0}; arr(i11) = 0.0; - kokkos_swap(arr(i10), arr(i01)); + const auto v01 = arr(i01); + const auto v10 = arr(i10); + arr(i10) = Kokkos::complex{-imag(v01), real(v01)}; + arr(i01) = Kokkos::complex{imag(v10), -real(v10)}; }; applyNC2Functor( ExecutionSpace{}, arr_, num_qubits, wires, core_function); @@ -205,10 +206,10 @@ void applyGenSingleExcitationMinus( std::size_t i01, std::size_t i10, std::size_t i11) { [[maybe_unused]] const auto i00_ = i00; [[maybe_unused]] const auto i11_ = i11; - - arr(i01) *= Kokkos::complex{0.0, 1.0}; - arr(i10) *= Kokkos::complex{0.0, -1.0}; - kokkos_swap(arr(i10), arr(i01)); + const auto v01 = arr(i01); + const auto v10 = arr(i10); + arr(i10) = Kokkos::complex{-imag(v01), real(v01)}; + arr(i01) = Kokkos::complex{imag(v10), -real(v10)}; }; applyNC2Functor( ExecutionSpace{}, arr_, num_qubits, wires, core_function); @@ -222,14 +223,12 @@ void applyGenSingleExcitationPlus( auto core_function = KOKKOS_LAMBDA( Kokkos::View *> arr, std::size_t i00, std::size_t i01, std::size_t i10, std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i11_ = i11; - arr(i00) *= -1; - arr(i01) *= Kokkos::complex{0.0, 1.0}; - arr(i10) *= Kokkos::complex{0.0, -1.0}; arr(i11) *= -1; - kokkos_swap(arr(i10), arr(i01)); + const auto v01 = arr(i01); + const auto v10 = arr(i10); + arr(i10) = Kokkos::complex{-imag(v01), real(v01)}; + arr(i01) = Kokkos::complex{imag(v10), -real(v10)}; }; applyNC2Functor( ExecutionSpace{}, arr_, num_qubits, wires, core_function); @@ -252,7 +251,7 @@ void applyGenDoubleExcitation(Kokkos::View *> arr_, arr(i0000) = 0.0; arr(i0001) = 0.0; arr(i0010) = 0.0; - arr(i0011) = v1100 * Kokkos::complex{0.0, -1.0}; + arr(i0011) = Kokkos::complex{imag(v1100), -real(v1100)}; arr(i0100) = 0.0; arr(i0101) = 0.0; arr(i0110) = 0.0; @@ -261,7 +260,7 @@ void applyGenDoubleExcitation(Kokkos::View *> arr_, arr(i1001) = 0.0; arr(i1010) = 0.0; arr(i1011) = 0.0; - arr(i1100) = v0011 * Kokkos::complex{0.0, 1.0}; + arr(i1100) = Kokkos::complex{-imag(v0011), real(v0011)}; arr(i1101) = 0.0; arr(i1110) = 0.0; arr(i1111) = 0.0; @@ -296,9 +295,10 @@ void applyGenDoubleExcitationMinus( [[maybe_unused]] const auto i1101_ = i1101; [[maybe_unused]] const auto i1110_ = i1110; [[maybe_unused]] const auto i1111_ = i1111; - arr(i0011) *= Kokkos::complex{0.0, 1.0}; - arr(i1100) *= Kokkos::complex{0.0, -1.0}; - kokkos_swap(arr(i1100), arr(i0011)); + const auto v0011 = arr(i0011); + const auto v1100 = arr(i1100); + arr(i0011) = Kokkos::complex{imag(v1100), -real(v1100)}; + arr(i1100) = Kokkos::complex{-imag(v0011), real(v0011)}; }; applyNC4Functor( ExecutionSpace{}, arr_, num_qubits, wires, core_function); @@ -330,9 +330,10 @@ void applyGenDoubleExcitationPlus( [[maybe_unused]] const auto i1101_ = i1101; [[maybe_unused]] const auto i1110_ = i1110; [[maybe_unused]] const auto i1111_ = i1111; - arr(i0011) *= Kokkos::complex{0.0, -1.0}; - arr(i1100) *= Kokkos::complex{0.0, 1.0}; - kokkos_swap(arr(i1100), arr(i0011)); + const auto v0011 = arr(i0011); + const auto v1100 = arr(i1100); + arr(i0011) = Kokkos::complex{-imag(v1100), real(v1100)}; + arr(i1100) = Kokkos::complex{imag(v0011), -real(v0011)}; }; applyNC4Functor( ExecutionSpace{}, arr_, num_qubits, wires, core_function); @@ -380,16 +381,17 @@ template class applyNCGenerator1Functor { FuncT core_function_) : arr(arr_), core_function(core_function_) { const std::size_t n_wires = wires.size(); - const std::size_t nw_tot = - controlled_wires.size() + n_wires; // n_contr + n_wires + const std::size_t n_contr = controlled_wires.size(); + const std::size_t nw_tot = n_contr + n_wires; PL_ASSERT(n_wires == 1); PL_ASSERT(num_qubits >= nw_tot); std::vector all_wires; all_wires.reserve(nw_tot); - all_wires.insert(all_wires.begin(), wires.begin(), wires.end()); all_wires.insert(all_wires.begin(), controlled_wires.begin(), controlled_wires.end()); + all_wires.insert(all_wires.begin() + n_contr, wires.begin(), + wires.end()); const auto &[parity_, rev_wires_] = reverseWires(num_qubits, wires, controlled_wires); @@ -525,6 +527,394 @@ void applyNCGenGlobalPhase( {target}, core_function); } +template class applyNCGenerator2Functor { + using KokkosIntVector = Kokkos::View; + + Kokkos::View *> arr; + const FuncT core_function; + KokkosIntVector indices; + KokkosIntVector parity; + KokkosIntVector rev_wires; + KokkosIntVector rev_wire_shifts; + std::size_t mask{0U}; + static constexpr std::size_t one{1U}; + static constexpr std::size_t two{2U}; + std::size_t i00; + std::size_t i01; + std::size_t i10; + std::size_t i11; + + public: + template + applyNCGenerator2Functor([[maybe_unused]] ExecutionSpace exec, + Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + FuncT core_function_) + : arr(arr_), core_function(core_function_) { + const std::size_t n_wires = wires.size(); + const std::size_t n_contr = controlled_wires.size(); + const std::size_t nw_tot = n_contr + n_wires; + PL_ASSERT(n_wires == 2); + PL_ASSERT(num_qubits >= nw_tot); + + std::vector all_wires; + all_wires.reserve(nw_tot); + all_wires.insert(all_wires.begin(), controlled_wires.begin(), + controlled_wires.end()); + all_wires.insert(all_wires.begin() + n_contr, wires.begin(), + wires.end()); + + const auto &[parity_, rev_wires_] = + reverseWires(num_qubits, wires, controlled_wires); + parity = parity_; + const std::vector indices_ = + generateBitPatterns(all_wires, num_qubits); + + std::size_t k = 0; + mask = std::accumulate( + controlled_values.rbegin(), controlled_values.rend(), + std::size_t{0}, [&k](std::size_t acc, std::size_t value) { + return acc | (static_cast(value) << k++); + }); + i00 = indices_[mask << two]; + i01 = indices_[(mask << two) | one]; + i10 = indices_[(mask << two) | two]; + i11 = indices_[(mask << two) | two | one]; + indices = vector2view(indices_); + Kokkos::parallel_for( + Kokkos::RangePolicy( + 0, exp2(num_qubits - controlled_wires.size() - wires.size())), + *this); + } + KOKKOS_FUNCTION void operator()(std::size_t k) const { + const std::size_t offset = parity_2_offset(parity, k); + for (std::size_t i = 0; i < indices.size(); i++) { + if ((i >> two) == mask) { + continue; + } + arr(indices(i) + offset) = 0.0; + } + core_function(arr, i00 + offset, i01 + offset, i10 + offset, + i11 + offset); + } +}; + +template +void applyNCGenIsingXX(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { + kokkos_swap(arr(i00), arr(i11)); + kokkos_swap(arr(i10), arr(i01)); + }; + applyNCGenerator2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, controlled_values, + wires, core_function); +} + +template +void applyNCGenIsingXY(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { + arr(i00) = 0.0; + arr(i11) = 0.0; + kokkos_swap(arr(i10), arr(i01)); + }; + applyNCGenerator2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, controlled_values, + wires, core_function); +} + +template +void applyNCGenIsingYY(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { + const auto v00 = arr(i00); + arr(i00) = -arr(i11); + arr(i11) = -v00; + kokkos_swap(arr(i10), arr(i01)); + }; + applyNCGenerator2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, controlled_values, + wires, core_function); +} + +template +void applyNCGenIsingZZ(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i11_ = i11; + arr(i10) *= -1; + arr(i01) *= -1; + }; + applyNCGenerator2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, controlled_values, + wires, core_function); +} + +template +void applyNCGenSingleExcitation( + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { + arr(i00) = 0.0; + arr(i11) = 0.0; + const auto v01 = arr(i01); + const auto v10 = arr(i10); + arr(i10) = Kokkos::complex{-imag(v01), real(v01)}; + arr(i01) = Kokkos::complex{imag(v10), -real(v10)}; + }; + applyNCGenerator2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, controlled_values, + wires, core_function); +} + +template +void applyNCGenSingleExcitationMinus( + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i11_ = i11; + const auto v01 = arr(i01); + const auto v10 = arr(i10); + arr(i10) = Kokkos::complex{-imag(v01), real(v01)}; + arr(i01) = Kokkos::complex{imag(v10), -real(v10)}; + }; + applyNCGenerator2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, controlled_values, + wires, core_function); +} + +template +void applyNCGenSingleExcitationPlus( + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, std::size_t i00, + std::size_t i01, std::size_t i10, std::size_t i11) { + arr(i00) *= -1; + arr(i11) *= -1; + const auto v01 = arr(i01); + const auto v10 = arr(i10); + arr(i10) = Kokkos::complex{-imag(v01), real(v01)}; + arr(i01) = Kokkos::complex{imag(v10), -real(v10)}; + }; + applyNCGenerator2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, controlled_values, + wires, core_function); +} + +template class applyNCGenerator4Functor { + using KokkosIntVector = Kokkos::View; + + Kokkos::View *> arr; + const FuncT core_function; + KokkosIntVector indices; + KokkosIntVector parity; + KokkosIntVector rev_wires; + KokkosIntVector rev_wire_shifts; + std::size_t mask{0U}; + static constexpr std::size_t one{1U}; + static constexpr std::size_t two{2U}; + std::size_t i0011; + std::size_t i1100; + + public: + template + applyNCGenerator4Functor([[maybe_unused]] ExecutionSpace exec, + Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + FuncT core_function_) + : arr(arr_), core_function(core_function_) { + const std::size_t n_wires = wires.size(); + const std::size_t n_contr = controlled_wires.size(); + const std::size_t nw_tot = n_contr + n_wires; + PL_ASSERT(n_wires == 4); + PL_ASSERT(num_qubits >= nw_tot); + + std::vector all_wires; + all_wires.reserve(nw_tot); + all_wires.insert(all_wires.begin(), controlled_wires.begin(), + controlled_wires.end()); + all_wires.insert(all_wires.begin() + n_contr, wires.begin(), + wires.end()); + + const auto &[parity_, rev_wires_] = + reverseWires(num_qubits, wires, controlled_wires); + parity = parity_; + const std::vector indices_ = + generateBitPatterns(all_wires, num_qubits); + + std::size_t k = 0; + mask = std::accumulate( + controlled_values.rbegin(), controlled_values.rend(), + std::size_t{0}, [&k](std::size_t acc, std::size_t value) { + return acc | (static_cast(value) << k++); + }); + i0011 = indices_[(mask << 4U) + 3U]; + i1100 = indices_[(mask << 4U) + 12U]; + indices = vector2view(indices_); + Kokkos::parallel_for( + Kokkos::RangePolicy( + 0, exp2(num_qubits - controlled_wires.size() - wires.size())), + *this); + } + KOKKOS_FUNCTION void operator()(std::size_t k) const { + const std::size_t offset = parity_2_offset(parity, k); + for (std::size_t i = 0; i < indices.size(); i++) { + if ((i >> 4U) == mask) { + continue; + } + arr(indices(i) + offset) = 0.0; + } + core_function(arr, i0011 + offset, i1100 + offset, indices, offset); + } +}; + +template +void applyNCGenDoubleExcitation( + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, std::size_t i0011, + std::size_t i1100, const KokkosIntVector &indices, std::size_t offset) { + const auto v0011 = arr(i0011); + const auto v1100 = arr(i1100); + for (std::size_t i = 0; i < indices.size(); i++) { + arr(indices(i) + offset) = 0.0; + } + arr(i0011) = Kokkos::complex{imag(v1100), -real(v1100)}; + arr(i1100) = Kokkos::complex{-imag(v0011), real(v0011)}; + }; + applyNCGenerator4Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, controlled_values, + wires, core_function); +} + +template +void applyNCGenDoubleExcitationMinus( + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, std::size_t i0011, + std::size_t i1100, [[maybe_unused]] const KokkosIntVector &indices, + [[maybe_unused]] std::size_t offset) { + const auto v0011 = arr(i0011); + const auto v1100 = arr(i1100); + arr(i0011) = Kokkos::complex{imag(v1100), -real(v1100)}; + arr(i1100) = Kokkos::complex{-imag(v0011), real(v0011)}; + }; + applyNCGenerator4Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, controlled_values, + wires, core_function); +} + +template +void applyNCGenDoubleExcitationPlus( + Kokkos::View *> arr_, std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, std::size_t i0011, + std::size_t i1100, [[maybe_unused]] const KokkosIntVector &indices, + [[maybe_unused]] std::size_t offset) { + const auto v0011 = arr(i0011); + const auto v1100 = arr(i1100); + arr(i0011) = Kokkos::complex{-imag(v1100), real(v1100)}; + arr(i1100) = Kokkos::complex{imag(v0011), -real(v0011)}; + }; + applyNCGenerator4Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, controlled_values, + wires, core_function); +} + +template +void applyNCGenMultiRZ(Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] bool inverse = false) { + auto ctrls_mask = static_cast(0U); + for (std::size_t i = 0; i < controlled_wires.size(); i++) { + ctrls_mask |= (static_cast(controlled_values[i]) + << (num_qubits - controlled_wires[i] - 1)); + } + std::size_t ctrls_parity = std::accumulate( + controlled_wires.begin(), controlled_wires.end(), std::size_t{0}, + [num_qubits](std::size_t acc, std::size_t wire) { + return acc | exp2(num_qubits - wire - 1); + }); + std::size_t wires_parity = + std::accumulate(wires.begin(), wires.end(), std::size_t{0}, + [num_qubits](std::size_t acc, std::size_t wire) { + return acc | exp2(num_qubits - wire - 1); + }); + + Kokkos::parallel_for( + Kokkos::RangePolicy(0, exp2(num_qubits)), + KOKKOS_LAMBDA(std::size_t k) { + if (ctrls_mask == (ctrls_parity & k)) { + arr_(k) *= static_cast( + 1 - 2 * int(Kokkos::Impl::bit_count(k & wires_parity) % 2)); + } else { + arr_(k) = 0.0; + } + }); +} + template PrecisionT applyNamedGenerator(const GeneratorOperation generator_op, Kokkos::View *> arr_, @@ -630,6 +1020,56 @@ PrecisionT applyNCNamedGenerator( applyNCGenPhaseShift(arr_, num_qubits, controlled_wires, controlled_values, wires, inverse); return static_cast(1.0); + case ControlledGeneratorOperation::IsingXX: + applyNCGenIsingXX(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse); + return -static_cast(0.5); + case ControlledGeneratorOperation::IsingXY: + applyNCGenIsingXY(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse); + return static_cast(0.5); + case ControlledGeneratorOperation::IsingYY: + applyNCGenIsingYY(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse); + return -static_cast(0.5); + case ControlledGeneratorOperation::IsingZZ: + applyNCGenIsingZZ(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse); + return -static_cast(0.5); + case ControlledGeneratorOperation::SingleExcitation: + applyNCGenSingleExcitation( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse); + return -static_cast(0.5); + case ControlledGeneratorOperation::SingleExcitationMinus: + applyNCGenSingleExcitationMinus( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse); + return -static_cast(0.5); + case ControlledGeneratorOperation::SingleExcitationPlus: + applyNCGenSingleExcitationPlus( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse); + return -static_cast(0.5); + case ControlledGeneratorOperation::DoubleExcitation: + applyNCGenDoubleExcitation( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse); + return -static_cast(0.5); + case ControlledGeneratorOperation::DoubleExcitationMinus: + applyNCGenDoubleExcitationMinus( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse); + return -static_cast(0.5); + case ControlledGeneratorOperation::DoubleExcitationPlus: + applyNCGenDoubleExcitationPlus( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse); + return static_cast(0.5); + case ControlledGeneratorOperation::MultiRZ: + applyNCGenMultiRZ(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse); + return -static_cast(0.5); case ControlledGeneratorOperation::GlobalPhase: applyNCGenGlobalPhase( arr_, num_qubits, controlled_wires, controlled_values, wires, diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Generator.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Generator.cpp index 832713179c..801e5c79af 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Generator.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Generator.cpp @@ -438,7 +438,7 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyGeneratorDoubleExcitationPlus", TEMPLATE_TEST_CASE("StateVectorKokkos::applyControlledGenerator", "[StateVectorKokkos_Generator]", float, double) { using StateVectorT = StateVectorKokkos; - const std::size_t num_qubits = 6; + const std::size_t num_qubits = 7; const TestType ep_deriv = 1e-3; const TestType ep_margin = 1e-4; @@ -452,8 +452,11 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyControlledGenerator", } const bool inverse = GENERATE(true, false); - const std::string controlled_gate_name = - GENERATE("RX", "RY", "RZ", "PhaseShift", "GlobalPhase"); + const std::string controlled_gate_name = GENERATE( + "RX", "RY", "RZ", "PhaseShift", "GlobalPhase", "IsingXX", "IsingXY", + "IsingYY", "IsingZZ", "SingleExcitation", "SingleExcitationMinus", + "SingleExcitationPlus", "DoubleExcitation", "DoubleExcitationMinus", + "DoubleExcitationPlus", "MultiRZ"); SECTION("1-control: c{5}") { StateVectorKokkos kokkos_gntr_sv{ini_st.data(), @@ -463,8 +466,13 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyControlledGenerator", StateVectorKokkos kokkos_gate_svm{ini_st.data(), ini_st.size()}; - const auto wires = createWires( - str_to_controlled_gates_.at(controlled_gate_name), num_qubits); + std::vector wires; + if (controlled_gate_name != "MultiRZ") { + wires = createWires( + str_to_controlled_gates_.at(controlled_gate_name), num_qubits); + } else { + wires = {0, 1, 2, 3}; + } const std::vector control_wires = {5}; const std::vector control_values = {true}; auto scale = @@ -501,9 +509,13 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyControlledGenerator", ini_st.size()}; StateVectorKokkos kokkos_gate_svm{ini_st.data(), ini_st.size()}; - - const auto wires = createWires( - str_to_controlled_gates_.at(controlled_gate_name), num_qubits); + std::vector wires; + if (controlled_gate_name != "MultiRZ") { + wires = createWires( + str_to_controlled_gates_.at(controlled_gate_name), num_qubits); + } else { + wires = {0, 1, 2, 3}; + } const std::vector control_wires = {4, 5}; const std::vector control_values = {true, false}; auto scale = @@ -533,17 +545,21 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyControlledGenerator", } } - SECTION("2-control: c{3,5}") { + SECTION("2-control: c{4,6}") { StateVectorKokkos kokkos_gntr_sv{ini_st.data(), ini_st.size()}; StateVectorKokkos kokkos_gate_svp{ini_st.data(), ini_st.size()}; StateVectorKokkos kokkos_gate_svm{ini_st.data(), ini_st.size()}; - - const auto wires = createWires( - str_to_controlled_gates_.at(controlled_gate_name), num_qubits); - const std::vector control_wires = {3, 5}; + std::vector wires; + if (controlled_gate_name != "MultiRZ") { + wires = createWires( + str_to_controlled_gates_.at(controlled_gate_name), num_qubits); + } else { + wires = {0, 1, 2, 3}; + } + const std::vector control_wires = {4, 6}; const std::vector control_values = {true, false}; auto scale = kokkos_gntr_sv.applyGenerator(controlled_gate_name, control_wires, @@ -572,17 +588,21 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyControlledGenerator", } } - SECTION("3-control: c{3,4,5}") { + SECTION("3-control: c{4,5,6}") { StateVectorKokkos kokkos_gntr_sv{ini_st.data(), ini_st.size()}; StateVectorKokkos kokkos_gate_svp{ini_st.data(), ini_st.size()}; StateVectorKokkos kokkos_gate_svm{ini_st.data(), ini_st.size()}; - - const auto wires = createWires( - str_to_controlled_gates_.at(controlled_gate_name), num_qubits); - const std::vector control_wires = {3, 4, 5}; + std::vector wires; + if (controlled_gate_name != "MultiRZ") { + wires = createWires( + str_to_controlled_gates_.at(controlled_gate_name), num_qubits); + } else { + wires = {0, 1, 2, 3}; + } + const std::vector control_wires = {4, 5, 6}; const std::vector control_values = {true, false, true}; auto scale = kokkos_gntr_sv.applyGenerator(controlled_gate_name, control_wires, From f5f83bca9216f5cdc34b4596fa222b8b044cbcd3 Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Fri, 22 Nov 2024 18:21:59 +0000 Subject: [PATCH 21/53] Auto update version from '0.40.0-dev14' to '0.40.0-dev15' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 46600effac..e3474d59e8 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev14" +__version__ = "0.40.0-dev15" From 04a7c84d24d6a69295cdd176d8a5ba253ec9932c Mon Sep 17 00:00:00 2001 From: Joseph Lee <40768758+josephleekl@users.noreply.github.com> Date: Fri, 22 Nov 2024 18:19:17 -0500 Subject: [PATCH 22/53] (LK-C-7) Add controlled adjoint support for Lightning Kokkos (#965) ### Before submitting Please complete the following checklist when submitting a PR: - [ ] All new features must include a unit test. If you've fixed a bug or added code that should be tested, add a test to the [`tests`](../tests) directory! - [ ] All new functions and code must be clearly commented and documented. If you do make documentation changes, make sure that the docs build and render correctly by running `make docs`. - [ ] Ensure that the test suite passes, by running `make test`. - [ ] Add a new entry to the `.github/CHANGELOG.md` file, summarizing the change, and including a link back to the PR. - [ ] Ensure that code is properly formatted by running `make format`. When all the above are checked, delete everything above the dashed line and fill in the pull request template. ------------------------------------------------------------------------------------------------------------ **Context:** This PR adds support for adjoint for controlled operations to Lightning Kokkos. **Description of the Change:** **Benefits:** **Possible Drawbacks:** **Related GitHub Issues:** [sc-76779] --- .../algorithms/tests/Test_AdjointJacobian.cpp | 2 +- .../algorithms/AdjointJacobianKokkos.hpp | 18 +++-- .../tests/Test_AdjointJacobianKokkos.cpp | 76 +++++++++++++++++++ .../test_adjoint_jacobian_class.py | 4 - 4 files changed, 90 insertions(+), 10 deletions(-) diff --git a/pennylane_lightning/core/src/algorithms/tests/Test_AdjointJacobian.cpp b/pennylane_lightning/core/src/algorithms/tests/Test_AdjointJacobian.cpp index 99a72f2cbf..fd4386989a 100644 --- a/pennylane_lightning/core/src/algorithms/tests/Test_AdjointJacobian.cpp +++ b/pennylane_lightning/core/src/algorithms/tests/Test_AdjointJacobian.cpp @@ -41,7 +41,7 @@ using namespace Pennylane::LightningQubit::Observables; #elif _ENABLE_PLKOKKOS == 1 constexpr bool BACKEND_FOUND = true; -constexpr bool SUPPORTS_CTRL = false; +constexpr bool SUPPORTS_CTRL = true; #include "AdjointJacobianKokkos.hpp" #include "ObservablesKokkos.hpp" diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/algorithms/AdjointJacobianKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/algorithms/AdjointJacobianKokkos.hpp index 429a27d23c..7b3dedabbb 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/algorithms/AdjointJacobianKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/algorithms/AdjointJacobianKokkos.hpp @@ -150,11 +150,19 @@ class AdjointJacobian final if (ops.hasParams(op_idx)) { if (current_param_idx == *tp_it) { const PrecisionT scalingFactor = - BaseType::applyGenerator( - mu, ops.getOpsName()[op_idx], - ops.getOpsWires()[op_idx], - !ops.getOpsInverses()[op_idx]) * - (ops.getOpsInverses()[op_idx] ? -1 : 1); + (ops.getOpsControlledWires()[op_idx].empty()) + ? BaseType::applyGenerator( + mu, ops.getOpsName()[op_idx], + ops.getOpsWires()[op_idx], + !ops.getOpsInverses()[op_idx]) * + (ops.getOpsInverses()[op_idx] ? -1 : 1) + : BaseType::applyGenerator( + mu, ops.getOpsName()[op_idx], + ops.getOpsControlledWires()[op_idx], + ops.getOpsControlledValues()[op_idx], + ops.getOpsWires()[op_idx], + !ops.getOpsInverses()[op_idx]) * + (ops.getOpsInverses()[op_idx] ? -1 : 1); for (std::size_t obs_idx = 0; obs_idx < num_observables; obs_idx++) { const std::size_t idx = diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/algorithms/tests/Test_AdjointJacobianKokkos.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/algorithms/tests/Test_AdjointJacobianKokkos.cpp index fe557b72bf..d8e1891f20 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/algorithms/tests/Test_AdjointJacobianKokkos.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/algorithms/tests/Test_AdjointJacobianKokkos.cpp @@ -104,6 +104,82 @@ TEMPLATE_PRODUCT_TEST_CASE( sv2.getLength(), eps)); } +TEMPLATE_PRODUCT_TEST_CASE( + "AdjointJacobianKokkos::adjointJacobian Op=Controlled-Mixed, Obs=[XXX]", + "[AdjointJacobianKokkos]", (StateVectorKokkos), (float, double)) { + using StateVectorT = TestType; + using PrecisionT = typename StateVectorT::PrecisionT; + AdjointJacobian adj; + std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; + std::vector tp{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + { + const std::size_t num_qubits = 3; + const std::size_t num_obs = 1; + std::vector jacobian(num_obs * tp.size(), 0); + + StateVectorT psi(num_qubits); + + const auto obs = std::make_shared>( + std::make_shared>( + "PauliX", std::vector{0}), + std::make_shared>( + "PauliX", std::vector{1}), + std::make_shared>( + "PauliX", std::vector{2})); + auto ops = OpsData( + {"RZ", "RY", "RZ", "CNOT", "CNOT", "RX", "RY", "RZ", + "SingleExcitation", "RZ", "RY", "RZ"}, + {{param[0]}, + {param[1]}, + {param[2]}, + {}, + {}, + {param[0]}, + {param[1]}, + {param[2]}, + {param[0]}, + {param[0]}, + {param[1]}, + {param[2]}}, + {{0}, + {0}, + {0}, + {0, 1}, + {1, 2}, + {1}, + {2}, + {1}, + {0, 2}, + {1}, + {1}, + {1}}, + {false, false, false, false, false, false, false, false, false, + false, false, false}, + {{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}}, + {{}, {}, {}, {}, {}, {0}, {0}, {0}, {1}, {}, {}, {}}, + {{}, {}, {}, {}, {}, {true}, {true}, {true}, {true}, {}, {}, {}}); + + JacobianData tape{ + param.size(), psi.getLength(), psi.getData(), {obs}, ops, tp}; + + adj.adjointJacobian(std::span{jacobian}, tape, psi, true); + + CAPTURE(jacobian); + + // Computed with PennyLane using default.qubit.adjoint_jacobian + CHECK(0.0 == Approx(jacobian[0]).margin(1e-7)); + CHECK(0.03722967 == Approx(jacobian[1]).margin(1e-7)); + CHECK(0.53917582 == Approx(jacobian[2]).margin(1e-7)); + CHECK(-0.06895157 == Approx(jacobian[3]).margin(1e-7)); + CHECK(-0.0020095 == Approx(jacobian[4]).margin(1e-7)); + CHECK(0.25057513 == Approx(jacobian[5]).margin(1e-7)); + CHECK(-0.00139217 == Approx(jacobian[6]).margin(1e-7)); + CHECK(0.52016303 == Approx(jacobian[7]).margin(1e-7)); + CHECK(-0.09895398 == Approx(jacobian[8]).margin(1e-7)); + CHECK(0.51843232 == Approx(jacobian[9]).margin(1e-7)); + } +} + /** * @brief Tests the constructability of the AdjointDiff.hpp classes. * diff --git a/tests/lightning_qubit/test_adjoint_jacobian_class.py b/tests/lightning_qubit/test_adjoint_jacobian_class.py index e1cc77e796..bfce917908 100644 --- a/tests/lightning_qubit/test_adjoint_jacobian_class.py +++ b/tests/lightning_qubit/test_adjoint_jacobian_class.py @@ -165,10 +165,6 @@ def test_not_expectation_return_type(self, lightning_sv): ): self.calculate_jacobian(lightning_sv(num_wires=1), tape) - @pytest.mark.skipif( - device_name in ("lightning.kokkos"), - reason="N-controlled operations are not implemented in lightning.kokkos.", - ) @pytest.mark.parametrize("n_qubits", [1, 2, 3, 4]) @pytest.mark.parametrize("par", [-np.pi / 7, np.pi / 5, 2 * np.pi / 3]) def test_phaseshift_gradient(self, n_qubits, par, tol, lightning_sv): From f084bf3790dc0da842ec97eb9bfd3f8cc9ddf3ee Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 25 Nov 2024 16:38:05 +0000 Subject: [PATCH 23/53] update changelog --- .github/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 8b3916b8d7..3c94b2970e 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,6 +2,9 @@ ### New features since last release +* Add native N-controlled gate/matrix operations and adjoint support to `lightning.kokkos`. + [(#950)](https://github.com/PennyLaneAI/pennylane-lightning/pull/950) + * Add native N-controlled generators and adjoint support to `lightning.gpu`'s single-GPU backend. [(#970)](https://github.com/PennyLaneAI/pennylane-lightning/pull/970) From 391ad57e4bb69b6dc5109cad2e840cb854cfd313 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 25 Nov 2024 16:46:09 +0000 Subject: [PATCH 24/53] fix codefactor complaint --- .../core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp index 882af5251d..f06f86bb3a 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp @@ -510,7 +510,6 @@ class StateVectorKokkos final const std::vector &controlled_values, const std::vector &wires, bool inverse = false) { - const std::size_t num_qubits = this->getNumQubits(); const std::size_t two2N = exp2(num_qubits - wires.size() - controlled_wires.size()); From 772e7e3b2c2d6f15eb6d07d263b4e2dc2e01a3e5 Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Mon, 25 Nov 2024 18:01:49 +0000 Subject: [PATCH 25/53] Auto update version from '0.40.0-dev15' to '0.40.0-dev16' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index e3474d59e8..39618be329 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev15" +__version__ = "0.40.0-dev16" From 7b029b5f16a9b9676e85989fcd0dfd54385042b8 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 25 Nov 2024 19:17:24 +0000 Subject: [PATCH 26/53] add controlled op in catalyst lightningkokkossimulator --- .../catalyst/LightningKokkosSimulator.cpp | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/LightningKokkosSimulator.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/LightningKokkosSimulator.cpp index 04fab62ac5..ed56a6e741 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/LightningKokkosSimulator.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/LightningKokkosSimulator.cpp @@ -164,25 +164,30 @@ void LightningKokkosSimulator::NamedOperation( const std::vector &wires, bool inverse, const std::vector &controlled_wires, const std::vector &controlled_values) { - RT_FAIL_IF(!controlled_wires.empty() || !controlled_values.empty(), - "LightningKokkos does not support native quantum control."); - // Check the validity of number of qubits and parameters + RT_FAIL_IF(controlled_wires.size() != controlled_values.size(), + "Controlled wires/values size mismatch"); RT_FAIL_IF(!isValidQubits(wires), "Given wires do not refer to qubits"); RT_FAIL_IF(!isValidQubits(controlled_wires), "Given controlled wires do not refer to qubits"); // Convert wires to device wires auto &&dev_wires = getDeviceWires(wires); + auto &&dev_controlled_wires = getDeviceWires(controlled_wires); // Update the state-vector - this->device_sv->applyOperation(name, dev_wires, inverse, params); + if (controlled_wires.empty()) { + this->device_sv->applyOperation(name, dev_wires, inverse, params); + } else { + this->device_sv->applyOperation(name, dev_controlled_wires, + controlled_values, dev_wires, inverse, params); + } // Update tape caching if required if (this->tape_recording) { this->cache_manager.addOperation(name, params, dev_wires, inverse, {}, - {/*controlled_wires*/}, - {/*controlled_values*/}); + dev_controlled_wires, + controlled_values); } } @@ -190,17 +195,20 @@ void LightningKokkosSimulator::MatrixOperation( const std::vector> &matrix, const std::vector &wires, bool inverse, const std::vector &controlled_wires, - [[maybe_unused]] const std::vector &controlled_values) { + const std::vector &controlled_values) { using UnmanagedComplexHostView = Kokkos::View *, Kokkos::HostSpace, Kokkos::MemoryTraits>; + RT_FAIL_IF(controlled_wires.size() != controlled_values.size(), + "Controlled wires/values size mismatch"); RT_FAIL_IF(!isValidQubits(wires), "Given wires do not refer to qubits"); RT_FAIL_IF(!isValidQubits(controlled_wires), "Given controlled wires do not refer to qubits"); // Convert wires to device wires auto &&dev_wires = getDeviceWires(wires); + auto &&dev_controlled_wires = getDeviceWires(controlled_wires); std::vector> matrix_kok; matrix_kok.resize(matrix.size()); @@ -214,13 +222,17 @@ void LightningKokkosSimulator::MatrixOperation( matrix_kok.size())); // Update the state-vector - this->device_sv->applyMultiQubitOp(gate_matrix, dev_wires, inverse); + if (controlled_wires.empty()) { + this->device_sv->applyMultiQubitOp(gate_matrix, dev_wires, inverse); + } else { + this->device_sv->applNCyMultiQubitOp(gate_matrix, dev_controlled_wires, controlled_values, dev_wires, inverse); + } // Update tape caching if required if (this->tape_recording) { this->cache_manager.addOperation("QubitUnitary", {}, dev_wires, inverse, - matrix_kok, {/*controlled_wires*/}, - {/*controlled_values*/}); + matrix_kok, dev_controlled_wires, + controlled_values); } } From 9c415e225d65619d1faf90e7cf2c46188358cf8c Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 25 Nov 2024 20:24:14 +0000 Subject: [PATCH 27/53] update simulator + test --- .../catalyst/LightningKokkosSimulator.cpp | 9 +- .../tests/Test_LightningKokkosSimulator.cpp | 90 +++++++++++++++++++ 2 files changed, 96 insertions(+), 3 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/LightningKokkosSimulator.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/LightningKokkosSimulator.cpp index ed56a6e741..a0888308d1 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/LightningKokkosSimulator.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/LightningKokkosSimulator.cpp @@ -180,7 +180,8 @@ void LightningKokkosSimulator::NamedOperation( this->device_sv->applyOperation(name, dev_wires, inverse, params); } else { this->device_sv->applyOperation(name, dev_controlled_wires, - controlled_values, dev_wires, inverse, params); + controlled_values, dev_wires, inverse, + params); } // Update tape caching if required @@ -223,9 +224,11 @@ void LightningKokkosSimulator::MatrixOperation( // Update the state-vector if (controlled_wires.empty()) { - this->device_sv->applyMultiQubitOp(gate_matrix, dev_wires, inverse); + this->device_sv->applyMultiQubitOp(gate_matrix, dev_wires, inverse); } else { - this->device_sv->applNCyMultiQubitOp(gate_matrix, dev_controlled_wires, controlled_values, dev_wires, inverse); + this->device_sv->applyNCMultiQubitOp(gate_matrix, dev_controlled_wires, + controlled_values, dev_wires, + inverse); } // Update tape caching if required diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/tests/Test_LightningKokkosSimulator.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/tests/Test_LightningKokkosSimulator.cpp index 8b6eacb467..418bf89870 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/tests/Test_LightningKokkosSimulator.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/tests/Test_LightningKokkosSimulator.cpp @@ -660,6 +660,94 @@ TEST_CASE("LightningKokkosSimulator::GateSet", "[GateSet]") { REQUIRE(LKsim->CacheManagerInfo() == expected); } + // ============= Controlled operations ============= + + SECTION("Controlled Pauli-X and RX") { + std::unique_ptr LKsim = std::make_unique(); + + constexpr std::size_t n_qubits = 2; + std::vector Qs; + Qs.reserve(n_qubits); + + for (std::size_t i = 0; i < n_qubits; i++) { + Qs[i] = LKsim->AllocateQubit(); + } + + LKsim->NamedOperation("Hadamard", {}, {Qs[0]}, false); + LKsim->NamedOperation("PauliX", {}, {Qs[1]}, false, {Qs[0]}, {true}); + LKsim->NamedOperation("RX", {M_PI_2}, {Qs[1]}, false, {Qs[0]}, {true}); + + std::vector> state(1U << LKsim->GetNumQubits()); + DataView, 1> view(state); + LKsim->State(view); + + CHECK( + state.at(0) == + PLApproxComplex(std::complex{M_SQRT1_2, 0}).epsilon(1e-5)); + CHECK(state.at(1) == + PLApproxComplex(std::complex{0, 0}).epsilon(1e-5)); + CHECK(state.at(2) == + PLApproxComplex(std::complex{0, -0.5}).epsilon(1e-5)); + CHECK(state.at(3) == + PLApproxComplex(std::complex{0.5, 0.0}).epsilon(1e-5)); + } + + SECTION("Hadamard, CNOT and Matrix") { + std::unique_ptr LKsim = std::make_unique(); + + constexpr std::size_t n_qubits = 2; + std::vector Qs = LKsim->AllocateQubits(n_qubits); + + LKsim->NamedOperation("Hadamard", {}, {Qs[0]}, false); + LKsim->NamedOperation("CNOT", {}, {Qs[0], Qs[1]}, false); + + const std::vector wires = {Qs[1]}; + const std::vector controlled_wires = {Qs[0]}; + const std::vector controlled_values = {true}; + std::vector> matrix{ + {-0.6709485262524046, -0.6304426335363695}, + {-0.14885403153998722, 0.3608498832392019}, + {-0.2376311670004963, 0.3096798175687841}, + {-0.8818365947322423, -0.26456390390903695}, + }; + LKsim->MatrixOperation(matrix, wires, false, controlled_wires, + controlled_values); + + std::vector> state(1U << LKsim->GetNumQubits()); + DataView, 1> view(state); + LKsim->State(view); + + CHECK(state[0] == PLApproxComplex(std::complex{0.70710678, 0.0}) + .epsilon(1e-5)); + CHECK(state[1] == + PLApproxComplex(std::complex{0.0, 0.0}).epsilon(1e-5)); + CHECK(state[2] == + PLApproxComplex(std::complex{-0.1052557, 0.2551594}) + .epsilon(1e-5)); + CHECK(state[3] == + PLApproxComplex(std::complex{-0.62355264, -0.187075}) + .epsilon(1e-5)); + } + + SECTION("Mismatch controlled wires") { + std::unique_ptr LKsim = std::make_unique(); + constexpr std::size_t n_qubits = 2; + std::vector Qs = LKsim->AllocateQubits(n_qubits); + + REQUIRE_THROWS_WITH( + LKsim->NamedOperation("Hadamard", {}, {Qs[0]}, false, {Qs[1]}, {}), + Catch::Contains("Controlled wires/values size mismatch")); + std::vector> matrix{ + {-0.6709485262524046, -0.6304426335363695}, + {-0.14885403153998722, 0.3608498832392019}, + {-0.2376311670004963, 0.3096798175687841}, + {-0.8818365947322423, -0.26456390390903695}, + }; + REQUIRE_THROWS_WITH( + LKsim->MatrixOperation(matrix, {Qs[0]}, false, {Qs[1]}, {}), + Catch::Contains("Controlled wires/values size mismatch")); + } + SECTION("Test setStateVector") { std::unique_ptr LKsim = std::make_unique(); constexpr std::size_t n_qubits = 2; @@ -702,3 +790,5 @@ TEST_CASE("LightningKokkosSimulator::GateSet", "[GateSet]") { CHECK(state[1] == PLApproxComplex(c2).epsilon(1e-5)); } } + +TEST_CASE("Test mismatch controlled wires", "[]") {} From 7ca95b64a1192acb5bae282ece9af753ea1d602c Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Mon, 25 Nov 2024 20:49:08 +0000 Subject: [PATCH 28/53] Auto update version from '0.40.0-dev16' to '0.40.0-dev17' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 39618be329..7f2f554755 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev16" +__version__ = "0.40.0-dev17" From 568c3ca993f8a5b16a823aada519c7c9b6b4e273 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Tue, 26 Nov 2024 19:56:28 +0000 Subject: [PATCH 29/53] fix lkokkos merge tests --- .../tests/Test_StateVectorLKokkos.cpp | 224 +----------------- 1 file changed, 8 insertions(+), 216 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp index f3d6e8cdb0..62ccbe1cc4 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp @@ -107,39 +107,6 @@ TEMPLATE_PRODUCT_TEST_CASE("StateVectorKokkos::applyMatrix with a std::vector", Catch::Contains( "The size of matrix does not match with the given")); } - - SECTION("Test with different number of wires") { - using KokkosVector = typename StateVectorT::KokkosVector; - const std::size_t num_qubits = 5; - for (std::size_t num_wires = 1; num_wires < num_qubits; num_wires++) { - VectorT st_data_1 = - createRandomStateVectorData(re, num_qubits); - VectorT st_data_2 = st_data_1; - StateVectorT state_vector_1( - reinterpret_cast(st_data_1.data()), - st_data_1.size()); - StateVectorT state_vector_2( - reinterpret_cast(st_data_2.data()), - st_data_2.size()); - - std::vector wires(num_wires); - std::iota(wires.begin(), wires.end(), 0); - - auto m = randomUnitary(re, num_wires); - std::vector mkvec(reinterpret_cast(m.data()), - reinterpret_cast(m.data()) + - m.size()); - KokkosVector mkview(reinterpret_cast(m.data()), - m.size()); - state_vector_1.applyMatrix(mkvec, wires); - state_vector_2.applyMultiQubitOp(mkview, wires); - - PrecisionT eps = std::numeric_limits::epsilon() * 10E3; - REQUIRE(isApproxEqual( - state_vector_1.getData(), state_vector_1.getLength(), - state_vector_2.getData(), state_vector_2.getLength(), eps)); - } - } } TEMPLATE_PRODUCT_TEST_CASE("StateVectorKokkos::setState", "[errors]", @@ -278,46 +245,6 @@ TEMPLATE_PRODUCT_TEST_CASE( Catch::Contains( "The size of matrix does not match with the given")); } - - SECTION("Test with different number of wires") { - using KokkosVector = typename StateVectorT::KokkosVector; - const std::size_t num_qubits = 5; - for (std::size_t num_wires = 1; num_wires < (num_qubits - 1); - num_wires++) { - VectorT st_data_1 = - createRandomStateVectorData(re, num_qubits); - VectorT st_data_2 = st_data_1; - StateVectorT state_vector_1( - reinterpret_cast(st_data_1.data()), - st_data_1.size()); - StateVectorT state_vector_2( - reinterpret_cast(st_data_2.data()), - st_data_2.size()); - - std::vector wires(num_wires); - std::iota(wires.begin(), wires.end(), 0); - std::vector controlled_wires(num_qubits - num_wires); - std::iota(controlled_wires.begin(), controlled_wires.end(), - num_wires); - std::vector controlled_values(num_qubits - num_wires, true); - - auto m = randomUnitary(re, num_wires); - std::vector mkvec(reinterpret_cast(m.data()), - reinterpret_cast(m.data()) + - m.size()); - KokkosVector mkview(reinterpret_cast(m.data()), - m.size()); - state_vector_1.applyControlledMatrix(mkvec, controlled_wires, - controlled_values, wires); - state_vector_2.applyNCMultiQubitOp(mkview, controlled_wires, - controlled_values, wires); - - PrecisionT eps = std::numeric_limits::epsilon() * 10E3; - REQUIRE(isApproxEqual( - state_vector_1.getData(), state_vector_1.getLength(), - state_vector_2.getData(), state_vector_2.getLength(), eps)); - } - } } TEMPLATE_PRODUCT_TEST_CASE( @@ -328,6 +255,9 @@ TEMPLATE_PRODUCT_TEST_CASE( using ComplexT = typename StateVectorT::ComplexT; using VectorT = TestVector>; std::mt19937_64 re{1337}; + using UnmanagedComplexHostView = + Kokkos::View>; SECTION("Test wrong matrix") { std::vector m(8, 0.0); @@ -368,10 +298,12 @@ TEMPLATE_PRODUCT_TEST_CASE( std::vector mkvec(reinterpret_cast(m.data()), reinterpret_cast(m.data()) + m.size()); - KokkosVector mkview(reinterpret_cast(m.data()), - m.size()); - state_vector_1.applyControlledMatrix(mkvec.data(), controlled_wires, + state_vector_1.applyControlledMatrix(mkvec, controlled_wires, controlled_values, wires); + KokkosVector mkview("mkview", m.size()); + Kokkos::deep_copy( + mkview, UnmanagedComplexHostView( + reinterpret_cast(m.data()), m.size())); state_vector_2.applyNCMultiQubitOp(mkview, controlled_wires, controlled_values, wires); @@ -382,146 +314,6 @@ TEMPLATE_PRODUCT_TEST_CASE( } } -TEMPLATE_PRODUCT_TEST_CASE( - "StateVectorKokkos::applyControlledMatrix with a std::vector", - "[applyControlledMatrix]", (StateVectorKokkos), (float, double)) { - using StateVectorT = TestType; - using PrecisionT = typename StateVectorT::PrecisionT; - using ComplexT = typename StateVectorT::ComplexT; - using VectorT = TestVector>; - std::mt19937_64 re{1337}; - - SECTION("Test wrong matrix size") { - std::vector m(7, 0.0); - const std::size_t num_qubits = 4; - VectorT st_data = - createRandomStateVectorData(re, num_qubits); - StateVectorT state_vector(reinterpret_cast(st_data.data()), - st_data.size()); - REQUIRE_THROWS_WITH( - state_vector.applyControlledMatrix(m, {2}, {true}, {0, 1}), - Catch::Contains( - "The size of matrix does not match with the given")); - } - - SECTION("Test wrong number of wires") { - std::vector m(8, 0.0); - const std::size_t num_qubits = 4; - VectorT st_data = - createRandomStateVectorData(re, num_qubits); - - StateVectorT state_vector(reinterpret_cast(st_data.data()), - st_data.size()); - REQUIRE_THROWS_WITH( - state_vector.applyControlledMatrix(m, {2}, {true}, {0}), - Catch::Contains( - "The size of matrix does not match with the given")); - } - - SECTION("Test with different number of wires") { - using KokkosVector = typename StateVectorT::KokkosVector; - const std::size_t num_qubits = 5; - for (std::size_t num_wires = 1; num_wires < (num_qubits - 1); - num_wires++) { - VectorT st_data_1 = - createRandomStateVectorData(re, num_qubits); - VectorT st_data_2 = st_data_1; - StateVectorT state_vector_1( - reinterpret_cast(st_data_1.data()), - st_data_1.size()); - StateVectorT state_vector_2( - reinterpret_cast(st_data_2.data()), - st_data_2.size()); - - std::vector wires(num_wires); - std::iota(wires.begin(), wires.end(), 0); - std::vector controlled_wires(num_qubits - num_wires); - std::iota(controlled_wires.begin(), controlled_wires.end(), - num_wires); - std::vector controlled_values(num_qubits - num_wires, true); - - auto m = randomUnitary(re, num_wires); - std::vector mkvec(reinterpret_cast(m.data()), - reinterpret_cast(m.data()) + - m.size()); - KokkosVector mkview(reinterpret_cast(m.data()), - m.size()); - state_vector_1.applyControlledMatrix(mkvec, controlled_wires, - controlled_values, wires); - state_vector_2.applyNCMultiQubitOp(mkview, controlled_wires, - controlled_values, wires); - - PrecisionT eps = std::numeric_limits::epsilon() * 10E3; - REQUIRE(isApproxEqual( - state_vector_1.getData(), state_vector_1.getLength(), - state_vector_2.getData(), state_vector_2.getLength(), eps)); - } - } -} - -TEMPLATE_PRODUCT_TEST_CASE( - "StateVectorKokkos::applyControlledMatrix with a pointer", - "[applyControlledMatrix]", (StateVectorKokkos), (float, double)) { - using StateVectorT = TestType; - using PrecisionT = typename StateVectorT::PrecisionT; - using ComplexT = typename StateVectorT::ComplexT; - using VectorT = TestVector>; - std::mt19937_64 re{1337}; - - SECTION("Test wrong matrix") { - std::vector m(8, 0.0); - const std::size_t num_qubits = 4; - VectorT st_data = - createRandomStateVectorData(re, num_qubits); - - StateVectorT state_vector(reinterpret_cast(st_data.data()), - st_data.size()); - REQUIRE_THROWS_WITH( - state_vector.applyControlledMatrix(m.data(), {0}, {true}, {}), - Catch::Contains("must be larger than 0")); - } - - SECTION("Test with different number of wires") { - using KokkosVector = typename StateVectorT::KokkosVector; - const std::size_t num_qubits = 5; - for (std::size_t num_wires = 1; num_wires < (num_qubits - 1); - num_wires++) { - VectorT st_data_1 = - createRandomStateVectorData(re, num_qubits); - VectorT st_data_2 = st_data_1; - StateVectorT state_vector_1( - reinterpret_cast(st_data_1.data()), - st_data_1.size()); - StateVectorT state_vector_2( - reinterpret_cast(st_data_2.data()), - st_data_2.size()); - - std::vector wires(num_wires); - std::iota(wires.begin(), wires.end(), 0); - std::vector controlled_wires(num_qubits - num_wires); - std::iota(controlled_wires.begin(), controlled_wires.end(), - num_wires); - std::vector controlled_values(num_qubits - num_wires, true); - - auto m = randomUnitary(re, num_wires); - std::vector mkvec(reinterpret_cast(m.data()), - reinterpret_cast(m.data()) + - m.size()); - KokkosVector mkview(reinterpret_cast(m.data()), - m.size()); - state_vector_1.applyControlledMatrix(mkvec.data(), controlled_wires, - controlled_values, wires); - state_vector_2.applyNCMultiQubitOp(mkview, controlled_wires, - controlled_values, wires); - - PrecisionT eps = std::numeric_limits::epsilon() * 10E3; - REQUIRE(isApproxEqual( - state_vector_1.getData(), state_vector_1.getLength(), - state_vector_2.getData(), state_vector_2.getLength(), eps)); - } - } -} - TEMPLATE_PRODUCT_TEST_CASE("StateVectorKokkos::applyOperations", "[applyOperations invalid arguments]", (StateVectorKokkos), (float, double)) { From c392a72e7a72aa71d8c714c6a275612955373763 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Tue, 26 Nov 2024 19:57:00 +0000 Subject: [PATCH 30/53] util add blankline --- pennylane_lightning/core/src/utils/Util.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/utils/Util.hpp b/pennylane_lightning/core/src/utils/Util.hpp index 34ad5b0697..6f512ac40d 100644 --- a/pennylane_lightning/core/src/utils/Util.hpp +++ b/pennylane_lightning/core/src/utils/Util.hpp @@ -592,4 +592,4 @@ bool areVecsDisjoint(const std::vector &v1, const std::vector &v2) { } return true; } -} // namespace Pennylane::Util \ No newline at end of file +} // namespace Pennylane::Util From 27ee7e51e40bd9b89a057d7b67f21b8c3328a097 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Tue, 26 Nov 2024 19:58:07 +0000 Subject: [PATCH 31/53] use m_pi_2 constant for test --- .../gates/tests/Test_StateVectorKokkos_Param.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp index 165c07981e..d7fe8194c9 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp @@ -1747,7 +1747,7 @@ TEMPLATE_TEST_CASE("StateVectorKokkosManaged::applyControlledGlobalPhase", } SECTION("Controlled GlobalPhase") { - const TestType pi2 = 1.5707963267948966; + const TestType pi2 = TestType(M_PI_2); auto sv_data = createRandomStateVectorData(re, num_qubits); StateVectorKokkos kokkos_sv( reinterpret_cast(sv_data.data()), sv_data.size()); From b48662a9dbdb1a779b6168fa1938586492dceae4 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Tue, 26 Nov 2024 20:01:49 +0000 Subject: [PATCH 32/53] update controlled toffoli test name --- .../gates/tests/Test_StateVectorKokkos_NonParam.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp index 0e3dcfbdac..31aec48e5d 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp @@ -1441,7 +1441,7 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " } } -TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation controlled Toffoli", +TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation controlled matrix (PauliX/Toffoli)", "[StateVectorKokkos_NonParam]", float, double) { using StateVectorT = StateVectorKokkos; From 71e3d24d06073d0ec0984fb28b6933876e6ee56b Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Tue, 26 Nov 2024 20:11:34 +0000 Subject: [PATCH 33/53] use dynamic section for tests --- .../tests/Test_StateVectorKokkos_Generator.cpp | 15 ++++++++++----- .../tests/Test_StateVectorKokkos_NonParam.cpp | 8 +++++--- .../gates/tests/Test_StateVectorKokkos_Param.cpp | 3 ++- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Generator.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Generator.cpp index 801e5c79af..c65a7324fb 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Generator.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Generator.cpp @@ -126,7 +126,8 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyGenerator", "SingleExcitationMinus", "SingleExcitationPlus", "DoubleExcitation", "DoubleExcitationMinus", "DoubleExcitationPlus", "MultiRZ", "GlobalPhase"); - { + DYNAMIC_SECTION("Generator - Gate = " << gate_name + << " Inverse = " << inverse) { StateVectorKokkos kokkos_gntr_sv{ini_st.data(), ini_st.size()}; StateVectorKokkos kokkos_gate_svp{ini_st.data(), @@ -458,7 +459,8 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyControlledGenerator", "SingleExcitationPlus", "DoubleExcitation", "DoubleExcitationMinus", "DoubleExcitationPlus", "MultiRZ"); - SECTION("1-control: c{5}") { + DYNAMIC_SECTION("Matrix - Gate = " << controlled_gate_name << " Inverse = " + << inverse << " 1-control: c{5}") { StateVectorKokkos kokkos_gntr_sv{ini_st.data(), ini_st.size()}; StateVectorKokkos kokkos_gate_svp{ini_st.data(), @@ -502,7 +504,8 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyControlledGenerator", } } - SECTION("2-control: c{4,5}") { + DYNAMIC_SECTION("Matrix - Gate = " << controlled_gate_name << " Inverse = " + << inverse << " 2-control: c{4,5}") { StateVectorKokkos kokkos_gntr_sv{ini_st.data(), ini_st.size()}; StateVectorKokkos kokkos_gate_svp{ini_st.data(), @@ -545,7 +548,8 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyControlledGenerator", } } - SECTION("2-control: c{4,6}") { + DYNAMIC_SECTION("Matrix - Gate = " << controlled_gate_name << " Inverse = " + << inverse << " 2-control: c{4,6}") { StateVectorKokkos kokkos_gntr_sv{ini_st.data(), ini_st.size()}; StateVectorKokkos kokkos_gate_svp{ini_st.data(), @@ -588,7 +592,8 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyControlledGenerator", } } - SECTION("3-control: c{4,5,6}") { + DYNAMIC_SECTION("Matrix - Gate = " << controlled_gate_name << " Inverse = " + << inverse << " 3-control: c{4,5,6}") { StateVectorKokkos kokkos_gntr_sv{ini_st.data(), ini_st.size()}; StateVectorKokkos kokkos_gate_svp{ini_st.data(), diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp index 31aec48e5d..3510405e08 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp @@ -105,7 +105,8 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyMatrix/Param-Operation", const std::string gate_name = GENERATE("Identity", "PauliX", "PauliY", "PauliZ", "Hadamard", "S", "T", "CNOT", "SWAP", "CY", "CZ", "CSWAP", "Toffoli"); - { + DYNAMIC_SECTION("Matrix - Gate = " << gate_name + << " Inverse = " << inverse) { auto gate_matrix = getMatrix( str_to_gates_.at(gate_name), {}, inverse); @@ -1441,8 +1442,9 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " } } -TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation controlled matrix (PauliX/Toffoli)", - "[StateVectorKokkos_NonParam]", float, double) { +TEMPLATE_TEST_CASE( + "StateVectorKokkos::applyOperation controlled matrix (PauliX/Toffoli)", + "[StateVectorKokkos_NonParam]", float, double) { using StateVectorT = StateVectorKokkos; const TestType EP = 1e-4; diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp index d7fe8194c9..036bb6de3e 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp @@ -89,7 +89,8 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyMatrix/Operation", "CRZ", "IsingXX", "IsingXY", "IsingYY", "IsingZZ", "SingleExcitation", "SingleExcitationMinus", "SingleExcitationPlus", "DoubleExcitation", "DoubleExcitationMinus", "DoubleExcitationPlus", "Rot", "CRot"); - { + DYNAMIC_SECTION("Matrix - Gate = " << gate_name + << " Inverse = " << inverse) { auto gate_op = reverse_lookup(Constant::gate_names, std::string_view{gate_name}); auto num_params = lookup(Constant::gate_num_params, gate_op); From 4d8126d47aaf3f4685bdeea83c54db7780425733 Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Tue, 26 Nov 2024 20:13:08 +0000 Subject: [PATCH 34/53] Auto update version from '0.40.0-dev17' to '0.40.0-dev18' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 7f2f554755..3d1e53b61d 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev17" +__version__ = "0.40.0-dev18" From 7885941dc22e4f617cd6f49fae7424e7c313f415 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Tue, 26 Nov 2024 21:33:56 +0000 Subject: [PATCH 35/53] remove using std::size_t --- .../gates/tests/Test_StateVectorKokkos_Generator.cpp | 1 - .../gates/tests/Test_StateVectorKokkos_NonParam.cpp | 1 - .../gates/tests/Test_StateVectorKokkos_Param.cpp | 1 - 3 files changed, 3 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Generator.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Generator.cpp index c65a7324fb..87baa45ceb 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Generator.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Generator.cpp @@ -40,7 +40,6 @@ using namespace Pennylane::Gates; using namespace Pennylane::LightningKokkos; using namespace Pennylane::LightningKokkos::Functors; using namespace Pennylane::Util; -using std::size_t; } // namespace /// @endcond diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp index 3510405e08..7394180d5d 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp @@ -40,7 +40,6 @@ namespace { using namespace Pennylane::LightningKokkos; using namespace Pennylane::Gates; using namespace Pennylane::Util; -using std::size_t; } // namespace /// @endcond diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp index 036bb6de3e..5dad121b44 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp @@ -49,7 +49,6 @@ using namespace Pennylane::LightningKokkos::Measures; using namespace Pennylane::LightningKokkos::Observables; using namespace Pennylane::Util; using Pennylane::LightningKokkos::Util::getRealOfComplexInnerProduct; -using std::size_t; } // namespace /// @endcond From 664a70669e90f692778939691416b015736c9b02 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Thu, 28 Nov 2024 21:33:06 +0000 Subject: [PATCH 36/53] implement comments --- .../algorithms/tests/Test_AdjointJacobian.cpp | 71 +++++++++---------- .../lightning_kokkos/StateVectorKokkos.hpp | 11 +-- .../tests/Test_AdjointJacobianKokkos.cpp | 15 ++-- .../bindings/LKokkosBindings.hpp | 2 - .../gates/BasicGateFunctors.hpp | 42 +++++------ .../lightning_kokkos/utils/UtilKokkos.hpp | 5 +- 6 files changed, 66 insertions(+), 80 deletions(-) diff --git a/pennylane_lightning/core/src/algorithms/tests/Test_AdjointJacobian.cpp b/pennylane_lightning/core/src/algorithms/tests/Test_AdjointJacobian.cpp index fd4386989a..a18221c145 100644 --- a/pennylane_lightning/core/src/algorithms/tests/Test_AdjointJacobian.cpp +++ b/pennylane_lightning/core/src/algorithms/tests/Test_AdjointJacobian.cpp @@ -25,7 +25,6 @@ using namespace Pennylane::Util; #ifdef _ENABLE_PLQUBIT constexpr bool BACKEND_FOUND = true; -constexpr bool SUPPORTS_CTRL = true; #include "AdjointJacobianLQubit.hpp" #include "ObservablesLQubit.hpp" @@ -41,7 +40,6 @@ using namespace Pennylane::LightningQubit::Observables; #elif _ENABLE_PLKOKKOS == 1 constexpr bool BACKEND_FOUND = true; -constexpr bool SUPPORTS_CTRL = true; #include "AdjointJacobianKokkos.hpp" #include "ObservablesKokkos.hpp" @@ -57,7 +55,6 @@ using namespace Pennylane::LightningKokkos::Observables; #elif _ENABLE_PLGPU == 1 constexpr bool BACKEND_FOUND = true; -constexpr bool SUPPORTS_CTRL = true; #include "AdjointJacobianGPU.hpp" #include "ObservablesGPU.hpp" #include "TestHelpersStateVectors.hpp" @@ -72,7 +69,6 @@ using namespace Pennylane::LightningGPU::Observables; #else constexpr bool BACKEND_FOUND = false; -constexpr bool SUPPORTS_CTRL = false; using TestStateVectorBackends = Pennylane::Util::TypeList; template struct StateVectorToName {}; @@ -140,41 +136,38 @@ template void testAdjointJacobian() { DYNAMIC_SECTION("Op=PhaseShift, Obs=Y - " << StateVectorToName::name) { - if (SUPPORTS_CTRL) { - const std::vector tp{0}; - const std::size_t num_qubits = GENERATE(2, 3, 4); - - const std::size_t num_params = 3; - const std::size_t num_obs = 1; - const auto obs = std::make_shared>( - "PauliY", std::vector{num_qubits - 1}); - std::vector jacobian(num_obs * tp.size(), 0); + const std::vector tp{0}; + const std::size_t num_qubits = GENERATE(2, 3, 4); + + const std::size_t num_params = 3; + const std::size_t num_obs = 1; + const auto obs = std::make_shared>( + "PauliY", std::vector{num_qubits - 1}); + std::vector jacobian(num_obs * tp.size(), 0); + + for (const auto &p : param) { + std::vector> controls{ + std::vector(num_qubits - 1)}; + std::iota(controls[0].begin(), controls[0].end(), 0); + std::vector> control_values{ + std::vector(num_qubits - 1, true)}; + auto ops = OpsData( + {"PhaseShift"}, {{p}}, {{num_qubits - 1}}, {false}, {{}}, + controls, control_values); - for (const auto &p : param) { - std::vector> controls{ - std::vector(num_qubits - 1)}; - std::iota(controls[0].begin(), controls[0].end(), 0); - std::vector> control_values{ - std::vector(num_qubits - 1, true)}; - auto ops = OpsData( - {"PhaseShift"}, {{p}}, {{num_qubits - 1}}, {false}, - {{}}, controls, control_values); - - std::vector cdata(1U << num_qubits); - cdata[cdata.size() - 2] = - Pennylane::Util::INVSQRT2(); - cdata[cdata.size() - 1] = - Pennylane::Util::INVSQRT2(); - - StateVectorT psi(cdata.data(), cdata.size()); - JacobianData tape{ - num_params, psi.getLength(), psi.getData(), {obs}, ops, - tp}; - adj.adjointJacobian(std::span{jacobian}, tape, psi, true); - - CAPTURE(jacobian); - CHECK(cos(p) == Approx(jacobian[0])); - } + std::vector cdata(1U << num_qubits); + cdata[cdata.size() - 2] = + Pennylane::Util::INVSQRT2(); + cdata[cdata.size() - 1] = + Pennylane::Util::INVSQRT2(); + + StateVectorT psi(cdata.data(), cdata.size()); + JacobianData tape{ + num_params, psi.getLength(), psi.getData(), {obs}, ops, tp}; + adj.adjointJacobian(std::span{jacobian}, tape, psi, true); + + CAPTURE(jacobian); + CHECK(cos(p) == Approx(jacobian[0])); } } @@ -671,4 +664,4 @@ TEST_CASE("Algorithms::adjointJacobian", "[Algorithms]") { if constexpr (BACKEND_FOUND) { testAdjointJacobian(); } -} \ No newline at end of file +} diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp index ce30cf2e3f..951b27248c 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp @@ -511,6 +511,7 @@ class StateVectorKokkos final const std::vector &wires, bool inverse = false) { const std::size_t num_qubits = this->getNumQubits(); + PL_ASSERT(num_qubits >= wires.size() + controlled_wires.size()); const std::size_t two2N = exp2(num_qubits - wires.size() - controlled_wires.size()); const std::size_t dim = exp2(wires.size()); @@ -548,8 +549,9 @@ class StateVectorKokkos final break; default: // TODO: explore runtime determine scratch space level (L0 vs L1) - std::size_t scratch_size = ScratchViewComplex::shmem_size(dim) + - ScratchViewSizeT::shmem_size(dim); + const std::size_t scratch_size = + ScratchViewComplex::shmem_size(dim) + + ScratchViewSizeT::shmem_size(dim); Kokkos::parallel_for( "multiNCQubitOpFunctor", TeamPolicy(two2N, Kokkos::AUTO, dim) @@ -756,10 +758,9 @@ class StateVectorKokkos final "`controlled_values`."); if (controlled_wires.empty()) { return applyGenerator(opName, wires, inverse); - } else { - return applyControlledGenerator(opName, controlled_wires, - controlled_values, wires, inverse); } + return applyControlledGenerator(opName, controlled_wires, + controlled_values, wires, inverse); } /** diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/algorithms/tests/Test_AdjointJacobianKokkos.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/algorithms/tests/Test_AdjointJacobianKokkos.cpp index ac39f9cc2c..4b0426f4ee 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/algorithms/tests/Test_AdjointJacobianKokkos.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/algorithms/tests/Test_AdjointJacobianKokkos.cpp @@ -111,6 +111,7 @@ TEMPLATE_PRODUCT_TEST_CASE( AdjointJacobian adj; std::vector param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3}; std::vector tp{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + const PrecisionT ep = 1e-6; { const std::size_t num_qubits = 3; const std::size_t num_obs = 1; @@ -166,16 +167,10 @@ TEMPLATE_PRODUCT_TEST_CASE( CAPTURE(jacobian); // Computed with PennyLane using default.qubit.adjoint_jacobian - CHECK(0.0 == Approx(jacobian[0]).margin(1e-7)); - CHECK(0.03722967 == Approx(jacobian[1]).margin(1e-7)); - CHECK(0.53917582 == Approx(jacobian[2]).margin(1e-7)); - CHECK(-0.06895157 == Approx(jacobian[3]).margin(1e-7)); - CHECK(-0.0020095 == Approx(jacobian[4]).margin(1e-7)); - CHECK(0.25057513 == Approx(jacobian[5]).margin(1e-7)); - CHECK(-0.00139217 == Approx(jacobian[6]).margin(1e-7)); - CHECK(0.52016303 == Approx(jacobian[7]).margin(1e-7)); - CHECK(-0.09895398 == Approx(jacobian[8]).margin(1e-7)); - CHECK(0.51843232 == Approx(jacobian[9]).margin(1e-7)); + std::vector expected_jacobian{ + 0.0, 0.03722967, 0.53917582, -0.06895157, -0.0020095, + 0.25057513, -0.00139217, 0.52016303, -0.09895398, 0.51843232}; + CHECK(expected_jacobian == PLApprox(jacobian).margin(ep)); } } diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/bindings/LKokkosBindings.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/bindings/LKokkosBindings.hpp index e39320a950..eb2d6d97f8 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/bindings/LKokkosBindings.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/bindings/LKokkosBindings.hpp @@ -188,8 +188,6 @@ void registerBackendClassSpecificBindings(PyClass &pyclass) { "Apply operation via the gate matrix") .def("collapse", &StateVectorT::collapse, "Collapse the statevector onto the 0 or 1 branch of a given wire.") - .def("normalize", &StateVectorT::normalize, - "Normalize the statevector to norm 1.") .def("applyControlledMatrix", &applyControlledMatrix, "Apply controlled operation"); } diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp index f98cbaf00f..8abe9ac117 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp @@ -58,7 +58,7 @@ template class applyNCNFunctor { const std::vector &controlled_values, const std::vector &wires, FuncT core_function_) : arr(arr_), core_function(core_function_) { - std::size_t two2N = + const std::size_t two2N = exp2(num_qubits - wires.size() - controlled_wires.size()); dim = exp2(wires.size()); const auto &[parity_, rev_wires_] = @@ -375,7 +375,7 @@ void applyNCPhaseShift(Kokkos::View *> arr_, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const Kokkos::complex shift = (inverse) ? Kokkos::exp(-Kokkos::complex(0, angle)) : Kokkos::exp(Kokkos::complex(0, angle)); @@ -412,7 +412,7 @@ void applyNCRX(Kokkos::View *> arr_, const std::vector &controlled_values, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const PrecisionT c = std::cos(angle * static_cast(0.5)); const PrecisionT s = (inverse) ? std::sin(angle * static_cast(0.5)) @@ -452,7 +452,7 @@ void applyNCRY(Kokkos::View *> arr_, const std::vector &controlled_values, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const PrecisionT c = std::cos(angle * static_cast(0.5)); const PrecisionT s = (inverse) ? -std::sin(angle * static_cast(0.5)) @@ -492,7 +492,7 @@ void applyNCRZ(Kokkos::View *> arr_, const std::vector &controlled_values, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const PrecisionT cos_angle = std::cos(angle * static_cast(0.5)); const PrecisionT sin_angle = std::sin(angle * static_cast(0.5)); const Kokkos::complex shift_0{ @@ -800,7 +800,7 @@ void applyControlledPhaseShift(Kokkos::View *> arr_, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const Kokkos::complex s = (inverse) ? Kokkos::exp(-Kokkos::complex(0, angle)) : Kokkos::exp(Kokkos::complex(0, angle)); @@ -821,7 +821,7 @@ void applyCRX(Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const PrecisionT c = std::cos(angle / 2); const PrecisionT js = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); @@ -846,7 +846,7 @@ void applyCRY(Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const PrecisionT c = std::cos(angle / 2); const PrecisionT s = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); auto core_function = KOKKOS_LAMBDA( @@ -868,7 +868,7 @@ void applyCRZ(Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const PrecisionT cos_angle = std::cos(angle * static_cast(0.5)); const PrecisionT sin_angle = std::sin(angle * static_cast(0.5)); const Kokkos::complex shift_0{ @@ -921,7 +921,7 @@ void applyNCIsingXX(Kokkos::View *> arr_, const std::vector &controlled_values, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); @@ -967,7 +967,7 @@ void applyNCIsingXY(Kokkos::View *> arr_, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); @@ -1011,7 +1011,7 @@ void applyNCIsingYY(Kokkos::View *> arr_, const std::vector &controlled_values, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); @@ -1057,7 +1057,7 @@ void applyNCIsingZZ(Kokkos::View *> arr_, const std::vector &controlled_values, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const Kokkos::complex shift_0 = Kokkos::complex{ std::cos(angle / 2), (inverse) ? std::sin(angle / 2) : -std::sin(angle / 2)}; @@ -1097,7 +1097,7 @@ void applyNCSingleExcitation(Kokkos::View *> arr_, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); @@ -1139,7 +1139,7 @@ void applyNCSingleExcitationMinus( const std::vector &controlled_values, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); @@ -1182,7 +1182,7 @@ void applyNCSingleExcitationPlus( const std::vector &controlled_values, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); @@ -1516,7 +1516,7 @@ void applyNCDoubleExcitation(Kokkos::View *> arr_, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); @@ -1573,7 +1573,7 @@ void applyNCDoubleExcitationMinus( const std::vector &controlled_values, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = inverse ? -std::sin(angle / 2) : std::sin(angle / 2); const Kokkos::complex e = @@ -1631,7 +1631,7 @@ void applyNCDoubleExcitationPlus( const std::vector &controlled_values, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = inverse ? -std::sin(angle / 2) : std::sin(angle / 2); const Kokkos::complex e = @@ -1688,7 +1688,7 @@ void applyMultiRZ(Kokkos::View *> arr_, std::size_t num_qubits, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const Kokkos::complex shift_0 = Kokkos::complex{ std::cos(angle / 2), (inverse) ? std::sin(angle / 2) : -std::sin(angle / 2)}; @@ -1714,7 +1714,7 @@ void applyNCMultiRZ(Kokkos::View *> arr_, const std::vector &controlled_values, const std::vector &wires, bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; + const PrecisionT angle = params[0]; const Kokkos::complex shift_0 = Kokkos::complex{ std::cos(angle / 2), (inverse) ? std::sin(angle / 2) : -std::sin(angle / 2)}; diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp index 005a2f50bc..eac2d0e612 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp @@ -136,9 +136,8 @@ inline auto reverseWires(const std::size_t num_qubits, const std::size_t nw_tot = n_contr + n_wires; std::vector all_wires; all_wires.reserve(nw_tot); - std::copy(wires.begin(), wires.end(), std::back_inserter(all_wires)); - std::copy(controlled_wires.begin(), controlled_wires.end(), - std::back_inserter(all_wires)); + all_wires.insert(all_wires.begin(), wires.begin(), wires.end()); + all_wires.insert(all_wires.begin() + n_wires, controlled_wires.begin(), controlled_wires.end()); std::vector rev_wires_(nw_tot, (num_qubits - 1)); std::transform(rev_wires_.begin(), rev_wires_.end(), all_wires.rbegin(), From 6b54cd218da981a71759a1d47c3f7614f84aeeee Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Thu, 28 Nov 2024 21:33:26 +0000 Subject: [PATCH 37/53] Auto update version from '0.40.0-dev18' to '0.40.0-dev22' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 3d1e53b61d..706ac97c51 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev18" +__version__ = "0.40.0-dev22" From d8b62ab7f0b10ece4a9fe4e71b68345572473451 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Thu, 28 Nov 2024 21:49:03 +0000 Subject: [PATCH 38/53] implement comments --- .../catalyst/tests/Test_LightningKokkosSimulator.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/tests/Test_LightningKokkosSimulator.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/tests/Test_LightningKokkosSimulator.cpp index 5143a771ef..2944b9dd7b 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/tests/Test_LightningKokkosSimulator.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/catalyst/tests/Test_LightningKokkosSimulator.cpp @@ -659,8 +659,6 @@ TEST_CASE("LightningKokkosSimulator::GateSet", "[GateSet]") { REQUIRE(LKsim->CacheManagerInfo() == expected); } - // ============= Controlled operations ============= - SECTION("Controlled Pauli-X and RX") { std::unique_ptr LKsim = std::make_unique(); From 8db2fd8737f6a0927d1bcb7f6c65b4087bec9846 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Thu, 28 Nov 2024 23:28:33 +0000 Subject: [PATCH 39/53] remove old c(globalphase) --- .../lightning_kokkos/StateVectorKokkos.hpp | 18 ------------------ .../tests/Test_StateVectorKokkos_Param.cpp | 16 ---------------- 2 files changed, 34 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp index 951b27248c..e5180e5a64 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp @@ -339,12 +339,6 @@ class StateVectorKokkos final const std::vector &gate_matrix = {}) { if (opName == "Identity") { // No op - } else if (opName == "C(GlobalPhase)") { - if (inverse) { - applyControlledGlobalPhase(gate_matrix); - } else { - applyControlledGlobalPhase(gate_matrix); - } } else if (array_contains(gate_names, std::string_view{opName})) { const std::size_t num_qubits = this->getNumQubits(); const GateOperation gateop = @@ -377,18 +371,6 @@ class StateVectorKokkos final getView(), this->getNumQubits(), wires, inverse, params[0], word); } - template - void applyControlledGlobalPhase(const std::vector &diagonal) { - auto diagonal_ = vector2view(diagonal); - auto two2N = BaseType::getLength(); - auto dataview = getView(); - Kokkos::parallel_for( - two2N, KOKKOS_LAMBDA(std::size_t i) { - dataview(i) *= - (inverse) ? Kokkos::conj(diagonal_(i)) : diagonal_(i); - }); - } - /** * @brief Apply a multi qubit operator to the state vector using a matrix * diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp index 5dad121b44..769f6a49ed 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp @@ -1717,7 +1717,6 @@ TEMPLATE_TEST_CASE("StateVectorKokkosManaged::applyControlledGlobalPhase", std::mt19937_64 re{1337}; const std::size_t num_qubits = 3; const bool inverse = GENERATE(false, true); - const std::size_t index = GENERATE(0, 1, 2); /* The `phase` array contains the diagonal entries of the controlled-phase operator. It can be created in Python using the following command @@ -1731,21 +1730,6 @@ TEMPLATE_TEST_CASE("StateVectorKokkosManaged::applyControlledGlobalPhase", const std::vector phase = {{1.0, 0.}, {1.0, 0.}, {0.0, 1.}, {0.0, 1.}, {1.0, 0.}, {1.0, 0.}, {1.0, 0.}, {1.0, 0.}}; - - SECTION("C(GlobalPhase)") { - auto sv_data = createRandomStateVectorData(re, num_qubits); - StateVectorKokkos kokkos_sv( - reinterpret_cast(sv_data.data()), sv_data.size()); - kokkos_sv.applyOperation("C(GlobalPhase)", {index}, inverse, {}, phase); - auto result_sv = kokkos_sv.getDataVector(); - for (std::size_t j = 0; j < exp2(num_qubits); j++) { - ComplexT tmp = (inverse) ? conj(phase[j]) : phase[j]; - tmp *= ComplexT(sv_data[j]); - CHECK((real(result_sv[j])) == Approx(real(tmp))); - CHECK((imag(result_sv[j])) == Approx(imag(tmp))); - } - } - SECTION("Controlled GlobalPhase") { const TestType pi2 = TestType(M_PI_2); auto sv_data = createRandomStateVectorData(re, num_qubits); From b678095fc570aa8d212d142f8813d1e3537dceac Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Thu, 28 Nov 2024 23:29:10 +0000 Subject: [PATCH 40/53] Auto update version from '0.40.0-dev22' to '0.40.0-dev24' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 706ac97c51..17c98c7d19 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev22" +__version__ = "0.40.0-dev24" From 19ffddcf2071a08a2ef711d68e61c3569a8c08da Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Thu, 28 Nov 2024 23:34:48 +0000 Subject: [PATCH 41/53] format --- .../core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp index eac2d0e612..ee942ed45d 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp @@ -137,7 +137,8 @@ inline auto reverseWires(const std::size_t num_qubits, std::vector all_wires; all_wires.reserve(nw_tot); all_wires.insert(all_wires.begin(), wires.begin(), wires.end()); - all_wires.insert(all_wires.begin() + n_wires, controlled_wires.begin(), controlled_wires.end()); + all_wires.insert(all_wires.begin() + n_wires, controlled_wires.begin(), + controlled_wires.end()); std::vector rev_wires_(nw_tot, (num_qubits - 1)); std::transform(rev_wires_.begin(), rev_wires_.end(), all_wires.rbegin(), From 647ef529c918718ec366bfe641f8716cb826247a Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Fri, 29 Nov 2024 15:17:26 +0000 Subject: [PATCH 42/53] clean up doc comments --- .../lightning_kokkos/StateVectorKokkos.hpp | 54 +++++++++++-------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp index e5180e5a64..6ce864c3ea 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp @@ -172,8 +172,6 @@ class StateVectorKokkos final /** * @brief Reset the data back to the \f$\ket{0}\f$ state. - * - * @param num_qubits Number of qubits */ void resetStateVector() { if (this->getLength() > 0) { @@ -184,8 +182,8 @@ class StateVectorKokkos final /** * @brief Set values for a batch of elements of the state-vector. * - * @param values Values to be set for the target elements. * @param indices Indices of the target elements. + * @param values Values to be set for the target elements. */ void setStateVector(const std::vector &indices, const std::vector &values) { @@ -250,8 +248,10 @@ class StateVectorKokkos final /** * @brief Create a new state vector from data on the host. - * - * @param num_qubits Number of qubits + * + * @param hostdata_ Host array for state vector + * @param length Length of host array (must be power of 2) + * @param kokkos_args Arguments for Kokkos initialization */ StateVectorKokkos(ComplexT *hostdata_, std::size_t length, const Kokkos::InitializationSettings &kokkos_args = {}) @@ -261,6 +261,13 @@ class StateVectorKokkos final HostToDevice(hostdata_, length); } + /** + * @brief Create a new state vector from data on the host. + * + * @param hostdata_ Host vector for state vector + * @param length Length of host array (must be power of 2) + * @param kokkos_args Arguments for Kokkos initialization + */ StateVectorKokkos(std::complex *hostdata_, std::size_t length, const Kokkos::InitializationSettings &kokkos_args = {}) : StateVectorKokkos(log2(length), kokkos_args) { @@ -271,8 +278,10 @@ class StateVectorKokkos final /** * @brief Create a new state vector from data on the host. - * - * @param num_qubits Number of qubits + * + * @param hostdata_ Host array for state vector + * @param length Length of host array (must be power of 2) + * @param kokkos_args Arguments for Kokkos initialization */ StateVectorKokkos(const ComplexT *hostdata_, std::size_t length, const Kokkos::InitializationSettings &kokkos_args = {}) @@ -285,8 +294,9 @@ class StateVectorKokkos final /** * @brief Create a new state vector from data on the host. - * - * @param num_qubits Number of qubits + * + * @param hostdata_ Host vector for state vector + * @param kokkos_args Arguments for Kokkos initialization */ StateVectorKokkos(std::vector hostdata_, const Kokkos::InitializationSettings &kokkos_args = {}) @@ -296,6 +306,7 @@ class StateVectorKokkos final * @brief Copy constructor * * @param other Another state vector + * @param kokkos_args Arguments for Kokkos initialization */ StateVectorKokkos(const StateVectorKokkos &other, const Kokkos::InitializationSettings &kokkos_args = {}) @@ -305,8 +316,6 @@ class StateVectorKokkos final /** * @brief Destructor for StateVectorKokkos class - * - * @param other Another state vector */ ~StateVectorKokkos() { data_.reset(); @@ -438,7 +447,7 @@ class StateVectorKokkos final * @param controlled_wires Control wires. * @param controlled_values Control values (false or true). * @param wires Wires to apply gate to. - * @param inverse Indicates whether to use adjoint of gate. Default to false + * @param inverse Indicates whether to use adjoint of gate. (Default to false) * @param params Optional parameter list for parametric gates. * @param gate_matrix Optional unitary gate matrix if opName doesn't exist. */ @@ -551,7 +560,7 @@ class StateVectorKokkos final * * @param matrix Pointer to the array data (in row-major format). * @param wires Wires to apply gate to. - * @param inverse Indicate whether inverse should be taken. + * @param inverse Indicate whether inverse should be taken. (Default to false) */ inline void applyMatrix(ComplexT *matrix, const std::vector &wires, @@ -576,7 +585,7 @@ class StateVectorKokkos final * * @param matrix Pointer to the array data (in row-major format). * @param wires Wires to apply gate to. - * @param inverse Indicate whether inverse should be taken. + * @param inverse Indicate whether inverse should be taken. (Default to false) */ inline void applyMatrix(const ComplexT *matrix, const std::vector &wires, @@ -593,7 +602,7 @@ class StateVectorKokkos final * * @param matrix Matrix data (in row-major format). * @param wires Wires to apply gate to. - * @param inverse Indicate whether inverse should be taken. + * @param inverse Indicate whether inverse should be taken. (Default to false) */ inline void applyMatrix(const std::vector &matrix, const std::vector &wires, @@ -613,7 +622,7 @@ class StateVectorKokkos final * @param controlled_wires Controlled wires * @param controlled_values Controlled values (true or false) * @param wires Wires to apply gate to. - * @param inverse Indicate whether inverse should be taken. + * @param inverse Indicate whether inverse should be taken. (Default to false) */ inline void applyControlledMatrix( ComplexT *matrix, const std::vector &controlled_wires, @@ -635,7 +644,7 @@ class StateVectorKokkos final * @param controlled_wires Controlled wires * @param controlled_values Controlled values (true or false) * @param wires Wires to apply gate to. - * @param inverse Indicate whether inverse should be taken. + * @param inverse Indicate whether inverse should be taken. (Default to false) */ inline void applyControlledMatrix(const ComplexT *matrix, @@ -659,7 +668,7 @@ class StateVectorKokkos final * @param controlled_wires Control wires. * @param controlled_values Control values (false or true). * @param wires Wires to apply gate to. - * @param inverse Indicate whether inverse should be taken. + * @param inverse Indicate whether inverse should be taken. (Default to false) */ inline void applyControlledMatrix(const std::vector &matrix, @@ -681,7 +690,8 @@ class StateVectorKokkos final * * @param opName Name of gate to apply. * @param wires Wires to apply gate to. - * @param inverse Indicates whether to use adjoint of gate. + * @param inverse Indicates whether to use adjoint of gate. (Default to false) + * @return PrecisionT Generator scale prefactor */ auto applyGenerator(const std::string &opName, const std::vector &wires, @@ -701,7 +711,8 @@ class StateVectorKokkos final * @param controlled_wires Control wires. * @param controlled_values Control values (true or false). * @param wires Wires to apply gate to. - * @param inverse Indicates whether to use adjoint of gate. + * @param inverse Indicates whether to use adjoint of gate. (Default to false) + * @return PrecisionT Generator scale prefactor */ auto applyControlledGenerator(const std::string &opName, @@ -725,7 +736,8 @@ class StateVectorKokkos final * @param controlled_wires Control wires. * @param controlled_values Control values (true or false). * @param wires Wires to apply gate to. - * @param inverse Indicates whether to use adjoint of gate. + * @param inverse Indicates whether to use adjoint of gate. (Default to false) + * @return PrecisionT Generator scale prefactor */ auto applyGenerator(const std::string &opName, const std::vector &controlled_wires, From a4df6c09f71b49eaeffa1ee8b2eeb92c6e5d6fde Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Fri, 29 Nov 2024 15:18:00 +0000 Subject: [PATCH 43/53] update ctrl/adj nested comment --- pennylane_lightning/lightning_kokkos/_state_vector.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/lightning_kokkos/_state_vector.py b/pennylane_lightning/lightning_kokkos/_state_vector.py index 9bfb9879f5..062284def4 100644 --- a/pennylane_lightning/lightning_kokkos/_state_vector.py +++ b/pennylane_lightning/lightning_kokkos/_state_vector.py @@ -197,7 +197,7 @@ def _apply_lightning_controlled(self, operation): control_wires = list(operation.control_wires) control_values = operation.control_values target_wires = list(operation.target_wires) - inv = False # TODO: update to use adjoint in C++ instead of in Python + inv = False # TODO: update to use recursive _apply_lightning to handle nested adjoint/ctrl if method is not None: # apply n-controlled specialized gate param = operation.parameters method(control_wires, control_values, target_wires, inv, param) From 95cd1dbf440355bacb0b5dc966bce33ae6920fc8 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Fri, 29 Nov 2024 15:51:59 +0000 Subject: [PATCH 44/53] update non-const pointer applycontrolledmatrix and add test --- .../lightning_kokkos/StateVectorKokkos.hpp | 2 +- .../tests/Test_StateVectorLKokkos.cpp | 92 +++++++++++++++++-- 2 files changed, 85 insertions(+), 9 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp index 6ce864c3ea..aa08d3bef2 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp @@ -630,7 +630,7 @@ class StateVectorKokkos final const std::vector &wires, bool inverse = false) { PL_ABORT_IF(wires.empty(), "Number of wires must be larger than 0"); const std::size_t n2 = exp2(wires.size() * 2); - KokkosVector matrix_(matrix, n2); + KokkosVector matrix_("matrix_", n2); Kokkos::deep_copy(matrix_, UnmanagedComplexHostView(matrix, n2)); applyNCMultiQubitOp(matrix_, controlled_wires, controlled_values, wires, inverse); diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp index 62ccbe1cc4..b212b8c7f4 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp @@ -80,6 +80,9 @@ TEMPLATE_PRODUCT_TEST_CASE("StateVectorKokkos::applyMatrix with a std::vector", using ComplexT = typename StateVectorT::ComplexT; using VectorT = TestVector>; std::mt19937_64 re{1337}; + using UnmanagedComplexHostView = + Kokkos::View>; SECTION("Test wrong matrix size") { std::vector m(7, 0.0); @@ -107,6 +110,41 @@ TEMPLATE_PRODUCT_TEST_CASE("StateVectorKokkos::applyMatrix with a std::vector", Catch::Contains( "The size of matrix does not match with the given")); } + + SECTION("Test with different number of wires") { + using KokkosVector = typename StateVectorT::KokkosVector; + const std::size_t num_qubits = 5; + for (std::size_t num_wires = 1; num_wires < num_qubits; num_wires++) { + VectorT st_data_1 = + createRandomStateVectorData(re, num_qubits); + VectorT st_data_2 = st_data_1; + StateVectorT state_vector_1( + reinterpret_cast(st_data_1.data()), + st_data_1.size()); + StateVectorT state_vector_2( + reinterpret_cast(st_data_2.data()), + st_data_2.size()); + + std::vector wires(num_wires); + std::iota(wires.begin(), wires.end(), 0); + + auto m = randomUnitary(re, num_wires); + std::vector mkvec(reinterpret_cast(m.data()), + reinterpret_cast(m.data()) + + m.size()); + state_vector_1.applyMatrix(mkvec, wires); + + KokkosVector mkview("mkview", m.size()); + Kokkos::deep_copy( + mkview, UnmanagedComplexHostView( + reinterpret_cast(m.data()), m.size())); + state_vector_2.applyMultiQubitOp(mkview, wires); + + PrecisionT eps = std::numeric_limits::epsilon() * 10E3; + REQUIRE(isApproxEqual(state_vector_1.getDataVector(), + state_vector_2.getDataVector(), eps)); + } + } } TEMPLATE_PRODUCT_TEST_CASE("StateVectorKokkos::setState", "[errors]", @@ -192,10 +230,7 @@ TEMPLATE_PRODUCT_TEST_CASE("StateVectorKokkos::applyMatrix with a pointer", std::iota(wires.begin(), wires.end(), 0); auto m = randomUnitary(re, num_wires); - std::vector mkvec(reinterpret_cast(m.data()), - reinterpret_cast(m.data()) + - m.size()); - state_vector_1.applyMatrix(mkvec, wires); + state_vector_1.applyMatrix(reinterpret_cast(m.data()), wires); KokkosVector mkview("mkview", m.size()); Kokkos::deep_copy( @@ -218,6 +253,9 @@ TEMPLATE_PRODUCT_TEST_CASE( using ComplexT = typename StateVectorT::ComplexT; using VectorT = TestVector>; std::mt19937_64 re{1337}; + using UnmanagedComplexHostView = + Kokkos::View>; SECTION("Test wrong matrix size") { std::vector m(7, 0.0); @@ -245,6 +283,47 @@ TEMPLATE_PRODUCT_TEST_CASE( Catch::Contains( "The size of matrix does not match with the given")); } + + SECTION("Test with different number of wires") { + using KokkosVector = typename StateVectorT::KokkosVector; + const std::size_t num_qubits = 5; + for (std::size_t num_wires = 1; num_wires < (num_qubits - 1); + num_wires++) { + VectorT st_data_1 = + createRandomStateVectorData(re, num_qubits); + VectorT st_data_2 = st_data_1; + StateVectorT state_vector_1( + reinterpret_cast(st_data_1.data()), + st_data_1.size()); + StateVectorT state_vector_2( + reinterpret_cast(st_data_2.data()), + st_data_2.size()); + + std::vector wires(num_wires); + std::iota(wires.begin(), wires.end(), 0); + std::vector controlled_wires(num_qubits - num_wires); + std::iota(controlled_wires.begin(), controlled_wires.end(), + num_wires); + std::vector controlled_values(num_qubits - num_wires, true); + + auto m = randomUnitary(re, num_wires); + std::vector mkvec(reinterpret_cast(m.data()), + reinterpret_cast(m.data()) + + m.size()); + state_vector_1.applyControlledMatrix(mkvec, controlled_wires, + controlled_values, wires); + KokkosVector mkview("mkview", m.size()); + Kokkos::deep_copy( + mkview, UnmanagedComplexHostView( + reinterpret_cast(m.data()), m.size())); + state_vector_2.applyNCMultiQubitOp(mkview, controlled_wires, + controlled_values, wires); + + PrecisionT eps = std::numeric_limits::epsilon() * 10E3; + REQUIRE(isApproxEqual(state_vector_1.getDataVector(), + state_vector_2.getDataVector(), eps)); + } + } } TEMPLATE_PRODUCT_TEST_CASE( @@ -295,10 +374,7 @@ TEMPLATE_PRODUCT_TEST_CASE( std::vector controlled_values(num_qubits - num_wires, true); auto m = randomUnitary(re, num_wires); - std::vector mkvec(reinterpret_cast(m.data()), - reinterpret_cast(m.data()) + - m.size()); - state_vector_1.applyControlledMatrix(mkvec, controlled_wires, + state_vector_1.applyControlledMatrix(reinterpret_cast(m.data()), controlled_wires, controlled_values, wires); KokkosVector mkview("mkview", m.size()); Kokkos::deep_copy( From f0ac9526a9d824a13ffefe7809240ca98801f07b Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Fri, 29 Nov 2024 15:52:55 +0000 Subject: [PATCH 45/53] format --- .../lightning_kokkos/StateVectorKokkos.hpp | 38 ++++++++++++------- .../tests/Test_StateVectorLKokkos.cpp | 8 ++-- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp index aa08d3bef2..b26a3c6fe6 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp @@ -248,7 +248,7 @@ class StateVectorKokkos final /** * @brief Create a new state vector from data on the host. - * + * * @param hostdata_ Host array for state vector * @param length Length of host array (must be power of 2) * @param kokkos_args Arguments for Kokkos initialization @@ -263,7 +263,7 @@ class StateVectorKokkos final /** * @brief Create a new state vector from data on the host. - * + * * @param hostdata_ Host vector for state vector * @param length Length of host array (must be power of 2) * @param kokkos_args Arguments for Kokkos initialization @@ -278,7 +278,7 @@ class StateVectorKokkos final /** * @brief Create a new state vector from data on the host. - * + * * @param hostdata_ Host array for state vector * @param length Length of host array (must be power of 2) * @param kokkos_args Arguments for Kokkos initialization @@ -294,7 +294,7 @@ class StateVectorKokkos final /** * @brief Create a new state vector from data on the host. - * + * * @param hostdata_ Host vector for state vector * @param kokkos_args Arguments for Kokkos initialization */ @@ -447,7 +447,8 @@ class StateVectorKokkos final * @param controlled_wires Control wires. * @param controlled_values Control values (false or true). * @param wires Wires to apply gate to. - * @param inverse Indicates whether to use adjoint of gate. (Default to false) + * @param inverse Indicates whether to use adjoint of gate. (Default to + * false) * @param params Optional parameter list for parametric gates. * @param gate_matrix Optional unitary gate matrix if opName doesn't exist. */ @@ -560,7 +561,8 @@ class StateVectorKokkos final * * @param matrix Pointer to the array data (in row-major format). * @param wires Wires to apply gate to. - * @param inverse Indicate whether inverse should be taken. (Default to false) + * @param inverse Indicate whether inverse should be taken. (Default to + * false) */ inline void applyMatrix(ComplexT *matrix, const std::vector &wires, @@ -585,7 +587,8 @@ class StateVectorKokkos final * * @param matrix Pointer to the array data (in row-major format). * @param wires Wires to apply gate to. - * @param inverse Indicate whether inverse should be taken. (Default to false) + * @param inverse Indicate whether inverse should be taken. (Default to + * false) */ inline void applyMatrix(const ComplexT *matrix, const std::vector &wires, @@ -602,7 +605,8 @@ class StateVectorKokkos final * * @param matrix Matrix data (in row-major format). * @param wires Wires to apply gate to. - * @param inverse Indicate whether inverse should be taken. (Default to false) + * @param inverse Indicate whether inverse should be taken. (Default to + * false) */ inline void applyMatrix(const std::vector &matrix, const std::vector &wires, @@ -622,7 +626,8 @@ class StateVectorKokkos final * @param controlled_wires Controlled wires * @param controlled_values Controlled values (true or false) * @param wires Wires to apply gate to. - * @param inverse Indicate whether inverse should be taken. (Default to false) + * @param inverse Indicate whether inverse should be taken. (Default to + * false) */ inline void applyControlledMatrix( ComplexT *matrix, const std::vector &controlled_wires, @@ -644,7 +649,8 @@ class StateVectorKokkos final * @param controlled_wires Controlled wires * @param controlled_values Controlled values (true or false) * @param wires Wires to apply gate to. - * @param inverse Indicate whether inverse should be taken. (Default to false) + * @param inverse Indicate whether inverse should be taken. (Default to + * false) */ inline void applyControlledMatrix(const ComplexT *matrix, @@ -668,7 +674,8 @@ class StateVectorKokkos final * @param controlled_wires Control wires. * @param controlled_values Control values (false or true). * @param wires Wires to apply gate to. - * @param inverse Indicate whether inverse should be taken. (Default to false) + * @param inverse Indicate whether inverse should be taken. (Default to + * false) */ inline void applyControlledMatrix(const std::vector &matrix, @@ -690,7 +697,8 @@ class StateVectorKokkos final * * @param opName Name of gate to apply. * @param wires Wires to apply gate to. - * @param inverse Indicates whether to use adjoint of gate. (Default to false) + * @param inverse Indicates whether to use adjoint of gate. (Default to + * false) * @return PrecisionT Generator scale prefactor */ auto applyGenerator(const std::string &opName, @@ -711,7 +719,8 @@ class StateVectorKokkos final * @param controlled_wires Control wires. * @param controlled_values Control values (true or false). * @param wires Wires to apply gate to. - * @param inverse Indicates whether to use adjoint of gate. (Default to false) + * @param inverse Indicates whether to use adjoint of gate. (Default to + * false) * @return PrecisionT Generator scale prefactor */ auto @@ -736,7 +745,8 @@ class StateVectorKokkos final * @param controlled_wires Control wires. * @param controlled_values Control values (true or false). * @param wires Wires to apply gate to. - * @param inverse Indicates whether to use adjoint of gate. (Default to false) + * @param inverse Indicates whether to use adjoint of gate. (Default to + * false) * @return PrecisionT Generator scale prefactor */ auto applyGenerator(const std::string &opName, diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp index b212b8c7f4..3fd9b7e89c 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/tests/Test_StateVectorLKokkos.cpp @@ -230,7 +230,8 @@ TEMPLATE_PRODUCT_TEST_CASE("StateVectorKokkos::applyMatrix with a pointer", std::iota(wires.begin(), wires.end(), 0); auto m = randomUnitary(re, num_wires); - state_vector_1.applyMatrix(reinterpret_cast(m.data()), wires); + state_vector_1.applyMatrix(reinterpret_cast(m.data()), + wires); KokkosVector mkview("mkview", m.size()); Kokkos::deep_copy( @@ -374,8 +375,9 @@ TEMPLATE_PRODUCT_TEST_CASE( std::vector controlled_values(num_qubits - num_wires, true); auto m = randomUnitary(re, num_wires); - state_vector_1.applyControlledMatrix(reinterpret_cast(m.data()), controlled_wires, - controlled_values, wires); + state_vector_1.applyControlledMatrix( + reinterpret_cast(m.data()), controlled_wires, + controlled_values, wires); KokkosVector mkview("mkview", m.size()); Kokkos::deep_copy( mkview, UnmanagedComplexHostView( From f42ea8b86d6da79ab3e999d85ffc854148e675d2 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Fri, 29 Nov 2024 15:58:22 +0000 Subject: [PATCH 46/53] update scratch level exploration comment --- .../src/simulators/lightning_kokkos/StateVectorKokkos.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp index b26a3c6fe6..4e1d20bce2 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp @@ -427,7 +427,8 @@ class StateVectorKokkos final matrix_trans, wires)); break; default: - // TODO: explore runtime determine scratch space level (L0 vs L1) + // TODO: explore runtime determine L0 or L1 scratch level (for GPU + // shared memory), or SIMD std::size_t scratch_size = ScratchViewComplex::shmem_size(dim) + ScratchViewSizeT::shmem_size(dim); Kokkos::parallel_for( @@ -540,7 +541,8 @@ class StateVectorKokkos final wires)); break; default: - // TODO: explore runtime determine scratch space level (L0 vs L1) + // TODO: explore runtime determine L0 or L1 scratch level (for GPU + // shared memory), or SIMD const std::size_t scratch_size = ScratchViewComplex::shmem_size(dim) + ScratchViewSizeT::shmem_size(dim); From f4f2527fbf1ffb7598475de03deb906223f390f9 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Fri, 29 Nov 2024 16:59:38 +0000 Subject: [PATCH 47/53] precompute mask in controlbitpatterns calc --- .../lightning_kokkos/utils/UtilKokkos.hpp | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp index ee942ed45d..76cb3ee3e8 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp @@ -201,20 +201,20 @@ inline void ControlBitPatterns(std::vector &indices, if (controlled_wires.empty()) { return; } - std::vector controlled_values_i(controlled_values.size()); - std::transform(controlled_values.begin(), controlled_values.end(), - controlled_values_i.begin(), - [](const bool v) { return static_cast(v); }); - std::for_each( - indices.begin(), indices.end(), - [num_qubits, &controlled_wires, &controlled_values_i](std::size_t &i) { - for (std::size_t k = 0; k < controlled_wires.size(); k++) { - const std::size_t rev_wire = - (num_qubits - 1) - controlled_wires[k]; - const std::size_t value = controlled_values_i[k]; - i = (i & ~(one << rev_wire)) | (value << rev_wire); - } - }); + std::vector masks(controlled_wires.size()); + std::vector values(controlled_wires.size()); + for (std::size_t k = 0; k < controlled_wires.size(); k++) { + const std::size_t rev_wire = num_qubits - 1 - controlled_wires[k]; + masks[k] = ~(one << rev_wire); + values[k] = static_cast(controlled_values[k]) << rev_wire; + } + + std::for_each(indices.begin(), indices.end(), + [&masks, &values](std::size_t &i) { + for (std::size_t k = 0; k < masks.size(); k++) { + i = (i & masks[k]) | values[k]; + } + }); } /** From e33fc533aaf39e74e6d71a250e7793934d7cc435 Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 2 Dec 2024 12:59:47 -0500 Subject: [PATCH 48/53] implement shuli comments --- .../simulators/lightning_kokkos/StateVectorKokkos.hpp | 9 ++++----- .../lightning_kokkos/gates/MatrixGateFunctors.hpp | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp index 4e1d20bce2..1310390be6 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp @@ -587,7 +587,7 @@ class StateVectorKokkos final * @brief Apply a given matrix directly to the statevector using a * raw matrix pointer vector. * - * @param matrix Pointer to the array data (in row-major format). + * @param matrix Pointer to host matrix to apply to wires (in row-major format). * @param wires Wires to apply gate to. * @param inverse Indicate whether inverse should be taken. (Default to * false) @@ -622,9 +622,9 @@ class StateVectorKokkos final /** * @brief Apply a given matrix for controlled operations directly to the - * statevector using a raw matrix pointer vector. + * statevector using a raw matrix pointer on host. * - * @param matrix Pointer to the array data (in row-major format). + * @param matrix Pointer to host matrix to apply to wires (in row-major format). * @param controlled_wires Controlled wires * @param controlled_values Controlled values (true or false) * @param wires Wires to apply gate to. @@ -671,8 +671,7 @@ class StateVectorKokkos final /** * @brief Apply a given controlled-matrix directly to the statevector. * - * @param matrix Vector containing the statevector data (in row-major - * format). + * @param matrix Pointer to host matrix to apply to target wires (in row-major format). * @param controlled_wires Control wires. * @param controlled_values Control values (false or true). * @param wires Wires to apply gate to. diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/MatrixGateFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/MatrixGateFunctors.hpp index 19efbccb52..aaeba481dc 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/MatrixGateFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/MatrixGateFunctors.hpp @@ -202,7 +202,7 @@ template struct apply1QubitOpFunctor { num_qubits = num_qubits_; rev_wire = num_qubits - wires_[0] - 1; - rev_wire_shift = (exp2(rev_wire)); + rev_wire_shift = exp2(rev_wire); wire_parity = fillTrailingOnes(rev_wire); wire_parity_inv = fillLeadingOnes(rev_wire + 1); } From 083dc9761c91d9451419eb36c0fee166b55bb47f Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Mon, 2 Dec 2024 14:44:33 -0500 Subject: [PATCH 49/53] implement shuli comments --- .../lightning_kokkos/StateVectorKokkos.hpp | 27 +++++++++++-------- .../lightning_kokkos/utils/UtilKokkos.hpp | 1 + 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp index 1310390be6..3a1617a1eb 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp @@ -559,9 +559,10 @@ class StateVectorKokkos final /** * @brief Apply a given matrix directly to the statevector using a - * raw matrix pointer vector. + * raw matrix pointer on host memory. * - * @param matrix Pointer to the array data (in row-major format). + * @param matrix Pointer to the array data on host memory (in row-major + * format). * @param wires Wires to apply gate to. * @param inverse Indicate whether inverse should be taken. (Default to * false) @@ -585,9 +586,10 @@ class StateVectorKokkos final /** * @brief Apply a given matrix directly to the statevector using a - * raw matrix pointer vector. + * raw matrix pointer vector on host memory. * - * @param matrix Pointer to host matrix to apply to wires (in row-major format). + * @param matrix Pointer to host matrix to apply to wires (in row-major + * format). * @param wires Wires to apply gate to. * @param inverse Indicate whether inverse should be taken. (Default to * false) @@ -603,9 +605,9 @@ class StateVectorKokkos final } /** - * @brief Apply a given matrix directly to the statevector. + * @brief Apply a given matrix as a vector directly to the statevector. * - * @param matrix Matrix data (in row-major format). + * @param matrix Matrix data as a vector (in row-major format). * @param wires Wires to apply gate to. * @param inverse Indicate whether inverse should be taken. (Default to * false) @@ -622,9 +624,10 @@ class StateVectorKokkos final /** * @brief Apply a given matrix for controlled operations directly to the - * statevector using a raw matrix pointer on host. + * statevector using a raw matrix pointer on host memory. * - * @param matrix Pointer to host matrix to apply to wires (in row-major format). + * @param matrix Pointer to host matrix to apply to wires (in row-major + * format). * @param controlled_wires Controlled wires * @param controlled_values Controlled values (true or false) * @param wires Wires to apply gate to. @@ -645,7 +648,7 @@ class StateVectorKokkos final /** * @brief Apply a given matrix directly to the statevector using a - * raw matrix pointer vector. + * raw matrix pointer on host memory. * * @param matrix Pointer to the array data (in row-major format). * @param controlled_wires Controlled wires @@ -669,9 +672,11 @@ class StateVectorKokkos final } /** - * @brief Apply a given controlled-matrix directly to the statevector. + * @brief Apply a given controlled-matrix as a vector directly to the + * statevector. * - * @param matrix Pointer to host matrix to apply to target wires (in row-major format). + * @param matrix Matrix data as a vector to apply to target wires (in + * row-major format). * @param controlled_wires Control wires. * @param controlled_values Control values (false or true). * @param wires Wires to apply gate to. diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp index 76cb3ee3e8..282e6824fe 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp @@ -162,6 +162,7 @@ inline auto reverseWires(const std::size_t num_qubits, /** * @brief Generate bit patterns for multi-qubit operations + * TODO: parallelize with LK * * @param wires List of target wires. * @param num_qubits From 3a03af23d77972011611f94d9f912c4ad1b7c42f Mon Sep 17 00:00:00 2001 From: Joseph Lee <40768758+josephleekl@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:05:28 -0500 Subject: [PATCH 50/53] Update pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp Co-authored-by: Ali Asadi <10773383+maliasadi@users.noreply.github.com> --- .../core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp index 282e6824fe..10fbca2268 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/utils/UtilKokkos.hpp @@ -195,7 +195,7 @@ inline auto generateBitPatterns(const std::vector &wires, * @param controlled_wires Control wires. * @param controlled_values Control values (false or true). */ -inline void ControlBitPatterns(std::vector &indices, +inline void controlBitPatterns(std::vector &indices, const std::size_t num_qubits, const std::vector &controlled_wires, const std::vector &controlled_values) { From 7d04fd371ddd12673662551efba9f679eaa112e4 Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Tue, 3 Dec 2024 22:05:45 +0000 Subject: [PATCH 51/53] Auto update version from '0.40.0-dev24' to '0.40.0-dev25' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 17c98c7d19..43e4a2f30f 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev24" +__version__ = "0.40.0-dev25" From 5cdc1fa720d3012fce4ae8c5fd7678878e1ab62c Mon Sep 17 00:00:00 2001 From: Joseph Lee Date: Tue, 3 Dec 2024 17:19:06 -0500 Subject: [PATCH 52/53] update controlBitPatterns case --- .../lightning_kokkos/gates/BasicGateFunctors.hpp | 10 +++++----- .../lightning_kokkos/gates/MatrixGateFunctors.hpp | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp index 8abe9ac117..39b7872d55 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp @@ -27,7 +27,7 @@ namespace { using namespace Pennylane::Util; using Kokkos::kokkos_swap; using Pennylane::Gates::GateOperation; -using Pennylane::LightningKokkos::Util::ControlBitPatterns; +using Pennylane::LightningKokkos::Util::controlBitPatterns; using Pennylane::LightningKokkos::Util::generateBitPatterns; using Pennylane::LightningKokkos::Util::parity_2_offset; using Pennylane::LightningKokkos::Util::reverseWires; @@ -66,7 +66,7 @@ template class applyNCNFunctor { parity = parity_; std::vector indices_ = generateBitPatterns(wires, num_qubits); - ControlBitPatterns(indices_, num_qubits, controlled_wires, + controlBitPatterns(indices_, num_qubits, controlled_wires, controlled_values); indices = vector2view(indices_); Kokkos::parallel_for(Kokkos::TeamPolicy(two2N, Kokkos::AUTO, dim), @@ -110,7 +110,7 @@ class applyNC1Functor { parity = parity_; std::vector indices_ = generateBitPatterns(wires, num_qubits); - ControlBitPatterns(indices_, num_qubits, controlled_wires, + controlBitPatterns(indices_, num_qubits, controlled_wires, controlled_values); indices = vector2view(indices_); Kokkos::parallel_for( @@ -639,7 +639,7 @@ class applyNC2Functor { parity = parity_; std::vector indices_ = generateBitPatterns(wires, num_qubits); - ControlBitPatterns(indices_, num_qubits, controlled_wires, + controlBitPatterns(indices_, num_qubits, controlled_wires, controlled_values); indices = vector2view(indices_); Kokkos::parallel_for( @@ -1359,7 +1359,7 @@ class applyNC4Functor { parity = parity_; std::vector indices_ = generateBitPatterns(wires, num_qubits); - ControlBitPatterns(indices_, num_qubits, controlled_wires, + controlBitPatterns(indices_, num_qubits, controlled_wires, controlled_values); indices = vector2view(indices_); Kokkos::parallel_for( diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/MatrixGateFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/MatrixGateFunctors.hpp index aaeba481dc..a02effee76 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/MatrixGateFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/MatrixGateFunctors.hpp @@ -23,7 +23,7 @@ namespace { using namespace Pennylane::Util; using Kokkos::Experimental::swap; -using Pennylane::LightningKokkos::Util::ControlBitPatterns; +using Pennylane::LightningKokkos::Util::controlBitPatterns; using Pennylane::LightningKokkos::Util::generateBitPatterns; using Pennylane::LightningKokkos::Util::one; using Pennylane::LightningKokkos::Util::parity_2_offset; @@ -146,7 +146,7 @@ template struct NCMultiQubitOpFunctor { parity = parity_; std::vector indices_ = generateBitPatterns(wires_, num_qubits_); - ControlBitPatterns(indices_, num_qubits_, controlled_wires_, + controlBitPatterns(indices_, num_qubits_, controlled_wires_, controlled_values_); indices = vector2view(indices_); } @@ -246,7 +246,7 @@ template struct applyNC1QubitOpFunctor { parity = parity_; std::vector indices_ = generateBitPatterns(wires_, num_qubits_); - ControlBitPatterns(indices_, num_qubits_, controlled_wires_, + controlBitPatterns(indices_, num_qubits_, controlled_wires_, controlled_values_); indices = vector2view(indices_); } @@ -351,7 +351,7 @@ template struct applyNC2QubitOpFunctor { parity = parity_; std::vector indices_ = generateBitPatterns(wires_, num_qubits_); - ControlBitPatterns(indices_, num_qubits_, controlled_wires_, + controlBitPatterns(indices_, num_qubits_, controlled_wires_, controlled_values_); indices = vector2view(indices_); } @@ -470,7 +470,7 @@ template struct applyNC3QubitOpFunctor { parity = parity_; std::vector indices_ = generateBitPatterns(wires_, num_qubits_); - ControlBitPatterns(indices_, num_qubits_, controlled_wires_, + controlBitPatterns(indices_, num_qubits_, controlled_wires_, controlled_values_); indices = vector2view(indices_); } From 0d2e2282462ca2c285c8541c79bc70ab365cf740 Mon Sep 17 00:00:00 2001 From: ringo-but-quantum Date: Tue, 3 Dec 2024 22:19:54 +0000 Subject: [PATCH 53/53] Auto update version from '0.40.0-dev25' to '0.40.0-dev26' --- pennylane_lightning/core/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 43e4a2f30f..c9aef96380 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev25" +__version__ = "0.40.0-dev26"