Skip to content

Commit b05087c

Browse files
Support Rydberg Hamiltonian in C++ Evolve API (#2704)
* QuEra / Pasqal support in C++ Signed-off-by: Dobri Y <[email protected]> * code review Signed-off-by: Dobri Y <[email protected]> * use EXPECT_ANY_THROW Signed-off-by: Dobri Y <[email protected]> * Update .github/workflows/integration_tests.yml Co-authored-by: Pradnya Khalate <[email protected]> Signed-off-by: nvidia-dobri <[email protected]> * Fix pasqal c++ test Signed-off-by: Dobri Y <[email protected]> * yeet pasqal C++ integration test Signed-off-by: Dobri Y <[email protected]> --------- Signed-off-by: Dobri Y <[email protected]> Signed-off-by: nvidia-dobri <[email protected]> Co-authored-by: Pradnya Khalate <[email protected]>
1 parent bc196e3 commit b05087c

File tree

14 files changed

+652
-61
lines changed

14 files changed

+652
-61
lines changed

docs/sphinx/targets/cpp/pasqal.cpp

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Compile and run with:
2+
// ```
3+
// nvq++ --target pasqal pasqal.cpp -o out.x
4+
// ./out.x
5+
// ```
6+
// Assumes a valid set of credentials (PASQAL_AUTH_TOKEN, PASQAL_PROJECT_ID)
7+
// have been set. To set PASQAL_AUTH_TOKEN from Pasqal Cloud username and
8+
// password, use pasqal_auth.py in this folder.
9+
10+
#include "cudaq/algorithms/evolve.h"
11+
#include "cudaq/dynamics_integrators.h"
12+
#include "cudaq/operators.h"
13+
#include "cudaq/schedule.h"
14+
#include <cmath>
15+
#include <map>
16+
#include <vector>
17+
18+
// This example illustrates how to use Pasqal's EMU_MPS emulator over Pasqal's
19+
// cloud via CUDA-Q. Contact Pasqal at [email protected] or through
20+
// https://community.pasqal.com for assistance.
21+
22+
int main() {
23+
// Topology initialization
24+
const double a = 5e-6;
25+
std::vector<std::pair<double, double>> register_sites;
26+
register_sites.push_back(std::make_pair(a, 0.0));
27+
register_sites.push_back(std::make_pair(2 * a, 0.0));
28+
register_sites.push_back(std::make_pair(3 * a, 0.0));
29+
30+
// Simulation Timing
31+
const double time_ramp = 0.000001; // seconds
32+
const double time_max = 0.000003; // seconds
33+
const double omega_max = 1000000; // rad/sec
34+
const double delta_end = 1000000;
35+
const double delta_start = 0.0;
36+
37+
std::vector<std::complex<double>> steps = {0.0, time_ramp,
38+
time_max - time_ramp, time_max};
39+
cudaq::schedule schedule(steps, {"t"}, {});
40+
41+
// Basic Rydberg Hamiltonian
42+
auto omega = cudaq::scalar_operator(
43+
[time_ramp, time_max,
44+
omega_max](const std::unordered_map<std::string, std::complex<double>>
45+
&parameters) {
46+
double t = std::real(parameters.at("t"));
47+
return std::complex<double>(
48+
(t > time_ramp && t < time_max) ? omega_max : 0.0, 0.0);
49+
});
50+
51+
auto phi = cudaq::scalar_operator(0.0);
52+
53+
auto delta = cudaq::scalar_operator(
54+
[time_ramp, time_max, delta_start,
55+
delta_end](const std::unordered_map<std::string, std::complex<double>>
56+
&parameters) {
57+
double t = std::real(parameters.at("t"));
58+
return std::complex<double>(
59+
(t > time_ramp && t < time_max) ? delta_end : delta_start, 0.0);
60+
});
61+
62+
auto hamiltonian =
63+
cudaq::rydberg_hamiltonian(register_sites, omega, phi, delta);
64+
65+
// Evolve the system
66+
auto result = cudaq::evolve(hamiltonian, schedule, 100);
67+
result.get_sampling_result()->dump();
68+
69+
return 0;
70+
}
+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Compile and run with:
2+
// ```
3+
// nvq++ --target quera quera_basic.cpp -o out.x
4+
// ./out.x
5+
// ```
6+
// Assumes a valid set of credentials have been stored.
7+
8+
#include "cudaq/algorithms/evolve.h"
9+
#include "cudaq/dynamics_integrators.h"
10+
#include "cudaq/schedule.h"
11+
#include <cmath>
12+
#include <map>
13+
#include <vector>
14+
15+
// NOTE: QuEra Aquila system is available via Amazon Braket.
16+
// Credentials must be set before running this program.
17+
// Amazon Braket costs apply.
18+
19+
// This example illustrates how to use QuEra's Aquila device on Braket with
20+
// CUDA-Q. It is a CUDA-Q implementation of the getting started materials for
21+
// Braket available here:
22+
// https://docs.aws.amazon.com/braket/latest/developerguide/braket-get-started-hello-ahs.html
23+
24+
int main() {
25+
// Topology initialization
26+
const double a = 5.7e-6;
27+
std::vector<std::pair<double, double>> register_sites;
28+
29+
auto make_coord = [a](double x, double y) {
30+
return std::make_pair(x * a, y * a);
31+
};
32+
33+
register_sites.push_back(make_coord(0.5, 0.5 + 1.0 / std::sqrt(2)));
34+
register_sites.push_back(make_coord(0.5 + 1.0 / std::sqrt(2), 0.5));
35+
register_sites.push_back(make_coord(0.5 + 1.0 / std::sqrt(2), -0.5));
36+
register_sites.push_back(make_coord(0.5, -0.5 - 1.0 / std::sqrt(2)));
37+
register_sites.push_back(make_coord(-0.5, -0.5 - 1.0 / std::sqrt(2)));
38+
register_sites.push_back(make_coord(-0.5 - 1.0 / std::sqrt(2), -0.5));
39+
register_sites.push_back(make_coord(-0.5 - 1.0 / std::sqrt(2), 0.5));
40+
register_sites.push_back(make_coord(-0.5, 0.5 + 1.0 / std::sqrt(2)));
41+
42+
// Simulation Timing
43+
const double time_max = 4e-6; // seconds
44+
const double time_ramp = 1e-7; // seconds
45+
const double omega_max = 6.3e6; // rad/sec
46+
const double delta_start = -5 * omega_max;
47+
const double delta_end = 5 * omega_max;
48+
49+
std::vector<std::complex<double>> steps = {0.0, time_ramp,
50+
time_max - time_ramp, time_max};
51+
cudaq::schedule schedule(steps, {"t"}, {});
52+
53+
// Basic Rydberg Hamiltonian
54+
auto omega = cudaq::scalar_operator(
55+
[time_ramp, time_max,
56+
omega_max](const std::unordered_map<std::string, std::complex<double>>
57+
&parameters) {
58+
double t = std::real(parameters.at("t"));
59+
return std::complex<double>(
60+
(t > time_ramp && t < time_max) ? omega_max : 0.0, 0.0);
61+
});
62+
63+
auto phi = cudaq::scalar_operator(0.0);
64+
65+
auto delta = cudaq::scalar_operator(
66+
[time_ramp, time_max, delta_start,
67+
delta_end](const std::unordered_map<std::string, std::complex<double>>
68+
&parameters) {
69+
double t = std::real(parameters.at("t"));
70+
return std::complex<double>(
71+
(t > time_ramp && t < time_max) ? delta_end : delta_start, 0.0);
72+
});
73+
74+
auto hamiltonian =
75+
cudaq::rydberg_hamiltonian(register_sites, omega, phi, delta);
76+
77+
// Evolve the system
78+
auto result = cudaq::evolve_async(hamiltonian, schedule, 10).get();
79+
result.get_sampling_result()->dump();
80+
81+
return 0;
82+
}
+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Compile and run with:
2+
// ```
3+
// nvq++ --target quera quera_intro.cpp -o out.x
4+
// ./out.x
5+
// ```
6+
// Assumes a valid set of credentials have been stored.
7+
8+
#include "cudaq/algorithms/evolve.h"
9+
#include "cudaq/dynamics_integrators.h"
10+
#include "cudaq/operators.h"
11+
#include "cudaq/schedule.h"
12+
#include <cmath>
13+
#include <map>
14+
#include <vector>
15+
16+
// This example illustrates how to use QuEra's Aquila device on Braket with
17+
// CUDA-Q. It is a CUDA-Q implementation of the getting started materials for
18+
// Braket available here:
19+
// https://github.com/amazon-braket/amazon-braket-examples/blob/main/examples/analog_hamiltonian_simulation/01_Introduction_to_Aquila.ipynb
20+
21+
int main() {
22+
// Topology initialization
23+
const double separation = 5e-6;
24+
const double block_separation = 15e-6;
25+
const int k_max = 5;
26+
const int m_max = 5;
27+
28+
std::vector<std::pair<double, double>> register_sites;
29+
30+
for (int k = 0; k < k_max; ++k) {
31+
for (int m = 0; m < m_max; ++m) {
32+
register_sites.push_back(
33+
std::make_pair(block_separation * m,
34+
block_separation * k + separation / std::sqrt(3)));
35+
36+
register_sites.push_back(std::make_pair(
37+
block_separation * m - separation / 2,
38+
block_separation * k - separation / (2 * std::sqrt(3))));
39+
40+
register_sites.push_back(std::make_pair(
41+
block_separation * m + separation / 2,
42+
block_separation * k - separation / (2 * std::sqrt(3))));
43+
}
44+
}
45+
46+
// Simulation Timing
47+
const double omega_const = 1.5e7; // rad/sec
48+
const double time_ramp = 5e-8; // seconds
49+
const double time_plateau = 7.091995761561453e-08; // seconds
50+
const double time_max = 2 * time_ramp + time_plateau; // seconds
51+
52+
std::vector<std::complex<double>> steps = {
53+
0.0, time_ramp, time_ramp + time_plateau, time_max};
54+
cudaq::schedule schedule(steps, {"t"}, {});
55+
56+
// Rydberg Hamiltonian with trapezoidal omega
57+
auto omega = cudaq::scalar_operator(
58+
[time_ramp, time_plateau, time_max,
59+
omega_const](const std::unordered_map<std::string, std::complex<double>>
60+
&parameters) {
61+
double t = std::real(parameters.at("t"));
62+
double slope = omega_const / time_ramp;
63+
double y_intercept = slope * time_max;
64+
65+
if (t > 0 && t < time_ramp + time_plateau) {
66+
return std::complex<double>(slope * t, 0.0);
67+
} else if (t > time_ramp && t < time_max) {
68+
return std::complex<double>(omega_const, 0.0);
69+
} else if (t > time_ramp + time_plateau && t < time_max) {
70+
return std::complex<double>((-slope * t) + y_intercept, 0.0);
71+
}
72+
return std::complex<double>(0.0, 0.0);
73+
});
74+
auto phi = cudaq::scalar_operator(0.0);
75+
auto delta = cudaq::scalar_operator(0.0);
76+
77+
auto hamiltonian =
78+
cudaq::rydberg_hamiltonian(register_sites, omega, phi, delta);
79+
80+
// Evolve the system
81+
auto result = cudaq::evolve(hamiltonian, schedule);
82+
result.get_sampling_result()->dump();
83+
84+
return 0;
85+
}

python/cudaq/operator/dynamics.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ gpu-requirements: true
1212
config:
1313
nvqir-simulation-backend: dynamics
1414
platform-library: mqpu
15-
preprocessor-defines: ["-D CUDAQ_DYNAMICS_TARGET"]
16-
library-mode: true
15+
preprocessor-defines: ["-D CUDAQ_ANALOG_TARGET"]
16+
library-mode: true

runtime/cudaq/algorithms/evolve.h

+32-11
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ evolve(const HamTy &hamiltonian, const cudaq::dimension_map &dimensions,
8383
std::initializer_list<ObserveOpTy> observables = {},
8484
bool store_intermediate_results = false,
8585
std::optional<int> shots_count = std::nullopt) {
86-
#if defined(CUDAQ_DYNAMICS_TARGET)
86+
#if defined(CUDAQ_ANALOG_TARGET)
8787
return cudaq::__internal__::evolveSingle(
8888
cudaq::__internal__::convertOp(hamiltonian), dimensions, schedule,
8989
initial_state, integrator,
@@ -113,7 +113,7 @@ evolve_result evolve(const HamTy &hamiltonian,
113113
const std::vector<ObserveOpTy> &observables = {},
114114
bool store_intermediate_results = false,
115115
std::optional<int> shots_count = std::nullopt) {
116-
#if defined(CUDAQ_DYNAMICS_TARGET)
116+
#if defined(CUDAQ_ANALOG_TARGET)
117117
return cudaq::__internal__::evolveSingle(
118118
cudaq::__internal__::convertOp(hamiltonian), dimensions, schedule,
119119
initial_state, integrator,
@@ -147,7 +147,7 @@ evolve(const HamTy &hamiltonian, const cudaq::dimension_map &dimensions,
147147
std::initializer_list<ObserveOpTy> observables = {},
148148
bool store_intermediate_results = false,
149149
std::optional<int> shots_count = std::nullopt) {
150-
#if defined(CUDAQ_DYNAMICS_TARGET)
150+
#if defined(CUDAQ_ANALOG_TARGET)
151151
std::vector<evolve_result> results;
152152
for (const auto &initial_state : initial_states)
153153
results.emplace_back(evolve(hamiltonian, dimensions, schedule,
@@ -183,13 +183,13 @@ evolve(const HamTy &hamiltonian, const cudaq::dimension_map &dimensions,
183183
const std::vector<ObserveOpTy> &observables = {},
184184
bool store_intermediate_results = false,
185185
std::optional<int> shots_count = std::nullopt) {
186-
#if defined(CUDAQ_DYNAMICS_TARGET)
186+
#if defined(CUDAQ_ANALOG_TARGET)
187187
std::vector<evolve_result> results;
188188
for (const auto &initial_state : initial_states)
189-
results.emplace_back(evolve(hamiltonian, dimensions, schedule,
190-
initial_state, integrator, collapse_operators,
191-
observables, store_intermediate_results,
192-
shots_count));
189+
results.emplace_back(evolve(
190+
hamiltonian, dimensions, schedule, initial_states, integrator,
191+
collapse_operators, initial_state, integrator, collapse_operators,
192+
observables, store_intermediate_results, shots_count));
193193
return results;
194194
#else
195195
static_assert(
@@ -219,7 +219,7 @@ evolve_async(const HamTy &hamiltonian, const cudaq::dimension_map &dimensions,
219219
std::initializer_list<ObserveOpTy> observables = {},
220220
bool store_intermediate_results = false,
221221
std::optional<int> shots_count = std::nullopt, int qpu_id = 0) {
222-
#if defined(CUDAQ_DYNAMICS_TARGET)
222+
#if defined(CUDAQ_ANALOG_TARGET)
223223
// Clone the integrator to extend its lifetime.
224224
auto cloneIntegrator = integrator.clone();
225225
auto collapseOperators = cudaq::__internal__::convertOps(collapse_operators);
@@ -259,7 +259,7 @@ evolve_async(const HamTy &hamiltonian, const cudaq::dimension_map &dimensions,
259259
const std::vector<ObserveOpTy> &observables = {},
260260
bool store_intermediate_results = false,
261261
std::optional<int> shots_count = std::nullopt, int qpu_id = 0) {
262-
#if defined(CUDAQ_DYNAMICS_TARGET)
262+
#if defined(CUDAQ_ANALOG_TARGET)
263263
// Clone the integrator to extend its lifetime.
264264
auto cloneIntegrator = integrator.clone();
265265
return __internal__::evolve_async(
@@ -277,4 +277,25 @@ evolve_async(const HamTy &hamiltonian, const cudaq::dimension_map &dimensions,
277277
"recompile your application with '--target dynamics' flag.");
278278
#endif
279279
}
280-
} // namespace cudaq
280+
281+
// Rydberg Hamiltonian
282+
evolve_result evolve(const cudaq::rydberg_hamiltonian &hamiltonian,
283+
const cudaq::schedule &schedule,
284+
std::optional<int> shots_count = std::nullopt) {
285+
return cudaq::__internal__::evolveSingle(hamiltonian, schedule, shots_count);
286+
}
287+
288+
async_evolve_result evolve_async(const cudaq::rydberg_hamiltonian &hamiltonian,
289+
const cudaq::schedule &schedule,
290+
std::optional<int> shots_count = std::nullopt,
291+
int qpu_id = 0) {
292+
return cudaq::__internal__::evolve_async(
293+
[=]() {
294+
ExecutionContext context("evolve");
295+
cudaq::get_platform().set_exec_ctx(&context, qpu_id);
296+
return evolve(hamiltonian, schedule, shots_count);
297+
},
298+
qpu_id);
299+
}
300+
301+
} // namespace cudaq

runtime/cudaq/algorithms/evolve_internal.h

+5
Original file line numberDiff line numberDiff line change
@@ -164,5 +164,10 @@ evolve_result evolveSingle(
164164
const std::vector<operator_sum<cudaq::matrix_operator>> &observables = {},
165165
bool store_intermediate_results = false,
166166
std::optional<int> shots_count = std::nullopt);
167+
168+
evolve_result evolveSingle(const cudaq::rydberg_hamiltonian &hamiltonian,
169+
const cudaq::schedule &schedule,
170+
std::optional<int> shots_count = std::nullopt);
171+
167172
} // namespace __internal__
168173
} // namespace cudaq

runtime/cudaq/dynamics/CMakeLists.txt

+8-6
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ set(INTERFACE_POSITION_INDEPENDENT_CODE ON)
1212

1313
set(CUDAQ_OPS_SRC
1414
callback.cpp
15-
scalar_operators.cpp
15+
scalar_operators.cpp
1616
spin_operators.cpp
1717
boson_operators.cpp
1818
fermion_operators.cpp
19-
matrix_operators.cpp
20-
product_operators.cpp
21-
operator_sum.cpp
19+
matrix_operators.cpp
20+
product_operators.cpp
21+
operator_sum.cpp
22+
rydberg_hamiltonian.cpp
23+
evolution.cpp
2224
handler.cpp
2325
schedule.cpp
2426
helpers.cpp
@@ -28,8 +30,8 @@ add_library(${LIBRARY_NAME} SHARED ${CUDAQ_OPS_SRC})
2830
set_property(GLOBAL APPEND PROPERTY CUDAQ_RUNTIME_LIBS ${LIBRARY_NAME})
2931
target_compile_definitions(${LIBRARY_NAME} PRIVATE -DCUDAQ_INSTANTIATE_TEMPLATES)
3032

31-
target_include_directories(${LIBRARY_NAME}
32-
PUBLIC
33+
target_include_directories(${LIBRARY_NAME}
34+
PUBLIC
3335
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/runtime>
3436
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/tpls/eigen>
3537
$<INSTALL_INTERFACE:include>

0 commit comments

Comments
 (0)