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 all 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
18 changes: 15 additions & 3 deletions qiskit/transpiler/passes/layout/csp_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,20 @@
from qiskit.transpiler.basepasses import AnalysisPass
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.utils import optionals as _optionals
from qiskit.transpiler.target import Target


@_optionals.HAS_CONSTRAINT.require_in_instance
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,
strict_direction=False,
seed=None,
call_limit=1000,
time_limit=10,
):
"""If possible, chooses a Layout as a CSP, using backtracking.

Expand All @@ -42,7 +48,7 @@ def __init__(
* time limit reached: If no perfect layout was found and the time limit was reached.

Args:
coupling_map (Coupling): Directed graph representing a coupling map.
coupling_map (Union[CouplingMap, Target]): Directed graph representing a coupling map.
strict_direction (bool): If True, considers the direction of the coupling map.
Default is False.
seed (int): Sets the seed of the PRNG.
Expand All @@ -53,7 +59,13 @@ def __init__(
None means no time limit. Default: 10 seconds.
"""
super().__init__()
self.coupling_map = coupling_map
if isinstance(coupling_map, Target):
self.target = coupling_map
self.coupling_map = self.target.build_coupling_map()
else:
self.target = None
self.coupling_map = coupling_map

self.strict_direction = strict_direction
self.call_limit = call_limit
self.time_limit = time_limit
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 @@ -15,6 +15,7 @@
from qiskit.circuit import QuantumRegister
from qiskit.transpiler.basepasses import AnalysisPass
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.target import Target


class FullAncillaAllocation(AnalysisPass):
Expand All @@ -35,10 +36,15 @@ def __init__(self, coupling_map):
"""FullAncillaAllocation initializer.

Args:
coupling_map (Coupling): directed graph representing a coupling map.
coupling_map (Union[CouplingMap, Target]): directed graph representing a coupling map.
"""
super().__init__()
self.coupling_map = coupling_map
if isinstance(coupling_map, Target):
self.target = coupling_map
self.coupling_map = self.target.build_coupling_map()
else:
self.target = None
self.coupling_map = coupling_map
self.ancilla_name = "ancilla"

def run(self, dag):
Expand Down
10 changes: 8 additions & 2 deletions qiskit/transpiler/passes/layout/layout_2q_distance.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"""

from qiskit.transpiler.basepasses import AnalysisPass
from qiskit.transpiler.target import Target


class Layout2qDistance(AnalysisPass):
Expand All @@ -34,11 +35,16 @@ def __init__(self, coupling_map, property_name="layout_score"):
"""Layout2qDistance initializer.

Args:
coupling_map (CouplingMap): Directed graph represented a coupling map.
coupling_map (Union[CouplingMap, Target]): Directed graph represented a coupling map.
property_name (str): The property name to save the score. Default: layout_score
"""
super().__init__()
self.coupling_map = coupling_map
if isinstance(coupling_map, Target):
self.target = coupling_map
self.coupling_map = self.target.build_coupling_map()
else:
self.target = None
self.coupling_map = coupling_map
self.property_name = property_name

def run(self, dag):
Expand Down
10 changes: 8 additions & 2 deletions 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, Target


class NoiseAdaptiveLayout(AnalysisPass):
Expand Down Expand Up @@ -58,13 +59,18 @@ def __init__(self, backend_prop):
"""NoiseAdaptiveLayout initializer.

Args:
backend_prop (BackendProperties): backend properties object
backend_prop (Union[BackendProperties, Target]): backend properties object

Raises:
TranspilerError: if invalid options
"""
super().__init__()
self.backend_prop = backend_prop
if isinstance(backend_prop, Target):
self.target = backend_prop
self.backend_prop = target_to_backend_properties(self.target)
else:
self.target = None
self.backend_prop = backend_prop
self.swap_graph = rx.PyDiGraph()
self.cx_reliability = {}
self.readout_reliability = {}
Expand Down
27 changes: 16 additions & 11 deletions qiskit/transpiler/passes/layout/sabre_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
NeighborTable,
)
from qiskit.transpiler.passes.routing.sabre_swap import process_swaps, apply_gate
from qiskit.transpiler.target import Target
from qiskit.tools.parallel import CPU_COUNT

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -86,7 +87,7 @@ def __init__(
"""SabreLayout initializer.

