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):