From 364fc459108677a0e23b84e8aed8c77d60cb04ee Mon Sep 17 00:00:00 2001 From: Saeed Bohloul Date: Mon, 20 Nov 2023 18:31:11 -0500 Subject: [PATCH 01/11] Add inplace omp parallel implementaion of expval * Add operations functors in ExpValFunctorsLQubit.hpp * Add method to call functors in Measurement class * Add enum class for functors * Add mapping for operation names to functors --- .../measurements/ExpValFunctorsLQubit.hpp | 210 ++++++++++++++++++ .../measurements/MeasurementsLQubit.hpp | 167 +++++++++++++- 2 files changed, 376 insertions(+), 1 deletion(-) create mode 100644 pennylane_lightning/core/src/simulators/lightning_qubit/measurements/ExpValFunctorsLQubit.hpp diff --git a/pennylane_lightning/core/src/simulators/lightning_qubit/measurements/ExpValFunctorsLQubit.hpp b/pennylane_lightning/core/src/simulators/lightning_qubit/measurements/ExpValFunctorsLQubit.hpp new file mode 100644 index 0000000000..93210226d1 --- /dev/null +++ b/pennylane_lightning/core/src/simulators/lightning_qubit/measurements/ExpValFunctorsLQubit.hpp @@ -0,0 +1,210 @@ +// 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 "BitUtil.hpp" + +/// @cond DEV +namespace { +using namespace Pennylane::Util; +using Pennylane::Util::INVSQRT2; +} // namespace +/// @endcond + +namespace Pennylane::LightningQubit::Functors { + +template struct getExpectationValueIdentityFunctor { + std::complex *arr; + size_t num_qubits; + + getExpectationValueIdentityFunctor( + std::complex *arr_, + [[maybe_unused]] std::size_t num_qubits_, + [[maybe_unused]] const std::vector &wires) { + arr = arr_; + num_qubits = num_qubits_; + } + + inline void operator()(PrecisionT &expval) const { + size_t k; +#if defined(_OPENMP) +#pragma omp parallel for default(none) shared(num_qubits, arr) private(k) \ + reduction(+ : expval) +#endif + for (k = 0; k < exp2(num_qubits - 1); k++) { + expval += real(conj(arr[k] * arr[k])); + } + } +}; + +template struct getExpectationValuePauliXFunctor { + std::complex *arr; + size_t num_qubits; + + size_t rev_wire; + size_t rev_wire_shift; + size_t wire_parity; + size_t wire_parity_inv; + + getExpectationValuePauliXFunctor(std::complex *arr_, + std::size_t num_qubits_, + const std::vector &wires) { + arr = arr_; + num_qubits = num_qubits_; + rev_wire = num_qubits - wires[0] - 1; + rev_wire_shift = (static_cast(1U) << rev_wire); + wire_parity = fillTrailingOnes(rev_wire); + wire_parity_inv = fillLeadingOnes(rev_wire + 1); + } + + inline void operator()(PrecisionT &expval) const { + size_t k, i0, i1; +#if defined(_OPENMP) +#pragma omp parallel for default(none) \ + shared(num_qubits, wire_parity_inv, wire_parity, rev_wire_shift, \ + arr) private(k, i0, i1) reduction(+ : expval) +#endif + for (k = 0; k < exp2(num_qubits - 1); k++) { + i0 = ((k << 1U) & wire_parity_inv) | (wire_parity & k); + i1 = i0 | rev_wire_shift; + + expval += + real(conj(arr[i0]) * arr[i1]) + real(conj(arr[i1]) * arr[i0]); + } + } +}; + +template struct getExpectationValuePauliYFunctor { + std::complex *arr; + size_t num_qubits; + + size_t rev_wire; + size_t rev_wire_shift; + size_t wire_parity; + size_t wire_parity_inv; + + getExpectationValuePauliYFunctor(std::complex *arr_, + std::size_t num_qubits_, + const std::vector &wires) { + arr = arr_; + num_qubits = num_qubits_; + rev_wire = num_qubits - wires[0] - 1; + rev_wire_shift = (static_cast(1U) << rev_wire); + wire_parity = fillTrailingOnes(rev_wire); + wire_parity_inv = fillLeadingOnes(rev_wire + 1); + } + + inline void operator()(PrecisionT &expval) const { + size_t k, i0, i1; + std::complex v0, v1; +#if defined(_OPENMP) +#pragma omp parallel for default(none) \ + shared(num_qubits, wire_parity_inv, wire_parity, rev_wire_shift, \ + arr) private(k, i0, i1, v0, v1) reduction(+ : expval) +#endif + for (k = 0; k < exp2(num_qubits - 1); k++) { + i0 = ((k << 1U) & wire_parity_inv) | (wire_parity & k); + i1 = i0 | rev_wire_shift; + v0 = arr[i0]; + v1 = arr[i1]; + + expval += real(conj(arr[i0]) * + std::complex{imag(v1), -real(v1)}) + + real(conj(arr[i1]) * + std::complex{-imag(v0), real(v0)}); + } + } +}; + +template struct getExpectationValuePauliZFunctor { + std::complex *arr; + size_t num_qubits; + + size_t rev_wire; + size_t rev_wire_shift; + size_t wire_parity; + size_t wire_parity_inv; + + getExpectationValuePauliZFunctor(std::complex *arr_, + std::size_t num_qubits_, + const std::vector &wires) { + arr = arr_; + num_qubits = num_qubits_; + rev_wire = num_qubits - wires[0] - 1; + rev_wire_shift = (static_cast(1U) << rev_wire); + wire_parity = fillTrailingOnes(rev_wire); + wire_parity_inv = fillLeadingOnes(rev_wire + 1); + } + + inline void operator()(PrecisionT &expval) const { + size_t k, i0, i1; +#if defined(_OPENMP) +#pragma omp parallel for default(none) \ + shared(num_qubits, wire_parity_inv, wire_parity, rev_wire_shift, \ + arr) private(k, i0, i1) reduction(+ : expval) +#endif + for (k = 0; k < exp2(num_qubits - 1); k++) { + + i0 = ((k << 1U) & wire_parity_inv) | (wire_parity & k); + i1 = i0 | rev_wire_shift; + + expval += real(conj(arr[i1]) * (-arr[i1])) + + real(conj(arr[i0]) * (arr[i0])); + } + } +}; + +template struct getExpectationValueHadamardFunctor { + std::complex *arr; + size_t num_qubits; + + size_t rev_wire; + size_t rev_wire_shift; + size_t wire_parity; + size_t wire_parity_inv; + + constexpr static auto isqrt2 = INVSQRT2(); + + getExpectationValueHadamardFunctor(std::complex *arr_, + std::size_t num_qubits_, + const std::vector &wires) { + arr = arr_; + num_qubits = num_qubits_; + rev_wire = num_qubits - wires[0] - 1; + rev_wire_shift = (static_cast(1U) << rev_wire); + wire_parity = fillTrailingOnes(rev_wire); + wire_parity_inv = fillLeadingOnes(rev_wire + 1); + } + + inline void operator()(PrecisionT &expval) const { + size_t k, i0, i1; + std::complex v0, v1; +#if defined(_OPENMP) +#pragma omp parallel for default(none) \ + shared(num_qubits, wire_parity_inv, wire_parity, rev_wire_shift, \ + arr) private(k, i0, i1, v0, v1) reduction(+ : expval) +#endif + + for (k = 0; k < exp2(num_qubits - 1); k++) { + i0 = ((k << 1U) & wire_parity_inv) | (wire_parity & k); + i1 = i0 | rev_wire_shift; + v0 = arr[i0]; + v1 = arr[i1]; + + expval += real(isqrt2 * (conj(arr[i0]) * (v0 + v1) + + conj(arr[i1]) * (v0 - v1))); + } + } +}; +} // namespace Pennylane::LightningQubit::Functors \ No newline at end of file diff --git a/pennylane_lightning/core/src/simulators/lightning_qubit/measurements/MeasurementsLQubit.hpp b/pennylane_lightning/core/src/simulators/lightning_qubit/measurements/MeasurementsLQubit.hpp index e1d3ef551d..856a0d4033 100644 --- a/pennylane_lightning/core/src/simulators/lightning_qubit/measurements/MeasurementsLQubit.hpp +++ b/pennylane_lightning/core/src/simulators/lightning_qubit/measurements/MeasurementsLQubit.hpp @@ -29,6 +29,7 @@ #include #include +#include "ExpValFunctorsLQubit.hpp" #include "LinearAlgebra.hpp" #include "MeasurementsBase.hpp" #include "Observables.hpp" @@ -38,12 +39,26 @@ #include "TransitionKernels.hpp" #include "Util.hpp" //transpose_state_tensor, sorting_indices +#include "BitUtil.hpp" +#include + /// @cond DEV namespace { using namespace Pennylane::Measures; using namespace Pennylane::Observables; using Pennylane::LightningQubit::StateVectorLQubitManaged; using Pennylane::LightningQubit::Util::innerProdC; +using namespace Pennylane::LightningQubit::Functors; +using Pennylane::Util::INVSQRT2; +enum class ExpValFunc : uint32_t { + BEGIN = 1, + Identity = 1, + PauliX, + PauliY, + PauliZ, + Hadamard, + END +}; } // namespace /// @endcond @@ -67,7 +82,63 @@ class Measurements final public: explicit Measurements(const StateVectorT &statevector) - : BaseType{statevector} {}; + : BaseType{statevector} { + init_expval_funcs_(); + }; + + /** + * @brief Templated method that returns the expectation value of named + * observables using in-place operations, without creating extra copy of + * the statevector. + * + * @tparam functor_t Expectation value functor class for in-place + * operations. + * @tparam nqubits Number of wires. + * @param wires Wires to which the observable is applied. + * @return expectation value of the observable. + */ + template