diff --git a/qiskit/transpiler/passes/routing/sabre_swap.py b/qiskit/transpiler/passes/routing/sabre_swap.py index 114f6d577898..a2d749b69b9c 100644 --- a/qiskit/transpiler/passes/routing/sabre_swap.py +++ b/qiskit/transpiler/passes/routing/sabre_swap.py @@ -150,14 +150,7 @@ def __init__(self, coupling_map, heuristic="basic", seed=None, fake_run=False, t if coupling_map is not None: self._neighbor_table = NeighborTable(retworkx.adjacency_matrix(self.coupling_map.graph)) - if heuristic == "basic": - self.heuristic = Heuristic.Basic - elif heuristic == "lookahead": - self.heuristic = Heuristic.Lookahead - elif heuristic == "decay": - self.heuristic = Heuristic.Decay - else: - raise TranspilerError("Heuristic %s not recognized." % heuristic) + self.heuristic = heuristic if seed is None: ii32 = np.iinfo(np.int32) @@ -191,6 +184,15 @@ def run(self, dag): if len(dag.qubits) > self.coupling_map.size(): raise TranspilerError("More virtual qubits exist than physical.") + if self.heuristic == "basic": + heuristic = Heuristic.Basic + elif self.heuristic == "lookahead": + heuristic = Heuristic.Lookahead + elif self.heuristic == "decay": + heuristic = Heuristic.Decay + else: + raise TranspilerError("Heuristic %s not recognized." % self.heuristic) + self.dist_matrix = self.coupling_map.distance_matrix # Preserve input DAG's name, regs, wire_map, etc. but replace the graph. @@ -229,7 +231,7 @@ def run(self, dag): sabre_dag, self._neighbor_table, self.dist_matrix, - self.heuristic, + heuristic, self.seed, layout, self.trials, diff --git a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py index d3bce75e11bb..ba4c1266ce68 100644 --- a/qiskit/transpiler/preset_passmanagers/builtin_plugins.py +++ b/qiskit/transpiler/preset_passmanagers/builtin_plugins.py @@ -218,7 +218,7 @@ def pass_manager(self, pass_manager_config, optimization_level=None) -> PassMana ) if optimization_level == 1: routing_pass = SabreSwap( - coupling_map, heuristic="lookahead", seed=seed_transpiler, trials=5 + coupling_map, heuristic="decay", seed=seed_transpiler, trials=5 ) return common.generate_routing_passmanager( routing_pass, diff --git a/qiskit/transpiler/preset_passmanagers/level1.py b/qiskit/transpiler/preset_passmanagers/level1.py index 48a798b62827..8d1f44bbca99 100644 --- a/qiskit/transpiler/preset_passmanagers/level1.py +++ b/qiskit/transpiler/preset_passmanagers/level1.py @@ -71,9 +71,9 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa inst_map = pass_manager_config.inst_map coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout - layout_method = pass_manager_config.layout_method or "dense" init_method = pass_manager_config.init_method - routing_method = pass_manager_config.routing_method or "stochastic" + 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 diff --git a/qiskit/transpiler/preset_passmanagers/level2.py b/qiskit/transpiler/preset_passmanagers/level2.py index d1599fd0bc9f..a8f193883c7a 100644 --- a/qiskit/transpiler/preset_passmanagers/level2.py +++ b/qiskit/transpiler/preset_passmanagers/level2.py @@ -74,8 +74,8 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> StagedPassMa coupling_map = pass_manager_config.coupling_map initial_layout = pass_manager_config.initial_layout init_method = pass_manager_config.init_method - layout_method = pass_manager_config.layout_method or "dense" - routing_method = pass_manager_config.routing_method or "stochastic" + 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 diff --git a/releasenotes/notes/sabres-for-everyone-3148ccf2064ccb0d.yaml b/releasenotes/notes/sabres-for-everyone-3148ccf2064ccb0d.yaml new file mode 100644 index 000000000000..2be30c7a290e --- /dev/null +++ b/releasenotes/notes/sabres-for-everyone-3148ccf2064ccb0d.yaml @@ -0,0 +1,17 @@ +--- +upgrade: + - | + The preset pass managers for levels 1 and 2, which will be used when + ``optimization_level=1`` or ``optimization_level=2`` with + :func:`~.transpile` or :func:`~.generate_preset_pass_manager` and output + from :func:`~.level_1_pass_manager` and :func:`~.level_2_pass_manager`, + will now use :class:`~.SabreLayout` and :func:`~SabreSwap` by default + instead of the previous defaults :class:`~.DenseLayout` and + :class:`~.StochasticSwap`. This change was made to improve the output + quality of the transpiler, the :class:`~.SabreLayout` and + :func:`~SabreSwap` combination typically results in fewer + :class:`~.SwapGate` objects being inserted into the output circuit. + If you would like to use the previous default passes you can set + ``layout_method='dense'`` and ``routing_method='stochastic'`` on + :func:`~.transpile` or :func:`~.generate_preset_pass_manager to + leverage :class:`~.DenseLayout` and :class:`~.StochasticSwap` respectively. diff --git a/src/sabre_swap/neighbor_table.rs b/src/sabre_swap/neighbor_table.rs index d59700ed6352..195875330cf6 100644 --- a/src/sabre_swap/neighbor_table.rs +++ b/src/sabre_swap/neighbor_table.rs @@ -36,38 +36,48 @@ pub struct NeighborTable { #[pymethods] impl NeighborTable { #[new] - pub fn new(adjacency_matrix: PyReadonlyArray2) -> Self { - let adj_mat = adjacency_matrix.as_array(); + pub fn new(adjacency_matrix: Option>) -> Self { let run_in_parallel = getenv_use_multiple_threads(); - let build_neighbors = |row: ArrayView1| -> Vec { - row.iter() - .enumerate() - .filter_map( - |(row_index, value)| { - if *value == 0. { - None - } else { - Some(row_index) - } - }, - ) - .collect() - }; - if run_in_parallel { - NeighborTable { - neighbors: adj_mat - .axis_iter(Axis(0)) - .into_par_iter() - .map(|row| build_neighbors(row)) - .collect(), - } - } else { - NeighborTable { - neighbors: adj_mat - .axis_iter(Axis(0)) - .map(|row| build_neighbors(row)) - .collect(), + let neighbors = match adjacency_matrix { + Some(adjacency_matrix) => { + let adj_mat = adjacency_matrix.as_array(); + let build_neighbors = |row: ArrayView1| -> Vec { + row.iter() + .enumerate() + .filter_map( + |(row_index, value)| { + if *value == 0. { + None + } else { + Some(row_index) + } + }, + ) + .collect() + }; + if run_in_parallel { + adj_mat + .axis_iter(Axis(0)) + .into_par_iter() + .map(|row| build_neighbors(row)) + .collect() + } else { + adj_mat + .axis_iter(Axis(0)) + .map(|row| build_neighbors(row)) + .collect() + } } - } + None => Vec::new(), + }; + NeighborTable { neighbors } + } + + fn __getstate__(&self) -> Vec> { + self.neighbors.clone() + } + + fn __setstate__(&mut self, state: Vec>) { + self.neighbors = state } } diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py index 40f5d59113dd..cae17c2227f4 100644 --- a/test/python/compiler/test_transpiler.py +++ b/test/python/compiler/test_transpiler.py @@ -716,7 +716,7 @@ def test_move_measurements(self): circ = QuantumCircuit.from_qasm_file(os.path.join(qasm_dir, "move_measurements.qasm")) lay = [0, 1, 15, 2, 14, 3, 13, 4, 12, 5, 11, 6] - out = transpile(circ, initial_layout=lay, coupling_map=cmap) + out = transpile(circ, initial_layout=lay, coupling_map=cmap, routing_method="stochastic") out_dag = circuit_to_dag(out) meas_nodes = out_dag.named_nodes("measure") for meas_node in meas_nodes: diff --git a/test/python/transpiler/test_preset_passmanagers.py b/test/python/transpiler/test_preset_passmanagers.py index e954932e2f1c..eadd82edb38a 100644 --- a/test/python/transpiler/test_preset_passmanagers.py +++ b/test/python/transpiler/test_preset_passmanagers.py @@ -701,29 +701,6 @@ def test_layout_tokyo_fully_connected_cx(self, level): 19: ancilla[14], } - dense_layout = { - 11: qr[0], - 6: qr[1], - 5: qr[2], - 10: qr[3], - 15: qr[4], - 0: ancilla[0], - 1: ancilla[1], - 2: ancilla[2], - 3: ancilla[3], - 4: ancilla[4], - 7: ancilla[5], - 8: ancilla[6], - 9: ancilla[7], - 12: ancilla[8], - 13: ancilla[9], - 14: ancilla[10], - 16: ancilla[11], - 17: ancilla[12], - 18: ancilla[13], - 19: ancilla[14], - } - sabre_layout = { 11: qr[0], 17: qr[1], @@ -748,8 +725,8 @@ def test_layout_tokyo_fully_connected_cx(self, level): } expected_layout_level0 = trivial_layout - expected_layout_level1 = dense_layout - expected_layout_level2 = dense_layout + expected_layout_level1 = sabre_layout + expected_layout_level2 = sabre_layout expected_layout_level3 = sabre_layout expected_layouts = [