diff --git a/src/circuit/quantumcircuit.hpp b/src/circuit/quantumcircuit.hpp index f1b6ebf..160384b 100644 --- a/src/circuit/quantumcircuit.hpp +++ b/src/circuit/quantumcircuit.hpp @@ -19,6 +19,7 @@ #define __qiskitcpp_circuit_quantum_circuit_hpp__ #include "circuit/quantumcircuit_def.hpp" +#include "qasm3/qasm3_exporter.hpp" #include "circuit/quantumcircuit_impl.hpp" #endif // __qiskitcpp_circuit_quantum_circuit_hpp__ diff --git a/src/circuit/quantumcircuit_def.hpp b/src/circuit/quantumcircuit_def.hpp index d8add04..fa74c49 100644 --- a/src/circuit/quantumcircuit_def.hpp +++ b/src/circuit/quantumcircuit_def.hpp @@ -22,8 +22,10 @@ #include #include #include +#include #include #include +#include #include #include "utils/types.hpp" @@ -53,6 +55,18 @@ using rust_circuit = ::QkCircuit; namespace Qiskit { +namespace circuit +{ +class QuantumCircuit; +} + +namespace qasm3 +{ +class Qasm3Exporter; +std::string dumps(circuit::QuantumCircuit &circuit); +std::string dumps(circuit::QuantumCircuit &circuit, const std::vector ¶meter_names); +} + namespace circuit { @@ -65,6 +79,7 @@ static Parameter null_param; /// @brief Qiskit representation of a quantum circuit. class QuantumCircuit { + friend class Qiskit::qasm3::Qasm3Exporter; protected: uint_t num_qubits_; // number of qubits uint_t num_clbits_; // number of classical bits @@ -79,6 +94,148 @@ class QuantumCircuit reg_t qubit_map_; // qubit map caused by transpiling std::vector> measure_map_; // a list of pair of qubit and clbit for measure + std::vector parameter_symbols_; // ordered names of parameters used in this circuit + std::map>> instruction_parameter_symbols_; + + void register_parameter_symbol(const std::string &symbol) + { + if (std::find(parameter_symbols_.begin(), parameter_symbols_.end(), symbol) == parameter_symbols_.end()) { + parameter_symbols_.push_back(symbol); + } + } + + void register_parameter(const Parameter ¶meter) + { + for (const auto &symbol : parameter.parameter_symbols_) { + register_parameter_symbol(symbol); + } + } + + void register_parameters(void) {} + + template + void register_parameters(const Parameter ¶meter, const Params &...parameters) + { + register_parameter(parameter); + register_parameters(parameters...); + } + + void register_parameters(const std::vector ¶meters) + { + for (const auto ¶meter : parameters) { + register_parameter(parameter); + } + } + + void merge_parameter_symbols(const std::vector &symbols) + { + for (const auto &symbol : symbols) { + register_parameter_symbol(symbol); + } + } + + void append_parameter_symbol_groups(std::vector> &groups) const {} + + template + void append_parameter_symbol_groups( + std::vector> &groups, + const Parameter ¶meter, + const Params &...parameters) const + { + groups.push_back(parameter.parameter_symbols_); + append_parameter_symbol_groups(groups, parameters...); + } + + template + std::vector> parameter_symbol_groups(const Params &...parameters) const + { + std::vector> groups; + append_parameter_symbol_groups(groups, parameters...); + return groups; + } + + std::vector> parameter_symbol_groups(const std::vector ¶meters) const + { + std::vector> groups; + for (const auto ¶meter : parameters) { + groups.push_back(parameter.parameter_symbols_); + } + return groups; + } + + void record_instruction_parameter_symbols( + const uint_t instruction_index, + const std::vector> &symbols) + { + bool has_symbols = false; + for (const auto &group : symbols) { + if (!group.empty()) { + has_symbols = true; + break; + } + } + if (has_symbols) { + instruction_parameter_symbols_[instruction_index] = symbols; + } + } + + void record_latest_instruction_parameter_symbols(const std::vector> &symbols) + { + const auto nops = qk_circuit_num_instructions(rust_circuit_.get()); + if (nops > 0) { + record_instruction_parameter_symbols(static_cast(nops - 1), symbols); + } + } + + static bool is_parameter_symbol_char(unsigned char c) + { + return std::isalnum(c) || c == '_'; + } + + static bool parameter_expression_contains_symbol(const std::string &expression, const std::string &symbol) + { + if (symbol.empty()) { + return false; + } + auto pos = expression.find(symbol); + while (pos != std::string::npos) { + const auto end = pos + symbol.size(); + const bool left_boundary = pos == 0 || !is_parameter_symbol_char(static_cast(expression[pos - 1])); + const bool right_boundary = end == expression.size() || !is_parameter_symbol_char(static_cast(expression[end])); + if (left_boundary && right_boundary) { + return true; + } + pos = expression.find(symbol, pos + symbol.size()); + } + return false; + } + + std::vector infer_parameter_symbols(QkParam *parameter) const + { + std::vector symbols; + char *expression_cstr = qk_param_str(parameter); + std::string expression = expression_cstr; + qk_str_free(expression_cstr); + + for (const auto &symbol : parameter_symbols_) { + if (parameter_expression_contains_symbol(expression, symbol)) { + symbols.push_back(symbol); + } + } + return symbols; + } + + + template + QkExitCode add_parameterized_gate(QkGate gate, const std::uint32_t *qubits, QkParam **params, const Params &...parameters) + { + auto result = qk_circuit_parameterized_gate(rust_circuit_.get(), gate, qubits, params); + if (result == QkExitCode_Success) { + register_parameters(parameters...); + record_latest_instruction_parameter_symbols(parameter_symbol_groups(parameters...)); + } + return result; + } public: /// @brief Create a new QuantumCircuit QuantumCircuit() {} @@ -196,6 +353,8 @@ class QuantumCircuit measure_map_ = circ.measure_map_; qubit_map_ = circ.qubit_map_; + parameter_symbols_ = circ.parameter_symbols_; + instruction_parameter_symbols_ = circ.instruction_parameter_symbols_; } ~QuantumCircuit() @@ -274,6 +433,8 @@ class QuantumCircuit copied.measure_map_ = measure_map_; copied.qubit_map_ = qubit_map_; + copied.parameter_symbols_ = parameter_symbols_; + copied.instruction_parameter_symbols_ = instruction_parameter_symbols_; return copied; } @@ -393,7 +554,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)qubit}; QkParam* params[] = {phase.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_Phase, qubits, params); + add_parameterized_gate(QkGate_Phase, qubits, params, phase); } /// @brief Apply RGate @@ -417,7 +578,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)qubit}; QkParam* params[] = {theta.qiskit_param_.get(), phi.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_R, qubits, params); + add_parameterized_gate(QkGate_R, qubits, params, theta, phi); } /// @brief Apply RXGate @@ -438,7 +599,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)qubit}; QkParam* params[] = {theta.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_RX, qubits, params); + add_parameterized_gate(QkGate_RX, qubits, params, theta); } /// @brief Apply RYGate @@ -459,7 +620,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)qubit}; QkParam* params[] = {theta.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_RY, qubits, params); + add_parameterized_gate(QkGate_RY, qubits, params, theta); } /// @brief Apply RZGate @@ -480,7 +641,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)qubit}; QkParam* params[] = {theta.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_RZ, qubits, params); + add_parameterized_gate(QkGate_RZ, qubits, params, theta); } /// @brief Apply SGate @@ -560,7 +721,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)qubit}; QkParam* params[] = {theta.qiskit_param_.get(), phi.qiskit_param_.get(), lam.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_U, qubits, params); + add_parameterized_gate(QkGate_U, qubits, params, theta, phi, lam); } /// @brief Apply U1Gate @@ -581,7 +742,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)qubit}; QkParam* params[] = {theta.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_U1, qubits, params); + add_parameterized_gate(QkGate_U1, qubits, params, theta); } /// @brief Apply U2Gate @@ -605,7 +766,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)qubit}; QkParam* params[] = {phi.qiskit_param_.get(), lam.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_U2, qubits, params); + add_parameterized_gate(QkGate_U2, qubits, params, phi, lam); } /// @brief Apply U3Gate @@ -631,7 +792,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)qubit}; QkParam* params[] = {theta.qiskit_param_.get(), phi.qiskit_param_.get(), lam.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_U3, qubits, params); + add_parameterized_gate(QkGate_U3, qubits, params, theta, phi, lam); } /// @brief Apply unitary gate specified by unitary to qubits @@ -747,7 +908,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)cqubit, (std::uint32_t)tqubit}; QkParam* params[] = {phase.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_CPhase, qubits, params); + add_parameterized_gate(QkGate_CPhase, qubits, params, phase); } /// @brief Apply controlled RXGate @@ -770,7 +931,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)cqubit, (std::uint32_t)tqubit}; QkParam* params[] = {theta.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_CRX, qubits, params); + add_parameterized_gate(QkGate_CRX, qubits, params, theta); } /// @brief Apply controlled RYGate @@ -793,7 +954,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)cqubit, (std::uint32_t)tqubit}; QkParam* params[] = {theta.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_CRY, qubits, params); + add_parameterized_gate(QkGate_CRY, qubits, params, theta); } /// @brief Apply controlled RZGate @@ -816,7 +977,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)cqubit, (std::uint32_t)tqubit}; QkParam* params[] = {theta.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_CRZ, qubits, params); + add_parameterized_gate(QkGate_CRZ, qubits, params, theta); } /// @brief Apply CSGate @@ -874,7 +1035,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)cqubit, (std::uint32_t)tqubit}; QkParam* params[] = {theta.qiskit_param_.get(), phi.qiskit_param_.get(), lam.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_CU, qubits, params); + add_parameterized_gate(QkGate_CU, qubits, params, theta, phi, lam); } /// @brief Apply CU1Gate @@ -898,7 +1059,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)cqubit, (std::uint32_t)tqubit}; QkParam* params[] = {theta.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_CU1, qubits, params); + add_parameterized_gate(QkGate_CU1, qubits, params, theta); } /// @brief Apply CU3Gate @@ -926,7 +1087,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)cqubit, (std::uint32_t)tqubit}; QkParam* params[] = {theta.qiskit_param_.get(), phi.qiskit_param_.get(), lam.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_CU3, qubits, params); + add_parameterized_gate(QkGate_CU3, qubits, params, theta, phi, lam); } /// @brief Apply RXXGate @@ -949,7 +1110,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)qubit1, (std::uint32_t)qubit2}; QkParam* params[] = {theta.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_RXX, qubits, params); + add_parameterized_gate(QkGate_RXX, qubits, params, theta); } /// @brief Apply RYYGate @@ -972,7 +1133,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)qubit1, (std::uint32_t)qubit2}; QkParam* params[] = {theta.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_RYY, qubits, params); + add_parameterized_gate(QkGate_RYY, qubits, params, theta); } /// @brief Apply RZZGate @@ -995,7 +1156,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)qubit1, (std::uint32_t)qubit2}; QkParam* params[] = {theta.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_RZZ, qubits, params); + add_parameterized_gate(QkGate_RZZ, qubits, params, theta); } /// @brief Apply RZXGate @@ -1018,7 +1179,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)qubit1, (std::uint32_t)qubit2}; QkParam* params[] = {theta.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_RZX, qubits, params); + add_parameterized_gate(QkGate_RZX, qubits, params, theta); } /// @brief Apply XXminusYY @@ -1044,7 +1205,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)qubit1, (std::uint32_t)qubit2}; QkParam* params[] = {theta.qiskit_param_.get(), beta.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_XXMinusYY, qubits, params); + add_parameterized_gate(QkGate_XXMinusYY, qubits, params, theta, beta); } /// @brief Apply XXplusYY @@ -1070,7 +1231,7 @@ class QuantumCircuit std::uint32_t qubits[] = {(std::uint32_t)qubit1, (std::uint32_t)qubit2}; QkParam* params[] = {theta.qiskit_param_.get(), beta.qiskit_param_.get()}; pre_add_gate(); - qk_circuit_parameterized_gate(rust_circuit_.get(), QkGate_XXPlusYY, qubits, params); + add_parameterized_gate(QkGate_XXPlusYY, qubits, params, theta, beta); } /// @brief Apply CCXGate @@ -1344,25 +1505,32 @@ class QuantumCircuit for (uint_t j = 0; j < op->num_clbits; j++) { vclbits[j] = (std::uint32_t)clbits[op->clbits[j]]; } - } - QkOperationKind kind = qk_circuit_instruction_kind(rust_circuit_.get(), i); - if (kind == QkOperationKind_Measure) { - qk_circuit_measure(rust_circuit_.get(), vqubits[0], vclbits[0]); - } else if (kind == QkOperationKind_Reset) { - qk_circuit_reset(rust_circuit_.get(), vqubits[0]); - } else if (kind == QkOperationKind_Barrier) { - qk_circuit_barrier(rust_circuit_.get(), vqubits.data(), (uint32_t)vqubits.size()); - } else if (kind == QkOperationKind_Gate) { - qk_circuit_parameterized_gate(rust_circuit_.get(), name_map[op->name].gate_map(), vqubits.data(), op->params); - } else if (kind == QkOperationKind_Unitary) { - // TO DO : how we can get unitary matrix from Rust ? - } + } + QkOperationKind kind = qk_circuit_instruction_kind(circ.rust_circuit_.get(), i); + if (kind == QkOperationKind_Measure) { + qk_circuit_measure(rust_circuit_.get(), vqubits[0], vclbits[0]); + } else if (kind == QkOperationKind_Reset) { + qk_circuit_reset(rust_circuit_.get(), vqubits[0]); + } else if (kind == QkOperationKind_Barrier) { + qk_circuit_barrier(rust_circuit_.get(), vqubits.data(), (uint32_t)vqubits.size()); + } else if (kind == QkOperationKind_Gate) { + auto result = qk_circuit_parameterized_gate(rust_circuit_.get(), name_map[op->name].gate_map(), vqubits.data(), op->params); + if (result == QkExitCode_Success) { + auto source_symbols = circ.instruction_parameter_symbols_.find(i); + if (source_symbols != circ.instruction_parameter_symbols_.end()) { + record_latest_instruction_parameter_symbols(source_symbols->second); + } + } + } else if (kind == QkOperationKind_Unitary) { + // TO DO : how we can get unitary matrix from Rust ? + } qk_circuit_instruction_clear(op); } for (auto m : circ.measure_map_) { measure_map_.push_back(m); } + merge_parameter_symbols(circ.parameter_symbols_); } /// @brief append a gate at the end of the circuit @@ -1383,7 +1551,11 @@ class QuantumCircuit for (auto &p : op.params()) { params.push_back(p.qiskit_param_.get()); } - qk_circuit_parameterized_gate(rust_circuit_.get(), op.gate_map(), vqubits.data(), params.data()); + auto result = qk_circuit_parameterized_gate(rust_circuit_.get(), op.gate_map(), vqubits.data(), params.data()); + if (result == QkExitCode_Success) { + register_parameters(op.params()); + record_latest_instruction_parameter_symbols(parameter_symbol_groups(op.params())); + } } else qk_circuit_gate(rust_circuit_.get(), op.gate_map(), vqubits.data(), nullptr); @@ -1412,7 +1584,11 @@ class QuantumCircuit for (auto &p : op.params()) { params.push_back(p.qiskit_param_.get()); } - qk_circuit_parameterized_gate(rust_circuit_.get(), op.gate_map(), qubits.data(), params.data()); + auto result = qk_circuit_parameterized_gate(rust_circuit_.get(), op.gate_map(), qubits.data(), params.data()); + if (result == QkExitCode_Success) { + register_parameters(op.params()); + record_latest_instruction_parameter_symbols(parameter_symbol_groups(op.params())); + } } else qk_circuit_gate(rust_circuit_.get(), op.gate_map(), qubits.data(), nullptr); @@ -1446,7 +1622,11 @@ class QuantumCircuit for (auto &p : inst.instruction().params()) { params.push_back(p.qiskit_param_.get()); } - qk_circuit_parameterized_gate(rust_circuit_.get(), inst.instruction().gate_map(), vqubits.data(), params.data()); + auto result = qk_circuit_parameterized_gate(rust_circuit_.get(), inst.instruction().gate_map(), vqubits.data(), params.data()); + if (result == QkExitCode_Success) { + register_parameters(inst.instruction().params()); + record_latest_instruction_parameter_symbols(parameter_symbol_groups(inst.instruction().params())); + } } else qk_circuit_gate(rust_circuit_.get(), inst.instruction().gate_map(), vqubits.data(), nullptr); @@ -1502,12 +1682,19 @@ class QuantumCircuit clbits[i] = op->clbits[i]; } - std::vector params; - if (op->num_params > 0) { - params.resize(op->num_params); - for (int i = 0; i < op->num_params; i++) - params[i] = Parameter(qk_param_copy(op->params[i])); - } + std::vector params; + if (op->num_params > 0) { + const auto metadata = instruction_parameter_symbols_.find(i); + params.resize(op->num_params); + for (int j = 0; j < op->num_params; j++) { + params[j] = Parameter(qk_param_copy(op->params[j])); + if (metadata != instruction_parameter_symbols_.end() && static_cast(j) < metadata->second.size()) { + params[j].parameter_symbols_ = metadata->second[j]; + } else { + params[j].parameter_symbols_ = infer_parameter_symbols(op->params[j]); + } + } + } std::string name = op->name; qk_circuit_instruction_clear(op); @@ -1541,362 +1728,7 @@ class QuantumCircuit /// @return An OpenQASM3 string. std::string to_qasm3(void) { - add_pending_control_flow_op(); - - std::stringstream qasm3; - qasm3 << std::setprecision(18); - qasm3 << "OPENQASM 3.0;" << std::endl; - qasm3 << "include \"stdgates.inc\";" << std::endl; - - auto name_map = get_standard_gate_name_mapping(); - // add header for non-standard gates - bool cs = false; - bool sxdg = false; - QkOpCounts opcounts = qk_circuit_count_ops(rust_circuit_.get()); - for (int i = 0; i < opcounts.len; i++) { - if (opcounts.data[i].count != 0) { - auto op = name_map[opcounts.data[i].name].gate_map(); - switch (op) - { - case QkGate_R: - qasm3 << "gate r(p0, p1) _gate_q_0 {" << std::endl; - qasm3 << " U(p0, -pi/2 + p1, pi/2 - p1) _gate_q_0;" << std::endl; - qasm3 << "}" << std::endl; - break; - case QkGate_SXdg: - case QkGate_RYY: - case QkGate_XXPlusYY: - case QkGate_XXMinusYY: - if (!sxdg) - { - qasm3 << "gate sxdg _gate_q_0 {" << std::endl; - qasm3 << " s _gate_q_0;" << std::endl; - qasm3 << " h _gate_q_0;" << std::endl; - qasm3 << " s _gate_q_0;" << std::endl; - qasm3 << "}" << std::endl; - sxdg = true; - } - if (op == QkGate_RYY) - { - qasm3 << "gate ryy(p0) _gate_q_0, _gate_q_1 {" << std::endl; - qasm3 << " sxdg _gate_q_0;" << std::endl; - qasm3 << " sxdg _gate_q_1;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " rz(p0) _gate_q_1;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " sx _gate_q_0;" << std::endl; - qasm3 << " sx _gate_q_1;" << std::endl; - qasm3 << "}" << std::endl; - } - if (op == QkGate_XXPlusYY) - { - qasm3 << "gate xx_plus_yy(p0, p1) _gate_q_0, _gate_q_1 {" << std::endl; - qasm3 << " rz(p1) _gate_q_0;" << std::endl; - qasm3 << " sdg _gate_q_1;" << std::endl; - qasm3 << " sx _gate_q_1;" << std::endl; - qasm3 << " s _gate_q_1;" << std::endl; - qasm3 << " s _gate_q_0;" << std::endl; - qasm3 << " cx _gate_q_1, _gate_q_0;" << std::endl; - qasm3 << " ry((-0.5)*p0) _gate_q_1;" << std::endl; - qasm3 << " ry((-0.5)*p0) _gate_q_0;" << std::endl; - qasm3 << " cx _gate_q_1, _gate_q_0;" << std::endl; - qasm3 << " sdg _gate_q_0;" << std::endl; - qasm3 << " sdg _gate_q_1;" << std::endl; - qasm3 << " sxdg _gate_q_1;" << std::endl; - qasm3 << " s _gate_q_1;" << std::endl; - qasm3 << " rz(-p1) _gate_q_0;" << std::endl; - qasm3 << "}" << std::endl; - } - if (op == QkGate_XXMinusYY) - { - qasm3 << "gate xx_minus_yy(p0, p1) _gate_q_0, _gate_q_1 {" << std::endl; - qasm3 << " rz(-p1) _gate_q_1;" << std::endl; - qasm3 << " sdg _gate_q_0;" << std::endl; - qasm3 << " sx _gate_q_0;" << std::endl; - qasm3 << " s _gate_q_0;" << std::endl; - qasm3 << " s _gate_q_1;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " ry(0.5*p0) _gate_q_0;" << std::endl; - qasm3 << " ry((-0.5)*p0) _gate_q_1;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " sdg _gate_q_1;" << std::endl; - qasm3 << " sdg _gate_q_0;" << std::endl; - qasm3 << " sxdg _gate_q_0;" << std::endl; - qasm3 << " s _gate_q_0;" << std::endl; - qasm3 << " rz(p1) _gate_q_1;" << std::endl; - qasm3 << "}" << std::endl; - } - break; - case QkGate_DCX: - qasm3 << "gate dcx _gate_q_0, _gate_q_1 {" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " cx _gate_q_1, _gate_q_0;" << std::endl; - qasm3 << "}" << std::endl; - break; - case QkGate_ECR: - qasm3 << "gate ecr _gate_q_0, _gate_q_1 {" << std::endl; - qasm3 << " s _gate_q_0;" << std::endl; - qasm3 << " sx _gate_q_1;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " x _gate_q_0;" << std::endl; - qasm3 << "}" << std::endl; - break; - case QkGate_ISwap: - qasm3 << "gate iswap _gate_q_0, _gate_q_1 {" << std::endl; - qasm3 << " s _gate_q_0;" << std::endl; - qasm3 << " s _gate_q_1;" << std::endl; - qasm3 << " h _gate_q_0;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " cx _gate_q_1, _gate_q_0;" << std::endl; - qasm3 << " h _gate_q_1;" << std::endl; - qasm3 << "}" << std::endl; - break; - case QkGate_CSX: - case QkGate_CS: - if (!cs) - { - qasm3 << "gate cs _gate_q_0, _gate_q_1 {" << std::endl; - qasm3 << " t _gate_q_0;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " tdg _gate_q_1;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " t _gate_q_1;" << std::endl; - qasm3 << "}" << std::endl; - cs = true; - } - if (op == QkGate_CSX) - { - qasm3 << "gate csx _gate_q_0, _gate_q_1 {" << std::endl; - qasm3 << " h _gate_q_1;" << std::endl; - qasm3 << " cs _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " h _gate_q_1;" << std::endl; - qasm3 << "}" << std::endl; - } - break; - case QkGate_CSdg: - qasm3 << "gate csdg _gate_q_0, _gate_q_1 {" << std::endl; - qasm3 << " tdg _gate_q_0;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " t _gate_q_1;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " tdg _gate_q_1;" << std::endl; - qasm3 << "}" << std::endl; - break; - case QkGate_CCZ: - qasm3 << "gate ccz _gate_q_0, _gate_q_1, _gate_q_2 {" << std::endl; - qasm3 << " h _gate_q_2;" << std::endl; - qasm3 << " ccx _gate_q_0, _gate_q_1, _gate_q_2;" << std::endl; - qasm3 << " h _gate_q_2;" << std::endl; - qasm3 << "}" << std::endl; - break; - case QkGate_RXX: - qasm3 << "gate rxx(p0) _gate_q_0, _gate_q_1 {" << std::endl; - qasm3 << " h _gate_q_0;" << std::endl; - qasm3 << " h _gate_q_1;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " rz(p0) _gate_q_1;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " h _gate_q_1;" << std::endl; - qasm3 << " h _gate_q_0;" << std::endl; - qasm3 << "}" << std::endl; - break; - case QkGate_RZX: - qasm3 << "gate rzx(p0) _gate_q_0, _gate_q_1 {" << std::endl; - qasm3 << " h _gate_q_1;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " rz(p0) _gate_q_1;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " h _gate_q_1;" << std::endl; - qasm3 << "}" << std::endl; - break; - case QkGate_RZZ: - qasm3 << "gate rzz(p0) _gate_q_0, _gate_q_1 {" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " rz(p0) _gate_q_1;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << "}" << std::endl; - break; - case QkGate_RCCX: - qasm3 << "gate rccx _gate_q_0, _gate_q_1, _gate_q_2 {" << std::endl; - qasm3 << " h _gate_q_2;" << std::endl; - qasm3 << " t _gate_q_2;" << std::endl; - qasm3 << " cx _gate_q_1, _gate_q_2;" << std::endl; - qasm3 << " tdg _gate_q_2;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_2;" << std::endl; - qasm3 << " t _gate_q_2;" << std::endl; - qasm3 << " cx _gate_q_1, _gate_q_2;" << std::endl; - qasm3 << " tdg _gate_q_2;" << std::endl; - qasm3 << " h _gate_q_2;" << std::endl; - qasm3 << "}" << std::endl; - break; - case QkGate_C3X: - qasm3 << "gate mcx _gate_q_0, _gate_q_1, _gate_q_2, _gate_q_3 {" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " p(pi/8) _gate_q_0;" << std::endl; - qasm3 << " p(pi/8) _gate_q_1;" << std::endl; - qasm3 << " p(pi/8) _gate_q_2;" << std::endl; - qasm3 << " p(pi/8) _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " p(-pi/8) _gate_q_1;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " cx _gate_q_1, _gate_q_2;" << std::endl; - qasm3 << " p(-pi/8) _gate_q_2;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_2;" << std::endl; - qasm3 << " p(pi/8) _gate_q_2;" << std::endl; - qasm3 << " cx _gate_q_1, _gate_q_2;" << std::endl; - qasm3 << " p(-pi/8) _gate_q_2;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_2;" << std::endl; - qasm3 << " cx _gate_q_2, _gate_q_3;" << std::endl; - qasm3 << " p(-pi/8) _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_1, _gate_q_3;" << std::endl; - qasm3 << " p(pi/8) _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_2, _gate_q_3;" << std::endl; - qasm3 << " p(-pi/8) _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_3;" << std::endl; - qasm3 << " p(pi/8) _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_2, _gate_q_3;" << std::endl; - qasm3 << " p(-pi/8) _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_1, _gate_q_3;" << std::endl; - qasm3 << " p(pi/8) _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_2, _gate_q_3;" << std::endl; - qasm3 << " p(-pi/8) _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_3;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << "}" << std::endl; - break; - case QkGate_C3SX: - qasm3 << "gate c3sx _gate_q_0, _gate_q_1, _gate_q_2, _gate_q_3 {" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " cp(pi/8) _gate_q_0, _gate_q_3;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " cp(-pi/8) _gate_q_1, _gate_q_3;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " cp(pi/8) _gate_q_1, _gate_q_3;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_1, _gate_q_2;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " cp(-pi/8) _gate_q_2, _gate_q_3;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_2;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " cp(pi/8) _gate_q_2, _gate_q_3;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_1, _gate_q_2;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " cp(-pi/8) _gate_q_2, _gate_q_3;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_2;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " cp(pi/8) _gate_q_2, _gate_q_3;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << "}" << std::endl; - break; - case QkGate_RC3X: - qasm3 << "gate rcccx _gate_q_0, _gate_q_1, _gate_q_2, _gate_q_3 {" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " t _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_2, _gate_q_3;" << std::endl; - qasm3 << " tdg _gate_q_3;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_3;" << std::endl; - qasm3 << " t _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_1, _gate_q_3;" << std::endl; - qasm3 << " tdg _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_0, _gate_q_3;" << std::endl; - qasm3 << " t _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_1, _gate_q_3;" << std::endl; - qasm3 << " tdg _gate_q_3;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << " t _gate_q_3;" << std::endl; - qasm3 << " cx _gate_q_2, _gate_q_3;" << std::endl; - qasm3 << " tdg _gate_q_3;" << std::endl; - qasm3 << " h _gate_q_3;" << std::endl; - qasm3 << "}" << std::endl; - break; - case QkGate_CU1: - qasm3 << "gate cu1(p0) _gate_q_0, _gate_q_1 {" << std::endl; - qasm3 << " cp(p0) _gate_q_0 _gate_q_1;" << std::endl; - qasm3 << "}" << std::endl; - break; - case QkGate_CU3: - qasm3 << "gate cu3(p0, p1, p2) _gate_q_0, _gate_q_1 {" << std::endl; - qasm3 << " cu(p0, p1, p2, 0) _gate_q_0 _gate_q_1;" << std::endl; - qasm3 << "}" << std::endl; - break; - default: - break; - } - } - } - qk_opcounts_clear(&opcounts); - - // save ops - uint_t nops; - nops = qk_circuit_num_instructions(rust_circuit_.get()); - - // Declare registers - // After transpilation, qubit registers will be mapped to physical registers, - // so we need to combined them in a single quantum register "q"; - const std::string qreg_name = "q"; - qasm3 << "qubit[" << num_qubits() << "] " << qreg_name << ";" << std::endl; - for(const auto& creg : cregs_) { - qasm3 << "bit[" << creg.size() << "] " << creg.name() << ";" << std::endl; - } - - auto recover_reg_data = [this](uint_t index) -> std::pair - { - auto it = std::upper_bound(cregs_.begin(), cregs_.end(), index, - [](uint_t v, const ClassicalRegister& reg) { return v < reg.base_index(); }); - assert(it != cregs_.begin()); - it = std::prev(it); - return std::make_pair(it->name(), index - it->base_index()); - }; - - for (uint_t i = 0; i < nops; i++) { - QkCircuitInstruction *op = new QkCircuitInstruction; - qk_circuit_get_instruction(rust_circuit_.get(), i, op); - if (op->num_clbits > 0) { - if (op->num_qubits == op->num_clbits) { - for (uint_t j = 0; j < op->num_qubits; j++) { - const auto creg_data = recover_reg_data(op->clbits[j]); - qasm3 << creg_data.first << "[" << creg_data.second << "] = " << op->name << " " << qreg_name << "[" << op->qubits[j] << "];" << std::endl; - } - } - } else { - if (strcmp(op->name, "u") == 0) { - qasm3 << "U"; - } else { - qasm3 << op->name; - } - if (op->num_params > 0) { - qasm3 << "("; - for (uint_t j = 0; j < op->num_params; j++) { - char* param = qk_param_str(op->params[j]); - qasm3 << param; - qk_str_free(param); - if (j != op->num_params - 1) - qasm3 << ", "; - } - qasm3 << ")"; - } - if (op->num_qubits > 0) { - qasm3 << " "; - for (uint_t j = 0; j < op->num_qubits; j++) { - qasm3 << qreg_name << "[" << op->qubits[j] << "]"; - if (j != op->num_qubits - 1) - qasm3 << ", "; - } - } - qasm3 << ";" << std::endl; - } - qk_circuit_instruction_clear(op); - } - - return qasm3.str(); + return Qiskit::qasm3::dumps(*this); } /// @brief print circuit (this is for debug) diff --git a/src/qasm3/qasm3_exporter.hpp b/src/qasm3/qasm3_exporter.hpp new file mode 100644 index 0000000..02c9786 --- /dev/null +++ b/src/qasm3/qasm3_exporter.hpp @@ -0,0 +1,555 @@ +/* +# 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. +*/ + +// OpenQASM 3 exporter + +#ifndef __qiskitcpp_qasm3_qasm3_exporter_hpp__ +#define __qiskitcpp_qasm3_qasm3_exporter_hpp__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "circuit/quantumcircuit_def.hpp" + +namespace Qiskit +{ +namespace qasm3 +{ + +class Qasm3Exporter +{ +private: + class OpCounts + { + private: + QkOpCounts counts_; + + public: + explicit OpCounts(QkCircuit *circuit) : counts_(qk_circuit_count_ops(circuit)) {} + + ~OpCounts() + { + qk_opcounts_clear(&counts_); + } + + OpCounts(const OpCounts &) = delete; + OpCounts &operator=(const OpCounts &) = delete; + + const QkOpCounts &get(void) const + { + return counts_; + } + }; + + class CircuitInstruction + { + private: + QkCircuitInstruction instruction_; + + public: + CircuitInstruction(QkCircuit *circuit, uint_t index) + { + qk_circuit_get_instruction(circuit, index, &instruction_); + } + + ~CircuitInstruction() + { + qk_circuit_instruction_clear(&instruction_); + } + + CircuitInstruction(const CircuitInstruction &) = delete; + CircuitInstruction &operator=(const CircuitInstruction &) = delete; + + QkCircuitInstruction *get(void) + { + return &instruction_; + } + }; + + class ParamString + { + private: + char *value_; + + public: + explicit ParamString(QkParam *param) : value_(qk_param_str(param)) {} + + ~ParamString() + { + qk_str_free(value_); + } + + ParamString(const ParamString &) = delete; + ParamString &operator=(const ParamString &) = delete; + + const char *c_str(void) const + { + return value_; + } + }; + + class ParameterSymbolName + { + private: + char *value_; + + public: + ParameterSymbolName(QkCircuit *circuit, std::size_t index) : value_(qk_circuit_param_symbol_name(circuit, index)) {} + + ~ParameterSymbolName() + { + qk_str_free(value_); + } + + ParameterSymbolName(const ParameterSymbolName &) = delete; + ParameterSymbolName &operator=(const ParameterSymbolName &) = delete; + + const char *c_str(void) const + { + return value_; + } + }; + + std::vector default_parameter_names(circuit::QuantumCircuit &circuit) const + { + const auto num_symbols = qk_circuit_num_param_symbols(circuit.rust_circuit_.get()); + std::vector names; + names.reserve(num_symbols); + for (std::size_t i = 0; i < num_symbols; i++) { + ParameterSymbolName name(circuit.rust_circuit_.get(), i); + if (name.c_str() == nullptr) { + throw std::runtime_error("OpenQASM 3 export failed to read circuit parameter name"); + } + names.emplace_back(name.c_str()); + } + return names; + } + + void validate_parameter_names(circuit::QuantumCircuit &circuit, const std::vector ¶meter_names) const + { + const auto num_symbols = qk_circuit_num_param_symbols(circuit.rust_circuit_.get()); + if (parameter_names.size() != static_cast(num_symbols)) { + throw std::invalid_argument("OpenQASM 3 parameter-name count does not match circuit free-parameter count"); + } + + std::set seen; + for (const auto &name : parameter_names) { + if (name.empty()) { + throw std::invalid_argument("OpenQASM 3 parameter names cannot be empty"); + } + if (!seen.insert(name).second) { + throw std::invalid_argument("OpenQASM 3 parameter names cannot contain duplicates"); + } + } + } + + void emit_custom_gate_definitions(std::stringstream &qasm3, circuit::QuantumCircuit &circuit) const + { + auto name_map = circuit::get_standard_gate_name_mapping(); + bool cs = false; + bool sxdg = false; + OpCounts opcounts(circuit.rust_circuit_.get()); + for (int i = 0; i < opcounts.get().len; i++) { + if (opcounts.get().data[i].count != 0) { + auto op = name_map[opcounts.get().data[i].name].gate_map(); + switch (op) + { + case QkGate_R: + qasm3 << "gate r(p0, p1) _gate_q_0 {" << std::endl; + qasm3 << " U(p0, -pi/2 + p1, pi/2 - p1) _gate_q_0;" << std::endl; + qasm3 << "}" << std::endl; + break; + case QkGate_SXdg: + case QkGate_RYY: + case QkGate_XXPlusYY: + case QkGate_XXMinusYY: + if (!sxdg) + { + qasm3 << "gate sxdg _gate_q_0 {" << std::endl; + qasm3 << " s _gate_q_0;" << std::endl; + qasm3 << " h _gate_q_0;" << std::endl; + qasm3 << " s _gate_q_0;" << std::endl; + qasm3 << "}" << std::endl; + sxdg = true; + } + if (op == QkGate_RYY) + { + qasm3 << "gate ryy(p0) _gate_q_0, _gate_q_1 {" << std::endl; + qasm3 << " sxdg _gate_q_0;" << std::endl; + qasm3 << " sxdg _gate_q_1;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " rz(p0) _gate_q_1;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " sx _gate_q_0;" << std::endl; + qasm3 << " sx _gate_q_1;" << std::endl; + qasm3 << "}" << std::endl; + } + if (op == QkGate_XXPlusYY) + { + qasm3 << "gate xx_plus_yy(p0, p1) _gate_q_0, _gate_q_1 {" << std::endl; + qasm3 << " rz(p1) _gate_q_0;" << std::endl; + qasm3 << " sdg _gate_q_1;" << std::endl; + qasm3 << " sx _gate_q_1;" << std::endl; + qasm3 << " s _gate_q_1;" << std::endl; + qasm3 << " s _gate_q_0;" << std::endl; + qasm3 << " cx _gate_q_1, _gate_q_0;" << std::endl; + qasm3 << " ry((-0.5)*p0) _gate_q_1;" << std::endl; + qasm3 << " ry((-0.5)*p0) _gate_q_0;" << std::endl; + qasm3 << " cx _gate_q_1, _gate_q_0;" << std::endl; + qasm3 << " sdg _gate_q_0;" << std::endl; + qasm3 << " sdg _gate_q_1;" << std::endl; + qasm3 << " sxdg _gate_q_1;" << std::endl; + qasm3 << " s _gate_q_1;" << std::endl; + qasm3 << " rz(-p1) _gate_q_0;" << std::endl; + qasm3 << "}" << std::endl; + } + if (op == QkGate_XXMinusYY) + { + qasm3 << "gate xx_minus_yy(p0, p1) _gate_q_0, _gate_q_1 {" << std::endl; + qasm3 << " rz(-p1) _gate_q_1;" << std::endl; + qasm3 << " sdg _gate_q_0;" << std::endl; + qasm3 << " sx _gate_q_0;" << std::endl; + qasm3 << " s _gate_q_0;" << std::endl; + qasm3 << " s _gate_q_1;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " ry(0.5*p0) _gate_q_0;" << std::endl; + qasm3 << " ry((-0.5)*p0) _gate_q_1;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " sdg _gate_q_1;" << std::endl; + qasm3 << " sdg _gate_q_0;" << std::endl; + qasm3 << " sxdg _gate_q_0;" << std::endl; + qasm3 << " s _gate_q_0;" << std::endl; + qasm3 << " rz(p1) _gate_q_1;" << std::endl; + qasm3 << "}" << std::endl; + } + break; + case QkGate_DCX: + qasm3 << "gate dcx _gate_q_0, _gate_q_1 {" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " cx _gate_q_1, _gate_q_0;" << std::endl; + qasm3 << "}" << std::endl; + break; + case QkGate_ECR: + qasm3 << "gate ecr _gate_q_0, _gate_q_1 {" << std::endl; + qasm3 << " s _gate_q_0;" << std::endl; + qasm3 << " sx _gate_q_1;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " x _gate_q_0;" << std::endl; + qasm3 << "}" << std::endl; + break; + case QkGate_ISwap: + qasm3 << "gate iswap _gate_q_0, _gate_q_1 {" << std::endl; + qasm3 << " s _gate_q_0;" << std::endl; + qasm3 << " s _gate_q_1;" << std::endl; + qasm3 << " h _gate_q_0;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " cx _gate_q_1, _gate_q_0;" << std::endl; + qasm3 << " h _gate_q_1;" << std::endl; + qasm3 << "}" << std::endl; + break; + case QkGate_CSX: + case QkGate_CS: + if (!cs) + { + qasm3 << "gate cs _gate_q_0, _gate_q_1 {" << std::endl; + qasm3 << " t _gate_q_0;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " tdg _gate_q_1;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " t _gate_q_1;" << std::endl; + qasm3 << "}" << std::endl; + cs = true; + } + if (op == QkGate_CSX) + { + qasm3 << "gate csx _gate_q_0, _gate_q_1 {" << std::endl; + qasm3 << " h _gate_q_1;" << std::endl; + qasm3 << " cs _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " h _gate_q_1;" << std::endl; + qasm3 << "}" << std::endl; + } + break; + case QkGate_CSdg: + qasm3 << "gate csdg _gate_q_0, _gate_q_1 {" << std::endl; + qasm3 << " tdg _gate_q_0;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " t _gate_q_1;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " tdg _gate_q_1;" << std::endl; + qasm3 << "}" << std::endl; + break; + case QkGate_CCZ: + qasm3 << "gate ccz _gate_q_0, _gate_q_1, _gate_q_2 {" << std::endl; + qasm3 << " h _gate_q_2;" << std::endl; + qasm3 << " ccx _gate_q_0, _gate_q_1, _gate_q_2;" << std::endl; + qasm3 << " h _gate_q_2;" << std::endl; + qasm3 << "}" << std::endl; + break; + case QkGate_RXX: + qasm3 << "gate rxx(p0) _gate_q_0, _gate_q_1 {" << std::endl; + qasm3 << " h _gate_q_0;" << std::endl; + qasm3 << " h _gate_q_1;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " rz(p0) _gate_q_1;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " h _gate_q_1;" << std::endl; + qasm3 << " h _gate_q_0;" << std::endl; + qasm3 << "}" << std::endl; + break; + case QkGate_RZX: + qasm3 << "gate rzx(p0) _gate_q_0, _gate_q_1 {" << std::endl; + qasm3 << " h _gate_q_1;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " rz(p0) _gate_q_1;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " h _gate_q_1;" << std::endl; + qasm3 << "}" << std::endl; + break; + case QkGate_RZZ: + qasm3 << "gate rzz(p0) _gate_q_0, _gate_q_1 {" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " rz(p0) _gate_q_1;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << "}" << std::endl; + break; + case QkGate_RCCX: + qasm3 << "gate rccx _gate_q_0, _gate_q_1, _gate_q_2 {" << std::endl; + qasm3 << " h _gate_q_2;" << std::endl; + qasm3 << " t _gate_q_2;" << std::endl; + qasm3 << " cx _gate_q_1, _gate_q_2;" << std::endl; + qasm3 << " tdg _gate_q_2;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_2;" << std::endl; + qasm3 << " t _gate_q_2;" << std::endl; + qasm3 << " cx _gate_q_1, _gate_q_2;" << std::endl; + qasm3 << " tdg _gate_q_2;" << std::endl; + qasm3 << " h _gate_q_2;" << std::endl; + qasm3 << "}" << std::endl; + break; + case QkGate_C3X: + qasm3 << "gate mcx _gate_q_0, _gate_q_1, _gate_q_2, _gate_q_3 {" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " p(pi/8) _gate_q_0;" << std::endl; + qasm3 << " p(pi/8) _gate_q_1;" << std::endl; + qasm3 << " p(pi/8) _gate_q_2;" << std::endl; + qasm3 << " p(pi/8) _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " p(-pi/8) _gate_q_1;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " cx _gate_q_1, _gate_q_2;" << std::endl; + qasm3 << " p(-pi/8) _gate_q_2;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_2;" << std::endl; + qasm3 << " p(pi/8) _gate_q_2;" << std::endl; + qasm3 << " cx _gate_q_1, _gate_q_2;" << std::endl; + qasm3 << " p(-pi/8) _gate_q_2;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_2;" << std::endl; + qasm3 << " cx _gate_q_2, _gate_q_3;" << std::endl; + qasm3 << " p(-pi/8) _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_1, _gate_q_3;" << std::endl; + qasm3 << " p(pi/8) _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_2, _gate_q_3;" << std::endl; + qasm3 << " p(-pi/8) _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_3;" << std::endl; + qasm3 << " p(pi/8) _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_2, _gate_q_3;" << std::endl; + qasm3 << " p(-pi/8) _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_1, _gate_q_3;" << std::endl; + qasm3 << " p(pi/8) _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_2, _gate_q_3;" << std::endl; + qasm3 << " p(-pi/8) _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_3;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << "}" << std::endl; + break; + case QkGate_C3SX: + qasm3 << "gate c3sx _gate_q_0, _gate_q_1, _gate_q_2, _gate_q_3 {" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " cp(pi/8) _gate_q_0, _gate_q_3;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " cp(-pi/8) _gate_q_1, _gate_q_3;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_1;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " cp(pi/8) _gate_q_1, _gate_q_3;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_1, _gate_q_2;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " cp(-pi/8) _gate_q_2, _gate_q_3;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_2;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " cp(pi/8) _gate_q_2, _gate_q_3;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_1, _gate_q_2;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " cp(-pi/8) _gate_q_2, _gate_q_3;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_2;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " cp(pi/8) _gate_q_2, _gate_q_3;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << "}" << std::endl; + break; + case QkGate_RC3X: + qasm3 << "gate rcccx _gate_q_0, _gate_q_1, _gate_q_2, _gate_q_3 {" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " t _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_2, _gate_q_3;" << std::endl; + qasm3 << " tdg _gate_q_3;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_3;" << std::endl; + qasm3 << " t _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_1, _gate_q_3;" << std::endl; + qasm3 << " tdg _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_0, _gate_q_3;" << std::endl; + qasm3 << " t _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_1, _gate_q_3;" << std::endl; + qasm3 << " tdg _gate_q_3;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << " t _gate_q_3;" << std::endl; + qasm3 << " cx _gate_q_2, _gate_q_3;" << std::endl; + qasm3 << " tdg _gate_q_3;" << std::endl; + qasm3 << " h _gate_q_3;" << std::endl; + qasm3 << "}" << std::endl; + break; + case QkGate_CU1: + qasm3 << "gate cu1(p0) _gate_q_0, _gate_q_1 {" << std::endl; + qasm3 << " cp(p0) _gate_q_0 _gate_q_1;" << std::endl; + qasm3 << "}" << std::endl; + break; + case QkGate_CU3: + qasm3 << "gate cu3(p0, p1, p2) _gate_q_0, _gate_q_1 {" << std::endl; + qasm3 << " cu(p0, p1, p2, 0) _gate_q_0 _gate_q_1;" << std::endl; + qasm3 << "}" << std::endl; + break; + default: + break; + } + } + } + } + + void emit_parameter_declarations(std::stringstream &qasm3, const std::vector ¶meter_names) const + { + for (const auto &symbol : parameter_names) { + qasm3 << "input float[64] " << symbol << ";" << std::endl; + } + } + + void emit_registers_and_operations(std::stringstream &qasm3, circuit::QuantumCircuit &circuit) const + { + const uint_t nops = qk_circuit_num_instructions(circuit.rust_circuit_.get()); + + const std::string qreg_name = "q"; + qasm3 << "qubit[" << circuit.num_qubits() << "] " << qreg_name << ";" << std::endl; + for (const auto &creg : circuit.cregs_) { + qasm3 << "bit[" << creg.size() << "] " << creg.name() << ";" << std::endl; + } + + auto recover_reg_data = [&circuit](uint_t index) -> std::pair + { + auto it = std::upper_bound(circuit.cregs_.begin(), circuit.cregs_.end(), index, + [](uint_t v, const circuit::ClassicalRegister ®) { return v < reg.base_index(); }); + assert(it != circuit.cregs_.begin()); + it = std::prev(it); + return std::make_pair(it->name(), index - it->base_index()); + }; + + for (uint_t i = 0; i < nops; i++) { + CircuitInstruction instruction(circuit.rust_circuit_.get(), i); + auto op = instruction.get(); + if (op->num_clbits > 0) { + if (op->num_qubits == op->num_clbits) { + for (uint_t j = 0; j < op->num_qubits; j++) { + const auto creg_data = recover_reg_data(op->clbits[j]); + qasm3 << creg_data.first << "[" << creg_data.second << "] = " << op->name << " " << qreg_name << "[" << op->qubits[j] << "];" << std::endl; + } + } + } else { + if (std::strcmp(op->name, "u") == 0) { + qasm3 << "U"; + } else { + qasm3 << op->name; + } + if (op->num_params > 0) { + qasm3 << "("; + for (uint_t j = 0; j < op->num_params; j++) { + ParamString param(op->params[j]); + qasm3 << param.c_str(); + if (j != op->num_params - 1) + qasm3 << ", "; + } + qasm3 << ")"; + } + if (op->num_qubits > 0) { + qasm3 << " "; + for (uint_t j = 0; j < op->num_qubits; j++) { + qasm3 << qreg_name << "[" << op->qubits[j] << "]"; + if (j != op->num_qubits - 1) + qasm3 << ", "; + } + } + qasm3 << ";" << std::endl; + } + } + } + +public: + std::string dumps(circuit::QuantumCircuit &circuit) const + { + circuit.add_pending_control_flow_op(); + return dumps(circuit, default_parameter_names(circuit)); + } + + std::string dumps(circuit::QuantumCircuit &circuit, const std::vector ¶meter_names) const + { + circuit.add_pending_control_flow_op(); + validate_parameter_names(circuit, parameter_names); + + std::stringstream qasm3; + qasm3 << std::setprecision(18); + qasm3 << "OPENQASM 3.0;" << std::endl; + qasm3 << "include \"stdgates.inc\";" << std::endl; + + emit_custom_gate_definitions(qasm3, circuit); + emit_parameter_declarations(qasm3, parameter_names); + emit_registers_and_operations(qasm3, circuit); + + return qasm3.str(); + } +}; + +inline std::string dumps(circuit::QuantumCircuit &circuit) +{ + return Qasm3Exporter().dumps(circuit); +} + +inline std::string dumps(circuit::QuantumCircuit &circuit, const std::vector ¶meter_names) +{ + return Qasm3Exporter().dumps(circuit, parameter_names); +} + +} // namespace qasm3 +} // namespace Qiskit + +#endif // __qiskitcpp_qasm3_qasm3_exporter_hpp__ diff --git a/test/test_circuit.cpp b/test/test_circuit.cpp index eae4cd8..b744aa6 100644 --- a/test/test_circuit.cpp +++ b/test/test_circuit.cpp @@ -12,10 +12,13 @@ #include #include +#include #include "common.hpp" #include "circuit/quantumcircuit.hpp" +#include "circuit/parameter.hpp" +#include "qasm3/qasm3_exporter.hpp" using namespace Qiskit; using namespace Qiskit::circuit; @@ -612,6 +615,64 @@ static int test_compose(void) { return Ok; } +static int test_compose_parameterized_circuit(void) { + auto qreg = QuantumRegister(1, std::string("qr")); + auto creg = ClassicalRegister(1, std::string("c")); + QuantumCircuit circ(qreg, creg); + QuantumCircuit sub(qreg, creg); + + Parameter theta("theta"); + sub.rx(theta, 0); + + circ.compose(sub, reg_t({0}), reg_t({0})); + + const auto actual = circ.to_qasm3(); + const std::string expected = + "OPENQASM 3.0;\n" + "include \"stdgates.inc\";\n" + "input float[64] theta;\n" + "qubit[1] q;\n" + "bit[1] c;\n" + "rx(theta) q[0];\n"; + if (actual != expected) { + std::cerr << " compose_parameterized_circuit test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + +static int test_operator_index_parameter_metadata(void) { + auto qreg = QuantumRegister(1, std::string("qr")); + auto creg = ClassicalRegister(1, std::string("c")); + QuantumCircuit src(qreg, creg); + QuantumCircuit dst(qreg, creg); + + Parameter theta("theta"); + Parameter phi("phi"); + src.rx(theta, 0); + src.ry(phi, 0); + + dst.append(src[0]); + + const auto actual = dst.to_qasm3(); + const std::string expected = + "OPENQASM 3.0;\n" + "include \"stdgates.inc\";\n" + "input float[64] theta;\n" + "qubit[1] q;\n" + "bit[1] c;\n" + "rx(theta) q[0];\n"; + if (actual != expected) { + std::cerr << " operator_index_parameter_metadata test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + static int test_to_qasm3_multi_regs(void) { auto qreg1 = QuantumRegister(2, std::string("q1")); auto qreg2 = QuantumRegister(1, std::string("q2")); @@ -641,6 +702,210 @@ static int test_to_qasm3_multi_regs(void) { return Ok; } +static int test_to_qasm3_parameterized_circuit(void) { + auto qreg = QuantumRegister(1, std::string("qr")); + auto creg = ClassicalRegister(1, std::string("c")); + QuantumCircuit circ(qreg, creg); + + Parameter theta("theta"); + circ.rx(theta, 0); + + const auto actual = circ.to_qasm3(); + const std::string expected = + "OPENQASM 3.0;\n" + "include \"stdgates.inc\";\n" + "input float[64] theta;\n" + "qubit[1] q;\n" + "bit[1] c;\n" + "rx(theta) q[0];\n"; + if (actual != expected) { + std::cerr << " to_qasm3_parameterized_circuit test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + +static int test_to_qasm3_parameter_expression(void) { + auto qreg = QuantumRegister(1, std::string("qr")); + auto creg = ClassicalRegister(1, std::string("c")); + QuantumCircuit circ(qreg, creg); + + Parameter theta("theta"); + Parameter phi("phi"); + circ.rz(theta + phi, 0); + + const auto actual = circ.to_qasm3(); + const std::string expected = + "OPENQASM 3.0;\n" + "include \"stdgates.inc\";\n" + "input float[64] phi;\n" + "input float[64] theta;\n" + "qubit[1] q;\n" + "bit[1] c;\n" + "rz(phi + theta) q[0];\n"; + if (actual != expected) { + std::cerr << " to_qasm3_parameter_expression test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + +static int test_qasm3_dumps_multiple_parameters(void) { + auto qreg = QuantumRegister(1, std::string("qr")); + auto creg = ClassicalRegister(1, std::string("c")); + QuantumCircuit circ(qreg, creg); + + Parameter phi("phi"); + Parameter theta("theta"); + circ.rx(phi, 0); + circ.ry(theta, 0); + + const auto actual = qasm3::dumps(circ); + const std::string expected = + "OPENQASM 3.0;\n" + "include \"stdgates.inc\";\n" + "input float[64] phi;\n" + "input float[64] theta;\n" + "qubit[1] q;\n" + "bit[1] c;\n" + "rx(phi) q[0];\n" + "ry(theta) q[0];\n"; + if (actual != expected) { + std::cerr << " qasm3_dumps_multiple_parameters test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + +static int test_qasm3_dumps_numeric_parameter(void) { + auto qreg = QuantumRegister(1, std::string("qr")); + auto creg = ClassicalRegister(1, std::string("c")); + QuantumCircuit circ(qreg, creg); + + circ.rx(0.25, 0); + + const auto actual = qasm3::dumps(circ); + const std::string expected = + "OPENQASM 3.0;\n" + "include \"stdgates.inc\";\n" + "qubit[1] q;\n" + "bit[1] c;\n" + "rx(0.25) q[0];\n"; + if (actual != expected) { + std::cerr << " qasm3_dumps_numeric_parameter test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + +static int test_qasm3_dumps_parameterized_u(void) { + auto qreg = QuantumRegister(1, std::string("qr")); + auto creg = ClassicalRegister(1, std::string("c")); + QuantumCircuit circ(qreg, creg); + + Parameter theta("theta"); + Parameter phi("phi"); + Parameter lam("lam"); + circ.u(theta, phi, lam, 0); + + const auto actual = qasm3::dumps(circ); + const std::string expected = + "OPENQASM 3.0;\n" + "include \"stdgates.inc\";\n" + "input float[64] lam;\n" + "input float[64] phi;\n" + "input float[64] theta;\n" + "qubit[1] q;\n" + "bit[1] c;\n" + "U(theta, phi, lam) q[0];\n"; + if (actual != expected) { + std::cerr << " qasm3_dumps_parameterized_u test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + +static int test_qasm3_dumps_explicit_parameter_names(void) { + auto qreg = QuantumRegister(1, std::string("qr")); + auto creg = ClassicalRegister(1, std::string("c")); + QuantumCircuit circ(qreg, creg); + + Parameter theta("theta"); + circ.rx(theta, 0); + + const auto actual = qasm3::dumps(circ, {"theta"}); + const std::string expected = + "OPENQASM 3.0;\n" + "include \"stdgates.inc\";\n" + "input float[64] theta;\n" + "qubit[1] q;\n" + "bit[1] c;\n" + "rx(theta) q[0];\n"; + if (actual != expected) { + std::cerr << " qasm3_dumps_explicit_parameter_names test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + +static int test_qasm3_dumps_invalid_parameter_names(void) { + QuantumCircuit one_param(1, 1); + Parameter theta("theta"); + one_param.rx(theta, 0); + + bool threw = false; + try { + qasm3::dumps(one_param, {}); + } catch (const std::invalid_argument &) { + threw = true; + } + if (!threw) { + std::cerr << " qasm3_dumps_invalid_parameter_names test : wrong count did not throw" << std::endl; + return RuntimeError; + } + + threw = false; + try { + qasm3::dumps(one_param, {""}); + } catch (const std::invalid_argument &) { + threw = true; + } + if (!threw) { + std::cerr << " qasm3_dumps_invalid_parameter_names test : empty name did not throw" << std::endl; + return RuntimeError; + } + + QuantumCircuit two_params(1, 1); + Parameter phi("phi"); + two_params.rx(theta, 0); + two_params.ry(phi, 0); + + threw = false; + try { + qasm3::dumps(two_params, {"theta", "theta"}); + } catch (const std::invalid_argument &) { + threw = true; + } + if (!threw) { + std::cerr << " qasm3_dumps_invalid_parameter_names test : duplicate names did not throw" << std::endl; + return RuntimeError; + } + + return Ok; +} + #if defined(_WIN32) int test_circuit(int argc, char** const argv) { #else @@ -651,7 +916,16 @@ int test_circuit(int argc, char** argv) { num_failed += RUN_TEST(test_measure); num_failed += RUN_TEST(test_append); num_failed += RUN_TEST(test_compose); + num_failed += RUN_TEST(test_compose_parameterized_circuit); + num_failed += RUN_TEST(test_operator_index_parameter_metadata); num_failed += RUN_TEST(test_to_qasm3_multi_regs); + num_failed += RUN_TEST(test_to_qasm3_parameterized_circuit); + num_failed += RUN_TEST(test_to_qasm3_parameter_expression); + num_failed += RUN_TEST(test_qasm3_dumps_multiple_parameters); + num_failed += RUN_TEST(test_qasm3_dumps_numeric_parameter); + num_failed += RUN_TEST(test_qasm3_dumps_parameterized_u); + num_failed += RUN_TEST(test_qasm3_dumps_explicit_parameter_names); + num_failed += RUN_TEST(test_qasm3_dumps_invalid_parameter_names); std::cerr << "=== Number of failed subtests: " << num_failed << std::endl; return num_failed;