Repository: Qiskit/qiskit

Issue:
Explicitly construct C3X and C4X in MCXGrayCode
<!-- ⚠️ 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**: latest GitHub code
- **Python version**: N/A
- **Operating system**: N/A

### What is the current behavior?
The docstring of the `MCXGate` constructor says "creates an explicit CX, CCX, C3X or C4X instance or a generic MCX gate", however it explicitly creates CX and CCX only.


### Steps to reproduce the problem
N/A


### What is the expected behavior?
Create C3X and C4X gates explicitly as well.




Discussion:
We have different implementations for MCX gates depending on the number of additional qubits available, namely based on gray-code (no additional qubits), recursion (one additional qubit) or a v-chain (several additional qubits). If we have one or two controls then all these methods generate a CX and CCX gate. However starting from three controls, the implementations start to differ in depth and width. Since the C3X and C4X gates don't use additional qubits, we should only explicitly construct them for the gray-code variant of MCX, i.e. in the `MCXGrayCode` class.

I updated the title to reflect that.

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
@@ -731,11 +731,11 @@ class MCXGate(ControlledGate):
     def __new__(cls, num_ctrl_qubits=None, label=None, ctrl_state=None):
         """Create a new MCX instance.
 
-        Depending on the number of controls, this creates an explicit CX, CCX, C3X or C4X
-        instance or a generic MCX gate.
+        Depending on the number of controls and which mode of the MCX, this creates an
+        explicit CX, CCX, C3X or C4X instance or a generic MCX gate.
         """
-        # these gates will always be implemented for all modes of the MCX if the number of control
-        # qubits matches this
+        # The CXGate and CCXGate will be implemented for all modes of the MCX, and
+        # the C3XGate and C4XGate will be implemented in the MCXGrayCode class.
         explicit = {
             1: CXGate,
             2: CCXGate
@@ -814,6 +814,23 @@ class MCXGrayCode(MCXGate):
 
     This delegates the implementation to the MCU1 gate, since :math:`X = H \cdot U1(\pi) \cdot H`.
     """
+    def __new__(cls, num_ctrl_qubits=None, label=None, ctrl_state=None):
+        """Create a new MCXGrayCode instance
+        """
+        # if 1 to 4 control qubits, create explicit gates
+        explicit = {
+            1: CXGate,
+            2: CCXGate,
+            3: C3XGate,
+            4: C4XGate
+        }
+        if num_ctrl_qubits in explicit.keys():
+            gate_class = explicit[num_ctrl_qubits]
+            gate = gate_class.__new__(gate_class, label=label, ctrl_state=ctrl_state)
+            # if __new__ does not return the same type as cls, init is not called
+            gate.__init__(label=label, ctrl_state=ctrl_state)
+            return gate
+        return super().__new__(cls)
 
     def __init__(self, num_ctrl_qubits, label=None, ctrl_state=None):
         super().__init__(num_ctrl_qubits, label=label, ctrl_state=ctrl_state, _name='mcx_gray')


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
@@ -606,6 +606,14 @@ def test_mcx_gates_yield_explicit_gates(self, num_ctrl_qubits):
         explicit = {1: CXGate, 2: CCXGate}
         self.assertEqual(cls, explicit[num_ctrl_qubits])
 
+    @data(1, 2, 3, 4)
+    def test_mcxgraycode_gates_yield_explicit_gates(self, num_ctrl_qubits):
+        """Test creating an mcx gate calls MCXGrayCode and yeilds explicit definition."""
+        qc = QuantumCircuit(num_ctrl_qubits+1)
+        qc.mcx(list(range(num_ctrl_qubits)), [num_ctrl_qubits])
+        explicit = {1: CXGate, 2: CCXGate, 3: C3XGate, 4: C4XGate}
+        self.assertEqual(type(qc[0][0]), explicit[num_ctrl_qubits])
+
     @data(3, 4, 5, 8)
     def test_mcx_gates(self, num_ctrl_qubits):
         """Test the mcx gates."""