Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6bac223
Added skelton for SQC backend
rwakizaka Nov 25, 2025
02352ec
Uncomment sample code for SQC
rwakizaka Nov 25, 2025
baa3bdf
Add init/free SQC system
rwakizaka Nov 26, 2025
35720a0
Build a target information via SQC API
rwakizaka Nov 26, 2025
0afd1d8
Implement a conversion from qiskit circuits to SQC circuits
rwakizaka Nov 26, 2025
bf3ad5c
[WIP] Implementing circuit execution
rwakizaka Nov 26, 2025
f323269
Implement a SQC Job and execution of sampler pubs
rwakizaka Nov 27, 2025
fbff507
minor fix
rwakizaka Nov 27, 2025
19be22e
Move initialization and finalization of SQC to runtime service
rwakizaka Nov 27, 2025
36ee1f9
Fix errors
rwakizaka Dec 4, 2025
4a110aa
Fix typo
rwakizaka Dec 4, 2025
caef4ae
Update CMakeLists.txt
rwakizaka Dec 4, 2025
cfc4448
Fix CMakeLists.txt
rwakizaka Dec 4, 2025
28f642f
Fix path
rwakizaka Dec 4, 2025
6c433bd
Clean unnecessary files
rwakizaka Dec 4, 2025
24c76cd
Add backend name for consistency
rwakizaka Dec 4, 2025
e8e5ddb
Fix
rwakizaka Dec 4, 2025
3950402
Update README
rwakizaka Dec 4, 2025
9acc915
Fix bugs
rwakizaka Dec 9, 2025
953a989
Fix type error
rwakizaka Dec 9, 2025
a3e93cf
Fix
rwakizaka Dec 9, 2025
d604c10
Fix pointer types
rwakizaka Dec 9, 2025
6b2c019
Set qasm data directly
rwakizaka Dec 10, 2025
95ecbca
Use SQC_LIBS in CMakeLists.txt
rwakizaka Dec 10, 2025
d30888f
Use malloc instead of new
rwakizaka Dec 10, 2025
2efb85c
Fix the format of QASM3 for SQC
rwakizaka Dec 11, 2025
c5381c1
Merge branch 'main' into add-sqc-backend
rwakizaka Dec 11, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ $ cmake ..
$ make
```

If you want to use [SQC C API](https://github.com/jhpc-quantum/SQC), follow the SQC documentation.

### Building Qiskit C++

Qiskit C++ only has C++ header files. There is nothing to do to build the SDK.
Expand All @@ -93,18 +95,24 @@ $ cmake -DQISKIT_ROOT=Path_to_qiskit ..
$ make
```

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

Then example can be built by setting `QISKIT_IBM_RUNTIME_C_ROOT` or `QRMI_ROOT` to cmake.
Then example can be built by setting `QISKIT_IBM_RUNTIME_C_ROOT` or `QRMI_ROOT` or `SQC_ROOT` to cmake.

```shell-session
$ cd samples
$ mkdir build
$ cd build
$ cmake -DQISKIT_ROOT=Path_to_qiskit -DQISKIT_IBM_RUNTIME_C_ROOT="path to qiskit-ibm-runtime C" or -DQRMI_ROOT="path to QRMI" ..
$ 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" ..
$ make
```

You also need to set the library options to the environment variable `SQC_LIBS` before cmake if you use SQC. The format of `SQC_LIBS` is assumued to be as follows, for example. For required options, please refer the the SQC documentation.

```
"-lsqc_api -lsqc_rpc ... -pthread"
```

