Repository: Qiskit/qiskit

Issue:
Unroll before noise aware layout assignment 
<!-- ⚠️ 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?

Noise aware layouts look at the number of cx gates, and which edges they act on, to determine a good initial layout. Therefore unrolling should be done before these passes. 



Discussion:

Resolving patch:
diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py
--- a/qiskit/transpiler/preset_passmanagers/level2.py
+++ b/qiskit/transpiler/preset_passmanagers/level2.py
@@ -70,7 +70,10 @@ def level_2_pass_manager(transpile_config):
     seed_transpiler = transpile_config.seed_transpiler
     backend_properties = transpile_config.backend_properties
 
-    # 1. Layout on good qubits if calibration info available, otherwise on dense links
+    # 1. Unroll to the basis first, to prepare for noise-adaptive layout
+    _unroll = Unroller(basis_gates)
+
+    # 2. Layout on good qubits if calibration info available, otherwise on dense links
     _given_layout = SetLayout(initial_layout)
 
     def _choose_layout_condition(property_set):
@@ -80,10 +83,10 @@ def _choose_layout_condition(property_set):
     if backend_properties:
         _choose_layout = NoiseAdaptiveLayout(backend_properties)
 
-    # 2. Extend dag/layout with ancillas using the full coupling map
+    # 3. Extend dag/layout with ancillas using the full coupling map
     _embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()]
 
-    # 3. Unroll to 1q or 2q gates, swap to fit the coupling map
+    # 4. Unroll to 1q or 2q gates, swap to fit the coupling map
     _swap_check = CheckMap(coupling_map)
 
     def _swap_condition(property_set):
@@ -94,9 +97,6 @@ def _swap_condition(property_set):
              StochasticSwap(coupling_map, trials=20, seed=seed_transpiler),
              Decompose(SwapGate)]
 
-    # 4. Unroll to the basis
-    _unroll = Unroller(basis_gates)
-
     # 5. Fix any bad CX directions
     # _direction_check = CheckCXDirection(coupling_map)  # TODO
     def _direction_condition(property_set):
@@ -116,12 +116,11 @@ def _opt_control(property_set):
     _opt = [Optimize1qGates(), CommutativeCancellation()]
 
     pm2 = PassManager()
+    pm2.append(_unroll)
     if coupling_map:
         pm2.append(_given_layout)
         pm2.append(_choose_layout, condition=_choose_layout_condition)
         pm2.append(_embed)
-    pm2.append(_unroll)
-    if coupling_map:
         pm2.append(_swap_check)
         pm2.append(_swap, condition=_swap_condition)
         # pm2.append(_direction_check)  # TODO
diff --git a/qiskit/transpiler/preset_passmanagers/level3.py b/qiskit/transpiler/preset_passmanagers/level3.py
--- a/qiskit/transpiler/preset_passmanagers/level3.py
+++ b/qiskit/transpiler/preset_passmanagers/level3.py
@@ -20,9 +20,10 @@
 """
 
 from qiskit.transpiler.passmanager import PassManager
-
+from qiskit.extensions.standard import SwapGate
 from qiskit.transpiler.passes import Unroller
 from qiskit.transpiler.passes import Unroll3qOrMore
+from qiskit.transpiler.passes import Decompose
 from qiskit.transpiler.passes import CheckMap
 from qiskit.transpiler.passes import CXDirection
 from qiskit.transpiler.passes import SetLayout
@@ -72,7 +73,10 @@ def level_3_pass_manager(transpile_config):
     seed_transpiler = transpile_config.seed_transpiler
     backend_properties = transpile_config.backend_properties
 
-    # 1. Layout on good qubits if calibration info available, otherwise on dense links
+    # 1. Unroll to the basis first, to prepare for noise-adaptive layout
+    _unroll = Unroller(basis_gates)
+
+    # 2. Layout on good qubits if calibration info available, otherwise on dense links
     _given_layout = SetLayout(initial_layout)
 
     def _choose_layout_condition(property_set):
@@ -82,10 +86,10 @@ def _choose_layout_condition(property_set):
     if backend_properties:
         _choose_layout = NoiseAdaptiveLayout(backend_properties)
 
-    # 2. Extend dag/layout with ancillas using the full coupling map
+    # 3. Extend dag/layout with ancillas using the full coupling map
     _embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()]
 
-    # 3. Unroll to 1q or 2q gates, swap to fit the coupling map
+    # 4. Unroll to 1q or 2q gates, swap to fit the coupling map
     _swap_check = CheckMap(coupling_map)
 
     def _swap_condition(property_set):
@@ -93,10 +97,8 @@ def _swap_condition(property_set):
 
     _swap = [BarrierBeforeFinalMeasurements(),
              Unroll3qOrMore(),
-             StochasticSwap(coupling_map, trials=20, seed=seed_transpiler)]
-
-    # 4. Unroll to the basis
-    _unroll = Unroller(basis_gates)
+             StochasticSwap(coupling_map, trials=20, seed=seed_transpiler),
+             Decompose(SwapGate)]
 
     # 5. 1q rotation merge and commutative cancellation iteratively until no more change in depth
     _depth_check = [Depth(), FixedPoint('depth')]
@@ -115,12 +117,11 @@ def _opt_control(property_set):
         # if a coupling map has been provided, match coupling
 
     pm3 = PassManager()
+    pm3.append(_unroll)
     if coupling_map:
         pm3.append(_given_layout)
         pm3.append(_choose_layout, condition=_choose_layout_condition)
         pm3.append(_embed)
-    pm3.append(_unroll)
-    if coupling_map:
         pm3.append(_swap_check)
         pm3.append(_swap, condition=_swap_condition)
     pm3.append(_depth_check + _opt, do_while=_opt_control)


Test patch: