Skip to content

Commit d2ae297

Browse files
rwakizakadoichanj
andauthored
Add SQC backend (#132)
* Added skelton for SQC backend * Uncomment sample code for SQC * Add init/free SQC system * Build a target information via SQC API * Implement a conversion from qiskit circuits to SQC circuits * [WIP] Implementing circuit execution * Implement a SQC Job and execution of sampler pubs * minor fix * Move initialization and finalization of SQC to runtime service * Fix errors * Fix typo * Update CMakeLists.txt * Fix CMakeLists.txt * Fix path * Clean unnecessary files * Add backend name for consistency * Fix * Update README * Fix bugs * Fix type error * Fix * Fix pointer types * Set qasm data directly * Use SQC_LIBS in CMakeLists.txt * Use malloc instead of new * Fix the format of QASM3 for SQC * Bug fix * Update README * Minor fix * Minor --------- Co-authored-by: Jun Doi <doichan@jp.ibm.com>
1 parent 47cc0ba commit d2ae297

6 files changed

Lines changed: 346 additions & 4 deletions

File tree

README.md

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ Qiskit C++ requires one of the following APIs to access IBM Quantum Platform to
4848

4949
- qiskit-ibm-runtime C (https://github.com/Qiskit/qiskit-ibm-runtime-c)
5050
- QRMI (https://github.com/qiskit-community/qrmi)
51+
- SQC 0.10.0 (https://github.com/jhpc-quantum/SQC)
5152

5253
Before building your application with Qiskit C++, build one of these APIs.
5354

@@ -72,6 +73,9 @@ $ cmake ..
7273
$ make
7374
```
7475

76+
If you want to use [SQC C API](https://github.com/jhpc-quantum/SQC), follow [the SQC documentation](https://github.com/jhpc-quantum/documents/blob/main/SQC_JHPC_Quantum_user_guide.md).
77+
Basically, SQC is pre-built and available in the environment, and thus you only need to set up authentication credentials (JWT token) and environment variables based on the provided scripts.
78+
7579
### Building Qiskit C++
7680

7781
Qiskit C++ only has C++ header files. There is nothing to do to build the SDK.
@@ -93,15 +97,15 @@ $ cmake -DQISKIT_ROOT=Path_to_qiskit ..
9397
$ make
9498
```
9599

96-
If you want to build sampler or transpiler example, you will need one of qiskit-ibm-runtime C or QRMI.
100+
If you want to build sampler or transpiler example, you will need one of qiskit-ibm-runtime C or QRMI or SQC.
97101

98-
Then example can be built by setting `QISKIT_IBM_RUNTIME_C_ROOT` or `QRMI_ROOT` to cmake.
102+
Then example can be built by setting `QISKIT_IBM_RUNTIME_C_ROOT` or `QRMI_ROOT` or `SQC_ROOT` to cmake.
99103

100104
```shell-session
101105
$ cd samples
102106
$ mkdir build
103107
$ cd build
104-
$ cmake -DQISKIT_ROOT=Path_to_qiskit -DQISKIT_IBM_RUNTIME_C_ROOT="path to qiskit-ibm-runtime C" or -DQRMI_ROOT="path to QRMI" ..
108+
$ cmake -DQISKIT_ROOT=Path_to_qiskit -DQISKIT_IBM_RUNTIME_C_ROOT="path to qiskit-ibm-runtime C" or -DQRMI_ROOT="path to QRMI" or -DSQC_ROOT="path to SQC" ..
105109
$ make
106110
```
107111

@@ -112,6 +116,12 @@ QISKIT_IBM_TOKEN=<your API key>
112116
QISKIT_IBM_INSTANCE=<your CRN>
113117
```
114118

119+
To run the sample example with SQC, you also need to set the library options to the environment variable `SQC_LIBS`, which is automatically set by a provided script (see [here](https://github.com/jhpc-quantum/documents/blob/main/SQC_JHPC_Quantum_user_guide.md)), before running cmake. In SQC 0.10.0, `backend_setup.sh` configures `SQC_LIBS` as follows:
120+
121+
```
122+
SQC_LIBS="-lsqc_api -lsqc_rpc ... (omitted) ... -pthread"
123+
```
124+
115125
### Making your own interface to Quantum hardware
116126

117127
Qiskit C++ offers an abstract interface to access Quantum hardware. You can make your own interface to the hardware

samples/CMakeLists.txt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ function(add_application APP_NAME CPP_FILE)
3636
PUBLIC
3737
${QISKIT_ROOT}/target/release
3838
${QRMI_ROOT}/target/release
39+
${SQC_ROOT}/lib
40+
${SQC_ROOT}/lib64
3941
${QISKIT_IBM_RUNTIME_C_ROOT}/build/cargo/debug
4042
)
4143
if(QRMI_ROOT)
@@ -44,6 +46,8 @@ function(add_application APP_NAME CPP_FILE)
4446
elseif(QISKIT_IBM_RUNTIME_C_ROOT)
4547
target_link_libraries(${APP_NAME} qiskit_cext.dll.lib qiskit_ibm_runtime.dll.lib nlohmann_json::nlohmann_json)
4648
set(CMAKE_CXX_FLAGS "-DQISKIT_IBM_RUNTIME_C_ROOT=${QISKIT_IBM_RUNTIME_C_ROOT}")
49+
#else(SQC_ROOT)
50+
# SQC for MSVC is not supported
4751
else()
4852
target_link_libraries(${APP_NAME} qiskit_cext.dll.lib nlohmann_json::nlohmann_json)
4953
target_compile_options(${APP_NAME} PRIVATE "-DQISKIT_IBM_RUNTIME_C_ROOT=${QISKIT_IBM_RUNTIME_C_ROOT}")
@@ -61,6 +65,12 @@ function(add_application APP_NAME CPP_FILE)
6165
"-L${QISKIT_ROOT}/dist/c/lib -L${QISKIT_IBM_RUNTIME_C_ROOT}/build/cargo/debug -Wl,-rpath ${QISKIT_ROOT}/dist/c/lib -Wl,-rpath ${QISKIT_IBM_RUNTIME_C_ROOT}/build/cargo/debug" qiskit qiskit_ibm_runtime nlohmann_json::nlohmann_json
6266
)
6367
target_compile_options(${APP_NAME} PRIVATE "-DQISKIT_IBM_RUNTIME_C_ROOT=${QISKIT_IBM_RUNTIME_C_ROOT}")
68+
elseif(SQC_ROOT)
69+
target_link_libraries(${APP_NAME}
70+
PRIVATE
71+
"-L${QISKIT_ROOT}/dist/c/lib -L${SQC_ROOT}/lib -L${SQC_ROOT}/lib64 -Wl,-rpath ${QISKIT_ROOT}/dist/c/lib -Wl,-rpath ${SQC_ROOT}/lib -Wl,-rpath ${SQC_ROOT}/lib64" qiskit nlohmann_json::nlohmann_json $ENV{SQC_LIBS}
72+
)
73+
target_compile_options(${APP_NAME} PRIVATE "-DSQC_ROOT=${SQC_ROOT}")
6474
else()
6575
target_link_libraries(${APP_NAME}
6676
PRIVATE
@@ -75,6 +85,7 @@ function(add_application APP_NAME CPP_FILE)
7585
${QISKIT_ROOT}/dist/c/include
7686
${QRMI_ROOT}
7787
${QISKIT_IBM_RUNTIME_C_ROOT}/include
88+
${SQC_ROOT}/include
7889
${SAMPLES_PATH}
7990
${CMAKE_CURRENT_SOURCE_DIR}/../src
8091
nlohmann_json::nlohmann_json
@@ -91,7 +102,7 @@ endfunction()
91102
add_application(circuit_test circuit_test.cpp)
92103
add_application(observable_test observable_test.cpp)
93104

94-
if(QRMI_ROOT OR QISKIT_IBM_RUNTIME_C_ROOT)
105+
if(QRMI_ROOT OR QISKIT_IBM_RUNTIME_C_ROOT OR SQC_ROOT)
95106
add_application(sampler_test sampler_test.cpp)
96107
add_application(transpile_test transpile_test.cpp)
97108
endif()

src/providers/sqc_backend.hpp

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
# This code is part of Qiskit.
3+
#
4+
# (C) Copyright IBM 2025.
5+
#
6+
# This code is licensed under the Apache License, Version 2.0. You may
7+
# obtain a copy of this license in the LICENSE.txt file in the root directory
8+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
9+
#
10+
# Any modifications or derivative works of this code must retain this
11+
# copyright notice, and modified files need to carry a notice indicating
12+
# that they have been altered from the originals.
13+
*/
14+
15+
// SQC Backend
16+
17+
#ifndef __qiskitcpp_providers_SQC_backend_def_hpp__
18+
#define __qiskitcpp_providers_SQC_backend_def_hpp__
19+
20+
#include <memory>
21+
#include <regex>
22+
23+
#include "utils/types.hpp"
24+
#include "transpiler/target.hpp"
25+
#include "primitives/containers/sampler_pub.hpp"
26+
#include "providers/sqc_job.hpp"
27+
28+
#include "sqc_ecode.h"
29+
#include "sqc_api.h"
30+
31+
namespace Qiskit {
32+
namespace providers {
33+
34+
std::string replace_all(std::string s, const std::string& from, const std::string& to);
35+
36+
/// @class SQCBackend
37+
/// @brief Backend class using SQC.
38+
class SQCBackend : public BackendV2 {
39+
private:
40+
const sqcBackend backend_type_;
41+
std::shared_ptr<transpiler::Target> target_;
42+
43+
public:
44+
/// @brief Create a new SQCBackend. Internally this initializes SQC.
45+
SQCBackend()
46+
: SQCBackend("unspecified")
47+
{}
48+
49+
/// @brief Create a new SQCBackend object
50+
/// @param backend_name a resource name for backend.
51+
SQCBackend(const std::string name)
52+
: BackendV2(name),
53+
backend_type_(SQC_RPC_SCHED_QC_TYPE_IBM_DACC),
54+
target_(nullptr)
55+
{}
56+
57+
SQCBackend(const SQCBackend& other)
58+
: BackendV2(other.name_),
59+
backend_type_(other.backend_type_),
60+
target_(other.target_)
61+
{}
62+
63+
~SQCBackend() {}
64+
65+
/// @brief Return a target properties for this backend.
66+
/// @return a target class (nullptr)
67+
std::shared_ptr<transpiler::Target> target(void) override
68+
{
69+
if(target_) return target_;
70+
71+
// Create a dummy circuit to get target json files
72+
std::unique_ptr<sqcQC, decltype(&sqcDestroyQuantumCircuit)> qc_handle(sqcQuantumCircuit(0), &sqcDestroyQuantumCircuit);
73+
if(sqcIbmdTranspileInfo(qc_handle.get(), backend_type_) != SQC_RESULT_OK) {
74+
std::cerr << "Failed to get the target information" << std::endl;
75+
return nullptr;
76+
}
77+
78+
nlohmann::ordered_json target_json;
79+
target_json["configuration"] = nlohmann::ordered_json::parse(qc_handle->backend_config_json);
80+
target_json["properties"] = nlohmann::ordered_json::parse(qc_handle->backend_props_json);
81+
auto target = std::make_shared<transpiler::Target>();
82+
if(!target->from_json(target_json)) {
83+
std::cerr << "Failed to create a target from json files" << std::endl;
84+
return nullptr;
85+
}
86+
target_ = target;
87+
88+
return target_;
89+
}
90+
91+
/// @brief Run and collect samples from each pub.
92+
/// @return SQCJob
93+
std::shared_ptr<providers::Job> run(std::vector<primitives::SamplerPub>& input_pubs, uint_t shots) override
94+
{
95+
auto circuit = input_pubs[0].circuit();
96+
const auto qasm3_str = circuit.to_qasm3();
97+
std::cout << "run qasm3: \n" << qasm3_str << std::endl;
98+
99+
// special modification of QASM3 for SQC
100+
std::string sqc_qasm3_str = qasm3_str;
101+
static const std::regex re(R"(\r\n|\r|\n)");
102+
sqc_qasm3_str = std::regex_replace(sqc_qasm3_str, re, std::string("\\n"));
103+
sqc_qasm3_str = replace_all(sqc_qasm3_str, "\"", "\\\"");
104+
sqc_qasm3_str.insert(0, "\"");
105+
sqc_qasm3_str.append("\"");
106+
std::cout << "qasm3 for SQC: \n" << sqc_qasm3_str << std::endl;
107+
108+
std::shared_ptr<sqcQC> sqc_circ(sqcQuantumCircuit(circuit.num_qubits()), sqcDestroyQuantumCircuit);
109+
sqc_circ->qasm = strdup(sqc_qasm3_str.c_str());
110+
111+
std::unique_ptr<sqcRunOptions> run_options(new sqcRunOptions);
112+
sqcInitializeRunOpt(run_options.get());
113+
run_options->nshots = shots;
114+
run_options->qubits = sqc_circ->qubits;
115+
run_options->outFormat = SQC_OUT_RAW; // Currently SQC supports the raw format only
116+
117+
std::shared_ptr<sqcOut> result((sqcOut*)malloc(sizeof(sqcOut)), [](sqcOut* out) { sqcFreeOut(out, SQC_OUT_RAW); });
118+
int error_code = sqcQCRun(sqc_circ.get(), backend_type_, *run_options, result.get());
119+
120+
if(error_code != SQC_RESULT_OK)
121+
{
122+
std::cerr << "Error: Failed to run a SQC circuit." << std::endl;
123+
return nullptr;
124+
}
125+
126+
auto results_json = nlohmann::ordered_json::parse(result->result);
127+
128+
return std::make_shared<SQCJob>(results_json);
129+
}
130+
};
131+
132+
133+
std::string replace_all(std::string s, const std::string& from, const std::string& to) {
134+
if (from.empty()) return s;
135+
std::string out;
136+
out.reserve(s.size());
137+
std::size_t pos = 0;
138+
while (true) {
139+
std::size_t found = s.find(from, pos);
140+
if (found == std::string::npos) {
141+
out.append(s, pos, std::string::npos);
142+
break;
143+
}
144+
out.append(s, pos, found - pos);
145+
out.append(to);
146+
pos = found + from.size();
147+
}
148+
return out;
149+
}
150+
151+
152+
} // namespace providers
153+
} // namespace Qiskit
154+
155+
156+
#endif //__qiskitcpp_providers_SQC_backend_def_hpp__

src/providers/sqc_job.hpp

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
# This code is part of Qiskit.
3+
#
4+
# (C) Copyright IBM 2025.
5+
#
6+
# This code is licensed under the Apache License, Version 2.0. You may
7+
# obtain a copy of this license in the LICENSE.txt file in the root directory
8+
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
9+
#
10+
# Any modifications or derivative works of this code must retain this
11+
# copyright notice, and modified files need to carry a notice indicating
12+
# that they have been altered from the originals.
13+
*/
14+
15+
// Job class for SQC
16+
17+
#ifndef __qiskitcpp_providers_SQC_job_hpp__
18+
#define __qiskitcpp_providers_SQC_job_hpp__
19+
20+
#include <nlohmann/json.hpp>
21+
22+
#include "utils/types.hpp"
23+
24+
#include "primitives/containers/sampler_pub_result.hpp"
25+
#include "providers/job.hpp"
26+
27+
namespace Qiskit {
28+
namespace providers {
29+
30+
/// @class SQCJob
31+
/// @brief Job class for SQC
32+
class SQCJob : public Job {
33+
private:
34+
std::string job_id_;
35+
nlohmann::ordered_json results_; // json formatted results (converted output from SQC)
36+
uint_t num_results_ = 0;
37+
38+
public:
39+
/// @brief Create a new SQCBackend
40+
SQCJob()
41+
: SQCJob(std::string{""})
42+
{}
43+
44+
/// @brief Create a new SQCBackend object
45+
SQCJob(const std::string& job_id)
46+
: job_id_(job_id),
47+
num_results_(0)
48+
{}
49+
50+
/// @note [TODO] This constructor will be removed after SQC API provides a async job execution
51+
SQCJob(const nlohmann::ordered_json results)
52+
: job_id_(""),
53+
num_results_(results["results"].size()),
54+
results_(results)
55+
{}
56+
57+
/// @brief Create a new SQCJob from other
58+
SQCJob(const SQCJob& other)
59+
{
60+
job_id_ = other.job_id_;
61+
num_results_ = other.num_results_;
62+
results_ = other.results_;
63+
}
64+
65+
~SQCJob() {}
66+
67+
/// @brief Return the status of the job.
68+
/// @return JobStatus enum.
69+
providers::JobStatus status(void) override
70+
{
71+
/// @todo Wait SQC API for making the status request API public.
72+
return providers::JobStatus::DONE;
73+
}
74+
75+
76+
/// @brief Return the number of results in this job
77+
/// @return number of results
78+
uint_t num_results(void) override
79+
{
80+
return num_results_;
81+
}
82+
83+
/// @brief get sampler pub result
84+
/// @param index an index of the result
85+
/// @param result an output sampler pub result
86+
/// @return true if result is successfully set
87+
bool result(uint_t index, primitives::SamplerPubResult& result) override
88+
{
89+
if (index >= num_results_)
90+
return false;
91+
92+
result.from_json(results_["results"][index]);
93+
94+
return true;
95+
}
96+
};
97+
98+
} // namespace providers
99+
} // namespace Qiskit
100+
101+
102+
#endif //__qiskitcpp_providers_SQC_job_hpp__
103+
104+

src/service/qiskit_runtime_service.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#ifdef QRMI_ROOT
1919
#include "service/qiskit_runtime_service_qrmi.hpp"
20+
#elif defined(SQC_ROOT)
21+
#include "service/qiskit_runtime_service_sqc.hpp"
2022
#else // otherwise use Qiskit IBM runtime C-API
2123
#include "service/qiskit_runtime_service_c.hpp"
2224
#endif

0 commit comments

Comments
 (0)