Repository: Qiskit/qiskit

Issue:
Restore level 1 layout/routing default to `SabreSwap`
### What should we add?

Part of #9417.  Depends on:
- #9419
- #9421

In #8830 we had to revert optimisation level 1 to use its legacy behaviour of laying out with `DenseLayout` and routing with `StochasticSwap` if the circuit was a control-flow circuit, since the new default `SabreSwap` couldn't handle the control-flow.  Once `SabreSwap` supports control-flow, we should remove this switching logic, and swap back to the preferred router.

`SabreLayout` should be easier to upgrade to initial support of control-flow (especially since it'll technically already run, it'll just not have good output), if only the Python-space paths are upgraded.


Discussion:

Resolving patch:
diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py
--- a/qiskit/transpiler/preset_passmanagers/level1.py
+++ b/qiskit/transpiler/preset_passmanagers/level1.py
@@ -73,8 +73,8 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa
     init_method = pass_manager_config.init_method
     # Unlike other presets, the layout and routing defaults aren't set here because they change
     # based on whether the input circuit has control flow.
-    layout_method = pass_manager_config.layout_method
-    routing_method = pass_manager_config.routing_method
+    layout_method = pass_manager_config.layout_method or "sabre"
+    routing_method = pass_manager_config.routing_method or "sabre"
     translation_method = pass_manager_config.translation_method or "translator"
     optimization_method = pass_manager_config.optimization_method
     scheduling_method = pass_manager_config.scheduling_method
@@ -157,43 +157,11 @@ def _vf2_match_not_found(property_set):
             skip_routing=pass_manager_config.routing_method is not None
             and routing_method != "sabre",
         )
-    elif layout_method is None:
-        _improve_layout = common.if_has_control_flow_else(
-            DenseLayout(coupling_map, backend_properties, target=target),
-            SabreLayout(
-                coupling_map_layout,
-                max_iterations=2,
-                seed=seed_transpiler,
-                swap_trials=5,
-                layout_trials=5,
-                skip_routing=pass_manager_config.routing_method is not None
-                and routing_method != "sabre",
-            ),
-        ).to_flow_controller()
 
     # Choose routing pass
-    routing_pm = None
-    if routing_method is None:
-        _stochastic_routing = plugin_manager.get_passmanager_stage(
-            "routing",
-            "stochastic",
-            pass_manager_config,
-            optimization_level=1,
-        )
-        _sabre_routing = plugin_manager.get_passmanager_stage(
-            "routing",
-            "sabre",
-            pass_manager_config,
-            optimization_level=1,
-        )
-        routing_pm = common.if_has_control_flow_else(_stochastic_routing, _sabre_routing)
-    else:
-        routing_pm = plugin_manager.get_passmanager_stage(
-            "routing",
-            routing_method,
-            pass_manager_config,
-            optimization_level=1,
-        )
+    routing_pm = plugin_manager.get_passmanager_stage(
+        "routing", routing_method, pass_manager_config, optimization_level=1
+    )
 
     # Build optimization loop: merge 1q rotations and cancel CNOT gates iteratively
     # until no more change in depth


Test patch:
diff --git a/test/python/transpiler/test_preset_passmanagers.py b/test/python/transpiler/test_preset_passmanagers.py
--- a/test/python/transpiler/test_preset_passmanagers.py
+++ b/test/python/transpiler/test_preset_passmanagers.py
@@ -322,6 +322,7 @@ def test_no_coupling_map(self, level):
         self.assertNotIn("TrivialLayout", self.passes)
         self.assertNotIn("ApplyLayout", self.passes)
         self.assertNotIn("StochasticSwap", self.passes)
+        self.assertNotIn("SabreSwap", self.passes)
         self.assertNotIn("CheckGateDirection", self.passes)
 
     @data(0, 1, 2, 3)
@@ -568,13 +569,11 @@ def test_level1_runs_vf2post_layout_when_routing_required_control_flow(self):
         # Expected call path for layout and routing is:
         # 1. TrivialLayout (no perfect match)
         # 2. VF2Layout (no perfect match)
-        # 3. DenseLayout (heuristic layout)
-        # 4. StochasticSwap
+        # 3. SabreLayout (heuristic layout)
         # 4. VF2PostLayout (applies a better layout)
         self.assertIn("TrivialLayout", self.passes)
         self.assertIn("VF2Layout", self.passes)
-        self.assertIn("DenseLayout", self.passes)
-        self.assertIn("StochasticSwap", self.passes)
+        self.assertIn("SabreLayout", self.passes)
         self.assertIn("VF2PostLayout", self.passes)
 
     def test_level1_not_runs_vf2post_layout_when_layout_method_set_control_flow(self):
@@ -599,7 +598,7 @@ def test_level1_not_runs_vf2post_layout_when_layout_method_set_control_flow(self
         self.assertNotIn("SabreLayout", self.passes)
         self.assertNotIn("VF2PostLayout", self.passes)
         self.assertIn("DenseLayout", self.passes)
-        self.assertIn("StochasticSwap", self.passes)
+        self.assertIn("SabreSwap", self.passes)
 
     def test_level1_not_run_vf2post_layout_when_trivial_is_perfect_control_flow(self):
         """Test that if we find a trivial perfect layout we don't run vf2post."""
@@ -615,8 +614,8 @@ def test_level1_not_run_vf2post_layout_when_trivial_is_perfect_control_flow(self
         _ = transpile(qc, target, optimization_level=1, callback=self.callback)
         self.assertIn("TrivialLayout", self.passes)
         self.assertNotIn("VF2Layout", self.passes)
-        self.assertNotIn("DenseLayout", self.passes)
-        self.assertNotIn("StochasticSwap", self.passes)
+        self.assertNotIn("SabreLayout", self.passes)
+        self.assertNotIn("SabreSwap", self.passes)
         self.assertNotIn("VF2PostLayout", self.passes)
 
     def test_level1_not_run_vf2post_layout_when_vf2layout_is_perfect_control_flow(self):
@@ -635,9 +634,9 @@ def test_level1_not_run_vf2post_layout_when_vf2layout_is_perfect_control_flow(se
         _ = transpile(qc, target, optimization_level=1, callback=self.callback)
         self.assertIn("TrivialLayout", self.passes)
         self.assertIn("VF2Layout", self.passes)
-        self.assertNotIn("DenseLayout", self.passes)
+        self.assertNotIn("SabreLayout", self.passes)
         self.assertNotIn("VF2PostLayout", self.passes)
-        self.assertNotIn("StochasticSwap", self.passes)
+        self.assertNotIn("SabreSwap", self.passes)
 
 
 @ddt