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

Add StagedPassManager class for pass manager with defined stages #6403

Merged
merged 43 commits into from
Jun 21, 2022
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
e45b115
Add FullPassManager class for pass manager with defined stages
mtreinish May 12, 2021
90c242c
Add docs
mtreinish May 12, 2021
c9a0ab4
Merge branch 'main' into transpiler-phases
mtreinish Jun 1, 2021
53f34ba
Deduplicate preset passmanager construction
mtreinish Jun 2, 2021
1bebc51
Update docs
mtreinish Jun 2, 2021
76ffb74
Merge branch 'main' into transpiler-phases
mtreinish Jun 2, 2021
c86a7fb
Merge remote-tracking branch 'origin/main' into transpiler-phases
mtreinish Sep 10, 2021
f177d5f
Add dedicated scheduling stage to FullPassManager
mtreinish Sep 10, 2021
cc3c4fd
Add missing new UnitarySynthesis kwargs after rebase
mtreinish Sep 10, 2021
1937354
Use basis_translator as default method instead of basis
mtreinish Oct 18, 2021
b392a45
Merge remote-tracking branch 'origin/main' into transpiler-phases
mtreinish Oct 18, 2021
f30ee80
Rename FullPassManager StructuredPassManager
mtreinish Oct 18, 2021
f336a34
Rename generate_scheduling_post_opt() generate_scheduling()
mtreinish Oct 18, 2021
8637fa1
Merge remote-tracking branch 'origin/main' into transpiler-phases
mtreinish May 23, 2022
06582d4
Fix missing and incorrect arguments
mtreinish May 23, 2022
35a8db9
Fix more rebase issues
mtreinish May 23, 2022
10f543d
Fix even more rebase issues
mtreinish May 24, 2022
ff55b06
Merge remote-tracking branch 'origin/main' into transpiler-phases
mtreinish May 25, 2022
5e5657e
Only run unroll3q on level 0-2 if coupling map is set
mtreinish May 25, 2022
aa1db1f
Rework StructuredPassManager as a more dynamic StagedPassManager
mtreinish May 25, 2022
d654b94
Merge branch 'main' into transpiler-phases
mtreinish May 25, 2022
c51518f
Fix docs
mtreinish May 26, 2022
f6cbb55
Update internal pass set on each access
mtreinish May 26, 2022
445fdfb
Rename phases attribute to stages
mtreinish May 26, 2022
e34cbb8
Fix lint
mtreinish May 26, 2022
5e007f3
Explicitly set name in qpy compat tests
mtreinish May 26, 2022
14494db
Merge remote-tracking branch 'origin/main' into transpiler-phases
mtreinish May 26, 2022
897678c
Merge branch 'main' into transpiler-phases
mtreinish Jun 14, 2022
8414533
Merge remote-tracking branch 'origin/main' into transpiler-phases
mtreinish Jun 14, 2022
75e3d41
Apply suggestions from code review
mtreinish Jun 16, 2022
7c14a6e
Merge remote-tracking branch 'origin/main' into transpiler-phases
mtreinish Jun 16, 2022
37ad778
Run black
mtreinish Jun 16, 2022
a2472f2
Update type hint
mtreinish Jun 16, 2022
c5ac5f6
Remove out of date docstring note
mtreinish Jun 16, 2022
af9e244
Update copyright header date in qiskit/transpiler/preset_passmanagers…
mtreinish Jun 16, 2022
5a446a0
Add check for invalid stage names
mtreinish Jun 17, 2022
2205e3e
Merge branch 'main' into transpiler-phases
mtreinish Jun 17, 2022
7b225ff
Merge branch 'main' into transpiler-phases
mtreinish Jun 17, 2022
0329fb3
Merge remote-tracking branch 'origin/main' into transpiler-phases
mtreinish Jun 20, 2022
5325217
Add backwards compatibility note
mtreinish Jun 20, 2022
8b27565
Add docs on using StagedPassManager features with preset passmanagers
mtreinish Jun 20, 2022
7a8a675
Merge branch 'main' into transpiler-phases
1ucian0 Jun 21, 2022
4063b13
Merge branch 'main' into transpiler-phases
mergify[bot] Jun 21, 2022
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
2 changes: 2 additions & 0 deletions qiskit/transpiler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@
.. autosummary::
:toctree: ../stubs/

