Repository: Qiskit/qiskit

Issue:
`C3SXGate` inconsistent with `SXGate`
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Terra version**: 0.18.3
- **Python version**: any
- **Operating system**: any

### What is the current behavior?

The definition of `C3SXGate()` is currently equivalent to `SXdgGate.control(3)`, but its name and its base gate definition indicate it should be equivalent to `SXgate.control(3)`.


### Steps to reproduce the problem

```python
In [1]: from qiskit.circuit.library import SXGate, SXdgGate, C3SXGate
   ...: from qiskit.quantum_info import Operator
   ...: import numpy as np
   ...:
   ...: (
   ...:     np.linalg.norm(Operator(SXGate().control(3)) - Operator(C3SXGate())),
   ...:     np.linalg.norm(Operator(SXdgGate().control(3)) - Operator(C3SXGate())),
   ...: )
Out[1]: (1.9999999999999984, 6.088839805355096e-15)
```

### What is the expected behavior?

`C3SXGate` should be consistent with `SXGate().control(3)`.


### Suggested solutions

Swap the negatives of the angles in `C3SXGate.definition`.  There's a fair amount more to do as well to ensure consistency - checking the equivalence library, QASM definitions, etc.


Discussion:

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
@@ -511,19 +511,19 @@ def _define(self):
         """
         gate c3sqrtx a,b,c,d
         {
-            h d; cu1(-pi/8) a,d; h d;
-            cx a,b;
-            h d; cu1(pi/8) b,d; h d;
+            h d; cu1(pi/8) a,d; h d;
             cx a,b;
             h d; cu1(-pi/8) b,d; h d;
+            cx a,b;
+            h d; cu1(pi/8) b,d; h d;
             cx b,c;
-            h d; cu1(pi/8) c,d; h d;
-            cx a,c;
             h d; cu1(-pi/8) c,d; h d;
-            cx b,c;
-            h d; cu1(pi/8) c,d; h d;
             cx a,c;
+            h d; cu1(pi/8) c,d; h d;
+            cx b,c;
             h d; cu1(-pi/8) c,d; h d;
+            cx a,c;
+            h d; cu1(pi/8) c,d; h d;
         }
         """
         # pylint: disable=cyclic-import
@@ -534,31 +534,31 @@ def _define(self):
         # pylint: disable=invalid-unary-operand-type
         rules = [
             (HGate(), [q[3]], []),
-            (CU1Gate(-self._angle), [q[0], q[3]], []),
+            (CU1Gate(self._angle), [q[0], q[3]], []),
             (HGate(), [q[3]], []),
             (CXGate(), [q[0], q[1]], []),
             (HGate(), [q[3]], []),
-            (CU1Gate(self._angle), [q[1], q[3]], []),
+            (CU1Gate(-self._angle), [q[1], q[3]], []),
             (HGate(), [q[3]], []),
             (CXGate(), [q[0], q[1]], []),
             (HGate(), [q[3]], []),
-            (CU1Gate(-self._angle), [q[1], q[3]], []),
+            (CU1Gate(self._angle), [q[1], q[3]], []),
             (HGate(), [q[3]], []),
             (CXGate(), [q[1], q[2]], []),
             (HGate(), [q[3]], []),
-            (CU1Gate(self._angle), [q[2], q[3]], []),
+            (CU1Gate(-self._angle), [q[2], q[3]], []),
             (HGate(), [q[3]], []),
             (CXGate(), [q[0], q[2]], []),
             (HGate(), [q[3]], []),
-            (CU1Gate(-self._angle), [q[2], q[3]], []),
+            (CU1Gate(self._angle), [q[2], q[3]], []),
             (HGate(), [q[3]], []),
             (CXGate(), [q[1], q[2]], []),
             (HGate(), [q[3]], []),
-            (CU1Gate(self._angle), [q[2], q[3]], []),
+            (CU1Gate(-self._angle), [q[2], q[3]], []),
             (HGate(), [q[3]], []),
             (CXGate(), [q[0], q[2]], []),
             (HGate(), [q[3]], []),
-            (CU1Gate(-self._angle), [q[2], q[3]], []),
+            (CU1Gate(self._angle), [q[2], q[3]], []),
             (HGate(), [q[3]], []),
         ]
         qc = QuantumCircuit(q)
