Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make all Layout and Routing passes target aware #9263

Merged
merged 30 commits into from
Apr 6, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ed529f1
Make all Layout and Routing passes target aware
mtreinish Dec 7, 2022
9a1e49b
Update pass manager drawer reference dot files
mtreinish Dec 7, 2022
9a247bb
Merge remote-tracking branch 'origin/main' into target-for-layout-rou…
mtreinish Dec 8, 2022
9b0c174
Merge remote-tracking branch 'origin/main' into target-for-layout-rou…
mtreinish Jan 3, 2023
5484e3c
Fix lint
mtreinish Jan 3, 2023
8d979c9
Optimize CheckMap
mtreinish Jan 4, 2023
1f244b8
Fix test failures
mtreinish Jan 4, 2023
d2e7135
Merge branch 'main' into target-for-layout-routing
mtreinish Jan 6, 2023
f9b04ca
Update qiskit/transpiler/passes/layout/full_ancilla_allocation.py
mtreinish Jan 18, 2023
be3ec18
Merge remote-tracking branch 'origin/main' into target-for-layout-rou…
mtreinish Jan 18, 2023
1ed4a68
Add test coverage for passes not run in preset pass managers
mtreinish Jan 18, 2023
26f71be
Merge branch 'main' into target-for-layout-routing
mtreinish Jan 18, 2023
7505c24
Merge remote-tracking branch 'origin/main' into target-for-layout-rou…
mtreinish Jan 19, 2023
86b00f0
Fix lint
mtreinish Jan 19, 2023
109e4bd
Merge remote-tracking branch 'origin/main' into target-for-layout-rou…
mtreinish Feb 20, 2023
5c081c2
Fix reference dot for new preset pass managers
mtreinish Feb 20, 2023
737b6d8
Rework arguments to take CouplingMap or Target
mtreinish Apr 3, 2023
12f3ae6
Expand test coverage
mtreinish Apr 3, 2023
bd1c69f
Merge remote-tracking branch 'origin/main' into target-for-layout-rou…
mtreinish Apr 3, 2023
4077598
Merge branch 'main' into target-for-layout-routing
mtreinish Apr 3, 2023
9c8fbe3
Small typo fixes from review
mtreinish Apr 3, 2023
27434c3
Fix lint and test failure
mtreinish Apr 3, 2023
a16bbfe
Update qiskit/transpiler/preset_passmanagers/level0.py
mtreinish Apr 4, 2023
eb12311
Merge branch 'main' into target-for-layout-routing
mtreinish Apr 4, 2023
99c7886
Update logic to favor target in all optimization levels
mtreinish Apr 4, 2023
acc47a0
Merge branch 'main' into target-for-layout-routing
mtreinish Apr 6, 2023
4aa28dc
Update release note for api changes in earlier iterations
mtreinish Apr 6, 2023
87d0d40
Update docstrings
mtreinish Apr 6, 2023
e0612d1
Use a single positional argument for embed pm function
mtreinish Apr 6, 2023
f18259d
Merge branch 'main' into target-for-layout-routing
mtreinish Apr 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion qiskit/transpiler/passes/layout/csp_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@ class CSPLayout(AnalysisPass):
"""If possible, chooses a Layout as a CSP, using backtracking."""

def __init__(
self, coupling_map, strict_direction=False, seed=None, call_limit=1000, time_limit=10
self,
coupling_map=None,
strict_direction=False,
seed=None,
call_limit=1000,
time_limit=10,
target=None,
):
"""If possible, chooses a Layout as a CSP, using backtracking.

Expand All @@ -50,13 +56,19 @@ def __init__(
None means no call limit. Default: 1000.
time_limit (int): Amount of seconds that the pass will try to find a solution.
None means no time limit. Default: 10 seconds.
target (Target): A target representing the target backend, if both
``coupling_map`` and this are specified then this argument will take
precedence and ``coupling_map`` will be ignored.
"""
super().__init__()
self.coupling_map = coupling_map
self.strict_direction = strict_direction
self.call_limit = call_limit
self.time_limit = time_limit
self.seed = seed
self.target = target
if self.target is not None:
self.coupling_map = self.target.build_coupling_map()

def run(self, dag):
"""run the layout method"""
Expand Down
10 changes: 8 additions & 2 deletions qiskit/transpiler/passes/layout/full_ancilla_allocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,21 @@ class FullAncillaAllocation(AnalysisPass):
circuit.
"""

def __init__(self, coupling_map):
"""FullAncillaAllocation initializer.
def __init__(self, coupling_map=None, target=None):
"""FullAncillaAllocation initializer.<F12>

Args:
coupling_map (Coupling): directed graph representing a coupling map.
target (Target): A target representing the target backend, if both
``coupling_map`` and this are specified then this argument will take
precedence and ``coupling_map`` will be ignored.
"""
super().__init__()
self.coupling_map = coupling_map
self.ancilla_name = "ancilla"
self.target = target
if self.target is not None:
self.coupling_map = self.target.build_coupling_map()

def run(self, dag):
"""Run the FullAncillaAllocation pass on `dag`.
Expand Down
9 changes: 8 additions & 1 deletion qiskit/transpiler/passes/layout/layout_2q_distance.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,23 @@ class Layout2qDistance(AnalysisPass):
No CX direction is considered.
"""

def __init__(self, coupling_map, property_name="layout_score"):
def __init__(self, coupling_map=None, property_name="layout_score", target=None):
"""Layout2qDistance initializer.

Args:
coupling_map (CouplingMap): Directed graph represented a coupling map.
property_name (str): The property name to save the score. Default: layout_score
target (Target): A target representing the target backend, if both
``coupling_map`` and this are specified then this argument will take
precedence and ``coupling_map`` will be ignored.

"""
super().__init__()
self.coupling_map = coupling_map
self.property_name = property_name
self.target = target
if self.target is not None:
self.coupling_map = self.target.build_coupling_map()

def run(self, dag):
"""
Expand Down
9 changes: 8 additions & 1 deletion qiskit/transpiler/passes/layout/noise_adaptive_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from qiskit.transpiler.layout import Layout
from qiskit.transpiler.basepasses import AnalysisPass
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.target import target_to_backend_properties


class NoiseAdaptiveLayout(AnalysisPass):
Expand Down Expand Up @@ -54,11 +55,14 @@ class NoiseAdaptiveLayout(AnalysisPass):
by being set in `property_set`.
"""

def __init__(self, backend_prop):
def __init__(self, backend_prop=None, target=None):
"""NoiseAdaptiveLayout initializer.

Args:
backend_prop (BackendProperties): backend properties object
target (Target): A target representing the target backend, if both
``backend_prop`` and this are specified then this argument will take
precedence and ``coupling_map`` will be ignored.

Raises:
TranspilerError: if invalid options
Expand All @@ -77,6 +81,9 @@ def __init__(self, backend_prop):
self.qarg_to_id = {}
self.pending_program_edges = []
self.prog2hw = {}
self.target = target
if self.target is not None:
self.backend_prop = target_to_backend_properties(self.target)

def _initialize_backend_prop(self):
"""Extract readout and CNOT errors and compute swap costs."""
Expand Down
24 changes: 15 additions & 9 deletions qiskit/transpiler/passes/layout/sabre_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def __init__(
seed=None,
max_iterations=3,
swap_trials=None,
target=None,
layout_trials=None,
skip_routing=False,
):
Expand All @@ -106,6 +107,9 @@ def __init__(
on the number of trials run. This option is mutually exclusive
with the ``routing_pass`` argument and an error will be raised
if both are used.
target (Target): A target representing the target backend, if both
``coupling_map`` and this are specified then this argument will take
precedence and ``coupling_map`` will be ignored.
layout_trials (int): The number of random seed trials to run
layout with. When > 1 the trial that resuls in the output with
the fewest swap gates will be selected. If this is not specified
Expand All @@ -126,21 +130,15 @@ def __init__(
super().__init__()
self.coupling_map = coupling_map
self._neighbor_table = None
if self.coupling_map is not None:
if not self.coupling_map.is_symmetric:
# deepcopy is needed here to avoid modifications updating
# shared references in passes which require directional
# constraints
self.coupling_map = copy.deepcopy(self.coupling_map)
self.coupling_map.make_symmetric()
self._neighbor_table = NeighborTable(rx.adjacency_matrix(self.coupling_map.graph))

if routing_pass is not None and (swap_trials is not None or layout_trials is not None):
raise TranspilerError("Both routing_pass and swap_trials can't be set at the same time")
self.routing_pass = routing_pass
self.seed = seed
self.max_iterations = max_iterations
self.trials = swap_trials
self.target = target
if self.target is not None:
self.coupling_map = self.target.build_coupling_map()
if swap_trials is None:
self.swap_trials = CPU_COUNT
else:
Expand All @@ -150,6 +148,14 @@ def __init__(
else:
self.layout_trials = layout_trials
self.skip_routing = skip_routing
if self.coupling_map is not None:
if not self.coupling_map.is_symmetric:
# deepcopy is needed here to avoid modifications updating
# shared references in passes which require directional
# constraints
self.coupling_map = copy.deepcopy(self.coupling_map)
self.coupling_map.make_symmetric()
self._neighbor_table = NeighborTable(rx.adjacency_matrix(self.coupling_map.graph))

def run(self, dag):
"""Run the SabreLayout pass on `dag`.
Expand Down
16 changes: 12 additions & 4 deletions qiskit/transpiler/passes/layout/trivial_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,21 @@ class TrivialLayout(AnalysisPass):
Does not assume any ancilla.
"""

def __init__(self, coupling_map):
def __init__(self, coupling_map=None, target=None):
"""TrivialLayout initializer.

