diff --git a/pennylane_lightning/core/_version.py b/pennylane_lightning/core/_version.py index 8432c4f82..84b4957c2 100644 --- a/pennylane_lightning/core/_version.py +++ b/pennylane_lightning/core/_version.py @@ -16,4 +16,4 @@ Version number (major.minor.patch[-label]) """ -__version__ = "0.40.0-dev4" +__version__ = "0.40.0-dev6" 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 537bfa663..e3325e5b8 100644 --- a/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_kokkos/gates/BasicGateFunctors.hpp @@ -31,6 +31,10 @@ using Pennylane::LightningKokkos::Util::ControlBitPatterns; using Pennylane::LightningKokkos::Util::generateBitPatterns; using Pennylane::LightningKokkos::Util::parity_2_offset; using Pennylane::LightningKokkos::Util::reverseWires; +using Pennylane::LightningKokkos::Util::ControlBitPatterns; +using Pennylane::LightningKokkos::Util::generateBitPatterns; +using Pennylane::LightningKokkos::Util::parity_2_offset; +using Pennylane::LightningKokkos::Util::reverseWires; using Pennylane::LightningKokkos::Util::vector2view; } // namespace /// @endcond @@ -82,6 +86,54 @@ class applyNC1Functor { } }; +template +class applyNC1Functor { + +template +class applyNC1Functor {}; + +template +class applyNC1Functor { + using KokkosIntVector = Kokkos::View; + + Kokkos::View *> arr; + const FuncT core_function; + KokkosIntVector indices; + KokkosIntVector parity; + KokkosIntVector rev_wires; + KokkosIntVector rev_wire_shifts; + + public: + template + applyNC1Functor([[maybe_unused]] ExecutionSpace exec, + Kokkos::View *> arr_, + std::size_t num_qubits, + const std::vector &controlled_wires, + const std::vector &controlled_values, + const std::vector &wires, FuncT core_function_) + : arr(arr_), core_function(core_function_) { + + std::tie(parity, rev_wires) = + reverseWires(num_qubits, wires, controlled_wires); + std::vector indices_ = + generateBitPatterns(wires, num_qubits); + ControlBitPatterns(indices_, num_qubits, controlled_wires, + controlled_values); + indices = vector2view(indices_); + Kokkos::parallel_for( + Kokkos::RangePolicy( + 0, exp2(num_qubits - controlled_wires.size() - wires.size())), + *this); + } + KOKKOS_FUNCTION void operator()(const std::size_t k) const { + const std::size_t offset = parity_2_offset(parity, k); + const std::size_t i0 = indices(0B00); + const std::size_t i1 = indices(0B01); + + core_function(arr, i0 + offset, i1 + offset); + } +}; + template class applyNC1Functor { @@ -510,6 +562,7 @@ void applyNCRot(Kokkos::View *> arr_, const Kokkos::complex mat_0b01 = mat[0b01]; const Kokkos::complex mat_0b10 = mat[0b10]; const Kokkos::complex mat_0b11 = mat[0b11]; + auto core_function = auto core_function = KOKKOS_LAMBDA(Kokkos::View *> arr, const std::size_t i0, const std::size_t i1) { @@ -847,6 +900,8 @@ void applyCRZ(Kokkos::View *> arr_, const PrecisionT &angle = params[0]; const PrecisionT cos_angle = std::cos(angle * static_cast(0.5)); const PrecisionT sin_angle = std::sin(angle * static_cast(0.5)); + const PrecisionT cos_angle = std::cos(angle * static_cast(0.5)); + const PrecisionT sin_angle = std::sin(angle * static_cast(0.5)); const Kokkos::complex shift_0{ cos_angle, (inverse) ? sin_angle : -sin_angle}; const Kokkos::complex shift_1 = Kokkos::conj(shift_0); 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 af9e31865..f04f6c4ea 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,45 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyCSWAP", } } +TEMPLATE_TEST_CASE("StateVectorKokkos::applyCSWAP", + "[StateVectorKokkos_Nonparam]", float, double) { + { + using ComplexT = StateVectorKokkos::ComplexT; + const std::size_t num_qubits = 3; + + StateVectorKokkos kokkos_sv{num_qubits}; + + kokkos_sv.applyOperations({{"Hadamard"}, {"PauliX"}}, {{0}, {1}}, + {{false}, {false}}); + + auto ini_sv = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, + kokkos_sv.getView()); + + auto z = ComplexT{ZERO()}; + auto i = ComplexT{INVSQRT2()}; + + SECTION("Apply using dispatcher") { + SECTION("CSWAP [0,1,2]|+10> -> |010> + |101>") { + const std::vector expected_results = {z, z, i, z, + z, i, z, z}; + + StateVectorKokkos svdat012{num_qubits}; + Kokkos::deep_copy(svdat012.getView(), ini_sv); + + svdat012.applyOperation("CSWAP", {0, 1, 2}, false); + + auto sv012 = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, svdat012.getView()); + + for (std::size_t j = 0; j < exp2(num_qubits); j++) { + CHECK(imag(expected_results[j]) == Approx(imag(sv012[j]))); + CHECK(real(expected_results[j]) == Approx(real(sv012[j]))); + } + } + } + } +} + TEMPLATE_TEST_CASE("StateVectorKokkos::applyMultiQubitOp", "[StateVectorKokkos_Nonparam][Inverse]", float, double) { const bool inverse = GENERATE(true, false); @@ -801,6 +840,7 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyMultiQubitOp", } } +TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation Controlled", TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation Controlled", "[StateVectorKokkos_Nonparam]", float, double) { @@ -823,6 +863,28 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " const std::size_t wire = GENERATE(0, 1, 2, 3); StateVectorKokkos kokkos_sv{num_qubits}; + using StateVectorT = StateVectorKokkos; + using PrecisionT = StateVectorT::PrecisionT; + const std::size_t num_qubits = 3; + StateVectorKokkos state_vector{num_qubits}; + auto matrix = getIdentity(); + PL_REQUIRE_THROWS_MATCHES( + state_vector.applyOperation("XXX", {0}, {true}, {1}, false, {}, matrix), + LightningException, "Controlled matrix operation not yet supported"); +} + +TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " + "one-qubit with controls", + "[StateVectorKokkos_NonParam]", float, double) { + const bool inverse = GENERATE(true, false); + const std::size_t num_qubits = 4; + const std::size_t control = GENERATE(0, 1, 2, 3); + const std::size_t wire = GENERATE(0, 1, 2, 3); + StateVectorKokkos kokkos_sv{num_qubits}; + + kokkos_sv.applyOperations( + {{"Hadamard"}, {"Hadamard"}, {"Hadamard"}, {"Hadamard"}}, + {{0}, {1}, {2}, {3}}, {{false}, {false}, {false}, {false}}); kokkos_sv.applyOperations( {{"Hadamard"}, {"Hadamard"}, {"Hadamard"}, {"Hadamard"}}, {{0}, {1}, {2}, {3}}, {{false}, {false}, {false}, {false}}); @@ -832,6 +894,35 @@ TEMPLATE_TEST_CASE("StateVectorKokkos::applyOperation non-param " StateVectorKokkos sv_gate{num_qubits}; 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); + + sv_gate.applyOperation("CNOT", {control, wire}, inverse); + sv_control.applyOperation( + "PauliX", std::vector{control}, + std::vector{true}, std::vector{wire}); + auto sv_gate_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_gate.getView()); + auto sv_control_host = Kokkos::create_mirror_view_and_copy( + Kokkos::HostSpace{}, sv_control.getView()); + auto ini_sv = Kokkos::create_mirror_view_and_copy(Kokkos::HostSpace{}, + kokkos_sv.getView()); + StateVectorKokkos sv_gate{num_qubits}; + StateVectorKokkos sv_control{num_qubits}; + SECTION("N-controlled PauliX ") { if (control == wire) { diff --git a/pennylane_lightning/lightning_kokkos/_state_vector.py b/pennylane_lightning/lightning_kokkos/_state_vector.py index 23fb1d626..44405d153 100644 --- a/pennylane_lightning/lightning_kokkos/_state_vector.py +++ b/pennylane_lightning/lightning_kokkos/_state_vector.py @@ -287,6 +287,7 @@ def _apply_lightning( elif isinstance(operation, qml.ops.Controlled) and not isinstance( operation.base, (qml.QubitUnitary, qml.BlockEncode, qml.MultiRZ) ): # apply n-controlled gate + # Kokkos does not support controlled gates except for GlobalPhase and single-qubit # Kokkos does not support controlled gates except for GlobalPhase and single-qubit self._apply_lightning_controlled(operation) else: # apply gate as a matrix diff --git a/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml b/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml index db6b22ab7..acc671862 100644 --- a/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml +++ b/pennylane_lightning/lightning_kokkos/lightning_kokkos.toml @@ -28,6 +28,10 @@ PauliX = { properties = [ "invertible", "controllable", "differe PauliY = { properties = [ "invertible", "controllable", "differentiable" ] } PauliZ = { properties = [ "invertible", "controllable", "differentiable" ] } PhaseShift = { properties = [ "invertible", "controllable", "differentiable" ] } +PauliX = { properties = [ "invertible", "controllable", "differentiable" ] } +PauliY = { properties = [ "invertible", "controllable", "differentiable" ] } +PauliZ = { properties = [ "invertible", "controllable", "differentiable" ] } +PhaseShift = { properties = [ "invertible", "controllable", "differentiable" ] } QubitUnitary = { properties = [ "invertible", ] } Rot = { properties = [ "invertible", "controllable" ] } RX = { properties = [ "invertible", "controllable", "differentiable" ] } @@ -40,6 +44,7 @@ S = { properties = [ "invertible", "controllable", "differe SWAP = { properties = [ "invertible", "controllable", "differentiable" ] } Toffoli = { properties = [ "invertible", "differentiable" ] } T = { properties = [ "invertible", "controllable", "differentiable" ] } +T = { properties = [ "invertible", "controllable", "differentiable" ] } # Operators that should be decomposed according to the algorithm used # by PennyLane's device API.