From 7e8774dbbc3b4d2e750b6520b6cf5c934f28cda1 Mon Sep 17 00:00:00 2001 From: Radu Marginean Date: Tue, 21 Oct 2025 21:22:05 +0300 Subject: [PATCH 1/8] Partial implementation. --- .../rest/helpers/ionq/IonQServerHelper.cpp | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp b/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp index 0225b6fe769..810799af67a 100644 --- a/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp +++ b/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp @@ -70,6 +70,10 @@ class IonQServerHelper : public ServerHelper { cudaq::sample_result processResults(ServerMessage &postJobResponse, std::string &jobId) override; + /// @brief extract the job shots url from jobs returned by Ion API + std::string getShotsUrl(nlohmann::json_v3_11_1::json &jobs, + const char *DEFAULT_URL); + private: /// @brief RestClient used for HTTP requests. RestClient client; @@ -126,6 +130,10 @@ void IonQServerHelper::initialize(BackendConfig config) { backendConfig["sharpen"] = config["sharpen"]; if (config.find("format") != config.end()) backendConfig["format"] = config["format"]; + + // Enable memory (return shot-wise bitstrings instead of counts) + if (config.find("memory") != config.end()) + backendConfig["memory"] = config["memory"]; } // Implementation of the getValueOrDefault function @@ -351,6 +359,19 @@ bool IonQServerHelper::jobIsDone(ServerMessage &getJobResponse) { return jobs[0].at("status").get() == "completed"; } +std::string IonQServerHelper::getShotsUrl(nlohmann::json_v3_11_1::json &jobs, + const char *DEFAULT_URL) { + std::string shotsUrl = ""; + if ( + jobs[0].contains("results") && + jobs[0].at("results").contains("shots") && + jobs[0].at("results").at("shots").contains("url")) { + shotsUrl = std::string(DEFAULT_URL) + + jobs[0].at("results").at("shots").at("url").get(); + } + return shotsUrl; +} + // Process the results from a job cudaq::sample_result IonQServerHelper::processResults(ServerMessage &postJobResponse, @@ -448,9 +469,33 @@ IonQServerHelper::processResults(ServerMessage &postJobResponse, execResults.emplace_back(regCounts, info.registerName); } + auto shotsUrl = getShotsUrl(jobs, DEFAULT_URL); + printf("SHOTS URL: %s\n", shotsUrl.c_str()); + + auto res = getResults(shotsUrl); + printf("SHOT RESULTS %s", res.dump().c_str()); + + bool targetHasMemoryOption = keyExists("memory") && backendConfig["memory"] == "true"; + if (targetHasMemoryOption) { + + std::vector bitStrings; + + for (const auto &element : results.items()) { + uint64_t s = std::stoull(element.key()); + std::string newkey = std::bitset<64>(s).to_string(); + std::reverse(newkey.begin(), newkey.end()); // perform endian swap + newkey.resize(nQubits); + bitStrings.push_back(newkey); + } + + if (!execResults.empty()) + execResults[0].sequentialData = std::move(bitStrings); + } + // Return a sample result including the global register and all individual // registers. auto ret = cudaq::sample_result(execResults); + return ret; } From ee87728d7a95fb1907aa0fdaa44b7797f6526fb3 Mon Sep 17 00:00:00 2001 From: Radu Marginean Date: Wed, 22 Oct 2025 20:39:26 +0300 Subject: [PATCH 2/8] Finish work on prodcution code. Signed-off-by: Radu Marginean --- .../rest/helpers/ionq/IonQServerHelper.cpp | 56 ++++++++++++++----- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp b/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp index 810799af67a..59d413232de 100644 --- a/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp +++ b/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp @@ -74,6 +74,9 @@ class IonQServerHelper : public ServerHelper { std::string getShotsUrl(nlohmann::json_v3_11_1::json &jobs, const char *DEFAULT_URL); + /// @brief Verify if shot-wise output was requested by user and can be extracted + bool shotWiseOutputIsNeeded(nlohmann::json_v3_11_1::json &jobs); + private: /// @brief RestClient used for HTTP requests. RestClient client; @@ -372,6 +375,29 @@ std::string IonQServerHelper::getShotsUrl(nlohmann::json_v3_11_1::json &jobs, return shotsUrl; } +bool IonQServerHelper::shotWiseOutputIsNeeded(nlohmann::json_v3_11_1::json &jobs) { + std::string noiseModel = "ideal"; + if (!jobs.empty() && jobs[0].contains("noise") && + jobs[0]["noise"].contains("model")) { + noiseModel = jobs[0]["noise"]["model"].get(); + } else if (keyExists("noise_model")) { + noiseModel = backendConfig["noise_model"]; + } + + std::string target = "simulator"; + if (!jobs.empty() && jobs[0].contains("target")) { + target = jobs[0]["target"].get(); + } else if (keyExists("target")) { + target = backendConfig["target"]; + } + + bool targetHasMemoryOption = keyExists("memory") && backendConfig["memory"] == "true"; + bool targetHasNoiseModel = noiseModel != "ideal"; + bool targetIsNotSimulator = target != "simulator"; + + return targetHasMemoryOption && (targetHasNoiseModel || targetIsNotSimulator); +} + // Process the results from a job cudaq::sample_result IonQServerHelper::processResults(ServerMessage &postJobResponse, @@ -469,23 +495,25 @@ IonQServerHelper::processResults(ServerMessage &postJobResponse, execResults.emplace_back(regCounts, info.registerName); } + // Add shot-wise output if requested by user + bool extractShots = shotWiseOutputIsNeeded(jobs); auto shotsUrl = getShotsUrl(jobs, DEFAULT_URL); - printf("SHOTS URL: %s\n", shotsUrl.c_str()); - - auto res = getResults(shotsUrl); - printf("SHOT RESULTS %s", res.dump().c_str()); - - bool targetHasMemoryOption = keyExists("memory") && backendConfig["memory"] == "true"; - if (targetHasMemoryOption) { + if (extractShots && shotsUrl != "") { std::vector bitStrings; - - for (const auto &element : results.items()) { - uint64_t s = std::stoull(element.key()); - std::string newkey = std::bitset<64>(s).to_string(); - std::reverse(newkey.begin(), newkey.end()); // perform endian swap - newkey.resize(nQubits); - bitStrings.push_back(newkey); + auto shotsResults = getResults(shotsUrl); + + for (const auto &element : shotsResults.items()) { + assert(nQubits <= 64); + int64_t s = std::stoull(element.value().get()); + std::string bitString = std::bitset<64>(s).to_string(); + auto firstone = bitString.find_first_not_of('0'); + bitString = (firstone == std::string::npos) ? "0" : bitString.substr(firstone); + if (bitString.size() < static_cast(nQubits)) { + bitString.insert(bitString.begin(), + static_cast(nQubits) - bitString.size(), '0'); + } + bitStrings.push_back(bitString); } if (!execResults.empty()) From 671dd89ed5cd27f3f1b0aa25abc1da5552834125 Mon Sep 17 00:00:00 2001 From: Radu Marginean Date: Wed, 29 Oct 2025 19:19:09 +0200 Subject: [PATCH 3/8] Adding unit tests. --- python/tests/backends/test_IonQ.py | 42 +++++++++++++++++++ .../rest/helpers/ionq/IonQServerHelper.cpp | 12 +++++- utils/mock_qpu/ionq/__init__.py | 23 +++++++++- 3 files changed, 74 insertions(+), 3 deletions(-) diff --git a/python/tests/backends/test_IonQ.py b/python/tests/backends/test_IonQ.py index 6c8587daf0b..e25c906ec97 100644 --- a/python/tests/backends/test_IonQ.py +++ b/python/tests/backends/test_IonQ.py @@ -360,6 +360,48 @@ def ctrl_z_kernel(): assert counts["0010011"] == 1000 +def test_shot_wise_output_with_memory_and_noise_model(): + + cudaq.set_target("ionq", url="http://localhost:{}".format(port), noise='forte-enterprise-1', memory=True) + + @cudaq.kernel + def bell_state(): + qubits = cudaq.qvector(3) + x(qubits[0]) + cx(qubits[0], qubits[1]) + + results = cudaq.sample(bell_state, shots_count=3) + assert(results.get_sequential_data() == ['011', '011', '011']) + + +def test_shot_wise_output_with_memory_but_no_noise_model(): + + cudaq.set_target("ionq", url="http://localhost:{}".format(port), noise='ideal', memory=True) + + @cudaq.kernel + def bell_state(): + qubits = cudaq.qvector(3) + x(qubits[0]) + cx(qubits[0], qubits[1]) + + results = cudaq.sample(bell_state, shots_count=3) + assert(results.get_sequential_data() == []) + + +def test_shot_wise_output_with_noise_model_but_no_memory(): + + cudaq.set_target("ionq", url="http://localhost:{}".format(port), noise='forte-enterprise-1', memory=False) + + @cudaq.kernel + def bell_state(): + qubits = cudaq.qvector(3) + x(qubits[0]) + cx(qubits[0], qubits[1]) + + results = cudaq.sample(bell_state, shots_count=3) + assert(results.get_sequential_data() == []) + + # leave for gdb debugging if __name__ == "__main__": loc = os.path.abspath(__file__) diff --git a/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp b/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp index 59d413232de..ac7ed98677e 100644 --- a/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp +++ b/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp @@ -110,6 +110,8 @@ void IonQServerHelper::initialize(BackendConfig config) { // Retrieve the noise model setting (if provided) if (config.find("noise") != config.end()) backendConfig["noise_model"] = config["noise"]; + else if (config.find("noise_model") != config.end()) + backendConfig["noise_model"] = config["noise_model"]; // Retrieve the API key from the environment variables bool isTokenRequired = [&]() { auto it = config.find("emulate"); @@ -364,14 +366,20 @@ bool IonQServerHelper::jobIsDone(ServerMessage &getJobResponse) { std::string IonQServerHelper::getShotsUrl(nlohmann::json_v3_11_1::json &jobs, const char *DEFAULT_URL) { + if (!keyExists("url")) + throw std::runtime_error("Key 'url' doesn't exist in backendConfig."); + + std::string base_url = backendConfig.at("url"); std::string shotsUrl = ""; - if ( + + if (!jobs.empty() && jobs[0].contains("results") && jobs[0].at("results").contains("shots") && jobs[0].at("results").at("shots").contains("url")) { - shotsUrl = std::string(DEFAULT_URL) + + shotsUrl = base_url + jobs[0].at("results").at("shots").at("url").get(); } + return shotsUrl; } diff --git a/utils/mock_qpu/ionq/__init__.py b/utils/mock_qpu/ionq/__init__.py index 616e37b78fc..8d1992f82b7 100644 --- a/utils/mock_qpu/ionq/__init__.py +++ b/utils/mock_qpu/ionq/__init__.py @@ -152,7 +152,12 @@ async def getJob(id: str): "jobs": [{ "status": "completed", "qubits": numQubitsRequired, - "results_url": "/v0.3/jobs/{}/results".format(id) + "results_url": "/v0.3/jobs/{}/results".format(id), + "results": { + "shots": { + "url": "/v0.4/jobs/{}/results/shots".format(id) + } + } }] } return res @@ -178,6 +183,22 @@ async def getResults(jobId: str): return res +@app.get("/v0.4/jobs/{jobId}/results/shots") +async def getResults(jobId: str): + global countJobGetRequests, createdJobs + + counts = createdJobs[jobId] + counts.dump() + retData = [] + # Note, the real IonQ backend reverses the bitstring relative to what the + # simulator does, so flip the bitstring with [::-1]. + for bits, count in counts.items(): + for _ in range(count): + retData.append(str(int(bits[::-1], 2))) + + res = retData + return res + def startServer(port): uvicorn.run(app, port=port, host='0.0.0.0', log_level="info") From d687584cbc46d740895d7487339904845d66dc71 Mon Sep 17 00:00:00 2001 From: Radu Marginean Date: Wed, 29 Oct 2025 19:33:50 +0200 Subject: [PATCH 4/8] DCO Remediation Commit for Radu Marginean I, Radu Marginean , hereby add my Signed-off-by to this commit: 7e8774dbbc3b4d2e750b6520b6cf5c934f28cda1 I, Radu Marginean , hereby add my Signed-off-by to this commit: 671dd89ed5cd27f3f1b0aa25abc1da5552834125 Signed-off-by: Radu Marginean --- .../platform/default/rest/helpers/ionq/IonQServerHelper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp b/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp index ac7ed98677e..29b85c28377 100644 --- a/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp +++ b/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp @@ -554,4 +554,4 @@ RestHeaders IonQServerHelper::getHeaders() { } // namespace cudaq // Register the IonQ server helper in the CUDA-Q server helper factory -CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::IonQServerHelper, ionq) +CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::IonQServerHelper, ionq) \ No newline at end of file From c6fe0dfe9a909fd9edca60f917973c732c1696b3 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 12 Nov 2025 11:04:16 -0800 Subject: [PATCH 5/8] formatting Signed-off-by: Sachin Pisal --- .../rest/helpers/ionq/IonQServerHelper.cpp | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp b/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp index 29b85c28377..dd2e338d017 100644 --- a/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp +++ b/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp @@ -71,10 +71,11 @@ class IonQServerHelper : public ServerHelper { std::string &jobId) override; /// @brief extract the job shots url from jobs returned by Ion API - std::string getShotsUrl(nlohmann::json_v3_11_1::json &jobs, + std::string getShotsUrl(nlohmann::json_v3_11_1::json &jobs, const char *DEFAULT_URL); - /// @brief Verify if shot-wise output was requested by user and can be extracted + /// @brief Verify if shot-wise output was requested by user and can be + /// extracted bool shotWiseOutputIsNeeded(nlohmann::json_v3_11_1::json &jobs); private: @@ -364,7 +365,7 @@ bool IonQServerHelper::jobIsDone(ServerMessage &getJobResponse) { return jobs[0].at("status").get() == "completed"; } -std::string IonQServerHelper::getShotsUrl(nlohmann::json_v3_11_1::json &jobs, +std::string IonQServerHelper::getShotsUrl(nlohmann::json_v3_11_1::json &jobs, const char *DEFAULT_URL) { if (!keyExists("url")) throw std::runtime_error("Key 'url' doesn't exist in backendConfig."); @@ -372,18 +373,18 @@ std::string IonQServerHelper::getShotsUrl(nlohmann::json_v3_11_1::json &jobs, std::string base_url = backendConfig.at("url"); std::string shotsUrl = ""; - if (!jobs.empty() && - jobs[0].contains("results") && + if (!jobs.empty() && jobs[0].contains("results") && jobs[0].at("results").contains("shots") && jobs[0].at("results").at("shots").contains("url")) { - shotsUrl = base_url + - jobs[0].at("results").at("shots").at("url").get(); + shotsUrl = base_url + + jobs[0].at("results").at("shots").at("url").get(); } return shotsUrl; } -bool IonQServerHelper::shotWiseOutputIsNeeded(nlohmann::json_v3_11_1::json &jobs) { +bool IonQServerHelper::shotWiseOutputIsNeeded( + nlohmann::json_v3_11_1::json &jobs) { std::string noiseModel = "ideal"; if (!jobs.empty() && jobs[0].contains("noise") && jobs[0]["noise"].contains("model")) { @@ -394,12 +395,13 @@ bool IonQServerHelper::shotWiseOutputIsNeeded(nlohmann::json_v3_11_1::json &jobs std::string target = "simulator"; if (!jobs.empty() && jobs[0].contains("target")) { - target = jobs[0]["target"].get(); + target = jobs[0]["target"].get(); } else if (keyExists("target")) { target = backendConfig["target"]; } - bool targetHasMemoryOption = keyExists("memory") && backendConfig["memory"] == "true"; + bool targetHasMemoryOption = + keyExists("memory") && backendConfig["memory"] == "true"; bool targetHasNoiseModel = noiseModel != "ideal"; bool targetIsNotSimulator = target != "simulator"; @@ -516,16 +518,17 @@ IonQServerHelper::processResults(ServerMessage &postJobResponse, int64_t s = std::stoull(element.value().get()); std::string bitString = std::bitset<64>(s).to_string(); auto firstone = bitString.find_first_not_of('0'); - bitString = (firstone == std::string::npos) ? "0" : bitString.substr(firstone); + bitString = + (firstone == std::string::npos) ? "0" : bitString.substr(firstone); if (bitString.size() < static_cast(nQubits)) { - bitString.insert(bitString.begin(), - static_cast(nQubits) - bitString.size(), '0'); - } + bitString.insert(bitString.begin(), + static_cast(nQubits) - bitString.size(), '0'); + } bitStrings.push_back(bitString); } if (!execResults.empty()) - execResults[0].sequentialData = std::move(bitStrings); + execResults[0].sequentialData = std::move(bitStrings); } // Return a sample result including the global register and all individual @@ -554,4 +557,4 @@ RestHeaders IonQServerHelper::getHeaders() { } // namespace cudaq // Register the IonQ server helper in the CUDA-Q server helper factory -CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::IonQServerHelper, ionq) \ No newline at end of file +CUDAQ_REGISTER_TYPE(cudaq::ServerHelper, cudaq::IonQServerHelper, ionq) From d4254720266df9761184d65d680890ab7964e8f1 Mon Sep 17 00:00:00 2001 From: Radu Marginean Date: Fri, 14 Nov 2025 20:23:01 +0200 Subject: [PATCH 6/8] Use only job info to trigger shotwise output behaviour. Fix and extend tests. --- python/tests/backends/test_IonQ.py | 62 ++++++++++++++++--- .../rest/helpers/ionq/IonQServerHelper.cpp | 14 ++--- .../default/rest/helpers/ionq/ionq.yml | 7 ++- utils/mock_qpu/ionq/__init__.py | 28 ++++++++- 4 files changed, 94 insertions(+), 17 deletions(-) diff --git a/python/tests/backends/test_IonQ.py b/python/tests/backends/test_IonQ.py index 8c2e719ac69..aa15629d032 100644 --- a/python/tests/backends/test_IonQ.py +++ b/python/tests/backends/test_IonQ.py @@ -6,7 +6,7 @@ # the terms of the Apache License 2.0 which accompanies this distribution. # # ============================================================================ # -import cudaq, pytest, os +import cudaq, pytest, os, requests from cudaq import spin import numpy as np from typing import List @@ -362,9 +362,14 @@ def ctrl_z_kernel(): assert counts["0010011"] == 1000 -def test_shot_wise_output_with_memory_and_noise_model(): +def test_shot_wise_output_with_memory_and_qpu(): + + url="http://localhost:{}".format(port) + + # set the mock server target + requests.post(f"{url}/_mock_server_config_target?target=aria-1") - cudaq.set_target("ionq", url="http://localhost:{}".format(port), noise='forte-enterprise-1', memory=True) + cudaq.set_target("ionq", url=url, noise='forte-enterprise-1', memory=True) @cudaq.kernel def bell_state(): @@ -375,10 +380,17 @@ def bell_state(): results = cudaq.sample(bell_state, shots_count=3) assert(results.get_sequential_data() == ['011', '011', '011']) + # reset the mock server target + requests.post(f"{url}/_mock_server_config_target?target=") -def test_shot_wise_output_with_memory_but_no_noise_model(): +def test_shot_wise_output_with_no_memory_and_qpu(): - cudaq.set_target("ionq", url="http://localhost:{}".format(port), noise='ideal', memory=True) + url="http://localhost:{}".format(port) + + # set the mock server target + requests.post(f"{url}/_mock_server_config_target?target=aria-1") + + cudaq.set_target("ionq", url=url, noise='forte-enterprise-1', memory=False) @cudaq.kernel def bell_state(): @@ -389,10 +401,42 @@ def bell_state(): results = cudaq.sample(bell_state, shots_count=3) assert(results.get_sequential_data() == []) + # reset the mock server target + requests.post(f"{url}/_mock_server_config_target?target=") + +def test_shot_wise_output_with_memory_and_noise_model(): + + url="http://localhost:{}".format(port) + + # set the mock server target + requests.post(f"{url}/_mock_server_config_target?target=simulator") + requests.post(f"{url}/_mock_server_config_noise_model?noise=aria-1") + + cudaq.set_target("ionq", url=url, noise='forte-enterprise-1', memory=True) + + @cudaq.kernel + def bell_state(): + qubits = cudaq.qvector(3) + x(qubits[0]) + cx(qubits[0], qubits[1]) + + results = cudaq.sample(bell_state, shots_count=3) + assert(results.get_sequential_data() == ['011', '011', '011']) + + # reset the mock server target + requests.post(f"{url}/_mock_server_config_target?target=") + requests.post(f"{url}/_mock_server_config_noise_model?noise=") + -def test_shot_wise_output_with_noise_model_but_no_memory(): +def test_shot_wise_output_with_no_memory_and_noise_model(): - cudaq.set_target("ionq", url="http://localhost:{}".format(port), noise='forte-enterprise-1', memory=False) + url="http://localhost:{}".format(port) + + # set the mock server target + requests.post(f"{url}/_mock_server_config_target?target=simulator") + requests.post(f"{url}/_mock_server_config_noise_model?noise=aria-1") + + cudaq.set_target("ionq", url=url, noise='forte-enterprise-1', memory=False) @cudaq.kernel def bell_state(): @@ -403,6 +447,10 @@ def bell_state(): results = cudaq.sample(bell_state, shots_count=3) assert(results.get_sequential_data() == []) + # reset the mock server target + requests.post(f"{url}/_mock_server_config_target?target=") + requests.post(f"{url}/_mock_server_config_noise_model?noise=") + # leave for gdb debugging if __name__ == "__main__": diff --git a/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp b/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp index dd2e338d017..ae78d52e194 100644 --- a/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp +++ b/runtime/cudaq/platform/default/rest/helpers/ionq/IonQServerHelper.cpp @@ -137,9 +137,11 @@ void IonQServerHelper::initialize(BackendConfig config) { if (config.find("format") != config.end()) backendConfig["format"] = config["format"]; - // Enable memory (return shot-wise bitstrings instead of counts) + // Enable memory, true by default if (config.find("memory") != config.end()) backendConfig["memory"] = config["memory"]; + else + backendConfig["memory"] = "true"; } // Implementation of the getValueOrDefault function @@ -389,23 +391,19 @@ bool IonQServerHelper::shotWiseOutputIsNeeded( if (!jobs.empty() && jobs[0].contains("noise") && jobs[0]["noise"].contains("model")) { noiseModel = jobs[0]["noise"]["model"].get(); - } else if (keyExists("noise_model")) { - noiseModel = backendConfig["noise_model"]; } std::string target = "simulator"; if (!jobs.empty() && jobs[0].contains("target")) { target = jobs[0]["target"].get(); - } else if (keyExists("target")) { - target = backendConfig["target"]; } bool targetHasMemoryOption = keyExists("memory") && backendConfig["memory"] == "true"; - bool targetHasNoiseModel = noiseModel != "ideal"; - bool targetIsNotSimulator = target != "simulator"; + bool noiseModelIsNotIdeal = noiseModel != "ideal"; + bool targetIsQpu = target != "simulator"; - return targetHasMemoryOption && (targetHasNoiseModel || targetIsNotSimulator); + return targetHasMemoryOption && (targetIsQpu || noiseModelIsNotIdeal); } // Process the results from a job diff --git a/runtime/cudaq/platform/default/rest/helpers/ionq/ionq.yml b/runtime/cudaq/platform/default/rest/helpers/ionq/ionq.yml index 8a2270fdc40..462f41dcade 100644 --- a/runtime/cudaq/platform/default/rest/helpers/ionq/ionq.yml +++ b/runtime/cudaq/platform/default/rest/helpers/ionq/ionq.yml @@ -41,10 +41,15 @@ target-arguments: - key: debias required: false type: string - platform-arg: debias + platform-arg: debias help-string: "Specify debiasing." - key: sharpen required: false type: string platform-arg: sharpen help-string: "Specify sharpening." + - key: memory + required: false + type: boolean + platform-arg: memory + help-string: "When `true`, returns sequential data for QPU or noisy simulation runs." \ No newline at end of file diff --git a/utils/mock_qpu/ionq/__init__.py b/utils/mock_qpu/ionq/__init__.py index 8d1992f82b7..196d550090b 100644 --- a/utils/mock_qpu/ionq/__init__.py +++ b/utils/mock_qpu/ionq/__init__.py @@ -39,6 +39,12 @@ class Job(BaseModel): # Save how many qubits were needed for each test (emulates real backend) numQubitsRequired = 0 +# Sets the target for the job +jobTarget = "" + +# Sets the noise model for the job +noiseModel = "" + llvm.initialize() llvm.initialize_native_target() llvm.initialize_native_asmprinter() @@ -141,7 +147,7 @@ async def postJob(job: Job, @app.get("/v0.3/jobs") async def getJob(id: str): global countJobGetRequests, createdJobs, numQubitsRequired - + global jobTarget, noiseModel # Simulate asynchronous execution if countJobGetRequests < 3: countJobGetRequests += 1 @@ -160,6 +166,11 @@ async def getJob(id: str): } }] } + if jobTarget: + res["jobs"][0]["target"] = jobTarget + if noiseModel: + res["jobs"][0]["noise"] = {"model": noiseModel} + return res @@ -199,6 +210,21 @@ async def getResults(jobId: str): res = retData return res + +@app.post("/_mock_server_config_target") +async def set_mock_server_target(target: str): + global jobTarget + jobTarget = target + return {"status": "ok"} + + +@app.post("/_mock_server_config_noise_model") +async def set_mock_server_noise_model(noise: str): + global noiseModel + noiseModel = noise + return {"status": "ok"} + + def startServer(port): uvicorn.run(app, port=port, host='0.0.0.0', log_level="info") From 86218fb6834984d2fe102f948d5da806a5eb3b04 Mon Sep 17 00:00:00 2001 From: Radu Marginean Date: Wed, 19 Nov 2025 12:33:31 +0200 Subject: [PATCH 7/8] Expose the memory argument for C++ users. --- runtime/cudaq/platform/default/rest/helpers/ionq/ionq.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/cudaq/platform/default/rest/helpers/ionq/ionq.yml b/runtime/cudaq/platform/default/rest/helpers/ionq/ionq.yml index 462f41dcade..f1c56ed1a88 100644 --- a/runtime/cudaq/platform/default/rest/helpers/ionq/ionq.yml +++ b/runtime/cudaq/platform/default/rest/helpers/ionq/ionq.yml @@ -50,6 +50,6 @@ target-arguments: help-string: "Specify sharpening." - key: memory required: false - type: boolean + type: string platform-arg: memory - help-string: "When `true`, returns sequential data for QPU or noisy simulation runs." \ No newline at end of file + help-string: "When `true`, returns sequential data for QPU or noisy simulation runs. Default is `true`." \ No newline at end of file From ba6c2882e2468435ef4560ecb838302773e886f9 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 19 Nov 2025 08:58:20 -0800 Subject: [PATCH 8/8] format Signed-off-by: Sachin Pisal --- python/tests/backends/test_IonQ.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/python/tests/backends/test_IonQ.py b/python/tests/backends/test_IonQ.py index aa15629d032..2e18a6a4747 100644 --- a/python/tests/backends/test_IonQ.py +++ b/python/tests/backends/test_IonQ.py @@ -364,7 +364,7 @@ def ctrl_z_kernel(): def test_shot_wise_output_with_memory_and_qpu(): - url="http://localhost:{}".format(port) + url = "http://localhost:{}".format(port) # set the mock server target requests.post(f"{url}/_mock_server_config_target?target=aria-1") @@ -378,14 +378,15 @@ def bell_state(): cx(qubits[0], qubits[1]) results = cudaq.sample(bell_state, shots_count=3) - assert(results.get_sequential_data() == ['011', '011', '011']) + assert (results.get_sequential_data() == ['011', '011', '011']) # reset the mock server target requests.post(f"{url}/_mock_server_config_target?target=") + def test_shot_wise_output_with_no_memory_and_qpu(): - url="http://localhost:{}".format(port) + url = "http://localhost:{}".format(port) # set the mock server target requests.post(f"{url}/_mock_server_config_target?target=aria-1") @@ -399,14 +400,15 @@ def bell_state(): cx(qubits[0], qubits[1]) results = cudaq.sample(bell_state, shots_count=3) - assert(results.get_sequential_data() == []) + assert (results.get_sequential_data() == []) # reset the mock server target requests.post(f"{url}/_mock_server_config_target?target=") + def test_shot_wise_output_with_memory_and_noise_model(): - url="http://localhost:{}".format(port) + url = "http://localhost:{}".format(port) # set the mock server target requests.post(f"{url}/_mock_server_config_target?target=simulator") @@ -421,7 +423,7 @@ def bell_state(): cx(qubits[0], qubits[1]) results = cudaq.sample(bell_state, shots_count=3) - assert(results.get_sequential_data() == ['011', '011', '011']) + assert (results.get_sequential_data() == ['011', '011', '011']) # reset the mock server target requests.post(f"{url}/_mock_server_config_target?target=") @@ -430,7 +432,7 @@ def bell_state(): def test_shot_wise_output_with_no_memory_and_noise_model(): - url="http://localhost:{}".format(port) + url = "http://localhost:{}".format(port) # set the mock server target requests.post(f"{url}/_mock_server_config_target?target=simulator") @@ -445,7 +447,7 @@ def bell_state(): cx(qubits[0], qubits[1]) results = cudaq.sample(bell_state, shots_count=3) - assert(results.get_sequential_data() == []) + assert (results.get_sequential_data() == []) # reset the mock server target requests.post(f"{url}/_mock_server_config_target?target=")