diff --git a/releasenotes/notes/qasm3-parameterized-export-146-6f6f1d31c9e74d2b.yaml b/releasenotes/notes/qasm3-parameterized-export-146-6f6f1d31c9e74d2b.yaml new file mode 100644 index 0000000..a1400a0 --- /dev/null +++ b/releasenotes/notes/qasm3-parameterized-export-146-6f6f1d31c9e74d2b.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + `QuantumCircuit::to_qasm3()` now uses a dedicated OpenQASM 3 exporter and + declares symbolic circuit parameters as OpenQASM 3 `input float[64]` + values before emitting parameterized gate operations. Generated input and + register names are adjusted when needed to avoid OpenQASM 3 global symbol + conflicts, including conflicts with custom gate definitions emitted by the + exporter. The exporter also emits valid OpenQASM 3 for controlled-U1, + controlled-U3, and global phase operations. diff --git a/src/circuit/qasm3_exporter.hpp b/src/circuit/qasm3_exporter.hpp new file mode 100644 index 0000000..bd501d5 --- /dev/null +++ b/src/circuit/qasm3_exporter.hpp @@ -0,0 +1,667 @@ +/* +# 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_circuit_qasm3_exporter_hpp__ +#define __qiskitcpp_circuit_qasm3_exporter_hpp__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "circuit/quantumcircuit_def.hpp" + +namespace Qiskit { +namespace circuit { + +class Qasm3Exporter { +protected: + QuantumCircuit &circuit_; + + static bool is_identifier_start(const unsigned char ch) { + return ch == '_' || std::isalpha(ch) || ch >= 0x80; + } + + static bool is_identifier_char(const unsigned char ch) { + return is_identifier_start(ch) || std::isdigit(ch); + } + + static bool is_qasm3_function_identifier(const std::string &token) { + return token == "sin" || token == "cos" || token == "tan" || + token == "asin" || token == "acos" || token == "atan" || + token == "log" || token == "exp" || token == "abs" || + token == "sign" || token == "conj" || token == "sqrt" || + token == "pow"; + } + + static bool is_function_call(const std::string &expression, + std::size_t token_end) { + while (token_end < expression.size() && + std::isspace(static_cast(expression[token_end]))) { + ++token_end; + } + return token_end < expression.size() && expression[token_end] == '('; + } + + static std::set reserved_global_identifiers(void) { + return std::set{ + "OPENQASM", "include", "defcalgrammar", "def", "cal", "defcal", + "gate", "extern", "box", "let", "break", "continue", "if", "else", + "end", "return", "for", "while", "in", "switch", "case", "default", + "input", "output", "const", "readonly", "mutable", "qreg", "qubit", + "creg", "bool", "bit", "int", "uint", "float", "angle", "complex", + "array", "void", "duration", "stretch", "gphase", "inv", "pow", + "ctrl", "negctrl", "durationof", "delay", "reset", "measure", + "barrier", "sizeof", "true", "false", "pi", "tau", "euler", "im", + "I", "h", "x", "y", "z", "s", "sdg", "sx", "sxdg", "t", "tdg", + "rx", "ry", "rz", "p", "u", "U", "cx", "cy", "cz", "ch", "swap", + "ccx", "cs", "csdg", "csx", "cp", "crx", "cry", "crz", "cswap", + "cu", "CX", "phase", "cphase", "id", "u1", "u2", "u3"}; + } + + static std::string unique_identifier(const std::string &base, + std::set &reserved) { + std::string candidate = base; + for (uint_t i = 0; reserved.count(candidate) != 0; i++) { + candidate = base + "_" + std::to_string(i); + } + reserved.insert(candidate); + return candidate; + } + + static void reserve_emitted_gate_definition_names( + QkGate op, std::set &reserved) { + switch (op) { + case QkGate_R: + reserved.insert("r"); + break; + case QkGate_SXdg: + reserved.insert("sxdg"); + break; + case QkGate_RYY: + reserved.insert("sxdg"); + reserved.insert("ryy"); + break; + case QkGate_XXPlusYY: + reserved.insert("sxdg"); + reserved.insert("xx_plus_yy"); + break; + case QkGate_XXMinusYY: + reserved.insert("sxdg"); + reserved.insert("xx_minus_yy"); + break; + case QkGate_DCX: + reserved.insert("dcx"); + break; + case QkGate_ECR: + reserved.insert("ecr"); + break; + case QkGate_ISwap: + reserved.insert("iswap"); + break; + case QkGate_CSX: + reserved.insert("cs"); + reserved.insert("csx"); + break; + case QkGate_CS: + reserved.insert("cs"); + break; + case QkGate_CSdg: + reserved.insert("csdg"); + break; + case QkGate_CCZ: + reserved.insert("ccz"); + break; + case QkGate_RXX: + reserved.insert("rxx"); + break; + case QkGate_RZX: + reserved.insert("rzx"); + break; + case QkGate_RZZ: + reserved.insert("rzz"); + break; + case QkGate_RCCX: + reserved.insert("rccx"); + break; + case QkGate_C3X: + reserved.insert("mcx"); + break; + case QkGate_C3SX: + reserved.insert("c3sx"); + break; + case QkGate_RC3X: + reserved.insert("rcccx"); + break; + case QkGate_CU1: + reserved.insert("cu1"); + break; + case QkGate_CU3: + reserved.insert("cu3"); + break; + default: + break; + } + } + + void harvest_parameter_identifiers(const std::string &expression, + std::set &inputs) const { + for (std::size_t i = 0; i < expression.size();) { + const unsigned char ch = static_cast(expression[i]); + if (!is_identifier_start(ch)) { + ++i; + continue; + } + + const std::size_t start = i++; + while (i < expression.size() && + is_identifier_char(static_cast(expression[i]))) { + ++i; + } + + if (start > 0 && + std::isdigit(static_cast(expression[start - 1]))) { + continue; + } + + const std::string token = expression.substr(start, i - start); + if (!(is_qasm3_function_identifier(token) && + is_function_call(expression, i))) { + inputs.insert(token); + } + } + } + + void harvest_parameter(const QkParam *param, + std::set &inputs) const { + char *param_str = qk_param_str(param); + harvest_parameter_identifiers(param_str, inputs); + qk_str_free(param_str); + } + + std::set collect_input_parameters(void) const { + std::set inputs; + + const uint_t nops = qk_circuit_num_instructions(circuit_.rust_circuit_.get()); + for (uint_t i = 0; i < nops; i++) { + QkCircuitInstruction op; + qk_circuit_get_instruction(circuit_.rust_circuit_.get(), i, &op); + for (uint_t j = 0; j < op.num_params; j++) { + harvest_parameter(op.params[j], inputs); + } + qk_circuit_instruction_clear(&op); + } + + QkParam *global_phase = qk_circuit_global_phase(circuit_.rust_circuit_.get()); + harvest_parameter(global_phase, inputs); + qk_param_free(global_phase); + + return inputs; + } + + std::map + allocate_input_names(const std::set &inputs, + std::set &reserved) const { + std::map names; + for (const auto &input : inputs) { + names[input] = unique_identifier(input, reserved); + } + return names; + } + + std::string format_parameter( + const QkParam *param, + const std::map &input_names) const { + char *param_str = qk_param_str(param); + const std::string expression = param_str; + qk_str_free(param_str); + + std::stringstream formatted; + for (std::size_t i = 0; i < expression.size();) { + const unsigned char ch = static_cast(expression[i]); + if (!is_identifier_start(ch)) { + formatted << expression[i++]; + continue; + } + + const std::size_t start = i++; + while (i < expression.size() && + is_identifier_char(static_cast(expression[i]))) { + ++i; + } + + const std::string token = expression.substr(start, i - start); + if (start > 0 && + std::isdigit(static_cast(expression[start - 1]))) { + formatted << token; + continue; + } + + const auto replacement = input_names.find(token); + if (replacement != input_names.end() && + !(is_qasm3_function_identifier(token) && + is_function_call(expression, i))) { + formatted << replacement->second; + } else { + formatted << token; + } + } + + return formatted.str(); + } + +public: + explicit Qasm3Exporter(QuantumCircuit &circuit) : circuit_(circuit) {} + + /// @brief Serialize a QuantumCircuit object as an OpenQASM3 string. + /// @return An OpenQASM3 string. + std::string to_qasm3(void) { + circuit_.add_pending_control_flow_op(); + + std::stringstream qasm3; + qasm3 << std::setprecision(18); + qasm3 << "OPENQASM 3.0;" << std::endl; + qasm3 << "include \"stdgates.inc\";" << std::endl; + std::set reserved = reserved_global_identifiers(); + auto name_map = get_standard_gate_name_mapping(); + QkOpCounts opcounts = qk_circuit_count_ops(circuit_.rust_circuit_.get()); + for (std::size_t i = 0; i < opcounts.len; i++) { + if (opcounts.data[i].count != 0) { + const auto op = name_map[opcounts.data[i].name].gate_map(); + reserve_emitted_gate_definition_names(op, reserved); + } + } + + const auto input_names = + allocate_input_names(collect_input_parameters(), reserved); + for (const auto &input : input_names) { + qasm3 << "input float[64] " << input.second << ";" << std::endl; + } + + // add header for non-standard gates + bool cs = false; + bool sxdg = false; + for (std::size_t 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(circuit_.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 = unique_identifier("q", reserved); + qasm3 << "qubit[" << circuit_.num_qubits() << "] " << qreg_name << ";" + << std::endl; + std::vector creg_names; + for (const auto &creg : circuit_.cregs_) { + creg_names.push_back(unique_identifier(creg.name(), reserved)); + qasm3 << "bit[" << creg.size() << "] " << creg_names.back() << ";" + << std::endl; + } + + auto recover_reg_data = + [this, &creg_names](uint_t index) -> std::pair { + auto it = + std::upper_bound(circuit_.cregs_.begin(), circuit_.cregs_.end(), + index, [](uint_t v, const ClassicalRegister ®) { + return v < reg.base_index(); + }); + assert(it != circuit_.cregs_.begin()); + it = std::prev(it); + const auto reg_index = + static_cast(std::distance(circuit_.cregs_.begin(), it)); + return std::make_pair(creg_names[reg_index], index - it->base_index()); + }; + + for (uint_t i = 0; i < nops; i++) { + QkCircuitInstruction op; + qk_circuit_get_instruction(circuit_.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 if (strcmp(op.name, "global_phase") == 0) { + qasm3 << "gphase"; + } else { + qasm3 << op.name; + } + if (op.num_params > 0) { + qasm3 << "("; + for (uint_t j = 0; j < op.num_params; j++) { + qasm3 << format_parameter(op.params[j], input_names); + 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(); + } +}; + +inline std::string QuantumCircuit::to_qasm3(void) { + return Qasm3Exporter(*this).to_qasm3(); +} + +} // namespace circuit +} // namespace Qiskit + +#endif // __qiskitcpp_circuit_qasm3_exporter_hpp__ diff --git a/src/circuit/quantumcircuit.hpp b/src/circuit/quantumcircuit.hpp index f1b6ebf..3a38222 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 "circuit/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..e27239c 100644 --- a/src/circuit/quantumcircuit_def.hpp +++ b/src/circuit/quantumcircuit_def.hpp @@ -58,6 +58,7 @@ namespace circuit class ControlFlowOp; class IfElseOp; +class Qasm3Exporter; static Parameter null_param; @@ -65,6 +66,7 @@ static Parameter null_param; /// @brief Qiskit representation of a quantum circuit. class QuantumCircuit { + friend class Qasm3Exporter; protected: uint_t num_qubits_; // number of qubits uint_t num_clbits_; // number of classical bits @@ -1539,365 +1541,7 @@ class QuantumCircuit /// @brief Serialize a QuantumCircuit object as an OpenQASM3 string. /// @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(); - } + std::string to_qasm3(void); /// @brief print circuit (this is for debug) void print(void) const diff --git a/test/test_circuit.cpp b/test/test_circuit.cpp index eae4cd8..6bb9de4 100644 --- a/test/test_circuit.cpp +++ b/test/test_circuit.cpp @@ -641,6 +641,350 @@ static int test_to_qasm3_multi_regs(void) { return Ok; } +static int test_to_qasm3_parameterized(void) { + auto qreg = QuantumRegister(2, std::string("q")); + auto creg = ClassicalRegister(1, std::string("c")); + QuantumCircuit circ(qreg, creg); + auto theta = Parameter("theta"); + auto phi = Parameter("phi"); + auto expr = theta + phi; + + circ.rx(theta, 0); + circ.rz(expr, 1); + circ.measure(0, 0); + + if (circ.num_parameters() != 2) { + std::cerr << " to_qasm3_parameterized test : number of parameters 2 != " << circ.num_parameters() << std::endl; + return EqualityError; + } + + 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[2] q;\n" + "bit[1] c;\n" + "rx(theta) q[0];\n" + "rz(phi + theta) q[1];\n" + "c[0] = measure q[0];\n"; + if (actual != expected) { + std::cerr << " to_qasm3_parameterized test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + +static int test_to_qasm3_parameter_name_conflicts(void) { + auto qreg = QuantumRegister(1, std::string("q")); + auto creg = ClassicalRegister(1, std::string("c")); + QuantumCircuit circ(qreg, creg); + auto qparam = Parameter("q"); + auto cparam = Parameter("c"); + auto rzparam = Parameter("rz"); + auto sinparam = Parameter("sin"); + auto measure_param = Parameter("measure"); + + circ.rx(qparam, 0); + circ.ry(cparam, 0); + circ.rz(rzparam, 0); + circ.p(sinparam, 0); + circ.rx(measure_param, 0); + circ.measure(0, 0); + + if (circ.num_parameters() != 5) { + std::cerr << " to_qasm3_parameter_name_conflicts test : number of parameters 5 != " << circ.num_parameters() << std::endl; + return EqualityError; + } + + const auto actual = circ.to_qasm3(); + const std::string expected = + "OPENQASM 3.0;\n" + "include \"stdgates.inc\";\n" + "input float[64] c;\n" + "input float[64] measure_0;\n" + "input float[64] q;\n" + "input float[64] rz_0;\n" + "input float[64] sin;\n" + "qubit[1] q_0;\n" + "bit[1] c_0;\n" + "rx(q) q_0[0];\n" + "ry(c) q_0[0];\n" + "rz(rz_0) q_0[0];\n" + "p(sin) q_0[0];\n" + "rx(measure_0) q_0[0];\n" + "c_0[0] = measure q_0[0];\n"; + if (actual != expected) { + std::cerr << " to_qasm3_parameter_name_conflicts test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + +static int test_to_qasm3_stdgate_name_conflicts(void) { + auto qreg = QuantumRegister(1, std::string("q")); + QuantumCircuit circ(std::vector({qreg}), std::vector()); + const std::vector names = { + "CX", "cphase", "crx", "cry", "crz", "cswap", + "cu", "id", "phase", "u1", "u2", "u3" + }; + + for (const auto &name : names) { + auto param = Parameter(name); + circ.rx(param, 0); + } + + if (circ.num_parameters() != names.size()) { + std::cerr << " to_qasm3_stdgate_name_conflicts test : number of parameters " << names.size() + << " != " << circ.num_parameters() << std::endl; + return EqualityError; + } + + const auto actual = circ.to_qasm3(); + const std::string expected = + "OPENQASM 3.0;\n" + "include \"stdgates.inc\";\n" + "input float[64] CX_0;\n" + "input float[64] cphase_0;\n" + "input float[64] crx_0;\n" + "input float[64] cry_0;\n" + "input float[64] crz_0;\n" + "input float[64] cswap_0;\n" + "input float[64] cu_0;\n" + "input float[64] id_0;\n" + "input float[64] phase_0;\n" + "input float[64] u1_0;\n" + "input float[64] u2_0;\n" + "input float[64] u3_0;\n" + "qubit[1] q;\n" + "rx(CX_0) q[0];\n" + "rx(cphase_0) q[0];\n" + "rx(crx_0) q[0];\n" + "rx(cry_0) q[0];\n" + "rx(crz_0) q[0];\n" + "rx(cswap_0) q[0];\n" + "rx(cu_0) q[0];\n" + "rx(id_0) q[0];\n" + "rx(phase_0) q[0];\n" + "rx(u1_0) q[0];\n" + "rx(u2_0) q[0];\n" + "rx(u3_0) q[0];\n"; + if (actual != expected) { + std::cerr << " to_qasm3_stdgate_name_conflicts test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + +static int test_to_qasm3_keyword_and_constant_name_conflicts(void) { + auto qreg = QuantumRegister(1, std::string("q")); + QuantumCircuit circ(std::vector({qreg}), std::vector()); + const std::vector names = { + "I", "array", "false", "im", "pi", "qreg", "readonly", "switch", "true" + }; + + for (const auto &name : names) { + auto param = Parameter(name); + circ.rx(param, 0); + } + + if (circ.num_parameters() != names.size()) { + std::cerr << " to_qasm3_keyword_and_constant_name_conflicts test : number of parameters " + << names.size() << " != " << circ.num_parameters() << std::endl; + return EqualityError; + } + + const auto actual = circ.to_qasm3(); + const std::string expected = + "OPENQASM 3.0;\n" + "include \"stdgates.inc\";\n" + "input float[64] I_0;\n" + "input float[64] array_0;\n" + "input float[64] false_0;\n" + "input float[64] im_0;\n" + "input float[64] pi_0;\n" + "input float[64] qreg_0;\n" + "input float[64] readonly_0;\n" + "input float[64] switch_0;\n" + "input float[64] true_0;\n" + "qubit[1] q;\n" + "rx(I_0) q[0];\n" + "rx(array_0) q[0];\n" + "rx(false_0) q[0];\n" + "rx(im_0) q[0];\n" + "rx(pi_0) q[0];\n" + "rx(qreg_0) q[0];\n" + "rx(readonly_0) q[0];\n" + "rx(switch_0) q[0];\n" + "rx(true_0) q[0];\n"; + if (actual != expected) { + std::cerr << " to_qasm3_keyword_and_constant_name_conflicts test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + +static int test_to_qasm3_classical_register_name_conflicts(void) { + auto qreg = QuantumRegister(1, std::string("q")); + auto stdgate_creg = ClassicalRegister(1, std::string("cu")); + auto keyword_creg = ClassicalRegister(1, std::string("creg")); + QuantumCircuit circ(std::vector({qreg}), std::vector({stdgate_creg, keyword_creg})); + + circ.measure(qreg, stdgate_creg); + circ.measure(qreg, keyword_creg); + + const auto actual = circ.to_qasm3(); + const std::string expected = + "OPENQASM 3.0;\n" + "include \"stdgates.inc\";\n" + "qubit[1] q;\n" + "bit[1] cu_0;\n" + "bit[1] creg_0;\n" + "cu_0[0] = measure q[0];\n" + "creg_0[0] = measure q[0];\n"; + if (actual != expected) { + std::cerr << " to_qasm3_classical_register_name_conflicts test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + +static int test_to_qasm3_custom_gate_name_conflicts(void) { + auto qreg = QuantumRegister(2, std::string("q")); + auto creg = ClassicalRegister(1, std::string("rzz")); + QuantumCircuit circ(qreg, creg); + auto rzzparam = Parameter("rzz"); + + circ.rzz(rzzparam, 0, 1); + circ.measure(0, 0); + + if (circ.num_parameters() != 1) { + std::cerr << " to_qasm3_custom_gate_name_conflicts test : number of parameters 1 != " << circ.num_parameters() << std::endl; + return EqualityError; + } + + const auto actual = circ.to_qasm3(); + const std::string expected = + "OPENQASM 3.0;\n" + "include \"stdgates.inc\";\n" + "input float[64] rzz_0;\n" + "gate rzz(p0) _gate_q_0, _gate_q_1 {\n" + " cx _gate_q_0, _gate_q_1;\n" + " rz(p0) _gate_q_1;\n" + " cx _gate_q_0, _gate_q_1;\n" + "}\n" + "qubit[2] q;\n" + "bit[1] rzz_1;\n" + "rzz(rzz_0) q[0], q[1];\n" + "rzz_1[0] = measure q[0];\n"; + if (actual != expected) { + std::cerr << " to_qasm3_custom_gate_name_conflicts test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + +static int test_to_qasm3_cu1_definition(void) { + auto qreg = QuantumRegister(2, std::string("q")); + QuantumCircuit circ(std::vector({qreg}), std::vector()); + auto theta = Parameter("theta"); + + circ.cu1(theta, 0, 1); + + if (circ.num_parameters() != 1) { + std::cerr << " to_qasm3_cu1_definition test : number of parameters 1 != " << circ.num_parameters() << std::endl; + return EqualityError; + } + + const auto actual = circ.to_qasm3(); + const std::string expected = + "OPENQASM 3.0;\n" + "include \"stdgates.inc\";\n" + "input float[64] theta;\n" + "gate cu1(p0) _gate_q_0, _gate_q_1 {\n" + " cp(p0) _gate_q_0, _gate_q_1;\n" + "}\n" + "qubit[2] q;\n" + "cu1(theta) q[0], q[1];\n"; + if (actual != expected) { + std::cerr << " to_qasm3_cu1_definition test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + +static int test_to_qasm3_cu3_definition(void) { + auto qreg = QuantumRegister(2, std::string("q")); + QuantumCircuit circ(std::vector({qreg}), std::vector()); + auto theta = Parameter("theta"); + auto phi = Parameter("phi"); + auto lam = Parameter("lam"); + + circ.cu3(theta, phi, lam, 0, 1); + + if (circ.num_parameters() != 3) { + std::cerr << " to_qasm3_cu3_definition test : number of parameters 3 != " << circ.num_parameters() << std::endl; + return EqualityError; + } + + const auto actual = circ.to_qasm3(); + 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" + "gate cu3(p0, p1, p2) _gate_q_0, _gate_q_1 {\n" + " cu(p0, p1, p2, 0) _gate_q_0, _gate_q_1;\n" + "}\n" + "qubit[2] q;\n" + "cu3(theta, phi, lam) q[0], q[1];\n"; + if (actual != expected) { + std::cerr << " to_qasm3_cu3_definition test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + +static int test_to_qasm3_global_phase(void) { + auto qreg = QuantumRegister(1, std::string("q")); + QuantumCircuit circ(std::vector({qreg}), std::vector(), 0.5); + + circ.h(0); + + const auto actual = circ.to_qasm3(); + const std::string expected = + "OPENQASM 3.0;\n" + "include \"stdgates.inc\";\n" + "qubit[1] q;\n" + "gphase(0.5);\n" + "h q[0];\n"; + if (actual != expected) { + std::cerr << " to_qasm3_global_phase test : \n expected:\n" << expected + << "\n actual:\n" << actual << std::endl; + return EqualityError; + } + + return Ok; +} + #if defined(_WIN32) int test_circuit(int argc, char** const argv) { #else @@ -652,6 +996,15 @@ int test_circuit(int argc, char** argv) { num_failed += RUN_TEST(test_append); num_failed += RUN_TEST(test_compose); num_failed += RUN_TEST(test_to_qasm3_multi_regs); + num_failed += RUN_TEST(test_to_qasm3_parameterized); + num_failed += RUN_TEST(test_to_qasm3_parameter_name_conflicts); + num_failed += RUN_TEST(test_to_qasm3_stdgate_name_conflicts); + num_failed += RUN_TEST(test_to_qasm3_keyword_and_constant_name_conflicts); + num_failed += RUN_TEST(test_to_qasm3_classical_register_name_conflicts); + num_failed += RUN_TEST(test_to_qasm3_custom_gate_name_conflicts); + num_failed += RUN_TEST(test_to_qasm3_cu1_definition); + num_failed += RUN_TEST(test_to_qasm3_cu3_definition); + num_failed += RUN_TEST(test_to_qasm3_global_phase); std::cerr << "=== Number of failed subtests: " << num_failed << std::endl; return num_failed;