Repository: Qiskit/qiskit

Issue:
Optimize1qGatesSimpleCommutation scales superlinearly
<!-- ⚠️ 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?
The transpiler pass Optimize1qGatesSimpleCommutation scales as the ~4th power in the number of qubits for a random circuit of equal width and depth.

![image](https://user-images.githubusercontent.com/25756217/138799195-e193b8cf-94a9-414e-9c33-074e71d1a717.png)

![image](https://user-images.githubusercontent.com/25756217/138895039-2019e6db-ee84-49a2-b9a6-a72b0c0bc2e7.png)

This is qiskit-terra 0.19.0.dev0+637acc0



Discussion:
yeah i also noticed this pass scales very poorly as circuit size increases.
Hi @ewinston, I was trying to work on this issue. Could you provide the details as to how you generate this profile? I know about `%prun` in python but it would be great to know about the profiler (or the script) that you use to generate the above graphs. 

Resolving patch:
diff --git a/qiskit/transpiler/passes/optimization/optimize_1q_commutation.py b/qiskit/transpiler/passes/optimization/optimize_1q_commutation.py
--- a/qiskit/transpiler/passes/optimization/optimize_1q_commutation.py
+++ b/qiskit/transpiler/passes/optimization/optimize_1q_commutation.py
@@ -14,6 +14,7 @@
 
 from copy import copy
 import logging
+from collections import deque
 
 from qiskit.circuit import QuantumCircuit
 from qiskit.circuit.library.standard_gates import CXGate, RZXGate
@@ -90,9 +91,15 @@ def _find_adjoining_run(dag, runs, run, front=True):
         adjoining_run = []
         for possibility in possibilities:
             if isinstance(possibility, DAGOpNode) and possibility.qargs == edge_node.qargs:
-                adjoining_run = next((run for run in runs if possibility in run), [])
+                adjoining_run = []
+                for single_run in runs:
+                    if (
+                        len(single_run) != 0 and single_run[0].qargs == possibility.qargs
+                    ):  # allows us to only check the run on a particular qubit
+                        if possibility in single_run:
+                            adjoining_run = single_run
+                            break
                 break
-
         return (blocker, adjoining_run)
 
     @staticmethod
@@ -106,10 +113,12 @@ def _commute_through(blocker, run, front=True):
 
         if run == []:
             return [], []
+        # use deque to have modification
+        # operations which are constant
+        # time
+        run_clone = deque(run)
 
-        run_clone = copy(run)
-
-        commuted = []
+        commuted = deque([])
         preindex, commutation_rule = None, None
         if isinstance(blocker, DAGOpNode):
             preindex = None
@@ -126,23 +135,20 @@ def _commute_through(blocker, run, front=True):
                 commutation_rule = commutation_table[type(blocker.op)][preindex]
 
         if commutation_rule is not None:
-            while run_clone != []:
+            while run_clone:
                 next_gate = run_clone[0] if front else run_clone[-1]
                 if next_gate.name not in commutation_rule:
                     break
                 if front:
+                    run_clone.popleft()
                     commuted.append(next_gate)
-                    del run_clone[0]
                 else:
-                    commuted.insert(0, next_gate)
-                    del run_clone[-1]
-
+                    run_clone.pop()
+                    commuted.appendleft(next_gate)
         if front:
-            assert commuted + run_clone == run
-            return commuted, run_clone
+            return list(commuted), list(run_clone)
         else:
-            assert run_clone + commuted == run
-            return run_clone, commuted
+            return list(run_clone), list(commuted)
 
     def _resynthesize(self, new_run):
         """


Test patch: