Skip to content
This repository has been archived by the owner on Jul 24, 2024. It is now read-only.

Update Scheduling and Dynamical Decoupling to support block-based control flow #480

Merged
merged 48 commits into from
Feb 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
599fad9
Begin to rework scheduling to be block-based.
taalexander Dec 10, 2022
130b838
Test passing.
taalexander Dec 29, 2022
5b2a762
Update ALAP block pushing.
taalexander Dec 29, 2022
95652aa
Fix issues with barriers, and reg widths.
taalexander Dec 29, 2022
b2b5a90
Fix issue with calibrations.
taalexander Dec 29, 2022
85990fb
Add support for canonicalizing control-flow tests.
taalexander Dec 30, 2022
8575255
Update handling of carg block padding.
taalexander Dec 30, 2022
e9fe2bc
Fix behaviour in terminating barriers.
taalexander Dec 30, 2022
bda0d66
Fix additional tests.
taalexander Dec 30, 2022
8f2307e
Update documentation.
taalexander Dec 30, 2022
e9b32ec
Add tests for other forms of control flow.
taalexander Dec 30, 2022
d8540c2
Fix bug in reset termination.
taalexander Dec 30, 2022
07dc71e
All scheduler tests passing.
taalexander Dec 31, 2022
98d57c6
All scheduling tests passing.
taalexander Dec 31, 2022
7cd75fd
Update documentation.
taalexander Dec 31, 2022
4bec4bd
Run black on the code.
taalexander Dec 31, 2022
b264e5a
Add test for conversion of old format c_if and scheduling.
taalexander Jan 1, 2023
cc759d0
Mypy updates.
taalexander Jan 1, 2023
efc2c1f
Linting.
taalexander Jan 1, 2023
cb81164
Update tests after rebasing.
taalexander Jan 1, 2023
4a0b424
Update reno.
taalexander Jan 1, 2023
ca8a6ac
Disable not-context-manager
taalexander Jan 1, 2023
9ad65f1
Fix docs underscore.
taalexander Jan 2, 2023
ef10366
Add broken test.
taalexander Jan 2, 2023
1d553c2
Update FakeJakarta import.
taalexander Jan 18, 2023
c820ac3
Fix import.
taalexander Jan 18, 2023
588d316
Fix missing with on else.
taalexander Jan 18, 2023
c7ec172
Fix bad grammar.
taalexander Jan 18, 2023
a173817
Remove unnecessary imports.
taalexander Jan 18, 2023
396795b
Fix padding to be a multiple of 16.
taalexander Jan 18, 2023
e9efd32
Update comments on register adding.
taalexander Jan 18, 2023
bcc1c9a
Fast-path only with single-qubit condition.
taalexander Jan 18, 2023
f81e338
Simplifyi the last node to touch logic.
taalexander Jan 18, 2023
3d69f69
Add timeunit warning.
taalexander Jan 18, 2023
73679b1
Styling.
taalexander Jan 18, 2023
27ff3c0
Add support for wire mapping.
taalexander Jan 19, 2023
49ceb6f
Fix ALAP scheduler and add test.
taalexander Jan 19, 2023
cea166a
Add more detail on sequence_min_length_ratios.
taalexander Jan 19, 2023
9b1107d
Switch to while loop.
taalexander Jan 26, 2023
4d2479c
Update test.
taalexander Jan 26, 2023
e7f1086
Avoid conversion to DAG.
taalexander Jan 31, 2023
6a69587
Fix outdated comment.
taalexander Jan 31, 2023
c857bd9
Update cross-referencing.
taalexander Jan 31, 2023
155db53
Fix old todo messages.
taalexander Jan 31, 2023
3362c69
Fix node duration setting.
taalexander Jan 31, 2023
e85a0a1
Map clbits.
taalexander Feb 1, 2023
c960ac9
Fix merge issue.
taalexander Feb 1, 2023
8bfec24
Fix bug in DD with multiple sequences.
taalexander Feb 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
252 changes: 246 additions & 6 deletions qiskit_ibm_provider/transpiler/passes/scheduling/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,15 @@
from qiskit_ibm_provider.transpiler.passes.scheduling import DynamicCircuitInstructionDurations
from qiskit_ibm_provider.transpiler.passes.scheduling import ALAPScheduleAnalysis
from qiskit_ibm_provider.transpiler.passes.scheduling import PadDelay
from qiskit.providers.fake_provider.backends.jakarta.fake_jakarta import FakeJakarta
from qiskit.providers.fake_provider import FakeJakarta


