Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add native PauliRot implementation in LightningKokkos [sc-71642] #855

Merged
merged 73 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 69 commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
9cda69a
Implement native PauliRot in LightningQubit.
vincentmr Jul 30, 2024
37418d9
Fix issues wih I in pauli word.
vincentmr Jul 31, 2024
e0d5377
Directly get indices and data.
vincentmr Jul 31, 2024
0266860
Change core_function signature for applyNCN
vincentmr Jul 31, 2024
5d49dab
Include sin in data.
vincentmr Jul 31, 2024
0a7a97e
*= c while copying arr.
vincentmr Jul 31, 2024
06675c7
Use numpy request to pass pointers.
vincentmr Jul 31, 2024
9e6863f
generateBitPatterns
vincentmr Jul 31, 2024
c915b96
Permute early.
vincentmr Aug 1, 2024
3a29b66
Do not precompute offsets.
vincentmr Aug 1, 2024
e74a2d4
Modify stopping_condition and clean up.
vincentmr Aug 1, 2024
1e6d324
Clean up
vincentmr Aug 1, 2024
8f3c99f
Auto update version from '0.38.0-dev20' to '0.38.0-dev22'
ringo-but-quantum Aug 1, 2024
1645a5a
Fix device test.
vincentmr Aug 2, 2024
8cca37e
Replace indices, data arguments by word for PauliRot.
vincentmr Aug 2, 2024
4a51fc6
Auto update version from '0.38.0-dev22' to '0.38.0-dev25'
ringo-but-quantum Aug 2, 2024
3d10b73
Merge branch 'master' into lq_pauli_rot
vincentmr Aug 2, 2024
aa24f4d
Fix few lint warnings.
vincentmr Aug 2, 2024
6927caf
Update changelog
vincentmr Aug 2, 2024
ee34eeb
Fix docstring.
vincentmr Aug 2, 2024
c9dc41e
Fix docstrings.
vincentmr Aug 5, 2024
c702b64
Merge remote-tracking branch 'origin/master' into lq_pauli_rot
vincentmr Aug 5, 2024
9bb5a8a
Auto update version from '0.38.0-dev25' to '0.38.0-dev26'
ringo-but-quantum Aug 5, 2024
6cb0f8e
Update GateIndices.hpp
vincentmr Aug 5, 2024
b8488c2
WIP loops
vincentmr Aug 6, 2024
670570f
Avoid copying arr into coeffs.
vincentmr Aug 7, 2024
bc58a7d
Comment code a bit.
vincentmr Aug 7, 2024
f43a77f
Auto update version from '0.38.0-dev26' to '0.38.0-dev27'
ringo-but-quantum Aug 7, 2024
6a79685
Apply suggestions from code review
vincentmr Aug 7, 2024
5bc8551
Revert to old implementation.
vincentmr Aug 8, 2024
32b9b34
Merge remote-tracking branch 'origin/lq_pauli_rot' into lq_pauli_rot
vincentmr Aug 8, 2024
b99e108
Add doc [skip ci].
vincentmr Aug 8, 2024
4b99691
Add PauliRot C++ tests.
vincentmr Aug 8, 2024
9072665
Merge branch 'master' into lq_pauli_rot
vincentmr Aug 8, 2024
2741f29
Reimplement applyPauliRot
vincentmr Aug 14, 2024
6326894
popcount
vincentmr Aug 14, 2024
73cfaed
Clean up.
vincentmr Aug 15, 2024
7b06ec1
Auto update version from '0.38.0-dev27' to '0.38.0-dev34'
ringo-but-quantum Aug 15, 2024
8762c39
Merge remote-tracking branch 'origin/master' into lq_pauli_rot
vincentmr Aug 15, 2024
9398b37
Merge remote-tracking branch 'origin/lq_pauli_rot' into lq_pauli_rot
vincentmr Aug 15, 2024
63ea43f
Fix setStateVecotr
vincentmr Aug 15, 2024
1b0aba9
Fix adjoint
vincentmr Aug 15, 2024
9059862
Fix tidy
vincentmr Aug 15, 2024
15b533e
Fix capture
vincentmr Aug 15, 2024
fe390cd
Merge branch 'master' into lq_pauli_rot
vincentmr Aug 15, 2024
f9bff5a
trigger ci
vincentmr Aug 15, 2024
e90ee3f
Clean up runner
vincentmr Aug 15, 2024
f04c69f
Auto update version from '0.38.0-dev34' to '0.38.0-dev35'
ringo-but-quantum Aug 15, 2024
0d1ad50
Fix tidy
vincentmr Aug 15, 2024
72d219f
sudo du -sh
vincentmr Aug 15, 2024
d1307f1
sudo du -sh
vincentmr Aug 15, 2024
2d37fdd
sudo du -sh
vincentmr Aug 15, 2024
b0f505e
sudo du -sh
vincentmr Aug 15, 2024
d8aea05
sudo du -sh
vincentmr Aug 15, 2024
509de91
sudo du -sh
vincentmr Aug 15, 2024
e46551b
sudo du -sh
vincentmr Aug 15, 2024
694f856
PauliRot in LK
vincentmr Aug 15, 2024
509c035
Update changelog [skip ci].
vincentmr Aug 15, 2024
f0d222e
Merge remote-tracking branch 'origin/master' into lk_pauli_rot
vincentmr Aug 20, 2024
131583f
Auto update version from '0.38.0-dev38' to '0.38.0-dev39'
ringo-but-quantum Aug 20, 2024
b36c48f
PauliRot => multirz
vincentmr Aug 20, 2024
e29c93c
Merge remote-tracking branch 'origin/master' into lk_pauli_rot
vincentmr Sep 5, 2024
3bdab93
Auto update version from '0.39.0-dev2' to '0.39.0-dev3'
ringo-but-quantum Sep 5, 2024
7d81d32
Merge branch 'master' into lk_pauli_rot
vincentmr Sep 5, 2024
be95a83
Auto update version from '0.39.0-dev4' to '0.39.0-dev5'
ringo-but-quantum Sep 5, 2024
4c3a365
Format
vincentmr Sep 5, 2024
e06fdab
Merge remote-tracking branch 'origin/master' into lk_pauli_rot
vincentmr Sep 9, 2024
210eb61
Add C++ tests
vincentmr Sep 9, 2024
0c37793
Merge remote-tracking branch 'origin/master' into lk_pauli_rot
vincentmr Sep 9, 2024
33ffd58
Merge branch 'master' into lk_pauli_rot
vincentmr Sep 10, 2024
3131c1f
Auto update version from '0.39.0-dev13' to '0.39.0-dev14'
ringo-but-quantum Sep 10, 2024
e4036ef
Merge branch 'master' into lk_pauli_rot
vincentmr Sep 10, 2024
391d0da
Auto update version from '0.39.0-dev14' to '0.39.0-dev15'
ringo-but-quantum Sep 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
* Unify Lightning Kokkos device and Lightning Qubit device under a Lightning Base device
[(#876)](https://github.com/PennyLaneAI/pennylane-lightning/pull/876)

* LightningKokkos gains native support for the `PauliRot` gate.
[(#855)](https://github.com/PennyLaneAI/pennylane-lightning/pull/855)

### Documentation

### Bug fixes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,25 @@ class StateVectorKokkos final
}
}

/**
* @brief Apply a PauliRot gate to the state-vector.
*
* @param wires Wires to apply gate to.
* @param inverse Indicates whether to use inverse of gate.
* @param params Rotation angle.
* @param word A Pauli word (e.g. "XYYX").
*/
void applyPauliRot(const std::vector<std::size_t> &wires,
const bool inverse,
const std::vector<PrecisionT> &params,
const std::string &word) {
PL_ABORT_IF_NOT(wires.size() == word.size(),
"wires and word have incompatible dimensions.");
Pennylane::LightningKokkos::Functors::applyPauliRot<KokkosExecSpace,
PrecisionT>(
getView(), this->getNumQubits(), wires, inverse, params[0], word);
}

template <bool inverse = false>
void applyControlledGlobalPhase(const std::vector<ComplexT> &diagonal) {
auto diagonal_ = vector2view(diagonal);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ void registerBackendClassSpecificBindings(PyClass &pyclass) {

registerGatesForStateVector<StateVectorT>(pyclass);

pyclass.def(
"applyPauliRot",
[](StateVectorT &sv, const std::vector<std::size_t> &wires,
const bool inverse, const std::vector<ParamT> &params,
const std::string &word) {
sv.applyPauliRot(wires, inverse, params, word);
},
"Apply a Pauli rotation.");
pyclass
.def(py::init([](std::size_t num_qubits) {
return new StateVectorT(num_qubits);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@
#include "BitUtil.hpp"
#include "GateOperation.hpp"
#include "Gates.hpp"
#include "Util.hpp" // exp2, INVSQRT2
#include "UtilKokkos.hpp"

/// @cond DEV
namespace {
using namespace Pennylane::Util;
using Kokkos::kokkos_swap;
using Pennylane::Gates::GateOperation;
using Pennylane::LightningKokkos::Util::vector2view;
} // namespace
/// @endcond

Expand Down Expand Up @@ -1092,6 +1095,59 @@ void applyMultiRZ(Kokkos::View<Kokkos::complex<PrecisionT> *> arr_,
});
}

template <class ExecutionSpace, class PrecisionT>
void applyPauliRot(Kokkos::View<Kokkos::complex<PrecisionT> *> arr_,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires, const bool inverse,
const PrecisionT angle, const std::string &word) {
using ComplexT = Kokkos::complex<PrecisionT>;
constexpr auto IMAG = Pennylane::Util::IMAG<PrecisionT>();
PL_ABORT_IF_NOT(wires.size() == word.size(),
"wires and word have incompatible dimensions.")
if (std::find_if_not(word.begin(), word.end(),
[](const int w) { return w == 'Z'; }) == word.end()) {
applyMultiRZ<ExecutionSpace>(arr_, num_qubits, wires, inverse,
std::vector<PrecisionT>{angle});
return;
}
const PrecisionT c = std::cos(angle / 2);
const ComplexT s = ((inverse) ? IMAG : -IMAG) * std::sin(angle / 2);
const std::vector<ComplexT> sines = {s, IMAG * s, -s, -IMAG * s};
auto d_sines = vector2view(sines);
auto get_mask =
[num_qubits, &wires](
[[maybe_unused]] const std::function<bool(const int)> &condition) {
std::size_t mask{0U};
for (std::size_t iw = 0; iw < wires.size(); iw++) {
const auto bit = static_cast<std::size_t>(condition(iw));
mask |= bit << (num_qubits - 1 - wires[iw]);
}
return mask;
};
const std::size_t mask_xy =
get_mask([&word](const int a) { return word[a] != 'Z'; });
const std::size_t mask_y =
get_mask([&word](const int a) { return word[a] == 'Y'; });
const std::size_t mask_z =
get_mask([&word](const int a) { return word[a] == 'Z'; });
const auto count_mask_y = std::popcount(mask_y);
Kokkos::parallel_for(
Kokkos::RangePolicy<ExecutionSpace>(0, exp2(num_qubits)),
KOKKOS_LAMBDA(const std::size_t i0) {
const 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;
const auto sign_i0 = count_z + count_mask_y * 3 - count_y;
const auto sign_i1 = count_z + count_mask_y + count_y;
const ComplexT v0 = arr_(i0);
const ComplexT v1 = arr_(i1);
arr_(i0) = c * v0 + d_sines(sign_i0 % 4) * v1;
arr_(i1) = c * v1 + d_sines(sign_i1 % 4) * v0;
}
});
}

template <class ExecutionSpace, class PrecisionT>
void applyGlobalPhase(Kokkos::View<Kokkos::complex<PrecisionT> *> arr_,
const std::size_t num_qubits,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,22 @@ TEMPLATE_TEST_CASE("StateVectorKokkosManaged::applyIsingXX",
}
}
}
SECTION("PauliRotXX 0,1") {
for (std::size_t index = 0; index < angles.size(); index++) {
StateVectorKokkos<TestType> kokkos_sv{num_qubits};
kokkos_sv.applyPauliRot({0, 1}, adjoint, {angles[index]}, "XX");
std::vector<ComplexT> result_sv(kokkos_sv.getLength(), {0, 0});
kokkos_sv.DeviceToHost(result_sv.data(), kokkos_sv.getLength());

for (std::size_t j = 0; j < exp2(num_qubits); j++) {
CHECK((real(result_sv[j])) ==
Approx(real(expected_results[index][j])));
CHECK((imag(result_sv[j])) ==
Approx(((adjoint) ? -1.0 : 1.0) *
imag(expected_results[index][j])));
}
}
}
SECTION("IsingXX 0,2") {
for (std::size_t index = 0; index < angles.size(); index++) {
StateVectorKokkos<TestType> kokkos_sv{num_qubits};
Expand Down Expand Up @@ -645,6 +661,22 @@ TEMPLATE_TEST_CASE("StateVectorKokkosManaged::applyIsingYY",
}
}
}
SECTION("PauliRotYY 0,1") {
for (std::size_t index = 0; index < angles.size(); index++) {
StateVectorKokkos<TestType> kokkos_sv{num_qubits};
kokkos_sv.applyPauliRot({0, 1}, adjoint, {angles[index]}, "YY");
std::vector<ComplexT> result_sv(kokkos_sv.getLength(), {0, 0});
kokkos_sv.DeviceToHost(result_sv.data(), kokkos_sv.getLength());

for (std::size_t j = 0; j < exp2(num_qubits); j++) {
CHECK((real(result_sv[j])) ==
Approx(real(expected_results[index][j])));
CHECK((imag(result_sv[j])) ==
Approx(((adjoint) ? -1.0 : 1.0) *
imag(expected_results[index][j])));
}
}
}
SECTION("IsingYY 0,2") {
for (std::size_t index = 0; index < angles.size(); index++) {
StateVectorKokkos<TestType> kokkos_sv{num_qubits};
Expand Down Expand Up @@ -700,6 +732,22 @@ TEMPLATE_TEST_CASE("StateVectorKokkosManaged::applyIsingZZ",
}
}
}
SECTION("PauliRotZZ 0,1") {
for (std::size_t index = 0; index < angles.size(); index++) {
StateVectorKokkos<TestType> kokkos_sv{num_qubits};
kokkos_sv.applyPauliRot({0, 1}, adjoint, {angles[index]}, "ZZ");
std::vector<ComplexT> result_sv(kokkos_sv.getLength(), {0, 0});
kokkos_sv.DeviceToHost(result_sv.data(), kokkos_sv.getLength());

for (std::size_t j = 0; j < exp2(num_qubits); j++) {
CHECK((real(result_sv[j])) ==
Approx(real(expected_results[index][j])));
CHECK((imag(result_sv[j])) ==
Approx(((adjoint) ? -1.0 : 1.0) *
imag(expected_results[index][j])));
}
}
}
SECTION("IsingZZ 0,2") {
for (std::size_t index = 0; index < angles.size(); index++) {
StateVectorKokkos<TestType> kokkos_sv{num_qubits};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,12 @@ class GateImplementationsLM : public PauliGenerator<GateImplementationsLM> {
constexpr auto IMAG = Pennylane::Util::IMAG<PrecisionT>();
PL_ABORT_IF_NOT(wires.size() == word.size(),
"wires and word have incompatible dimensions.")
if (std::find_if_not(word.begin(), word.end(), [](const int w) {
return w == 'Z';
}) == word.end()) {
applyMultiRZ(arr, num_qubits, wires, inverse, angle);
return;
}
const PrecisionT c = std::cos(angle / 2);
const ComplexT s = ((inverse) ? IMAG : -IMAG) * std::sin(angle / 2);
const std::array<ComplexT, 4> sines{s, IMAG * s, -s, -IMAG * s};
Expand Down Expand Up @@ -615,15 +621,11 @@ class GateImplementationsLM : public PauliGenerator<GateImplementationsLM> {
const auto count_y = std::popcount(i0 & mask_y) * 2;
const auto count_z = std::popcount(i0 & mask_z) * 2;
const auto sign_i0 = count_z + count_mask_y * 3 - count_y;
if (mask_xy) [[likely]] {
const auto sign_i1 = count_z + count_mask_y + count_y;
const ComplexT v0 = arr[i0];
const ComplexT v1 = arr[i1];
arr[i0] = c * v0 + sines[sign_i0 % 4] * v1;
arr[i1] = c * v1 + sines[sign_i1 % 4] * v0;
} else [[unlikely]] {
arr[i0] *= c + sines[sign_i0 % 4];
}
const auto sign_i1 = count_z + count_mask_y + count_y;
const ComplexT v0 = arr[i0];
const ComplexT v1 = arr[i1];
arr[i0] = c * v0 + sines[sign_i0 % 4] * v1;
arr[i1] = c * v1 + sines[sign_i1 % 4] * v0;
}
}

Expand Down
6 changes: 6 additions & 0 deletions pennylane_lightning/lightning_kokkos/_state_vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,12 @@
self._apply_lightning_midmeasure(
operation, mid_measurements, postselect_mode=postselect_mode
)
elif isinstance(operation, qml.PauliRot):
method = getattr(state, "applyPauliRot")
paulis = operation._hyperparameters["pauli_word"]
wires = [i for i, w in zip(wires, paulis) if w != "I"]
word = "".join(p for p in paulis if p != "I") # pylint: disable=protected-access
method(wires, invert_param, operation.parameters, word)

Check warning on line 276 in pennylane_lightning/lightning_kokkos/_state_vector.py

View check run for this annotation

Codecov / codecov/patch

pennylane_lightning/lightning_kokkos/_state_vector.py#L271-L276

Added lines #L271 - L276 were not covered by tests
elif method is not None: # apply specialized gate
param = operation.parameters
method(wires, invert_param, param)
Expand Down
8 changes: 6 additions & 2 deletions pennylane_lightning/lightning_kokkos/lightning_kokkos.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import os
import sys
from dataclasses import replace
from functools import reduce
from pathlib import Path
from typing import Optional

Expand Down Expand Up @@ -154,7 +155,10 @@
return len(op.wires) < 10
if isinstance(op, qml.GroverOperator):
return len(op.wires) < 13

if isinstance(op, qml.PauliRot):
word = op._hyperparameters["pauli_word"] # pylint: disable=protected-access

Check warning on line 159 in pennylane_lightning/lightning_kokkos/lightning_kokkos.py

View check run for this annotation

Codecov / codecov/patch

pennylane_lightning/lightning_kokkos/lightning_kokkos.py#L158-L159

Added lines #L158 - L159 were not covered by tests
# decomposes to IsingXX, etc. for n <= 2
return reduce(lambda x, y: x + (y != "I"), word, 0) > 2

Check warning on line 161 in pennylane_lightning/lightning_kokkos/lightning_kokkos.py

View check run for this annotation

Codecov / codecov/patch

pennylane_lightning/lightning_kokkos/lightning_kokkos.py#L161

Added line #L161 was not covered by tests
return op.name in _operations


Expand Down Expand Up @@ -210,7 +214,7 @@

def _adjoint_ops(op: qml.operation.Operator) -> bool:
"""Specify whether or not an Operator is supported by adjoint differentiation."""
return adjoint_ops(op)
return not isinstance(op, qml.PauliRot) and adjoint_ops(op)

Check warning on line 217 in pennylane_lightning/lightning_kokkos/lightning_kokkos.py

View check run for this annotation

Codecov / codecov/patch

pennylane_lightning/lightning_kokkos/lightning_kokkos.py#L217

Added line #L217 was not covered by tests


def _add_adjoint_transforms(program: TransformProgram) -> None:
Expand Down
12 changes: 8 additions & 4 deletions tests/test_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,8 +523,8 @@ def circuit(x):


@pytest.mark.skipif(
device_name != "lightning.qubit",
reason="PauliRot operations only implemented in lightning.qubit.",
device_name not in ("lightning.qubit", "lightning.kokkos"),
reason="PauliRot operations only implemented in lightning.qubit and lightning.kokkos.",
)
@pytest.mark.parametrize("n_wires", [1, 2, 3, 4, 5, 10, 15])
@pytest.mark.parametrize("n_targets", [1, 2, 3, 4, 5, 10, 15])
Expand All @@ -540,8 +540,12 @@ def test_paulirot(n_wires, n_targets, tol):
init_state /= np.linalg.norm(init_state)
theta = 0.3

for _ in range(10):
word = "".join(pws[w] for w in np.random.randint(0, 3, n_targets))
for i in range(10):
word = (
"Z" * n_targets
if i == 0
else "".join(pws[w] for w in np.random.randint(0, 3, n_targets))
)
wires = np.random.permutation(n_wires)[0:n_targets]
stateprep = qml.StatePrep(init_state, wires=range(n_wires))
op = qml.PauliRot(theta, word, wires=wires)
Expand Down
Loading