Skip to content

Commit

Permalink
Downstream LightningSimulator C++ API to the pennylane-lightning repo…
Browse files Browse the repository at this point in the history
…sitory (#960)

**Context:** Catalyst needs to build a class wrapping the Lightning
Qubit class. Here we are moving the logic from Catalyst to the Lightning
repository.

**Description of the Change:** Moving code from Catalyst to this
repository and updating the build system to build against Catalyst.

**Benefits:** Catalyst wheels will build faster.

**Possible Drawbacks:** Our build system now relies on headers coming
from Catalyst. It is unclear if this may cause a deadlock in the future
because of our cyclic dependencies (PennyLane, PennyLane Lightning,
Catalyst). Chances are small because we don't rely on Catalyst wheels or
build it from scratch.

[sc-70316]
[sc-70318]
[sc-70317]

---------

Co-authored-by: ringo-but-quantum <[email protected]>
Co-authored-by: Lee James O'Riordan <[email protected]>
Co-authored-by: paul0403 <[email protected]>
Co-authored-by: Haochen Wang <[email protected]>
Co-authored-by: Ali Asadi <[email protected]>
  • Loading branch information
6 people authored Nov 12, 2024
1 parent b491a37 commit 4659093
Show file tree
Hide file tree
Showing 22 changed files with 5,062 additions and 8 deletions.
5 changes: 4 additions & 1 deletion .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
* Add native N-controlled generators and adjoint support to `lightning.gpu`'s single-GPU backend.
[(#970)](https://github.com/PennyLaneAI/pennylane-lightning/pull/970)

* Add a Catalyst-specific wrapping class for Lightning Qubit.
[(#960)](https://github.com/PennyLaneAI/pennylane-lightning/pull/960)

* Add native N-controlled gates support to `lightning.gpu`'s single-GPU backend.
[(#938)](https://github.com/PennyLaneAI/pennylane-lightning/pull/938)

Expand Down Expand Up @@ -35,7 +38,7 @@

This release contains contributions from (in alphabetical order):

Ali Asadi, Joseph Lee, Luis Alfredo Nuñez Meneses, Shuli Shu
Ali Asadi, Joseph Lee, Luis Alfredo Nuñez Meneses, Shuli Shu, Raul Torres, Haochen Paul Wang

---

Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/tests_lqcpu_python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -197,10 +197,11 @@ jobs:
- name: Run PennyLane-Lightning unit tests
run: |
DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"`
OMP_NUM_THREADS=1 PL_DEVICE=${DEVICENAME} python -m pytest -n auto tests/ -k "not unitary_correct" \
# Remove `python -m` to avoid running tests with relative modules
OMP_NUM_THREADS=1 PL_DEVICE=${DEVICENAME} pytest -n auto tests/ -k "not unitary_correct" \
$COVERAGE_FLAGS --splits 4 --group ${{ matrix.group }} \
--durations-path='.github/workflows/python_lightning_qubit_test_durations.json' --splitting-algorithm=least_duration
PL_DEVICE=${DEVICENAME} python -m pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append
PL_DEVICE=${DEVICENAME} pytest tests/ -k "unitary_correct" $COVERAGE_FLAGS --cov-append
pl-device-test --device ${DEVICENAME} --skip-ops --shots=20000 $COVERAGE_FLAGS --cov-append
pl-device-test --device ${DEVICENAME} --shots=None --skip-ops $COVERAGE_FLAGS --cov-append
mv .coverage ${{ github.workspace }}/.coverage-${{ github.job }}-${{ matrix.pl_backend }}-${{ matrix.blas }}-${{ matrix.group }}
Expand Down
6 changes: 3 additions & 3 deletions cmake/support_catalyst.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ macro(FindCatalyst target_name)
${HEADER_NAME}
URL https://raw.githubusercontent.com/PennyLaneAI/catalyst/${CATALYST_GIT_TAG}/runtime/lib/backend/common/${HEADER}
DOWNLOAD_NO_EXTRACT True
SOURCE_DIR include
SOURCE_DIR ../../include
)

FetchContent_MakeAvailable(${HEADER_NAME})
Expand All @@ -62,13 +62,13 @@ macro(FindCatalyst target_name)
${HEADER_NAME}
URL https://raw.githubusercontent.com/PennyLaneAI/catalyst/${CATALYST_GIT_TAG}/runtime/include/${HEADER}
DOWNLOAD_NO_EXTRACT True
SOURCE_DIR include
SOURCE_DIR ../../include
)

FetchContent_MakeAvailable(${HEADER_NAME})
endforeach()

target_include_directories(${target_name} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include)
target_include_directories(${target_name} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/../../include)

endif()
endmacro()
2 changes: 1 addition & 1 deletion pennylane_lightning/core/_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@
Version number (major.minor.patch[-label])
"""

__version__ = "0.40.0-dev6"
__version__ = "0.40.0-dev7"
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,13 @@ set(COMPONENT_SUBDIRS algorithms
utils
)

# Do not build the LQ plugin for Windows
if (NOT WIN32)
set(COMPONENT_SUBDIRS ${COMPONENT_SUBDIRS}
catalyst
)
endif()

foreach(COMP ${COMPONENT_SUBDIRS})
add_subdirectory(${COMP})
endforeach()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
cmake_minimum_required(VERSION 3.20)

project(lightning_qubit_catalyst LANGUAGES CXX)

set(LQ_CATALYST_FILES LightningSimulator.cpp CACHE INTERNAL "")
add_library(lightning_qubit_catalyst SHARED ${LQ_CATALYST_FILES})

include(FetchContent)

include("${pennylane_lightning_SOURCE_DIR}/cmake/support_catalyst.cmake")
FindCatalyst(lightning_qubit_catalyst)

target_include_directories(lightning_qubit_catalyst INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(lightning_qubit_catalyst PUBLIC lightning_compile_options
lightning_external_libs
lightning_qubit_algorithms
lightning_qubit_measurements
lightning_qubit_observables
)

if (BUILD_TESTS)
enable_testing()
add_subdirectory("tests")
endif()
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
// Copyright 2022 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 <array>
#include <stdexcept>
#include <tuple>
#include <utility>

#include "Exception.hpp"
#include "Types.h"
#include "Utils.hpp"

#include "ObservablesLQubit.hpp"
#include "StateVectorLQubitDynamic.hpp"

namespace {
using Pennylane::LightningQubit::StateVectorLQubitDynamic;
using Pennylane::LightningQubit::Observables::HermitianObs;
using Pennylane::LightningQubit::Observables::NamedObs;
using Pennylane::LightningQubit::Observables::TensorProdObs;
using Pennylane::Observables::Observable;
} // namespace

namespace Catalyst::Runtime::Simulator {

/**
* @brief The LightningObsManager 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 <typename PrecisionT> class LightningObsManager {
private:
using VectorStateT = StateVectorLQubitDynamic<PrecisionT>;
using ComplexT = typename VectorStateT::ComplexT;
using ObservablePairType =
std::pair<std::shared_ptr<Observable<VectorStateT>>, ObsType>;
std::vector<ObservablePairType> observables_{};

public:
LightningObsManager() = default;
~LightningObsManager() = default;

LightningObsManager(const LightningObsManager &) = delete;
LightningObsManager &operator=(const LightningObsManager &) = delete;
LightningObsManager(LightningObsManager &&) = delete;
LightningObsManager &operator=(LightningObsManager &&) = delete;

/**
* @brief A helper function to clear constructed observables in the program.
*/
void clear() { observables_.clear(); }

/**
* @brief Check the validity of observable keys.
*
* @param obsKeys The vector of observable keys
* @return bool
*/
[[nodiscard]] auto
isValidObservables(const std::vector<ObsIdType> &obsKeys) const -> bool {
return std::all_of(obsKeys.begin(), obsKeys.end(), [this](auto i) {
return (i >= 0 && static_cast<size_t>(i) < observables_.size());
});
}

/**
* @brief Get the constructed observable instance.
*
* @param key The observable key
* @return std::shared_ptr<Observable<VectorStateT>>
*/
[[nodiscard]] auto getObservable(ObsIdType key)
-> std::shared_ptr<Observable<VectorStateT>> {
RT_FAIL_IF(!isValidObservables({key}), "Invalid observable key");
return std::get<0>(observables_[key]);
}

/**
* @brief Get the number of observables.
*
* @return size_t
*/
[[nodiscard]] auto numObservables() const -> size_t {
return 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<size_t> &wires)
-> ObsIdType {
auto &&obs_str = std::string(
Lightning::lookup_obs<Lightning::simulator_observable_support_size>(
Lightning::simulator_observable_support, obsId));

observables_.push_back(std::make_pair(
std::make_shared<NamedObs<VectorStateT>>(obs_str, wires),
ObsType::Basic));
return static_cast<ObsIdType>(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<ComplexT> &matrix,
const std::vector<size_t> &wires)
-> ObsIdType {
observables_.push_back(
std::make_pair(std::make_shared<HermitianObs<VectorStateT>>(
HermitianObs<VectorStateT>{matrix, wires}),
ObsType::Basic));

return static_cast<ObsIdType>(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<ObsIdType> &obsKeys) -> ObsIdType {
const auto key_size = obsKeys.size();
const auto obs_size = observables_.size();

std::vector<std::shared_ptr<Observable<VectorStateT>>> obs_vec;
obs_vec.reserve(key_size);

for (const auto &key : obsKeys) {
RT_FAIL_IF(static_cast<size_t>(key) >= obs_size || key < 0,
"Invalid observable key");

auto &&[obs, type] = observables_[key];
obs_vec.push_back(obs);
}

observables_.push_back(std::make_pair(
TensorProdObs<VectorStateT>::create(obs_vec), ObsType::TensorProd));

return static_cast<ObsIdType>(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<PrecisionT> &coeffs,
const std::vector<ObsIdType> &obsKeys) -> ObsIdType {
const auto key_size = obsKeys.size();
const auto obs_size = 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<std::shared_ptr<Observable<VectorStateT>>> obs_vec;
obs_vec.reserve(key_size);

for (auto key : obsKeys) {
RT_FAIL_IF(static_cast<size_t>(key) >= obs_size || key < 0,
"Invalid observable key");

auto &&[obs, type] = observables_[key];
obs_vec.push_back(obs);
}

observables_.push_back(std::make_pair(
std::make_shared<Pennylane::LightningQubit::Observables::
Hamiltonian<VectorStateT>>(
Pennylane::LightningQubit::Observables::Hamiltonian<
VectorStateT>(coeffs, std::move(obs_vec))),
ObsType::Hamiltonian));

return static_cast<ObsIdType>(obs_size);
}
};
} // namespace Catalyst::Runtime::Simulator
Loading

0 comments on commit 4659093

Please sign in to comment.