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

(LK-C-Base) Add native controlled operation support for Lightning Kokkos #950

Merged
merged 65 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
fd9df9f
initial commit
josephleekl Oct 18, 2024
942c841
Auto update version from '0.39.0-dev46' to '0.39.0-dev47'
ringo-but-quantum Oct 18, 2024
39785ee
Merge branch 'master' into lk-control-base
josephleekl Oct 18, 2024
035bea0
Auto update version from '0.39.0-dev47' to '0.39.0-dev48'
ringo-but-quantum Oct 18, 2024
ba5439e
Merge branch 'master' into lk-control-base
josephleekl Oct 24, 2024
78d4f7b
Auto update version from '0.39.0-dev48' to '0.39.0-dev49'
ringo-but-quantum Oct 24, 2024
4526b00
Merge branch 'master' into lk-control-base
josephleekl Nov 8, 2024
a964795
merge with master
josephleekl Nov 8, 2024
0f5beee
Auto update version from '0.40.0-dev4' to '0.40.0-dev5'
ringo-but-quantum Nov 8, 2024
6536bb6
(LK-C-1) Add controlled 1-qubit named-gate (e.g. NCPauliX) support to…
josephleekl Nov 8, 2024
e89065f
Auto update version from '0.40.0-dev5' to '0.40.0-dev6'
ringo-but-quantum Nov 8, 2024
1979bd7
(LK-C-2) Add controlled named 2/4-qubit gate (e.g. NCIsingXX) support…
josephleekl Nov 12, 2024
0acc148
Auto update version from '0.40.0-dev4' to '0.40.0-dev7'
ringo-but-quantum Nov 12, 2024
d7aecd7
(LK-C-3) Add controlled MultiRZ support to Lightning Kokkos (#954)
josephleekl Nov 13, 2024
7ea9296
Auto update version from '0.40.0-dev7' to '0.40.0-dev9'
ringo-but-quantum Nov 13, 2024
56b1d04
Merge branch 'master' into lk-control-base
josephleekl Nov 15, 2024
eb2ac82
format
josephleekl Nov 15, 2024
8e578b1
fix code factor
josephleekl Nov 15, 2024
7b5713e
Auto update version from '0.40.0-dev9' to '0.40.0-dev10'
ringo-but-quantum Nov 15, 2024
b00dbf9
Merge branch 'master' into lk-control-base
josephleekl Nov 19, 2024
5ab8064
Auto update version from '0.40.0-dev11' to '0.40.0-dev12'
ringo-but-quantum Nov 19, 2024
bde6107
(LK-C-4) Add controlled QubitUnitary support to Lightning Kokkos (#…
josephleekl Nov 19, 2024
8834735
(LK-C-5) Add controlled generator (1-qubit) support for Lightning Kok…
josephleekl Nov 22, 2024
2fd51fe
Auto update version from '0.40.0-dev12' to '0.40.0-dev14'
ringo-but-quantum Nov 22, 2024
873c6fb
(LK-C-6) Add controlled generator (multi-qubit) support for Lightning…
josephleekl Nov 22, 2024
f5f83bc
Auto update version from '0.40.0-dev14' to '0.40.0-dev15'
ringo-but-quantum Nov 22, 2024
04a7c84
(LK-C-7) Add controlled adjoint support for Lightning Kokkos (#965)
josephleekl Nov 22, 2024
ee93769
Merge branch 'master' into lk-control-base
josephleekl Nov 25, 2024
f084bf3
update changelog
josephleekl Nov 25, 2024
391ad57
fix codefactor complaint
josephleekl Nov 25, 2024
da99d67
Merge branch 'master' into lk-control-base
josephleekl Nov 25, 2024
772e7e3
Auto update version from '0.40.0-dev15' to '0.40.0-dev16'
ringo-but-quantum Nov 25, 2024
7b029b5
add controlled op in catalyst lightningkokkossimulator
josephleekl Nov 25, 2024
9c415e2
update simulator + test
josephleekl Nov 25, 2024
242d39a
Merge branch 'master' into lk-control-base
josephleekl Nov 25, 2024
7ca95b6
Auto update version from '0.40.0-dev16' to '0.40.0-dev17'
ringo-but-quantum Nov 25, 2024
094aebc
Merge branch 'master' into lk-control-base
josephleekl Nov 26, 2024
568c3ca
fix lkokkos merge tests
josephleekl Nov 26, 2024
c392a72
util add blankline
josephleekl Nov 26, 2024
27ee7e5
use m_pi_2 constant for test
josephleekl Nov 26, 2024
b48662a
update controlled toffoli test name
josephleekl Nov 26, 2024
71e3d24
use dynamic section for tests
josephleekl Nov 26, 2024
4d8126d
Auto update version from '0.40.0-dev17' to '0.40.0-dev18'
ringo-but-quantum Nov 26, 2024
7885941
remove using std::size_t
josephleekl Nov 26, 2024
664a706
implement comments
josephleekl Nov 28, 2024
6b54cd2
Auto update version from '0.40.0-dev18' to '0.40.0-dev22'
ringo-but-quantum Nov 28, 2024
d8b62ab
implement comments
josephleekl Nov 28, 2024
8db2fd8
remove old c(globalphase)
josephleekl Nov 28, 2024
b678095
Auto update version from '0.40.0-dev22' to '0.40.0-dev24'
ringo-but-quantum Nov 28, 2024
19ffddc
format
josephleekl Nov 28, 2024
647ef52
clean up doc comments
josephleekl Nov 29, 2024
a4df6c0
update ctrl/adj nested comment
josephleekl Nov 29, 2024
95cd1db
update non-const pointer applycontrolledmatrix and add test
josephleekl Nov 29, 2024
f0ac952
format
josephleekl Nov 29, 2024
f42ea8b
update scratch level exploration comment
josephleekl Nov 29, 2024
f4f2527
precompute mask in controlbitpatterns calc
josephleekl Nov 29, 2024
fd27096
Merge branch 'master' into lk-control-base
josephleekl Nov 29, 2024
e33fc53
implement shuli comments
josephleekl Dec 2, 2024
083dc97
implement shuli comments
josephleekl Dec 2, 2024
3a03af2
Update pennylane_lightning/core/src/simulators/lightning_kokkos/utils…
josephleekl Dec 3, 2024
7d04fd3
Auto update version from '0.40.0-dev24' to '0.40.0-dev25'
ringo-but-quantum Dec 3, 2024
2bd4058
Merge branch 'master' into lk-control-base
josephleekl Dec 3, 2024
5cdc1fa
update controlBitPatterns case
josephleekl Dec 3, 2024
0d2e228
Auto update version from '0.40.0-dev25' to '0.40.0-dev26'
ringo-but-quantum Dec 3, 2024
9582e93
Merge branch 'master' into lk-control-base
josephleekl Dec 3, 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 @@ -2,6 +2,9 @@

### New features since last release

* Add native N-controlled gate/matrix operations and adjoint support to `lightning.kokkos`.
[(#950)](https://github.com/PennyLaneAI/pennylane-lightning/pull/950)

* Add native N-controlled generators and adjoint support to `lightning.gpu`'s single-GPU backend.
[(#970)](https://github.com/PennyLaneAI/pennylane-lightning/pull/970)

Expand Down
2 changes: 1 addition & 1 deletion pennylane_lightning/core/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
Version number (major.minor.patch[-label])
"""

__version__ = "0.40.0-dev17"
__version__ = "0.40.0-dev18"
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ using namespace Pennylane::LightningQubit::Observables;

#elif _ENABLE_PLKOKKOS == 1
constexpr bool BACKEND_FOUND = true;
constexpr bool SUPPORTS_CTRL = false;
constexpr bool SUPPORTS_CTRL = true;
josephleekl marked this conversation as resolved.
Show resolved Hide resolved
josephleekl marked this conversation as resolved.
Show resolved Hide resolved

#include "AdjointJacobianKokkos.hpp"
#include "ObservablesKokkos.hpp"
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,19 @@ class AdjointJacobian final
if (ops.hasParams(op_idx)) {
if (current_param_idx == *tp_it) {
const PrecisionT scalingFactor =
BaseType::applyGenerator(
mu, ops.getOpsName()[op_idx],
ops.getOpsWires()[op_idx],
!ops.getOpsInverses()[op_idx]) *
(ops.getOpsInverses()[op_idx] ? -1 : 1);
(ops.getOpsControlledWires()[op_idx].empty())
? BaseType::applyGenerator(
mu, ops.getOpsName()[op_idx],
ops.getOpsWires()[op_idx],
!ops.getOpsInverses()[op_idx]) *
(ops.getOpsInverses()[op_idx] ? -1 : 1)
: BaseType::applyGenerator(
mu, ops.getOpsName()[op_idx],
ops.getOpsControlledWires()[op_idx],
ops.getOpsControlledValues()[op_idx],
ops.getOpsWires()[op_idx],
!ops.getOpsInverses()[op_idx]) *
(ops.getOpsInverses()[op_idx] ? -1 : 1);
for (std::size_t obs_idx = 0; obs_idx < num_observables;
obs_idx++) {
const std::size_t idx =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,82 @@ TEMPLATE_PRODUCT_TEST_CASE(
REQUIRE(isApproxEqual(sv1.getDataVector(), sv2.getDataVector(), eps));
}

TEMPLATE_PRODUCT_TEST_CASE(
"AdjointJacobianKokkos::adjointJacobian Op=Controlled-Mixed, Obs=[XXX]",
"[AdjointJacobianKokkos]", (StateVectorKokkos), (float, double)) {
using StateVectorT = TestType;
using PrecisionT = typename StateVectorT::PrecisionT;
AdjointJacobian<StateVectorT> adj;
std::vector<PrecisionT> param{-M_PI / 7, M_PI / 5, 2 * M_PI / 3};
std::vector<std::size_t> tp{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
{
const std::size_t num_qubits = 3;
const std::size_t num_obs = 1;
std::vector<PrecisionT> jacobian(num_obs * tp.size(), 0);

StateVectorT psi(num_qubits);

const auto obs = std::make_shared<TensorProdObs<StateVectorT>>(
std::make_shared<NamedObs<StateVectorT>>(
"PauliX", std::vector<std::size_t>{0}),
std::make_shared<NamedObs<StateVectorT>>(
"PauliX", std::vector<std::size_t>{1}),
std::make_shared<NamedObs<StateVectorT>>(
"PauliX", std::vector<std::size_t>{2}));
auto ops = OpsData<StateVectorT>(
{"RZ", "RY", "RZ", "CNOT", "CNOT", "RX", "RY", "RZ",
"SingleExcitation", "RZ", "RY", "RZ"},
{{param[0]},
{param[1]},
{param[2]},
{},
{},
{param[0]},
{param[1]},
{param[2]},
{param[0]},
{param[0]},
{param[1]},
{param[2]}},
{{0},
{0},
{0},
{0, 1},
{1, 2},
{1},
{2},
{1},
{0, 2},
{1},
{1},
{1}},
{false, false, false, false, false, false, false, false, false,
false, false, false},
{{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}},
{{}, {}, {}, {}, {}, {0}, {0}, {0}, {1}, {}, {}, {}},
{{}, {}, {}, {}, {}, {true}, {true}, {true}, {true}, {}, {}, {}});

JacobianData<StateVectorT> tape{
param.size(), psi.getLength(), psi.getData(), {obs}, ops, tp};

adj.adjointJacobian(std::span{jacobian}, tape, psi, true);

CAPTURE(jacobian);

// Computed with PennyLane using default.qubit.adjoint_jacobian
CHECK(0.0 == Approx(jacobian[0]).margin(1e-7));
josephleekl marked this conversation as resolved.
Show resolved Hide resolved
CHECK(0.03722967 == Approx(jacobian[1]).margin(1e-7));
CHECK(0.53917582 == Approx(jacobian[2]).margin(1e-7));
CHECK(-0.06895157 == Approx(jacobian[3]).margin(1e-7));
CHECK(-0.0020095 == Approx(jacobian[4]).margin(1e-7));
CHECK(0.25057513 == Approx(jacobian[5]).margin(1e-7));
CHECK(-0.00139217 == Approx(jacobian[6]).margin(1e-7));
CHECK(0.52016303 == Approx(jacobian[7]).margin(1e-7));
CHECK(-0.09895398 == Approx(jacobian[8]).margin(1e-7));
CHECK(0.51843232 == Approx(jacobian[9]).margin(1e-7));
}
}

/**
* @brief Tests the constructability of the AdjointDiff.hpp classes.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,52 @@ namespace Pennylane::LightningKokkos {
using StateVectorBackends =
Pennylane::Util::TypeList<StateVectorKokkos<float>,
StateVectorKokkos<double>, void>;
/**
* @brief Register controlled matrix kernel.
*/
template <class StateVectorT>
void applyControlledMatrix(
StateVectorT &st,
const py::array_t<std::complex<typename StateVectorT::PrecisionT>,
py::array::c_style | py::array::forcecast> &matrix,
const std::vector<std::size_t> &controlled_wires,
const std::vector<bool> &controlled_values,
const std::vector<std::size_t> &wires, bool inverse = false) {
using ComplexT = typename StateVectorT::ComplexT;
st.applyControlledMatrix(
static_cast<const ComplexT *>(matrix.request().ptr), controlled_wires,
controlled_values, wires, inverse);
}

/**
* @brief Register controlled gates.
*/
template <class StateVectorT, class PyClass>
void registerControlledGate(PyClass &pyclass) {
using ParamT = typename StateVectorT::PrecisionT;

using Pennylane::Gates::ControlledGateOperation;
using Pennylane::Util::for_each_enum;
namespace Constant = Pennylane::Gates::Constant;

for_each_enum<ControlledGateOperation>(
[&pyclass](ControlledGateOperation gate_op) {
using Pennylane::Util::lookup;
const auto gate_name =
std::string(lookup(Constant::controlled_gate_names, gate_op));
const std::string doc = "Apply the " + gate_name + " gate.";
auto func = [gate_name = gate_name](
StateVectorT &sv,
const std::vector<std::size_t> &controlled_wires,
const std::vector<bool> &controlled_values,
const std::vector<std::size_t> &wires, bool inverse,
const std::vector<ParamT> &params) {
sv.applyOperation(gate_name, controlled_wires,
controlled_values, wires, inverse, params);
};
pyclass.def(gate_name.c_str(), func, doc.c_str());
});
}

/**
* @brief Get a gate kernel map for a statevector.
Expand All @@ -61,7 +107,7 @@ void registerBackendClassSpecificBindings(PyClass &pyclass) {
py::array::c_style | py::array::forcecast>;

registerGatesForStateVector<StateVectorT>(pyclass);

registerControlledGate<StateVectorT>(pyclass);
pyclass.def(
"applyPauliRot",
[](StateVectorT &sv, const std::vector<std::size_t> &wires,
Expand Down Expand Up @@ -143,7 +189,9 @@ void registerBackendClassSpecificBindings(PyClass &pyclass) {
.def("collapse", &StateVectorT::collapse,
"Collapse the statevector onto the 0 or 1 branch of a given wire.")
.def("normalize", &StateVectorT::normalize,
"Normalize the statevector to norm 1.");
"Normalize the statevector to norm 1.")
josephleekl marked this conversation as resolved.
Show resolved Hide resolved
.def("applyControlledMatrix", &applyControlledMatrix<StateVectorT>,
"Apply controlled operation");
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,25 +164,31 @@ void LightningKokkosSimulator::NamedOperation(
const std::vector<QubitIdType> &wires, bool inverse,
const std::vector<QubitIdType> &controlled_wires,
const std::vector<bool> &controlled_values) {
RT_FAIL_IF(!controlled_wires.empty() || !controlled_values.empty(),
"LightningKokkos does not support native quantum control.");

// Check the validity of number of qubits and parameters
RT_FAIL_IF(controlled_wires.size() != controlled_values.size(),
"Controlled wires/values size mismatch");
RT_FAIL_IF(!isValidQubits(wires), "Given wires do not refer to qubits");
RT_FAIL_IF(!isValidQubits(controlled_wires),
"Given controlled wires do not refer to qubits");

// Convert wires to device wires
auto &&dev_wires = getDeviceWires(wires);
auto &&dev_controlled_wires = getDeviceWires(controlled_wires);

// Update the state-vector
this->device_sv->applyOperation(name, dev_wires, inverse, params);
if (controlled_wires.empty()) {
this->device_sv->applyOperation(name, dev_wires, inverse, params);
} else {
this->device_sv->applyOperation(name, dev_controlled_wires,
controlled_values, dev_wires, inverse,
params);
}

// Update tape caching if required
if (this->tape_recording) {
this->cache_manager.addOperation(name, params, dev_wires, inverse, {},
{/*controlled_wires*/},
{/*controlled_values*/});
dev_controlled_wires,
controlled_values);
}
}

Expand All @@ -195,16 +201,15 @@ void LightningKokkosSimulator::MatrixOperation(
Kokkos::View<Kokkos::complex<double> *, Kokkos::HostSpace,
Kokkos::MemoryTraits<Kokkos::Unmanaged>>;

// TODO: Remove when controlled wires API is supported
RT_FAIL_IF(
!controlled_wires.empty() || !controlled_values.empty(),
"LightningKokkos device does not support native quantum control.");
RT_FAIL_IF(controlled_wires.size() != controlled_values.size(),
"Controlled wires/values size mismatch");
RT_FAIL_IF(!isValidQubits(wires), "Given wires do not refer to qubits");
RT_FAIL_IF(!isValidQubits(controlled_wires),
"Given controlled wires do not refer to qubits");

// Convert wires to device wires
auto &&dev_wires = getDeviceWires(wires);
auto &&dev_controlled_wires = getDeviceWires(controlled_wires);

std::vector<Kokkos::complex<double>> matrix_kok;
matrix_kok.resize(matrix.size());
Expand All @@ -218,13 +223,19 @@ void LightningKokkosSimulator::MatrixOperation(
matrix_kok.size()));

// Update the state-vector
this->device_sv->applyMultiQubitOp(gate_matrix, dev_wires, inverse);
if (controlled_wires.empty()) {
this->device_sv->applyMultiQubitOp(gate_matrix, dev_wires, inverse);
} else {
this->device_sv->applyNCMultiQubitOp(gate_matrix, dev_controlled_wires,
controlled_values, dev_wires,
inverse);
}

// Update tape caching if required
if (this->tape_recording) {
this->cache_manager.addOperation("QubitUnitary", {}, dev_wires, inverse,
matrix_kok, {/*controlled_wires*/},
{/*controlled_values*/});
matrix_kok, dev_controlled_wires,
controlled_values);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,94 @@ TEST_CASE("LightningKokkosSimulator::GateSet", "[GateSet]") {
REQUIRE(LKsim->CacheManagerInfo() == expected);
}

// ============= Controlled operations =============
josephleekl marked this conversation as resolved.
Show resolved Hide resolved

SECTION("Controlled Pauli-X and RX") {
std::unique_ptr<LKSimulator> LKsim = std::make_unique<LKSimulator>();

constexpr std::size_t n_qubits = 2;
std::vector<intptr_t> Qs;
Qs.reserve(n_qubits);

for (std::size_t i = 0; i < n_qubits; i++) {
Qs[i] = LKsim->AllocateQubit();
}

LKsim->NamedOperation("Hadamard", {}, {Qs[0]}, false);
LKsim->NamedOperation("PauliX", {}, {Qs[1]}, false, {Qs[0]}, {true});
LKsim->NamedOperation("RX", {M_PI_2}, {Qs[1]}, false, {Qs[0]}, {true});

std::vector<std::complex<double>> state(1U << LKsim->GetNumQubits());
DataView<std::complex<double>, 1> view(state);
LKsim->State(view);

CHECK(
state.at(0) ==
PLApproxComplex(std::complex<double>{M_SQRT1_2, 0}).epsilon(1e-5));
CHECK(state.at(1) ==
PLApproxComplex(std::complex<double>{0, 0}).epsilon(1e-5));
CHECK(state.at(2) ==
PLApproxComplex(std::complex<double>{0, -0.5}).epsilon(1e-5));
CHECK(state.at(3) ==
PLApproxComplex(std::complex<double>{0.5, 0.0}).epsilon(1e-5));
}

SECTION("Hadamard, CNOT and Matrix") {
std::unique_ptr<LKSimulator> LKsim = std::make_unique<LKSimulator>();

constexpr std::size_t n_qubits = 2;
std::vector<intptr_t> Qs = LKsim->AllocateQubits(n_qubits);

LKsim->NamedOperation("Hadamard", {}, {Qs[0]}, false);
LKsim->NamedOperation("CNOT", {}, {Qs[0], Qs[1]}, false);

const std::vector<intptr_t> wires = {Qs[1]};
const std::vector<intptr_t> controlled_wires = {Qs[0]};
const std::vector<bool> controlled_values = {true};
std::vector<std::complex<double>> matrix{
{-0.6709485262524046, -0.6304426335363695},
{-0.14885403153998722, 0.3608498832392019},
{-0.2376311670004963, 0.3096798175687841},
{-0.8818365947322423, -0.26456390390903695},
};
LKsim->MatrixOperation(matrix, wires, false, controlled_wires,
controlled_values);

std::vector<std::complex<double>> state(1U << LKsim->GetNumQubits());
DataView<std::complex<double>, 1> view(state);
LKsim->State(view);

CHECK(state[0] == PLApproxComplex(std::complex<double>{0.70710678, 0.0})
.epsilon(1e-5));
CHECK(state[1] ==
PLApproxComplex(std::complex<double>{0.0, 0.0}).epsilon(1e-5));
CHECK(state[2] ==
PLApproxComplex(std::complex<double>{-0.1052557, 0.2551594})
.epsilon(1e-5));
CHECK(state[3] ==
PLApproxComplex(std::complex<double>{-0.62355264, -0.187075})
.epsilon(1e-5));
}

SECTION("Mismatch controlled wires") {
std::unique_ptr<LKSimulator> LKsim = std::make_unique<LKSimulator>();
constexpr std::size_t n_qubits = 2;
std::vector<intptr_t> Qs = LKsim->AllocateQubits(n_qubits);

REQUIRE_THROWS_WITH(
LKsim->NamedOperation("Hadamard", {}, {Qs[0]}, false, {Qs[1]}, {}),
Catch::Contains("Controlled wires/values size mismatch"));
std::vector<std::complex<double>> matrix{
{-0.6709485262524046, -0.6304426335363695},
{-0.14885403153998722, 0.3608498832392019},
{-0.2376311670004963, 0.3096798175687841},
{-0.8818365947322423, -0.26456390390903695},
};
REQUIRE_THROWS_WITH(
LKsim->MatrixOperation(matrix, {Qs[0]}, false, {Qs[1]}, {}),
Catch::Contains("Controlled wires/values size mismatch"));
}

SECTION("Test setStateVector") {
std::unique_ptr<LKSimulator> LKsim = std::make_unique<LKSimulator>();
constexpr std::size_t n_qubits = 2;
Expand Down Expand Up @@ -701,3 +789,5 @@ TEST_CASE("LightningKokkosSimulator::GateSet", "[GateSet]") {
CHECK(state[1] == PLApproxComplex(c2).epsilon(1e-5));
}
}

TEST_CASE("Test mismatch controlled wires", "[]") {}
Loading
Loading