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 N-control gate support to lightning.gpu #938

Open
wants to merge 49 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
4f37e20
initial commit
multiphaseCFD Oct 8, 2024
dad7de8
Auto update version from '0.39.0-dev40' to '0.39.0-dev41'
ringo-but-quantum Oct 8, 2024
3d30f21
Merge branch 'master' into applyControlledMatrix_LGPU
multiphaseCFD Oct 16, 2024
dd0307a
Auto update version from '0.39.0-dev45' to '0.39.0-dev46'
ringo-but-quantum Oct 16, 2024
d81874b
fix applyControlledMatrix
multiphaseCFD Oct 16, 2024
ed2df45
update GlobalPhase and CGlobalPhase gate support
multiphaseCFD Oct 16, 2024
e06cd31
fix applyControlledMatrix
multiphaseCFD Oct 16, 2024
ad6a179
update unit tests
multiphaseCFD Oct 17, 2024
c4b6d85
make format
multiphaseCFD Oct 17, 2024
3ce16e6
update GlobalPhase support with `scaleC_CUDA`
multiphaseCFD Oct 17, 2024
008c4f3
remove global_phase_diagonal
multiphaseCFD Oct 17, 2024
015b50a
remove unused lines
multiphaseCFD Oct 17, 2024
02eb290
initial commit
multiphaseCFD Oct 17, 2024
dd2dbab
Auto update version from '0.39.0-dev45' to '0.39.0-dev46'
ringo-but-quantum Oct 17, 2024
a77fef3
add py unit tests for globalphase gate
multiphaseCFD Oct 17, 2024
dffb78f
raise error for mpi-cGlobalPhase
multiphaseCFD Oct 17, 2024
2641cfc
make format
multiphaseCFD Oct 17, 2024
56b26e2
add changelog
multiphaseCFD Oct 17, 2024
38b762e
Trigger CIs
multiphaseCFD Oct 17, 2024
a0abe2e
quick fix for the setup.py
multiphaseCFD Oct 18, 2024
2f65169
add C++ unit tests
multiphaseCFD Oct 18, 2024
d303e87
Auto update version from '0.39.0-dev46' to '0.39.0-dev47'
ringo-but-quantum Oct 18, 2024
95662ec
add more C++ unit tests
multiphaseCFD Oct 18, 2024
c035b51
update the frontend
multiphaseCFD Oct 21, 2024
faad0e9
Auto update version from '0.39.0-dev47' to '0.39.0-dev48'
ringo-but-quantum Oct 21, 2024
148b8df
update changelog
multiphaseCFD Oct 21, 2024
a87d77b
Merge branch 'master' into applyControlledMatrix_LGPU
multiphaseCFD Oct 21, 2024
7ea85d8
Auto update version from '0.39.0-dev47' to '0.39.0-dev48'
ringo-but-quantum Oct 21, 2024
388efae
Trigger CIs
multiphaseCFD Oct 21, 2024
5974b47
Merge branch 'master' into update_gphase_lgpu
multiphaseCFD Oct 21, 2024
fbae962
Auto update version from '0.39.0-dev47' to '0.39.0-dev48'
ringo-but-quantum Oct 21, 2024
7ec070e
update py CIs
multiphaseCFD Oct 21, 2024
c1a141a
add more C++ unit tests
multiphaseCFD Oct 22, 2024
b54b8ca
add checks for ctrls and tgts
multiphaseCFD Oct 22, 2024
b7ec1da
tidy up naming
multiphaseCFD Oct 22, 2024
2970625
Merge branch 'update_gphase_lgpu' into applyControlledMatrix_LGPU
multiphaseCFD Oct 22, 2024
4e69bb3
update
multiphaseCFD Oct 22, 2024
454e4e0
quick fix
multiphaseCFD Oct 22, 2024
add8525
Merge branch 'master' into applyControlledMatrix_LGPU
multiphaseCFD Oct 23, 2024
9b56685
Auto update version from '0.39.0-dev48' to '0.39.0-dev49'
ringo-but-quantum Oct 23, 2024
b0300d6
update
multiphaseCFD Oct 23, 2024
7e4a34b
quick fix
multiphaseCFD Oct 23, 2024
8c07c01
make format
multiphaseCFD Oct 23, 2024
f2306cc
apply Joseph's comments
multiphaseCFD Oct 25, 2024
e6fe638
Auto update version from '0.39.0-dev49' to '0.39.0-dev51'
ringo-but-quantum Oct 25, 2024
3da92b2
apply afredo's comments
multiphaseCFD Oct 25, 2024
c3bd58a
add todos
multiphaseCFD Oct 25, 2024
e17ab64
Merge branch 'master' into applyControlledMatrix_LGPU
multiphaseCFD Oct 25, 2024
fb8d102
Auto update version from '0.39.0-dev50' to '0.39.0-dev51'
ringo-but-quantum Oct 25, 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 gates support to `lightning.gpu`'s single-GPU backend.
[(#938)](https://github.com/PennyLaneAI/pennylane-lightning/pull/938)

* Add `mid-circuit measurements` support to `lightning.gpu`'s single-GPU backend.
[(#931)](https://github.com/PennyLaneAI/pennylane-lightning/pull/931)

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests_gpu_python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ jobs:
run: |
rm -rf build
PL_BACKEND=lightning_qubit python scripts/configure_pyproject_toml.py || true
PL_BACKEND=lightning_qubit SKIP_COMPILATION=True python -m pip install . -vv
PL_BACKEND=lightning_qubit python -m pip install . -vv

rm -rf build
PL_BACKEND=${{ matrix.pl_backend }} python scripts/configure_pyproject_toml.py || true
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.39.0-dev50"
__version__ = "0.39.0-dev51"

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,55 @@ using StateVectorBackends =
StateVectorCudaManaged<double>, void>;

/**
* @brief Get a gate kernel map for a statevector.
* @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),
static_cast<const std::size_t>(matrix.request().size), controlled_wires,
controlled_values, wires, inverse);
}

template <class StateVectorT, class PyClass>
void registerControlledGate(PyClass &pyclass) {
using PrecisionT =
typename StateVectorT::PrecisionT; // Statevector's precision
using ParamT = PrecisionT; // Parameter's data precision

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](
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
auto func = [gate_name = gate_name](
auto func = [&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.
*/
template <class StateVectorT, class PyClass>
void registerBackendClassSpecificBindings(PyClass &pyclass) {
using PrecisionT =
Expand All @@ -65,6 +111,7 @@ void registerBackendClassSpecificBindings(PyClass &pyclass) {
py::array::c_style | py::array::forcecast>;

registerGatesForStateVector<StateVectorT>(pyclass);
registerControlledGate<StateVectorT>(pyclass);

pyclass
.def(py::init<std::size_t>()) // qubits, device
Expand Down Expand Up @@ -96,6 +143,8 @@ void registerBackendClassSpecificBindings(PyClass &pyclass) {
},
"Set State Vector on GPU with values for the state vector and "
"wires on the host memory.")
.def("applyControlledMatrix", &applyControlledMatrix<StateVectorT>,
"Apply controlled operation")
.def(
"DeviceToDevice",
[](StateVectorT &sv, const StateVectorT &other, bool async) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1105,4 +1105,250 @@ TEMPLATE_TEST_CASE("StateVectorCudaManaged::SetIthStates",

CHECK(expected_state == Pennylane::Util::approx(sv.getDataVector()));
}
}
}

TEMPLATE_TEST_CASE("StateVectorCudaManaged::applyOperation non-param "
"one-qubit with controls",
"[StateVectorCudaManaged]", float, double) {
using PrecisionT = TestType;
std::mt19937 re{1337};
const int num_qubits = 4;
const auto margin = PrecisionT{1e-5};
const std::size_t control = GENERATE(0, 1, 2, 3);
const std::size_t wire = GENERATE(0, 1, 2, 3);
multiphaseCFD marked this conversation as resolved.
Show resolved Hide resolved
auto st0 = createRandomStateVectorData<PrecisionT>(re, num_qubits);
StateVectorCudaManaged<PrecisionT> sv0(num_qubits);
StateVectorCudaManaged<PrecisionT> sv1(num_qubits);

sv0.CopyHostDataToGpu(st0.data(), st0.size());
sv1.CopyHostDataToGpu(st0.data(), st0.size());

DYNAMIC_SECTION("N-controlled PauliX - "
<< "controls = {" << control << "} "
<< ", wires = {" << wire << "} - "
<< PrecisionToName<PrecisionT>::value) {
if (control != wire) {
sv0.applyOperation("CNOT", {control, wire});
sv1.applyOperation("PauliX", std::vector<std::size_t>{control},
std::vector<bool>{true},
std::vector<std::size_t>{wire});
REQUIRE(sv0.getDataVector() ==
approx(sv1.getDataVector()).margin(margin));
multiphaseCFD marked this conversation as resolved.
Show resolved Hide resolved
}

if (control != 0 && wire != 0 && control != wire) {
sv0.applyOperation("Toffoli", {0, control, wire});
sv1.applyOperation("PauliX", std::vector<std::size_t>{0, control},
std::vector<bool>{true, true},
std::vector<std::size_t>{wire});
REQUIRE(sv0.getDataVector() ==
approx(sv1.getDataVector()).margin(margin));

sv0.applyOperation("Toffoli", {control, 0, wire});
sv1.applyOperation("PauliX", std::vector<std::size_t>{control, 0},
std::vector<bool>{true, true},
std::vector<std::size_t>{wire});
REQUIRE(sv0.getDataVector() ==
approx(sv1.getDataVector()).margin(margin));
}
}

DYNAMIC_SECTION("N-controlled PauliY - "
<< "controls = {" << control << "} "
<< ", wires = {" << wire << "} - "
<< PrecisionToName<PrecisionT>::value) {
if (control != wire) {
sv0.applyOperation("CY", {control, wire});
sv1.applyOperation("PauliY", std::vector<std::size_t>{control},
std::vector<bool>{true},
std::vector<std::size_t>{wire});
REQUIRE(sv0.getDataVector() ==
approx(sv1.getDataVector()).margin(margin));
}
}

DYNAMIC_SECTION("N-controlled PauliZ - "
<< "controls = {" << control << "} "
<< ", wires = {" << wire << "} - "
<< PrecisionToName<PrecisionT>::value) {
if (control != wire) {
sv0.applyOperation("CZ", {control, wire});
sv1.applyOperation("PauliZ", std::vector<std::size_t>{control},
std::vector<bool>{true},
std::vector<std::size_t>{wire});
REQUIRE(sv0.getDataVector() ==
approx(sv1.getDataVector()).margin(margin));
}
}

DYNAMIC_SECTION("N-controlled Hadamard - "
<< "controls = {" << control << "} "
<< ", wires = {" << wire << "} - "
<< PrecisionToName<PrecisionT>::value) {
if (control != wire) {
const auto matrix = getHadamard<std::complex, PrecisionT>();

sv0.applyControlledMatrix(
matrix.data(), matrix.size(), std::vector<std::size_t>{control},
std::vector<bool>{true}, std::vector<std::size_t>{wire});
sv1.applyOperation("Hadamard", std::vector<std::size_t>{control},
std::vector<bool>{true},
std::vector<std::size_t>{wire});
REQUIRE(sv0.getDataVector() ==
approx(sv1.getDataVector()).margin(margin));
}
}
DYNAMIC_SECTION("N-controlled S - "
<< "controls = {" << control << "} "
<< ", wires = {" << wire << "} - "
<< PrecisionToName<PrecisionT>::value) {
if (control != wire) {
const auto matrix = getS<std::complex, PrecisionT>();

sv0.applyControlledMatrix(
matrix.data(), matrix.size(), std::vector<std::size_t>{control},
std::vector<bool>{true}, std::vector<std::size_t>{wire});
sv1.applyOperation("S", std::vector<std::size_t>{control},
std::vector<bool>{true},
std::vector<std::size_t>{wire});
REQUIRE(sv0.getDataVector() ==
approx(sv1.getDataVector()).margin(margin));
}
}

DYNAMIC_SECTION("N-controlled T - "
<< "controls = {" << control << "} "
<< ", wires = {" << wire << "} - "
<< PrecisionToName<PrecisionT>::value) {
if (control != wire) {
const std::vector<std::complex<PrecisionT>> matrix =
getT<std::complex, PrecisionT>();

sv0.applyControlledMatrix(
matrix.data(), matrix.size(), std::vector<std::size_t>{control},
std::vector<bool>{true}, std::vector<std::size_t>{wire});
sv1.applyOperation("T", std::vector<std::size_t>{control},
std::vector<bool>{true},
std::vector<std::size_t>{wire});
REQUIRE(sv0.getDataVector() ==
approx(sv1.getDataVector()).margin(margin));
}
}
}

TEMPLATE_TEST_CASE("StateVectorCudaManaged::applyOperation non-param "
"two-qubit with controls",
"[StateVectorCudaManaged]", float, double) {
using PrecisionT = TestType;
std::mt19937 re{1337};
const int num_qubits = 4;
const auto margin = PrecisionT{1e-5};
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 st0 = createRandomStateVectorData<PrecisionT>(re, num_qubits);
StateVectorCudaManaged<PrecisionT> sv0(num_qubits);
StateVectorCudaManaged<PrecisionT> sv1(num_qubits);

sv0.CopyHostDataToGpu(st0.data(), st0.size());
sv1.CopyHostDataToGpu(st0.data(), st0.size());

DYNAMIC_SECTION("N-controlled SWAP - "
<< "controls = {" << control << "} "
<< ", wires = {" << wire0 << ", " << wire1 << "} - "
<< PrecisionToName<PrecisionT>::value) {
if (control != wire0 && control != wire1 && wire0 != wire1) {
sv0.applyOperation("CSWAP", {control, wire0, wire1});
sv1.applyOperation("SWAP", std::vector<std::size_t>{control},
std::vector<bool>{true},
std::vector<std::size_t>{wire0, wire1});
REQUIRE(sv0.getDataVector() ==
approx(sv1.getDataVector()).margin(margin));
}
}

DYNAMIC_SECTION("N-controlled SWAP with matrix- "
<< "controls = {" << control << "} "
<< ", wires = {" << wire0 << ", " << wire1 << "} - "
<< PrecisionToName<PrecisionT>::value) {
if (control != wire0 && control != wire1 && wire0 != wire1) {
const std::vector<std::complex<PrecisionT>> matrix =
getSWAP<std::complex, PrecisionT>();
sv0.applyControlledMatrix(matrix.data(), matrix.size(),
std::vector<std::size_t>{control},
std::vector<bool>{true},
std::vector<std::size_t>{wire0, wire1});
sv1.applyOperation("SWAP", std::vector<std::size_t>{control},
std::vector<bool>{true},
std::vector<std::size_t>{wire0, wire1});
REQUIRE(sv0.getDataVector() ==
approx(sv1.getDataVector()).margin(margin));
}
}
}

TEMPLATE_TEST_CASE("StateVectorCudaManaged::controlled Toffoli",
"[StateVectorCudaManaged]", float, double) {
using PrecisionT = TestType;
std::mt19937 re{1337};
const int num_qubits = 6;
const auto margin = PrecisionT{1e-5};
const std::size_t control = GENERATE(0, 1, 2);
auto st0 = createRandomStateVectorData<PrecisionT>(re, num_qubits);

StateVectorCudaManaged<PrecisionT> sv0(num_qubits);
StateVectorCudaManaged<PrecisionT> sv1(num_qubits);

sv0.CopyHostDataToGpu(st0.data(), st0.size());
sv1.CopyHostDataToGpu(st0.data(), st0.size());

const std::vector<std::complex<PrecisionT>> matrix =
getToffoli<std::complex, PrecisionT>();
sv0.applyControlledMatrix(
matrix.data(), matrix.size(), std::vector<std::size_t>{control},
std::vector<bool>{true}, std::vector<std::size_t>{3, 4, 5});
sv1.applyOperation("PauliX", std::vector<std::size_t>{control, 3, 4},
std::vector<bool>{true, true, true},
std::vector<std::size_t>{5});
REQUIRE(sv0.getDataVector() == approx(sv1.getDataVector()).margin(margin));
}

TEMPLATE_TEST_CASE(
"StateVectorCudaManaged::controlled_gate_direct_matrix_offload",
"[StateVectorCudaManaged]", float, double) {
using PrecisionT = TestType;
std::mt19937 re{1337};
const int num_qubits = 6;
const auto margin = PrecisionT{1e-5};
const std::size_t control = GENERATE(0, 1, 2);
auto st0 = createRandomStateVectorData<PrecisionT>(re, num_qubits);

StateVectorCudaManaged<PrecisionT> sv0(num_qubits);
StateVectorCudaManaged<PrecisionT> sv1(num_qubits);

sv0.CopyHostDataToGpu(st0.data(), st0.size());
sv1.CopyHostDataToGpu(st0.data(), st0.size());

SECTION("Catch failures caused by unsupported named gates") {
PL_CHECK_THROWS_MATCHES(
sv0.applyOperation("paulix",
std::vector<std::size_t>{control, 3, 4},
std::vector<bool>{true, true, true},
std::vector<std::size_t>{5}),
LightningException, "Currently unsupported gate: paulix");
}

SECTION("direct base matrix offload") {
const std::vector<std::complex<PrecisionT>> matrix = {
{0.0, 0.0}, {1.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}};
sv0.applyControlledMatrix(matrix.data(), matrix.size(),
std::vector<std::size_t>{control, 3, 4},
std::vector<bool>{true, true, true},
std::vector<std::size_t>{5});
sv1.applyOperation("Paulix", std::vector<std::size_t>{control, 3, 4},
std::vector<bool>{true, true, true},
std::vector<std::size_t>{5}, false, {0.0}, matrix);
REQUIRE(sv0.getDataVector() ==
approx(sv1.getDataVector()).margin(margin));
}
}
Loading
Loading