Skip to content

Commit 0fc077e

Browse files
multiphaseCFDringo-but-quantummaliasadi
authored
Integrate LGPU to Catalyst (#928)
### Before submitting Please complete the following checklist when submitting a PR: - [ ] All new features must include a unit test. If you've fixed a bug or added code that should be tested, add a test to the [`tests`](../tests) directory! - [ ] All new functions and code must be clearly commented and documented. If you do make documentation changes, make sure that the docs build and render correctly by running `make docs`. - [ ] Ensure that the test suite passes, by running `make test`. - [x] Add a new entry to the `.github/CHANGELOG.md` file, summarizing the change, and including a link back to the PR. - [x] Ensure that code is properly formatted by running `make format`. When all the above are checked, delete everything above the dashed line and fill in the pull request template. ------------------------------------------------------------------------------------------------------------ **Context:** [SC-73065] **Description of the Change:** **Benefits:** **Possible Drawbacks:** **Related GitHub Issues:** --------- Co-authored-by: ringo-but-quantum <[email protected]> Co-authored-by: Ali Asadi <[email protected]>
1 parent a38b036 commit 0fc077e

18 files changed

+4062
-112
lines changed

.github/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
### New features since last release
44

5+
* Integrate Lightning-GPU with Catalyst.
6+
[(#928)](https://github.com/PennyLaneAI/pennylane-lightning/pull/928)
7+
58
* Add `mid-circuit measurements` support to `lightning.gpu`'s single-GPU backend.
69
[(#931)](https://github.com/PennyLaneAI/pennylane-lightning/pull/931)
710

.github/workflows/tests_gpu_python.yml

+13
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,19 @@ jobs:
206206
run: |
207207
DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"`
208208
PL_DEVICE=${DEVICENAME} python -m pytest tests/ $COVERAGE_FLAGS
209+
210+
- name: Test wheels for Lightning-GPU
211+
if : matrix.pl_backend == 'lightning_gpu'
212+
run: |
213+
python -m pip install -r requirements-dev.txt
214+
PL_BACKEND=lightning_qubit python scripts/configure_pyproject_toml.py
215+
SKIP_COMPILATION=True python -m pip install . -vv
216+
217+
DEVICENAME=`echo ${{ matrix.pl_backend }} | sed "s/_/./g"`
218+
PL_BACKEND=${DEVICENAME} python scripts/configure_pyproject_toml.py
219+
python -m build
220+
python -m pip install dist/*.whl --force-reinstall --no-deps
221+
PL_DEVICE=${DEVICENAME} python -m pytest tests/test_device.py $COVERAGE_FLAGS
209222
210223
- name: Move coverage file
211224
run: |

cmake/support_catalyst.cmake

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
###############################################################################################
2+
# This file provides macros to process Catalyst.
3+
###############################################################################################
4+
5+
# Include this only once
6+
include_guard()
7+
8+
macro(FindCatalyst target_name)
9+
if(LIGHTNING_CATALYST_SRC_PATH)
10+
if(NOT IS_ABSOLUTE ${LIGHTNING_CATALYST_SRC_PATH})
11+
message(FATAL_ERROR " LIGHTNING_CATALYST_SRC_PATH=${LIGHTNING_CATALYST_SRC_PATH} must be set to an absolute path")
12+
endif()
13+
if(CATALYST_GIT_TAG)
14+
message(WARN " Setting `LIGHTNING_CATALYST_SRC_PATH=${LIGHTNING_CATALYST_SRC_PATH}` overrides `CATALYST_GIT_TAG=${CATALYST_GIT_TAG}`")
15+
endif()
16+
17+
# Acquire local git hash and use for CATALYST_GIT_TAG
18+
execute_process(COMMAND git rev-parse --short HEAD
19+
WORKING_DIRECTORY ${LIGHTNING_CATALYST_SRC_PATH}
20+
OUTPUT_VARIABLE CATALYST_GIT_TAG
21+
)
22+
message(INFO " Building against local Catalyst - path: ${LIGHTNING_CATALYST_SRC_PATH} - GIT TAG: ${CATALYST_GIT_TAG}")
23+
24+
target_include_directories(${target_name} PUBLIC ${LIGHTNING_CATALYST_SRC_PATH}/runtime/lib/backend/common)
25+
target_include_directories(${target_name} PUBLIC ${LIGHTNING_CATALYST_SRC_PATH}/runtime/include)
26+
27+
else()
28+
if(NOT CATALYST_GIT_TAG)
29+
set(CATALYST_GIT_TAG "main" CACHE STRING "GIT_TAG value to build Catalyst")
30+
endif()
31+
message(INFO " Building against Catalyst GIT TAG ${CATALYST_GIT_TAG}")
32+
33+
# Fetching /lib/backend/common hpp headers
34+
set(LIB_BACKEND_COMMON_HEADERS CacheManager.hpp
35+
QubitManager.hpp
36+
Utils.hpp
37+
)
38+
39+
foreach(HEADER ${LIB_BACKEND_COMMON_HEADERS})
40+
string(REGEX REPLACE "\\.[^.]*$" "" HEADER_NAME ${HEADER})
41+
FetchContent_Declare(
42+
${HEADER_NAME}
43+
URL https://raw.githubusercontent.com/PennyLaneAI/catalyst/${CATALYST_GIT_TAG}/runtime/lib/backend/common/${HEADER}
44+
DOWNLOAD_NO_EXTRACT True
45+
SOURCE_DIR include
46+
)
47+
48+
FetchContent_MakeAvailable(${HEADER_NAME})
49+
endforeach()
50+
51+
# Fetching include hpp headers
52+
set(INCLUDE_HEADERS DataView.hpp
53+
Exception.hpp
54+
QuantumDevice.hpp
55+
RuntimeCAPI.h
56+
Types.h
57+
)
58+
59+
foreach(HEADER ${INCLUDE_HEADERS})
60+
string(REGEX REPLACE "\\.[^.]*$" "" HEADER_NAME ${HEADER})
61+
FetchContent_Declare(
62+
${HEADER_NAME}
63+
URL https://raw.githubusercontent.com/PennyLaneAI/catalyst/${CATALYST_GIT_TAG}/runtime/include/${HEADER}
64+
DOWNLOAD_NO_EXTRACT True
65+
SOURCE_DIR include
66+
)
67+
68+
FetchContent_MakeAvailable(${HEADER_NAME})
69+
endforeach()
70+
71+
target_include_directories(${target_name} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include)
72+
73+
endif()
74+
endmacro()

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

+1
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ endif()
7474
###############################################################################
7575
set(COMPONENT_SUBDIRS algorithms
7676
bindings
77+
catalyst
7778
measurements
7879
gates
7980
observables
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
cmake_minimum_required(VERSION 3.20)
2+
3+
project(lightning_gpu_catalyst LANGUAGES CXX)
4+
5+
set(LGPU_CATALYST_FILES LightningGPUSimulator.cpp CACHE INTERNAL "")
6+
add_library(lightning_gpu_catalyst SHARED ${LGPU_CATALYST_FILES})
7+
8+
include(FetchContent)
9+
10+
include("${pennylane_lightning_SOURCE_DIR}/cmake/support_catalyst.cmake")
11+
FindCatalyst(lightning_gpu_catalyst)
12+
13+
target_include_directories(lightning_gpu_catalyst INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
14+
target_link_libraries(lightning_gpu_catalyst PUBLIC lightning_compile_options
15+
lightning_gpu_algorithms
16+
lightning_gpu_measurements
17+
)
18+
19+
if (BUILD_TESTS)
20+
enable_testing()
21+
add_subdirectory("tests")
22+
endif()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
// Copyright 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 "ObservablesGPU.hpp"
26+
27+
namespace Catalyst::Runtime::Simulator {
28+
29+
/**
30+
* @brief The LightningGPUObsManager 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 LightningGPUObsManager final {
35+
private:
36+
using StateVectorT =
37+
Pennylane::LightningGPU::StateVectorCudaManaged<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+
LightningGPUObsManager() = default;
44+
~LightningGPUObsManager() = default;
45+
46+
LightningGPUObsManager(const LightningGPUObsManager &) = delete;
47+
LightningGPUObsManager &operator=(const LightningGPUObsManager &) = delete;
48+
LightningGPUObsManager(LightningGPUObsManager &&) = delete;
49+
LightningGPUObsManager &operator=(LightningGPUObsManager &&) = delete;
50+
51+
/**
52+
* @brief A helper function to clear constructed observables in the program.
53+
*/
54+
void clear() { this->observables_.clear(); }
55+
56+
/**
57+
* @brief Check the validity of observable keys.
58+
*
59+
* @param obsKeys The vector of observable keys
60+
* @return bool
61+
*/
62+
[[nodiscard]] auto
63+
isValidObservables(const std::vector<ObsIdType> &obsKeys) const -> bool {
64+
return std::all_of(obsKeys.begin(), obsKeys.end(), [this](auto i) {
65+
return (i >= 0 &&
66+
static_cast<std::size_t>(i) < this->observables_.size());
67+
});
68+
}
69+
70+
/**
71+
* @brief Get the constructed observable instance.
72+
*
73+
* @param key The observable key
74+
* @return std::shared_ptr<ObservableT>
75+
*/
76+
[[nodiscard]] auto getObservable(ObsIdType key)
77+
-> std::shared_ptr<ObservableT> {
78+
RT_FAIL_IF(!this->isValidObservables({key}), "Invalid observable key");
79+
return std::get<0>(this->observables_[key]);
80+
}
81+
82+
/**
83+
* @brief Get the number of observables.
84+
*
85+
* @return std::size_t
86+
*/
87+
[[nodiscard]] auto numObservables() const -> std::size_t {
88+
return this->observables_.size();
89+
}
90+
91+
/**
92+
* @brief Create and cache a new NamedObs instance.
93+
*
94+
* @param obsId The named observable id of type ObsId
95+
* @param wires The vector of wires the observable acts on
96+
* @return ObsIdType
97+
*/
98+
[[nodiscard]] auto createNamedObs(ObsId obsId,
99+
const std::vector<std::size_t> &wires)
100+
-> ObsIdType {
101+
auto &&obs_str = std::string(
102+
Lightning::lookup_obs<Lightning::simulator_observable_support_size>(
103+
Lightning::simulator_observable_support, obsId));
104+
105+
this->observables_.push_back(std::make_pair(
106+
std::make_shared<
107+
Pennylane::LightningGPU::Observables::NamedObs<StateVectorT>>(
108+
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<std::size_t> &wires) -> ObsIdType {
123+
std::vector<std::complex<PrecisionT>> matrix_k;
124+
matrix_k.reserve(matrix.size());
125+
for (const auto &elem : matrix) {
126+
matrix_k.push_back(static_cast<std::complex<PrecisionT>>(elem));
127+
}
128+
129+
this->observables_.push_back(std::make_pair(
130+
std::make_shared<Pennylane::LightningGPU::Observables::HermitianObs<
131+
StateVectorT>>(
132+
Pennylane::LightningGPU::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<std::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(
162+
std::make_pair(Pennylane::LightningGPU::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<std::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::LightningGPU::Observables::Hamiltonian<
200+
StateVectorT>>(
201+
Pennylane::LightningGPU::Observables::Hamiltonian<StateVectorT>(
202+
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)