Skip to content

Commit a69a877

Browse files
authored
Add Braket backend (#2337)
* Add Braket backend Signed-off-by: Ryan <[email protected]> * Adjust build scripts, add license notice, move result processing to BraketServerHelper Signed-off-by: Ryan <[email protected]> * Fix more CI issues Signed-off-by: Ryan <[email protected]> * Revert unintentional changes Signed-off-by: Ryan <[email protected]> * Build fixes Co-authored-by: Thien Nguyen <[email protected]> Signed-off-by: Pradnya Khalate <[email protected]> * Fix for clang compiler warning / error: 'cudaq::OrcaExecutor::execute' hides overloaded virtual function Signed-off-by: Pradnya Khalate <[email protected]> * Restore permissions for the script to build CUDA-Q Signed-off-by: Pradnya Khalate <[email protected]> * Apply suggestions from code review Co-authored-by: Bettina Heim <[email protected]> Signed-off-by: Pradnya Khalate <[email protected]> * Change mock port to unused value ('62445') Signed-off-by: Pradnya Khalate <[email protected]> * Remove braket target from sudoku tests Signed-off-by: Ryan Shaffer <[email protected]> * Add braket to additional tests Signed-off-by: Ryan Shaffer <[email protected]> * CMake fixes (with inputs from Bettina Heim and Ryan Shaffer) Signed-off-by: Pradnya Khalate <[email protected]> * Remove the anyon test (issue#2249) Signed-off-by: Pradnya Khalate <[email protected]> * Restore the use of 'crypto_LIBRARY' and 'crypto_INCLUDE_DIR' Signed-off-by: Pradnya Khalate <[email protected]> --------- Signed-off-by: Ryan <[email protected]> Signed-off-by: Pradnya Khalate <[email protected]> Signed-off-by: Pradnya Khalate <[email protected]> Signed-off-by: Ryan Shaffer <[email protected]>
1 parent 03bd77c commit a69a877

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1590
-7
lines changed

.github/workflows/config/spelling_allowlist.txt

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Anyon
99
Asynchronous
1010
BFGS
1111
Bloch
12+
Braket
1213
CLA
1314
CLI
1415
CMake

.gitmodules

+3
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,6 @@
4848
[submodule "tpls/Stim"]
4949
path = tpls/Stim
5050
url = https://github.com/quantumlib/Stim
51+
[submodule "tpls/aws-sdk-cpp"]
52+
path = tpls/aws-sdk-cpp
53+
url = https://github.com/aws/aws-sdk-cpp.git

CMakeLists.txt

+47-1
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,14 @@ if(NOT CURL_LIBRARY AND EXISTS "$ENV{CURL_INSTALL_PREFIX}/lib/libcurl.a")
162162
SET(CMAKE_USE_SYSTEM_CURL TRUE)
163163
SET(CURL_NO_CURL_CMAKE ON)
164164
endif()
165+
if (NOT crypto_LIBRARY AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND EXISTS "$ENV{OPENSSL_INSTALL_PREFIX}/lib64/libcrypto.a")
166+
SET(crypto_LIBRARY "$ENV{OPENSSL_INSTALL_PREFIX}/lib64/libcrypto.a" CACHE INTERNAL "")
167+
elseif(NOT crypto_LIBRARY AND NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND EXISTS "$ENV{OPENSSL_INSTALL_PREFIX}/lib/libcrypto.a")
168+
SET(crypto_LIBRARY "$ENV{OPENSSL_INSTALL_PREFIX}/lib/libcrypto.a" CACHE INTERNAL "")
169+
endif()
170+
if (NOT crypto_INCLUDE_DIR AND EXISTS "$ENV{OPENSSL_INSTALL_PREFIX}/include")
171+
SET(crypto_INCLUDE_DIR "$ENV{OPENSSL_INSTALL_PREFIX}/include" CACHE INTERNAL "")
172+
endif()
165173
if(NOT CUDAQ_EXTERNAL_NVQIR_SIMS)
166174
SET(CUDAQ_EXTERNAL_NVQIR_SIMS $ENV{CUDAQ_EXTERNAL_NVQIR_SIMS})
167175
endif()
@@ -476,9 +484,47 @@ if (OPENSSL_FOUND AND CUDAQ_ENABLE_REST)
476484
# The asio submodule doesn't use cmake, so use find_path for it.
477485
find_path(ASIO_INCLUDE_DIR asio.hpp PATHS tpls/asio/asio/include)
478486
add_subdirectory(tpls/Crow)
487+
# AWS SDK for C++
488+
set(SERVICE_COMPONENTS braket s3-crt sts)
489+
# Call find_package without REQUIRED flag to see whether AWSSDK is installed.
490+
find_package(AWSSDK COMPONENTS ${SERVICE_COMPONENTS})
491+
if (NOT AWSSDK_FOUND)
492+
message(STATUS "AWS SDK not found. Building it.")
493+
include(ProcessorCount)
494+
ProcessorCount(N)
495+
if(CMAKE_MAKE_PROGRAM MATCHES "make$")
496+
set(MAKE_PARALLEL ${CMAKE_MAKE_PROGRAM} -j${N})
497+
else()
498+
set(MAKE_PARALLEL ${CMAKE_MAKE_PROGRAM})
499+
endif()
500+
execute_process(RESULT_VARIABLE result COMMAND cmake "-B${CMAKE_CURRENT_BINARY_DIR}/tpls/aws-sdk-cpp" "${CMAKE_CURRENT_SOURCE_DIR}/tpls/aws-sdk-cpp"
501+
"-DAUTORUN_UNIT_TESTS=OFF"
502+
"-DAWS_SDK_WARNINGS_ARE_ERRORS=OFF"
503+
"-DAWS_USER_AGENT_CUSTOMIZATION=CUDA-Q/${CUDA_QUANTUM_VERSION}"
504+
"-DBUILD_ONLY=braket;s3-crt;sts"
505+
"-DBUILD_SHARED_LIBS=OFF"
506+
"-DCMAKE_COMPILE_WARNING_AS_ERROR=OFF"
507+
"-Dcrypto_LIBRARY=${crypto_LIBRARY}"
508+
"-Dcrypto_INCLUDE_DIR=${crypto_INCLUDE_DIR}"
509+
"-DCURL_LIBRARY=${CURL_LIBRARY}"
510+
"-DCURL_INCLUDE_DIR=${CURL_INCLUDE_DIR}"
511+
"-DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR}"
512+
"-DENABLE_TESTING=OFF"
513+
"-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}"
514+
"-G ${CMAKE_GENERATOR}"
515+
)
516+
if(NOT ${result} STREQUAL "0")
517+
message(FATAL_ERROR "Configuring aws-sdk-cpp failed.")
518+
endif()
519+
execute_process(RESULT_VARIABLE result COMMAND ${CMAKE_COMMAND} -E chdir ${CMAKE_CURRENT_BINARY_DIR}/tpls/aws-sdk-cpp ${MAKE_PARALLEL} install)
520+
if(NOT ${result} STREQUAL "0")
521+
message(FATAL_ERROR "Building aws-sdk-cpp failed.")
522+
endif()
523+
endif()
524+
# AWSSDK should be installed now. Call find_package with REQUIRED flag.
525+
find_package(AWSSDK REQUIRED COMPONENTS ${SERVICE_COMPONENTS})
479526
endif()
480527

481-
482528
# Check for CUDA Support
483529
# ==============================================================================
484530
include(CheckLanguage)

NOTICE

+8
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,11 @@ The incorporated source code and its license can be found as a submodule on the
180180
License at <https://github.com/quantumlib/Stim/blob/main/LICENSE>
181181

182182
----------------------------------------------------------------
183+
184+
AWS SDK for C++ - Apache License 2.0
185+
<https://github.com/aws/aws-sdk-cpp>
186+
187+
The incorporated source code and its license can be found as a submodule on the CUDA-Q repository.
188+
License at <https://github.com/aws/aws-sdk-cpp/blob/main/LICENSE>
189+
190+
----------------------------------------------------------------

runtime/common/BaseRemoteRESTQPU.h

+6-3
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,6 @@ class BaseRemoteRESTQPU : public cudaq::QPU {
144144
BaseRemoteRESTQPU() : QPU() {
145145
std::filesystem::path cudaqLibPath{cudaq::getCUDAQLibraryPath()};
146146
platformPath = cudaqLibPath.parent_path().parent_path() / "targets";
147-
// Default is to run sampling via the remote rest call
148-
executor = std::make_unique<cudaq::Executor>();
149147
}
150148

151149
BaseRemoteRESTQPU(BaseRemoteRESTQPU &&) = delete;
@@ -315,14 +313,19 @@ class BaseRemoteRESTQPU : public cudaq::QPU {
315313

316314
// Set the qpu name
317315
qpuName = mutableBackend;
318-
319316
// Create the ServerHelper for this QPU and give it the backend config
320317
serverHelper = cudaq::registry::get<cudaq::ServerHelper>(qpuName);
321318
if (!serverHelper) {
322319
throw std::runtime_error("ServerHelper not found for target");
323320
}
324321
serverHelper->initialize(backendConfig);
325322
serverHelper->updatePassPipeline(platformPath, passPipelineConfig);
323+
cudaq::info("Retrieving executor with name {}", qpuName);
324+
cudaq::info("Is this executor registered? {}",
325+
cudaq::registry::isRegistered<cudaq::Executor>(qpuName));
326+
executor = cudaq::registry::isRegistered<cudaq::Executor>(qpuName)
327+
? cudaq::registry::get<cudaq::Executor>(qpuName)
328+
: std::make_unique<cudaq::Executor>();
326329

327330
// Give the server helper to the executor
328331
executor->setServerHelper(serverHelper.get());

runtime/common/Executor.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,5 @@ Executor::execute(std::vector<KernelExecution> &codesToExecute) {
5656
return details::future(ids, name, config);
5757
}
5858
} // namespace cudaq
59+
60+
LLVM_INSTANTIATE_REGISTRY(cudaq::Executor::RegistryType)

runtime/common/Executor.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class Executor : public registry::RegisteredType<Executor> {
4141

4242
/// @brief Execute the provided quantum codes and return a future object
4343
/// The caller can make this synchronous by just immediately calling .get().
44-
details::future execute(std::vector<KernelExecution> &codesToExecute);
44+
virtual details::future execute(std::vector<KernelExecution> &codesToExecute);
4545
};
4646

4747
} // namespace cudaq

runtime/cudaq/platform/default/rest/CMakeLists.txt

+5-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ target_include_directories(cudaq-rest-qpu PRIVATE .
1919
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/runtime>
2020
$<INSTALL_INTERFACE:include>)
2121

22+
set(SERVICE_COMPONENTS braket s3-crt sts)
23+
find_package(AWSSDK REQUIRED COMPONENTS ${SERVICE_COMPONENTS})
24+
2225
target_link_libraries(cudaq-rest-qpu
2326
PUBLIC
2427
cudaq-spin
@@ -28,7 +31,8 @@ target_link_libraries(cudaq-rest-qpu
2831
MLIRTranslateLib
2932
fmt::fmt-header-only
3033
cudaq
31-
cudaq-platform-default)
34+
cudaq-platform-default
35+
${AWSSDK_LINK_LIBRARIES})
3236

3337
install(TARGETS cudaq-rest-qpu DESTINATION lib)
3438

runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
# the terms of the Apache License 2.0 which accompanies this distribution. #
77
# ============================================================================ #
88
add_subdirectory(anyon)
9+
add_subdirectory(braket)
910
add_subdirectory(oqc)
1011
add_subdirectory(ionq)
1112
add_subdirectory(quantinuum)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. *
3+
* All rights reserved. *
4+
* *
5+
* This source code and the accompanying materials are made available under *
6+
* the terms of the Apache License 2.0 which accompanies this distribution. *
7+
******************************************************************************/
8+
9+
#include "BraketExecutor.h"
10+
#include "BraketServerHelper.h"
11+
12+
namespace cudaq {
13+
14+
details::future
15+
BraketExecutor::execute(std::vector<KernelExecution> &codesToExecute) {
16+
auto braketServerHelper = dynamic_cast<BraketServerHelper *>(serverHelper);
17+
assert(braketServerHelper);
18+
braketServerHelper->setShots(shots);
19+
20+
auto [dummy1, dummy2, messages] =
21+
braketServerHelper->createJob(codesToExecute);
22+
23+
std::string const defaultBucket = defaultBucketFuture.get();
24+
std::string const defaultPrefix = "tasks";
25+
26+
auto config = braketServerHelper->getConfig();
27+
cudaq::info("Backend config: {}, shots {}", config, shots);
28+
config.insert({"shots", std::to_string(shots)});
29+
30+
std::vector<Aws::Braket::Model::CreateQuantumTaskOutcomeCallable>
31+
createOutcomes;
32+
33+
for (const auto &message : messages) {
34+
Aws::Braket::Model::CreateQuantumTaskRequest req;
35+
req.SetAction(message["action"]);
36+
req.SetDeviceArn(message["deviceArn"]);
37+
req.SetShots(message["shots"]);
38+
if (jobToken)
39+
req.SetJobToken(jobToken);
40+
req.SetOutputS3Bucket(defaultBucket);
41+
req.SetOutputS3KeyPrefix(defaultPrefix);
42+
43+
createOutcomes.push_back(braketClient.CreateQuantumTaskCallable(req));
44+
}
45+
46+
return std::async(
47+
std::launch::async,
48+
[this](std::vector<Aws::Braket::Model::CreateQuantumTaskOutcomeCallable>
49+
createOutcomes) {
50+
std::vector<ExecutionResult> results;
51+
for (auto &outcome : createOutcomes) {
52+
auto createResponse = outcome.get();
53+
if (!createResponse.IsSuccess()) {
54+
throw std::runtime_error(createResponse.GetError().GetMessage());
55+
}
56+
std::string taskArn = createResponse.GetResult().GetQuantumTaskArn();
57+
cudaq::info("Created Braket quantum task {}", taskArn);
58+
59+
Aws::Braket::Model::GetQuantumTaskRequest req;
60+
req.SetQuantumTaskArn(taskArn);
61+
auto getResponse = braketClient.GetQuantumTask(req);
62+
if (!getResponse.IsSuccess()) {
63+
throw std::runtime_error(getResponse.GetError().GetMessage());
64+
}
65+
auto taskStatus = getResponse.GetResult().GetStatus();
66+
while (
67+
taskStatus != Aws::Braket::Model::QuantumTaskStatus::COMPLETED &&
68+
taskStatus != Aws::Braket::Model::QuantumTaskStatus::FAILED &&
69+
taskStatus != Aws::Braket::Model::QuantumTaskStatus::CANCELLED) {
70+
std::this_thread::sleep_for(pollingInterval);
71+
72+
getResponse = braketClient.GetQuantumTask(req);
73+
if (!getResponse.IsSuccess()) {
74+
throw std::runtime_error(getResponse.GetError().GetMessage());
75+
}
76+
taskStatus = getResponse.GetResult().GetStatus();
77+
}
78+
79+
auto getResult = getResponse.GetResult();
80+
if (taskStatus != Aws::Braket::Model::QuantumTaskStatus::COMPLETED) {
81+
// Task terminated without results
82+
throw std::runtime_error(
83+
fmt::format("Braket task {} terminated without results. {}",
84+
taskArn, getResult.GetFailureReason()));
85+
}
86+
87+
std::string outBucket = getResult.GetOutputS3Bucket();
88+
std::string outPrefix = getResult.GetOutputS3Directory();
89+
90+
cudaq::info("Fetching braket quantum task {} results from "
91+
"s3://{}/{}/results.json",
92+
taskArn, outBucket, outPrefix);
93+
94+
Aws::S3Crt::Model::GetObjectRequest resultsJsonRequest;
95+
resultsJsonRequest.SetBucket(outBucket);
96+
resultsJsonRequest.SetKey(fmt::format("{}/results.json", outPrefix));
97+
auto s3Response = s3Client.GetObject(resultsJsonRequest);
98+
if (!s3Response.IsSuccess()) {
99+
throw std::runtime_error(s3Response.GetError().GetMessage());
100+
}
101+
auto resultsJson = nlohmann::json::parse(
102+
s3Response.GetResultWithOwnership().GetBody());
103+
auto c = serverHelper->processResults(resultsJson, taskArn);
104+
105+
for (auto &regName : c.register_names()) {
106+
results.emplace_back(c.to_map(regName), regName);
107+
results.back().sequentialData = c.sequential_data(regName);
108+
}
109+
}
110+
111+
return sample_result(results);
112+
},
113+
std::move(createOutcomes));
114+
};
115+
} // namespace cudaq
116+
117+
CUDAQ_REGISTER_TYPE(cudaq::Executor, cudaq::BraketExecutor, braket);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. *
3+
* All rights reserved. *
4+
* *
5+
* This source code and the accompanying materials are made available under *
6+
* the terms of the Apache License 2.0 which accompanies this distribution. *
7+
******************************************************************************/
8+
9+
#pragma once
10+
#include "common/Executor.h"
11+
#include "common/FmtCore.h"
12+
#include "common/MeasureCounts.h"
13+
#include "cudaq.h"
14+
15+
#include <chrono>
16+
#include <iostream>
17+
18+
#include <aws/core/Aws.h>
19+
20+
#include <aws/braket/BraketClient.h>
21+
#include <aws/braket/model/CreateQuantumTaskRequest.h>
22+
#include <aws/braket/model/GetQuantumTaskRequest.h>
23+
#include <aws/braket/model/QuantumTaskStatus.h>
24+
25+
#include <aws/sts/STSClient.h>
26+
27+
#include <aws/s3-crt/S3CrtClient.h>
28+
#include <aws/s3-crt/model/GetObjectRequest.h>
29+
30+
#include <aws/core/utils/logging/AWSLogging.h>
31+
#include <aws/core/utils/logging/ConsoleLogSystem.h>
32+
#include <aws/core/utils/logging/LogLevel.h>
33+
34+
#include "BraketServerHelper.h"
35+
#include "common/Logger.h"
36+
37+
#include <nlohmann/json.hpp>
38+
#include <regex>
39+
#include <string>
40+
#include <thread>
41+
42+
namespace cudaq {
43+
/// @brief The Executor subclass for Amazon Braket
44+
class BraketExecutor : public Executor {
45+
Aws::SDKOptions options;
46+
47+
class ScopedApi {
48+
Aws::SDKOptions &options;
49+
50+
public:
51+
ScopedApi(Aws::SDKOptions &options) : options(options) {
52+
cudaq::debug("Initializing AWS API");
53+
Aws::InitAPI(options);
54+
}
55+
~ScopedApi() { Aws::ShutdownAPI(options); }
56+
};
57+
58+
ScopedApi api;
59+
Aws::Braket::BraketClient braketClient;
60+
Aws::STS::STSClient stsClient;
61+
Aws::S3Crt::S3CrtClient s3Client;
62+
63+
std::future<std::string> defaultBucketFuture;
64+
char const *jobToken;
65+
66+
std::chrono::microseconds pollingInterval = std::chrono::milliseconds{100};
67+
68+
static auto getClientConfig() {
69+
Aws::Client::ClientConfiguration clientConfig;
70+
clientConfig.verifySSL = false;
71+
return clientConfig;
72+
}
73+
74+
static auto getS3ClientConfig() {
75+
Aws::S3Crt::ClientConfiguration clientConfig;
76+
clientConfig.verifySSL = false;
77+
return clientConfig;
78+
}
79+
80+
public:
81+
BraketExecutor()
82+
: api(options), braketClient(getClientConfig()),
83+
stsClient(getClientConfig()), s3Client(getS3ClientConfig()),
84+
jobToken(std::getenv("AMZN_BRAKET_JOB_TOKEN")) {
85+
cudaq::debug("Creating BraketExecutor");
86+
87+
defaultBucketFuture = std::async(std::launch::async, [this] {
88+
auto response = stsClient.GetCallerIdentity();
89+
std::string bucketName;
90+
if (response.IsSuccess()) {
91+
bucketName =
92+
fmt::format("amazon-braket-{}-{}", getClientConfig().region,
93+
response.GetResult().GetAccount());
94+
cudaq::info("Braket task results will use S3 bucket \"{}\"",
95+
bucketName);
96+
return bucketName;
97+
} else {
98+
throw std::runtime_error(response.GetError().GetMessage());
99+
}
100+
});
101+
}
102+
103+
~BraketExecutor() = default;
104+
105+
/// @brief Execute the provided Braket task
106+
details::future
107+
execute(std::vector<KernelExecution> &codesToExecute) override;
108+
};
109+
} // namespace cudaq

0 commit comments

Comments
 (0)