Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert deprecation and breaking changes of scheduling and alignment passes #7835

Merged
merged 13 commits into from
Mar 31, 2022
Merged
12 changes: 9 additions & 3 deletions qiskit/transpiler/passes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,17 @@
:toctree: ../stubs/

TimeUnitConversion
ALAPSchedule
ASAPSchedule
DynamicalDecoupling
ALAPScheduleAnalysis
ASAPScheduleAnalysis
DynamicalDecouplingPadding
ConstrainedReschedule
AlignMeasures
ValidatePulseGates
InstructionDurationCheck
SetIOLatency
ALAPSchedule
ASAPSchedule
DynamicalDecoupling

Circuit Analysis
================
Expand Down Expand Up @@ -227,8 +230,11 @@

# circuit scheduling
from .scheduling import TimeUnitConversion
from .scheduling import ALAPScheduleAnalysis
from .scheduling import ASAPScheduleAnalysis
from .scheduling import ALAPSchedule
from .scheduling import ASAPSchedule
from .scheduling import DynamicalDecouplingPadding
Copy link
Contributor

Choose a reason for hiding this comment

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

We should be careful for this because some people are trying to use upgraded pass for their workshop. This change could break upgraded code so we need kind announce in community slack channel about how to use DD pass with recent systems and how they can migrate (I assumed there is no subclass of them and keep API consistent so that this doesn't become breaking "API" change).

If I understand correctly, DynamicalDecouplingPadding pass will be deprecated again once it replaces DynamicalDecoupling. However, this pass is not integrated into preset pass manager and user should integrate DynamicalDecouplingPadding into their experimental code. This could bother experimentalists.

Copy link
Member Author

Choose a reason for hiding this comment

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

Well we don't necessarily need to deprecate and rename DynamicalDecouplingPadding back to DynamicalDecoupling. We can just deprecate and remove DynamicalDecoupling and make DynamicalDecouplingPadding the new canonical name moving forward. I do think it's a bit uglier but it is a bit more descriptive too.

TBH, I wasn't thinking too much about the longer term plan here more that we should just avoid a breaking change here because anyone that was using DynamicalDecoupling today successfully (which there are even with the new alignment constraints on IBM backends it works elsewhere) would be broken by changing the underlying implementation to be a padding pass

Copy link
Contributor

@nkanazawa1989 nkanazawa1989 Mar 30, 2022

Choose a reason for hiding this comment

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

Then the name should be PadDynamicalDecoupling or rename PadDelay to DelayPadding? I think both should be okey. Anyways I am not an active user of this pass, so I'd like to hear @ajavadia 's thought.

because anyone that was using DynamicalDecoupling today successfully

No one can use the pass successfully today. Anyways I agree if someone has custom DD pass this change will break their code.

Copy link
Member Author

Choose a reason for hiding this comment

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

I like PadDynamicalDecoupling that fits the new naming and is clearer to me than what I used. I'll rename it

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated in: ecfa245

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm almost good with this PR. Just holding my approval until we hear some user's voice about the class name and future deprecation plan.

Copy link
Member

Choose a reason for hiding this comment

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

yeah I think ASAPScheduleAnalysis and ALAPScheduleAnalysis and PadDynamicalDecoupling are fine, they are explicit about what they do.

from .scheduling import DynamicalDecoupling
from .scheduling import AlignMeasures # Deprecated
from .scheduling import ValidatePulseGates
Expand Down
7 changes: 5 additions & 2 deletions qiskit/transpiler/passes/scheduling/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@

"""Module containing circuit scheduling passes."""

from .scheduling import ALAPSchedule, ASAPSchedule, SetIOLatency
from .alap import ALAPSchedule
from .asap import ASAPSchedule
from .dynamical_decoupling import DynamicalDecoupling
from .scheduling import ALAPScheduleAnalysis, ASAPScheduleAnalysis, SetIOLatency
from .time_unit_conversion import TimeUnitConversion
from .padding import PadDelay, DynamicalDecoupling
from .padding import PadDelay, DynamicalDecouplingPadding
from .alignments import InstructionDurationCheck, ValidatePulseGates, ConstrainedReschedule

# For backward compability
Expand Down
155 changes: 155 additions & 0 deletions qiskit/transpiler/passes/scheduling/alap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2020.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""ALAP Scheduling."""

import warnings

from qiskit.circuit import Delay, Qubit, Measure
from qiskit.dagcircuit import DAGCircuit
from qiskit.transpiler.exceptions import TranspilerError

from .base_scheduler import BaseScheduler


class ALAPSchedule(BaseScheduler):
"""ALAP Scheduling pass, which schedules the **stop** time of instructions as late as possible.

See :class:`~qiskit.transpiler.passes.scheduling.base_scheduler.BaseScheduler` for the
detailed behavior of the control flow operation, i.e. ``c_if``.
"""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
warnings.warn(
"The ALAPSchedule class has been supersceded by the ALAPScheduleAnalysis class "
"which performs the as analysis pass that requires a padding pass to later modify "
"the circuit. This class will be deprecated in a future release and subsequently "
"removed after that.",
PendingDeprecationWarning,
)

def run(self, dag):
"""Run the ALAPSchedule pass on `dag`.

Args:
dag (DAGCircuit): DAG to schedule.

Returns:
DAGCircuit: A scheduled DAG.

Raises:
TranspilerError: if the circuit is not mapped on physical qubits.
TranspilerError: if conditional bit is added to non-supported instruction.
"""
if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None:
raise TranspilerError("ALAP schedule runs on physical circuits only")

time_unit = self.property_set["time_unit"]
new_dag = DAGCircuit()
for qreg in dag.qregs.values():
new_dag.add_qreg(qreg)
for creg in dag.cregs.values():
new_dag.add_creg(creg)

idle_before = {q: 0 for q in dag.qubits + dag.clbits}
bit_indices = {bit: index for index, bit in enumerate(dag.qubits)}
for node in reversed(list(dag.topological_op_nodes())):
op_duration = self._get_node_duration(node, bit_indices, dag)

# compute t0, t1: instruction interval, note that
# t0: start time of instruction
# t1: end time of instruction

# since this is alap scheduling, node is scheduled in reversed topological ordering
# and nodes are packed from the very end of the circuit.
# the physical meaning of t0 and t1 is flipped here.
if isinstance(node.op, self.CONDITIONAL_SUPPORTED):
t0q = max(idle_before[q] for q in node.qargs)
if node.op.condition_bits:
# conditional is bit tricky due to conditional_latency
t0c = max(idle_before[c] for c in node.op.condition_bits)
# Assume following case (t0c > t0q):
#
# |t0q
# Q ░░░░░░░░░░░░░▒▒▒
# C ░░░░░░░░▒▒▒▒▒▒▒▒
# |t0c
#
# In this case, there is no actual clbit read before gate.
#
# |t0q' = t0c - conditional_latency
# Q ░░░░░░░░▒▒▒░░▒▒▒
# C ░░░░░░▒▒▒▒▒▒▒▒▒▒
# |t1c' = t0c + conditional_latency
#
# rather than naively doing
#
# |t1q' = t0c + duration
# Q ░░░░░▒▒▒░░░░░▒▒▒
# C ░░▒▒░░░░▒▒▒▒▒▒▒▒
# |t1c' = t0c + duration + conditional_latency
#
t0 = max(t0q, t0c - op_duration)
t1 = t0 + op_duration
for clbit in node.op.condition_bits:
idle_before[clbit] = t1 + self.conditional_latency
else:
t0 = t0q
t1 = t0 + op_duration
else:
if node.op.condition_bits:
raise TranspilerError(
f"Conditional instruction {node.op.name} is not supported in ALAP scheduler."
)

if isinstance(node.op, Measure):
# clbit time is always right (alap) justified
t0 = max(idle_before[bit] for bit in node.qargs + node.cargs)
t1 = t0 + op_duration
#
# |t1 = t0 + duration
# Q ░░░░░▒▒▒▒▒▒▒▒▒▒▒
# C ░░░░░░░░░▒▒▒▒▒▒▒
# |t0 + (duration - clbit_write_latency)
#
for clbit in node.cargs:
idle_before[clbit] = t0 + (op_duration - self.clbit_write_latency)
else:
# It happens to be directives such as barrier
t0 = max(idle_before[bit] for bit in node.qargs + node.cargs)
t1 = t0 + op_duration

for bit in node.qargs:
delta = t0 - idle_before[bit]
if delta > 0:
new_dag.apply_operation_front(Delay(delta, time_unit), [bit], [])
idle_before[bit] = t1

new_dag.apply_operation_front(node.op, node.qargs, node.cargs)

circuit_duration = max(idle_before.values())
for bit, before in idle_before.items():
delta = circuit_duration - before
if not (delta > 0 and isinstance(bit, Qubit)):
continue
new_dag.apply_operation_front(Delay(delta, time_unit), [bit], [])

new_dag.name = dag.name
new_dag.metadata = dag.metadata
new_dag.calibrations = dag.calibrations

# set circuit duration and unit to indicate it is scheduled
new_dag.duration = circuit_duration
new_dag.unit = time_unit

return new_dag
Loading