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 GateImplementationsLM::applyGeneratorDoubleExcitation(Minus/Plus)… #512

Merged
merged 22 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
6 changes: 6 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@

### Breaking changes

* Implement `LM::GeneratorDoubleExcitation`, `LM::GeneratorDoubleExcitationMinus`, `LM::GeneratorDoubleExcitationPlus` kernels. L-Qubit default kernels are now strictly from the `LM` implementation, which requires less memory and is faster for large state vectors.
[(#512)](https://github.com/PennyLaneAI/pennylane-lightning/pull/512)

* Add workflows validating compatibility between PennyLane and Lightning's most recent stable releases and development (latest) versions.
[(#507)](https://github.com/PennyLaneAI/pennylane-lightning/pull/507)
[(#498)](https://github.com/PennyLaneAI/pennylane-lightning/pull/498)
Expand Down Expand Up @@ -56,6 +59,9 @@

### Bug fixes

* Switch most L-Qubit default kernels to `LM`. Add `LM::multiQubitOp` tests, failing when targeting out-of-order wires clustered close to `num_qubits-1`. Fix the `LM::multiQubitOp` kernel implementation by introducing a generic `revWireParity` routine and replacing the `bitswap`-based implementation. Mimic the changes fixing the corresponding `multiQubitOp` and `expval` functors in L-Kokkos.
[(#511)](https://github.com/PennyLaneAI/pennylane-lightning/pull/511)

* Fix RTD builds by removing unsupported `sytem_packages` configuration option.
[(#491)](https://github.com/PennyLaneAI/pennylane-lightning/pull/491)

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.33.0-dev16"
__version__ = "0.33.0-dev17"

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ target_link_libraries(lightning_kokkos_gates_tests INTERFACE Catch2::Catch2
lightning_kokkos_measurements
lightning_kokkos_observables
lightning_kokkos
lightning_kokkos_utils
)

ProcessTestOptions(lightning_kokkos_gates_tests)
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2018-2023 Xanadu Quantum Technologies Inc.

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

// http://www.apache.org/licenses/LICENSE-2.0

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
* @file
* Defines utility functions for Bitwise operations.
*/
#pragma once
#include <Kokkos_Core.hpp>

#include "BitUtil.hpp"

/// @cond DEV
namespace {
using namespace Pennylane::Util;
using KokkosIntVector = Kokkos::View<std::size_t *>;
} // namespace
/// @endcond

namespace Pennylane::LightningKokkos::Util {

constexpr std::size_t one{1};

/**
* @brief Compute the parities and shifts for multi-qubit operations.
*
* @param num_qubits Number of qubits in the state vector.
* @param wires List of target wires.
* @return std::pair<KokkosIntVector, KokkosIntVector> Parities and shifts for
* multi-qubit operations.
*/
inline auto wires2Parity(const std::size_t num_qubits,
const std::vector<std::size_t> &wires)
-> std::pair<KokkosIntVector, KokkosIntVector> {
KokkosIntVector parity;
KokkosIntVector rev_wire_shifts;

std::vector<std::size_t> rev_wires_(wires.size());
std::vector<std::size_t> rev_wire_shifts_(wires.size());
for (std::size_t k = 0; k < wires.size(); k++) {
rev_wires_[k] = (num_qubits - 1) - wires[(wires.size() - 1) - k];
rev_wire_shifts_[k] = (one << rev_wires_[k]);
}
const std::vector<std::size_t> parity_ = revWireParity(rev_wires_);

Kokkos::View<const size_t *, Kokkos::HostSpace,
Kokkos::MemoryTraits<Kokkos::Unmanaged>>
rev_wire_shifts_host(rev_wire_shifts_.data(), rev_wire_shifts_.size());
Kokkos::resize(rev_wire_shifts, rev_wire_shifts_host.size());
Kokkos::deep_copy(rev_wire_shifts, rev_wire_shifts_host);

Kokkos::View<const size_t *, Kokkos::HostSpace,
Kokkos::MemoryTraits<Kokkos::Unmanaged>>
parity_host(parity_.data(), parity_.size());
Kokkos::resize(parity, parity_host.size());
Kokkos::deep_copy(parity, parity_host);

return {parity, rev_wire_shifts};
}

} // namespace Pennylane::LightningKokkos::Util
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,10 @@ void assignKernelsForGateOp_Default() {
/* Three-qubit gates */
instance.assignKernelForOp(GateOperation::Toffoli, all_threading,
all_memory_model, all_qubit_numbers,
KernelType::PI);
KernelType::LM);
instance.assignKernelForOp(GateOperation::CSWAP, all_threading,
all_memory_model, all_qubit_numbers,
KernelType::PI);
KernelType::LM);

/* QChem gates */
instance.assignKernelForOp(GateOperation::SingleExcitation, all_threading,
Expand All @@ -138,13 +138,13 @@ void assignKernelsForGateOp_Default() {
all_qubit_numbers, KernelType::LM);
instance.assignKernelForOp(GateOperation::DoubleExcitation, all_threading,
all_memory_model, all_qubit_numbers,
KernelType::PI);
KernelType::LM);
instance.assignKernelForOp(GateOperation::DoubleExcitationPlus,
all_threading, all_memory_model,
all_qubit_numbers, KernelType::PI);
all_qubit_numbers, KernelType::LM);
instance.assignKernelForOp(GateOperation::DoubleExcitationMinus,
all_threading, all_memory_model,
all_qubit_numbers, KernelType::PI);
all_qubit_numbers, KernelType::LM);

/* Multi-qubit gates */
instance.assignKernelForOp(GateOperation::MultiRZ, all_threading,
Expand Down Expand Up @@ -203,13 +203,13 @@ void assignKernelsForGeneratorOp_Default() {
all_qubit_numbers, KernelType::LM);
instance.assignKernelForOp(GeneratorOperation::DoubleExcitation,
all_threading, all_memory_model,
all_qubit_numbers, KernelType::PI);
all_qubit_numbers, KernelType::LM);
instance.assignKernelForOp(GeneratorOperation::DoubleExcitationPlus,
all_threading, all_memory_model,
all_qubit_numbers, KernelType::PI);
all_qubit_numbers, KernelType::LM);
instance.assignKernelForOp(GeneratorOperation::DoubleExcitationMinus,
all_threading, all_memory_model,
all_qubit_numbers, KernelType::PI);
all_qubit_numbers, KernelType::LM);

instance.assignKernelForOp(GeneratorOperation::MultiRZ, all_threading,
all_memory_model, all_qubit_numbers,
Expand All @@ -226,6 +226,6 @@ void assignKernelsForMatrixOp_Default() {
KernelType::LM);
instance.assignKernelForOp(MatrixOperation::MultiQubitOp, all_threading,
all_memory_model, all_qubit_numbers,
KernelType::PI);
KernelType::LM);
}
} // namespace Pennylane::LightningQubit::KernelMap::Internal
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,157 @@ auto GateImplementationsLM::applyGeneratorSingleExcitationPlus(
return -static_cast<PrecisionT>(0.5);
}

template <class PrecisionT>
auto GateImplementationsLM::applyGeneratorDoubleExcitation(
std::complex<PrecisionT> *arr, std::size_t num_qubits,
const std::vector<std::size_t> &wires, [[maybe_unused]] bool adj)
-> PrecisionT {
using ComplexT = std::complex<PrecisionT>;
PL_ASSERT(wires.size() == 4);

const std::size_t rev_wire0 = num_qubits - wires[3] - 1;
const std::size_t rev_wire1 = num_qubits - wires[2] - 1;
const std::size_t rev_wire2 = num_qubits - wires[1] - 1;
const std::size_t rev_wire3 = num_qubits - wires[0] - 1;

const std::size_t rev_wire0_shift = static_cast<std::size_t>(1U)
<< rev_wire0;
const std::size_t rev_wire1_shift = static_cast<std::size_t>(1U)
<< rev_wire1;
const std::size_t rev_wire2_shift = static_cast<std::size_t>(1U)
<< rev_wire2;
const std::size_t rev_wire3_shift = static_cast<std::size_t>(1U)
<< rev_wire3;

auto parity = revWireParity(rev_wire0, rev_wire1, rev_wire2, rev_wire3);

for (std::size_t k = 0; k < exp2(num_qubits - 4); k++) {

AmintorDusko marked this conversation as resolved.
Show resolved Hide resolved
const std::size_t i0000 =
((k << 4U) & parity[4]) | ((k << 3U) & parity[3]) |
((k << 2U) & parity[2]) | ((k << 1U) & parity[1]) | (k & parity[0]);
const std::size_t i0001 = i0000 | rev_wire0_shift;
const std::size_t i0010 = i0000 | rev_wire1_shift;
const std::size_t i0011 = i0000 | rev_wire1_shift | rev_wire0_shift;
const std::size_t i0100 = i0000 | rev_wire2_shift;
const std::size_t i0101 = i0000 | rev_wire2_shift | rev_wire0_shift;
const std::size_t i0110 = i0000 | rev_wire2_shift | rev_wire1_shift;
const std::size_t i0111 =
i0000 | rev_wire2_shift | rev_wire1_shift | rev_wire0_shift;
const std::size_t i1000 = i0000 | rev_wire3_shift;
const std::size_t i1001 = i0000 | rev_wire3_shift | rev_wire0_shift;
const std::size_t i1010 = i0000 | rev_wire3_shift | rev_wire1_shift;
const std::size_t i1011 =
i0000 | rev_wire3_shift | rev_wire1_shift | rev_wire0_shift;
const std::size_t i1100 = i0000 | rev_wire3_shift | rev_wire2_shift;
const std::size_t i1101 =
i0000 | rev_wire3_shift | rev_wire2_shift | rev_wire0_shift;
const std::size_t i1110 =
i0000 | rev_wire3_shift | rev_wire2_shift | rev_wire1_shift;
const std::size_t i1111 = i0000 | rev_wire3_shift | rev_wire2_shift |
rev_wire1_shift | rev_wire0_shift;

const ComplexT v3 = arr[i0011];
const ComplexT v12 = arr[i1100];
arr[i0000] = ComplexT{};
arr[i0001] = ComplexT{};
arr[i0010] = ComplexT{};
arr[i0011] = v12 * ComplexT{0, -1};
arr[i0100] = ComplexT{};
arr[i0101] = ComplexT{};
arr[i0110] = ComplexT{};
arr[i0111] = ComplexT{};
arr[i1000] = ComplexT{};
arr[i1001] = ComplexT{};
arr[i1010] = ComplexT{};
arr[i1011] = ComplexT{};
arr[i1100] = v3 * ComplexT{0, 1};
arr[i1101] = ComplexT{};
arr[i1110] = ComplexT{};
arr[i1111] = ComplexT{};
}
// NOLINTNEXTLINE(readability - magic - numbers)
return -static_cast<PrecisionT>(0.5);
}

template <class PrecisionT>
auto GateImplementationsLM::applyGeneratorDoubleExcitationMinus(
std::complex<PrecisionT> *arr, std::size_t num_qubits,
const std::vector<std::size_t> &wires, [[maybe_unused]] bool adj)
-> PrecisionT {
using ComplexT = std::complex<PrecisionT>;
PL_ASSERT(wires.size() == 4);

const std::size_t rev_wire0 = num_qubits - wires[3] - 1;
const std::size_t rev_wire1 = num_qubits - wires[2] - 1;
const std::size_t rev_wire2 = num_qubits - wires[1] - 1;
const std::size_t rev_wire3 = num_qubits - wires[0] - 1;

const std::size_t rev_wire0_shift = static_cast<std::size_t>(1U)
<< rev_wire0;
const std::size_t rev_wire1_shift = static_cast<std::size_t>(1U)
<< rev_wire1;
const std::size_t rev_wire2_shift = static_cast<std::size_t>(1U)
<< rev_wire2;
const std::size_t rev_wire3_shift = static_cast<std::size_t>(1U)
<< rev_wire3;

auto parity = revWireParity(rev_wire0, rev_wire1, rev_wire2, rev_wire3);

for (std::size_t k = 0; k < exp2(num_qubits - 4); k++) {
const std::size_t i0000 =
((k << 4U) & parity[4]) | ((k << 3U) & parity[3]) |
((k << 2U) & parity[2]) | ((k << 1U) & parity[1]) | (k & parity[0]);
const std::size_t i0011 = i0000 | rev_wire1_shift | rev_wire0_shift;
const std::size_t i1100 = i0000 | rev_wire3_shift | rev_wire2_shift;

arr[i0011] *= ComplexT{0, 1};
arr[i1100] *= ComplexT{0, -1};
swap(arr[i1100], arr[i0011]);
}
// NOLINTNEXTLINE(readability - magic - numbers)
return -static_cast<PrecisionT>(0.5);
}

template <class PrecisionT>
auto GateImplementationsLM::applyGeneratorDoubleExcitationPlus(
std::complex<PrecisionT> *arr, std::size_t num_qubits,
const std::vector<std::size_t> &wires, [[maybe_unused]] bool adj)
-> PrecisionT {
using ComplexT = std::complex<PrecisionT>;
PL_ASSERT(wires.size() == 4);

const std::size_t rev_wire0 = num_qubits - wires[3] - 1;
const std::size_t rev_wire1 = num_qubits - wires[2] - 1;
const std::size_t rev_wire2 = num_qubits - wires[1] - 1;
const std::size_t rev_wire3 = num_qubits - wires[0] - 1;

const std::size_t rev_wire0_shift = static_cast<std::size_t>(1U)
<< rev_wire0;
const std::size_t rev_wire1_shift = static_cast<std::size_t>(1U)
<< rev_wire1;
const std::size_t rev_wire2_shift = static_cast<std::size_t>(1U)
<< rev_wire2;
const std::size_t rev_wire3_shift = static_cast<std::size_t>(1U)
<< rev_wire3;

auto parity = revWireParity(rev_wire0, rev_wire1, rev_wire2, rev_wire3);

for (std::size_t k = 0; k < exp2(num_qubits - 4); k++) {
const std::size_t i0000 =
((k << 4U) & parity[4]) | ((k << 3U) & parity[3]) |
((k << 2U) & parity[2]) | ((k << 1U) & parity[1]) | (k & parity[0]);
const std::size_t i0011 = i0000 | rev_wire1_shift | rev_wire0_shift;
const std::size_t i1100 = i0000 | rev_wire3_shift | rev_wire2_shift;

arr[i0011] *= ComplexT{0, -1};
arr[i1100] *= ComplexT{0, 1};
swap(arr[i1100], arr[i0011]);
}
// NOLINTNEXTLINE(readability - magic - numbers)
return static_cast<PrecisionT>(0.5);
}

// Explicit instantiation starts
/* Matrix operations */
template void GateImplementationsLM::applySingleQubitOp<float>(
Expand Down Expand Up @@ -571,6 +722,31 @@ template auto GateImplementationsLM::applyGeneratorSingleExcitationPlus<double>(
std::complex<double> *arr, size_t num_qubits,
const std::vector<size_t> &wires, [[maybe_unused]] bool adj) -> double;

template auto GateImplementationsLM::applyGeneratorDoubleExcitation<float>(
std::complex<float> *arr, size_t num_qubits,
const std::vector<size_t> &wires, [[maybe_unused]] bool adj) -> float;

template auto GateImplementationsLM::applyGeneratorDoubleExcitation<double>(
std::complex<double> *arr, size_t num_qubits,
const std::vector<size_t> &wires, [[maybe_unused]] bool adj) -> double;

template auto GateImplementationsLM::applyGeneratorDoubleExcitationMinus<float>(
std::complex<float> *arr, size_t num_qubits,
const std::vector<size_t> &wires, [[maybe_unused]] bool adj) -> float;

template auto
GateImplementationsLM::applyGeneratorDoubleExcitationMinus<double>(
std::complex<double> *arr, size_t num_qubits,
const std::vector<size_t> &wires, [[maybe_unused]] bool adj) -> double;

template auto GateImplementationsLM::applyGeneratorDoubleExcitationPlus<float>(
std::complex<float> *arr, size_t num_qubits,
const std::vector<size_t> &wires, [[maybe_unused]] bool adj) -> float;

template auto GateImplementationsLM::applyGeneratorDoubleExcitationPlus<double>(
std::complex<double> *arr, size_t num_qubits,
const std::vector<size_t> &wires, [[maybe_unused]] bool adj) -> double;

// Explicit instantiations ends

} // namespace Pennylane::LightningQubit::Gates
Loading
Loading