From 3c548d4a4f865e14a59b10ac837e3c8973b9faa3 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Fri, 13 Mar 2026 17:45:38 +0900 Subject: [PATCH 1/8] add customization of Target, add StagedPassManager and preset pass managers --- samples/CMakeLists.txt | 1 + samples/target_test.cpp | 57 ++++ src/compiler/transpiler.hpp | 17 +- src/providers/backend.hpp | 2 +- src/providers/qkrt_backend.hpp | 12 +- src/providers/qrmi_backend.hpp | 13 +- src/providers/sqc_backend.hpp | 17 +- src/transpiler/passmanager.hpp | 219 ++++++++++++++ .../generate_preset_pass_manager.hpp | 61 ++++ src/transpiler/target.hpp | 281 ++++++++++++++---- 10 files changed, 582 insertions(+), 98 deletions(-) create mode 100644 samples/target_test.cpp create mode 100644 src/transpiler/passmanager.hpp create mode 100644 src/transpiler/preset_passmanagers/generate_preset_pass_manager.hpp diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index b69cb01..e17c9e2 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -101,6 +101,7 @@ endfunction() add_application(circuit_test circuit_test.cpp) add_application(observable_test observable_test.cpp) +add_application(target_test target_test.cpp) if(QRMI_ROOT OR QISKIT_IBM_RUNTIME_C_ROOT OR SQC_ROOT) add_application(sampler_test sampler_test.cpp) diff --git a/samples/target_test.cpp b/samples/target_test.cpp new file mode 100644 index 0000000..e29c763 --- /dev/null +++ b/samples/target_test.cpp @@ -0,0 +1,57 @@ +/* +# This code is part of Qiskit. +# +# (C) Copyright IBM 2024. +# +# 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. +*/ + +// Test program for local target + +#define _USE_MATH_DEFINES +#include +#include +#include +#include + +#include "circuit/quantumcircuit.hpp" +#include "transpiler/target.hpp" +#include "transpiler/passmanager.hpp" +#include "transpiler/preset_passmanagers/generate_preset_pass_manager.hpp" + +using namespace Qiskit::circuit; +using namespace Qiskit::transpiler; + +int main() +{ + QuantumRegister qr(4); + ClassicalRegister cr(4); + QuantumCircuit circ(qr, cr); + + circ.h(0); + for (int i=1;i<4;i++) { + circ.cx(0, i); + } + for (int i=0;i<4;i++) { + circ.measure(i,i); + } + + std::cout << "input circuit" << std::endl; + circ.print(); + + auto target = Target({"cz", "id", "rx", "rz", "rzz", "sx", "x"}, {{0, 1}, {1, 0}, {1, 3}, {3, 1}, {0, 2}, {2, 0}, {2, 3}, {3, 2}}); + auto pass = StagedPassManager({"init", "layout", "routing", "translation"}, target); + auto transpiled = pass.run(circ); + + std::cout << "transpiled circuit" << std::endl; + transpiled.print(); + + return 0; +} + diff --git a/src/compiler/transpiler.hpp b/src/compiler/transpiler.hpp index 0ec75ad..4e8819d 100644 --- a/src/compiler/transpiler.hpp +++ b/src/compiler/transpiler.hpp @@ -34,9 +34,10 @@ namespace compiler { circuit::QuantumCircuit transpile(circuit::QuantumCircuit &circ, providers::BackendV2 &backend, int optimization_level = 2, double approximation_degree = 1.0, int seed_transpiler = -1) { auto target = backend.target(); - if (target == nullptr) - { - return circ; + auto capi_target = target.rust_target(); + if (capi_target == nullptr) { + std::cerr << "transpile error : Target object for the backend is not valid." << std::endl; + return circ.copy(); } QkTranspileOptions options = qk_transpiler_default_options(); @@ -47,12 +48,10 @@ circuit::QuantumCircuit transpile(circuit::QuantumCircuit &circ, providers::Back QkTranspileResult result; char *error; - QkExitCode ret = qk_transpile(circ.get_rust_circuit().get(), target->rust_target(), &options, &result, &error); - if (ret != QkExitCode_Success) - { + QkExitCode ret = qk_transpile(circ.get_rust_circuit().get(), capi_target, &options, &result, &error); + if (ret != QkExitCode_Success) { std::cerr << "transpile error (" << ret << ") : " << error << std::endl; - target.reset(); - return circ; + return circ.copy(); } // save qubit map after transpile @@ -63,8 +62,6 @@ circuit::QuantumCircuit transpile(circuit::QuantumCircuit &circ, providers::Back transpiled.set_qiskit_circuit(std::shared_ptr(result.circuit, qk_circuit_free), layout_map); qk_transpile_layout_free(result.layout); - target.reset(); - return transpiled; } diff --git a/src/providers/backend.hpp b/src/providers/backend.hpp index 7d7bc0e..27c4ac6 100644 --- a/src/providers/backend.hpp +++ b/src/providers/backend.hpp @@ -60,7 +60,7 @@ class BackendV2 { /// @brief Return a target properties for this backend /// @return a target class - virtual std::shared_ptr target(void) = 0; + virtual const transpiler::Target& target(void) = 0; /// @brief Run and collect samples from each pub. /// @param pubs An iterable of pub-like objects. diff --git a/src/providers/qkrt_backend.hpp b/src/providers/qkrt_backend.hpp index debaeaf..c42661d 100644 --- a/src/providers/qkrt_backend.hpp +++ b/src/providers/qkrt_backend.hpp @@ -44,7 +44,7 @@ class QkrtBackend : public BackendV2 { std::string primitive_name_ = "sampler"; qkrt_Backend* backend_ = nullptr; std::shared_ptr service_; - std::shared_ptr target_ = nullptr; + transpiler::Target target_; public: /// @brief Create a new QkrtBackend QkrtBackend() {} @@ -72,24 +72,22 @@ class QkrtBackend : public BackendV2 { { if (service_) service_.reset(); - if (target_) - target_.reset(); } /// @brief Return a target properties for this backend /// @return a target class - std::shared_ptr target(void) override + const transpiler::Target& target(void) override { - if (target_) { + if (target_.is_set()) { return target_; } QkTarget *target = qkrt_get_backend_target(service_.get(), backend_); if (target == nullptr) { std::cerr << "ERROR: failed to get target for the backend " << name_ << std::endl; - return nullptr; + return target_; } - target_ = std::make_shared(target); + target_ = transpiler::Target(target); return target_; } diff --git a/src/providers/qrmi_backend.hpp b/src/providers/qrmi_backend.hpp index cea946d..7df229f 100644 --- a/src/providers/qrmi_backend.hpp +++ b/src/providers/qrmi_backend.hpp @@ -36,7 +36,7 @@ class QRMIBackend : public BackendV2 { std::string primitive_name_ = "sampler"; std::string acc_token_; std::shared_ptr qrmi_ = nullptr; - std::shared_ptr target_ = nullptr; + transpiler::Target target_; public: /// @brief Create a new QRMIBackend QRMIBackend() {} @@ -68,22 +68,21 @@ class QRMIBackend : public BackendV2 { /// @brief Return a target properties for this backend /// @return a target class - std::shared_ptr target(void) override + const transpiler::Target& target(void) override { - if (target_) { + if (target_.is_set()) { return target_; } - transpiler::Target target; char *target_str = NULL; QrmiReturnCode rc = qrmi_resource_target(qrmi_.get(), &target_str); if (rc != QRMI_RETURN_CODE_SUCCESS) { - return nullptr; + return target_; } nlohmann::ordered_json json_target = nlohmann::ordered_json::parse(target_str); qrmi_string_free((char *)target_str); - target_ = std::make_shared(); - target_->from_json(json_target); + target_ = transpiler::Target(); + target_.from_json(json_target); return target_; } diff --git a/src/providers/sqc_backend.hpp b/src/providers/sqc_backend.hpp index 213f5d9..34ebb75 100644 --- a/src/providers/sqc_backend.hpp +++ b/src/providers/sqc_backend.hpp @@ -38,7 +38,7 @@ std::string replace_all(std::string s, const std::string& from, const std::strin class SQCBackend : public BackendV2 { private: const sqcBackend backend_type_; - std::shared_ptr target_; + transpiler::Target target_; public: /// @brief Create a new SQCBackend. Internally this initializes SQC. @@ -64,27 +64,26 @@ class SQCBackend : public BackendV2 { /// @brief Return a target properties for this backend. /// @return a target class (nullptr) - std::shared_ptr target(void) override + const transpiler::Target& target(void) override { - if(target_) return target_; + if (target_.is_set()) { + return target_; + } // Create a dummy circuit to get target json files std::unique_ptr 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; + return target_; } 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(); - if(!target->from_json(target_json)) { + target_ = 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_; } diff --git a/src/transpiler/passmanager.hpp b/src/transpiler/passmanager.hpp new file mode 100644 index 0000000..73a93ea --- /dev/null +++ b/src/transpiler/passmanager.hpp @@ -0,0 +1,219 @@ +/* +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 2024. +# +# 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. +*/ + +// passmanager class + +#ifndef __qiskitcpp_transpiler_passmanager_def_hpp__ +#define __qiskitcpp_transpiler_passmanager_def_hpp__ + +#include "utils/types.hpp" +#include "qiskit.h" + +#include "circuit/quantumcircuit.hpp" +#include "providers/backend.hpp" +#include "transpiler/target.hpp" + +namespace Qiskit +{ +namespace transpiler +{ + +/// @class PassManager +/// @brief base class for PassManager for transpilation +class PassManager { +protected: + Target target_; +public: + PassManager() {} + + PassManager(const PassManager& other) + { + target_ = other.target_; + } + + PassManager(const Target& target) : target_(target) {} + + /// @brief virtual function to run transpiler pass + /// @param input an input quantum circuit + /// @return a new quantum circuit + virtual circuit::QuantumCircuit run(circuit::QuantumCircuit& input) = 0; + + /// @brief virtual function to run transpiler pass + /// @param circuits a list of input quantum circuit + /// @return a list of output quantum circuits + virtual std::vector run(std::vector& circuits) + { + std::vector output; + for (auto &circ : circuits) { + output.push_back(run(circ)); + } + return output; + } +}; + + + +/// @class StagedPassManager +/// @brief StagedPassManager class +class StagedPassManager : public PassManager +{ +protected: + std::vector stages_; + uint8_t optimization_level_ = 2; + double approximation_degree_ = 1.0; + int seed_transpiler_ = -1; +public: + /// @brief Create a new StagedPassManager + StagedPassManager() {} + + /// @brief Create a new StagedPassManager + StagedPassManager(const StagedPassManager& other) : PassManager(other) + { + stages_ = other.stages_; + optimization_level_ = optimization_level_; + approximation_degree_ = other.approximation_degree_; + seed_transpiler_ = other.seed_transpiler_; + } + + /// @brief Create a new StagedPassManager + /// @param stages list of stages used for this pass + /// @param target target for the backend + StagedPassManager(std::vector stages, Target& target, uint8_t level = 2, double approximation_degree = 1.0, int seed_transpiler = -1) + { + target_ = target; + stages_ = stages; + optimization_level_ = level; + approximation_degree_ = approximation_degree; + seed_transpiler_ = seed_transpiler; + } + + /// @brief Create a new StagedPassManager + /// @param stages list of stages used for this pass + /// @param backend backend used for this pass + StagedPassManager(std::vector stages, providers::BackendV2& backend, uint8_t level = 2, double approximation_degree = 1.0, int seed_transpiler = -1) + { + target_ = backend.target(); + stages_ = stages; + optimization_level_ = level; + approximation_degree_ = approximation_degree; + seed_transpiler_ = seed_transpiler; + } + + ~StagedPassManager() + { + } + + + circuit::QuantumCircuit run(circuit::QuantumCircuit& circ) override; +}; + + +circuit::QuantumCircuit StagedPassManager::run(circuit::QuantumCircuit& circ) +{ + QkTranspileOptions options = qk_transpiler_default_options(); + options.optimization_level = optimization_level_; + options.approximation_degree = approximation_degree_; + if (seed_transpiler_ >= 0) { + options.seed = seed_transpiler_; + } + char *error; + QkExitCode ret; + + /*if (stages_.size() == 6) { + if (stages_[0] == "init" && stages_[1] == "layout" && stages_[2] == "routing" && + stages_[3] == "translation" && stages_[4] == "optimization" && stages_[5] == "scheduling") { + // use default transpiler + + QkTranspileResult result; + + ret = qk_transpile(circ.get_rust_circuit().get(), target_.rust_target(), &options, &result, &error); + if (ret != QkExitCode_Success) { + std::cerr << "transpile error (" << ret << ") : " << error << std::endl; + return circ.copy(); + } + // save qubit map after transpile + std::vector layout_map(qk_transpile_layout_num_output_qubits(result.layout)); + qk_transpile_layout_final_layout(result.layout, false, layout_map.data()); + + circuit::QuantumCircuit transpiled = circ; + transpiled.set_qiskit_circuit(std::shared_ptr(result.circuit, qk_circuit_free), layout_map); + + qk_transpile_layout_free(result.layout); + return transpiled; + } + }*/ + + QkDag* dag = qk_circuit_to_dag(circ.get_rust_circuit().get()); + if (dag == nullptr) { + return circ.copy(); + } + QkTranspileLayout* layout = nullptr; + + for (auto stage : stages_) { + if (stage == "init") { + ret = qk_transpile_stage_init(dag, target_.rust_target(), &options, &layout, &error); + if (ret != QkExitCode_Success) { + std::cerr << "StagedPassManager Error in init stage (" << ret << ") : " << error << std::endl; + } + } else if (stage == "layout") { + ret = qk_transpile_stage_layout(dag, target_.rust_target(), &options, &layout, &error); + if (ret != QkExitCode_Success) { + std::cerr << "StagedPassManager Error in layout stage (" << ret << ") : " << error << std::endl; + } + } else if (stage == "routing") { + ret = qk_transpile_stage_routing(dag, target_.rust_target(), &options, layout, &error); + if (ret != QkExitCode_Success) { + std::cerr << "StagedPassManager Error in routing stage (" << ret << ") : " << error << std::endl; + } + } else if (stage == "translation") { + ret = qk_transpile_stage_translation(dag, target_.rust_target(), &options, &error); + if (ret != QkExitCode_Success) { + std::cerr << "StagedPassManager Error in transplation stage (" << ret << ") : " << error << std::endl; + } + } else if (stage == "optimization") { + ret = qk_transpile_stage_optimization(dag, target_.rust_target(), &options, &error); + if (ret != QkExitCode_Success) { + std::cerr << "StagedPassManager Error in optimization stage (" << ret << ") : " << error << std::endl; + } + } else if (stage == "scheduling") { + // to be implemented (?) in C-API + } + } + QkCircuit* result_circ = qk_dag_to_circuit(dag); + + circuit::QuantumCircuit transpiled = circ; + if (layout) { + std::vector layout_map(qk_transpile_layout_num_output_qubits(layout)); + qk_transpile_layout_final_layout(layout, false, layout_map.data()); + + transpiled.set_qiskit_circuit(std::shared_ptr(result_circ, qk_circuit_free), layout_map); + + qk_transpile_layout_free(layout); + } else { + std::vector layout_map; + transpiled.set_qiskit_circuit(std::shared_ptr(result_circ, qk_circuit_free), layout_map); + } + + return transpiled; +} + + + + +} // namespace transpiler +} // namespace Qiskit + +#endif //__qiskitcpp_transpiler_passmanager_def_hpp__ + + diff --git a/src/transpiler/preset_passmanagers/generate_preset_pass_manager.hpp b/src/transpiler/preset_passmanagers/generate_preset_pass_manager.hpp new file mode 100644 index 0000000..82a8a8a --- /dev/null +++ b/src/transpiler/preset_passmanagers/generate_preset_pass_manager.hpp @@ -0,0 +1,61 @@ +/* +# This code is part of Qiskit. +# +# (C) Copyright IBM 2017, 2024. +# +# 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. +*/ + +// passmanager class + +#ifndef __qiskitcpp_transpiler_generate_preset_pass_manager_def_hpp__ +#define __qiskitcpp_transpiler_generate_preset_pass_manager_def_hpp__ + +#include "transpiler/passmanager.hpp" + +namespace Qiskit +{ +namespace transpiler +{ + + +const std::vector default_stages = {"init", "layout", "routing", "translation", "optimization", "scheduling"}; + + +// generate presetmanager from backend +StagedPassManager generate_preset_pass_manager(uint_t optimization_level, providers::BackendV2& backend, double approximation_degree = 1.0, int seed_transpiler = -1) +{ + return StagedPassManager(default_stages, backend, optimization_level, approximation_degree, seed_transpiler); +} + +// generate presetmanager from target +StagedPassManager generate_preset_pass_manager(uint_t optimization_level, Target& target, double approximation_degree = 1.0, int seed_transpiler = -1) +{ + return StagedPassManager(default_stages, target, optimization_level, approximation_degree, seed_transpiler); +} + +// generate presetmanager from basis gates and coupling map +StagedPassManager generate_preset_pass_manager(uint_t optimization_level, const std::vector& basis_gates, const std::vector>& coupling_map, double approximation_degree = 1.0, int seed_transpiler = -1) +{ + auto target = Target(basis_gates, coupling_map); + return StagedPassManager(default_stages, target, optimization_level, approximation_degree, seed_transpiler); +} + +// generate presetmanager from instruction properties +StagedPassManager generate_preset_pass_manager(uint_t optimization_level, const std::unordered_map>& props, double approximation_degree = 1.0, int seed_transpiler = -1) +{ + auto target = Target(props); + return StagedPassManager(default_stages, target, optimization_level, approximation_degree, seed_transpiler); +} + +} // namespace transpiler +} // namespace Qiskit + +#endif //__qiskitcpp_transpiler_passmanager_def_hpp__ + diff --git a/src/transpiler/target.hpp b/src/transpiler/target.hpp index 837947b..061e251 100644 --- a/src/transpiler/target.hpp +++ b/src/transpiler/target.hpp @@ -30,12 +30,23 @@ namespace Qiskit namespace transpiler { +/// @struct InstructionProperty +/// @brief properties for an instruction +struct InstructionProperty +{ + std::vector qargs; + double duration; + double error; +}; + + + /// @class Target /// @brief target object to describe backend's properties class Target { protected: - QkTarget *target_ = nullptr; + std::shared_ptr target_ = nullptr; std::string backend_name_; std::vector basis_gates_; double dt_; @@ -43,7 +54,8 @@ class Target uint_t max_shots_; uint_t num_qubits_ = 0; bool is_set_ = false; - + std::vector> coupling_map_; + std::unordered_map> properties_; public: /// @brief Create a new target Target() {} @@ -51,23 +63,48 @@ class Target /// @brief Create a new target Target(QkTarget* target) { - target_ = target; + target_ = std::shared_ptr(target, qk_target_free); } + /// @brief Create a new target + Target(const Target& other) + { + target_ = other.target_; + backend_name_ = other.backend_name_; + basis_gates_ = other.basis_gates_; + dt_ = other.dt_; + max_experiments_ = other.max_experiments_; + max_shots_ = other.max_shots_; + num_qubits_ = other.num_qubits_; + is_set_ = other.is_set_; + } + + Target(const std::unordered_map>& props) + { + properties_ = props; + } + + Target(const std::vector& basis_gates, const std::vector>& coupling_map, double default_duration = 0.0, double default_error = 0.0); + ~Target() { - if (target_) - { - qk_target_free(target_); + if (target_) { + target_.reset(); } } bool is_set(void) const { return is_set_; } - const QkTarget *rust_target(void) const + const QkTarget *rust_target(void) { - return target_; + if (!is_set_) { + build_target(); + } + if (target_) { + return target_.get(); + } + return nullptr; } /// @brief name of the target @@ -95,8 +132,63 @@ class Target /// @param input json target obtained from IQP /// @return true if target is successfully made bool from_json(nlohmann::ordered_json &input); + + /// @brief add instruction to the target + /// @param instruction reference to the instruction to be added + /// @param properties properties of the instruction + void add_instruction(const circuit::Instruction& instruction, const std::vector& properties); + +protected: + void build_target(void); + }; + +Target::Target(const std::vector& basis_gates, const std::vector>& coupling_map, double default_duration, double default_error) +{ + basis_gates_ = basis_gates; + coupling_map_ = coupling_map; + + // get num qubits + num_qubits_ = 0; + for (auto &qubits : coupling_map) { + if (qubits.first > num_qubits_) { + num_qubits_ = qubits.first; + } + if (qubits.second > num_qubits_) { + num_qubits_ = qubits.second; + } + } + num_qubits_ += 1; + + auto name_map = Qiskit::circuit::get_standard_gate_name_mapping(); + for (auto &gate_name : basis_gates) { + std::vector props; + auto gate = name_map.find(gate_name); + if (gate != name_map.end()) { + if (gate->second.num_qubits() == 1) { + for (uint_t i = 0; i < num_qubits_; i++) { + InstructionProperty p; + p.qargs = {(uint32_t)i}; + p.duration = default_duration; + p.error = default_error; + props.push_back(p); + } + } else if (gate->second.num_qubits() == 2) { + for (auto &qargs : coupling_map) { + InstructionProperty p; + p.qargs = {qargs.first, qargs.second}; + p.duration = default_duration; + p.error = default_error; + props.push_back(p); + } + } + } + properties_[gate_name] = props; + } +} + + bool Target::from_json(nlohmann::ordered_json &input) { if (!input.contains("configuration")) { @@ -116,14 +208,14 @@ bool Target::from_json(nlohmann::ordered_json &input) } num_qubits_ = backend_configuration["n_qubits"]; - if (target_) - { - qk_target_free(target_); + if (target_) { + target_.reset(); } - target_ = qk_target_new((uint32_t)num_qubits_); - if (target_ == nullptr) + QkTarget* t = qk_target_new((uint32_t)num_qubits_); + if (t == nullptr) return false; + target_ = std::shared_ptr(t, qk_target_free); max_experiments_ = backend_configuration["max_experiments"]; max_shots_ = backend_configuration["max_shots"]; @@ -131,30 +223,27 @@ bool Target::from_json(nlohmann::ordered_json &input) // set target configs available in C-API if (backend_configuration.at("dt").is_number()) { dt_ = backend_configuration["dt"]; - qk_target_set_dt(target_, dt_); + qk_target_set_dt(target_.get(), dt_); } if (backend_configuration.contains("timing_constraints")) { auto timing_constraints = backend_configuration["timing_constraints"]; - qk_target_set_granularity(target_, timing_constraints["granularity"]); - qk_target_set_min_length(target_, timing_constraints["min_length"]); - qk_target_set_pulse_alignment(target_, timing_constraints["pulse_alignment"]); - qk_target_set_acquire_alignment(target_, timing_constraints["acquire_alignment"]); + qk_target_set_granularity(target_.get(), timing_constraints["granularity"]); + qk_target_set_min_length(target_.get(), timing_constraints["min_length"]); + qk_target_set_pulse_alignment(target_.get(), timing_constraints["pulse_alignment"]); + qk_target_set_acquire_alignment(target_.get(), timing_constraints["acquire_alignment"]); } // get basis gates and make property entries auto name_map = Qiskit::circuit::get_standard_gate_name_mapping(); - for (auto &gate : backend_configuration["basis_gates"]) - { + for (auto &gate : backend_configuration["basis_gates"]) { basis_gates_.push_back(gate); } // add gate properties std::unordered_map property_map; - for (auto &prop : backend_properties["gates"]) - { + for (auto &prop : backend_properties["gates"]) { std::string gate = prop["gate"]; - if (gate == "rzz") - { + if (gate == "rzz") { // TODO: Add RZZ support when we have angle wrapping in // Qiskit's target and C transpiler. continue; @@ -162,49 +251,35 @@ bool Target::from_json(nlohmann::ordered_json &input) std::vector qubits = prop["qubits"]; double duration = 0.0; double error = 0.0; - for (auto ¶m : prop["parameters"]) - { - if (param["name"] == "gate_error") - { + for (auto ¶m : prop["parameters"]) { + if (param["name"] == "gate_error") { error = param["value"]; - } - else if (param["name"] == "gate_length") - { + } else if (param["name"] == "gate_length") { duration = 1e-9 * (double)param["value"]; } } QkTargetEntry *target_entry = nullptr; auto entry = property_map.find(gate); - if (entry == property_map.end()) - { + if (entry == property_map.end()) { auto inst = name_map.find(gate); - if (inst == name_map.end()) - { - if (gate == "reset") - { + if (inst == name_map.end()) { + if (gate == "reset") { target_entry = qk_target_entry_new_reset(); property_map[gate] = target_entry; } - } - else - { + } else { target_entry = qk_target_entry_new(inst->second.gate_map()); property_map[gate] = target_entry; } - } - else - { + } else { target_entry = entry->second; } - if (target_entry) - { + if (target_entry) { QkExitCode ret = qk_target_entry_add_property(target_entry, qubits.data(), (uint32_t)qubits.size(), duration, error); - if (ret != QkExitCode_Success) - { + if (ret != QkExitCode_Success) { std::cerr << " target qk_target_entry_add_property error (" << ret << ") : " << gate << " ["; - for (int i = 0; i < qubits.size(); i++) - { + for (int i = 0; i < qubits.size(); i++) { std::cerr << qubits[i] << ", "; } std::cerr << "]" << std::endl; @@ -212,11 +287,9 @@ bool Target::from_json(nlohmann::ordered_json &input) } } - for (auto &entry : property_map) - { - QkExitCode ret = qk_target_add_instruction(target_, entry.second); - if (ret != QkExitCode_Success) - { + for (auto &entry : property_map) { + QkExitCode ret = qk_target_add_instruction(target_.get(), entry.second); + if (ret != QkExitCode_Success) { std::cerr << " target qk_target_add_instruction error (" << ret << ")" << std::endl; } // qk_target_entry_free(entry.second); @@ -225,29 +298,109 @@ bool Target::from_json(nlohmann::ordered_json &input) // add measure properties QkTargetEntry *measure = qk_target_entry_new_measure(); uint32_t qubit = 0; - for (uint32_t qubit = 0; qubit < backend_properties["qubits"].size(); qubit++) - { + for (uint32_t qubit = 0; qubit < backend_properties["qubits"].size(); qubit++) { double duration = 0.0; double error = 0.0; - for (auto ¶m : backend_properties["qubits"][qubit]) - { - if (param["name"] == "readout_error") - { + for (auto ¶m : backend_properties["qubits"][qubit]) { + if (param["name"] == "readout_error") { error = param["value"]; - } - else if (param["name"] == "readout_length") - { + } else if (param["name"] == "readout_length") { duration = 1e-9 * (double)param["value"]; } } qk_target_entry_add_property(measure, &qubit, 1, duration, error); } - qk_target_add_instruction(target_, measure); + qk_target_add_instruction(target_.get(), measure); is_set_ = true; return true; } + +void Target::add_instruction(const circuit::Instruction& instruction, const std::vector& properties) +{ + auto prop = properties_.find(instruction.name()); + if (prop == properties_.end()) { + properties_[instruction.name()] = properties; + } else { + prop->second.insert(prop->second.end(), properties.begin(), properties.end()); + } +} + + +void Target::build_target(void) +{ + if (target_) { + target_.reset(); + } + + // get num qubits + if (num_qubits_ == 0) { + for (auto &prop : properties_) { + for (auto &inst : prop.second) { + for (auto &qubit : inst.qargs) { + if (qubit > num_qubits_) { + num_qubits_ = qubit; + } + } + } + } + } + num_qubits_ += 1; + + QkTarget* t = qk_target_new((uint32_t)num_qubits_); + if (t == nullptr) + return; + target_ = std::shared_ptr(t, qk_target_free); + + // add properties + auto name_map = Qiskit::circuit::get_standard_gate_name_mapping(); + for (auto &prop : properties_) { + QkTargetEntry *target_entry = nullptr; + if (prop.first == "reset") { + target_entry = qk_target_entry_new_reset(); + } else if (prop.first == "rzz") { + std::cerr << " Target: rzz gate is not supported until Qiskit C-API will support it." << std::endl; + } else { + auto gate = name_map.find(prop.first); + if (gate != name_map.end()) { + target_entry = qk_target_entry_new(gate->second.gate_map()); + } + } + + if (target_entry != nullptr) { + for (auto &inst : prop.second) { + QkExitCode ret = qk_target_entry_add_property(target_entry, inst.qargs.data(), (uint32_t)inst.qargs.size(), inst.duration, inst.error); + if (ret != QkExitCode_Success) { + std::cerr << " Target : qk_target_entry_add_property error (" << ret << ") : " << prop.first << " ["; + for (int i = 0; i < inst.qargs.size(); i++) { + std::cerr << inst.qargs[i] << ", "; + } + std::cerr << "]" << std::endl; + } + } + QkExitCode ret = qk_target_add_instruction(target_.get(), target_entry); + if (ret != QkExitCode_Success) { + std::cerr << " Target : qk_target_add_instruction error (" << ret << ")" << std::endl; + } + } + } + + // add measure properties + QkTargetEntry *measure = qk_target_entry_new_measure(); + uint32_t qubit = 0; + for (uint32_t qubit = 0; qubit < num_qubits_; qubit++) { + double duration = 0.0; + double error = 0.0; + qk_target_entry_add_property(measure, &qubit, 1, duration, error); + } + qk_target_add_instruction(target_.get(), measure); + + is_set_ = true; +} + + + } // namespace transpiler } // namespace Qiskit From 5e830f1ad3f2d36cd397f19359c67ad5a1c84540 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Mon, 16 Mar 2026 13:11:48 +0900 Subject: [PATCH 2/8] add target test --- samples/target_test.cpp | 7 +- src/circuit/quantumcircuit_def.hpp | 11 +- src/circuit/quantumcircuit_impl.hpp | 62 +++++++++ src/transpiler/passmanager.hpp | 4 +- .../generate_preset_pass_manager.hpp | 35 +++++- test/test_transpiler.cpp | 118 ++++++++++++++++++ 6 files changed, 228 insertions(+), 9 deletions(-) create mode 100644 test/test_transpiler.cpp diff --git a/samples/target_test.cpp b/samples/target_test.cpp index e29c763..cfd39ff 100644 --- a/samples/target_test.cpp +++ b/samples/target_test.cpp @@ -45,8 +45,11 @@ int main() std::cout << "input circuit" << std::endl; circ.print(); - auto target = Target({"cz", "id", "rx", "rz", "rzz", "sx", "x"}, {{0, 1}, {1, 0}, {1, 3}, {3, 1}, {0, 2}, {2, 0}, {2, 3}, {3, 2}}); - auto pass = StagedPassManager({"init", "layout", "routing", "translation"}, target); + + auto target = Target({"h", "cx"}, {{0, 1}, {1, 0}, {1, 3}, {3, 1}, {0, 2}, {2, 0}, {2, 3}, {3, 2}}); +// auto target = Target({"cz", "id", "rx", "rz", "rzz", "sx", "x"}, {{0, 1}, {1, 0}, {1, 3}, {3, 1}, {0, 2}, {2, 0}, {2, 3}, {3, 2}}); +// auto pass = StagedPassManager({"init", "layout", "routing", "translation"}, target); + auto pass = generate_preset_pass_manager(2, {"h", "cx"}, {{0, 1}, {1, 0}, {1, 3}, {3, 1}, {0, 2}, {2, 0}, {2, 3}, {3, 2}}, 1.0, 1); auto transpiled = pass.run(circ); std::cout << "transpiled circuit" << std::endl; diff --git a/src/circuit/quantumcircuit_def.hpp b/src/circuit/quantumcircuit_def.hpp index 26a9405..1f547ba 100644 --- a/src/circuit/quantumcircuit_def.hpp +++ b/src/circuit/quantumcircuit_def.hpp @@ -156,7 +156,7 @@ class QuantumCircuit /// @param circ smart pointer to RUst circuit /// @param map layout mapping void set_qiskit_circuit(std::shared_ptr circ, const std::vector &map); - + /// @brief get qubit mapping /// @return qubit mapping const reg_t &get_qubit_map(void) @@ -690,6 +690,15 @@ class QuantumCircuit /// @brief print circuit (this is for debug) void print(void) const; + + /// @brief compare two circuits + /// @param other a circuit to be compared with this circuit + /// @return true if two circuits are the same + bool operator==(const QuantumCircuit& other) const; + bool operator!=(const QuantumCircuit& other) const + { + return !(*this == other); + } protected: void add_pending_control_flow_op(void); diff --git a/src/circuit/quantumcircuit_impl.hpp b/src/circuit/quantumcircuit_impl.hpp index 9f9fca2..241a942 100644 --- a/src/circuit/quantumcircuit_impl.hpp +++ b/src/circuit/quantumcircuit_impl.hpp @@ -1583,6 +1583,68 @@ std::string QuantumCircuit::to_qasm3(void) return qasm3.str(); } + +bool QuantumCircuit::operator==(const QuantumCircuit& other) const +{ + if (num_qubits_ != other.num_qubits_) { + return false; + } + if (num_clbits_ != other.num_clbits_) { + return false; + } + if (global_phase_ != other.global_phase_) { + return false; + } + + // compare instructions + uint_t nops; + uint_t nops_other; + nops = qk_circuit_num_instructions(rust_circuit_.get()); + nops_other = qk_circuit_num_instructions(other.rust_circuit_.get()); + + for (uint_t i = 0; i < nops; i++) { + QkCircuitInstruction *op = new QkCircuitInstruction; + QkCircuitInstruction *op_other = new QkCircuitInstruction; + qk_circuit_get_instruction(rust_circuit_.get(), i, op); + qk_circuit_get_instruction(other.rust_circuit_.get(), i, op_other); + + if (std::string(op->name) != std::string(op_other->name)) { + qk_circuit_instruction_clear(op); + qk_circuit_instruction_clear(op_other); + return false; + } + if (op->num_qubits != op_other->num_qubits || op->num_clbits != op_other->num_clbits || op->num_params != op_other->num_qubits) { + qk_circuit_instruction_clear(op); + qk_circuit_instruction_clear(op_other); + return false; + } + for (int i = 0; i < op->num_qubits; i++) { + if (op->qubits[i] != op_other->qubits[i]) { + qk_circuit_instruction_clear(op); + qk_circuit_instruction_clear(op_other); + return false; + } + } + for (int i = 0; i < op->num_params; i++) { + if (op->params[i] != op_other->params[i]) { + qk_circuit_instruction_clear(op); + qk_circuit_instruction_clear(op_other); + return false; + } + } + for (int i = 0; i < op->num_clbits; i++) { + if (op->clbits[i] != op_other->clbits[i]) { + qk_circuit_instruction_clear(op); + qk_circuit_instruction_clear(op_other); + return false; + } + } + } + return true; +} + + + } // namespace circuit } // namespace Qiskit diff --git a/src/transpiler/passmanager.hpp b/src/transpiler/passmanager.hpp index 73a93ea..2ca65e4 100644 --- a/src/transpiler/passmanager.hpp +++ b/src/transpiler/passmanager.hpp @@ -130,7 +130,7 @@ circuit::QuantumCircuit StagedPassManager::run(circuit::QuantumCircuit& circ) char *error; QkExitCode ret; - /*if (stages_.size() == 6) { + if (stages_.size() == 6) { if (stages_[0] == "init" && stages_[1] == "layout" && stages_[2] == "routing" && stages_[3] == "translation" && stages_[4] == "optimization" && stages_[5] == "scheduling") { // use default transpiler @@ -152,7 +152,7 @@ circuit::QuantumCircuit StagedPassManager::run(circuit::QuantumCircuit& circ) qk_transpile_layout_free(result.layout); return transpiled; } - }*/ + } QkDag* dag = qk_circuit_to_dag(circ.get_rust_circuit().get()); if (dag == nullptr) { diff --git a/src/transpiler/preset_passmanagers/generate_preset_pass_manager.hpp b/src/transpiler/preset_passmanagers/generate_preset_pass_manager.hpp index 82a8a8a..e519df6 100644 --- a/src/transpiler/preset_passmanagers/generate_preset_pass_manager.hpp +++ b/src/transpiler/preset_passmanagers/generate_preset_pass_manager.hpp @@ -28,26 +28,53 @@ namespace transpiler const std::vector default_stages = {"init", "layout", "routing", "translation", "optimization", "scheduling"}; -// generate presetmanager from backend +/// @brief generate presetmanager from backend +/// @param optimization_level an optimization level (0 to 3) +/// @param backend a backend object used for transpilation +/// @param approximation_degree Heuristic dial used for circuit approximation +/// (1.0=no approximation, 0.0=maximal approximation). +/// @param seed_transpiler Sets random seed for the stochastic parts of the transpiler. +/// @return a new preset pass manager StagedPassManager generate_preset_pass_manager(uint_t optimization_level, providers::BackendV2& backend, double approximation_degree = 1.0, int seed_transpiler = -1) { return StagedPassManager(default_stages, backend, optimization_level, approximation_degree, seed_transpiler); } -// generate presetmanager from target + +/// @brief generate presetmanager from target +/// @param optimization_level an optimization level (0 to 3) +/// @param target a target object used for transpilation +/// @param approximation_degree Heuristic dial used for circuit approximation +/// (1.0=no approximation, 0.0=maximal approximation). +/// @param seed_transpiler Sets random seed for the stochastic parts of the transpiler. +/// @return a new preset pass manager StagedPassManager generate_preset_pass_manager(uint_t optimization_level, Target& target, double approximation_degree = 1.0, int seed_transpiler = -1) { return StagedPassManager(default_stages, target, optimization_level, approximation_degree, seed_transpiler); } -// generate presetmanager from basis gates and coupling map +/// @brief generate presetmanager from basis gates and coupling map +/// @param optimization_level an optimization level (0 to 3) +/// @param basis_gates a list of basis gates in string +/// @param coupling_map a list of pairs of connected qubits +/// @param approximation_degree Heuristic dial used for circuit approximation +/// (1.0=no approximation, 0.0=maximal approximation). +/// @param seed_transpiler Sets random seed for the stochastic parts of the transpiler. +/// @return a new preset pass manager StagedPassManager generate_preset_pass_manager(uint_t optimization_level, const std::vector& basis_gates, const std::vector>& coupling_map, double approximation_degree = 1.0, int seed_transpiler = -1) { auto target = Target(basis_gates, coupling_map); return StagedPassManager(default_stages, target, optimization_level, approximation_degree, seed_transpiler); } -// generate presetmanager from instruction properties + +/// @brief generate presetmanager from instruction properties +/// @param optimization_level an optimization level (0 to 3) +/// @param props a list of instruction properties for the backend used for transpilation +/// @param approximation_degree Heuristic dial used for circuit approximation +/// (1.0=no approximation, 0.0=maximal approximation). +/// @param seed_transpiler Sets random seed for the stochastic parts of the transpiler. +/// @return a new preset pass manager StagedPassManager generate_preset_pass_manager(uint_t optimization_level, const std::unordered_map>& props, double approximation_degree = 1.0, int seed_transpiler = -1) { auto target = Target(props); diff --git a/test/test_transpiler.cpp b/test/test_transpiler.cpp new file mode 100644 index 0000000..9d71cf0 --- /dev/null +++ b/test/test_transpiler.cpp @@ -0,0 +1,118 @@ +// 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. + +#include +#include +#include + +#include "circuit/quantumcircuit.hpp" +#include "transpiler/target.hpp" +#include "transpiler/preset_passmanagers/generate_preset_pass_manager.hpp" + +using namespace Qiskit; +using namespace Qiskit::circuit; +using namespace Qiskit::transpiler; + +extern "C" { + #include "common.h" +} + +static int test_translate_h(void) +{ + QuantumRegister qr(1); + ClassicalRegister cr(1); + QuantumCircuit circ(qr, cr); + + circ.h(0); + + auto target = Target({"cz", "id", "rx", "rz", "sx", "x"}, {}); + auto pass = StagedPassManager({"init", "layout", "routing", "translation"}, target, 1.0, 1); + auto transpiled = pass.run(circ); + + QuantumCircuit circ_ref(1, 1); + circ_ref.rz(std::numers::pi / 2, 0); + circ_ref.sx(0); + circ_ref.rz(std::numers::pi / 2, 0); + + if (transpiled != circ_ref) { + return EqualityError; + } + return Ok; +} + +static int test_translate_cx(void) +{ + QuantumRegister qr(2); + ClassicalRegister cr(2); + QuantumCircuit circ(qr, cr); + + circ.cx(0, 1); + + auto target = Target({"cz", "id", "rx", "rz", "sx", "x"}, {{0, 1}, {1, 0}}); + auto pass = StagedPassManager({"init", "layout", "routing", "translation"}, target, 1.0, 1); + auto transpiled = pass.run(circ); + + QuantumCircuit circ_ref(2, 2); + circ_ref.rz(std::numers::pi / 2, 1); + circ_ref.sx(1); + circ_ref.rz(std::numers::pi / 2, 1); + circ_ref.cz(0, 1); + circ_ref.rz(std::numers::pi / 2, 1); + circ_ref.sx(1); + circ_ref.rz(std::numers::pi / 2, 1); + + if (transpiled != circ_ref) { + return EqualityError; + } + return Ok; +} + +static int test_ghz_routing(void) +{ + QuantumRegister qr(3); + ClassicalRegister cr(3); + QuantumCircuit circ(qr, cr); + + circ.h(0); + for (int i = 1; i < 3; i++) { + circ.cx(0, i); + } + circ.measure(qr, cr); + + auto target = Target({"h", "cx"}, {{0, 1}, {1, 0}, {1, 2}, {2, 1}}); + auto pass = StagedPassManager({"init", "layout", "routing"}, target); + auto transpiled = pass.run(circ); + + QuantumCircuit circ_ref(3, 3); + circ_ref.h(1); + circ_ref.cx(1, 0); + circ_ref.cx(1, 2); + circ_ref.measure(1, 0); + circ_ref.measure(0, 1); + circ_ref.measure(2, 2); + + if (transpiled != circ_ref) { + return EqualityError; + } + return Ok; +} + + +extern "C" int test_circuit(void) { + int num_failed = 0; + num_failed += RUN_TEST(test_translate_h); + num_failed += RUN_TEST(test_translate_cx); + num_failed += RUN_TEST(test_ghz_routing); + + std::cerr << "=== Number of failed subtests: " << num_failed << std::endl; + return num_failed; +} From b57c211700e777f48ae59f8b72e7a2b86b47a5f7 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Mon, 16 Mar 2026 13:51:59 +0900 Subject: [PATCH 3/8] fix test --- test/test_transpiler.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/test_transpiler.cpp b/test/test_transpiler.cpp index 9d71cf0..1fc02e4 100644 --- a/test/test_transpiler.cpp +++ b/test/test_transpiler.cpp @@ -39,9 +39,9 @@ static int test_translate_h(void) auto transpiled = pass.run(circ); QuantumCircuit circ_ref(1, 1); - circ_ref.rz(std::numers::pi / 2, 0); + circ_ref.rz(std::numbers::pi / 2, 0); circ_ref.sx(0); - circ_ref.rz(std::numers::pi / 2, 0); + circ_ref.rz(std::numbers::pi / 2, 0); if (transpiled != circ_ref) { return EqualityError; @@ -62,13 +62,13 @@ static int test_translate_cx(void) auto transpiled = pass.run(circ); QuantumCircuit circ_ref(2, 2); - circ_ref.rz(std::numers::pi / 2, 1); + circ_ref.rz(std::numbers::pi / 2, 1); circ_ref.sx(1); - circ_ref.rz(std::numers::pi / 2, 1); + circ_ref.rz(std::numbers::pi / 2, 1); circ_ref.cz(0, 1); - circ_ref.rz(std::numers::pi / 2, 1); + circ_ref.rz(std::numbers::pi / 2, 1); circ_ref.sx(1); - circ_ref.rz(std::numers::pi / 2, 1); + circ_ref.rz(std::numbers::pi / 2, 1); if (transpiled != circ_ref) { return EqualityError; From 590d08536fc60bda420fb480ba7b144ddad02097 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Mon, 16 Mar 2026 14:08:26 +0900 Subject: [PATCH 4/8] fix test --- test/test_transpiler.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/test/test_transpiler.cpp b/test/test_transpiler.cpp index 1fc02e4..8c553f3 100644 --- a/test/test_transpiler.cpp +++ b/test/test_transpiler.cpp @@ -12,7 +12,9 @@ #include #include -#include + +#define _USE_MATH_DEFINES +#include #include "circuit/quantumcircuit.hpp" #include "transpiler/target.hpp" @@ -39,9 +41,9 @@ static int test_translate_h(void) auto transpiled = pass.run(circ); QuantumCircuit circ_ref(1, 1); - circ_ref.rz(std::numbers::pi / 2, 0); + circ_ref.rz(M_PI / 2.0, 0); circ_ref.sx(0); - circ_ref.rz(std::numbers::pi / 2, 0); + circ_ref.rz(M_PI / 2.0, 0); if (transpiled != circ_ref) { return EqualityError; @@ -62,13 +64,13 @@ static int test_translate_cx(void) auto transpiled = pass.run(circ); QuantumCircuit circ_ref(2, 2); - circ_ref.rz(std::numbers::pi / 2, 1); + circ_ref.rz(M_PI / 2.0, 1); circ_ref.sx(1); - circ_ref.rz(std::numbers::pi / 2, 1); + circ_ref.rz(M_PI / 2.0, 1); circ_ref.cz(0, 1); - circ_ref.rz(std::numbers::pi / 2, 1); + circ_ref.rz(M_PI / 2.0, 1); circ_ref.sx(1); - circ_ref.rz(std::numbers::pi / 2, 1); + circ_ref.rz(M_PI / 2.0, 1); if (transpiled != circ_ref) { return EqualityError; From baa4320caaf5271f5ad571afb497c51c2d823618 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Mon, 16 Mar 2026 15:10:28 +0900 Subject: [PATCH 5/8] fix parameter for qk_transpile_stage_optimization --- samples/target_test.cpp | 4 ++-- src/transpiler/passmanager.hpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/samples/target_test.cpp b/samples/target_test.cpp index cfd39ff..2df2f05 100644 --- a/samples/target_test.cpp +++ b/samples/target_test.cpp @@ -48,8 +48,8 @@ int main() auto target = Target({"h", "cx"}, {{0, 1}, {1, 0}, {1, 3}, {3, 1}, {0, 2}, {2, 0}, {2, 3}, {3, 2}}); // auto target = Target({"cz", "id", "rx", "rz", "rzz", "sx", "x"}, {{0, 1}, {1, 0}, {1, 3}, {3, 1}, {0, 2}, {2, 0}, {2, 3}, {3, 2}}); -// auto pass = StagedPassManager({"init", "layout", "routing", "translation"}, target); - auto pass = generate_preset_pass_manager(2, {"h", "cx"}, {{0, 1}, {1, 0}, {1, 3}, {3, 1}, {0, 2}, {2, 0}, {2, 3}, {3, 2}}, 1.0, 1); + auto pass = StagedPassManager({"init", "layout", "routing", "translation"}, target); + //auto pass = generate_preset_pass_manager(2, {"h", "cx"}, {{0, 1}, {1, 0}, {1, 3}, {3, 1}, {0, 2}, {2, 0}, {2, 3}, {3, 2}}, 1.0, 1); auto transpiled = pass.run(circ); std::cout << "transpiled circuit" << std::endl; diff --git a/src/transpiler/passmanager.hpp b/src/transpiler/passmanager.hpp index 2ca65e4..1b3d941 100644 --- a/src/transpiler/passmanager.hpp +++ b/src/transpiler/passmanager.hpp @@ -81,7 +81,7 @@ class StagedPassManager : public PassManager StagedPassManager(const StagedPassManager& other) : PassManager(other) { stages_ = other.stages_; - optimization_level_ = optimization_level_; + optimization_level_ = other.optimization_level_; approximation_degree_ = other.approximation_degree_; seed_transpiler_ = other.seed_transpiler_; } @@ -182,7 +182,7 @@ circuit::QuantumCircuit StagedPassManager::run(circuit::QuantumCircuit& circ) std::cerr << "StagedPassManager Error in transplation stage (" << ret << ") : " << error << std::endl; } } else if (stage == "optimization") { - ret = qk_transpile_stage_optimization(dag, target_.rust_target(), &options, &error); + ret = qk_transpile_stage_optimization(dag, target_.rust_target(), &options, &error, layout); if (ret != QkExitCode_Success) { std::cerr << "StagedPassManager Error in optimization stage (" << ret << ") : " << error << std::endl; } From ee5f54a9941deaa836476933beba3ad875bc64d7 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Mon, 16 Mar 2026 15:35:37 +0900 Subject: [PATCH 6/8] fix test --- samples/target_test.cpp | 2 +- test/test_transpiler.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/target_test.cpp b/samples/target_test.cpp index 2df2f05..07e6f33 100644 --- a/samples/target_test.cpp +++ b/samples/target_test.cpp @@ -48,7 +48,7 @@ int main() auto target = Target({"h", "cx"}, {{0, 1}, {1, 0}, {1, 3}, {3, 1}, {0, 2}, {2, 0}, {2, 3}, {3, 2}}); // auto target = Target({"cz", "id", "rx", "rz", "rzz", "sx", "x"}, {{0, 1}, {1, 0}, {1, 3}, {3, 1}, {0, 2}, {2, 0}, {2, 3}, {3, 2}}); - auto pass = StagedPassManager({"init", "layout", "routing", "translation"}, target); + auto pass = StagedPassManager({"init", "layout", "routing", "translation", "optimization"}, target); //auto pass = generate_preset_pass_manager(2, {"h", "cx"}, {{0, 1}, {1, 0}, {1, 3}, {3, 1}, {0, 2}, {2, 0}, {2, 3}, {3, 2}}, 1.0, 1); auto transpiled = pass.run(circ); diff --git a/test/test_transpiler.cpp b/test/test_transpiler.cpp index 8c553f3..05dfe70 100644 --- a/test/test_transpiler.cpp +++ b/test/test_transpiler.cpp @@ -109,7 +109,7 @@ static int test_ghz_routing(void) } -extern "C" int test_circuit(void) { +extern "C" int test_transpiler(void) { int num_failed = 0; num_failed += RUN_TEST(test_translate_h); num_failed += RUN_TEST(test_translate_cx); From 83bd4069c6b9431f5b038a73f517b2be47feaf94 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Mon, 16 Mar 2026 18:19:17 +0900 Subject: [PATCH 7/8] fix test, fix QuantumCircuit::operator== --- src/circuit/controlflow/control_flow.hpp | 4 +-- src/circuit/quantumcircuit_def.hpp | 3 +-- src/circuit/quantumcircuit_impl.hpp | 27 ++++++++++--------- test/common.h | 5 ++++ test/{test_circuit.cpp => test_circuit.hpp} | 0 test/test_main.cpp | 25 +++++++++++++++++ ...{test_parameter.cpp => test_parameter.hpp} | 0 ...est_transpiler.cpp => test_transpiler.hpp} | 19 ++++++++++--- 8 files changed, 63 insertions(+), 20 deletions(-) rename test/{test_circuit.cpp => test_circuit.hpp} (100%) create mode 100644 test/test_main.cpp rename test/{test_parameter.cpp => test_parameter.hpp} (100%) rename test/{test_transpiler.cpp => test_transpiler.hpp} (84%) diff --git a/src/circuit/controlflow/control_flow.hpp b/src/circuit/controlflow/control_flow.hpp index 947eaa2..689a173 100644 --- a/src/circuit/controlflow/control_flow.hpp +++ b/src/circuit/controlflow/control_flow.hpp @@ -29,7 +29,7 @@ class ControlFlowOp { protected: // TO DO : this is temporary, make condition class to handle multiple conditions - Expr expr_; + //Expr expr_; uint32_t clbit_; uint32_t value_; public: @@ -44,7 +44,7 @@ class ControlFlowOp /// @brief Create a new instruction. /// @param (expr) expression - ControlFlowOp(Expr expr) : expr_(expr) {} + //ControlFlowOp(Expr expr) : expr_(expr) {} virtual void add_control_flow_op(QuantumCircuit& circ) = 0; }; diff --git a/src/circuit/quantumcircuit_def.hpp b/src/circuit/quantumcircuit_def.hpp index 1f547ba..6083330 100644 --- a/src/circuit/quantumcircuit_def.hpp +++ b/src/circuit/quantumcircuit_def.hpp @@ -23,12 +23,11 @@ #include "utils/types.hpp" #include "circuit/parameter.hpp" -#include "circuit/classical/expr.hpp" +//#include "circuit/classical/expr.hpp" #include "circuit/classicalregister.hpp" #include "circuit/quantumregister.hpp" #include "circuit/library/standard_gates/standard_gates.hpp" #include "circuit/circuitinstruction.hpp" -#include "transpiler/target.hpp" #include #include "qiskit.h" diff --git a/src/circuit/quantumcircuit_impl.hpp b/src/circuit/quantumcircuit_impl.hpp index 241a942..1e2c537 100644 --- a/src/circuit/quantumcircuit_impl.hpp +++ b/src/circuit/quantumcircuit_impl.hpp @@ -20,8 +20,12 @@ #include #include +#include +#include +#include #include #include +#include #include "controlflow/__init__.hpp" @@ -1586,12 +1590,6 @@ std::string QuantumCircuit::to_qasm3(void) bool QuantumCircuit::operator==(const QuantumCircuit& other) const { - if (num_qubits_ != other.num_qubits_) { - return false; - } - if (num_clbits_ != other.num_clbits_) { - return false; - } if (global_phase_ != other.global_phase_) { return false; } @@ -1601,6 +1599,9 @@ bool QuantumCircuit::operator==(const QuantumCircuit& other) const uint_t nops_other; nops = qk_circuit_num_instructions(rust_circuit_.get()); nops_other = qk_circuit_num_instructions(other.rust_circuit_.get()); + if (nops != nops_other) { + return false; + } for (uint_t i = 0; i < nops; i++) { QkCircuitInstruction *op = new QkCircuitInstruction; @@ -1613,27 +1614,27 @@ bool QuantumCircuit::operator==(const QuantumCircuit& other) const qk_circuit_instruction_clear(op_other); return false; } - if (op->num_qubits != op_other->num_qubits || op->num_clbits != op_other->num_clbits || op->num_params != op_other->num_qubits) { + if (op->num_qubits != op_other->num_qubits || op->num_clbits != op_other->num_clbits || op->num_params != op_other->num_params) { qk_circuit_instruction_clear(op); qk_circuit_instruction_clear(op_other); return false; } - for (int i = 0; i < op->num_qubits; i++) { - if (op->qubits[i] != op_other->qubits[i]) { + for (int j = 0; j < op->num_qubits; j++) { + if (op->qubits[j] != op_other->qubits[j]) { qk_circuit_instruction_clear(op); qk_circuit_instruction_clear(op_other); return false; } } - for (int i = 0; i < op->num_params; i++) { - if (op->params[i] != op_other->params[i]) { + for (int j = 0; j < op->num_params; j++) { + if (op->params[j] != op_other->params[j]) { qk_circuit_instruction_clear(op); qk_circuit_instruction_clear(op_other); return false; } } - for (int i = 0; i < op->num_clbits; i++) { - if (op->clbits[i] != op_other->clbits[i]) { + for (int j = 0; j < op->num_clbits; j++) { + if (op->clbits[j] != op_other->clbits[j]) { qk_circuit_instruction_clear(op); qk_circuit_instruction_clear(op_other); return false; diff --git a/test/common.h b/test/common.h index a474800..238ef78 100644 --- a/test/common.h +++ b/test/common.h @@ -10,6 +10,9 @@ // copyright notice, and modified files need to carry a notice indicating // that they have been altered from the originals. +#ifndef _TEST_COMMON_H_ +#define _TEST_COMMON_H_ + #include // An enumeration of test results. These should be returned by test functions to @@ -30,3 +33,5 @@ enum TestResult { // post-process the returned `TestResult` to product a minimal info message for // the developer running the test suite. int run(const char *name, int (*test_function)(void)); + +#endif//_TEST_COMMON_H_ diff --git a/test/test_circuit.cpp b/test/test_circuit.hpp similarity index 100% rename from test/test_circuit.cpp rename to test/test_circuit.hpp diff --git a/test/test_main.cpp b/test/test_main.cpp new file mode 100644 index 0000000..bd25a96 --- /dev/null +++ b/test/test_main.cpp @@ -0,0 +1,25 @@ +// 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. + +#include "test_circuit.hpp" +#include "test_parameter.hpp" +#include "test_transpiler.hpp" + +extern "C" int test_main(void) { + int num_failed = 0; + + num_failed += RUN_TEST(test_circuit); + num_failed += RUN_TEST(test_parameter); + num_failed += RUN_TEST(test_transpiler); + + return num_failed; +} diff --git a/test/test_parameter.cpp b/test/test_parameter.hpp similarity index 100% rename from test/test_parameter.cpp rename to test/test_parameter.hpp diff --git a/test/test_transpiler.cpp b/test/test_transpiler.hpp similarity index 84% rename from test/test_transpiler.cpp rename to test/test_transpiler.hpp index 05dfe70..012ebf9 100644 --- a/test/test_transpiler.cpp +++ b/test/test_transpiler.hpp @@ -12,13 +12,11 @@ #include #include - #define _USE_MATH_DEFINES #include #include "circuit/quantumcircuit.hpp" -#include "transpiler/target.hpp" -#include "transpiler/preset_passmanagers/generate_preset_pass_manager.hpp" +#include "transpiler/passmanager.hpp" using namespace Qiskit; using namespace Qiskit::circuit; @@ -46,6 +44,11 @@ static int test_translate_h(void) circ_ref.rz(M_PI / 2.0, 0); if (transpiled != circ_ref) { + std::cout << " reference circuit : " << std::endl; + circ_ref.print(); + std::cout << " transpiled circuit : " << std::endl; + transpiled.print(); + return EqualityError; } return Ok; @@ -73,6 +76,11 @@ static int test_translate_cx(void) circ_ref.rz(M_PI / 2.0, 1); if (transpiled != circ_ref) { + std::cout << " reference circuit : " << std::endl; + circ_ref.print(); + std::cout << " transpiled circuit : " << std::endl; + transpiled.print(); + return EqualityError; } return Ok; @@ -103,6 +111,11 @@ static int test_ghz_routing(void) circ_ref.measure(2, 2); if (transpiled != circ_ref) { + std::cout << " reference circuit : " << std::endl; + circ_ref.print(); + std::cout << " transpiled circuit : " << std::endl; + transpiled.print(); + return EqualityError; } return Ok; From 88829cb8f48b83a17ab1976e7e3498d3ccf3e120 Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Mon, 16 Mar 2026 22:10:09 +0900 Subject: [PATCH 8/8] fix for Windows --- src/primitives/backend_sampler_job.hpp | 1 + src/service/qiskit_runtime_service_c.hpp | 1 + src/service/qiskit_runtime_service_qrmi.hpp | 1 + src/utils/utils.hpp | 1 + 4 files changed, 4 insertions(+) diff --git a/src/primitives/backend_sampler_job.hpp b/src/primitives/backend_sampler_job.hpp index fb7d2c0..1512360 100644 --- a/src/primitives/backend_sampler_job.hpp +++ b/src/primitives/backend_sampler_job.hpp @@ -18,6 +18,7 @@ #define __qiskitcpp_primitives_qrmi_qiskit_ibm_runtime_job_def_hpp__ #ifdef _MSC_VER +#define NOMINMAX #include #else #include diff --git a/src/service/qiskit_runtime_service_c.hpp b/src/service/qiskit_runtime_service_c.hpp index 8bf37de..c3eb6ae 100644 --- a/src/service/qiskit_runtime_service_c.hpp +++ b/src/service/qiskit_runtime_service_c.hpp @@ -23,6 +23,7 @@ #include "providers/qkrt_backend.hpp" #ifdef _MSC_VER +#define NOMINMAX #include #else #include diff --git a/src/service/qiskit_runtime_service_qrmi.hpp b/src/service/qiskit_runtime_service_qrmi.hpp index dbca8ce..339f72c 100644 --- a/src/service/qiskit_runtime_service_qrmi.hpp +++ b/src/service/qiskit_runtime_service_qrmi.hpp @@ -25,6 +25,7 @@ #include "providers/qrmi_backend.hpp" #ifdef _MSC_VER +#define NOMINMAX #include #else #include diff --git a/src/utils/utils.hpp b/src/utils/utils.hpp index cf1e598..25b5667 100644 --- a/src/utils/utils.hpp +++ b/src/utils/utils.hpp @@ -23,6 +23,7 @@ #include #include #elif defined(_MSC_VER) +#define NOMINMAX #include #endif