Program Listing for File GateImplementationsAVXCommon.hpp¶
↰ Return to documentation for file (pennylane_lightning/core/src/simulators/lightning_qubit/gates/cpu_kernels/GateImplementationsAVXCommon.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 "avx_common/AVXGateKernels.hpp"
#include "avx_common/SingleQubitGateHelper.hpp"
#include "avx_common/TwoQubitGateHelper.hpp"
#include "Error.hpp"
#include "GateImplementationsLM.hpp"
#include "GateOperation.hpp"
#include "GatePragmas.hpp"
#include "Gates.hpp"
#include "KernelType.hpp"
#include "LinearAlgebra.hpp"
#include <immintrin.h>
#include <complex>
#include <vector>
namespace {
using Pennylane::Gates::GateOperation;
using Pennylane::Gates::getRot;
} // namespace
namespace Pennylane::LightningQubit::Gates {
template <class Derived>
class GateImplementationsAVXCommon
: public PauliGenerator<GateImplementationsAVXCommon<Derived>> {
public:
constexpr static std::array implemented_gates = {
GateOperation::PauliX, GateOperation::PauliY,
GateOperation::PauliZ, GateOperation::Hadamard,
GateOperation::S, GateOperation::T,
GateOperation::PhaseShift, GateOperation::RX,
GateOperation::RY, GateOperation::RZ,
GateOperation::Rot, GateOperation::CNOT,
GateOperation::CZ, GateOperation::SWAP,
GateOperation::IsingXX, GateOperation::IsingYY,
GateOperation::IsingZZ, GateOperation::CY,
GateOperation::IsingXY, GateOperation::ControlledPhaseShift,
GateOperation::CRY, GateOperation::CRZ,
GateOperation::CRX,
/* CRot */
};
constexpr static std::array implemented_generators = {
GeneratorOperation::PhaseShift, GeneratorOperation::RX,
GeneratorOperation::RY, GeneratorOperation::RZ,
GeneratorOperation::IsingXX, GeneratorOperation::IsingYY,
GeneratorOperation::IsingZZ,
/* IsingXY, CRX, CRY, CRZ, ControllPhaseShift */
};
constexpr static std::array<ControlledGateOperation, 0>
implemented_controlled_gates{};
constexpr static std::array<ControlledGeneratorOperation, 0>
implemented_controlled_generators{};
constexpr static std::array<ControlledMatrixOperation, 0>
implemented_controlled_matrices{};
template <class PrecisionT>
static void applyPauliX(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse) {
using ApplyPauliXAVX =
AVXCommon::ApplyPauliX<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 1);
auto helper =
AVXCommon::SingleQubitGateWithoutParamHelper<ApplyPauliXAVX>(
&GateImplementationsLM::applyPauliX);
helper(arr, num_qubits, wires, inverse);
}
template <class PrecisionT>
static void applyPauliY(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse) {
using ApplyPauliYAVX =
AVXCommon::ApplyPauliY<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 1);
auto helper =
AVXCommon::SingleQubitGateWithoutParamHelper<ApplyPauliYAVX>(
&GateImplementationsLM::applyPauliY);
helper(arr, num_qubits, wires, inverse);
}
template <class PrecisionT>
static void applyPauliZ(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse) {
using ApplyPauliZAVX =
AVXCommon::ApplyPauliZ<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 1);
auto helper =
AVXCommon::SingleQubitGateWithoutParamHelper<ApplyPauliZAVX>(
&GateImplementationsLM::applyPauliZ);
helper(arr, num_qubits, wires, inverse);
}
template <class PrecisionT>
static void applyS(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse) {
using ApplySAVX = AVXCommon::ApplyS<PrecisionT, Derived::packed_bytes /
sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
auto helper = AVXCommon::SingleQubitGateWithoutParamHelper<ApplySAVX>(
&GateImplementationsLM::applyS);
helper(arr, num_qubits, wires, inverse);
}
template <class PrecisionT>
static void applyT(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse) {
using ApplyTAVX = AVXCommon::ApplyT<PrecisionT, Derived::packed_bytes /
sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 1);
auto helper = AVXCommon::SingleQubitGateWithoutParamHelper<ApplyTAVX>(
&GateImplementationsLM::applyT);
helper(arr, num_qubits, wires, inverse);
}
template <class PrecisionT, class ParamT = PrecisionT>
static void applyPhaseShift(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
bool inverse, ParamT angle) {
using ApplyPhaseShiftAVX =
AVXCommon::ApplyPhaseShift<PrecisionT, Derived::packed_bytes /
sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 1);
auto helper =
AVXCommon::SingleQubitGateWithParamHelper<ApplyPhaseShiftAVX,
ParamT>(
&GateImplementationsLM::applyPhaseShift);
helper(arr, num_qubits, wires, inverse, angle);
}
template <class PrecisionT>
static void applyHadamard(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse) {
using ApplyHadamardAVX =
AVXCommon::ApplyHadamard<PrecisionT, Derived::packed_bytes /
sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 1);
auto helper =
AVXCommon::SingleQubitGateWithoutParamHelper<ApplyHadamardAVX>(
&GateImplementationsLM::applyHadamard);
helper(arr, num_qubits, wires, inverse);
}
template <class PrecisionT, class ParamT = PrecisionT>
static void
applyRX(std::complex<PrecisionT> *arr, const std::size_t num_qubits,
const std::vector<std::size_t> &wires, bool inverse, ParamT angle) {
using ApplyRXAVX =
AVXCommon::ApplyRX<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 1);
auto helper =
AVXCommon::SingleQubitGateWithParamHelper<ApplyRXAVX, ParamT>(
&GateImplementationsLM::applyRX);
helper(arr, num_qubits, wires, inverse, angle);
}
template <class PrecisionT, class ParamT = PrecisionT>
static void
applyRY(std::complex<PrecisionT> *arr, const std::size_t num_qubits,
const std::vector<std::size_t> &wires, bool inverse, ParamT angle) {
using ApplyRYAVX =
AVXCommon::ApplyRY<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 1);
auto helper =
AVXCommon::SingleQubitGateWithParamHelper<ApplyRYAVX, ParamT>(
&GateImplementationsLM::applyRY);
helper(arr, num_qubits, wires, inverse, angle);
}
template <class PrecisionT, class ParamT = PrecisionT>
static void
applyRZ(std::complex<PrecisionT> *arr, const std::size_t num_qubits,
const std::vector<std::size_t> &wires, bool inverse, ParamT angle) {
using ApplyRZAVX =
AVXCommon::ApplyRZ<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 1);
auto helper =
AVXCommon::SingleQubitGateWithParamHelper<ApplyRZAVX, ParamT>(
&GateImplementationsLM::applyRZ);
helper(arr, num_qubits, wires, inverse, angle);
}
template <class PrecisionT, class ParamT = PrecisionT>
static void applyRot(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires, bool inverse,
ParamT phi, ParamT theta, ParamT omega) {
PL_ASSERT(wires.size() == 1);
const auto rotMat =
(inverse) ? getRot<std::complex, PrecisionT>(-omega, -theta, -phi)
: getRot<std::complex, PrecisionT>(phi, theta, omega);
Derived::applySingleQubitOp(arr, num_qubits, rotMat.data(), wires);
}
/* Two-qubit gates*/
template <class PrecisionT>
static void applyCZ(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse) {
using ApplyCZAVX =
AVXCommon::ApplyCZ<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 2);
const AVXCommon::TwoQubitGateWithoutParamHelper<ApplyCZAVX> gate_helper(
&GateImplementationsLM::applyCZ<PrecisionT>);
gate_helper(arr, num_qubits, wires, inverse);
}
template <class PrecisionT>
static void applyCY(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse) {
using ApplyCYAVX =
AVXCommon::ApplyCY<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 2);
const AVXCommon::TwoQubitGateWithoutParamHelper<ApplyCYAVX> gate_helper(
&GateImplementationsLM::applyCY<PrecisionT>);
gate_helper(arr, num_qubits, wires, inverse);
}
template <class PrecisionT>
static void applySWAP(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse) {
using ApplySWAPAVX =
AVXCommon::ApplySWAP<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 2);
const AVXCommon::TwoQubitGateWithoutParamHelper<ApplySWAPAVX>
gate_helper(&GateImplementationsLM::applySWAP<PrecisionT>);
gate_helper(arr, num_qubits, wires, inverse);
}
template <class PrecisionT>
static void applyCNOT(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse) {
PL_ASSERT(wires.size() == 2);
using ApplyCNOTAVX =
AVXCommon::ApplyCNOT<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(
AVXCommon::AsymmetricTwoQubitGateWithoutParam<ApplyCNOTAVX>);
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 2);
const AVXCommon::TwoQubitGateWithoutParamHelper<ApplyCNOTAVX>
gate_helper(&GateImplementationsLM::applyCNOT<PrecisionT>);
gate_helper(arr, num_qubits, wires, inverse);
}
template <class PrecisionT, class ParamT = PrecisionT>
static void applyIsingXX(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse, ParamT angle) {
PL_ASSERT(wires.size() == 2);
using ApplyIsingXXAVX =
AVXCommon::ApplyIsingXX<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
const AVXCommon::TwoQubitGateWithParamHelper<ApplyIsingXXAVX, ParamT>
gate_helper(
&GateImplementationsLM::applyIsingXX<PrecisionT, ParamT>);
gate_helper(arr, num_qubits, wires, inverse, angle);
}
template <class PrecisionT, class ParamT = PrecisionT>
static void applyIsingXY(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse, ParamT angle) {
PL_ASSERT(wires.size() == 2);
using ApplyIsingXYAVX =
AVXCommon::ApplyIsingXY<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
const AVXCommon::TwoQubitGateWithParamHelper<ApplyIsingXYAVX, ParamT>
gate_helper(
&GateImplementationsLM::applyIsingXY<PrecisionT, ParamT>);
gate_helper(arr, num_qubits, wires, inverse, angle);
}
template <class PrecisionT, class ParamT = PrecisionT>
static void applyIsingYY(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse, ParamT angle) {
PL_ASSERT(wires.size() == 2);
using ApplyIsingYYAVX =
AVXCommon::ApplyIsingYY<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
const AVXCommon::TwoQubitGateWithParamHelper<ApplyIsingYYAVX, ParamT>
gate_helper(
&GateImplementationsLM::applyIsingYY<PrecisionT, ParamT>);
gate_helper(arr, num_qubits, wires, inverse, angle);
}
template <class PrecisionT, class ParamT = PrecisionT>
static void applyIsingZZ(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse, ParamT angle) {
using ApplyIsingZZAVX =
AVXCommon::ApplyIsingZZ<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 2);
const AVXCommon::TwoQubitGateWithParamHelper<ApplyIsingZZAVX, ParamT>
gate_helper(
&GateImplementationsLM::applyIsingZZ<PrecisionT, ParamT>);
gate_helper(arr, num_qubits, wires, inverse, angle);
}
template <class PrecisionT, class ParamT = PrecisionT>
static void applyControlledPhaseShift(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse,
ParamT angle) {
using ApplyControlledPhaseShiftAVX =
AVXCommon::ApplyControlledPhaseShift<
PrecisionT, Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 2);
const AVXCommon::TwoQubitGateWithParamHelper<
ApplyControlledPhaseShiftAVX, ParamT>
gate_helper(
&GateImplementationsLM::applyControlledPhaseShift<PrecisionT,
ParamT>);
gate_helper(arr, num_qubits, wires, inverse, angle);
}
template <class PrecisionT, class ParamT = PrecisionT>
static void applyCRX(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires, bool inverse,
ParamT angle) {
using ApplyCRXAVX =
AVXCommon::ApplyCRX<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 2);
const AVXCommon::TwoQubitGateWithParamHelper<ApplyCRXAVX, ParamT>
gate_helper(&GateImplementationsLM::applyCRX<PrecisionT, ParamT>);
gate_helper(arr, num_qubits, wires, inverse, angle);
}
template <class PrecisionT, class ParamT = PrecisionT>
static void applyCRY(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires, bool inverse,
ParamT angle) {
using ApplyCRYAVX =
AVXCommon::ApplyCRY<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 2);
const AVXCommon::TwoQubitGateWithParamHelper<ApplyCRYAVX, ParamT>
gate_helper(&GateImplementationsLM::applyCRY<PrecisionT, ParamT>);
gate_helper(arr, num_qubits, wires, inverse, angle);
}
template <class PrecisionT, class ParamT = PrecisionT>
static void applyCRZ(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires, bool inverse,
ParamT angle) {
using ApplyCRZAVX =
AVXCommon::ApplyCRZ<PrecisionT,
Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 2);
const AVXCommon::TwoQubitGateWithParamHelper<ApplyCRZAVX, ParamT>
gate_helper(&GateImplementationsLM::applyCRZ<PrecisionT, ParamT>);
gate_helper(arr, num_qubits, wires, inverse, angle);
}
/* Generators */
template <class PrecisionT>
static auto applyGeneratorPhaseShift(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse)
-> PrecisionT {
using ApplyGeneratorPhaseShiftAVX = AVXCommon::ApplyGeneratorPhaseShift<
PrecisionT, Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 1);
const AVXCommon::SingleQubitGateWithoutParamHelper<
ApplyGeneratorPhaseShiftAVX>
gate_helper(
&GateImplementationsLM::applyGeneratorPhaseShift<PrecisionT>);
return gate_helper(arr, num_qubits, wires, inverse);
}
template <class PrecisionT>
static auto applyGeneratorIsingXX(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse)
-> PrecisionT {
using ApplyGeneratorIsingXXAVX = AVXCommon::ApplyGeneratorIsingXX<
PrecisionT, Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 2);
const AVXCommon::TwoQubitGateWithoutParamHelper<
ApplyGeneratorIsingXXAVX>
gate_helper(
&GateImplementationsLM::applyGeneratorIsingXX<PrecisionT>);
return gate_helper(arr, num_qubits, wires, inverse);
}
template <class PrecisionT>
static auto applyGeneratorIsingYY(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse)
-> PrecisionT {
using ApplyGeneratorIsingYYAVX = AVXCommon::ApplyGeneratorIsingYY<
PrecisionT, Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 2);
const AVXCommon::TwoQubitGateWithoutParamHelper<
ApplyGeneratorIsingYYAVX>
gate_helper(
&GateImplementationsLM::applyGeneratorIsingYY<PrecisionT>);
return gate_helper(arr, num_qubits, wires, inverse);
}
template <class PrecisionT>
static auto applyGeneratorIsingZZ(std::complex<PrecisionT> *arr,
const std::size_t num_qubits,
const std::vector<std::size_t> &wires,
[[maybe_unused]] bool inverse)
-> PrecisionT {
using ApplyGeneratorIsingZZAVX = AVXCommon::ApplyGeneratorIsingZZ<
PrecisionT, Derived::packed_bytes / sizeof(PrecisionT)>;
static_assert(std::is_same_v<PrecisionT, float> ||
std::is_same_v<PrecisionT, double>,
"Only float and double are supported.");
PL_ASSERT(wires.size() == 2);
const AVXCommon::TwoQubitGateWithoutParamHelper<
ApplyGeneratorIsingZZAVX>
gate_helper(
&GateImplementationsLM::applyGeneratorIsingZZ<PrecisionT>);
return gate_helper(arr, num_qubits, wires, inverse);
}
};
} // namespace Pennylane::LightningQubit::Gates
api/program_listing_file_pennylane_lightning_core_src_simulators_lightning_qubit_gates_cpu_kernels_GateImplementationsAVXCommon.hpp
Download Python script
Download Notebook
View on GitHub