@@ -834,25 +834,25 @@ def _define(self):
         """
         gate c3sqrtx a,b,c,d
         {
-            h d; cu1(-pi/8) a,d; h d;
-            cx a,b;
-            h d; cu1(pi/8) b,d; h d;
+            h d; cu1(pi/8) a,d; h d;
             cx a,b;
             h d; cu1(-pi/8) b,d; h d;
+            cx a,b;
+            h d; cu1(pi/8) b,d; h d;
             cx b,c;
-            h d; cu1(pi/8) c,d; h d;
-            cx a,c;
             h d; cu1(-pi/8) c,d; h d;
-            cx b,c;
-            h d; cu1(pi/8) c,d; h d;
             cx a,c;
+            h d; cu1(pi/8) c,d; h d;
+            cx b,c;
             h d; cu1(-pi/8) c,d; h d;
+            cx a,c;
+            h d; cu1(pi/8) c,d; h d;
         }
         gate c4x a,b,c,d,e
         {
-            h e; cu1(-pi/2) d,e; h e;
+            h e; cu1(pi/2) d,e; h e;
             rc3x a,b,c,d;
-            h e; cu1(pi/4) d,e; h e;
+            h e; cu1(-pi/2) d,e; h e;
             rc3x a,b,c,d;
             c3sqrtx a,b,c,e;
         }
@@ -865,11 +865,11 @@ def _define(self):
         qc = QuantumCircuit(q, name=self.name)
         rules = [
             (HGate(), [q[4]], []),
-            (CU1Gate(-numpy.pi / 2), [q[3], q[4]], []),
+            (CU1Gate(numpy.pi / 2), [q[3], q[4]], []),
             (HGate(), [q[4]], []),
             (RC3XGate(), [q[0], q[1], q[2], q[3]], []),
             (HGate(), [q[4]], []),
-            (CU1Gate(numpy.pi / 2), [q[3], q[4]], []),
+            (CU1Gate(-numpy.pi / 2), [q[3], q[4]], []),
             (HGate(), [q[4]], []),
             (RC3XGate().inverse(), [q[0], q[1], q[2], q[3]], []),
             (C3SXGate(), [q[0], q[1], q[2], q[4]], []),


Test patch:
diff --git a/test/python/circuit/test_controlled_gate.py b/test/python/circuit/test_controlled_gate.py
--- a/test/python/circuit/test_controlled_gate.py
+++ b/test/python/circuit/test_controlled_gate.py
@@ -169,6 +169,45 @@ def test_controlled_swap(self):
         """Test creation of controlled swap gate"""
         self.assertEqual(SwapGate().control(), CSwapGate())
 
+    def test_special_cases_equivalent_to_controlled_base_gate(self):
+        """Test that ``ControlledGate`` subclasses for more efficient representations give
+        equivalent matrices and definitions to the naive ``base_gate.control(n)``."""
+        # Angles used here are not important, we just pick slightly strange values to ensure that
+        # there are no coincidental equivalences.
+        tests = [
+            (CXGate(), 1),
+            (CCXGate(), 2),
+            (C3XGate(), 3),
+            (C4XGate(), 4),
+            (MCXGate(5), 5),
+            (CYGate(), 1),
+            (CZGate(), 1),
+            (CPhaseGate(np.pi / 7), 1),
+            (MCPhaseGate(np.pi / 7, 2), 2),
+            (CSwapGate(), 1),
+            (CSXGate(), 1),
+            (C3SXGate(), 3),
+            (CHGate(), 1),
+            (CU1Gate(np.pi / 7), 1),
+            (MCU1Gate(np.pi / 7, 2), 2),
+            # `CUGate` takes an extra "global" phase parameter compared to `UGate`, and consequently
+            # is only equal to `base_gate.control()` when this extra phase is 0.
+            (CUGate(np.pi / 7, np.pi / 5, np.pi / 3, 0), 1),
+            (CU3Gate(np.pi / 7, np.pi / 5, np.pi / 3), 1),
+            (CRXGate(np.pi / 7), 1),
+            (CRYGate(np.pi / 7), 1),
+            (CRZGate(np.pi / 7), 1),
+        ]
+        for special_case_gate, n_controls in tests:
+            with self.subTest(gate=special_case_gate.name):
+                naive_operator = Operator(special_case_gate.base_gate.control(n_controls))
+                # Ensure that both the array form (if the gate overrides `__array__`) and the
+                # circuit-definition form are tested.
+                self.assertTrue(Operator(special_case_gate).equiv(naive_operator))
+                if not isinstance(special_case_gate, CXGate):
+                    # CX is treated like a primitive within Terra, and doesn't have a definition.
+                    self.assertTrue(Operator(special_case_gate.definition).equiv(naive_operator))
+
     def test_circuit_append(self):
         """Test appending a controlled gate to a quantum circuit."""
         circ = QuantumCircuit(5)