Skip to content

Commit

Permalink
SX gate on lightning devices (#731)
Browse files Browse the repository at this point in the history
### Before submitting

Please complete the following checklist when submitting a PR:

- [x] All new features must include a unit test.
If you've fixed a bug or added code that should be tested, add a test to
the
      [`tests`](../tests) directory!

- [ ] All new functions and code must be clearly commented and
documented.
If you do make documentation changes, make sure that the docs build and
      render correctly by running `make docs`.

- [x] Ensure that the test suite passes, by running `make test`.

- [ ] Add a new entry to the `.github/CHANGELOG.md` file, summarizing
the
      change, and including a link back to the PR.

- [x] Ensure that code is properly formatted by running `make format`. 

When all the above are checked, delete everything above the dashed
line and fill in the pull request template.


------------------------------------------------------------------------------------------------------------

**Context:**
Internal assignment. 

**Description of the Change:**
`SX` gate implementation in `lightning.qubit` C++ backend

**Benefits:**
Improve the computation speed of the `SX` gate using `lightning.qubit`
C++ backend

**Possible Drawbacks:**

**Related GitHub Issues:**
Close #710

[sc-62536]

---------

Co-authored-by: AmintorDusko <[email protected]>
Co-authored-by: Vincent Michaud-Rioux <[email protected]>
Co-authored-by: Dev version update bot <github-actions[bot]@users.noreply.github.com>
Co-authored-by: ringo-but-quantum <[email protected]>
Co-authored-by: Rashid N H M <[email protected]>
Co-authored-by: Pietropaolo Frisoni <[email protected]>
Co-authored-by: Amintor Dusko <[email protected]>
Co-authored-by: Ali Asadi <[email protected]>
Co-authored-by: Christina Lee <[email protected]>
Co-authored-by: Lee James O'Riordan <[email protected]>
Co-authored-by: Vincent Michaud-Rioux <[email protected]>
  • Loading branch information
12 people authored Jan 28, 2025
1 parent e1572f5 commit 5f6c420
Show file tree
Hide file tree
Showing 28 changed files with 449 additions and 18 deletions.
4 changes: 4 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
* Reduce the number of shots in the PennyLane Python tests on CIs, from 20k to 10k.
[(#1046)](https://github.com/PennyLaneAI/pennylane-lightning/pull/1046)

* `SX` and `C(SX)` gates are natively supported for all lightning devices.
[(#731)](https://github.com/PennyLaneAI/pennylane-lightning/pull/731)

### Documentation

### Bug fixes
Expand All @@ -46,6 +49,7 @@ Yushao Chen,
Amintor Dusko,
Christina Lee,
Joseph Lee,
Luis Alfredo Nuñez Meneses,
Andrija Paurevic,
Shuli Shu

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.41.0-dev9"
__version__ = "0.41.0-dev10"
6 changes: 6 additions & 0 deletions pennylane_lightning/core/src/gates/Constant.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ using GateView = typename std::pair<GateOperation, std::string_view>;
GateView{GateOperation::PauliZ, "PauliZ"},
GateView{GateOperation::Hadamard, "Hadamard"},
GateView{GateOperation::S, "S"},
GateView{GateOperation::SX, "SX"},
GateView{GateOperation::T, "T"},
GateView{GateOperation::PhaseShift, "PhaseShift"},
GateView{GateOperation::RX, "RX"},
Expand Down Expand Up @@ -93,6 +94,7 @@ using CGateView = typename std::pair<ControlledGateOperation, std::string_view>;
CGateView{ControlledGateOperation::PauliZ, "PauliZ"},
CGateView{ControlledGateOperation::Hadamard, "Hadamard"},
CGateView{ControlledGateOperation::S, "S"},
CGateView{ControlledGateOperation::SX, "SX"},
CGateView{ControlledGateOperation::T, "T"},
CGateView{ControlledGateOperation::PhaseShift, "PhaseShift"},
CGateView{ControlledGateOperation::RX, "RX"},
Expand Down Expand Up @@ -207,6 +209,7 @@ using GateNWires = typename std::pair<GateOperation, std::size_t>;
GateNWires{GateOperation::PauliZ, 1},
GateNWires{GateOperation::Hadamard, 1},
GateNWires{GateOperation::S, 1},
GateNWires{GateOperation::SX, 1},
GateNWires{GateOperation::T, 1},
GateNWires{GateOperation::PhaseShift, 1},
GateNWires{GateOperation::RX, 1},
Expand Down Expand Up @@ -244,6 +247,7 @@ using CGateNWires = typename std::pair<ControlledGateOperation, std::size_t>;
CGateNWires{ControlledGateOperation::PauliZ, 1},
CGateNWires{ControlledGateOperation::Hadamard, 1},
CGateNWires{ControlledGateOperation::S, 1},
CGateNWires{ControlledGateOperation::SX, 1},
CGateNWires{ControlledGateOperation::T, 1},
CGateNWires{ControlledGateOperation::PhaseShift, 1},
CGateNWires{ControlledGateOperation::RX, 1},
Expand Down Expand Up @@ -321,6 +325,7 @@ using GateNParams = typename std::pair<GateOperation, std::size_t>;
GateNParams{GateOperation::PauliZ, 0},
GateNParams{GateOperation::Hadamard, 0},
GateNParams{GateOperation::S, 0},
GateNParams{GateOperation::SX, 0},
GateNParams{GateOperation::T, 0},
GateNParams{GateOperation::PhaseShift, 1},
GateNParams{GateOperation::RX, 1},
Expand Down Expand Up @@ -362,6 +367,7 @@ using CGateNParams = typename std::pair<ControlledGateOperation, std::size_t>;
CGateNParams{ControlledGateOperation::PauliZ, 0},
CGateNParams{ControlledGateOperation::Hadamard, 0},
CGateNParams{ControlledGateOperation::S, 0},
CGateNParams{ControlledGateOperation::SX, 0},
CGateNParams{ControlledGateOperation::T, 0},
CGateNParams{ControlledGateOperation::PhaseShift, 1},
CGateNParams{ControlledGateOperation::RX, 1},
Expand Down
2 changes: 2 additions & 0 deletions pennylane_lightning/core/src/gates/GateOperation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ enum class GateOperation : uint32_t {
PauliZ,
Hadamard,
S,
SX,
T,
PhaseShift,
RX,
Expand Down Expand Up @@ -78,6 +79,7 @@ enum class ControlledGateOperation : uint32_t {
PauliZ,
Hadamard,
S,
SX,
T,
PhaseShift,
RX,
Expand Down
20 changes: 20 additions & 0 deletions pennylane_lightning/core/src/gates/Gates.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,24 @@ static constexpr auto getS(const bool inverse = false)
(inverse) ? -IMAG<ComplexT, T>() : IMAG<ComplexT, T>()};
}

/**
* @brief Create a matrix representation of the SX gate data in row-major
* format.
*
* @tparam ComplexT Complex class.
* @tparam T Required precision of gate (`float` or `double`).
* @return constexpr std::vector<ComplexT<T>> Return constant expression
* of SX data.
*/
template <template <typename...> class ComplexT, typename T>
static constexpr auto getSX(const bool inverse = false)
-> std::vector<ComplexT<T>> {
const T half = (inverse) ? -0.5 : 0.5;
const ComplexT<T> z0{0.5, half};
const ComplexT<T> z1{0.5, -half};
return {z0, z1, z1, z0};
}

/**
* @brief Create a matrix representation of the T gate data in row-major format.
*
Expand Down Expand Up @@ -1216,6 +1234,8 @@ std::vector<ComplexT<T>> getMatrix(const GateOperation gate_op,
return getHadamard<ComplexT, T>();
case GateOperation::S:
return getS<ComplexT, T>(inverse);
case GateOperation::SX:
return getSX<ComplexT, T>(inverse);
case GateOperation::T:
return getT<ComplexT, T>(inverse);
case GateOperation::RX:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,9 @@ class StateVectorCudaBase : public StateVectorBase<Precision, Derived> {
private:
std::unique_ptr<LightningGPU::DataBuffer<CFP_t>> data_buffer_;
const std::unordered_set<std::string> const_gates_{
"Identity", "PauliX", "PauliY", "PauliZ", "Hadamard", "T", "S",
"CNOT", "SWAP", "CY", "CZ", "CSWAP", "Toffoli"};
"Identity", "PauliX", "PauliY", "PauliZ", "Hadamard",
"T", "S", "SX", "CNOT", "SWAP",
"CY", "CZ", "CSWAP", "Toffoli"};
const std::unordered_map<std::string, std::size_t> ctrl_map_{
// Add mapping from function name to required wires.
{"Identity", 0},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,13 @@ class StateVectorCudaMPI final
{wires.begin(), wires.end() - 1}, {wires.back()},
adjoint);
}
inline void applySX(const std::vector<std::size_t> &wires, bool adjoint) {
static const std::string name{"SX"};
static const Precision param = 0.0;
applyDeviceMatrixGate(gate_cache_.get_gate_device_ptr(name, param),
{wires.begin(), wires.end() - 1}, {wires.back()},
adjoint);
}
inline void applyT(const std::vector<std::size_t> &wires, bool adjoint) {
static const std::string name{"T"};
static const Precision param = 0.0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,13 @@ class StateVectorCudaManaged
{wires.begin(), wires.end() - 1}, {wires.back()},
adjoint);
}
inline void applySX(const std::vector<std::size_t> &wires, bool adjoint) {
static const std::string name{"SX"};
static const Precision param = 0.0;
applyDeviceMatrixGate_(gate_cache_.get_gate_device_ptr(name, param),
{wires.begin(), wires.end() - 1}, {wires.back()},
adjoint);
}
inline void applyT(const std::vector<std::size_t> &wires, bool adjoint) {
static const std::string name{"T"};
static const Precision param = 0.0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ template <class fp_t> class GateCache {
/**
* @brief Add a default gate-set to the given cache. Assumes
* initializer-list evaluated gates for "PauliX", "PauliY", "PauliZ",
* "Hadamard", "S", "T", "SWAP", with "CNOT" and "CZ" represented as their
* single-qubit values.
* "Hadamard", "S", "SX", "T", "SWAP", with "CNOT" and "CZ" represented as
* their single-qubit values.
*
*/
void defaultPopulateCache() {
Expand Down Expand Up @@ -108,6 +108,22 @@ template <class fp_t> class GateCache {
std::forward_as_tuple(std::vector<CFP_t>{
cuUtil::ONE<CFP_t>(), cuUtil::ZERO<CFP_t>(),
cuUtil::ZERO<CFP_t>(), cuUtil::IMAG<CFP_t>()}));
host_gates_.emplace(
std::piecewise_construct,
std::forward_as_tuple(std::make_pair(std::string{"SX"}, 0.0)),
std::forward_as_tuple(std::vector<CFP_t>{
cuUtil::ConstMultSC(0.5,
cuUtil::ConstSum(cuUtil::ONE<CFP_t>(),
cuUtil::IMAG<CFP_t>())),
cuUtil::ConstMultSC(0.5,
cuUtil::ConstSum(cuUtil::ONE<CFP_t>(),
-cuUtil::IMAG<CFP_t>())),
cuUtil::ConstMultSC(0.5,
cuUtil::ConstSum(cuUtil::ONE<CFP_t>(),
-cuUtil::IMAG<CFP_t>())),
cuUtil::ConstMultSC(0.5,
cuUtil::ConstSum(cuUtil::ONE<CFP_t>(),
cuUtil::IMAG<CFP_t>()))}));
host_gates_.emplace(
std::piecewise_construct,
std::forward_as_tuple(std::make_pair(std::string{"T"}, 0.0)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,53 @@ TEMPLATE_TEST_CASE("StateVectorCudaManaged::applyS",
}
}

TEMPLATE_TEST_CASE("StateVectorCudaManaged::applySX",
"[StateVectorCudaManaged_Nonparam]", float, double) {
const bool inverse = GENERATE(true, false);
{
using cp_t = std::complex<TestType>;
const std::size_t num_qubits = 3;
StateVectorCudaManaged<TestType> sv{num_qubits};
// Test using |000> state

const cp_t z(0.0, 0.0);
cp_t p(0.5, 0.5);
cp_t m(0.5, -0.5);

if (inverse) {
p = conj(p);
m = conj(m);
}

const std::vector<std::vector<cp_t>> expected_results = {
{p, z, z, z, m, z, z, z},
{p, z, m, z, z, z, z, z},
{p, m, z, z, z, z, z, z}};

const auto init_state = sv.getDataVector();
SECTION("Apply directly") {
for (std::size_t index = 0; index < num_qubits; index++) {
StateVectorCudaManaged<TestType> sv_direct{init_state.data(),
init_state.size()};
CHECK(sv_direct.getDataVector() == init_state);
sv_direct.applySX({index}, inverse);
CHECK(sv_direct.getDataVector() ==
Pennylane::Util::approx(expected_results[index]));
}
}
SECTION("Apply using dispatcher") {
for (std::size_t index = 0; index < num_qubits; index++) {
StateVectorCudaManaged<TestType> sv_dispatch{init_state.data(),
init_state.size()};
CHECK(sv_dispatch.getDataVector() == init_state);
sv_dispatch.applyOperation("SX", {index}, inverse);
CHECK(sv_dispatch.getDataVector() ==
Pennylane::Util::approx(expected_results[index]));
}
}
}
}

TEMPLATE_TEST_CASE("StateVectorCudaManaged::applyT",
"[StateVectorCudaManaged_Nonparam]", float, double) {
const bool inverse = GENERATE(true, false);
Expand Down Expand Up @@ -1205,6 +1252,7 @@ TEMPLATE_TEST_CASE("StateVectorCudaManaged::applyOperation non-param "
approx(sv1.getDataVector()).margin(margin));
}
}

DYNAMIC_SECTION("N-controlled S - "
<< "controls = {" << control << "} "
<< ", wires = {" << wire << "} - "
Expand All @@ -1223,6 +1271,24 @@ TEMPLATE_TEST_CASE("StateVectorCudaManaged::applyOperation non-param "
}
}

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

sv0.applyControlledMatrix(
matrix.data(), std::vector<std::size_t>{control},
std::vector<bool>{true}, std::vector<std::size_t>{wire});
sv1.applyOperation("SX", 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 << "} - "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,14 @@ TEMPLATE_TEST_CASE("StateVectorCudaMPI::S", "[StateVectorCudaMPI_Nonparam]",
{num_qubits - 1});
}

TEMPLATE_TEST_CASE("StateVectorCudaMPI::SX", "[StateVectorCudaMPI_Nonparam]",
float, double) {
PLGPU_MPI_TEST_GATE_OPS_NONPARAM(TestType, num_qubits, applySX, "SX",
lsb_1qbit);
PLGPU_MPI_TEST_GATE_OPS_NONPARAM(TestType, num_qubits, applySX, "SX",
{num_qubits - 1});
}

TEMPLATE_TEST_CASE("StateVectorCudaMPI::T", "[StateVectorCudaMPI_Nonparam]",
float, double) {
PLGPU_MPI_TEST_GATE_OPS_NONPARAM(TestType, num_qubits, applyT, "T",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,46 @@ void applyS(Kokkos::View<Kokkos::complex<PrecisionT> *> arr_,
inverse);
}

template <class ExecutionSpace, class PrecisionT>
void applyNCSX(Kokkos::View<Kokkos::complex<PrecisionT> *> arr_,
std::size_t num_qubits,
const std::vector<std::size_t> &controlled_wires,
const std::vector<bool> &controlled_values,
const std::vector<std::size_t> &wires, bool inverse = false,
[[maybe_unused]] const std::vector<PrecisionT> &params = {}) {

constexpr PrecisionT half = 0.5;
const Kokkos::complex<PrecisionT> z0{half, (inverse) ? -half : half};
const Kokkos::complex<PrecisionT> z1 = Kokkos::conj(z0);

auto core_function =
KOKKOS_LAMBDA(Kokkos::View<Kokkos::complex<PrecisionT> *> arr,
std::size_t i0, std::size_t i1) {
const Kokkos::complex<PrecisionT> v0 = arr(i0);
const Kokkos::complex<PrecisionT> v1 = arr(i1);

arr[i0] = z0 * v0 + z1 * v1;
arr[i1] = z1 * v0 + z0 * v1;
};
if (controlled_wires.empty()) {
applyNC1Functor<PrecisionT, decltype(core_function), false>(
ExecutionSpace{}, arr_, num_qubits, wires, core_function);
} else {
applyNC1Functor<PrecisionT, decltype(core_function), true>(
ExecutionSpace{}, arr_, num_qubits, controlled_wires,
controlled_values, wires, core_function);
}
}

template <class ExecutionSpace, class PrecisionT>
void applySX(Kokkos::View<Kokkos::complex<PrecisionT> *> arr_,
std::size_t num_qubits, const std::vector<std::size_t> &wires,
bool inverse = false,
[[maybe_unused]] const std::vector<PrecisionT> &params = {}) {
applyNCSX<ExecutionSpace, PrecisionT>(arr_, num_qubits, {}, {}, wires,
inverse);
}

template <class ExecutionSpace, class PrecisionT>
void applyNCT(Kokkos::View<Kokkos::complex<PrecisionT> *> arr_,
std::size_t num_qubits,
Expand Down Expand Up @@ -1821,6 +1861,9 @@ void applyNamedOperation(const GateOperation gateop,
case GateOperation::S:
applyS<ExecutionSpace>(arr_, num_qubits, wires, inverse, params);
return;
case GateOperation::SX:
applySX<ExecutionSpace>(arr_, num_qubits, wires, inverse, params);
return;
case GateOperation::T:
applyT<ExecutionSpace>(arr_, num_qubits, wires, inverse, params);
return;
Expand Down Expand Up @@ -1956,6 +1999,10 @@ void applyNCNamedOperation(const ControlledGateOperation gateop,
applyNCS<ExecutionSpace>(arr_, num_qubits, controlled_wires,
controlled_values, wires, inverse, params);
return;
case ControlledGateOperation::SX:
applyNCSX<ExecutionSpace>(arr_, num_qubits, controlled_wires,
controlled_values, wires, inverse, params);
return;
case ControlledGateOperation::T:
applyNCT<ExecutionSpace>(arr_, num_qubits, controlled_wires,
controlled_values, wires, inverse, params);
Expand Down
Loading

0 comments on commit 5f6c420

Please sign in to comment.