FullPassManager
PassManager
PassManagerConfig
PropertySet
Expand Down Expand Up @@ -417,6 +418,7 @@
from .runningpassmanager import FlowController
from .passmanager import PassManager
from .passmanager_config import PassManagerConfig
from .passmanager import FullPassManager
from .propertyset import PropertySet
from .exceptions import TranspilerError, TranspilerAccessError
from .fencedobjs import FencedDAGCircuit, FencedPropertySet
Expand Down
176 changes: 176 additions & 0 deletions qiskit/transpiler/passmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,3 +312,179 @@ def passes(self) -> List[Dict[str, BasePass]]:
item["flow_controllers"] = {}
ret.append(item)
return ret


class FullPassManager(PassManager):
"""A full Pass manager pipeline for a backend
mtreinish marked this conversation as resolved.
Show resolved Hide resolved

Instances of FullPassManager define a full compilation pipeline from a abstract virtual
circuit to one that is optimized and capable of running on the specified backend. It is
built using predefined stages:

1. Init - any initial passes that are run before we start embedding the circuit to the backend
2. Layout - This stage runs layout and maps the virtual qubits in the
circuit to the physical qubits on a backend
3. Routing - This stage runs after a layout has been run and will insert any
necessary gates to move the qubit states around until it can be run on
backend's compuling map.
4. Translation - Perform the basis gate translation, in other words translate the gates
in the circuit to the target backend's basis set
5. Pre-Optimization - Any passes to run before the main optimization loop
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
6. Optimization - The main optimization loop, this will typically run in a loop trying to optimize
the circuit until a condtion (such as fixed depth) is reached.
7. Post-Optimization - Any passes to run after the main optimization loop
8. Scheduling - Any hardware aware scheduling passes

These stages will be executed in order and any stage set to ``None`` will be skipped. If
a :class:`~qiskit.transpiler.PassManager` input is being used for more than 1 stage here
(for example in the case of a Pass that covers both Layout and Routing) you will want to set
that to the earliest stage in sequence that it covers.
"""

phases = [
"init",
"layout",
"routing",
"translation",
"pre_optimization",
"optimization",
"post_optimization",
"scheduling",
]

def __init__(
self,
init=None,
layout=None,
routing=None,
translation=None,
pre_optimization=None,
optimization=None,
post_optimization=None,
scheduling=None,
):
"""Initialize a new FullPassManager object

Args:
init (PassManager): A passmanager to run for the initial stage of the
compilation.
layout (PassManager): A passmanager to run for the layout stage of the
compilation.
routing (PassManager): A pass manager to run for the routing stage
of the compilation
translation (PassManager): A pass manager to run for the translation
stage of the compilation
pre_opt (PassManager): A pass manager to run before the optimization
loop
optimization (PassManager): A pass manager to run for the
optimization loop stage
post_opt (PassManager): A pass manager to run after the optimization
loop
scheduling (PassManager): A pass manager to run any scheduling passes
"""
super().__init__()
self._init = init
self._layout = layout
self._routing = routing
self._translation = translation
self._pre_optimization = pre_optimization
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
self._optimization = optimization
self._post_optimization = post_optimization
self._scheduling = scheduling
self._update_passmanager()

def _update_passmanager(self):
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
self._pass_sets = []
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
if self._init:
self._pass_sets.extend(self._init._pass_sets)
if self._layout:
self._pass_sets.extend(self._layout._pass_sets)
if self._routing:
self._pass_sets.extend(self._routing._pass_sets)
if self._translation:
self._pass_sets.extend(self._translation._pass_sets)
if self._pre_optimization:
self._pass_sets.extend(self._pre_optimization._pass_sets)
if self._optimization:
self._pass_sets.extend(self._optimization._pass_sets)
if self._post_optimization:
self._pass_sets.extend(self._post_optimization._pass_sets)
if self._scheduling:
self._pass_sets.extend(self._scheduling._pass_sets)

@property
def init(self):
"""Get the :class:`~qiskit.transpiler.PassManager` for the init stage."""
return self._init

@init.setter
def init(self, value):
"""Set the :class:`~qiskit.transpiler.PassManager` for the init stage."""
self._init = value
self._update_passmanager()

@property
def layout(self):
"""Get the :class:`~qiskit.transpiler.PassManager` for the layout stage."""
return self._layout

@layout.setter
def layout(self, value):
"""Set the :class:`~qiskit.transpiler.PassManager` for the layout stage."""
self._layout = value
self._update_passmanager()

@property
def routing(self):
"""Get the :class:`~qiskit.transpiler.PassManager` for the routing stage."""
return self._routing

@routing.setter
def routing(self, value):
"""Set the :class:`~qiskit.transpiler.PassManager` for the routing stage."""
self._routing = value
self._update_passmanager()

