Repository: Qiskit/qiskit

Issue:
QuantumCircuit.decompose() should take which gate(s) to decompose
<!-- ⚠️ 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?
If find myself looking at circuits that look like this:

<img width="928" alt="Screen Shot 2019-08-05 at 6 28 05 AM" src="https://user-images.githubusercontent.com/1249193/62458217-54d1a880-b74a-11e9-89ef-5bf7b2dcca8d.png">

and I am always interested in seeing what the circuits look like in terms of cx and other gates.  However, this requires doing:

```python

from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import Unroller
pass_ = Unroller(['u1', 'u2', 'u3', 'cx'])
pm = PassManager(pass_)
new_circ = pm.run(circ)
```

It would be nice if there was a `decompose_unitaries(basis)` method that did this for you.





Discussion:
I will note that I initially thought you wanted to have some general way to decompose any unitary, at least that's what `decompose_unitary` implies for me. But the unroller currently only takes the op definition and puts that in the circuit. If that's not defined an error is generated.
Then, if that is the case, KAK it would be the solution.
`circuit.decompose()` should do this.

Decompose is a "shallow" unroller, it unrolls every instruction one level down. But I agree that this method needs to take a list of gates to decompose. Currently it applies to every instruction, but you may want to only decompose UnitaryGates and not CZ gates for example.
Currently decompositions of 2 qubit unitaries via KAK is supported. There is code for arbitrary unitary decomposition in qiskit (see `qiskit.extensions.quantum_initializer.isometry`), but it is not integrated yet. The plan is to rely on that code to decompose any unitary (indeed any isometry).
@nonhermitian as I commented above, `circuit.decompose()` does what you want.

I will update the issue title to reflect that we want to control *which* gate gets decomposed.

I'm looking for first issues to contribute to and would love to tackle this one!
Assigning you @septembrr !
If anyone wants to pick up this issue please look at previous PR https://github.com/Qiskit/qiskit-terra/pull/5446 for guidance 😃 
I would like to work on this if its okay. I am new to this.
go for it @fs1132429! Assigning to you 
@fs1132429 as there is already a PR #5446 that was opened previously, the best way to approach this issue would be to create a new branch by branching off of `septembrr:quantum-circuit-decompose-issue-2906`, then adding your new work to what has already been done. Then create a new PR against main 😄 
can anyone guide me how to proceed on adding support for the * wildcard, at the end of a string for circuit.decompose("circuit*") ?

Resolving patch:
diff --git a/qiskit/circuit/quantumcircuit.py b/qiskit/circuit/quantumcircuit.py
--- a/qiskit/circuit/quantumcircuit.py
+++ b/qiskit/circuit/quantumcircuit.py
@@ -1276,10 +1276,14 @@ def to_gate(self, parameter_map=None, label=None):
 
         return circuit_to_gate(self, parameter_map, label=label)
 
-    def decompose(self):
+    def decompose(self, gates_to_decompose=None):
         """Call a decomposition pass on this circuit,
         to decompose one level (shallow decompose).
 
+        Args:
+            gates_to_decompose (str or list(str)): optional subset of gates to decompose.
+                Defaults to all gates in circuit.
+
         Returns:
             QuantumCircuit: a circuit one level decomposed
         """
@@ -1288,7 +1292,7 @@ def decompose(self):
         from qiskit.converters.circuit_to_dag import circuit_to_dag
         from qiskit.converters.dag_to_circuit import dag_to_circuit
 
-        pass_ = Decompose()
+        pass_ = Decompose(gates_to_decompose=gates_to_decompose)
         decomposed_dag = pass_.run(circuit_to_dag(self))
         return dag_to_circuit(decomposed_dag)
 
diff --git a/qiskit/transpiler/passes/basis/decompose.py b/qiskit/transpiler/passes/basis/decompose.py
--- a/qiskit/transpiler/passes/basis/decompose.py
+++ b/qiskit/transpiler/passes/basis/decompose.py
@@ -11,26 +11,69 @@
 # that they have been altered from the originals.
 
 """Expand a gate in a circuit using its decomposition rules."""
