diff --git a/qiskit/circuit/library/standard_gates/x.py b/qiskit/circuit/library/standard_gates/x.py index cc6eebc2f96e..2ebee778683e 100644 --- a/qiskit/circuit/library/standard_gates/x.py +++ b/qiskit/circuit/library/standard_gates/x.py @@ -569,6 +569,20 @@ def _define(self): self.definition = qc + def qasm(self): + # Gross hack to override the Qiskit name with the name this gate has in Terra's version of + # 'qelib1.inc'. In general, the larger exporter mechanism should know about this to do the + # mapping itself, but right now that's not possible without a complete rewrite of the OQ2 + # exporter code (low priority), or we would need to modify 'qelib1.inc' which would be + # needlessly disruptive this late in OQ2's lifecycle. The current OQ2 exporter _always_ + # outputs the `include 'qelib1.inc' line. ---Jake, 2022-11-21. + try: + old_name = self.name + self.name = "c3sqrtx" + return super().qasm() + finally: + self.name = old_name + class C3XGate(ControlledGate): r"""The X gate controlled on 3 qubits. diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py index 9144e60ca174..17c7684123d1 100644 --- a/qiskit/circuit/quantumcircuit.py +++ b/qiskit/circuit/quantumcircuit.py @@ -1644,7 +1644,7 @@ def qasm( "rccx", "rc3x", "c3x", - "c3sx", + "c3sx", # This is the Qiskit gate name, but the qelib1.inc name is 'c3sqrtx'. "c4x", ] diff --git a/qiskit/converters/ast_to_dag.py b/qiskit/converters/ast_to_dag.py index 3883f7c5668c..eb012ad38336 100644 --- a/qiskit/converters/ast_to_dag.py +++ b/qiskit/converters/ast_to_dag.py @@ -25,42 +25,7 @@ from qiskit.circuit.reset import Reset from qiskit.circuit.barrier import Barrier from qiskit.circuit.delay import Delay -from qiskit.circuit.library.standard_gates.x import CCXGate -from qiskit.circuit.library.standard_gates.swap import CSwapGate -from qiskit.circuit.library.standard_gates.x import CXGate -from qiskit.circuit.library.standard_gates.y import CYGate -from qiskit.circuit.library.standard_gates.z import CZGate -from qiskit.circuit.library.standard_gates.swap import SwapGate -from qiskit.circuit.library.standard_gates.h import HGate -from qiskit.circuit.library.standard_gates.i import IGate -from qiskit.circuit.library.standard_gates.s import SGate -from qiskit.circuit.library.standard_gates.s import SdgGate -from qiskit.circuit.library.standard_gates.sx import SXGate -from qiskit.circuit.library.standard_gates.sx import SXdgGate -from qiskit.circuit.library.standard_gates.t import TGate -from qiskit.circuit.library.standard_gates.t import TdgGate -from qiskit.circuit.library.standard_gates.p import PhaseGate -from qiskit.circuit.library.standard_gates.u1 import U1Gate -from qiskit.circuit.library.standard_gates.u2 import U2Gate -from qiskit.circuit.library.standard_gates.u3 import U3Gate -from qiskit.circuit.library.standard_gates.u import UGate -from qiskit.circuit.library.standard_gates.x import XGate -from qiskit.circuit.library.standard_gates.y import YGate -from qiskit.circuit.library.standard_gates.z import ZGate -from qiskit.circuit.library.standard_gates.rx import RXGate -from qiskit.circuit.library.standard_gates.ry import RYGate -from qiskit.circuit.library.standard_gates.rz import RZGate -from qiskit.circuit.library.standard_gates.rxx import RXXGate -from qiskit.circuit.library.standard_gates.rzz import RZZGate -from qiskit.circuit.library.standard_gates.p import CPhaseGate -from qiskit.circuit.library.standard_gates.u import CUGate -from qiskit.circuit.library.standard_gates.u1 import CU1Gate -from qiskit.circuit.library.standard_gates.u3 import CU3Gate -from qiskit.circuit.library.standard_gates.h import CHGate -from qiskit.circuit.library.standard_gates.rx import CRXGate -from qiskit.circuit.library.standard_gates.ry import CRYGate -from qiskit.circuit.library.standard_gates.rz import CRZGate -from qiskit.circuit.library.standard_gates.sx import CSXGate +from qiskit.circuit.library import standard_gates as std def ast_to_dag(ast): @@ -102,43 +67,48 @@ class AstInterpreter: """Interprets an OpenQASM by expanding subroutines and unrolling loops.""" standard_extension = { - "u1": U1Gate, - "u2": U2Gate, - "u3": U3Gate, - "u": UGate, - "p": PhaseGate, - "x": XGate, - "y": YGate, - "z": ZGate, - "t": TGate, - "tdg": TdgGate, - "s": SGate, - "sdg": SdgGate, - "sx": SXGate, - "sxdg": SXdgGate, - "swap": SwapGate, - "rx": RXGate, - "rxx": RXXGate, - "ry": RYGate, - "rz": RZGate, - "rzz": RZZGate, - "id": IGate, - "h": HGate, - "cx": CXGate, - "cy": CYGate, - "cz": CZGate, - "ch": CHGate, - "crx": CRXGate, - "cry": CRYGate, - "crz": CRZGate, - "csx": CSXGate, - "cu1": CU1Gate, - "cp": CPhaseGate, - "cu": CUGate, - "cu3": CU3Gate, - "ccx": CCXGate, - "cswap": CSwapGate, + "u1": std.U1Gate, + "u2": std.U2Gate, + "u3": std.U3Gate, + "u": std.UGate, + "p": std.PhaseGate, + "x": std.XGate, + "y": std.YGate, + "z": std.ZGate, + "t": std.TGate, + "tdg": std.TdgGate, + "s": std.SGate, + "sdg": std.SdgGate, + "sx": std.SXGate, + "sxdg": std.SXdgGate, + "swap": std.SwapGate, + "rx": std.RXGate, + "rxx": std.RXXGate, + "ry": std.RYGate, + "rz": std.RZGate, + "rzz": std.RZZGate, + "id": std.IGate, + "h": std.HGate, + "cx": std.CXGate, + "cy": std.CYGate, + "cz": std.CZGate, + "ch": std.CHGate, + "crx": std.CRXGate, + "cry": std.CRYGate, + "crz": std.CRZGate, + "csx": std.CSXGate, + "cu1": std.CU1Gate, + "cp": std.CPhaseGate, + "cu": std.CUGate, + "cu3": std.CU3Gate, + "ccx": std.CCXGate, + "cswap": std.CSwapGate, "delay": Delay, + "rccx": std.RCCXGate, + "rc3x": std.RC3XGate, + "c3x": std.C3XGate, + "c3sqrtx": std.C3SXGate, + "c4x": std.C4XGate, } def __init__(self, dag): @@ -263,7 +233,7 @@ def _process_cnot(self, node): ) maxidx = max([len(id0), len(id1)]) for idx in range(maxidx): - cx_gate = CXGate() + cx_gate = std.CXGate() cx_gate.condition = self.condition if len(id0) > 1 and len(id1) > 1: self.dag.apply_operation_back(cx_gate, [id0[idx], id1[idx]], []) diff --git a/releasenotes/notes/fix-qasm2-c3sxgate-47171c9d17876219.yaml b/releasenotes/notes/fix-qasm2-c3sxgate-47171c9d17876219.yaml new file mode 100644 index 000000000000..94b3ebd4da90 --- /dev/null +++ b/releasenotes/notes/fix-qasm2-c3sxgate-47171c9d17876219.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Circuits containing :class:`.C3SXGate` can now be output and read in again safely from the + OpenQASM 2.0 exporter (:meth:`.QuantumCircuit.qasm`) and parser (:meth:`.QuantumCircuit.from_qasm_str`). diff --git a/test/python/circuit/test_circuit_qasm.py b/test/python/circuit/test_circuit_qasm.py index 060178eb61e8..b8089e7756f0 100644 --- a/test/python/circuit/test_circuit_qasm.py +++ b/test/python/circuit/test_circuit_qasm.py @@ -19,6 +19,7 @@ from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.test import QiskitTestCase from qiskit.circuit import Parameter, Qubit, Clbit, Gate +from qiskit.circuit.library import C3SXGate from qiskit.qasm.exceptions import QasmError # Regex pattern to match valid OpenQASM identifiers @@ -247,6 +248,22 @@ def test_circuit_qasm_with_composite_circuit_with_many_params_and_qubits(self): self.assertEqual(original_str, qc.qasm()) + def test_c3sxgate_roundtrips(self): + """Test that C3SXGate correctly round trips. Qiskit gives this gate a different name + ('c3sx') to the name in Qiskit's version of qelib1.inc ('c3sqrtx') gate, which can lead to + resolution issues.""" + qc = QuantumCircuit(4) + qc.append(C3SXGate(), qc.qubits, []) + qasm = qc.qasm() + expected = """OPENQASM 2.0; +include "qelib1.inc"; +qreg q[4]; +c3sqrtx q[0],q[1],q[2],q[3]; +""" + self.assertEqual(qasm, expected) + parsed = QuantumCircuit.from_qasm_str(qasm) + self.assertIsInstance(parsed.data[0].operation, C3SXGate) + def test_unbound_circuit_raises(self): """Test circuits with unbound parameters raises.""" qc = QuantumCircuit(1)