@property
def pre_optimization(self):
"""Get the :class:`~qiskit.transpiler.PassManager` for the pre_optimization stage."""
return self._pre_optimization

@pre_optimization.setter
def pre_optimization(self, value):
"""Set the :class:`~qiskit.transpiler.PassManager` for the pre_optimization stage."""
self._pre_optimization = value
self._update_passmanager()

@property
def optimization(self):
"""Get the :class:`~qiskit.transpiler.PassManager` for the optimization stage."""
return self._optimization

@optimization.setter
def optimization(self, value):
"""Set the :class:`~qiskit.transpiler.PassManager` for the optimization stage."""
self._optimization = value
self._update_passmanager()

@property
def post_optimization(self):
"""Get the :class:`~qiskit.transpiler.PassManager` for the post_optimization stage."""
return self._post_optimization

@post_optimization.setter
def post_optimization(self, value):
"""Set the :class:`~qiskit.transpiler.PassManager` for the post_optimization stage."""
self._post_optimization = value
self._update_passmanager()

@property
def scheduling(self):
"""Get the :class:`~qiskit.transpiler.PassManager` for the scheduling stage."""
return self._post_optimization

@scheduling.setter
def scheduling(self, value):
"""Set the :class:`~qiskit.transpiler.PassManager` for the scheduling stage."""
self._post_optimization = value
self._update_passmanager()
190 changes: 190 additions & 0 deletions qiskit/transpiler/preset_passmanagers/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2018.
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
#
# 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
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

# pylint: disable=invalid-name

"""Common preset passmanager generators."""

from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel

from qiskit.transpiler.passmanager import PassManager
from qiskit.transpiler.passes import Unroller
from qiskit.transpiler.passes import BasisTranslator
from qiskit.transpiler.passes import UnrollCustomDefinitions
from qiskit.transpiler.passes import Unroll3qOrMore
from qiskit.transpiler.passes import Collect2qBlocks
from qiskit.transpiler.passes import ConsolidateBlocks
from qiskit.transpiler.passes import UnitarySynthesis
from qiskit.transpiler.passes import CheckMap
from qiskit.transpiler.passes import GateDirection
from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements
from qiskit.transpiler.passes import CheckGateDirection
from qiskit.transpiler.passes import TimeUnitConversion
from qiskit.transpiler.passes import ALAPSchedule
from qiskit.transpiler.passes import ASAPSchedule
from qiskit.transpiler.passes import FullAncillaAllocation
from qiskit.transpiler.passes import EnlargeWithAncilla
from qiskit.transpiler.passes import ApplyLayout
from qiskit.transpiler.passes import RemoveResetInZeroState
from qiskit.transpiler.passes import ValidatePulseGates
from qiskit.transpiler.passes import AlignMeasures
from qiskit.transpiler.passes import PulseGates
from qiskit.transpiler.exceptions import TranspilerError


def generate_embed_passmanager(coupling_map):
Copy link
Member

@chriseclectic chriseclectic Feb 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think better names for these sort of functions would be default_*_passmanager

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

"""Generate a layout embedding :class:`~qiskit.transpiler.PassManager`

This is used to generate a :class:`~qiskit.transpiler.PassManager` object
that can be used to expand and apply an initial layout to a circuit

Args:
coupling_map (CouplingMap): The coupling map for the backend to embed
the circuit to.
Returns:
PassManager: The embedding passmanager that assumes the layout property
set has been set in earlier stages
"""
return PassManager([FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()])


def generate_routing_passmanager(routing_pass, coupling_map):
"""Generate a routing :class:`~qiskit.transpiler.PassManager`

Args:
routing_pass (TransformationPass): The pass which will perform the
routing
coupling_map (CouplingMap): The coupling map of the backend to route
for
Returns:
PassManager: The routing pass manager
"""
routing = PassManager()
routing.append(Unroll3qOrMore())
routing.append(CheckMap(coupling_map))

def _swap_condition(property_set):
return not property_set["is_swap_mapped"]

routing.append([BarrierBeforeFinalMeasurements(), routing_pass], condition=_swap_condition)
return routing


def generate_pre_op_passmanager(coupling_map=None, remove_reset_in_zero=False):
"""Generate a pre-optimization loop :class:`~qiskit.transpiler.PassManager`

This pass manager will check to ensure that directionality from the coupling
map is respected

Args:
coupling_map (CouplingMap): The coupling map to use
remove_reset_in_zero (bool): If ``True`` include the remove reset in
zero pass in the generated PassManager
Returns:
PassManager: The pass manager

"""
pre_opt = PassManager()
if coupling_map:
pre_opt.append(CheckGateDirection(coupling_map))

