373 lines
17 KiB
Plaintext
373 lines
17 KiB
Plaintext
---
|
||
title: scheduling (v0.29)
|
||
description: API reference for qiskit_ibm_runtime.transpiler.passes.scheduling in qiskit-ibm-runtime v0.29
|
||
in_page_toc_min_heading_level: 2
|
||
python_api_type: module
|
||
python_api_name: qiskit_ibm_runtime.transpiler.passes.scheduling
|
||
---
|
||
|
||
<span id="transpiler-scheduling-passes-qiskit-ibm-runtime-transpiler-passes-scheduling" />
|
||
|
||
<span id="module-qiskit_ibm_runtime.transpiler.passes.scheduling" />
|
||
|
||
# Transpiler scheduling passes
|
||
|
||
`qiskit_ibm_runtime.transpiler.passes.scheduling`
|
||
|
||
A collection of scheduling passes for working with IBM Quantum’s next-generation backends that support advanced “dynamic circuit” capabilities. Ie., circuits with support for classical control-flow/feedback based off of measurement results.
|
||
|
||
<Admonition title="Warning" type="caution">
|
||
You should not mix these scheduling passes with Qiskit’s builtin scheduling passes as they will negatively interact with the scheduling routines for dynamic circuits. This includes setting `scheduling_method` in [`transpile()`](/api/qiskit/compiler#qiskit.compiler.transpile "(in Qiskit v1.2)") or [`generate_preset_pass_manager()`](/api/qiskit/transpiler_preset#qiskit.transpiler.preset_passmanagers.generate_preset_pass_manager "(in Qiskit v1.2)").
|
||
</Admonition>
|
||
|
||
## Classes
|
||
|
||
| | |
|
||
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||
| [`BlockBasePadder`](qiskit_ibm_runtime.transpiler.passes.scheduling.BlockBasePadder "qiskit_ibm_runtime.transpiler.passes.scheduling.BlockBasePadder")(\[schedule\_idle\_qubits, ...]) | The base class of padding pass. |
|
||
| [`ALAPScheduleAnalysis`](qiskit_ibm_runtime.transpiler.passes.scheduling.ALAPScheduleAnalysis "qiskit_ibm_runtime.transpiler.passes.scheduling.ALAPScheduleAnalysis")(durations\[, ...]) | Dynamic circuits as-late-as-possible (ALAP) scheduling analysis pass. |
|
||
| [`ASAPScheduleAnalysis`](qiskit_ibm_runtime.transpiler.passes.scheduling.ASAPScheduleAnalysis "qiskit_ibm_runtime.transpiler.passes.scheduling.ASAPScheduleAnalysis")(durations\[, ...]) | Dynamic circuits as-soon-as-possible (ASAP) scheduling analysis pass. |
|
||
| [`DynamicCircuitInstructionDurations`](qiskit_ibm_runtime.transpiler.passes.scheduling.DynamicCircuitInstructionDurations "qiskit_ibm_runtime.transpiler.passes.scheduling.DynamicCircuitInstructionDurations")(\[...]) | For dynamic circuits the IBM Qiskit backend currently reports instruction durations that differ compared with those required for the legacy Qobj-based path. |
|
||
| [`PadDelay`](qiskit_ibm_runtime.transpiler.passes.scheduling.PadDelay "qiskit_ibm_runtime.transpiler.passes.scheduling.PadDelay")(durations\[, fill\_very\_end, ...]) | Padding idle time with Delay instructions. |
|
||
| [`PadDynamicalDecoupling`](qiskit_ibm_runtime.transpiler.passes.scheduling.PadDynamicalDecoupling "qiskit_ibm_runtime.transpiler.passes.scheduling.PadDynamicalDecoupling")(durations, dd\_sequences) | Dynamical decoupling insertion pass for IBM dynamic circuit backends. |
|
||
|
||
## Example usage
|
||
|
||
Below we demonstrate how to schedule and pad a teleportation circuit with delays for a dynamic circuit backend’s execution model:
|
||
|
||
```python
|
||
from qiskit.circuit import ClassicalRegister, QuantumCircuit, QuantumRegister
|
||
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
|
||
from qiskit.transpiler.passmanager import PassManager
|
||
|
||
from qiskit_ibm_runtime.transpiler.passes.scheduling import DynamicCircuitInstructionDurations
|
||
from qiskit_ibm_runtime.transpiler.passes.scheduling import ALAPScheduleAnalysis
|
||
from qiskit_ibm_runtime.transpiler.passes.scheduling import PadDelay
|
||
from qiskit_ibm_runtime.fake_provider import FakeJakartaV2
|
||
|
||
backend = FakeJakartaV2()
|
||
|
||
# Use this duration class to get appropriate durations for dynamic
|
||
# circuit backend scheduling
|
||
durations = DynamicCircuitInstructionDurations.from_backend(backend)
|
||
# Generate the main Qiskit transpile passes.
|
||
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
|
||
# Configure the as-late-as-possible scheduling pass
|
||
pm.scheduling = PassManager([ALAPScheduleAnalysis(durations), PadDelay(durations)])
|
||
|
||
qr = QuantumRegister(3)
|
||
crz = ClassicalRegister(1, name="crz")
|
||
crx = ClassicalRegister(1, name="crx")
|
||
result = ClassicalRegister(1, name="result")
|
||
|
||
teleport = QuantumCircuit(qr, crz, crx, result, name="Teleport")
|
||
|
||
teleport.h(qr[1])
|
||
teleport.cx(qr[1], qr[2])
|
||
teleport.cx(qr[0], qr[1])
|
||
teleport.h(qr[0])
|
||
teleport.measure(qr[0], crz)
|
||
teleport.measure(qr[1], crx)
|
||
with teleport.if_test((crz, 1)):
|
||
teleport.z(qr[2])
|
||
with teleport.if_test((crx, 1)):
|
||
teleport.x(qr[2])
|
||
teleport.measure(qr[2], result)
|
||
|
||
# Transpile.
|
||
scheduled_teleport = pm.run(teleport)
|
||
|
||
scheduled_teleport.draw(output="mpl", style="iqp")
|
||
```
|
||
|
||
![../\_images/qiskit\_ibm\_runtime.transpiler.passes.scheduling\_0\_0.png](/images/api/qiskit-ibm-runtime/0.29/qiskit_ibm_runtime.transpiler.passes.scheduling_0_0.png)
|
||
|
||
Instead of padding with delays we may also insert a dynamical decoupling sequence using the [`PadDynamicalDecoupling`](qiskit_ibm_runtime.transpiler.passes.scheduling.PadDynamicalDecoupling "qiskit_ibm_runtime.transpiler.passes.scheduling.PadDynamicalDecoupling") pass as shown below:
|
||
|
||
```python
|
||
from qiskit.circuit.library import XGate
|
||
|
||
from qiskit_ibm_runtime.transpiler.passes.scheduling import PadDynamicalDecoupling
|
||
|
||
|
||
dd_sequence = [XGate(), XGate()]
|
||
|
||
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
|
||
pm.scheduling = PassManager(
|
||
[
|
||
ALAPScheduleAnalysis(durations),
|
||
PadDynamicalDecoupling(durations, dd_sequence),
|
||
]
|
||
)
|
||
|
||
dd_teleport = pm.run(teleport)
|
||
|
||
dd_teleport.draw(output="mpl", style="iqp")
|
||
```
|
||
|
||
![../\_images/qiskit\_ibm\_runtime.transpiler.passes.scheduling\_1\_0.png](/images/api/qiskit-ibm-runtime/0.29/qiskit_ibm_runtime.transpiler.passes.scheduling_1_0.png)
|
||
|
||
When compiling a circuit with Qiskit, it is more efficient and more robust to perform all the transformations in a single transpilation. This has been done above by extending Qiskit’s preset pass managers. For example, Qiskit’s [`transpile()`](/api/qiskit/compiler#qiskit.compiler.transpile "(in Qiskit v1.2)") function internally builds its pass set by using [`generate_preset_pass_manager()`](/api/qiskit/transpiler_preset#qiskit.transpiler.preset_passmanagers.generate_preset_pass_manager "(in Qiskit v1.2)"). This returns instances of [`StagedPassManager`](/api/qiskit/qiskit.transpiler.StagedPassManager "(in Qiskit v1.2)"), which can be extended.
|
||
|
||
<span id="scheduling-old-format-c-if-conditioned-gates" />
|
||
|
||
## Scheduling old format c\_if conditioned gates
|
||
|
||
Scheduling with old format `c_if` conditioned gates is not supported.
|
||
|
||
```python
|
||
qc_c_if = QuantumCircuit(1, 1)
|
||
qc_c_if.x(0).c_if(0, 1)
|
||
qc_c_if.draw(output="mpl", style="iqp")
|
||
```
|
||
|
||
![../\_images/qiskit\_ibm\_runtime.transpiler.passes.scheduling\_2\_0.png](/images/api/qiskit-ibm-runtime/0.29/qiskit_ibm_runtime.transpiler.passes.scheduling_2_0.png)
|
||
|
||
The [`IBMBackend`](qiskit_ibm_runtime.IBMBackend "qiskit_ibm_runtime.IBMBackend") configures a translation plugin `IBMTranslationPlugin` to automatically apply transformations and optimizations for IBM hardware backends when invoking [`transpile()`](/api/qiskit/compiler#qiskit.compiler.transpile "(in Qiskit v1.2)"). This will automatically convert all old style `c_if` conditioned gates to new-style control-flow. We may then schedule the transpiled circuit without further modification.
|
||
|
||
```python
|
||
# Temporary workaround for mock backends. For real backends this is not required.
|
||
backend.get_translation_stage_plugin = lambda: "ibm_dynamic_circuits"
|
||
|
||
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
|
||
pm.scheduling = PassManager(
|
||
[
|
||
ALAPScheduleAnalysis(durations),
|
||
PadDynamicalDecoupling(durations, dd_sequence),
|
||
]
|
||
)
|
||
|
||
qc_if_dd = pm.run(qc_c_if, backend)
|
||
qc_if_dd.draw(output="mpl", style="iqp")
|
||
```
|
||
|
||
![../\_images/qiskit\_ibm\_runtime.transpiler.passes.scheduling\_3\_0.png](/images/api/qiskit-ibm-runtime/0.29/qiskit_ibm_runtime.transpiler.passes.scheduling_3_0.png)
|
||
|
||
If you are not using the transpiler plugin stages to work around this please manually run the pass [`qiskit.transpiler.passes.ConvertConditionsToIfOps`](/api/qiskit/qiskit.transpiler.passes.ConvertConditionsToIfOps "(in Qiskit v1.2)") prior to your scheduling pass.
|
||
|
||
```python
|
||
from qiskit.transpiler.passes import ConvertConditionsToIfOps
|
||
|
||
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
|
||
pm.scheduling = PassManager(
|
||
[
|
||
ConvertConditionsToIfOps(),
|
||
ALAPScheduleAnalysis(durations),
|
||
PadDelay(durations),
|
||
]
|
||
)
|
||
|
||
qc_if_dd = pm.run(qc_c_if)
|
||
qc_if_dd.draw(output="mpl", style="iqp")
|
||
```
|
||
|
||
![../\_images/qiskit\_ibm\_runtime.transpiler.passes.scheduling\_4\_0.png](/images/api/qiskit-ibm-runtime/0.29/qiskit_ibm_runtime.transpiler.passes.scheduling_4_0.png)
|
||
|
||
<span id="exploiting-ibm-backend-s-local-parallel-fast-path" />
|
||
|
||
## Exploiting IBM backend’s local parallel “fast-path”
|
||
|
||
IBM quantum hardware supports a localized “fast-path” which enables a block of gates applied to a *single qubit* that are conditional on an immediately predecessor measurement *of the same qubit* to be completed with lower latency. The hardware is also able to do this in *parallel* on disjoint qubits that satisfy this condition.
|
||
|
||
For example, the conditional gates below are performed in parallel with lower latency as the measurements flow directly into the conditional blocks which in turn only apply gates to the same measurement qubit.
|
||
|
||
```python
|
||
qc = QuantumCircuit(2, 2)
|
||
qc.measure(0, 0)
|
||
qc.measure(1, 1)
|
||
# Conditional blocks will be performed in parallel in the hardware
|
||
with qc.if_test((0, 1)):
|
||
qc.x(0)
|
||
with qc.if_test((1, 1)):
|
||
qc.x(1)
|
||
|
||
qc.draw(output="mpl", style="iqp")
|
||
```
|
||
|
||
![../\_images/qiskit\_ibm\_runtime.transpiler.passes.scheduling\_5\_0.png](/images/api/qiskit-ibm-runtime/0.29/qiskit_ibm_runtime.transpiler.passes.scheduling_5_0.png)
|
||
|
||
The circuit below will not use the fast-path as the conditional gate is on a different qubit than the measurement qubit.
|
||
|
||
```python
|
||
qc = QuantumCircuit(2, 2)
|
||
qc.measure(0, 0)
|
||
with qc.if_test((0, 1)):
|
||
qc.x(1)
|
||
|
||
qc.draw(output="mpl", style="iqp")
|
||
```
|
||
|
||
![../\_images/qiskit\_ibm\_runtime.transpiler.passes.scheduling\_6\_0.png](/images/api/qiskit-ibm-runtime/0.29/qiskit_ibm_runtime.transpiler.passes.scheduling_6_0.png)
|
||
|
||
Similarly, the circuit below contains gates on multiple qubits and will not be performed using the fast-path.
|
||
|
||
```python
|
||
qc = QuantumCircuit(2, 2)
|
||
qc.measure(0, 0)
|
||
with qc.if_test((0, 1)):
|
||
qc.x(0)
|
||
qc.x(1)
|
||
|
||
qc.draw(output="mpl", style="iqp")
|
||
```
|
||
|
||
![../\_images/qiskit\_ibm\_runtime.transpiler.passes.scheduling\_7\_0.png](/images/api/qiskit-ibm-runtime/0.29/qiskit_ibm_runtime.transpiler.passes.scheduling_7_0.png)
|
||
|
||
A fast-path block may contain multiple gates as long as they are on the fast-path qubit. If there are multiple fast-path blocks being performed in parallel each block will be padded out to the duration of the longest block.
|
||
|
||
```python
|
||
qc = QuantumCircuit(2, 2)
|
||
qc.measure(0, 0)
|
||
qc.measure(1, 1)
|
||
# Conditional blocks will be performed in parallel in the hardware
|
||
with qc.if_test((0, 1)):
|
||
qc.x(0)
|
||
# Will be padded out to a duration of 1600 on the backend.
|
||
with qc.if_test((1, 1)):
|
||
qc.delay(1600, 1)
|
||
|
||
qc.draw(output="mpl", style="iqp")
|
||
```
|
||
|
||
![../\_images/qiskit\_ibm\_runtime.transpiler.passes.scheduling\_8\_0.png](/images/api/qiskit-ibm-runtime/0.29/qiskit_ibm_runtime.transpiler.passes.scheduling_8_0.png)
|
||
|
||
This behavior is also applied to the else condition of a fast-path eligible branch.
|
||
|
||
```python
|
||
qc = QuantumCircuit(1, 1)
|
||
qc.measure(0, 0)
|
||
# Conditional blocks will be performed in parallel in the hardware
|
||
with qc.if_test((0, 1)) as else_:
|
||
qc.x(0)
|
||
# Will be padded out to a duration of 1600 on the backend.
|
||
with else_:
|
||
qc.delay(1600, 0)
|
||
|
||
qc.draw(output="mpl", style="iqp")
|
||
```
|
||
|
||
![../\_images/qiskit\_ibm\_runtime.transpiler.passes.scheduling\_9\_0.png](/images/api/qiskit-ibm-runtime/0.29/qiskit_ibm_runtime.transpiler.passes.scheduling_9_0.png)
|
||
|
||
If a single measurement result is used with several conditional blocks, if there is a fast-path eligible block it will be applied followed by the non-fast-path blocks which will execute with the standard higher latency conditional branch.
|
||
|
||
```python
|
||
qc = QuantumCircuit(2, 2)
|
||
qc.measure(0, 0)
|
||
# Conditional blocks will be performed in parallel in the hardware
|
||
with qc.if_test((0, 1)):
|
||
# Uses fast-path
|
||
qc.x(0)
|
||
with qc.if_test((0, 1)):
|
||
# Does not use fast-path
|
||
qc.x(1)
|
||
|
||
qc.draw(output="mpl", style="iqp")
|
||
```
|
||
|
||
![../\_images/qiskit\_ibm\_runtime.transpiler.passes.scheduling\_10\_0.png](/images/api/qiskit-ibm-runtime/0.29/qiskit_ibm_runtime.transpiler.passes.scheduling_10_0.png)
|
||
|
||
If you wish to prevent the usage of the fast-path you may insert a barrier between the measurement and the conditional branch.
|
||
|
||
```python
|
||
qc = QuantumCircuit(1, 2)
|
||
qc.measure(0, 0)
|
||
# Barrier prevents the fast-path.
|
||
qc.barrier()
|
||
with qc.if_test((0, 1)):
|
||
qc.x(0)
|
||
|
||
qc.draw(output="mpl", style="iqp")
|
||
```
|
||
|
||
![../\_images/qiskit\_ibm\_runtime.transpiler.passes.scheduling\_11\_0.png](/images/api/qiskit-ibm-runtime/0.29/qiskit_ibm_runtime.transpiler.passes.scheduling_11_0.png)
|
||
|
||
Conditional measurements are not eligible for the fast-path.
|
||
|
||
```python
|
||
qc = QuantumCircuit(1, 2)
|
||
qc.measure(0, 0)
|
||
with qc.if_test((0, 1)):
|
||
# Does not use the fast-path
|
||
qc.measure(0, 1)
|
||
|
||
qc.draw(output="mpl", style="iqp")
|
||
```
|
||
|
||
![../\_images/qiskit\_ibm\_runtime.transpiler.passes.scheduling\_12\_0.png](/images/api/qiskit-ibm-runtime/0.29/qiskit_ibm_runtime.transpiler.passes.scheduling_12_0.png)
|
||
|
||
Similarly nested control-flow is not eligible.
|
||
|
||
```python
|
||
qc = QuantumCircuit(1, 1)
|
||
qc.measure(0, 0)
|
||
with qc.if_test((0, 1)):
|
||
# Does not use the fast-path
|
||
qc.x(0)
|
||
with qc.if_test((0, 1)):
|
||
qc.x(0)
|
||
|
||
qc.draw(output="mpl", style="iqp")
|
||
```
|
||
|
||
![../\_images/qiskit\_ibm\_runtime.transpiler.passes.scheduling\_13\_0.png](/images/api/qiskit-ibm-runtime/0.29/qiskit_ibm_runtime.transpiler.passes.scheduling_13_0.png)
|
||
|
||
The scheduler is aware of the fast-path behavior and will not insert delays on idle qubits in blocks that satisfy the fast-path conditions so as to avoid preventing the backend compiler from performing the necessary optimizations to utilize the fast-path. If there are fast-path blocks that will be performed in parallel they currently *will not* be padded out by the scheduler to ensure they are of the same duration in Qiskit
|
||
|
||
```python
|
||
dd_sequence = [XGate(), XGate()]
|
||
|
||
pm = PassManager(
|
||
[
|
||
ALAPScheduleAnalysis(durations),
|
||
PadDynamicalDecoupling(durations, dd_sequence),
|
||
]
|
||
)
|
||
|
||
qc = QuantumCircuit(2, 2)
|
||
qc.measure(0, 0)
|
||
qc.measure(1, 1)
|
||
with qc.if_test((0, 1)):
|
||
qc.x(0)
|
||
# Is currently not padded to ensure
|
||
# a duration of 1000. If you desire
|
||
# this you would need to manually add
|
||
# qc.delay(840, 0)
|
||
with qc.if_test((1, 1)):
|
||
qc.delay(1000, 0)
|
||
|
||
|
||
qc.draw(output="mpl", style="iqp")
|
||
|
||
qc_dd = pm.run(qc)
|
||
|
||
qc_dd.draw(output="mpl", style="iqp")
|
||
```
|
||
|
||
![../\_images/qiskit\_ibm\_runtime.transpiler.passes.scheduling\_14\_0.png](/images/api/qiskit-ibm-runtime/0.29/qiskit_ibm_runtime.transpiler.passes.scheduling_14_0.png)
|
||
|
||
<Admonition title="Note" type="note">
|
||
If there are qubits that are *not* involved in a fast-path decision it is not currently possible to use them in a fast-path branch in parallel with the fast-path qubits resulting from a measurement. This will be revised in the future as we further improve these capabilities.
|
||
|
||
For example:
|
||
|
||
```python
|
||
qc = QuantumCircuit(3, 2)
|
||
qc.x(1)
|
||
qc.measure(0, 0)
|
||
with qc.if_test((0, 1)):
|
||
qc.x(0)
|
||
# Qubit 1 sits idle throughout the fast-path decision
|
||
with qc.if_test((1, 0)):
|
||
# Qubit 2 is idle but there is no measurement
|
||
# to make it fast-path eligible. This will
|
||
# however avoid a communication event in the hardware
|
||
# since the condition is compile time evaluated.
|
||
qc.x(2)
|
||
|
||
qc.draw(output="mpl", style="iqp")
|
||
```
|
||
|
||
![../\_images/qiskit\_ibm\_runtime.transpiler.passes.scheduling\_15\_0.png](/images/api/qiskit-ibm-runtime/0.29/qiskit_ibm_runtime.transpiler.passes.scheduling_15_0.png)
|
||
</Admonition>
|
||
|