Skip to content

Commit

Permalink
layout_method and routing_method selectors in transpile() (#3999)
Browse files Browse the repository at this point in the history
* add ability to select layout and routing method from top transpile function

* set lookahead width/depth in increasing order

* releasenote

* review comments

* lint

* lint

Co-authored-by: Kevin Krsulich <[email protected]>
  • Loading branch information
ajavadia and kdk authored Mar 30, 2020
1 parent 5c4914e commit a8dcbe1
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 42 deletions.
43 changes: 36 additions & 7 deletions qiskit/compiler/transpile.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# that they have been altered from the originals.

"""Circuit transpile function"""
import warnings
from typing import List, Union, Dict, Callable, Any, Optional, Tuple
from qiskit.circuit.quantumcircuit import QuantumCircuit
from qiskit.providers import BaseBackend
Expand Down Expand Up @@ -40,6 +41,8 @@ def transpile(circuits: Union[QuantumCircuit, List[QuantumCircuit]],
coupling_map: Optional[Union[CouplingMap, List[List[int]]]] = None,
backend_properties: Optional[BackendProperties] = None,
initial_layout: Optional[Union[Layout, Dict, List]] = None,
layout_method: Optional[str] = None,
routing_method: Optional[str] = None,
seed_transpiler: Optional[int] = None,
optimization_level: Optional[int] = None,
pass_manager: Optional[PassManager] = None,
Expand Down Expand Up @@ -108,6 +111,10 @@ def transpile(circuits: Union[QuantumCircuit, List[QuantumCircuit]],
[qr[0], None, None, qr[1], None, qr[2]]
layout_method: Name of layout selection pass ('trivial', 'dense', 'noise_adaptive')
Sometimes a perfect layout can be available in which case the layout_method
may not run.
routing_method: Name of routing pass ('basic', 'lookahead', 'stochastic')
seed_transpiler: Sets random seed for the stochastic parts of the transpiler
optimization_level: How much optimization to perform on the circuits.
Higher levels generate more optimized circuits,
Expand Down Expand Up @@ -170,6 +177,7 @@ def callback_func(**kwargs):
circuits = circuits if isinstance(circuits, list) else [circuits]
transpile_args = _parse_transpile_args(circuits, backend, basis_gates, coupling_map,
backend_properties, initial_layout,
layout_method, routing_method,
seed_transpiler, optimization_level,
pass_manager, callback, output_name)
# Check circuit width against number of qubits in coupling_map(s)
Expand Down Expand Up @@ -263,7 +271,8 @@ def _transpile_circuit(circuit_config_tuple: Tuple[QuantumCircuit, Dict]) -> Qua

def _parse_transpile_args(circuits, backend,
basis_gates, coupling_map, backend_properties,
initial_layout, seed_transpiler, optimization_level,
initial_layout, layout_method, routing_method,
seed_transpiler, optimization_level,
pass_manager, callback, output_name) -> List[Dict]:
"""Resolve the various types of args allowed to the transpile() function through
duck typing, overriding args, etc. Refer to the transpile() docstring for details on
Expand All @@ -276,6 +285,9 @@ def _parse_transpile_args(circuits, backend,
Returns:
list[dicts]: a list of transpile parameters.
"""
if initial_layout is not None and layout_method is not None:
warnings.warn("initial_layout provided; layout_method is ignored.",
UserWarning)
# Each arg could be single or a list. If list, it must be the same size as
# number of circuits. If single, duplicate to create a list of that size.
num_circuits = len(circuits)
Expand All @@ -284,6 +296,8 @@ def _parse_transpile_args(circuits, backend,
coupling_map = _parse_coupling_map(coupling_map, backend, num_circuits)
backend_properties = _parse_backend_properties(backend_properties, backend, num_circuits)
initial_layout = _parse_initial_layout(initial_layout, circuits)
layout_method = _parse_layout_method(layout_method, num_circuits)
routing_method = _parse_routing_method(routing_method, num_circuits)
seed_transpiler = _parse_seed_transpiler(seed_transpiler, num_circuits)
optimization_level = _parse_optimization_level(optimization_level, num_circuits)
pass_manager = _parse_pass_manager(pass_manager, num_circuits)
Expand All @@ -292,17 +306,20 @@ def _parse_transpile_args(circuits, backend,

list_transpile_args = []
for args in zip(basis_gates, coupling_map, backend_properties,
initial_layout, seed_transpiler, optimization_level,
initial_layout, layout_method, routing_method,
seed_transpiler, optimization_level,
pass_manager, output_name, callback):
transpile_args = {'pass_manager_config': PassManagerConfig(basis_gates=args[0],
coupling_map=args[1],
backend_properties=args[2],
initial_layout=args[3],
seed_transpiler=args[4]),
'optimization_level': args[5],
'pass_manager': args[6],
'output_name': args[7],
'callback': args[8]}
layout_method=args[4],
routing_method=args[5],
seed_transpiler=args[6]),
'optimization_level': args[7],
'pass_manager': args[8],
'output_name': args[9],
'callback': args[10]}
list_transpile_args.append(transpile_args)

return list_transpile_args
Expand Down Expand Up @@ -388,6 +405,18 @@ def _layout_from_raw(initial_layout, circuit):
return initial_layout


def _parse_layout_method(layout_method, num_circuits):
if not isinstance(layout_method, list):
layout_method = [layout_method] * num_circuits
return layout_method


def _parse_routing_method(routing_method, num_circuits):
if not isinstance(routing_method, list):
routing_method = [routing_method] * num_circuits
return routing_method


def _parse_seed_transpiler(seed_transpiler, num_circuits):
if not isinstance(seed_transpiler, list):
seed_transpiler = [seed_transpiler] * num_circuits
Expand Down
22 changes: 9 additions & 13 deletions qiskit/transpiler/pass_manager_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2019.
# (C) Copyright IBM 2017, 2020.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
Expand All @@ -17,24 +17,14 @@

class PassManagerConfig:
"""Pass Manager Configuration.
Attributes:
initial_layout (Layout): Initial position of virtual qubits on physical
qubits.
basis_gates (list): List of basis gate names to unroll to.
coupling_map (CouplingMap): Directed graph represented a coupling map.
backend_properties (BackendProperties): Properties returned by a
backend, including
information on gate errors, readout errors, qubit coherence times,
etc.
seed_transpiler (int): Sets random seed for the stochastic parts of the
transpiler.
"""

def __init__(self,
initial_layout=None,
basis_gates=None,
coupling_map=None,
layout_method=None,
routing_method=None,
backend_properties=None,
seed_transpiler=None):
"""Initialize a PassManagerConfig object
Expand All @@ -45,6 +35,10 @@ def __init__(self,
basis_gates (list): List of basis gate names to unroll to.
coupling_map (CouplingMap): Directed graph represented a coupling
map.
layout_method (str): the pass to use for choosing initial qubit
placement.
routing_method (str): the pass to use for routing qubits on the
architecture.
backend_properties (BackendProperties): Properties returned by a
backend, including information on gate errors, readout errors,
qubit coherence times, etc.
Expand All @@ -54,5 +48,7 @@ def __init__(self,
self.initial_layout = initial_layout
self.basis_gates = basis_gates
self.coupling_map = coupling_map
self.layout_method = layout_method
self.routing_method = routing_method
self.backend_properties = backend_properties
self.seed_transpiler = seed_transpiler
34 changes: 30 additions & 4 deletions qiskit/transpiler/preset_passmanagers/level0.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,20 @@
from qiskit.transpiler.passes import CXDirection
from qiskit.transpiler.passes import SetLayout
from qiskit.transpiler.passes import TrivialLayout
from qiskit.transpiler.passes import DenseLayout
from qiskit.transpiler.passes import NoiseAdaptiveLayout
from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements
from qiskit.transpiler.passes import BasicSwap
from qiskit.transpiler.passes import LookaheadSwap
from qiskit.transpiler.passes import StochasticSwap
from qiskit.transpiler.passes import FullAncillaAllocation
from qiskit.transpiler.passes import EnlargeWithAncilla
from qiskit.transpiler.passes import RemoveResetInZeroState
from qiskit.transpiler.passes import ApplyLayout
from qiskit.transpiler.passes import CheckCXDirection

from qiskit.transpiler import TranspilerError


def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
"""Level 0 pass manager: no explicit optimization other than mapping to backend.
Expand All @@ -54,19 +60,32 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
Returns:
a level 0 pass manager.
Raises:
TranspilerError: if the passmanager config is invalid.
"""
basis_gates = pass_manager_config.basis_gates
coupling_map = pass_manager_config.coupling_map
initial_layout = pass_manager_config.initial_layout
layout_method = pass_manager_config.layout_method or 'trivial'
routing_method = pass_manager_config.routing_method or 'stochastic'
seed_transpiler = pass_manager_config.seed_transpiler
backend_properties = pass_manager_config.backend_properties

# 1. Use trivial layout if no layout given
# 1. Choose an initial layout if not set by user (default: trivial layout)
_given_layout = SetLayout(initial_layout)

def _choose_layout_condition(property_set):
return not property_set['layout']

_choose_layout = TrivialLayout(coupling_map)
if layout_method == 'trivial':
_choose_layout = TrivialLayout(coupling_map)
elif layout_method == 'dense':
_choose_layout = DenseLayout(coupling_map, backend_properties)
elif layout_method == 'noise_adaptive':
_choose_layout = NoiseAdaptiveLayout(backend_properties)
else:
raise TranspilerError("Invalid layout method %s." % layout_method)

# 2. Extend dag/layout with ancillas using the full coupling map
_embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()]
Expand All @@ -80,8 +99,15 @@ def _choose_layout_condition(property_set):
def _swap_condition(property_set):
return not property_set['is_swap_mapped']

_swap = [BarrierBeforeFinalMeasurements(),
StochasticSwap(coupling_map, trials=20, seed=seed_transpiler)]
_swap = [BarrierBeforeFinalMeasurements()]
if routing_method == 'basic':
_swap += [BasicSwap(coupling_map)]
elif routing_method == 'stochastic':
_swap += [StochasticSwap(coupling_map, trials=20, seed=seed_transpiler)]
elif routing_method == 'lookahead':
_swap += [LookaheadSwap(coupling_map, search_depth=2, search_width=2)]
else:
raise TranspilerError("Invalid routing method %s." % routing_method)

# 5. Unroll to the basis
_unroll = Unroller(basis_gates)
Expand Down
41 changes: 33 additions & 8 deletions qiskit/transpiler/preset_passmanagers/level1.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@
from qiskit.transpiler.passes import CXDirection
from qiskit.transpiler.passes import SetLayout
from qiskit.transpiler.passes import TrivialLayout
from qiskit.transpiler.passes import DenseLayout
from qiskit.transpiler.passes import NoiseAdaptiveLayout
from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements
from qiskit.transpiler.passes import BasicSwap
from qiskit.transpiler.passes import LookaheadSwap
from qiskit.transpiler.passes import StochasticSwap
from qiskit.transpiler.passes import FullAncillaAllocation
from qiskit.transpiler.passes import EnlargeWithAncilla
Expand All @@ -38,16 +42,18 @@
from qiskit.transpiler.passes import ApplyLayout
from qiskit.transpiler.passes import CheckCXDirection
from qiskit.transpiler.passes import Layout2qDistance
from qiskit.transpiler.passes import DenseLayout

from qiskit.transpiler import TranspilerError


def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
"""Level 1 pass manager: light optimization by simple adjacent gate collapsing.
This pass manager applies the user-given initial layout. If none is given, and a trivial
layout (i-th virtual -> i-th physical) makes the circuit fit the coupling map, that is used.
Otherwise, the circuit is mapped to the most densely connected coupling subgraph, and swaps
are inserted to map. Any unused physical qubit is allocated as ancilla space.
This pass manager applies the user-given initial layout. If none is given,
and a trivial layout (i-th virtual -> i-th physical) makes the circuit fit
the coupling map, that is used.
Otherwise, the circuit is mapped to the most densely connected coupling subgraph,
and swaps are inserted to map. Any unused physical qubit is allocated as ancilla space.
The pass manager then unrolls the circuit to the desired basis, and transforms the
circuit to match the coupling map. Finally, optimizations in the form of adjacent
gate collapse and redundant reset removal are performed.
Expand All @@ -61,10 +67,15 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
Returns:
a level 1 pass manager.
Raises:
TranspilerError: if the passmanager config is invalid.
"""
basis_gates = pass_manager_config.basis_gates
coupling_map = pass_manager_config.coupling_map
initial_layout = pass_manager_config.initial_layout
layout_method = pass_manager_config.layout_method or 'dense'
routing_method = pass_manager_config.routing_method or 'stochastic'
seed_transpiler = pass_manager_config.seed_transpiler
backend_properties = pass_manager_config.backend_properties

Expand All @@ -79,7 +90,14 @@ def _choose_layout_condition(property_set):
return not property_set['layout']

# 2. Use a better layout on densely connected qubits, if circuit needs swaps
_improve_layout = DenseLayout(coupling_map, backend_properties)
if layout_method == 'trivial':
_improve_layout = TrivialLayout(coupling_map)
elif layout_method == 'dense':
_improve_layout = DenseLayout(coupling_map, backend_properties)
elif layout_method == 'noise_adaptive':
_improve_layout = NoiseAdaptiveLayout(backend_properties)
else:
raise TranspilerError("Invalid layout method %s." % layout_method)

def _not_perfect_yet(property_set):
return property_set['trivial_layout_score'] is not None and \
Expand All @@ -97,8 +115,15 @@ def _not_perfect_yet(property_set):
def _swap_condition(property_set):
return not property_set['is_swap_mapped']

_swap = [BarrierBeforeFinalMeasurements(),
StochasticSwap(coupling_map, trials=20, seed=seed_transpiler)]
_swap = [BarrierBeforeFinalMeasurements()]
if routing_method == 'basic':
_swap += [BasicSwap(coupling_map)]
elif routing_method == 'stochastic':
_swap += [StochasticSwap(coupling_map, trials=50, seed=seed_transpiler)]
elif routing_method == 'lookahead':
_swap += [LookaheadSwap(coupling_map, search_depth=4, search_width=4)]
else:
raise TranspilerError("Invalid routing method %s." % routing_method)

# 6. Unroll to the basis
_unroll = Unroller(basis_gates)
Expand Down
33 changes: 29 additions & 4 deletions qiskit/transpiler/preset_passmanagers/level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,13 @@
from qiskit.transpiler.passes import CheckMap
from qiskit.transpiler.passes import CXDirection
from qiskit.transpiler.passes import SetLayout
from qiskit.transpiler.passes import DenseLayout
from qiskit.transpiler.passes import CSPLayout
from qiskit.transpiler.passes import TrivialLayout
from qiskit.transpiler.passes import DenseLayout
from qiskit.transpiler.passes import NoiseAdaptiveLayout
from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements
from qiskit.transpiler.passes import BasicSwap
from qiskit.transpiler.passes import LookaheadSwap
from qiskit.transpiler.passes import StochasticSwap
from qiskit.transpiler.passes import FullAncillaAllocation
from qiskit.transpiler.passes import EnlargeWithAncilla
Expand All @@ -40,6 +44,8 @@
from qiskit.transpiler.passes import ApplyLayout
from qiskit.transpiler.passes import CheckCXDirection

from qiskit.transpiler import TranspilerError


def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
"""Level 2 pass manager: medium optimization by initial layout selection and
Expand All @@ -64,10 +70,15 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
Returns:
a level 2 pass manager.
Raises:
TranspilerError: if the passmanager config is invalid.
"""
basis_gates = pass_manager_config.basis_gates
coupling_map = pass_manager_config.coupling_map
initial_layout = pass_manager_config.initial_layout
layout_method = pass_manager_config.layout_method or 'dense'
routing_method = pass_manager_config.routing_method or 'stochastic'
seed_transpiler = pass_manager_config.seed_transpiler
backend_properties = pass_manager_config.backend_properties

Expand All @@ -78,7 +89,14 @@ def _choose_layout_condition(property_set):
return not property_set['layout']

_choose_layout_1 = CSPLayout(coupling_map, call_limit=1000, time_limit=10)
_choose_layout_2 = DenseLayout(coupling_map, backend_properties)
if layout_method == 'trivial':
_choose_layout_2 = TrivialLayout(coupling_map)
elif layout_method == 'dense':
_choose_layout_2 = DenseLayout(coupling_map, backend_properties)
elif layout_method == 'noise_adaptive':
_choose_layout_2 = NoiseAdaptiveLayout(backend_properties)
else:
raise TranspilerError("Invalid layout method %s." % layout_method)

# 2. Extend dag/layout with ancillas using the full coupling map
_embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()]
Expand All @@ -92,8 +110,15 @@ def _choose_layout_condition(property_set):
def _swap_condition(property_set):
return not property_set['is_swap_mapped']

_swap = [BarrierBeforeFinalMeasurements(),
StochasticSwap(coupling_map, trials=20, seed=seed_transpiler)]
_swap = [BarrierBeforeFinalMeasurements()]
if routing_method == 'basic':
_swap += [BasicSwap(coupling_map)]
elif routing_method == 'stochastic':
_swap += [StochasticSwap(coupling_map, trials=100, seed=seed_transpiler)]
elif routing_method == 'lookahead':
_swap += [LookaheadSwap(coupling_map, search_depth=5, search_width=5)]
else:
raise TranspilerError("Invalid routing method %s." % routing_method)

# 5. Unroll to the basis
_unroll = Unroller(basis_gates)
Expand Down
Loading

0 comments on commit a8dcbe1

Please sign in to comment.