Program Listing for File DynamicDispatcher.hpp¶
↰ Return to documentation for file (pennylane_lightning/core/src/simulators/lightning_qubit/gates/DynamicDispatcher.hpp
)
// 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.
#pragma once
#include <complex>
#include <functional>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <variant>
#include <vector>
#include "Constant.hpp"
#include "ConstantUtil.hpp" // lookup
#include "Error.hpp"
#include "GateIndices.hpp"
#include "KernelType.hpp"
#include "Macros.hpp"
#include "OpToMemberFuncPtr.hpp"
#include "Util.hpp" // PairHash, exp2
namespace {
namespace GateConstant = Pennylane::Gates::Constant;
using Pennylane::Gates::GateOperation;
using Pennylane::Gates::GeneratorOperation;
using Pennylane::Gates::KernelType;
using Pennylane::Gates::MatrixOperation;
using Pennylane::Util::exp2;
using Pennylane::Util::lookup;
using Pennylane::Util::PairHash;
} // namespace
namespace Pennylane::LightningQubit::Internal {
constexpr auto generatorNamesWithoutPrefix() {
namespace GateConstant = Pennylane::Gates::Constant;
std::array<std::pair<GeneratorOperation, std::string_view>,
GateConstant::generator_names.size()>
res{};
for (size_t i = 0; i < GateConstant::generator_names.size(); i++) {
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-constant-array-index)
const auto [gntr_op, gntr_name] = GateConstant::generator_names[i];
res[i].first = gntr_op;
res[i].second = gntr_name.substr(0);
// NOLINTEND(cppcoreguidelines-pro-bounds-constant-array-index)
}
return res;
}
} // namespace Pennylane::LightningQubit::Internal
namespace Pennylane::LightningQubit {
template <typename PrecisionT> class DynamicDispatcher {
public:
using CFP_t = std::complex<PrecisionT>;
using GateFunc = std::function<void(
std::complex<PrecisionT> * /*data*/, std::size_t /*num_qubits*/,
const std::vector<std::size_t> & /*wires*/, bool /*inverse*/,
const std::vector<PrecisionT> & /*params*/)>;
using ControlledGateFunc = std::function<void(
std::complex<PrecisionT> * /*data*/, 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*/,
const std::vector<PrecisionT> & /*params*/)>;
using GeneratorFunc = Gates::GeneratorFuncPtrT<PrecisionT>;
using ControlledGeneratorFunc =
Gates::ControlledGeneratorFuncPtrT<PrecisionT>;
using MatrixFunc = Gates::MatrixFuncPtrT<PrecisionT>;
using ControlledMatrixFunc = Gates::ControlledMatrixFuncPtrT<PrecisionT>;
private:
std::unordered_map<std::string, GateOperation> str_to_gates_{};
std::unordered_map<std::string, GeneratorOperation> str_to_gntrs_{};
std::unordered_map<std::pair<GateOperation, KernelType>, GateFunc, PairHash>
gate_kernels_{};
std::unordered_map<std::pair<GeneratorOperation, KernelType>, GeneratorFunc,
PairHash>
generator_kernels_{};
std::unordered_map<std::pair<MatrixOperation, KernelType>, MatrixFunc,
PairHash>
matrix_kernels_{};
std::unordered_map<KernelType, std::string> kernel_names_{};
std::unordered_map<std::string, ControlledGateOperation>
str_to_controlled_gates_{};
std::unordered_map<std::string, ControlledGeneratorOperation>
str_to_controlled_gntrs_{};
std::unordered_map<std::pair<ControlledGateOperation, KernelType>,
ControlledGateFunc, PairHash>
controlled_gate_kernels_{};
std::unordered_map<std::pair<ControlledGeneratorOperation, KernelType>,
ControlledGeneratorFunc, PairHash>
controlled_generator_kernels_{};
std::unordered_map<std::pair<ControlledMatrixOperation, KernelType>,
ControlledMatrixFunc, PairHash>
controlled_matrix_kernels_{};
DynamicDispatcher() {
constexpr static auto gntr_names_without_prefix =
Internal::generatorNamesWithoutPrefix();
for (const auto &[gate_op, gate_name] : GateConstant::gate_names) {
str_to_gates_.emplace(gate_name, gate_op);
}
for (const auto &[gntr_op, gntr_name] : gntr_names_without_prefix) {
str_to_gntrs_.emplace(gntr_name, gntr_op);
}
for (const auto &[gate_op, gate_name] :
GateConstant::controlled_gate_names) {
str_to_controlled_gates_.emplace(gate_name, gate_op);
}
for (const auto &[gntr_op, gntr_name] :
GateConstant::controlled_generator_names) {
str_to_controlled_gntrs_.emplace(gntr_name, gntr_op);
}
}
public:
DynamicDispatcher(const DynamicDispatcher &) = delete;
DynamicDispatcher(DynamicDispatcher &&) = delete;
DynamicDispatcher &operator=(const DynamicDispatcher &) = delete;
DynamicDispatcher &operator=(DynamicDispatcher &&) = delete;
~DynamicDispatcher() = default;
static DynamicDispatcher &getInstance() {
static DynamicDispatcher singleton;
return singleton;
}
[[nodiscard]] auto registeredKernels() const -> std::vector<KernelType> {
std::vector<KernelType> kernels;
kernels.reserve(kernel_names_.size());
for (const auto &[kernel, name] : kernel_names_) {
kernels.emplace_back(kernel);
}
return kernels;
}
[[nodiscard]] auto isRegisteredKernel(KernelType kernel) const {
return kernel_names_.contains(kernel);
}
void registerKernelName(KernelType kernel, std::string name) {
kernel_names_.emplace(kernel, std::move(name));
}
[[nodiscard]] auto getKernelName(KernelType kernel) const -> std::string {
return kernel_names_.at(kernel);
}
[[nodiscard]] auto registeredGatesForKernel(KernelType kernel) const
-> std::unordered_set<GateOperation> {
std::unordered_set<GateOperation> gates;
for (const auto &[key, val] : gate_kernels_) {
if (key.second == kernel) {
gates.emplace(key.first);
}
}
return gates;
}
[[nodiscard]] auto
registeredControlledGatesForKernel(KernelType kernel) const
-> std::unordered_set<ControlledGateOperation> {
std::unordered_set<ControlledGateOperation> gates;
for (const auto &[key, val] : controlled_gate_kernels_) {
if (key.second == kernel) {
gates.emplace(key.first);
}
}
return gates;
}
[[nodiscard]] auto registeredGeneratorsForKernel(KernelType kernel) const
-> std::unordered_set<GeneratorOperation> {
std::unordered_set<GeneratorOperation> gntrs;
for (const auto &[key, val] : generator_kernels_) {
if (key.second == kernel) {
gntrs.emplace(key.first);
}
}
return gntrs;
}
[[nodiscard]] auto
registeredControlledGeneratorsForKernel(KernelType kernel) const
-> std::unordered_set<ControlledGeneratorOperation> {
std::unordered_set<ControlledGeneratorOperation> generators;
for (const auto &[key, val] : controlled_generator_kernels_) {
if (key.second == kernel) {
generators.emplace(key.first);
}
}
return generators;
}
[[nodiscard]] auto registeredMatricesForKernel(KernelType kernel) const
-> std::unordered_set<MatrixOperation> {
std::unordered_set<MatrixOperation> matrices;
for (const auto &[key, val] : matrix_kernels_) {
if (key.second == kernel) {
matrices.emplace(key.first);
}
}
return matrices;
}
[[nodiscard]] auto
registeredControlledMatricesForKernel(KernelType kernel) const
-> std::unordered_set<ControlledMatrixOperation> {
std::unordered_set<ControlledMatrixOperation> matrices;
for (const auto &[key, val] : controlled_matrix_kernels_) {
if (key.second == kernel) {
matrices.emplace(key.first);
}
}
return matrices;
}
[[nodiscard]] auto strToGateOp(const std::string &gate_name) const
-> GateOperation {
return str_to_gates_.at(gate_name);
}
[[nodiscard]] auto strToControlledGateOp(const std::string &gate_name) const
-> ControlledGateOperation {
return str_to_controlled_gates_.at(gate_name);
}
[[nodiscard]] auto hasGateOp(const std::string &gate_name) const -> bool {
return str_to_gates_.contains(gate_name);
}
[[nodiscard]] auto strToGeneratorOp(const std::string &gntr_name) const
-> GeneratorOperation {
return str_to_gntrs_.at(gntr_name);
}
[[nodiscard]] auto
strToControlledGeneratorOp(const std::string &gntr_name) const
-> ControlledGeneratorOperation {
return str_to_controlled_gntrs_.at(gntr_name);
}
template <typename FunctionType>
void registerGateOperation(GateOperation gate_op, KernelType kernel,
FunctionType &&func) {
gate_kernels_.emplace(std::make_pair(gate_op, kernel),
std::forward<FunctionType>(func));
}
template <typename FunctionType>
void registerControlledGateOperation(ControlledGateOperation gate_op,
KernelType kernel,
FunctionType &&func) {
controlled_gate_kernels_.emplace(std::make_pair(gate_op, kernel),
std::forward<FunctionType>(func));
}
template <typename FunctionType>
void registerGeneratorOperation(GeneratorOperation gntr_op,
KernelType kernel, FunctionType &&func) {
generator_kernels_.emplace(std::make_pair(gntr_op, kernel),
std::forward<FunctionType>(func));
}
template <typename FunctionType>
void
registerControlledGeneratorOperation(ControlledGeneratorOperation gen_op,
KernelType kernel,
FunctionType &&func) {
controlled_generator_kernels_.emplace(std::make_pair(gen_op, kernel),
std::forward<FunctionType>(func));
}
void registerMatrixOperation(MatrixOperation mat_op, KernelType kernel,
MatrixFunc func) {
matrix_kernels_.emplace(std::make_pair(mat_op, kernel), func);
}
void registerControlledMatrixOperation(ControlledMatrixOperation mat_op,
KernelType kernel,
ControlledMatrixFunc func) {
controlled_matrix_kernels_.emplace(std::make_pair(mat_op, kernel),
func);
}
[[nodiscard]] bool isRegistered(GateOperation gate_op,
KernelType kernel) const {
return gate_kernels_.find(std::make_pair(gate_op, kernel)) !=
gate_kernels_.cend();
}
bool isRegistered(ControlledGateOperation gate_op,
KernelType kernel) const {
return controlled_gate_kernels_.find(std::make_pair(gate_op, kernel)) !=
controlled_gate_kernels_.cend();
}
[[nodiscard]] bool isRegistered(GeneratorOperation gntr_op,
KernelType kernel) const {
return generator_kernels_.find(std::make_pair(gntr_op, kernel)) !=
generator_kernels_.cend();
}
bool isRegistered(ControlledGeneratorOperation gen_op,
KernelType kernel) const {
return controlled_generator_kernels_.find(std::make_pair(
gen_op, kernel)) != controlled_generator_kernels_.cend();
}
[[nodiscard]] bool isRegistered(MatrixOperation mat_op,
KernelType kernel) const {
return matrix_kernels_.find(std::make_pair(mat_op, kernel)) !=
matrix_kernels_.cend();
}
bool isRegistered(ControlledMatrixOperation mat_op,
KernelType kernel) const {
return controlled_matrix_kernels_.find(std::make_pair(
mat_op, kernel)) != controlled_matrix_kernels_.cend();
}
void applyOperation(KernelType kernel, CFP_t *data, std::size_t num_qubits,
const std::string &op_name,
const std::vector<std::size_t> &wires, bool inverse,
const std::vector<PrecisionT> ¶ms = {}) const {
applyOperation(kernel, data, num_qubits, strToGateOp(op_name), wires,
inverse, params);
}
void applyOperation(KernelType kernel, CFP_t *data, std::size_t num_qubits,
GateOperation gate_op,
const std::vector<std::size_t> &wires, bool inverse,
const std::vector<PrecisionT> ¶ms = {}) const {
const auto iter = gate_kernels_.find(std::make_pair(gate_op, kernel));
PL_ABORT_IF(iter == gate_kernels_.cend(),
"Cannot find a registered kernel for a given gate "
"and kernel pair");
(iter->second)(data, num_qubits, wires, inverse, params);
}
void applyControlledGate(KernelType kernel, CFP_t *data,
std::size_t num_qubits, const std::string &op_name,
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<PrecisionT> ¶ms = {}) const {
applyControlledGate(kernel, data, num_qubits,
strToControlledGateOp(op_name), controlled_wires,
controlled_values, wires, inverse, params);
}
void applyControlledGate(KernelType kernel, CFP_t *data,
std::size_t num_qubits,
const ControlledGateOperation op_name,
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<PrecisionT> ¶ms = {}) const {
PL_ABORT_IF_NOT(controlled_wires.size() == controlled_values.size(),
"`controlled_wires` must have the same size as "
"`controlled_values`.");
const auto iter =
controlled_gate_kernels_.find(std::make_pair(op_name, kernel));
PL_ABORT_IF(
iter == controlled_gate_kernels_.cend(),
"Cannot find a registered kernel for a given controlled gate "
"and kernel pair");
(iter->second)(data, num_qubits, controlled_wires, controlled_values,
wires, inverse, params);
}
void
applyOperations(KernelType kernel, CFP_t *data, std::size_t num_qubits,
const std::vector<std::string> &ops,
const std::vector<std::vector<std::size_t>> &wires,
const std::vector<bool> &inverse,
const std::vector<std::vector<PrecisionT>> ¶ms) const {
const std::size_t numOperations = ops.size();
PL_ABORT_IF(numOperations != wires.size() ||
numOperations != params.size(),
"Invalid arguments: number of operations, wires, and "
"parameters must all be equal");
for (size_t i = 0; i < numOperations; i++) {
applyOperation(kernel, data, num_qubits, ops[i], wires[i],
inverse[i], params[i]);
}
}
void applyOperations(KernelType kernel, CFP_t *data, std::size_t num_qubits,
const std::vector<std::string> &ops,
const std::vector<std::vector<std::size_t>> &wires,
const std::vector<bool> &inverse) const {
const std::size_t numOperations = ops.size();
PL_ABORT_IF(numOperations != wires.size(),
"Invalid arguments: number of operations, wires, and "
"parameters must all be equal");
for (size_t i = 0; i < numOperations; i++) {
applyOperation(kernel, data, num_qubits, ops[i], wires[i],
inverse[i], {});
}
}
void applyMatrix(KernelType kernel, CFP_t *data, std::size_t num_qubits,
const std::complex<PrecisionT> *matrix,
const std::vector<std::size_t> &wires,
bool inverse) const {
PL_ASSERT(num_qubits >= wires.size());
const auto mat_op = [n_wires = wires.size()]() {
switch (n_wires) {
case 1:
return MatrixOperation::SingleQubitOp;
case 2:
return MatrixOperation::TwoQubitOp;
default:
return MatrixOperation::MultiQubitOp;
}
}();
const auto iter = matrix_kernels_.find(std::make_pair(mat_op, kernel));
PL_ABORT_IF(iter == matrix_kernels_.end(),
std::string(lookup(GateConstant::matrix_names, mat_op)) +
" is not registered for the given kernel");
(iter->second)(data, num_qubits, matrix, wires, inverse);
}
void applyMatrix(KernelType kernel, CFP_t *data, std::size_t num_qubits,
const std::vector<std::complex<PrecisionT>> &matrix,
const std::vector<std::size_t> &wires,
bool inverse) const {
PL_ABORT_IF_NOT(matrix.size() == exp2(2 * wires.size()),
"The size of matrix does not match with the given "
"number of wires");
applyMatrix(kernel, data, num_qubits, matrix.data(), wires, inverse);
}
void applyControlledMatrix(KernelType kernel, CFP_t *data,
std::size_t num_qubits,
const std::complex<PrecisionT> *matrix,
const std::vector<std::size_t> &controlled_wires,
const std::vector<bool> &controlled_values,
const std::vector<std::size_t> &wires,
bool inverse) const {
PL_ASSERT(num_qubits >= controlled_wires.size() + wires.size());
PL_ABORT_IF_NOT(controlled_wires.size() == controlled_values.size(),
"`controlled_wires` must have the same size as "
"`controlled_values`.");
const auto mat_op = [n_wires = wires.size()]() {
switch (n_wires) {
case 1:
return ControlledMatrixOperation::NCSingleQubitOp;
case 2:
return ControlledMatrixOperation::NCTwoQubitOp;
default:
return ControlledMatrixOperation::NCMultiQubitOp;
}
}();
const auto iter =
controlled_matrix_kernels_.find(std::make_pair(mat_op, kernel));
PL_ABORT_IF(
iter == controlled_matrix_kernels_.end(),
std::string(lookup(GateConstant::controlled_matrix_names, mat_op)) +
" is not registered for the given kernel");
(iter->second)(data, num_qubits, matrix, controlled_wires,
controlled_values, wires, inverse);
}
auto applyGenerator(KernelType kernel, CFP_t *data, std::size_t num_qubits,
GeneratorOperation gntr_op,
const std::vector<std::size_t> &wires, bool adj) const
-> PrecisionT {
using Pennylane::Gates::Constant::generator_names;
const auto iter =
generator_kernels_.find(std::make_pair(gntr_op, kernel));
PL_ABORT_IF(iter == generator_kernels_.cend(),
"Cannot find a registered kernel for a given generator "
"and kernel pair.");
return (iter->second)(data, num_qubits, wires, adj);
}
auto applyGenerator(KernelType kernel, CFP_t *data, std::size_t num_qubits,
const std::string &op_name,
const std::vector<std::size_t> &wires, bool adj) const
-> PrecisionT {
return applyGenerator(kernel, data, num_qubits,
strToGeneratorOp(op_name), wires, adj);
}
auto
applyControlledGenerator(KernelType kernel, CFP_t *data,
std::size_t num_qubits,
const ControlledGeneratorOperation gntr_op,
const std::vector<std::size_t> &controlled_wires,
const std::vector<bool> &controlled_values,
const std::vector<std::size_t> &wires,
bool inverse) const -> PrecisionT {
using Pennylane::Gates::Constant::controlled_generator_names;
const auto iter =
controlled_generator_kernels_.find(std::make_pair(gntr_op, kernel));
PL_ABORT_IF(iter == controlled_generator_kernels_.cend(),
"Cannot find a registered kernel for a given controlled "
"generator "
"and kernel pair.");
return (iter->second)(data, num_qubits, controlled_wires,
controlled_values, wires, inverse);
}
auto
applyControlledGenerator(KernelType kernel, CFP_t *data,
std::size_t num_qubits, const std::string &op_name,
const std::vector<std::size_t> &controlled_wires,
const std::vector<bool> &controlled_values,
const std::vector<std::size_t> &wires,
bool inverse) const -> PrecisionT {
return applyControlledGenerator(
kernel, data, num_qubits, strToControlledGeneratorOp(op_name),
controlled_wires, controlled_values, wires, inverse);
}
};
} // namespace Pennylane::LightningQubit
namespace Pennylane::LightningQubit::Internal {
int registerAllAvailableKernels_Float();
int registerAllAvailableKernels_Double();
struct RegisterBeforeMain_Float {
const static inline int dummy = registerAllAvailableKernels_Float();
};
struct RegisterBeforeMain_Double {
const static inline int dummy = registerAllAvailableKernels_Double();
};
} // namespace Pennylane::LightningQubit::Internal
api/program_listing_file_pennylane_lightning_core_src_simulators_lightning_qubit_gates_DynamicDispatcher.hpp
Download Python script
Download Notebook
View on GitHub