Repository: Qiskit/qiskit

Issue:
Transpiling 3+ qubit `UnitaryGate` circuits should work automatically
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues to confirm this idea does not exist. -->

### What is the expected enhancement?

3+ qubit unitary matrices can be transpiled to basis gates using the `isometry` instruction, however this is not used automatically if a circuit contains unitary gate instructions --- circuits with unitary gate instructions can currently only be transpiled if they are 1 or 2 qubits.

The definition of the `UnitaryGate` class should be updated to unroll using an `isometry` instruction for 3+ qubits.



Discussion:

Resolving patch:
diff --git a/qiskit/extensions/quantum_initializer/isometry.py b/qiskit/extensions/quantum_initializer/isometry.py
--- a/qiskit/extensions/quantum_initializer/isometry.py
+++ b/qiskit/extensions/quantum_initializer/isometry.py
@@ -25,9 +25,9 @@
 import itertools
 import numpy as np
 
-from qiskit import QuantumRegister
-from qiskit.circuit import Instruction
-from qiskit.circuit import QuantumCircuit
+from qiskit.circuit.instruction import Instruction
+from qiskit.circuit.quantumcircuit import QuantumCircuit
+from qiskit.circuit.quantumregister import QuantumRegister
 from qiskit.exceptions import QiskitError
 from qiskit.quantum_info.operators.predicates import is_isometry
 from qiskit.extensions.quantum_initializer.uc import UCGate
diff --git a/qiskit/extensions/unitary.py b/qiskit/extensions/unitary.py
--- a/qiskit/extensions/unitary.py
+++ b/qiskit/extensions/unitary.py
@@ -24,6 +24,7 @@
 from qiskit.circuit import QuantumRegister
 from qiskit.circuit._utils import _compute_control_matrix
 from qiskit.extensions.standard import U3Gate
+from qiskit.extensions.quantum_initializer import isometry
 from qiskit.quantum_info.operators.predicates import matrix_equal
 from qiskit.quantum_info.operators.predicates import is_unitary_matrix
 from qiskit.quantum_info.synthesis.one_qubit_decompose import OneQubitEulerDecomposer
@@ -111,8 +112,9 @@ def _define(self):
         elif self.num_qubits == 2:
             self.definition = two_qubit_cnot_decompose(self.to_matrix()).data
         else:
-            raise NotImplementedError("Not able to generate a subcircuit for "
-                                      "a {}-qubit unitary".format(self.num_qubits))
+            q = QuantumRegister(self.num_qubits, "q")
+            self.definition = [(isometry.Isometry(self.to_matrix(), 0, 0),
+                                q[:], [])]
 
     def control(self, num_ctrl_qubits=1, label=None, ctrl_state=None):
         r"""Return controlled version of gate


Test patch:
diff --git a/test/python/circuit/test_unitary.py b/test/python/circuit/test_unitary.py
--- a/test/python/circuit/test_unitary.py
+++ b/test/python/circuit/test_unitary.py
@@ -26,6 +26,8 @@
 from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
 from qiskit.transpiler import PassManager
 from qiskit.converters import circuit_to_dag, dag_to_circuit
+from qiskit.quantum_info.random import random_unitary
+from qiskit.quantum_info.operators import Operator
 from qiskit.transpiler.passes import CXCancellation
 
 
@@ -267,3 +269,9 @@ def test_qasm_2q_unitary(self):
                         "custom_gate q0[0],q0[1];\n" \
                         "custom_gate q0[1],q0[0];\n"
         self.assertEqual(expected_qasm, qc.qasm())
+
+    def test_unitary_decomposition(self):
+        """Test decomposition for unitary gates over 2 qubits."""
+        qc = QuantumCircuit(3)
+        qc.unitary(random_unitary(8, seed=42), [0, 1, 2])
+        self.assertTrue(Operator(qc).equiv(Operator(qc.decompose())))
diff --git a/test/python/transpiler/test_unroll_3q_or_more.py b/test/python/transpiler/test_unroll_3q_or_more.py
--- a/test/python/transpiler/test_unroll_3q_or_more.py
+++ b/test/python/transpiler/test_unroll_3q_or_more.py
@@ -16,7 +16,9 @@
 
 from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
 from qiskit.transpiler.passes import Unroll3qOrMore
-from qiskit.converters import circuit_to_dag
+from qiskit.converters import circuit_to_dag, dag_to_circuit
+from qiskit.quantum_info.operators import Operator
+from qiskit.quantum_info.random import random_unitary
 from qiskit.test import QiskitTestCase
 
 
@@ -69,3 +71,15 @@ def test_decompose_conditional(self):
         for node in op_nodes:
             self.assertIn(node.name, ['h', 't', 'tdg', 'cx'])
             self.assertEqual(node.condition, (cr, 0))
+
+    def test_decompose_unitary(self):
+        """Test unrolling of unitary gate over 4qubits."""
+        qr = QuantumRegister(4, 'qr')
+        circuit = QuantumCircuit(qr)
+        unitary = random_unitary(16, seed=42)
+        circuit.unitary(unitary, [0, 1, 2, 3])
+        dag = circuit_to_dag(circuit)
+        pass_ = Unroll3qOrMore()
+        after_dag = pass_.run(dag)
+        after_circ = dag_to_circuit(after_dag)
+        self.assertTrue(Operator(circuit).equiv(Operator(after_circ)))