Args:
coupling_map (Coupling): directed graph representing a coupling map.
target (Target): A target representing the target backend, if both
``coupling_map`` and this are specified then this argument will take
precedence and ``coupling_map`` will be ignored.

Raises:
TranspilerError: if invalid options
"""
super().__init__()
self.coupling_map = coupling_map
self.target = target

def run(self, dag):
"""Run the TrivialLayout pass on `dag`.
Expand All @@ -48,10 +52,14 @@ def run(self, dag):
dag (DAGCircuit): DAG to find layout for.

Raises:
TranspilerError: if dag wider than self.coupling_map
TranspilerError: if dag wider than the target backend
"""
if dag.num_qubits() > self.coupling_map.size():
raise TranspilerError("Number of qubits greater than device.")
if self.target is not None:
if dag.num_qubits() > self.target.num_qubits:
raise TranspilerError("Number of qubits greater than device.")
else:
if dag.num_qubits() > self.coupling_map.size():
raise TranspilerError("Number of qubits greater than device.")
self.property_set["layout"] = Layout.generate_trivial_layout(
*(dag.qubits + list(dag.qregs.values()))
)
8 changes: 7 additions & 1 deletion qiskit/transpiler/passes/routing/basic_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,23 @@ class BasicSwap(TransformationPass):
one or more swaps in front to make it compatible.
"""

def __init__(self, coupling_map, fake_run=False):
def __init__(self, coupling_map=None, fake_run=False, target=None):
"""BasicSwap initializer.

Args:
coupling_map (CouplingMap): Directed graph represented a coupling map.
fake_run (bool): if true, it only pretend to do routing, i.e., no
swap is effectively added.
target (Target): A target representing the target backend, if both
``coupling_map`` and this are specified then this argument will take
precedence and ``coupling_map`` will be ignored.
"""
super().__init__()
self.coupling_map = coupling_map
self.fake_run = fake_run
self.target = target
if self.target is not None:
self.coupling_map = self.target.build_coupling_map()

def run(self, dag):
"""Run the BasicSwap pass on `dag`.
Expand Down
16 changes: 13 additions & 3 deletions qiskit/transpiler/passes/routing/bip_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from qiskit.transpiler import TransformationPass
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.passes.routing.algorithms.bip_model import BIPMappingModel
from qiskit.transpiler.target import target_to_backend_properties

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -64,7 +65,7 @@ class BIPMapping(TransformationPass):

def __init__(
self,
coupling_map,
coupling_map=None,
qubit_subset=None,
objective="balanced",
backend_prop=None,
Expand All @@ -73,6 +74,7 @@ def __init__(
max_swaps_inbetween_layers=None,
depth_obj_weight=0.1,
default_cx_error_rate=5e-3,
target=None,
):
"""BIPMapping initializer.