Args:
coupling_map (Coupling): directed graph representing a coupling map.
coupling_map (Union[CouplingMap, Target]): directed graph representing a coupling map.
routing_pass (BasePass): the routing pass to use while iterating.
If specified this pass operates as an :class:`~.AnalysisPass` and
will only populate the ``layout`` field in the property set and
Expand Down Expand Up @@ -124,17 +125,13 @@ def __init__(
both ``routing_pass`` and ``layout_trials`` are specified
"""
super().__init__()
self.coupling_map = coupling_map
if isinstance(coupling_map, Target):
self.target = coupling_map
self.coupling_map = self.target.build_coupling_map()
else:
self.target = None
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
Expand All @@ -150,6 +147,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
19 changes: 14 additions & 5 deletions qiskit/transpiler/passes/layout/trivial_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,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


class TrivialLayout(AnalysisPass):
Expand All @@ -33,13 +34,18 @@ def __init__(self, coupling_map):
"""TrivialLayout initializer.

Args:
coupling_map (Coupling): directed graph representing a coupling map.
coupling_map (Union[CouplingMap, Target]): directed graph representing a coupling map.

Raises:
TranspilerError: if invalid options
"""
super().__init__()
self.coupling_map = coupling_map
if isinstance(coupling_map, Target):
self.target = coupling_map
self.coupling_map = self.target.build_coupling_map()
else:
self.target = None
self.coupling_map = coupling_map

def run(self, dag):
"""Run the TrivialLayout pass on `dag`.
Expand All @@ -48,15 +54,18 @@ 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 self.target is not None:
if dag.num_qubits() > self.target.num_qubits:
raise TranspilerError("Number of qubits greater than device.")
elif dag.num_qubits() > self.coupling_map.size():
raise TranspilerError("Number of qubits greater than device.")
if not self.coupling_map.is_connected():
raise TranspilerError(
"Coupling Map is disjoint, this pass can't be used with a disconnected coupling "
"map."
)
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()))
)
10 changes: 8 additions & 2 deletions qiskit/transpiler/passes/routing/basic_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from qiskit.dagcircuit import DAGCircuit
from qiskit.transpiler.layout import Layout
from qiskit.circuit.library.standard_gates import SwapGate
from qiskit.transpiler.target import Target


class BasicSwap(TransformationPass):
Expand All @@ -31,12 +32,17 @@ def __init__(self, coupling_map, fake_run=False):
"""BasicSwap initializer.

Args:
coupling_map (CouplingMap): Directed graph represented a coupling map.
coupling_map (Union[CouplingMap, Target]): Directed graph represented a coupling map.
fake_run (bool): if true, it only pretend to do routing, i.e., no
swap is effectively added.
"""
super().__init__()
self.coupling_map = coupling_map
if isinstance(coupling_map, Target):
self.target = coupling_map
self.coupling_map = self.target.build_coupling_map()
else:
self.target = None
self.coupling_map = coupling_map
self.fake_run = fake_run

def run(self, dag):
Expand Down
19 changes: 14 additions & 5 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, Target

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -77,7 +78,7 @@ def __init__(
"""BIPMapping initializer.

Args:
coupling_map (CouplingMap): Directed graph represented a coupling map.
coupling_map (Union[CouplingMap, Target]): Directed graph represented a coupling map.
qubit_subset (list[int]): Sublist of physical qubits to be used in the mapping.
If None, all qubits in the coupling_map will be considered.
objective (str): Type of objective function to be minimized:
Expand Down Expand Up @@ -112,17 +113,25 @@ def __init__(
TranspilerError: if invalid options are specified.
"""
super().__init__()
self.coupling_map = coupling_map
if isinstance(coupling_map, Target):
self.target = coupling_map
self.coupling_map = self.target.build_coupling_map()
self.backend_prop = target_to_backend_properties(self.target)
else:
self.target = None
self.coupling_map = coupling_map
self.backend_prop = None
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
if backend_prop is not None:
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
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
16 changes: 10 additions & 6 deletions qiskit/transpiler/passes/routing/layout_transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.passes.routing.algorithms import ApproximateTokenSwapper
from qiskit.transpiler.target import Target


class LayoutTransformation(TransformationPass):
Expand All @@ -30,7 +31,7 @@ class LayoutTransformation(TransformationPass):

def __init__(
self,
coupling_map: CouplingMap,
coupling_map: Union[CouplingMap, Target, None],
from_layout: Union[Layout, str],
to_layout: Union[Layout, str],
seed: Union[int, np.random.default_rng] = None,
Expand All @@ -39,7 +40,7 @@ def __init__(
"""LayoutTransformation initializer.

Args:
coupling_map (CouplingMap):
coupling_map:
Directed graph representing a coupling map.

from_layout (Union[Layout, str]):
Expand All @@ -59,12 +60,15 @@ def __init__(
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()
if isinstance(coupling_map, Target):
self.target = coupling_map
self.coupling_map = self.target.build_coupling_map()
else:
self.target = None
self.coupling_map = 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
10 changes: 8 additions & 2 deletions qiskit/transpiler/passes/routing/lookahead_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.layout import Layout
from qiskit.dagcircuit import DAGOpNode
from qiskit.transpiler.target import Target

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -85,15 +86,20 @@ def __init__(self, coupling_map, search_depth=4, search_width=4, fake_run=False)
"""LookaheadSwap initializer.

Args:
coupling_map (CouplingMap): CouplingMap of the target backend.
coupling_map (Union[CouplingMap, Target]): CouplingMap of the target backend.
search_depth (int): lookahead tree depth when ranking best SWAP options.
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.
"""

super().__init__()
self.coupling_map = coupling_map
if isinstance(coupling_map, Target):
self.target = coupling_map
self.coupling_map = self.target.build_coupling_map()
else:
self.target = None
self.coupling_map = coupling_map
self.search_depth = search_depth
self.search_width = search_width
self.fake_run = fake_run
Expand Down
Loading