From 8a0cf71032e65ebd957384ee7c15181557127a35 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Tue, 7 Jan 2025 17:47:57 +0000 Subject: [PATCH 01/13] Increase documentation of built-in transpiler plugins This overhauls how the `qiskit.transpiler` documentation talks about the transpiler plugins. All of the built-in plugins now have a decent amount of overview documentation, and the requirements and expectations for each stage of the preset pipelines is explained in more detail. This form of the documentation makes the distinction between "a compilation routine in general" and "Qiskit's specific choice of default pipeline" clearer, to avoid confusion for advanced users. Much of the guide-level explanations of the different preset pipeline stages moved to https://docs.quantum.ibm.com some time ago, so this PR removes those, in favour of focussing on the actual API, and inserts links to learn more about the principles elsewhere. The guide-level explanation of scheduling is left in-place for now, because the content on the other parts of the IBM documentation isn't as complete for that. --- qiskit/transpiler/__init__.py | 1289 ++++++++++------- qiskit/transpiler/layout.py | 12 +- .../passes/scheduling/scheduling/alap.py | 2 +- .../passes/scheduling/scheduling/asap.py | 2 +- .../preset_passmanagers/__init__.py | 30 +- .../transpiler/preset_passmanagers/plugin.py | 71 +- 6 files changed, 822 insertions(+), 584 deletions(-) diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index 7924d4034aef..bbce40d7a545 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -34,76 +34,156 @@ .. image:: /source_images/transpiling_core_steps.png -.. raw:: html - -
- -Qiskit has four pre-built transpilation pipelines available here: -:mod:`qiskit.transpiler.preset_passmanagers`. Unless the reader is familiar with -quantum circuit optimization methods and their usage, it is best to use one of -these ready-made routines. By default the preset pass managers are composed -of six stages: - -#. ``init`` - This stage runs any initial passes that are required before we start embedding the - circuit to the backend. This typically involves unrolling custom instructions and converting - the circuit to all 1 and 2 qubit gates. -#. ``layout`` - This stage applies a layout, mapping the virtual qubits in the circuit to the - physical qubits on a backend. See :ref:`layout_stage` for more details. -#. ``routing`` - This stage runs after a layout has been applied and will inject - gates (i.e. swaps) into the original circuit to make it compatible - with the backend's connectivity. See :ref:`routing_stage` for more details. -#. ``translation`` - This stage translates the gates in the circuit to the target backend's basis set. - See :ref:`translation_stage` for more details. -#. ``optimization`` - This stage runs the main optimization loop repeatedly - until a condition (such as fixed depth) is reached. See :ref:`optimization_stage` for more details. -#. ``scheduling`` - This stage is for any hardware-aware scheduling passes. See - :ref:`scheduling_stage` for more details. - -When using :func:`~.transpile`, the implementation of each stage can be modified with the ``*_method`` -arguments (e.g. ``layout_method``). These can be set to one of the built-in methods and -can also refer to available external plugins. See -:mod:`qiskit.transpiler.preset_passmanagers.plugin` for details on this plugin interface. - -.. _working_with_preset_pass_managers: - -Working with Preset Pass Managers -================================= - -Qiskit includes functions to build preset :class:`~.PassManager` objects. -These preset passmanagers are used by the :func:`~.transpile` function -for each optimization level. There are 4 optimization levels ranging from 0 to 3, where higher -optimization levels take more time and computational effort but may yield a -more optimal circuit. -Optimization level 0 is intended for device characterization experiments and, as such, only -maps the input circuit to the constraints of the target backend, without -performing any optimizations. Optimization level 3 spends the most effort to optimize the circuit. -However, as many of the optimization techniques in the transpiler are heuristic based, spending more -computational effort does not always result in an improvement in the quality of the output -circuit. +Qiskit uses the graph-based :class:`.DAGCircuit` intermediate representation (IR) of a circuit +throughout the transpiler stack, rather than the tree-based :class:`.QuantumCircuit`. A transpiler +pipeline is a :class:`.PassManager` object, whose :meth:`.PassManager.run` method takes in a +:class:`.QuantumCircuit`, converts it to a :class:`.DAGCircuit`, then subjects the IR to a sequence +of *passes*, finally returning a :class:`.QuantumCircuit` back. A pass is either an +:class:`.AnalysisPass`, whose purpose is to calculate and store properties about the circuit in the +stateful :class:`.PropertySet`, or a :class:`.TransformationPass`, whose purpose is to modify the IR +to achieve a particular singular goal. We typically think of a pipeline as being split into +"stages", where each stage is responsible for one high-level transformation. + +Qiskit exposes a default transpilation pipeline builder via the function +:func:`.generate_preset_pass_manager`. This will return a properly configured pipeline for complete +transpilation, at a chosen ``optimization_level`` (between 0 and 3, inclusive). Unless you are +looking for something highly specialized, this is almost certainly the entry point you want. A +sample transpilation looks like:: + + from qiskit.circuit import QuantumCircuit + from qiskit.transpiler import generate_preset_pass_manager + from qiskit_ibm_runtime import QiskitRuntimeService + + # Any abstract circuit you want: + abstract = QuantumCircuit(2) + abstract.h(0) + abstract.cx(0, 1) + + # Any method you like to retrieve the backend you want to run on: + backend = QiskitRuntimeService().backend("some-backend") + + # Create the pass manager for the transpilation ... + pm = generate_preset_pass_manager(backend=backend) + # ... and use it (as many times as you like). + physical = pm.run(abstract) + +For most use cases, this is all you will need. +All of Qiskit's transpiler infrastructure is highly extensible and configurable, however. +The rest of this page details how to harness the low-level capabilities of the transpiler stack. + +.. _transpiler-preset: + +Preset Pass Managers +==================== -If you'd like to work directly with a -preset pass manager you can use the :func:`~.generate_preset_pass_manager` -function to easily generate one. For example: +The function :func:`.generate_preset_pass_manager` creates the "preset pass managers". +These are all instances of :class:`.PassManager`, so are used by pass a :class:`.QuantumCircuit` to +the :meth:`.PassManager.run` method. More specifically, the preset pass managers are instances of +:class:`.StagedPassManager`, which allows greater configuration of the individual stages of a +tranpsilation. + +A preset pass manager has up to six named stages. These are summarized, in order of execution, +below, with more in-depth information in the following subsections. + +``init`` + Abstract-circuit optimizations, and reduction of multi-qubit operations to 1- and 2-qubit + operations. See :ref:`transpiler-preset-stage-init` for more detail. + +``layout`` + Choose an initial mapping of virtual qubits to physical qubits, including expansion of the + circuit to contain explicit ancillas. This stage sometimes subsumes ``routing``. See + :ref:`transpiler-preset-stage-layout` for more detail. + +``routing`` + Insert gates into the circuit to ensure it matches connectivity constraints of the + :class:`.Target`. The inserted gates need not match the target ISA yet, so are often just + ``swap`` instructions. This stage is sometimes omitted, when the ``layout`` stage handles its + job. See :ref:`transpiler-preset-stage-routing` for more detail. + +``translation`` + Convert all gates in the circuit to ones matching the :class:`Target`\\ s ISA. See + :ref:`transpiler-preset-stage-translation` for more detail. + +``optimization`` + Low-level, hardware-aware optimizations. Unlike the abstract optimizations of the ``init`` + stage, this stage acts on a physical circuit. See :ref:`transpiler-preset-stage-optimization` + for more detail. + +``scheduling`` + Insert :class:`~.circuit.Delay` instructions to make the wall-clock timing of a circuit + explicit. This may also include hardware-aware online error reduction techniques such as + dynamical decoupling, which are dependent on knowing wall-clock timings. See + :ref:`transpiler-preset-stage-scheduling` for more details. + +The preset transpiler pipelines can also be configured at a high level by setting an +``optimization_level``. This is in integer from 0 to 3 inclusive, indicating the relative effort to +put into attempting to optimize the circuit for the hardware. Level 0 disables all unnecessary +optimizations; only transformations needed to make the circuit runnable at all will be present. On +the other end, level 3 enables a full barrage of optimization techniques, some of which can be very +expensive in compilation time. Similar to classical compilers, optimization level 3 is not always +guaranteed to produce the best results. Qiskit defaults to optimization level 2. + +The optimization level affects which implementations are used for a given stage by default, though +this can be overridden by passing explicit ``_method=""`` arguments to +:func:`.generate_preset_pass_manager`. + +.. note:: + + The preset pass managers almost always include stochastic, heuristic-based passes. If you need + to ensure reproducibility of a compilation, pass a known integer to the ``seed_transpiler`` + argument to the generator functions. + + This stochasticity is because many of the problems the transpiler must solve are known to be + non-polynomial in complexity, and we need to ensure we finish the job in a workable amount of + time. + +Choosing Preset Stage Implementations +------------------------------------- + +Qiskit includes several implementations of several of the above stages, and more can be installed as +separate "plugins". To control which implementation of a stage is used, pass its name to the +``_method`` keyword argument of the two functions, such as +``translation_method="translator"``. To read more about implementing such external plugins for a +stage, see :mod:`qiskit.transpiler.preset_passmanagers.plugin`. + +For example, to generate a preset pass manager at optimization level 1 that explicitly uses the +``trivial`` method for layout with the ``sabre`` method for routing, we would do: .. plot:: - :include-source: - :nofigs: + :include-source: + :nofigs: - from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager + from qiskit.transpiler import generate_preset_pass_manager from qiskit.providers.fake_provider import GenericBackendV2 + # Whatever backend you like: backend = GenericBackendV2(num_qubits=5) - pass_manager = generate_preset_pass_manager(3, backend) - -which will generate a :class:`~.StagedPassManager` object for optimization level 3 -targeting the :class:`~.GenericBackendV2` backend (equivalent to what is used internally -by :func:`~.transpile` with ``backend=GenericBackendV2(5)`` and ``optimization_level=3``). -You can use this just like you would any other :class:`~.PassManager`. However, -because it is a :class:`~.StagedPassManager` it also makes it easy to compose and/or -replace stages of the pipeline. For example, if you wanted to run a custom scheduling -stage using dynamical decoupling (via the :class:`~.PadDynamicalDecoupling` pass) and -also add initial logical optimization prior to routing, you would do something like -(building off the previous example): + + pass_manager = generate_preset_pass_manager( + optimization_level=1, + backend=backend, + layout_method="trivial", + routing_method="sabre", + ) + +.. note:: + + The built-in set of available plugins for each stage is part of Qiskit's public API, and subject + to all the stability guarantees. This includes the high-level logical effects of that method + (for example, ``routing_method="sabre"`` will always use a Sabre-derived algorithm). The exact + internal construction of the :class:`.PassManager` representing the stage is not, however; the + order of passes might change between minor versions, or new passes might be introduced. + + For any stage that has one, the method named ``"default"`` is most subject to change. We will + typically only make complete algorithmic changes in the default method across a major-version + boundary, but we may well rebalance heuristics and add new passes to default methods between + minor versions. + +Since the output of :func:`.generate_preset_pass_manager` is a :class:`.StagedPassManager`, you can +also modify the pass manager after its creation to provide an entirely custom stage implementation. +For example, if you wanted to run a custom scheduling stage using dynamical decoupling (via the +:class:`~.PadDynamicalDecoupling` pass) and also add initial logical optimization prior to routing, +you would do something like (building off the previous example): .. plot:: :include-source: @@ -111,7 +191,7 @@ import numpy as np from qiskit.providers.fake_provider import GenericBackendV2 - from qiskit.circuit.library import HGate, PhaseGate, RXGate, TdgGate, TGate, XGate, CXGate + from qiskit.circuit import library as lib from qiskit.transpiler import PassManager, generate_preset_pass_manager from qiskit.transpiler.passes import ( ALAPScheduleAnalysis, @@ -120,7 +200,7 @@ ) backend = GenericBackendV2(num_qubits=5) - dd_sequence = [XGate(), XGate()] + dd_sequence = [lib.XGate(), lib.XGate()] scheduling_pm = PassManager( [ ALAPScheduleAnalysis(target=backend.target), @@ -128,22 +208,15 @@ ] ) inverse_gate_list = [ - HGate(), - (RXGate(np.pi / 4), RXGate(-np.pi / 4)), - (PhaseGate(np.pi / 4), PhaseGate(-np.pi / 4)), - (TGate(), TdgGate()), + lib.CXGate(), + lib.HGate(), + (lib.RXGate(np.pi / 4), lib.RXGate(-np.pi / 4)), + (lib.PhaseGate(np.pi / 4), lib.PhaseGate(-np.pi / 4)), + (lib.TGate(), lib.TdgGate()), ] - logical_opt = PassManager( - [ - InverseCancellation([CXGate()]), - InverseCancellation(inverse_gate_list), - ] - ) - - pass_manager = generate_preset_pass_manager( - optimization_level=0 - ) + logical_opt = PassManager([InverseCancellation(inverse_gate_list)]) + pass_manager = generate_preset_pass_manager(optimization_level=0) # Add pre-layout stage to run extra logical optimization pass_manager.pre_layout = logical_opt # Set scheduling stage to custom pass manager @@ -153,6 +226,588 @@ the ``logical_opt`` pass manager will be called before the ``layout`` stage, and the ``scheduling_pm`` pass manager will be used for the ``scheduling`` stage instead of the default. +If you are constructing custom stages for the preset pass managers, you may find some of the +low-level helper functions in :mod:`qiskit.transpiler.preset_passmanagers` useful. + +.. _transpiler-preset-stage-init: + +Initialization Stage +-------------------- + +.. seealso:: + `Init stage explanation `__ + Higher-level user-facing explanation of the init stage in the IBM Quantum guide. + +The ``init`` stage is responsible for high-level, logical optimizations on abstract circuits, and +for lowering multi-qubit (3+) operations down to a series of 1- and 2-qubit operations. As this is +the first stage run, its input is a fully abstract circuit. The ``init`` stage must be able to +handle custom user-defined gates, and all the high-level abstract circuit-description objects, such +as :class:`.AnnotatedOperation`. + +The output of the ``init`` stage is an abstract circuit that contains only 1- and 2-qubit +operations. + +When writing :ref:`stage plugins `, the entry point for ``init`` is +``qiskit.transpiler.init``. The built-in plugins are: + +.. list-table:: + :header-rows: 1 + + * - Method + - Summary + + * - :ref:`default ` + - Built-in unrolling of multi-qubit operations and abstract optimizations. + + +.. _transpiler-preset-stage-init-default: + +Built-in ``default`` plugin +........................... + +At optimization level 0, no abstract optimization is done. The default plugin simply "unrolls" +operations with more than three qubits by accessing their hierarchical +:class:`~.circuit.Instruction.definition` fields. + +At optimization levels 1 and above, the default plugin also does simple cancellation of adjacent +inverse gates, such as two back-to-back ``cx`` gates. + +At optimization levels 2 and 3, the default plugin enables a much wider range of abstract +optimizations. This includes: + +* "virtual permutation elision" (see :class:`.ElidePermutations`), where explicit + permutation-inducing operations are removed and instead effected as remapping of virtual qubits. +* analysis of the commutation structure of the IR to find pairs of gates that can be cancelled out. +* numerical splitting of two-qubit operations that can be expressed as series of separable one-qubit + operations. +* removal of imperceivable operations, such as tiny-angle Pauli rotations and diagonal operations + immediately preceding measurements. + +.. _transpiler-preset-stage-layout: + +Layout Stage +------------ + +.. seealso:: + `Layout stage explanation `__ + Higher-level user-facing explanation of the layout stage in the IBM Quantum guide. + +The layout stage is responsible for making an initial mapping between the virtual qubits of the +input circuit, and the hardware qubits of the target. This includes expanding the input circuit +with explicit ancillas so it has as many qubits as the target has, and rewriting all operations in +terms of hardware qubits. You may also see this problem called the "placement" problem in other +toolkits or literature. + +The layout stage must set the properties ``layout`` and ``original_qubit_indices`` in the pipeline's +:class:`.PropertySet`. + +.. note:: + + All built-in plugins for the layout stage will defer to an explicit layout selected using the + ``initial_layout`` argument to :func:`.generate_preset_pass_manager` or :func:`.transpile`. + +At any given point in a circuit, we can identify a mapping between currently active "virtual" qubits +of the input circuit to hardware qubits of the backend. A hardware qubit can only ever represent a +single virtual qubit at a given point, but the mapping might vary over the course of the circuit. +In principle, some virtual qubits may not necessarily be mapped at all points in the circuit +execution, if the lifetime of a virtual qubit state can be shortened, though Qiskit's built-in +pipelines do not use this currently. + +.. image:: /source_images/mapping.png + :alt: Illustration of how virtual qubits from an input circuit could be mapped to hardware + qubits on a backend device's connectivity map. + +The layout stage is not responsible for ensuring that the connectivity of the target of the target +is respected all the way through the circuit, nor that all operations are valid for direct execution +on the target; these are the responsibilities of the :ref:`routing +` and :ref:`translation ` +stages, respectively. + +The choice of initial layout is one of the most important factors that affects the quality of the +output circuit. The layout stage is often the most computationally expensive stage in the default +pipelines; the default plugin for layout even tries several different algorithms (described in more +detail in :ref:`transpiler-preset-stage-layout-default`). + +The ideal situation for the layout stage is to find a "perfect" layout, which causes all operations +to already respect the connectivity constraints of the :class:`.Target` such that the routing stage +is not required. This is typically not possible for arbitrary input circuits, but when it is, the +:class:`.VF2Layout` pass can be used to find an valid initial layout. If multiple perfect layouts +are found, a scoring heuristic based on estimated error rates is used to decide. + +In all built-in plugins, passing the :func:`.generate_preset_pass_manager` argument +``initial_layout`` causes the given layout to be used verbatim, skipping the individual "choosing" +logic. All built-in plugins also handle embedding the circuit into the full width of the device, +including assigning ancillas. + +If writing your own layout plugin, you might find :func:`.generate_embed_passmanager` useful for +automating the "embedding" stage of the layout application. + +When writing :ref:`stage plugins `, the entry point for ``layout`` +is ``qiskit.transpiler.layout``. The built-in plugins are: + +.. list-table:: + :header-rows: 1 + + * - Method + - Summary + + * - :ref:`default ` + - At the highest optimization levels, attempts to find a perfect layout, then tries a + Sabre-based layout-and-routing combined pass. + + * - :ref:`dense ` + - Finds the densest subgraph (in terms of qubit link degrees) of the backend to use as the + initial qubits. + + * - :ref:`trivial ` + - Maps virtual qubit 0 to physical qubit 0, etc. + + * - :ref:`sabre ` + - Uses `Qiskit's enhanced Sabre layout algorithm `_. + +At all optimization levels, the default layout method is ``default``, though the structure of this +stage itself changes dramatically based on the level. + +.. _transpiler-preset-stage-layout-default: + +Built-in ``default`` plugin +........................... + +An amalgamation of several different layout techniques. + +At optimization level 0, the trivial layout is chosen. + +At optimization levels above 0, there is a two-step process: + +#. First, use :class:`.VF2Layout` to attempt to find a "perfect" layout. The maximum number of + calls to the isomorphism evaluator increases with optimization level. For huge, complex targets, + we are not guaranteed to find perfect layouts even if they exist, but the chance increases with + the optimization level. + +#. If no perfect layout can be found, use :class:`.SabreLayout` to choose an initial layout, with + the numbers of initial layout trials, swap-map trials, and forwards–backwards iterations + increasing with the optimization level. + +In addition, optimization level also attempts the trivial layout before the VF2-based version, for +historical backwards compatibility. + + +.. _transpiler-preset-stage-layout-dense: + +Built-in ``dense`` plugin +......................... + +Uses the class:`.DenseLayout` pass to choose the layout. This pass finds the densest connected +subgraph of the complete target connectivity graph, where "densest" means that hardware qubits with +the greatest number of available connections are preferred. The virtual-to-hardware mapping is +completed by assigning the highest-degree virtual qubits to the highest-degree hardware qubits. + +This is a relatively cheap heuristic for choosing an initial layout, but typically has far worse +output quality than Sabre-based methods. The :ref:`default layout plugin +` uses the initial mapping selected by :class:`.DenseLayout` +as one of its initial layouts to seed the Sabre algorithm. + +.. _transpiler-preset-stage-layout-trivial: + +Built-in ``trivial`` plugin +........................... + +Uses the :class:`.TrivialLayout` pass to choose the layout. This is the simplest assignment, where +each virtual qubit is assigned to the hardware qubit with the same index, so virtual qubit 0 is +mapped to hardware qubit 0, and so on. + +This method is most useful for hardware-characterization experiments, where the incoming "abstract" +circuit is already full-width on the device, its operations correspond to physical operations, and +the transpiler is just being invoked to formalize the creation of a physical +:class:`.QuantumCircuit`. + + +.. _transpiler-preset-stage-layout-sabre: + +Built-in ``sabre`` plugin +......................... + +Uses the :class:`.SabreLayout` to choose an initial layout, using Qiskit's modified :ref:`Sabre +routing algorithm ` as the subroutine to swap-map the +candidate circuit both forwards and backwards. + +Summarily, the layout component of `the original Sabre algorithm `_ is to +choose an initial layout arbitrarily, then to "improve" it by running routing on the circuit, +reversing the circuit, and running routing on the reversed circuit with the previous "final" +virtual-to-hardware assignment as the initial state. The configured optimization level decides how +many iterations of this to-and-fro we do, and how many different random initial layouts we try. + +The principal difference to the :ref:`default stage ` at +optimization levels other than zero is that this plugin *only* runs the Sabre-based algorithm. It +does not attempt to find a perfect layout, nor attempt the trivial layout. + + + +.. _transpiler-preset-stage-routing: + +Routing Stage +------------- + +.. seealso:: + `Routing stage explanation `__ + Higher-level user-facing explanation of the routing stage in the IBM Quantum guide. + +The routing stage ensures that the virtual connectivity graph of the circuit is compatible with the +hardware connectivity graph of the target. In simpler terms, the routing stage makes sure that all +two-qubit gates in the circuit take place on hardware qubits that have a defined two-qubit operation +in the target ISA. You may also see this problem referred to as the "mapping" or "swap-mapping" +problem in other toolkits or literature. + +Routing algorithms typically do this by inserting ``swap`` gates into the circuit, and modifying the +virtual-to-hardware mapping of qubits over the course of the circuit execution. + +The routing stage does not need to ensure that all the gates in the circuit are valid for the target +ISA. For example, a routing plugin can leave literal ``swap`` gates in the circuit, even if the +:class:`.Target` does not contain :class:`.SwapGate`. However, there must be at least one two-qubit +gate defined in the :class:`.Target` for any pair of hardware qubits that has a gate applied in the +circuit. + +The routing stage must set the properties ``final_layout`` and ``virtual_permutation_layout`` in +the :class:`.PropertySet` if routing has taken place. + +All of Qiskit's built-in routing stages will additionally run the :class:`.VF2PostLayout` pass after +routing. This may choose to reassign the initial layout, if lower-error qubits can be found. This +pass is very similar to the :class:`.VF2Layout` class that :ref:`the default layout plugin +` uses, except in :class:`.VF2PostLayout` we can guarantee +that there is at least one isomorphic induced subgraph of the target topology that matches the +circuit topology. + +.. note:: + + Qiskit's built-in routing plugins will all generally assume that all pairs of qubits with a + defined two-qubit link have a *universal* set of gates defined for those two qubits. Hardware + does not necessarily need to respect this (for example, if the only defined two-qubit gate is + ``swap``, then entangling operations like ``cx`` cannot be realised), but Qiskit does not yet + consider this possibility. + +.. note:: + + Finding the minimal number of swaps to insert is known to be a non-polynomial problem. This + means it is prohibitively expensive to attempt, so many of Qiskit's built-in algorithms are + stochastic, and you may see large variation between different compilations. If you need + reproducibility, be sure to set the ``seed_transpiler`` argument of + :func:`.generate_preset_pass_manager` or :func:`.transpile`. + +When writing :ref:`stage plugins `, the entry point for ``routing`` +is ``qiskit.transpiler.routing``. The built-in plugins are: + +.. list-table:: + :header-rows: 1 + + * - Method + - Summary + + * - :ref:`sabre ` + - Default. Uses `Qiskit's modified Sabre routing algorithm `_ to swap + map. + + * - :ref:`none ` + - Disable routing. Raises an error if routing is required. + + * - :ref:`basic ` + - Greedy swap insertion to route a single operation at a time. + + * - :ref:`stochastic ` + - Consider operations layer-by-layer, using a stochastic algorithm to find swap networks that + implement a suitable permutation to make the layer executable. + + * - :ref:`lookahead ` + - Breadth-first search with heuristic pruning to find swaps that make gates executable. + +.. _transpiler-preset-stage-routing-none: + +Built-in ``none`` plugin +........................ + +A dummy plugin used to disable routing entirely. This can occasionally be useful for +hardware-configuration experiments, or in certain special cases of partial compilation. + +.. _transpiler-preset-stage-routing-basic: + +Built-in ``basic`` plugin +......................... + +Uses the :class:`.BasisSwap` greedy swap-insertion algorithm. This is conceptually very simple; for +each operation in topological order, insert the shortest-path swaps needed to make the connection +executable on the device. + +The optimization level only affects the amount of work the :class:`.VF2PostLayout` step does to +attempt to improve the initial layout after routing. + +This method typically has incredibly poor output quality. + +.. _transpiler-preset-stage-routing-stochastic: + +Built-in ``stochastic`` plugin +.............................. + +Uses the :class:`.StochasticSwap` algorithm to route. In short, this stratifies the circuit into +layers, then uses a stochastic algorithm to find a permutation that will allow the layer to execute, +and a series of swaps that will implement that permutation in a hardware-valid way. + +The optimization level affects the number of stochastic trials used for each layer, and the amount +of work spent in :class:`.VF2PostLayout` to optimize the initial layout. + +This was Qiskit's primary routing algorithm for several years, until approximately 2021. Now, it +is reliably beaten in runtime and output quality by :ref:`Qiskit's custom Sabre-based routing +algorithm `. + +.. _transpiler-preset-stage-routing-lookahead: + +Built-in ``lookahead`` plugin +............................. + +Uses the :class:`.LookaheadSwap` algorithm to route. Approximately, this is a breadth-first search +at producing a swap network, where the tree being explored is pruned down to a small number of +candidate swaps at each depth. + +This algorithm is somewhat akin to the ``basic`` heuristic of :ref:`the "sabre" plugin +`, except it considers the following effects of each swap to +a small depth as well. + +The optimization level affects the search depth, the amount of per-depth pruning, and amount of work +done by :class:`.VF2PostLayout` to post-optimize the initial layout. + +In practice, :ref:`the "sabre" plugin ` runs several orders +of magnitude faster, and produces better output. + +.. _transpiler-preset-stage-routing-sabre: + +Built-in ``sabre`` plugin +......................... + +Uses the :class:`.SabreSwap` algorithm to route. This uses `Qiskit's enhanced version +`_ of `the original Sabre routing algorithm `_. + +This routing algorithm runs with threaded parallelism to consider several different possibilities +for routing, choosing the one that minimizes the number of inserted swaps. + +The optimization level affects how many different stochastic seeds are attempted for the full +routing, and the amount of work done by :class:`.VF2PostLayout` to post-optimize the initial layout. + +This is almost invariably the best-performing built-in plugin, and the one Qiskit uses by default in +all cases where routing is necessary. + +.. _transpiler-preset-stage-translation: + +Translation Stage +----------------- + +.. seealso:: + `Translation stage explanation`__ + Higher-level user-facing explanation of the translation stage in the IBM Quantum guide. + +.. __: https://docs.quantum.ibm.com/guides/transpiler-stages#translation-stage + +The translation stage is responsible for rewriting all gates in the circuit into ones that are +supported by the target ISA. For example, if a ``cx`` is requested on hardware qubits 0 and 1, but +the ISA only contains a ``cz`` operation on those qubits, the translation stage must find a way of +representing the ``cx`` gate using the ``cz`` and available one-qubit gates. + +.. note:: + + In the Qiskit 1.x series, translation plugins need not output gates with the correct + directionality, provided the gate exists with opposite directionality on the given qubit pair. + For example, if ``cx(0, 1)`` is ISA-supported, the translation stage is permitted to output + ``cx(1, 0)``. + + This is likely to change in later versions of Qiskit. + +The translation stage is called before entering the optimization stage. Optimization plugins +(including Qiskit's built-in plugins) may also use the translation stage as a "fixup" stage after +the optimization loop, if the optimization loop returns a circuit that includes non-ISA gates. This +latter situation is fairly common; the optimization loop may only be concerned with minimizing +properties like "number of two qubit gates", and will leave its output in terms of locally +equivalent gates, which the translation stage can easily rewrite without affecting the target +optimization properties. This allows easier separation of concerns between the two stages. Some +optimization plugins may be stricter in their output, and so this follow-up to the translation stage +may no longer be necessary. + +When writing :ref:`stage plugins `, the entry point for +``translation`` is ``qiskit.transpiler.translation``. The built-in plugins are: + +.. list-table:: + :header-rows: 1 + + * - Method + - Summary + + * - :ref:`translator ` + - Symbolic translation of gates to the target basis using known equivalences. + + * - :ref:`synthesis ` + - Collect each run of one- and two-qubit gates into a matrix representation, and resynthesis + from there. + +.. _transpiler-preset-stage-translation-synthesis: + +Built-in ``synthesis`` plugin +............................. + +Collect runs of gates on the same qubits into matrix form, and then resynthesise using the +:class:`.UnitarySynthesis` pass (with the configured ``unitary_synthesis_method``). This is, in +large part, similar to the optimization loop itself at high optimization levels. + +The collection to matrices is typically more expensive than matrix-free translations, but in +principle the quality of the translations can be better. In practice, this requires a synthesis +algorithm tailored to the target ISA, which makes this method less general than other methods. It +can produce higher quality results when targeting simple ISAs that match the synthesis routines +already in Qiskit. + +If this method is used, one often does not need the optimization loop at all. + +The optimization level has no effect on this plugin. + + +.. _transpiler-preset-stage-translation-translator: + +Built-in ``translator`` plugin +.............................. + +Uses the :class:`.BasisTranslator` algorithm to symbolically translate gates into the target basis. +At a high level, this starts from the set of gates requested by the circuit, and uses rules from a +given :class:`.EquivalenceLibrary` (typically the :data:`.SessionEquivalenceLibrary`) to move +towards the ISA. + +This is the default translation method. + +The optimization level has no effect on this plugin. + + +.. _transpiler-preset-stage-optimization: + +Optimization Stage +------------------ + +.. seealso:: + `Optimization stage explanation`__ + Higher-level user-facing explanation of the optimization stage in the IBM Quantum guide. + +.. __: https://docs.quantum.ibm.com/guides/transpiler-stages#optimization-stage + +The optimization stage is for low level hardware-aware optimizations. Unlike :ref:`the init stage +`, the input to this stage is a circuit that is already +ISA-compatible, so a low-level optimization plugin can be tailored for a particular ISA. + +There are very few requirements on an optimization plugin, other than it takes in ISA-supported +circuits, and returns ISA-supported circuits. An optimization plugin will often contain a loop, +such as the :class:`.DoWhileController`, and may choose to include the configured translation stage +as a fix-up pipeline. + +Qiskit's built-in optimization plugins are general, and apply well to most real-word ISAs for +non-error-corrected devices. The built-in plugins are less well suited to ISAs that have no +continuously parametrized single-qubit gate, such as a Clifford+T basis set. + +When writing :ref:`stage plugins `, the entry point for +``optimization`` is ``qiskit.transpiler.optimization``. The built-in plugins are: + +.. list-table:: + :header-rows: 1 + + * - Method + - Summary + + * - :ref:`default ` + - A default set of optimization passes. This varies significantly between optimization + levels. + +.. _transpiler-preset-stage-optimization-default: + +Built-in ``default`` plugin +........................... + +This varies significantly between optimization levels. + +The specifics of this pipeline are subject to change between Qiskit versions. The broad principles +are described below. + +At optimization level 0, the stage is empty. + +At optimization level 1, the stage does matrix-based resynthesis of runs of 1q gates, and very +simply symbolic inverse cancellation of two-qubit gates, if they appear consecutively. This runs +in a loop until the size and depth of the circuit are fixed. + +At optimization level 2, in addition the optimizations of level 1, the loop contains commutation +analysis of sets of gates to widen the range of gates that can be considered for cancellation. +Before the loop, runs of both one- and two-qubit gates undergo a single matrix-based resynthesis. + +At optimization level 3, the two-qubit matrix-based resynthesis runs inside the optimization loop. +The optimization loop condition also tries multiple runs and chooses the minimum point in the case +of fluctuating output; this is necessary because matrix-based resynthesis is relatively unstable in +terms of concrete gates. + +Optimization level 3 is typically very expensive for large circuits. + + +.. _transpiler-preset-stage-scheduling: + +Scheduling Stage +---------------- + +.. seealso:: + :ref:`transpiler-scheduling-description` + A guide-level explanation of scheduling concepts. + +The scheduling stage, if requested, is responsible for inserting explicit :class:`~.circuit.Delay` +instructions to make idle periods of qubits explicit. Plugins may optionally choose to do +walltime-sensitive transformations, such as inserting dynamical decoupling sequences. + +The input to the scheduling stage is an ISA-compatible circuit. The output of the scheduling stage +must also be an ISA-compatible circuit, with explicit :class:`~.circuit.Delay` instructions that +satisfy the hardware's timing information, if appropriate. + +The scheduling stage should set the ``node_start_time`` property in the pipeline's +:class:`.PropertySet`. + +When writing :ref:`stage plugins `, the entry point for +``scheduling`` is ``qiskit.transpiler.scheduling``. The built-in plugins are: + +.. list-table:: + :header-rows: 1 + + * - Method + - Summary + + * - :ref:`default ` + - Attempt to satisfy timing alignment constraints without otherwise scheduling + + * - :ref:`alap ` + - Schedule the circuit preferring operations to be as late as possible. + + * - :ref:`asap ` + - Schedule the circuit preferring operations to be as soon as possible. + +.. _transpiler-preset-stage-scheduling-default: + +Built-in ``default`` plugin +........................... + +Do nothing, unless the circuit already contains instructions with explicit timings. If there are +explicitly timed operations in the circuit, insert additional padding to ensure that these timings +satisfy the alignment and other constraints of the hardware. + +.. _transpiler-preset-stage-scheduling-alap: + +Builtin ``alap`` plugin +....................... + +Explicitly schedule all operations using an "as late as possible" strategy. This uses the +:class:`.ALAPScheduleAnalysis` algorithm to decide where to place gates. + +.. _transpiler-preset-stage-scheduling-asap: + +Builtin ``asap`` plugin +....................... + +Explicitly schedule all operations using an "as soon as possible" strategy. This uses the +:class:`.ASAPScheduleAnalysis` algorithm to decide where to place gates. + + Custom Pass Managers ==================== @@ -161,7 +816,9 @@ circuits. You can use the :class:`~.StagedPassManager` class directly to do this. You can define arbitrary stage names and populate them with a :class:`~.PassManager` instance. For example, the following code creates a new :class:`~.StagedPassManager` -that has 2 stages, ``init`` and ``translation``.:: +that has 2 stages, ``init`` and ``translation``. + +.. code-block:: from qiskit.transpiler.passes import ( UnitarySynthesis, @@ -186,7 +843,8 @@ stages=["init", "translation"], init=init, translation=translate ) -There is no limit on the number of stages you can put in a :class:`~.StagedPassManager`. +There is no limit on the number of stages you can put in a :class:`~.StagedPassManager`. The stages +do not need to correspond to the stages used by Qiskit's preset pipelines. The :ref:`stage_generators` may be useful for the construction of custom :class:`~.StagedPassManager`s. They generate pass managers which provide common functionality used in many stages. @@ -504,453 +1162,19 @@ target.build_coupling_map('cz').draw() -.. _transpiler_stage_descriptions: - -Transpiler Stage Details -======================== - -Below are a description of the default transpiler stages and the problems -they solve. The default passes used for each stage are described, but -the specifics are configurable via the ``*_method`` keyword arguments for -the :func:`~.transpile` and :func:`~.generate_preset_pass_manager` functions -which can be used to override the methods described in this section. - -.. _translation_stage: - -Translation Stage ------------------ - -When writing a quantum circuit you are free to use any quantum gate (unitary operator) that -you like, along with a collection of non-gate operations such as qubit measurements and -reset operations. However, most quantum devices only natively support a handful of quantum gates -and non-gate operations. The allowed instructions for a given backend can be found by querying the -:class:`~.Target` for the devices: - -.. plot:: - :include-source: - :nofigs: - - from qiskit.providers.fake_provider import GenericBackendV2 - backend = GenericBackendV2(5) - - print(backend.target) - -Every quantum circuit run on the target device must be expressed using only these instructions. -For example, to run a simple phase estimation circuit: - -.. plot:: - :include-source: - - import numpy as np - from qiskit import QuantumCircuit - from qiskit.providers.fake_provider import GenericBackendV2 - - backend = GenericBackendV2(5) - - qc = QuantumCircuit(2, 1) - - qc.h(0) - qc.x(1) - qc.cp(np.pi/4, 0, 1) - qc.h(0) - qc.measure([0], [0]) - qc.draw(output='mpl') - -We have :math:`H`, :math:`X`, and controlled-:math:`P` gates, none of which are -in our device's basis gate set, and thus must be translated. -We can -transpile the circuit to show what it will look like in the native gate set of -the target IBM Quantum device (the :class:`~.GenericBackendV2` class generates -a fake backend with a specified number of qubits for test purposes): - -.. plot:: - :include-source: - :context: reset - - from qiskit import transpile - from qiskit import QuantumCircuit - from qiskit.providers.fake_provider import GenericBackendV2 - - backend = GenericBackendV2(5) - - qc = QuantumCircuit(2, 1) - - qc.h(0) - qc.x(1) - qc.cp(np.pi/4, 0, 1) - qc.h(0) - qc.measure([0], [0]) - - qc_basis = transpile(qc, backend) - qc_basis.draw(output='mpl') - -A few things to highlight. First, the circuit has gotten longer with respect to the -original. This can be verified by checking the depth of both circuits: - -.. plot:: - :include-source: - :nofigs: - :context: - - print('Original depth:', qc.depth(), 'Decomposed Depth:', qc_basis.depth()) - -.. code-block:: text - - Original depth: 4 Decomposed Depth: 10 - -Second, although we had a single controlled gate, the fact that it was not in the basis -set means that, when expanded, it requires more than a single :class:`~.CXGate` to implement. -All said, unrolling to the basis set of gates leads to an increase in the depth of a -quantum circuit and the number of gates. - -It is important to highlight two special cases: - -1. If A swap gate is not a native gate and must be decomposed this requires three CNOT gates: - - .. plot:: - :include-source: - :nofigs: - - from qiskit.providers.fake_provider import GenericBackendV2 - backend = GenericBackendV2(5) - - print(backend.operation_names) - - .. code-block:: text - - ['id', 'rz', 'sx', 'x', 'cx', 'measure', 'delay'] - - .. plot: - :include-source: - - from qiskit.circuit import QuantumCircuit - - swap_circ = QuantumCircuit(2) - swap_circ.swap(0, 1) - swap_circ.decompose().draw(output='mpl') - - As a product of three CNOT gates, swap gates are expensive operations to perform on - noisy quantum devices. However, such operations are usually necessary for embedding a - circuit into the limited gate connectivities of many devices. Thus, - minimizing the number of swap gates in a circuit is a primary goal in the - transpilation process. - -2. A Toffoli, or controlled-controlled-not gate (``ccx``), is a three-qubit gate. Given - that our basis gate set includes only single- and two-qubit gates, it is obvious that - this gate must be decomposed. This decomposition is quite costly: +.. _transpiler-scheduling-description: - .. plot:: - :include-source: +Scheduling of Circuits +====================== - from qiskit.circuit import QuantumCircuit +.. + This section is still here because the content hasn't fully migrated to other places yet, unlike + other discussions of the components of quantum compilation. - ccx_circ = QuantumCircuit(3) - ccx_circ.ccx(0, 1, 2) - ccx_circ.decompose().draw(output='mpl') - - For every Toffoli gate in a quantum circuit, the hardware may execute up to six CNOT - gates, and a handful of single-qubit gates. From this example, it should be - clear that any algorithm that makes use of multiple Toffoli gates will end up as a - circuit with large depth and will therefore be appreciably affected by noise and gate - errors. - - -.. _layout_stage: - -Layout Stage ------------- - -Quantum circuits are abstract entities whose qubits are "virtual" representations of actual -qubits used in computations. We need to be able to map these virtual qubits in a one-to-one -manner to the "physical" qubits in an actual quantum device. - -.. image:: /source_images/mapping.png - - -By default, qiskit will do this mapping for you. The choice of mapping depends on the -properties of the circuit, the particular device you are targeting, and the optimization -level that is chosen. The choice of initial layout is extremely important for minimizing the -number of swap operations needed to map the input circuit onto the device topology and -for minimizing the loss due to non-uniform noise properties across a device. Due to the -importance of this stage, the preset pass managers -try a few different methods to find the best layout. Typically this involves 2 steps: first, -trying to find a "perfect" layout (a layout which does not require any swap operations), and then, -a heuristic pass that tries to find the best layout to use if a perfect layout cannot be found. -There are 2 passes typically used for the first stage: - -- :class:`~.VF2Layout`: Models layout selection as a subgraph isomorphism problem and tries - to find a subgraph of the connectivity graph that is isomorphic to the - graph of 2 qubit interactions in the circuit. If more than one isomorphic mapping is found a - scoring heuristic is run to select the mapping which would result in the lowest average error - when executing the circuit. - -- :class:`~.TrivialLayout`: Maps each virtual qubit to the same numbered physical qubit on the device, - i.e. ``[0,1,2,3,4]`` -> ``[0,1,2,3,4]``. This is historical behavior used only in - ``optimization_level=1`` to try to find a perfect layout. If it fails to do so, :class:`~.VF2Layout` - is tried next. - -Next, for the heuristic stage, 2 passes are used by default: - -- :class:`~.SabreLayout`: Selects a layout by starting from an initial random layout and then - repeatedly running a routing algorithm (by default :class:`~.SabreSwap`) both forward and - backward over the circuit, using the permutation caused by swap insertions to adjust that - initial random layout. For more details you can refer to the paper describing the algorithm: - `arXiv:1809.02573 `__ - :class:`~.SabreLayout` is used to select a layout if a perfect layout isn't found for - optimization levels 1, 2, and 3. -- :class:`~.TrivialLayout`: Always used for the layout at optimization level 0. - -There are other passes than can be used for the heuristic stage, but are not included in the default -pipeline, such as: - -- :class:`~.DenseLayout`: Finds the sub-graph of the device with greatest connectivity - that has the same number of qubits as the circuit. - -Let's see what layouts are automatically picked at various optimization levels. The circuits -returned by :func:`qiskit.compiler.transpile` are annotated with this initial layout information, -and we can view this layout selection graphically using -:func:`qiskit.visualization.plot_circuit_layout`: - -.. plot:: - :include-source: - - from qiskit import QuantumCircuit, transpile - from qiskit.visualization import plot_circuit_layout - from qiskit.providers.fake_provider import Fake5QV1 - backend = Fake5QV1() - - ghz = QuantumCircuit(3, 3) - ghz.h(0) - ghz.cx(0,range(1,3)) - ghz.barrier() - ghz.measure(range(3), range(3)) - ghz.draw(output='mpl') - - -- **Layout Using Optimization Level 0** - - .. plot:: - :include-source: - - from qiskit import QuantumCircuit, transpile - from qiskit.visualization import plot_circuit_layout - from qiskit.providers.fake_provider import Fake5QV1 - backend = Fake5QV1() - - ghz = QuantumCircuit(3, 3) - ghz.h(0) - ghz.cx(0,range(1,3)) - ghz.barrier() - ghz.measure(range(3), range(3)) - - new_circ_lv0 = transpile(ghz, backend=backend, optimization_level=0) - plot_circuit_layout(new_circ_lv0, backend) - -- **Layout Using Optimization Level 3** - - .. plot:: - :include-source: - - from qiskit import QuantumCircuit, transpile - from qiskit.visualization import plot_circuit_layout - from qiskit.providers.fake_provider import Fake5QV1 - backend = Fake5QV1() - - ghz = QuantumCircuit(3, 3) - ghz.h(0) - ghz.cx(0,range(1,3)) - ghz.barrier() - ghz.measure(range(3), range(3)) - - new_circ_lv3 = transpile(ghz, backend=backend, optimization_level=3) - plot_circuit_layout(new_circ_lv3, backend) - - -It is possible to override automatic layout selection by specifying an initial layout. To do so we can -pass a list of integers to :func:`qiskit.compiler.transpile` via the `initial_layout` -keyword argument, where the index labels the virtual qubit in the circuit and the -corresponding value is the label for the physical qubit to map onto: - -.. plot:: - :include-source: - - from qiskit import QuantumCircuit, transpile - from qiskit.visualization import plot_circuit_layout - from qiskit.providers.fake_provider import Fake5QV1 - backend = Fake5QV1() - - ghz = QuantumCircuit(3, 3) - ghz.h(0) - ghz.cx(0,range(1,3)) - ghz.barrier() - ghz.measure(range(3), range(3)) - - # Virtual -> physical - # 0 -> 3 - # 1 -> 4 - # 2 -> 2 - - my_ghz = transpile(ghz, backend, initial_layout=[3, 4, 2]) - plot_circuit_layout(my_ghz, backend) - -.. _routing_stage: - -Routing Stage -------------- - -In order to implement a 2-qubit gate between qubits in a quantum circuit that are not directly -connected on a quantum device, one or more swap gates must be inserted into the circuit to -move the qubit states around until they are adjacent on the device gate map. Each swap -gate typically represents an expensive and noisy operation to perform. Thus, finding the -minimum number of swap gates needed to map a circuit onto a given device, is an important -step (if not the most important) in the whole execution process. - -However, as with many important things in life, finding the optimal swap mapping is hard. -In fact it is in a class of problems called NP-hard, and is thus prohibitively expensive -to compute for all but the smallest quantum devices and input circuits. To get around this, -by default Qiskit uses a stochastic heuristic algorithm called :class:`~.SabreSwap` to compute -a good, but not necessarily optimal swap mapping. The use of a stochastic method means the -circuits generated by :func:`~.transpile` -are not guaranteed to be the same over repeated runs. Indeed, running the same -circuit repeatedly will in general result in a distribution of circuit depths and gate counts -at the output. - -In order to highlight this, we run a GHZ circuit 100 times, using a "bad" (disconnected) -``initial_layout`` in a heavy hex coupling map: - -.. plot:: - - from qiskit import QuantumCircuit, transpile - - ghz = QuantumCircuit(15) - ghz.h(0) - ghz.cx(0, range(1, 15)) - ghz.draw(output='mpl') - -.. plot:: - :include-source: - - import matplotlib.pyplot as plt - from qiskit import QuantumCircuit, transpile - from qiskit.providers.fake_provider import GenericBackendV2 - from qiskit.transpiler import CouplingMap - - coupling_map = CouplingMap.from_heavy_hex(3) - backend = GenericBackendV2(coupling_map.size(), coupling_map=coupling_map) - - ghz = QuantumCircuit(15) - ghz.h(0) - ghz.cx(0, range(1, 15)) - - depths = [] - for i in range(100): - depths.append( - transpile( - ghz, - backend, - seed_transpiler=i, - layout_method='trivial' # Fixed layout mapped in circuit order - ).depth() - ) - - plt.figure(figsize=(8, 6)) - plt.hist(depths, align='left', color='#AC557C') - plt.xlabel('Depth', fontsize=14) - plt.ylabel('Counts', fontsize=14); - - -This distribution is quite wide, signaling the difficulty the swap mapper is having -in computing the best mapping. Most circuits will have a distribution of depths, -perhaps not as wide as this one, due to the stochastic nature of the default swap -mapper. Of course, we want the best circuit we can get, especially in cases where -the depth is critical to success or failure. The :class:`~.SabreSwap` pass will by default by run its -algorithm in parallel with multiple seed values and select the output which -uses the fewest swaps. If you would like to increase the number of trials -:class:`~.SabreSwap` runs you can refer to :ref:`working_with_preset_pass_managers` -and modify the ``routing`` stage with a custom instance of :class:`~.SabreSwap` -with a larger value for the ``trials`` argument. - -Typically, following the swap mapper, the routing stage in the preset pass managers -also includes running the :class:`~.VF2PostLayout` pass. As its name implies, -:class:`~.VF2PostLayout` uses the same basic algorithm as :class:`~.VF2Layout`, -but instead of using it to find a perfect initial layout, it is designed to run after -mapping and try to find a layout on qubits with lower error rates which will -result in better output fidelity when running the circuit. The details of this -algorithm are described in `arXiv:2209.15512 `__. - -.. _optimization_stage: - -Optimization Stage ------------------- - -Decomposing quantum circuits into the basis gate set of the target device, -and the addition of swap gates needed to match hardware topology, conspire to -increase the depth and gate count of quantum circuits. Fortunately many routines -for optimizing circuits by combining or eliminating gates exist. In some cases -these methods are so effective the output circuits have lower depth than the inputs. -In other cases, not much can be done, and the computation may be difficult to -perform on noisy devices. Different gate optimizations are turned on with -different ``optimization_level`` values. Below we show the benefits gained from -setting the optimization level higher: - -.. important:: - - The output from :func:`.transpile` varies due to the stochastic swap mapper. - So the numbers below will likely change each time you run the code. - - -.. plot:: - - import matplotlib.pyplot as plt - from qiskit import QuantumCircuit, transpile - from qiskit.providers.fake_provider import GenericBackendV2 - backend = GenericBackendV2(16) - - ghz = QuantumCircuit(15) - ghz.h(0) - ghz.cx(0, range(1, 15)) - ghz.draw(output='mpl') - -.. plot:: - :include-source: - - import matplotlib.pyplot as plt - from qiskit import QuantumCircuit, transpile - from qiskit.providers.fake_provider import GenericBackendV2 - backend = GenericBackendV2(16) - - ghz = QuantumCircuit(15) - ghz.h(0) - ghz.cx(0, range(1, 15)) - - depths = [] - gate_counts = [] - non_local_gate_counts = [] - levels = [str(x) for x in range(4)] - for level in range(4): - circ = transpile(ghz, backend, optimization_level=level) - depths.append(circ.depth()) - gate_counts.append(sum(circ.count_ops().values())) - non_local_gate_counts.append(circ.num_nonlocal_gates()) - fig, (ax1, ax2) = plt.subplots(2, 1) - ax1.bar(levels, depths, label='Depth') - ax1.set_xlabel("Optimization Level") - ax1.set_ylabel("Depth") - ax1.set_title("Output Circuit Depth") - ax2.bar(levels, gate_counts, label='Number of Circuit Operations') - ax2.bar(levels, non_local_gate_counts, label='Number of non-local gates') - ax2.set_xlabel("Optimization Level") - ax2.set_ylabel("Number of gates") - ax2.legend() - ax2.set_title("Number of output circuit gates") - fig.tight_layout() - plt.show() - -.. _scheduling_stage: - -Scheduling Stage ----------------- +.. seealso:: + :ref:`transpiler-preset-stage-scheduling` + How to configure the scheduling stages of the preset pass managers. After the circuit has been translated to the target basis, mapped to the device, and optimized, a scheduling phase can be applied to optionally account for all the idle time in the circuit. @@ -1020,7 +1244,7 @@ to insert the instructions into the circuit, which completes the scheduling. Scheduling Analysis with control flow instructions -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +-------------------------------------------------- When running scheduling analysis passes on a circuit, you must keep in mind that there are additional constraints on classical conditions and control flow instructions. This section @@ -1028,7 +1252,7 @@ constraints that any scheduling pass will need to account for. Topological node ordering in scheduling -'''''''''''''''''''''''''''''''''''''''' +....................................... The DAG representation of ``QuantumCircuit`` respects the node ordering in the classical register wires, though theoretically two conditional instructions @@ -1064,7 +1288,7 @@ not to break the topological ordering of the original circuit. Realistic control flow scheduling (respecting microarchitecture) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' +................................................................ In the dispersive QND readout scheme, the qubit (Q) is measured by sending a microwave stimulus, followed by a resonator ring-down (depopulation). This @@ -1218,8 +1442,8 @@ Transpiler API ============== -Transpiler Target ------------------ +Hardware Description +-------------------- .. autosummary:: :toctree: ../stubs/ @@ -1227,8 +1451,8 @@ Target InstructionProperties -Pass Manager Construction -------------------------- +Pass Manager Definition +----------------------- .. autosummary:: :toctree: ../stubs/ @@ -1236,6 +1460,7 @@ StagedPassManager PassManager PassManagerConfig + generate_preset_pass_manager Layout and Topology ------------------- @@ -1274,6 +1499,8 @@ .. autoexception:: CircuitTooWideForTarget .. autoexception:: InvalidLayoutError +.. _sabre-original-paper: https://arxiv.org/abs/1809.02573 +.. _sabre-lightsabre-paper: https://arxiv.org/abs/2409.08368 """ # For backward compatibility diff --git a/qiskit/transpiler/layout.py b/qiskit/transpiler/layout.py index bece19671794..fbf50a5a2be6 100644 --- a/qiskit/transpiler/layout.py +++ b/qiskit/transpiler/layout.py @@ -445,10 +445,10 @@ class TranspileLayout: The :mod:`~qiskit.transpiler` is unitary-preserving up to the "initial layout" and "final layout" permutations. The initial layout permutation is caused by - setting and applying the initial layout during the :ref:`layout_stage`. + setting and applying the initial layout during the :ref:`transpiler-preset-stage-layout`. The final layout permutation is caused by :class:`~.SwapGate` insertion during - the :ref:`routing_stage`. This class provides an interface to reason about these - permutations using a variety of helper methods. + the :ref:`transpiler-preset-stage-routing`. This class provides an interface to reason about + these permutations using a variety of helper methods. During the layout stage, the transpiler can potentially remap the order of the qubits in the circuit as it fits the circuit to the target backend. For example, @@ -521,7 +521,7 @@ class TranspileLayout: state from the transpiler. They are defined as: * :attr:`initial_layout` - This attribute is used to model the - permutation caused by the :ref:`layout_stage`. It is a + permutation caused by the :ref:`transpiler-preset-stage-layout`. It is a :class:`~.Layout` object that maps the input :class:`~.QuantumCircuit`\s :class:`~.circuit.Qubit` objects to the position in the output :class:`.QuantumCircuit.qubits` list. @@ -533,12 +533,12 @@ class TranspileLayout: is needed when computing the permutation of the :class:`Operator` of the circuit (and used by :meth:`.Operator.from_circuit`). * :attr:`final_layout` - This attribute is used to model the - permutation caused by the :ref:`routing_stage`. It is a + permutation caused by the :ref:`transpiler-preset-stage-routing`. It is a :class:`~.Layout` object that maps the output circuit's qubits from :class:`.QuantumCircuit.qubits` in the output circuit to their final positions after routing. Importantly, this only represents the permutation caused by inserting :class:`~.SwapGate`\s into - the :class:`~.QuantumCircuit` during the :ref:`routing_stage`. + the :class:`~.QuantumCircuit` during the :ref:`transpiler-preset-stage-routing`. It is **not** a mapping from the original input circuit's position to the final position at the end of the transpiled circuit. If you need this, you can use the :meth:`.final_index_layout` to generate this. diff --git a/qiskit/transpiler/passes/scheduling/scheduling/alap.py b/qiskit/transpiler/passes/scheduling/scheduling/alap.py index 2001c3d66f2f..2d60c78ae070 100644 --- a/qiskit/transpiler/passes/scheduling/scheduling/alap.py +++ b/qiskit/transpiler/passes/scheduling/scheduling/alap.py @@ -20,7 +20,7 @@ class ALAPScheduleAnalysis(BaseScheduler): """ALAP Scheduling pass, which schedules the **stop** time of instructions as late as possible. - See the :ref:`scheduling_stage` section in the :mod:`qiskit.transpiler` + See the :ref:`transpiler-scheduling-description` section in the :mod:`qiskit.transpiler` module documentation for the detailed behavior of the control flow operation, i.e. ``c_if``. """ diff --git a/qiskit/transpiler/passes/scheduling/scheduling/asap.py b/qiskit/transpiler/passes/scheduling/scheduling/asap.py index fa07ae0c5f61..fc24aa9d70db 100644 --- a/qiskit/transpiler/passes/scheduling/scheduling/asap.py +++ b/qiskit/transpiler/passes/scheduling/scheduling/asap.py @@ -20,7 +20,7 @@ class ASAPScheduleAnalysis(BaseScheduler): """ASAP Scheduling pass, which schedules the start time of instructions as early as possible. - See the :ref:`scheduling_stage` section in the :mod:`qiskit.transpiler` + See the :ref:`transpiler-scheduling-description` section in the :mod:`qiskit.transpiler` module documentation for the detailed behavior of the control flow operation, i.e. ``c_if``. """ diff --git a/qiskit/transpiler/preset_passmanagers/__init__.py b/qiskit/transpiler/preset_passmanagers/__init__.py index fbbd5a6c202a..b8d96bca48fc 100644 --- a/qiskit/transpiler/preset_passmanagers/__init__.py +++ b/qiskit/transpiler/preset_passmanagers/__init__.py @@ -32,21 +32,23 @@ .. _preset_pass_manager_generators: -Preset Pass Manager Generation ------------------------------- +Low-level Preset Pass Manager Generation +---------------------------------------- -.. autofunction:: generate_preset_pass_manager .. autofunction:: level_0_pass_manager .. autofunction:: level_1_pass_manager .. autofunction:: level_2_pass_manager .. autofunction:: level_3_pass_manager +.. + `generate_preset_pass_manager` is not documented here because it's documented to be at the root + of `qiskit.transpiler`. + .. _stage_generators: Stage Generator Functions ------------------------- -.. currentmodule:: qiskit.transpiler.preset_passmanagers.common .. autofunction:: generate_control_flow_options_check .. autofunction:: generate_error_on_control_flow .. autofunction:: generate_unroll_3q @@ -55,8 +57,18 @@ .. autofunction:: generate_pre_op_passmanager .. autofunction:: generate_translation_passmanager .. autofunction:: generate_scheduling -.. currentmodule:: qiskit.transpiler.preset_passmanagers """ + +from .common import ( + generate_control_flow_options_check, + generate_error_on_control_flow, + generate_unroll_3q, + generate_embed_passmanager, + generate_routing_passmanager, + generate_pre_op_passmanager, + generate_translation_passmanager, + generate_scheduling, +) from .generate_preset_pass_manager import generate_preset_pass_manager from .level0 import level_0_pass_manager from .level1 import level_1_pass_manager @@ -70,4 +82,12 @@ "level_2_pass_manager", "level_3_pass_manager", "generate_preset_pass_manager", + "generate_control_flow_options_check", + "generate_error_on_control_flow", + "generate_unroll_3q", + "generate_embed_passmanager", + "generate_routing_passmanager", + "generate_pre_op_passmanager", + "generate_translation_passmanager", + "generate_scheduling", ] diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index ac3b2b618cc9..ad2cd7e2d916 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -11,6 +11,8 @@ # that they have been altered from the originals. """ +.. _transpiler-preset-stage-plugins: + ======================================================================================= Transpiler Stage Plugin Interface (:mod:`qiskit.transpiler.preset_passmanagers.plugin`) ======================================================================================= @@ -36,7 +38,9 @@ ============= Currently, there are 6 stages in the preset pass managers, all of which actively -load external plugins via corresponding entry points. +load external plugins via corresponding entry points. The table below gives a quick summary of each +stage. For more details on the description and expectations of each stage, follow the link in the +stages' names to the full documentation. .. list-table:: Stages :header-rows: 1 @@ -44,57 +48,44 @@ * - Stage Name - Entry Point - Reserved Names - - Description and expectations - * - ``init`` + - Summary + + * - :ref:`init ` - ``qiskit.transpiler.init`` - ``default`` - - This stage runs first and is typically used for any initial logical optimization. Because most - layout and routing algorithms are only designed to work with 1 and 2 qubit gates, this stage - is also used to translate any gates that operate on more than 2 qubits into gates that only - operate on 1 or 2 qubits. - * - ``layout`` + - High-level, logical optimizations on abstract circuits, and reduction of multi-qubit + operations to 1- and 2-qubit operations. + + * - :ref:`layout ` - ``qiskit.transpiler.layout`` - ``trivial``, ``dense``, ``sabre``, ``default`` - - The output from this stage is expected to have the ``layout`` property - set field set with a :class:`~.Layout` object. Additionally, the circuit is - typically expected to be embedded so that it is expanded to include all - qubits and the :class:`~.ApplyLayout` pass is expected to be run to apply the - layout. The embedding of the :class:`~.Layout` can be generated with - :func:`~.generate_embed_passmanager`. - * - ``routing`` + - Choose an initial mapping of virtual qubits to physical qubits, including expansion of the + circuit to include explicit ancillas. This stage is sometimes combined with ``routing``. + + * - :ref:`routing ` - ``qiskit.transpiler.routing`` - ``basic``, ``stochastic``, ``lookahead``, ``sabre`` - - The output from this stage is expected to have the circuit match the - connectivity constraints of the target backend. This does not necessarily - need to match the directionality of the edges in the target as a later - stage typically will adjust directional gates to match that constraint - (but there is no penalty for doing that in the ``routing`` stage). The output - of this stage is also expected to have the ``final_layout`` property set field - set with a :class:`~.Layout` object that maps the :class:`.Qubit` to the - output final position of that qubit in the circuit. If there is an - existing ``final_layout`` entry in the property set (such as might be set - by an optimization pass that introduces a permutation) it is expected - that the final layout will be the composition of the two layouts (this - can be computed using :meth:`.DAGCircuit.compose`, for example: - ``second_final_layout.compose(first_final_layout, dag.qubits)``). - * - ``translation`` + - Insert gates into the circuit to ensure it matches the connectivity constraints of the + :class:`.Target`. The inserted gates do not need to be in the target ISA yet, so are often + just output as ``swap`` instructions. This stage is sometimes subsumed by ``layout``. + + * - :ref:`translation ` - ``qiskit.transpiler.translation`` - ``translator``, ``synthesis``, ``unroller`` - - The output of this stage is expected to have every operation be a native - instruction on the target backend. - * - ``optimization`` + - Rewrite all gates outside the target ISA to use only gates within the ISA. + + * - :ref:`optimization ` - ``qiskit.transpiler.optimization`` - ``default`` - - This stage is expected to perform optimization and simplification. - The constraints from earlier stages still apply to the output of this - stage. After the ``optimization`` stage is run we expect the circuit - to still be executable on the target. - * - ``scheduling`` + - Low-level, physical-circuit-aware optimizations. Unlike ``init``, the ``optimization`` stage + acts at the level of a physical circuit. + + * - :ref:`scheduling ` - ``qiskit.transpiler.scheduling`` - ``alap``, ``asap``, ``default`` - - This is the last stage run and it is expected to output a scheduled - circuit such that all idle periods in the circuit are marked by explicit - :class:`~qiskit.circuit.Delay` instructions. + - Insert :class:`~.circuit.Delay` instructions to make the wall-clock timing of the circuit + fully explicit. + Writing Plugins =============== From 1f8ed6edeb249810acc59f3bb95f261008938bbc Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Tue, 7 Jan 2025 18:27:59 +0000 Subject: [PATCH 02/13] Fix cross references in release notes --- .../notes/0.24/add-layout-attribute-c84e56c08ca93ada.yaml | 2 +- releasenotes/notes/1.1/add-elide-swaps-b0a4c373c9af1efd.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/releasenotes/notes/0.24/add-layout-attribute-c84e56c08ca93ada.yaml b/releasenotes/notes/0.24/add-layout-attribute-c84e56c08ca93ada.yaml index b08f699c583e..eb4406277a18 100644 --- a/releasenotes/notes/0.24/add-layout-attribute-c84e56c08ca93ada.yaml +++ b/releasenotes/notes/0.24/add-layout-attribute-c84e56c08ca93ada.yaml @@ -4,7 +4,7 @@ features: Added a new attribute, :attr:`~.QuantumCircuit.layout`, to the :class:`~.QuantumCircuit` class. This attribute is typically populated by :func:`~.transpile` or :class:`.PassManager.run` (when the - :ref:`layout_stage` and :ref:`routing_stage` are run in the + :ref:`transpiler-preset-stage-layout` and :ref:`routing_stage` are run in the :class:`~PassManager`) and contains a :class:`~.TranspileLayout` which contains the information about the permutation of the input circuit during :class:`~.transpile`. diff --git a/releasenotes/notes/1.1/add-elide-swaps-b0a4c373c9af1efd.yaml b/releasenotes/notes/1.1/add-elide-swaps-b0a4c373c9af1efd.yaml index a8da2921990a..2bb2a8a2a290 100644 --- a/releasenotes/notes/1.1/add-elide-swaps-b0a4c373c9af1efd.yaml +++ b/releasenotes/notes/1.1/add-elide-swaps-b0a4c373c9af1efd.yaml @@ -2,7 +2,7 @@ features: - | Added a new optimization transpiler pass, :class:`~.ElidePermutations`, - which is designed to run prior to the :ref:`layout_stage` and will + which is designed to run prior to the :ref:`transpiler-preset-stage-layout` and will optimize away any :class:`~.SwapGate`\s and :class:`~qiskit.circuit.library.PermutationGate`\s in a circuit by permuting virtual From adacfe9ab7c37859d91e96e7b7a6a63d2c9f597e Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Wed, 22 Jan 2025 15:16:35 +0000 Subject: [PATCH 03/13] Include Elena's suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com> --- qiskit/transpiler/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index 3e454910c7ae..df4dbf5d152b 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -82,7 +82,7 @@ These are all instances of :class:`.PassManager`, so are used by pass a :class:`.QuantumCircuit` to the :meth:`.PassManager.run` method. More specifically, the preset pass managers are instances of :class:`.StagedPassManager`, which allows greater configuration of the individual stages of a -tranpsilation. +tranpsilation, include pre- and post-stage hooks. A preset pass manager has up to six named stages. These are summarized, in order of execution, below, with more in-depth information in the following subsections. @@ -123,7 +123,8 @@ optimizations; only transformations needed to make the circuit runnable at all will be present. On the other end, level 3 enables a full barrage of optimization techniques, some of which can be very expensive in compilation time. Similar to classical compilers, optimization level 3 is not always -guaranteed to produce the best results. Qiskit defaults to optimization level 2. +guaranteed to produce the best results. Qiskit defaults to optimization level 2, as a trade-off +between compilation time and the expected amount of optimization. The optimization level affects which implementations are used for a given stage by default, though this can be overridden by passing explicit ``_method=""`` arguments to From d541d405957ddebd0a72f78cfd1dc1946ae20527 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Wed, 22 Jan 2025 18:03:06 +0000 Subject: [PATCH 04/13] Apply copy-editing suggestions Co-authored-by: abbycross --- qiskit/transpiler/__init__.py | 82 +++++++++---------- .../preset_passmanagers/__init__.py | 4 +- .../transpiler/preset_passmanagers/plugin.py | 6 +- 3 files changed, 46 insertions(+), 46 deletions(-) diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index df4dbf5d152b..cfc0a08c704e 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -39,18 +39,18 @@ Qiskit uses the graph-based :class:`.DAGCircuit` intermediate representation (IR) of a circuit throughout the transpiler stack, rather than the tree-based :class:`.QuantumCircuit`. A transpiler pipeline is a :class:`.PassManager` object, whose :meth:`.PassManager.run` method takes in a -:class:`.QuantumCircuit`, converts it to a :class:`.DAGCircuit`, then subjects the IR to a sequence +:class:`.QuantumCircuit` and converts it to a :class:`.DAGCircuit`, then subjects the IR to a sequence of *passes*, finally returning a :class:`.QuantumCircuit` back. A pass is either an :class:`.AnalysisPass`, whose purpose is to calculate and store properties about the circuit in the stateful :class:`.PropertySet`, or a :class:`.TransformationPass`, whose purpose is to modify the IR -to achieve a particular singular goal. We typically think of a pipeline as being split into +to achieve a particular singular goal. You can think of a pipeline as being split into "stages", where each stage is responsible for one high-level transformation. -Qiskit exposes a default transpilation pipeline builder via the function +Qiskit exposes a default transpilation pipeline builder using the function :func:`.generate_preset_pass_manager`. This will return a properly configured pipeline for complete transpilation, at a chosen ``optimization_level`` (between 0 and 3, inclusive). Unless you are looking for something highly specialized, this is almost certainly the entry point you want. A -sample transpilation looks like:: +sample transpilation looks like: from qiskit.circuit import QuantumCircuit from qiskit.transpiler import generate_preset_pass_manager @@ -75,20 +75,20 @@ .. _transpiler-preset: -Preset Pass Managers +Preset pass managers ==================== The function :func:`.generate_preset_pass_manager` creates the "preset pass managers". -These are all instances of :class:`.PassManager`, so are used by pass a :class:`.QuantumCircuit` to +These are all instances of :class:`.PassManager`, so are used by passing a :class:`.QuantumCircuit` to the :meth:`.PassManager.run` method. More specifically, the preset pass managers are instances of :class:`.StagedPassManager`, which allows greater configuration of the individual stages of a -tranpsilation, include pre- and post-stage hooks. +transpilation, include pre- and post-stage hooks. A preset pass manager has up to six named stages. These are summarized, in order of execution, below, with more in-depth information in the following subsections. ``init`` - Abstract-circuit optimizations, and reduction of multi-qubit operations to 1- and 2-qubit + Abstract-circuit optimizations, and reduction of multi-qubit operations to one- and two-qubit operations. See :ref:`transpiler-preset-stage-init` for more detail. ``layout`` @@ -118,8 +118,8 @@ :ref:`transpiler-preset-stage-scheduling` for more details. The preset transpiler pipelines can also be configured at a high level by setting an -``optimization_level``. This is in integer from 0 to 3 inclusive, indicating the relative effort to -put into attempting to optimize the circuit for the hardware. Level 0 disables all unnecessary +``optimization_level``. This is an integer from 0 to 3 inclusive, indicating the relative effort to +exert in attempting to optimize the circuit for the hardware. Level 0 disables all unnecessary optimizations; only transformations needed to make the circuit runnable at all will be present. On the other end, level 3 enables a full barrage of optimization techniques, some of which can be very expensive in compilation time. Similar to classical compilers, optimization level 3 is not always @@ -140,7 +140,7 @@ non-polynomial in complexity, and we need to ensure we finish the job in a workable amount of time. -Choosing Preset Stage Implementations +Choosing preset stage implementations ------------------------------------- Qiskit includes several implementations of several of the above stages, and more can be installed as @@ -184,7 +184,7 @@ Since the output of :func:`.generate_preset_pass_manager` is a :class:`.StagedPassManager`, you can also modify the pass manager after its creation to provide an entirely custom stage implementation. -For example, if you wanted to run a custom scheduling stage using dynamical decoupling (via the +For example, if you wanted to run a custom scheduling stage using dynamical decoupling (using the :class:`~.PadDynamicalDecoupling` pass) and also add initial logical optimization prior to routing, you would do something like (building off the previous example): @@ -234,7 +234,7 @@ .. _transpiler-preset-stage-init: -Initialization Stage +Initialization stage -------------------- .. seealso:: @@ -242,12 +242,12 @@ Higher-level user-facing explanation of the init stage in the IBM Quantum guide. The ``init`` stage is responsible for high-level, logical optimizations on abstract circuits, and -for lowering multi-qubit (3+) operations down to a series of 1- and 2-qubit operations. As this is +for lowering multi-qubit (3+) operations down to a series of one- and two-qubit operations. As this is the first stage run, its input is a fully abstract circuit. The ``init`` stage must be able to handle custom user-defined gates, and all the high-level abstract circuit-description objects, such as :class:`.AnnotatedOperation`. -The output of the ``init`` stage is an abstract circuit that contains only 1- and 2-qubit +The output of the ``init`` stage is an abstract circuit that contains only one- and two-qubit operations. When writing :ref:`stage plugins `, the entry point for ``init`` is @@ -280,15 +280,15 @@ * "virtual permutation elision" (see :class:`.ElidePermutations`), where explicit permutation-inducing operations are removed and instead effected as remapping of virtual qubits. -* analysis of the commutation structure of the IR to find pairs of gates that can be cancelled out. -* numerical splitting of two-qubit operations that can be expressed as series of separable one-qubit +* analysis of the commutation structure of the IR to find pairs of gates that can be canceled out. +* numerical splitting of two-qubit operations that can be expressed as a series of separable one-qubit operations. * removal of imperceivable operations, such as tiny-angle Pauli rotations and diagonal operations immediately preceding measurements. .. _transpiler-preset-stage-layout: -Layout Stage +Layout stage ------------ .. seealso:: @@ -320,7 +320,7 @@ :alt: Illustration of how virtual qubits from an input circuit could be mapped to hardware qubits on a backend device's connectivity map. -The layout stage is not responsible for ensuring that the connectivity of the target of the target +The layout stage is not responsible for ensuring that the connectivity of the target is respected all the way through the circuit, nor that all operations are valid for direct execution on the target; these are the responsibilities of the :ref:`routing ` and :ref:`translation ` @@ -438,7 +438,7 @@ choose an initial layout arbitrarily, then to "improve" it by running routing on the circuit, reversing the circuit, and running routing on the reversed circuit with the previous "final" virtual-to-hardware assignment as the initial state. The configured optimization level decides how -many iterations of this to-and-fro we do, and how many different random initial layouts we try. +many iterations of this to-and-fro to do, and how many different random initial layouts to try. The principal difference to the :ref:`default stage ` at optimization levels other than zero is that this plugin *only* runs the Sabre-based algorithm. It @@ -448,7 +448,7 @@ .. _transpiler-preset-stage-routing: -Routing Stage +Routing stage ------------- .. seealso:: @@ -482,10 +482,10 @@ .. note:: - Qiskit's built-in routing plugins will all generally assume that all pairs of qubits with a + Qiskit's built-in routing plugins all generally assume that all pairs of qubits with a defined two-qubit link have a *universal* set of gates defined for those two qubits. Hardware does not necessarily need to respect this (for example, if the only defined two-qubit gate is - ``swap``, then entangling operations like ``cx`` cannot be realised), but Qiskit does not yet + ``swap``, then entangling operations like ``cx`` cannot be realized), but Qiskit does not yet consider this possibility. .. note:: @@ -542,7 +542,7 @@ The optimization level only affects the amount of work the :class:`.VF2PostLayout` step does to attempt to improve the initial layout after routing. -This method typically has incredibly poor output quality. +This method typically has poor output quality. .. _transpiler-preset-stage-routing-stochastic: @@ -598,7 +598,7 @@ .. _transpiler-preset-stage-translation: -Translation Stage +Translation stage ----------------- .. seealso:: @@ -625,7 +625,7 @@ (including Qiskit's built-in plugins) may also use the translation stage as a "fixup" stage after the optimization loop, if the optimization loop returns a circuit that includes non-ISA gates. This latter situation is fairly common; the optimization loop may only be concerned with minimizing -properties like "number of two qubit gates", and will leave its output in terms of locally +properties like "number of two-qubit gates", and will leave its output in terms of locally equivalent gates, which the translation stage can easily rewrite without affecting the target optimization properties. This allows easier separation of concerns between the two stages. Some optimization plugins may be stricter in their output, and so this follow-up to the translation stage @@ -644,7 +644,7 @@ - Symbolic translation of gates to the target basis using known equivalences. * - :ref:`synthesis ` - - Collect each run of one- and two-qubit gates into a matrix representation, and resynthesis + - Collect each run of one- and two-qubit gates into a matrix representation, and resynthesize from there. .. _transpiler-preset-stage-translation-synthesis: @@ -652,17 +652,17 @@ Built-in ``synthesis`` plugin ............................. -Collect runs of gates on the same qubits into matrix form, and then resynthesise using the +Collect runs of gates on the same qubits into matrix form, and then resynthesize using the :class:`.UnitarySynthesis` pass (with the configured ``unitary_synthesis_method``). This is, in large part, similar to the optimization loop itself at high optimization levels. The collection to matrices is typically more expensive than matrix-free translations, but in principle the quality of the translations can be better. In practice, this requires a synthesis algorithm tailored to the target ISA, which makes this method less general than other methods. It -can produce higher quality results when targeting simple ISAs that match the synthesis routines +can produce higher-quality results when targeting simple ISAs that match the synthesis routines already in Qiskit. -If this method is used, one often does not need the optimization loop at all. +If this method is used, you might not need the optimization loop at all. The optimization level has no effect on this plugin. @@ -684,7 +684,7 @@ .. _transpiler-preset-stage-optimization: -Optimization Stage +Optimization stage ------------------ .. seealso:: @@ -693,7 +693,7 @@ .. __: https://docs.quantum.ibm.com/guides/transpiler-stages#optimization-stage -The optimization stage is for low level hardware-aware optimizations. Unlike :ref:`the init stage +The optimization stage is for low-level hardware-aware optimizations. Unlike :ref:`the init stage `, the input to this stage is a circuit that is already ISA-compatible, so a low-level optimization plugin can be tailored for a particular ISA. @@ -702,8 +702,8 @@ such as the :class:`.DoWhileController`, and may choose to include the configured translation stage as a fix-up pipeline. -Qiskit's built-in optimization plugins are general, and apply well to most real-word ISAs for -non-error-corrected devices. The built-in plugins are less well suited to ISAs that have no +Qiskit's built-in optimization plugins are general, and apply well to most real-world ISAs for +non-error-corrected devices. The built-in plugins are less well-suited to ISAs that have no continuously parametrized single-qubit gate, such as a Clifford+T basis set. When writing :ref:`stage plugins `, the entry point for @@ -749,7 +749,7 @@ .. _transpiler-preset-stage-scheduling: -Scheduling Stage +Scheduling stage ---------------- .. seealso:: @@ -777,7 +777,7 @@ - Summary * - :ref:`default ` - - Attempt to satisfy timing alignment constraints without otherwise scheduling + - Attempt to satisfy timing alignment constraints without otherwise scheduling. * - :ref:`alap ` - Schedule the circuit preferring operations to be as late as possible. @@ -811,7 +811,7 @@ :class:`.ASAPScheduleAnalysis` algorithm to decide where to place gates. -Custom Pass Managers +Custom pass managers ==================== In addition to modifying preset pass managers, it is also possible to construct a pass @@ -819,7 +819,7 @@ circuits. You can use the :class:`~.StagedPassManager` class directly to do this. You can define arbitrary stage names and populate them with a :class:`~.PassManager` instance. For example, the following code creates a new :class:`~.StagedPassManager` -that has 2 stages, ``init`` and ``translation``. +that has two stages, ``init`` and ``translation``. .. code-block:: @@ -1171,7 +1171,7 @@ .. _transpiler-scheduling-description: -Scheduling of Circuits +Scheduling of circuits ====================== .. @@ -1252,7 +1252,7 @@ such as :class:`~.PadDelay` or :class:`~.PadDynamicalDecoupling` is run to insert the instructions into the circuit, which completes the scheduling. -Scheduling Analysis with control flow instructions +Scheduling analysis with control-flow instructions -------------------------------------------------- When running scheduling analysis passes on a circuit, you must keep in mind that there @@ -1451,7 +1451,7 @@ Transpiler API ============== -Hardware Description +Hardware description -------------------- .. autosummary:: diff --git a/qiskit/transpiler/preset_passmanagers/__init__.py b/qiskit/transpiler/preset_passmanagers/__init__.py index b8d96bca48fc..17118e1c5d6e 100644 --- a/qiskit/transpiler/preset_passmanagers/__init__.py +++ b/qiskit/transpiler/preset_passmanagers/__init__.py @@ -32,7 +32,7 @@ .. _preset_pass_manager_generators: -Low-level Preset Pass Manager Generation +Low-level preset pass manager generation ---------------------------------------- .. autofunction:: level_0_pass_manager @@ -46,7 +46,7 @@ .. _stage_generators: -Stage Generator Functions +Stage generator functions ------------------------- .. autofunction:: generate_control_flow_options_check diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index ad2cd7e2d916..74411e2fca70 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -37,8 +37,8 @@ Plugin Stages ============= -Currently, there are 6 stages in the preset pass managers, all of which actively -load external plugins via corresponding entry points. The table below gives a quick summary of each +Currently, there are six stages in the preset pass managers, all of which actively +load external plugins using corresponding entry points. The following table gives a quick summary of each stage. For more details on the description and expectations of each stage, follow the link in the stages' names to the full documentation. @@ -54,7 +54,7 @@ - ``qiskit.transpiler.init`` - ``default`` - High-level, logical optimizations on abstract circuits, and reduction of multi-qubit - operations to 1- and 2-qubit operations. + operations to one- and two-qubit operations. * - :ref:`layout ` - ``qiskit.transpiler.layout`` From 3414146d5faea78b21d73627e2e4321af6c1574f Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Wed, 22 Jan 2025 18:07:34 +0000 Subject: [PATCH 05/13] Add manual copy-editing suggestions --- qiskit/transpiler/__init__.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index cfc0a08c704e..233fabf7a4bb 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -137,8 +137,7 @@ argument to the generator functions. This stochasticity is because many of the problems the transpiler must solve are known to be - non-polynomial in complexity, and we need to ensure we finish the job in a workable amount of - time. + non-polynomial in complexity, but transpilation must complete in a workable amount of time. Choosing preset stage implementations ------------------------------------- @@ -177,10 +176,10 @@ internal construction of the :class:`.PassManager` representing the stage is not, however; the order of passes might change between minor versions, or new passes might be introduced. - For any stage that has one, the method named ``"default"`` is most subject to change. We will - typically only make complete algorithmic changes in the default method across a major-version - boundary, but we may well rebalance heuristics and add new passes to default methods between - minor versions. + For any stage that has one, the method named ``"default"`` is most subject to change. Qiskit + will typically only make complete algorithmic changes in the default method across a + major-version boundary, but it may well rebalance heuristics and add new passes to default + methods between minor versions. Since the output of :func:`.generate_preset_pass_manager` is a :class:`.StagedPassManager`, you can also modify the pass manager after its creation to provide an entirely custom stage implementation. From ac0098c847b083ca8077e99d082a1668b2c9afe1 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Wed, 22 Jan 2025 19:46:14 +0000 Subject: [PATCH 06/13] Reflow text --- qiskit/transpiler/preset_passmanagers/plugin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index 74411e2fca70..33fa879728c0 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -38,9 +38,9 @@ ============= Currently, there are six stages in the preset pass managers, all of which actively -load external plugins using corresponding entry points. The following table gives a quick summary of each -stage. For more details on the description and expectations of each stage, follow the link in the -stages' names to the full documentation. +load external plugins using corresponding entry points. The following table gives a quick summary +of each stage. For more details on the description and expectations of each stage, follow the link +in the stages' names to the full documentation. .. list-table:: Stages :header-rows: 1 From 80ca1e00d70677d18e92093da0bf43d80e1dc396 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Tue, 4 Feb 2025 10:44:21 +0000 Subject: [PATCH 07/13] Copy edit Co-authored-by: Rebecca Dimock <66339736+beckykd@users.noreply.github.com> --- qiskit/transpiler/__init__.py | 98 +++++++++---------- .../transpiler/preset_passmanagers/plugin.py | 6 +- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index 233fabf7a4bb..d3289355096d 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -41,13 +41,13 @@ pipeline is a :class:`.PassManager` object, whose :meth:`.PassManager.run` method takes in a :class:`.QuantumCircuit` and converts it to a :class:`.DAGCircuit`, then subjects the IR to a sequence of *passes*, finally returning a :class:`.QuantumCircuit` back. A pass is either an -:class:`.AnalysisPass`, whose purpose is to calculate and store properties about the circuit in the -stateful :class:`.PropertySet`, or a :class:`.TransformationPass`, whose purpose is to modify the IR +:class:`.AnalysisPass`, which calculates and stores properties about the circuit in the +stateful :class:`.PropertySet`, or a :class:`.TransformationPass`, which modifies the IR to achieve a particular singular goal. You can think of a pipeline as being split into "stages", where each stage is responsible for one high-level transformation. Qiskit exposes a default transpilation pipeline builder using the function -:func:`.generate_preset_pass_manager`. This will return a properly configured pipeline for complete +:func:`.generate_preset_pass_manager`. This returns a properly configured pipeline for complete transpilation, at a chosen ``optimization_level`` (between 0 and 3, inclusive). Unless you are looking for something highly specialized, this is almost certainly the entry point you want. A sample transpilation looks like: @@ -69,7 +69,7 @@ # ... and use it (as many times as you like). physical = pm.run(abstract) -For most use cases, this is all you will need. +For most use cases, this is all you need. All of Qiskit's transpiler infrastructure is highly extensible and configurable, however. The rest of this page details how to harness the low-level capabilities of the transpiler stack. @@ -89,27 +89,27 @@ ``init`` Abstract-circuit optimizations, and reduction of multi-qubit operations to one- and two-qubit - operations. See :ref:`transpiler-preset-stage-init` for more detail. + operations. See :ref:`transpiler-preset-stage-init` for more details. ``layout`` Choose an initial mapping of virtual qubits to physical qubits, including expansion of the circuit to contain explicit ancillas. This stage sometimes subsumes ``routing``. See - :ref:`transpiler-preset-stage-layout` for more detail. + :ref:`transpiler-preset-stage-layout` for more details. ``routing`` - Insert gates into the circuit to ensure it matches connectivity constraints of the + Insert gates into the circuit to ensure it matches the connectivity constraints of the :class:`.Target`. The inserted gates need not match the target ISA yet, so are often just ``swap`` instructions. This stage is sometimes omitted, when the ``layout`` stage handles its - job. See :ref:`transpiler-preset-stage-routing` for more detail. + job. See :ref:`transpiler-preset-stage-routing` for more details. ``translation`` Convert all gates in the circuit to ones matching the :class:`Target`\\ s ISA. See - :ref:`transpiler-preset-stage-translation` for more detail. + :ref:`transpiler-preset-stage-translation` for more details. ``optimization`` Low-level, hardware-aware optimizations. Unlike the abstract optimizations of the ``init`` stage, this stage acts on a physical circuit. See :ref:`transpiler-preset-stage-optimization` - for more detail. + for more details. ``scheduling`` Insert :class:`~.circuit.Delay` instructions to make the wall-clock timing of a circuit @@ -118,10 +118,10 @@ :ref:`transpiler-preset-stage-scheduling` for more details. The preset transpiler pipelines can also be configured at a high level by setting an -``optimization_level``. This is an integer from 0 to 3 inclusive, indicating the relative effort to +``optimization_level``. This is an integer from 0 to 3, inclusive, indicating the relative effort to exert in attempting to optimize the circuit for the hardware. Level 0 disables all unnecessary -optimizations; only transformations needed to make the circuit runnable at all will be present. On -the other end, level 3 enables a full barrage of optimization techniques, some of which can be very +optimizations; only transformations needed to make the circuit runnable are used. On +the other end, level 3 enables a full range of optimization techniques, some of which can be very expensive in compilation time. Similar to classical compilers, optimization level 3 is not always guaranteed to produce the best results. Qiskit defaults to optimization level 2, as a trade-off between compilation time and the expected amount of optimization. @@ -142,7 +142,7 @@ Choosing preset stage implementations ------------------------------------- -Qiskit includes several implementations of several of the above stages, and more can be installed as +Qiskit includes several implementations of the above stages, and more can be installed as separate "plugins". To control which implementation of a stage is used, pass its name to the ``_method`` keyword argument of the two functions, such as ``translation_method="translator"``. To read more about implementing such external plugins for a @@ -176,16 +176,16 @@ internal construction of the :class:`.PassManager` representing the stage is not, however; the order of passes might change between minor versions, or new passes might be introduced. - For any stage that has one, the method named ``"default"`` is most subject to change. Qiskit - will typically only make complete algorithmic changes in the default method across a - major-version boundary, but it may well rebalance heuristics and add new passes to default + For any stage that has one, the method named ``"default"`` is the most subject to change. Qiskit + typically only makes complete algorithmic changes in the default method across a + major-version boundary, but it might rebalance heuristics and add new passes to default methods between minor versions. Since the output of :func:`.generate_preset_pass_manager` is a :class:`.StagedPassManager`, you can also modify the pass manager after its creation to provide an entirely custom stage implementation. For example, if you wanted to run a custom scheduling stage using dynamical decoupling (using the :class:`~.PadDynamicalDecoupling` pass) and also add initial logical optimization prior to routing, -you would do something like (building off the previous example): +you would do something like the following (building off the previous example): .. plot:: :include-source: @@ -277,12 +277,12 @@ At optimization levels 2 and 3, the default plugin enables a much wider range of abstract optimizations. This includes: -* "virtual permutation elision" (see :class:`.ElidePermutations`), where explicit +* "Virtual permutation elision" (see :class:`.ElidePermutations`), where explicit permutation-inducing operations are removed and instead effected as remapping of virtual qubits. -* analysis of the commutation structure of the IR to find pairs of gates that can be canceled out. -* numerical splitting of two-qubit operations that can be expressed as a series of separable one-qubit +* Analysis of the commutation structure of the IR to find pairs of gates that can be canceled out. +* Numerical splitting of two-qubit operations that can be expressed as a series of separable one-qubit operations. -* removal of imperceivable operations, such as tiny-angle Pauli rotations and diagonal operations +* Removal of imperceivable operations, such as tiny-angle Pauli rotations and diagonal operations immediately preceding measurements. .. _transpiler-preset-stage-layout: @@ -311,7 +311,7 @@ At any given point in a circuit, we can identify a mapping between currently active "virtual" qubits of the input circuit to hardware qubits of the backend. A hardware qubit can only ever represent a single virtual qubit at a given point, but the mapping might vary over the course of the circuit. -In principle, some virtual qubits may not necessarily be mapped at all points in the circuit +In principle, some virtual qubits might not be mapped at all points in the circuit execution, if the lifetime of a virtual qubit state can be shortened, though Qiskit's built-in pipelines do not use this currently. @@ -330,18 +330,18 @@ pipelines; the default plugin for layout even tries several different algorithms (described in more detail in :ref:`transpiler-preset-stage-layout-default`). -The ideal situation for the layout stage is to find a "perfect" layout, which causes all operations -to already respect the connectivity constraints of the :class:`.Target` such that the routing stage +The ideal situation for the layout stage is to find a "perfect" layout, where all operations +respect the connectivity constraints of the :class:`.Target` such that the routing stage is not required. This is typically not possible for arbitrary input circuits, but when it is, the -:class:`.VF2Layout` pass can be used to find an valid initial layout. If multiple perfect layouts -are found, a scoring heuristic based on estimated error rates is used to decide. +:class:`.VF2Layout` pass can be used to find a valid initial layout. If multiple perfect layouts +are found, a scoring heuristic based on estimated error rates is used to decide which one to use. In all built-in plugins, passing the :func:`.generate_preset_pass_manager` argument ``initial_layout`` causes the given layout to be used verbatim, skipping the individual "choosing" logic. All built-in plugins also handle embedding the circuit into the full width of the device, including assigning ancillas. -If writing your own layout plugin, you might find :func:`.generate_embed_passmanager` useful for +If you write your own layout plugin, you might find :func:`.generate_embed_passmanager` useful for automating the "embedding" stage of the layout application. When writing :ref:`stage plugins `, the entry point for ``layout`` @@ -362,13 +362,13 @@ initial qubits. * - :ref:`trivial ` - - Maps virtual qubit 0 to physical qubit 0, etc. + - Maps virtual qubit 0 to physical qubit 0, and so on. * - :ref:`sabre ` - Uses `Qiskit's enhanced Sabre layout algorithm `_. At all optimization levels, the default layout method is ``default``, though the structure of this -stage itself changes dramatically based on the level. +stage changes dramatically based on the level. .. _transpiler-preset-stage-layout-default: @@ -382,7 +382,7 @@ At optimization levels above 0, there is a two-step process: #. First, use :class:`.VF2Layout` to attempt to find a "perfect" layout. The maximum number of - calls to the isomorphism evaluator increases with optimization level. For huge, complex targets, + calls to the isomorphism evaluator increases with the optimization level. For huge, complex targets, we are not guaranteed to find perfect layouts even if they exist, but the chance increases with the optimization level. @@ -390,7 +390,7 @@ the numbers of initial layout trials, swap-map trials, and forwards–backwards iterations increasing with the optimization level. -In addition, optimization level also attempts the trivial layout before the VF2-based version, for +In addition, optimization levels above 0 also try the trivial layout before the VF2-based version, for historical backwards compatibility. @@ -433,14 +433,14 @@ routing algorithm ` as the subroutine to swap-map the candidate circuit both forwards and backwards. -Summarily, the layout component of `the original Sabre algorithm `_ is to -choose an initial layout arbitrarily, then to "improve" it by running routing on the circuit, +Summarily, the layout component of `the original Sabre algorithm `_ +chooses an initial layout arbitrarily, then tries to "improve" it by running routing on the circuit, reversing the circuit, and running routing on the reversed circuit with the previous "final" virtual-to-hardware assignment as the initial state. The configured optimization level decides how many iterations of this to-and-fro to do, and how many different random initial layouts to try. The principal difference to the :ref:`default stage ` at -optimization levels other than zero is that this plugin *only* runs the Sabre-based algorithm. It +optimization levels other than 0 is that this plugin *only* runs the Sabre-based algorithm. It does not attempt to find a perfect layout, nor attempt the trivial layout. @@ -456,7 +456,7 @@ The routing stage ensures that the virtual connectivity graph of the circuit is compatible with the hardware connectivity graph of the target. In simpler terms, the routing stage makes sure that all -two-qubit gates in the circuit take place on hardware qubits that have a defined two-qubit operation +two-qubit gates in the circuit are mapped to hardware qubits that have a defined two-qubit operation in the target ISA. You may also see this problem referred to as the "mapping" or "swap-mapping" problem in other toolkits or literature. @@ -473,7 +473,7 @@ the :class:`.PropertySet` if routing has taken place. All of Qiskit's built-in routing stages will additionally run the :class:`.VF2PostLayout` pass after -routing. This may choose to reassign the initial layout, if lower-error qubits can be found. This +routing. This might reassign the initial layout, if lower-error qubits can be found. This pass is very similar to the :class:`.VF2Layout` class that :ref:`the default layout plugin ` uses, except in :class:`.VF2PostLayout` we can guarantee that there is at least one isomorphic induced subgraph of the target topology that matches the @@ -491,7 +491,7 @@ Finding the minimal number of swaps to insert is known to be a non-polynomial problem. This means it is prohibitively expensive to attempt, so many of Qiskit's built-in algorithms are - stochastic, and you may see large variation between different compilations. If you need + stochastic, and you may see large variations between different compilations. If you need reproducibility, be sure to set the ``seed_transpiler`` argument of :func:`.generate_preset_pass_manager` or :func:`.transpile`. @@ -568,7 +568,7 @@ at producing a swap network, where the tree being explored is pruned down to a small number of candidate swaps at each depth. -This algorithm is somewhat akin to the ``basic`` heuristic of :ref:`the "sabre" plugin +This algorithm is similar to the ``basic`` heuristic of :ref:`the "sabre" plugin `, except it considers the following effects of each swap to a small depth as well. @@ -613,9 +613,9 @@ .. note:: - In the Qiskit 1.x series, translation plugins need not output gates with the correct + In Qiskit 1.x, translation plugins need not output gates with the correct directionality, provided the gate exists with opposite directionality on the given qubit pair. - For example, if ``cx(0, 1)`` is ISA-supported, the translation stage is permitted to output + For example, if ``cx(0, 1)`` is ISA-supported, the translation stage can output ``cx(1, 0)``. This is likely to change in later versions of Qiskit. @@ -655,13 +655,13 @@ :class:`.UnitarySynthesis` pass (with the configured ``unitary_synthesis_method``). This is, in large part, similar to the optimization loop itself at high optimization levels. -The collection to matrices is typically more expensive than matrix-free translations, but in +The collection into matrices is typically more expensive than matrix-free translations, but in principle the quality of the translations can be better. In practice, this requires a synthesis algorithm tailored to the target ISA, which makes this method less general than other methods. It can produce higher-quality results when targeting simple ISAs that match the synthesis routines already in Qiskit. -If this method is used, you might not need the optimization loop at all. +If this method is used, you might not need the optimization loop. The optimization level has no effect on this plugin. @@ -698,7 +698,7 @@ There are very few requirements on an optimization plugin, other than it takes in ISA-supported circuits, and returns ISA-supported circuits. An optimization plugin will often contain a loop, -such as the :class:`.DoWhileController`, and may choose to include the configured translation stage +such as the :class:`.DoWhileController`, and might include the configured translation stage as a fix-up pipeline. Qiskit's built-in optimization plugins are general, and apply well to most real-world ISAs for @@ -730,8 +730,8 @@ At optimization level 0, the stage is empty. -At optimization level 1, the stage does matrix-based resynthesis of runs of 1q gates, and very -simply symbolic inverse cancellation of two-qubit gates, if they appear consecutively. This runs +At optimization level 1, the stage does matrix-based resynthesis of runs of single-qubit gates, and very +simple symbolic inverse cancellation of two-qubit gates, if they appear consecutively. This runs in a loop until the size and depth of the circuit are fixed. At optimization level 2, in addition the optimizations of level 1, the loop contains commutation @@ -779,10 +779,10 @@ - Attempt to satisfy timing alignment constraints without otherwise scheduling. * - :ref:`alap ` - - Schedule the circuit preferring operations to be as late as possible. + - Schedule the circuit, preferring operations to be as late as possible. * - :ref:`asap ` - - Schedule the circuit preferring operations to be as soon as possible. + - Schedule the circuit, preferring operations to be as soon as possible. .. _transpiler-preset-stage-scheduling-default: @@ -791,7 +791,7 @@ Do nothing, unless the circuit already contains instructions with explicit timings. If there are explicitly timed operations in the circuit, insert additional padding to ensure that these timings -satisfy the alignment and other constraints of the hardware. +satisfy the alignment and other hardware constraints. .. _transpiler-preset-stage-scheduling-alap: diff --git a/qiskit/transpiler/preset_passmanagers/plugin.py b/qiskit/transpiler/preset_passmanagers/plugin.py index 33fa879728c0..d75de993e965 100644 --- a/qiskit/transpiler/preset_passmanagers/plugin.py +++ b/qiskit/transpiler/preset_passmanagers/plugin.py @@ -37,9 +37,9 @@ Plugin Stages ============= -Currently, there are six stages in the preset pass managers, all of which actively -load external plugins using corresponding entry points. The following table gives a quick summary -of each stage. For more details on the description and expectations of each stage, follow the link +There are six stages in the preset pass managers, all of which actively +load external plugins using corresponding entry points. The following table summarizes +each stage. For more details on the description and expectations of each stage, follow the link in the stages' names to the full documentation. .. list-table:: Stages From 34ce928463802aa62f29cf544d09f3e2d33325e8 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Tue, 4 Feb 2025 10:48:46 +0000 Subject: [PATCH 08/13] Add missed copy edit Co-authored-by: Rebecca Dimock <66339736+beckykd@users.noreply.github.com> --- qiskit/transpiler/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index d3289355096d..2416169c08c0 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -564,7 +564,7 @@ Built-in ``lookahead`` plugin ............................. -Uses the :class:`.LookaheadSwap` algorithm to route. Approximately, this is a breadth-first search +Uses the :class:`.LookaheadSwap` algorithm to route. This is essentially a breadth-first search at producing a swap network, where the tree being explored is pruned down to a small number of candidate swaps at each depth. From dcaabc2bc2a766c6b82dfc1a11be890357bf47ce Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Tue, 4 Feb 2025 10:53:06 +0000 Subject: [PATCH 09/13] Reflow line breaks --- qiskit/transpiler/__init__.py | 48 +++++++++++++++++------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index 2416169c08c0..9f9ebe07ccb5 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -39,8 +39,8 @@ Qiskit uses the graph-based :class:`.DAGCircuit` intermediate representation (IR) of a circuit throughout the transpiler stack, rather than the tree-based :class:`.QuantumCircuit`. A transpiler pipeline is a :class:`.PassManager` object, whose :meth:`.PassManager.run` method takes in a -:class:`.QuantumCircuit` and converts it to a :class:`.DAGCircuit`, then subjects the IR to a sequence -of *passes*, finally returning a :class:`.QuantumCircuit` back. A pass is either an +:class:`.QuantumCircuit` and converts it to a :class:`.DAGCircuit`, then subjects the IR to a +sequence of *passes*, finally returning a :class:`.QuantumCircuit` back. A pass is either an :class:`.AnalysisPass`, which calculates and stores properties about the circuit in the stateful :class:`.PropertySet`, or a :class:`.TransformationPass`, which modifies the IR to achieve a particular singular goal. You can think of a pipeline as being split into @@ -79,9 +79,9 @@ ==================== The function :func:`.generate_preset_pass_manager` creates the "preset pass managers". -These are all instances of :class:`.PassManager`, so are used by passing a :class:`.QuantumCircuit` to -the :meth:`.PassManager.run` method. More specifically, the preset pass managers are instances of -:class:`.StagedPassManager`, which allows greater configuration of the individual stages of a +These are all instances of :class:`.PassManager`, so are used by passing a :class:`.QuantumCircuit` +to the :meth:`.PassManager.run` method. More specifically, the preset pass managers are instances +of :class:`.StagedPassManager`, which allows greater configuration of the individual stages of a transpilation, include pre- and post-stage hooks. A preset pass manager has up to six named stages. These are summarized, in order of execution, @@ -176,8 +176,8 @@ internal construction of the :class:`.PassManager` representing the stage is not, however; the order of passes might change between minor versions, or new passes might be introduced. - For any stage that has one, the method named ``"default"`` is the most subject to change. Qiskit - typically only makes complete algorithmic changes in the default method across a + For any stage that has one, the method named ``"default"`` is the most subject to change. + Qiskit typically only makes complete algorithmic changes in the default method across a major-version boundary, but it might rebalance heuristics and add new passes to default methods between minor versions. @@ -280,8 +280,8 @@ * "Virtual permutation elision" (see :class:`.ElidePermutations`), where explicit permutation-inducing operations are removed and instead effected as remapping of virtual qubits. * Analysis of the commutation structure of the IR to find pairs of gates that can be canceled out. -* Numerical splitting of two-qubit operations that can be expressed as a series of separable one-qubit - operations. +* Numerical splitting of two-qubit operations that can be expressed as a series of separable + one-qubit operations. * Removal of imperceivable operations, such as tiny-angle Pauli rotations and diagonal operations immediately preceding measurements. @@ -382,16 +382,16 @@ At optimization levels above 0, there is a two-step process: #. First, use :class:`.VF2Layout` to attempt to find a "perfect" layout. The maximum number of - calls to the isomorphism evaluator increases with the optimization level. For huge, complex targets, - we are not guaranteed to find perfect layouts even if they exist, but the chance increases with - the optimization level. + calls to the isomorphism evaluator increases with the optimization level. For huge, complex + targets, we are not guaranteed to find perfect layouts even if they exist, but the chance + increases with the optimization level. #. If no perfect layout can be found, use :class:`.SabreLayout` to choose an initial layout, with the numbers of initial layout trials, swap-map trials, and forwards–backwards iterations increasing with the optimization level. -In addition, optimization levels above 0 also try the trivial layout before the VF2-based version, for -historical backwards compatibility. +In addition, optimization levels above 0 also try the trivial layout before the VF2-based version, +for historical backwards compatibility. .. _transpiler-preset-stage-layout-dense: @@ -433,7 +433,7 @@ routing algorithm ` as the subroutine to swap-map the candidate circuit both forwards and backwards. -Summarily, the layout component of `the original Sabre algorithm `_ +Summarily, the layout component of `the original Sabre algorithm `_ chooses an initial layout arbitrarily, then tries to "improve" it by running routing on the circuit, reversing the circuit, and running routing on the reversed circuit with the previous "final" virtual-to-hardware assignment as the initial state. The configured optimization level decides how @@ -505,8 +505,8 @@ - Summary * - :ref:`sabre ` - - Default. Uses `Qiskit's modified Sabre routing algorithm `_ to swap - map. + - Default. Uses `Qiskit's modified Sabre routing algorithm `_ to + swap map. * - :ref:`none ` - Disable routing. Raises an error if routing is required. @@ -730,9 +730,9 @@ At optimization level 0, the stage is empty. -At optimization level 1, the stage does matrix-based resynthesis of runs of single-qubit gates, and very -simple symbolic inverse cancellation of two-qubit gates, if they appear consecutively. This runs -in a loop until the size and depth of the circuit are fixed. +At optimization level 1, the stage does matrix-based resynthesis of runs of single-qubit gates, and +very simple symbolic inverse cancellation of two-qubit gates, if they appear consecutively. This +runs in a loop until the size and depth of the circuit are fixed. At optimization level 2, in addition the optimizations of level 1, the loop contains commutation analysis of sets of gates to widen the range of gates that can be considered for cancellation. @@ -848,10 +848,10 @@ There is no limit on the number of stages you can put in a :class:`~.StagedPassManager`. The stages do not need to correspond to the stages used by Qiskit's preset pipelines. -The :ref:`stage_generators` may be useful for the construction of custom :class:`~.StagedPassManager`s. -They generate pass managers which provide common functionality used in many stages. -For example, :func:`~.generate_embed_passmanager` generates a :class:`~.PassManager` -to "embed" a selected initial :class:`~.Layout` from a layout pass to the specified target device. +The :ref:`stage_generators` may be useful for the construction of custom :class:`~.StagedPassManager` +instances. They generate pass managers which provide common functionality used in many stages. For +example, :func:`~.generate_embed_passmanager` generates a :class:`~.PassManager` to "embed" a +selected initial :class:`~.Layout` from a layout pass to the specified target device. Representing Quantum Computers ============================== From c16d18dd684dcfa6637007192be548d4059838cc Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Tue, 4 Feb 2025 18:44:25 +0000 Subject: [PATCH 10/13] Apply Elena's suggestions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Elena Peña Tapia <57907331+ElePT@users.noreply.github.com> --- qiskit/transpiler/__init__.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index 9f9ebe07ccb5..cac198f0932e 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -50,7 +50,7 @@ :func:`.generate_preset_pass_manager`. This returns a properly configured pipeline for complete transpilation, at a chosen ``optimization_level`` (between 0 and 3, inclusive). Unless you are looking for something highly specialized, this is almost certainly the entry point you want. A -sample transpilation looks like: +sample transpilation looks like:: from qiskit.circuit import QuantumCircuit from qiskit.transpiler import generate_preset_pass_manager @@ -82,7 +82,7 @@ These are all instances of :class:`.PassManager`, so are used by passing a :class:`.QuantumCircuit` to the :meth:`.PassManager.run` method. More specifically, the preset pass managers are instances of :class:`.StagedPassManager`, which allows greater configuration of the individual stages of a -transpilation, include pre- and post-stage hooks. +transpilation, including pre- and post-stage hooks. A preset pass manager has up to six named stages. These are summarized, in order of execution, below, with more in-depth information in the following subsections. @@ -136,7 +136,7 @@ to ensure reproducibility of a compilation, pass a known integer to the ``seed_transpiler`` argument to the generator functions. - This stochasticity is because many of the problems the transpiler must solve are known to be + This stochasticity arises because many of the problems the transpiler must solve are known to be non-polynomial in complexity, but transpilation must complete in a workable amount of time. Choosing preset stage implementations @@ -399,7 +399,7 @@ Built-in ``dense`` plugin ......................... -Uses the class:`.DenseLayout` pass to choose the layout. This pass finds the densest connected +Uses the :class:`.DenseLayout` pass to choose the layout. This pass finds the densest connected subgraph of the complete target connectivity graph, where "densest" means that hardware qubits with the greatest number of available connections are preferred. The virtual-to-hardware mapping is completed by assigning the highest-degree virtual qubits to the highest-degree hardware qubits. @@ -469,7 +469,7 @@ gate defined in the :class:`.Target` for any pair of hardware qubits that has a gate applied in the circuit. -The routing stage must set the properties ``final_layout`` and ``virtual_permutation_layout`` in +The routing stage must set the ``final_layout`` and ``virtual_permutation_layout`` properties in the :class:`.PropertySet` if routing has taken place. All of Qiskit's built-in routing stages will additionally run the :class:`.VF2PostLayout` pass after @@ -820,7 +820,9 @@ instance. For example, the following code creates a new :class:`~.StagedPassManager` that has two stages, ``init`` and ``translation``. -.. code-block:: +.. plot:: + :include-source: + :nofigs: from qiskit.transpiler.passes import ( UnitarySynthesis, From 828756cb506ebfebac5d22c4798730f738503bc1 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Tue, 4 Feb 2025 18:44:52 +0000 Subject: [PATCH 11/13] Apply Elena's suggestions that needed line wraps --- qiskit/transpiler/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index cac198f0932e..dde8df540d84 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -103,7 +103,7 @@ job. See :ref:`transpiler-preset-stage-routing` for more details. ``translation`` - Convert all gates in the circuit to ones matching the :class:`Target`\\ s ISA. See + Convert all gates in the circuit to ones matching the ISA of the :class:`Target`. See :ref:`transpiler-preset-stage-translation` for more details. ``optimization`` @@ -305,8 +305,9 @@ .. note:: - All built-in plugins for the layout stage will defer to an explicit layout selected using the - ``initial_layout`` argument to :func:`.generate_preset_pass_manager` or :func:`.transpile`. + All built-in plugins for the layout stage will give priority to an explicit layout selected + using the ``initial_layout`` argument to :func:`.generate_preset_pass_manager` or + :func:`.transpile`. At any given point in a circuit, we can identify a mapping between currently active "virtual" qubits of the input circuit to hardware qubits of the backend. A hardware qubit can only ever represent a From 438f9a69e7504f6cc9cefa157a587858e8a4ed61 Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Wed, 5 Feb 2025 12:56:02 +0000 Subject: [PATCH 12/13] Correct layout description --- qiskit/transpiler/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index dde8df540d84..bef1fc2a2ff1 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -391,7 +391,7 @@ the numbers of initial layout trials, swap-map trials, and forwards–backwards iterations increasing with the optimization level. -In addition, optimization levels above 0 also try the trivial layout before the VF2-based version, +In addition, optimization level 1 also tries the trivial layout before the VF2-based version, for historical backwards compatibility. From 397e66e282597e743a97c062c4b41bdcffc1c4dc Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Wed, 5 Feb 2025 14:54:35 +0000 Subject: [PATCH 13/13] Mark stochastic plugin as deprecated --- qiskit/transpiler/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qiskit/transpiler/__init__.py b/qiskit/transpiler/__init__.py index bef1fc2a2ff1..696df5151cc3 100644 --- a/qiskit/transpiler/__init__.py +++ b/qiskit/transpiler/__init__.py @@ -549,6 +549,9 @@ Built-in ``stochastic`` plugin .............................. +.. deprecated:: 1.3 + Use :ref:`transpiler-preset-stage-routing-sabre` instead. + Uses the :class:`.StochasticSwap` algorithm to route. In short, this stratifies the circuit into layers, then uses a stochastic algorithm to find a permutation that will allow the layer to execute, and a series of swaps that will implement that permutation in a hardware-valid way.