Skip to content

Commit

Permalink
Fix C3SXGate roundtrip in OpenQASM 2 (#9183)
Browse files Browse the repository at this point in the history
* Fix C3SXGate roundtrip in OpenQASM 2

Qiskit and its version of `qelib1.inc` give `C3SXGate` different names:
`c3sx` and `c3sqrtx` respectively.  At this point, changing either of
these two names is likely to cause user pain, and it should not be
difficult for the OpenQASM 2 exporter to simply know about this
remapping, like it knows about other standard gate mappings.  In
practice, the structure of the exporter makes this rather difficult, but
we need not expose that complexity to users.

* Correct namespace lookup

* Break import cycle

---------

Co-authored-by: Luciano Bello <[email protected]>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Feb 7, 2023
1 parent 202ec09 commit 00b4754
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 74 deletions.
14 changes: 14 additions & 0 deletions qiskit/circuit/library/standard_gates/x.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
]

Expand Down
116 changes: 43 additions & 73 deletions qiskit/converters/ast_to_dag.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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]], [])
Expand Down
5 changes: 5 additions & 0 deletions releasenotes/notes/fix-qasm2-c3sxgate-47171c9d17876219.yaml
Original file line number Diff line number Diff line change
@@ -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`).
17 changes: 17 additions & 0 deletions test/python/circuit/test_circuit_qasm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 00b4754

Please sign in to comment.