diff --git a/.github/workflows/build-wheel-linux-x86_64.yaml b/.github/workflows/build-wheel-linux-x86_64.yaml index 07bb03adf5..276772a037 100644 --- a/.github/workflows/build-wheel-linux-x86_64.yaml +++ b/.github/workflows/build-wheel-linux-x86_64.yaml @@ -310,11 +310,8 @@ jobs: -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=$GITHUB_WORKSPACE/runtime-build/lib \ -DPYTHON_EXECUTABLE=$(which python${{ matrix.python_version }}) \ -Dpybind11_DIR=$(python${{ matrix.python_version }} -c "import pybind11; print(pybind11.get_cmake_dir())") \ - -DENABLE_LIGHTNING_KOKKOS=ON \ -DENABLE_LAPACK=OFF \ -DLIGHTNING_GIT_TAG=latest_release \ - -DKokkos_ENABLE_SERIAL=ON \ - -DKokkos_ENABLE_OPENMP=ON \ -DENABLE_WARNINGS=OFF \ -DENABLE_OPENQASM=ON \ -DENABLE_OPENMP=OFF \ diff --git a/.github/workflows/build-wheel-macos-arm64.yaml b/.github/workflows/build-wheel-macos-arm64.yaml index 9d6c2243a5..52a0368854 100644 --- a/.github/workflows/build-wheel-macos-arm64.yaml +++ b/.github/workflows/build-wheel-macos-arm64.yaml @@ -289,12 +289,8 @@ jobs: -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=$GITHUB_WORKSPACE/runtime-build/lib \ -DPYTHON_EXECUTABLE=$(which python${{ matrix.python_version }}) \ -Dpybind11_DIR=$(python${{ matrix.python_version }} -c "import pybind11; print(pybind11.get_cmake_dir())") \ - -DENABLE_LIGHTNING_KOKKOS=ON \ -DLIGHTNING_GIT_TAG=latest_release \ -DENABLE_LAPACK=OFF \ - -DKokkos_ENABLE_SERIAL=ON \ - -DKokkos_ENABLE_OPENMP=ON \ - -DKokkos_ENABLE_COMPLEX_ALIGN=OFF \ -DENABLE_WARNINGS=OFF \ -DENABLE_OPENQASM=ON \ -DENABLE_OPENMP=OFF \ diff --git a/.github/workflows/build-wheel-macos-x86_64.yaml b/.github/workflows/build-wheel-macos-x86_64.yaml index 9d8c13480d..c9e95194a5 100644 --- a/.github/workflows/build-wheel-macos-x86_64.yaml +++ b/.github/workflows/build-wheel-macos-x86_64.yaml @@ -263,12 +263,7 @@ jobs: -DPYTHON_EXECUTABLE=$(which python${{ matrix.python_version }}) \ -Dpybind11_DIR=$(python${{ matrix.python_version }} -c "import pybind11; print(pybind11.get_cmake_dir())") \ -DLIGHTNING_GIT_TAG=latest_release \ - -DENABLE_LIGHTNING_KOKKOS=ON \ -DENABLE_LAPACK=OFF \ - -DKokkos_ENABLE_SERIAL=ON \ - -DKokkos_ENABLE_OPENMP=OFF \ - -DKokkos_ENABLE_COMPLEX_ALIGN=OFF \ - -DKokkos_ENABLE_DEPRECATION_WARNINGS=OFF \ -DENABLE_WARNINGS=OFF \ -DENABLE_OPENQASM=ON \ -DENABLE_OPENMP=OFF \ diff --git a/.github/workflows/check-catalyst.yaml b/.github/workflows/check-catalyst.yaml index 23f87d5d1b..4cd442f62e 100644 --- a/.github/workflows/check-catalyst.yaml +++ b/.github/workflows/check-catalyst.yaml @@ -618,7 +618,6 @@ jobs: C_COMPILER=$(which ${{ needs.constants.outputs[format('c_compiler.{0}', matrix.compiler)] }}) \ CXX_COMPILER=$(which ${{ needs.constants.outputs[format('cxx_compiler.{0}', matrix.compiler)] }}) \ COMPILER_LAUNCHER="" \ - ENABLE_LIGHTNING_KOKKOS=OFF \ ENABLE_OPENQASM=OFF \ ENABLE_ASAN=ON \ make test-runtime @@ -640,7 +639,6 @@ jobs: C_COMPILER=$(which ${{ needs.constants.outputs[format('c_compiler.{0}', matrix.compiler)] }}) \ CXX_COMPILER=$(which ${{ needs.constants.outputs[format('cxx_compiler.{0}', matrix.compiler)] }}) \ COMPILER_LAUNCHER="" \ - ENABLE_LIGHTNING_KOKKOS=OFF \ ENABLE_ASAN=ON \ make test-runtime diff --git a/.github/workflows/check-pl-compat.yaml b/.github/workflows/check-pl-compat.yaml index 3200a111c2..2a397d2c3a 100644 --- a/.github/workflows/check-pl-compat.yaml +++ b/.github/workflows/check-pl-compat.yaml @@ -125,7 +125,6 @@ jobs: CXX_COMPILER=$(which g++ }}) \ RT_BUILD_DIR="$(pwd)/runtime-build" \ LIGHTNING_GIT_TAG_VALUE=latest_release \ - ENABLE_LIGHTNING_KOKKOS=ON \ ENABLE_OPENQASM=ON \ make runtime @@ -137,7 +136,6 @@ jobs: CXX_COMPILER=$(which g++ }}) \ RT_BUILD_DIR="$(pwd)/runtime-build" \ LIGHTNING_GIT_TAG_VALUE=v0.37.0_rc \ - ENABLE_LIGHTNING_KOKKOS=ON \ ENABLE_OPENQASM=ON \ make runtime diff --git a/.github/workflows/scripts/linux_arm64/rh8/build_catalyst.sh b/.github/workflows/scripts/linux_arm64/rh8/build_catalyst.sh index d5b29837df..e225febf8b 100644 --- a/.github/workflows/scripts/linux_arm64/rh8/build_catalyst.sh +++ b/.github/workflows/scripts/linux_arm64/rh8/build_catalyst.sh @@ -45,12 +45,8 @@ cmake -S runtime -B runtime-build -G Ninja \ -DPYTHON_INCLUDE_DIR=/opt/_internal/cpython-${PYTHON_VERSION}.${PYTHON_SUBVERSION}/include/python${PYTHON_VERSION} \ -DPYTHON_LIBRARY=/opt/_internal/cpython-${PYTHON_VERSION}.${PYTHON_SUBVERSION}/lib \ -Dpybind11_DIR=/opt/_internal/cpython-${PYTHON_VERSION}.${PYTHON_SUBVERSION}/lib/python${PYTHON_VERSION}/site-packages/pybind11/share/cmake/pybind11 \ - -DENABLE_LIGHTNING_KOKKOS=ON \ -DLIGHTNING_GIT_TAG=latest_release \ -DENABLE_LAPACK=OFF \ - -DKokkos_ENABLE_SERIAL=ON \ - -DKokkos_ENABLE_OPENMP=ON \ - -DKokkos_ENABLE_COMPLEX_ALIGN=OFF \ -DENABLE_WARNINGS=OFF \ -DENABLE_OPENQASM=ON \ -DENABLE_OPENMP=OFF \ diff --git a/doc/dev/installation.rst b/doc/dev/installation.rst index 97034cdf51..e267308877 100644 --- a/doc/dev/installation.rst +++ b/doc/dev/installation.rst @@ -350,7 +350,7 @@ them, but using the ``test-runtime`` target instead: .. code-block:: console - make test-runtime ENABLE_LIGHTNING_KOKKOS=ON ENABLE_OPENQASM=ON + make test-runtime ENABLE_OPENQASM=ON .. note:: diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index c019cfa148..5e48d9a8ba 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -17,7 +17,6 @@ option(ENABLE_ADDRESS_SANITIZER "Enable address sanitizer" OFF) option(RUNTIME_CLANG_TIDY "Enable Clang Tidy" OFF) option(ENABLE_LIGHTNING "Build Lightning backend device" ON) -option(ENABLE_LIGHTNING_KOKKOS "Build Lightning-Kokkos backend device" OFF) option(ENABLE_OPENQASM "Build OpenQasm backend device" OFF) set(CMAKE_VERBOSE_MAKEFILE ON) @@ -79,20 +78,14 @@ if(RUNTIME_ENABLE_WARNINGS) endif() message(STATUS "ENABLE_LIGHTNING is ${ENABLE_LIGHTNING}.") -message(STATUS "ENABLE_LIGHTNING_KOKKOS is ${ENABLE_LIGHTNING_KOKKOS}.") message(STATUS "ENABLE_OPENQASM is ${ENABLE_OPENQASM}.") set(devices_list) list(APPEND devices_list rtd_dummy) -if(ENABLE_LIGHTNING OR ENABLE_LIGHTNING_KOKKOS) +if(ENABLE_LIGHTNING) list(APPEND devices_list pennylane_lightning rtd_lightning) - if(ENABLE_LIGHTNING) - list(APPEND backend_includes "${PROJECT_SOURCE_DIR}/lib/backend/lightning/lightning_dynamic") - endif() - if(ENABLE_LIGHTNING_KOKKOS) - list(APPEND backend_includes "${PROJECT_SOURCE_DIR}/lib/backend/lightning/lightning_kokkos") - endif() + list(APPEND backend_includes "${PROJECT_SOURCE_DIR}/lib/backend/lightning/lightning_dynamic") endif() if(ENABLE_OPENQASM) diff --git a/runtime/Makefile b/runtime/Makefile index 568f7a631c..b36347933d 100644 --- a/runtime/Makefile +++ b/runtime/Makefile @@ -23,9 +23,6 @@ TEST_TARGETS := "" ifeq ($(ENABLE_LIGHTNING), ON) BUILD_TARGETS += rtd_lightning TEST_TARGETS += runner_tests_lightning -else ifeq ($(ENABLE_LIGHTNING_KOKKOS), ON) - BUILD_TARGETS += rtd_lightning - TEST_TARGETS += runner_tests_lightning endif ifeq ($(ENABLE_OPENQASM), ON) @@ -34,7 +31,6 @@ ifeq ($(ENABLE_OPENQASM), ON) endif LIGHTNING_ENABLE_OPENMP?=OFF -KOKKOS_ENABLE_OPENMP?=ON coverage: CODE_COVERAGE=ON coverage: BUILD_TYPE=Debug @@ -75,10 +71,8 @@ configure: -DCMAKE_C_COMPILER_LAUNCHER=$(COMPILER_LAUNCHER) \ -DCMAKE_CXX_COMPILER_LAUNCHER=$(COMPILER_LAUNCHER) \ -DENABLE_LIGHTNING=$(ENABLE_LIGHTNING) \ - -DENABLE_LIGHTNING_KOKKOS=$(ENABLE_LIGHTNING_KOKKOS) \ -DENABLE_OPENQASM=$(ENABLE_OPENQASM) \ -DENABLE_OPENMP=$(LIGHTNING_ENABLE_OPENMP) \ - -DKokkos_ENABLE_OPENMP=$(KOKKOS_ENABLE_OPENMP) \ -DENABLE_CODE_COVERAGE=$(CODE_COVERAGE) \ -DRUNTIME_ENABLE_WARNINGS=$(ENABLE_WARNINGS) \ -DENABLE_WARNINGS=OFF \ diff --git a/runtime/README.rst b/runtime/README.rst index 38ce1c8178..ad5f581c99 100644 --- a/runtime/README.rst +++ b/runtime/README.rst @@ -98,23 +98,8 @@ Installation By default, the runtime builds all supported backend devices. You can build the runtime with custom devices from the list of Backend Devices. -You can use ``ENABLE_LIGHTNING_KOKKOS=OFF`` to disable building the runtime with -`lightning.kokkos `_: -.. code-block:: console - - make runtime ENABLE_LIGHTNING_KOKKOS=OFF - -Lightning-Kokkos provides support for other Kokkos backends including OpenMP, HIP and CUDA. -Please refer to `the installation guideline `_ for the requirements. -You can further use the ``CMAKE_ARGS`` flag to issue any additional compiler arguments or override the preset ones in the make commands. -To build the runtime with the ``Kokkos::OpenMP`` backend execution space: - -.. code-block:: console - - make runtime CMAKE_ARGS="-DKokkos_ENABLE_OPENMP=ON" - -You can also use ``ENABLE_OPENQASM=OFF`` to disable building the runtime with `Amazon-Braket-OpenQasm `_: +You can use ``ENABLE_OPENQASM=OFF`` to disable building the runtime with `Amazon-Braket-OpenQasm `_: .. code-block:: console diff --git a/runtime/lib/backend/CMakeLists.txt b/runtime/lib/backend/CMakeLists.txt index c688d78cc6..f93c9321f2 100644 --- a/runtime/lib/backend/CMakeLists.txt +++ b/runtime/lib/backend/CMakeLists.txt @@ -1,6 +1,6 @@ add_subdirectory(dummy) configure_file(dummy/dummy_device.toml dummy_device.toml) -if(ENABLE_LIGHTNING OR ENABLE_LIGHTNING_KOKKOS) +if(ENABLE_LIGHTNING) add_subdirectory(lightning) endif() if(ENABLE_OPENQASM) diff --git a/runtime/lib/backend/lightning/CMakeLists.txt b/runtime/lib/backend/lightning/CMakeLists.txt index 2ae61bfdc8..0612d47e4f 100644 --- a/runtime/lib/backend/lightning/CMakeLists.txt +++ b/runtime/lib/backend/lightning/CMakeLists.txt @@ -46,12 +46,8 @@ else() endif() set(CMAKE_POSITION_INDEPENDENT_CODE ON) # build with -fPIC -if(ENABLE_LIGHTNING AND ENABLE_LIGHTNING_KOKKOS) - set(PL_BACKEND "lightning_qubit;lightning_kokkos" CACHE STRING "PennyLane Lightning backend") -elseif(ENABLE_LIGHTNING) +if(ENABLE_LIGHTNING) set(PL_BACKEND "lightning_qubit" CACHE STRING "PennyLane Lightning backend") -elseif(ENABLE_LIGHTNING_KOKKOS) - set(PL_BACKEND "lightning_kokkos" CACHE STRING "PennyLane Lightning backend") endif() set(ENABLE_PYTHON OFF CACHE BOOL "Enable compilation of the Python module") @@ -65,11 +61,6 @@ if(ENABLE_LIGHTNING) lightning_dynamic/LightningSimulator.cpp ) endif() -if(ENABLE_LIGHTNING_KOKKOS) - list(APPEND src_files - lightning_kokkos/LightningKokkosSimulator.cpp - ) -endif() add_library(rtd_lightning SHARED ${src_files}) add_dependencies(rtd_lightning pennylane_lightning) diff --git a/runtime/lib/backend/lightning/lightning_kokkos/LightningKokkosObsManager.hpp b/runtime/lib/backend/lightning/lightning_kokkos/LightningKokkosObsManager.hpp deleted file mode 100644 index 0a7079e5c8..0000000000 --- a/runtime/lib/backend/lightning/lightning_kokkos/LightningKokkosObsManager.hpp +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2023 Xanadu Quantum Technologies Inc. - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#pragma once - -#include -#include -#include -#include - -#include "Types.h" -#include "Utils.hpp" - -#include "ObservablesKokkos.hpp" - -namespace Catalyst::Runtime::Simulator { - -/** - * @brief The LightningKokkosObsManager caches observables of a program at runtime - * and maps each one to a const unique index (`int64_t`) in the scope - * of the global context manager. - */ -template class LightningKokkosObsManager { - private: - using VectorStateT = Pennylane::LightningKokkos::StateVectorKokkos; - using ObservableClassName = Pennylane::Observables::Observable; - using ObservablePairType = std::pair, ObsType>; - std::vector observables_{}; - - public: - LightningKokkosObsManager() = default; - ~LightningKokkosObsManager() = default; - - LightningKokkosObsManager(const LightningKokkosObsManager &) = delete; - LightningKokkosObsManager &operator=(const LightningKokkosObsManager &) = delete; - LightningKokkosObsManager(LightningKokkosObsManager &&) = delete; - LightningKokkosObsManager &operator=(LightningKokkosObsManager &&) = delete; - - /** - * @brief A helper function to clear constructed observables in the program. - */ - void clear() { this->observables_.clear(); } - - /** - * @brief Check the validity of observable keys. - * - * @param obsKeys The vector of observable keys - * @return bool - */ - [[nodiscard]] auto isValidObservables(const std::vector &obsKeys) const -> bool - { - return std::all_of(obsKeys.begin(), obsKeys.end(), [this](auto i) { - return (i >= 0 && static_cast(i) < this->observables_.size()); - }); - } - - /** - * @brief Get the constructed observable instance. - * - * @param key The observable key - * @return std::shared_ptr - */ - [[nodiscard]] auto getObservable(ObsIdType key) -> std::shared_ptr - { - RT_FAIL_IF(!this->isValidObservables({key}), "Invalid observable key"); - return std::get<0>(this->observables_[key]); - } - - /** - * @brief Get the number of observables. - * - * @return size_t - */ - [[nodiscard]] auto numObservables() const -> size_t { return this->observables_.size(); } - - /** - * @brief Create and cache a new NamedObs instance. - * - * @param obsId The named observable id of type ObsId - * @param wires The vector of wires the observable acts on - * @return ObsIdType - */ - [[nodiscard]] auto createNamedObs(ObsId obsId, const std::vector &wires) -> ObsIdType - { - auto &&obs_str = - std::string(Lightning::lookup_obs( - Lightning::simulator_observable_support, obsId)); - - this->observables_.push_back(std::make_pair( - std::make_shared>( - obs_str, wires), - ObsType::Basic)); - return static_cast(this->observables_.size() - 1); - } - - /** - * @brief Create and cache a new HermitianObs instance. - * - * @param matrix The row-wise Hermitian matrix - * @param wires The vector of wires the observable acts on - * @return ObsIdType - */ - [[nodiscard]] auto createHermitianObs(const std::vector> &matrix, - const std::vector &wires) -> ObsIdType - { - std::vector> matrix_k; - matrix_k.reserve(matrix.size()); - for (const auto &elem : matrix) { - matrix_k.push_back(static_cast>(elem)); - } - - this->observables_.push_back(std::make_pair( - std::make_shared>( - Pennylane::LightningKokkos::Observables::HermitianObs{matrix_k, - wires}), - ObsType::Basic)); - - return static_cast(this->observables_.size() - 1); - } - - /** - * @brief Create and cache a new TensorProd instance. - * - * @param obsKeys The vector of observable keys - * @return ObsIdType - */ - [[nodiscard]] auto createTensorProdObs(const std::vector &obsKeys) -> ObsIdType - { - const auto key_size = obsKeys.size(); - const auto obs_size = this->observables_.size(); - - std::vector> obs_vec; - obs_vec.reserve(key_size); - - for (const auto &key : obsKeys) { - RT_FAIL_IF(static_cast(key) >= obs_size || key < 0, "Invalid observable key"); - - auto &&[obs, type] = this->observables_[key]; - obs_vec.push_back(obs); - } - - this->observables_.push_back(std::make_pair( - Pennylane::LightningKokkos::Observables::TensorProdObs::create(obs_vec), - ObsType::TensorProd)); - - return static_cast(obs_size); - } - - /** - * @brief Create and cache a new HamiltonianObs instance. - * - * @param coeffs The vector of coefficients - * @param obsKeys The vector of observable keys - * @return ObsIdType - */ - [[nodiscard]] auto createHamiltonianObs(const std::vector &coeffs, - const std::vector &obsKeys) -> ObsIdType - { - const auto key_size = obsKeys.size(); - const auto obs_size = this->observables_.size(); - - RT_FAIL_IF(key_size != coeffs.size(), - "Incompatible list of observables and coefficients; " - "Number of observables and number of coefficients must be equal"); - - std::vector> obs_vec; - obs_vec.reserve(key_size); - - for (auto key : obsKeys) { - RT_FAIL_IF(static_cast(key) >= obs_size || key < 0, "Invalid observable key"); - - auto &&[obs, type] = this->observables_[key]; - obs_vec.push_back(obs); - } - - this->observables_.push_back(std::make_pair( - std::make_shared>( - Pennylane::LightningKokkos::Observables::Hamiltonian( - coeffs, std::move(obs_vec))), - ObsType::Hamiltonian)); - - return static_cast(obs_size); - } -}; -} // namespace Catalyst::Runtime::Simulator diff --git a/runtime/lib/backend/lightning/lightning_kokkos/LightningKokkosSimulator.cpp b/runtime/lib/backend/lightning/lightning_kokkos/LightningKokkosSimulator.cpp deleted file mode 100644 index 0eb6eb3145..0000000000 --- a/runtime/lib/backend/lightning/lightning_kokkos/LightningKokkosSimulator.cpp +++ /dev/null @@ -1,544 +0,0 @@ -// Copyright 2022-2023 Xanadu Quantum Technologies Inc. - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include - -#include "LightningKokkosSimulator.hpp" - -namespace Catalyst::Runtime::Simulator { - -auto LightningKokkosSimulator::AllocateQubit() -> QubitIdType -{ - const size_t num_qubits = this->device_sv->getNumQubits(); - - if (!num_qubits) { - this->device_sv = std::make_unique(1); - return this->qubit_manager.Allocate(num_qubits); - } - - std::vector> data = this->device_sv->getDataVector(); - const size_t dsize = data.size(); - data.resize(dsize << 1UL); - - auto src = data.begin(); - std::advance(src, dsize - 1); - - for (auto dst = data.end() - 2; src != data.begin(); - std::advance(src, -1), std::advance(dst, -2)) { - *dst = std::move(*src); - *src = Kokkos::complex(.0, .0); - } - - this->device_sv = std::make_unique(data); - return this->qubit_manager.Allocate(num_qubits); -} - -auto LightningKokkosSimulator::AllocateQubits(size_t num_qubits) -> std::vector -{ - if (!num_qubits) { - return {}; - } - - // at the first call when num_qubits == 0 - if (!this->GetNumQubits()) { - this->device_sv = std::make_unique(num_qubits); - return this->qubit_manager.AllocateRange(0, num_qubits); - } - - std::vector result(num_qubits); - std::generate_n(result.begin(), num_qubits, [this]() { return AllocateQubit(); }); - return result; -} - -void LightningKokkosSimulator::ReleaseQubit(QubitIdType q) { this->qubit_manager.Release(q); } - -void LightningKokkosSimulator::ReleaseAllQubits() -{ - this->qubit_manager.ReleaseAll(); - this->device_sv = std::make_unique(0); // reset the device -} - -auto LightningKokkosSimulator::GetNumQubits() const -> size_t -{ - return this->device_sv->getNumQubits(); -} - -void LightningKokkosSimulator::StartTapeRecording() -{ - RT_FAIL_IF(this->tape_recording, "Cannot re-activate the cache manager"); - this->tape_recording = true; - this->cache_manager.Reset(); -} - -void LightningKokkosSimulator::StopTapeRecording() -{ - RT_FAIL_IF(!this->tape_recording, "Cannot stop an already stopped cache manager"); - this->tape_recording = false; -} - -auto LightningKokkosSimulator::CacheManagerInfo() - -> std::tuple, std::vector> -{ - return {this->cache_manager.getNumOperations(), this->cache_manager.getNumObservables(), - this->cache_manager.getNumParams(), this->cache_manager.getOperationsNames(), - this->cache_manager.getObservablesKeys()}; -} - -void LightningKokkosSimulator::SetDeviceShots(size_t shots) { this->device_shots = shots; } - -auto LightningKokkosSimulator::GetDeviceShots() const -> size_t { return this->device_shots; } - -void LightningKokkosSimulator::SetDevicePRNG(std::mt19937 *gen) { this->gen = gen; } - -void LightningKokkosSimulator::PrintState() -{ - using std::cout; - using std::endl; - using UnmanagedComplexHostView = Kokkos::View *, Kokkos::HostSpace, - Kokkos::MemoryTraits>; - - const size_t num_qubits = this->device_sv->getNumQubits(); - const size_t size = Pennylane::Util::exp2(num_qubits); - - std::vector> state(size, {0.0, 0.0}); - auto *state_kptr = reinterpret_cast *>(state.data()); - auto device_data = this->device_sv->getView(); - Kokkos::deep_copy(UnmanagedComplexHostView(state_kptr, size), device_data); - - size_t idx = 0; - cout << "*** State-Vector of Size " << size << " ***" << endl; - cout << "["; - for (; idx < size - 1; idx++) { - cout << state[idx] << ", "; - } - cout << state[idx] << "]" << endl; -} - -auto LightningKokkosSimulator::Zero() const -> Result -{ - return const_cast(&GLOBAL_RESULT_FALSE_CONST); -} - -auto LightningKokkosSimulator::One() const -> Result -{ - return const_cast(&GLOBAL_RESULT_TRUE_CONST); -} - -void LightningKokkosSimulator::NamedOperation(const std::string &name, - const std::vector ¶ms, - const std::vector &wires, bool inverse, - const std::vector &controlled_wires, - const std::vector &controlled_values) -{ - RT_FAIL_IF(!controlled_wires.empty() || !controlled_values.empty(), - "LightningKokkos does not support native quantum control."); - - // Check the validity of number of qubits and parameters - RT_FAIL_IF(!isValidQubits(wires), "Given wires do not refer to qubits"); - RT_FAIL_IF(!isValidQubits(controlled_wires), "Given controlled wires do not refer to qubits"); - - // Convert wires to device wires - auto &&dev_wires = getDeviceWires(wires); - - // Update the state-vector - this->device_sv->applyOperation(name, dev_wires, inverse, params); - - // Update tape caching if required - if (this->tape_recording) { - this->cache_manager.addOperation(name, params, dev_wires, inverse, {}, - {/*controlled_wires*/}, {/*controlled_values*/}); - } -} - -void LightningKokkosSimulator::MatrixOperation(const std::vector> &matrix, - const std::vector &wires, bool inverse, - const std::vector &controlled_wires, - const std::vector &controlled_values) -{ - using UnmanagedComplexHostView = Kokkos::View *, Kokkos::HostSpace, - Kokkos::MemoryTraits>; - - // TODO: Remove when controlled wires API is supported - RT_FAIL_IF(!controlled_wires.empty() || !controlled_values.empty(), - "LightningKokkos device does not support native quantum control."); - RT_FAIL_IF(!isValidQubits(wires), "Given wires do not refer to qubits"); - RT_FAIL_IF(!isValidQubits(controlled_wires), "Given controlled wires do not refer to qubits"); - - // Convert wires to device wires - auto &&dev_wires = getDeviceWires(wires); - - std::vector> matrix_kok; - matrix_kok.resize(matrix.size()); - std::transform(matrix.begin(), matrix.end(), matrix_kok.begin(), - [](auto c) { return static_cast>(c); }); - - Kokkos::View *> gate_matrix("gate_matrix", matrix_kok.size()); - Kokkos::deep_copy(gate_matrix, UnmanagedComplexHostView(matrix_kok.data(), matrix_kok.size())); - - // Update the state-vector - this->device_sv->applyMultiQubitOp(gate_matrix, dev_wires, inverse); - - // Update tape caching if required - if (this->tape_recording) { - this->cache_manager.addOperation("QubitUnitary", {}, dev_wires, inverse, matrix_kok, - {/*controlled_wires*/}, {/*controlled_values*/}); - } -} - -auto LightningKokkosSimulator::Observable(ObsId id, const std::vector> &matrix, - const std::vector &wires) -> ObsIdType -{ - RT_FAIL_IF(wires.size() > this->GetNumQubits(), "Invalid number of wires"); - RT_FAIL_IF(!isValidQubits(wires), "Invalid given wires"); - - auto &&dev_wires = getDeviceWires(wires); - - if (id == ObsId::Hermitian) { - return this->obs_manager.createHermitianObs(matrix, dev_wires); - } - - return this->obs_manager.createNamedObs(id, dev_wires); -} - -auto LightningKokkosSimulator::TensorObservable(const std::vector &obs) -> ObsIdType -{ - return this->obs_manager.createTensorProdObs(obs); -} - -auto LightningKokkosSimulator::HamiltonianObservable(const std::vector &coeffs, - const std::vector &obs) -> ObsIdType -{ - return this->obs_manager.createHamiltonianObs(coeffs, obs); -} - -auto LightningKokkosSimulator::Expval(ObsIdType obsKey) -> double -{ - RT_FAIL_IF(!this->obs_manager.isValidObservables({obsKey}), - "Invalid key for cached observables"); - - // update tape caching - if (this->tape_recording) { - cache_manager.addObservable(obsKey, MeasurementsT::Expval); - } - - auto &&obs = this->obs_manager.getObservable(obsKey); - - Pennylane::LightningKokkos::Measures::Measurements m{*(this->device_sv)}; - - return device_shots ? m.expval(*obs, device_shots, {}) : m.expval(*obs); -} - -auto LightningKokkosSimulator::Var(ObsIdType obsKey) -> double -{ - RT_FAIL_IF(!this->obs_manager.isValidObservables({obsKey}), - "Invalid key for cached observables"); - - // update tape caching - if (this->tape_recording) { - this->cache_manager.addObservable(obsKey, MeasurementsT::Var); - } - - auto &&obs = this->obs_manager.getObservable(obsKey); - - Pennylane::LightningKokkos::Measures::Measurements m{*(this->device_sv)}; - - return device_shots ? m.var(*obs, device_shots) : m.var(*obs); -} - -void LightningKokkosSimulator::State(DataView, 1> &state) -{ - using UnmanagedComplexHostView = Kokkos::View *, Kokkos::HostSpace, - Kokkos::MemoryTraits>; - - const size_t num_qubits = this->device_sv->getNumQubits(); - const size_t size = Pennylane::Util::exp2(num_qubits); - RT_FAIL_IF(state.size() != size, "Invalid size for the pre-allocated state vector"); - - // create a temporary buffer to copy the underlying state-vector to - std::vector> buffer(size); - auto *state_kptr = reinterpret_cast *>(buffer.data()); - - // copy data from device to host - auto device_data = this->device_sv->getView(); - Kokkos::deep_copy(UnmanagedComplexHostView(state_kptr, size), device_data); - - // move data to state leveraging MemRefIter - std::move(buffer.begin(), buffer.end(), state.begin()); -} - -void LightningKokkosSimulator::Probs(DataView &probs) -{ - Pennylane::LightningKokkos::Measures::Measurements m{*(this->device_sv)}; - auto &&dv_probs = device_shots ? m.probs(device_shots) : m.probs(); - - RT_FAIL_IF(probs.size() != dv_probs.size(), "Invalid size for the pre-allocated probabilities"); - - std::move(dv_probs.begin(), dv_probs.end(), probs.begin()); -} - -void LightningKokkosSimulator::PartialProbs(DataView &probs, - const std::vector &wires) -{ - const size_t numWires = wires.size(); - const size_t numQubits = this->GetNumQubits(); - - RT_FAIL_IF(numWires > numQubits, "Invalid number of wires"); - RT_FAIL_IF(!isValidQubits(wires), "Invalid given wires to measure"); - - auto dev_wires = getDeviceWires(wires); - Pennylane::LightningKokkos::Measures::Measurements m{*(this->device_sv)}; - auto &&dv_probs = device_shots ? m.probs(dev_wires, device_shots) : m.probs(dev_wires); - - RT_FAIL_IF(probs.size() != dv_probs.size(), - "Invalid size for the pre-allocated partial-probabilities"); - - std::move(dv_probs.begin(), dv_probs.end(), probs.begin()); -} - -void LightningKokkosSimulator::Sample(DataView &samples, size_t shots) -{ - Pennylane::LightningKokkos::Measures::Measurements m{*(this->device_sv)}; - // PL-Lightning-Kokkos generates samples using the alias method. - // Reference: https://en.wikipedia.org/wiki/Inverse_transform_sampling - auto li_samples = m.generate_samples(shots); - - RT_FAIL_IF(samples.size() != li_samples.size(), "Invalid size for the pre-allocated samples"); - - const size_t numQubits = this->GetNumQubits(); - - // The lightning samples are layed out as a single vector of size - // shots*qubits, where each element represents a single bit. The - // corresponding shape is (shots, qubits). Gather the desired bits - // corresponding to the input wires into a bitstring. - auto samplesIter = samples.begin(); - for (size_t shot = 0; shot < shots; shot++) { - for (size_t wire = 0; wire < numQubits; wire++) { - *(samplesIter++) = static_cast(li_samples[shot * numQubits + wire]); - } - } -} -void LightningKokkosSimulator::PartialSample(DataView &samples, - const std::vector &wires, size_t shots) -{ - const size_t numWires = wires.size(); - const size_t numQubits = this->GetNumQubits(); - - RT_FAIL_IF(numWires > numQubits, "Invalid number of wires"); - RT_FAIL_IF(!isValidQubits(wires), "Invalid given wires to measure"); - RT_FAIL_IF(samples.size() != shots * numWires, - "Invalid size for the pre-allocated partial-samples"); - - // get device wires - auto &&dev_wires = getDeviceWires(wires); - - // generate_samples is a member function of the MeasuresKokkos class. - Pennylane::LightningKokkos::Measures::Measurements m{*(this->device_sv)}; - - // PL-Lightning-Kokkos generates samples using the alias method. - // Reference: https://en.wikipedia.org/wiki/Inverse_transform_sampling - auto li_samples = m.generate_samples(shots); - - // The lightning samples are layed out as a single vector of size - // shots*qubits, where each element represents a single bit. The - // corresponding shape is (shots, qubits). Gather the desired bits - // corresponding to the input wires into a bitstring. - auto samplesIter = samples.begin(); - for (size_t shot = 0; shot < shots; shot++) { - for (auto wire : dev_wires) { - *(samplesIter++) = static_cast(li_samples[shot * numQubits + wire]); - } - } -} - -void LightningKokkosSimulator::Counts(DataView &eigvals, DataView &counts, - size_t shots) -{ - const size_t numQubits = this->GetNumQubits(); - const size_t numElements = 1U << numQubits; - - RT_FAIL_IF(eigvals.size() != numElements || counts.size() != numElements, - "Invalid size for the pre-allocated counts"); - - // generate_samples is a member function of the MeasuresKokkos class. - Pennylane::LightningKokkos::Measures::Measurements m{*(this->device_sv)}; - - // PL-Lightning-Kokkos generates samples using the alias method. - // Reference: https://en.wikipedia.org/wiki/Inverse_transform_sampling - auto li_samples = m.generate_samples(shots); - - // Fill the eigenvalues with the integer representation of the corresponding - // computational basis bitstring. In the future, eigenvalues can also be - // obtained from an observable, hence the bitstring integer is stored as a - // double. - std::iota(eigvals.begin(), eigvals.end(), 0); - std::fill(counts.begin(), counts.end(), 0); - - // The lightning samples are layed out as a single vector of size - // shots*qubits, where each element represents a single bit. The - // corresponding shape is (shots, qubits). Gather the bits of all qubits - // into a bitstring. - for (size_t shot = 0; shot < shots; shot++) { - std::bitset basisState; - size_t idx = numQubits; - for (size_t wire = 0; wire < numQubits; wire++) { - basisState[--idx] = li_samples[shot * numQubits + wire]; - } - counts(static_cast(basisState.to_ulong())) += 1; - } -} - -void LightningKokkosSimulator::PartialCounts(DataView &eigvals, - DataView &counts, - const std::vector &wires, size_t shots) -{ - const size_t numWires = wires.size(); - const size_t numQubits = this->GetNumQubits(); - const size_t numElements = 1U << numWires; - - RT_FAIL_IF(numWires > numQubits, "Invalid number of wires"); - RT_FAIL_IF(!isValidQubits(wires), "Invalid given wires to measure"); - RT_FAIL_IF((eigvals.size() != numElements || counts.size() != numElements), - "Invalid size for the pre-allocated partial-counts"); - - // get device wires - auto &&dev_wires = getDeviceWires(wires); - - // generate_samples is a member function of the MeasuresKokkos class. - Pennylane::LightningKokkos::Measures::Measurements m{*(this->device_sv)}; - - // PL-Lightning-Kokkos generates samples using the alias method. - // Reference: https://en.wikipedia.org/wiki/Inverse_transform_sampling - auto li_samples = m.generate_samples(shots); - - // Fill the eigenvalues with the integer representation of the corresponding - // computational basis bitstring. In the future, eigenvalues can also be - // obtained from an observable, hence the bitstring integer is stored as a - // double. - std::iota(eigvals.begin(), eigvals.end(), 0); - std::fill(counts.begin(), counts.end(), 0); - - // The lightning samples are layed out as a single vector of size - // shots*qubits, where each element represents a single bit. The - // corresponding shape is (shots, qubits). Gather the desired bits - // corresponding to the input wires into a bitstring. - for (size_t shot = 0; shot < shots; shot++) { - std::bitset basisState; - size_t idx = dev_wires.size(); - for (auto wire : dev_wires) { - basisState[--idx] = li_samples[shot * numQubits + wire]; - } - counts(static_cast(basisState.to_ulong())) += 1; - } -} - -auto LightningKokkosSimulator::Measure(QubitIdType wire, std::optional postselect) - -> Result -{ - // get a measurement - std::vector wires = {reinterpret_cast(wire)}; - - std::vector probs(1U << wires.size()); - DataView buffer_view(probs); - auto device_shots = GetDeviceShots(); - SetDeviceShots(0); - PartialProbs(buffer_view, wires); - SetDeviceShots(device_shots); - - // It represents the measured result, true for 1, false for 0 - bool mres = Lightning::simulateDraw(probs, postselect, this->gen); - auto dev_wires = getDeviceWires(wires); - this->device_sv->collapse(dev_wires[0], mres ? 1 : 0); - return mres ? this->One() : this->Zero(); -} - -void LightningKokkosSimulator::Gradient(std::vector> &gradients, - const std::vector &trainParams) -{ - const bool tp_empty = trainParams.empty(); - const size_t num_observables = this->cache_manager.getNumObservables(); - const size_t num_params = this->cache_manager.getNumParams(); - const size_t num_train_params = tp_empty ? num_params : trainParams.size(); - const size_t jac_size = num_train_params * this->cache_manager.getNumObservables(); - - if (!jac_size) { - return; - } - - RT_FAIL_IF(gradients.size() != num_observables, "Invalid number of pre-allocated gradients"); - - auto &&obs_callees = this->cache_manager.getObservablesCallees(); - bool is_valid_measurements = - std::all_of(obs_callees.begin(), obs_callees.end(), - [](const auto &m) { return m == MeasurementsT::Expval; }); - RT_FAIL_IF(!is_valid_measurements, - "Unsupported measurements to compute gradient; " - "Adjoint differentiation method only supports expectation return type"); - - // Create OpsData - auto &&ops_names = this->cache_manager.getOperationsNames(); - auto &&ops_params = this->cache_manager.getOperationsParameters(); - auto &&ops_wires = this->cache_manager.getOperationsWires(); - auto &&ops_inverses = this->cache_manager.getOperationsInverses(); - auto &&ops_matrices = this->cache_manager.getOperationsMatrices(); - auto &&ops_controlled_wires = this->cache_manager.getOperationsControlledWires(); - auto &&ops_controlled_values = this->cache_manager.getOperationsControlledValues(); - - const auto &&ops = Pennylane::Algorithms::OpsData( - ops_names, ops_params, ops_wires, ops_inverses, ops_matrices, ops_controlled_wires, - ops_controlled_values); - - // Create the vector of observables - auto &&obs_keys = this->cache_manager.getObservablesKeys(); - std::vector>> obs_vec; - obs_vec.reserve(obs_keys.size()); - for (auto idx : obs_keys) { - obs_vec.emplace_back(this->obs_manager.getObservable(idx)); - } - - std::vector all_params; - if (tp_empty) { - all_params.reserve(num_params); - for (size_t i = 0; i < num_params; i++) { - all_params.push_back(i); - } - } - - auto &&state = this->device_sv->getDataVector(); - - // construct the Jacobian data - Pennylane::Algorithms::JacobianData tape{ - num_params, state.size(), state.data(), obs_vec, ops, tp_empty ? all_params : trainParams}; - - Pennylane::LightningKokkos::Algorithms::AdjointJacobian adj; - std::vector jacobian(jac_size, 0); - adj.adjointJacobian(std::span{jacobian}, tape, - /* ref_data */ *this->device_sv, - /* apply_operations */ false); - - std::vector cur_buffer(num_train_params); - auto begin_loc_iter = jacobian.begin(); - for (size_t obs_idx = 0; obs_idx < num_observables; obs_idx++) { - RT_ASSERT(begin_loc_iter != jacobian.end()); - RT_ASSERT(num_train_params <= gradients[obs_idx].size()); - std::move(begin_loc_iter, begin_loc_iter + num_train_params, cur_buffer.begin()); - std::move(cur_buffer.begin(), cur_buffer.end(), gradients[obs_idx].begin()); - begin_loc_iter += num_train_params; - } -} - -} // namespace Catalyst::Runtime::Simulator - -GENERATE_DEVICE_FACTORY(LightningKokkosSimulator, - Catalyst::Runtime::Simulator::LightningKokkosSimulator); diff --git a/runtime/lib/backend/lightning/lightning_kokkos/LightningKokkosSimulator.hpp b/runtime/lib/backend/lightning/lightning_kokkos/LightningKokkosSimulator.hpp deleted file mode 100644 index 24ddd140c0..0000000000 --- a/runtime/lib/backend/lightning/lightning_kokkos/LightningKokkosSimulator.hpp +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2022-2023 Xanadu Quantum Technologies Inc. - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -#pragma once - -#define __device_lightning_kokkos - -#include -#include -#include -#include -#include -#include -#include - -#include "AdjointJacobianKokkos.hpp" -#include "MeasurementsKokkos.hpp" -#include "StateVectorKokkos.hpp" - -#include "CacheManager.hpp" -#include "Exception.hpp" -#include "LightningKokkosObsManager.hpp" -#include "QuantumDevice.hpp" -#include "QubitManager.hpp" -#include "Utils.hpp" - -namespace Catalyst::Runtime::Simulator { -class LightningKokkosSimulator final : public Catalyst::Runtime::QuantumDevice { - private: - using StateVectorT = Pennylane::LightningKokkos::StateVectorKokkos; - - // static constants for RESULT values - static constexpr bool GLOBAL_RESULT_TRUE_CONST = true; - static constexpr bool GLOBAL_RESULT_FALSE_CONST = false; - - Catalyst::Runtime::QubitManager qubit_manager{}; - Catalyst::Runtime::CacheManager> cache_manager{}; - bool tape_recording{false}; - - size_t device_shots; - - std::mt19937 *gen = nullptr; - - std::unique_ptr device_sv = std::make_unique(0); - LightningKokkosObsManager obs_manager{}; - - inline auto isValidQubit(QubitIdType wire) -> bool - { - return this->qubit_manager.isValidQubitId(wire); - } - - inline auto isValidQubits(const std::vector &wires) -> bool - { - return std::all_of(wires.begin(), wires.end(), - [this](QubitIdType w) { return this->isValidQubit(w); }); - } - - inline auto isValidQubits(size_t numWires, const QubitIdType *wires) -> bool - { - return std::all_of(wires, wires + numWires, - [this](QubitIdType w) { return this->isValidQubit(w); }); - } - - inline auto getDeviceWires(const std::vector &wires) -> std::vector - { - std::vector res; - res.reserve(wires.size()); - std::transform(wires.begin(), wires.end(), std::back_inserter(res), - [this](auto w) { return this->qubit_manager.getDeviceId(w); }); - return res; - } - - public: - explicit LightningKokkosSimulator(const std::string &kwargs = "{}") - { - auto &&args = Catalyst::Runtime::parse_kwargs(kwargs); - device_shots = args.contains("shots") ? static_cast(std::stoll(args["shots"])) : 0; - } - ~LightningKokkosSimulator() = default; - - void SetDevicePRNG(std::mt19937 *) override; - - QUANTUM_DEVICE_DEL_DECLARATIONS(LightningKokkosSimulator); - - QUANTUM_DEVICE_RT_DECLARATIONS; - QUANTUM_DEVICE_QIS_DECLARATIONS; - - auto CacheManagerInfo() - -> std::tuple, std::vector>; -}; -} // namespace Catalyst::Runtime::Simulator diff --git a/runtime/tests/CMakeLists.txt b/runtime/tests/CMakeLists.txt index 99a96f17ec..1a7bc84f1b 100644 --- a/runtime/tests/CMakeLists.txt +++ b/runtime/tests/CMakeLists.txt @@ -24,7 +24,7 @@ list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/contrib) include(CTest) include(Catch) -if(ENABLE_LIGHTNING OR ENABLE_LIGHTNING_KOKKOS) +if(ENABLE_LIGHTNING) add_executable(runner_tests_lightning runner_main.cpp) target_include_directories(runner_tests_lightning PRIVATE catalyst_python_interpreter) @@ -66,11 +66,6 @@ if(ENABLE_LIGHTNING OR ENABLE_LIGHTNING_KOKKOS) Test_SVDynamicCPU_Allocation.cpp ) - if(KOKKOS_ENABLE_OPENMP) - find_package(OpenMP REQUIRED) - target_link_libraries(runner_tests_lightning INTERFACE OpenMP::OpenMP_CXX) - endif() - if(PLKOKKOS_ENABLE_NATIVE) message(STATUS "ENABLE_NATIVE is ON. Use -march=native for cpptests.") target_compile_options(runner_tests_lightning PRIVATE -march=native)