diff --git a/.github/CHANGELOG.md b/.github/CHANGELOG.md index 6b799e305a..0088ebd23f 100644 --- a/.github/CHANGELOG.md +++ b/.github/CHANGELOG.md @@ -2,22 +2,36 @@ ### New features +- C++ layer now supports float (32-bit) and double (64-bit) templated complex data ([#113](https://github.com/PennyLaneAI/pennylane-lightning/pull/113)) + ### Improvements -* Move changelog to `.github` and add a changelog reminder. +- C++ tests have been ported to use Catch2 framework. ([#115](https://github.com/PennyLaneAI/pennylane-lightning/pull/115)) + +- Testing now exists for both float and double precision methods in C++ layer. ([#113](https://github.com/PennyLaneAI/pennylane-lightning/pull/113),[#115](https://github.com/PennyLaneAI/pennylane-lightning/pull/115)) + +- Compile-time utility methods with `constexpr` have been added. ([#113](https://github.com/PennyLaneAI/pennylane-lightning/pull/113)) + +- Wheel-build support for ARM64 (Linux and MacOS) and PowerPC (Linux) added. ([#110](https://github.com/PennyLaneAI/pennylane-lightning/pull/110)) -* Add support for Controlled Phase Gate (CPhaseShift). +- Add support for Controlled Phase Gate (CPhaseShift). [(#112)](https://github.com/PennyLaneAI/pennylane-lightning/issues/112) +- Move changelog to `.github` and add a changelog reminder. + ### Breaking changes +- Compilers with C++17 support are now required to build C++ module. ([#113](https://github.com/PennyLaneAI/pennylane-lightning/pull/113)) + +- Gate classes have been removed with functionality added to StateVector class. ([#113](https://github.com/PennyLaneAI/pennylane-lightning/pull/113)) + ### Bug fixes ### Contributors This release contains contributions from (in alphabetical order): -Ali Asadi, Thomas Bromley +Ali Asadi, Thomas Bromley, Lee J. O'Riordan --- diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2cb9e47b85..d09fa6ef04 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,21 +13,36 @@ jobs: matrix: os: [ubuntu-20.04] steps: + - name: Cancel previous runs + uses: styfle/cancel-workflow-action@0.4.1 + with: + access_token: ${{ github.token }} + - uses: actions/checkout@v2 - name: Install dependencies run: sudo apt-get update && sudo apt-get -y -q install cmake gcc - - name: Install Google Test - run: | - wget -qO - https://github.com/google/googletest/archive/release-1.10.0.tar.gz | tar -xz - cmake -D CMAKE_INSTALL_PREFIX:PATH=$HOME/googletest -D CMAKE_BUILD_TYPE=Release googletest-release-1.10.0 - make install - - name: Build and run unit tests run: | - cd pennylane_lightning/src/tests - GOOGLETEST_DIR=$HOME/googletest make test + cmake . -BBuild -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTS=1 + cmake --build ./Build + mkdir -p ./Build/tests/results + ./Build/tests/runner --order lex --reporter junit --out ./Build/tests/results/report.xml + + - name: Upload test results + uses: actions/upload-artifact@v2 + if: always() + with: + name: ubuntu-test-report + path: Build/tests/results/report.xml + + - name: Publish test results + uses: EnricoMi/publish-unit-test-result-action@v1 + if: always() + with: + check_name: Test Report (C++) on Ubuntu + files: Build/tests/results/report.xml pythontests: name: Python tests diff --git a/CMakeLists.txt b/CMakeLists.txt index ff1587f294..a13db7c294 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,10 +20,9 @@ set_pennylane_lightning_version("${CMAKE_SOURCE_DIR}/pennylane_lightning/_versio message(STATUS "pennylane_lightning version ${VERSION_STRING}") set(PROJECT_VERSION ${VERSION_STRING}) -set(CMAKE_CXX_STANDARD 11) # At least C++11 is required +set(CMAKE_CXX_STANDARD 17) # At least C++17 is required find_package(OpenMP REQUIRED) # find OpenMP - if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() @@ -36,32 +35,31 @@ option(BUILD_TESTS "Build cpp tests" OFF) # Add pybind11 include(FetchContent) FetchContent_Declare( - pybind11 - GIT_REPOSITORY https://github.com/pybind/pybind11.git - GIT_TAG v2.6.2 + pybind11 + GIT_REPOSITORY https://github.com/pybind/pybind11.git + GIT_TAG v2.6.2 ) FetchContent_MakeAvailable(pybind11) +add_library(pennylane_lightning SHARED "pennylane_lightning/src/StateVector.cpp") +target_include_directories(pennylane_lightning INTERFACE "pennylane_lightning/src") add_library(external_dependency INTERFACE) - if ("$ENV{USE_LAPACK}" OR "${USE_LAPACK}") - message(STATUS "Use LAPACKE") - target_link_libraries(external_dependency INTERFACE lapacke) - target_compile_options(external_dependency INTERFACE "-DLAPACKE=1") + message(STATUS "Use LAPACKE") + target_link_libraries(external_dependency INTERFACE lapacke) + target_compile_options(external_dependency INTERFACE "-DLAPACKE=1") endif() if ("$ENV{USE_OPENBLAS}" OR "${USE_OPENBLAS}") - message(STATUS "Use OPENBLAS") - target_link_libraries(external_dependency INTERFACE openblas) - target_compile_options(external_dependency INTERFACE "-DOPENBLAS=1") + message(STATUS "Use OPENBLAS") + target_link_libraries(external_dependency INTERFACE openblas) + target_compile_options(external_dependency INTERFACE "-DOPENBLAS=1") endif() -pybind11_add_module(lightning_qubit_ops "pennylane_lightning/src/Apply.cpp" - "pennylane_lightning/src/Bindings.cpp" - "pennylane_lightning/src/Gates.cpp" - "pennylane_lightning/src/StateVector.cpp") +pybind11_add_module(lightning_qubit_ops "pennylane_lightning/src/StateVector.cpp" + "pennylane_lightning/src/Bindings.cpp") target_link_libraries(lightning_qubit_ops PRIVATE external_dependency) set_target_properties(lightning_qubit_ops PROPERTIES CXX_VISIBILITY_PRESET hidden) @@ -71,12 +69,12 @@ target_compile_options(lightning_qubit_ops PRIVATE "$<$:-Wall>") target_compile_definitions(lightning_qubit_ops PRIVATE VERSION_INFO=${VERSION_STRING}) if(ENABLE_NATIVE) - message(STATUS "ENABLE_NATIVE is ON. Use -march=native for lightning_qubit_ops.") - target_compile_options(lightning_qubit_ops PRIVATE -march=native) + message(STATUS "ENABLE_NATIVE is ON. Use -march=native for lightning_qubit_ops.") + target_compile_options(pennylane_lightning PRIVATE -march=native) + target_compile_options(lightning_qubit_ops PRIVATE -march=native) endif() - if (BUILD_TESTS) - enable_testing() - add_subdirectory("pennylane_lightning/src/tests" "tests") + enable_testing() + add_subdirectory("pennylane_lightning/src/tests" "tests") endif() diff --git a/Makefile b/Makefile index 2b29e37fcd..4699397fe1 100644 --- a/Makefile +++ b/Makefile @@ -54,15 +54,17 @@ clean-docs: make -C doc clean test: - $(PYTHON) $(TESTRUNNER) + $(PYTHON) -I $(TESTRUNNER) coverage: @echo "Generating coverage report..." $(PYTHON) $(TESTRUNNER) $(COVERAGE) test-cpp: - make -C pennylane_lightning/src/tests clean - GOOGLETEST_DIR=$(HOME)/googletest make -C pennylane_lightning/src/tests test + rm -rf ./BuildTests + cmake . -BBuildTests -DBUILD_TESTS=1 + cmake --build ./BuildTests + ./BuildTests/tests/runner .PHONY: format format: diff --git a/bin/format b/bin/format index 9b295bb020..b2e6fc5e3a 100755 --- a/bin/format +++ b/bin/format @@ -18,7 +18,6 @@ IGNORE_PATTERN = "external" BASE_CMD = ("clang-format", f"-style={json.dumps(CLANG_FMT_STYLE_CFG)}") - def check_bin(): try: subprocess.run( diff --git a/pennylane_lightning/lightning_qubit.py b/pennylane_lightning/lightning_qubit.py index 0018faf702..802abe5c3c 100644 --- a/pennylane_lightning/lightning_qubit.py +++ b/pennylane_lightning/lightning_qubit.py @@ -16,9 +16,9 @@ interfaces with C++ for fast linear algebra calculations. """ from pennylane.devices import DefaultQubit -from .lightning_qubit_ops import apply import numpy as np from pennylane import QubitStateVector, BasisState, DeviceError, QubitUnitary +from .lightning_qubit_ops import apply, StateVectorC64, StateVectorC128 from ._version import __version__ @@ -137,7 +137,7 @@ def apply_lightning(self, state, operations): op_inverse = [o.inverse for o in operations] state_vector = np.ravel(state) - apply(state_vector, op_names, op_wires, op_param, op_inverse, self.num_wires) + apply(state_vector, op_names, op_wires, op_inverse, op_param) return np.reshape(state_vector, state.shape) @staticmethod diff --git a/pennylane_lightning/src/Apply.cpp b/pennylane_lightning/src/Apply.cpp deleted file mode 100644 index 60a50abaac..0000000000 --- a/pennylane_lightning/src/Apply.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2021 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. -#include - -#include "Apply.hpp" -#include "Gates.hpp" -#include "StateVector.hpp" -#include "Util.hpp" - -using std::set; -using std::string; -using std::unique_ptr; -using std::vector; - -vector Pennylane::getIndicesAfterExclusion( - const vector &indicesToExclude, const unsigned int qubits) { - set indices; - for (unsigned int i = 0; i < qubits; i++) { - indices.insert(indices.end(), i); - } - for (const unsigned int &excludedIndex : indicesToExclude) { - indices.erase(excludedIndex); - } - return vector(indices.begin(), indices.end()); -} - -vector -Pennylane::generateBitPatterns(const vector &qubitIndices, - const unsigned int qubits) { - vector indices; - indices.reserve(exp2(qubitIndices.size())); - indices.push_back(0); - for (int i = qubitIndices.size() - 1; i >= 0; i--) { - size_t value = maxDecimalForQubit(qubitIndices[i], qubits); - size_t currentSize = indices.size(); - for (size_t j = 0; j < currentSize; j++) { - indices.push_back(indices[j] + value); - } - } - return indices; -} - -void Pennylane::constructAndApplyOperation(StateVector &state, - const string &opLabel, - const vector &opWires, - const vector &opParams, - bool inverse, - const unsigned int qubits) { - unique_ptr gate = constructGate(opLabel, opParams); - if (gate->numQubits != opWires.size()) - throw std::invalid_argument( - string("The gate of type ") + opLabel + " requires " + - std::to_string(gate->numQubits) + " wires, but " + - std::to_string(opWires.size()) + " were supplied"); - - vector internalIndices = generateBitPatterns(opWires, qubits); - - vector externalWires = - getIndicesAfterExclusion(opWires, qubits); - vector externalIndices = generateBitPatterns(externalWires, qubits); - - gate->applyKernel(state, internalIndices, externalIndices, inverse); -} - -void Pennylane::applyGateGenerator(StateVector &state, - unique_ptr gate, - const vector &opWires, - const unsigned int qubits) { - vector internalIndices = generateBitPatterns(opWires, qubits); - - vector externalWires = - getIndicesAfterExclusion(opWires, qubits); - vector externalIndices = generateBitPatterns(externalWires, qubits); - - gate->applyGenerator(state, internalIndices, externalIndices); -} - -void Pennylane::apply(StateVector &state, const vector &ops, - const vector> &wires, - const vector> ¶ms, - const vector &inverse, const unsigned int qubits) { - if (qubits <= 0) - throw std::invalid_argument("Must specify one or more qubits"); - - size_t expectedLength = exp2(qubits); - if (state.length != expectedLength) - throw std::invalid_argument( - string("Input state vector length (") + - std::to_string(state.length) + - ") does not match the given number of qubits " + - std::to_string(qubits)); - - size_t numOperations = ops.size(); - if (numOperations != wires.size() || numOperations != params.size()) - throw std::invalid_argument("Invalid arguments: number of operations, " - "wires, and parameters must all be equal"); - - for (int i = 0; i < numOperations; i++) { - constructAndApplyOperation(state, ops[i], wires[i], params[i], - inverse[i], qubits); - } -} diff --git a/pennylane_lightning/src/Apply.hpp b/pennylane_lightning/src/Apply.hpp deleted file mode 100644 index c795fdb5f0..0000000000 --- a/pennylane_lightning/src/Apply.hpp +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2021 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 - * Contains the main `apply()` function for applying a set of operations to a - * multiqubit statevector. - * - * Also includes PyBind boilerplate for interfacing with Python. - */ -#pragma once - -#include -#include - -#include "Gates.hpp" -#include "StateVector.hpp" -#include "typedefs.hpp" - -namespace Pennylane { - -/** - * Produces the list of qubit indices that excludes a given set of indices. - * - * @param excludedIndices indices to exclude (must be in the range [0, - * qubits-1]) - * @param qubits number of qubits - * @return Set difference of [0, ..., qubits-1] and excludedIndices, in - * ascending order - */ -std::vector -getIndicesAfterExclusion(const std::vector &indicesToExclude, - const unsigned int qubits); - -/** - * Produces the decimal values for all possible bit patterns determined by a set - * of indices, taking other indices to be fixed at 0. The qubit indices are - * taken to be big-endian, i.e. qubit 0 is the most significant bit. - * - * For instance, in a circuit with 5 qubits: - * [0, 1] -> 00000, 01000, 10000, 11000 -> 0, 8, 16, 24 - * - * The order of the indices determines the order in which bit patterns are - * generated, e.g. [1, 0] -> 00000, 10000, 01000, 11000 -> 0, 16, 8, 24 - * - * i.e. the qubit indices are evaluted from last-to-first. - * - * @param qubitIndices indices of qubits that comprise the bit pattern - * @param qubits number of qubits - * @return decimal value corresponding to all possible bit patterns for the - * given indices - */ -std::vector -generateBitPatterns(const std::vector &qubitIndices, - const unsigned int qubits); - -/** - * Constructs the gate defined by the supplied parameters and applies it to the - * state vector. - * - * @param state state vector to which to apply the operation - * @param opLabel unique string corresponding to a gate type - * @param opWires index of qubits on which the gate acts - * @param opParams defines the gate parameterisation (may be zero-length for - * some gates) - * @param inverse boolean indicating whether to apply the gate or its inverse - * @param qubits number of qubits - */ -void constructAndApplyOperation(StateVector &state, const std::string &opLabel, - const std::vector &opWires, - const std::vector &opParams, - bool inverse, const unsigned int qubits); - -/** - * Applies the generator of the gate to the state vector. - * - * @param state state vector to which to apply the operation - * @param gate unique pointer to the gate whose generator is to be applied - * @param opWires index of qubits on which the operation acts - * @param qubits number of qubits - */ -void applyGateGenerator(StateVector &state, std::unique_ptr gate, - const std::vector &opWires, - const unsigned int qubits); - -/** - * Applies specified operations onto an input state of an arbitrary number of - * qubits. - * - * @param state the multiqubit statevector, modified in place - * @param ops list of unique string names corresponding to gate types, in the - * order they should be applied - * @param wires list of wires on which each gate acts - * @param params list of parameters that defines the gate parameterisation - * @param inverse list of booleans indicating whether a given gate or its - * inverse should be applied - * @param qubits number of qubits - */ -void apply(StateVector &state, const std::vector &ops, - const std::vector> &wires, - const std::vector> ¶ms, - const std::vector &inverse, const unsigned int qubits); - -} // namespace Pennylane diff --git a/pennylane_lightning/src/Bindings.cpp b/pennylane_lightning/src/Bindings.cpp index 59b98af3c2..d9e33248aa 100644 --- a/pennylane_lightning/src/Bindings.cpp +++ b/pennylane_lightning/src/Bindings.cpp @@ -11,38 +11,109 @@ // 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. +#include "StateVector.hpp" #include "pybind11/complex.h" #include "pybind11/numpy.h" #include "pybind11/pybind11.h" #include "pybind11/stl.h" -#include "Apply.hpp" - -using Pennylane::CplxType; using Pennylane::StateVector; +using std::complex; using std::string; using std::vector; -static StateVector create(const pybind11::array_t *numpyArray) { - pybind11::buffer_info numpyArrayInfo = numpyArray->request(); +namespace py = pybind11; + +template +static StateVector create(const py::array_t> *numpyArray) { + py::buffer_info numpyArrayInfo = numpyArray->request(); if (numpyArrayInfo.ndim != 1) throw std::invalid_argument( "NumPy array must be a 1-dimensional array"); - if (numpyArrayInfo.itemsize != sizeof(CplxType)) - throw std::invalid_argument("NumPy array must be a complex128 array"); + if (numpyArrayInfo.itemsize != sizeof(complex)) + throw std::invalid_argument( + "NumPy array must be of type np.complex64 or np.complex128"); + complex *data_ptr = static_cast *>(numpyArrayInfo.ptr); + return StateVector( + {data_ptr, static_cast(numpyArrayInfo.shape[0])}); +} - return StateVector((CplxType *)numpyArrayInfo.ptr, numpyArrayInfo.shape[0]); +template +void apply(py::array_t> &stateNumpyArray, const vector &ops, + const vector> &wires, const vector &inverse, + const vector> ¶ms) { + auto state = create(&stateNumpyArray); + state.applyOperations(ops, wires, inverse, params); } -void apply(pybind11::array_t &stateNumpyArray, vector ops, - vector> wires, vector> params, - vector inverse, const unsigned int qubits) { - StateVector state = create(&stateNumpyArray); - Pennylane::apply(state, ops, wires, params, inverse, qubits); +template class StateVecBinder : public StateVector { + public: + explicit StateVecBinder(const py::array_t> &stateNumpyArray) + : StateVector( + static_cast *>(stateNumpyArray.request().ptr), + static_cast(stateNumpyArray.request().shape[0])) {} + + void apply(const vector &ops, const vector> &wires, + const vector &inverse, + const vector> ¶ms) { + this->applyOperations(ops, wires, inverse, params); + } +}; + +template void lightning_class_bindings(py::module &m) { + // Enable module name to be based on size of complex datatype + const std::string bitsize = std::to_string(sizeof(PrecisionT) * 8 * 2); + const std::string class_name = "StateVectorC" + bitsize; + py::class_>(m, class_name.c_str()) + .def(py::init< + py::array_t, + py::array::c_style | py::array::forcecast> &>()) + .def("apply", &StateVecBinder::apply) + .def("PauliX", &StateVecBinder::applyPauliX) + .def("PauliY", &StateVecBinder::applyPauliY) + .def("PauliZ", &StateVecBinder::applyPauliZ) + .def("Hadamard", &StateVecBinder::applyHadamard) + .def("S", &StateVecBinder::applyS) + .def("T", &StateVecBinder::applyT) + .def("CNOT", &StateVecBinder::applyCNOT) + .def("SWAP", &StateVecBinder::applySWAP) + .def("CSWAP", &StateVecBinder::applyCSWAP) + .def("Toffoli", &StateVecBinder::applyToffoli) + .def("CZ", &StateVecBinder::applyCZ) + .def("PhaseShift", + &StateVecBinder::template applyPhaseShift) + .def("PhaseShift", + &StateVecBinder::template applyPhaseShift) + .def("ControlledPhaseShift", + &StateVecBinder::template applyControlledPhaseShift< + float>) + .def("ControlledPhaseShift", + &StateVecBinder::template applyControlledPhaseShift< + double>) + .def("RX", &StateVecBinder::template applyRX) + .def("RX", &StateVecBinder::template applyRX) + .def("RY", &StateVecBinder::template applyRY) + .def("RY", &StateVecBinder::template applyRY) + .def("RZ", &StateVecBinder::template applyRZ) + .def("RZ", &StateVecBinder::template applyRZ) + .def("Rot", &StateVecBinder::template applyRot) + .def("Rot", &StateVecBinder::template applyRot) + .def("CRX", &StateVecBinder::template applyCRX) + .def("CRX", &StateVecBinder::template applyCRX) + .def("CRY", &StateVecBinder::template applyCRY) + .def("CRY", &StateVecBinder::template applyCRY) + .def("CRZ", &StateVecBinder::template applyCRZ) + .def("CRZ", &StateVecBinder::template applyCRZ) + .def("CRot", &StateVecBinder::template applyCRot) + .def("CRot", &StateVecBinder::template applyCRot); } PYBIND11_MODULE(lightning_qubit_ops, m) { m.doc() = "lightning.qubit apply() method"; - m.def("apply", apply, "lightning.qubit apply() method"); + m.def("apply", apply, "lightning.qubit apply() method"); + m.def("apply", apply, "lightning.qubit apply() method"); + + lightning_class_bindings(m); + lightning_class_bindings(m); } diff --git a/pennylane_lightning/src/Gates.cpp b/pennylane_lightning/src/Gates.cpp deleted file mode 100644 index 13a43ffb14..0000000000 --- a/pennylane_lightning/src/Gates.cpp +++ /dev/null @@ -1,834 +0,0 @@ -// Copyright 2021 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. - -#define _USE_MATH_DEFINES - -#include -#include -#include -#include - -#include "Gates.hpp" -#include "Util.hpp" - -using std::conj; -using std::function; -using std::map; -using std::string; -using std::swap; -using std::unique_ptr; -using std::vector; - -using Pennylane::CplxType; - -template -static void validateLength(const string &errorPrefix, const vector &vec, - int requiredLength) { - if (vec.size() != requiredLength) - throw std::invalid_argument( - errorPrefix + ": requires " + std::to_string(requiredLength) + - " arguments but got " + std::to_string(vec.size()) + - " arguments instead"); -} - -// ------------------------------------------------------------------------------------------------------------- - -Pennylane::AbstractGate::AbstractGate(int numQubits) - : numQubits(numQubits), length(exp2(numQubits)) {} - -void Pennylane::AbstractGate::applyKernel( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices, bool inverse) { - const vector &matrix = asMatrix(); - assert(indices.size() == length); - - vector v(indices.size()); - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - // Gather - size_t pos = 0; - for (const size_t &index : indices) { - v[pos] = shiftedState[index]; - pos++; - } - - // Apply + scatter - for (size_t i = 0; i < indices.size(); i++) { - size_t index = indices[i]; - shiftedState[index] = 0; - - if (inverse == true) { - for (size_t j = 0; j < indices.size(); j++) { - size_t baseIndex = j * indices.size(); - shiftedState[index] += conj(matrix[baseIndex + i]) * v[j]; - } - } else { - size_t baseIndex = i * indices.size(); - for (size_t j = 0; j < indices.size(); j++) { - shiftedState[index] += matrix[baseIndex + j] * v[j]; - } - } - } - } -} - -const double Pennylane::AbstractGate::generatorScalingFactor{}; -void Pennylane::AbstractGate::applyGenerator(const StateVector &, - const std::vector &, - const std::vector &) { - throw NotImplementedException(); -} - -// ------------------------------------------------------------------------------------------------------------- - -Pennylane::SingleQubitGate::SingleQubitGate() : AbstractGate(1) {} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::XGate::label = "PauliX"; - -Pennylane::XGate Pennylane::XGate::create(const vector ¶meters) { - validateLength(Pennylane::XGate::label, parameters, 0); - return Pennylane::XGate(); -} - -const vector Pennylane::XGate::matrix{0, 1, 1, 0}; - -void Pennylane::XGate::applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, - bool) { - // gate is its own inverse - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - swap(shiftedState[indices[0]], shiftedState[indices[1]]); - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::YGate::label = "PauliY"; - -Pennylane::YGate Pennylane::YGate::create(const vector ¶meters) { - validateLength(Pennylane::YGate::label, parameters, 0); - return Pennylane::YGate(); -} - -const vector Pennylane::YGate::matrix{0, -IMAG, IMAG, 0}; - -void Pennylane::YGate::applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, - bool) { - // gate is its own inverse - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - CplxType v0 = shiftedState[indices[0]]; - shiftedState[indices[0]] = -IMAG * shiftedState[indices[1]]; - shiftedState[indices[1]] = IMAG * v0; - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::ZGate::label = "PauliZ"; - -Pennylane::ZGate Pennylane::ZGate::create(const vector ¶meters) { - validateLength(Pennylane::ZGate::label, parameters, 0); - return Pennylane::ZGate(); -} - -const std::vector Pennylane::ZGate::matrix{1, 0, 0, -1}; - -void Pennylane::ZGate::applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, - bool) { - // gate is its own inverse - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - shiftedState[indices[1]] *= -1; - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::HadamardGate::label = "Hadamard"; - -Pennylane::HadamardGate -Pennylane::HadamardGate::create(const vector ¶meters) { - validateLength(Pennylane::HadamardGate::label, parameters, 0); - return Pennylane::HadamardGate(); -} - -const vector Pennylane::HadamardGate::matrix{SQRT2INV, SQRT2INV, - SQRT2INV, -SQRT2INV}; - -void Pennylane::HadamardGate::applyKernel( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices, bool) { - // gate is its own inverse - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - CplxType v0 = shiftedState[indices[0]]; - CplxType v1 = shiftedState[indices[1]]; - shiftedState[indices[0]] = SQRT2INV * (v0 + v1); - shiftedState[indices[1]] = SQRT2INV * (v0 - v1); - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::SGate::label = "S"; - -Pennylane::SGate Pennylane::SGate::create(const vector ¶meters) { - validateLength(Pennylane::SGate::label, parameters, 0); - return Pennylane::SGate(); -} - -const vector Pennylane::SGate::matrix{1, 0, 0, IMAG}; - -void Pennylane::SGate::applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, - bool inverse) { - CplxType shift = (inverse == true) ? -IMAG : IMAG; - - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - shiftedState[indices[1]] *= shift; - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::TGate::label = "T"; - -Pennylane::TGate Pennylane::TGate::create(const vector ¶meters) { - validateLength(Pennylane::TGate::label, parameters, 0); - return Pennylane::TGate(); -} - -const CplxType Pennylane::TGate::shift = std::pow(M_E, CplxType(0, M_PI / 4)); - -const vector Pennylane::TGate::matrix{1, 0, 0, - Pennylane::TGate::shift}; - -void Pennylane::TGate::applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, - bool inverse) { - CplxType shift = (inverse == true) ? conj(Pennylane::TGate::shift) - : Pennylane::TGate::shift; - - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - shiftedState[indices[1]] *= shift; - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::RotationXGate::label = "RX"; - -Pennylane::RotationXGate -Pennylane::RotationXGate::create(const vector ¶meters) { - validateLength(Pennylane::RotationXGate::label, parameters, 1); - return Pennylane::RotationXGate(parameters[0]); -} - -Pennylane::RotationXGate::RotationXGate(double rotationAngle) - : c(std::cos(rotationAngle / 2), 0), - js(0, std::sin(-rotationAngle / 2)), matrix{c, js, js, c} {} - -const double Pennylane::RotationXGate::generatorScalingFactor{-0.5}; - -void Pennylane::RotationXGate::applyGenerator( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices) { - unique_ptr gate; - gate->applyKernel(state, indices, externalIndices, false); -} - -void Pennylane::RotationXGate::applyKernel( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices, bool inverse) { - CplxType js_ = (inverse == true) ? -js : js; - - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - CplxType v0 = shiftedState[indices[0]]; - CplxType v1 = shiftedState[indices[1]]; - shiftedState[indices[0]] = c * v0 + js_ * v1; - shiftedState[indices[1]] = js_ * v0 + c * v1; - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::RotationYGate::label = "RY"; - -Pennylane::RotationYGate -Pennylane::RotationYGate::create(const vector ¶meters) { - validateLength(Pennylane::RotationYGate::label, parameters, 1); - return Pennylane::RotationYGate(parameters[0]); -} - -Pennylane::RotationYGate::RotationYGate(double rotationAngle) - : c(std::cos(rotationAngle / 2), 0), - s(std::sin(rotationAngle / 2), 0), matrix{c, -s, s, c} {} - -const double Pennylane::RotationYGate::generatorScalingFactor{-0.5}; - -void Pennylane::RotationYGate::applyGenerator( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices) { - unique_ptr gate; - gate->applyKernel(state, indices, externalIndices, false); -} - -void Pennylane::RotationYGate::applyKernel( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices, bool inverse) { - CplxType s_ = (inverse == true) ? -s : s; - - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - CplxType v0 = shiftedState[indices[0]]; - CplxType v1 = shiftedState[indices[1]]; - shiftedState[indices[0]] = c * v0 - s_ * v1; - shiftedState[indices[1]] = s_ * v0 + c * v1; - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::RotationZGate::label = "RZ"; - -Pennylane::RotationZGate -Pennylane::RotationZGate::create(const vector ¶meters) { - validateLength(Pennylane::RotationZGate::label, parameters, 1); - return Pennylane::RotationZGate(parameters[0]); -} - -Pennylane::RotationZGate::RotationZGate(double rotationAngle) - : first(std::pow(M_E, CplxType(0, -rotationAngle / 2))), - second(std::pow(M_E, CplxType(0, rotationAngle / 2))), matrix{first, 0, 0, - second} {} - -void Pennylane::RotationZGate::applyKernel( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices, bool inverse) { - CplxType shift1 = first; - CplxType shift2 = second; - - if (inverse == true) { - shift1 = conj(first); - shift2 = conj(second); - } - - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - shiftedState[indices[0]] *= shift1; - shiftedState[indices[1]] *= shift2; - } -} - -const double Pennylane::RotationZGate::generatorScalingFactor{-0.5}; - -void Pennylane::RotationZGate::applyGenerator( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices) { - unique_ptr gate; - gate->applyKernel(state, indices, externalIndices, false); -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::PhaseShiftGate::label = "PhaseShift"; - -Pennylane::PhaseShiftGate -Pennylane::PhaseShiftGate::create(const vector ¶meters) { - validateLength(Pennylane::PhaseShiftGate::label, parameters, 1); - return Pennylane::PhaseShiftGate(parameters[0]); -} - -Pennylane::PhaseShiftGate::PhaseShiftGate(double rotationAngle) - : shift(std::pow(M_E, CplxType(0, rotationAngle))), matrix{1, 0, 0, shift} { -} - -void Pennylane::PhaseShiftGate::applyKernel( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices, bool inverse) { - const CplxType s = (inverse == true) ? conj(shift) : shift; - - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - shiftedState[indices[1]] *= s; - } -} - -const double Pennylane::PhaseShiftGate::generatorScalingFactor{1.0}; - -void Pennylane::PhaseShiftGate::applyGenerator( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices) { - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - shiftedState[indices[0]] = 0; - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::GeneralRotationGate::label = "Rot"; - -Pennylane::GeneralRotationGate -Pennylane::GeneralRotationGate::create(const vector ¶meters) { - validateLength(Pennylane::GeneralRotationGate::label, parameters, 3); - return Pennylane::GeneralRotationGate(parameters[0], parameters[1], - parameters[2]); -} - -Pennylane::GeneralRotationGate::GeneralRotationGate(double phi, double theta, - double omega) - : c(std::cos(theta / 2), 0), s(std::sin(theta / 2), 0), - r1(c * std::pow(M_E, CplxType(0, (-phi - omega) / 2))), - r2(-s * std::pow(M_E, CplxType(0, (phi - omega) / 2))), - r3(s * std::pow(M_E, CplxType(0, (-phi + omega) / 2))), - r4(c * std::pow(M_E, CplxType(0, (phi + omega) / 2))), matrix{r1, r2, r3, - r4} {} - -void Pennylane::GeneralRotationGate::applyKernel( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices, bool inverse) { - CplxType t1 = r1; - CplxType t2 = r2; - CplxType t3 = r3; - CplxType t4 = r4; - - if (inverse == true) { - t1 = conj(r1); - t2 *= -1; - t3 *= -1; - t4 = conj(t4); - } - - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - CplxType v0 = shiftedState[indices[0]]; - CplxType v1 = shiftedState[indices[1]]; - shiftedState[indices[0]] = t1 * v0 + t2 * v1; - shiftedState[indices[1]] = t3 * v0 + t4 * v1; - } -} - -// ------------------------------------------------------------------------------------------------------------- - -Pennylane::TwoQubitGate::TwoQubitGate() : AbstractGate(2) {} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::CNOTGate::label = "CNOT"; - -Pennylane::CNOTGate -Pennylane::CNOTGate::create(const vector ¶meters) { - validateLength(Pennylane::CNOTGate::label, parameters, 0); - return Pennylane::CNOTGate(); -} - -const std::vector Pennylane::CNOTGate::matrix{1, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 1, 0}; - -void Pennylane::CNOTGate::applyKernel( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices, bool) { - // gate is its own inverse - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - swap(shiftedState[indices[2]], shiftedState[indices[3]]); - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::SWAPGate::label = "SWAP"; - -Pennylane::SWAPGate -Pennylane::SWAPGate::create(const vector ¶meters) { - validateLength(Pennylane::SWAPGate::label, parameters, 0); - return Pennylane::SWAPGate(); -} - -const std::vector Pennylane::SWAPGate::matrix{1, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 1}; - -void Pennylane::SWAPGate::applyKernel( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices, bool) { - // gate is its own inverse - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - swap(shiftedState[indices[1]], shiftedState[indices[2]]); - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::CZGate::label = "CZ"; - -Pennylane::CZGate Pennylane::CZGate::create(const vector ¶meters) { - validateLength(Pennylane::CZGate::label, parameters, 0); - return Pennylane::CZGate(); -} - -const std::vector Pennylane::CZGate::matrix{1, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 0, 0, -1}; - -void Pennylane::CZGate::applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, - bool) { - // gate is its own inverse - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - shiftedState[indices[3]] *= -1; - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::CRotationXGate::label = "CRX"; - -Pennylane::CRotationXGate -Pennylane::CRotationXGate::create(const vector ¶meters) { - validateLength(Pennylane::CRotationXGate::label, parameters, 1); - return Pennylane::CRotationXGate(parameters[0]); -} - -Pennylane::CRotationXGate::CRotationXGate(double rotationAngle) - : c(std::cos(rotationAngle / 2), 0), - js(0, std::sin(-rotationAngle / 2)), matrix{1, 0, 0, 0, 0, 1, 0, 0, - 0, 0, c, js, 0, 0, js, c} {} - -void Pennylane::CRotationXGate::applyKernel( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices, bool inverse) { - CplxType js_ = (inverse == true) ? -js : js; - - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - CplxType v0 = shiftedState[indices[2]]; - CplxType v1 = shiftedState[indices[3]]; - shiftedState[indices[2]] = c * v0 + js_ * v1; - shiftedState[indices[3]] = js_ * v0 + c * v1; - } -} - -const double Pennylane::CRotationXGate::generatorScalingFactor{-0.5}; - -void Pennylane::CRotationXGate::applyGenerator( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices) { - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - shiftedState[indices[0]] = shiftedState[indices[1]] = 0; - swap(shiftedState[indices[2]], shiftedState[indices[3]]); - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::CRotationYGate::label = "CRY"; - -Pennylane::CRotationYGate -Pennylane::CRotationYGate::create(const vector ¶meters) { - validateLength(Pennylane::CRotationYGate::label, parameters, 1); - return Pennylane::CRotationYGate(parameters[0]); -} - -Pennylane::CRotationYGate::CRotationYGate(double rotationAngle) - : c(std::cos(rotationAngle / 2), 0), - s(std::sin(rotationAngle / 2), 0), matrix{1, 0, 0, 0, 0, 1, 0, 0, - 0, 0, c, -s, 0, 0, s, c} {} - -void Pennylane::CRotationYGate::applyKernel( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices, bool inverse) { - CplxType s_ = (inverse == true) ? -s : s; - - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - CplxType v0 = shiftedState[indices[2]]; - CplxType v1 = shiftedState[indices[3]]; - shiftedState[indices[2]] = c * v0 - s_ * v1; - shiftedState[indices[3]] = s_ * v0 + c * v1; - } -} - -const double Pennylane::CRotationYGate::generatorScalingFactor{-0.5}; - -void Pennylane::CRotationYGate::applyGenerator( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices) { - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - CplxType v0 = shiftedState[indices[0]]; - shiftedState[indices[0]] = shiftedState[indices[1]] = 0; - shiftedState[indices[2]] = -IMAG * shiftedState[indices[3]]; - shiftedState[indices[3]] = IMAG * v0; - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::CRotationZGate::label = "CRZ"; - -Pennylane::CRotationZGate -Pennylane::CRotationZGate::create(const vector ¶meters) { - validateLength(Pennylane::CRotationZGate::label, parameters, 1); - return Pennylane::CRotationZGate(parameters[0]); -} - -Pennylane::CRotationZGate::CRotationZGate(double rotationAngle) - : first(std::pow(M_E, CplxType(0, -rotationAngle / 2))), - second(std::pow(M_E, CplxType(0, rotationAngle / 2))), - matrix{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, first, 0, 0, 0, 0, second} {} - -void Pennylane::CRotationZGate::applyKernel( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices, bool inverse) { - CplxType shift1 = first; - CplxType shift2 = second; - - if (inverse == true) { - shift1 = conj(first); - shift2 = conj(second); - } - - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - shiftedState[indices[2]] *= shift1; - shiftedState[indices[3]] *= shift2; - } -} - -const double Pennylane::CRotationZGate::generatorScalingFactor{-0.5}; - -void Pennylane::CRotationZGate::applyGenerator( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices) { - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - shiftedState[indices[0]] = shiftedState[indices[1]] = 0; - shiftedState[indices[3]] *= -1; - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::CGeneralRotationGate::label = "CRot"; - -Pennylane::CGeneralRotationGate -Pennylane::CGeneralRotationGate::create(const vector ¶meters) { - validateLength(Pennylane::CGeneralRotationGate::label, parameters, 3); - return Pennylane::CGeneralRotationGate(parameters[0], parameters[1], - parameters[2]); -} - -Pennylane::CGeneralRotationGate::CGeneralRotationGate(double phi, double theta, - double omega) - : c(std::cos(theta / 2), 0), s(std::sin(theta / 2), 0), - r1(c * std::pow(M_E, CplxType(0, (-phi - omega) / 2))), - r2(-s * std::pow(M_E, CplxType(0, (phi - omega) / 2))), - r3(s * std::pow(M_E, CplxType(0, (-phi + omega) / 2))), - r4(c * std::pow(M_E, CplxType(0, (phi + omega) / 2))), - matrix{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, r1, r2, 0, 0, r3, r4} {} - -void Pennylane::CGeneralRotationGate::applyKernel( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices, bool inverse) { - CplxType t1 = r1; - CplxType t2 = r2; - CplxType t3 = r3; - CplxType t4 = r4; - - if (inverse == true) { - t1 = conj(r1); - t2 *= -1; - t3 *= -1; - t4 = conj(t4); - } - - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - CplxType v0 = shiftedState[indices[2]]; - CplxType v1 = shiftedState[indices[3]]; - shiftedState[indices[2]] = t1 * v0 + t2 * v1; - shiftedState[indices[3]] = t3 * v0 + t4 * v1; - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::CPhaseShiftGate::label = "ControlledPhaseShift"; - -Pennylane::CPhaseShiftGate -Pennylane::CPhaseShiftGate::create(const vector ¶meters) { - validateLength(Pennylane::CPhaseShiftGate::label, parameters, 1); - return Pennylane::CPhaseShiftGate(parameters[0]); -} - -Pennylane::CPhaseShiftGate::CPhaseShiftGate(double phi) - : shift(std::pow(M_E, CplxType(0, phi))), matrix{1, 0, 0, 0, 0, 1, - 0, 0, 0, 0, 1, 0, - 0, 0, 0, shift} {} - -void Pennylane::CPhaseShiftGate::applyKernel( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices, bool inverse) { - const CplxType s = inverse ? conj(shift) : shift; - - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - shiftedState[indices[3]] *= s; - } -} - -const double Pennylane::CPhaseShiftGate::generatorScalingFactor{1.0}; - -void Pennylane::CPhaseShiftGate::applyGenerator( - const StateVector &state, - - const std::vector &indices, - const std::vector &externalIndices) { - - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - shiftedState[indices[0]] = 0; - shiftedState[indices[1]] = 0; - shiftedState[indices[2]] = 0; - } -} - -// ------------------------------------------------------------------------------------------------------------- - -Pennylane::ThreeQubitGate::ThreeQubitGate() : AbstractGate(3) {} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::ToffoliGate::label = "Toffoli"; - -Pennylane::ToffoliGate -Pennylane::ToffoliGate::create(const vector ¶meters) { - validateLength(Pennylane::ToffoliGate::label, parameters, 0); - return Pennylane::ToffoliGate(); -} - -const std::vector Pennylane::ToffoliGate::matrix{ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0}; - -void Pennylane::ToffoliGate::applyKernel( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices, bool) { - // gate is its own inverse - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - swap(shiftedState[indices[6]], shiftedState[indices[7]]); - } -} - -// ------------------------------------------------------------------------------------------------------------- - -const string Pennylane::CSWAPGate::label = "CSWAP"; - -Pennylane::CSWAPGate -Pennylane::CSWAPGate::create(const vector ¶meters) { - validateLength(Pennylane::CSWAPGate::label, parameters, 0); - return Pennylane::CSWAPGate(); -} - -const std::vector Pennylane::CSWAPGate::matrix{ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; - -void Pennylane::CSWAPGate::applyKernel( - const StateVector &state, const std::vector &indices, - const std::vector &externalIndices, bool) { - // gate is its own inverse - for (const size_t &externalIndex : externalIndices) { - CplxType *shiftedState = state.arr + externalIndex; - swap(shiftedState[indices[5]], shiftedState[indices[6]]); - } -} - -// ------------------------------------------------------------------------------------------------------------- - -template -static void addToDispatchTable( - map(const vector &)>> - &dispatchTable) { - dispatchTable.emplace( - GateType::label, [](const vector ¶meters) { - return make_unique(GateType::create(parameters)); - }); -} - -static map( - const vector &)>> -createDispatchTable() { - map(const vector &)>> - dispatchTable; - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - addToDispatchTable(dispatchTable); - return dispatchTable; -} - -static const map( - const vector &)>> - dispatchTable = createDispatchTable(); - -unique_ptr -Pennylane::constructGate(const string &label, - const vector ¶meters) { - auto dispatchTableIterator = dispatchTable.find(label); - if (dispatchTableIterator == dispatchTable.end()) - throw std::invalid_argument(label + " is not a supported gate type"); - - return dispatchTableIterator->second(parameters); -} diff --git a/pennylane_lightning/src/Gates.hpp b/pennylane_lightning/src/Gates.hpp index 619e038721..6b0e7ceeec 100644 --- a/pennylane_lightning/src/Gates.hpp +++ b/pennylane_lightning/src/Gates.hpp @@ -1,426 +1,216 @@ -// Copyright 2021 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 quantum gates and their actions. - */ #pragma once -#include +#include +#include #include -#include "StateVector.hpp" -#include "typedefs.hpp" +#include "Util.hpp" -namespace Pennylane { - -const double SQRT2INV = 0.7071067811865475; -const CplxType IMAG = CplxType(0, 1); - -class AbstractGate { - public: - const int numQubits; - const size_t length; - - protected: - AbstractGate(int numQubits); - - public: - /** - * @return the matrix representation for the gate as a one-dimensional - * vector. - */ - virtual const std::vector &asMatrix() = 0; - - /** - * Generic matrix-multiplication kernel - */ - virtual void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, - bool inverse); - - /** - * Kernel for applying the generator of an operation - */ - virtual void applyGenerator(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices); - - /** - * Scaling factor applied to the generator operation - */ - static const double generatorScalingFactor; -}; - -// Single-qubit gates: - -class SingleQubitGate : public AbstractGate { - protected: - SingleQubitGate(); -}; - -class XGate : public SingleQubitGate { - private: - static const std::vector matrix; - - public: - static const std::string label; - static XGate create(const std::vector ¶meters); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); -}; - -class YGate : public SingleQubitGate { - private: - static const std::vector matrix; - - public: - static const std::string label; - static YGate create(const std::vector ¶meters); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); -}; - -class ZGate : public SingleQubitGate { - private: - static const std::vector matrix; - - public: - static const std::string label; - static ZGate create(const std::vector ¶meters); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); -}; - -class HadamardGate : public SingleQubitGate { - private: - static const std::vector matrix; - - public: - static const std::string label; - static HadamardGate create(const std::vector ¶meters); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); -}; - -class SGate : public SingleQubitGate { - private: - static const std::vector matrix; - - public: - static const std::string label; - static SGate create(const std::vector ¶meters); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); -}; - -class TGate : public SingleQubitGate { - private: - static const CplxType shift; - static const std::vector matrix; - - public: - static const std::string label; - static TGate create(const std::vector ¶meters); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); -}; - -class RotationXGate : public SingleQubitGate { - private: - const CplxType c, js; - const std::vector matrix; - - public: - static const std::string label; - static RotationXGate create(const std::vector ¶meters); - RotationXGate(double rotationAngle); - inline const std::vector &asMatrix() { return matrix; } - void applyGenerator(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices); - static const double generatorScalingFactor; - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); -}; - -class RotationYGate : public SingleQubitGate { - private: - const CplxType c, s; - const std::vector matrix; - - public: - static const std::string label; - static RotationYGate create(const std::vector ¶meters); - RotationYGate(double rotationAngle); - inline const std::vector &asMatrix() { return matrix; } - void applyGenerator(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices); - static const double generatorScalingFactor; - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); -}; - -class RotationZGate : public SingleQubitGate { - private: - const CplxType first, second; - const std::vector matrix; - - public: - static const std::string label; - static RotationZGate create(const std::vector ¶meters); - RotationZGate(double rotationAngle); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); - void applyGenerator(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices); - static const double generatorScalingFactor; -}; - -class PhaseShiftGate : public SingleQubitGate { - private: - const CplxType shift; - const std::vector matrix; - - public: - static const std::string label; - static PhaseShiftGate create(const std::vector ¶meters); - PhaseShiftGate(double rotationAngle); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); - void applyGenerator(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices); - static const double generatorScalingFactor; -}; - -class GeneralRotationGate : public SingleQubitGate { - private: - const CplxType c, s, r1, r2, r3, r4; - const std::vector matrix; - - public: - static const std::string label; - static GeneralRotationGate create(const std::vector ¶meters); - GeneralRotationGate(double phi, double theta, double omega); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); -}; - -// Two-qubit gates - -class TwoQubitGate : public AbstractGate { - protected: - TwoQubitGate(); -}; - -class CNOTGate : public TwoQubitGate { - private: - static const std::vector matrix; - - public: - static const std::string label; - static CNOTGate create(const std::vector ¶meters); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); -}; - -class SWAPGate : public TwoQubitGate { - private: - static const std::vector matrix; - - public: - static const std::string label; - static SWAPGate create(const std::vector ¶meters); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); -}; - -class CZGate : public TwoQubitGate { - private: - static const std::vector matrix; - - public: - static const std::string label; - static CZGate create(const std::vector ¶meters); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); -}; - -class CRotationXGate : public TwoQubitGate { - private: - const CplxType c, js; - const std::vector matrix; - - public: - static const std::string label; - static CRotationXGate create(const std::vector ¶meters); - CRotationXGate(double rotationAngle); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); - void applyGenerator(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices); - static const double generatorScalingFactor; -}; - -class CRotationYGate : public TwoQubitGate { - private: - const CplxType c, s; - const std::vector matrix; - - public: - static const std::string label; - static CRotationYGate create(const std::vector ¶meters); - CRotationYGate(double rotationAngle); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); - void applyGenerator(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices); - static const double generatorScalingFactor; -}; - -class CRotationZGate : public TwoQubitGate { - private: - const CplxType first, second; - const std::vector matrix; - - public: - static const std::string label; - static CRotationZGate create(const std::vector ¶meters); - CRotationZGate(double rotationAngle); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); - void applyGenerator(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices); - static const double generatorScalingFactor; -}; - -class CGeneralRotationGate : public TwoQubitGate { - private: - const CplxType c, s, r1, r2, r3, r4; - const std::vector matrix; - - public: - static const std::string label; - static CGeneralRotationGate create(const std::vector ¶meters); - CGeneralRotationGate(double phi, double theta, double omega); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); -}; - -class CPhaseShiftGate : public TwoQubitGate { - private: - const CplxType shift; - const std::vector matrix; - - public: - static const std::string label; - static CPhaseShiftGate create(const std::vector ¶meters); - CPhaseShiftGate(double phi); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); - void applyGenerator(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices); - static const double generatorScalingFactor; -}; - -// Three-qubit gates -class ThreeQubitGate : public AbstractGate { - protected: - ThreeQubitGate(); -}; - -class ToffoliGate : public ThreeQubitGate { - private: - static const std::vector matrix; - - public: - static const std::string label; - static ToffoliGate create(const std::vector ¶meters); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); -}; - -class CSWAPGate : public ThreeQubitGate { - private: - static const std::vector matrix; - - public: - static const std::string label; - static CSWAPGate create(const std::vector ¶meters); - inline const std::vector &asMatrix() { return matrix; } - void applyKernel(const StateVector &state, - const std::vector &indices, - const std::vector &externalIndices, bool inverse); -}; - -/** - * Produces the requested gate, defined by a label and the list of parameters - * - * @param label unique string corresponding to a gate type - * @param parameters defines the gate parameterisation (may be zero-length for - * some gates) - * @return the gate wrapped in std::unique_ptr - * @throws std::invalid_argument thrown if the gate type is not defined, or if - * the number of parameters to the gate is incorrect - */ -std::unique_ptr -constructGate(const std::string &label, const std::vector ¶meters); +namespace { +using namespace Pennylane::Util; +} +namespace Pennylane { +namespace Gates { + +template static constexpr std::vector> getPauliX() { + return {ZERO(), ONE(), ONE(), ZERO()}; +} + +template static constexpr std::vector> getPauliY() { + return {ZERO(), -IMAG(), IMAG(), ZERO()}; +} + +template static constexpr std::vector> getPauliZ() { + return {ONE(), ZERO(), ZERO(), -ONE()}; +} + +template static constexpr std::vector> getHadamard() { + return {INVSQRT2(), INVSQRT2(), INVSQRT2(), -INVSQRT2()}; +} + +template static constexpr std::vector> getS() { + return {ONE(), ZERO(), ZERO(), IMAG()}; +} + +template static constexpr std::vector> getT() { + return {ONE(), ZERO(), ZERO(), IMAG()}; +} + +template static constexpr std::vector> getCNOT() { + return {ONE(), ZERO(), ZERO(), ZERO(), ZERO(), ONE(), + ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), ONE(), + ZERO(), ZERO(), ONE(), ZERO()}; +} + +template static constexpr std::vector> getSWAP() { + return {ONE(), ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), + ONE(), ZERO(), ZERO(), ONE(), ZERO(), ZERO(), + ZERO(), ZERO(), ZERO(), ONE()}; +} + +template static constexpr std::vector> getCZ() { + return {ONE(), ZERO(), ZERO(), ZERO(), ZERO(), ONE(), + ZERO(), ZERO(), ZERO(), ZERO(), ONE(), ZERO(), + ZERO(), ZERO(), ZERO(), -ONE()}; +} + +template static constexpr std::vector> getCSWAP() { + return {ONE(), ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), + ZERO(), ZERO(), ZERO(), ONE(), ZERO(), ZERO(), + ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), + ONE(), ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), + ZERO(), ZERO(), ZERO(), ONE(), ZERO(), ZERO(), + ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), + ONE(), ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), + ZERO(), ZERO(), ZERO(), ZERO(), ONE(), ZERO(), + ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), ONE(), + ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), + ZERO(), ZERO(), ZERO(), ONE()}; +} +template static constexpr std::vector> getToffoli() { + return {ONE(), ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), + ZERO(), ZERO(), ZERO(), ONE(), ZERO(), ZERO(), + ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), + ONE(), ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), + ZERO(), ZERO(), ZERO(), ONE(), ZERO(), ZERO(), + ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), + ONE(), ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), + ZERO(), ZERO(), ZERO(), ONE(), ZERO(), ZERO(), + ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), ZERO(), + ZERO(), ONE(), ZERO(), ZERO(), ZERO(), ZERO(), + ZERO(), ZERO(), ONE(), ZERO()}; +} + +template +static const std::vector> getPhaseShift(U angle) { + return {ONE(), ZERO(), ZERO(), std::exp(IMAG() * angle)}; +} + +template +static const std::vector> +getPhaseShift(const std::vector ¶ms) { + return getPhaseShift(params.front()); +} + +template +static const std::vector> getRX(U angle) { + const std::complex c(std::cos(angle / 2), 0); + const std::complex js(0, -std::sin(angle / 2)); + return {c, js, js, c}; +} + +template +static const std::vector> getRX(const std::vector ¶ms) { + return getRX(params.front()); +} + +template +static const std::vector> getRY(U angle) { + const std::complex c(std::cos(angle / 2), 0); + const std::complex s(std::sin(angle / 2), 0); + return {c, -s, s, c}; +} + +template +static const std::vector> getRY(const std::vector ¶ms) { + return getRY(params.front()); +} + +template +static const std::vector> getRZ(U angle) { + return {std::exp(-IMAG() * (angle / 2)), ZERO(), ZERO(), + std::exp(IMAG() * (angle / 2))}; +} + +template +static const std::vector getRZ(const std::vector ¶ms) { + return getRZ(params.front()); +} + +template +static const std::vector> getRot(U phi, U theta, U omega) { + const std::complex c(std::cos(theta / 2), 0), s(std::sin(theta / 2), 0); + const U p{phi + omega}, m{phi - omega}; + return {std::exp(static_cast(p / 2) * (-IMAG())) * c, + -std::exp(static_cast(m / 2) * IMAG()) * s, + std::exp(static_cast(m / 2) * (-IMAG())) * s, + std::exp(static_cast(p / 2) * IMAG()) * c}; +} + +template +static const std::vector> getRot(const std::vector ¶ms) { + return getRot(params[0], params[1], params[2]); +} + +template +static const std::vector> getCRX(U angle) { + const std::complex c(std::cos(angle / 2), 0), + js(0, std::sin(-angle / 2)); + return {ONE(), ZERO(), ZERO(), ZERO(), ZERO(), ONE(), + ZERO(), ZERO(), ZERO(), ZERO(), c, js, + ZERO(), ZERO(), js, c}; +} + +template +static const std::vector> getCRX(const std::vector ¶ms) { + return getCRX(params.front()); +} + +template +static const std::vector> getCRY(U angle) { + const std::complex c(std::cos(angle / 2), 0), s(std::sin(angle / 2), 0); + return {ONE(), ZERO(), ZERO(), ZERO(), ZERO(), ONE(), + ZERO(), ZERO(), ZERO(), ZERO(), c, -s, + ZERO(), ZERO(), s, c}; +} + +template +static const std::vector> getCRY(const std::vector ¶ms) { + return getCRY(params.front()); +} + +template +static const std::vector> getCRZ(U angle) { + const std::complex first = std::exp(-IMAG() * (angle / 2)); + const std::complex second = std::exp(IMAG() * (angle / 2)); + return {ONE(), ZERO(), ZERO(), ZERO(), ZERO(), + ONE(), ZERO(), ZERO(), ZERO(), ZERO(), + first, ZERO(), ZERO(), ZERO(), second}; +} + +template +static const std::vector> getCRZ(const std::vector ¶ms) { + return getCRZ(params.front()); +} + +template +static const std::vector> getCRot(U phi, U theta, U omega) { + const std::vector> rot = getRot(phi, theta, omega); + return {ONE(), ZERO(), ZERO(), ZERO(), ZERO(), ONE(), + ZERO(), ZERO(), ZERO(), ZERO(), rot[0], rot[1], + ZERO(), ZERO(), rot[2], rot[3]}; +} + +template +static const std::vector> +getCRot(const std::vector ¶ms) { + return getCRot(params[0], params[1], params[2]); +} + +template +static const std::vector> getControlledPhaseShift(U angle) { + return {ONE(), ZERO(), ZERO(), ZERO(), + ZERO(), ONE(), ZERO(), ZERO(), + ZERO(), ZERO(), ONE(), ZERO(), + ZERO(), ZERO(), ZERO(), std::exp(IMAG() * angle)}; +} + +template +static const std::vector> +getControlledPhaseShift(const std::vector ¶ms) { + return getControlledPhaseShift(params.front()); +} + +} // namespace Gates } // namespace Pennylane diff --git a/pennylane_lightning/src/StateVector.cpp b/pennylane_lightning/src/StateVector.cpp index eaef0af915..5bfb75913d 100644 --- a/pennylane_lightning/src/StateVector.cpp +++ b/pennylane_lightning/src/StateVector.cpp @@ -11,7 +11,9 @@ // 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. + #include "StateVector.hpp" -Pennylane::StateVector::StateVector(CplxType *arr, size_t length) - : arr(arr), length(length) {} +// explicit instantiation +template class Pennylane::StateVector; +template class Pennylane::StateVector; \ No newline at end of file diff --git a/pennylane_lightning/src/StateVector.hpp b/pennylane_lightning/src/StateVector.hpp index b2a936d059..a13a384495 100644 --- a/pennylane_lightning/src/StateVector.hpp +++ b/pennylane_lightning/src/StateVector.hpp @@ -15,19 +15,655 @@ * @file * Defines the class representation for quantum state vectors. */ + #pragma once -#include "typedefs.hpp" +// Required for compilation with MSVC +#define _USE_MATH_DEFINES + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Gates.hpp" +#include "Util.hpp" + +namespace { +using namespace std::placeholders; +using std::bind; +using std::size_t; +using std::string; +using std::vector; +}; // namespace namespace Pennylane { -class StateVector { +template class StateVector { + private: + using CFP_t = std::complex; + + using Func = + std::function &, const vector &, bool, + const vector &)>; + + using FMap = std::unordered_map; + + const FMap gates_; + const std::unordered_map gate_wires_; + + CFP_t *const arr_; + const size_t length_; + const size_t num_qubits_; public: - CplxType *const arr; - const size_t length; + StateVector() + : arr_{nullptr}, length_{0}, num_qubits_{0}, gate_wires_{}, gates_{} {}; + StateVector(CFP_t *arr, size_t length) + : arr_{arr}, length_{length}, num_qubits_{Util::log2(length_)}, + gate_wires_{ + {"PauliX", 1}, {"PauliY", 1}, {"PauliZ", 1}, + {"Hadamard", 1}, {"T", 1}, {"S", 1}, + {"RX", 1}, {"RY", 1}, {"RZ", 1}, + {"Rot", 1}, {"PhaseShift", 1}, {"ControlledPhaseShift", 2}, + {"CNOT", 2}, {"SWAP", 2}, {"CZ", 2}, + {"CRX", 2}, {"CRY", 2}, {"CRZ", 2}, + {"CRot", 2}, {"CSWAP", 3}, {"Toffoli", 3}}, + gates_{ + {"PauliX", + bind(&StateVector::applyPauliX_, this, _1, _2, _3, _4)}, + {"PauliY", + bind(&StateVector::applyPauliY_, this, _1, _2, _3, _4)}, + {"PauliZ", + bind(&StateVector::applyPauliZ_, this, _1, _2, _3, _4)}, + {"Hadamard", + bind(&StateVector::applyHadamard_, this, _1, _2, _3, _4)}, + {"S", bind(&StateVector::applyS_, this, _1, _2, _3, _4)}, + {"T", bind(&StateVector::applyT_, this, _1, _2, _3, _4)}, + {"CNOT", + bind(&StateVector::applyCNOT_, this, _1, _2, _3, _4)}, + {"SWAP", + bind(&StateVector::applySWAP_, this, _1, _2, _3, _4)}, + {"CSWAP", + bind(&StateVector::applyCSWAP_, this, _1, _2, _3, _4)}, + {"CZ", bind(&StateVector::applyCZ_, this, _1, _2, _3, _4)}, + {"Toffoli", + bind(&StateVector::applyToffoli_, this, _1, _2, _3, _4)}, + {"PhaseShift", bind(&StateVector::applyPhaseShift_, this, + _1, _2, _3, _4)}, + {"ControlledPhaseShift", + bind(&StateVector::applyControlledPhaseShift_, this, _1, + _2, _3, _4)}, + {"RX", bind(&StateVector::applyRX_, this, _1, _2, _3, _4)}, + {"RY", bind(&StateVector::applyRY_, this, _1, _2, _3, _4)}, + {"RZ", bind(&StateVector::applyRZ_, this, _1, _2, _3, _4)}, + {"Rot", + bind(&StateVector::applyRot_, this, _1, _2, _3, _4)}, + {"CRX", + bind(&StateVector::applyCRX_, this, _1, _2, _3, _4)}, + {"CRY", + bind(&StateVector::applyCRY_, this, _1, _2, _3, _4)}, + {"CRZ", + bind(&StateVector::applyCRZ_, this, _1, _2, _3, _4)}, + {"CRot", + bind(&StateVector::applyCRot_, this, _1, _2, _3, _4)}} {}; + + CFP_t *getData() { return arr_; } + std::size_t getLength() { return length_; } + + /** + * @brief Apply a single gate to the state-vector. + * + * @param opName Name of gate to apply. + * @param wires Wires to apply gate to. + * @param inverse Indicates whether to use inverse of gate. + * @param params Optional parameter list for parametric gates. + */ + void applyOperation(const string &opName, const vector &wires, + bool inverse = false, const vector ¶ms = {}) { + const auto gate = gates_.at(opName); + if (gate_wires_.at(opName) != wires.size()) + throw std::invalid_argument( + string("The gate of type ") + opName + " requires " + + std::to_string(gate_wires_.at(opName)) + " wires, but " + + std::to_string(wires.size()) + " were supplied"); + + const vector internalIndices = generateBitPatterns(wires); + const vector externalWires = getIndicesAfterExclusion(wires); + const vector externalIndices = + generateBitPatterns(externalWires); + + gate(internalIndices, externalIndices, inverse, params); + } + + /** + * @brief Apply multiple gates to the state-vector. + * + * @param ops Vector of gate names to be applied in order. + * @param wires Vector of wires on which to apply index-matched gate name. + * @param inverse Indicates whether gate at matched index is to be inverted. + * @param params Optional parameter data for index matched gates. + */ + void applyOperations(const vector &ops, + const vector> &wires, + const vector &inverse, + const vector> ¶ms) { + const size_t numOperations = ops.size(); + if (numOperations != wires.size() || numOperations != params.size()) + throw std::invalid_argument( + "Invalid arguments: number of operations, wires, and " + "parameters must all be equal"); + + for (size_t i = 0; i < numOperations; i++) { + applyOperation(ops[i], wires[i], inverse[i], params[i]); + } + } + /** + * @brief Apply multiple gates to the state-vector. + * + * @param ops Vector of gate names to be applied in order. + * @param wires Vector of wires on which to apply index-matched gate name. + * @param inverse Indicates whether gate at matched index is to be inverted. + * @param params Optional parameter data for index matched gates. + */ + void applyOperations(const vector &ops, + const vector> &wires, + const vector &inverse) { + const size_t numOperations = ops.size(); + if (numOperations != wires.size()) + throw std::invalid_argument( + "Invalid arguments: number of operations, wires, and " + "parameters must all be equal"); + + for (size_t i = 0; i < numOperations; i++) { + applyOperation(ops[i], wires[i], inverse[i]); + } + } + + /** + * @brief Get indices not participating in operation. + * + * @param indicesToExclude + * @return vector + */ + vector static getIndicesAfterExclusion( + const vector &indicesToExclude, size_t num_qubits) { + std::set indices; + for (size_t i = 0; i < num_qubits; i++) { + indices.emplace(i); + } + for (const size_t &excludedIndex : indicesToExclude) { + indices.erase(excludedIndex); + } + return {indices.begin(), indices.end()}; + } + vector + getIndicesAfterExclusion(const vector &indicesToExclude) { + return getIndicesAfterExclusion(indicesToExclude, num_qubits_); + } + + /** + * @brief Generate bit patterns for applying operations. + * + * @param qubitIndices Indices of the qubits to apply operations. + * @return vector + */ + static vector + generateBitPatterns(const vector &qubitIndices, size_t num_qubits) { + vector indices; + indices.reserve(Util::exp2(qubitIndices.size())); + indices.emplace_back(0); + + for (auto index_it = qubitIndices.rbegin(); + index_it != qubitIndices.rend(); index_it++) { + const size_t value = + Util::maxDecimalForQubit(*index_it, num_qubits); + const size_t currentSize = indices.size(); + for (size_t j = 0; j < currentSize; j++) { + indices.emplace_back(indices[j] + value); + } + } + return indices; + } + + vector generateBitPatterns(const vector &qubitIndices) { + return generateBitPatterns(qubitIndices, num_qubits_); + } + + // Apply Gates + void applyUnitary(const vector &matrix, + const vector &indices, + const vector &externalIndices, bool inverse) { + if (indices.size() != length_) + throw std::out_of_range( + "The given indices do not match the state-vector length."); + + vector v(indices.size()); + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + // Gather + size_t pos = 0; + for (const size_t &index : indices) { + v[pos] = shiftedState[index]; + pos++; + } + + // Apply + scatter + for (size_t i = 0; i < indices.size(); i++) { + size_t index = indices[i]; + shiftedState[index] = 0; + + if (inverse == true) { + for (size_t j = 0; j < indices.size(); j++) { + const size_t baseIndex = j * indices.size(); + shiftedState[index] += + conj(matrix[baseIndex + i]) * v[j]; + } + } else { + const size_t baseIndex = i * indices.size(); + for (size_t j = 0; j < indices.size(); j++) { + shiftedState[index] += matrix[baseIndex + j] * v[j]; + } + } + } + } + } + + void applyPauliX(const vector &indices, + const vector &externalIndices, bool inverse) { + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + std::swap(shiftedState[indices[0]], shiftedState[indices[1]]); + } + } + + void applyPauliY(const vector &indices, + const vector &externalIndices, bool inverse) { + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + CFP_t v0 = shiftedState[indices[0]]; + shiftedState[indices[0]] = + -Util::IMAG() * shiftedState[indices[1]]; + shiftedState[indices[1]] = Util::IMAG() * v0; + } + } + void applyPauliZ(const vector &indices, + const vector &externalIndices, bool inverse) { + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + shiftedState[indices[1]] *= -1; + } + } + + void applyHadamard(const vector &indices, + const vector &externalIndices, bool inverse) { + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + + const CFP_t v0 = shiftedState[indices[0]]; + const CFP_t v1 = shiftedState[indices[1]]; + + shiftedState[indices[0]] = Util::INVSQRT2() * (v0 + v1); + shiftedState[indices[1]] = Util::INVSQRT2() * (v0 - v1); + } + } + + void applyS(const vector &indices, + const vector &externalIndices, bool inverse) { + const CFP_t shift = + (inverse == true) ? -Util::IMAG() : Util::IMAG(); + + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + shiftedState[indices[1]] *= shift; + } + } + + void applyT(const vector &indices, + const vector &externalIndices, bool inverse) { + + const CFP_t shift = (inverse == true) + ? std::conj(std::exp(CFP_t(0, M_PI / 4))) + : std::exp(CFP_t(0, M_PI / 4)); + + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + shiftedState[indices[1]] *= shift; + } + } + + template + void applyRX(const vector &indices, + const vector &externalIndices, bool inverse, + Param_t angle) { + const CFP_t c(std::cos(angle / 2), 0); + + const CFP_t js = (inverse == true) ? CFP_t(0, -std::sin(-angle / 2)) + : CFP_t(0, std::sin(-angle / 2)); + + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + const CFP_t v0 = shiftedState[indices[0]]; + const CFP_t v1 = shiftedState[indices[1]]; + shiftedState[indices[0]] = c * v0 + js * v1; + shiftedState[indices[1]] = js * v0 + c * v1; + } + } + + template + void applyRY(const vector &indices, + const vector &externalIndices, bool inverse, + Param_t angle) { + const CFP_t c(std::cos(angle / 2), 0); + const CFP_t s = (inverse == true) ? CFP_t(-std::sin(angle / 2), 0) + : CFP_t(std::sin(angle / 2), 0); + + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + const CFP_t v0 = shiftedState[indices[0]]; + const CFP_t v1 = shiftedState[indices[1]]; + shiftedState[indices[0]] = c * v0 - s * v1; + shiftedState[indices[1]] = s * v0 + c * v1; + } + } + + template + void applyRZ(const vector &indices, + const vector &externalIndices, bool inverse, + Param_t angle) { + const CFP_t first = std::exp(CFP_t(0, -angle / 2)); + const CFP_t second = std::exp(CFP_t(0, angle / 2)); + const CFP_t shift1 = (inverse == true) ? std::conj(first) : first; + const CFP_t shift2 = (inverse == true) ? std::conj(second) : second; + + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + shiftedState[indices[0]] *= shift1; + shiftedState[indices[1]] *= shift2; + } + } + + template + void applyPhaseShift(const vector &indices, + const vector &externalIndices, bool inverse, + Param_t angle) { + const CFP_t s = inverse ? conj(std::exp(CFP_t(0, angle))) + : std::exp(CFP_t(0, angle)); + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + shiftedState[indices[1]] *= s; + } + } + + template + void applyControlledPhaseShift(const std::vector &indices, + const std::vector &externalIndices, + bool inverse, Param_t angle) { + const CFP_t s = inverse ? conj(std::exp(CFP_t(0, angle))) + : std::exp(CFP_t(0, angle)); + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + shiftedState[indices[3]] *= s; + } + } + + template + void applyRot(const vector &indices, + const vector &externalIndices, bool inverse, + Param_t phi, Param_t theta, Param_t omega) { + const vector rot = Gates::getRot(phi, theta, omega); + + const CFP_t t1 = (inverse == true) ? conj(rot[0]) : rot[0]; + const CFP_t t2 = (inverse == true) ? -rot[1] : rot[1]; + const CFP_t t3 = (inverse == true) ? -rot[2] : rot[2]; + const CFP_t t4 = (inverse == true) ? conj(rot[3]) : rot[3]; + + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + const CFP_t v0 = shiftedState[indices[0]]; + const CFP_t v1 = shiftedState[indices[1]]; + shiftedState[indices[0]] = t1 * v0 + t2 * v1; + shiftedState[indices[1]] = t3 * v0 + t4 * v1; + } + } + + void applyCNOT(const vector &indices, + const vector &externalIndices, bool inverse) { + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + std::swap(shiftedState[indices[2]], shiftedState[indices[3]]); + } + } + + void applySWAP(const vector &indices, + const vector &externalIndices, bool inverse) { + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + swap(shiftedState[indices[1]], shiftedState[indices[2]]); + } + } + void applyCZ(const vector &indices, + const vector &externalIndices, bool inverse) { + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + shiftedState[indices[3]] *= -1; + } + } + + template + void applyCRX(const vector &indices, + const vector &externalIndices, bool inverse, + Param_t angle) { + const CFP_t c(std::cos(angle / 2), 0); + const CFP_t js = (inverse == true) ? CFP_t(0, -std::sin(-angle / 2)) + : CFP_t(0, std::sin(-angle / 2)); + + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + const CFP_t v0 = shiftedState[indices[2]]; + const CFP_t v1 = shiftedState[indices[3]]; + shiftedState[indices[2]] = c * v0 + js * v1; + shiftedState[indices[3]] = js * v0 + c * v1; + } + } + + template + void applyCRY(const vector &indices, + const vector &externalIndices, bool inverse, + Param_t angle) { + const CFP_t c(std::cos(angle / 2), 0); + const CFP_t s = (inverse == true) ? CFP_t(-std::sin(angle / 2), 0) + : CFP_t(std::sin(angle / 2), 0); + + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + const CFP_t v0 = shiftedState[indices[2]]; + const CFP_t v1 = shiftedState[indices[3]]; + shiftedState[indices[2]] = c * v0 - s * v1; + shiftedState[indices[3]] = s * v0 + c * v1; + } + } + + template + void applyCRZ(const vector &indices, + const vector &externalIndices, bool inverse, + Param_t angle) { + const CFP_t m00 = (inverse == true) + ? std::conj(std::exp(CFP_t(0, -angle / 2))) + : std::exp(CFP_t(0, -angle / 2)); + const CFP_t m11 = (inverse == true) + ? std::conj(std::exp(CFP_t(0, angle / 2))) + : std::exp(CFP_t(0, angle / 2)); + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + shiftedState[indices[2]] *= m00; + shiftedState[indices[3]] *= m11; + } + } + + template + void applyCRot(const vector &indices, + const vector &externalIndices, bool inverse, + Param_t phi, Param_t theta, Param_t omega) { + const auto rot = Gates::getRot(phi, theta, omega); + + const CFP_t t1 = (inverse == true) ? conj(rot[0]) : rot[0]; + const CFP_t t2 = (inverse == true) ? -rot[1] : rot[1]; + const CFP_t t3 = (inverse == true) ? -rot[2] : rot[2]; + const CFP_t t4 = (inverse == true) ? conj(rot[3]) : rot[3]; + + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + const CFP_t v0 = shiftedState[indices[2]]; + const CFP_t v1 = shiftedState[indices[3]]; + shiftedState[indices[2]] = t1 * v0 + t2 * v1; + shiftedState[indices[3]] = t3 * v0 + t4 * v1; + } + } + + void applyToffoli(const vector &indices, + const vector &externalIndices, bool inverse) { + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + std::swap(shiftedState[indices[6]], shiftedState[indices[7]]); + } + } + + void applyCSWAP(const vector &indices, + const vector &externalIndices, bool inverse) { + for (const size_t &externalIndex : externalIndices) { + CFP_t *shiftedState = arr_ + externalIndex; + std::swap(shiftedState[indices[5]], shiftedState[indices[6]]); + } + } - StateVector(CplxType *arr, size_t length); + //***********************************************************************// + // Internal utility functions for opName dispatch use only. + //***********************************************************************// + private: + inline void applyPauliX_(const vector &indices, + const vector &externalIndices, + bool inverse, const vector ¶ms) { + static_cast(params); + applyPauliX(indices, externalIndices, inverse); + } + inline void applyPauliY_(const vector &indices, + const vector &externalIndices, + bool inverse, const vector ¶ms) { + static_cast(params); + applyPauliY(indices, externalIndices, inverse); + } + inline void applyPauliZ_(const vector &indices, + const vector &externalIndices, + bool inverse, const vector ¶ms) { + static_cast(params); + applyPauliZ(indices, externalIndices, inverse); + } + inline void applyHadamard_(const vector &indices, + const vector &externalIndices, + bool inverse, const vector ¶ms) { + static_cast(params); + applyHadamard(indices, externalIndices, inverse); + } + inline void applyS_(const vector &indices, + const vector &externalIndices, bool inverse, + const vector ¶ms) { + static_cast(params); + applyS(indices, externalIndices, inverse); + } + inline void applyT_(const vector &indices, + const vector &externalIndices, bool inverse, + const vector ¶ms) { + static_cast(params); + applyT(indices, externalIndices, inverse); + } + inline void applyRX_(const vector &indices, + const vector &externalIndices, bool inverse, + const vector ¶ms) { + applyRX(indices, externalIndices, inverse, params[0]); + } + inline void applyRY_(const vector &indices, + const vector &externalIndices, bool inverse, + const vector ¶ms) { + applyRY(indices, externalIndices, inverse, params[0]); + } + inline void applyRZ_(const vector &indices, + const vector &externalIndices, bool inverse, + const vector ¶ms) { + applyRZ(indices, externalIndices, inverse, params[0]); + } + inline void applyPhaseShift_(const vector &indices, + const vector &externalIndices, + bool inverse, const vector ¶ms) { + applyPhaseShift(indices, externalIndices, inverse, params[0]); + } + inline void + applyControlledPhaseShift_(const vector &indices, + const vector &externalIndices, + bool inverse, const vector ¶ms) { + applyControlledPhaseShift(indices, externalIndices, inverse, params[0]); + } + inline void applyRot_(const vector &indices, + const vector &externalIndices, bool inverse, + const vector ¶ms) { + applyRot(indices, externalIndices, inverse, params[0], params[1], + params[2]); + } + inline void applyCNOT_(const vector &indices, + const vector &externalIndices, bool inverse, + const vector ¶ms) { + static_cast(params); + applyCNOT(indices, externalIndices, inverse); + } + inline void applySWAP_(const vector &indices, + const vector &externalIndices, bool inverse, + const vector ¶ms) { + static_cast(params); + applySWAP(indices, externalIndices, inverse); + } + inline void applyCZ_(const vector &indices, + const vector &externalIndices, bool inverse, + const vector ¶ms) { + static_cast(params); + applyCZ(indices, externalIndices, inverse); + } + inline void applyCRX_(const vector &indices, + const vector &externalIndices, bool inverse, + const vector ¶ms) { + applyCRX(indices, externalIndices, inverse, params[0]); + } + inline void applyCRY_(const vector &indices, + const vector &externalIndices, bool inverse, + const vector ¶ms) { + applyCRY(indices, externalIndices, inverse, params[0]); + } + inline void applyCRZ_(const vector &indices, + const vector &externalIndices, bool inverse, + const vector ¶ms) { + applyCRZ(indices, externalIndices, inverse, params[0]); + } + inline void applyCRot_(const vector &indices, + const vector &externalIndices, bool inverse, + const vector ¶ms) { + applyCRot(indices, externalIndices, inverse, params[0], params[1], + params[2]); + } + inline void applyToffoli_(const vector &indices, + const vector &externalIndices, + bool inverse, const vector ¶ms) { + static_cast(params); + applyToffoli(indices, externalIndices, inverse); + } + inline void applyCSWAP_(const vector &indices, + const vector &externalIndices, bool inverse, + const vector ¶ms) { + static_cast(params); + applyCSWAP(indices, externalIndices, inverse); + } }; } // namespace Pennylane diff --git a/pennylane_lightning/src/Util.hpp b/pennylane_lightning/src/Util.hpp index 4a446df911..ff7051e339 100644 --- a/pennylane_lightning/src/Util.hpp +++ b/pennylane_lightning/src/Util.hpp @@ -18,19 +18,109 @@ #pragma once #include +#include +#include #include +#include #include #include +#include namespace Pennylane { +namespace Util { + +/** + * @brief Compile-time scalar real times complex number. + * + * @tparam T Precision of complex value and result. + * @tparam U Precision of real value. + * @param a Real scalar value. + * @param b Complex scalar value. + * @return constexpr std::complex + */ +template +inline static constexpr std::complex ConstMult(U a, std::complex b) { + return {a * b.real(), a * b.imag()}; +} +template +inline static constexpr std::complex ConstMult(std::complex a, + std::complex b) { + return {a.real() * b.real() - a.imag() * b.imag(), + a.real() * b.imag() + a.imag() * b.real()}; +} + /** - * Calculates 2^n for some integer n > 0 using bitshifts. + * @brief Return complex value 1+0i in the given precision. + * + * @tparam T Floating point precision type. Accepts `double` and `float`. + * @return constexpr std::complex{1,0} + */ +template inline static constexpr std::complex ONE() { + return {1, 0}; +} + +/** + * @brief Return complex value 0+0i in the given precision. + * + * @tparam T Floating point precision type. Accepts `double` and `float`. + * @return constexpr std::complex{0,0} + */ +template inline static constexpr std::complex ZERO() { + return {0, 0}; +} + +/** + * @brief Return complex value 0+1i in the given precision. + * + * @tparam T Floating point precision type. Accepts `double` and `float`. + * @return constexpr std::complex{0,1} + */ +template inline static constexpr std::complex IMAG() { + return {0, 1}; +} + +/** + * @brief Returns sqrt(2) as a compile-time constant. + * + * @tparam T Precision of result. `double`, `float` are accepted values. + * @return constexpr T sqrt(2) + */ +template inline static constexpr T SQRT2() { + if constexpr (std::is_same_v) { + return {0x1.6a09e6p+0f}; + } else { + return {0x1.6a09e667f3bcdp+0}; + } +} + +/** + * @brief Returns inverse sqrt(2) as a compile-time constant. + * + * @tparam T Precision of result. `double`, `float` are accepted values. + * @return constexpr T 1/sqrt(2) + */ +template inline static constexpr T INVSQRT2() { + return {1 / SQRT2()}; +} + +/** + * Calculates 2^n -1 for some integer n > 0 using bitshifts. * * @param n the exponent - * @return value of 2^n + * @return value of 2^n -1 */ -inline size_t exp2(const unsigned int &n) { return (size_t)1 << n; } +inline size_t exp2(const size_t &n) { return static_cast(1) << n; } + +/** + * @brief + * + * @param value + * @return size_t + */ +inline size_t log2(size_t value) { + return static_cast(std::log2(value)); +} /** * Calculates the decimal value for a qubit, assuming a big-endian convention. @@ -39,12 +129,12 @@ inline size_t exp2(const unsigned int &n) { return (size_t)1 << n; } * @param qubits the number of qubits in the circuit * @return decimal value for the qubit at specified index */ -inline size_t maxDecimalForQubit(const unsigned int qubitIndex, - const unsigned int qubits) { +inline size_t maxDecimalForQubit(size_t qubitIndex, size_t qubits) { assert(qubitIndex < qubits); return exp2(qubits - qubitIndex - 1); } +} // namespace Util } // namespace Pennylane // Helper similar to std::make_unique from c++14 @@ -56,6 +146,7 @@ std::unique_ptr make_unique(Args &&...args) { // Exception for functions that aren't implemented class NotImplementedException : public std::logic_error { public: - NotImplementedException() - : std::logic_error("Function is not implemented."){}; + explicit NotImplementedException(const std::string &fname) + : std::logic_error(std::string("Function is not implemented. ") + + fname){}; }; diff --git a/pennylane_lightning/src/tests/CMakeLists.txt b/pennylane_lightning/src/tests/CMakeLists.txt index 5af09c05e3..afb6be1c93 100644 --- a/pennylane_lightning/src/tests/CMakeLists.txt +++ b/pennylane_lightning/src/tests/CMakeLists.txt @@ -1,8 +1,7 @@ cmake_minimum_required(VERSION 3.14) - project(pennylane_lightning_tests) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) find_package(OpenMP) # Default build type for test code is Debug @@ -10,39 +9,36 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug) endif() - option(ENABLE_NATIVE "Enable native CPU build tuning" OFF) +Include(FetchContent) -include(FetchContent) FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG release-1.11.0 + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v2.13.1 ) -# For Windows: Prevent overriding the parent project's compiler/linker settings -set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) -FetchContent_MakeAvailable(googletest) -enable_testing() - -add_executable(cpptests lightning_apply_unittest.cpp - lightning_gates_unittest.cpp - lightning_util_unittest.cpp - "../Apply.cpp" - "../Gates.cpp") - -target_link_libraries(cpptests PRIVATE gtest_main) -if(OpenMP_CXX_FOUND) - target_link_libraries(cpptests PRIVATE OpenMP::OpenMP_CXX) -endif() -target_compile_options(cpptests PRIVATE "$<$:-Wall>") +FetchContent_MakeAvailable(Catch2) + +# Required for catch_discover_tests(). +list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/contrib) + +# Modify `ctest` to only run the supported subset of tests. +include(CTest) +include(Catch) + +add_executable(runner runner_main.cpp) +target_link_libraries(runner pennylane_lightning Catch2::Catch2) + +target_sources(runner PRIVATE Test_StateVector.cpp + Test_Bindings.cpp) + +target_compile_options(runner PRIVATE "$<$:-Wall>") if(ENABLE_NATIVE) - message(STATUS "ENABLE_NATIVE is ON. Use -march=native for cpptests.") - target_compile_options(cpptests PRIVATE -march=native) + message(STATUS "ENABLE_NATIVE is ON. Use -march=native for cpptests.") + target_compile_options(runner PRIVATE -march=native) endif() - -include(GoogleTest) -gtest_discover_tests(cpptests) +catch_discover_tests(runner) diff --git a/pennylane_lightning/src/tests/GateData.hpp b/pennylane_lightning/src/tests/GateData.hpp deleted file mode 100644 index 490c3ed5b7..0000000000 --- a/pennylane_lightning/src/tests/GateData.hpp +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2021 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 -#define _USE_MATH_DEFINES - -#include "../typedefs.hpp" - -#include -#include -#include - -using Pennylane::CplxType; -using std::vector; - -// Useful constants -const std::complex IMAG(0, 1); -const std::complex NEGATIVE_IMAG(0, -1); -const double SQRT_2 = sqrt(2); -const std::complex EXPONENT(0, M_PI / 4); - -// Non-parametrized single qubit gates -static const vector PauliX = {0, 1, 1, 0}; -static const vector PauliY = {0, NEGATIVE_IMAG, IMAG, 0}; -static const vector PauliZ = {1, 0, 0, -1}; -static const vector Hadamard = {1 / SQRT_2, 1 / SQRT_2, 1 / SQRT_2, - -1 / SQRT_2}; -static const vector S = {1, 0, 0, IMAG}; -static const vector T = {1, 0, 0, std::pow(M_E, EXPONENT)}; - -// Parametrized single qubit gates -vector RX(const vector &pars) { - double parameter = pars.at(0); - - const std::complex c(std::cos(parameter / 2), 0); - const std::complex js(0, std::sin(-parameter / 2)); - return {c, js, js, c}; -} - -vector RY(const vector &pars) { - double parameter = pars.at(0); - - const double c = std::cos(parameter / 2); - const double s = std::sin(parameter / 2); - return {c, -s, s, c}; -} - -vector RZ(const vector &pars) { - double parameter = pars.at(0); - const std::complex phase(0, -parameter / 2); - const std::complex phase_second(0, parameter / 2); - const std::complex first = std::pow(M_E, phase); - const std::complex second = std::pow(M_E, phase_second); - return {first, 0, 0, second}; -} - -vector PhaseShift(const vector &pars) { - double parameter = pars.at(0); - - const std::complex phase(0, parameter); - const std::complex shift = std::pow(M_E, phase); - - return {1, 0, 0, shift}; -} - -vector CPhaseShift(const vector &pars) { - double parameter = pars.at(0); - - const std::complex phase(0, parameter); - const std::complex shift = std::pow(M_E, phase); - - return {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, shift}; -} - -vector Rot(const vector &pars) { - double phi = pars.at(0); - double theta = pars.at(1); - double omega = pars.at(2); - - const std::complex e00(0, (-phi - omega) / 2); - const std::complex e10(0, (-phi + omega) / 2); - const std::complex e01(0, (phi - omega) / 2); - const std::complex e11(0, (phi + omega) / 2); - - const std::complex exp00 = std::pow(M_E, e00); - const std::complex exp10 = std::pow(M_E, e10); - const std::complex exp01 = std::pow(M_E, e01); - const std::complex exp11 = std::pow(M_E, e11); - - const double c = std::cos(theta / 2); - const double s = std::sin(theta / 2); - - return {exp00 * c, -exp01 * s, exp10 * s, exp11 * c}; -} - -vector CRX(const vector &pars) { - double parameter = pars.at(0); - - const std::complex c(std::cos(parameter / 2), 0); - const std::complex js(0, std::sin(-parameter / 2)); - - vector matrix = {1, 0, 0, 0, 0, 1, 0, 0, - 0, 0, c, js, 0, 0, js, c}; - - return matrix; -} - -vector CRY(const vector &pars) { - double parameter = pars.at(0); - - const double c = std::cos(parameter / 2); - const double s = std::sin(parameter / 2); - - vector matrix = {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, c, -s, 0, 0, s, c}; - - return matrix; -} - -vector CRZ(const vector &pars) { - double parameter = pars.at(0); - - const std::complex phase(0, -parameter / 2); - const std::complex phase_second(0, parameter / 2); - const std::complex first = std::pow(M_E, phase); - const std::complex second = std::pow(M_E, phase_second); - - vector matrix = {1, 0, 0, 0, 0, 1, 0, 0, - 0, 0, first, 0, 0, 0, 0, second}; - - return matrix; -} - -vector CRot(const vector &pars) { - double phi = pars.at(0); - double theta = pars.at(1); - double omega = pars.at(2); - - const std::complex e00(0, (-phi - omega) / 2); - const std::complex e10(0, (-phi + omega) / 2); - const std::complex e01(0, (phi - omega) / 2); - const std::complex e11(0, (phi + omega) / 2); - - const std::complex exp00 = std::pow(M_E, e00); - const std::complex exp10 = std::pow(M_E, e10); - const std::complex exp01 = std::pow(M_E, e01); - const std::complex exp11 = std::pow(M_E, e11); - - const double c = std::cos(theta / 2); - const double s = std::sin(theta / 2); - - vector matrix = {1, 0, 0, 0, 0, 1, - 0, 0, 0, 0, exp00 * c, -exp01 * s, - 0, 0, exp10 * s, exp11 * c}; - - return matrix; -} - -// Type alias for the functions of parametrized matrices -using pfunc_params = std::function(const vector &)>; - -static const vector CNOT = {1, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 1, 0, 0, 1, 0}; - -static const vector SWAP = {1, 0, 0, 0, 0, 0, 1, 0, - 0, 1, 0, 0, 0, 0, 0, 1}; - -static const vector CZ = {1, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 1, 0, 0, 0, 0, -1}; - -static const vector Toffoli = { - 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0}; - -static const vector CSWAP = { - 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; diff --git a/pennylane_lightning/src/tests/Makefile b/pennylane_lightning/src/tests/Makefile deleted file mode 100644 index c1a5d5348c..0000000000 --- a/pennylane_lightning/src/tests/Makefile +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright 2020 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. -CC = g++ -CFLAGS = -std=c++11 -g -Wall -fopenmp -fPIC -I/usr/include \ - -I../include -I$(GOOGLETEST_DIR)/include \ - -march=native - -LFLAGS = -fopenmp -L$(GOOGLETEST_DIR)/lib -lgtest - -all: test - -lightning_util_unittest.o: lightning_util_unittest.cpp - $(CC) $^ $(CFLAGS) -c - -Gates.o: ../Gates.cpp - $(CC) $^ $(CFLAGS) -c - -Apply.o: ../Apply.cpp - $(CC) $^ $(CFLAGS) -c - -lightning_gates_unittest.o: lightning_gates_unittest.cpp - $(CC) $^ $(CFLAGS) -c - -lightning_apply_unittest.o: lightning_apply_unittest.cpp - $(CC) $^ $(CFLAGS) -c - -gtest_main.o: gtest_main.cpp - $(CC) $^ $(CFLAGS) -c - -cpptests: gtest_main.o lightning_util_unittest.o lightning_gates_unittest.o Gates.o lightning_apply_unittest.o Apply.o - $(CC) $^ -o $@ $(LFLAGS) - -test: cpptests - ./cpptests - -clean: - rm -rf *~ cpptests *.out *.o *.so *.pyc *.mod - diff --git a/pennylane_lightning/src/tests/Test_Bindings.cpp b/pennylane_lightning/src/tests/Test_Bindings.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/pennylane_lightning/src/tests/Test_StateVector.cpp b/pennylane_lightning/src/tests/Test_StateVector.cpp new file mode 100644 index 0000000000..e2fa79b8e4 --- /dev/null +++ b/pennylane_lightning/src/tests/Test_StateVector.cpp @@ -0,0 +1,1167 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "Gates.hpp" +#include "StateVector.hpp" +#include "Util.hpp" + +using namespace Pennylane; + +/** + * @brief Utility function to compare complex statevector data. + * + * @tparam Data_t Floating point data-type. + * @param data1 StateVector data 1. + * @param data2 StateVector data 2. + * @return true Data are approximately equal. + * @return false Data are not approximately equal. + */ +template +inline bool isApproxEqual( + const std::vector &data1, const std::vector &data2, + const typename Data_t::value_type eps = + std::numeric_limits::epsilon() * 100) { + if (data1.size() != data2.size()) + return false; + + for (size_t i = 0; i < data1.size(); i++) { + if (data1[i].real() != Approx(data2[i].real()).epsilon(eps) || + data1[i].imag() != Approx(data2[i].imag()).epsilon(eps)) { + return false; + } + } + return true; +} + +template +void scaleVector(std::vector> &data, + std::complex scalar) { + std::transform( + data.begin(), data.end(), data.begin(), + [scalar](const std::complex &c) { return c * scalar; }); +} + +/** + * @brief Tests the constructability of the StateVector class. + * + */ +TEMPLATE_TEST_CASE("StateVector::StateVector", "[StateVector]", float, double) { + SECTION("StateVector") { + REQUIRE(std::is_constructible>::value); + } + SECTION("StateVector {}") { + REQUIRE(std::is_constructible>::value); + } + SECTION("StateVector {std::complex, size_t}") { + REQUIRE(std::is_constructible, + std::complex *, size_t>::value); + } + SECTION("StateVector cross types") { + if constexpr (!std::is_same_v) { + REQUIRE_FALSE( + std::is_constructible, + std::complex *, size_t>::value); + REQUIRE_FALSE( + std::is_constructible, + std::complex *, size_t>::value); + } else if constexpr (!std::is_same_v) { + REQUIRE_FALSE( + std::is_constructible, + std::complex *, size_t>::value); + REQUIRE_FALSE( + std::is_constructible, + std::complex *, size_t>::value); + } + } +} + +/** + * @brief Utility data-structure to assist with testing StateVector class + * + * @tparam fp_t Floating-point type. Supported options: float, double + */ +template struct SVData { + size_t num_qubits; + std::vector> cdata; + StateVector sv; + + explicit SVData(size_t num_qubits) + : num_qubits{num_qubits}, // qubit_indices{num_qubits}, + cdata(0b1 << num_qubits), sv{cdata.data(), cdata.size()} { + cdata[0] = std::complex{1, 0}; + } + explicit SVData(size_t num_qubits, + const std::vector> &cdata_input) + : num_qubits{num_qubits}, // qubit_indices{num_qubits}, + cdata(cdata_input), sv{cdata.data(), cdata.size()} {} + vector + getInternalIndices(const std::vector &qubit_indices) { + return sv.generateBitPatterns(qubit_indices); + } + vector + getExternalIndices(const std::vector &qubit_indices) { + vector externalWires = + sv.getIndicesAfterExclusion(qubit_indices); + vector externalIndices = sv.generateBitPatterns(externalWires); + return externalIndices; + } +}; + +TEST_CASE("StateVector::generateBitPatterns", "[StateVector]") { + const size_t num_qubits = 4; + SECTION("Qubit indices {}") { + auto bit_pattern = StateVector<>::generateBitPatterns({}, num_qubits); + CHECK(bit_pattern == std::vector{0}); + } + SECTION("Qubit indices {i}") { + for (size_t i = 0; i < num_qubits; i++) { + std::vector expected{0, 0b1UL << (num_qubits - i - 1)}; + auto bit_pattern = + StateVector<>::generateBitPatterns({i}, num_qubits); + CHECK(bit_pattern == expected); + } + } + SECTION("Qubit indices {i,i+1,i+2}") { + std::vector expected_123{0, 1, 2, 3, 4, 5, 6, 7}; + std::vector expected_012{0, 2, 4, 6, 8, 10, 12, 14}; + auto bit_pattern_123 = + StateVector<>::generateBitPatterns({1, 2, 3}, num_qubits); + auto bit_pattern_012 = + StateVector<>::generateBitPatterns({0, 1, 2}, num_qubits); + + CHECK(bit_pattern_123 == expected_123); + CHECK(bit_pattern_012 == expected_012); + } + SECTION("Qubit indices {0,2,3}") { + std::vector expected{0, 1, 2, 3, 8, 9, 10, 11}; + auto bit_pattern = + StateVector<>::generateBitPatterns({0, 2, 3}, num_qubits); + + CHECK(bit_pattern == expected); + } + SECTION("Qubit indices {3,1,0}") { + std::vector expected{0, 8, 4, 12, 1, 9, 5, 13}; + auto bit_pattern = + StateVector<>::generateBitPatterns({3, 1, 0}, num_qubits); + CHECK(bit_pattern == expected); + } +} + +TEST_CASE("StateVector::getIndicesAfterExclusion", "[StateVector]") { + const size_t num_qubits = 4; + SECTION("Qubit indices {}") { + std::vector expected{0, 1, 2, 3}; + auto indices = StateVector<>::getIndicesAfterExclusion({}, num_qubits); + CHECK(indices == expected); + } + SECTION("Qubit indices {i}") { + for (size_t i = 0; i < num_qubits; i++) { + std::vector expected{0, 1, 2, 3}; + expected.erase(expected.begin() + i); + + auto indices = + StateVector<>::getIndicesAfterExclusion({i}, num_qubits); + CHECK(indices == expected); + } + } + SECTION("Qubit indices {i,i+1,i+2}") { + std::vector expected_123{0}; + std::vector expected_012{3}; + auto indices_123 = + StateVector<>::getIndicesAfterExclusion({1, 2, 3}, num_qubits); + auto indices_012 = + StateVector<>::getIndicesAfterExclusion({0, 1, 2}, num_qubits); + + CHECK(indices_123 == expected_123); + CHECK(indices_012 == expected_012); + } + SECTION("Qubit indices {0,2,3}") { + std::vector expected{1}; + auto indices = + StateVector<>::getIndicesAfterExclusion({0, 2, 3}, num_qubits); + + CHECK(indices == expected); + } + SECTION("Qubit indices {3,1,0}") { + std::vector expected{2}; + auto indices = + StateVector<>::getIndicesAfterExclusion({3, 1, 0}, num_qubits); + + CHECK(indices == expected); + } +} + +TEMPLATE_TEST_CASE("StateVector::applyHadamard", "[StateVector]", float, + double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SECTION("Apply directly") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat{num_qubits}; + + auto int_idx = svdat.getInternalIndices({index}); + auto ext_idx = svdat.getExternalIndices({index}); + CHECK(svdat.cdata[0] == cp_t{1, 0}); + svdat.sv.applyHadamard(int_idx, ext_idx, false); + + cp_t expected = {1 / std::sqrt(2), 0}; + CHECK(expected.real() == Approx(svdat.cdata[0].real())); + CHECK(expected.imag() == Approx(svdat.cdata[0].imag())); + + CHECK( + expected.real() == + Approx( + svdat.cdata[0b1 << (svdat.num_qubits - index - 1)].real())); + CHECK( + expected.imag() == + Approx( + svdat.cdata[0b1 << (svdat.num_qubits - index - 1)].imag())); + } + } + SECTION("Apply using dispatcher") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat{num_qubits}; + CHECK(svdat.cdata[0] == cp_t{1, 0}); + svdat.sv.applyOperation("Hadamard", {index}, false); + + cp_t expected = {1 / std::sqrt(2), 0}; + + CHECK(expected.real() == Approx(svdat.cdata[0].real())); + CHECK(expected.imag() == Approx(svdat.cdata[0].imag())); + + CHECK( + expected.real() == + Approx( + svdat.cdata[0b1 << (svdat.num_qubits - index - 1)].real())); + CHECK( + expected.imag() == + Approx( + svdat.cdata[0b1 << (svdat.num_qubits - index - 1)].imag())); + } + } +} + +TEMPLATE_TEST_CASE("StateVector::applyPauliX", "[StateVector]", float, double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SECTION("Apply directly") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat{num_qubits}; + auto int_idx = svdat.getInternalIndices({index}); + auto ext_idx = svdat.getExternalIndices({index}); + CHECK(svdat.cdata[0] == Util::ONE()); + svdat.sv.applyPauliX(int_idx, ext_idx, false); + CHECK(svdat.cdata[0] == Util::ZERO()); + CHECK(svdat.cdata[0b1 << (svdat.num_qubits - index - 1)] == + Util::ONE()); + } + } + SECTION("Apply using dispatcher") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat{num_qubits}; + CHECK(svdat.cdata[0] == Util::ONE()); + svdat.sv.applyOperation("PauliX", {index}, false); + CHECK(svdat.cdata[0] == Util::ZERO()); + CHECK(svdat.cdata[0b1 << (svdat.num_qubits - index - 1)] == + Util::ONE()); + } + } +} + +TEMPLATE_TEST_CASE("StateVector::applyPauliY", "[StateVector]", float, double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SVData svdat{num_qubits}; + // Test using |+++> state + svdat.sv.applyOperations({{"Hadamard"}, {"Hadamard"}, {"Hadamard"}}, + {{0}, {1}, {2}}, {{false}, {false}, {false}}); + + constexpr cp_t p = Util::ConstMult( + static_cast(0.5), + Util::ConstMult(Util::INVSQRT2(), Util::IMAG())); + constexpr cp_t m = Util::ConstMult(-1, p); + + const std::vector> expected_results = { + {m, m, m, m, p, p, p, p}, + {m, m, p, p, m, m, p, p}, + {m, p, m, p, m, p, m, p}}; + + const auto init_state = svdat.cdata; + SECTION("Apply directly") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_direct{num_qubits, init_state}; + auto int_idx = svdat_direct.getInternalIndices({index}); + auto ext_idx = svdat_direct.getExternalIndices({index}); + + CHECK(svdat_direct.cdata == init_state); + svdat_direct.sv.applyPauliY(int_idx, ext_idx, false); + + CHECK(isApproxEqual(svdat_direct.cdata, expected_results[index])); + } + } + SECTION("Apply using dispatcher") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_dispatch{num_qubits, init_state}; + CHECK(svdat_dispatch.cdata == init_state); + svdat_dispatch.sv.applyOperation("PauliY", {index}, false); + CHECK(isApproxEqual(svdat_dispatch.cdata, expected_results[index])); + } + } +} + +TEMPLATE_TEST_CASE("StateVector::applyPauliZ", "[StateVector]", float, double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SVData svdat{num_qubits}; + // Test using |+++> state + svdat.sv.applyOperations({{"Hadamard"}, {"Hadamard"}, {"Hadamard"}}, + {{0}, {1}, {2}}, {{false}, {false}, {false}}); + + constexpr cp_t p = static_cast(0.5) * Util::INVSQRT2(); + constexpr cp_t m = Util::ConstMult(-1, p); + + const std::vector> expected_results = { + {p, p, p, p, m, m, m, m}, + {p, p, m, m, p, p, m, m}, + {p, m, p, m, p, m, p, m}}; + + const auto init_state = svdat.cdata; + SECTION("Apply directly") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_direct{num_qubits, init_state}; + auto int_idx = svdat_direct.getInternalIndices({index}); + auto ext_idx = svdat_direct.getExternalIndices({index}); + + CHECK(svdat_direct.cdata == init_state); + svdat_direct.sv.applyPauliZ(int_idx, ext_idx, false); + + CHECK(isApproxEqual(svdat_direct.cdata, expected_results[index])); + } + } + SECTION("Apply using dispatcher") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_dispatch{num_qubits, init_state}; + CHECK(svdat_dispatch.cdata == init_state); + svdat_dispatch.sv.applyOperation("PauliZ", {index}, false); + CHECK(isApproxEqual(svdat_dispatch.cdata, expected_results[index])); + } + } +} + +TEMPLATE_TEST_CASE("StateVector::applyS", "[StateVector]", float, double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SVData svdat{num_qubits}; + // Test using |+++> state + svdat.sv.applyOperations({{"Hadamard"}, {"Hadamard"}, {"Hadamard"}}, + {{0}, {1}, {2}}, {{false}, {false}, {false}}); + + constexpr cp_t r = static_cast(0.5) * Util::INVSQRT2(); + constexpr cp_t i = Util::ConstMult(r, Util::IMAG()); + + const std::vector> expected_results = { + {r, r, r, r, i, i, i, i}, + {r, r, i, i, r, r, i, i}, + {r, i, r, i, r, i, r, i}}; + + const auto init_state = svdat.cdata; + SECTION("Apply directly") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_direct{num_qubits, init_state}; + auto int_idx = svdat_direct.getInternalIndices({index}); + auto ext_idx = svdat_direct.getExternalIndices({index}); + + CHECK(svdat_direct.cdata == init_state); + svdat_direct.sv.applyS(int_idx, ext_idx, false); + + CHECK(isApproxEqual(svdat_direct.cdata, expected_results[index])); + } + } + SECTION("Apply using dispatcher") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_dispatch{num_qubits, init_state}; + CHECK(svdat_dispatch.cdata == init_state); + svdat_dispatch.sv.applyOperation("S", {index}, false); + CHECK(isApproxEqual(svdat_dispatch.cdata, expected_results[index])); + } + } +} + +TEMPLATE_TEST_CASE("StateVector::applyT", "[StateVector]", float, double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SVData svdat{num_qubits}; + // Test using |+++> state + svdat.sv.applyOperations({{"Hadamard"}, {"Hadamard"}, {"Hadamard"}}, + {{0}, {1}, {2}}, {{false}, {false}, {false}}); + + cp_t r = {1 / (2 * std::sqrt(2)), 0}; + cp_t i = {1.0 / 4, 1.0 / 4}; + + const std::vector> expected_results = { + {r, r, r, r, i, i, i, i}, + {r, r, i, i, r, r, i, i}, + {r, i, r, i, r, i, r, i}}; + + const auto init_state = svdat.cdata; + SECTION("Apply directly") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_direct{num_qubits, init_state}; + auto int_idx = svdat_direct.getInternalIndices({index}); + auto ext_idx = svdat_direct.getExternalIndices({index}); + + CHECK(svdat_direct.cdata == init_state); + svdat_direct.sv.applyT(int_idx, ext_idx, false); + + CHECK(isApproxEqual(svdat_direct.cdata, expected_results[index])); + } + } + SECTION("Apply using dispatcher") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_dispatch{num_qubits, init_state}; + CHECK(svdat_dispatch.cdata == init_state); + svdat_dispatch.sv.applyOperation("T", {index}, false); + CHECK(isApproxEqual(svdat_dispatch.cdata, expected_results[index])); + } + } +} + +TEMPLATE_TEST_CASE("StateVector::applyRX", "[StateVector]", float, double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SVData svdat{num_qubits}; + + const std::vector angles{0.1, 0.6, 2.1}; + std::vector> expected_results{ + std::vector(8), std::vector(8), std::vector(8)}; + + for (size_t i = 0; i < angles.size(); i++) { + const auto rx_mat = Gates::getRX(angles[i]); + expected_results[i][0] = rx_mat[0]; + expected_results[i][0b1 << (num_qubits - i - 1)] = rx_mat[1]; + } + + const auto init_state = svdat.cdata; + SECTION("Apply directly") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_direct{num_qubits}; + auto int_idx = svdat_direct.getInternalIndices({index}); + auto ext_idx = svdat_direct.getExternalIndices({index}); + + svdat_direct.sv.applyRX(int_idx, ext_idx, false, {angles[index]}); + + CHECK(isApproxEqual(svdat_direct.cdata, expected_results[index])); + } + } + SECTION("Apply using dispatcher") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_dispatch{num_qubits}; + svdat_dispatch.sv.applyOperation("RX", {index}, false, + {angles[index]}); + CHECK(isApproxEqual(svdat_dispatch.cdata, expected_results[index])); + } + } +} + +TEMPLATE_TEST_CASE("StateVector::applyRY", "[StateVector]", float, double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SVData svdat{num_qubits}; + + const std::vector angles{0.2, 0.7, 2.9}; + std::vector> expected_results{ + std::vector(8), std::vector(8), std::vector(8)}; + + for (size_t i = 0; i < angles.size(); i++) { + const auto ry_mat = Gates::getRY(angles[i]); + expected_results[i][0] = ry_mat[0]; + expected_results[i][0b1 << (num_qubits - i - 1)] = ry_mat[2]; + } + + const auto init_state = svdat.cdata; + SECTION("Apply directly") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_direct{num_qubits}; + auto int_idx = svdat_direct.getInternalIndices({index}); + auto ext_idx = svdat_direct.getExternalIndices({index}); + + svdat_direct.sv.applyRY(int_idx, ext_idx, false, {angles[index]}); + CAPTURE(svdat_direct.cdata); + CAPTURE(expected_results[index]); + + CHECK(isApproxEqual(svdat_direct.cdata, expected_results[index])); + } + } + SECTION("Apply using dispatcher") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_dispatch{num_qubits}; + svdat_dispatch.sv.applyOperation("RY", {index}, false, + {angles[index]}); + CHECK(isApproxEqual(svdat_dispatch.cdata, expected_results[index])); + } + } +} + +TEMPLATE_TEST_CASE("StateVector::applyRZ", "[StateVector]", float, double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SVData svdat{num_qubits}; + + // Test using |+++> state + svdat.sv.applyOperations({{"Hadamard"}, {"Hadamard"}, {"Hadamard"}}, + {{0}, {1}, {2}}, {{false}, {false}, {false}}); + + const std::vector angles{0.2, 0.7, 2.9}; + const cp_t coef = {1 / (2 * std::sqrt(2)), 0}; + + std::vector> rz_data; + for (auto &a : angles) { + rz_data.push_back(Gates::getRZ(a)); + } + + std::vector> expected_results = { + {rz_data[0][0], rz_data[0][0], rz_data[0][0], rz_data[0][0], + rz_data[0][3], rz_data[0][3], rz_data[0][3], rz_data[0][3]}, + { + rz_data[1][0], + rz_data[1][0], + rz_data[1][3], + rz_data[1][3], + rz_data[1][0], + rz_data[1][0], + rz_data[1][3], + rz_data[1][3], + }, + {rz_data[2][0], rz_data[2][3], rz_data[2][0], rz_data[2][3], + rz_data[2][0], rz_data[2][3], rz_data[2][0], rz_data[2][3]}}; + + for (auto &vec : expected_results) { + scaleVector(vec, coef); + } + + const auto init_state = svdat.cdata; + SECTION("Apply directly") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_direct{num_qubits, init_state}; + auto int_idx = svdat_direct.getInternalIndices({index}); + auto ext_idx = svdat_direct.getExternalIndices({index}); + + svdat_direct.sv.applyRZ(int_idx, ext_idx, false, {angles[index]}); + + CHECK(isApproxEqual(svdat_direct.cdata, expected_results[index])); + } + } + SECTION("Apply using dispatcher") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_dispatch{num_qubits, init_state}; + svdat_dispatch.sv.applyOperation("RZ", {index}, false, + {angles[index]}); + CHECK(isApproxEqual(svdat_dispatch.cdata, expected_results[index])); + } + } +} + +TEMPLATE_TEST_CASE("StateVector::applyPhaseShift", "[StateVector]", float, + double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SVData svdat{num_qubits}; + + // Test using |+++> state + svdat.sv.applyOperations({{"Hadamard"}, {"Hadamard"}, {"Hadamard"}}, + {{0}, {1}, {2}}, {{false}, {false}, {false}}); + + const std::vector angles{0.3, 0.8, 2.4}; + const cp_t coef = {1 / (2 * std::sqrt(2)), 0}; + + std::vector> ps_data; + for (auto &a : angles) { + ps_data.push_back(Gates::getPhaseShift(a)); + } + + std::vector> expected_results = { + {ps_data[0][0], ps_data[0][0], ps_data[0][0], ps_data[0][0], + ps_data[0][3], ps_data[0][3], ps_data[0][3], ps_data[0][3]}, + { + ps_data[1][0], + ps_data[1][0], + ps_data[1][3], + ps_data[1][3], + ps_data[1][0], + ps_data[1][0], + ps_data[1][3], + ps_data[1][3], + }, + {ps_data[2][0], ps_data[2][3], ps_data[2][0], ps_data[2][3], + ps_data[2][0], ps_data[2][3], ps_data[2][0], ps_data[2][3]}}; + + for (auto &vec : expected_results) { + scaleVector(vec, coef); + } + + const auto init_state = svdat.cdata; + SECTION("Apply directly") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_direct{num_qubits, init_state}; + auto int_idx = svdat_direct.getInternalIndices({index}); + auto ext_idx = svdat_direct.getExternalIndices({index}); + + svdat_direct.sv.applyPhaseShift(int_idx, ext_idx, false, + {angles[index]}); + + CHECK(isApproxEqual(svdat_direct.cdata, expected_results[index])); + } + } + SECTION("Apply using dispatcher") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_dispatch{num_qubits, init_state}; + svdat_dispatch.sv.applyOperation("PhaseShift", {index}, false, + {angles[index]}); + CHECK(isApproxEqual(svdat_dispatch.cdata, expected_results[index])); + } + } +} + +TEMPLATE_TEST_CASE("StateVector::applyControlledPhaseShift", "[StateVector]", + float, double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SVData svdat{num_qubits}; + + // Test using |+++> state + svdat.sv.applyOperations({{"Hadamard"}, {"Hadamard"}, {"Hadamard"}}, + {{0}, {1}, {2}}, {{false}, {false}, {false}}); + + const std::vector angles{0.3, 2.4}; + const cp_t coef = {1 / (2 * std::sqrt(2)), 0}; + + std::vector> ps_data; + for (auto &a : angles) { + ps_data.push_back(Gates::getPhaseShift(a)); + } + + std::vector> expected_results = { + {ps_data[0][0], ps_data[0][0], ps_data[0][0], ps_data[0][0], + ps_data[0][0], ps_data[0][0], ps_data[0][3], ps_data[0][3]}, + {ps_data[1][0], ps_data[1][0], ps_data[1][0], ps_data[1][3], + ps_data[1][0], ps_data[1][0], ps_data[1][0], ps_data[1][3]}}; + + for (auto &vec : expected_results) { + scaleVector(vec, coef); + } + + const auto init_state = svdat.cdata; + SECTION("Apply directly") { + SVData svdat_direct{num_qubits, init_state}; + auto int_idx = svdat_direct.getInternalIndices({0, 1}); + auto ext_idx = svdat_direct.getExternalIndices({0, 1}); + + svdat_direct.sv.applyControlledPhaseShift(int_idx, ext_idx, false, + {angles[0]}); + CAPTURE(svdat_direct.cdata); + CHECK(isApproxEqual(svdat_direct.cdata, expected_results[0])); + } + SECTION("Apply using dispatcher") { + SVData svdat_dispatch{num_qubits, init_state}; + svdat_dispatch.sv.applyOperation("ControlledPhaseShift", {1, 2}, false, + {angles[1]}); + CAPTURE(svdat_dispatch.cdata); + CHECK(isApproxEqual(svdat_dispatch.cdata, expected_results[1])); + } +} + +TEMPLATE_TEST_CASE("StateVector::applyRot", "[StateVector]", float, double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SVData svdat{num_qubits}; + + const std::vector> angles{ + std::vector{0.3, 0.8, 2.4}, + std::vector{0.5, 1.1, 3.0}, + std::vector{2.3, 0.1, 0.4}}; + + std::vector> expected_results{ + std::vector(0b1 << num_qubits), + std::vector(0b1 << num_qubits), + std::vector(0b1 << num_qubits)}; + + for (size_t i = 0; i < angles.size(); i++) { + const auto rot_mat = + Gates::getRot(angles[i][0], angles[i][1], angles[i][2]); + expected_results[i][0] = rot_mat[0]; + expected_results[i][0b1 << (num_qubits - i - 1)] = rot_mat[2]; + } + + SECTION("Apply directly") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_direct{num_qubits}; + auto int_idx = svdat_direct.getInternalIndices({index}); + auto ext_idx = svdat_direct.getExternalIndices({index}); + svdat_direct.sv.applyRot(int_idx, ext_idx, false, angles[index][0], + angles[index][1], angles[index][2]); + + CHECK(isApproxEqual(svdat_direct.cdata, expected_results[index])); + } + } + SECTION("Apply using dispatcher") { + for (size_t index = 0; index < num_qubits; index++) { + SVData svdat_dispatch{num_qubits}; + svdat_dispatch.sv.applyOperation("Rot", {index}, false, + angles[index]); + CHECK(isApproxEqual(svdat_dispatch.cdata, expected_results[index])); + } + } +} + +TEMPLATE_TEST_CASE("StateVector::applyCNOT", "[StateVector]", float, double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SVData svdat{num_qubits}; + + // Test using |+00> state to generate 3-qubit GHZ state + svdat.sv.applyOperation("Hadamard", {0}); + const auto init_state = svdat.cdata; + + SECTION("Apply directly") { + SVData svdat_direct{num_qubits, init_state}; + + for (size_t index = 1; index < num_qubits; index++) { + auto int_idx = svdat_direct.getInternalIndices({index - 1, index}); + auto ext_idx = svdat_direct.getExternalIndices({index - 1, index}); + + svdat_direct.sv.applyCNOT(int_idx, ext_idx, false); + } + CHECK(svdat_direct.cdata.front() == Util::INVSQRT2()); + CHECK(svdat_direct.cdata.back() == Util::INVSQRT2()); + } + + SECTION("Apply using dispatcher") { + SVData svdat_dispatch{num_qubits, init_state}; + + for (size_t index = 1; index < num_qubits; index++) { + svdat_dispatch.sv.applyOperation("CNOT", {index - 1, index}, false); + } + CHECK(svdat_dispatch.cdata.front() == Util::INVSQRT2()); + CHECK(svdat_dispatch.cdata.back() == Util::INVSQRT2()); + } +} + +TEMPLATE_TEST_CASE("StateVector::applySWAP", "[StateVector]", float, double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SVData svdat{num_qubits}; + + // Test using |+10> state + svdat.sv.applyOperations({{"Hadamard"}, {"PauliX"}}, {{0}, {1}}, + {false, false}); + const auto init_state = svdat.cdata; + + SECTION("Apply directly") { + CHECK(svdat.cdata == + std::vector{ + Util::ZERO(), Util::ZERO(), + Util::INVSQRT2(), Util::ZERO(), + Util::ZERO(), Util::ZERO(), + Util::INVSQRT2(), Util::ZERO()}); + + SECTION("SWAP0,1 |+10> -> |1+0>") { + std::vector expected{ + Util::ZERO(), Util::ZERO(), + Util::ZERO(), Util::ZERO(), + {1 / sqrt(2), 0}, Util::ZERO(), + {1 / sqrt(2), 0}, Util::ZERO()}; + + SVData svdat01{num_qubits, init_state}; + SVData svdat10{num_qubits, init_state}; + + svdat01.sv.applySWAP(svdat.getInternalIndices({0, 1}), + svdat.getExternalIndices({0, 1}), false); + svdat10.sv.applySWAP(svdat.getInternalIndices({1, 0}), + svdat.getExternalIndices({1, 0}), false); + + CHECK(svdat01.cdata == expected); + CHECK(svdat10.cdata == expected); + } + + SECTION("SWAP0,2 |+10> -> |01+>") { + std::vector expected{ + Util::ZERO(), Util::ZERO(), + {1 / sqrt(2), 0}, {1 / sqrt(2), 0}, + Util::ZERO(), Util::ZERO(), + Util::ZERO(), Util::ZERO()}; + + SVData svdat02{num_qubits, init_state}; + SVData svdat20{num_qubits, init_state}; + + svdat02.sv.applySWAP(svdat.getInternalIndices({0, 2}), + svdat.getExternalIndices({0, 2}), false); + svdat20.sv.applySWAP(svdat.getInternalIndices({2, 0}), + svdat.getExternalIndices({2, 0}), false); + CHECK(svdat02.cdata == expected); + CHECK(svdat20.cdata == expected); + } + SECTION("SWAP1,2 |+10> -> |+01>") { + std::vector expected{ + Util::ZERO(), {1 / sqrt(2), 0}, + Util::ZERO(), Util::ZERO(), + Util::ZERO(), {1 / sqrt(2), 0}, + Util::ZERO(), Util::ZERO()}; + + SVData svdat12{num_qubits, init_state}; + SVData svdat21{num_qubits, init_state}; + + svdat12.sv.applySWAP(svdat.getInternalIndices({1, 2}), + svdat.getExternalIndices({1, 2}), false); + svdat21.sv.applySWAP(svdat.getInternalIndices({2, 1}), + svdat.getExternalIndices({2, 1}), false); + CHECK(svdat12.cdata == expected); + CHECK(svdat21.cdata == expected); + } + } + SECTION("Apply using dispatcher") { + SECTION("SWAP0,1 |+10> -> |1+0>") { + std::vector expected{ + Util::ZERO(), Util::ZERO(), + Util::ZERO(), Util::ZERO(), + {1 / sqrt(2), 0}, Util::ZERO(), + {1 / sqrt(2), 0}, Util::ZERO()}; + + SVData svdat01{num_qubits, init_state}; + SVData svdat10{num_qubits, init_state}; + + svdat01.sv.applyOperation("SWAP", {0, 1}); + svdat10.sv.applyOperation("SWAP", {1, 0}); + + CHECK(svdat01.cdata == expected); + CHECK(svdat10.cdata == expected); + } + + SECTION("SWAP0,2 |+10> -> |01+>") { + std::vector expected{ + Util::ZERO(), Util::ZERO(), + {1 / sqrt(2), 0}, {1 / sqrt(2), 0}, + Util::ZERO(), Util::ZERO(), + Util::ZERO(), Util::ZERO()}; + + SVData svdat02{num_qubits, init_state}; + SVData svdat20{num_qubits, init_state}; + + svdat02.sv.applyOperation("SWAP", {0, 2}); + svdat20.sv.applyOperation("SWAP", {2, 0}); + + CHECK(svdat02.cdata == expected); + CHECK(svdat20.cdata == expected); + } + SECTION("SWAP1,2 |+10> -> |+01>") { + std::vector expected{ + Util::ZERO(), {1 / sqrt(2), 0}, + Util::ZERO(), Util::ZERO(), + Util::ZERO(), {1 / sqrt(2), 0}, + Util::ZERO(), Util::ZERO()}; + + SVData svdat12{num_qubits, init_state}; + SVData svdat21{num_qubits, init_state}; + + svdat12.sv.applyOperation("SWAP", {1, 2}); + svdat21.sv.applyOperation("SWAP", {2, 1}); + + CHECK(svdat12.cdata == expected); + CHECK(svdat21.cdata == expected); + } + } +} + +TEMPLATE_TEST_CASE("StateVector::applyCZ", "[StateVector]", float, double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SVData svdat{num_qubits}; + + // Test using |+10> state + svdat.sv.applyOperations({{"Hadamard"}, {"PauliX"}}, {{0}, {1}}, + {false, false}); + const auto init_state = svdat.cdata; + + SECTION("Apply directly") { + CHECK(svdat.cdata == std::vector{Util::ZERO(), + Util::ZERO(), + {1 / sqrt(2), 0}, + Util::ZERO(), + Util::ZERO(), + Util::ZERO(), + {1 / sqrt(2), 0}, + Util::ZERO()}); + + SECTION("CZ0,1 |+10> -> |-10>") { + std::vector expected{ + Util::ZERO(), Util::ZERO(), + {1 / sqrt(2), 0}, Util::ZERO(), + Util::ZERO(), Util::ZERO(), + {-1 / sqrt(2), 0}, Util::ZERO()}; + + SVData svdat01{num_qubits, init_state}; + SVData svdat10{num_qubits, init_state}; + + svdat01.sv.applyCZ(svdat.getInternalIndices({0, 1}), + svdat.getExternalIndices({0, 1}), false); + svdat10.sv.applyCZ(svdat.getInternalIndices({1, 0}), + svdat.getExternalIndices({1, 0}), false); + + CHECK(svdat01.cdata == expected); + CHECK(svdat10.cdata == expected); + } + + SECTION("CZ0,2 |+10> -> |+10>") { + std::vector expected{init_state}; + + SVData svdat02{num_qubits, init_state}; + SVData svdat20{num_qubits, init_state}; + + svdat02.sv.applyCZ(svdat.getInternalIndices({0, 2}), + svdat.getExternalIndices({0, 2}), false); + svdat20.sv.applyCZ(svdat.getInternalIndices({2, 0}), + svdat.getExternalIndices({2, 0}), false); + CHECK(svdat02.cdata == expected); + CHECK(svdat20.cdata == expected); + } + SECTION("CZ1,2 |+10> -> |+10>") { + std::vector expected{init_state}; + + SVData svdat12{num_qubits, init_state}; + SVData svdat21{num_qubits, init_state}; + + svdat12.sv.applyCZ(svdat.getInternalIndices({1, 2}), + svdat.getExternalIndices({1, 2}), false); + svdat21.sv.applyCZ(svdat.getInternalIndices({2, 1}), + svdat.getExternalIndices({2, 1}), false); + + CHECK(svdat12.cdata == expected); + CHECK(svdat21.cdata == expected); + } + } + SECTION("Apply using dispatcher") { + SECTION("CZ0,1 |+10> -> |1+0>") { + std::vector expected{ + Util::ZERO(), Util::ZERO(), + {1 / sqrt(2), 0}, Util::ZERO(), + Util::ZERO(), Util::ZERO(), + {-1 / sqrt(2), 0}, Util::ZERO()}; + + SVData svdat01{num_qubits, init_state}; + SVData svdat10{num_qubits, init_state}; + + svdat01.sv.applyOperation("CZ", {0, 1}); + svdat10.sv.applyOperation("CZ", {1, 0}); + + CHECK(svdat01.cdata == expected); + CHECK(svdat10.cdata == expected); + } + } +} + +TEMPLATE_TEST_CASE("StateVector::applyCRot", "[StateVector]", float, double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SVData svdat{num_qubits}; + + const std::vector angles{0.3, 0.8, 2.4}; + + std::vector expected_results(8); + const auto rot_mat = + Gates::getRot(angles[0], angles[1], angles[2]); + expected_results[0b1 << (num_qubits - 1)] = rot_mat[0]; + expected_results[(0b1 << num_qubits) - 2] = rot_mat[2]; + + const auto init_state = svdat.cdata; + + SECTION("Apply directly") { + SECTION("CRot0,1 |000> -> |000>") { + SVData svdat_direct{num_qubits}; + auto int_idx = svdat_direct.getInternalIndices({0, 1}); + auto ext_idx = svdat_direct.getExternalIndices({0, 1}); + svdat_direct.sv.applyCRot(int_idx, ext_idx, false, angles[0], + angles[1], angles[2]); + + CHECK(isApproxEqual(svdat_direct.cdata, init_state)); + } + SECTION("CRot0,1 |100> -> |1>(a|0>+b|1>)|0>") { + SVData svdat_direct{num_qubits}; + svdat_direct.sv.applyOperation("PauliX", {0}); + + auto int_idx = svdat_direct.getInternalIndices({0, 1}); + auto ext_idx = svdat_direct.getExternalIndices({0, 1}); + + svdat_direct.sv.applyCRot(int_idx, ext_idx, false, angles[0], + angles[1], angles[2]); + + CHECK(isApproxEqual(svdat_direct.cdata, expected_results)); + } + } + SECTION("Apply using dispatcher") { + SECTION("CRot0,1 |100> -> |1>(a|0>+b|1>)|0>") { + SVData svdat_direct{num_qubits}; + svdat_direct.sv.applyOperation("PauliX", {0}); + + svdat_direct.sv.applyOperation("CRot", {0, 1}, false, angles); + CHECK(isApproxEqual(svdat_direct.cdata, expected_results)); + } + } +} + +TEMPLATE_TEST_CASE("StateVector::applyToffoli", "[StateVector]", float, + double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SVData svdat{num_qubits}; + + // Test using |+10> state + svdat.sv.applyOperations({{"Hadamard"}, {"PauliX"}}, {{0}, {1}}, + {false, false}); + const auto init_state = svdat.cdata; + + SECTION("Apply directly") { + SECTION("Toffoli 0,1,2 |+10> -> |010> + |111>") { + std::vector expected{ + Util::ZERO(), Util::ZERO(), + {1 / sqrt(2), 0}, Util::ZERO(), + Util::ZERO(), Util::ZERO(), + Util::ZERO(), {1 / sqrt(2), 0}}; + + SVData svdat012{num_qubits, init_state}; + + svdat012.sv.applyToffoli(svdat.getInternalIndices({0, 1, 2}), + svdat.getExternalIndices({0, 1, 2}), + false); + + CHECK(svdat012.cdata == expected); + } + + SECTION("Toffoli 1,0,2 |+10> -> |010> + |111>") { + std::vector expected{ + Util::ZERO(), Util::ZERO(), + {1 / sqrt(2), 0}, Util::ZERO(), + Util::ZERO(), Util::ZERO(), + Util::ZERO(), {1 / sqrt(2), 0}}; + + SVData svdat102{num_qubits, init_state}; + + svdat102.sv.applyToffoli(svdat.getInternalIndices({1, 0, 2}), + svdat.getExternalIndices({1, 0, 2}), + false); + + CHECK(svdat102.cdata == expected); + } + SECTION("Toffoli 0,2,1 |+10> -> |+10>") { + std::vector expected{init_state}; + + SVData svdat021{num_qubits, init_state}; + + svdat021.sv.applyToffoli(svdat.getInternalIndices({0, 2, 1}), + svdat.getExternalIndices({0, 2, 1}), + false); + + CHECK(svdat021.cdata == expected); + } + SECTION("Toffoli 1,2,0 |+10> -> |+10>") { + std::vector expected{init_state}; + + SVData svdat120{num_qubits, init_state}; + + svdat120.sv.applyToffoli(svdat.getInternalIndices({1, 2, 0}), + svdat.getExternalIndices({1, 2, 0}), + false); + + CHECK(svdat120.cdata == expected); + } + } + SECTION("Apply using dispatcher") { + SECTION("Toffoli [0,1,2], [1,0,2] |+10> -> |+1+>") { + std::vector expected{ + Util::ZERO(), Util::ZERO(), + {1 / sqrt(2), 0}, Util::ZERO(), + Util::ZERO(), Util::ZERO(), + Util::ZERO(), {1 / sqrt(2), 0}}; + + SVData svdat012{num_qubits, init_state}; + SVData svdat102{num_qubits, init_state}; + + svdat012.sv.applyOperation("Toffoli", {0, 1, 2}); + svdat102.sv.applyOperation("Toffoli", {1, 0, 2}); + + CHECK(svdat012.cdata == expected); + CHECK(svdat102.cdata == expected); + } + } +} + +TEMPLATE_TEST_CASE("StateVector::applyCSWAP", "[StateVector]", float, double) { + using cp_t = std::complex; + const size_t num_qubits = 3; + SVData svdat{num_qubits}; + + // Test using |+10> state + svdat.sv.applyOperations({{"Hadamard"}, {"PauliX"}}, {{0}, {1}}, + {false, false}); + const auto init_state = svdat.cdata; + + SECTION("Apply directly") { + SECTION("CSWAP 0,1,2 |+10> -> |010> + |101>") { + std::vector expected{ + Util::ZERO(), Util::ZERO(), + {1 / sqrt(2), 0}, Util::ZERO(), + Util::ZERO(), {1 / sqrt(2), 0}, + Util::ZERO(), Util::ZERO()}; + SVData svdat012{num_qubits, init_state}; + + svdat012.sv.applyCSWAP(svdat.getInternalIndices({0, 1, 2}), + svdat.getExternalIndices({0, 1, 2}), false); + + CHECK(svdat012.cdata == expected); + } + + SECTION("CSWAP 1,0,2 |+10> -> |01+>") { + std::vector expected{ + Util::ZERO(), Util::ZERO(), + {1 / sqrt(2), 0}, {1 / sqrt(2), 0}, + Util::ZERO(), Util::ZERO(), + Util::ZERO(), Util::ZERO()}; + + SVData svdat102{num_qubits, init_state}; + + svdat102.sv.applyCSWAP(svdat.getInternalIndices({1, 0, 2}), + svdat.getExternalIndices({1, 0, 2}), false); + + CHECK(svdat102.cdata == expected); + } + SECTION("CSWAP 2,1,0 |+10> -> |+10>") { + std::vector expected{init_state}; + + SVData svdat021{num_qubits, init_state}; + + svdat021.sv.applyCSWAP(svdat.getInternalIndices({2, 1, 0}), + svdat.getExternalIndices({2, 1, 0}), false); + + CHECK(svdat021.cdata == expected); + } + } + SECTION("Apply using dispatcher") { + SECTION("CSWAP 0,1,2 |+10> -> |010> + |101>") { + std::vector expected{ + Util::ZERO(), Util::ZERO(), + {1 / sqrt(2), 0}, Util::ZERO(), + Util::ZERO(), {1 / sqrt(2), 0}, + Util::ZERO(), Util::ZERO()}; + SVData svdat012{num_qubits, init_state}; + + svdat012.sv.applyOperation("CSWAP", {0, 1, 2}); + + CHECK(svdat012.cdata == expected); + } + } +} \ No newline at end of file diff --git a/pennylane_lightning/src/tests/TestingUtils.hpp b/pennylane_lightning/src/tests/TestingUtils.hpp deleted file mode 100644 index 030cd2578b..0000000000 --- a/pennylane_lightning/src/tests/TestingUtils.hpp +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2021 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 - -// Helper macro to check if an exception is thrown with the correct complete -// error message -#define EXPECT_THROW_WITH_MESSAGE(stmt, etype, whatstring) \ - EXPECT_THROW( \ - try { stmt; } catch (const etype &ex) { \ - EXPECT_EQ(whatstring, std::string(ex.what())); \ - throw; \ - }, \ - etype) - -// Helper macro to check if an exception is thrown with the correct error -// message substring -#define EXPECT_THROW_WITH_MESSAGE_SUBSTRING(stmt, etype, whatstring) \ - EXPECT_THROW( \ - try { stmt; } catch (const etype &ex) { \ - EXPECT_NE(std::string(ex.what()).find(whatstring), \ - std::string::npos); \ - throw; \ - }, \ - etype) diff --git a/pennylane_lightning/src/tests/gtest_main.cpp b/pennylane_lightning/src/tests/gtest_main.cpp deleted file mode 100644 index f1fa096728..0000000000 --- a/pennylane_lightning/src/tests/gtest_main.cpp +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2006, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "gtest/gtest.h" -#include - -#ifdef ARDUINO -void setup() { testing::InitGoogleTest(); } - -void loop() { RUN_ALL_TESTS(); } - -#else - -GTEST_API_ int main(int argc, char **argv) { - printf("Running main() from %s\n", __FILE__); - testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} -#endif diff --git a/pennylane_lightning/src/tests/lightning_apply_unittest.cpp b/pennylane_lightning/src/tests/lightning_apply_unittest.cpp deleted file mode 100644 index b8b0e642d4..0000000000 --- a/pennylane_lightning/src/tests/lightning_apply_unittest.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2021 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. -#include "../Apply.hpp" -#include "gtest/gtest.h" - -using std::vector; - -namespace test_apply { - -class getIndicesAfterExclusionTestFixture - : public ::testing::TestWithParam> { -}; - -TEST_P(getIndicesAfterExclusionTestFixture, - CheckgetIndicesAfterExclusionResults) { - unsigned int index = std::get<0>(GetParam()); - unsigned int qubits = std::get<1>(GetParam()); - - vector> inputs = { - {0, 1}, {2}, {1, 2}, {1, 2, 4}, {3, 2, 4}, - }; - - vector> outputs = { - {2}, {0, 1}, {0}, {0, 3}, {0, 1, 5}, - }; - - vector input = inputs[index]; - vector output = outputs[index]; - ASSERT_EQ(output, Pennylane::getIndicesAfterExclusion(input, qubits)); -} - -INSTANTIATE_TEST_SUITE_P( - getIndicesAfterExclusionTests, getIndicesAfterExclusionTestFixture, - ::testing::Values(std::make_tuple(0, 3), std::make_tuple(1, 3), - std::make_tuple(2, 3), std::make_tuple(3, 5), - std::make_tuple(4, 6))); - -} // namespace test_apply diff --git a/pennylane_lightning/src/tests/lightning_gates_unittest.cpp b/pennylane_lightning/src/tests/lightning_gates_unittest.cpp deleted file mode 100644 index b3cf5a511b..0000000000 --- a/pennylane_lightning/src/tests/lightning_gates_unittest.cpp +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2021 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. -#include "../Gates.hpp" -#include "GateData.hpp" -#include "TestingUtils.hpp" -#include "gtest/gtest.h" - -#include - -using std::function; -using std::string; -using std::unique_ptr; -using std::vector; - -using Pennylane::CplxType; - -namespace test_gates { - -const vector ZERO_PARAM = {}; -const vector ONE_PARAM = {0.123}; -const vector THREE_PARAMS = {0.123, 2.345, 1.4321}; - -// ------------------------------------------------------------------------------------------------------------- -// Non-parametrized gates - -class MatrixNoParamFixture - : public ::testing::TestWithParam>> {}; - -TEST_P(MatrixNoParamFixture, CheckMatrix) { - const string gate_name = std::get<0>(GetParam()); - const vector matrix = std::get<1>(GetParam()); - - const vector params = {}; - - unique_ptr gate = - Pennylane::constructGate(gate_name, params); - EXPECT_EQ(gate->asMatrix(), matrix); -} - -INSTANTIATE_TEST_SUITE_P( - GateMatrix, MatrixNoParamFixture, - ::testing::Values( - std::make_tuple("PauliX", PauliX), std::make_tuple("PauliY", PauliY), - std::make_tuple("PauliZ", PauliZ), - std::make_tuple("Hadamard", Hadamard), std::make_tuple("S", S), - std::make_tuple("T", T), std::make_tuple("CNOT", CNOT), - std::make_tuple("SWAP", SWAP), std::make_tuple("CZ", CZ), - std::make_tuple("Toffoli", Toffoli), std::make_tuple("CSWAP", CSWAP))); - -// ------------------------------------------------------------------------------------------------------------- -// Parametrized gates - -class MatrixWithParamsFixture - : public ::testing::TestWithParam< - std::tuple>> {}; - -TEST_P(MatrixWithParamsFixture, CheckMatrix) { - const string gate_name = std::get<0>(GetParam()); - pfunc_params func = std::get<1>(GetParam()); - const vector params = std::get<2>(GetParam()); - - unique_ptr gate = - Pennylane::constructGate(gate_name, params); - EXPECT_EQ(gate->asMatrix(), func(params)); -} - -INSTANTIATE_TEST_SUITE_P( - GateMatrix, MatrixWithParamsFixture, - ::testing::Values(std::make_tuple("RX", RX, ONE_PARAM), - std::make_tuple("RY", RY, ONE_PARAM), - std::make_tuple("RZ", RZ, ONE_PARAM), - std::make_tuple("PhaseShift", PhaseShift, ONE_PARAM), - std::make_tuple("Rot", Rot, THREE_PARAMS), - std::make_tuple("CRX", CRX, ONE_PARAM), - std::make_tuple("CRY", CRY, ONE_PARAM), - std::make_tuple("CRZ", CRZ, ONE_PARAM), - std::make_tuple("CRot", CRot, THREE_PARAMS), - std::make_tuple("ControlledPhaseShift", CPhaseShift, - ONE_PARAM))); - -// ------------------------------------------------------------------------------------------------------------- -// Parameter length validation - -class NumParamsThrowsFixture - : public ::testing::TestWithParam>> {}; - -TEST_P(NumParamsThrowsFixture, CheckParamLength) { - const string gate_name = std::get<0>(GetParam()); - const vector params = std::get<1>(GetParam()); - - EXPECT_THROW_WITH_MESSAGE_SUBSTRING( - Pennylane::constructGate(gate_name, params), std::invalid_argument, - gate_name); -} - -const vector non_param_gates = { - "PauliX", "PauliY", "PauliZ", "Hadamard", "S", "T", - "CNOT", "SWAP", "CZ", "Toffoli", "CSWAP"}; -const vector> many_params = {ONE_PARAM, THREE_PARAMS}; - -INSTANTIATE_TEST_SUITE_P( - NoParameterGateChecks, NumParamsThrowsFixture, - ::testing::Combine(::testing::ValuesIn(non_param_gates), - ::testing::ValuesIn(many_params))); - -const vector param_gates = { - "RX", "RY", "RZ", "PhaseShift", "Rot", - "CRX", "CRY", "CRZ", "CRot", "ControlledPhaseShift"}; -const vector> zero_params = {ZERO_PARAM}; - -INSTANTIATE_TEST_SUITE_P(ParametrizedGateChecks, NumParamsThrowsFixture, - ::testing::Combine(::testing::ValuesIn(param_gates), - ::testing::ValuesIn(zero_params))); - -TEST(DispatchTable, constructGateThrows) { - const string test_gate_name = "Non-existent gate"; - EXPECT_THROW_WITH_MESSAGE_SUBSTRING( - Pennylane::constructGate(test_gate_name, {}), std::invalid_argument, - test_gate_name); -} - -} // namespace test_gates diff --git a/pennylane_lightning/src/tests/lightning_util_unittest.cpp b/pennylane_lightning/src/tests/lightning_util_unittest.cpp deleted file mode 100644 index b10649f49e..0000000000 --- a/pennylane_lightning/src/tests/lightning_util_unittest.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2021 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. -#include "../Util.hpp" -#include "gtest/gtest.h" - -#include - -namespace test_utils { - -class Exp2TestFixture : public ::testing::TestWithParam> { -}; - -TEST_P(Exp2TestFixture, CheckExp2Results) { - int input = std::get<0>(GetParam()); - size_t expected = std::get<1>(GetParam()); - ASSERT_EQ(expected, Pennylane::exp2(input)); -} - -INSTANTIATE_TEST_SUITE_P(Exp2Tests, Exp2TestFixture, - ::testing::Values(std::make_tuple(1, 2), - std::make_tuple(2, 4), - std::make_tuple(5, 32), - std::make_tuple(8, 256))); - -class maxDecimalForQubitTestFixture - : public ::testing::TestWithParam< - std::tuple> {}; - -TEST_P(maxDecimalForQubitTestFixture, CheckMaxDecimalResults) { - unsigned int qubitIndex = std::get<0>(GetParam()); - unsigned int qubits = std::get<1>(GetParam()); - size_t expected = std::get<2>(GetParam()); - ASSERT_EQ(expected, Pennylane::maxDecimalForQubit(qubitIndex, qubits)); -} - -INSTANTIATE_TEST_SUITE_P( - maxDecimalForQubitTests, maxDecimalForQubitTestFixture, - ::testing::Values(std::make_tuple(0, 3, 4), std::make_tuple(1, 3, 2), - std::make_tuple(2, 3, 1), std::make_tuple(0, 4, 8), - std::make_tuple(2, 4, 2), std::make_tuple(2, 5, 4))); - -} // namespace test_utils diff --git a/pennylane_lightning/src/tests/runner_main.cpp b/pennylane_lightning/src/tests/runner_main.cpp new file mode 100644 index 0000000000..4ed06df1f7 --- /dev/null +++ b/pennylane_lightning/src/tests/runner_main.cpp @@ -0,0 +1,2 @@ +#define CATCH_CONFIG_MAIN +#include diff --git a/pennylane_lightning/src/typedefs.hpp b/pennylane_lightning/src/typedefs.hpp deleted file mode 100644 index 49cbac1b58..0000000000 --- a/pennylane_lightning/src/typedefs.hpp +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2021 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 - * Contains any widely-used type aliases. - */ -#pragma once - -#include - -namespace Pennylane { - -using CplxType = std::complex; -} diff --git a/setup.py b/setup.py index ba16014977..72be14fa2f 100644 --- a/setup.py +++ b/setup.py @@ -59,23 +59,22 @@ def has_flag(compiler, flagname): def cpp_flag(compiler): - """Return the -std=c++[11/14/17] compiler flag. - The newer version is prefered over c++11 (when it is available). + """Return the -std=c++17 compiler flag. """ - flags = ["-std=c++17", "-std=c++14", "-std=c++11"] + flags = ["-std=c++17"] for flag in flags: if has_flag(compiler, flag): return flag - raise RuntimeError("Unsupported compiler -- at least C++11 support is needed!") + raise RuntimeError("Unsupported compiler -- at least C++17 support is needed!") class BuildExt(build_ext): """A custom build extension for adding compiler-specific options.""" c_opts = { - "msvc": ["-EHsc", "-O2", "-W1", "-std:c++11"], + "msvc": ["-EHsc", "-O2", "-W1", "-std:c++17"], "unix": ["-O3", "-W", "-fPIC", "-shared", "-fopenmp"], } @@ -148,16 +147,11 @@ def build_extensions(self): Extension( "lightning_qubit_ops", sources=[ - "pennylane_lightning/src/Apply.cpp", - "pennylane_lightning/src/Gates.cpp", - "pennylane_lightning/src/Bindings.cpp", "pennylane_lightning/src/StateVector.cpp", + "pennylane_lightning/src/Bindings.cpp", ], depends=[ - "pennylane_lightning/src/Apply.hpp", - "pennylane_lightning/src/Gates.hpp", "pennylane_lightning/src/StateVector.hpp", - "pennylane_lightning/src/typedefs.hpp", "pennylane_lightning/src/Util.hpp", ], include_dirs=include_dirs, diff --git a/tests/test_apply.py b/tests/test_apply.py index a289b81410..b5d479df0c 100644 --- a/tests/test_apply.py +++ b/tests/test_apply.py @@ -347,6 +347,12 @@ def test_apply_operation_single_wire_with_parameters( [0, 0, 1, 0], [math.pi / 2], ), + ( + qml.ControlledPhaseShift, + [0, 0, 0, 1], + [0, 0, 0, 1 / math.sqrt(2) + 1j / math.sqrt(2)], + [math.pi / 4], + ), ( qml.ControlledPhaseShift, [1 / math.sqrt(2), 1 / math.sqrt(2), 1 / math.sqrt(2), 1 / math.sqrt(2)], @@ -588,6 +594,7 @@ def test_no_backprop(self): differentiation method.""" dev = qml.device("lightning.qubit", wires=2) + def circuit(): """Simple quantum function.""" return qml.expval(qml.PauliZ(0)) @@ -599,6 +606,7 @@ def test_best_gets_lightning(self): """Test that the best differentiation method returns lightning qubit.""" dev = qml.device("lightning.qubit", wires=2) + def circuit(): """Simple quantum function.""" return qml.expval(qml.PauliZ(0)) @@ -885,7 +893,7 @@ def circuit(): # This test is ran against the state 1/2|00>+sqrt(3)/2|11> with two Z expvals @pytest.mark.parametrize( "name,par,expected_output", - [ + [ ("CRX", [0], [-1 / 2, -1 / 2]), ("CRX", [-math.pi], [-1 / 2, 1]), ("CRX", [math.pi / 2], [-1 / 2, 1 / 4]), @@ -1209,7 +1217,9 @@ def test_paulix_pauliy(self, theta, phi, varphi, shots, monkeypatch, tol): ) / 16 assert np.allclose(var, expected, atol=tolerance, rtol=0) - def test_pauliz_hadamard(self, theta, phi, varphi, monkeypatch, shots, qubit_device_3_wires, tol): + def test_pauliz_hadamard( + self, theta, phi, varphi, monkeypatch, shots, qubit_device_3_wires, tol + ): """Test that a tensor product involving PauliZ and PauliY and hadamard works correctly""" tolerance = tol if shots is None else TOL_STOCHASTIC dev = qubit_device_3_wires diff --git a/tests/test_expval.py b/tests/test_expval.py index 5bf0645703..00644f68b3 100644 --- a/tests/test_expval.py +++ b/tests/test_expval.py @@ -41,12 +41,8 @@ def test_identity_expectation(self, theta, phi, qubit_device_3_wires, tol): O2 = qml.Identity(wires=[1]) dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.CNOT(wires=[0, 1]) - ], - rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()] + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], ) res = np.array([dev.expval(O1), dev.expval(O2)]) @@ -59,12 +55,8 @@ def test_pauliz_expectation(self, theta, phi, qubit_device_3_wires, tol): O2 = qml.PauliZ(wires=[1]) dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.CNOT(wires=[0, 1]) - ], - rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()] + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], ) res = np.array([dev.expval(O1), dev.expval(O2)]) @@ -77,12 +69,8 @@ def test_paulix_expectation(self, theta, phi, qubit_device_3_wires, tol): O2 = qml.PauliX(wires=[1]) dev.apply( - [ - qml.RY(theta, wires=[0]), - qml.RY(phi, wires=[1]), - qml.CNOT(wires=[0, 1]) - ], - rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()] + [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], ) res = np.array([dev.expval(O1), dev.expval(O2)]) @@ -95,12 +83,8 @@ def test_pauliy_expectation(self, theta, phi, qubit_device_3_wires, tol): O2 = qml.PauliY(wires=[1]) dev.apply( - [ - qml.RX(theta, wires=[0]), - qml.RX(phi, wires=[1]), - qml.CNOT(wires=[0, 1]) - ], - rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()] + [qml.RX(theta, wires=[0]), qml.RX(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], ) res = np.array([dev.expval(O1), dev.expval(O2)]) @@ -113,12 +97,8 @@ def test_hadamard_expectation(self, theta, phi, qubit_device_3_wires, tol): O2 = qml.Hadamard(wires=[1]) dev.apply( - [ - qml.RY(theta, wires=[0]), - qml.RY(phi, wires=[1]), - qml.CNOT(wires=[0, 1]) - ], - rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()] + [qml.RY(theta, wires=[0]), qml.RY(phi, wires=[1]), qml.CNOT(wires=[0, 1])], + rotations=[*O1.diagonalizing_gates(), *O2.diagonalizing_gates()], ) res = np.array([dev.expval(O1), dev.expval(O2)]) @@ -144,9 +124,9 @@ def test_paulix_pauliy(self, theta, phi, varphi, qubit_device_3_wires, tol): qml.RX(phi, wires=[1]), qml.RX(varphi, wires=[2]), qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]) + qml.CNOT(wires=[1, 2]), ], - rotations=obs.diagonalizing_gates() + rotations=obs.diagonalizing_gates(), ) res = dev.expval(obs) @@ -166,14 +146,14 @@ def test_pauliz_identity(self, theta, phi, varphi, qubit_device_3_wires, tol): qml.RX(phi, wires=[1]), qml.RX(varphi, wires=[2]), qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]) + qml.CNOT(wires=[1, 2]), ], - rotations=obs.diagonalizing_gates() + rotations=obs.diagonalizing_gates(), ) res = dev.expval(obs) - expected = np.cos(varphi)*np.cos(phi) + expected = np.cos(varphi) * np.cos(phi) assert np.allclose(res, expected, tol) @@ -189,9 +169,9 @@ def test_pauliz_hadamard_pauliy(self, theta, phi, varphi, qubit_device_3_wires, qml.RX(phi, wires=[1]), qml.RX(varphi, wires=[2]), qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]) + qml.CNOT(wires=[1, 2]), ], - rotations=obs.diagonalizing_gates() + rotations=obs.diagonalizing_gates(), ) res = dev.expval(obs) diff --git a/tests/test_var.py b/tests/test_var.py index 9bcada8cf7..d8f4c5cd53 100644 --- a/tests/test_var.py +++ b/tests/test_var.py @@ -45,7 +45,7 @@ def test_var(self, theta, phi, tol): qml.RX(phi, wires=[0]), qml.RY(theta, wires=[0]), ], - rotations=[*observable.diagonalizing_gates()] + rotations=[*observable.diagonalizing_gates()], ) var = dev.var(observable) @@ -69,9 +69,9 @@ def test_paulix_pauliy(self, theta, phi, varphi, tol): qml.RX(phi, wires=[1]), qml.RX(varphi, wires=[2]), qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]) + qml.CNOT(wires=[1, 2]), ], - rotations=obs.diagonalizing_gates() + rotations=obs.diagonalizing_gates(), ) res = dev.var(obs) @@ -98,9 +98,9 @@ def test_pauliz_hadamard_pauliy(self, theta, phi, varphi, tol): qml.RX(phi, wires=[1]), qml.RX(varphi, wires=[2]), qml.CNOT(wires=[0, 1]), - qml.CNOT(wires=[1, 2]) + qml.CNOT(wires=[1, 2]), ], - rotations=obs.diagonalizing_gates() + rotations=obs.diagonalizing_gates(), ) res = dev.var(obs)