diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp index 59d607606..f75ce36a7 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/StateVectorKokkos.hpp @@ -466,9 +466,6 @@ class StateVectorKokkos final bool inverse = false, const std::vector ¶ms = {}, const std::vector &gate_matrix = {}) { - PL_ABORT_IF_NOT( - areVecsDisjoint(controlled_wires, wires), - "`controlled_wires` and `target wires` must be disjoint."); PL_ABORT_IF_NOT(controlled_wires.size() == controlled_values.size(), "`controlled_wires` must have the same size as " "`controlled_values`."); 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 2f5dd3f77..2e5badf07 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp @@ -529,54 +529,56 @@ void applyRot(Kokkos::View *> arr_, inverse, params); } -template -void applyNCGlobalPhase(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &controlled_wires, - const std::vector &controlled_values, - [[maybe_unused]] const std::vector &wires, - const bool inverse = false, - const std::vector ¶ms = {}) { - const Kokkos::complex phase = Kokkos::exp( - Kokkos::complex{0, (inverse) ? params[0] : -params[0]}); - auto core_function = - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i0, const std::size_t i1) { - arr(i1) *= phase; - arr(i0) *= phase; - }; - std::size_t target{0U}; - if (!controlled_wires.empty()) { - for (std::size_t i = 0; i < num_qubits; i++) { - if (std::find(controlled_wires.begin(), controlled_wires.end(), - i) == controlled_wires.end()) { - target = i; - break; - } - } +template +class applyNC2Functor {}; + +template +class applyNC2Functor { + using KokkosIntVector = Kokkos::View; + + Kokkos::View *> arr; + const FuncT core_function; + KokkosIntVector indices; + KokkosIntVector parity; + KokkosIntVector rev_wires; + KokkosIntVector rev_wire_shifts; + + public: + template + applyNC2Functor([[maybe_unused]] ExecutionSpace exec, + Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, FuncT core_function_) + : arr(arr_), core_function(core_function_) { + + std::tie(parity, rev_wires) = + Util::reverseWires(num_qubits, wires, controlled_wires); + indices = Util::generateControlBitPatterns(num_qubits, controlled_wires, + controlled_values, wires); + Kokkos::parallel_for( + Kokkos::RangePolicy( + 0, exp2(num_qubits - controlled_wires.size() - wires.size())), + *this); } - if (controlled_wires.empty()) { - applyNC1Functor( - ExecutionSpace{}, arr_, num_qubits, {target}, core_function); - } else { - applyNC1Functor( - ExecutionSpace{}, arr_, num_qubits, controlled_wires, - controlled_values, {target}, core_function); + KOKKOS_FUNCTION void operator()(const std::size_t k) const { + const std::size_t offset = Util::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); + + core_function(arr, i00 + offset, i01 + offset, i10 + offset, + i11 + offset); } -} +}; -template -void applyGlobalPhase(Kokkos::View *> arr_, - const std::size_t num_qubits, - [[maybe_unused]] const std::vector &wires, - const bool inverse = false, - const std::vector ¶ms = {}) { - applyNCGlobalPhase(arr_, num_qubits, {}, {}, - wires, inverse, params); -} +template +class applyNC2Functor { -template class applyNC2Functor { Kokkos::View *> arr; + const FuncT core_function; const std::size_t rev_wire0; const std::size_t rev_wire1; @@ -625,16 +627,17 @@ void applyCNOT(Kokkos::View *> arr_, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i01_ = i01; + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i01_ = i01; - kokkos_swap(arr(i10), arr(i11)); - }); + kokkos_swap(arr(i10), arr(i11)); + }; + + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -643,18 +646,17 @@ void applyCY(Kokkos::View *> arr_, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i01_ = i01; - Kokkos::complex v10 = arr(i10); - arr(i10) = - Kokkos::complex{imag(arr(i11)), -real(arr(i11))}; - arr(i11) = Kokkos::complex{-imag(v10), real(v10)}; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i01_ = i01; + Kokkos::complex v10 = arr(i10); + arr(i10) = Kokkos::complex{imag(arr(i11)), -real(arr(i11))}; + arr(i11) = Kokkos::complex{-imag(v10), real(v10)}; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -663,16 +665,42 @@ void applyCZ(Kokkos::View *> arr_, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i01_ = i01; - [[maybe_unused]] const auto i10_ = i10; - arr(i11) *= -1; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i01_ = i01; + [[maybe_unused]] const auto i10_ = i10; + arr(i11) *= -1; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); +} + +template +void applyNCSWAP(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + [[maybe_unused]] const bool inverse = false, + [[maybe_unused]] const std::vector ¶ms = {}) { + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i11_ = i11; + + kokkos_swap(arr(i10), arr(i01)); + }; + if (controlled_wires.empty()) { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -681,16 +709,8 @@ void applySWAP(Kokkos::View *> arr_, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i11_ = i11; - - kokkos_swap(arr(i10), arr(i01)); - }); + applyNCSWAP(arr_, num_qubits, {}, {}, wires, + inverse, params); } template @@ -703,16 +723,16 @@ void applyControlledPhaseShift(Kokkos::View *> arr_, const Kokkos::complex s = (inverse) ? exp(-Kokkos::complex(0, angle)) : exp(Kokkos::complex(0, angle)); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i01_ = i01; - [[maybe_unused]] const auto i10_ = i10; - arr(i11) *= s; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i01_ = i01; + [[maybe_unused]] const auto i10_ = i10; + arr(i11) *= s; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -724,20 +744,20 @@ void applyCRX(Kokkos::View *> arr_, const PrecisionT c = std::cos(angle / 2); const PrecisionT js = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i01_ = i01; - const Kokkos::complex v10 = arr(i10); - const Kokkos::complex v11 = arr(i11); - arr(i10) = Kokkos::complex{ - c * real(v10) + js * imag(v11), c * imag(v10) - js * real(v11)}; - arr(i11) = Kokkos::complex{ - c * real(v11) + js * imag(v10), c * imag(v11) - js * real(v10)}; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i01_ = i01; + const Kokkos::complex v10 = arr(i10); + const Kokkos::complex v11 = arr(i11); + arr(i10) = Kokkos::complex{c * real(v10) + js * imag(v11), + c * imag(v10) - js * real(v11)}; + arr(i11) = Kokkos::complex{c * real(v11) + js * imag(v10), + c * imag(v11) - js * real(v10)}; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -748,18 +768,18 @@ void applyCRY(Kokkos::View *> arr_, const PrecisionT &angle = params[0]; const PrecisionT c = std::cos(angle / 2); const PrecisionT s = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i01_ = i01; - const Kokkos::complex v10 = arr(i10); - const Kokkos::complex v11 = arr(i11); - arr(i10) = c * v10 - s * v11; - arr(i11) = s * v10 + c * v11; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i01_ = i01; + const Kokkos::complex v10 = arr(i10); + const Kokkos::complex v11 = arr(i11); + arr(i10) = c * v10 - s * v11; + arr(i11) = s * v10 + c * v11; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -773,17 +793,17 @@ void applyCRZ(Kokkos::View *> arr_, const Kokkos::complex shift_0{ cos_angle, (inverse) ? sin_angle : -sin_angle}; const Kokkos::complex shift_1 = Kokkos::conj(shift_0); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i01_ = i01; - - arr(i10) *= shift_0; - arr(i11) *= shift_1; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i01_ = i01; + + arr(i10) *= shift_0; + arr(i11) *= shift_1; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -801,18 +821,57 @@ void applyCRot(Kokkos::View *> arr_, const Kokkos::complex mat_0b01 = mat[0b01]; const Kokkos::complex mat_0b10 = mat[0b10]; const Kokkos::complex mat_0b11 = mat[0b11]; - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i01_ = i01; - const Kokkos::complex v0 = arr(i10); - const Kokkos::complex v1 = arr(i11); - arr(i10) = mat_0b00 * v0 + mat_0b01 * v1; - arr(i11) = mat_0b10 * v0 + mat_0b11 * v1; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i01_ = i01; + const Kokkos::complex v0 = arr(i10); + const Kokkos::complex v1 = arr(i11); + arr(i10) = mat_0b00 * v0 + mat_0b01 * v1; + arr(i11) = mat_0b10 * v0 + mat_0b11 * v1; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); +} + +template +void applyNCIsingXX(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + + const PrecisionT &angle = params[0]; + const PrecisionT cr = std::cos(angle / 2); + const PrecisionT sj = + (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + const Kokkos::complex v00 = arr(i00); + const Kokkos::complex v01 = arr(i01); + const Kokkos::complex v10 = arr(i10); + const Kokkos::complex v11 = arr(i11); + arr(i00) = Kokkos::complex{cr * real(v00) + sj * imag(v11), + cr * imag(v00) - sj * real(v11)}; + arr(i01) = Kokkos::complex{cr * real(v01) + sj * imag(v10), + cr * imag(v01) - sj * real(v10)}; + arr(i10) = Kokkos::complex{cr * real(v10) + sj * imag(v01), + cr * imag(v10) - sj * real(v01)}; + arr(i11) = Kokkos::complex{cr * real(v11) + sj * imag(v00), + cr * imag(v11) - sj * real(v00)}; + }; + if (controlled_wires.empty()) { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -821,32 +880,44 @@ void applyIsingXX(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { + applyNCIsingXX(arr_, num_qubits, {}, {}, wires, + inverse, params); +} +template +void applyNCIsingXY(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - const Kokkos::complex v00 = arr(i00); - const Kokkos::complex v01 = arr(i01); - const Kokkos::complex v10 = arr(i10); - const Kokkos::complex v11 = arr(i11); - arr(i00) = - Kokkos::complex{cr * real(v00) + sj * imag(v11), - cr * imag(v00) - sj * real(v11)}; - arr(i01) = - Kokkos::complex{cr * real(v01) + sj * imag(v10), - cr * imag(v01) - sj * real(v10)}; - arr(i10) = - Kokkos::complex{cr * real(v10) + sj * imag(v01), - cr * imag(v10) - sj * real(v01)}; - arr(i11) = - Kokkos::complex{cr * real(v11) + sj * imag(v00), - cr * imag(v11) - sj * real(v00)}; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + const Kokkos::complex v00 = arr(i00); + const Kokkos::complex v01 = arr(i01); + const Kokkos::complex v10 = arr(i10); + const Kokkos::complex v11 = arr(i11); + arr(i00) = Kokkos::complex{real(v00), imag(v00)}; + arr(i01) = Kokkos::complex{cr * real(v01) - sj * imag(v10), + cr * imag(v01) + sj * real(v10)}; + arr(i10) = Kokkos::complex{cr * real(v10) - sj * imag(v01), + cr * imag(v10) + sj * real(v01)}; + arr(i11) = Kokkos::complex{real(v11), imag(v11)}; + }; + if (controlled_wires.empty()) { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -855,28 +926,47 @@ void applyIsingXY(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { + applyNCIsingXY(arr_, num_qubits, {}, {}, wires, + inverse, params); +} + +template +void applyNCIsingYY(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - const Kokkos::complex v00 = arr(i00); - const Kokkos::complex v01 = arr(i01); - const Kokkos::complex v10 = arr(i10); - const Kokkos::complex v11 = arr(i11); - arr(i00) = Kokkos::complex{real(v00), imag(v00)}; - arr(i01) = - Kokkos::complex{cr * real(v01) - sj * imag(v10), - cr * imag(v01) + sj * real(v10)}; - arr(i10) = - Kokkos::complex{cr * real(v10) - sj * imag(v01), - cr * imag(v10) + sj * real(v01)}; - arr(i11) = Kokkos::complex{real(v11), imag(v11)}; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + const Kokkos::complex v00 = arr(i00); + const Kokkos::complex v01 = arr(i01); + const Kokkos::complex v10 = arr(i10); + const Kokkos::complex v11 = arr(i11); + arr(i00) = Kokkos::complex{cr * real(v00) - sj * imag(v11), + cr * imag(v00) + sj * real(v11)}; + arr(i01) = Kokkos::complex{cr * real(v01) + sj * imag(v10), + cr * imag(v01) - sj * real(v10)}; + arr(i10) = Kokkos::complex{cr * real(v10) + sj * imag(v01), + cr * imag(v10) - sj * real(v01)}; + arr(i11) = Kokkos::complex{cr * real(v11) - sj * imag(v00), + cr * imag(v11) + sj * real(v00)}; + }; + if (controlled_wires.empty()) { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -885,32 +975,40 @@ void applyIsingYY(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { + applyNCIsingYY(arr_, num_qubits, {}, {}, wires, + inverse, params); +} + +template +void applyNCIsingZZ(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + const PrecisionT &angle = params[0]; - const PrecisionT cr = std::cos(angle / 2); - const PrecisionT sj = - (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - const Kokkos::complex v00 = arr(i00); - const Kokkos::complex v01 = arr(i01); - const Kokkos::complex v10 = arr(i10); - const Kokkos::complex v11 = arr(i11); - arr(i00) = - Kokkos::complex{cr * real(v00) - sj * imag(v11), - cr * imag(v00) + sj * real(v11)}; - arr(i01) = - Kokkos::complex{cr * real(v01) + sj * imag(v10), - cr * imag(v01) - sj * real(v10)}; - arr(i10) = - Kokkos::complex{cr * real(v10) + sj * imag(v01), - cr * imag(v10) - sj * real(v01)}; - arr(i11) = - Kokkos::complex{cr * real(v11) - sj * imag(v00), - cr * imag(v11) + sj * real(v00)}; - }); + const Kokkos::complex shift_0 = Kokkos::complex{ + std::cos(angle / 2), + (inverse) ? std::sin(angle / 2) : -std::sin(angle / 2)}; + const Kokkos::complex shift_1 = conj(shift_0); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + arr(i00) *= shift_0; + arr(i01) *= shift_1; + arr(i10) *= shift_1; + arr(i11) *= shift_0; + }; + if (controlled_wires.empty()) { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -919,21 +1017,41 @@ void applyIsingZZ(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { + applyNCIsingZZ(arr_, num_qubits, {}, {}, wires, + inverse, params); +} + +template +void applyNCSingleExcitation(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; - const Kokkos::complex shift_0 = Kokkos::complex{ - std::cos(angle / 2), - (inverse) ? std::sin(angle / 2) : -std::sin(angle / 2)}; - const Kokkos::complex shift_1 = conj(shift_0); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - arr(i00) *= shift_0; - arr(i01) *= shift_1; - arr(i10) *= shift_1; - arr(i11) *= shift_0; - }); + const PrecisionT cr = std::cos(angle / 2); + const PrecisionT sj = + (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i11_ = i11; + + const Kokkos::complex v01 = arr(i01); + const Kokkos::complex v10 = arr(i10); + arr(i01) = cr * v01 - sj * v10; + arr(i10) = sj * v01 + cr * v10; + }; + if (controlled_wires.empty()) { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -942,23 +1060,43 @@ void applySingleExcitation(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { + applyNCSingleExcitation( + arr_, num_qubits, {}, {}, wires, inverse, params); +} + +template +void applyNCSingleExcitationMinus( + Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, const bool inverse = false, + const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i11_ = i11; - - const Kokkos::complex v01 = arr(i01); - const Kokkos::complex v10 = arr(i10); - arr(i01) = cr * v01 - sj * v10; - arr(i10) = sj * v01 + cr * v10; - }); + const Kokkos::complex e = + (inverse) ? exp(Kokkos::complex(0, angle / 2)) + : exp(Kokkos::complex(0, -angle / 2)); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + const Kokkos::complex v01 = arr(i01); + const Kokkos::complex v10 = arr(i10); + arr(i00) *= e; + arr(i01) = cr * v01 - sj * v10; + arr(i10) = sj * v01 + cr * v10; + arr(i11) *= e; + }; + if (controlled_wires.empty()) { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -966,25 +1104,46 @@ void applySingleExcitationMinus( Kokkos::View *> arr_, const std::size_t num_qubits, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { + applyNCSingleExcitationMinus( + arr_, num_qubits, {}, {}, wires, inverse, params); +} + +template +void applyNCSingleExcitationPlus( + Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, const bool inverse = false, + const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); const Kokkos::complex e = - (inverse) ? exp(Kokkos::complex(0, angle / 2)) - : exp(Kokkos::complex(0, -angle / 2)); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - const Kokkos::complex v01 = arr(i01); - const Kokkos::complex v10 = arr(i10); - arr(i00) *= e; - arr(i01) = cr * v01 - sj * v10; - arr(i10) = sj * v01 + cr * v10; - arr(i11) *= e; - }); + (inverse) ? exp(Kokkos::complex(0, -angle / 2)) + : exp(Kokkos::complex(0, angle / 2)); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i11_ = i11; + + const Kokkos::complex v01 = arr(i01); + const Kokkos::complex v10 = arr(i10); + arr(i00) *= e; + arr(i01) = cr * v01 - sj * v10; + arr(i10) = sj * v01 + cr * v10; + arr(i11) *= e; + }; + if (controlled_wires.empty()) { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template @@ -993,28 +1152,8 @@ void applySingleExcitationPlus(Kokkos::View *> arr_, const std::vector &wires, const bool inverse = false, const std::vector ¶ms = {}) { - const PrecisionT &angle = params[0]; - const PrecisionT cr = std::cos(angle / 2); - const PrecisionT sj = - (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - const Kokkos::complex e = - (inverse) ? exp(Kokkos::complex(0, -angle / 2)) - : exp(Kokkos::complex(0, angle / 2)); - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i11_ = i11; - - const Kokkos::complex v01 = arr(i01); - const Kokkos::complex v10 = arr(i10); - arr(i00) *= e; - arr(i01) = cr * v01 - sj * v10; - arr(i10) = sj * v01 + cr * v10; - arr(i11) *= e; - }); + applyNCSingleExcitationPlus( + arr_, num_qubits, {}, {}, wires, inverse, params); } template class applyNC3Functor { @@ -1130,7 +1269,68 @@ void applyToffoli(Kokkos::View *> arr_, }); } -template class applyNC4Functor { +template +class applyNC4Functor {}; + +template +class applyNC4Functor { + using KokkosIntVector = Kokkos::View; + + Kokkos::View *> arr; + const FuncT core_function; + KokkosIntVector indices; + KokkosIntVector parity; + KokkosIntVector rev_wires; + KokkosIntVector rev_wire_shifts; + + public: + template + applyNC4Functor([[maybe_unused]] ExecutionSpace exec, + Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, FuncT core_function_) + : arr(arr_), core_function(core_function_) { + + std::tie(parity, rev_wires) = + Util::reverseWires(num_qubits, wires, controlled_wires); + indices = Util::generateControlBitPatterns(num_qubits, controlled_wires, + controlled_values, wires); + Kokkos::parallel_for( + Kokkos::RangePolicy( + 0, exp2(num_qubits - controlled_wires.size() - wires.size())), + *this); + } + KOKKOS_FUNCTION void operator()(const std::size_t k) const { + const std::size_t offset = Util::parity_2_offset(parity, k); + 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, + i0100 + offset, i0101 + offset, i0110 + offset, i0111 + offset, + i1000 + offset, i1001 + offset, i1010 + offset, i1011 + offset, + i1100 + offset, i1101 + offset, i1110 + offset, i1111 + offset); + } +}; + +template +class applyNC4Functor { Kokkos::View *> arr; const FuncT core_function; const std::size_t rev_wire0; @@ -1244,17 +1444,18 @@ template class applyNC4Functor { }; template -void applyDoubleExcitation(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - const bool inverse = false, - const std::vector ¶ms = {}) { +void applyNCDoubleExcitation(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = (inverse) ? -std::sin(angle / 2) : std::sin(angle / 2); - applyNC4Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0000, const std::size_t i0001, const std::size_t i0010, const std::size_t i0011, @@ -1264,40 +1465,60 @@ void applyDoubleExcitation(Kokkos::View *> arr_, const std::size_t i1010, const std::size_t i1011, const std::size_t i1100, const std::size_t i1101, const std::size_t i1110, const std::size_t i1111) { - [[maybe_unused]] const auto i0000_ = i0000; - [[maybe_unused]] const auto i0001_ = i0001; - [[maybe_unused]] const auto i0010_ = i0010; - [[maybe_unused]] const auto i0100_ = i0100; - [[maybe_unused]] const auto i0101_ = i0101; - [[maybe_unused]] const auto i0110_ = i0110; - [[maybe_unused]] const auto i0111_ = i0111; - [[maybe_unused]] const auto i1000_ = i1000; - [[maybe_unused]] const auto i1001_ = i1001; - [[maybe_unused]] const auto i1010_ = i1010; - [[maybe_unused]] const auto i1011_ = i1011; - [[maybe_unused]] const auto i1101_ = i1101; - [[maybe_unused]] const auto i1110_ = i1110; - [[maybe_unused]] const auto i1111_ = i1111; - const Kokkos::complex v3 = arr(i0011); - const Kokkos::complex v12 = arr(i1100); - arr(i0011) = cr * v3 - sj * v12; - arr(i1100) = sj * v3 + cr * v12; - }); + [[maybe_unused]] const auto i0000_ = i0000; + [[maybe_unused]] const auto i0001_ = i0001; + [[maybe_unused]] const auto i0010_ = i0010; + [[maybe_unused]] const auto i0100_ = i0100; + [[maybe_unused]] const auto i0101_ = i0101; + [[maybe_unused]] const auto i0110_ = i0110; + [[maybe_unused]] const auto i0111_ = i0111; + [[maybe_unused]] const auto i1000_ = i1000; + [[maybe_unused]] const auto i1001_ = i1001; + [[maybe_unused]] const auto i1010_ = i1010; + [[maybe_unused]] const auto i1011_ = i1011; + [[maybe_unused]] const auto i1101_ = i1101; + [[maybe_unused]] const auto i1110_ = i1110; + [[maybe_unused]] const auto i1111_ = i1111; + const Kokkos::complex v3 = arr(i0011); + const Kokkos::complex v12 = arr(i1100); + arr(i0011) = cr * v3 - sj * v12; + arr(i1100) = sj * v3 + cr * v12; + }; + if (controlled_wires.empty()) { + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template -void applyDoubleExcitationMinus( +void applyDoubleExcitation(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + applyNCDoubleExcitation( + arr_, num_qubits, {}, {}, wires, inverse, params); +} + +template +void applyNCDoubleExcitationMinus( Kokkos::View *> arr_, - const std::size_t num_qubits, const std::vector &wires, - const bool inverse = false, const std::vector ¶ms = {}) { + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, const bool inverse = false, + const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = inverse ? -std::sin(angle / 2) : std::sin(angle / 2); const Kokkos::complex e = inverse ? exp(Kokkos::complex(0, angle / 2)) : exp(Kokkos::complex(0, -angle / 2)); - applyNC4Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0000, const std::size_t i0001, const std::size_t i0010, const std::size_t i0011, @@ -1307,41 +1528,59 @@ void applyDoubleExcitationMinus( const std::size_t i1010, const std::size_t i1011, const std::size_t i1100, const std::size_t i1101, const std::size_t i1110, const std::size_t i1111) { - const Kokkos::complex v3 = arr(i0011); - const Kokkos::complex v12 = arr(i1100); - arr(i0000) *= e; - arr(i0001) *= e; - arr(i0010) *= e; - arr(i0011) = cr * v3 - sj * v12; - arr(i0100) *= e; - arr(i0101) *= e; - arr(i0110) *= e; - arr(i0111) *= e; - arr(i1000) *= e; - arr(i1001) *= e; - arr(i1010) *= e; - arr(i1011) *= e; - arr(i1100) = sj * v3 + cr * v12; - arr(i1101) *= e; - arr(i1110) *= e; - arr(i1111) *= e; - }); + const Kokkos::complex v3 = arr(i0011); + const Kokkos::complex v12 = arr(i1100); + arr(i0000) *= e; + arr(i0001) *= e; + arr(i0010) *= e; + arr(i0011) = cr * v3 - sj * v12; + arr(i0100) *= e; + arr(i0101) *= e; + arr(i0110) *= e; + arr(i0111) *= e; + arr(i1000) *= e; + arr(i1001) *= e; + arr(i1010) *= e; + arr(i1011) *= e; + arr(i1100) = sj * v3 + cr * v12; + arr(i1101) *= e; + arr(i1110) *= e; + arr(i1111) *= e; + }; + if (controlled_wires.empty()) { + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } } template -void applyDoubleExcitationPlus(Kokkos::View *> arr_, - const std::size_t num_qubits, - const std::vector &wires, - const bool inverse = false, - const std::vector ¶ms = {}) { +void applyDoubleExcitationMinus( + Kokkos::View *> arr_, + const std::size_t num_qubits, const std::vector &wires, + const bool inverse = false, const std::vector ¶ms = {}) { + applyNCDoubleExcitationMinus( + arr_, num_qubits, {}, {}, wires, inverse, params); +} + +template +void applyNCDoubleExcitationPlus( + Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, const bool inverse = false, + const std::vector ¶ms = {}) { const PrecisionT &angle = params[0]; const PrecisionT cr = std::cos(angle / 2); const PrecisionT sj = inverse ? -std::sin(angle / 2) : std::sin(angle / 2); const Kokkos::complex e = inverse ? exp(Kokkos::complex(0, -angle / 2)) : exp(Kokkos::complex(0, angle / 2)); - applyNC4Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0000, const std::size_t i0001, const std::size_t i0010, const std::size_t i0011, @@ -1351,25 +1590,43 @@ void applyDoubleExcitationPlus(Kokkos::View *> arr_, const std::size_t i1010, const std::size_t i1011, const std::size_t i1100, const std::size_t i1101, const std::size_t i1110, const std::size_t i1111) { - const Kokkos::complex v3 = arr(i0011); - const Kokkos::complex v12 = arr(i1100); - arr(i0000) *= e; - arr(i0001) *= e; - arr(i0010) *= e; - arr(i0011) = cr * v3 - sj * v12; - arr(i0100) *= e; - arr(i0101) *= e; - arr(i0110) *= e; - arr(i0111) *= e; - arr(i1000) *= e; - arr(i1001) *= e; - arr(i1010) *= e; - arr(i1011) *= e; - arr(i1100) = sj * v3 + cr * v12; - arr(i1101) *= e; - arr(i1110) *= e; - arr(i1111) *= e; - }); + const Kokkos::complex v3 = arr(i0011); + const Kokkos::complex v12 = arr(i1100); + arr(i0000) *= e; + arr(i0001) *= e; + arr(i0010) *= e; + arr(i0011) = cr * v3 - sj * v12; + arr(i0100) *= e; + arr(i0101) *= e; + arr(i0110) *= e; + arr(i0111) *= e; + arr(i1000) *= e; + arr(i1001) *= e; + arr(i1010) *= e; + arr(i1011) *= e; + arr(i1100) = sj * v3 + cr * v12; + arr(i1101) *= e; + arr(i1110) *= e; + arr(i1111) *= e; + }; + if (controlled_wires.empty()) { + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); + } else { + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, wires, core_function); + } +} + +template +void applyDoubleExcitationPlus(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + applyNCDoubleExcitationPlus( + arr_, num_qubits, {}, {}, wires, inverse, params); } template @@ -1450,6 +1707,52 @@ void applyPauliRot(Kokkos::View *> arr_, }); } +template +void applyNCGlobalPhase(Kokkos::View *> arr_, + const std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + [[maybe_unused]] const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + const Kokkos::complex phase = Kokkos::exp( + Kokkos::complex{0, (inverse) ? params[0] : -params[0]}); + auto core_function = + KOKKOS_LAMBDA(Kokkos::View *> arr, + const std::size_t i0, const std::size_t i1) { + arr(i1) *= phase; + arr(i0) *= phase; + }; + std::size_t target{0U}; + if (!controlled_wires.empty()) { + for (std::size_t i = 0; i < num_qubits; i++) { + if (std::find(controlled_wires.begin(), controlled_wires.end(), + i) == controlled_wires.end()) { + target = i; + break; + } + } + } + if (controlled_wires.empty()) { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, {target}, core_function); + } else { + applyNC1Functor( + ExecutionSpace{}, arr_, num_qubits, controlled_wires, + controlled_values, {target}, core_function); + } +} + +template +void applyGlobalPhase(Kokkos::View *> arr_, + const std::size_t num_qubits, + [[maybe_unused]] const std::vector &wires, + const bool inverse = false, + const std::vector ¶ms = {}) { + applyNCGlobalPhase(arr_, num_qubits, {}, {}, + wires, inverse, params); +} + template void applyNamedOperation(const GateOperation gateop, Kokkos::View *> arr_, @@ -1633,6 +1936,60 @@ void applyNCNamedOperation(const ControlledGateOperation gateop, applyNCRot(arr_, num_qubits, controlled_wires, controlled_values, wires, inverse, params); return; + case ControlledGateOperation::SWAP: + applyNCSWAP(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, params); + return; + case ControlledGateOperation::IsingXX: + applyNCIsingXX(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, + params); + return; + case ControlledGateOperation::IsingXY: + applyNCIsingXY(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, + params); + return; + case ControlledGateOperation::IsingYY: + applyNCIsingYY(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, + params); + return; + case ControlledGateOperation::IsingZZ: + applyNCIsingZZ(arr_, num_qubits, controlled_wires, + controlled_values, wires, inverse, + params); + return; + case ControlledGateOperation::SingleExcitation: + applyNCSingleExcitation( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse, params); + return; + case ControlledGateOperation::SingleExcitationMinus: + applyNCSingleExcitationMinus( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse, params); + return; + case ControlledGateOperation::SingleExcitationPlus: + applyNCSingleExcitationPlus( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse, params); + return; + case ControlledGateOperation::DoubleExcitation: + applyNCDoubleExcitation( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse, params); + return; + case ControlledGateOperation::DoubleExcitationMinus: + applyNCDoubleExcitationMinus( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse, params); + return; + case ControlledGateOperation::DoubleExcitationPlus: + applyNCDoubleExcitationPlus( + arr_, num_qubits, controlled_wires, controlled_values, wires, + inverse, params); + return; case ControlledGateOperation::GlobalPhase: applyNCGlobalPhase(arr_, num_qubits, controlled_wires, controlled_values, wires, inverse, diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp index e35b75b69..7dea81e23 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGeneratorFunctors.hpp @@ -52,17 +52,17 @@ void applyGenControlledPhaseShift( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i11_ = i11; + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i11_ = i11; - arr(i00) = 0.0; - arr(i01) = 0.0; - arr(i10) = 0.0; - }); + arr(i00) = 0.0; + arr(i01) = 0.0; + arr(i10) = 0.0; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -71,15 +71,15 @@ void applyGenCRX(Kokkos::View *> arr_, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - arr(i00) = 0.0; - arr(i01) = 0.0; - kokkos_swap(arr(i10), arr(i11)); - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + arr(i00) = 0.0; + arr(i01) = 0.0; + kokkos_swap(arr(i10), arr(i11)); + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -88,18 +88,17 @@ void applyGenCRY(Kokkos::View *> arr_, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - arr(i00) = 0.0; - arr(i01) = 0.0; - const auto v0 = arr(i10); - arr(i10) = - Kokkos::complex{imag(arr(i11)), -real(arr(i11))}; - arr(i11) = Kokkos::complex{-imag(v0), real(v0)}; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + arr(i00) = 0.0; + arr(i01) = 0.0; + const auto v0 = arr(i10); + arr(i10) = Kokkos::complex{imag(arr(i11)), -real(arr(i11))}; + arr(i11) = Kokkos::complex{-imag(v0), real(v0)}; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -108,16 +107,16 @@ void applyGenCRZ(Kokkos::View *> arr_, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i10_ = i10; - arr(i00) = 0.0; - arr(i01) = 0.0; - arr(i11) *= -1; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i10_ = i10; + arr(i00) = 0.0; + arr(i01) = 0.0; + arr(i11) *= -1; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -126,14 +125,14 @@ void applyGenIsingXX( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - kokkos_swap(arr(i00), arr(i11)); - kokkos_swap(arr(i10), arr(i01)); - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + kokkos_swap(arr(i00), arr(i11)); + kokkos_swap(arr(i10), arr(i01)); + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -142,15 +141,15 @@ void applyGenIsingXY( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - kokkos_swap(arr(i10), arr(i01)); - arr(i00) = 0.0; - arr(i11) = 0.0; - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + kokkos_swap(arr(i10), arr(i01)); + arr(i00) = 0.0; + arr(i11) = 0.0; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -159,16 +158,16 @@ void applyGenIsingYY( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - const auto v00 = arr(i00); - arr(i00) = -arr(i11); - arr(i11) = -v00; - kokkos_swap(arr(i10), arr(i01)); - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + const auto v00 = arr(i00); + arr(i00) = -arr(i11); + arr(i11) = -v00; + kokkos_swap(arr(i10), arr(i01)); + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -177,17 +176,17 @@ void applyGenIsingZZ( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i11_ = i11; + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i11_ = i11; - arr(i10) *= -1; - arr(i01) *= -1; - }); + arr(i10) *= -1; + arr(i01) *= -1; + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -196,17 +195,17 @@ void applyGenSingleExcitation( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - arr(i00) = 0.0; - arr(i01) *= Kokkos::complex{0.0, 1.0}; - arr(i10) *= Kokkos::complex{0.0, -1.0}; - arr(i11) = 0.0; - kokkos_swap(arr(i10), arr(i01)); - }); + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + arr(i00) = 0.0; + arr(i01) *= Kokkos::complex{0.0, 1.0}; + arr(i10) *= Kokkos::complex{0.0, -1.0}; + arr(i11) = 0.0; + kokkos_swap(arr(i10), arr(i01)); + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -215,18 +214,18 @@ void applyGenSingleExcitationMinus( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i11_ = i11; + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i11_ = i11; - arr(i01) *= Kokkos::complex{0.0, 1.0}; - arr(i10) *= Kokkos::complex{0.0, -1.0}; - kokkos_swap(arr(i10), arr(i01)); - }); + arr(i01) *= Kokkos::complex{0.0, 1.0}; + arr(i10) *= Kokkos::complex{0.0, -1.0}; + kokkos_swap(arr(i10), arr(i01)); + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -235,20 +234,20 @@ void applyGenSingleExcitationPlus( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC2Functor( - ExecutionSpace{}, arr_, num_qubits, wires, - KOKKOS_LAMBDA(Kokkos::View *> arr, - const std::size_t i00, const std::size_t i01, - const std::size_t i10, const std::size_t i11) { - [[maybe_unused]] const auto i00_ = i00; - [[maybe_unused]] const auto i11_ = i11; + auto core_function = KOKKOS_LAMBDA( + Kokkos::View *> arr, const std::size_t i00, + const std::size_t i01, const std::size_t i10, const std::size_t i11) { + [[maybe_unused]] const auto i00_ = i00; + [[maybe_unused]] const auto i11_ = i11; - arr(i00) *= -1; - arr(i01) *= Kokkos::complex{0.0, 1.0}; - arr(i10) *= Kokkos::complex{0.0, -1.0}; - arr(i11) *= -1; - kokkos_swap(arr(i10), arr(i01)); - }); + arr(i00) *= -1; + arr(i01) *= Kokkos::complex{0.0, 1.0}; + arr(i10) *= Kokkos::complex{0.0, -1.0}; + arr(i11) *= -1; + kokkos_swap(arr(i10), arr(i01)); + }; + applyNC2Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -257,8 +256,7 @@ void applyGenDoubleExcitation( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC4Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0000, const std::size_t i0001, const std::size_t i0010, const std::size_t i0011, @@ -268,25 +266,27 @@ void applyGenDoubleExcitation( const std::size_t i1010, const std::size_t i1011, const std::size_t i1100, const std::size_t i1101, const std::size_t i1110, const std::size_t i1111) { - const Kokkos::complex v3 = arr(i0011); - const Kokkos::complex v12 = arr(i1100); - arr(i0000) = 0.0; - arr(i0001) = 0.0; - arr(i0010) = 0.0; - arr(i0011) = v12 * Kokkos::complex{0.0, -1.0}; - arr(i0100) = 0.0; - arr(i0101) = 0.0; - arr(i0110) = 0.0; - arr(i0111) = 0.0; - arr(i1000) = 0.0; - arr(i1001) = 0.0; - arr(i1010) = 0.0; - arr(i1011) = 0.0; - arr(i1100) = v3 * Kokkos::complex{0.0, 1.0}; - arr(i1101) = 0.0; - arr(i1110) = 0.0; - arr(i1111) = 0.0; - }); + const Kokkos::complex v3 = arr(i0011); + const Kokkos::complex v12 = arr(i1100); + arr(i0000) = 0.0; + arr(i0001) = 0.0; + arr(i0010) = 0.0; + arr(i0011) = v12 * Kokkos::complex{0.0, -1.0}; + arr(i0100) = 0.0; + arr(i0101) = 0.0; + arr(i0110) = 0.0; + arr(i0111) = 0.0; + arr(i1000) = 0.0; + arr(i1001) = 0.0; + arr(i1010) = 0.0; + arr(i1011) = 0.0; + arr(i1100) = v3 * Kokkos::complex{0.0, 1.0}; + arr(i1101) = 0.0; + arr(i1110) = 0.0; + arr(i1111) = 0.0; + }; + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -295,8 +295,7 @@ void applyGenDoubleExcitationMinus( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC4Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0000, const std::size_t i0001, const std::size_t i0010, const std::size_t i0011, @@ -306,24 +305,26 @@ void applyGenDoubleExcitationMinus( const std::size_t i1010, const std::size_t i1011, const std::size_t i1100, const std::size_t i1101, const std::size_t i1110, const std::size_t i1111) { - [[maybe_unused]] const auto i0000_ = i0000; - [[maybe_unused]] const auto i0001_ = i0001; - [[maybe_unused]] const auto i0010_ = i0010; - [[maybe_unused]] const auto i0100_ = i0100; - [[maybe_unused]] const auto i0101_ = i0101; - [[maybe_unused]] const auto i0110_ = i0110; - [[maybe_unused]] const auto i0111_ = i0111; - [[maybe_unused]] const auto i1000_ = i1000; - [[maybe_unused]] const auto i1001_ = i1001; - [[maybe_unused]] const auto i1010_ = i1010; - [[maybe_unused]] const auto i1011_ = i1011; - [[maybe_unused]] const auto i1101_ = i1101; - [[maybe_unused]] const auto i1110_ = i1110; - [[maybe_unused]] const auto i1111_ = i1111; - arr(i0011) *= Kokkos::complex{0.0, 1.0}; - arr(i1100) *= Kokkos::complex{0.0, -1.0}; - kokkos_swap(arr(i1100), arr(i0011)); - }); + [[maybe_unused]] const auto i0000_ = i0000; + [[maybe_unused]] const auto i0001_ = i0001; + [[maybe_unused]] const auto i0010_ = i0010; + [[maybe_unused]] const auto i0100_ = i0100; + [[maybe_unused]] const auto i0101_ = i0101; + [[maybe_unused]] const auto i0110_ = i0110; + [[maybe_unused]] const auto i0111_ = i0111; + [[maybe_unused]] const auto i1000_ = i1000; + [[maybe_unused]] const auto i1001_ = i1001; + [[maybe_unused]] const auto i1010_ = i1010; + [[maybe_unused]] const auto i1011_ = i1011; + [[maybe_unused]] const auto i1101_ = i1101; + [[maybe_unused]] const auto i1110_ = i1110; + [[maybe_unused]] const auto i1111_ = i1111; + arr(i0011) *= Kokkos::complex{0.0, 1.0}; + arr(i1100) *= Kokkos::complex{0.0, -1.0}; + kokkos_swap(arr(i1100), arr(i0011)); + }; + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template @@ -332,8 +333,7 @@ void applyGenDoubleExcitationPlus( const std::size_t num_qubits, const std::vector &wires, [[maybe_unused]] const bool inverse = false, [[maybe_unused]] const std::vector ¶ms = {}) { - applyNC4Functor( - ExecutionSpace{}, arr_, num_qubits, wires, + auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0000, const std::size_t i0001, const std::size_t i0010, const std::size_t i0011, @@ -343,24 +343,26 @@ void applyGenDoubleExcitationPlus( const std::size_t i1010, const std::size_t i1011, const std::size_t i1100, const std::size_t i1101, const std::size_t i1110, const std::size_t i1111) { - [[maybe_unused]] const auto i0000_ = i0000; - [[maybe_unused]] const auto i0001_ = i0001; - [[maybe_unused]] const auto i0010_ = i0010; - [[maybe_unused]] const auto i0100_ = i0100; - [[maybe_unused]] const auto i0101_ = i0101; - [[maybe_unused]] const auto i0110_ = i0110; - [[maybe_unused]] const auto i0111_ = i0111; - [[maybe_unused]] const auto i1000_ = i1000; - [[maybe_unused]] const auto i1001_ = i1001; - [[maybe_unused]] const auto i1010_ = i1010; - [[maybe_unused]] const auto i1011_ = i1011; - [[maybe_unused]] const auto i1101_ = i1101; - [[maybe_unused]] const auto i1110_ = i1110; - [[maybe_unused]] const auto i1111_ = i1111; - arr(i0011) *= Kokkos::complex{0.0, -1.0}; - arr(i1100) *= Kokkos::complex{0.0, 1.0}; - kokkos_swap(arr(i1100), arr(i0011)); - }); + [[maybe_unused]] const auto i0000_ = i0000; + [[maybe_unused]] const auto i0001_ = i0001; + [[maybe_unused]] const auto i0010_ = i0010; + [[maybe_unused]] const auto i0100_ = i0100; + [[maybe_unused]] const auto i0101_ = i0101; + [[maybe_unused]] const auto i0110_ = i0110; + [[maybe_unused]] const auto i0111_ = i0111; + [[maybe_unused]] const auto i1000_ = i1000; + [[maybe_unused]] const auto i1001_ = i1001; + [[maybe_unused]] const auto i1010_ = i1010; + [[maybe_unused]] const auto i1011_ = i1011; + [[maybe_unused]] const auto i1101_ = i1101; + [[maybe_unused]] const auto i1110_ = i1110; + [[maybe_unused]] const auto i1111_ = i1111; + arr(i0011) *= Kokkos::complex{0.0, -1.0}; + arr(i1100) *= Kokkos::complex{0.0, 1.0}; + kokkos_swap(arr(i1100), arr(i0011)); + }; + applyNC4Functor( + ExecutionSpace{}, arr_, num_qubits, wires, core_function); } template diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_NonParam.cpp index 983a677c7..2de6b0565 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 @@ -822,17 +822,6 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " StateVectorKokkos sv_control{num_qubits}; SECTION("N-controlled PauliX ") { - - if (control == wire) { - Kokkos::deep_copy(sv_control.getView(), ini_sv); - - REQUIRE_THROWS_AS(sv_control.applyOperation( - "PauliX", std::vector{control}, - std::vector{true}, - std::vector{wire}), - LightningException); - } - if (control != wire) { Kokkos::deep_copy(sv_gate.getView(), ini_sv); Kokkos::deep_copy(sv_control.getView(), ini_sv); @@ -854,7 +843,7 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " } } - if (control != 0 && wire != 0 && control != wire) { + if (control != 0 && wire != 0) { Kokkos::deep_copy(sv_gate.getView(), ini_sv); Kokkos::deep_copy(sv_control.getView(), ini_sv); @@ -958,6 +947,93 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " } } +TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " + "two-qubit with controls", + "[StateVectorKokkos_NonParam]", float, double) { + using StateVectorT = StateVectorKokkos; + + const TestType EP = 1e-4; + const std::size_t num_qubits = 4; + const bool inverse = GENERATE(true, false); + const std::size_t control = GENERATE(0, 1, 2, 3); + const std::size_t wire0 = GENERATE(0, 1, 2, 3); + const std::size_t wire1 = GENERATE(0, 1, 2, 3); + + auto ini_st = createNonTrivialState(num_qubits); + + SECTION("N-controlled SWAP") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + + StateVectorT kokkos_sv0{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv1{ini_st.data(), ini_st.size()}; + kokkos_sv0.applyOperation("CSWAP", {control, wire0, wire1}, + inverse); + auto matrix = getSWAP(); + kokkos_sv1.applyOperation("SWAP", std::vector{control}, + std::vector{true}, + std::vector{wire0, wire1}, + inverse); + auto result_sv0 = kokkos_sv0.getDataVector(); + auto result_sv1 = kokkos_sv1.getDataVector(); + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_sv0[j]) == + Approx(real(result_sv1[j])).margin(EP)); + CHECK(imag(result_sv0[j]) == + Approx(imag(result_sv1[j])).margin(EP)); + } + } + } + + 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( + "XXXXXXXX", 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::SetStateVector", "[StateVectorKokkos_Nonparam]", float, double) { using PrecisionT = TestType; diff --git a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/tests/Test_StateVectorKokkos_Param.cpp index c127253a2..829946332 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 @@ -228,6 +228,348 @@ TEMPLATE_TEST_CASE( } } +TEMPLATE_TEST_CASE( + "StateVectorKokkos::applyOperation param two-qubit with controls", + "[StateVectorKokkos_Operation]", float, double) { + using StateVectorT = StateVectorKokkos; + using PrecisionT = StateVectorT::PrecisionT; + using ComplexT = Kokkos::complex; + + const std::size_t num_qubits = 4; + const TestType EP = 1e-4; + auto ini_st = createNonTrivialState(num_qubits); + + const bool inverse = GENERATE(false, true); + const std::size_t control = GENERATE(0, 1, 2, 3); + const std::size_t wire0 = GENERATE(0, 1, 2, 3); + const std::size_t wire1 = GENERATE(0, 1, 2, 3); + const PrecisionT param = GENERATE(-1.5, -0.5, 0, 0.5, 1.5); + + auto getControlledGate = [](std::vector matrix) { + std::vector cmatrix(matrix.size() * 4); + for (std::size_t i = 0; i < 4; i++) { + cmatrix[i * 8 + i] = ComplexT{1.0}; + } + for (std::size_t i = 0; i < 4; i++) { + for (std::size_t j = 0; j < 4; j++) { + cmatrix[(i + 4) * 8 + j + 4] = matrix[i * 4 + j]; + } + } + return cmatrix; + }; + + SECTION("N-controlled IsingXX") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = getIsingXX(param); + std::vector cmatrix = getControlledGate(matrix); + kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1}, + inverse); + kokkos_sv_op.applyOperation( + "IsingXX", std::vector{control}, + std::vector{true}, std::vector{wire0, wire1}, + inverse, {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } + + SECTION("N-controlled IsingXY") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = getIsingXY(param); + std::vector cmatrix = getControlledGate(matrix); + kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1}, + inverse); + kokkos_sv_op.applyOperation( + "IsingXY", std::vector{control}, + std::vector{true}, std::vector{wire0, wire1}, + inverse, {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } + + SECTION("N-controlled IsingYY") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = getIsingYY(param); + std::vector cmatrix = getControlledGate(matrix); + kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1}, + inverse); + kokkos_sv_op.applyOperation( + "IsingYY", std::vector{control}, + std::vector{true}, std::vector{wire0, wire1}, + inverse, {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } + + SECTION("N-controlled IsingZZ") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = getIsingZZ(param); + std::vector cmatrix = getControlledGate(matrix); + kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1}, + inverse); + kokkos_sv_op.applyOperation( + "IsingZZ", std::vector{control}, + std::vector{true}, std::vector{wire0, wire1}, + inverse, {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } + + SECTION("N-controlled SingleExcitation") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = + getSingleExcitation(param); + std::vector cmatrix = getControlledGate(matrix); + kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1}, + inverse); + kokkos_sv_op.applyOperation( + "SingleExcitation", std::vector{control}, + std::vector{true}, std::vector{wire0, wire1}, + inverse, {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } + + SECTION("N-controlled SingleExcitationMinus") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = + getSingleExcitationMinus(param); + std::vector cmatrix = getControlledGate(matrix); + kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1}, + inverse); + kokkos_sv_op.applyOperation( + "SingleExcitationMinus", std::vector{control}, + std::vector{true}, std::vector{wire0, wire1}, + inverse, {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } + + SECTION("N-controlled SingleExcitationPlus") { + if (control != wire0 && control != wire1 && wire0 != wire1) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = + getSingleExcitationPlus(param); + std::vector cmatrix = getControlledGate(matrix); + kokkos_sv_mat.applyMatrix(cmatrix, {control, wire0, wire1}, + inverse); + kokkos_sv_op.applyOperation( + "SingleExcitationPlus", std::vector{control}, + std::vector{true}, std::vector{wire0, wire1}, + inverse, {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } +} + +TEMPLATE_TEST_CASE( + "StateVectorKokkos::applyOperation param four-qubit with controls", + "[StateVectorKokkos_Operation]", float, double) { + using StateVectorT = StateVectorKokkos; + using PrecisionT = StateVectorT::PrecisionT; + using ComplexT = Kokkos::complex; + + const std::size_t num_qubits = 5; + const TestType EP = 1e-4; + auto ini_st = createNonTrivialState(num_qubits); + + const bool inverse = GENERATE(false, true); + const std::size_t control = GENERATE(0, 1, 2, 3, 4); + const std::size_t wire0 = GENERATE(0, 1, 2, 3, 4); + const std::size_t wire1 = GENERATE(0, 1, 2, 3, 4); + const std::size_t wire2 = GENERATE(0, 1, 2, 3, 4); + const std::size_t wire3 = GENERATE(0, 1, 2, 3, 4); + const PrecisionT param = GENERATE(-1.5, -0.5, 0, 0.5, 1.5); + + auto getControlledGate = [](std::vector matrix) { + std::vector cmatrix(matrix.size() * 4); + for (std::size_t i = 0; i < 16; i++) { + cmatrix[i * 32 + i] = ComplexT{1.0}; + } + for (std::size_t i = 0; i < 16; i++) { + for (std::size_t j = 0; j < 16; j++) { + cmatrix[(i + 16) * 32 + j + 16] = matrix[i * 16 + j]; + } + } + return cmatrix; + }; + + SECTION("N-controlled DoubleExcitation") { + std::vector wires = {control, wire0, wire1, wire2, wire3}; + std::sort(wires.begin(), wires.end()); + if (std::adjacent_find(wires.begin(), wires.end()) == wires.end()) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = + getDoubleExcitation(param); + std::vector cmatrix = getControlledGate(matrix); + + kokkos_sv_mat.applyMatrix( + cmatrix, {control, wire0, wire1, wire2, wire3}, inverse); + kokkos_sv_op.applyOperation( + "DoubleExcitation", std::vector{control}, + std::vector{true}, + std::vector{wire0, wire1, wire2, wire3}, inverse, + {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } + + SECTION("N-controlled DoubleExcitationPlus") { + std::vector wires = {control, wire0, wire1, wire2, wire3}; + std::sort(wires.begin(), wires.end()); + if (std::adjacent_find(wires.begin(), wires.end()) == wires.end()) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = + getDoubleExcitationPlus(param); + std::vector cmatrix = getControlledGate(matrix); + + kokkos_sv_mat.applyMatrix( + cmatrix, {control, wire0, wire1, wire2, wire3}, inverse); + kokkos_sv_op.applyOperation( + "DoubleExcitationPlus", std::vector{control}, + std::vector{true}, + std::vector{wire0, wire1, wire2, wire3}, inverse, + {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } + + SECTION("N-controlled DoubleExcitationMinus") { + std::vector wires = {control, wire0, wire1, wire2, wire3}; + std::sort(wires.begin(), wires.end()); + if (std::adjacent_find(wires.begin(), wires.end()) == wires.end()) { + StateVectorT kokkos_sv_mat{ini_st.data(), ini_st.size()}; + StateVectorT kokkos_sv_op{ini_st.data(), ini_st.size()}; + + auto matrix = + getDoubleExcitationMinus(param); + std::vector cmatrix = getControlledGate(matrix); + + kokkos_sv_mat.applyMatrix( + cmatrix, {control, wire0, wire1, wire2, wire3}, inverse); + kokkos_sv_op.applyOperation( + "DoubleExcitationMinus", std::vector{control}, + std::vector{true}, + std::vector{wire0, wire1, wire2, wire3}, inverse, + {param}); + + auto result_mat = kokkos_sv_mat.getDataVector(); + auto result_op = kokkos_sv_op.getDataVector(); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(real(result_op[j]) == + Approx(real(result_mat[j])).margin(EP)); + CHECK(imag(result_op[j]) == + Approx(imag(result_mat[j])).margin(EP)); + } + } + } +} + TEMPLATE_TEST_CASE("StateVectorKokkosManaged::applyIsingXY", "[StateVectorKokkosManaged_Param]", float, double) { { diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py index 647994633..8bd3c3b8f 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.py +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.py @@ -117,6 +117,17 @@ "C(RY)", "C(RZ)", "C(Rot)", + "C(SWAP)", + "C(IsingXX)", + "C(IsingXY)", + "C(IsingYY)", + "C(IsingZZ)", + "C(SingleExcitation)", + "C(SingleExcitationMinus)", + "C(SingleExcitationPlus)", + "C(DoubleExcitation)", + "C(DoubleExcitationMinus)", + "C(DoubleExcitationPlus)", "CRot", "IsingXX", "IsingYY", diff --git a/tests/test_gates.py b/tests/test_gates.py index 8a3fc0134..8dd8fe91a 100644 --- a/tests/test_gates.py +++ b/tests/test_gates.py @@ -416,7 +416,7 @@ def circuit(): @pytest.mark.skipif( device_name != "lightning.kokkos", - reason="Controlled Single Qubit gate supported by Lightning Kokkos", + reason="Controlled 1/2/3/4 Qubit gate supported by Lightning Kokkos", ) @pytest.mark.parametrize( "operation", @@ -432,11 +432,23 @@ def circuit(): qml.RY, qml.RZ, qml.Rot, + qml.SWAP, + qml.IsingXX, + qml.IsingXY, + qml.IsingYY, + qml.IsingZZ, + qml.SingleExcitation, + qml.SingleExcitationMinus, + qml.SingleExcitationPlus, + qml.DoubleExcitation, + qml.DoubleExcitationMinus, + qml.DoubleExcitationPlus, + qml.GlobalPhase ], ) @pytest.mark.parametrize("control_value", [False, True]) @pytest.mark.parametrize("n_qubits", list(range(2, 8))) -def test_controlled_qubit_gates(operation, n_qubits, control_value, tol): +def test_controlled_qubit_gates_kokkos(operation, n_qubits, control_value, tol): """Test that multi-controlled gates are correctly applied to a state""" dev_def = qml.device("default.qubit", wires=n_qubits) dev = qml.device(device_name, wires=n_qubits)