-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add thread-safety to XACC/QCOR (Experimental). We introduce `_XACC_MUTEX` macro in `xacc.hpp` to turn on/off the feature (default is OFF). When the macro is ON, only a single thread can execute the following routines by using either `std::mutex` or `std::recursive_mutex`: - User-facing API routines - `createObjectiveFunction()` (`qcor::__internal__::get_objective()`) - `createOptimizer()` - Compiler-related modules - Syntax Handler - `QuantumKernel::operator()` - `QuantumKernel::~QuantumKernel()` - QJIT - `QJIT::QJIT()` - `QJIT::jit_compile()` - `QJIT::write_cache()` Also, regardless of the macro, the `VQEObjective` class is now `xacc::Cloneable`. Add the following test files to test multi-thread execution - `examples/simple/bell_threaded.cpp` - `examples/simple/simple-objective-function-async.cpp` - `examples/simple/simple-objective-function-threaded.cpp` - `examples/qpu_lambda/deuteron_threaded.cpp` - `examples/qpu_lambda/deuteron_vqe_threaded.cpp` - `examples/qpu_lambda/deuteron_vqe_obj_func_threaded.cpp` - `examples/qpu_lambda/lambda_test_bell_threaded.cpp` Signed-off-by: Akihiro Hayashi <ahayashi@gatech.edu>
- Loading branch information
Showing
14 changed files
with
456 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
#include "qcor.hpp" | ||
// for _XACC_MUTEX | ||
#include "xacc.hpp" | ||
|
||
void foo() { | ||
|
||
auto ansatz_X0X1 = qpu_lambda([](qreg q, double x) { | ||
X(q[0]); | ||
Ry(q[1], x); | ||
CX(q[1], q[0]); | ||
H(q); | ||
Measure(q); | ||
}); | ||
|
||
OptFunction obj( | ||
[&](const std::vector<double> &x, std::vector<double> &) { | ||
auto q = qalloc(2); | ||
ansatz_X0X1(q, x[0]); | ||
auto exp = q.exp_val_z(); | ||
print("<X0X1(",x[0],") = ", exp); | ||
return exp; | ||
}, | ||
1); | ||
|
||
auto optimizer = createOptimizer( | ||
"nlopt", | ||
{{"initial-parameters", std::vector<double>{1.2}}, {"maxeval", 10}}); | ||
auto [opt_val, opt_params] = optimizer->optimize(obj); | ||
print("opt_val = ", opt_val); | ||
} | ||
|
||
int main(int argc, char **argv) { | ||
#ifdef _XACC_MUTEX | ||
std::cout << "_XACC_MUTEX is defined: multi-threding execution" << std::endl; | ||
std::thread t0(foo); | ||
std::thread t1(foo); | ||
t0.join(); | ||
t1.join(); | ||
#else | ||
std::cout << "_XACC_MUTEX is NOT defined: sequential execution" << std::endl; | ||
foo(); | ||
foo(); | ||
#endif | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
#include "qcor.hpp" | ||
// for _XACC_MUTEX | ||
#include "xacc.hpp" | ||
|
||
void foo() { | ||
// Create the Hamiltonian | ||
auto H = -2.1433 * X(0) * X(1) - 2.1433 * Y(0) * Y(1) + .21829 * Z(0) - | ||
6.125 * Z(1) + 5.907; | ||
int iter_count = 0; | ||
auto ansatz = qpu_lambda( | ||
[](qreg q, double x) { | ||
X(q[0]); | ||
Ry(q[1], x); | ||
CX(q[1], q[0]); | ||
print("Iter", iter_count, "; angle = ", x); | ||
iter_count++; | ||
}, | ||
iter_count); | ||
|
||
auto q = qalloc(2); | ||
auto objective = createObjectiveFunction(ansatz, H, q, 1); | ||
// Create a qcor Optimizer | ||
auto optimizer = createOptimizer("nlopt"); | ||
|
||
// Optimize the above function | ||
auto [optval, opt_params] = optimizer->optimize(*objective.get()); | ||
std::cout << "Energy: " << optval << "\n"; | ||
qcor_expect(std::abs(optval + 1.74886) < 0.1); | ||
|
||
auto ansatz_vec_param = qpu_lambda([](qreg q, std::vector<double> x) { | ||
X(q[0]); | ||
Ry(q[1], x[0]); | ||
CX(q[1], q[0]); | ||
}); | ||
|
||
auto q1 = qalloc(2); | ||
auto objective_vec = createObjectiveFunction(ansatz_vec_param, H, q1, 1); | ||
|
||
// Optimize the above function | ||
auto [optval_vec, opt_params_vec] = optimizer->optimize(*objective_vec.get()); | ||
std::cout << "Energy: " << optval_vec << "\n"; | ||
qcor_expect(std::abs(optval_vec + 1.74886) < 0.1); | ||
} | ||
|
||
int main(int argc, char **argv) { | ||
#ifdef _XACC_MUTEX | ||
std::cout << "_XACC_MUTEX is defined: multi-threding execution" << std::endl; | ||
std::thread t0(foo); | ||
std::thread t1(foo); | ||
t0.join(); | ||
t1.join(); | ||
#else | ||
std::cout << "_XACC_MUTEX is NOT defined: sequential execution" << std::endl; | ||
foo(); | ||
foo(); | ||
#endif | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
#include "qcor.hpp" | ||
// for _XACC_MUTEX | ||
#include "xacc.hpp" | ||
|
||
void foo() { | ||
// Create the Hamiltonian | ||
auto H = -2.1433 * X(0) * X(1) - 2.1433 * Y(0) * Y(1) + .21829 * Z(0) - | ||
6.125 * Z(1) + 5.907; | ||
|
||
auto ansatz = qpu_lambda([](qreg q, double x) { | ||
print("x = ", x); | ||
X(q[0]); | ||
Ry(q[1], x); | ||
CX(q[1], q[0]); | ||
}); | ||
|
||
auto ansatz_take_vec = qpu_lambda([](qreg q, std::vector<double> x) { | ||
print("x = ", x[0]); | ||
X(q[0]); | ||
Ry(q[1], x[0]); | ||
CX(q[1], q[0]); | ||
}); | ||
|
||
OptFunction opt_function( | ||
[&](std::vector<double> x) { return ansatz.observe(H, qalloc(2), x[0]); }, | ||
1); | ||
|
||
OptFunction opt_function_vec( | ||
[&](std::vector<double> x) { | ||
return ansatz_take_vec.observe(H, qalloc(2), x); | ||
}, | ||
1); | ||
|
||
auto optimizer = createOptimizer("nlopt"); | ||
auto [ground_energy, opt_params] = optimizer->optimize(opt_function); | ||
print("Energy: ", ground_energy); | ||
qcor_expect(std::abs(ground_energy + 1.74886) < 0.1); | ||
|
||
auto [ground_energy_vec, opt_params_vec] = | ||
optimizer->optimize(opt_function_vec); | ||
print("Energy: ", ground_energy_vec); | ||
qcor_expect(std::abs(ground_energy_vec + 1.74886) < 0.1); | ||
} | ||
|
||
int main(int argc, char **argv) { | ||
#ifdef _XACC_MUTEX | ||
std::cout << "_XACC_MUTEX is defined: multi-threding execution" << std::endl; | ||
std::thread t0(foo); | ||
std::thread t1(foo); | ||
t0.join(); | ||
t1.join(); | ||
#else | ||
std::cout << "_XACC_MUTEX is NOT defined: sequential execution" << std::endl; | ||
foo(); | ||
foo(); | ||
#endif | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
#include "qcor.hpp" | ||
// for _XACC_MUTEX | ||
#include "xacc.hpp" | ||
void foo() { | ||
auto x_lambda = qpu_lambda([](qubit q) { | ||
X(q); }); | ||
|
||
auto bell = qpu_lambda([](qreg q) { | ||
H(q[0]); | ||
// Call the captured lambda | ||
x_lambda.ctrl(q[0], q[1]); | ||
Measure(q); | ||
}, x_lambda); | ||
|
||
auto q = qalloc(2); | ||
bell(q); | ||
q.print(); | ||
qcor_expect(q.counts().size() == 2); | ||
qcor_expect(q.counts()["00"] > 400); | ||
qcor_expect(q.counts()["11"] > 400); | ||
// Entangled... | ||
qcor_expect(q.counts()["00"] + q.counts()["11"] == 1024); | ||
} | ||
|
||
int main(int argc, char **argv) { | ||
set_shots(1024); | ||
#ifdef _XACC_MUTEX | ||
std::cout << "_XACC_MUTEX is defined: multi-threding execution" << std::endl; | ||
std::thread t0(foo); | ||
std::thread t1(foo); | ||
t0.join(); | ||
t1.join(); | ||
#else | ||
std::cout << "_XACC_MUTEX is NOT defined: sequential execution" << std::endl; | ||
foo(); | ||
foo(); | ||
#endif | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// for _XACC_MUTEX | ||
#include "xacc.hpp" | ||
|
||
// Define the bell kernel | ||
__qpu__ void bell(qreg q) { | ||
using qcor::xasm; | ||
H(q[0]); | ||
CX(q[0], q[1]); | ||
|
||
for (int i = 0; i < q.size(); i++) { | ||
Measure(q[i]); | ||
} | ||
} | ||
|
||
void foo() { | ||
// Create two qubit registers, each size 2 | ||
auto q = qalloc(2); | ||
|
||
// Run the quantum kernel | ||
bell(q); | ||
|
||
// dump the results | ||
q.print(); | ||
} | ||
|
||
int main(int argc, char **argv) { | ||
#ifdef _XACC_MUTEX | ||
std::cout << "_XACC_MUTEX is defined: multi-threding execution" << std::endl; | ||
std::thread t0(foo); | ||
std::thread t1(foo); | ||
t0.join(); | ||
t1.join(); | ||
#else | ||
std::cout << "_XACC_MUTEX is NOT defined: sequential execution" << std::endl; | ||
foo(); | ||
foo(); | ||
#endif | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
// Note no includes here, we are just | ||
// using the language extension | ||
// | ||
// run this with | ||
// qcor -qpu qpp simple-objective-function-async.cpp | ||
// ./a.out | ||
|
||
// for _XACC_MUTEX | ||
#include "xacc.hpp" | ||
|
||
__qpu__ void ansatz(qreg q, double theta) { | ||
X(q[0]); | ||
Ry(q[1], theta); | ||
CX(q[1], q[0]); | ||
} | ||
|
||
int main(int argc, char **argv) { | ||
#ifdef _XACC_MUTEX | ||
std::cout << "_XACC_MUTEX is defined: execute taskInitiate asynchronously" << std::endl; | ||
#else | ||
std::cout << "_XACC_MUTEX is NOT defined: execute taskInitiate sequentially" << std::endl; | ||
#endif | ||
// Allocate 2 qubits | ||
auto q1 = qalloc(2); | ||
auto q2 = qalloc(2); | ||
|
||
// Programmer needs to set | ||
// the number of variational params | ||
auto n_variational_params1 = 1; | ||
auto n_variational_params2 = 1; | ||
|
||
// Create the Deuteron Hamiltonian | ||
auto H1 = 5.907 - 2.1433 * X(0) * X(1) - 2.1433 * Y(0) * Y(1) + .21829 * Z(0) - | ||
6.125 * Z(1); | ||
auto H2 = 5.907 - 2.1433 * X(0) * X(1) - 2.1433 * Y(0) * Y(1) + .21829 * Z(0) - | ||
6.125 * Z(1); | ||
|
||
// Create the ObjectiveFunction, here we want to run VQE | ||
// need to provide ansatz, Operator, and qreg | ||
auto objective1 = createObjectiveFunction( | ||
ansatz, H1, q1, n_variational_params1, | ||
{{"gradient-strategy", "parameter-shift"}}); | ||
auto objective2 = createObjectiveFunction( | ||
ansatz, H2, q2, n_variational_params2, | ||
{{"gradient-strategy", "parameter-shift"}}); | ||
|
||
// Create the Optimizer. | ||
auto optimizer1 = createOptimizer("nlopt", {{"nlopt-optimizer", "l-bfgs"}}); | ||
auto optimizer2 = createOptimizer("nlopt", {{"nlopt-optimizer", "l-bfgs"}}); | ||
|
||
#ifdef _XACC_MUTEX | ||
// Launch the Optimization Task with taskInitiate | ||
auto handle1 = taskInitiate(objective1, optimizer1); | ||
// Go do other work... | ||
auto handle2 = taskInitiate(objective2, optimizer2); | ||
|
||
// Query results when ready. | ||
auto results1 = sync(handle1); | ||
auto results2 = sync(handle2); | ||
#else | ||
// Launch the Optimization Task with taskInitiate | ||
auto handle1 = taskInitiate(objective1, optimizer1); | ||
// Query results when ready. | ||
auto results1 = sync(handle1); | ||
|
||
// Launch the Optimization Task with taskInitiate | ||
auto handle2 = taskInitiate(objective2, optimizer2); | ||
// Query results when ready. | ||
auto results2 = sync(handle2); | ||
#endif | ||
printf("vqe-energy from taskInitiate1 = %f\n", results1.opt_val); | ||
printf("vqe-energy from taskInitiate2 = %f\n", results2.opt_val); | ||
|
||
printf("From objetive1\n"); | ||
for (auto &x : linspace(-constants::pi, constants::pi, 20)) { | ||
std::cout << x << ", " << (*objective1)({x}) << "\n"; | ||
} | ||
printf("From objetive2\n"); | ||
for (auto &x : linspace(-constants::pi, constants::pi, 20)) { | ||
std::cout << x << ", " << (*objective2)({x}) << "\n"; | ||
} | ||
} |
Oops, something went wrong.