To run sampler example, set your account information in `$HOME/.qiskit/qiskit-ibm.json` (see https://github.com/Qiskit/qiskit-ibm-runtime?tab=readme-ov-file#save-your-account-on-disk) or setting following environment variables to access Quantum hardware.

```
Expand Down
13 changes: 12 additions & 1 deletion samples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ function(add_application APP_NAME CPP_FILE)
PUBLIC
${QISKIT_ROOT}/target/release
${QRMI_ROOT}/target/release
${SQC_ROOT}/lib
${SQC_ROOT}/lib64
${QISKIT_IBM_RUNTIME_C_ROOT}/build/cargo/debug
)
if(QRMI_ROOT)
Expand All @@ -44,6 +46,8 @@ function(add_application APP_NAME CPP_FILE)
elseif(QISKIT_IBM_RUNTIME_C_ROOT)
target_link_libraries(${APP_NAME} qiskit_cext.dll.lib qiskit_ibm_runtime.dll.lib nlohmann_json::nlohmann_json)
set(CMAKE_CXX_FLAGS "-DQISKIT_IBM_RUNTIME_C_ROOT=${QISKIT_IBM_RUNTIME_C_ROOT}")
#else(SQC_ROOT)
# SQC for MSVC is not supported
else()
target_link_libraries(${APP_NAME} qiskit_cext.dll.lib nlohmann_json::nlohmann_json)
target_compile_options(${APP_NAME} PRIVATE "-DQISKIT_IBM_RUNTIME_C_ROOT=${QISKIT_IBM_RUNTIME_C_ROOT}")
Expand All @@ -61,6 +65,12 @@ function(add_application APP_NAME CPP_FILE)
"-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
)
target_compile_options(${APP_NAME} PRIVATE "-DQISKIT_IBM_RUNTIME_C_ROOT=${QISKIT_IBM_RUNTIME_C_ROOT}")
elseif(SQC_ROOT)
target_link_libraries(${APP_NAME}
PRIVATE
"-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}
)
target_compile_options(${APP_NAME} PRIVATE "-DSQC_ROOT=${SQC_ROOT}")
else()
target_link_libraries(${APP_NAME}
PRIVATE
Expand All @@ -75,6 +85,7 @@ function(add_application APP_NAME CPP_FILE)
${QISKIT_ROOT}/dist/c/include
${QRMI_ROOT}
${QISKIT_IBM_RUNTIME_C_ROOT}/include
${SQC_ROOT}/include
${SAMPLES_PATH}
${CMAKE_CURRENT_SOURCE_DIR}/../src
nlohmann_json::nlohmann_json
Expand All @@ -91,7 +102,7 @@ endfunction()
add_application(circuit_test circuit_test.cpp)
add_application(observable_test observable_test.cpp)

if(QRMI_ROOT OR QISKIT_IBM_RUNTIME_C_ROOT)
if(QRMI_ROOT OR QISKIT_IBM_RUNTIME_C_ROOT OR SQC_ROOT)
add_application(sampler_test sampler_test.cpp)
add_application(transpile_test transpile_test.cpp)
endif()
156 changes: 156 additions & 0 deletions src/providers/sqc_backend.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
# This code is part of Qiskit.
#
# (C) Copyright IBM 2025.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
*/

// SQC Backend

#ifndef __qiskitcpp_providers_SQC_backend_def_hpp__
#define __qiskitcpp_providers_SQC_backend_def_hpp__

#include <memory>
#include <regex>

#include "utils/types.hpp"
#include "transpiler/target.hpp"
#include "primitives/containers/sampler_pub.hpp"
#include "providers/sqc_job.hpp"

#include "sqc_ecode.h"
#include "sqc_api.h"

namespace Qiskit {
namespace providers {

std::string replace_all(std::string s, const std::string& from, const std::string& to);

/// @class SQCBackend
/// @brief Backend class using SQC.
class SQCBackend : public BackendV2 {
private:
const sqcBackend backend_type_;
std::shared_ptr<transpiler::Target> target_;

public:
/// @brief Create a new SQCBackend. Internally this initializes SQC.
SQCBackend()
: SQCBackend("unspecified")
{}

/// @brief Create a new SQCBackend object
/// @param backend_name a resource name for backend.
SQCBackend(const std::string name)
: BackendV2(name),
backend_type_(SQC_RPC_SCHED_QC_TYPE_IBM_DACC),
target_(nullptr)
{}

SQCBackend(const SQCBackend& other)
: BackendV2(other.name_),
backend_type_(other.backend_type_),
target_(other.target_)
{}

~SQCBackend() {}

/// @brief Return a target properties for this backend.
/// @return a target class (nullptr)
std::shared_ptr<transpiler::Target> target(void) override
{
if(target_) return target_;

// Create a dummy circuit to get target json files
std::unique_ptr<sqcQC, decltype(&sqcDestroyQuantumCircuit)> qc_handle(sqcQuantumCircuit(0), &sqcDestroyQuantumCircuit);
if(sqcIbmdTranspileInfo(qc_handle.get(), backend_type_) != SQC_RESULT_OK) {
std::cerr << "Failed to get the target information" << std::endl;
return nullptr;
}

nlohmann::ordered_json target_json;
target_json["configuration"] = nlohmann::ordered_json::parse(qc_handle->backend_config_json);
target_json["properties"] = nlohmann::ordered_json::parse(qc_handle->backend_props_json);
auto target = std::make_shared<transpiler::Target>();
if(!target->from_json(target_json)) {
std::cerr << "Failed to create a target from json files" << std::endl;
return nullptr;
}
target_ = target;

return target_;
}

/// @brief Run and collect samples from each pub.
/// @return SQCJob
std::shared_ptr<providers::Job> run(std::vector<primitives::SamplerPub>& input_pubs, uint_t shots) override
{
auto circuit = input_pubs[0].circuit();
const auto qasm3_str = circuit.to_qasm3();
std::cout << "run qasm3: \n" << qasm3_str << std::endl;

// special modification of QASM3 for SQC
std::string sqc_qasm3_str = qasm3_str;
static const std::regex re(R"(\r\n|\r|\n)");
sqc_qasm3_str = std::regex_replace(sqc_qasm3_str, re, std::string("\\n"));
sqc_qasm3_str = replace_all(sqc_qasm3_str, "\"", "\\\"");
sqc_qasm3_str.insert(0, "\"");
sqc_qasm3_str.append("\"");
std::cout << "qasm3 for SQC: \n" << sqc_qasm3_str << std::endl;

std::shared_ptr<sqcQC> sqc_circ(sqcQuantumCircuit(circuit.num_qubits()), sqcDestroyQuantumCircuit);
sqc_circ->qasm = strdup(qasm3_str.c_str());

std::unique_ptr<sqcRunOptions> run_options(new sqcRunOptions);
sqcInitializeRunOpt(run_options.get());
run_options->nshots = shots;
run_options->qubits = sqc_circ->qubits;
run_options->outFormat = SQC_OUT_RAW; // Currently SQC supports the raw format only

std::shared_ptr<sqcOut> result((sqcOut*)malloc(sizeof(sqcOut)), [](sqcOut* out) { sqcFreeOut(out, SQC_OUT_RAW); });
int error_code = sqcQCRun(sqc_circ.get(), backend_type_, *run_options, result.get());

if(error_code != SQC_RESULT_OK)
{
std::cerr << "Error: Failed to run a SQC circuit." << std::endl;
return nullptr;
}

auto results_json = nlohmann::ordered_json::parse(result->result);

return std::make_shared<SQCJob>(results_json);
}
};


std::string replace_all(std::string s, const std::string& from, const std::string& to) {
if (from.empty()) return s;
std::string out;
out.reserve(s.size());
std::size_t pos = 0;
while (true) {
std::size_t found = s.find(from, pos);
if (found == std::string::npos) {
out.append(s, pos, std::string::npos);
break;
}
out.append(s, pos, found - pos);
out.append(to);
pos = found + from.size();
}
return out;
}


} // namespace providers
} // namespace Qiskit


#endif //__qiskitcpp_providers_SQC_backend_def_hpp__
104 changes: 104 additions & 0 deletions src/providers/sqc_job.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
# This code is part of Qiskit.
#
# (C) Copyright IBM 2025.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
*/

// Job class for SQC

#ifndef __qiskitcpp_providers_SQC_job_hpp__
#define __qiskitcpp_providers_SQC_job_hpp__

#include <nlohmann/json.hpp>

#include "utils/types.hpp"

#include "primitives/containers/sampler_pub_result.hpp"
#include "providers/job.hpp"

namespace Qiskit {
namespace providers {

/// @class SQCJob
/// @brief Job class for SQC
class SQCJob : public Job {
private:
std::string job_id_;
nlohmann::ordered_json results_; // json formatted results (converted output from SQC)
uint_t num_results_ = 0;

public:
/// @brief Create a new SQCBackend
SQCJob()
: SQCJob(std::string{""})
{}

/// @brief Create a new SQCBackend object
SQCJob(const std::string& job_id)
: job_id_(job_id),
num_results_(0)
{}

/// @note [TODO] This constructor will be removed after SQC API provides a async job execution
SQCJob(const nlohmann::ordered_json results)
: job_id_(""),
num_results_(results["results"].size()),
results_(results)
{}

/// @brief Create a new SQCJob from other
SQCJob(const SQCJob& other)
{
job_id_ = other.job_id_;
num_results_ = other.num_results_;
results_ = other.results_;
}

~SQCJob() {}

/// @brief Return the status of the job.
/// @return JobStatus enum.
providers::JobStatus status(void) override
{
/// @todo Wait SQC API for making the status request API public.
return providers::JobStatus::DONE;
}


/// @brief Return the number of results in this job
/// @return number of results
uint_t num_results(void) override
{
return num_results_;
}

/// @brief get sampler pub result
/// @param index an index of the result
/// @param result an output sampler pub result
/// @return true if result is successfully set
bool result(uint_t index, primitives::SamplerPubResult& result) override
{
if (index >= num_results_)
return false;

result.from_json(results_["results"][index]);

return true;
}
};

} // namespace providers
} // namespace Qiskit


#endif //__qiskitcpp_providers_SQC_job_hpp__


2 changes: 2 additions & 0 deletions src/service/qiskit_runtime_service.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

#ifdef QRMI_ROOT
#include "service/qiskit_runtime_service_qrmi.hpp"
#elif defined(SQC_ROOT)
#include "service/qiskit_runtime_service_sqc.hpp"
#else // otherwise use Qiskit IBM runtime C-API
#include "service/qiskit_runtime_service_c.hpp"
#endif
Expand Down
Loading
Loading