Repository: Qiskit/qiskit

Issue:
Quantum circuits with a C3SX gate fail to convert back from qasm
### Information

- **Qiskit Terra version**: 0.18.3
- **Python version**: 3.8.5
- **Operating system**: macOS-10.16-x86_64-i386-64bit

### What is the current behavior?

A circuit that contains a C3SX gate can be translated to qasm, but the generated qasm cannot be translated back to a quantum circuit.
The error stems from a mismatch in the name of this gate between qelib1.inc ("c3sqrtx"):
https://github.com/Qiskit/qiskit-terra/blob/6af71f9d1e249a3747772449f299160df4047b83/qiskit/qasm/libs/qelib1.inc#L242
and qiskit's python code ("c3sx"):
https://github.com/Qiskit/qiskit-terra/blob/cf4005ff9fdc672111a827582e6b7d8ae2683be5/qiskit/circuit/library/standard_gates/x.py#L446
 https://github.com/Qiskit/qiskit-terra/blob/6af71f9d1e249a3747772449f299160df4047b83/qiskit/circuit/quantumcircuit.py#L1563

### Steps to reproduce the problem

```python
import qiskit

qc = qiskit.QuantumCircuit(4)
gate = qiskit.circuit.library.C3SXGate()
qc.append(gate, qargs=qc.qubits)

qasm = qc.qasm()
qc_from_qasm = qiskit.QuantumCircuit.from_qasm_str(qasm)  # fails:
# QasmError(
# qiskit.qasm.exceptions.QasmError: "Cannot find gate definition for 'c3sx', line 4 file "
```

### What is the expected behavior?

I would like the gate names to concur and the qasm to be convertible to a quantum circuit.

### Suggested solutions

- Rename the gate in qelib1.inc to "c3sx".
- Change the python code to use "c3sqrtx".

(This issue probably relates to #4943.)

![image](https://user-images.githubusercontent.com/36337649/137648025-f7a38444-ed6e-4c64-a822-6d5ad87a1277.png)

Fix `c3sx` gate definition (`c3sqrtx`->`c3sx`)
- [x] I have added the tests to cover my changes.
- [x] I have updated the documentation accordingly.
- [x] I have read the CONTRIBUTING document.

### Summary
Fixes #7148.
Change all the occurrences of `c3sqrtx` to `c3sx` to allow a quantum circuit with a
`C3SXGate` to be converted to qasm and back to a quantum circuit from the qasm.
Modify `test_loading_all_qelib1_gates` to truly test all `qelib1.inc` gates (except for specific ones).

### Details and comments
During working on this PR, I encountered more issues:

- Duplicate definitions and names of gates: `rcccx`, `c3x`, `c4x`, etc.
- Initial fixes for the issues should be similar to the one here.

Unfortunately, I would not be able to fix them all now.




Discussion:
Good catch! I think renaming the qasm definition to c3sx is the way to go so we stay consistent in our naming policy.
Thanks, if this solution is accepted I would like to submit a PR that replaces all the occurrences of `c3sqrtx` with `c3sx` - please let me know.
Yeah please go ahead! 
## Pull Request Test Coverage Report for [Build 1440339589](https://coveralls.io/builds/44133540)

* **0** of **0**   changed or added relevant lines in **0** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.005%**) to **82.484%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/44133540/badge)](https://coveralls.io/builds/44133540) |
| :-- | --: |
| Change from base [Build 1439902905](https://coveralls.io/builds/44130092): |  0.005% |
| Covered Lines: | 49616 |
| Relevant Lines: | 60152 |

---
##### 💛  - [Coveralls](https://coveralls.io)

> I'm not 100% sure that we're _safe_ to change the name of an operation in `qelib1.inc`; it may well have knock-on implications for how gates are serialised when being passed to the backends, so I'll need to check internally if it's actually safe for us to do that at all.

So I'm waiting for your decision before moving forward with this PR.
Please check the linked issue - there is an alternative solution.

Resolving patch:
diff --git a/qiskit/circuit/library/standard_gates/x.py b/qiskit/circuit/library/standard_gates/x.py
--- 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
--- 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
--- 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]], [])


Test patch:
diff --git a/test/python/circuit/test_circuit_qasm.py b/test/python/circuit/test_circuit_qasm.py
--- 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)