qiskit/releasenotes/notes/0.20/update-instruction-alignmen...

160 lines
8.0 KiB
YAML

---
features:
- |
Introduced a new transpiler pass workflow for building :class:`~.PassManager` objects
for scheduling :class:`~.QuantumCircuit` objects in the transpiler. In the new
workflow, scheduling and alignment passes are all :class:`~.AnalysisPass` objects that
only update the pass manager's property set, specifically a new property set item
``node_start_time``, which holds the absolute start time of each opnode. A separate
:class:`~.TransformationPass` such as :class:`~.PadDelay` is subsequently used
to apply scheduling to the DAG. This new workflow is both more efficient and can
correct for additional timing constraints exposed by a backend.
Previously, the pass chain would have been implemented as ``scheduling -> alignment``
which were both transform passes thus there were multiple :class:`~.DAGCircuit`
instances recreated during each pass. In addition, scheduling occurred in each pass
to obtain instruction start time. Now the required pass chain becomes
``scheduling -> alignment -> padding`` where the :class:`~.DAGCircuit` update only
occurs at the end with the ``padding`` pass.
For those who are creating custom :class:`~.PassManager` objects that involve
circuit scheduling you will need to adjust your :class:`~.PassManager`
to insert one of the :class:`~.BasePadding` passes (currently
either :class:`~.PadDelay` or :class:`~.PadDynamicalDecoupling` can be used)
at the end of the scheduling pass chain. Without the padding pass the scheduling
passes will not be reflected in the output circuit of the :meth:`~.PassManager.run`
method of your custom :class:`~.PassManager`.
For example, if you were previously building your :class:`~.PassManager`
with something like::
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import TimeUnitConversion, ALAPSchedule, ValidatePulseGates, AlignMeasures
pm = PassManager()
scheduling = [
ALAPSchedule(instruction_durations), PadDelay()),
ValidatePulseGates(granularity=timing_constraints.granularity, min_length=timing_constraints.min_length),
AlignMeasures(alignment=timing_constraints.acquire_alignment),
]
pm.append(scheduling)
you can instead use::
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import TimeUnitConversion, ALAPScheduleAnalysis, ValidatePulseGates, AlignMeasures, PadDelay
pm = PassManager()
scheduling = [
ALAPScheduleAnalysis(instruction_durations), PadDelay()),
ConstrainedReschedule(acquire_alignment=timing_constraints.acquire_alignment, pulse_alignment=timing_constraints.pulse_alignment),
ValidatePulseGates(granularity=timing_constraints.granularity, min_length=timing_constraints.min_length),
PadDelay()
]
pm.append(scheduling)
which will both be more efficient and also align instructions based on any hardware constraints.
- |
Added a new transpiler pass :class:`~.ConstrainedReschedule` pass.
The :class:`~.ConstrainedReschedule` pass considers both hardware
alignment constraints that can be definied in a :class:`.BackendConfiguration`
object, ``pulse_alignment`` and ``acquire_alignment``. This new class superscedes
the previously existing :class:`~.AlignMeasures` as it performs the same alignment
(via the property set) for measurement instructions in addition to general instruction
alignment. By setting the ``acquire_alignment`` constraint argument for the
:class:`~.ConstrainedReschedule` pass it is a drop-in replacement of
:class:`~.AlignMeasures` when paired with a new :class:`~.BasePadding` pass.
- |
Added two new transpiler passes :class:`~.ALAPScheduleAnalysis` and :class:`~.ASAPScheduleAnalysis`
which superscede the :class:`~.ALAPSchedule` and :class:`~.ASAPSchedule` as part of the
reworked transpiler workflow for scheduling. The new passes perform the same scheduling but
in the property set and relying on a :class:`~.BasePadding` pass to adjust the circuit
based on all the scheduling alignment analysis.
The standard behavior of these passes also aligns timing ordering with the topological
ordering of the DAG nodes. This change may affect the scheduling outcome if it includes
conditional operations, or simultaneously measuring two qubits with the same classical
register (edge-case). To reproduce conventional behavior, set ``clbit_write_latency``
identical to the measurement instruction length.
For example, consider scheduling an input circuit like:
.. code-block:: text
┌───┐┌─┐
q_0: ┤ X ├┤M├──────────────
└───┘└╥┘ ┌───┐
q_1: ──────╫────┤ X ├──────
║ └─╥─┘ ┌─┐
q_2: ──────╫──────╫─────┤M├
║ ┌────╨────┐└╥┘
c: 1/══════╩═╡ c_0=0x1 ╞═╩═
0 └─────────┘ 0
.. code-block::
from qiskit import QuantumCircuit
from qiskit.transpiler import InstructionDurations, PassManager
from qiskit.transpiler.passes import ALAPScheduleAnalysis, PadDelay, SetIOLatency
from qiskit.visualization.timeline import draw
circuit = QuantumCircuit(3, 1)
circuit.x(0)
circuit.measure(0, 0)
circuit.x(1).c_if(0, 1)
circuit.measure(2, 0)
durations = InstructionDurations([("x", None, 160), ("measure", None, 800)])
pm = PassManager(
[
SetIOLatency(clbit_write_latency=800, conditional_latency=0),
ALAPScheduleAnalysis(durations),
PadDelay(),
]
)
draw(pm.run(circuit))
As you can see in the timeline view, the measurement on ``q_2`` starts before
the conditional X gate on the ``q_1``, which seems to be opposite to the
topological ordering of the node. This is also expected behavior
because clbit write-access happens at the end edge of the measure instruction,
and the read-access of the conditional gate happens the begin edge of the instruction.
Thus topological ordering is preserved on the timeslot of the classical register,
which is not captured by the timeline view.
However, this assumes a paticular microarchitecture design, and the circuit is
not necessary scheduled like this.
By using the default configuration of passes, the circuit is schedule like below.
.. code-block::
from qiskit import QuantumCircuit
from qiskit.transpiler import InstructionDurations, PassManager
from qiskit.transpiler.passes import ALAPScheduleAnalysis, PadDelay
from qiskit.visualization.timeline import draw
circuit = QuantumCircuit(3, 1)
circuit.x(0)
circuit.measure(0, 0)
circuit.x(1).c_if(0, 1)
circuit.measure(2, 0)
durations = InstructionDurations([("x", None, 160), ("measure", None, 800)])
pm = PassManager([ALAPScheduleAnalysis(durations), PadDelay()])
draw(pm.run(circuit))
Note that clbit is locked throughout the measurement instruction interval.
This behavior is designed based on the Qiskit Pulse, in which the acquire instruction takes
``AcquireChannel`` and ``MemorySlot`` which are not allowed to overlap with other instructions,
i.e. simultaneous memory access from the different instructions is prohibited.
This also always aligns the timing ordering with the topological node ordering.
- |
Added a new transpiler pass :class:`~.PadDynamicalDecoupling`
which superscedes the :class:`~.DynamicalDecoupling` pass as part of the
reworked transpiler workflow for scheduling. This new pass will insert dynamical decoupling
sequences into the circuit per any scheduling and alignment analysis that occurred in earlier
passes.