Skip to content

Commit

Permalink
(LK-C-1) Add controlled 1-qubit named-gate (e.g. NCPauliX) support to…
Browse files Browse the repository at this point in the history
… Lightning Kokkos (#952)

### Before submitting

Please complete the following checklist when submitting a PR:

- [ ] 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`.

- [ ] 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.

- [ ] 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:**

**Description of the Change:**
This PR adds support for named controlled 1-qubit gates (e.g. PauliX /
RX / PhaseShift / Hadamard / Rot / GlobalPhase). These are defined in
`BasicGateFunctors.hpp`, and are applied through `applyNC1Functor`
defined in the same file.

**Benefits:**
Performance benchmarks for gates are shown here:
https://www.notion.so/xanaduai/Lightning-Kokkos-Native-Controlled-Operation-Gate-Benchmarks-12ebc6bd17648017a2dcd237748b24fe

**Possible Drawbacks:**

**Related GitHub Issues:**

[sc-76773]
  • Loading branch information
josephleekl authored Nov 8, 2024
1 parent 0f5beee commit 6536bb6
Show file tree
Hide file tree
Showing 15 changed files with 1,469 additions and 274 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -389,32 +389,6 @@ class StateVectorKokkos final
});
}

/**
* @brief Apply a single gate to the state vector.
*
* @param opName Name of gate to apply.
* @param controlled_wires Control wires.
* @param controlled_values Control values (false or true).
* @param wires Wires to apply gate to.
* @param inverse Indicates whether to use adjoint of gate.
* @param params Optional parameter list for parametric gates.
* @param gate_matrix Optional std gate matrix if opName doesn't exist.
*/
void applyOperation(const std::string &opName,
const std::vector<std::size_t> &controlled_wires,
const std::vector<bool> &controlled_values,
const std::vector<std::size_t> &wires,
bool inverse = false,
const std::vector<fp_t> &params = {},
const std::vector<ComplexT> &gate_matrix = {}) {
PL_ABORT_IF_NOT(controlled_wires.empty(),
"Controlled kernels not implemented.");
PL_ABORT_IF_NOT(controlled_wires.size() == controlled_values.size(),
"`controlled_wires` must have the same size as "
"`controlled_values`.");
applyOperation(opName, wires, inverse, params, gate_matrix);
}

/**
* @brief Apply a multi qubit operator to the state vector using a matrix
*
Expand Down Expand Up @@ -474,6 +448,46 @@ class StateVectorKokkos final
}
}

/**
* @brief Apply a controlled-single gate to the state vector.
*
* @param opName Name of gate to apply.
* @param controlled_wires Control wires.
* @param controlled_values Control values (false or true).
* @param wires Wires to apply gate to.
* @param inverse Indicates whether to use adjoint of gate. Default to false
* @param params Optional parameter list for parametric gates.
* @param gate_matrix Optional unitary gate matrix if opName doesn't exist.
*/
void applyOperation(const std::string &opName,
const std::vector<std::size_t> &controlled_wires,
const std::vector<bool> &controlled_values,
const std::vector<std::size_t> &wires,
bool inverse = false,
const std::vector<fp_t> &params = {},
const std::vector<ComplexT> &gate_matrix = {}) {
PL_ABORT_IF_NOT(
areVecsDisjoint<std::size_t>(controlled_wires, wires),
"`controlled_wires` and target wires must be disjoint.");
PL_ABORT_IF_NOT(controlled_wires.size() == controlled_values.size(),
"`controlled_wires` must have the same size as "
"`controlled_values`.");
PL_ABORT_IF_NOT(
array_contains(controlled_gate_names, std::string_view{opName}),
"Controlled matrix operation not yet supported.");

if (controlled_wires.empty()) {
return applyOperation(opName, wires, inverse, params, gate_matrix);
}

const std::size_t num_qubits = this->getNumQubits();
const ControlledGateOperation gateop =
reverse_lookup(controlled_gate_names, std::string_view{opName});
applyNCNamedOperation<KokkosExecSpace>(
gateop, *data_, num_qubits, controlled_wires, controlled_values,
wires, inverse, params);
}

/**
* @brief Apply a given matrix directly to the statevector using a
* raw matrix pointer vector.
Expand All @@ -491,23 +505,6 @@ class StateVectorKokkos final
applyMultiQubitOp(matrix_, wires, inverse);
}

/**
* @brief Apply a given matrix directly to the statevector.
*
* @param matrix Matrix data (in row-major format).
* @param wires Wires to apply gate to.
* @param inverse Indicate whether inverse should be taken.
*/
inline void applyMatrix(std::vector<ComplexT> &matrix,
const std::vector<std::size_t> &wires,
bool inverse = false) {
PL_ABORT_IF(wires.empty(), "Number of wires must be larger than 0");
PL_ABORT_IF(matrix.size() != exp2(2 * wires.size()),
"The size of matrix does not match with the given "
"number of wires");
applyMatrix(matrix.data(), wires, inverse);
}

/**
* @brief Apply a given matrix directly to the statevector using a
* raw matrix pointer vector.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,33 @@ using StateVectorBackends =
Pennylane::Util::TypeList<StateVectorKokkos<float>,
StateVectorKokkos<double>, void>;

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 +88,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
Original file line number Diff line number Diff line change
Expand Up @@ -190,15 +190,11 @@ void LightningKokkosSimulator::MatrixOperation(
const std::vector<std::complex<double>> &matrix,
const std::vector<QubitIdType> &wires, bool inverse,
const std::vector<QubitIdType> &controlled_wires,
const std::vector<bool> &controlled_values) {
[[maybe_unused]] const std::vector<bool> &controlled_values) {
using UnmanagedComplexHostView =
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(!isValidQubits(wires), "Given wires do not refer to qubits");
RT_FAIL_IF(!isValidQubits(controlled_wires),
"Given controlled wires do not refer to qubits");
Expand Down
Loading

0 comments on commit 6536bb6

Please sign in to comment.