Skip to content

Commit b9b6e1c

Browse files
AmintorDuskoringo-but-quantummaliasadi
authored
Add Catalyst specific Lightning Kokkos class (#770)
**Context:** Catalyst needs to build a class wrapping the Lightning Kokkos 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. **Usage:** This PR offers three ways to configure the CMake building system: 1. Providing a local source path for Catalyst. Example: ```bash cmake -BBuildTests -G Ninja \ -DCMAKE_INSTALL_PREFIX=$path_to_kokkos \ -DCATALYST_SRC_PATH=$path_to_catalyst \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ -DENABLE_WARNINGS=ON \ -DPL_BACKEND=lightning_kokkos ``` 3. Providing a GIT TAG. Example with the main branch: ```bash cmake -BBuildTests -G Ninja \ -DCMAKE_INSTALL_PREFIX=$path_to_kokkos \ -DCATALYST_GIT_TAG=main \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ -DENABLE_WARNINGS=ON \ -DPL_BACKEND=lightning_kokkos ``` 4. If the dev/user doesn't say anything the configuration will default to get headers from Catalyst GitHub main branch. Example: ```bash cmake -BBuildTests -G Ninja \ -DCMAKE_INSTALL_PREFIX=$path_to_kokkos \ -DCMAKE_BUILD_TYPE=Debug \ -DBUILD_TESTS=ON \ -DENABLE_WARNINGS=ON \ -DPL_BACKEND=lightning_kokkos ``` Check out [ADR 76](PennyLaneAI/adrs#76) for alternative approaches. [sc-59312] --------- Co-authored-by: ringo-but-quantum <[email protected]> Co-authored-by: Ali Asadi <[email protected]>
1 parent 91241a0 commit b9b6e1c

12 files changed

+3809
-1
lines changed

.github/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
### Breaking changes
66

77
### Improvements
8+
* Add a Catalyst-specific wrapping class for Lightning Kokkos.
9+
[(#770)](https://github.com/PennyLaneAI/pennylane-lightning/pull/770)
810

911
### Documentation
1012

@@ -14,6 +16,8 @@
1416

1517
This release contains contributions from (in alphabetical order):
1618

19+
Amintor Dusko
20+
1721
---
1822

1923
# Release 0.37.0

pennylane_lightning/core/_version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@
1616
Version number (major.minor.patch[-label])
1717
"""
1818

19-
__version__ = "0.38.0-dev0"
19+
__version__ = "0.38.0-dev1"

pennylane_lightning/core/src/simulators/lightning_kokkos/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ endif()
7070
###############################################################################
7171
set(COMPONENT_SUBDIRS algorithms
7272
bindings
73+
catalyst
7374
gates
7475
measurements
7576
observables
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
cmake_minimum_required(VERSION 3.20)
2+
3+
project(lightning_kokkos_catalyst LANGUAGES CXX)
4+
5+
set(LK_CATALYST_FILES LightningKokkosSimulator.cpp CACHE INTERNAL "")
6+
add_library(lightning_kokkos_catalyst SHARED ${LK_CATALYST_FILES})
7+
8+
include(FetchContent)
9+
10+
if(LIGHTNING_CATALYST_SRC_PATH)
11+
if(NOT IS_ABSOLUTE ${LIGHTNING_CATALYST_SRC_PATH})
12+
message(FATAL_ERROR " LIGHTNING_CATALYST_SRC_PATH=${LIGHTNING_CATALYST_SRC_PATH} must be set to an absolute path")
13+
endif()
14+
if(CATALYST_GIT_TAG)
15+
message(WARN " Setting `LIGHTNING_CATALYST_SRC_PATH=${LIGHTNING_CATALYST_SRC_PATH}` overrides `CATALYST_GIT_TAG=${CATALYST_GIT_TAG}`")
16+
endif()
17+
18+
# Acquire local git hash and use for CATALYST_GIT_TAG
19+
execute_process(COMMAND git rev-parse --short HEAD
20+
WORKING_DIRECTORY ${LIGHTNING_CATALYST_SRC_PATH}
21+
OUTPUT_VARIABLE CATALYST_GIT_TAG
22+
)
23+
message(INFO " Building against local Catalyst - path: ${LIGHTNING_CATALYST_SRC_PATH} - GIT TAG: ${CATALYST_GIT_TAG}")
24+
25+
target_include_directories(lightning_kokkos_catalyst PUBLIC ${LIGHTNING_CATALYST_SRC_PATH}/runtime/lib/backend/common)
26+
target_include_directories(lightning_kokkos_catalyst PUBLIC ${LIGHTNING_CATALYST_SRC_PATH}/runtime/include)
27+
28+
else()
29+
if(NOT CATALYST_GIT_TAG)
30+
set(CATALYST_GIT_TAG "main" CACHE STRING "GIT_TAG value to build Catalyst")
31+
endif()
32+
message(INFO " Building against Catalyst GIT TAG ${CATALYST_GIT_TAG}")
33+
34+
# Fetching /lib/backend/common hpp headers
35+
set(LIB_BACKEND_COMMON_HEADERS CacheManager.hpp
36+
QubitManager.hpp
37+
Utils.hpp
38+
)
39+
40+
foreach(HEADER ${LIB_BACKEND_COMMON_HEADERS})
41+
string(REGEX REPLACE "\\.[^.]*$" "" HEADER_NAME ${HEADER})
42+
FetchContent_Declare(
43+
${HEADER_NAME}
44+
URL https://raw.githubusercontent.com/PennyLaneAI/catalyst/${CATALYST_GIT_TAG}/runtime/lib/backend/common/${HEADER}
45+
DOWNLOAD_NO_EXTRACT True
46+
SOURCE_DIR include
47+
)
48+
49+
FetchContent_MakeAvailable(${HEADER_NAME})
50+
endforeach()
51+
52+
# Fetching include hpp headers
53+
set(INCLUDE_HEADERS DataView.hpp
54+
Exception.hpp
55+
QuantumDevice.hpp
56+
RuntimeCAPI.h
57+
Types.h
58+
)
59+
60+
foreach(HEADER ${INCLUDE_HEADERS})
61+
string(REGEX REPLACE "\\.[^.]*$" "" HEADER_NAME ${HEADER})
62+
FetchContent_Declare(
63+
${HEADER_NAME}
64+
URL https://raw.githubusercontent.com/PennyLaneAI/catalyst/${CATALYST_GIT_TAG}/runtime/include/${HEADER}
65+
DOWNLOAD_NO_EXTRACT True
66+
SOURCE_DIR include
67+
)
68+
69+
FetchContent_MakeAvailable(${HEADER_NAME})
70+
endforeach()
71+
72+
target_include_directories(lightning_kokkos_catalyst PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include)
73+
74+
endif()
75+
76+
target_include_directories(lightning_kokkos_catalyst INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
77+
target_link_libraries(lightning_kokkos_catalyst PUBLIC lightning_compile_options
78+
lightning_external_libs
79+
lightning_base
80+
lightning_gates
81+
lightning_utils
82+
lightning_kokkos
83+
lightning_kokkos_algorithms
84+
lightning_kokkos_gates
85+
lightning_kokkos_measurements
86+
lightning_kokkos_utils
87+
)
88+
89+
if (BUILD_TESTS)
90+
enable_testing()
91+
add_subdirectory("tests")
92+
endif()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
// Copyright 2023-2024 Xanadu Quantum Technologies Inc.
2+
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <array>
18+
#include <stdexcept>
19+
#include <tuple>
20+
#include <utility>
21+
22+
#include "Types.h"
23+
#include "Utils.hpp"
24+
25+
#include "ObservablesKokkos.hpp"
26+
27+
namespace Catalyst::Runtime::Simulator {
28+
29+
/**
30+
* @brief The LightningKokkosObsManager caches observables of a program at
31+
* runtime and maps each one to a const unique index (`int64_t`) in the scope of
32+
* the global context manager.
33+
*/
34+
template <typename PrecisionT> class LightningKokkosObsManager final {
35+
private:
36+
using StateVectorT =
37+
Pennylane::LightningKokkos::StateVectorKokkos<PrecisionT>;
38+
using ObservableT = Pennylane::Observables::Observable<StateVectorT>;
39+
using ObservablePairType = std::pair<std::shared_ptr<ObservableT>, ObsType>;
40+
std::vector<ObservablePairType> observables_{};
41+
42+
public:
43+
LightningKokkosObsManager() = default;
44+
~LightningKokkosObsManager() = default;
45+
46+
LightningKokkosObsManager(const LightningKokkosObsManager &) = delete;
47+
LightningKokkosObsManager &
48+
operator=(const LightningKokkosObsManager &) = delete;
49+
LightningKokkosObsManager(LightningKokkosObsManager &&) = delete;
50+
LightningKokkosObsManager &operator=(LightningKokkosObsManager &&) = delete;
51+
52+
/**
53+
* @brief A helper function to clear constructed observables in the program.
54+
*/
55+
void clear() { this->observables_.clear(); }
56+
57+
/**
58+
* @brief Check the validity of observable keys.
59+
*
60+
* @param obsKeys The vector of observable keys
61+
* @return bool
62+
*/
63+
[[nodiscard]] auto
64+
isValidObservables(const std::vector<ObsIdType> &obsKeys) const -> bool {
65+
return std::all_of(obsKeys.begin(), obsKeys.end(), [this](auto i) {
66+
return (i >= 0 &&
67+
static_cast<size_t>(i) < this->observables_.size());
68+
});
69+
}
70+
71+
/**
72+
* @brief Get the constructed observable instance.
73+
*
74+
* @param key The observable key
75+
* @return std::shared_ptr<ObservableT>
76+
*/
77+
[[nodiscard]] auto getObservable(ObsIdType key)
78+
-> std::shared_ptr<ObservableT> {
79+
RT_FAIL_IF(!this->isValidObservables({key}), "Invalid observable key");
80+
return std::get<0>(this->observables_[key]);
81+
}
82+
83+
/**
84+
* @brief Get the number of observables.
85+
*
86+
* @return size_t
87+
*/
88+
[[nodiscard]] auto numObservables() const -> size_t {
89+
return this->observables_.size();
90+
}
91+
92+
/**
93+
* @brief Create and cache a new NamedObs instance.
94+
*
95+
* @param obsId The named observable id of type ObsId
96+
* @param wires The vector of wires the observable acts on
97+
* @return ObsIdType
98+
*/
99+
[[nodiscard]] auto createNamedObs(ObsId obsId,
100+
const std::vector<size_t> &wires)
101+
-> ObsIdType {
102+
auto &&obs_str = std::string(
103+
Lightning::lookup_obs<Lightning::simulator_observable_support_size>(
104+
Lightning::simulator_observable_support, obsId));
105+
106+
this->observables_.push_back(std::make_pair(
107+
std::make_shared<Pennylane::LightningKokkos::Observables::NamedObs<
108+
StateVectorT>>(obs_str, wires),
109+
ObsType::Basic));
110+
return static_cast<ObsIdType>(this->observables_.size() - 1);
111+
}
112+
113+
/**
114+
* @brief Create and cache a new HermitianObs instance.
115+
*
116+
* @param matrix The row-wise Hermitian matrix
117+
* @param wires The vector of wires the observable acts on
118+
* @return ObsIdType
119+
*/
120+
[[nodiscard]] auto
121+
createHermitianObs(const std::vector<std::complex<PrecisionT>> &matrix,
122+
const std::vector<size_t> &wires) -> ObsIdType {
123+
std::vector<Kokkos::complex<PrecisionT>> matrix_k;
124+
matrix_k.reserve(matrix.size());
125+
for (const auto &elem : matrix) {
126+
matrix_k.push_back(static_cast<Kokkos::complex<PrecisionT>>(elem));
127+
}
128+
129+
this->observables_.push_back(std::make_pair(
130+
std::make_shared<Pennylane::LightningKokkos::Observables::
131+
HermitianObs<StateVectorT>>(
132+
Pennylane::LightningKokkos::Observables::HermitianObs<
133+
StateVectorT>{matrix_k, wires}),
134+
ObsType::Basic));
135+
136+
return static_cast<ObsIdType>(this->observables_.size() - 1);
137+
}
138+
139+
/**
140+
* @brief Create and cache a new TensorProd instance.
141+
*
142+
* @param obsKeys The vector of observable keys
143+
* @return ObsIdType
144+
*/
145+
[[nodiscard]] auto
146+
createTensorProdObs(const std::vector<ObsIdType> &obsKeys) -> ObsIdType {
147+
const auto key_size = obsKeys.size();
148+
const auto obs_size = this->observables_.size();
149+
150+
std::vector<std::shared_ptr<ObservableT>> obs_vec;
151+
obs_vec.reserve(key_size);
152+
153+
for (const auto &key : obsKeys) {
154+
RT_FAIL_IF(static_cast<size_t>(key) >= obs_size || key < 0,
155+
"Invalid observable key");
156+
157+
auto &&[obs, type] = this->observables_[key];
158+
obs_vec.push_back(obs);
159+
}
160+
161+
this->observables_.push_back(std::make_pair(
162+
Pennylane::LightningKokkos::Observables::TensorProdObs<
163+
StateVectorT>::create(obs_vec),
164+
ObsType::TensorProd));
165+
166+
return static_cast<ObsIdType>(obs_size);
167+
}
168+
169+
/**
170+
* @brief Create and cache a new HamiltonianObs instance.
171+
*
172+
* @param coeffs The vector of coefficients
173+
* @param obsKeys The vector of observable keys
174+
* @return ObsIdType
175+
*/
176+
[[nodiscard]] auto
177+
createHamiltonianObs(const std::vector<PrecisionT> &coeffs,
178+
const std::vector<ObsIdType> &obsKeys) -> ObsIdType {
179+
const auto key_size = obsKeys.size();
180+
const auto obs_size = this->observables_.size();
181+
182+
RT_FAIL_IF(
183+
key_size != coeffs.size(),
184+
"Incompatible list of observables and coefficients; "
185+
"Number of observables and number of coefficients must be equal");
186+
187+
std::vector<std::shared_ptr<ObservableT>> obs_vec;
188+
obs_vec.reserve(key_size);
189+
190+
for (auto key : obsKeys) {
191+
RT_FAIL_IF(static_cast<size_t>(key) >= obs_size || key < 0,
192+
"Invalid observable key");
193+
194+
auto &&[obs, type] = this->observables_[key];
195+
obs_vec.push_back(obs);
196+
}
197+
198+
this->observables_.push_back(std::make_pair(
199+
std::make_shared<Pennylane::LightningKokkos::Observables::
200+
Hamiltonian<StateVectorT>>(
201+
Pennylane::LightningKokkos::Observables::Hamiltonian<
202+
StateVectorT>(coeffs, std::move(obs_vec))),
203+
ObsType::Hamiltonian));
204+
205+
return static_cast<ObsIdType>(obs_size);
206+
}
207+
};
208+
} // namespace Catalyst::Runtime::Simulator

0 commit comments

Comments
 (0)