Skip to content

Commit

Permalink
Pass control values to the L-Qubit kernels (#576)
Browse files Browse the repository at this point in the history
* Add control values to multiqubitop.

* 3+ controlled qubit U

* Introduce reverseWires.

* Pass control_values down to C++ layer for applyMatrix.

* Add control values to all gates.

* Fix adjoint for multicontrolled gates with control values.

* Fix couple warnings and remove extern which do not accelerate compilation.

* Update changelog.

* Remove unused bitset function.

* Fix tidy-warning.

* Auto update version

* Add throw tests in L-GPU/Kokkos.

* Fix docstring [skip ci].

* trigger ci

* Fix windows wheels?

---------

Co-authored-by: Dev version update bot <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
vincentmr and github-actions[bot] authored Dec 14, 2023
1 parent 998362a commit 5427421
Show file tree
Hide file tree
Showing 34 changed files with 781 additions and 906 deletions.
4 changes: 4 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Release 0.34.0-dev

### New features since last release

* Lightning-Qubit support arbitrary controlled gates (any wires and any control values). The kernels are implemented in the `LM` module.
[(#576)](https://github.com/PennyLaneAI/pennylane-lightning/pull/576)

* Shot-noise related methods now accommodate observable objects with arbitrary eigenvalues. Add a Kronecker product method for two diagonal matrices.
[(#570)](https://github.com/PennyLaneAI/pennylane-lightning/pull/570)

Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/wheel_win_x86_64.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
fail-fast: false
matrix:
os: [windows-2022]
exec_model: ${{ fromJson(needs.set_wheel_build_matrix.outputs.exec_model) }}
exec_model: [\"SERIAL\"]
kokkos_version: ${{ fromJson(needs.set_wheel_build_matrix.outputs.kokkos_version) }}
timeout-minutes: 30
name: Kokkos core (${{ matrix.exec_model }})
Expand Down Expand Up @@ -88,9 +88,9 @@ jobs:
matrix:
os: [windows-2022]
arch: [AMD64]
pl_backend: ["lightning_qubit"]
pl_backend: ["lightning_kokkos", "lightning_qubit"]
cibw_build: ${{ fromJson(needs.set_wheel_build_matrix.outputs.python_version) }}
exec_model: ${{ fromJson(needs.set_wheel_build_matrix.outputs.exec_model) }}
exec_model: [\"SERIAL\"]
kokkos_version: ${{ fromJson(needs.set_wheel_build_matrix.outputs.kokkos_version) }}
timeout-minutes: 30
name: ${{ matrix.os }} - ${{ matrix.pl_backend }} (Python ${{ fromJson('{ "cp39-*":"3.9","cp310-*":"3.10","cp311-*":"3.11" }')[matrix.cibw_build] }})
Expand Down Expand Up @@ -183,7 +183,7 @@ jobs:
strategy:
matrix:
arch: [AMD64]
pl_backend: ["lightning_qubit"]
pl_backend: ["lightning_kokkos", "lightning_qubit"]
runs-on: ubuntu-latest

if: ${{ github.event_name == 'release' || github.ref == 'refs/heads/master'}}
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ format-python:
.PHONY: check-tidy
check-tidy:
rm -rf ./BuildTidy
cmake -BBuildTidy -DENABLE_CLANG_TIDY=ON -DBUILD_TESTS=ON -DENABLE_WARNINGS=ON -DPL_BACKEND=$(PL_BACKEND) $(OPTIONS)
cmake -BBuildTidy -DENABLE_CLANG_TIDY=ON -DBUILD_TESTS=ON -DENABLE_WARNINGS=ON -DCLANG_TIDY_BINARY=clang-tidy -DPL_BACKEND=$(PL_BACKEND) $(OPTIONS)
ifdef target
cmake --build ./BuildTidy $(VERBOSE) --target $(target)
else
Expand Down
43 changes: 35 additions & 8 deletions pennylane_lightning/core/_serialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,15 @@ def serialize_observables(self, tape: QuantumTape, wires_map: dict) -> List:

def serialize_ops(
self, tape: QuantumTape, wires_map: dict
) -> Tuple[List[List[str]], List[np.ndarray], List[List[int]], List[bool], List[np.ndarray]]:
) -> Tuple[
List[List[str]],
List[np.ndarray],
List[List[int]],
List[bool],
List[np.ndarray],
List[List[int]],
List[List[bool]],
]:
"""Serializes the operations of an input tape.
The state preparation operations are not included.
Expand All @@ -313,30 +321,34 @@ def serialize_ops(
names = []
params = []
controlled_wires = []
controlled_values = []
wires = []
mats = []

uses_stateprep = False

def get_wires(operation, single_op):
if operation.name[0:2] == "C(" or (
operation.name == "MultiControlledX"
and all(char == "1" for char in operation.hyperparameters["control_values"])
):
if operation.name[0:2] == "C(" or operation.name == "MultiControlledX":
name = "PauliX" if operation.name == "MultiControlledX" else operation.base.name
controlled_wires_list = operation.control_wires
if operation.name == "MultiControlledX":
wires_list = list(set(operation.wires) - set(controlled_wires_list))
else:
wires_list = operation.target_wires
control_values_list = (
[bool(int(i)) for i in operation.hyperparameters["control_values"]]
if operation.name == "MultiControlledX"
else operation.control_values
)
if not hasattr(self.sv_type, name):
single_op = QubitUnitary(matrix(single_op.base), single_op.base.wires)
name = single_op.name
else:
name = single_op.name
wires_list = single_op.wires.tolist()
controlled_wires_list = []
return single_op, name, wires_list, controlled_wires_list
control_values_list = []
return single_op, name, wires_list, controlled_wires_list, control_values_list

for operation in tape.operations:
if isinstance(operation, (BasisState, StatePrep)):
Expand All @@ -348,7 +360,13 @@ def get_wires(operation, single_op):
op_list = [operation]

for single_op in op_list:
single_op, name, wires_list, controlled_wires_list = get_wires(operation, single_op)
(
single_op,
name,
wires_list,
controlled_wires_list,
controlled_values_list,
) = get_wires(operation, single_op)
names.append(name)
# QubitUnitary is a special case, it has a parameter which is not differentiable.
# We thus pass a dummy 0.0 parameter which will not be referenced
Expand All @@ -362,8 +380,17 @@ def get_wires(operation, single_op):
params.append(single_op.parameters)
mats.append([])

controlled_values.append(controlled_values_list)
controlled_wires.append([wires_map[w] for w in controlled_wires_list])
wires.append([wires_map[w] for w in wires_list])

inverses = [False] * len(names)
return (names, params, wires, inverses, mats, controlled_wires), uses_stateprep
return (
names,
params,
wires,
inverses,
mats,
controlled_wires,
controlled_values,
), uses_stateprep
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.34.0-dev18"
__version__ = "0.34.0-dev19"
20 changes: 13 additions & 7 deletions pennylane_lightning/core/src/algorithms/AdjointJacobianBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,14 @@ template <class StateVectorT, class Derived> class AdjointJacobianBase {
operations.getOpsParams()[op_idx],
operations.getOpsMatrices()[op_idx]);
} else {
state.applyOperation(operations.getOpsName()[op_idx],
operations.getOpsControlledWires()[op_idx],
operations.getOpsWires()[op_idx],
operations.getOpsInverses()[op_idx] ^ adj,
operations.getOpsParams()[op_idx],
operations.getOpsMatrices()[op_idx]);
state.applyOperation(
operations.getOpsName()[op_idx],
operations.getOpsControlledWires()[op_idx],
operations.getOpsControlledValues()[op_idx],
operations.getOpsWires()[op_idx],
operations.getOpsInverses()[op_idx] ^ adj,
operations.getOpsParams()[op_idx],
operations.getOpsMatrices()[op_idx]);
}
}
}
Expand All @@ -94,6 +96,7 @@ template <class StateVectorT, class Derived> class AdjointJacobianBase {
} else {
state.applyOperation(operations.getOpsName()[op_idx],
operations.getOpsControlledWires()[op_idx],
operations.getOpsControlledValues()[op_idx],
operations.getOpsWires()[op_idx],
!operations.getOpsInverses()[op_idx],
operations.getOpsParams()[op_idx],
Expand Down Expand Up @@ -141,15 +144,18 @@ template <class StateVectorT, class Derived> class AdjointJacobianBase {
* @param sv Statevector data to operate upon.
* @param op_name Name of parametric gate.
* @param controlled_wires Control wires.
* @param controlled_values Control values (false or true).
* @param wires Wires to operate upon.
* @param adj Indicate whether to take the adjoint of the operation.
* @return PrecisionT Generator scaling coefficient.
*/
inline auto applyGenerator(StateVectorT &sv, const std::string &op_name,
const std::vector<size_t> &controlled_wires,
const std::vector<bool> &controlled_values,
const std::vector<size_t> &wires, const bool adj)
-> PrecisionT {
return sv.applyGenerator(op_name, controlled_wires, wires, adj);
return sv.applyGenerator(op_name, controlled_wires, controlled_values,
wires, adj);
}

/**
Expand Down
23 changes: 19 additions & 4 deletions pennylane_lightning/core/src/algorithms/JacobianData.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ template <class StateVectorT> class OpsData {
const std::vector<bool> ops_inverses_;
const std::vector<std::vector<ComplexT>> ops_matrices_;
const std::vector<std::vector<size_t>> ops_controlled_wires_;
const std::vector<std::vector<bool>> ops_controlled_values_;

public:
/**
Expand All @@ -64,18 +65,21 @@ template <class StateVectorT> class OpsData {
* @param ops_matrices Numerical representation of given matrix if not
* supported.
* @param ops_controlled_wires Control wires
* @param ops_controlled_wires Control values
*/
OpsData(std::vector<std::string> ops_name,
const std::vector<std::vector<PrecisionT>> &ops_params,
std::vector<std::vector<size_t>> ops_wires,
std::vector<bool> ops_inverses,
std::vector<std::vector<ComplexT>> ops_matrices,
std::vector<std::vector<size_t>> ops_controlled_wires)
std::vector<std::vector<size_t>> ops_controlled_wires,
std::vector<std::vector<bool>> ops_controlled_values)
: num_par_ops_{0}, ops_name_{std::move(ops_name)},
ops_params_{ops_params}, ops_wires_{std::move(ops_wires)},
ops_inverses_{std::move(ops_inverses)},
ops_matrices_{std::move(ops_matrices)},
ops_controlled_wires_{std::move(ops_controlled_wires)} {
ops_controlled_wires_{std::move(ops_controlled_wires)},
ops_controlled_values_{std::move(ops_controlled_values)} {
for (const auto &p : ops_params) {
num_par_ops_ += static_cast<size_t>(!p.empty());
}
Expand Down Expand Up @@ -103,7 +107,8 @@ template <class StateVectorT> class OpsData {
ops_params_{ops_params}, ops_wires_{std::move(ops_wires)},
ops_inverses_{std::move(ops_inverses)},
ops_matrices_{std::move(ops_matrices)},
ops_controlled_wires_(ops_name.size()) {
ops_controlled_wires_(ops_name.size()),
ops_controlled_values_(ops_name.size()) {
for (const auto &p : ops_params) {
num_par_ops_ += static_cast<size_t>(!p.empty());
}
Expand All @@ -128,7 +133,8 @@ template <class StateVectorT> class OpsData {
ops_wires_{std::move(ops_wires)},
ops_inverses_{std::move(ops_inverses)},
ops_matrices_(ops_name.size()),
ops_controlled_wires_(ops_name.size()) {
ops_controlled_wires_(ops_name.size()),
ops_controlled_values_(ops_name.size()) {
for (const auto &p : ops_params) {
num_par_ops_ += static_cast<size_t>(!p.empty());
}
Expand Down Expand Up @@ -178,6 +184,15 @@ template <class StateVectorT> class OpsData {
-> const std::vector<std::vector<size_t>> & {
return ops_controlled_wires_;
}
/**
* @brief Get the controlled values for each operation.
*
* @return const std::vector<std::vector<size_t>>&
*/
[[nodiscard]] auto getOpsControlledValues() const
-> const std::vector<std::vector<bool>> & {
return ops_controlled_values_;
}
/**
* @brief Get the adjoint flag for each operation.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,11 @@ template <typename TypeList> void testAdjointJacobian() {
std::vector<std::vector<size_t>> controls{
std::vector<size_t>(num_qubits - 1)};
std::iota(controls[0].begin(), controls[0].end(), 0);
auto ops = OpsData<StateVectorT>({"PhaseShift"}, {{p}},
{{num_qubits - 1}},
{false}, {{}}, controls);
std::vector<std::vector<bool>> control_values{
std::vector<bool>(num_qubits - 1, true)};
auto ops = OpsData<StateVectorT>(
{"PhaseShift"}, {{p}}, {{num_qubits - 1}}, {false},
{{}}, controls, control_values);

std::vector<ComplexT> cdata(1U << num_qubits);
cdata[cdata.size() - 2] =
Expand Down Expand Up @@ -411,7 +413,8 @@ template <typename TypeList> void testAdjointJacobian() {
std::vector<std::vector<ComplexT>>{
{}, {}, {}, cnot, {}, {}, {}, {}},
std::vector<std::vector<size_t>>{
{}, {}, {}, {}, {}, {}, {}, {}});
{}, {}, {}, {}, {}, {}, {}, {}},
std::vector<std::vector<bool>>{{}, {}, {}, {}, {}, {}, {}, {}});

JacobianData<StateVectorT> tape{
num_params, psi.getLength(), psi.getData(), {obs}, ops, tp};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,8 @@ template <typename TypeList> void testAdjointJacobian() {
std::vector<std::vector<ComplexT>>{
{}, {}, {}, cnot, {}, {}, {}, {}},
std::vector<std::vector<size_t>>{
{}, {}, {}, {}, {}, {}, {}, {}});
{}, {}, {}, {}, {}, {}, {}, {}},
std::vector<std::vector<bool>>{{}, {}, {}, {}, {}, {}, {}, {}});

JacobianDataMPI<StateVectorT> tape{num_params, psi, {obs}, ops, tp};

Expand Down
18 changes: 13 additions & 5 deletions pennylane_lightning/core/src/bindings/Bindings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,8 @@ void registerBackendAgnosticAlgorithms(py::module_ &m) {
const std::vector<std::vector<size_t>> &,
const std::vector<bool> &,
const std::vector<std::vector<ComplexT>> &,
const std::vector<std::vector<size_t>> &>())
const std::vector<std::vector<size_t>> &,
const std::vector<std::vector<bool>> &>())
.def("__repr__", [](const OpsData<StateVectorT> &ops) {
using namespace Pennylane::Util;
std::ostringstream ops_stream;
Expand All @@ -535,6 +536,8 @@ void registerBackendAgnosticAlgorithms(py::module_ &m) {
ops_stream << ", 'inv': " << ops.getOpsInverses()[op];
ops_stream << ", 'controlled_wires': "
<< ops.getOpsControlledWires()[op];
ops_stream << ", 'controlled_values': "
<< ops.getOpsControlledValues()[op];
ops_stream << ", 'wires': " << ops.getOpsWires()[op];
ops_stream << "}";
if (op < ops.getSize() - 1) {
Expand All @@ -555,7 +558,8 @@ void registerBackendAgnosticAlgorithms(py::module_ &m) {
const std::vector<std::vector<size_t>> &ops_wires,
const std::vector<bool> &ops_inverses,
const std::vector<np_arr_c> &ops_matrices,
const std::vector<std::vector<size_t>> &ops_controlled_wires) {
const std::vector<std::vector<size_t>> &ops_controlled_wires,
const std::vector<std::vector<bool>> &ops_controlled_values) {
std::vector<std::vector<ComplexT>> conv_matrices(
ops_matrices.size());
for (size_t op = 0; op < ops_name.size(); op++) {
Expand All @@ -567,9 +571,13 @@ void registerBackendAgnosticAlgorithms(py::module_ &m) {
std::vector<ComplexT>{m_ptr, m_ptr + m_buffer.size};
}
}
return OpsData<StateVectorT>{ops_name, ops_params,
ops_wires, ops_inverses,
conv_matrices, ops_controlled_wires};
return OpsData<StateVectorT>{ops_name,
ops_params,
ops_wires,
ops_inverses,
conv_matrices,
ops_controlled_wires,
ops_controlled_values};
},
"Create a list of operations from data.");

Expand Down
13 changes: 9 additions & 4 deletions pennylane_lightning/core/src/bindings/BindingsMPI.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,8 @@ void registerBackendAgnosticAlgorithmsMPI(py::module_ &m) {
const std::vector<std::vector<size_t>> &ops_wires,
const std::vector<bool> &ops_inverses,
const std::vector<np_arr_c> &ops_matrices,
const std::vector<std::vector<size_t>> &ops_controlled_wires) {
const std::vector<std::vector<size_t>> &ops_controlled_wires,
const std::vector<std::vector<bool>> &ops_controlled_values) {
std::vector<std::vector<ComplexT>> conv_matrices(
ops_matrices.size());
for (size_t op = 0; op < ops_name.size(); op++) {
Expand All @@ -385,9 +386,13 @@ void registerBackendAgnosticAlgorithmsMPI(py::module_ &m) {
std::vector<ComplexT>{m_ptr, m_ptr + m_buffer.size};
}
}
return OpsData<StateVectorT>{ops_name, ops_params,
ops_wires, ops_inverses,
conv_matrices, ops_controlled_wires};
return OpsData<StateVectorT>{ops_name,
ops_params,
ops_wires,
ops_inverses,
conv_matrices,
ops_controlled_wires,
ops_controlled_values};
},
"Create a list of operations from data.");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,11 @@ template <typename TypeList> void testApplyOperations() {
reinterpret_cast<ComplexT *>(st_data_2.data()),
st_data_2.size());

state_vector_1.applyOperation("PauliX", {}, {0}, false);
state_vector_1.applyOperation("PauliY", {}, {1}, false);
state_vector_1.applyOperation("PauliX", {}, {}, {0}, false);
state_vector_1.applyOperation("PauliY", {}, {}, {1}, false);

state_vector_2.applyOperation("PauliX", {}, {0}, false);
state_vector_2.applyOperation("PauliY", {}, {1}, false);
state_vector_2.applyOperation("PauliX", {}, {}, {0}, false);
state_vector_2.applyOperation("PauliY", {}, {}, {1}, false);

REQUIRE(isApproxEqual(
state_vector_1.getData(), state_vector_1.getLength(),
Expand Down
Loading

0 comments on commit 5427421

Please sign in to comment.