Expand Down Expand Up @@ -106,6 +108,9 @@ def __init__(

default_cx_error_rate (float):
Default CX error rate to be used if backend_prop is not available.
target (Target): A target representing the target backend, if both
``coupling_map`` or ``backend_prop`` and this are specified then this argument will take
precedence and the other argument will be ignored.

Raises:
MissingOptionalLibraryError: if cplex or docplex are not installed.
Expand All @@ -114,15 +119,20 @@ def __init__(
super().__init__()
self.coupling_map = coupling_map
self.qubit_subset = qubit_subset
if self.coupling_map is not None and self.qubit_subset is None:
self.qubit_subset = list(range(self.coupling_map.size()))
self.objective = objective
self.backend_prop = backend_prop
self.time_limit = time_limit
self.threads = threads
self.max_swaps_inbetween_layers = max_swaps_inbetween_layers
self.depth_obj_weight = depth_obj_weight
self.default_cx_error_rate = default_cx_error_rate
self.target = target
if self.target is not None:
self.coupling_map = self.target.build_coupling_map()
self.backend_prop = target_to_backend_properties(self.target)

if self.coupling_map is not None and self.qubit_subset is None:
self.qubit_subset = list(range(self.coupling_map.size()))

def run(self, dag):
"""Run the BIPMapping pass on `dag`, assuming the number of virtual qubits (defined in
Expand Down
15 changes: 10 additions & 5 deletions qiskit/transpiler/passes/routing/layout_transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def __init__(
to_layout: Union[Layout, str],
seed: Union[int, np.random.default_rng] = None,
trials=4,
target=None,
):
"""LayoutTransformation initializer.

Expand All @@ -55,16 +56,20 @@ def __init__(

trials (int):
How many randomized trials to perform, taking the best circuit as output.
target (Target): A target representing the target backend, if both
``coupling_map`` and this are specified then this argument will take
precedence and the other argument will be ignored.
"""
super().__init__()
self.from_layout = from_layout
self.to_layout = to_layout
if coupling_map:
self.coupling_map = coupling_map
graph = coupling_map.graph.to_undirected()
else:
self.coupling_map = coupling_map
self.target = target
if self.target is not None:
self.coupling_map = target.build_coupling_map()
if self.coupling_map is None:
self.coupling_map = CouplingMap.from_full(len(to_layout))
graph = self.coupling_map.graph.to_undirected()
graph = self.coupling_map.graph.to_undirected()
self.token_swapper = ApproximateTokenSwapper(graph, seed)
self.trials = trials

Expand Down
8 changes: 7 additions & 1 deletion qiskit/transpiler/passes/routing/lookahead_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class LookaheadSwap(TransformationPass):
https://medium.com/qiskit/improving-a-quantum-compiler-48410d7a7084
"""

def __init__(self, coupling_map, search_depth=4, search_width=4, fake_run=False):
def __init__(self, coupling_map, search_depth=4, search_width=4, fake_run=False, target=None):
"""LookaheadSwap initializer.

Args:
Expand All @@ -90,13 +90,19 @@ def __init__(self, coupling_map, search_depth=4, search_width=4, fake_run=False)
search_width (int): lookahead tree width when ranking best SWAP options.
fake_run (bool): if true, it only pretend to do routing, i.e., no
swap is effectively added.
target (Target): A target representing the target backend, if both
``coupling_map`` and this are specified then this argument will take
precedence and the other argument will be ignored.
"""

super().__init__()
self.coupling_map = coupling_map
self.search_depth = search_depth
self.search_width = search_width
self.fake_run = fake_run
self.target = target
if self.target is not None:
self.coupling_map = self.target.build_coupling_map()

def run(self, dag):
"""Run the LookaheadSwap pass on `dag`.
Expand Down
15 changes: 11 additions & 4 deletions qiskit/transpiler/passes/routing/sabre_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ class SabreSwap(TransformationPass):
`arXiv:1809.02573 <https://arxiv.org/pdf/1809.02573.pdf>`_
"""

def __init__(self, coupling_map, heuristic="basic", seed=None, fake_run=False, trials=None):
def __init__(
self, coupling_map, heuristic="basic", seed=None, fake_run=False, trials=None, target=None
):
r"""SabreSwap initializer.

Args:
Expand All @@ -88,6 +90,9 @@ def __init__(self, coupling_map, heuristic="basic", seed=None, fake_run=False, t
CPUs on the local system. For reproducible results it is recommended
that you set this explicitly, as the output will be deterministic for
a fixed number of trials.
target (Target): A target representing the target backend, if both
``coupling_map`` and this are specified then this argument will take
precedence and the other argument will be ignored.

Raises:
TranspilerError: If the specified heuristic is not valid.
Expand Down Expand Up @@ -139,9 +144,11 @@ def __init__(self, coupling_map, heuristic="basic", seed=None, fake_run=False, t
super().__init__()

# Assume bidirectional couplings, fixing gate direction is easy later.
if coupling_map is None or coupling_map.is_symmetric:
self.coupling_map = coupling_map
else:
self.coupling_map = coupling_map
self.target = target
if self.target is not None:
self.coupling_map = self.target.build_coupling_map()
if self.coupling_map is not None and not self.coupling_map.is_symmetric:
# A deepcopy is needed here to avoid modifications updating
# shared references in passes which require directional
# constraints
Expand Down
Loading