From ca259f7271f16163920788d8311e080ec19c800d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erc=C3=BCment=20Kaya?= Date: Wed, 17 Jun 2026 17:32:49 +0200 Subject: [PATCH 01/10] =?UTF-8?q?=E2=9C=A8=20C-bindings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 3 ++- include/mqss-c/client.h | 48 +++++++++++++++++++++++++++++++++++++++ include/mqss-c/job.h | 46 +++++++++++++++++++++++++++++++++++++ include/mqss-c/resource.h | 40 ++++++++++++++++++++++++++++++++ include/mqss/client.h | 8 +++++++ include/mqss/resource.h | 10 +++++--- src/client.cpp | 38 +++++++++++++++++++++++++++++++ src/job.cpp | 9 ++++++++ src/resource.cpp | 35 ++++++++++++++++++++++++++++ 9 files changed, 233 insertions(+), 4 deletions(-) create mode 100644 include/mqss-c/client.h create mode 100644 include/mqss-c/job.h create mode 100644 include/mqss-c/resource.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d861af..821c84e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,7 @@ project( MQSS-Client VERSION 0.1 DESCRIPTION "MQSS Client" - LANGUAGES CXX) + LANGUAGES CXX C) # Generate compile_commands.json to make it easier to work with clang based tools set(CMAKE_EXPORT_COMPILE_COMMANDS @@ -33,6 +33,7 @@ option(BUILD_TESTS "Build the tests" OFF) option(BUILD_UNIT_TESTS "Build the unit tests" OFF) option(BUILD_INTEGRATION_TESTS "Build the unit tests" OFF) option(ENABLE_COVERAGE "Enabling coverage" OFF) +option(BUILD_BINDINGS "Build Python Bindings" OFF) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") diff --git a/include/mqss-c/client.h b/include/mqss-c/client.h new file mode 100644 index 0000000..35926f6 --- /dev/null +++ b/include/mqss-c/client.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 - 2026 MQSS Project + * All rights reserved. + * + * Licensed under the Apache License v2.0 with LLVM Exceptions (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://llvm.org/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifdef __cplusplus +extern "C" { +#endif +#include "job.h" +#include "resource.h" + +#include + +typedef struct MQSSOpaqueClient* MQSSClientRef; + +MQSSClientRef mqssClientCreateClient(char* token, char* urlOrQueue, bool isHpc); + +MQSSResourceRef* mqssClientGetAllResources(MQSSClientRef client, int* size); + +MQSSResourceRef* mqssClientGetResourceInfo(MQSSClientRef client, + char* resourceName); + +int mqssClientSubmitJob(MQSSClientRef client, MQSSJobRef job); + +void mqssClientCancelJob(MQSSClientRef client, MQSSJobRef job); + +MQSSJobResultRef mqssClientGetJobResult(MQSSClientRef client, MQSSJobRef job, bool wait, + unsigned int timeout); + +int mqssClientGetNumberPendingJobs(MQSSClientRef client, char* resourceName, + int pendingJobNumber); +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/include/mqss-c/job.h b/include/mqss-c/job.h new file mode 100644 index 0000000..eb0d210 --- /dev/null +++ b/include/mqss-c/job.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 - 2026 MQSS Project + * All rights reserved. + * + * Licensed under the Apache License v2.0 with LLVM Exceptions (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://llvm.org/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + + +#ifdef __cplusplus +extern "C" { +#endif +#include +typedef struct MQSSOpaqueJob *MQSSJobRef; + +typedef struct MQSSOpaqueJobResult *MQSSJobResultRef; + +MQSSJobRef mqssClientCreateCircuitJob(char* circuit, + char* circuitFormat, char* resourceName, + unsigned int shots, bool noModify, bool queued); + +int mqssClientCreateHamiltonianJob(MQSSJobRef job, char* resourceName, + char* interactionStr, char* coefficientsStr); + +int mqssClientGetJobResultCounts(MQSSJobResultRef jobResult, char** bitstreams, int* counts, int size); + +int mqssClientGetJobResultCompletedTimestamp(MQSSJobResultRef jobResult, unsigned int completedTimestamp); + +int mqssClientGetJobResultSubmittedTimestamp(MQSSJobResultRef jobResult, unsigned int submittedTimestamp); + +int mqssClientGetJobResultScheduledTimestamp(MQSSJobResultRef jobResult, unsigned int scheduledTimestamp); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/include/mqss-c/resource.h b/include/mqss-c/resource.h new file mode 100644 index 0000000..9ae91c6 --- /dev/null +++ b/include/mqss-c/resource.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024 - 2026 MQSS Project + * All rights reserved. + * + * Licensed under the Apache License v2.0 with LLVM Exceptions (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://llvm.org/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#ifdef __cplusplus +extern "C" { +#endif +#include + +typedef struct MQSSOpaqueResource *MQSSResourceRef; + +typedef struct MQSSOpaqueGate *MQSSGateRef; + +int mqssClientResourceGetInfo(MQSSResourceRef resource, char** name, + unsigned* qubitCount, bool* online, + int** couplingMap, MQSSGateRef** nativeGateset, + unsigned int* gateCount); + +int mqssClientResourceGetGateInfo(MQSSGateRef gate, unsigned int qubitNumber, + unsigned int parameterNumber, + int* supportedQubits); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/include/mqss/client.h b/include/mqss/client.h index 222c415..ec58d04 100644 --- a/include/mqss/client.h +++ b/include/mqss/client.h @@ -32,6 +32,14 @@ #define MQP_DEFAULT_URL "https://portal.quantum.lrz.de:4000/v1/" +template inline T* unwrap(RefT ref) { + return reinterpret_cast(ref); +} + +template inline RefT wrap(T* ptr) { + return reinterpret_cast(ptr); +} + namespace mqss::client { class MQSSBaseClient { diff --git a/include/mqss/resource.h b/include/mqss/resource.h index ba6bbf8..4d4c8cc 100644 --- a/include/mqss/resource.h +++ b/include/mqss/resource.h @@ -33,6 +33,12 @@ class Gate { mParameterNumber(parameterNumber), mSupportedQubits(std::move(supportedQubits)) {} + Gate(const Gate&) = default; + Gate& operator=(const Gate&) = default; + + Gate(Gate&&) = default; + Gate& operator=(Gate&&) = default; + const std::string& getName() const noexcept { return mName; } const unsigned int& getQubitNumber() const noexcept { return mQubitNumber; } @@ -69,9 +75,7 @@ class Resource { return mCouplingMap; } - const std::vector& getNativeGateset() const noexcept { - return mNativeGateset; - } + std::vector& getNativeGateset() { return mNativeGateset; } private: std::string mName; diff --git a/src/client.cpp b/src/client.cpp index 36eb940..112cb27 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -21,6 +21,7 @@ #include "clients/hpc_client.h" #include "clients/rest_client.h" +#include "mqss-c/client.h" #include #include @@ -139,3 +140,40 @@ int MQSSClient::getNumberPendingJobs(const std::string& resource) const { return parsed.value("num_pending_jobs", -1); } + +MQSSClientRef mqssClientCreateClient(char* token, char* urlOrQueue, + bool isHpc) { + + return wrap(new MQSSClient(token, urlOrQueue, isHpc)); +} +MQSSResourceRef* mqssClientGetAllResources(MQSSClientRef client, int* size) { + auto resources_ = unwrap(client)->getAllResources(); + + auto resources = + (MQSSResourceRef*)malloc(resources_.size() * sizeof(MQSSResourceRef)); + + for (size_t i = 0; i < resources_.size(); ++i) { + resources[i] = wrap(new Resource(resources_[i])); + } + + *size = (int)resources_.size(); + return resources; +} + +int mqssClientSubmitJob(MQSSClientRef client, MQSSJobRef job) { + auto uuid = + unwrap(client)->submitJob(*unwrap(job)); + if (!uuid.has_value()) { + return -1; + } + return std::stoi(*uuid); +} + +MQSSJobResultRef mqssClientGetJobResult(MQSSClientRef client, MQSSJobRef job, + bool wait, unsigned int timeout) { + + std::unique_ptr jobResult = unwrap(client)->getJobResult(*unwrap(job), + wait, timeout); + std::cout << jobResult->getTimestampCompleted() << "\n"; + return wrap(jobResult.get()); +} \ No newline at end of file diff --git a/src/job.cpp b/src/job.cpp index d5b38b0..04dac8e 100644 --- a/src/job.cpp +++ b/src/job.cpp @@ -18,6 +18,8 @@ */ #include "mqss/job.h" +#include "mqss-c/job.h" +#include "mqss/client.h" using namespace mqss::client; @@ -96,3 +98,10 @@ JobResult::JobResult(const nlohmann::json& parsed) { mTimestampSubmitted = parsed.at("timestamp_submitted").get(); mTimestampScheduled = parsed.at("timestamp_scheduled").get(); } + +MQSSJobRef mqssClientCreateCircuitJob(char* circuit, + char* circuitFormat, char* resourceName, + unsigned int shots, bool noModify, bool queued) { + return wrap(new CircuitJobRequest( + circuit, circuitFormat, resourceName, shots, noModify, queued)); +} diff --git a/src/resource.cpp b/src/resource.cpp index e0635f4..d6fa523 100644 --- a/src/resource.cpp +++ b/src/resource.cpp @@ -19,6 +19,8 @@ #include "mqss/resource.h" +#include "mqss-c/resource.h" +#include "mqss/client.h" #include using namespace mqss::client; @@ -170,3 +172,36 @@ Resource::Resource(const nlohmann::json& json) { mCouplingMap = std::move(couplingMap); mNativeGateset = std::move(nativeGateset); } + +int mqssClientResourceGetInfo(MQSSResourceRef resource, char** name, + unsigned* qubitCount, bool* online, + int** couplingMap, MQSSGateRef** nativeGateset, + unsigned int* gateCount) { + auto* resource_ = unwrap(resource); + if (resource_ == nullptr) + return -2; + + // Name + if (asprintf(name, "%s", resource_->getName().c_str()) < 0) + return -3; + + *qubitCount = resource_->getQubitCount(); + *online = resource_->isOnline(); + + *couplingMap = nullptr; + + auto& gates = resource_->getNativeGateset(); + + *gateCount = gates.size(); + + *nativeGateset = (MQSSGateRef*)malloc(sizeof(MQSSGateRef) * gates.size()); + + if (!*nativeGateset) + return -4; + + for (size_t i = 0; i < gates.size(); ++i) { + (*nativeGateset)[i] = wrap(&gates[i]); + } + + return 0; +} \ No newline at end of file From e121edff08c743b2d4063dc4cf1cb6428aafd330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erc=C3=BCment=20Kaya?= Date: Mon, 22 Jun 2026 16:11:02 +0200 Subject: [PATCH 02/10] additional implementations --- bindings/resource.cpp | 6 ++- include/mqss-c/client.h | 6 +-- include/mqss-c/job.h | 26 +++++---- include/mqss-c/resource.h | 6 +-- include/mqss/resource.h | 2 +- src/client.cpp | 10 ++-- src/job.cpp | 108 ++++++++++++++++++++++++++++++++++++-- src/resource.cpp | 3 +- 8 files changed, 138 insertions(+), 29 deletions(-) diff --git a/bindings/resource.cpp b/bindings/resource.cpp index 2aedbbe..ca2d68a 100644 --- a/bindings/resource.cpp +++ b/bindings/resource.cpp @@ -34,8 +34,10 @@ void registerResourceInterface(const py::module& m) { py::class_(m, "Gate") .def_property_readonly("name", &mqss::client::Gate::getName) - .def_property_readonly("qubit_number", &mqss::client::Gate::getQubitNumber) - .def_property_readonly("parameter_number", &mqss::client::Gate::getParameterNumber) + .def_property_readonly("qubit_number", + &mqss::client::Gate::getQubitNumber) + .def_property_readonly("parameter_number", + &mqss::client::Gate::getParameterNumber) .def_property_readonly("supported_qubits", &mqss::client::Gate::getSupportedQubits); } diff --git a/include/mqss-c/client.h b/include/mqss-c/client.h index 35926f6..33e1864 100644 --- a/include/mqss-c/client.h +++ b/include/mqss-c/client.h @@ -38,11 +38,11 @@ int mqssClientSubmitJob(MQSSClientRef client, MQSSJobRef job); void mqssClientCancelJob(MQSSClientRef client, MQSSJobRef job); -MQSSJobResultRef mqssClientGetJobResult(MQSSClientRef client, MQSSJobRef job, bool wait, - unsigned int timeout); +MQSSJobResultRef mqssClientGetJobResult(MQSSClientRef client, MQSSJobRef job, + bool wait, unsigned int timeout); int mqssClientGetNumberPendingJobs(MQSSClientRef client, char* resourceName, int pendingJobNumber); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/include/mqss-c/job.h b/include/mqss-c/job.h index eb0d210..446368d 100644 --- a/include/mqss-c/job.h +++ b/include/mqss-c/job.h @@ -17,30 +17,34 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ - +#include #ifdef __cplusplus extern "C" { #endif #include -typedef struct MQSSOpaqueJob *MQSSJobRef; +typedef struct MQSSOpaqueJob* MQSSJobRef; -typedef struct MQSSOpaqueJobResult *MQSSJobResultRef; +typedef struct MQSSOpaqueJobResult* MQSSJobResultRef; -MQSSJobRef mqssClientCreateCircuitJob(char* circuit, - char* circuitFormat, char* resourceName, - unsigned int shots, bool noModify, bool queued); +MQSSJobRef mqssClientCreateCircuitJob(char* circuit, char* circuitFormat, + char* resourceName, unsigned int shots, + bool noModify, bool queued); int mqssClientCreateHamiltonianJob(MQSSJobRef job, char* resourceName, char* interactionStr, char* coefficientsStr); -int mqssClientGetJobResultCounts(MQSSJobResultRef jobResult, char** bitstreams, int* counts, int size); +int mqssClientGetJobResultCounts(MQSSJobResultRef jobResult, char*** bitstreams, + int** counts, int* size); -int mqssClientGetJobResultCompletedTimestamp(MQSSJobResultRef jobResult, unsigned int completedTimestamp); +int mqssClientGetJobResultCompletedTimestamp(MQSSJobResultRef jobResult, + uint64_t* completedTimestamp); -int mqssClientGetJobResultSubmittedTimestamp(MQSSJobResultRef jobResult, unsigned int submittedTimestamp); +int mqssClientGetJobResultSubmittedTimestamp(MQSSJobResultRef jobResult, + uint64_t* submittedTimestamp); -int mqssClientGetJobResultScheduledTimestamp(MQSSJobResultRef jobResult, unsigned int scheduledTimestamp); +int mqssClientGetJobResultScheduledTimestamp(MQSSJobResultRef jobResult, + uint64_t* scheduledTimestamp); #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/include/mqss-c/resource.h b/include/mqss-c/resource.h index 9ae91c6..6a6fd01 100644 --- a/include/mqss-c/resource.h +++ b/include/mqss-c/resource.h @@ -22,9 +22,9 @@ extern "C" { #endif #include -typedef struct MQSSOpaqueResource *MQSSResourceRef; +typedef struct MQSSOpaqueResource* MQSSResourceRef; -typedef struct MQSSOpaqueGate *MQSSGateRef; +typedef struct MQSSOpaqueGate* MQSSGateRef; int mqssClientResourceGetInfo(MQSSResourceRef resource, char** name, unsigned* qubitCount, bool* online, @@ -37,4 +37,4 @@ int mqssClientResourceGetGateInfo(MQSSGateRef gate, unsigned int qubitNumber, #ifdef __cplusplus } -#endif \ No newline at end of file +#endif diff --git a/include/mqss/resource.h b/include/mqss/resource.h index 4d4c8cc..0c41d0f 100644 --- a/include/mqss/resource.h +++ b/include/mqss/resource.h @@ -38,7 +38,7 @@ class Gate { Gate(Gate&&) = default; Gate& operator=(Gate&&) = default; - + const std::string& getName() const noexcept { return mName; } const unsigned int& getQubitNumber() const noexcept { return mQubitNumber; } diff --git a/src/client.cpp b/src/client.cpp index 112cb27..0d3ae37 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -172,8 +172,8 @@ int mqssClientSubmitJob(MQSSClientRef client, MQSSJobRef job) { MQSSJobResultRef mqssClientGetJobResult(MQSSClientRef client, MQSSJobRef job, bool wait, unsigned int timeout) { - std::unique_ptr jobResult = unwrap(client)->getJobResult(*unwrap(job), - wait, timeout); - std::cout << jobResult->getTimestampCompleted() << "\n"; - return wrap(jobResult.get()); -} \ No newline at end of file + std::unique_ptr jobResult = + unwrap(client)->getJobResult(*unwrap(job), + wait, timeout); + return wrap(jobResult.release()); +} diff --git a/src/job.cpp b/src/job.cpp index 04dac8e..019665d 100644 --- a/src/job.cpp +++ b/src/job.cpp @@ -18,6 +18,7 @@ */ #include "mqss/job.h" + #include "mqss-c/job.h" #include "mqss/client.h" @@ -99,9 +100,110 @@ JobResult::JobResult(const nlohmann::json& parsed) { mTimestampScheduled = parsed.at("timestamp_scheduled").get(); } -MQSSJobRef mqssClientCreateCircuitJob(char* circuit, - char* circuitFormat, char* resourceName, - unsigned int shots, bool noModify, bool queued) { +MQSSJobRef mqssClientCreateCircuitJob(char* circuit, char* circuitFormat, + char* resourceName, unsigned int shots, + bool noModify, bool queued) { return wrap(new CircuitJobRequest( circuit, circuitFormat, resourceName, shots, noModify, queued)); } + +int mqssClientGetJobResultCounts(MQSSJobResultRef jobResult, char*** bitstreams, + int** counts, int* size) { + if (!jobResult || !bitstreams || !counts || !size) + return -1; + + auto results = unwrap(jobResult)->getResults(); + + // Count total entries across all result maps. + size_t totalEntries = 0; + for (const auto& result : results) + totalEntries += result.size(); + + *size = static_cast(totalEntries); + + if (totalEntries == 0) { + *bitstreams = nullptr; + *counts = nullptr; + return 0; + } + + *counts = static_cast(malloc(sizeof(int) * totalEntries)); + *bitstreams = static_cast(malloc(sizeof(char*) * totalEntries)); + + if (!*counts || !*bitstreams) { + free(*counts); + free(*bitstreams); + return -1; + } + + size_t index = 0; + + for (const auto& result : results) { + for (const auto& [bitstream, count] : result) { + + (*bitstreams)[index] = static_cast(malloc(bitstream.size() + 1)); + + if (!(*bitstreams)[index]) { + for (size_t i = 0; i < index; ++i) + free((*bitstreams)[i]); + free(*bitstreams); + free(*counts); + return -1; + } + + std::memcpy((*bitstreams)[index], bitstream.c_str(), + bitstream.size() + 1); + + (*counts)[index] = static_cast(count); + + ++index; + } + } + + return 0; +} + +uint64_t parseTimestamp(const std::string& s) { + + std::tm tm = {}; + std::istringstream ss(s.substr(0, 19)); // YYYY-MM-DD HH:MM:SS + + ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S"); + + auto tt = std::mktime(&tm); + + auto tp = std::chrono::system_clock::from_time_t(tt); + + // Parse microseconds + auto dot = s.find('.'); + if (dot != std::string::npos) { + int micros = std::stoi(s.substr(dot + 1)); + tp += std::chrono::microseconds(micros); + } + return std::chrono::duration_cast( + tp.time_since_epoch()) + .count(); +} +int mqssClientGetJobResultCompletedTimestamp(MQSSJobResultRef jobResult, + uint64_t* completedTimestamp) { + + *completedTimestamp = + parseTimestamp(unwrap(jobResult)->getTimestampCompleted()); + return 0; +} + +int mqssClientGetJobResultSubmittedTimestamp(MQSSJobResultRef jobResult, + uint64_t* submittedTimestamp) { + + *submittedTimestamp = + parseTimestamp(unwrap(jobResult)->getTimestampSubmitted()); + return 0; +} + +int mqssClientGetJobResultScheduledTimestamp(MQSSJobResultRef jobResult, + uint64_t* scheduledTimestamp) { + + *scheduledTimestamp = + parseTimestamp(unwrap(jobResult)->getTimestampScheduled()); + return 0; +} diff --git a/src/resource.cpp b/src/resource.cpp index d6fa523..4f96752 100644 --- a/src/resource.cpp +++ b/src/resource.cpp @@ -21,6 +21,7 @@ #include "mqss-c/resource.h" #include "mqss/client.h" + #include using namespace mqss::client; @@ -204,4 +205,4 @@ int mqssClientResourceGetInfo(MQSSResourceRef resource, char** name, } return 0; -} \ No newline at end of file +} From 2630cd5331cc676c5cc7d8af1aae99e306fe3752 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erc=C3=BCment=20Kaya?= Date: Wed, 1 Jul 2026 10:46:09 +0200 Subject: [PATCH 03/10] =?UTF-8?q?=F0=9F=8E=A8=20minor=20name=20change?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bindings/job.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bindings/job.cpp b/bindings/job.cpp index fb2ff81..6bdbbdd 100644 --- a/bindings/job.cpp +++ b/bindings/job.cpp @@ -66,10 +66,10 @@ void registerJobInterface(const py::module& m) { py::class_(m, "JobResult") .def_property_readonly("results", &mqss::client::JobResult::getResults) - .def_property_readonly("completion_timestamp", + .def_property_readonly("timestamp_completed", &mqss::client::JobResult::getTimestampCompleted) - .def_property_readonly("submission_timestamp", + .def_property_readonly("timestamp_submitted", &mqss::client::JobResult::getTimestampSubmitted) - .def_property_readonly("scheduled_timestamp", + .def_property_readonly("res.timestamp_scheduled", &mqss::client::JobResult::getTimestampScheduled); } From 56b2c96af1dcc4ff61fdf8dca986eb8e14a4610a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erc=C3=BCment=20Kaya?= Date: Wed, 1 Jul 2026 10:57:12 +0200 Subject: [PATCH 04/10] =?UTF-8?q?=E2=9C=8F=EF=B8=8F=20fix=20typo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bindings/job.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/job.cpp b/bindings/job.cpp index 6bdbbdd..2336b2f 100644 --- a/bindings/job.cpp +++ b/bindings/job.cpp @@ -70,6 +70,6 @@ void registerJobInterface(const py::module& m) { &mqss::client::JobResult::getTimestampCompleted) .def_property_readonly("timestamp_submitted", &mqss::client::JobResult::getTimestampSubmitted) - .def_property_readonly("res.timestamp_scheduled", + .def_property_readonly("timestamp_scheduled", &mqss::client::JobResult::getTimestampScheduled); } From 411b69c844817c1edfa69430784204da4624b640 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erc=C3=BCment=20Kaya?= Date: Fri, 3 Jul 2026 16:51:03 +0200 Subject: [PATCH 05/10] =?UTF-8?q?=E2=9C=A8=20C-Bindings=20Implementations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .clang-tidy | 4 +- include/mqss-c/client.h | 15 ++++---- include/mqss-c/job.h | 12 +++--- include/mqss-c/resource.h | 12 +++--- src/client.cpp | 28 ++++++++++++-- src/job.cpp | 17 ++++++--- src/resource.cpp | 78 ++++++++++++++++++++++++++++++--------- 7 files changed, 117 insertions(+), 49 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 7be7a6f..86090d6 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -30,8 +30,6 @@ Checks: | CheckOptions: - key: readability-identifier-naming.ClassCase value: CamelCase - - key: readability-identifier-naming.ClassIgnoredRegexp - value: ".*ZX.*|.*SWAP.*|.*CEX.*|.*DD.*|.*EQ.*" - key: readability-identifier-naming.ConstantParameterCase value: camelBack - key: readability-identifier-naming.EnumCase @@ -41,7 +39,7 @@ CheckOptions: - key: readability-identifier-naming.FunctionCase value: camelBack - key: readability-identifier-naming.FunctionIgnoredRegexp - value: ".*ZX.*|.*SWAP.*|.*CEX.*|.*DD.*|.*EQ.*" + value: ".*MQSS.* - key: readability-identifier-naming.GlobalConstantCase value: UPPER_CASE - key: readability-identifier-naming.IgnoreMainLikeFunctions diff --git a/include/mqss-c/client.h b/include/mqss-c/client.h index 33e1864..fa1ca63 100644 --- a/include/mqss-c/client.h +++ b/include/mqss-c/client.h @@ -27,22 +27,21 @@ extern "C" { typedef struct MQSSOpaqueClient* MQSSClientRef; -MQSSClientRef mqssClientCreateClient(char* token, char* urlOrQueue, bool isHpc); +MQSSClientRef MQSSClientCreateClient(char* token, char* urlOrQueue, bool isHpc); -MQSSResourceRef* mqssClientGetAllResources(MQSSClientRef client, int* size); +MQSSResourceRef* MQSSClientGetAllResources(MQSSClientRef client, int* size); -MQSSResourceRef* mqssClientGetResourceInfo(MQSSClientRef client, +MQSSResourceRef MQSSClientGetResourceInfo(MQSSClientRef client, char* resourceName); -int mqssClientSubmitJob(MQSSClientRef client, MQSSJobRef job); +int MQSSClientSubmitJob(MQSSClientRef client, MQSSJobRef job); -void mqssClientCancelJob(MQSSClientRef client, MQSSJobRef job); +void MQSSClientCancelJob(MQSSClientRef client, MQSSJobRef job); -MQSSJobResultRef mqssClientGetJobResult(MQSSClientRef client, MQSSJobRef job, +MQSSJobResultRef MQSSClientGetJobResult(MQSSClientRef client, MQSSJobRef job, bool wait, unsigned int timeout); -int mqssClientGetNumberPendingJobs(MQSSClientRef client, char* resourceName, - int pendingJobNumber); +int MQSSClientGetNumberPendingJobs(MQSSClientRef client, char* resourceName); #ifdef __cplusplus } #endif diff --git a/include/mqss-c/job.h b/include/mqss-c/job.h index 446368d..e156a14 100644 --- a/include/mqss-c/job.h +++ b/include/mqss-c/job.h @@ -26,23 +26,23 @@ typedef struct MQSSOpaqueJob* MQSSJobRef; typedef struct MQSSOpaqueJobResult* MQSSJobResultRef; -MQSSJobRef mqssClientCreateCircuitJob(char* circuit, char* circuitFormat, +MQSSJobRef MQSSClientCreateCircuitJob(char* circuit, char* circuitFormat, char* resourceName, unsigned int shots, bool noModify, bool queued); -int mqssClientCreateHamiltonianJob(MQSSJobRef job, char* resourceName, +MQSSJobRef MQSSClientCreateHamiltonianJob(char* resourceName, char* interactionStr, char* coefficientsStr); -int mqssClientGetJobResultCounts(MQSSJobResultRef jobResult, char*** bitstreams, +int MQSSClientGetJobResultCounts(MQSSJobResultRef jobResult, char*** bitstreams, int** counts, int* size); -int mqssClientGetJobResultCompletedTimestamp(MQSSJobResultRef jobResult, +int MQSSClientGetJobResultCompletedTimestamp(MQSSJobResultRef jobResult, uint64_t* completedTimestamp); -int mqssClientGetJobResultSubmittedTimestamp(MQSSJobResultRef jobResult, +int MQSSClientGetJobResultSubmittedTimestamp(MQSSJobResultRef jobResult, uint64_t* submittedTimestamp); -int mqssClientGetJobResultScheduledTimestamp(MQSSJobResultRef jobResult, +int MQSSClientGetJobResultScheduledTimestamp(MQSSJobResultRef jobResult, uint64_t* scheduledTimestamp); #ifdef __cplusplus diff --git a/include/mqss-c/resource.h b/include/mqss-c/resource.h index 6a6fd01..40af3e3 100644 --- a/include/mqss-c/resource.h +++ b/include/mqss-c/resource.h @@ -26,14 +26,16 @@ typedef struct MQSSOpaqueResource* MQSSResourceRef; typedef struct MQSSOpaqueGate* MQSSGateRef; -int mqssClientResourceGetInfo(MQSSResourceRef resource, char** name, +int MQSSClientResourceGetInfo(MQSSResourceRef resource, char** name, unsigned* qubitCount, bool* online, - int** couplingMap, MQSSGateRef** nativeGateset, + int** couplingMap, unsigned int* couplingMapSize, + MQSSGateRef** nativeGateset, unsigned int* gateCount); -int mqssClientResourceGetGateInfo(MQSSGateRef gate, unsigned int qubitNumber, - unsigned int parameterNumber, - int* supportedQubits); +int MQSSClientResourceGetGateInfo(MQSSGateRef gate, char** name, unsigned* qubitNumber, + unsigned* parameterNumber, + int** supportedQubits, + unsigned int* supportedQubitCount); #ifdef __cplusplus } diff --git a/src/client.cpp b/src/client.cpp index 0d3ae37..897fc2d 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -141,12 +141,12 @@ int MQSSClient::getNumberPendingJobs(const std::string& resource) const { return parsed.value("num_pending_jobs", -1); } -MQSSClientRef mqssClientCreateClient(char* token, char* urlOrQueue, +MQSSClientRef MQSSClientCreateClient(char* token, char* urlOrQueue, bool isHpc) { return wrap(new MQSSClient(token, urlOrQueue, isHpc)); } -MQSSResourceRef* mqssClientGetAllResources(MQSSClientRef client, int* size) { +MQSSResourceRef* MQSSClientGetAllResources(MQSSClientRef client, int* size) { auto resources_ = unwrap(client)->getAllResources(); auto resources = @@ -160,7 +160,18 @@ MQSSResourceRef* mqssClientGetAllResources(MQSSClientRef client, int* size) { return resources; } -int mqssClientSubmitJob(MQSSClientRef client, MQSSJobRef job) { +MQSSResourceRef MQSSClientGetResourceInfo(MQSSClientRef client, + const char* resourceName) { + if (!resourceName) + return nullptr; + + auto resource = unwrap(client)->getResourceInfo(resourceName); + if (!resource) + return nullptr; + + return wrap(new Resource(*resource)); +} +int MQSSClientSubmitJob(MQSSClientRef client, MQSSJobRef job) { auto uuid = unwrap(client)->submitJob(*unwrap(job)); if (!uuid.has_value()) { @@ -169,7 +180,11 @@ int mqssClientSubmitJob(MQSSClientRef client, MQSSJobRef job) { return std::stoi(*uuid); } -MQSSJobResultRef mqssClientGetJobResult(MQSSClientRef client, MQSSJobRef job, +void MQSSClientCancelJob(MQSSClientRef client, MQSSJobRef job) { + unwrap(client)->cancelJob(*unwrap(job)); +} + +MQSSJobResultRef MQSSClientGetJobResult(MQSSClientRef client, MQSSJobRef job, bool wait, unsigned int timeout) { std::unique_ptr jobResult = @@ -177,3 +192,8 @@ MQSSJobResultRef mqssClientGetJobResult(MQSSClientRef client, MQSSJobRef job, wait, timeout); return wrap(jobResult.release()); } + +int MQSSClientGetNumberPendingJobs(MQSSClientRef client, char* resourceName) { + + return unwrap(client)->getNumberPendingJobs(resourceName); +} diff --git a/src/job.cpp b/src/job.cpp index 019665d..c493133 100644 --- a/src/job.cpp +++ b/src/job.cpp @@ -100,14 +100,21 @@ JobResult::JobResult(const nlohmann::json& parsed) { mTimestampScheduled = parsed.at("timestamp_scheduled").get(); } -MQSSJobRef mqssClientCreateCircuitJob(char* circuit, char* circuitFormat, +MQSSJobRef MQSSClientCreateCircuitJob(char* circuit, char* circuitFormat, char* resourceName, unsigned int shots, bool noModify, bool queued) { return wrap(new CircuitJobRequest( circuit, circuitFormat, resourceName, shots, noModify, queued)); } -int mqssClientGetJobResultCounts(MQSSJobResultRef jobResult, char*** bitstreams, +MQSSJobRef MQSSClientCreateHamiltonianJob(char* resourceName, + char* interactionStr, + char* coefficientsStr) { + return wrap( + new HamiltonianJobRequest(resourceName, interactionStr, coefficientsStr)); +} + +int MQSSClientGetJobResultCounts(MQSSJobResultRef jobResult, char*** bitstreams, int** counts, int* size) { if (!jobResult || !bitstreams || !counts || !size) return -1; @@ -184,7 +191,7 @@ uint64_t parseTimestamp(const std::string& s) { tp.time_since_epoch()) .count(); } -int mqssClientGetJobResultCompletedTimestamp(MQSSJobResultRef jobResult, +int MQSSClientGetJobResultCompletedTimestamp(MQSSJobResultRef jobResult, uint64_t* completedTimestamp) { *completedTimestamp = @@ -192,7 +199,7 @@ int mqssClientGetJobResultCompletedTimestamp(MQSSJobResultRef jobResult, return 0; } -int mqssClientGetJobResultSubmittedTimestamp(MQSSJobResultRef jobResult, +int MQSSClientGetJobResultSubmittedTimestamp(MQSSJobResultRef jobResult, uint64_t* submittedTimestamp) { *submittedTimestamp = @@ -200,7 +207,7 @@ int mqssClientGetJobResultSubmittedTimestamp(MQSSJobResultRef jobResult, return 0; } -int mqssClientGetJobResultScheduledTimestamp(MQSSJobResultRef jobResult, +int MQSSClientGetJobResultScheduledTimestamp(MQSSJobResultRef jobResult, uint64_t* scheduledTimestamp) { *scheduledTimestamp = diff --git a/src/resource.cpp b/src/resource.cpp index 4f96752..93c15a0 100644 --- a/src/resource.cpp +++ b/src/resource.cpp @@ -173,36 +173,78 @@ Resource::Resource(const nlohmann::json& json) { mCouplingMap = std::move(couplingMap); mNativeGateset = std::move(nativeGateset); } +template +int* flatten2D(const std::vector>& input, unsigned int* count) { + size_t total = 0; + for (const auto& row : input) + total += row.size(); + + int* result = static_cast(malloc(total * sizeof(int))); + if (!result) + return nullptr; + + size_t index = 0; + for (const auto& row : input) { + for (T value : row) { + result[index++] = static_cast(value); + } + } + *count = total; + return result; +} -int mqssClientResourceGetInfo(MQSSResourceRef resource, char** name, +int MQSSClientResourceGetInfo(MQSSResourceRef resource, char** name, unsigned* qubitCount, bool* online, - int** couplingMap, MQSSGateRef** nativeGateset, - unsigned int* gateCount) { - auto* resource_ = unwrap(resource); - if (resource_ == nullptr) + int** couplingMap, unsigned int* couplingMapSize, + MQSSGateRef** nativeGateset, + unsigned* gateCount) { + auto* r = unwrap(resource); + if (!r) return -2; - // Name - if (asprintf(name, "%s", resource_->getName().c_str()) < 0) + if (asprintf(name, "%s", r->getName().c_str()) < 0) return -3; - *qubitCount = resource_->getQubitCount(); - *online = resource_->isOnline(); + *qubitCount = r->getQubitCount(); + *online = r->isOnline(); - *couplingMap = nullptr; - - auto& gates = resource_->getNativeGateset(); + *couplingMap = flatten2D(r->getCouplingMap(), couplingMapSize); + if (!*couplingMap) + return -4; - *gateCount = gates.size(); + const auto& gates = r->getNativeGateset(); + *gateCount = static_cast(gates.size()); - *nativeGateset = (MQSSGateRef*)malloc(sizeof(MQSSGateRef) * gates.size()); + *nativeGateset = + static_cast(malloc(gates.size() * sizeof(MQSSGateRef))); if (!*nativeGateset) - return -4; + return -5; - for (size_t i = 0; i < gates.size(); ++i) { - (*nativeGateset)[i] = wrap(&gates[i]); - } + for (size_t i = 0; i < gates.size(); ++i) + (*nativeGateset)[i] = wrap(new Gate(gates[i])); return 0; } + +int MQSSClientResourceGetGateInfo(MQSSGateRef gate,char** name, unsigned* qubitNumber, + unsigned* parameterNumber, + int** supportedQubits, + unsigned int* supportedQubitCount) { + auto* g = unwrap(gate); + if (!g) + return -1; + auto gate_name = g->getName(); + *name = (char*)malloc(gate_name.size() * sizeof(char)); + strcpy(*name, gate_name.data()); + + + *qubitNumber = g->getQubitNumber(); + *parameterNumber = g->getParameterNumber(); + + *supportedQubits = flatten2D(g->getSupportedQubits(), supportedQubitCount); + if (!*supportedQubits) + return -2; + + return 0; +} \ No newline at end of file From 7ba5ae199ff55ad0d33b6942d43c92a72603eef7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erc=C3=BCment=20Kaya?= Date: Fri, 3 Jul 2026 16:53:58 +0200 Subject: [PATCH 06/10] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/mqss-c/client.h | 2 +- include/mqss-c/job.h | 3 ++- include/mqss-c/resource.h | 3 ++- src/resource.cpp | 6 +++--- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/mqss-c/client.h b/include/mqss-c/client.h index fa1ca63..3807996 100644 --- a/include/mqss-c/client.h +++ b/include/mqss-c/client.h @@ -32,7 +32,7 @@ MQSSClientRef MQSSClientCreateClient(char* token, char* urlOrQueue, bool isHpc); MQSSResourceRef* MQSSClientGetAllResources(MQSSClientRef client, int* size); MQSSResourceRef MQSSClientGetResourceInfo(MQSSClientRef client, - char* resourceName); + char* resourceName); int MQSSClientSubmitJob(MQSSClientRef client, MQSSJobRef job); diff --git a/include/mqss-c/job.h b/include/mqss-c/job.h index e156a14..f332f6f 100644 --- a/include/mqss-c/job.h +++ b/include/mqss-c/job.h @@ -31,7 +31,8 @@ MQSSJobRef MQSSClientCreateCircuitJob(char* circuit, char* circuitFormat, bool noModify, bool queued); MQSSJobRef MQSSClientCreateHamiltonianJob(char* resourceName, - char* interactionStr, char* coefficientsStr); + char* interactionStr, + char* coefficientsStr); int MQSSClientGetJobResultCounts(MQSSJobResultRef jobResult, char*** bitstreams, int** counts, int* size); diff --git a/include/mqss-c/resource.h b/include/mqss-c/resource.h index 40af3e3..56e07fb 100644 --- a/include/mqss-c/resource.h +++ b/include/mqss-c/resource.h @@ -32,7 +32,8 @@ int MQSSClientResourceGetInfo(MQSSResourceRef resource, char** name, MQSSGateRef** nativeGateset, unsigned int* gateCount); -int MQSSClientResourceGetGateInfo(MQSSGateRef gate, char** name, unsigned* qubitNumber, +int MQSSClientResourceGetGateInfo(MQSSGateRef gate, char** name, + unsigned* qubitNumber, unsigned* parameterNumber, int** supportedQubits, unsigned int* supportedQubitCount); diff --git a/src/resource.cpp b/src/resource.cpp index 93c15a0..12dce08 100644 --- a/src/resource.cpp +++ b/src/resource.cpp @@ -227,7 +227,8 @@ int MQSSClientResourceGetInfo(MQSSResourceRef resource, char** name, return 0; } -int MQSSClientResourceGetGateInfo(MQSSGateRef gate,char** name, unsigned* qubitNumber, +int MQSSClientResourceGetGateInfo(MQSSGateRef gate, char** name, + unsigned* qubitNumber, unsigned* parameterNumber, int** supportedQubits, unsigned int* supportedQubitCount) { @@ -238,7 +239,6 @@ int MQSSClientResourceGetGateInfo(MQSSGateRef gate,char** name, unsigned* qubitN *name = (char*)malloc(gate_name.size() * sizeof(char)); strcpy(*name, gate_name.data()); - *qubitNumber = g->getQubitNumber(); *parameterNumber = g->getParameterNumber(); @@ -247,4 +247,4 @@ int MQSSClientResourceGetGateInfo(MQSSGateRef gate,char** name, unsigned* qubitN return -2; return 0; -} \ No newline at end of file +} From 56f1fa8cff361bda307569c473987e2eb269b644 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erc=C3=BCment=20Kaya?= <49598189+kayaercument@users.noreply.github.com> Date: Mon, 6 Jul 2026 13:57:29 +0200 Subject: [PATCH 07/10] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Ercüment Kaya <49598189+kayaercument@users.noreply.github.com> --- .clang-tidy | 2 +- include/mqss-c/client.h | 4 +++- include/mqss-c/job.h | 2 ++ include/mqss-c/resource.h | 2 ++ src/client.cpp | 32 ++++++++++++++++++++++------ src/job.cpp | 45 ++++++++++++++++++++++++++++++++------- src/resource.cpp | 37 ++++++++++++++++++++++++++------ 7 files changed, 102 insertions(+), 22 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 86090d6..ee7db0b 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -39,7 +39,7 @@ CheckOptions: - key: readability-identifier-naming.FunctionCase value: camelBack - key: readability-identifier-naming.FunctionIgnoredRegexp - value: ".*MQSS.* + value: ".*MQSS.*" - key: readability-identifier-naming.GlobalConstantCase value: UPPER_CASE - key: readability-identifier-naming.IgnoreMainLikeFunctions diff --git a/include/mqss-c/client.h b/include/mqss-c/client.h index 3807996..e7c589d 100644 --- a/include/mqss-c/client.h +++ b/include/mqss-c/client.h @@ -17,6 +17,8 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +#pragma once + #ifdef __cplusplus extern "C" { #endif @@ -32,7 +34,7 @@ MQSSClientRef MQSSClientCreateClient(char* token, char* urlOrQueue, bool isHpc); MQSSResourceRef* MQSSClientGetAllResources(MQSSClientRef client, int* size); MQSSResourceRef MQSSClientGetResourceInfo(MQSSClientRef client, - char* resourceName); + const char* resourceName); int MQSSClientSubmitJob(MQSSClientRef client, MQSSJobRef job); diff --git a/include/mqss-c/job.h b/include/mqss-c/job.h index f332f6f..6b51f34 100644 --- a/include/mqss-c/job.h +++ b/include/mqss-c/job.h @@ -17,6 +17,8 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +#pragma once + #include #ifdef __cplusplus extern "C" { diff --git a/include/mqss-c/resource.h b/include/mqss-c/resource.h index 56e07fb..5fc7198 100644 --- a/include/mqss-c/resource.h +++ b/include/mqss-c/resource.h @@ -17,6 +17,8 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ +#pragma once + #ifdef __cplusplus extern "C" { #endif diff --git a/src/client.cpp b/src/client.cpp index 897fc2d..5e624b3 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -143,26 +143,33 @@ int MQSSClient::getNumberPendingJobs(const std::string& resource) const { MQSSClientRef MQSSClientCreateClient(char* token, char* urlOrQueue, bool isHpc) { + if (!token || !urlOrQueue) + return nullptr; return wrap(new MQSSClient(token, urlOrQueue, isHpc)); } MQSSResourceRef* MQSSClientGetAllResources(MQSSClientRef client, int* size) { + if (!client || !size) + return nullptr; + auto resources_ = unwrap(client)->getAllResources(); + *size = static_cast(resources_.size()); - auto resources = - (MQSSResourceRef*)malloc(resources_.size() * sizeof(MQSSResourceRef)); + auto resources = static_cast( + malloc(resources_.size() * sizeof(MQSSResourceRef))); + if (!resources && !resources_.empty()) + return nullptr; for (size_t i = 0; i < resources_.size(); ++i) { resources[i] = wrap(new Resource(resources_[i])); } - *size = (int)resources_.size(); return resources; } MQSSResourceRef MQSSClientGetResourceInfo(MQSSClientRef client, const char* resourceName) { - if (!resourceName) + if (!client || !resourceName) return nullptr; auto resource = unwrap(client)->getResourceInfo(resourceName); @@ -172,20 +179,31 @@ MQSSResourceRef MQSSClientGetResourceInfo(MQSSClientRef client, return wrap(new Resource(*resource)); } int MQSSClientSubmitJob(MQSSClientRef client, MQSSJobRef job) { + if (!client || !job) + return -1; + auto uuid = unwrap(client)->submitJob(*unwrap(job)); - if (!uuid.has_value()) { + if (!uuid.has_value()) return -1; + + try { + return std::stoi(*uuid); + } catch (...) { + return -2; } - return std::stoi(*uuid); } void MQSSClientCancelJob(MQSSClientRef client, MQSSJobRef job) { + if (!client || !job) + return; unwrap(client)->cancelJob(*unwrap(job)); } MQSSJobResultRef MQSSClientGetJobResult(MQSSClientRef client, MQSSJobRef job, bool wait, unsigned int timeout) { + if (!client || !job) + return nullptr; std::unique_ptr jobResult = unwrap(client)->getJobResult(*unwrap(job), @@ -194,6 +212,8 @@ MQSSJobResultRef MQSSClientGetJobResult(MQSSClientRef client, MQSSJobRef job, } int MQSSClientGetNumberPendingJobs(MQSSClientRef client, char* resourceName) { + if (!client || !resourceName) + return -1; return unwrap(client)->getNumberPendingJobs(resourceName); } diff --git a/src/job.cpp b/src/job.cpp index c493133..1906dbc 100644 --- a/src/job.cpp +++ b/src/job.cpp @@ -22,6 +22,13 @@ #include "mqss-c/job.h" #include "mqss/client.h" +#include +#include +#include +#include +#include +#include + using namespace mqss::client; nlohmann::json CircuitJobRequest::toJson() const { @@ -103,6 +110,8 @@ JobResult::JobResult(const nlohmann::json& parsed) { MQSSJobRef MQSSClientCreateCircuitJob(char* circuit, char* circuitFormat, char* resourceName, unsigned int shots, bool noModify, bool queued) { + if (!circuit || !circuitFormat || !resourceName) + return nullptr; return wrap(new CircuitJobRequest( circuit, circuitFormat, resourceName, shots, noModify, queued)); } @@ -110,6 +119,8 @@ MQSSJobRef MQSSClientCreateCircuitJob(char* circuit, char* circuitFormat, MQSSJobRef MQSSClientCreateHamiltonianJob(char* resourceName, char* interactionStr, char* coefficientsStr) { + if (!resourceName || !interactionStr || !coefficientsStr) + return nullptr; return wrap( new HamiltonianJobRequest(resourceName, interactionStr, coefficientsStr)); } @@ -193,24 +204,42 @@ uint64_t parseTimestamp(const std::string& s) { } int MQSSClientGetJobResultCompletedTimestamp(MQSSJobResultRef jobResult, uint64_t* completedTimestamp) { + if (!jobResult || !completedTimestamp) + return -1; - *completedTimestamp = - parseTimestamp(unwrap(jobResult)->getTimestampCompleted()); + try { + *completedTimestamp = + parseTimestamp(unwrap(jobResult)->getTimestampCompleted()); + } catch (...) { + return -2; + } return 0; } int MQSSClientGetJobResultSubmittedTimestamp(MQSSJobResultRef jobResult, - uint64_t* submittedTimestamp) { + uint64_t* submittedTimestamp) { + if (!jobResult || !submittedTimestamp) + return -1; - *submittedTimestamp = - parseTimestamp(unwrap(jobResult)->getTimestampSubmitted()); + try { + *submittedTimestamp = + parseTimestamp(unwrap(jobResult)->getTimestampSubmitted()); + } catch (...) { + return -2; + } return 0; } int MQSSClientGetJobResultScheduledTimestamp(MQSSJobResultRef jobResult, - uint64_t* scheduledTimestamp) { + uint64_t* scheduledTimestamp) { + if (!jobResult || !scheduledTimestamp) + return -1; - *scheduledTimestamp = - parseTimestamp(unwrap(jobResult)->getTimestampScheduled()); + try { + *scheduledTimestamp = + parseTimestamp(unwrap(jobResult)->getTimestampScheduled()); + } catch (...) { + return -2; + } return 0; } diff --git a/src/resource.cpp b/src/resource.cpp index 12dce08..095b88d 100644 --- a/src/resource.cpp +++ b/src/resource.cpp @@ -175,6 +175,8 @@ Resource::Resource(const nlohmann::json& json) { } template int* flatten2D(const std::vector>& input, unsigned int* count) { + if (!count) + return nullptr; size_t total = 0; for (const auto& row : input) total += row.size(); @@ -198,6 +200,11 @@ int MQSSClientResourceGetInfo(MQSSResourceRef resource, char** name, int** couplingMap, unsigned int* couplingMapSize, MQSSGateRef** nativeGateset, unsigned* gateCount) { + if (!name || !qubitCount || !online || !couplingMap || !couplingMapSize || + !nativeGateset || !gateCount) { + return -1; + } + auto* r = unwrap(resource); if (!r) return -2; @@ -209,8 +216,11 @@ int MQSSClientResourceGetInfo(MQSSResourceRef resource, char** name, *online = r->isOnline(); *couplingMap = flatten2D(r->getCouplingMap(), couplingMapSize); - if (!*couplingMap) + if (!*couplingMap) { + free(*name); + *name = nullptr; return -4; + } const auto& gates = r->getNativeGateset(); *gateCount = static_cast(gates.size()); @@ -218,8 +228,13 @@ int MQSSClientResourceGetInfo(MQSSResourceRef resource, char** name, *nativeGateset = static_cast(malloc(gates.size() * sizeof(MQSSGateRef))); - if (!*nativeGateset) + if (!*nativeGateset) { + free(*couplingMap); + *couplingMap = nullptr; + free(*name); + *name = nullptr; return -5; + } for (size_t i = 0; i < gates.size(); ++i) (*nativeGateset)[i] = wrap(new Gate(gates[i])); @@ -232,19 +247,29 @@ int MQSSClientResourceGetGateInfo(MQSSGateRef gate, char** name, unsigned* parameterNumber, int** supportedQubits, unsigned int* supportedQubitCount) { + if (!gate || !name || !qubitNumber || !parameterNumber || !supportedQubits || + !supportedQubitCount) { + return -1; + } + auto* g = unwrap(gate); if (!g) return -1; - auto gate_name = g->getName(); - *name = (char*)malloc(gate_name.size() * sizeof(char)); - strcpy(*name, gate_name.data()); + const auto& gateName = g->getName(); + *name = static_cast(malloc(gateName.size() + 1)); + if (!*name) + return -2; + std::memcpy(*name, gateName.c_str(), gateName.size() + 1); *qubitNumber = g->getQubitNumber(); *parameterNumber = g->getParameterNumber(); *supportedQubits = flatten2D(g->getSupportedQubits(), supportedQubitCount); - if (!*supportedQubits) + if (!*supportedQubits) { + free(*name); + *name = nullptr; return -2; + } return 0; } From f6024f665b5972cfadea6fb60f0d1feb2e2d5bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erc=C3=BCment=20Kaya?= Date: Tue, 7 Jul 2026 13:17:03 +0200 Subject: [PATCH 08/10] =?UTF-8?q?=F0=9F=8E=A8=20improve=20structure=20and?= =?UTF-8?q?=20CI=20pipeline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/mqss_client-test.yml | 48 +++++--- .github/workflows/publish.yml | 36 ------ .gitignore | 1 + include/mqss/client.h | 8 -- src/c_bindings/CMakeLists.txt | 49 ++++++++ src/c_bindings/client.cpp | 102 ++++++++++++++++ src/c_bindings/common.h | 28 +++++ src/c_bindings/job.cpp | 162 +++++++++++++++++++++++++ src/c_bindings/resource.cpp | 126 +++++++++++++++++++ src/client.cpp | 77 ------------ src/job.cpp | 137 --------------------- src/resource.cpp | 100 --------------- 12 files changed, 498 insertions(+), 376 deletions(-) delete mode 100644 .github/workflows/publish.yml create mode 100644 src/c_bindings/CMakeLists.txt create mode 100644 src/c_bindings/client.cpp create mode 100644 src/c_bindings/common.h create mode 100644 src/c_bindings/job.cpp create mode 100644 src/c_bindings/resource.cpp diff --git a/.github/workflows/mqss_client-test.yml b/.github/workflows/mqss_client-test.yml index b1124b3..4669664 100644 --- a/.github/workflows/mqss_client-test.yml +++ b/.github/workflows/mqss_client-test.yml @@ -1,6 +1,3 @@ -# This workflow will install Python dependencies, run tests and lint with a single version of Python -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python - name: MQSS Client Testing on: @@ -15,25 +12,40 @@ permissions: jobs: build: runs-on: ubuntu-latest - strategy: - matrix: - python: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] steps: - - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python }} + - name: Checkout repository + uses: actions/checkout@v4 + - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install uv - uv sync + sudo apt update -y + sudo apt-get install -y \ + software-properties-common \ + cmake \ + nlohmann-json3-dev \ + librabbitmq-dev \ + libboost-dev \ + libgtest-dev \ + libgmock-dev \ + libcurl4-openssl-dev + + python3 -m pip install --upgrade pip + pip install uv pre-commit + - name: Run pre-commit run: | - uv run pre-commit install - uv run pre-commit run --all-files - - name: Test with pytest + pre-commit install + pre-commit run --all-files + + - name: Configure project + run: | + cmake -S . -B build -DBUILD_UNIT_TESTS=ON + + - name: Build + run: | + cmake --build build --parallel + + - name: Run tests run: | - uv run nox -s test + ctest --test-dir build/tests/unit/c++/ --output-on-failure diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index e7fc0dd..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,36 +0,0 @@ -# .github/workflows/publish.yml -name: Publish to PyPI - -on: - push: - tags: - - "v*.*.*" # Trigger on version tags - -jobs: - publish: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: "3.x" - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install uv - - - name: Build package - run: uv build - - - name: Publish package - env: - PYPI_USERNAME: __token__ - PYPI_PASSWORD: ${{ secrets.MQSS_PYPI_API_TOKEN }} - run: uv publish -v -u ${PYPI_USERNAME} -p ${PYPI_PASSWORD} - - name: Test package installation - run: uv run --with mqss-client --no-project -- python -c "import mqss_client" diff --git a/.gitignore b/.gitignore index 537bb38..bcf6790 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,7 @@ coverage.xml .hypothesis/ .pytest_cache/ cover/ +Testing/* # Translations *.mo diff --git a/include/mqss/client.h b/include/mqss/client.h index ec58d04..222c415 100644 --- a/include/mqss/client.h +++ b/include/mqss/client.h @@ -32,14 +32,6 @@ #define MQP_DEFAULT_URL "https://portal.quantum.lrz.de:4000/v1/" -template inline T* unwrap(RefT ref) { - return reinterpret_cast(ref); -} - -template inline RefT wrap(T* ptr) { - return reinterpret_cast(ptr); -} - namespace mqss::client { class MQSSBaseClient { diff --git a/src/c_bindings/CMakeLists.txt b/src/c_bindings/CMakeLists.txt new file mode 100644 index 0000000..a33c37d --- /dev/null +++ b/src/c_bindings/CMakeLists.txt @@ -0,0 +1,49 @@ +# Copyright (c) 2024 - 2026 MQSS Project +# All rights reserved. +# +# Licensed under the Apache License v2.0 with LLVM Exceptions (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://llvm.org/LICENSE.txt +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +find_package(CURL REQUIRED) +find_package(nlohmann_json REQUIRED) +find_package(rabbitmq-c REQUIRED) + +file(GLOB_RECURSE MQSS_CLIENT_SOURCES **.cpp) + +add_library(mqss_client SHARED ${MQSS_CLIENT_SOURCES}) + +target_include_directories( + mqss_client PUBLIC $ + $) + +target_link_libraries(mqss_client PUBLIC curl nlohmann_json rabbitmq) + +include(GNUInstallDirs) + +add_subdirectory(c_bindings) + +install( + TARGETS mqss_client + EXPORT mqss_client_targets + LIBRARY DESTINATION . COMPONENT mqss_client_bindings + ARCHIVE DESTINATION . COMPONENT mqss_client_bindings + RUNTIME DESTINATION . COMPONENT mqss_client_bindings) + +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +install( + EXPORT mqss_client_targets + FILE MQSSClientTargets.cmake + NAMESPACE MQSS:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mqss_client) diff --git a/src/c_bindings/client.cpp b/src/c_bindings/client.cpp new file mode 100644 index 0000000..d1ac2fd --- /dev/null +++ b/src/c_bindings/client.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2024 - 2026 MQSS Project + * All rights reserved. + * + * Licensed under the Apache License v2.0 with LLVM Exceptions (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://llvm.org/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "mqss-c/client.h" + +#include "common.h" +#include "mqss/client.h" + +using namespace mqss::client; + +MQSSClientRef MQSSClientCreateClient(char* token, char* urlOrQueue, + bool isHpc) { + if (!token || !urlOrQueue) + return nullptr; + + return wrap(new MQSSClient(token, urlOrQueue, isHpc)); +} +MQSSResourceRef* MQSSClientGetAllResources(MQSSClientRef client, int* size) { + if (!client || !size) + return nullptr; + + auto resources_ = unwrap(client)->getAllResources(); + *size = static_cast(resources_.size()); + + auto resources = static_cast( + malloc(resources_.size() * sizeof(MQSSResourceRef))); + if (!resources && !resources_.empty()) + return nullptr; + + for (size_t i = 0; i < resources_.size(); ++i) { + resources[i] = wrap(new Resource(resources_[i])); + } + + return resources; +} + +MQSSResourceRef MQSSClientGetResourceInfo(MQSSClientRef client, + const char* resourceName) { + if (!client || !resourceName) + return nullptr; + + auto resource = unwrap(client)->getResourceInfo(resourceName); + if (!resource) + return nullptr; + + return wrap(new Resource(*resource)); +} +int MQSSClientSubmitJob(MQSSClientRef client, MQSSJobRef job) { + if (!client || !job) + return -1; + + auto uuid = + unwrap(client)->submitJob(*unwrap(job)); + if (!uuid.has_value()) + return -1; + + try { + return std::stoi(*uuid); + } catch (...) { + return -2; + } +} + +void MQSSClientCancelJob(MQSSClientRef client, MQSSJobRef job) { + if (!client || !job) + return; + unwrap(client)->cancelJob(*unwrap(job)); +} + +MQSSJobResultRef MQSSClientGetJobResult(MQSSClientRef client, MQSSJobRef job, + bool wait, unsigned int timeout) { + if (!client || !job) + return nullptr; + + std::unique_ptr jobResult = + unwrap(client)->getJobResult(*unwrap(job), + wait, timeout); + return wrap(jobResult.release()); +} + +int MQSSClientGetNumberPendingJobs(MQSSClientRef client, char* resourceName) { + if (!client || !resourceName) + return -1; + + return unwrap(client)->getNumberPendingJobs(resourceName); +} diff --git a/src/c_bindings/common.h b/src/c_bindings/common.h new file mode 100644 index 0000000..d752480 --- /dev/null +++ b/src/c_bindings/common.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 - 2026 MQSS Project + * All rights reserved. + * + * Licensed under the Apache License v2.0 with LLVM Exceptions (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://llvm.org/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#pragma once + +template inline T* unwrap(RefT ref) { + return reinterpret_cast(ref); +} + +template inline RefT wrap(T* ptr) { + return reinterpret_cast(ptr); +} diff --git a/src/c_bindings/job.cpp b/src/c_bindings/job.cpp new file mode 100644 index 0000000..c26f40e --- /dev/null +++ b/src/c_bindings/job.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2024 - 2026 MQSS Project + * All rights reserved. + * + * Licensed under the Apache License v2.0 with LLVM Exceptions (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://llvm.org/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "mqss-c/job.h" + +#include "common.h" +#include "mqss/job.h" + +using namespace mqss::client; + +MQSSJobRef MQSSClientCreateCircuitJob(char* circuit, char* circuitFormat, + char* resourceName, unsigned int shots, + bool noModify, bool queued) { + if (!circuit || !circuitFormat || !resourceName) + return nullptr; + return wrap(new CircuitJobRequest( + circuit, circuitFormat, resourceName, shots, noModify, queued)); +} + +MQSSJobRef MQSSClientCreateHamiltonianJob(char* resourceName, + char* interactionStr, + char* coefficientsStr) { + if (!resourceName || !interactionStr || !coefficientsStr) + return nullptr; + return wrap( + new HamiltonianJobRequest(resourceName, interactionStr, coefficientsStr)); +} + +int MQSSClientGetJobResultCounts(MQSSJobResultRef jobResult, char*** bitstreams, + int** counts, int* size) { + if (!jobResult || !bitstreams || !counts || !size) + return -1; + + auto results = unwrap(jobResult)->getResults(); + + // Count total entries across all result maps. + size_t totalEntries = 0; + for (const auto& result : results) + totalEntries += result.size(); + + *size = static_cast(totalEntries); + + if (totalEntries == 0) { + *bitstreams = nullptr; + *counts = nullptr; + return 0; + } + + *counts = static_cast(malloc(sizeof(int) * totalEntries)); + *bitstreams = static_cast(malloc(sizeof(char*) * totalEntries)); + + if (!*counts || !*bitstreams) { + free(*counts); + free(*bitstreams); + return -1; + } + + size_t index = 0; + + for (const auto& result : results) { + for (const auto& [bitstream, count] : result) { + + (*bitstreams)[index] = static_cast(malloc(bitstream.size() + 1)); + + if (!(*bitstreams)[index]) { + for (size_t i = 0; i < index; ++i) + free((*bitstreams)[i]); + free(*bitstreams); + free(*counts); + return -1; + } + + std::memcpy((*bitstreams)[index], bitstream.c_str(), + bitstream.size() + 1); + + (*counts)[index] = static_cast(count); + + ++index; + } + } + + return 0; +} + +uint64_t parseTimestamp(const std::string& s) { + + std::tm tm = {}; + std::istringstream ss(s.substr(0, 19)); // YYYY-MM-DD HH:MM:SS + + ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S"); + + auto tt = std::mktime(&tm); + + auto tp = std::chrono::system_clock::from_time_t(tt); + + // Parse microseconds + auto dot = s.find('.'); + if (dot != std::string::npos) { + int micros = std::stoi(s.substr(dot + 1)); + tp += std::chrono::microseconds(micros); + } + return std::chrono::duration_cast( + tp.time_since_epoch()) + .count(); +} +int MQSSClientGetJobResultCompletedTimestamp(MQSSJobResultRef jobResult, + uint64_t* completedTimestamp) { + if (!jobResult || !completedTimestamp) + return -1; + + try { + *completedTimestamp = + parseTimestamp(unwrap(jobResult)->getTimestampCompleted()); + } catch (...) { + return -2; + } + return 0; +} + +int MQSSClientGetJobResultSubmittedTimestamp(MQSSJobResultRef jobResult, + uint64_t* submittedTimestamp) { + if (!jobResult || !submittedTimestamp) + return -1; + + try { + *submittedTimestamp = + parseTimestamp(unwrap(jobResult)->getTimestampSubmitted()); + } catch (...) { + return -2; + } + return 0; +} + +int MQSSClientGetJobResultScheduledTimestamp(MQSSJobResultRef jobResult, + uint64_t* scheduledTimestamp) { + if (!jobResult || !scheduledTimestamp) + return -1; + + try { + *scheduledTimestamp = + parseTimestamp(unwrap(jobResult)->getTimestampScheduled()); + } catch (...) { + return -2; + } + return 0; +} diff --git a/src/c_bindings/resource.cpp b/src/c_bindings/resource.cpp new file mode 100644 index 0000000..9afb1a0 --- /dev/null +++ b/src/c_bindings/resource.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2024 - 2026 MQSS Project + * All rights reserved. + * + * Licensed under the Apache License v2.0 with LLVM Exceptions (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://llvm.org/LICENSE.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + * + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + */ + +#include "mqss/resource.h" + +#include "common.h" +#include "mqss-c/resource.h" + +using namespace mqss::client; + +template +int* flatten2D(const std::vector>& input, unsigned int* count) { + if (!count) + return nullptr; + size_t total = 0; + for (const auto& row : input) + total += row.size(); + + int* result = static_cast(malloc(total * sizeof(int))); + if (!result) + return nullptr; + + size_t index = 0; + for (const auto& row : input) { + for (T value : row) { + result[index++] = static_cast(value); + } + } + *count = total; + return result; +} + +int MQSSClientResourceGetInfo(MQSSResourceRef resource, char** name, + unsigned* qubitCount, bool* online, + int** couplingMap, unsigned int* couplingMapSize, + MQSSGateRef** nativeGateset, + unsigned* gateCount) { + if (!name || !qubitCount || !online || !couplingMap || !couplingMapSize || + !nativeGateset || !gateCount) { + return -1; + } + + auto* r = unwrap(resource); + if (!r) + return -2; + + if (asprintf(name, "%s", r->getName().c_str()) < 0) + return -3; + + *qubitCount = r->getQubitCount(); + *online = r->isOnline(); + + *couplingMap = flatten2D(r->getCouplingMap(), couplingMapSize); + if (!*couplingMap) { + free(*name); + *name = nullptr; + return -4; + } + + const auto& gates = r->getNativeGateset(); + *gateCount = static_cast(gates.size()); + + *nativeGateset = + static_cast(malloc(gates.size() * sizeof(MQSSGateRef))); + + if (!*nativeGateset) { + free(*couplingMap); + *couplingMap = nullptr; + free(*name); + *name = nullptr; + return -5; + } + + for (size_t i = 0; i < gates.size(); ++i) + (*nativeGateset)[i] = wrap(new Gate(gates[i])); + + return 0; +} + +int MQSSClientResourceGetGateInfo(MQSSGateRef gate, char** name, + unsigned* qubitNumber, + unsigned* parameterNumber, + int** supportedQubits, + unsigned int* supportedQubitCount) { + if (!gate || !name || !qubitNumber || !parameterNumber || !supportedQubits || + !supportedQubitCount) { + return -1; + } + + auto* g = unwrap(gate); + if (!g) + return -1; + const auto& gateName = g->getName(); + *name = static_cast(malloc(gateName.size() + 1)); + if (!*name) + return -2; + std::memcpy(*name, gateName.c_str(), gateName.size() + 1); + + *qubitNumber = g->getQubitNumber(); + *parameterNumber = g->getParameterNumber(); + + *supportedQubits = flatten2D(g->getSupportedQubits(), supportedQubitCount); + if (!*supportedQubits) { + free(*name); + *name = nullptr; + return -2; + } + + return 0; +} diff --git a/src/client.cpp b/src/client.cpp index 5e624b3..4cf6ed4 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -140,80 +140,3 @@ int MQSSClient::getNumberPendingJobs(const std::string& resource) const { return parsed.value("num_pending_jobs", -1); } - -MQSSClientRef MQSSClientCreateClient(char* token, char* urlOrQueue, - bool isHpc) { - if (!token || !urlOrQueue) - return nullptr; - - return wrap(new MQSSClient(token, urlOrQueue, isHpc)); -} -MQSSResourceRef* MQSSClientGetAllResources(MQSSClientRef client, int* size) { - if (!client || !size) - return nullptr; - - auto resources_ = unwrap(client)->getAllResources(); - *size = static_cast(resources_.size()); - - auto resources = static_cast( - malloc(resources_.size() * sizeof(MQSSResourceRef))); - if (!resources && !resources_.empty()) - return nullptr; - - for (size_t i = 0; i < resources_.size(); ++i) { - resources[i] = wrap(new Resource(resources_[i])); - } - - return resources; -} - -MQSSResourceRef MQSSClientGetResourceInfo(MQSSClientRef client, - const char* resourceName) { - if (!client || !resourceName) - return nullptr; - - auto resource = unwrap(client)->getResourceInfo(resourceName); - if (!resource) - return nullptr; - - return wrap(new Resource(*resource)); -} -int MQSSClientSubmitJob(MQSSClientRef client, MQSSJobRef job) { - if (!client || !job) - return -1; - - auto uuid = - unwrap(client)->submitJob(*unwrap(job)); - if (!uuid.has_value()) - return -1; - - try { - return std::stoi(*uuid); - } catch (...) { - return -2; - } -} - -void MQSSClientCancelJob(MQSSClientRef client, MQSSJobRef job) { - if (!client || !job) - return; - unwrap(client)->cancelJob(*unwrap(job)); -} - -MQSSJobResultRef MQSSClientGetJobResult(MQSSClientRef client, MQSSJobRef job, - bool wait, unsigned int timeout) { - if (!client || !job) - return nullptr; - - std::unique_ptr jobResult = - unwrap(client)->getJobResult(*unwrap(job), - wait, timeout); - return wrap(jobResult.release()); -} - -int MQSSClientGetNumberPendingJobs(MQSSClientRef client, char* resourceName) { - if (!client || !resourceName) - return -1; - - return unwrap(client)->getNumberPendingJobs(resourceName); -} diff --git a/src/job.cpp b/src/job.cpp index 1906dbc..34d8880 100644 --- a/src/job.cpp +++ b/src/job.cpp @@ -106,140 +106,3 @@ JobResult::JobResult(const nlohmann::json& parsed) { mTimestampSubmitted = parsed.at("timestamp_submitted").get(); mTimestampScheduled = parsed.at("timestamp_scheduled").get(); } - -MQSSJobRef MQSSClientCreateCircuitJob(char* circuit, char* circuitFormat, - char* resourceName, unsigned int shots, - bool noModify, bool queued) { - if (!circuit || !circuitFormat || !resourceName) - return nullptr; - return wrap(new CircuitJobRequest( - circuit, circuitFormat, resourceName, shots, noModify, queued)); -} - -MQSSJobRef MQSSClientCreateHamiltonianJob(char* resourceName, - char* interactionStr, - char* coefficientsStr) { - if (!resourceName || !interactionStr || !coefficientsStr) - return nullptr; - return wrap( - new HamiltonianJobRequest(resourceName, interactionStr, coefficientsStr)); -} - -int MQSSClientGetJobResultCounts(MQSSJobResultRef jobResult, char*** bitstreams, - int** counts, int* size) { - if (!jobResult || !bitstreams || !counts || !size) - return -1; - - auto results = unwrap(jobResult)->getResults(); - - // Count total entries across all result maps. - size_t totalEntries = 0; - for (const auto& result : results) - totalEntries += result.size(); - - *size = static_cast(totalEntries); - - if (totalEntries == 0) { - *bitstreams = nullptr; - *counts = nullptr; - return 0; - } - - *counts = static_cast(malloc(sizeof(int) * totalEntries)); - *bitstreams = static_cast(malloc(sizeof(char*) * totalEntries)); - - if (!*counts || !*bitstreams) { - free(*counts); - free(*bitstreams); - return -1; - } - - size_t index = 0; - - for (const auto& result : results) { - for (const auto& [bitstream, count] : result) { - - (*bitstreams)[index] = static_cast(malloc(bitstream.size() + 1)); - - if (!(*bitstreams)[index]) { - for (size_t i = 0; i < index; ++i) - free((*bitstreams)[i]); - free(*bitstreams); - free(*counts); - return -1; - } - - std::memcpy((*bitstreams)[index], bitstream.c_str(), - bitstream.size() + 1); - - (*counts)[index] = static_cast(count); - - ++index; - } - } - - return 0; -} - -uint64_t parseTimestamp(const std::string& s) { - - std::tm tm = {}; - std::istringstream ss(s.substr(0, 19)); // YYYY-MM-DD HH:MM:SS - - ss >> std::get_time(&tm, "%Y-%m-%d %H:%M:%S"); - - auto tt = std::mktime(&tm); - - auto tp = std::chrono::system_clock::from_time_t(tt); - - // Parse microseconds - auto dot = s.find('.'); - if (dot != std::string::npos) { - int micros = std::stoi(s.substr(dot + 1)); - tp += std::chrono::microseconds(micros); - } - return std::chrono::duration_cast( - tp.time_since_epoch()) - .count(); -} -int MQSSClientGetJobResultCompletedTimestamp(MQSSJobResultRef jobResult, - uint64_t* completedTimestamp) { - if (!jobResult || !completedTimestamp) - return -1; - - try { - *completedTimestamp = - parseTimestamp(unwrap(jobResult)->getTimestampCompleted()); - } catch (...) { - return -2; - } - return 0; -} - -int MQSSClientGetJobResultSubmittedTimestamp(MQSSJobResultRef jobResult, - uint64_t* submittedTimestamp) { - if (!jobResult || !submittedTimestamp) - return -1; - - try { - *submittedTimestamp = - parseTimestamp(unwrap(jobResult)->getTimestampSubmitted()); - } catch (...) { - return -2; - } - return 0; -} - -int MQSSClientGetJobResultScheduledTimestamp(MQSSJobResultRef jobResult, - uint64_t* scheduledTimestamp) { - if (!jobResult || !scheduledTimestamp) - return -1; - - try { - *scheduledTimestamp = - parseTimestamp(unwrap(jobResult)->getTimestampScheduled()); - } catch (...) { - return -2; - } - return 0; -} diff --git a/src/resource.cpp b/src/resource.cpp index 095b88d..d9414ef 100644 --- a/src/resource.cpp +++ b/src/resource.cpp @@ -173,103 +173,3 @@ Resource::Resource(const nlohmann::json& json) { mCouplingMap = std::move(couplingMap); mNativeGateset = std::move(nativeGateset); } -template -int* flatten2D(const std::vector>& input, unsigned int* count) { - if (!count) - return nullptr; - size_t total = 0; - for (const auto& row : input) - total += row.size(); - - int* result = static_cast(malloc(total * sizeof(int))); - if (!result) - return nullptr; - - size_t index = 0; - for (const auto& row : input) { - for (T value : row) { - result[index++] = static_cast(value); - } - } - *count = total; - return result; -} - -int MQSSClientResourceGetInfo(MQSSResourceRef resource, char** name, - unsigned* qubitCount, bool* online, - int** couplingMap, unsigned int* couplingMapSize, - MQSSGateRef** nativeGateset, - unsigned* gateCount) { - if (!name || !qubitCount || !online || !couplingMap || !couplingMapSize || - !nativeGateset || !gateCount) { - return -1; - } - - auto* r = unwrap(resource); - if (!r) - return -2; - - if (asprintf(name, "%s", r->getName().c_str()) < 0) - return -3; - - *qubitCount = r->getQubitCount(); - *online = r->isOnline(); - - *couplingMap = flatten2D(r->getCouplingMap(), couplingMapSize); - if (!*couplingMap) { - free(*name); - *name = nullptr; - return -4; - } - - const auto& gates = r->getNativeGateset(); - *gateCount = static_cast(gates.size()); - - *nativeGateset = - static_cast(malloc(gates.size() * sizeof(MQSSGateRef))); - - if (!*nativeGateset) { - free(*couplingMap); - *couplingMap = nullptr; - free(*name); - *name = nullptr; - return -5; - } - - for (size_t i = 0; i < gates.size(); ++i) - (*nativeGateset)[i] = wrap(new Gate(gates[i])); - - return 0; -} - -int MQSSClientResourceGetGateInfo(MQSSGateRef gate, char** name, - unsigned* qubitNumber, - unsigned* parameterNumber, - int** supportedQubits, - unsigned int* supportedQubitCount) { - if (!gate || !name || !qubitNumber || !parameterNumber || !supportedQubits || - !supportedQubitCount) { - return -1; - } - - auto* g = unwrap(gate); - if (!g) - return -1; - const auto& gateName = g->getName(); - *name = static_cast(malloc(gateName.size() + 1)); - if (!*name) - return -2; - std::memcpy(*name, gateName.c_str(), gateName.size() + 1); - - *qubitNumber = g->getQubitNumber(); - *parameterNumber = g->getParameterNumber(); - - *supportedQubits = flatten2D(g->getSupportedQubits(), supportedQubitCount); - if (!*supportedQubits) { - free(*name); - *name = nullptr; - return -2; - } - - return 0; -} From 8c633cda219c0db6ea0ca268c825b2ebdc5a56e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erc=C3=BCment=20Kaya?= Date: Tue, 7 Jul 2026 13:17:40 +0200 Subject: [PATCH 09/10] =?UTF-8?q?=F0=9F=94=A5=20remove=20cmakelist.txt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/c_bindings/CMakeLists.txt | 49 ----------------------------------- 1 file changed, 49 deletions(-) delete mode 100644 src/c_bindings/CMakeLists.txt diff --git a/src/c_bindings/CMakeLists.txt b/src/c_bindings/CMakeLists.txt deleted file mode 100644 index a33c37d..0000000 --- a/src/c_bindings/CMakeLists.txt +++ /dev/null @@ -1,49 +0,0 @@ -# Copyright (c) 2024 - 2026 MQSS Project -# All rights reserved. -# -# Licensed under the Apache License v2.0 with LLVM Exceptions (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://llvm.org/LICENSE.txt -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations under -# the License. -# -# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -find_package(CURL REQUIRED) -find_package(nlohmann_json REQUIRED) -find_package(rabbitmq-c REQUIRED) - -file(GLOB_RECURSE MQSS_CLIENT_SOURCES **.cpp) - -add_library(mqss_client SHARED ${MQSS_CLIENT_SOURCES}) - -target_include_directories( - mqss_client PUBLIC $ - $) - -target_link_libraries(mqss_client PUBLIC curl nlohmann_json rabbitmq) - -include(GNUInstallDirs) - -add_subdirectory(c_bindings) - -install( - TARGETS mqss_client - EXPORT mqss_client_targets - LIBRARY DESTINATION . COMPONENT mqss_client_bindings - ARCHIVE DESTINATION . COMPONENT mqss_client_bindings - RUNTIME DESTINATION . COMPONENT mqss_client_bindings) - -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - -install( - EXPORT mqss_client_targets - FILE MQSSClientTargets.cmake - NAMESPACE MQSS:: - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/mqss_client) From 65159379da11deae91ca4632d6ea840b04b31163 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erc=C3=BCment=20Kaya?= Date: Tue, 7 Jul 2026 16:48:21 +0200 Subject: [PATCH 10/10] =?UTF-8?q?=E2=8F=AA=20Revert=20the=20publish.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/publish.yml | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..e7fc0dd --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,36 @@ +# .github/workflows/publish.yml +name: Publish to PyPI + +on: + push: + tags: + - "v*.*.*" # Trigger on version tags + +jobs: + publish: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: "3.x" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install uv + + - name: Build package + run: uv build + + - name: Publish package + env: + PYPI_USERNAME: __token__ + PYPI_PASSWORD: ${{ secrets.MQSS_PYPI_API_TOKEN }} + run: uv publish -v -u ${PYPI_USERNAME} -p ${PYPI_PASSWORD} + - name: Test package installation + run: uv run --with mqss-client --no-project -- python -c "import mqss_client"