+import warnings
+from typing import Type, Union, List, Optional
+from fnmatch import fnmatch
 
-from typing import Type
-
-from qiskit.circuit.gate import Gate
 from qiskit.transpiler.basepasses import TransformationPass
 from qiskit.dagcircuit.dagcircuit import DAGCircuit
 from qiskit.converters.circuit_to_dag import circuit_to_dag
+from qiskit.circuit.gate import Gate
+from qiskit.utils.deprecation import deprecate_arguments
 
 
 class Decompose(TransformationPass):
     """Expand a gate in a circuit using its decomposition rules."""
 
-    def __init__(self, gate: Type[Gate] = None):
+    @deprecate_arguments({"gate": "gates_to_decompose"})
+    def __init__(
+        self,
+        gate: Optional[Type[Gate]] = None,
+        gates_to_decompose: Optional[Union[Type[Gate], List[Type[Gate]], List[str], str]] = None,
+    ) -> None:
         """Decompose initializer.
 
         Args:
-            gate: gate to decompose.
+            gate: DEPRECATED gate to decompose.
+            gates_to_decompose: optional subset of gates to be decomposed,
+                identified by gate label, name or type. Defaults to all gates.
         """
         super().__init__()
-        self.gate = gate
+
+        if gate is not None:
+            self.gates_to_decompose = gate
+        else:
+            self.gates_to_decompose = gates_to_decompose
+
+    @property
+    def gate(self) -> Gate:
+        """Returns the gate"""
+        warnings.warn(
+            "The gate argument is deprecated as of 0.18.0, and "
+            "will be removed no earlier than 3 months after that "
+            "release date. You should use the gates_to_decompose argument "
+            "instead.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        return self.gates_to_decompose
+
+    @gate.setter
+    def gate(self, value):
+        """Sets the gate
+
+        Args:
+            value (Gate): new value for gate
+        """
+        warnings.warn(
+            "The gate argument is deprecated as of 0.18.0, and "
+            "will be removed no earlier than 3 months after that "
+            "release date. You should use the gates_to_decompose argument "
+            "instead.",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+        self.gates_to_decompose = value
 
     def run(self, dag: DAGCircuit) -> DAGCircuit:
         """Run the Decompose pass on `dag`.
@@ -42,18 +85,52 @@ def run(self, dag: DAGCircuit) -> DAGCircuit:
             output dag where ``gate`` was expanded.
         """
         # Walk through the DAG and expand each non-basis node
-        for node in dag.op_nodes(self.gate):
-            # opaque or built-in gates are not decomposable
-            if not node.op.definition:
-                continue
-            # TODO: allow choosing among multiple decomposition rules
-            rule = node.op.definition.data
-
-            if len(rule) == 1 and len(node.qargs) == len(rule[0][1]) == 1:
-                if node.op.definition.global_phase:
-                    dag.global_phase += node.op.definition.global_phase
-                dag.substitute_node(node, rule[0][0], inplace=True)
-            else:
-                decomposition = circuit_to_dag(node.op.definition)
-                dag.substitute_node_with_dag(node, decomposition)
+        for node in dag.op_nodes():
+            if self._should_decompose(node):
+                if not node.op.definition:
+                    continue
+                # TODO: allow choosing among multiple decomposition rules
+                rule = node.op.definition.data
+                if len(rule) == 1 and len(node.qargs) == len(rule[0][1]) == 1:
+                    if node.op.definition.global_phase:
+                        dag.global_phase += node.op.definition.global_phase
+                    dag.substitute_node(node, rule[0][0], inplace=True)
+                else:
+                    decomposition = circuit_to_dag(node.op.definition)
+                    dag.substitute_node_with_dag(node, decomposition)
+
         return dag
+
+    def _should_decompose(self, node) -> bool:
+        """Call a decomposition pass on this circuit,
+        to decompose one level (shallow decompose)."""
+        if self.gates_to_decompose is None:  # check if no gates given
+            return True
+
+        has_label = False
+
+        if not isinstance(self.gates_to_decompose, list):
+            gates = [self.gates_to_decompose]
+        else:
+            gates = self.gates_to_decompose
+
+        strings_list = [s for s in gates if isinstance(s, str)]
+        gate_type_list = [g for g in gates if isinstance(g, type)]
+
+        if hasattr(node.op, "label") and node.op.label is not None:
+            has_label = True
+
+        if has_label and (  # check if label or label wildcard is given
+            node.op.label in gates or any(fnmatch(node.op.label, p) for p in strings_list)
+        ):
+            return True
+        elif not has_label and (  # check if name or name wildcard is given
+            node.name in gates or any(fnmatch(node.name, p) for p in strings_list)
+        ):
+            return True
+        elif not has_label and (  # check if Gate type given
+            any(isinstance(node.op, op) for op in gate_type_list)
+        ):
+            return True
+        else:
+            return False


Test patch:
diff --git a/test/python/transpiler/test_decompose.py b/test/python/transpiler/test_decompose.py
--- a/test/python/transpiler/test_decompose.py
+++ b/test/python/transpiler/test_decompose.py
@@ -25,6 +25,41 @@
 class TestDecompose(QiskitTestCase):
     """Tests the decompose pass."""
 
+    def setUp(self):
+        super().setUp()
+        # example complex circuit
+        #       ┌────────┐               ┌───┐┌─────────────┐
+        # q2_0: ┤0       ├────────────■──┤ H ├┤0            ├
+        #       │        │            │  └───┘│  circuit-57 │
+        # q2_1: ┤1 gate1 ├────────────■───────┤1            ├
+        #       │        │┌────────┐  │       └─────────────┘
+        # q2_2: ┤2       ├┤0       ├──■──────────────────────
+        #       └────────┘│        │  │
+        # q2_3: ──────────┤1 gate2 ├──■──────────────────────
+        #                 │        │┌─┴─┐
+        # q2_4: ──────────┤2       ├┤ X ├────────────────────
+        #                 └────────┘└───┘
+        circ1 = QuantumCircuit(3)
+        circ1.h(0)
+        circ1.t(1)
+        circ1.x(2)
+        my_gate = circ1.to_gate(label="gate1")
+        circ2 = QuantumCircuit(3)
+        circ2.h(0)
+        circ2.cx(0, 1)
+        circ2.x(2)
+        my_gate2 = circ2.to_gate(label="gate2")
+        circ3 = QuantumCircuit(2)
+        circ3.x(0)
+        q_bits = QuantumRegister(5)
+        qc = QuantumCircuit(q_bits)
+        qc.append(my_gate, q_bits[:3])
+        qc.append(my_gate2, q_bits[2:])
+        qc.mct(q_bits[:4], q_bits[4])
+        qc.h(0)
+        qc.append(circ3, [0, 1])
+        self.complex_circuit = qc
+
     def test_basic(self):
         """Test decompose a single H into u2."""
         qr = QuantumRegister(1, "qr")
@@ -37,6 +72,20 @@ def test_basic(self):
         self.assertEqual(len(op_nodes), 1)
         self.assertEqual(op_nodes[0].name, "u2")
 
+    def test_decompose_none(self):
+        """Test decompose a single H into u2."""
+        qr = QuantumRegister(1, "qr")
+        circuit = QuantumCircuit(qr)
+        circuit.h(qr[0])
+        dag = circuit_to_dag(circuit)
+        pass_ = Decompose(HGate)
+        with self.assertWarns(DeprecationWarning):
+            pass_.gate = None
+        after_dag = pass_.run(dag)
+        op_nodes = after_dag.op_nodes()
+        self.assertEqual(len(op_nodes), 1)
+        self.assertEqual(op_nodes[0].name, "u2")
+
     def test_decompose_only_h(self):
         """Test to decompose a single H, without the rest"""
         qr = QuantumRegister(2, "qr")
@@ -100,9 +149,9 @@ def test_decompose_oversized_instruction(self):
     def test_decomposition_preserves_qregs_order(self):
         """Test decomposing a gate preserves it's definition registers order"""
         qr = QuantumRegister(2, "qr1")
-        qc = QuantumCircuit(qr)
-        qc.cx(1, 0)
-        gate = qc.to_gate()
+        qc1 = QuantumCircuit(qr)
+        qc1.cx(1, 0)
+        gate = qc1.to_gate()
 
         qr2 = QuantumRegister(2, "qr2")
         qc2 = QuantumCircuit(qr2)
@@ -115,20 +164,20 @@ def test_decomposition_preserves_qregs_order(self):
 
     def test_decompose_global_phase_1q(self):
         """Test decomposition of circuit with global phase"""
-        qc = QuantumCircuit(1)
-        qc.rz(0.1, 0)
-        qc.ry(0.5, 0)
-        qc.global_phase += pi / 4
-        qcd = qc.decompose()
-        self.assertEqual(Operator(qc), Operator(qcd))
+        qc1 = QuantumCircuit(1)
+        qc1.rz(0.1, 0)
+        qc1.ry(0.5, 0)
+        qc1.global_phase += pi / 4
+        qcd = qc1.decompose()
+        self.assertEqual(Operator(qc1), Operator(qcd))
 
     def test_decompose_global_phase_2q(self):
         """Test decomposition of circuit with global phase"""
-        qc = QuantumCircuit(2, global_phase=pi / 4)
-        qc.rz(0.1, 0)
-        qc.rxx(0.2, 0, 1)
-        qcd = qc.decompose()
-        self.assertEqual(Operator(qc), Operator(qcd))
+        qc1 = QuantumCircuit(2, global_phase=pi / 4)
+        qc1.rz(0.1, 0)
+        qc1.rxx(0.2, 0, 1)
+        qcd = qc1.decompose()
+        self.assertEqual(Operator(qc1), Operator(qcd))
 
     def test_decompose_global_phase_1q_composite(self):
         """Test decomposition of circuit with global phase in a composite gate."""
@@ -137,7 +186,102 @@ def test_decompose_global_phase_1q_composite(self):
         circ.h(0)
         v = circ.to_gate()
 
-        qc = QuantumCircuit(1)
-        qc.append(v, [0])
-        qcd = qc.decompose()
-        self.assertEqual(Operator(qc), Operator(qcd))
+        qc1 = QuantumCircuit(1)
+        qc1.append(v, [0])
+        qcd = qc1.decompose()
+        self.assertEqual(Operator(qc1), Operator(qcd))
+
+    def test_decompose_only_h_gate(self):
+        """Test decomposition parameters so that only a certain gate is decomposed."""
+        circ = QuantumCircuit(2, 1)
+        circ.h(0)
+        circ.cz(0, 1)
+        decom_circ = circ.decompose(["h"])
+        dag = circuit_to_dag(decom_circ)
+        self.assertEqual(len(dag.op_nodes()), 2)
+        self.assertEqual(dag.op_nodes()[0].name, "u2")
+        self.assertEqual(dag.op_nodes()[1].name, "cz")
+
+    def test_decompose_only_given_label(self):
+        """Test decomposition parameters so that only a given label is decomposed."""
+        decom_circ = self.complex_circuit.decompose(["gate2"])
+        dag = circuit_to_dag(decom_circ)
+
+        self.assertEqual(len(dag.op_nodes()), 7)
+        self.assertEqual(dag.op_nodes()[0].op.label, "gate1")
+        self.assertEqual(dag.op_nodes()[1].name, "h")
+        self.assertEqual(dag.op_nodes()[2].name, "cx")
+        self.assertEqual(dag.op_nodes()[3].name, "x")
+        self.assertEqual(dag.op_nodes()[4].name, "mcx")
+        self.assertEqual(dag.op_nodes()[5].name, "h")
+        self.assertRegex(dag.op_nodes()[6].name, "circuit-")
+
+    def test_decompose_only_given_name(self):
+        """Test decomposition parameters so that only given name is decomposed."""
+        decom_circ = self.complex_circuit.decompose(["mcx"])
+        dag = circuit_to_dag(decom_circ)
+
+        self.assertEqual(len(dag.op_nodes()), 13)
+        self.assertEqual(dag.op_nodes()[0].op.label, "gate1")
+        self.assertEqual(dag.op_nodes()[1].op.label, "gate2")
+        self.assertEqual(dag.op_nodes()[2].name, "h")
+        self.assertEqual(dag.op_nodes()[3].name, "cu1")
+        self.assertEqual(dag.op_nodes()[4].name, "rcccx")
+        self.assertEqual(dag.op_nodes()[5].name, "h")
+        self.assertEqual(dag.op_nodes()[6].name, "h")
+        self.assertEqual(dag.op_nodes()[7].name, "cu1")
+        self.assertEqual(dag.op_nodes()[8].name, "rcccx_dg")
+        self.assertEqual(dag.op_nodes()[9].name, "h")
+        self.assertEqual(dag.op_nodes()[10].name, "c3sx")
+        self.assertEqual(dag.op_nodes()[11].name, "h")
+        self.assertRegex(dag.op_nodes()[12].name, "circuit-")
+
+    def test_decompose_mixture_of_names_and_labels(self):
+        """Test decomposition parameters so that mixture of names and labels is decomposed"""
+        decom_circ = self.complex_circuit.decompose(["mcx", "gate2"])
+        dag = circuit_to_dag(decom_circ)
+
+        self.assertEqual(len(dag.op_nodes()), 15)
+        self.assertEqual(dag.op_nodes()[0].op.label, "gate1")
+        self.assertEqual(dag.op_nodes()[1].name, "h")
+        self.assertEqual(dag.op_nodes()[2].name, "cx")
+        self.assertEqual(dag.op_nodes()[3].name, "x")
+        self.assertEqual(dag.op_nodes()[4].name, "h")
+        self.assertEqual(dag.op_nodes()[5].name, "cu1")
+        self.assertEqual(dag.op_nodes()[6].name, "rcccx")
+        self.assertEqual(dag.op_nodes()[7].name, "h")
+        self.assertEqual(dag.op_nodes()[8].name, "h")
+        self.assertEqual(dag.op_nodes()[9].name, "cu1")
+        self.assertEqual(dag.op_nodes()[10].name, "rcccx_dg")
+        self.assertEqual(dag.op_nodes()[11].name, "h")
+        self.assertEqual(dag.op_nodes()[12].name, "c3sx")
+        self.assertEqual(dag.op_nodes()[13].name, "h")
+        self.assertRegex(dag.op_nodes()[14].name, "circuit-")
+
+    def test_decompose_name_wildcards(self):
+        """Test decomposition parameters so that name wildcards is decomposed"""
+        decom_circ = self.complex_circuit.decompose(["circuit-*"])
+        dag = circuit_to_dag(decom_circ)
+
+        self.assertEqual(len(dag.op_nodes()), 5)
+        self.assertEqual(dag.op_nodes()[0].op.label, "gate1")
+        self.assertEqual(dag.op_nodes()[1].op.label, "gate2")
+        self.assertEqual(dag.op_nodes()[2].name, "mcx")
+        self.assertEqual(dag.op_nodes()[3].name, "h")
+        self.assertRegex(dag.op_nodes()[4].name, "x")
+
+    def test_decompose_label_wildcards(self):
+        """Test decomposition parameters so that label wildcards is decomposed"""
+        decom_circ = self.complex_circuit.decompose(["gate*"])
+        dag = circuit_to_dag(decom_circ)
+
+        self.assertEqual(len(dag.op_nodes()), 9)
+        self.assertEqual(dag.op_nodes()[0].name, "h")
+        self.assertEqual(dag.op_nodes()[1].name, "t")
+        self.assertEqual(dag.op_nodes()[2].name, "x")
+        self.assertEqual(dag.op_nodes()[3].name, "h")
+        self.assertEqual(dag.op_nodes()[4].name, "cx")
+        self.assertEqual(dag.op_nodes()[5].name, "x")
+        self.assertEqual(dag.op_nodes()[6].name, "mcx")
+        self.assertEqual(dag.op_nodes()[7].name, "h")
+        self.assertRegex(dag.op_nodes()[8].name, "circuit-")