def _direction_condition(property_set):
return not property_set["is_direction_mapped"]

pre_opt.append([GateDirection(coupling_map)], condition=_direction_condition)
if remove_reset_in_zero:
pre_opt.append(RemoveResetInZeroState())
return pre_opt


def generate_translation_passmanager(
basis_gates, method="basis", approximation_degree=None, coupling_map=None, backend_props=None
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
):
"""Generate a basis translation :class:`~qiskit.transpiler.PassManager`

Args:
basis_gates (list): A list
method (str): The basis translation method to use
approximation_degree (float): The heuristic approximation degree to
use. Can be between 0 and 1.
coupling_map (CouplingMap): the coupling map of the backend
in case synthesis is done on a physical circuit. The
directionality of the coupling_map will be taken into
account if pulse_optimize is True/None and natural_direction
is True/None.
backend_props (BackendProperties): Properties of a backend to
synthesize for (e.g. gate fidelities).

Returns:
PassManager: The basis translation pass manager

Raises:
TranspilerError: If the ``method`` kwarg is not a valid value
"""
if method == "unroller":
unroll = [Unroller(basis_gates)]
elif method == "translator":
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
unroll = [UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates)]
elif method == "synthesis":
unroll = [
Unroll3qOrMore(),
Collect2qBlocks(),
ConsolidateBlocks(basis_gates=basis_gates),
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
coupling_map=coupling_map,
backend_props=backend_props,
),
]
else:
raise TranspilerError("Invalid translation method %s." % method)
return PassManager(unroll)


def generate_scheduling_post_opt(
mtreinish marked this conversation as resolved.
Show resolved Hide resolved
instruction_durations, scheduling_method, timing_constraints, inst_map
):
"""Generate a post optimization scheduling :class:`~qiskit.transpiler.PassManager`

Args:
instruction_durations (dict): The dictionary of instruction durations
scheduling_method (str): The scheduling method to use, can either be
``'asap'``/``'as_soon_as_possible'`` or
``'alap'``/``'as_late_as_possible'``
timing_constraints (TimingConstraints): Hardware time alignment restrictions.
inst_map (InstructionScheduleMap): Mapping object that maps gate to schedule.

Returns:
PassManager: The scheduling pass manager

Raises:
TranspilerError: If the ``scheduling_method`` kwarg is not a valid value
"""
scheduling = PassManager()
if inst_map and inst_map.has_custom_gate():
scheduling.append(PulseGates(inst_map=inst_map))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to me more like optimization or translation. Pulse gate is indeed a Schedule but this is used to override the instruction set. Updating the instruction set doesn't perform any scheduling.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put it in scheduling since it was done after the optimization loop. I can make it a post-optimization stage or a pre-scheduling stage if you'd prefer that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Post-optimization sounds good to me. I still think we can have this in translation, because gate with calibration doesn't need to be translated or decomposed. For example, if I calibrate a CCX for (0, 1, 2), what I want to do would be

circuit = QuantumCircuit(3)
circuit.ccx(0, 1, 2)
circuit.ccx(1, 2, 0)

backend.defaults().instruction_schdule_map.add("ccx", (0, 1, 2), my_toffoli_schedule)
transpiled = transpile(circuit, backend)

however, in this code, ccx is translated before reaching to the pulse gate pass. And no calibration is attached because no ccx primitive exist in the circuit. However, I cannot add ccx to basis gates because I have no calibration for ccx(1, 2, 0). This is why I want to move the pass before translation. Actually, we need to call pulse gate pass twice before and after the translation, because we may also want to add calibration for basis gates. Perhaps this can be solved with Target.

scheduling.append(TimeUnitConversion(instruction_durations))
if scheduling_method:
if scheduling_method in {"alap", "as_late_as_possible"}:
scheduling.append(ALAPSchedule(instruction_durations))
elif scheduling_method in {"asap", "as_soon_as_possible"}:
scheduling.append(ASAPSchedule(instruction_durations))
else:
raise TranspilerError("Invalid scheduling method %s." % scheduling_method)
scheduling.append(
ValidatePulseGates(
granularity=timing_constraints.granularity, min_length=timing_constraints.min_length
)
)
scheduling.append(AlignMeasures(alignment=timing_constraints.acquire_alignment))
return scheduling
Loading