diff --git a/CMakeLists.txt b/CMakeLists.txt index 91aeb33..c5e08d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,8 +29,9 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON CACHE BOOL "Export compile commands" FORCE) -option(BUILD_UNIT_TESTS "Build the unit tests" ON) -option(ENABLE_COVERAGE "Enabling coverage" ON) +option(BUILD_TESTS "Build the tests" OFF) +option(BUILD_UNIT_TESTS "Build the unit tests" OFF) +option(ENABLE_COVERAGE "Enabling coverage" OFF) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") @@ -58,3 +59,7 @@ endif() if(BUILD_DOCUMENTATION) add_subdirectory(docs) endif() + +if(BUILD_BINDINGS) + add_subdirectory(bindings) +endif() diff --git a/bindings/CMakeLists.txt b/bindings/CMakeLists.txt new file mode 100644 index 0000000..44d893f --- /dev/null +++ b/bindings/CMakeLists.txt @@ -0,0 +1,39 @@ +# 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 + +set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +set(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE) + +find_package(pybind11 REQUIRED) + +set(MQSS_PYTHON_MODULE_NAME PyMQSSClient) + +file(GLOB_RECURSE MQSS_CLIENT_SOURCES **.cpp) + +pybind11_add_module(${MQSS_PYTHON_MODULE_NAME} MODULE ${MQSS_CLIENT_SOURCES}) + +target_include_directories(${MQSS_PYTHON_MODULE_NAME} PRIVATE ${PROJECT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries(${MQSS_PYTHON_MODULE_NAME} PRIVATE mqss_client) + +set_target_properties(${MQSS_PYTHON_MODULE_NAME} PROPERTIES INSTALL_RPATH "$ORIGIN") + +install( + TARGETS ${MQSS_PYTHON_MODULE_NAME} + DESTINATION . + COMPONENT mqss_client_bindings) diff --git a/bindings/bindings.cpp b/bindings/bindings.cpp new file mode 100644 index 0000000..7232735 --- /dev/null +++ b/bindings/bindings.cpp @@ -0,0 +1,27 @@ +/* + * 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 "bindings.h" + +PYBIND11_MODULE(PyMQSSClient, m) { + m.doc() = "Python bindings for the MQSS client"; + registerClientInterface(m); + registerJobInterface(m); + registerResourceInterface(m); +} diff --git a/bindings/bindings.h b/bindings/bindings.h new file mode 100644 index 0000000..7e20b47 --- /dev/null +++ b/bindings/bindings.h @@ -0,0 +1,26 @@ +/* + * 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 + +namespace py = pybind11; + +void registerClientInterface(const py::module& m); +void registerJobInterface(const py::module& m); +void registerResourceInterface(const py::module& m); diff --git a/bindings/client.cpp b/bindings/client.cpp new file mode 100644 index 0000000..fb91213 --- /dev/null +++ b/bindings/client.cpp @@ -0,0 +1,43 @@ +/* + * 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/client.h" + +#include "bindings.h" + +void registerClientInterface(const py::module& m) { + + py::class_(m, "MQSSClient") + .def(py::init(), + py::arg("token") = "", py::arg("url_or_queue") = MQP_DEFAULT_URL, + py::arg("is_hpc") = false) + .def_property_readonly("resources", + &mqss::client::MQSSClient::getAllResources) + .def("resource", &mqss::client::MQSSClient::getResourceInfo, + py::arg("resource")) + .def("submit_job", &mqss::client::MQSSClient::submitJob, + py::arg("job_request")) + .def("cancel_job", &mqss::client::MQSSClient::cancelJob, py::arg("job")) + .def("job_status", &mqss::client::MQSSClient::getJobStatus, + py::arg("job")) + .def("job_results", &mqss::client::MQSSClient::getJobResult, + py::arg("job"), py::arg("wait") = false, py::arg("timeout") = 100) + .def("pending_job_count", &mqss::client::MQSSClient::getNumberPendingJobs, + py::arg("resource")); +} diff --git a/src/python/bindings.cpp b/bindings/job.cpp similarity index 62% rename from src/python/bindings.cpp rename to bindings/job.cpp index 444384c..fb2ff81 100644 --- a/src/python/bindings.cpp +++ b/bindings/job.cpp @@ -17,51 +17,15 @@ * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception */ -#include "mqss/client.h" +#include "mqss/job.h" -#include -#include -#include - -namespace py = pybind11; - -PYBIND11_MODULE(mqss, m) { - m.doc() = "Python bindings for the MQSS client"; - - py::module_ clientModule = m.def_submodule("client", "Submodule Client"); - - py::class_(clientModule, "MQSSClient") - .def(py::init(), - py::arg("token") = "", py::arg("url_or_queue") = MQP_DEFAULT_URL, - py::arg("is_hpc") = false) - .def_property_readonly("resources", - &mqss::client::MQSSClient::getAllResources) - .def("resource", &mqss::client::MQSSClient::getResourceInfo, - py::arg("resource")) - .def("submit_job", &mqss::client::MQSSClient::submitJob, - py::arg("job_request")) - .def("cancel_job", &mqss::client::MQSSClient::cancelJob, py::arg("job")) - .def("job_status", &mqss::client::MQSSClient::getJobStatus, - py::arg("job")) - .def("job_results", &mqss::client::MQSSClient::getJobResult, - py::arg("job"), py::arg("wait") = false, py::arg("timeout") = 100) - .def("pending_job_count", &mqss::client::MQSSClient::getNumberPendingJobs, - py::arg("resource")); - - py::class_(clientModule, "Resource") - .def_property_readonly("name", &mqss::client::Resource::getName) - .def_property_readonly("qubit_count", - &mqss::client::Resource::getQubitCount) - .def_property_readonly("online", &mqss::client::Resource::isOnline) - .def_property_readonly("coupling_map", - &mqss::client::Resource::getCouplingMap) - .def_property_readonly("native_gateset", - &mqss::client::Resource::getNativeGateset); +#include "bindings.h" +void registerJobInterface(const py::module& m) { py::class_(m, "JobRequest").doc(); py::class_( - clientModule, "CircuitJobRequest") + m, "CircuitJobRequest") .def(py::init<>()) .def(py::init(), @@ -84,7 +48,7 @@ PYBIND11_MODULE(mqss, m) { &mqss::client::CircuitJobRequest::setQueued); py::class_( - clientModule, "HamiltonianJobRequest") + m, "HamiltonianJobRequest") .def(py::init<>()) .def(py::init(), py::arg("resource_name"), py::arg("interaction"), @@ -100,7 +64,7 @@ PYBIND11_MODULE(mqss, m) { &mqss::client::HamiltonianJobRequest::getCoefficientsString, &mqss::client::HamiltonianJobRequest::setCoefficientsString); - py::class_(clientModule, "JobResult") + py::class_(m, "JobResult") .def_property_readonly("results", &mqss::client::JobResult::getResults) .def_property_readonly("completion_timestamp", &mqss::client::JobResult::getTimestampCompleted) diff --git a/bindings/resource.cpp b/bindings/resource.cpp new file mode 100644 index 0000000..c9e1521 --- /dev/null +++ b/bindings/resource.cpp @@ -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 + */ + +#include "mqss/resource.h" + +#include "bindings.h" + +void registerResourceInterface(const py::module& m) { + py::class_(m, "Resource") + .def_property_readonly("name", &mqss::client::Resource::getName) + .def_property_readonly("qubit_count", + &mqss::client::Resource::getQubitCount) + .def_property_readonly("online", &mqss::client::Resource::isOnline) + .def_property_readonly("coupling_map", + &mqss::client::Resource::getCouplingMap) + .def_property_readonly("native_gateset", + &mqss::client::Resource::getNativeGateset); + + py::class_(m, "Gate") + .def_property_readonly("name", &mqss::client::Gate::getName) + .def_property_readonly("arity", &mqss::client::Gate::getArity) + .def_property_readonly("supported_qubits", + &mqss::client::Gate::getSupportedQubits); +} diff --git a/include/mqss/job.h b/include/mqss/job.h index 788b154..dc15af9 100644 --- a/include/mqss/job.h +++ b/include/mqss/job.h @@ -108,19 +108,21 @@ class HamiltonianJobRequest : public JobRequest { class JobResult { - std::map mResults; + std::vector> mResults; std::string mTimestampCompleted; std::string mTimestampSubmitted; std::string mTimestampScheduled; public: - JobResult(std::map results, + JobResult(std::vector> results, std::string timestampCompleted, std::string timestampSubmitted, std::string timestampScheduled); JobResult(const nlohmann::json& parsed); - std::map getResults() const { return mResults; } + std::vector> getResults() const { + return mResults; + } std::string getTimestampCompleted() const { return mTimestampCompleted; } std::string getTimestampSubmitted() const { return mTimestampSubmitted; } std::string getTimestampScheduled() const { return mTimestampScheduled; } diff --git a/include/mqss/resource.h b/include/mqss/resource.h index 44fdb0b..4af8dc3 100644 --- a/include/mqss/resource.h +++ b/include/mqss/resource.h @@ -22,11 +22,35 @@ #include #include namespace mqss::client { + +class Gate { + +public: + Gate(std::string name, unsigned int arity, + std::vector> supportedQubits) + : mName(std::move(name)), mArity(arity), + mSupportedQubits(std::move(supportedQubits)) {} + + const std::string& getName() const noexcept { return mName; } + + const unsigned int& getArity() const noexcept { return mArity; } + + const std::vector>& + getSupportedQubits() const noexcept { + return mSupportedQubits; + } + +private: + std::string mName; + unsigned int mArity; + std::vector> mSupportedQubits; +}; + class Resource { public: Resource(std::string name, unsigned qubitCount, bool online, - std::vector> couplingMap, - std::vector nativeGateset); + std::vector> couplingMap, + std::vector nativeGateset); Resource(const nlohmann::json& json); @@ -34,11 +58,11 @@ class Resource { unsigned getQubitCount() const noexcept { return mQubitCount; } bool isOnline() const noexcept { return mOnline; } - const std::vector>& getCouplingMap() const noexcept { + const std::vector>& getCouplingMap() const noexcept { return mCouplingMap; } - const std::vector& getNativeGateset() const noexcept { + const std::vector& getNativeGateset() const noexcept { return mNativeGateset; } @@ -46,7 +70,8 @@ class Resource { std::string mName; unsigned mQubitCount; bool mOnline; - std::vector> mCouplingMap; - std::vector mNativeGateset; + std::vector> mCouplingMap; + std::vector mNativeGateset; }; + } // namespace mqss::client diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..d64ff66 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,145 @@ +# 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 + +[build-system] +requires = [ + "scikit-build-core>=0.12.2", + "pybind11>=2.13.0", + "setuptools-scm>=9.2", +] +build-backend = "scikit_build_core.build" +[project] +name = "mqss-client" +description = "Unified client SDK for the Munich Quantum Software Stack (MQSS)" +readme = "README.md" +authors = [ + { name = "Munich Quantum Software Stack", email = "mqss@munich-quantum-valley.de" } +] +license = { text = "Apache-2.0" } +requires-python = ">=3.9" +dynamic = ["version"] +keywords = [ + "mqss", + "quantum-computing", + "quantum", + "hpc", + "rabbitmq", + "distributed-computing", +] +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: Apache Software License", + "Natural Language :: English", + "Operating System :: POSIX :: Linux", + "Operating System :: MacOS", + "Operating System :: Microsoft :: Windows", + "Programming Language :: C++", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Scientific/Engineering", + "Topic :: Software Development :: Libraries", + "Typing :: Typed", +] +[project.urls] +Homepage = "https://github.com/Munich-Quantum-Software-Stack/MQSS-Client" +Repository = "https://github.com/Munich-Quantum-Software-Stack/MQSS-Client" +Issues = "https://github.com/Munich-Quantum-Software-Stack/MQSS-Client/issues" +[project.optional-dependencies] +test = [ + "pytest>=8.0", + "pytest-cov>=5.0", + "pytest-xdist>=3.6", +] +dev = [ + "ruff>=0.11", + "mypy>=1.15", +] +[tool.scikit-build] +minimum-version = "build-system.requires" +build-dir = "build/{wheel_tag}/{build_type}" +cmake.build-type = "Release" +wheel.install-dir = "mqss/client" +wheel.packages = ["python/mqss"] +install.components = ["mqss_client_bindings"] +sdist.include = [ + "bindings/", + "cmake/", + "docs/", + "include/", + "python/", + "src/", + "tests/", + "CMakeLists.txt", +] +sdist.exclude = [ + "**/.github", + "**/__pycache__", + "**/*.pyc", +] +[tool.scikit-build.cmake.define] +BUILD_TESTS = "OFF" +BUILD_BINDINGS = "ON" +[tool.scikit-build.metadata.version] +provider = "scikit_build_core.metadata.setuptools_scm" +[tool.setuptools_scm] +local_scheme = "no-local-version" +[tool.pytest.ini_options] +minversion = "8.0" +testpaths = [ + "tests" +] +addopts = [ + "-ra", + "--showlocals", +] +pythonpath = [ + "python" +] +[tool.ruff] +line-length = 100 +[tool.ruff.lint] +select = [ + "E", + "F", + "I", + "UP", +] +[tool.cibuildwheel] +build = "cp3*" +skip = [ + "*-musllinux_*", +] +archs = "auto64" +build-frontend = "build" +test-command = "pytest {project}/tests" +[tool.cibuildwheel.linux] +repair-wheel-command = "auditwheel repair -w {dest_dir} {wheel}" +[tool.cibuildwheel.macos] +repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}" +[tool.cibuildwheel.windows] +repair-wheel-command = "delvewheel repair -w {dest_dir} {wheel}" +build.targets = [ + "mqss_client_bindings", +] diff --git a/src/python/CMakeLists.txt b/python/mqss/client/__init__.py similarity index 70% rename from src/python/CMakeLists.txt rename to python/mqss/client/__init__.py index 8b601a4..c925d26 100644 --- a/src/python/CMakeLists.txt +++ b/python/mqss/client/__init__.py @@ -15,8 +15,4 @@ # # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -find_package(pybind11 REQUIRED) -pybind11_add_module(mqss THIN_LTO OPT_SIZE bindings.cpp) -target_include_directories(mqss PRIVATE ${CMAKE_SOURCE_DIR}/include) -target_link_libraries(mqss PRIVATE mqss_client) -set_target_properties(mqss PROPERTIES POSITION_INDEPENDENT_CODE ON) +from .PyMQSSClient import MQSSClient, CircuitJobRequest, HamiltonianJobRequest, JobResult, Resource, Gate diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6c63bce..394fe99 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -19,19 +19,18 @@ find_package(CURL REQUIRED) find_package(nlohmann_json REQUIRED) find_package(rabbitmq-c REQUIRED) -file(GLOB CLIENT_SRC_FILES "${CMAKE_CURRENT_SOURCE_DIR}/clients/*.cpp") -add_library(mqss_client SHARED ${CLIENT_SRC_FILES} client.cpp job.cpp resource.cpp) +file(GLOB_RECURSE MQSS_CLIENT_SOURCES **.cpp) -target_include_directories(mqss_client PUBLIC ${CMAKE_SOURCE_DIR}/include) +add_library(mqss_client SHARED ${MQSS_CLIENT_SOURCES}) +target_include_directories(mqss_client PUBLIC ${CMAKE_SOURCE_DIR}/include) target_link_libraries(mqss_client PUBLIC curl nlohmann_json rabbitmq) -add_subdirectory(python) include(GNUInstallDirs) install( TARGETS mqss_client - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib) + DESTINATION . + COMPONENT mqss_client_bindings) install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/src/job.cpp b/src/job.cpp index dfe9f49..80b0e1e 100644 --- a/src/job.cpp +++ b/src/job.cpp @@ -23,9 +23,12 @@ using namespace mqss::client; nlohmann::json CircuitJobRequest::toJson() const { - return {{"circuit", {mCircuit}}, {"circuit_format", mCircuitFormat}, - {"resource_name", mResourceName}, {"shots", mShots}, - {"no_modify", mNoModify}, {"queued", mQueued}}; + return {{"circuit", mCircuit}, + {"circuit_format", mCircuitFormat}, + {"resource_name", mResourceName}, + {"shots", mShots}, + {"no_modify", mNoModify}, + {"queued", mQueued}}; } CircuitJobRequest::CircuitJobRequest(std::string circuit, @@ -51,7 +54,7 @@ nlohmann::json HamiltonianJobRequest::toJson() const { {"coefficients_str", mCoefficientsStr}}; } -JobResult::JobResult(std::map results, +JobResult::JobResult(std::vector> results, std::string timestampCompleted, std::string timestampSubmitted, std::string timestampScheduled) @@ -62,20 +65,30 @@ JobResult::JobResult(std::map results, JobResult::JobResult(const nlohmann::json& parsed) { + std::vector> results; const auto& rResultStr = parsed.at("result").get_ref(); if (!nlohmann::json::accept(rResultStr)) return; - nlohmann::json resultMap = nlohmann::json::parse(rResultStr); - if (resultMap.is_array()) - resultMap = resultMap[0]; - - for (auto& [key, value] : resultMap.items()) { - resultMap[key] = value.get(); + nlohmann::json resultMaps = nlohmann::json::parse(rResultStr); + if (resultMaps.is_array()) { + for (auto resultMap : resultMaps) { + std::map result; + for (auto& [key, value] : resultMap.items()) { + result[key] = value.get(); + } + results.push_back(result); + } + } else { + std::map result; + for (auto& [key, value] : resultMaps.items()) { + result[key] = value.get(); + } + results.push_back(result); } - mResults = std::move(resultMap); + mResults = std::move(results); mTimestampCompleted = parsed.at("timestamp_completed").get(); mTimestampSubmitted = parsed.at("timestamp_submitted").get(); mTimestampScheduled = parsed.at("timestamp_scheduled").get(); diff --git a/src/resource.cpp b/src/resource.cpp index f74aa6b..117ecdf 100644 --- a/src/resource.cpp +++ b/src/resource.cpp @@ -25,15 +25,36 @@ using namespace mqss::client; namespace { -const std::regex COUPLING_PATTERN(R"(\(\s*(\d+)\s*,\s*(\d+)\s*\))"); +std::vector> extractCouplingMap(const nlohmann::json& response, + const std::string& key) { + std::vector> result; -const std::regex GATE_PATTERN(R"('([^']+)')"); + const auto it = response.find(key); + if (it == response.end() || !it->is_string()) + return result; + + const std::string& text = it->get_ref(); + if (text == "None") + return result; + std::regex re(R"(\[(\d+),\s*(\d+)\])"); + + auto begin = std::sregex_iterator(text.begin(), text.end(), re); + auto end = std::sregex_iterator(); + + for (auto it = begin; it != end; ++it) { + int a = std::stoi((*it)[1].str()); + int b = std::stoi((*it)[2].str()); + std::vector pairs = {a, b}; + result.push_back(pairs); + } + + return result; +} -template -std::vector -extractFromStringField(const nlohmann::json& response, const std::string& key, - const std::regex& pattern, Converter&& convert) { - std::vector result; +std::vector extractGates(const nlohmann::json& response, + const std::string& key) { + + std::vector result; const auto it = response.find(key); if (it == response.end() || !it->is_string()) @@ -42,14 +63,46 @@ extractFromStringField(const nlohmann::json& response, const std::string& key, const std::string& text = it->get_ref(); if (text == "None") return result; + std::regex gateRegex(R"(\(\s*'([^']+)'\s*,\s*\{([^}]*)\}\s*\))"); + + auto gateBegin = std::sregex_iterator(text.begin(), text.end(), gateRegex); + auto gateEnd = std::sregex_iterator(); + std::string GateName; + unsigned int arity; + std::vector> supportedQubits; + + for (auto it = gateBegin; it != gateEnd; ++it) { + + GateName = (*it)[1]; + + std::string body = (*it)[2]; + std::regex tupleRegex(R"(([^)]*)\)\s*:\s*None)"); + + auto tupleBegin = + std::sregex_iterator(body.begin(), body.end(), tupleRegex); + + auto tupleEnd = std::sregex_iterator(); + + for (auto jt = tupleBegin; jt != tupleEnd; ++jt) { + std::string tupleText = (*jt)[1]; + + std::vector qubits; + + std::regex numberRegex(R"(\d+)"); + + auto numBegin = + std::sregex_iterator(tupleText.begin(), tupleText.end(), numberRegex); + + for (auto kt = numBegin; kt != std::sregex_iterator(); ++kt) { + qubits.push_back(static_cast(std::stoi((*kt).str()))); + } + + supportedQubits.push_back(qubits); + } - std::smatch match; - auto begin = text.cbegin(); - auto end = text.cend(); + arity = !supportedQubits.empty() ? supportedQubits.at(0).size() : 0; - while (std::regex_search(begin, end, match, pattern)) { - result.push_back(convert(match)); - begin = match.suffix().first; + result.push_back(Gate(GateName, arity, std::move(supportedQubits))); } return result; @@ -58,8 +111,8 @@ extractFromStringField(const nlohmann::json& response, const std::string& key, } // namespace Resource::Resource(std::string name, unsigned qubitCount, bool online, - std::vector> couplingMap, - std::vector nativeGateset) + std::vector> couplingMap, + std::vector nativeGateset) : mName(std::move(name)), mQubitCount(qubitCount), mOnline(online), mCouplingMap(std::move(couplingMap)), mNativeGateset(std::move(nativeGateset)) {} @@ -70,14 +123,10 @@ Resource::Resource(const nlohmann::json& json) { unsigned qubitCount = json.value("qubits", 0); bool online = json.value("online", false); - auto couplingMap = extractFromStringField>( - json, "connectivity", COUPLING_PATTERN, [](const std::smatch& m) { - return std::make_pair(std::stoi(m[1]), std::stoi(m[2])); - }); + std::vector> couplingMap = + extractCouplingMap(json, "connectivity"); - auto nativeGateset = extractFromStringField( - json, "instructions", GATE_PATTERN, - [](const std::smatch& m) { return m[1].str(); }); + std::vector nativeGateset = extractGates(json, "Gates"); mName = std::move(name); mQubitCount = qubitCount;