Repository: Qiskit/qiskit

Issue:
Cannot apply gates to more than 3 qubits
<!-- ⚠️ 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**: master
- **Python version**: 3.6
- **Operating system**: MacOS

### What is the current behavior?

If I try and apply a 4-qubit unitary, or define a custom gate that acts on 4 qubits I get an error of `QiskitError: 'This gate cannot handle 4 arguments'` (and similarly for any number of qubits greater than 3).

This only occurs in master, seems to work fine in 0.8/stable.

### Steps to reproduce the problem

The following circuits will give this error

```python
qr = QuantumRegister(4)
circ = QuantumCircuit(qr)
circ.unitary(np.eye(2 ** 4), [qr[0], qr[1], qr[2], qr[3]])
```

```python
qr = QuantumRegister(4)
circ = QuantumCircuit(qr)
circ.append(Gate("mcx", 4, []), [qr[0], qr[1], qr[2], qr[3]])
```

### What is the expected behavior?

The `Gate` object should work for any number of qubits.

### Suggested solutions

The error is being raised by the `Gate.broadcast_arguments` function which looks to be hardcoded to only handle 1,2,3 qubit cases.


Discussion:

Resolving patch:
diff --git a/qiskit/circuit/gate.py b/qiskit/circuit/gate.py
--- a/qiskit/circuit/gate.py
+++ b/qiskit/circuit/gate.py
@@ -103,16 +103,13 @@ def _broadcast_2_arguments(qarg0, qarg1):
                               (qarg0, qarg1))
 
     @staticmethod
-    def _broadcast_3_arguments(qarg0, qarg1, qarg2):
-        if len(qarg0) == len(qarg1) == len(qarg2):
-            # [q[0], q[1]], [r[0], r[1]],  [s[0], s[1]] -> [q[0], r[0], s[0]]
-            #                                           -> [q[1], r[1], s[1]]
-            for arg0, arg1, arg2 in zip(qarg0, qarg1, qarg2):
-                yield [arg0, arg1, arg2], []
+    def _broadcast_3_or_more_args(qargs):
+        if all(len(qarg) == len(qargs[0]) for qarg in qargs):
+            for arg in zip(*qargs):
+                yield list(arg), []
         else:
             raise QiskitError(
-                'Not sure how to combine these three qubit arguments:\n %s\n %s\n %s' %
-                (qarg0, qarg1, qarg2))
+                'Not sure how to combine these qubit arguments:\n %s\n' % qargs)
 
     def broadcast_arguments(self, qargs, cargs):
         """
@@ -129,8 +126,8 @@ def broadcast_arguments(self, qargs, cargs):
                 [[q[0], q[1]], [r[0], r[1]]] -> [q[0], r[0]], [q[1], r[1]]
                 [[q[0]], [r[0], r[1]]]       -> [q[0], r[0]], [q[0], r[1]]
                 [[q[0], q[1]], [r[0]]]       -> [q[0], r[0]], [q[1], r[0]]
-         * If len(qargs) == 3:
-                [q[0], q[1]], [r[0], r[1]],  [s[0], s[1]] -> [q[0], r[0], s[0]], [q[1], r[1], s[1]]
+         * If len(qargs) >= 3:
+                [q[0], q[1]], [r[0], r[1]],  ...] -> [q[0], r[0], ...], [q[1], r[1], ...]
 
         Args:
             qargs (List): List of quantum bit arguments.
@@ -154,7 +151,7 @@ def broadcast_arguments(self, qargs, cargs):
             return Gate._broadcast_single_argument(qargs[0])
         elif len(qargs) == 2:
             return Gate._broadcast_2_arguments(qargs[0], qargs[1])
-        elif len(qargs) == 3:
-            return Gate._broadcast_3_arguments(qargs[0], qargs[1], qargs[2])
+        elif len(qargs) >= 3:
+            return Gate._broadcast_3_or_more_args(qargs)
         else:
             raise QiskitError('This gate cannot handle %i arguments' % len(qargs))


Test patch:
diff --git a/test/python/circuit/test_circuit_registers.py b/test/python/circuit/test_circuit_registers.py
--- a/test/python/circuit/test_circuit_registers.py
+++ b/test/python/circuit/test_circuit_registers.py
@@ -23,7 +23,7 @@
 
 import qiskit.extensions.simulator
 from qiskit import BasicAer
-from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit, Qubit, Clbit
+from qiskit.circuit import QuantumRegister, ClassicalRegister, QuantumCircuit, Qubit, Clbit, Gate
 from qiskit import execute
 from qiskit import QiskitError
 from qiskit.quantum_info import state_fidelity
@@ -368,6 +368,46 @@ def test_list_indexing(self):
             self.assertEqual(qargs[0].index, ind1)
             self.assertEqual(qargs[1].index, ind2)
 
+    def test_4_args_custom_gate_trivial_expansion(self):
+        """test 'expansion' of 4 args in custom gate.
+        See https://github.com/Qiskit/qiskit-terra/issues/2508"""
+        qr = QuantumRegister(4)
+        circ = QuantumCircuit(qr)
+        circ.append(Gate("mcx", 4, []), [qr[0], qr[1], qr[2], qr[3]])
+
+        self.assertEqual(len(circ.data), 1)
+        (gate, qargs, _) = circ.data[0]
+        self.assertEqual(gate.name, 'mcx')
+        self.assertEqual(len(qargs), 4)
+
+    def test_4_args_unitary_trivial_expansion(self):
+        """test 'expansion' of 4 args in unitary gate.
+        See https://github.com/Qiskit/qiskit-terra/issues/2508"""
+        qr = QuantumRegister(4)
+        circ = QuantumCircuit(qr)
+        circ.unitary(np.eye(2 ** 4), [qr[0], qr[1], qr[2], qr[3]])
+
+        self.assertEqual(len(circ.data), 1)
+        (gate, qargs, _) = circ.data[0]
+        self.assertEqual(gate.name, 'unitary')
+        self.assertEqual(len(qargs), 4)
+
+    def test_4_args_unitary_zip_expansion(self):
+        """test zip expansion of 4 args in unitary gate.
+        See https://github.com/Qiskit/qiskit-terra/issues/2508"""
+        qr1 = QuantumRegister(4)
+        qr2 = QuantumRegister(4)
+        qr3 = QuantumRegister(4)
+        qr4 = QuantumRegister(4)
+
+        circ = QuantumCircuit(qr1, qr2, qr3, qr4)
+        circ.unitary(np.eye(2 ** 4), [qr1, qr2, qr3, qr4])
+
+        self.assertEqual(len(circ.data), 4)
+        for (gate, qargs, _) in circ.data:
+            self.assertEqual(gate.name, 'unitary')
+            self.assertEqual(len(qargs), 4)
+
 
 class TestCircuitBit(QiskitTestCase):
     """QuantumCircuit Registers tests."""