From 1b1b20aab74f5273d534c07e9e460d8af2438e5b Mon Sep 17 00:00:00 2001 From: feelerx Date: Tue, 11 Mar 2025 15:40:51 +0000 Subject: [PATCH 01/11] working implementation using openQasm --- .../default/rest/helpers/CMakeLists.txt | 1 + .../rest/helpers/qbraid/CMakeLists.txt | 17 + .../helpers/qbraid/QbraidServerHelper.cpp | 319 ++++++++++++++++++ .../default/rest/helpers/qbraid/qbraid.yml | 45 +++ 4 files changed, 382 insertions(+) create mode 100644 runtime/cudaq/platform/default/rest/helpers/qbraid/CMakeLists.txt create mode 100644 runtime/cudaq/platform/default/rest/helpers/qbraid/QbraidServerHelper.cpp create mode 100644 runtime/cudaq/platform/default/rest/helpers/qbraid/qbraid.yml diff --git a/runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt b/runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt index 24beff59bf5..0f0de26f9ef 100644 --- a/runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt +++ b/runtime/cudaq/platform/default/rest/helpers/CMakeLists.txt @@ -10,6 +10,7 @@ add_subdirectory(infleqtion) add_subdirectory(ionq) add_subdirectory(iqm) add_subdirectory(oqc) +add_subdirectory(qbraid) add_subdirectory(quantinuum) if (AWSSDK_ROOT) add_subdirectory(braket) diff --git a/runtime/cudaq/platform/default/rest/helpers/qbraid/CMakeLists.txt b/runtime/cudaq/platform/default/rest/helpers/qbraid/CMakeLists.txt new file mode 100644 index 00000000000..05b059ecd25 --- /dev/null +++ b/runtime/cudaq/platform/default/rest/helpers/qbraid/CMakeLists.txt @@ -0,0 +1,17 @@ +# ============================================================================ # +# Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # +target_sources(cudaq-rest-qpu PRIVATE QbraidServerHelper.cpp) +add_target_config(qbraid) + +add_library(cudaq-serverhelper-qbraid SHARED QbraidServerHelper.cpp ) +target_link_libraries(cudaq-serverhelper-qbraid + PUBLIC + cudaq-common + fmt::fmt-header-only +) +install(TARGETS cudaq-serverhelper-qbraid DESTINATION lib) \ No newline at end of file diff --git a/runtime/cudaq/platform/default/rest/helpers/qbraid/QbraidServerHelper.cpp b/runtime/cudaq/platform/default/rest/helpers/qbraid/QbraidServerHelper.cpp new file mode 100644 index 00000000000..ac47742f657 --- /dev/null +++ b/runtime/cudaq/platform/default/rest/helpers/qbraid/QbraidServerHelper.cpp @@ -0,0 +1,319 @@ +#include "common/Logger.h" +#include "common/RestClient.h" +#include "common/ServerHelper.h" +#include "cudaq/Support/Version.h" +#include "cudaq/utils/cudaq_utils.h" +#include +#include +#include +#include + +namespace cudaq { + +class QbraidServerHelper : public ServerHelper { + static constexpr const char *DEFAULT_URL = "https://api.qbraid.com/api"; + static constexpr const char *DEFAULT_DEVICE = "ionq_simulator"; + static constexpr int DEFAULT_QUBITS = 29; + +public: + const std::string name() const override { return "qbraid"; } + + void initialize(BackendConfig config) override { + cudaq::info("Initializing Qbraid Backend."); + + // Initialize required configuration + backendConfig.clear(); + backendConfig["url"] = getValueOrDefault(config, "url", DEFAULT_URL); + backendConfig["device_id"] = getValueOrDefault(config, "device_id", DEFAULT_DEVICE); + backendConfig["user_agent"] = "cudaq/" + std::string(cudaq::getVersion()); + backendConfig["qubits"] = std::to_string(DEFAULT_QUBITS); // Set default qubits + + // Get API key from environment + backendConfig["api_key"] = getEnvVar("QBRAID_API_KEY", "", true); + + // Job endpoints + backendConfig["job_path"] = backendConfig["url"] + "/quantum-jobs"; + // result endpoints + backendConfig["results_path"] = backendConfig["url"] + "/quantum-jobs/result/"; + + // Add results output directory and file naming pattern + backendConfig["results_output_dir"] = getValueOrDefault(config, "results_output_dir", "./qbraid_results"); + backendConfig["results_file_prefix"] = getValueOrDefault(config, "results_file_prefix", "qbraid_job_"); + + if (!config["shots"].empty()) { + backendConfig["shots"] = config["shots"]; + this->setShots(std::stoul(config["shots"])); + } else { + backendConfig["shots"] = "1000"; + this->setShots(1000); + } + + parseConfigForCommonParams(config); + + cudaq::info("Qbraid configuration initialized:"); + for (const auto& [key, value] : backendConfig) { + cudaq::info(" {} = {}", key, value); + } + + // Create results directory if it doesn't exist + std::string resultsDir = backendConfig["results_output_dir"]; + std::filesystem::create_directories(resultsDir); + cudaq::info("Created results directory: {}", resultsDir); + } + + ServerJobPayload createJob(std::vector &circuitCodes) override { + if (backendConfig.find("job_path") == backendConfig.end()) { + throw std::runtime_error("job_path not found in config. Was initialize() called?"); + } + + std::vector jobs; + for (auto &circuitCode : circuitCodes) { + ServerMessage job; + job["qbraidDeviceId"] = backendConfig.at("device_id"); + job["openQasm"] = circuitCode.code; + job["shots"] = std::stoi(backendConfig.at("shots")); + job["circuitNumQubits"] = std::stoi(backendConfig.at("qubits")); + + if (!circuitCode.name.empty()) { + nlohmann::json tags; + tags["name"] = circuitCode.name; + job["tags"] = tags; + } + + jobs.push_back(job); + } + + return std::make_tuple(backendConfig.at("job_path"), getHeaders(), jobs); + } + + std::string extractJobId(ServerMessage &postResponse) override { + if (!postResponse.contains("qbraidJobId")) + throw std::runtime_error("ServerMessage doesn't contain 'qbraidJobId' key."); + return postResponse.at("qbraidJobId"); + } + + std::string constructGetJobPath(ServerMessage &postResponse) override { + if (!postResponse.contains("qbraidJobId")) + throw std::runtime_error("ServerMessage doesn't contain 'qbraidJobId' key."); + // Use qbraidJobId instead of vendorJobId + return backendConfig.at("job_path") + "?qbraidJobId=" + + postResponse.at("qbraidJobId").get(); + } + + std::string constructGetJobPath(std::string &jobId) override { + // Use qbraidJobId instead of vendorJobId + return backendConfig.at("job_path") + "?qbraidJobId=" + jobId; + } + + // Getting results path - simplified to match working implementation + std::string constructGetResultsPath(const std::string &jobId) { + return backendConfig.at("results_path") + jobId; + } + + // Job is done with sample results api - simplified based on working implementation + bool jobIsDone(ServerMessage &getJobResponse) override { + // Save the current job response to a file regardless of status + saveResponseToFile(getJobResponse); + + std::string status; + + // Check if response has the jobsArray format + if (getJobResponse.contains("jobsArray") && !getJobResponse["jobsArray"].empty()) { + status = getJobResponse["jobsArray"][0]["status"].get(); + cudaq::info("Job status from jobs endpoint: {}", status); + } + // Check if it uses the direct format + else if (getJobResponse.contains("status")) { + status = getJobResponse["status"].get(); + cudaq::info("Job status from direct response: {}", status); + } + // Or if it's in the data object + else if (getJobResponse.contains("data") && getJobResponse["data"].contains("status")) { + status = getJobResponse["data"]["status"].get(); + cudaq::info("Job status from data object: {}", status); + } + else { + cudaq::info("Unexpected job response format: {}", getJobResponse.dump()); + throw std::runtime_error("Invalid job response format"); + } + + if (status == "FAILED") + throw std::runtime_error("The job failed upon submission. Check your qBraid account for more information."); + + return status == "COMPLETED"; + } + + // Sample results with results api - with retry logic + cudaq::sample_result processResults(ServerMessage &getJobResponse, + std::string &jobId) override { + // Save the final job response to file + saveResponseToFile(getJobResponse, jobId + "_final"); + + // Try to get results using the direct results endpoint with retries + int maxRetries = 5; + int waitTime = 2; + float backoffFactor = 2.0; + + for (int attempt = 0; attempt < maxRetries; ++attempt) { + try { + auto resultsPath = constructGetResultsPath(jobId); + auto headers = getHeaders(); + + cudaq::info("Fetching results using direct endpoint (attempt {}/{}): {}", + attempt + 1, maxRetries, resultsPath); + RestClient client; + auto resultJson = client.get("", resultsPath, headers, true); + + // Save direct results response to file + saveResponseToFile(resultJson, jobId + "_direct_results_" + std::to_string(attempt)); + + if (resultJson.contains("error") && !resultJson["error"].is_null()) { + std::string errorMsg = resultJson["error"].is_string() ? + resultJson["error"].get() : + resultJson["error"].dump(); + cudaq::info("Error from results endpoint: {}", errorMsg); + + // Only throw if on last attempt + if (attempt == maxRetries - 1) { + throw std::runtime_error("Error retrieving results: " + errorMsg); + } + } + else if (resultJson.contains("data") && + resultJson["data"].contains("measurementCounts")) { + cudaq::info("Processing results from direct endpoint"); + CountsDictionary counts; + auto &measurements = resultJson["data"]["measurementCounts"]; + + for (const auto &[bitstring, count] : measurements.items()) { + counts[bitstring] = count.is_number() ? + static_cast(count.get()) : + static_cast(count); + } + + std::vector execResults; + execResults.emplace_back(ExecutionResult{counts}); + return cudaq::sample_result(execResults); + } + + // If we get here, no valid data was found but also no error - retry + if (attempt < maxRetries - 1) { + int sleepTime = waitTime * std::pow(backoffFactor, attempt); + cudaq::info("No valid results yet, retrying in {} seconds", sleepTime); + std::this_thread::sleep_for(std::chrono::seconds(sleepTime)); + } + + } catch (const std::exception &e) { + cudaq::info("Exception when using direct results endpoint: {}", e.what()); + if (attempt < maxRetries - 1) { + int sleepTime = waitTime * std::pow(backoffFactor, attempt); + cudaq::info("Retrying in {} seconds", sleepTime); + std::this_thread::sleep_for(std::chrono::seconds(sleepTime)); + } + else { + cudaq::info("Falling back to original results processing method"); + } + } + } + + // Original result processing as fallback + cudaq::info("Processing results from job response for job {}", jobId); + if (getJobResponse.contains("jobsArray") && !getJobResponse["jobsArray"].empty()) { + auto &job = getJobResponse["jobsArray"][0]; + + if (job.contains("measurementCounts")) { + CountsDictionary counts; + auto &measurements = job["measurementCounts"]; + + for (const auto &[bitstring, count] : measurements.items()) { + counts[bitstring] = count.get(); + } + + std::vector execResults; + execResults.emplace_back(ExecutionResult{counts}); + return cudaq::sample_result(execResults); + } + } + + // Last resort - check for direct measurementCounts in the response + if (getJobResponse.contains("measurementCounts")) { + CountsDictionary counts; + auto &measurements = getJobResponse["measurementCounts"]; + + for (const auto &[bitstring, count] : measurements.items()) { + counts[bitstring] = count.get(); + } + + std::vector execResults; + execResults.emplace_back(ExecutionResult{counts}); + return cudaq::sample_result(execResults); + } + + throw std::runtime_error("No measurement counts found in any response format"); + } + +private: + // New method to save response to file + void saveResponseToFile(const ServerMessage &response, const std::string &identifier = "") { + try { + std::string outputDir = backendConfig.at("results_output_dir"); + std::string filePrefix = backendConfig.at("results_file_prefix"); + + // Create a unique filename using timestamp if no identifier provided + std::string filename; + if (identifier.empty()) { + auto now = std::chrono::system_clock::now(); + auto timestamp = std::chrono::duration_cast( + now.time_since_epoch()).count(); + filename = outputDir + "/" + filePrefix + std::to_string(timestamp) + ".json"; + } else { + filename = outputDir + "/" + filePrefix + identifier + ".json"; + } + + // Write the JSON response to the file with proper formatting + std::ofstream outputFile(filename); + if (!outputFile.is_open()) { + cudaq::info("Failed to open file for writing: {}", filename); + return; + } + + outputFile << response.dump(2); // 2 spaces for indentation + outputFile.close(); + + cudaq::info("Response saved to file: {}", filename); + } catch (const std::exception &e) { + cudaq::info("Error saving response to file: {}", e.what()); + } + } + + RestHeaders getHeaders() override { + if (backendConfig.find("api_key") == backendConfig.end()) { + throw std::runtime_error("API key not found in config. Was initialize() called?"); + } + + RestHeaders headers; + headers["api-key"] = backendConfig.at("api_key"); + headers["Content-Type"] = "application/json"; + headers["User-Agent"] = backendConfig.at("user_agent"); + return headers; + } + + std::string getEnvVar(const std::string &key, const std::string &defaultVal, + const bool isRequired) const { + const char *env_var = std::getenv(key.c_str()); + if (env_var == nullptr) { + if (isRequired) + throw std::runtime_error(key + " environment variable is not set."); + return defaultVal; + } + return std::string(env_var); + } + + std::string getValueOrDefault(const BackendConfig &config, + const std::string &key, + const std::string &defaultValue) const { + return config.find(key) != config.end() ? config.at(key) : defaultValue; + } +}; +} + +CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::QbraidServerHelper, qbraid) diff --git a/runtime/cudaq/platform/default/rest/helpers/qbraid/qbraid.yml b/runtime/cudaq/platform/default/rest/helpers/qbraid/qbraid.yml new file mode 100644 index 00000000000..e62840d5f2d --- /dev/null +++ b/runtime/cudaq/platform/default/rest/helpers/qbraid/qbraid.yml @@ -0,0 +1,45 @@ +# ============================================================================ # +# Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # + +name: qbraid +description: "CUDA-Q target for qbraid." +config: + # Tell DefaultQuantumPlatform what QPU subtype to use + platform-qpu: remote_rest + # Tell NVQ++ to generate glue code to set the target backend name + gen-target-backend: true + # Add the rest-qpu library to the link list + link-libs: ["-lcudaq-rest-qpu"] + # Define the lowering pipeline + platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg),canonicalize,func.func(multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,CCZToCX,CRyToCX,CRxToCX,R1AdjToR1,RxAdjToRx,RyAdjToRy,RzAdjToRz},quake-to-cc-prep,func.func(memtoreg{quantum=0}),symbol-dce" + # Tell the rest-qpu that we are generating QIR. + codegen-emission: qasm2 + # Library mode is only for simulators, physical backends must turn this off + library-mode: false + +target-arguments: + - key: machine + required: false + type: string + platform-arg: qpu + help-string: "Specify the qbraid QPU." + - key: noise-model + required: false + type: string + platform-arg: noise + help-string: "Specify the noise model for simulation." + - key: debias + required: false + type: string + platform-arg: debias + help-string: "Specify debiasing." + - key: sharpen + required: false + type: string + platform-arg: sharpen + help-string: "Specify sharpening." From a2624e7b5bca06c31f20f273b72700c59df0b8f1 Mon Sep 17 00:00:00 2001 From: feelerx Date: Wed, 19 Mar 2025 06:58:59 +0000 Subject: [PATCH 02/11] modified and added test files(incomplete) --- .github/workflows/integration_tests.yml | 1 + lib/Optimizer/CodeGen/Passes.cpp | 14 ++ .../helpers/qbraid/QbraidServerHelper.cpp | 18 +- targettests/execution/bug_qubit.cpp | 1 + targettests/execution/callable_kernel_arg.cpp | 1 + targettests/execution/cudaq_observe-cpp17.cpp | 1 + targettests/execution/cudaq_observe.cpp | 1 + .../execution/custom_operation_adj.cpp | 1 + .../execution/custom_operation_basic.cpp | 1 + .../execution/custom_operation_ctrl.cpp | 1 + targettests/execution/exp_pauli.cpp | 1 + targettests/execution/if_jit.cpp | 1 + targettests/execution/int8_t.cpp | 1 + targettests/execution/int8_t_free_func.cpp | 1 + targettests/execution/load_value.cpp | 1 + targettests/execution/qspan_slices.cpp | 1 + targettests/execution/state_preparation.cpp | 1 + .../execution/state_preparation_vector.cpp | 1 + .../state_preparation_vector_sizes.cpp | 1 + targettests/execution/sudoku_2x2-1.cpp | 1 + .../execution/sudoku_2x2-bit_names.cpp | 1 + targettests/execution/sudoku_2x2-reg_name.cpp | 1 + targettests/execution/sudoku_2x2.cpp | 1 + targettests/execution/swap_gate.cpp | 1 + targettests/execution/uccsd.cpp | 1 + targettests/execution/variable_size_qreg.cpp | 1 + targettests/lit.cfg.py | 2 +- targettests/qbraid/bug_qubit.cpp | 50 +++++ targettests/qbraid/callable_kernel_arg.cpp | 50 +++++ targettests/qbraid/cudaq_observe.cpp | 57 ++++++ targettests/qbraid/if_jit.cpp | 45 +++++ targettests/qbraid/load_value.cpp | 63 ++++++ targettests/qbraid/sudoku_2x2-1.cpp | 79 ++++++++ targettests/qbraid/sudoku_2x2-bit_names.cpp | 103 ++++++++++ targettests/qbraid/sudoku_2x2-reg_name.cpp | 79 ++++++++ targettests/qbraid/sudoku_2x2.cpp | 78 ++++++++ targettests/qbraid/swap_gate.cpp | 43 ++++ targettests/qbraid/test-int8_t.cpp | 48 +++++ targettests/qbraid/test-int8_t_free_func.cpp | 46 +++++ targettests/qbraid/variable_size_qreg.cpp | 46 +++++ unittests/backends/CMakeLists.txt | 1 + unittests/backends/qbraid/CMakeLists.txt | 28 +++ .../qbraid/QbraidStartServerAndTest.sh.in | 43 ++++ unittests/backends/qbraid/QbraidTester.cpp | 185 ++++++++++++++++++ utils/mock_qpu/__init__.py | 1 + utils/mock_qpu/qbraid/__init__.py | 176 +++++++++++++++++ 46 files changed, 1268 insertions(+), 11 deletions(-) create mode 100644 targettests/qbraid/bug_qubit.cpp create mode 100644 targettests/qbraid/callable_kernel_arg.cpp create mode 100644 targettests/qbraid/cudaq_observe.cpp create mode 100644 targettests/qbraid/if_jit.cpp create mode 100644 targettests/qbraid/load_value.cpp create mode 100644 targettests/qbraid/sudoku_2x2-1.cpp create mode 100644 targettests/qbraid/sudoku_2x2-bit_names.cpp create mode 100644 targettests/qbraid/sudoku_2x2-reg_name.cpp create mode 100644 targettests/qbraid/sudoku_2x2.cpp create mode 100644 targettests/qbraid/swap_gate.cpp create mode 100644 targettests/qbraid/test-int8_t.cpp create mode 100644 targettests/qbraid/test-int8_t_free_func.cpp create mode 100644 targettests/qbraid/variable_size_qreg.cpp create mode 100644 unittests/backends/qbraid/CMakeLists.txt create mode 100644 unittests/backends/qbraid/QbraidStartServerAndTest.sh.in create mode 100644 unittests/backends/qbraid/QbraidTester.cpp create mode 100644 utils/mock_qpu/qbraid/__init__.py diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml index 5619f5baea0..97fc973a323 100644 --- a/.github/workflows/integration_tests.yml +++ b/.github/workflows/integration_tests.yml @@ -25,6 +25,7 @@ on: - nvqc - orca - fermioniq + - qbraid single_test_name: type: string required: false diff --git a/lib/Optimizer/CodeGen/Passes.cpp b/lib/Optimizer/CodeGen/Passes.cpp index 81b2805c5a1..215b2279c9e 100644 --- a/lib/Optimizer/CodeGen/Passes.cpp +++ b/lib/Optimizer/CodeGen/Passes.cpp @@ -96,6 +96,17 @@ static void addFermioniqPipeline(OpPassManager &pm) { pm.addPass(createBasisConversionPass(options)); } +static void addQbraidPipeline(OpPassManager &pm) { + using namespace cudaq::opt; + std::string basis[] = { + "h", "s", "t", "rx", "ry", "rz", "x", "y", "z", "x(1)", + }; + BasisConversionPassOptions options; + options.basis = basis; + options.disabledPatterns = z_disabledPatterns; + pm.addPass(createBasisConversionPass(options)); +} + void cudaq::opt::registerTargetPipelines() { PassPipelineRegistration<>("anyon-cgate-set-mapping", "Convert kernels to Anyon gate set.", @@ -118,6 +129,9 @@ void cudaq::opt::registerTargetPipelines() { PassPipelineRegistration<>("fermioniq-gate-set-mapping", "Convert kernels to Fermioniq gate set.", addFermioniqPipeline); + PassPipelineRegistration<>("qbraid-gate-set-mapping", + "Convert kernels to qBraid gate set.", + addQbraidPipeline); } void cudaq::opt::registerCodeGenDialect(DialectRegistry ®istry) { diff --git a/runtime/cudaq/platform/default/rest/helpers/qbraid/QbraidServerHelper.cpp b/runtime/cudaq/platform/default/rest/helpers/qbraid/QbraidServerHelper.cpp index ac47742f657..a2df0793969 100644 --- a/runtime/cudaq/platform/default/rest/helpers/qbraid/QbraidServerHelper.cpp +++ b/runtime/cudaq/platform/default/rest/helpers/qbraid/QbraidServerHelper.cpp @@ -26,7 +26,7 @@ class QbraidServerHelper : public ServerHelper { backendConfig["url"] = getValueOrDefault(config, "url", DEFAULT_URL); backendConfig["device_id"] = getValueOrDefault(config, "device_id", DEFAULT_DEVICE); backendConfig["user_agent"] = "cudaq/" + std::string(cudaq::getVersion()); - backendConfig["qubits"] = std::to_string(DEFAULT_QUBITS); // Set default qubits + backendConfig["qubits"] = std::to_string(DEFAULT_QUBITS); // Get API key from environment backendConfig["api_key"] = getEnvVar("QBRAID_API_KEY", "", true); @@ -36,7 +36,7 @@ class QbraidServerHelper : public ServerHelper { // result endpoints backendConfig["results_path"] = backendConfig["url"] + "/quantum-jobs/result/"; - // Add results output directory and file naming pattern + // Add results output directory backendConfig["results_output_dir"] = getValueOrDefault(config, "results_output_dir", "./qbraid_results"); backendConfig["results_file_prefix"] = getValueOrDefault(config, "results_file_prefix", "qbraid_job_"); @@ -95,24 +95,22 @@ class QbraidServerHelper : public ServerHelper { std::string constructGetJobPath(ServerMessage &postResponse) override { if (!postResponse.contains("qbraidJobId")) throw std::runtime_error("ServerMessage doesn't contain 'qbraidJobId' key."); - // Use qbraidJobId instead of vendorJobId return backendConfig.at("job_path") + "?qbraidJobId=" + postResponse.at("qbraidJobId").get(); } std::string constructGetJobPath(std::string &jobId) override { - // Use qbraidJobId instead of vendorJobId return backendConfig.at("job_path") + "?qbraidJobId=" + jobId; } - // Getting results path - simplified to match working implementation + // Getting results path std::string constructGetResultsPath(const std::string &jobId) { return backendConfig.at("results_path") + jobId; } - // Job is done with sample results api - simplified based on working implementation + // Job is done with sample results api bool jobIsDone(ServerMessage &getJobResponse) override { - // Save the current job response to a file regardless of status + // Save the current job response to a file saveResponseToFile(getJobResponse); std::string status; @@ -252,13 +250,13 @@ class QbraidServerHelper : public ServerHelper { } private: - // New method to save response to file + // Method to save response to file void saveResponseToFile(const ServerMessage &response, const std::string &identifier = "") { try { std::string outputDir = backendConfig.at("results_output_dir"); std::string filePrefix = backendConfig.at("results_file_prefix"); - // Create a unique filename using timestamp if no identifier provided + // Create a unique filename using timestamp if no identifier is provided std::string filename; if (identifier.empty()) { auto now = std::chrono::system_clock::now(); @@ -276,7 +274,7 @@ class QbraidServerHelper : public ServerHelper { return; } - outputFile << response.dump(2); // 2 spaces for indentation + outputFile << response.dump(2); outputFile.close(); cudaq::info("Response saved to file: {}", filename); diff --git a/targettests/execution/bug_qubit.cpp b/targettests/execution/bug_qubit.cpp index 0b33f3fa3bc..2179c9f4da1 100644 --- a/targettests/execution/bug_qubit.cpp +++ b/targettests/execution/bug_qubit.cpp @@ -14,6 +14,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/execution/callable_kernel_arg.cpp b/targettests/execution/callable_kernel_arg.cpp index 39f8574d11d..c51db0dca15 100644 --- a/targettests/execution/callable_kernel_arg.cpp +++ b/targettests/execution/callable_kernel_arg.cpp @@ -12,6 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/execution/cudaq_observe-cpp17.cpp b/targettests/execution/cudaq_observe-cpp17.cpp index a58eeec05fb..a5b5b415695 100644 --- a/targettests/execution/cudaq_observe-cpp17.cpp +++ b/targettests/execution/cudaq_observe-cpp17.cpp @@ -14,6 +14,7 @@ // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // clang-format on diff --git a/targettests/execution/cudaq_observe.cpp b/targettests/execution/cudaq_observe.cpp index edb11895072..484054ed758 100644 --- a/targettests/execution/cudaq_observe.cpp +++ b/targettests/execution/cudaq_observe.cpp @@ -14,6 +14,7 @@ // RUN: nvq++ --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ --target braket --emulate %s -o %t && %t | FileCheck %s; fi // clang-format on diff --git a/targettests/execution/custom_operation_adj.cpp b/targettests/execution/custom_operation_adj.cpp index b3f3f24a1ad..ed537a104db 100644 --- a/targettests/execution/custom_operation_adj.cpp +++ b/targettests/execution/custom_operation_adj.cpp @@ -13,6 +13,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // clang-format on diff --git a/targettests/execution/custom_operation_basic.cpp b/targettests/execution/custom_operation_basic.cpp index e2374d34fdf..77064dff90f 100644 --- a/targettests/execution/custom_operation_basic.cpp +++ b/targettests/execution/custom_operation_basic.cpp @@ -13,6 +13,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // clang-format on diff --git a/targettests/execution/custom_operation_ctrl.cpp b/targettests/execution/custom_operation_ctrl.cpp index 148007afa65..2af189a478b 100644 --- a/targettests/execution/custom_operation_ctrl.cpp +++ b/targettests/execution/custom_operation_ctrl.cpp @@ -12,6 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // clang-format on diff --git a/targettests/execution/exp_pauli.cpp b/targettests/execution/exp_pauli.cpp index 9cb63b8096c..7cab2844518 100644 --- a/targettests/execution/exp_pauli.cpp +++ b/targettests/execution/exp_pauli.cpp @@ -16,6 +16,7 @@ // RUN: nvq++ %cpp_std -fkernel-exec-kind=2 -target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std -fkernel-exec-kind=2 -target oqc --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std -fkernel-exec-kind=2 -target anyon --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std -fkernel-exec-kind=2 -target qbraid --emulate %s -o %t && %t | FileCheck %s // 2 different IQM machines for 2 different topologies // RUN: nvq++ %cpp_std -fkernel-exec-kind=2 -target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s diff --git a/targettests/execution/if_jit.cpp b/targettests/execution/if_jit.cpp index 0778aeec9e0..2516f79fe62 100644 --- a/targettests/execution/if_jit.cpp +++ b/targettests/execution/if_jit.cpp @@ -14,6 +14,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/execution/int8_t.cpp b/targettests/execution/int8_t.cpp index fedd8a527b3..210e7e4f41e 100644 --- a/targettests/execution/int8_t.cpp +++ b/targettests/execution/int8_t.cpp @@ -12,6 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/execution/int8_t_free_func.cpp b/targettests/execution/int8_t_free_func.cpp index 1ff98459748..b7f4ce2d2f0 100644 --- a/targettests/execution/int8_t_free_func.cpp +++ b/targettests/execution/int8_t_free_func.cpp @@ -12,6 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/execution/load_value.cpp b/targettests/execution/load_value.cpp index 4214262798f..19fecb53e11 100644 --- a/targettests/execution/load_value.cpp +++ b/targettests/execution/load_value.cpp @@ -13,6 +13,7 @@ // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/execution/qspan_slices.cpp b/targettests/execution/qspan_slices.cpp index d91cb17e513..74371cf8a32 100644 --- a/targettests/execution/qspan_slices.cpp +++ b/targettests/execution/qspan_slices.cpp @@ -13,6 +13,7 @@ // RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // Tests for --disable-qubit-mapping: // RUN: nvq++ -v %s -o %t --target oqc --emulate --disable-qubit-mapping && CUDAQ_MLIR_PRINT_EACH_PASS=1 %t |& FileCheck --check-prefix=DISABLE %s diff --git a/targettests/execution/state_preparation.cpp b/targettests/execution/state_preparation.cpp index 74632d76903..5ccad9019bf 100644 --- a/targettests/execution/state_preparation.cpp +++ b/targettests/execution/state_preparation.cpp @@ -17,6 +17,7 @@ // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi #include diff --git a/targettests/execution/state_preparation_vector.cpp b/targettests/execution/state_preparation_vector.cpp index 4c1092023c3..1f958cb85db 100644 --- a/targettests/execution/state_preparation_vector.cpp +++ b/targettests/execution/state_preparation_vector.cpp @@ -15,6 +15,7 @@ // RUN: nvq++ %cpp_std -target quantinuum -emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std -target ionq -emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std -target oqc -emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std -target qbraid -emulate %s -o %t && %t | FileCheck %s // 2 different IQM machines for 2 different topologies // RUN: nvq++ %cpp_std -target iqm --iqm-machine Adonis -emulate %s -o %t && %t | FileCheck %s diff --git a/targettests/execution/state_preparation_vector_sizes.cpp b/targettests/execution/state_preparation_vector_sizes.cpp index ed2eff1e5a6..d894478c350 100644 --- a/targettests/execution/state_preparation_vector_sizes.cpp +++ b/targettests/execution/state_preparation_vector_sizes.cpp @@ -16,6 +16,7 @@ // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi #include diff --git a/targettests/execution/sudoku_2x2-1.cpp b/targettests/execution/sudoku_2x2-1.cpp index 902790bf91b..8d88a3ed3ce 100644 --- a/targettests/execution/sudoku_2x2-1.cpp +++ b/targettests/execution/sudoku_2x2-1.cpp @@ -13,6 +13,7 @@ // RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // clang-format on diff --git a/targettests/execution/sudoku_2x2-bit_names.cpp b/targettests/execution/sudoku_2x2-bit_names.cpp index 415535b72fb..756e77fc973 100644 --- a/targettests/execution/sudoku_2x2-bit_names.cpp +++ b/targettests/execution/sudoku_2x2-bit_names.cpp @@ -13,6 +13,7 @@ // RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // clang-format on diff --git a/targettests/execution/sudoku_2x2-reg_name.cpp b/targettests/execution/sudoku_2x2-reg_name.cpp index 31db334acfb..e562e553205 100644 --- a/targettests/execution/sudoku_2x2-reg_name.cpp +++ b/targettests/execution/sudoku_2x2-reg_name.cpp @@ -13,6 +13,7 @@ // RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // clang-format on diff --git a/targettests/execution/sudoku_2x2.cpp b/targettests/execution/sudoku_2x2.cpp index db1fe3a87a0..cd5d42ada5f 100644 --- a/targettests/execution/sudoku_2x2.cpp +++ b/targettests/execution/sudoku_2x2.cpp @@ -13,6 +13,7 @@ // RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // clang-format on diff --git a/targettests/execution/swap_gate.cpp b/targettests/execution/swap_gate.cpp index c9ef30ad232..c2d0d37fc36 100644 --- a/targettests/execution/swap_gate.cpp +++ b/targettests/execution/swap_gate.cpp @@ -12,6 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t && %t | FileCheck %s diff --git a/targettests/execution/uccsd.cpp b/targettests/execution/uccsd.cpp index e692d1c1b71..e4a38508cf3 100644 --- a/targettests/execution/uccsd.cpp +++ b/targettests/execution/uccsd.cpp @@ -12,6 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --enable-mlir %s -o %t // clang-format on diff --git a/targettests/execution/variable_size_qreg.cpp b/targettests/execution/variable_size_qreg.cpp index 9db7b9b8001..c5cfade8e8d 100644 --- a/targettests/execution/variable_size_qreg.cpp +++ b/targettests/execution/variable_size_qreg.cpp @@ -12,6 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/lit.cfg.py b/targettests/lit.cfg.py index 017514732df..69bd476f063 100644 --- a/targettests/lit.cfg.py +++ b/targettests/lit.cfg.py @@ -24,7 +24,7 @@ # Exclude a list of directories from the test suite: # - 'Inputs' contain auxiliary inputs for various tests. -local_excludes = ['anyon', 'ionq', 'iqm', 'oqc', 'quantinuum', 'fermioniq', 'infleqtion', +local_excludes = ['anyon', 'ionq', 'iqm', 'oqc', 'quantinuum', 'fermioniq', 'qbraid', 'infleqtion', 'Inputs', 'CMakeLists.txt', 'README.txt', 'LICENSE.txt'] config.excludes = [exclude for exclude in config.excludes] + local_excludes diff --git a/targettests/qbraid/bug_qubit.cpp b/targettests/qbraid/bug_qubit.cpp new file mode 100644 index 00000000000..2179c9f4da1 --- /dev/null +++ b/targettests/qbraid/bug_qubit.cpp @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// This code is from Issue 251. + +// clang-format off +// RUN: nvq++ %cpp_std --target anyon --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target infleqtion --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s +// RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi +// RUN: nvq++ -std=c++17 --enable-mlir %s -o %t +// RUN: cudaq-quake %cpp_std %s | cudaq-opt --promote-qubit-allocation | FileCheck --check-prefixes=MLIR %s + +#include +#include + +struct simple_x { + void operator()() __qpu__ { + cudaq::qubit q; + x(q); + mz(q); + } +}; + +// MLIR-LABEL: func.func @__nvqpp__mlirgen__simple_x() +// MLIR-NOT: quake.alloca !quake.ref +// MLIR: %[[VAL_0:.*]] = quake.alloca !quake.veq<1> +// MLIR-NEXT: %[[VAL_1:.*]] = quake.extract_ref %[[VAL_0]][0] : (!quake.veq<1>) -> !quake.ref + +int main() { + auto result = cudaq::sample(simple_x{}); + +#ifndef SYNTAX_CHECK + std::cout << result.most_probable() << '\n'; + assert("1" == result.most_probable()); +#endif + + return 0; +} + +// CHECK: 1 diff --git a/targettests/qbraid/callable_kernel_arg.cpp b/targettests/qbraid/callable_kernel_arg.cpp new file mode 100644 index 00000000000..c51db0dca15 --- /dev/null +++ b/targettests/qbraid/callable_kernel_arg.cpp @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// clang-format off +// RUN: nvq++ %cpp_std --target anyon --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target infleqtion --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s +// RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi +// RUN: nvq++ -std=c++17 --enable-mlir %s -o %t +// clang-format on + +#include +#include + +__qpu__ void bar(cudaq::qubit &q) { x(q); } + +struct baz { + __qpu__ void operator()(cudaq::qubit &q) { x(q); } +}; + +struct foo { + template + __qpu__ void operator()(CallableKernel &&func, int size) { + cudaq::qvector q(size); + func(q[0]); + auto result = mz(q[0]); + } +}; + +int main() { + auto result = cudaq::sample(1000, foo{}, baz{}, /*qreg size*/ 1); + +#ifndef SYNTAX_CHECK + std::cout << result.most_probable() << '\n'; + assert("1" == result.most_probable()); +#endif + + return 0; +} + +// CHECK: 1 diff --git a/targettests/qbraid/cudaq_observe.cpp b/targettests/qbraid/cudaq_observe.cpp new file mode 100644 index 00000000000..484054ed758 --- /dev/null +++ b/targettests/qbraid/cudaq_observe.cpp @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// REQUIRES: c++20 +// clang-format off +// RUN: nvq++ %cpp_std --target infleqtion --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s +// 2 different IQM machines for 2 different topologies +// RUN: nvq++ --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s +// RUN: if %braket_avail; then nvq++ --target braket --emulate %s -o %t && %t | FileCheck %s; fi +// clang-format on + +#include +#include + +// The example here shows a simple use case for the `cudaq::observe` +// function in computing expected values of provided spin_ops. + +struct ansatz { + auto operator()(double theta) __qpu__ { + cudaq::qvector q(2); + x(q[0]); + ry(theta, q[1]); + x(q[1], q[0]); + } +}; + +int main() { + + // Build up your spin op algebraically + using namespace cudaq::spin; + cudaq::spin_op h = 5.907 - 2.1433 * x(0) * x(1) - 2.1433 * y(0) * y(1) + + .21829 * z(0) - 6.125 * z(1); + + // Make repeatable for shots-based emulation + cudaq::set_random_seed(13); + + // Observe takes the kernel, the spin_op, and the concrete + // parameters for the kernel + double energy = cudaq::observe(ansatz{}, h, .59); + printf("Energy is %.16lf\n", energy); + return 0; +} + +// Note: seeds 2 and 12 will push this to -2 instead of -1. All other seeds in +// 1-100 range will be -1.x. + +// CHECK: Energy is -1. diff --git a/targettests/qbraid/if_jit.cpp b/targettests/qbraid/if_jit.cpp new file mode 100644 index 00000000000..2516f79fe62 --- /dev/null +++ b/targettests/qbraid/if_jit.cpp @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// This code is from Issue 296. + +// clang-format off +// RUN: nvq++ %cpp_std --target anyon --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target infleqtion --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s +// RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi +// RUN: nvq++ -std=c++17 --enable-mlir %s -o %t +// clang-format on + +#include +#include + +__qpu__ void foo(bool value) { + cudaq::qubit q; + if (value) + x(q); + + mz(q); +} + +int main() { + auto result = cudaq::sample(100, foo, true); + +#ifndef SYNTAX_CHECK + std::cout << result.most_probable() << '\n'; + assert("1" == result.most_probable()); +#endif + + return 0; +} + +// CHECK: 1 diff --git a/targettests/qbraid/load_value.cpp b/targettests/qbraid/load_value.cpp new file mode 100644 index 00000000000..19fecb53e11 --- /dev/null +++ b/targettests/qbraid/load_value.cpp @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// clang-format off +// RUN: nvq++ %cpp_std --target anyon --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target infleqtion --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s +// RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi +// RUN: nvq++ -std=c++17 --enable-mlir %s -o %t +// clang-format on + +#include +#include + +__qpu__ void load_value(unsigned value) { + cudaq::qvector qubits(4); + for (std::size_t i = 0; i < 4; ++i) { + // Doesn't work, even with: `if (value)` + if (value & (1 << i)) + x(qubits[3 - i]); + } + + mz(qubits); +} + +int main() { + for (auto i = 0; i < 16; ++i) { + auto result = cudaq::sample(1000, load_value, i); + +#ifndef SYNTAX_CHECK + std::cout << result.most_probable() << '\n'; + assert(i == std::stoi(result.most_probable(), nullptr, 2)); +#endif + } + return 0; +} + +// CHECK: 0000 +// CHECK-NEXT: 0001 +// CHECK-NEXT: 0010 +// CHECK-NEXT: 0011 +// CHECK-NEXT: 0100 +// CHECK-NEXT: 0101 +// CHECK-NEXT: 0110 +// CHECK-NEXT: 0111 +// CHECK-NEXT: 1000 +// CHECK-NEXT: 1001 +// CHECK-NEXT: 1010 +// CHECK-NEXT: 1011 +// CHECK-NEXT: 1100 +// CHECK-NEXT: 1101 +// CHECK-NEXT: 1110 +// CHECK-NEXT: 1111 diff --git a/targettests/qbraid/sudoku_2x2-1.cpp b/targettests/qbraid/sudoku_2x2-1.cpp new file mode 100644 index 00000000000..8d88a3ed3ce --- /dev/null +++ b/targettests/qbraid/sudoku_2x2-1.cpp @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// REQUIRES: c++20 +// clang-format off +// RUN: nvq++ --target anyon --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target infleqtion --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s +// clang-format on + +#include +#include +#include +#include + +__qpu__ void reflect_uniform(cudaq::qvector<> &qubits) { + h(qubits); + x(qubits); + z(qubits[0], qubits[1], qubits[2], qubits[3]); + x(qubits); + h(qubits); +} + +__qpu__ void oracle(cudaq::qvector<> &cs, cudaq::qubit &target) { + x(cs[0], !cs[1], !cs[2], cs[3], target); + x(!cs[0], cs[1], cs[2], !cs[3], target); +} + +__qpu__ void grover() { + cudaq::qvector qubits(4); + cudaq::qubit ancilla; + + // Initialization + x(ancilla); + h(ancilla); + h(qubits); // uniform initialization + + // Don't work?: + for (int i = 0; i < 2; ++i) { + oracle(qubits, ancilla); + reflect_uniform(qubits); + } + + mz(qubits); +}; + +int main() { + auto result = cudaq::sample(1000, grover); + +#ifndef SYNTAX_CHECK + std::vector strings; + for (auto &&[bits, count] : result) { + strings.push_back(bits); + } + std::sort(strings.begin(), strings.end(), [&](auto &a, auto &b) { + return result.count(a) > result.count(b); + }); + std::cout << strings[0] << '\n'; + std::cout << strings[1] << '\n'; + + std::unordered_set most_probable{strings[0], strings[1]}; + assert(most_probable.count("1001") == 1); + assert(most_probable.count("0110") == 1); +#endif + + return 0; +} + +// CHECK-DAG: 1001 +// CHECK-DAG: 0110 diff --git a/targettests/qbraid/sudoku_2x2-bit_names.cpp b/targettests/qbraid/sudoku_2x2-bit_names.cpp new file mode 100644 index 00000000000..756e77fc973 --- /dev/null +++ b/targettests/qbraid/sudoku_2x2-bit_names.cpp @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// REQUIRES: c++20 +// clang-format off +// RUN: nvq++ --target anyon --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target infleqtion --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s +// clang-format on + +#include +#include +#include +#include + +__qpu__ void reflect_uniform(cudaq::qvector<> &qubits) { + h(qubits); + x(qubits); + z(qubits[0], qubits[1], qubits[2], qubits[3]); + x(qubits); + h(qubits); +} + +__qpu__ void oracle(cudaq::qvector<> &cs, cudaq::qubit &target) { + x(cs[0], !cs[1], !cs[2], cs[3], target); + x(!cs[0], cs[1], cs[2], !cs[3], target); +} + +__qpu__ void grover() { + cudaq::qvector qubits(4); + cudaq::qubit ancilla; + + // Initialization + x(ancilla); + h(ancilla); + h(qubits); // uniform initialization + + oracle(qubits, ancilla); + reflect_uniform(qubits); + oracle(qubits, ancilla); + reflect_uniform(qubits); + + auto groverQubits0 = mz(qubits[0]); + auto groverQubits1 = mz(qubits[1]); + auto groverQubits2 = mz(qubits[2]); + auto groverQubits3 = mz(qubits[3]); +}; + +int main() { + auto result = cudaq::sample(1000, grover); + result.dump(); + + auto& platform = cudaq::get_platform(); + if (platform.is_remote() || platform.is_emulated()) { + // Make sure that the get_marginal() results for the individual register names + // match the subset of the bits from the global register. + // Note that this will fail if you only compile this in library mode. + auto numBits = result.begin()->first.size(); + std::cout << "Checking " << numBits << " bits against global register\n"; + for (size_t b = 0; b < numBits; b++) { + auto regName = "groverQubits" + std::to_string(b); + auto valFromRegName = result.get_marginal({0}, regName); + auto valFromGlobal = result.get_marginal({b}); + if (valFromRegName.to_map() != valFromGlobal.to_map()) { + std::cout << "--- MISMATCH DETECTED in bit " << b << " ---\n"; + valFromRegName.dump(); + valFromGlobal.dump(); + // Mark test failure + assert(valFromRegName.to_map() == valFromGlobal.to_map()); + } + } + } + +#ifndef SYNTAX_CHECK + std::vector strings; + for (auto &&[bits, count] : result) { + strings.push_back(bits); + } + std::sort(strings.begin(), strings.end(), [&](auto& a, auto& b) { + return result.count(a) > result.count(b); + }); + std::cout << strings[0] << '\n'; + std::cout << strings[1] << '\n'; + + std::unordered_set most_probable{strings[0], strings[1]}; + assert(most_probable.count("1001") == 1); + assert(most_probable.count("0110") == 1); +#endif + + return 0; +} + +// CHECK-DAG: 1001 +// CHECK-DAG: 0110 diff --git a/targettests/qbraid/sudoku_2x2-reg_name.cpp b/targettests/qbraid/sudoku_2x2-reg_name.cpp new file mode 100644 index 00000000000..e562e553205 --- /dev/null +++ b/targettests/qbraid/sudoku_2x2-reg_name.cpp @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// REQUIRES: c++20 +// clang-format off +// RUN: nvq++ --target anyon --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target infleqtion --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s +// clang-format on + +#include +#include +#include +#include + +__qpu__ void reflect_uniform(cudaq::qvector<> &qubits) { + h(qubits); + x(qubits); + z(qubits[0], qubits[1], qubits[2], qubits[3]); + x(qubits); + h(qubits); +} + +__qpu__ void oracle(cudaq::qvector<> &cs, cudaq::qubit &target) { + x(cs[0], !cs[1], !cs[2], cs[3], target); + x(!cs[0], cs[1], cs[2], !cs[3], target); +} + +__qpu__ void grover() { + cudaq::qvector qubits(4); + cudaq::qubit ancilla; + + // Initialization + x(ancilla); + h(ancilla); + h(qubits); // uniform initialization + + oracle(qubits, ancilla); + reflect_uniform(qubits); + oracle(qubits, ancilla); + reflect_uniform(qubits); + + auto groverQubits = mz(qubits); +}; + +int main() { + auto result = cudaq::sample(1000, grover); + result.dump(); + +#ifndef SYNTAX_CHECK + std::vector strings; + for (auto &&[bits, count] : result) { + strings.push_back(bits); + } + std::sort(strings.begin(), strings.end(), [&](auto& a, auto& b) { + return result.count(a) > result.count(b); + }); + std::cout << strings[0] << '\n'; + std::cout << strings[1] << '\n'; + + std::unordered_set most_probable{strings[0], strings[1]}; + assert(most_probable.count("1001") == 1); + assert(most_probable.count("0110") == 1); +#endif + + return 0; +} + +// CHECK-DAG: 1001 +// CHECK-DAG: 0110 diff --git a/targettests/qbraid/sudoku_2x2.cpp b/targettests/qbraid/sudoku_2x2.cpp new file mode 100644 index 00000000000..cd5d42ada5f --- /dev/null +++ b/targettests/qbraid/sudoku_2x2.cpp @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// REQUIRES: c++20 +// clang-format off +// RUN: nvq++ --target anyon --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target infleqtion --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s +// clang-format on + +#include +#include +#include +#include + +__qpu__ void reflect_uniform(cudaq::qvector<> &qubits) { + h(qubits); + x(qubits); + z(qubits[0], qubits[1], qubits[2], qubits[3]); + x(qubits); + h(qubits); +} + +__qpu__ void oracle(cudaq::qvector<> &cs, cudaq::qubit &target) { + x(cs[0], !cs[1], !cs[2], cs[3], target); + x(!cs[0], cs[1], cs[2], !cs[3], target); +} + +__qpu__ void grover() { + cudaq::qvector qubits(4); + cudaq::qubit ancilla; + + // Initialization + x(ancilla); + h(ancilla); + h(qubits); // uniform initialization + + oracle(qubits, ancilla); + reflect_uniform(qubits); + oracle(qubits, ancilla); + reflect_uniform(qubits); + + mz(qubits); +}; + +int main() { + auto result = cudaq::sample(1000, grover); + +#ifndef SYNTAX_CHECK + std::vector strings; + for (auto &&[bits, count] : result) { + strings.push_back(bits); + } + std::sort(strings.begin(), strings.end(), [&](auto& a, auto& b) { + return result.count(a) > result.count(b); + }); + std::cout << strings[0] << '\n'; + std::cout << strings[1] << '\n'; + + std::unordered_set most_probable{strings[0], strings[1]}; + assert(most_probable.count("1001") == 1); + assert(most_probable.count("0110") == 1); +#endif + + return 0; +} + +// CHECK-DAG: 1001 +// CHECK-DAG: 0110 diff --git a/targettests/qbraid/swap_gate.cpp b/targettests/qbraid/swap_gate.cpp new file mode 100644 index 00000000000..c2d0d37fc36 --- /dev/null +++ b/targettests/qbraid/swap_gate.cpp @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// clang-format off +// RUN: nvq++ %cpp_std --target anyon --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target infleqtion --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s +// RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi +// RUN: nvq++ -std=c++17 --enable-mlir %s -o %t && %t | FileCheck %s + +#include "cudaq.h" +#include + +int main() { + + auto swapKernel = []() __qpu__ { + cudaq::qvector q(2); + x(q[0]); + swap(q[0], q[1]); + + mz(q); + }; + + auto counts = cudaq::sample(swapKernel); + +#ifndef SYNTAX_CHECK + std::cout << counts.most_probable() << '\n'; + assert("01" == counts.most_probable()); +#endif + + return 0; +} + +// CHECK: 01 diff --git a/targettests/qbraid/test-int8_t.cpp b/targettests/qbraid/test-int8_t.cpp new file mode 100644 index 00000000000..210e7e4f41e --- /dev/null +++ b/targettests/qbraid/test-int8_t.cpp @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// clang-format off +// RUN: nvq++ %cpp_std --target anyon --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target infleqtion --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s +// RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi +// RUN: nvq++ -std=c++17 --enable-mlir %s -o %t +// clang-format on + +#include +#include + +struct variable_qreg { + __qpu__ void operator()(std::uint8_t value) { + cudaq::qvector qubits(value); + + mz(qubits); + } +}; + +int main() { + for (auto i = 1; i < 5; ++i) { + auto result = cudaq::sample(1000, variable_qreg{}, i); + +#ifndef SYNTAX_CHECK + std::cout << result.most_probable() << '\n'; + assert(std::string(i, '0') == result.most_probable()); +#endif + } + + return 0; +} + +// CHECK: 0 +// CHECK: 00 +// CHECK: 000 +// CHECK: 0000 diff --git a/targettests/qbraid/test-int8_t_free_func.cpp b/targettests/qbraid/test-int8_t_free_func.cpp new file mode 100644 index 00000000000..b7f4ce2d2f0 --- /dev/null +++ b/targettests/qbraid/test-int8_t_free_func.cpp @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// clang-format off +// RUN: nvq++ %cpp_std --target anyon --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target infleqtion --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s +// RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi +// RUN: nvq++ -std=c++17 --enable-mlir %s -o %t +// clang-format on + +#include +#include + +__qpu__ void variable_qreg(std::uint8_t value) { + cudaq::qvector qubits(value); + + mz(qubits); +} + +int main() { + for (auto i = 1; i < 5; ++i) { + auto result = cudaq::sample(1000, variable_qreg, i); + +#ifndef SYNTAX_CHECK + std::cout << result.most_probable() << '\n'; + assert(std::string(i, '0') == result.most_probable()); +#endif + } + + return 0; +} + +// CHECK: 0 +// CHECK-NEXT: 00 +// CHECK-NEXT: 000 +// CHECK-NEXT: 0000 diff --git a/targettests/qbraid/variable_size_qreg.cpp b/targettests/qbraid/variable_size_qreg.cpp new file mode 100644 index 00000000000..c5cfade8e8d --- /dev/null +++ b/targettests/qbraid/variable_size_qreg.cpp @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// clang-format off +// RUN: nvq++ %cpp_std --target anyon --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target infleqtion --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s +// RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi +// RUN: nvq++ -std=c++17 --enable-mlir %s -o %t +// clang-format on + +#include +#include + +__qpu__ void variable_qreg(unsigned value) { + cudaq::qvector qubits(value); + + mz(qubits); +} + +int main() { + for (auto i = 1; i < 5; ++i) { + auto result = cudaq::sample(1000, variable_qreg, i); + +#ifndef SYNTAX_CHECK + std::cout << result.most_probable() << '\n'; + assert(std::string(i, '0') == result.most_probable()); +#endif + } + + return 0; +} + +// CHECK: 0 +// CHECK-NEXT: 00 +// CHECK-NEXT: 000 +// CHECK-NEXT: 0000 diff --git a/unittests/backends/CMakeLists.txt b/unittests/backends/CMakeLists.txt index c22c22333b6..1480af2aa92 100644 --- a/unittests/backends/CMakeLists.txt +++ b/unittests/backends/CMakeLists.txt @@ -14,6 +14,7 @@ if (OPENSSL_FOUND AND CUDAQ_ENABLE_PYTHON AND CUDAQ_TEST_MOCK_SERVERS) add_subdirectory(ionq) add_subdirectory(iqm) add_subdirectory(oqc) + add_subdirectory(qbraid) add_subdirectory(quantinuum) endif() add_subdirectory(qpp_observe) diff --git a/unittests/backends/qbraid/CMakeLists.txt b/unittests/backends/qbraid/CMakeLists.txt new file mode 100644 index 00000000000..11333c554fe --- /dev/null +++ b/unittests/backends/qbraid/CMakeLists.txt @@ -0,0 +1,28 @@ +# ============================================================================ # +# Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # + +add_executable(test_qbraid QbraidTester.cpp) +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) + target_link_options(test_qbraid PRIVATE -Wl,--no-as-needed) +endif() +target_compile_definitions(test_qbraid PRIVATE -DNVQIR_BACKEND_NAME=qbraid) +target_include_directories(test_qbraid PRIVATE ../..) +target_link_libraries(test_qbraid + PRIVATE fmt::fmt-header-only + cudaq-common + cudaq + cudaq-builder + cudaq-mlir-runtime + cudaq-rest-qpu + cudaq-spin + cudaq-platform-default + gtest_main) + + +configure_file("QbraidStartServerAndTest.sh.in" "${CMAKE_BINARY_DIR}/unittests/backends/qbraid/QbraidStartServerAndTest.sh" @ONLY) +add_test(NAME qbraid-tests COMMAND bash QbraidStartServerAndTest.sh WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/unittests/backends/qbraid/) diff --git a/unittests/backends/qbraid/QbraidStartServerAndTest.sh.in b/unittests/backends/qbraid/QbraidStartServerAndTest.sh.in new file mode 100644 index 00000000000..8ba8b822945 --- /dev/null +++ b/unittests/backends/qbraid/QbraidStartServerAndTest.sh.in @@ -0,0 +1,43 @@ +#!/bin/bash + +# ============================================================================ # +# Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # + +checkServerConnection() { + PYTHONPATH=@CMAKE_BINARY_DIR@/python @Python_EXECUTABLE@ - << EOF +import socket +try: + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect(("localhost", 62449)) + s.close() +except Exception: + exit(1) +EOF +} + +# Launch the fake server +PYTHONPATH=@CMAKE_BINARY_DIR@/python @Python_EXECUTABLE@ @CMAKE_SOURCE_DIR@/utils/mock_qpu/qbraid/__init__.py & +# we'll need the process id to kill it +pid=$(echo "$!") +n=0 +while ! checkServerConnection; do + sleep 1 + n=$((n+1)) + if [ "$n" -eq "10" ]; then + kill -INT $pid + exit 99 + fi +done +# Run the tests +./test_qbraid +# Did they fail? +testsPassed=$? +# kill the server +kill -INT $pid +# return success / failure +exit $testsPassed diff --git a/unittests/backends/qbraid/QbraidTester.cpp b/unittests/backends/qbraid/QbraidTester.cpp new file mode 100644 index 00000000000..3190320bca0 --- /dev/null +++ b/unittests/backends/qbraid/QbraidTester.cpp @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +//Not worked on yet for Qbraid. Just serves as a placeholder!!! + +#include "CUDAQTestUtils.h" +#include "common/FmtCore.h" +#include "cudaq/algorithm.h" +#include +#include +#include + +std::string mockPort = "62449"; +std::string backendStringTemplate = + "qbraid;emulate;false;url;http://localhost:{}"; + +bool isValidExpVal(double value) { + // give us some wiggle room while keep the tests fast + return value < -1.1 && value > -2.3; +} + +CUDAQ_TEST(QbraidTester, checkSampleSync) { + auto backendString = + fmt::format(fmt::runtime(backendStringTemplate), mockPort); + + auto &platform = cudaq::get_platform(); + platform.setTargetBackend(backendString); + + auto kernel = cudaq::make_kernel(); + auto qubit = kernel.qalloc(2); + kernel.h(qubit[0]); + kernel.mz(qubit[0]); + + auto counts = cudaq::sample(kernel); + counts.dump(); + EXPECT_EQ(counts.size(), 2); +} + +CUDAQ_TEST(QbraidTester, checkSampleAsync) { + auto backendString = + fmt::format(fmt::runtime(backendStringTemplate), mockPort); + + auto &platform = cudaq::get_platform(); + platform.setTargetBackend(backendString); + + auto kernel = cudaq::make_kernel(); + auto qubit = kernel.qalloc(2); + kernel.h(qubit[0]); + kernel.mz(qubit[0]); + + auto future = cudaq::sample_async(kernel); + auto counts = future.get(); + EXPECT_EQ(counts.size(), 2); +} + +CUDAQ_TEST(QbraidTester, checkSampleAsyncLoadFromFile) { + auto backendString = + fmt::format(fmt::runtime(backendStringTemplate), mockPort); + + auto &platform = cudaq::get_platform(); + platform.setTargetBackend(backendString); + + auto kernel = cudaq::make_kernel(); + auto qubit = kernel.qalloc(2); + kernel.h(qubit[0]); + kernel.mz(qubit[0]); + + // Can sample asynchronously and get a future + auto future = cudaq::sample_async(kernel); + + // Future can be persisted for later + { + std::ofstream out("saveMe.json"); + out << future; + } + + // Later you can come back and read it in + cudaq::async_result readIn; + std::ifstream in("saveMe.json"); + in >> readIn; + + // Get the results of the read in future. + auto counts = readIn.get(); + EXPECT_EQ(counts.size(), 2); + + std::remove("saveMe.json"); +} + +CUDAQ_TEST(QbraidTester, checkObserveSync) { + auto backendString = + fmt::format(fmt::runtime(backendStringTemplate), mockPort); + + auto &platform = cudaq::get_platform(); + platform.setTargetBackend(backendString); + + auto [kernel, theta] = cudaq::make_kernel(); + auto qubit = kernel.qalloc(2); + kernel.x(qubit[0]); + kernel.ry(theta, qubit[1]); + kernel.x(qubit[1], qubit[0]); + + using namespace cudaq::spin; + cudaq::spin_op h = 5.907 - 2.1433 * x(0) * x(1) - 2.1433 * y(0) * y(1) + + .21829 * z(0) - 6.125 * z(1); + auto result = cudaq::observe(kernel, h, .59); + result.dump(); + + printf("ENERGY: %lf\n", result.expectation()); + EXPECT_TRUE(isValidExpVal(result.expectation())); +} + +CUDAQ_TEST(QbraidTester, checkObserveAsync) { + auto backendString = + fmt::format(fmt::runtime(backendStringTemplate), mockPort); + + auto &platform = cudaq::get_platform(); + platform.setTargetBackend(backendString); + + auto [kernel, theta] = cudaq::make_kernel(); + auto qubit = kernel.qalloc(2); + kernel.x(qubit[0]); + kernel.ry(theta, qubit[1]); + kernel.x(qubit[1], qubit[0]); + + using namespace cudaq::spin; + cudaq::spin_op h = 5.907 - 2.1433 * x(0) * x(1) - 2.1433 * y(0) * y(1) + + .21829 * z(0) - 6.125 * z(1); + auto future = cudaq::observe_async(kernel, h, .59); + + auto result = future.get(); + result.dump(); + + printf("ENERGY: %lf\n", result.expectation()); + EXPECT_TRUE(isValidExpVal(result.expectation())); +} + +CUDAQ_TEST(QbraidTester, checkObserveAsyncLoadFromFile) { + auto backendString = + fmt::format(fmt::runtime(backendStringTemplate), mockPort); + + auto &platform = cudaq::get_platform(); + platform.setTargetBackend(backendString); + + auto [kernel, theta] = cudaq::make_kernel(); + auto qubit = kernel.qalloc(2); + kernel.x(qubit[0]); + kernel.ry(theta, qubit[1]); + kernel.x(qubit[1], qubit[0]); + + using namespace cudaq::spin; + cudaq::spin_op h = 5.907 - 2.1433 * x(0) * x(1) - 2.1433 * y(0) * y(1) + + .21829 * z(0) - 6.125 * z(1); + auto future = cudaq::observe_async(kernel, h, .59); + + { + std::ofstream out("saveMeObserve.json"); + out << future; + } + + // Later you can come back and read it in + cudaq::async_result readIn(&h); + std::ifstream in("saveMeObserve.json"); + in >> readIn; + + // Get the results of the read in future. + auto result = readIn.get(); + + std::remove("saveMeObserve.json"); + result.dump(); + + printf("ENERGY: %lf\n", result.expectation()); + EXPECT_TRUE(isValidExpVal(result.expectation())); +} + +int main(int argc, char **argv) { + setenv("QBRAID_API_KEY", "00000000000000000000000000000000", 0); + ::testing::InitGoogleTest(&argc, argv); + auto ret = RUN_ALL_TESTS(); + return ret; +} diff --git a/utils/mock_qpu/__init__.py b/utils/mock_qpu/__init__.py index 1605e27712c..a13812043d9 100644 --- a/utils/mock_qpu/__init__.py +++ b/utils/mock_qpu/__init__.py @@ -11,4 +11,5 @@ from .infleqtion import * from .ionq import * from .iqm import * +from .qbraid import * from .quantinuum import * diff --git a/utils/mock_qpu/qbraid/__init__.py b/utils/mock_qpu/qbraid/__init__.py new file mode 100644 index 00000000000..a1f836d7f07 --- /dev/null +++ b/utils/mock_qpu/qbraid/__init__.py @@ -0,0 +1,176 @@ +# ============================================================================ # +# Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # + +#Also not worked on for qbraid. Just a placeholder (IonQ)! + +import cudaq +from fastapi import FastAPI, HTTPException, Header +from typing import Union +import uvicorn, uuid, base64, ctypes +from pydantic import BaseModel +from llvmlite import binding as llvm + +# Define the REST Server App +app = FastAPI() + + +class Input(BaseModel): + format: str + data: str + + +# Jobs look like the following type +class Job(BaseModel): + target: str + qubits: str + shots: int + input: Input + + +# Keep track of Job Ids to their Names +createdJobs = {} + +# Could how many times the client has requested the Job +countJobGetRequests = 0 + +# Save how many qubits were needed for each test (emulates real backend) +numQubitsRequired = 0 + +llvm.initialize() +llvm.initialize_native_target() +llvm.initialize_native_asmprinter() +target = llvm.Target.from_default_triple() +targetMachine = target.create_target_machine() +backing_mod = llvm.parse_assembly("") +engine = llvm.create_mcjit_compiler(backing_mod, targetMachine) + + +def getKernelFunction(module): + for f in module.functions: + if not f.is_declaration: + return f + return None + + +def getNumRequiredQubits(function): + for a in function.attributes: + if "requiredQubits" in str(a): + return int( + str(a).split("requiredQubits\"=")[-1].split(" ")[0].replace( + "\"", "").replace("'", "")) + + +# Here we test that the login endpoint works +@app.post("/login") +async def login(token: Union[str, None] = Header(alias="Authorization", + default=None)): + if token == None: + raise HTTPException(status_code(401), detail="Credentials not provided") + return {"id-token": "hello", "refresh-token": "refreshToken"} + + +# Here we expose a way to post jobs, +# Must have a Access Token, Job Program must be Adaptive Profile +# with entry_point tag +@app.post("/v0.3/jobs") +async def postJob(job: Job, + token: Union[str, None] = Header(alias="Authorization", + default=None)): + global createdJobs, shots, numQubitsRequired + + if token == None: + raise HTTPException(status_code(401), detail="Credentials not provided") + + print('Posting job with shots = ', job.shots) + newId = str(uuid.uuid4()) + shots = job.shots + program = job.input.data + decoded = base64.b64decode(program) + m = llvm.module.parse_bitcode(decoded) + mstr = str(m) + assert ('entry_point' in mstr) + + # Get the function, number of qubits, and kernel name + function = getKernelFunction(m) + if function == None: + raise Exception("Could not find kernel function") + numQubitsRequired = getNumRequiredQubits(function) + kernelFunctionName = function.name + + print("Kernel name = ", kernelFunctionName) + print("Requires {} qubits".format(numQubitsRequired)) + + # JIT Compile and get Function Pointer + engine.add_module(m) + engine.finalize_object() + engine.run_static_constructors() + funcPtr = engine.get_function_address(kernelFunctionName) + kernel = ctypes.CFUNCTYPE(None)(funcPtr) + + # Invoke the Kernel + cudaq.testing.toggleDynamicQubitManagement() + qubits, context = cudaq.testing.initialize(numQubitsRequired, job.shots) + kernel() + results = cudaq.testing.finalize(qubits, context) + results.dump() + createdJobs[newId] = results + + engine.remove_module(m) + + # Job "created", return the id + return {"id": newId, "jobs": {"status": "running"}} + + +# Retrieve the job, simulate having to wait by counting to 3 +# until we return the job results +@app.get("/v0.3/jobs") +async def getJob(id: str): + global countJobGetRequests, createdJobs, numQubitsRequired + + # Simulate asynchronous execution + if countJobGetRequests < 3: + countJobGetRequests += 1 + return {"jobs": [{"status": "running"}]} + + countJobGetRequests = 0 + res = { + "jobs": [{ + "status": "completed", + "qubits": numQubitsRequired, + "results_url": "/v0.3/jobs/{}/results".format(id) + }] + } + return res + + +@app.get("/v0.3/jobs/{jobId}/results") +async def getResults(jobId: str): + global countJobGetRequests, createdJobs + + counts = createdJobs[jobId] + counts.dump() + retData = {} + N = 0 + for bits, count in counts.items(): + N += count + # Note, the real IonQ backend reverses the bitstring relative to what the + # simulator does, so flip the bitstring with [::-1]. Also convert + # to decimal to match the real IonQ backend. + for bits, count in counts.items(): + retData[str(int(bits[::-1], 2))] = float(count / N) + + res = retData + return res + + +def startServer(port): + uvicorn.run(app, port=port, host='0.0.0.0', log_level="info") + + +if __name__ == '__main__': + startServer(62449) From aae7cc1affbcdea476253035650b50d088cc94e9 Mon Sep 17 00:00:00 2001 From: feelerx Date: Thu, 20 Mar 2025 16:05:58 +0000 Subject: [PATCH 03/11] fix emulate command alignment --- targettests/execution/callable_kernel_arg.cpp | 2 +- targettests/execution/cudaq_observe-cpp17.cpp | 2 +- targettests/execution/cudaq_observe.cpp | 2 +- targettests/execution/custom_operation_adj.cpp | 2 +- targettests/execution/custom_operation_basic.cpp | 2 +- targettests/execution/custom_operation_ctrl.cpp | 2 +- targettests/execution/if_jit.cpp | 2 +- targettests/execution/int8_t.cpp | 2 +- targettests/execution/int8_t_free_func.cpp | 2 +- targettests/execution/load_value.cpp | 2 +- targettests/execution/qspan_slices.cpp | 2 +- targettests/execution/state_preparation.cpp | 2 +- targettests/execution/state_preparation_vector_sizes.cpp | 2 +- targettests/execution/sudoku_2x2-1.cpp | 2 +- targettests/execution/sudoku_2x2-bit_names.cpp | 2 +- targettests/execution/sudoku_2x2-reg_name.cpp | 2 +- targettests/execution/sudoku_2x2.cpp | 2 +- targettests/execution/swap_gate.cpp | 2 +- targettests/execution/variable_size_qreg.cpp | 2 +- targettests/qbraid/callable_kernel_arg.cpp | 2 +- targettests/qbraid/cudaq_observe.cpp | 2 +- targettests/qbraid/if_jit.cpp | 2 +- targettests/qbraid/load_value.cpp | 2 +- targettests/qbraid/sudoku_2x2-1.cpp | 2 +- targettests/qbraid/sudoku_2x2-bit_names.cpp | 2 +- targettests/qbraid/sudoku_2x2-reg_name.cpp | 2 +- targettests/qbraid/sudoku_2x2.cpp | 2 +- targettests/qbraid/swap_gate.cpp | 2 +- targettests/qbraid/test-int8_t.cpp | 2 +- targettests/qbraid/test-int8_t_free_func.cpp | 2 +- targettests/qbraid/variable_size_qreg.cpp | 2 +- 31 files changed, 31 insertions(+), 31 deletions(-) diff --git a/targettests/execution/callable_kernel_arg.cpp b/targettests/execution/callable_kernel_arg.cpp index c51db0dca15..759469537e7 100644 --- a/targettests/execution/callable_kernel_arg.cpp +++ b/targettests/execution/callable_kernel_arg.cpp @@ -12,7 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/execution/cudaq_observe-cpp17.cpp b/targettests/execution/cudaq_observe-cpp17.cpp index a5b5b415695..2e8788ed213 100644 --- a/targettests/execution/cudaq_observe-cpp17.cpp +++ b/targettests/execution/cudaq_observe-cpp17.cpp @@ -14,7 +14,7 @@ // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // clang-format on diff --git a/targettests/execution/cudaq_observe.cpp b/targettests/execution/cudaq_observe.cpp index 484054ed758..d9d1c537d85 100644 --- a/targettests/execution/cudaq_observe.cpp +++ b/targettests/execution/cudaq_observe.cpp @@ -14,7 +14,7 @@ // RUN: nvq++ --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ --target braket --emulate %s -o %t && %t | FileCheck %s; fi // clang-format on diff --git a/targettests/execution/custom_operation_adj.cpp b/targettests/execution/custom_operation_adj.cpp index ed537a104db..3ab1b4bb506 100644 --- a/targettests/execution/custom_operation_adj.cpp +++ b/targettests/execution/custom_operation_adj.cpp @@ -13,7 +13,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // clang-format on diff --git a/targettests/execution/custom_operation_basic.cpp b/targettests/execution/custom_operation_basic.cpp index 77064dff90f..52ad6194ccc 100644 --- a/targettests/execution/custom_operation_basic.cpp +++ b/targettests/execution/custom_operation_basic.cpp @@ -13,7 +13,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // clang-format on diff --git a/targettests/execution/custom_operation_ctrl.cpp b/targettests/execution/custom_operation_ctrl.cpp index 2af189a478b..fa9cb90e445 100644 --- a/targettests/execution/custom_operation_ctrl.cpp +++ b/targettests/execution/custom_operation_ctrl.cpp @@ -12,7 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // clang-format on diff --git a/targettests/execution/if_jit.cpp b/targettests/execution/if_jit.cpp index 2516f79fe62..5719dc5b770 100644 --- a/targettests/execution/if_jit.cpp +++ b/targettests/execution/if_jit.cpp @@ -14,7 +14,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/execution/int8_t.cpp b/targettests/execution/int8_t.cpp index 210e7e4f41e..7178f6c57bb 100644 --- a/targettests/execution/int8_t.cpp +++ b/targettests/execution/int8_t.cpp @@ -12,7 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/execution/int8_t_free_func.cpp b/targettests/execution/int8_t_free_func.cpp index b7f4ce2d2f0..ca9db25ec6c 100644 --- a/targettests/execution/int8_t_free_func.cpp +++ b/targettests/execution/int8_t_free_func.cpp @@ -12,7 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/execution/load_value.cpp b/targettests/execution/load_value.cpp index 19fecb53e11..ab5d9cec62e 100644 --- a/targettests/execution/load_value.cpp +++ b/targettests/execution/load_value.cpp @@ -13,7 +13,7 @@ // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/execution/qspan_slices.cpp b/targettests/execution/qspan_slices.cpp index 74371cf8a32..57f3b6683c9 100644 --- a/targettests/execution/qspan_slices.cpp +++ b/targettests/execution/qspan_slices.cpp @@ -13,7 +13,7 @@ // RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // Tests for --disable-qubit-mapping: // RUN: nvq++ -v %s -o %t --target oqc --emulate --disable-qubit-mapping && CUDAQ_MLIR_PRINT_EACH_PASS=1 %t |& FileCheck --check-prefix=DISABLE %s diff --git a/targettests/execution/state_preparation.cpp b/targettests/execution/state_preparation.cpp index 5ccad9019bf..faafc4fe70e 100644 --- a/targettests/execution/state_preparation.cpp +++ b/targettests/execution/state_preparation.cpp @@ -17,7 +17,7 @@ // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi #include diff --git a/targettests/execution/state_preparation_vector_sizes.cpp b/targettests/execution/state_preparation_vector_sizes.cpp index d894478c350..2f20de714ff 100644 --- a/targettests/execution/state_preparation_vector_sizes.cpp +++ b/targettests/execution/state_preparation_vector_sizes.cpp @@ -16,7 +16,7 @@ // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi #include diff --git a/targettests/execution/sudoku_2x2-1.cpp b/targettests/execution/sudoku_2x2-1.cpp index 8d88a3ed3ce..cd028025a0c 100644 --- a/targettests/execution/sudoku_2x2-1.cpp +++ b/targettests/execution/sudoku_2x2-1.cpp @@ -13,7 +13,7 @@ // RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // clang-format on diff --git a/targettests/execution/sudoku_2x2-bit_names.cpp b/targettests/execution/sudoku_2x2-bit_names.cpp index 756e77fc973..ef53021b359 100644 --- a/targettests/execution/sudoku_2x2-bit_names.cpp +++ b/targettests/execution/sudoku_2x2-bit_names.cpp @@ -13,7 +13,7 @@ // RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // clang-format on diff --git a/targettests/execution/sudoku_2x2-reg_name.cpp b/targettests/execution/sudoku_2x2-reg_name.cpp index e562e553205..6200c1070f7 100644 --- a/targettests/execution/sudoku_2x2-reg_name.cpp +++ b/targettests/execution/sudoku_2x2-reg_name.cpp @@ -13,7 +13,7 @@ // RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // clang-format on diff --git a/targettests/execution/sudoku_2x2.cpp b/targettests/execution/sudoku_2x2.cpp index cd5d42ada5f..e3d4bc2c0c3 100644 --- a/targettests/execution/sudoku_2x2.cpp +++ b/targettests/execution/sudoku_2x2.cpp @@ -13,7 +13,7 @@ // RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // clang-format on diff --git a/targettests/execution/swap_gate.cpp b/targettests/execution/swap_gate.cpp index c2d0d37fc36..4f37edae871 100644 --- a/targettests/execution/swap_gate.cpp +++ b/targettests/execution/swap_gate.cpp @@ -12,7 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t && %t | FileCheck %s diff --git a/targettests/execution/variable_size_qreg.cpp b/targettests/execution/variable_size_qreg.cpp index c5cfade8e8d..1f6c139a085 100644 --- a/targettests/execution/variable_size_qreg.cpp +++ b/targettests/execution/variable_size_qreg.cpp @@ -12,7 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/qbraid/callable_kernel_arg.cpp b/targettests/qbraid/callable_kernel_arg.cpp index c51db0dca15..759469537e7 100644 --- a/targettests/qbraid/callable_kernel_arg.cpp +++ b/targettests/qbraid/callable_kernel_arg.cpp @@ -12,7 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/qbraid/cudaq_observe.cpp b/targettests/qbraid/cudaq_observe.cpp index 484054ed758..d9d1c537d85 100644 --- a/targettests/qbraid/cudaq_observe.cpp +++ b/targettests/qbraid/cudaq_observe.cpp @@ -14,7 +14,7 @@ // RUN: nvq++ --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ --target braket --emulate %s -o %t && %t | FileCheck %s; fi // clang-format on diff --git a/targettests/qbraid/if_jit.cpp b/targettests/qbraid/if_jit.cpp index 2516f79fe62..5719dc5b770 100644 --- a/targettests/qbraid/if_jit.cpp +++ b/targettests/qbraid/if_jit.cpp @@ -14,7 +14,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/qbraid/load_value.cpp b/targettests/qbraid/load_value.cpp index 19fecb53e11..ab5d9cec62e 100644 --- a/targettests/qbraid/load_value.cpp +++ b/targettests/qbraid/load_value.cpp @@ -13,7 +13,7 @@ // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/qbraid/sudoku_2x2-1.cpp b/targettests/qbraid/sudoku_2x2-1.cpp index 8d88a3ed3ce..cd028025a0c 100644 --- a/targettests/qbraid/sudoku_2x2-1.cpp +++ b/targettests/qbraid/sudoku_2x2-1.cpp @@ -13,7 +13,7 @@ // RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // clang-format on diff --git a/targettests/qbraid/sudoku_2x2-bit_names.cpp b/targettests/qbraid/sudoku_2x2-bit_names.cpp index 756e77fc973..ef53021b359 100644 --- a/targettests/qbraid/sudoku_2x2-bit_names.cpp +++ b/targettests/qbraid/sudoku_2x2-bit_names.cpp @@ -13,7 +13,7 @@ // RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // clang-format on diff --git a/targettests/qbraid/sudoku_2x2-reg_name.cpp b/targettests/qbraid/sudoku_2x2-reg_name.cpp index e562e553205..6200c1070f7 100644 --- a/targettests/qbraid/sudoku_2x2-reg_name.cpp +++ b/targettests/qbraid/sudoku_2x2-reg_name.cpp @@ -13,7 +13,7 @@ // RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // clang-format on diff --git a/targettests/qbraid/sudoku_2x2.cpp b/targettests/qbraid/sudoku_2x2.cpp index cd5d42ada5f..e3d4bc2c0c3 100644 --- a/targettests/qbraid/sudoku_2x2.cpp +++ b/targettests/qbraid/sudoku_2x2.cpp @@ -13,7 +13,7 @@ // RUN: nvq++ --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ --target quantinuum --emulate %s -o %t && %t | FileCheck %s // clang-format on diff --git a/targettests/qbraid/swap_gate.cpp b/targettests/qbraid/swap_gate.cpp index c2d0d37fc36..4f37edae871 100644 --- a/targettests/qbraid/swap_gate.cpp +++ b/targettests/qbraid/swap_gate.cpp @@ -12,7 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t && %t | FileCheck %s diff --git a/targettests/qbraid/test-int8_t.cpp b/targettests/qbraid/test-int8_t.cpp index 210e7e4f41e..7178f6c57bb 100644 --- a/targettests/qbraid/test-int8_t.cpp +++ b/targettests/qbraid/test-int8_t.cpp @@ -12,7 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/qbraid/test-int8_t_free_func.cpp b/targettests/qbraid/test-int8_t_free_func.cpp index b7f4ce2d2f0..ca9db25ec6c 100644 --- a/targettests/qbraid/test-int8_t_free_func.cpp +++ b/targettests/qbraid/test-int8_t_free_func.cpp @@ -12,7 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t diff --git a/targettests/qbraid/variable_size_qreg.cpp b/targettests/qbraid/variable_size_qreg.cpp index c5cfade8e8d..1f6c139a085 100644 --- a/targettests/qbraid/variable_size_qreg.cpp +++ b/targettests/qbraid/variable_size_qreg.cpp @@ -12,7 +12,7 @@ // RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target qbraid --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s // RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s; fi // RUN: nvq++ -std=c++17 --enable-mlir %s -o %t From 2063d59a126a31556e3c9a9e122617149b04723c Mon Sep 17 00:00:00 2001 From: feelerx Date: Thu, 20 Mar 2025 18:05:24 +0000 Subject: [PATCH 04/11] update polling + format --- .../helpers/qbraid/QbraidServerHelper.cpp | 194 ++++++++++-------- 1 file changed, 109 insertions(+), 85 deletions(-) diff --git a/runtime/cudaq/platform/default/rest/helpers/qbraid/QbraidServerHelper.cpp b/runtime/cudaq/platform/default/rest/helpers/qbraid/QbraidServerHelper.cpp index a2df0793969..53925d097db 100644 --- a/runtime/cudaq/platform/default/rest/helpers/qbraid/QbraidServerHelper.cpp +++ b/runtime/cudaq/platform/default/rest/helpers/qbraid/QbraidServerHelper.cpp @@ -11,59 +11,65 @@ namespace cudaq { class QbraidServerHelper : public ServerHelper { - static constexpr const char *DEFAULT_URL = "https://api.qbraid.com/api"; + static constexpr const char *DEFAULT_URL = "http://192.168.20.159:8080/api"; static constexpr const char *DEFAULT_DEVICE = "ionq_simulator"; - static constexpr int DEFAULT_QUBITS = 29; + static constexpr int DEFAULT_QUBITS = 29; public: const std::string name() const override { return "qbraid"; } void initialize(BackendConfig config) override { cudaq::info("Initializing Qbraid Backend."); - + // Initialize required configuration - backendConfig.clear(); + backendConfig.clear(); backendConfig["url"] = getValueOrDefault(config, "url", DEFAULT_URL); - backendConfig["device_id"] = getValueOrDefault(config, "device_id", DEFAULT_DEVICE); + backendConfig["device_id"] = + getValueOrDefault(config, "device_id", DEFAULT_DEVICE); backendConfig["user_agent"] = "cudaq/" + std::string(cudaq::getVersion()); - backendConfig["qubits"] = std::to_string(DEFAULT_QUBITS); - + backendConfig["qubits"] = std::to_string(DEFAULT_QUBITS); + // Get API key from environment backendConfig["api_key"] = getEnvVar("QBRAID_API_KEY", "", true); - + // Job endpoints backendConfig["job_path"] = backendConfig["url"] + "/quantum-jobs"; // result endpoints - backendConfig["results_path"] = backendConfig["url"] + "/quantum-jobs/result/"; + backendConfig["results_path"] = + backendConfig["url"] + "/quantum-jobs/result/"; - // Add results output directory - backendConfig["results_output_dir"] = getValueOrDefault(config, "results_output_dir", "./qbraid_results"); - backendConfig["results_file_prefix"] = getValueOrDefault(config, "results_file_prefix", "qbraid_job_"); + // Add results output directory + backendConfig["results_output_dir"] = + getValueOrDefault(config, "results_output_dir", "./qbraid_results"); + backendConfig["results_file_prefix"] = + getValueOrDefault(config, "results_file_prefix", "qbraid_job_"); if (!config["shots"].empty()) { backendConfig["shots"] = config["shots"]; this->setShots(std::stoul(config["shots"])); } else { - backendConfig["shots"] = "1000"; + backendConfig["shots"] = "1000"; this->setShots(1000); } parseConfigForCommonParams(config); cudaq::info("Qbraid configuration initialized:"); - for (const auto& [key, value] : backendConfig) { + for (const auto &[key, value] : backendConfig) { cudaq::info(" {} = {}", key, value); } - + // Create results directory if it doesn't exist std::string resultsDir = backendConfig["results_output_dir"]; std::filesystem::create_directories(resultsDir); cudaq::info("Created results directory: {}", resultsDir); } - ServerJobPayload createJob(std::vector &circuitCodes) override { + ServerJobPayload + createJob(std::vector &circuitCodes) override { if (backendConfig.find("job_path") == backendConfig.end()) { - throw std::runtime_error("job_path not found in config. Was initialize() called?"); + throw std::runtime_error( + "job_path not found in config. Was initialize() called?"); } std::vector jobs; @@ -72,14 +78,13 @@ class QbraidServerHelper : public ServerHelper { job["qbraidDeviceId"] = backendConfig.at("device_id"); job["openQasm"] = circuitCode.code; job["shots"] = std::stoi(backendConfig.at("shots")); - job["circuitNumQubits"] = std::stoi(backendConfig.at("qubits")); - + if (!circuitCode.name.empty()) { nlohmann::json tags; tags["name"] = circuitCode.name; job["tags"] = tags; } - + jobs.push_back(job); } @@ -88,35 +93,36 @@ class QbraidServerHelper : public ServerHelper { std::string extractJobId(ServerMessage &postResponse) override { if (!postResponse.contains("qbraidJobId")) - throw std::runtime_error("ServerMessage doesn't contain 'qbraidJobId' key."); + throw std::runtime_error( + "ServerMessage doesn't contain 'qbraidJobId' key."); return postResponse.at("qbraidJobId"); } std::string constructGetJobPath(ServerMessage &postResponse) override { if (!postResponse.contains("qbraidJobId")) - throw std::runtime_error("ServerMessage doesn't contain 'qbraidJobId' key."); - return backendConfig.at("job_path") + "?qbraidJobId=" + - postResponse.at("qbraidJobId").get(); + throw std::runtime_error( + "ServerMessage doesn't contain 'qbraidJobId' key."); + return backendConfig.at("job_path") + + "?qbraidJobId=" + postResponse.at("qbraidJobId").get(); } std::string constructGetJobPath(std::string &jobId) override { return backendConfig.at("job_path") + "?qbraidJobId=" + jobId; } - // Getting results path + // Getting results path std::string constructGetResultsPath(const std::string &jobId) { return backendConfig.at("results_path") + jobId; } // Job is done with sample results api bool jobIsDone(ServerMessage &getJobResponse) override { - // Save the current job response to a file - saveResponseToFile(getJobResponse); - + std::string status; - - // Check if response has the jobsArray format - if (getJobResponse.contains("jobsArray") && !getJobResponse["jobsArray"].empty()) { + + // Check if response has the jobsArray format + if (getJobResponse.contains("jobsArray") && + !getJobResponse["jobsArray"].empty()) { status = getJobResponse["jobsArray"][0]["status"].get(); cudaq::info("Job status from jobs endpoint: {}", status); } @@ -126,102 +132,115 @@ class QbraidServerHelper : public ServerHelper { cudaq::info("Job status from direct response: {}", status); } // Or if it's in the data object - else if (getJobResponse.contains("data") && getJobResponse["data"].contains("status")) { + else if (getJobResponse.contains("data") && + getJobResponse["data"].contains("status")) { status = getJobResponse["data"]["status"].get(); cudaq::info("Job status from data object: {}", status); - } - else { + } else { cudaq::info("Unexpected job response format: {}", getJobResponse.dump()); throw std::runtime_error("Invalid job response format"); } - + if (status == "FAILED") - throw std::runtime_error("The job failed upon submission. Check your qBraid account for more information."); - + // Save job response to a file + saveResponseToFile(getJobResponse); + throw std::runtime_error("The job failed upon submission. Check your " + "qBraid account for more information."); + + // Save job response to a file + saveResponseToFile(getJobResponse); return status == "COMPLETED"; } // Sample results with results api - with retry logic cudaq::sample_result processResults(ServerMessage &getJobResponse, - std::string &jobId) override { + std::string &jobId) override { // Save the final job response to file - saveResponseToFile(getJobResponse, jobId + "_final"); - + // saveResponseToFile(getJobResponse, jobId + "_final"); + // Try to get results using the direct results endpoint with retries int maxRetries = 5; int waitTime = 2; float backoffFactor = 2.0; - + for (int attempt = 0; attempt < maxRetries; ++attempt) { try { auto resultsPath = constructGetResultsPath(jobId); auto headers = getHeaders(); - - cudaq::info("Fetching results using direct endpoint (attempt {}/{}): {}", - attempt + 1, maxRetries, resultsPath); + + cudaq::info( + "Fetching results using direct endpoint (attempt {}/{}): {}", + attempt + 1, maxRetries, resultsPath); RestClient client; auto resultJson = client.get("", resultsPath, headers, true); - + // Save direct results response to file - saveResponseToFile(resultJson, jobId + "_direct_results_" + std::to_string(attempt)); + // saveResponseToFile(resultJson, jobId + "_direct_results_" + + // std::to_string(attempt)); if (resultJson.contains("error") && !resultJson["error"].is_null()) { - std::string errorMsg = resultJson["error"].is_string() ? - resultJson["error"].get() : - resultJson["error"].dump(); + std::string errorMsg = resultJson["error"].is_string() + ? resultJson["error"].get() + : resultJson["error"].dump(); cudaq::info("Error from results endpoint: {}", errorMsg); - + // Only throw if on last attempt if (attempt == maxRetries - 1) { throw std::runtime_error("Error retrieving results: " + errorMsg); } - } - else if (resultJson.contains("data") && - resultJson["data"].contains("measurementCounts")) { + } else if (resultJson.contains("data") && + resultJson["data"].contains("measurementCounts")) { cudaq::info("Processing results from direct endpoint"); CountsDictionary counts; auto &measurements = resultJson["data"]["measurementCounts"]; - + for (const auto &[bitstring, count] : measurements.items()) { - counts[bitstring] = count.is_number() ? - static_cast(count.get()) : - static_cast(count); + counts[bitstring] = + count.is_number() + ? static_cast(count.get()) + : static_cast(count); } - + std::vector execResults; execResults.emplace_back(ExecutionResult{counts}); return cudaq::sample_result(execResults); } - + // If we get here, no valid data was found but also no error - retry if (attempt < maxRetries - 1) { - int sleepTime = waitTime * std::pow(backoffFactor, attempt); - cudaq::info("No valid results yet, retrying in {} seconds", sleepTime); + int sleepTime = (attempt == 0) + ? waitTime + : waitTime * std::pow(backoffFactor, attempt); + cudaq::info("No valid results yet, retrying in {} seconds", + sleepTime); std::this_thread::sleep_for(std::chrono::seconds(sleepTime)); } - + } catch (const std::exception &e) { - cudaq::info("Exception when using direct results endpoint: {}", e.what()); + cudaq::info("Exception when using direct results endpoint: {}", + e.what()); if (attempt < maxRetries - 1) { - int sleepTime = waitTime * std::pow(backoffFactor, attempt); + int sleepTime = (attempt == 0) + ? waitTime + : waitTime * std::pow(backoffFactor, attempt); cudaq::info("Retrying in {} seconds", sleepTime); std::this_thread::sleep_for(std::chrono::seconds(sleepTime)); - } - else { + } else { cudaq::info("Falling back to original results processing method"); } } } - + // Original result processing as fallback cudaq::info("Processing results from job response for job {}", jobId); - if (getJobResponse.contains("jobsArray") && !getJobResponse["jobsArray"].empty()) { + if (getJobResponse.contains("jobsArray") && + !getJobResponse["jobsArray"].empty()) { auto &job = getJobResponse["jobsArray"][0]; - + if (job.contains("measurementCounts")) { CountsDictionary counts; auto &measurements = job["measurementCounts"]; - + for (const auto &[bitstring, count] : measurements.items()) { counts[bitstring] = count.get(); } @@ -231,12 +250,12 @@ class QbraidServerHelper : public ServerHelper { return cudaq::sample_result(execResults); } } - + // Last resort - check for direct measurementCounts in the response if (getJobResponse.contains("measurementCounts")) { CountsDictionary counts; auto &measurements = getJobResponse["measurementCounts"]; - + for (const auto &[bitstring, count] : measurements.items()) { counts[bitstring] = count.get(); } @@ -245,38 +264,42 @@ class QbraidServerHelper : public ServerHelper { execResults.emplace_back(ExecutionResult{counts}); return cudaq::sample_result(execResults); } - - throw std::runtime_error("No measurement counts found in any response format"); + + throw std::runtime_error( + "No measurement counts found in any response format"); } private: // Method to save response to file - void saveResponseToFile(const ServerMessage &response, const std::string &identifier = "") { + void saveResponseToFile(const ServerMessage &response, + const std::string &identifier = "") { try { std::string outputDir = backendConfig.at("results_output_dir"); std::string filePrefix = backendConfig.at("results_file_prefix"); - + // Create a unique filename using timestamp if no identifier is provided std::string filename; if (identifier.empty()) { auto now = std::chrono::system_clock::now(); auto timestamp = std::chrono::duration_cast( - now.time_since_epoch()).count(); - filename = outputDir + "/" + filePrefix + std::to_string(timestamp) + ".json"; + now.time_since_epoch()) + .count(); + filename = + outputDir + "/" + filePrefix + std::to_string(timestamp) + ".json"; } else { filename = outputDir + "/" + filePrefix + identifier + ".json"; } - + // Write the JSON response to the file with proper formatting std::ofstream outputFile(filename); if (!outputFile.is_open()) { cudaq::info("Failed to open file for writing: {}", filename); return; } - + outputFile << response.dump(2); outputFile.close(); - + cudaq::info("Response saved to file: {}", filename); } catch (const std::exception &e) { cudaq::info("Error saving response to file: {}", e.what()); @@ -285,7 +308,8 @@ class QbraidServerHelper : public ServerHelper { RestHeaders getHeaders() override { if (backendConfig.find("api_key") == backendConfig.end()) { - throw std::runtime_error("API key not found in config. Was initialize() called?"); + throw std::runtime_error( + "API key not found in config. Was initialize() called?"); } RestHeaders headers; @@ -307,11 +331,11 @@ class QbraidServerHelper : public ServerHelper { } std::string getValueOrDefault(const BackendConfig &config, - const std::string &key, - const std::string &defaultValue) const { + const std::string &key, + const std::string &defaultValue) const { return config.find(key) != config.end() ? config.at(key) : defaultValue; } }; -} +} // namespace cudaq CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::QbraidServerHelper, qbraid) From d94320a25faa8be6a07c4d6ffe85c42ff2a67f4f Mon Sep 17 00:00:00 2001 From: feelerx Date: Thu, 20 Mar 2025 20:20:43 +0000 Subject: [PATCH 05/11] update polling interval and make code more readable --- .../helpers/qbraid/QbraidServerHelper.cpp | 134 ++++++------------ 1 file changed, 44 insertions(+), 90 deletions(-) diff --git a/runtime/cudaq/platform/default/rest/helpers/qbraid/QbraidServerHelper.cpp b/runtime/cudaq/platform/default/rest/helpers/qbraid/QbraidServerHelper.cpp index 53925d097db..5e2bf74787e 100644 --- a/runtime/cudaq/platform/default/rest/helpers/qbraid/QbraidServerHelper.cpp +++ b/runtime/cudaq/platform/default/rest/helpers/qbraid/QbraidServerHelper.cpp @@ -11,7 +11,7 @@ namespace cudaq { class QbraidServerHelper : public ServerHelper { - static constexpr const char *DEFAULT_URL = "http://192.168.20.159:8080/api"; + static constexpr const char *DEFAULT_URL = "https://api.qbraid.com/api"; static constexpr const char *DEFAULT_DEVICE = "ionq_simulator"; static constexpr int DEFAULT_QUBITS = 29; @@ -21,28 +21,18 @@ class QbraidServerHelper : public ServerHelper { void initialize(BackendConfig config) override { cudaq::info("Initializing Qbraid Backend."); - // Initialize required configuration backendConfig.clear(); backendConfig["url"] = getValueOrDefault(config, "url", DEFAULT_URL); - backendConfig["device_id"] = - getValueOrDefault(config, "device_id", DEFAULT_DEVICE); + backendConfig["device_id"] = getValueOrDefault(config, "device_id", DEFAULT_DEVICE); backendConfig["user_agent"] = "cudaq/" + std::string(cudaq::getVersion()); backendConfig["qubits"] = std::to_string(DEFAULT_QUBITS); - // Get API key from environment backendConfig["api_key"] = getEnvVar("QBRAID_API_KEY", "", true); - - // Job endpoints backendConfig["job_path"] = backendConfig["url"] + "/quantum-jobs"; - // result endpoints - backendConfig["results_path"] = - backendConfig["url"] + "/quantum-jobs/result/"; + backendConfig["results_path"] = backendConfig["url"] + "/quantum-jobs/result/"; - // Add results output directory - backendConfig["results_output_dir"] = - getValueOrDefault(config, "results_output_dir", "./qbraid_results"); - backendConfig["results_file_prefix"] = - getValueOrDefault(config, "results_file_prefix", "qbraid_job_"); + backendConfig["results_output_dir"] = getValueOrDefault(config, "results_output_dir", "./qbraid_results"); + backendConfig["results_file_prefix"] = getValueOrDefault(config, "results_file_prefix", "qbraid_job_"); if (!config["shots"].empty()) { backendConfig["shots"] = config["shots"]; @@ -59,7 +49,6 @@ class QbraidServerHelper : public ServerHelper { cudaq::info(" {} = {}", key, value); } - // Create results directory if it doesn't exist std::string resultsDir = backendConfig["results_output_dir"]; std::filesystem::create_directories(resultsDir); cudaq::info("Created results directory: {}", resultsDir); @@ -68,8 +57,7 @@ class QbraidServerHelper : public ServerHelper { ServerJobPayload createJob(std::vector &circuitCodes) override { if (backendConfig.find("job_path") == backendConfig.end()) { - throw std::runtime_error( - "job_path not found in config. Was initialize() called?"); + throw std::runtime_error("job_path not found in config. Was initialize() called?"); } std::vector jobs; @@ -92,48 +80,38 @@ class QbraidServerHelper : public ServerHelper { } std::string extractJobId(ServerMessage &postResponse) override { - if (!postResponse.contains("qbraidJobId")) - throw std::runtime_error( - "ServerMessage doesn't contain 'qbraidJobId' key."); + if (!postResponse.contains("qbraidJobId")) { + throw std::runtime_error("ServerMessage doesn't contain 'qbraidJobId' key."); + } return postResponse.at("qbraidJobId"); } std::string constructGetJobPath(ServerMessage &postResponse) override { - if (!postResponse.contains("qbraidJobId")) - throw std::runtime_error( - "ServerMessage doesn't contain 'qbraidJobId' key."); - return backendConfig.at("job_path") + - "?qbraidJobId=" + postResponse.at("qbraidJobId").get(); + if (!postResponse.contains("qbraidJobId")) { + throw std::runtime_error("ServerMessage doesn't contain 'qbraidJobId' key."); + } + + return backendConfig.at("job_path") + "?qbraidJobId=" + postResponse.at("qbraidJobId").get(); } std::string constructGetJobPath(std::string &jobId) override { return backendConfig.at("job_path") + "?qbraidJobId=" + jobId; } - // Getting results path std::string constructGetResultsPath(const std::string &jobId) { return backendConfig.at("results_path") + jobId; } - // Job is done with sample results api bool jobIsDone(ServerMessage &getJobResponse) override { - std::string status; - // Check if response has the jobsArray format - if (getJobResponse.contains("jobsArray") && - !getJobResponse["jobsArray"].empty()) { + if (getJobResponse.contains("jobsArray") && !getJobResponse["jobsArray"].empty()) { status = getJobResponse["jobsArray"][0]["status"].get(); cudaq::info("Job status from jobs endpoint: {}", status); - } - // Check if it uses the direct format - else if (getJobResponse.contains("status")) { + } else if (getJobResponse.contains("status")) { status = getJobResponse["status"].get(); cudaq::info("Job status from direct response: {}", status); - } - // Or if it's in the data object - else if (getJobResponse.contains("data") && - getJobResponse["data"].contains("status")) { + } else if (getJobResponse.contains("data") && getJobResponse["data"].contains("status")) { status = getJobResponse["data"]["status"].get(); cudaq::info("Job status from data object: {}", status); } else { @@ -141,24 +119,16 @@ class QbraidServerHelper : public ServerHelper { throw std::runtime_error("Invalid job response format"); } - if (status == "FAILED") - // Save job response to a file + if (status == "FAILED" || status == "COMPLETED" || status == "CANCELLED") { saveResponseToFile(getJobResponse); - throw std::runtime_error("The job failed upon submission. Check your " - "qBraid account for more information."); + return true; + } - // Save job response to a file - saveResponseToFile(getJobResponse); - return status == "COMPLETED"; + return false; } // Sample results with results api - with retry logic - cudaq::sample_result processResults(ServerMessage &getJobResponse, - std::string &jobId) override { - // Save the final job response to file - // saveResponseToFile(getJobResponse, jobId + "_final"); - - // Try to get results using the direct results endpoint with retries + cudaq::sample_result processResults(ServerMessage &getJobResponse, std::string &jobId) override { int maxRetries = 5; int waitTime = 2; float backoffFactor = 2.0; @@ -168,28 +138,20 @@ class QbraidServerHelper : public ServerHelper { auto resultsPath = constructGetResultsPath(jobId); auto headers = getHeaders(); - cudaq::info( - "Fetching results using direct endpoint (attempt {}/{}): {}", - attempt + 1, maxRetries, resultsPath); + cudaq::info("Fetching results using direct endpoint (attempt {}/{}): {}", attempt + 1, maxRetries, resultsPath); RestClient client; auto resultJson = client.get("", resultsPath, headers, true); - // Save direct results response to file - // saveResponseToFile(resultJson, jobId + "_direct_results_" + - // std::to_string(attempt)); - if (resultJson.contains("error") && !resultJson["error"].is_null()) { std::string errorMsg = resultJson["error"].is_string() ? resultJson["error"].get() : resultJson["error"].dump(); cudaq::info("Error from results endpoint: {}", errorMsg); - // Only throw if on last attempt if (attempt == maxRetries - 1) { throw std::runtime_error("Error retrieving results: " + errorMsg); } - } else if (resultJson.contains("data") && - resultJson["data"].contains("measurementCounts")) { + } else if (resultJson.contains("data") && resultJson["data"].contains("measurementCounts")) { cudaq::info("Processing results from direct endpoint"); CountsDictionary counts; auto &measurements = resultJson["data"]["measurementCounts"]; @@ -208,21 +170,15 @@ class QbraidServerHelper : public ServerHelper { // If we get here, no valid data was found but also no error - retry if (attempt < maxRetries - 1) { - int sleepTime = (attempt == 0) - ? waitTime - : waitTime * std::pow(backoffFactor, attempt); - cudaq::info("No valid results yet, retrying in {} seconds", - sleepTime); + int sleepTime = (attempt == 0) ? waitTime : waitTime * std::pow(backoffFactor, attempt); + cudaq::info("No valid results yet, retrying in {} seconds", sleepTime); std::this_thread::sleep_for(std::chrono::seconds(sleepTime)); } } catch (const std::exception &e) { - cudaq::info("Exception when using direct results endpoint: {}", - e.what()); + cudaq::info("Exception when using direct results endpoint: {}", e.what()); if (attempt < maxRetries - 1) { - int sleepTime = (attempt == 0) - ? waitTime - : waitTime * std::pow(backoffFactor, attempt); + int sleepTime = (attempt == 0) ? waitTime : waitTime * std::pow(backoffFactor, attempt); cudaq::info("Retrying in {} seconds", sleepTime); std::this_thread::sleep_for(std::chrono::seconds(sleepTime)); } else { @@ -233,8 +189,7 @@ class QbraidServerHelper : public ServerHelper { // Original result processing as fallback cudaq::info("Processing results from job response for job {}", jobId); - if (getJobResponse.contains("jobsArray") && - !getJobResponse["jobsArray"].empty()) { + if (getJobResponse.contains("jobsArray") && !getJobResponse["jobsArray"].empty()) { auto &job = getJobResponse["jobsArray"][0]; if (job.contains("measurementCounts")) { @@ -265,14 +220,17 @@ class QbraidServerHelper : public ServerHelper { return cudaq::sample_result(execResults); } - throw std::runtime_error( - "No measurement counts found in any response format"); + throw std::runtime_error("No measurement counts found in any response format"); + } + + /// @brief Override the polling interval method + std::chrono::microseconds + nextResultPollingInterval(ServerMessage &postResponse) override { + return std::chrono::seconds(1); } private: - // Method to save response to file - void saveResponseToFile(const ServerMessage &response, - const std::string &identifier = "") { + void saveResponseToFile(const ServerMessage &response, const std::string &identifier = "") { try { std::string outputDir = backendConfig.at("results_output_dir"); std::string filePrefix = backendConfig.at("results_file_prefix"); @@ -281,16 +239,12 @@ class QbraidServerHelper : public ServerHelper { std::string filename; if (identifier.empty()) { auto now = std::chrono::system_clock::now(); - auto timestamp = std::chrono::duration_cast( - now.time_since_epoch()) - .count(); - filename = - outputDir + "/" + filePrefix + std::to_string(timestamp) + ".json"; + auto timestamp = std::chrono::duration_cast(now.time_since_epoch()).count(); + filename = outputDir + "/" + filePrefix + std::to_string(timestamp) + ".json"; } else { filename = outputDir + "/" + filePrefix + identifier + ".json"; } - // Write the JSON response to the file with proper formatting std::ofstream outputFile(filename); if (!outputFile.is_open()) { cudaq::info("Failed to open file for writing: {}", filename); @@ -308,8 +262,7 @@ class QbraidServerHelper : public ServerHelper { RestHeaders getHeaders() override { if (backendConfig.find("api_key") == backendConfig.end()) { - throw std::runtime_error( - "API key not found in config. Was initialize() called?"); + throw std::runtime_error("API key not found in config. Was initialize() called?"); } RestHeaders headers; @@ -319,12 +272,13 @@ class QbraidServerHelper : public ServerHelper { return headers; } - std::string getEnvVar(const std::string &key, const std::string &defaultVal, - const bool isRequired) const { + std::string getEnvVar(const std::string &key, const std::string &defaultVal, const bool isRequired) const { const char *env_var = std::getenv(key.c_str()); if (env_var == nullptr) { - if (isRequired) + if (isRequired) { throw std::runtime_error(key + " environment variable is not set."); + } + return defaultVal; } return std::string(env_var); From e0e44ab5f5eeb4dc904b7cdbf90e36e498f0d754 Mon Sep 17 00:00:00 2001 From: feelerx Date: Thu, 20 Mar 2025 20:42:22 +0000 Subject: [PATCH 06/11] remove ionq fields from target-arguments --- .../default/rest/helpers/qbraid/qbraid.yml | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/runtime/cudaq/platform/default/rest/helpers/qbraid/qbraid.yml b/runtime/cudaq/platform/default/rest/helpers/qbraid/qbraid.yml index e62840d5f2d..5132a74d1a7 100644 --- a/runtime/cudaq/platform/default/rest/helpers/qbraid/qbraid.yml +++ b/runtime/cudaq/platform/default/rest/helpers/qbraid/qbraid.yml @@ -7,7 +7,7 @@ # ============================================================================ # name: qbraid -description: "CUDA-Q target for qbraid." +description: "CUDA-Q target for qBraid." config: # Tell DefaultQuantumPlatform what QPU subtype to use platform-qpu: remote_rest @@ -17,7 +17,7 @@ config: link-libs: ["-lcudaq-rest-qpu"] # Define the lowering pipeline platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg),canonicalize,func.func(multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,CCZToCX,CRyToCX,CRxToCX,R1AdjToR1,RxAdjToRx,RyAdjToRy,RzAdjToRz},quake-to-cc-prep,func.func(memtoreg{quantum=0}),symbol-dce" - # Tell the rest-qpu that we are generating QIR. + # Tell the rest-qpu that we are generating OpenQASM. codegen-emission: qasm2 # Library mode is only for simulators, physical backends must turn this off library-mode: false @@ -27,19 +27,4 @@ target-arguments: required: false type: string platform-arg: qpu - help-string: "Specify the qbraid QPU." - - key: noise-model - required: false - type: string - platform-arg: noise - help-string: "Specify the noise model for simulation." - - key: debias - required: false - type: string - platform-arg: debias - help-string: "Specify debiasing." - - key: sharpen - required: false - type: string - platform-arg: sharpen - help-string: "Specify sharpening." + help-string: "Specify the qBraid QPU." \ No newline at end of file From 15fee8c29459b24b01ea2de7c0270a5f04bf0b4f Mon Sep 17 00:00:00 2001 From: feelerx Date: Thu, 20 Mar 2025 20:55:33 +0000 Subject: [PATCH 07/11] fix formatting --- targettests/execution/exp_pauli.cpp | 2 +- targettests/execution/state_preparation_vector.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/targettests/execution/exp_pauli.cpp b/targettests/execution/exp_pauli.cpp index 7cab2844518..545302a56a4 100644 --- a/targettests/execution/exp_pauli.cpp +++ b/targettests/execution/exp_pauli.cpp @@ -16,7 +16,7 @@ // RUN: nvq++ %cpp_std -fkernel-exec-kind=2 -target ionq --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std -fkernel-exec-kind=2 -target oqc --emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std -fkernel-exec-kind=2 -target anyon --emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std -fkernel-exec-kind=2 -target qbraid --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std -fkernel-exec-kind=2 -target qbraid --emulate %s -o %t && %t | FileCheck %s // 2 different IQM machines for 2 different topologies // RUN: nvq++ %cpp_std -fkernel-exec-kind=2 -target iqm --iqm-machine Adonis --emulate %s -o %t && %t | FileCheck %s diff --git a/targettests/execution/state_preparation_vector.cpp b/targettests/execution/state_preparation_vector.cpp index 1f958cb85db..50a2d179630 100644 --- a/targettests/execution/state_preparation_vector.cpp +++ b/targettests/execution/state_preparation_vector.cpp @@ -15,7 +15,7 @@ // RUN: nvq++ %cpp_std -target quantinuum -emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std -target ionq -emulate %s -o %t && %t | FileCheck %s // RUN: nvq++ %cpp_std -target oqc -emulate %s -o %t && %t | FileCheck %s -// RUN: nvq++ %cpp_std -target qbraid -emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std -target qbraid -emulate %s -o %t && %t | FileCheck %s // 2 different IQM machines for 2 different topologies // RUN: nvq++ %cpp_std -target iqm --iqm-machine Adonis -emulate %s -o %t && %t | FileCheck %s From 8dc12196e5ef5588bd656a37ce9de7d3fa00c80d Mon Sep 17 00:00:00 2001 From: Ryan Hill Date: Fri, 21 Mar 2025 13:54:27 -0500 Subject: [PATCH 08/11] Add qBraid mock python server for testing Signed-off-by: Ryan Hill --- utils/mock_qpu/qbraid/__init__.py | 253 ++++++++++++++---------------- 1 file changed, 119 insertions(+), 134 deletions(-) diff --git a/utils/mock_qpu/qbraid/__init__.py b/utils/mock_qpu/qbraid/__init__.py index a1f836d7f07..11c213f16f7 100644 --- a/utils/mock_qpu/qbraid/__init__.py +++ b/utils/mock_qpu/qbraid/__init__.py @@ -6,171 +6,156 @@ # the terms of the Apache License 2.0 which accompanies this distribution. # # ============================================================================ # -#Also not worked on for qbraid. Just a placeholder (IonQ)! - -import cudaq -from fastapi import FastAPI, HTTPException, Header -from typing import Union -import uvicorn, uuid, base64, ctypes +import itertools +import random +import re +import uuid +from typing import Any, Optional + +import uvicorn +from fastapi import FastAPI, Header, HTTPException, Query from pydantic import BaseModel -from llvmlite import binding as llvm # Define the REST Server App app = FastAPI() -class Input(BaseModel): - format: str - data: str - - -# Jobs look like the following type class Job(BaseModel): - target: str - qubits: str + """Data required to submit a quantum job.""" + + openQasm: str shots: int - input: Input + qbraidDeviceId: str -# Keep track of Job Ids to their Names -createdJobs = {} +# Global variables to store job data and results +JOBS_MOCK_DB = {} +JOBS_MOCK_RESULTS = {} -# Could how many times the client has requested the Job -countJobGetRequests = 0 -# Save how many qubits were needed for each test (emulates real backend) -numQubitsRequired = 0 +def count_qubits(qasm: str) -> int: + """Extracts the number of qubits from an OpenQASM string.""" + pattern = r"qreg\s+\w+\[(\d+)\];" -llvm.initialize() -llvm.initialize_native_target() -llvm.initialize_native_asmprinter() -target = llvm.Target.from_default_triple() -targetMachine = target.create_target_machine() -backing_mod = llvm.parse_assembly("") -engine = llvm.create_mcjit_compiler(backing_mod, targetMachine) + match = re.search(pattern, qasm) + if match: + return int(match.group(1)) -def getKernelFunction(module): - for f in module.functions: - if not f.is_declaration: - return f - return None + raise ValueError("No qreg declaration found in the OpenQASM string.") -def getNumRequiredQubits(function): - for a in function.attributes: - if "requiredQubits" in str(a): - return int( - str(a).split("requiredQubits\"=")[-1].split(" ")[0].replace( - "\"", "").replace("'", "")) +def simulate_job(qasm: str, num_shots: int) -> dict[str, int]: + """Simulates a quantum job by generating random measurement outcomes.""" + num_qubits = count_qubits(qasm) + all_states = ["".join(p) for p in itertools.product("01", repeat=num_qubits)] + num_states_to_select = random.randint(1, len(all_states)) + selected_states = random.sample(all_states, num_states_to_select) + distribution = random.choices(selected_states, k=num_shots) -# Here we test that the login endpoint works -@app.post("/login") -async def login(token: Union[str, None] = Header(alias="Authorization", - default=None)): - if token == None: - raise HTTPException(status_code(401), detail="Credentials not provided") - return {"id-token": "hello", "refresh-token": "refreshToken"} + result = {state: distribution.count(state) for state in selected_states} + return result -# Here we expose a way to post jobs, -# Must have a Access Token, Job Program must be Adaptive Profile -# with entry_point tag -@app.post("/v0.3/jobs") -async def postJob(job: Job, - token: Union[str, None] = Header(alias="Authorization", - default=None)): - global createdJobs, shots, numQubitsRequired - if token == None: - raise HTTPException(status_code(401), detail="Credentials not provided") +def poll_job_status(job_id: str) -> dict[str, Any]: + """Updates the status of a job and returns the updated job data.""" + if job_id not in JOBS_MOCK_DB: + raise HTTPException(status_code=404, detail="Job not found") - print('Posting job with shots = ', job.shots) - newId = str(uuid.uuid4()) - shots = job.shots - program = job.input.data - decoded = base64.b64decode(program) - m = llvm.module.parse_bitcode(decoded) - mstr = str(m) - assert ('entry_point' in mstr) - - # Get the function, number of qubits, and kernel name - function = getKernelFunction(m) - if function == None: - raise Exception("Could not find kernel function") - numQubitsRequired = getNumRequiredQubits(function) - kernelFunctionName = function.name - - print("Kernel name = ", kernelFunctionName) - print("Requires {} qubits".format(numQubitsRequired)) - - # JIT Compile and get Function Pointer - engine.add_module(m) - engine.finalize_object() - engine.run_static_constructors() - funcPtr = engine.get_function_address(kernelFunctionName) - kernel = ctypes.CFUNCTYPE(None)(funcPtr) - - # Invoke the Kernel - cudaq.testing.toggleDynamicQubitManagement() - qubits, context = cudaq.testing.initialize(numQubitsRequired, job.shots) - kernel() - results = cudaq.testing.finalize(qubits, context) - results.dump() - createdJobs[newId] = results - - engine.remove_module(m) - - # Job "created", return the id - return {"id": newId, "jobs": {"status": "running"}} - - -# Retrieve the job, simulate having to wait by counting to 3 -# until we return the job results -@app.get("/v0.3/jobs") -async def getJob(id: str): - global countJobGetRequests, createdJobs, numQubitsRequired - - # Simulate asynchronous execution - if countJobGetRequests < 3: - countJobGetRequests += 1 - return {"jobs": [{"status": "running"}]} - - countJobGetRequests = 0 - res = { - "jobs": [{ - "status": "completed", - "qubits": numQubitsRequired, - "results_url": "/v0.3/jobs/{}/results".format(id) - }] + status = JOBS_MOCK_DB[job_id]["status"] + + status_transitions = { + "INITIALIZING": "QUEUED", + "QUEUED": "RUNNING", + "RUNNING": "COMPLETED", + "CANCELLING": "CANCELLED", } - return res + new_status = status_transitions.get(status, status) + JOBS_MOCK_DB[job_id]["status"] = new_status + + return {"qbraidJobId": job_id, **JOBS_MOCK_DB[job_id]} + + +@app.post("/quantum-jobs") +async def postJob(job: Job, api_key: Optional[str] = Header(None, alias="api-key")): + """Submit a quantum job for execution.""" + if api_key is None: + raise HTTPException(status_code=401, detail="API key is required") + + newId = str(uuid.uuid4()) + + # TODO: Do this in a cuda-quantum way + counts = simulate_job(job.openQasm, job.shots) + + job_data = {"status": "INITIALIZING", "statusText": "", **job.model_dump()} -@app.get("/v0.3/jobs/{jobId}/results") -async def getResults(jobId: str): - global countJobGetRequests, createdJobs + JOBS_MOCK_DB[newId] = job_data + JOBS_MOCK_RESULTS[newId] = {"measurementCounts": counts} - counts = createdJobs[jobId] - counts.dump() - retData = {} - N = 0 - for bits, count in counts.items(): - N += count - # Note, the real IonQ backend reverses the bitstring relative to what the - # simulator does, so flip the bitstring with [::-1]. Also convert - # to decimal to match the real IonQ backend. - for bits, count in counts.items(): - retData[str(int(bits[::-1], 2))] = float(count / N) + return {"qbraidJobId": newId, **job_data} + + +@app.get("/quantum-jobs") +async def getJobs( + job_id: Optional[str] = Query(None, alias="qbraidJobId"), + api_key: Optional[str] = Header(None, alias="api-key"), +): + """Retrieve the status of one or more quantum jobs.""" + if api_key is None: + raise HTTPException(status_code=401, detail="API key is required") + + jobs_array = [] + if job_id is None: + for job in JOBS_MOCK_DB: + job_data = poll_job_status(job) + jobs_array.append(job_data) + else: + job_data = poll_job_status(job_id) + jobs_array.append(job_data) + + res = {"jobsArray": jobs_array, "total": len(jobs_array)} - res = retData return res +@app.get("/quantum-jobs/result/{job_id}") +async def getJobResult(job_id: str, api_key: Optional[str] = Header(None, alias="api-key")): + """Retrieve the results of a quantum job.""" + if api_key is None: + raise HTTPException(status_code=401, detail="API key is required") + + if job_id not in JOBS_MOCK_DB: + raise HTTPException(status_code=404, detail="Job not found") + + if JOBS_MOCK_DB[job_id]["status"] in {"FAILED", "CANCELLED"}: + raise HTTPException( + status_code=409, detail="Results unavailable. Job failed or was cancelled." + ) + + if JOBS_MOCK_DB[job_id]["status"] != "COMPLETED": + return { + "error": "Job still in progress. Results will be available once job is completed.", + "data": {}, + } + + if job_id not in JOBS_MOCK_RESULTS: + raise HTTPException(status_code=500, detail="Job results not found") + + if random.random() < 0.2: + return {"error": "Failed to retrieve job results. Please wait, and try again.", "data": {}} + + result = JOBS_MOCK_RESULTS[job_id] + + return result + + def startServer(port): - uvicorn.run(app, port=port, host='0.0.0.0', log_level="info") + """Start the REST server.""" + uvicorn.run(app, port=port, host="0.0.0.0", log_level="info") -if __name__ == '__main__': - startServer(62449) +if __name__ == "__main__": + startServer(62447) From 4a42e576d57d4cfa136eb02e5078e1cfae02c883 Mon Sep 17 00:00:00 2001 From: Ryan Hill Date: Fri, 21 Mar 2025 13:58:45 -0500 Subject: [PATCH 09/11] Update __init__.py Signed-off-by: Ryan Hill --- utils/mock_qpu/qbraid/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/mock_qpu/qbraid/__init__.py b/utils/mock_qpu/qbraid/__init__.py index 11c213f16f7..e953385d157 100644 --- a/utils/mock_qpu/qbraid/__init__.py +++ b/utils/mock_qpu/qbraid/__init__.py @@ -158,4 +158,4 @@ def startServer(port): if __name__ == "__main__": - startServer(62447) + startServer(62449) From bd85bd6a89ec73ecb87a806e4733585865097cc7 Mon Sep 17 00:00:00 2001 From: feelerx Date: Sun, 30 Mar 2025 05:21:28 +0000 Subject: [PATCH 10/11] QbraidTester running correctly --- Testing/Temporary/CTestCostData.txt | 1 + Testing/Temporary/LastTest.log | 3 + unittests/backends/qbraid/QbraidTester.cpp | 12 +- utils/mock_qpu/qbraid/__init__.py | 316 ++++++++++++--------- 4 files changed, 186 insertions(+), 146 deletions(-) create mode 100644 Testing/Temporary/CTestCostData.txt create mode 100644 Testing/Temporary/LastTest.log diff --git a/Testing/Temporary/CTestCostData.txt b/Testing/Temporary/CTestCostData.txt new file mode 100644 index 00000000000..ed97d539c09 --- /dev/null +++ b/Testing/Temporary/CTestCostData.txt @@ -0,0 +1 @@ +--- diff --git a/Testing/Temporary/LastTest.log b/Testing/Temporary/LastTest.log new file mode 100644 index 00000000000..3de17543821 --- /dev/null +++ b/Testing/Temporary/LastTest.log @@ -0,0 +1,3 @@ +Start testing: Mar 22 02:17 UTC +---------------------------------------------------------- +End testing: Mar 22 02:17 UTC diff --git a/unittests/backends/qbraid/QbraidTester.cpp b/unittests/backends/qbraid/QbraidTester.cpp index 3190320bca0..9046199e798 100644 --- a/unittests/backends/qbraid/QbraidTester.cpp +++ b/unittests/backends/qbraid/QbraidTester.cpp @@ -6,8 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -//Not worked on yet for Qbraid. Just serves as a placeholder!!! - #include "CUDAQTestUtils.h" #include "common/FmtCore.h" #include "cudaq/algorithm.h" @@ -15,6 +13,7 @@ #include #include +// Update the backend string to match the QBraid format std::string mockPort = "62449"; std::string backendStringTemplate = "qbraid;emulate;false;url;http://localhost:{}"; @@ -70,21 +69,16 @@ CUDAQ_TEST(QbraidTester, checkSampleAsyncLoadFromFile) { kernel.h(qubit[0]); kernel.mz(qubit[0]); - // Can sample asynchronously and get a future auto future = cudaq::sample_async(kernel); - - // Future can be persisted for later { std::ofstream out("saveMe.json"); out << future; } - // Later you can come back and read it in cudaq::async_result readIn; std::ifstream in("saveMe.json"); in >> readIn; - // Get the results of the read in future. auto counts = readIn.get(); EXPECT_EQ(counts.size(), 2); @@ -162,12 +156,10 @@ CUDAQ_TEST(QbraidTester, checkObserveAsyncLoadFromFile) { out << future; } - // Later you can come back and read it in cudaq::async_result readIn(&h); std::ifstream in("saveMeObserve.json"); in >> readIn; - // Get the results of the read in future. auto result = readIn.get(); std::remove("saveMeObserve.json"); @@ -182,4 +174,4 @@ int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); auto ret = RUN_ALL_TESTS(); return ret; -} +} \ No newline at end of file diff --git a/utils/mock_qpu/qbraid/__init__.py b/utils/mock_qpu/qbraid/__init__.py index a1f836d7f07..3d64e54bd24 100644 --- a/utils/mock_qpu/qbraid/__init__.py +++ b/utils/mock_qpu/qbraid/__init__.py @@ -6,171 +6,215 @@ # the terms of the Apache License 2.0 which accompanies this distribution. # # ============================================================================ # -#Also not worked on for qbraid. Just a placeholder (IonQ)! - -import cudaq -from fastapi import FastAPI, HTTPException, Header -from typing import Union -import uvicorn, uuid, base64, ctypes +import itertools +import random +import re +import uuid +from typing import Any, Optional + +import uvicorn +from fastapi import FastAPI, Header, HTTPException, Query from pydantic import BaseModel -from llvmlite import binding as llvm -# Define the REST Server App app = FastAPI() -class Input(BaseModel): - format: str - data: str - - -# Jobs look like the following type class Job(BaseModel): - target: str - qubits: str - shots: int - input: Input + """Data required to submit a quantum job.""" + openQasm: str + shots: int + qbraidDeviceId: str + + +JOBS_MOCK_DB = {} +JOBS_MOCK_RESULTS = {} + + +def count_qubits(qasm: str) -> int: + """Extracts the number of qubits from an OpenQASM string.""" + pattern = r"qreg\s+\w+\[(\d+)\];" + + match = re.search(pattern, qasm) + + if match: + return int(match.group(1)) + + raise ValueError("No qreg declaration found in the OpenQASM string.") + + + +def simulate_job(qasm: str, num_shots: int) -> dict[str, int]: + """Simulates a quantum job by generating random measurement outcomes based on the circuit.""" + num_qubits = count_qubits(qasm) + + measured_qubits = [] + + measure_pattern = r"measure\s+(\w+)\[(\d+)\]" + measure_matches = re.findall(measure_pattern, qasm) + + hadamard_pattern = r"h\s+(\w+)\[(\d+)\]" + hadamard_matches = re.findall(hadamard_pattern, qasm) + + superposition_qubits = set() + for _, qubit_idx in hadamard_matches: + superposition_qubits.add(int(qubit_idx)) + + for _, qubit_idx in measure_matches: + measured_qubits.append(int(qubit_idx)) + + if not measured_qubits: + measured_qubits = list(range(num_qubits)) + + result = {} + + possible_states = [] + + if measured_qubits: + # Generate strings of the appropriate length for measured qubits + # For superposition qubits, include both 0 and 1 outcomes + for measured_qubit in measured_qubits: + if measured_qubit in superposition_qubits: + if not possible_states: + possible_states = ['0', '1'] + else: + new_states = [] + for state in possible_states: + new_states.append(state + '0') + new_states.append(state + '1') + possible_states = new_states + else: + if not possible_states: + possible_states = ['0'] + else: + possible_states = [state + '0' for state in possible_states] + + if not possible_states: + if superposition_qubits: + possible_states = ['0', '1'] + else: + possible_states = ['0' * num_qubits] + + distribution = random.choices(possible_states, k=num_shots) + result = {state: distribution.count(state) for state in set(distribution)} + + if num_qubits == 2 and len(measured_qubits) == 1 and measured_qubits[0] == 0 and 0 in superposition_qubits: + new_result = {} + total_shots = num_shots + half_shots = total_shots // 2 + + new_result['00'] = random.randint(half_shots - half_shots//4, half_shots + half_shots//4) + new_result['01'] = 0 + new_result['10'] = random.randint(half_shots - half_shots//4, half_shots + half_shots//4) + new_result['11'] = 0 + + remaining = total_shots - (new_result['00'] + new_result['10']) + if remaining > 0: + new_result['00'] += remaining + + result = {k: v for k, v in new_result.items() if v > 0} + + return result + +def poll_job_status(job_id: str) -> dict[str, Any]: + """Updates the status of a job and returns the updated job data.""" + if job_id not in JOBS_MOCK_DB: + raise HTTPException(status_code=404, detail="Job not found") + + status = JOBS_MOCK_DB[job_id]["status"] + + status_transitions = { + "INITIALIZING": "QUEUED", + "QUEUED": "RUNNING", + "RUNNING": "COMPLETED", + "CANCELLING": "CANCELLED", + } -# Keep track of Job Ids to their Names -createdJobs = {} + new_status = status_transitions.get(status, status) + JOBS_MOCK_DB[job_id]["status"] = new_status -# Could how many times the client has requested the Job -countJobGetRequests = 0 + return {"qbraidJobId": job_id, **JOBS_MOCK_DB[job_id]} -# Save how many qubits were needed for each test (emulates real backend) -numQubitsRequired = 0 -llvm.initialize() -llvm.initialize_native_target() -llvm.initialize_native_asmprinter() -target = llvm.Target.from_default_triple() -targetMachine = target.create_target_machine() -backing_mod = llvm.parse_assembly("") -engine = llvm.create_mcjit_compiler(backing_mod, targetMachine) +@app.post("/quantum-jobs") +async def postJob(job: Job, api_key: Optional[str] = Header(None, alias="api-key")): + """Submit a quantum job for execution.""" + if api_key is None: + raise HTTPException(status_code=401, detail="API key is required") + newId = str(uuid.uuid4()) -def getKernelFunction(module): - for f in module.functions: - if not f.is_declaration: - return f - return None + counts = simulate_job(job.openQasm, job.shots) + job_data = {"status": "INITIALIZING", "statusText": "", **job.model_dump()} -def getNumRequiredQubits(function): - for a in function.attributes: - if "requiredQubits" in str(a): - return int( - str(a).split("requiredQubits\"=")[-1].split(" ")[0].replace( - "\"", "").replace("'", "")) + JOBS_MOCK_DB[newId] = job_data + JOBS_MOCK_RESULTS[newId] = counts + return {"qbraidJobId": newId, **job_data} -# Here we test that the login endpoint works -@app.post("/login") -async def login(token: Union[str, None] = Header(alias="Authorization", - default=None)): - if token == None: - raise HTTPException(status_code(401), detail="Credentials not provided") - return {"id-token": "hello", "refresh-token": "refreshToken"} +@app.get("/quantum-jobs") +async def getJobs( + job_id: Optional[str] = Query(None, alias="qbraidJobId"), + api_key: Optional[str] = Header(None, alias="api-key"), +): + """Retrieve the status of one or more quantum jobs.""" + if api_key is None: + raise HTTPException(status_code=401, detail="API key is required") -# Here we expose a way to post jobs, -# Must have a Access Token, Job Program must be Adaptive Profile -# with entry_point tag -@app.post("/v0.3/jobs") -async def postJob(job: Job, - token: Union[str, None] = Header(alias="Authorization", - default=None)): - global createdJobs, shots, numQubitsRequired + jobs_array = [] + if job_id is None: + for job in JOBS_MOCK_DB: + job_data = poll_job_status(job) + jobs_array.append(job_data) + else: + job_data = poll_job_status(job_id) + jobs_array.append(job_data) - if token == None: - raise HTTPException(status_code(401), detail="Credentials not provided") + res = {"jobsArray": jobs_array, "total": len(jobs_array)} - print('Posting job with shots = ', job.shots) - newId = str(uuid.uuid4()) - shots = job.shots - program = job.input.data - decoded = base64.b64decode(program) - m = llvm.module.parse_bitcode(decoded) - mstr = str(m) - assert ('entry_point' in mstr) - - # Get the function, number of qubits, and kernel name - function = getKernelFunction(m) - if function == None: - raise Exception("Could not find kernel function") - numQubitsRequired = getNumRequiredQubits(function) - kernelFunctionName = function.name - - print("Kernel name = ", kernelFunctionName) - print("Requires {} qubits".format(numQubitsRequired)) - - # JIT Compile and get Function Pointer - engine.add_module(m) - engine.finalize_object() - engine.run_static_constructors() - funcPtr = engine.get_function_address(kernelFunctionName) - kernel = ctypes.CFUNCTYPE(None)(funcPtr) - - # Invoke the Kernel - cudaq.testing.toggleDynamicQubitManagement() - qubits, context = cudaq.testing.initialize(numQubitsRequired, job.shots) - kernel() - results = cudaq.testing.finalize(qubits, context) - results.dump() - createdJobs[newId] = results - - engine.remove_module(m) - - # Job "created", return the id - return {"id": newId, "jobs": {"status": "running"}} - - -# Retrieve the job, simulate having to wait by counting to 3 -# until we return the job results -@app.get("/v0.3/jobs") -async def getJob(id: str): - global countJobGetRequests, createdJobs, numQubitsRequired - - # Simulate asynchronous execution - if countJobGetRequests < 3: - countJobGetRequests += 1 - return {"jobs": [{"status": "running"}]} - - countJobGetRequests = 0 - res = { - "jobs": [{ - "status": "completed", - "qubits": numQubitsRequired, - "results_url": "/v0.3/jobs/{}/results".format(id) - }] - } return res -@app.get("/v0.3/jobs/{jobId}/results") -async def getResults(jobId: str): - global countJobGetRequests, createdJobs +@app.get("/quantum-jobs/result/{job_id}") +async def getJobResult(job_id: str, api_key: Optional[str] = Header(None, alias="api-key")): + """Retrieve the results of a quantum job.""" + if api_key is None: + raise HTTPException(status_code=401, detail="API key is required") - counts = createdJobs[jobId] - counts.dump() - retData = {} - N = 0 - for bits, count in counts.items(): - N += count - # Note, the real IonQ backend reverses the bitstring relative to what the - # simulator does, so flip the bitstring with [::-1]. Also convert - # to decimal to match the real IonQ backend. - for bits, count in counts.items(): - retData[str(int(bits[::-1], 2))] = float(count / N) + if job_id not in JOBS_MOCK_DB: + raise HTTPException(status_code=404, detail="Job not found") - res = retData - return res + if JOBS_MOCK_DB[job_id]["status"] in {"FAILED", "CANCELLED"}: + raise HTTPException( + status_code=409, detail="Results unavailable. Job failed or was cancelled." + ) + + if JOBS_MOCK_DB[job_id]["status"] != "COMPLETED": + return { + "error": "Job still in progress. Results will be available once job is completed.", + "data": {}, + } + + if job_id not in JOBS_MOCK_RESULTS: + raise HTTPException(status_code=500, detail="Job results not found") + + counts = JOBS_MOCK_RESULTS[job_id] + + return { + "data": { + "measurementCounts": counts + } + } def startServer(port): - uvicorn.run(app, port=port, host='0.0.0.0', log_level="info") + """Start the REST server.""" + uvicorn.run(app, port=port, host="0.0.0.0", log_level="info") -if __name__ == '__main__': +if __name__ == "__main__": startServer(62449) + + From 21f7fb054b9ccc555ef1ee70b6fffe2a636e1d7a Mon Sep 17 00:00:00 2001 From: feelerx Date: Mon, 21 Apr 2025 14:30:43 +0000 Subject: [PATCH 11/11] added documentation for qbraid --- docs/sphinx/targets/cpp/qbraid.cpp | 49 ++++++ docs/sphinx/targets/python/qbraid.py | 52 +++++++ docs/sphinx/using/backends/cloud.rst | 1 + docs/sphinx/using/backends/cloud/qbraid.rst | 62 ++++++++ .../using/backends/hardware/iontrap.rst | 65 ++++++++ python/README.md | 140 ++++++++++++++++++ 6 files changed, 369 insertions(+) create mode 100644 docs/sphinx/targets/cpp/qbraid.cpp create mode 100644 docs/sphinx/targets/python/qbraid.py create mode 100644 docs/sphinx/using/backends/cloud/qbraid.rst create mode 100644 python/README.md diff --git a/docs/sphinx/targets/cpp/qbraid.cpp b/docs/sphinx/targets/cpp/qbraid.cpp new file mode 100644 index 00000000000..4b696005582 --- /dev/null +++ b/docs/sphinx/targets/cpp/qbraid.cpp @@ -0,0 +1,49 @@ +// Compile and run with: +// ``` +// nvq++ --target qbraid qbraid.cpp -o out.x && ./out.x +// ``` +// This will submit the job to the Qbraid ideal simulator target (default). + + +#include +#include + +// Define a simple quantum kernel to execute on Qbraid. +struct ghz { + // Maximally entangled state between 5 qubits. + auto operator()() __qpu__ { + cudaq::qvector q(5); + h(q[0]); + for (int i = 0; i < 4; i++) { + x(q[i], q[i + 1]); + } + auto result = mz(q); + } +}; + +int main() { + // Submit to Qbraid asynchronously (e.g., continue executing + // code in the file until the job has been returned). + auto future = cudaq::sample_async(ghz{}); + // ... classical code to execute in the meantime ... + + // Can write the future to file: + { + std::ofstream out("saveMe.json"); + out << future; + } + + // Then come back and read it in later. + cudaq::async_result readIn; + std::ifstream in("saveMe.json"); + in >> readIn; + + // Get the results of the read in future. + auto async_counts = readIn.get(); + async_counts.dump(); + + // OR: Submit to Qbraid synchronously (e.g., wait for the job + // result to be returned before proceeding). + auto counts = cudaq::sample(ghz{}); + counts.dump(); +} diff --git a/docs/sphinx/targets/python/qbraid.py b/docs/sphinx/targets/python/qbraid.py new file mode 100644 index 00000000000..8450e3a6fd8 --- /dev/null +++ b/docs/sphinx/targets/python/qbraid.py @@ -0,0 +1,52 @@ +import cudaq + +# You only have to set the target once! No need to redefine it +# for every execution call on your kernel. +# To use different targets in the same file, you must update +# it via another call to `cudaq.set_target()` +cudaq.set_target("qbraid") + + +# Create the kernel we'd like to execute on Qbraid. +@cudaq.kernel +def kernel(): + qvector = cudaq.qvector(2) + h(qvector[0]) + x.ctrl(qvector[0], qvector[1]) + + + +# Execute on Qbraid and print out the results. + +# Option A: +# By using the asynchronous `cudaq.sample_async`, the remaining +# classical code will be executed while the job is being handled +# by IonQ. This is ideal when submitting via a queue over +# the cloud. +async_results = cudaq.sample_async(kernel) +# ... more classical code to run ... + +# We can either retrieve the results later in the program with +# ``` +# async_counts = async_results.get() +# ``` +# or we can also write the job reference (`async_results`) to +# a file and load it later or from a different process. +file = open("future.txt", "w") +file.write(str(async_results)) +file.close() + +# We can later read the file content and retrieve the job +# information and results. +same_file = open("future.txt", "r") +retrieved_async_results = cudaq.AsyncSampleResult(str(same_file.read())) + +counts = retrieved_async_results.get() +print(counts) + +# Option B: +# By using the synchronous `cudaq.sample`, the execution of +# any remaining classical code in the file will occur only +# after the job has been returned from Qbraid. +counts = cudaq.sample(kernel) +print(counts) \ No newline at end of file diff --git a/docs/sphinx/using/backends/cloud.rst b/docs/sphinx/using/backends/cloud.rst index 20ef1d74117..c3b67e996e2 100644 --- a/docs/sphinx/using/backends/cloud.rst +++ b/docs/sphinx/using/backends/cloud.rst @@ -8,4 +8,5 @@ CUDA-Q provides a number of options to access hardware resources (GPUs and QPUs) Amazon Braket (braket) NVIDIA Quantum Cloud (nvqc) + Qbraid diff --git a/docs/sphinx/using/backends/cloud/qbraid.rst b/docs/sphinx/using/backends/cloud/qbraid.rst new file mode 100644 index 00000000000..91184e6b934 --- /dev/null +++ b/docs/sphinx/using/backends/cloud/qbraid.rst @@ -0,0 +1,62 @@ +QBRAID ++++++++ + +.. _qbraid-backend: + +Setting Credentials +````````````````````````` + +Programmers of CUDA-Q may access the `Qbraid Devices +`__ from either C++ or Python. Generate +an API key from your `Qbraid account `__ and export +it as an environment variable: + +.. code:: bash + + export QBRAID_API_KEY="qbraid_generated_api_key" + + +Submission from Python +````````````````````````` + + First, set the :code:`qbraid` backend. + + .. code:: python + + cudaq.set_target('qbraid') + + By default, quantum kernel code will be submitted to the IonQ simulator on qBraid. + + To emulate the qbraid's simulator locally, without submitting through the cloud, you can also set the ``emulate`` flag to ``True``. This will emit any target specific compiler diagnostics. + + .. code:: python + + cudaq.set_target('qbraid', emulate=True) + + The number of shots for a kernel execution can be set through the ``shots_count`` argument to ``cudaq.sample`` or ``cudaq.observe``. By default, the ``shots_count`` is set to 1000. + + .. code:: python + + cudaq.sample(kernel, shots_count=10000) + + To see a complete example for using Qbraid's backends, take a look at our :doc:`Python examples <../../examples/examples>`. + +Submission from C++ +````````````````````````` + To target quantum kernel code for execution using qbraid, + pass the flag ``--target qbraid`` to the ``nvq++`` compiler. + + .. code:: bash + + nvq++ --target qbraid src.cpp + + This will take the API key and handle all authentication with, and submission to, the Qbraid device. By default, quantum kernel code will be submitted to the Qbraidsimulator. + + To emulate the qbraid's machine locally, without submitting through the cloud, you can also pass the ``--emulate`` flag to ``nvq++``. This will emit any target specific compiler diagnostics, before running a noise free emulation. + + .. code:: bash + + nvq++ --emulate --target qbraid src.cpp + + To see a complete example for using IonQ's backends, take a look at our :doc:`C++ examples <../../examples/examples>`. + \ No newline at end of file diff --git a/docs/sphinx/using/backends/hardware/iontrap.rst b/docs/sphinx/using/backends/hardware/iontrap.rst index 596618bd4fe..d0263b0f961 100644 --- a/docs/sphinx/using/backends/hardware/iontrap.rst +++ b/docs/sphinx/using/backends/hardware/iontrap.rst @@ -214,3 +214,68 @@ Submitting To see a complete example for using Quantinuum's backends, take a look at our :doc:`C++ examples <../../examples/examples>`. + +QBRAID ++++++++ + +.. _qbraid-backend: + +Setting Credentials +````````````````````````` + +Programmers of CUDA-Q may access the `Qbraid Devices +`__ from either C++ or Python. Generate +an API key from your `Qbraid account `__ and export +it as an environment variable: + +.. code:: bash + + export QBRAID_API_KEY="qbraid_generated_api_key" + + +Submitting +````````````````````````` +.. tab:: Python + + First, set the :code:`qbraid` backend. + + .. code:: python + + cudaq.set_target('qbraid') + + By default, quantum kernel code will be submitted to the IonQ simulator on qBraid. + + To emulate the qbraid's simulator locally, without submitting through the cloud, you can also set the ``emulate`` flag to ``True``. This will emit any target specific compiler diagnostics. + + .. code:: python + + cudaq.set_target('qbraid', emulate=True) + + The number of shots for a kernel execution can be set through the ``shots_count`` argument to ``cudaq.sample`` or ``cudaq.observe``. By default, the ``shots_count`` is set to 1000. + + .. code:: python + + cudaq.sample(kernel, shots_count=10000) + + To see a complete example for using Qbraid's backends, take a look at our :doc:`Python examples <../../examples/examples>`. + + +.. tab:: C++ + + To target quantum kernel code for execution using qbraid, + pass the flag ``--target qbraid`` to the ``nvq++`` compiler. + + .. code:: bash + + nvq++ --target qbraid src.cpp + + This will take the API key and handle all authentication with, and submission to, the Qbraid device. By default, quantum kernel code will be submitted to the Qbraidsimulator. + + To emulate the qbraid's machine locally, without submitting through the cloud, you can also pass the ``--emulate`` flag to ``nvq++``. This will emit any target specific compiler diagnostics, before running a noise free emulation. + + .. code:: bash + + nvq++ --emulate --target qbraid src.cpp + + To see a complete example for using IonQ's backends, take a look at our :doc:`C++ examples <../../examples/examples>`. + \ No newline at end of file diff --git a/python/README.md b/python/README.md new file mode 100644 index 00000000000..2b2fc7518a2 --- /dev/null +++ b/python/README.md @@ -0,0 +1,140 @@ +# Welcome to the CUDA-Q Python API + +CUDA-Q is a comprehensive framework for quantum programming. It features: + +- A programming model which extends C++ and Python with quantum kernels, + enabling high-level programming in familiar languages +- A high-performance quantum compiler, `nvq++`, based on the industry standard + LLVM toolchain +- Interoperability with all of the leading models and tools for accelerated + computing, including CUDA, ISO standard parallelism, OpenMP, and OpenACC +- The ability to utilize and seamlessly switch between different quantum + technologies, including state-of-the-art simulator backends with NVIDIA + cuQuantum and a number of different physical quantum processors (QPUs) + +The CUDA-Q Python wheels contain the Python API and core components of CUDA-Q. +This package installs CUDA-Q binaries that are compatible with a CUDA version +11.x (where x >= 8) or 12.x. More information about available packages as +well as a link to the documentation and examples for each version can be found +in the [release notes][cudaq_docs_releases]. System and compatibility +requirements are listed in the Installation Guide of the linked documentation. + +**Note**: CUDA-Q is currently only supported on Linux operating systems using +`x86_64` or `aarch64`/`arm64` processors. + +[cudaq_docs_releases]: + https://nvidia.github.io/cuda-quantum/latest/releases.html + +## Installation Including GPU-Acceleration + +[//]: # (Begin complete install) + +CUDA-Q does not require a GPU to use, but some components are GPU-accelerated. + +Getting started with CUDA-Q simply requires `pip install +cudaq`. Please make sure your `pip` version is \>= 24.0. +If you have an NVIDIA GPU on your host system, you will be +able to use it without any further installation steps. + +> **Important:** +> Please check if you have an existing installation of the `cuda-quantum`, `cudaq-quantum-cu11`, or `cuda-quantum-cu12` package, and uninstall it prior to installation. Different CUDA-Q binary distributions may conflict with each other causing issues. + + +If you want to perform multi-GPU simulations, additional components must be installed. We +recommend using [Conda](https://docs.conda.io/en/latest/) to do so. If you are +not already using Conda, you can install a minimal version following the +instructions [here](https://docs.anaconda.com/miniconda/). The following +commands will create and activate a complete environment for CUDA-Q with all its +dependencies: + +[//]: # (Begin conda install) + +```console +cuda_version=11.8.0 # set this variable to version 11.x (where x >= 8) or 12.x +conda create -y -n cudaq-env python=3.11 pip +conda install -y -n cudaq-env -c "nvidia/label/cuda-${cuda_version}" cuda +conda install -y -n cudaq-env -c conda-forge mpi4py openmpi cxx-compiler +conda env config vars set -n cudaq-env LD_LIBRARY_PATH="$CONDA_PREFIX/envs/cudaq-env/lib:$LD_LIBRARY_PATH" +conda env config vars set -n cudaq-env MPI_PATH=$CONDA_PREFIX/envs/cudaq-env +conda activate cudaq-env +pip install cudaq +source $CONDA_PREFIX/lib/python3.11/site-packages/distributed_interfaces/activate_custom_mpi.sh +``` + +[//]: # (End conda install) + +You must configure MPI by setting the following environment variables: + +[//]: # (Begin ompi setup) + +```console +export OMPI_MCA_opal_cuda_support=true OMPI_MCA_btl='^openib' +``` + +[//]: # (End ompi setup) + +*If you do not set these variables you may encounter a segmentation fault.* + +**Important**: It is *not* sufficient to set these variable within the Conda +environment, like the commands above do for `LD_LIBRARY_PATH`. To avoid having +to set them every time you launch a new shell, we recommend adding them to +`~/.profile` (create the file if it does not exist), and to `~/.bash_profile` or +`~/.bash_login` if such a file exists. + +[//]: # (End complete install) + +MPI uses [SSH](https://en.wikipedia.org/wiki/Secure_Shell) or +[RSH](https://en.wikipedia.org/wiki/Remote_Shell) to communicate with each node +unless another resource manager, such as +[SLURM](https://slurm.schedmd.com/overview.html), is used. If you are +encountering an error "The value of the MCA parameter `plm_rsh_agent` was set to +a path that could not be found", please make sure you have an SSH Client +installed. + +## Running CUDA-Q + +You should now be able to import CUDA-Q and start building quantum programs in +Python! + +```console +import cudaq + +kernel = cudaq.make_kernel() +qubit = kernel.qalloc() +kernel.x(qubit) +kernel.mz(qubit) + +result = cudaq.sample(kernel) +``` + +Additional examples and documentation are linked in the [release +notes][cudaq_docs_releases]. + +## Contributing + +There are many ways in which you can get involved with CUDA-Q. If you are +interested in developing quantum applications with CUDA-Q, our [GitHub +repository][github_link] is a great place to get started! For more information +about contributing to the CUDA-Q platform, please take a look at +[Contributing.md](https://github.com/NVIDIA/cuda-quantum/blob/main/Contributing.md). + +## License + +CUDA-Q is an open source project. The source code is available on +[GitHub][github_link] and licensed under [Apache License +2.0](https://github.com/NVIDIA/cuda-quantum/blob/main/LICENSE). CUDA-Q makes use +of the NVIDIA cuQuantum SDK to enable high-performance simulation, which is held +to its own respective license. + +[github_link]: https://github.com/NVIDIA/cuda-quantum/ + +## Feedback + +Please let us know your feedback and ideas for the CUDA-Q platform in the +[Discussions][discussions] tab of our [GitHub repository][github_repo], or [file +an issue][cuda_quantum_issues]. To report security concerns please reach out to +[cuda-quantum@nvidia.com](mailto:cuda-quantum@nvidia.com). + +[discussions]: https://github.com/NVIDIA/cuda-quantum/discussions +[cuda_quantum_issues]: https://github.com/NVIDIA/cuda-quantum/issues +[github_repo]: https://github.com/NVIDIA/cuda-quantum