backend = FakeJakarta()

# Temporary workaround for mock backends. For real backends this is not required.
backend.configuration().basis_gates.append("if_else")


# Use this duration class to get appropriate durations for dynamic
# circuit backend scheduling
durations = DynamicCircuitInstructionDurations.from_backend(backend)
Expand All @@ -63,8 +67,10 @@
teleport.h(qr[0])
teleport.measure(qr[0], crz)
teleport.measure(qr[1], crx)
teleport.z(qr[2]).c_if(crz, 1)
teleport.x(qr[2]).c_if(crx, 1)
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)

teleport = transpile(teleport, backend)
Expand Down Expand Up @@ -98,6 +104,243 @@
dd_teleport.draw(output="mpl")


Scheduling old format ``c_if`` conditioned gates
------------------------------------------------

Scheduling with old format ``c_if`` conditioned gates is not supported.

.. jupyter-execute::

qc_c_if = QuantumCircuit(1, 1)
qc_c_if.x(0).c_if(0, 1)
qc_c_if.draw(output="mpl")

To work around this please run the pass
:class:`qiskit.transpiler.passes.ConvertConditionsToIfOps`
prior to your scheduling pass.

.. jupyter-execute::

from qiskit.transpiler.passes import ConvertConditionsToIfOps

pm = PassManager(
[
ConvertConditionsToIfOps(),
ALAPScheduleAnalysis(durations),
PadDelay(),
]
)
taalexander marked this conversation as resolved.
Show resolved Hide resolved

qc_if_test = pm.run(qc_c_if)
qc_if_test.draw(output="mpl")


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.

.. jupyter-execute::

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")


The circuit below will not use the fast-path as the conditional gate is
on a different qubit than the measurement qubit.

.. jupyter-execute::

qc = QuantumCircuit(2, 2)
qc.measure(0, 0)
with qc.if_test((0, 1)):
qc.x(1)

qc.draw(output="mpl")

Similarly, the circuit below contains gates on multiple qubits
and will not be performed using the fast-path.

.. jupyter-execute::

qc = QuantumCircuit(2, 2)
qc.measure(0, 0)
with qc.if_test((0, 1)):
qc.x(0)
qc.x(1)

qc.draw(output="mpl")

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.

.. jupyter-execute::

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")

This behavior is also applied to the else condition of a fast-path eligible branch.

.. jupyter-execute::

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")


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.

.. jupyter-execute::

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")
Comment on lines +223 to +239
Copy link
Member

Choose a reason for hiding this comment

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

(Unrelated to review, it's just me asking to get better intuition.) If I logically wanted to do if (c) {x $0; x $1;}, is it likely that if (c) x $0; if (c) x $1; would have significantly different error performance on our hardware because of the fast-path, assuming I'm going to use both $0 and $1 later. My gut says that putting both into a slow-path block would be better, because then neither is idle waiting for the other.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It depends on the measurement result. If c is directly a result from predcessor measurements then if (c) x $0; if (c) x $1; will take the same amount of time as if (c) x $0; (as it will be done in parallel) as you said with the fast-path so neither is waiting.


If you wish to prevent the usage of the fast-path you may insert a barrier between the measurement and
the conditional branch.

.. jupyter-execute::

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")

Conditional measurements are not eligible for the fast-path.

.. jupyter-execute::

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")

Similarly nested control-flow is not eligible.

.. jupyter-execute::

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")


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

.. jupyter-execute::

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)
taalexander marked this conversation as resolved.
Show resolved Hide resolved


qc.draw(output="mpl")

qc_dd = pm.run(qc)

qc_dd.draw(output="mpl")

.. 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:

.. jupyter-execute::

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")


Scheduling & Dynamical Decoupling
=================================
.. autosummary::
Expand All @@ -109,9 +352,6 @@
DynamicCircuitInstructionDurations
PadDelay
PadDynamicalDecoupling



"""

from .block_base_padder import BlockBasePadder
Expand Down
Loading