mirror of https://github.com/Qiskit/qiskit.git
Removed code deprecated in qiskit-terra 0.21, released on June 2022 (#10754)
* Removed code and tests deprecated in 0.21 * Undone removal of optimizers/spsa. * undo changes in spsa * Added release note. * Removed deprecated classes from docstrings. * Improved release note. * Added removed files to resolve conflict. * Removed the conflicting files * Added conflicting file but with no code * Align measure class file * Removed alap dynamical coupling class * Resolving colflict with dynamical coupling * Deleted dynamical coupling * Fixed release note. * Apply suggestions from code review * Update qiskit/execute_function.py * recover benchmark test --------- Co-authored-by: Luciano Bello <bel@zurich.ibm.com> Co-authored-by: Luciano Bello <luciano@debian.org>
This commit is contained in:
parent
998b5595df
commit
d86e708f11
|
@ -26,7 +26,6 @@ from qiskit.compiler import transpile, schedule
|
|||
from qiskit.providers.backend import Backend
|
||||
from qiskit.pulse import Schedule, ScheduleBlock
|
||||
from qiskit.exceptions import QiskitError
|
||||
from qiskit.utils.deprecation import deprecate_arg
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -36,8 +35,6 @@ def _log_submission_time(start_time, end_time):
|
|||
logger.info(log_msg)
|
||||
|
||||
|
||||
@deprecate_arg("qobj_id", since="0.21.0", additional_msg="This argument has no effect anymore.")
|
||||
@deprecate_arg("qobj_header", since="0.21.0", additional_msg="This argument has no effect anymore.")
|
||||
def execute(
|
||||
experiments,
|
||||
backend,
|
||||
|
@ -48,8 +45,6 @@ def execute(
|
|||
seed_transpiler=None,
|
||||
optimization_level=None,
|
||||
pass_manager=None,
|
||||
qobj_id=None,
|
||||
qobj_header=None,
|
||||
shots=None, # common run options
|
||||
memory=None,
|
||||
seed_simulator=None,
|
||||
|
@ -159,16 +154,6 @@ def execute(
|
|||
arg is present, auto-selection of pass manager based on the transpile options
|
||||
will be turned off and this pass manager will be used directly.
|
||||
|
||||
qobj_id (str): DEPRECATED: String identifier to annotate the Qobj. This has no effect
|
||||
and the :attr:`~.QuantumCircuit.name` attribute of the input circuit(s) should be used
|
||||
instead.
|
||||
|
||||
qobj_header (QobjHeader or dict): DEPRECATED: User input that will be inserted in Qobj
|
||||
header, and will also be copied to the corresponding :class:`qiskit.result.Result`
|
||||
header. Headers do not affect the run. Headers do not affect the run. This kwarg
|
||||
has no effect anymore and the :attr:`~.QuantumCircuit.metadata` attribute of the
|
||||
input circuit(s) should be used instead.
|
||||
|
||||
shots (int): Number of repetitions of each circuit, for sampling. Default: 1024
|
||||
|
||||
memory (bool): If True, per-shot measurement bitstrings are returned as well
|
||||
|
@ -279,8 +264,6 @@ def execute(
|
|||
|
||||
job = execute(qc, backend, shots=4321)
|
||||
"""
|
||||
del qobj_id
|
||||
del qobj_header
|
||||
if isinstance(experiments, (Schedule, ScheduleBlock)) or (
|
||||
isinstance(experiments, list) and isinstance(experiments[0], (Schedule, ScheduleBlock))
|
||||
):
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"""A collection of passes to reallocate the timeslots of instructions according to context."""
|
||||
|
||||
import abc
|
||||
from typing import Callable, Dict, Any, Union, Tuple
|
||||
from typing import Callable, Union, Tuple
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
@ -20,7 +20,6 @@ from qiskit.circuit.parameterexpression import ParameterExpression, ParameterVal
|
|||
from qiskit.pulse.exceptions import PulseError
|
||||
from qiskit.pulse.schedule import Schedule, ScheduleComponent
|
||||
from qiskit.pulse.utils import instruction_duration_validation
|
||||
from qiskit.utils.deprecation import deprecate_func
|
||||
|
||||
|
||||
class AlignmentKind(abc.ABC):
|
||||
|
@ -45,11 +44,6 @@ class AlignmentKind(abc.ABC):
|
|||
"""
|
||||
pass
|
||||
|
||||
@deprecate_func(since="0.21")
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Returns dictionary to represent this alignment."""
|
||||
return {"alignment": self.__class__.__name__}
|
||||
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def is_sequential(self) -> bool:
|
||||
|
@ -330,11 +324,6 @@ class AlignEquispaced(AlignmentKind):
|
|||
|
||||
return aligned
|
||||
|
||||
@deprecate_func(since="0.21")
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Returns dictionary to represent this alignment."""
|
||||
return {"alignment": self.__class__.__name__, "duration": self.duration}
|
||||
|
||||
|
||||
class AlignFunc(AlignmentKind):
|
||||
"""Allocate instructions at position specified by callback function.
|
||||
|
@ -415,15 +404,3 @@ class AlignFunc(AlignmentKind):
|
|||
aligned.insert(_t0, child, inplace=True)
|
||||
|
||||
return aligned
|
||||
|
||||
@deprecate_func(since="0.21")
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""Returns dictionary to represent this alignment.
|
||||
|
||||
.. note:: ``func`` is not presented in this dictionary. Just name.
|
||||
"""
|
||||
return {
|
||||
"alignment": self.__class__.__name__,
|
||||
"duration": self.duration,
|
||||
"func": self.func.__name__,
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ from qiskit.exceptions import QiskitError
|
|||
from qiskit.qpy import formats, common, binary_io, type_keys
|
||||
from qiskit.qpy.exceptions import QpyError
|
||||
from qiskit.version import __version__
|
||||
from qiskit.utils.deprecation import deprecate_arg
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
|
@ -72,7 +71,6 @@ VERSION_PATTERN = (
|
|||
VERSION_PATTERN_REGEX = re.compile(VERSION_PATTERN, re.VERBOSE | re.IGNORECASE)
|
||||
|
||||
|
||||
@deprecate_arg("circuits", new_alias="programs", since="0.21.0")
|
||||
def dump(
|
||||
programs: Union[List[QPY_SUPPORTED_TYPES], QPY_SUPPORTED_TYPES],
|
||||
file_obj: BinaryIO,
|
||||
|
|
|
@ -114,13 +114,9 @@ Scheduling
|
|||
PadDynamicalDecoupling
|
||||
PadDelay
|
||||
ConstrainedReschedule
|
||||
AlignMeasures
|
||||
ValidatePulseGates
|
||||
InstructionDurationCheck
|
||||
SetIOLatency
|
||||
ALAPSchedule
|
||||
ASAPSchedule
|
||||
DynamicalDecoupling
|
||||
|
||||
Circuit Analysis
|
||||
================
|
||||
|
@ -164,9 +160,7 @@ Additional Passes
|
|||
:toctree: ../stubs/
|
||||
|
||||
CheckMap
|
||||
CheckCXDirection
|
||||
CheckGateDirection
|
||||
CXDirection
|
||||
GateDirection
|
||||
MergeAdjacentBarriers
|
||||
RemoveBarriers
|
||||
|
@ -267,11 +261,7 @@ from .calibration import RXCalibrationBuilder
|
|||
from .scheduling import TimeUnitConversion
|
||||
from .scheduling import ALAPScheduleAnalysis
|
||||
from .scheduling import ASAPScheduleAnalysis
|
||||
from .scheduling import ALAPSchedule
|
||||
from .scheduling import ASAPSchedule
|
||||
from .scheduling import PadDynamicalDecoupling
|
||||
from .scheduling import DynamicalDecoupling
|
||||
from .scheduling import AlignMeasures # Deprecated
|
||||
from .scheduling import ValidatePulseGates
|
||||
from .scheduling import PadDelay
|
||||
from .scheduling import ConstrainedReschedule
|
||||
|
@ -280,8 +270,6 @@ from .scheduling import SetIOLatency
|
|||
|
||||
# additional utility passes
|
||||
from .utils import CheckMap
|
||||
from .utils import CheckCXDirection # Deprecated
|
||||
from .utils import CXDirection # Deprecated
|
||||
from .utils import CheckGateDirection
|
||||
from .utils import GateDirection
|
||||
from .utils import BarrierBeforeFinalMeasurements
|
||||
|
|
|
@ -12,9 +12,6 @@
|
|||
|
||||
"""Module containing circuit scheduling passes."""
|
||||
|
||||
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, PadDynamicalDecoupling
|
||||
|
@ -24,4 +21,3 @@ from .alignments import InstructionDurationCheck, ValidatePulseGates, Constraine
|
|||
from . import alignments as instruction_alignments
|
||||
|
||||
# TODO Deprecated pass. Will be removed after deprecation period.
|
||||
from .alignments import AlignMeasures
|
||||
|
|
|
@ -1,155 +0,0 @@
|
|||
# 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."""
|
||||
|
||||
from qiskit.circuit import Delay, Qubit, Measure
|
||||
from qiskit.dagcircuit import DAGCircuit
|
||||
from qiskit.transpiler.exceptions import TranspilerError
|
||||
from qiskit.utils.deprecation import deprecate_func
|
||||
|
||||
from .base_scheduler import BaseSchedulerTransform
|
||||
|
||||
|
||||
class ALAPSchedule(BaseSchedulerTransform):
|
||||
"""ALAP Scheduling pass, which schedules the **stop** time of instructions as late as possible.
|
||||
|
||||
See :class:`~qiskit.transpiler.passes.scheduling.base_scheduler.BaseSchedulerTransform` for the
|
||||
detailed behavior of the control flow operation, i.e. ``c_if``.
|
||||
"""
|
||||
|
||||
@deprecate_func(
|
||||
additional_msg=(
|
||||
"Instead, use :class:`~.ALAPScheduleAnalysis`, which is an "
|
||||
"analysis pass that requires a padding pass to later modify the circuit."
|
||||
),
|
||||
since="0.21.0",
|
||||
pending=True,
|
||||
)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
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}
|
||||
for node in reversed(list(dag.topological_op_nodes())):
|
||||
op_duration = self._get_node_duration(node, 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 and self._delay_supported(dag.find_bit(bit).index):
|
||||
new_dag.apply_operation_front(Delay(delta, time_unit), [bit], [], check=False)
|
||||
idle_before[bit] = t1
|
||||
|
||||
new_dag.apply_operation_front(node.op, node.qargs, node.cargs, check=False)
|
||||
|
||||
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
|
||||
if self._delay_supported(dag.find_bit(bit).index):
|
||||
new_dag.apply_operation_front(Delay(delta, time_unit), [bit], [], check=False)
|
||||
|
||||
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
|
|
@ -78,4 +78,3 @@ Minimum pulse length constraint
|
|||
from .check_durations import InstructionDurationCheck
|
||||
from .pulse_gate_validation import ValidatePulseGates
|
||||
from .reschedule import ConstrainedReschedule
|
||||
from .align_measures import AlignMeasures
|
||||
|
|
|
@ -1,256 +0,0 @@
|
|||
# This code is part of Qiskit.
|
||||
#
|
||||
# (C) Copyright IBM 2021.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Align measurement instructions."""
|
||||
from __future__ import annotations
|
||||
import itertools
|
||||
import warnings
|
||||
from collections import defaultdict
|
||||
from collections.abc import Iterable
|
||||
from typing import Type
|
||||
|
||||
from qiskit.circuit.quantumcircuit import ClbitSpecifier, QubitSpecifier
|
||||
|
||||
from qiskit.circuit.delay import Delay
|
||||
from qiskit.circuit.measure import Measure
|
||||
from qiskit.circuit.parameterexpression import ParameterExpression
|
||||
from qiskit.dagcircuit import DAGCircuit
|
||||
from qiskit.transpiler.basepasses import TransformationPass
|
||||
from qiskit.transpiler.exceptions import TranspilerError
|
||||
from qiskit.utils.deprecation import deprecate_func
|
||||
|
||||
|
||||
class AlignMeasures(TransformationPass):
|
||||
"""Measurement alignment.
|
||||
|
||||
This is a control electronics aware optimization pass.
|
||||
|
||||
In many quantum computing architectures gates (instructions) are implemented with
|
||||
shaped analog stimulus signals. These signals are digitally stored in the
|
||||
waveform memory of the control electronics and converted into analog voltage signals
|
||||
by electronic components called digital to analog converters (DAC).
|
||||
|
||||
In a typical hardware implementation of superconducting quantum processors,
|
||||
a single qubit instruction is implemented by a
|
||||
microwave signal with the duration of around several tens of ns with a per-sample
|
||||
time resolution of ~0.1-10ns, as reported by ``backend.configuration().dt``.
|
||||
In such systems requiring higher DAC bandwidth, control electronics often
|
||||
defines a `pulse granularity`, in other words a data chunk, to allow the DAC to
|
||||
perform the signal conversion in parallel to gain the bandwidth.
|
||||
|
||||
Measurement alignment is required if a backend only allows triggering ``measure``
|
||||
instructions at a certain multiple value of this pulse granularity.
|
||||
This value is usually provided by ``backend.configuration().timing_constraints``.
|
||||
|
||||
In Qiskit SDK, the duration of delay can take arbitrary value in units of ``dt``,
|
||||
thus circuits involving delays may violate the above alignment constraint (i.e. misalignment).
|
||||
This pass shifts measurement instructions to a new time position to fix the misalignment,
|
||||
by inserting extra delay right before the measure instructions.
|
||||
The input of this pass should be scheduled :class:`~qiskit.dagcircuit.DAGCircuit`,
|
||||
thus one should select one of the scheduling passes
|
||||
(:class:`~qiskit.transpiler.passes.ALAPSchedule` or
|
||||
:class:`~qiskit.trasnpiler.passes.ASAPSchedule`) before calling this.
|
||||
|
||||
Examples:
|
||||
We assume executing the following circuit on a backend with ``alignment=16``.
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
┌───┐┌────────────────┐┌─┐
|
||||
q_0: ┤ X ├┤ Delay(100[dt]) ├┤M├
|
||||
└───┘└────────────────┘└╥┘
|
||||
c: 1/════════════════════════╩═
|
||||
0
|
||||
|
||||
Note that delay of 100 dt induces a misalignment of 4 dt at the measurement.
|
||||
This pass appends an extra 12 dt time shift to the input circuit.
|
||||
|
||||
.. parsed-literal::
|
||||
|
||||
┌───┐┌────────────────┐┌─┐
|
||||
q_0: ┤ X ├┤ Delay(112[dt]) ├┤M├
|
||||
└───┘└────────────────┘└╥┘
|
||||
c: 1/════════════════════════╩═
|
||||
0
|
||||
|
||||
This pass always inserts a positive delay before measurements
|
||||
rather than reducing other delays.
|
||||
|
||||
Notes:
|
||||
The Backend may allow users to execute circuits violating the alignment constraint.
|
||||
However, it may return meaningless measurement data mainly due to the phase error.
|
||||
"""
|
||||
|
||||
@deprecate_func(
|
||||
additional_msg=(
|
||||
"Instead, use :class:`~.ConstrainedReschedule`, which performs the same function "
|
||||
"but also supports aligning to additional timing constraints."
|
||||
),
|
||||
since="0.21.0",
|
||||
pending=True,
|
||||
)
|
||||
def __init__(self, alignment: int = 1):
|
||||
"""Create new pass.
|
||||
|
||||
Args:
|
||||
alignment: Integer number representing the minimum time resolution to
|
||||
trigger measure instruction in units of ``dt``. This value depends on
|
||||
the control electronics of your quantum processor.
|
||||
"""
|
||||
super().__init__()
|
||||
self.alignment = alignment
|
||||
|
||||
def run(self, dag: DAGCircuit):
|
||||
"""Run the measurement alignment pass on `dag`.
|
||||
|
||||
Args:
|
||||
dag (DAGCircuit): DAG to be checked.
|
||||
|
||||
Returns:
|
||||
DAGCircuit: DAG with consistent timing and op nodes annotated with duration.
|
||||
|
||||
Raises:
|
||||
TranspilerError: If circuit is not scheduled.
|
||||
"""
|
||||
time_unit = self.property_set["time_unit"]
|
||||
|
||||
if not _check_alignment_required(dag, self.alignment, Measure):
|
||||
# return input as-is to avoid unnecessary scheduling.
|
||||
# because following procedure regenerate new DAGCircuit,
|
||||
# we should avoid continuing if not necessary from performance viewpoint.
|
||||
return dag
|
||||
|
||||
# if circuit is not yet scheduled, schedule with ALAP method
|
||||
if dag.duration is None:
|
||||
raise TranspilerError(
|
||||
f"This circuit {dag.name} may involve a delay instruction violating the "
|
||||
"pulse controller alignment. To adjust instructions to "
|
||||
"right timing, you should call one of scheduling passes first. "
|
||||
"This is usually done by calling transpiler with scheduling_method='alap'."
|
||||
)
|
||||
|
||||
# the following lines are basically copied from ASAPSchedule pass
|
||||
#
|
||||
# * some validations for non-scheduled nodes are dropped, since we assume scheduled input
|
||||
# * pad_with_delay is called only with non-delay node to avoid consecutive delay
|
||||
new_dag = dag.copy_empty_like()
|
||||
|
||||
qubit_time_available: dict[QubitSpecifier, int] = defaultdict(int) # to track op start time
|
||||
qubit_stop_times: dict[QubitSpecifier, int] = defaultdict(
|
||||
int
|
||||
) # to track delay start time for padding
|
||||
clbit_readable: dict[ClbitSpecifier, int] = defaultdict(int)
|
||||
clbit_writeable: dict[ClbitSpecifier, int] = defaultdict(int)
|
||||
|
||||
def pad_with_delays(qubits: Iterable[QubitSpecifier], until, unit) -> None:
|
||||
"""Pad idle time-slots in ``qubits`` with delays in ``unit`` until ``until``."""
|
||||
for q in qubits:
|
||||
if qubit_stop_times[q] < until:
|
||||
idle_duration = until - qubit_stop_times[q]
|
||||
new_dag.apply_operation_back(Delay(idle_duration, unit), (q,), check=False)
|
||||
|
||||
for node in dag.topological_op_nodes():
|
||||
# choose appropriate clbit available time depending on op
|
||||
clbit_time_available = (
|
||||
clbit_writeable if isinstance(node.op, Measure) else clbit_readable
|
||||
)
|
||||
# correction to change clbit start time to qubit start time
|
||||
delta = node.op.duration if isinstance(node.op, Measure) else 0
|
||||
start_time = max(
|
||||
itertools.chain(
|
||||
(qubit_time_available[q] for q in node.qargs),
|
||||
(
|
||||
clbit_time_available[c] - delta
|
||||
for c in node.cargs + tuple(node.op.condition_bits)
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
if isinstance(node.op, Measure):
|
||||
if start_time % self.alignment != 0:
|
||||
start_time = ((start_time // self.alignment) + 1) * self.alignment
|
||||
|
||||
if not isinstance(node.op, Delay): # exclude delays for combining consecutive delays
|
||||
pad_with_delays(node.qargs, until=start_time, unit=time_unit)
|
||||
new_dag.apply_operation_back(node.op, node.qargs, node.cargs, check=False)
|
||||
|
||||
stop_time = start_time + node.op.duration
|
||||
# update time table
|
||||
for q in node.qargs:
|
||||
qubit_time_available[q] = stop_time
|
||||
if not isinstance(node.op, Delay):
|
||||
qubit_stop_times[q] = stop_time
|
||||
for c in node.cargs: # measure
|
||||
clbit_writeable[c] = clbit_readable[c] = stop_time
|
||||
for c in node.op.condition_bits: # conditional op
|
||||
clbit_writeable[c] = max(start_time, clbit_writeable[c])
|
||||
|
||||
working_qubits = qubit_time_available.keys()
|
||||
circuit_duration = max(qubit_time_available[q] for q in working_qubits)
|
||||
pad_with_delays(new_dag.qubits, until=circuit_duration, unit=time_unit)
|
||||
|
||||
new_dag.name = dag.name
|
||||
new_dag.metadata = dag.metadata
|
||||
|
||||
# set circuit duration and unit to indicate it is scheduled
|
||||
new_dag.duration = circuit_duration
|
||||
new_dag.unit = time_unit
|
||||
|
||||
return new_dag
|
||||
|
||||
|
||||
def _check_alignment_required(
|
||||
dag: DAGCircuit,
|
||||
alignment: int,
|
||||
instructions: Type | list[Type],
|
||||
) -> bool:
|
||||
"""Check DAG nodes and return a boolean representing if instruction scheduling is necessary.
|
||||
|
||||
Args:
|
||||
dag: DAG circuit to check.
|
||||
alignment: Instruction alignment condition.
|
||||
instructions: Target instructions.
|
||||
|
||||
Returns:
|
||||
If instruction scheduling is necessary.
|
||||
"""
|
||||
if not isinstance(instructions, list):
|
||||
instructions = [instructions]
|
||||
|
||||
if alignment == 1:
|
||||
# disable alignment if arbitrary t0 value can be used
|
||||
return False
|
||||
|
||||
if all(len(dag.op_nodes(inst)) == 0 for inst in instructions):
|
||||
# disable alignment if target instruction is not involved
|
||||
return False
|
||||
|
||||
# check delay durations
|
||||
for delay_node in dag.op_nodes(Delay):
|
||||
duration = delay_node.op.duration
|
||||
if isinstance(duration, ParameterExpression):
|
||||
# duration is parametrized:
|
||||
# raise user warning if backend alignment is not 1.
|
||||
warnings.warn(
|
||||
f"Parametrized delay with {repr(duration)} is found in circuit {dag.name}. "
|
||||
f"This backend requires alignment={alignment}. "
|
||||
"Please make sure all assigned values are multiple values of the alignment.",
|
||||
UserWarning,
|
||||
)
|
||||
else:
|
||||
# duration is bound:
|
||||
# check duration and trigger alignment if it violates constraint
|
||||
if duration % alignment != 0:
|
||||
return True
|
||||
|
||||
# disable alignment if all delays are multiple values of the alignment
|
||||
return False
|
|
@ -1,177 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""ASAP Scheduling."""
|
||||
|
||||
from qiskit.circuit import Delay, Qubit, Measure
|
||||
from qiskit.dagcircuit import DAGCircuit
|
||||
from qiskit.transpiler.exceptions import TranspilerError
|
||||
from qiskit.utils.deprecation import deprecate_func
|
||||
|
||||
from .base_scheduler import BaseSchedulerTransform
|
||||
|
||||
|
||||
class ASAPSchedule(BaseSchedulerTransform):
|
||||
"""ASAP Scheduling pass, which schedules the start time of instructions as early as possible..
|
||||
|
||||
See :class:`~qiskit.transpiler.passes.scheduling.base_scheduler.BaseSchedulerTransform` for the
|
||||
detailed behavior of the control flow operation, i.e. ``c_if``.
|
||||
|
||||
.. note::
|
||||
|
||||
This base class has been superseded by :class:`~.ASAPScheduleAnalysis` and
|
||||
the new scheduling workflow. It will be deprecated and subsequently
|
||||
removed in a future release.
|
||||
"""
|
||||
|
||||
@deprecate_func(
|
||||
additional_msg=(
|
||||
"Instead, use :class:`~.ASAPScheduleAnalysis`, which is an "
|
||||
"analysis pass that requires a padding pass to later modify the circuit."
|
||||
),
|
||||
since="0.21.0",
|
||||
pending=True,
|
||||
)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def run(self, dag):
|
||||
"""Run the ASAPSchedule 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("ASAP 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_after = {q: 0 for q in dag.qubits + dag.clbits}
|
||||
for node in dag.topological_op_nodes():
|
||||
op_duration = self._get_node_duration(node, dag)
|
||||
|
||||
# compute t0, t1: instruction interval, note that
|
||||
# t0: start time of instruction
|
||||
# t1: end time of instruction
|
||||
if isinstance(node.op, self.CONDITIONAL_SUPPORTED):
|
||||
t0q = max(idle_after[q] for q in node.qargs)
|
||||
if node.op.condition_bits:
|
||||
# conditional is bit tricky due to conditional_latency
|
||||
t0c = max(idle_after[bit] for bit in node.op.condition_bits)
|
||||
if t0q > t0c:
|
||||
# This is situation something like below
|
||||
#
|
||||
# |t0q
|
||||
# Q ▒▒▒▒▒▒▒▒▒░░
|
||||
# C ▒▒▒░░░░░░░░
|
||||
# |t0c
|
||||
#
|
||||
# In this case, you can insert readout access before tq0
|
||||
#
|
||||
# |t0q
|
||||
# Q ▒▒▒▒▒▒▒▒▒▒▒
|
||||
# C ▒▒▒░░░▒▒░░░
|
||||
# |t0q - conditional_latency
|
||||
#
|
||||
t0c = max(t0q - self.conditional_latency, t0c)
|
||||
t1c = t0c + self.conditional_latency
|
||||
for bit in node.op.condition_bits:
|
||||
# Lock clbit until state is read
|
||||
idle_after[bit] = t1c
|
||||
# It starts after register read access
|
||||
t0 = max(t0q, t1c)
|
||||
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 ASAP scheduler."
|
||||
)
|
||||
|
||||
if isinstance(node.op, Measure):
|
||||
# measure instruction handling is bit tricky due to clbit_write_latency
|
||||
t0q = max(idle_after[q] for q in node.qargs)
|
||||
t0c = max(idle_after[c] for c in node.cargs)
|
||||
# Assume following case (t0c > t0q)
|
||||
#
|
||||
# |t0q
|
||||
# Q ▒▒▒▒░░░░░░░░░░░░
|
||||
# C ▒▒▒▒▒▒▒▒░░░░░░░░
|
||||
# |t0c
|
||||
#
|
||||
# In this case, there is no actual clbit access until clbit_write_latency.
|
||||
# The node t0 can be push backward by this amount.
|
||||
#
|
||||
# |t0q' = t0c - clbit_write_latency
|
||||
# Q ▒▒▒▒░░▒▒▒▒▒▒▒▒▒▒
|
||||
# C ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
||||
# |t0c' = t0c
|
||||
#
|
||||
# rather than naively doing
|
||||
#
|
||||
# |t0q' = t0c
|
||||
# Q ▒▒▒▒░░░░▒▒▒▒▒▒▒▒
|
||||
# C ▒▒▒▒▒▒▒▒░░░▒▒▒▒▒
|
||||
# |t0c' = t0c + clbit_write_latency
|
||||
#
|
||||
t0 = max(t0q, t0c - self.clbit_write_latency)
|
||||
t1 = t0 + op_duration
|
||||
for clbit in node.cargs:
|
||||
idle_after[clbit] = t1
|
||||
else:
|
||||
# It happens to be directives such as barrier
|
||||
t0 = max(idle_after[bit] for bit in node.qargs + node.cargs)
|
||||
t1 = t0 + op_duration
|
||||
|
||||
# Add delay to qubit wire
|
||||
for bit in node.qargs:
|
||||
delta = t0 - idle_after[bit]
|
||||
if (
|
||||
delta > 0
|
||||
and isinstance(bit, Qubit)
|
||||
and self._delay_supported(dag.find_bit(bit).index)
|
||||
):
|
||||
new_dag.apply_operation_back(Delay(delta, time_unit), [bit], [])
|
||||
idle_after[bit] = t1
|
||||
|
||||
new_dag.apply_operation_back(node.op, node.qargs, node.cargs)
|
||||
|
||||
circuit_duration = max(idle_after.values())
|
||||
for bit, after in idle_after.items():
|
||||
delta = circuit_duration - after
|
||||
if not (delta > 0 and isinstance(bit, Qubit)):
|
||||
continue
|
||||
if self._delay_supported(dag.find_bit(bit).index):
|
||||
new_dag.apply_operation_back(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
|
|
@ -1,293 +0,0 @@
|
|||
# This code is part of Qiskit.
|
||||
#
|
||||
# (C) Copyright IBM 2021.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Dynamical Decoupling insertion pass."""
|
||||
|
||||
import itertools
|
||||
|
||||
import numpy as np
|
||||
from qiskit.circuit import Gate, Delay, Reset
|
||||
from qiskit.circuit.library.standard_gates import IGate, UGate, U3Gate
|
||||
from qiskit.dagcircuit import DAGOpNode, DAGInNode
|
||||
from qiskit.quantum_info.operators.predicates import matrix_equal
|
||||
from qiskit.quantum_info.synthesis import OneQubitEulerDecomposer
|
||||
from qiskit.transpiler.passes.optimization import Optimize1qGates
|
||||
from qiskit.transpiler.basepasses import TransformationPass
|
||||
from qiskit.transpiler.exceptions import TranspilerError
|
||||
from qiskit.utils.deprecation import deprecate_func
|
||||
|
||||
|
||||
class DynamicalDecoupling(TransformationPass):
|
||||
"""Dynamical decoupling insertion pass.
|
||||
|
||||
This pass works on a scheduled, physical circuit. It scans the circuit for
|
||||
idle periods of time (i.e. those containing delay instructions) and inserts
|
||||
a DD sequence of gates in those spots. These gates amount to the identity,
|
||||
so do not alter the logical action of the circuit, but have the effect of
|
||||
mitigating decoherence in those idle periods.
|
||||
|
||||
As a special case, the pass allows a length-1 sequence (e.g. [XGate()]).
|
||||
In this case the DD insertion happens only when the gate inverse can be
|
||||
absorbed into a neighboring gate in the circuit (so we would still be
|
||||
replacing Delay with something that is equivalent to the identity).
|
||||
This can be used, for instance, as a Hahn echo.
|
||||
|
||||
This pass ensures that the inserted sequence preserves the circuit exactly
|
||||
(including global phase).
|
||||
|
||||
.. plot::
|
||||
:include-source:
|
||||
|
||||
import numpy as np
|
||||
from qiskit.circuit import QuantumCircuit
|
||||
from qiskit.circuit.library import XGate
|
||||
from qiskit.transpiler import PassManager, InstructionDurations
|
||||
from qiskit.transpiler.passes import ALAPSchedule, DynamicalDecoupling
|
||||
from qiskit.visualization import timeline_drawer
|
||||
|
||||
# Because the legacy passes do not propagate the scheduling information correctly, it is
|
||||
# necessary to run a no-op "re-schedule" before the output circuits can be drawn.
|
||||
def draw(circuit):
|
||||
from qiskit import transpile
|
||||
|
||||
scheduled = transpile(
|
||||
circuit,
|
||||
optimization_level=0,
|
||||
instruction_durations=InstructionDurations(),
|
||||
scheduling_method="alap",
|
||||
)
|
||||
return timeline_drawer(scheduled)
|
||||
|
||||
circ = QuantumCircuit(4)
|
||||
circ.h(0)
|
||||
circ.cx(0, 1)
|
||||
circ.cx(1, 2)
|
||||
circ.cx(2, 3)
|
||||
circ.measure_all()
|
||||
durations = InstructionDurations(
|
||||
[("h", 0, 50), ("cx", [0, 1], 700), ("reset", None, 10),
|
||||
("cx", [1, 2], 200), ("cx", [2, 3], 300),
|
||||
("x", None, 50), ("measure", None, 1000)]
|
||||
)
|
||||
# balanced X-X sequence on all qubits
|
||||
dd_sequence = [XGate(), XGate()]
|
||||
pm = PassManager([ALAPSchedule(durations),
|
||||
DynamicalDecoupling(durations, dd_sequence)])
|
||||
circ_dd = pm.run(circ)
|
||||
draw(circ_dd)
|
||||
|
||||
# Uhrig sequence on qubit 0
|
||||
n = 8
|
||||
dd_sequence = [XGate()] * n
|
||||
def uhrig_pulse_location(k):
|
||||
return np.sin(np.pi * (k + 1) / (2 * n + 2)) ** 2
|
||||
spacing = []
|
||||
for k in range(n):
|
||||
spacing.append(uhrig_pulse_location(k) - sum(spacing))
|
||||
spacing.append(1 - sum(spacing))
|
||||
pm = PassManager(
|
||||
[
|
||||
ALAPSchedule(durations),
|
||||
DynamicalDecoupling(durations, dd_sequence, qubits=[0], spacing=spacing),
|
||||
]
|
||||
)
|
||||
circ_dd = pm.run(circ)
|
||||
draw(circ_dd)
|
||||
"""
|
||||
|
||||
@deprecate_func(
|
||||
additional_msg=(
|
||||
"Instead, use :class:`~.PadDynamicalDecoupling`, which performs the same "
|
||||
"function but requires scheduling and alignment analysis passes to run prior to it."
|
||||
),
|
||||
since="0.21.0",
|
||||
pending=True,
|
||||
)
|
||||
def __init__(
|
||||
self, durations, dd_sequence, qubits=None, spacing=None, skip_reset_qubits=True, target=None
|
||||
):
|
||||
"""Dynamical decoupling initializer.
|
||||
|
||||
Args:
|
||||
durations (InstructionDurations): Durations of instructions to be
|
||||
used in scheduling.
|
||||
dd_sequence (list[Gate]): sequence of gates to apply in idle spots.
|
||||
qubits (list[int]): physical qubits on which to apply DD.
|
||||
If None, all qubits will undergo DD (when possible).
|
||||
spacing (list[float]): a list of spacings between the DD gates.
|
||||
The available slack will be divided according to this.
|
||||
The list length must be one more than the length of dd_sequence,
|
||||
and the elements must sum to 1. If None, a balanced spacing
|
||||
will be used [d/2, d, d, ..., d, d, d/2].
|
||||
skip_reset_qubits (bool): if True, does not insert DD on idle
|
||||
periods that immediately follow initialized/reset qubits (as
|
||||
qubits in the ground state are less susceptile to decoherence).
|
||||
target (Target): The :class:`~.Target` representing the target backend, if both
|
||||
``durations`` and this are specified then this argument will take
|
||||
precedence and ``durations`` will be ignored.
|
||||
"""
|
||||
super().__init__()
|
||||
self._durations = durations
|
||||
self._dd_sequence = dd_sequence
|
||||
self._qubits = qubits
|
||||
self._spacing = spacing
|
||||
self._skip_reset_qubits = skip_reset_qubits
|
||||
self._target = target
|
||||
if target is not None:
|
||||
self._durations = target.durations()
|
||||
for gate in dd_sequence:
|
||||
if gate.name not in target.operation_names:
|
||||
raise TranspilerError(
|
||||
f"{gate.name} in dd_sequence is not supported in the target"
|
||||
)
|
||||
|
||||
def run(self, dag):
|
||||
"""Run the DynamicalDecoupling pass on dag.
|
||||
|
||||
Args:
|
||||
dag (DAGCircuit): a scheduled DAG.
|
||||
|
||||
Returns:
|
||||
DAGCircuit: equivalent circuit with delays interrupted by DD,
|
||||
where possible.
|
||||
|
||||
Raises:
|
||||
TranspilerError: if the circuit is not mapped on physical qubits.
|
||||
"""
|
||||
if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None:
|
||||
raise TranspilerError("DD runs on physical circuits only.")
|
||||
|
||||
if dag.duration is None:
|
||||
raise TranspilerError("DD runs after circuit is scheduled.")
|
||||
|
||||
num_pulses = len(self._dd_sequence)
|
||||
sequence_gphase = 0
|
||||
if num_pulses != 1:
|
||||
if num_pulses % 2 != 0:
|
||||
raise TranspilerError("DD sequence must contain an even number of gates (or 1).")
|
||||
noop = np.eye(2)
|
||||
for gate in self._dd_sequence:
|
||||
noop = noop.dot(gate.to_matrix())
|
||||
if not matrix_equal(noop, IGate().to_matrix(), ignore_phase=True):
|
||||
raise TranspilerError("The DD sequence does not make an identity operation.")
|
||||
sequence_gphase = np.angle(noop[0][0])
|
||||
|
||||
if self._qubits is None:
|
||||
self._qubits = set(range(dag.num_qubits()))
|
||||
else:
|
||||
self._qubits = set(self._qubits)
|
||||
|
||||
if self._spacing:
|
||||
if sum(self._spacing) != 1 or any(a < 0 for a in self._spacing):
|
||||
raise TranspilerError(
|
||||
"The spacings must be given in terms of fractions "
|
||||
"of the slack period and sum to 1."
|
||||
)
|
||||
else: # default to balanced spacing
|
||||
mid = 1 / num_pulses
|
||||
end = mid / 2
|
||||
self._spacing = [end] + [mid] * (num_pulses - 1) + [end]
|
||||
|
||||
for qarg in list(self._qubits):
|
||||
for gate in self._dd_sequence:
|
||||
if not self.__gate_supported(gate, qarg):
|
||||
self._qubits.discard(qarg)
|
||||
break
|
||||
|
||||
index_sequence_duration_map = {}
|
||||
for physical_qubit in self._qubits:
|
||||
dd_sequence_duration = 0
|
||||
for index, gate in enumerate(self._dd_sequence):
|
||||
gate = gate.to_mutable()
|
||||
self._dd_sequence[index] = gate
|
||||
gate.duration = self._durations.get(gate, physical_qubit)
|
||||
|
||||
dd_sequence_duration += gate.duration
|
||||
index_sequence_duration_map[physical_qubit] = dd_sequence_duration
|
||||
|
||||
new_dag = dag.copy_empty_like()
|
||||
|
||||
for nd in dag.topological_op_nodes():
|
||||
if not isinstance(nd.op, Delay):
|
||||
new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs, check=False)
|
||||
continue
|
||||
|
||||
dag_qubit = nd.qargs[0]
|
||||
physical_qubit = dag.find_bit(dag_qubit).index
|
||||
if physical_qubit not in self._qubits: # skip unwanted qubits
|
||||
new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs, check=False)
|
||||
continue
|
||||
|
||||
pred = next(dag.predecessors(nd))
|
||||
succ = next(dag.successors(nd))
|
||||
if self._skip_reset_qubits: # discount initial delays
|
||||
if isinstance(pred, DAGInNode) or isinstance(pred.op, Reset):
|
||||
new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs, check=False)
|
||||
continue
|
||||
|
||||
dd_sequence_duration = index_sequence_duration_map[physical_qubit]
|
||||
slack = nd.op.duration - dd_sequence_duration
|
||||
if slack <= 0: # dd doesn't fit
|
||||
new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs, check=False)
|
||||
continue
|
||||
|
||||
if num_pulses == 1: # special case of using a single gate for DD
|
||||
u_inv = self._dd_sequence[0].inverse().to_matrix()
|
||||
theta, phi, lam, phase = OneQubitEulerDecomposer().angles_and_phase(u_inv)
|
||||
# absorb the inverse into the successor (from left in circuit)
|
||||
if isinstance(succ, DAGOpNode) and isinstance(succ.op, (UGate, U3Gate)):
|
||||
theta_r, phi_r, lam_r = succ.op.params
|
||||
succ.op.params = Optimize1qGates.compose_u3(
|
||||
theta_r, phi_r, lam_r, theta, phi, lam
|
||||
)
|
||||
sequence_gphase += phase
|
||||
# absorb the inverse into the predecessor (from right in circuit)
|
||||
elif isinstance(pred, DAGOpNode) and isinstance(pred.op, (UGate, U3Gate)):
|
||||
theta_l, phi_l, lam_l = pred.op.params
|
||||
pred.op.params = Optimize1qGates.compose_u3(
|
||||
theta, phi, lam, theta_l, phi_l, lam_l
|
||||
)
|
||||
sequence_gphase += phase
|
||||
# don't do anything if there's no single-qubit gate to absorb the inverse
|
||||
else:
|
||||
new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs, check=False)
|
||||
continue
|
||||
|
||||
# insert the actual DD sequence
|
||||
taus = [int(slack * a) for a in self._spacing]
|
||||
unused_slack = slack - sum(taus) # unused, due to rounding to int multiples of dt
|
||||
middle_index = int((len(taus) - 1) / 2) # arbitrary: redistribute to middle
|
||||
taus[middle_index] += unused_slack # now we add up to original delay duration
|
||||
|
||||
for tau, gate in itertools.zip_longest(taus, self._dd_sequence):
|
||||
if tau > 0:
|
||||
new_dag.apply_operation_back(Delay(tau), [dag_qubit], check=False)
|
||||
if gate is not None:
|
||||
new_dag.apply_operation_back(gate, [dag_qubit], check=False)
|
||||
|
||||
new_dag.global_phase = _mod_2pi(new_dag.global_phase + sequence_gphase)
|
||||
|
||||
return new_dag
|
||||
|
||||
def __gate_supported(self, gate: Gate, qarg: int) -> bool:
|
||||
"""A gate is supported on the qubit (qarg) or not."""
|
||||
if self._target is None or self._target.instruction_supported(gate.name, qargs=(qarg,)):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _mod_2pi(angle: float, atol: float = 0):
|
||||
"""Wrap angle into interval [-π,π). If within atol of the endpoint, clamp to -π"""
|
||||
wrapped = (angle + np.pi) % (2 * np.pi) - np.pi
|
||||
if abs(wrapped - np.pi) < atol:
|
||||
wrapped = -np.pi
|
||||
return wrapped
|
|
@ -13,8 +13,6 @@
|
|||
"""Utility passes and functions used for other main passes."""
|
||||
|
||||
from .check_map import CheckMap
|
||||
from .check_cx_direction import CheckCXDirection # Deprecated
|
||||
from .cx_direction import CXDirection # Deprecated
|
||||
from .check_gate_direction import CheckGateDirection
|
||||
from .gate_direction import GateDirection
|
||||
from .barrier_before_final_measurements import BarrierBeforeFinalMeasurements
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
# This code is part of Qiskit.
|
||||
#
|
||||
# (C) Copyright IBM 2017, 2018.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Check if the CNOTs follow the right direction with respect to the coupling map.."""
|
||||
|
||||
from qiskit.transpiler.passes.utils.check_gate_direction import CheckGateDirection
|
||||
from qiskit.utils.deprecation import deprecate_func
|
||||
|
||||
|
||||
class CheckCXDirection(CheckGateDirection):
|
||||
"""Deprecated: use :class:`qiskit.transpiler.passes.CheckGateDirection` pass instead."""
|
||||
|
||||
@deprecate_func(
|
||||
additional_msg="Instead, use the more generic :class:`~.CheckGateDirection` pass.",
|
||||
since="0.21.0",
|
||||
)
|
||||
def __init__(self, coupling_map=None, target=None):
|
||||
super().__init__(coupling_map=coupling_map, target=target)
|
|
@ -1,27 +0,0 @@
|
|||
# This code is part of Qiskit.
|
||||
#
|
||||
# (C) Copyright IBM 2017, 2018.
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""Rearrange the direction of the cx nodes to match the directed coupling map."""
|
||||
|
||||
from qiskit.transpiler.passes.utils.gate_direction import GateDirection
|
||||
from qiskit.utils.deprecation import deprecate_func
|
||||
|
||||
|
||||
class CXDirection(GateDirection):
|
||||
"""Deprecated: use :class:`qiskit.transpiler.passes.GateDirection` pass instead."""
|
||||
|
||||
@deprecate_func(
|
||||
additional_msg="Instead, use the more generic :class:`~.GateDirection` pass.",
|
||||
since="0.21.0",
|
||||
)
|
||||
def __init__(self, coupling_map):
|
||||
super().__init__(coupling_map)
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
upgrade:
|
||||
- |
|
||||
The function :func:`~qiskit.execute_function.execute`
|
||||
does not accept the arguments `qobj_id` and `qobj_header` any more.
|
||||
Their use was deprecated in Qiskit 0.37 (with Terra 0.21), released on June 2022.
|
||||
- |
|
||||
The transpilation pass ``qiskit.transpiler.passes.ALAPSchedule`` is removed. It use was deprecated
|
||||
in Qiskit 0.37 (with Terra 0.21), released on June 2022 and replaced by
|
||||
:class:`~.transpiler.passes.ALAPScheduleAnalysis`, which is an
|
||||
analysis pass.
|
||||
- |
|
||||
The transpilation pass ``qiskit.transpiler.passes.ASAPSchedule`` is removed. It use was deprecated
|
||||
in Qiskit 0.37 (with Terra 0.21), released on June 2022. It has been superseded by
|
||||
:class:`~.ASAPScheduleAnalysis` and the new scheduling workflow.
|
||||
- |
|
||||
The transpilation pass ``qiskit.transpiler.passes.DynamicalDecoupling`` is removed. It use was deprecated
|
||||
in Qiskit 0.37 (with Terra 0.21), released on June 2022.
|
||||
Instead, use :class:`~.transpiler.passes.PadDynamicalDecoupling`, which performs the same
|
||||
function but requires scheduling and alignment analysis passes to run prior to it.
|
||||
- |
|
||||
The transpilation pass ``qiskit.transpiler.passes.AlignMeasures`` is removed. It use was deprecated
|
||||
in Qiskit 0.37 (with Terra 0.21), released on June 2022.
|
||||
Instead, use :class:`~.ConstrainedReschedule`, which performs the same function
|
||||
and also supports aligning to additional timing constraints.
|
||||
- |
|
||||
The transpilation pass ``qiskit.transpiler.passes.CXDirection`` is removed. It use was deprecated
|
||||
in Qiskit 0.37 (with Terra 0.21), released on June 2022.
|
||||
Instead, use the more generic :class:`~.GateDirection` pass.
|
||||
- |
|
||||
The transpilation pass ``qiskit.transpiler.passes.CheckCXDirection`` is removed. It use was deprecated
|
||||
in Qiskit 0.37 (with Terra 0.21), released on June 2022.
|
||||
Instead, use the more generic :class:`~.CheckGateDirection` pass.
|
||||
- |
|
||||
The methods ``to_dict`` in the classes :class:`.pulse.transforms.AlignmentKind`,
|
||||
`.pulse.transforms.AlignEquispaced`, and :class:`.pulse.transforms.AlignFunc` are removed.
|
||||
They were deprecated
|
||||
in Qiskit 0.37 (with Terra 0.21), released on June 2022.
|
||||
- |
|
||||
The argument ``circuits`` in the method :meth:`qiskit.qpy.interface.dump`
|
||||
is removed as its usage was deprecated
|
||||
in Qiskit 0.37 (with Terra 0.21), released on June 2022.
|
||||
Instead, use the argument ``programs``, which behaves identically.
|
||||
|
|
@ -15,13 +15,13 @@
|
|||
|
||||
from qiskit import transpile
|
||||
from qiskit.circuit.library.standard_gates import XGate
|
||||
from qiskit.transpiler import CouplingMap
|
||||
from qiskit.transpiler import CouplingMap, PassManager
|
||||
from qiskit.transpiler import InstructionDurations
|
||||
from qiskit.transpiler.passes import (
|
||||
TimeUnitConversion,
|
||||
ASAPSchedule,
|
||||
ALAPSchedule,
|
||||
DynamicalDecoupling,
|
||||
ASAPScheduleAnalysis,
|
||||
ALAPScheduleAnalysis,
|
||||
PadDynamicalDecoupling,
|
||||
)
|
||||
from qiskit.converters import circuit_to_dag
|
||||
|
||||
|
@ -109,22 +109,39 @@ class SchedulingPassBenchmarks:
|
|||
dt=1e-9,
|
||||
)
|
||||
self.timed_dag = TimeUnitConversion(self.durations).run(self.dag)
|
||||
_pass = ALAPSchedule(self.durations)
|
||||
_pass.property_set["time_unit"] = "dt"
|
||||
self.scheduled_dag = _pass.run(self.timed_dag)
|
||||
dd_sequence = [XGate(), XGate()]
|
||||
pm = PassManager(
|
||||
[
|
||||
ALAPScheduleAnalysis(self.durations),
|
||||
PadDynamicalDecoupling(self.durations, dd_sequence),
|
||||
]
|
||||
)
|
||||
self.scheduled_dag = pm.run(self.timed_dag)
|
||||
|
||||
def time_time_unit_conversion_pass(self, _, __):
|
||||
TimeUnitConversion(self.durations).run(self.dag)
|
||||
|
||||
def time_alap_schedule_pass(self, _, __):
|
||||
_pass = ALAPSchedule(self.durations)
|
||||
_pass.property_set["time_unit"] = "dt"
|
||||
_pass.run(self.timed_dag)
|
||||
dd_sequence = [XGate(), XGate()]
|
||||
pm = PassManager(
|
||||
[
|
||||
ALAPScheduleAnalysis(self.durations),
|
||||
PadDynamicalDecoupling(self.durations, dd_sequence),
|
||||
]
|
||||
)
|
||||
pm.run(self.timed_dag)
|
||||
|
||||
def time_asap_schedule_pass(self, _, __):
|
||||
_pass = ASAPSchedule(self.durations)
|
||||
_pass.property_set["time_unit"] = "dt"
|
||||
_pass.run(self.timed_dag)
|
||||
dd_sequence = [XGate(), XGate()]
|
||||
pm = PassManager(
|
||||
[
|
||||
ASAPScheduleAnalysis(self.durations),
|
||||
PadDynamicalDecoupling(self.durations, dd_sequence),
|
||||
]
|
||||
)
|
||||
pm.run(self.timed_dag)
|
||||
|
||||
def time_dynamical_decoupling_pass(self, _, __):
|
||||
DynamicalDecoupling(self.durations, dd_sequence=[XGate(), XGate()]).run(self.scheduled_dag)
|
||||
PadDynamicalDecoupling(self.durations, dd_sequence=[XGate(), XGate()]).run(
|
||||
self.scheduled_dag
|
||||
)
|
||||
|
|
|
@ -14,316 +14,8 @@
|
|||
|
||||
from qiskit import QuantumCircuit, pulse
|
||||
from qiskit.test import QiskitTestCase
|
||||
from qiskit.transpiler import InstructionDurations
|
||||
from qiskit.transpiler.exceptions import TranspilerError
|
||||
from qiskit.transpiler.passes import (
|
||||
AlignMeasures,
|
||||
ValidatePulseGates,
|
||||
ALAPSchedule,
|
||||
TimeUnitConversion,
|
||||
)
|
||||
|
||||
|
||||
class TestAlignMeasures(QiskitTestCase):
|
||||
"""A test for measurement alignment pass."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
instruction_durations = InstructionDurations()
|
||||
instruction_durations.update(
|
||||
[
|
||||
("rz", (0,), 0),
|
||||
("rz", (1,), 0),
|
||||
("x", (0,), 160),
|
||||
("x", (1,), 160),
|
||||
("sx", (0,), 160),
|
||||
("sx", (1,), 160),
|
||||
("cx", (0, 1), 800),
|
||||
("cx", (1, 0), 800),
|
||||
("measure", None, 1600),
|
||||
]
|
||||
)
|
||||
self.time_conversion_pass = TimeUnitConversion(inst_durations=instruction_durations)
|
||||
# reproduce old behavior of 0.20.0 before #7655
|
||||
# currently default write latency is 0
|
||||
self.scheduling_pass = ALAPSchedule(
|
||||
durations=instruction_durations,
|
||||
clbit_write_latency=1600,
|
||||
conditional_latency=0,
|
||||
)
|
||||
self.align_measure_pass = AlignMeasures(alignment=16)
|
||||
|
||||
def test_t1_experiment_type(self):
|
||||
"""Test T1 experiment type circuit.
|
||||
|
||||
(input)
|
||||
|
||||
┌───┐┌────────────────┐┌─┐
|
||||
q_0: ┤ X ├┤ Delay(100[dt]) ├┤M├
|
||||
└───┘└────────────────┘└╥┘
|
||||
c: 1/════════════════════════╩═
|
||||
0
|
||||
|
||||
(aligned)
|
||||
|
||||
┌───┐┌────────────────┐┌─┐
|
||||
q_0: ┤ X ├┤ Delay(112[dt]) ├┤M├
|
||||
└───┘└────────────────┘└╥┘
|
||||
c: 1/════════════════════════╩═
|
||||
0
|
||||
|
||||
This type of experiment slightly changes delay duration of interest.
|
||||
However the quantization error should be less than alignment * dt.
|
||||
"""
|
||||
circuit = QuantumCircuit(1, 1)
|
||||
circuit.x(0)
|
||||
circuit.delay(100, 0, unit="dt")
|
||||
circuit.measure(0, 0)
|
||||
|
||||
timed_circuit = self.time_conversion_pass(circuit)
|
||||
scheduled_circuit = self.scheduling_pass(timed_circuit, property_set={"time_unit": "dt"})
|
||||
aligned_circuit = self.align_measure_pass(
|
||||
scheduled_circuit, property_set={"time_unit": "dt"}
|
||||
)
|
||||
|
||||
ref_circuit = QuantumCircuit(1, 1)
|
||||
ref_circuit.x(0)
|
||||
ref_circuit.delay(112, 0, unit="dt")
|
||||
ref_circuit.measure(0, 0)
|
||||
|
||||
self.assertEqual(aligned_circuit, ref_circuit)
|
||||
|
||||
def test_hanh_echo_experiment_type(self):
|
||||
"""Test Hahn echo experiment type circuit.
|
||||
|
||||
(input)
|
||||
|
||||
┌────┐┌────────────────┐┌───┐┌────────────────┐┌────┐┌─┐
|
||||
q_0: ┤ √X ├┤ Delay(100[dt]) ├┤ X ├┤ Delay(100[dt]) ├┤ √X ├┤M├
|
||||
└────┘└────────────────┘└───┘└────────────────┘└────┘└╥┘
|
||||
c: 1/══════════════════════════════════════════════════════╩═
|
||||
0
|
||||
|
||||
(output)
|
||||
|
||||
┌────┐┌────────────────┐┌───┐┌────────────────┐┌────┐┌──────────────┐┌─┐
|
||||
q_0: ┤ √X ├┤ Delay(100[dt]) ├┤ X ├┤ Delay(100[dt]) ├┤ √X ├┤ Delay(8[dt]) ├┤M├
|
||||
└────┘└────────────────┘└───┘└────────────────┘└────┘└──────────────┘└╥┘
|
||||
c: 1/══════════════════════════════════════════════════════════════════════╩═
|
||||
0
|
||||
|
||||
This type of experiment doesn't change duration of interest (two in the middle).
|
||||
However induces slight delay less than alignment * dt before measurement.
|
||||
This might induce extra amplitude damping error.
|
||||
"""
|
||||
circuit = QuantumCircuit(1, 1)
|
||||
circuit.sx(0)
|
||||
circuit.delay(100, 0, unit="dt")
|
||||
circuit.x(0)
|
||||
circuit.delay(100, 0, unit="dt")
|
||||
circuit.sx(0)
|
||||
circuit.measure(0, 0)
|
||||
|
||||
timed_circuit = self.time_conversion_pass(circuit)
|
||||
scheduled_circuit = self.scheduling_pass(timed_circuit, property_set={"time_unit": "dt"})
|
||||
aligned_circuit = self.align_measure_pass(
|
||||
scheduled_circuit, property_set={"time_unit": "dt"}
|
||||
)
|
||||
|
||||
ref_circuit = QuantumCircuit(1, 1)
|
||||
ref_circuit.sx(0)
|
||||
ref_circuit.delay(100, 0, unit="dt")
|
||||
ref_circuit.x(0)
|
||||
ref_circuit.delay(100, 0, unit="dt")
|
||||
ref_circuit.sx(0)
|
||||
ref_circuit.delay(8, 0, unit="dt")
|
||||
ref_circuit.measure(0, 0)
|
||||
|
||||
self.assertEqual(aligned_circuit, ref_circuit)
|
||||
|
||||
def test_mid_circuit_measure(self):
|
||||
"""Test circuit with mid circuit measurement.
|
||||
|
||||
(input)
|
||||
|
||||
┌───┐┌────────────────┐┌─┐┌───────────────┐┌───┐┌────────────────┐┌─┐
|
||||
q_0: ┤ X ├┤ Delay(100[dt]) ├┤M├┤ Delay(10[dt]) ├┤ X ├┤ Delay(120[dt]) ├┤M├
|
||||
└───┘└────────────────┘└╥┘└───────────────┘└───┘└────────────────┘└╥┘
|
||||
c: 2/════════════════════════╩══════════════════════════════════════════╩═
|
||||
0 1
|
||||
|
||||
(output)
|
||||
|
||||
┌───┐┌────────────────┐┌─┐┌───────────────┐┌───┐┌────────────────┐┌─┐
|
||||
q_0: ┤ X ├┤ Delay(112[dt]) ├┤M├┤ Delay(10[dt]) ├┤ X ├┤ Delay(134[dt]) ├┤M├
|
||||
└───┘└────────────────┘└╥┘└───────────────┘└───┘└────────────────┘└╥┘
|
||||
c: 2/════════════════════════╩══════════════════════════════════════════╩═
|
||||
0 1
|
||||
|
||||
Extra delay is always added to the existing delay right before the measurement.
|
||||
Delay after measurement is unchanged.
|
||||
"""
|
||||
circuit = QuantumCircuit(1, 2)
|
||||
circuit.x(0)
|
||||
circuit.delay(100, 0, unit="dt")
|
||||
circuit.measure(0, 0)
|
||||
circuit.delay(10, 0, unit="dt")
|
||||
circuit.x(0)
|
||||
circuit.delay(120, 0, unit="dt")
|
||||
circuit.measure(0, 1)
|
||||
|
||||
timed_circuit = self.time_conversion_pass(circuit)
|
||||
scheduled_circuit = self.scheduling_pass(timed_circuit, property_set={"time_unit": "dt"})
|
||||
aligned_circuit = self.align_measure_pass(
|
||||
scheduled_circuit, property_set={"time_unit": "dt"}
|
||||
)
|
||||
|
||||
ref_circuit = QuantumCircuit(1, 2)
|
||||
ref_circuit.x(0)
|
||||
ref_circuit.delay(112, 0, unit="dt")
|
||||
ref_circuit.measure(0, 0)
|
||||
ref_circuit.delay(10, 0, unit="dt")
|
||||
ref_circuit.x(0)
|
||||
ref_circuit.delay(134, 0, unit="dt")
|
||||
ref_circuit.measure(0, 1)
|
||||
|
||||
self.assertEqual(aligned_circuit, ref_circuit)
|
||||
|
||||
def test_mid_circuit_multiq_gates(self):
|
||||
"""Test circuit with mid circuit measurement and multi qubit gates.
|
||||
|
||||
(input)
|
||||
|
||||
┌───┐┌────────────────┐┌─┐ ┌─┐
|
||||
q_0: ┤ X ├┤ Delay(100[dt]) ├┤M├──■───────■──┤M├
|
||||
└───┘└────────────────┘└╥┘┌─┴─┐┌─┐┌─┴─┐└╥┘
|
||||
q_1: ────────────────────────╫─┤ X ├┤M├┤ X ├─╫─
|
||||
║ └───┘└╥┘└───┘ ║
|
||||
c: 2/════════════════════════╩═══════╩═══════╩═
|
||||
0 1 0
|
||||
|
||||
(output)
|
||||
|
||||
┌───┐ ┌────────────────┐┌─┐ ┌─────────────────┐ ┌─┐»
|
||||
q_0: ───────┤ X ├───────┤ Delay(112[dt]) ├┤M├──■──┤ Delay(1600[dt]) ├──■──┤M├»
|
||||
┌──────┴───┴──────┐└────────────────┘└╥┘┌─┴─┐└───────┬─┬───────┘┌─┴─┐└╥┘»
|
||||
q_1: ┤ Delay(1872[dt]) ├───────────────────╫─┤ X ├────────┤M├────────┤ X ├─╫─»
|
||||
└─────────────────┘ ║ └───┘ └╥┘ └───┘ ║ »
|
||||
c: 2/══════════════════════════════════════╩═══════════════╩═══════════════╩═»
|
||||
0 1 0 »
|
||||
«
|
||||
«q_0: ───────────────────
|
||||
« ┌─────────────────┐
|
||||
«q_1: ┤ Delay(1600[dt]) ├
|
||||
« └─────────────────┘
|
||||
«c: 2/═══════════════════
|
||||
«
|
||||
|
||||
Delay for the other channel paired by multi-qubit instruction is also scheduled.
|
||||
Delay (1872dt) = X (160dt) + Delay (100dt + extra 12dt) + Measure (1600dt).
|
||||
"""
|
||||
circuit = QuantumCircuit(2, 2)
|
||||
circuit.x(0)
|
||||
circuit.delay(100, 0, unit="dt")
|
||||
circuit.measure(0, 0)
|
||||
circuit.cx(0, 1)
|
||||
circuit.measure(1, 1)
|
||||
circuit.cx(0, 1)
|
||||
circuit.measure(0, 0)
|
||||
|
||||
timed_circuit = self.time_conversion_pass(circuit)
|
||||
scheduled_circuit = self.scheduling_pass(timed_circuit, property_set={"time_unit": "dt"})
|
||||
aligned_circuit = self.align_measure_pass(
|
||||
scheduled_circuit, property_set={"time_unit": "dt"}
|
||||
)
|
||||
|
||||
ref_circuit = QuantumCircuit(2, 2)
|
||||
ref_circuit.x(0)
|
||||
ref_circuit.delay(112, 0, unit="dt")
|
||||
ref_circuit.measure(0, 0)
|
||||
ref_circuit.delay(160 + 112 + 1600, 1, unit="dt")
|
||||
ref_circuit.cx(0, 1)
|
||||
ref_circuit.delay(1600, 0, unit="dt")
|
||||
ref_circuit.measure(1, 1)
|
||||
ref_circuit.cx(0, 1)
|
||||
ref_circuit.delay(1600, 1, unit="dt")
|
||||
ref_circuit.measure(0, 0)
|
||||
|
||||
self.assertEqual(aligned_circuit, ref_circuit)
|
||||
|
||||
def test_alignment_is_not_processed(self):
|
||||
"""Test avoid pass processing if delay is aligned."""
|
||||
circuit = QuantumCircuit(2, 2)
|
||||
circuit.x(0)
|
||||
circuit.delay(160, 0, unit="dt")
|
||||
circuit.measure(0, 0)
|
||||
circuit.cx(0, 1)
|
||||
circuit.measure(1, 1)
|
||||
circuit.cx(0, 1)
|
||||
circuit.measure(0, 0)
|
||||
|
||||
# pre scheduling is not necessary because alignment is skipped
|
||||
# this is to minimize breaking changes to existing code.
|
||||
transpiled = self.align_measure_pass(circuit, property_set={"time_unit": "dt"})
|
||||
|
||||
self.assertEqual(transpiled, circuit)
|
||||
|
||||
def test_circuit_using_clbit(self):
|
||||
"""Test a circuit with instructions using a common clbit.
|
||||
|
||||
(input)
|
||||
┌───┐┌────────────────┐┌─┐
|
||||
q_0: ┤ X ├┤ Delay(100[dt]) ├┤M├──────────────
|
||||
└───┘└────────────────┘└╥┘ ┌───┐
|
||||
q_1: ────────────────────────╫────┤ X ├──────
|
||||
║ └─╥─┘ ┌─┐
|
||||
q_2: ────────────────────────╫──────╫─────┤M├
|
||||
║ ┌────╨────┐└╥┘
|
||||
c: 1/════════════════════════╩═╡ c_0 = T ╞═╩═
|
||||
0 └─────────┘ 0
|
||||
|
||||
(aligned)
|
||||
┌───┐ ┌────────────────┐┌─┐┌────────────────┐
|
||||
q_0: ───────┤ X ├───────┤ Delay(112[dt]) ├┤M├┤ Delay(160[dt]) ├───
|
||||
┌──────┴───┴──────┐└────────────────┘└╥┘└─────┬───┬──────┘
|
||||
q_1: ┤ Delay(1872[dt]) ├───────────────────╫───────┤ X ├──────────
|
||||
└┬────────────────┤ ║ └─╥─┘ ┌─┐
|
||||
q_2: ─┤ Delay(432[dt]) ├───────────────────╫─────────╫─────────┤M├
|
||||
└────────────────┘ ║ ┌────╨────┐ └╥┘
|
||||
c: 1/══════════════════════════════════════╩════╡ c_0 = T ╞═════╩═
|
||||
0 └─────────┘ 0
|
||||
|
||||
Looking at the q_0, the total schedule length T becomes
|
||||
160 (x) + 112 (aligned delay) + 1600 (measure) + 160 (delay) = 2032.
|
||||
The last delay comes from ALAP scheduling called before the AlignMeasure pass,
|
||||
which aligns stop times as late as possible, so the start time of x(1).c_if(0)
|
||||
and the stop time of measure(0, 0) become T - 160.
|
||||
"""
|
||||
circuit = QuantumCircuit(3, 1)
|
||||
circuit.x(0)
|
||||
circuit.delay(100, 0, unit="dt")
|
||||
circuit.measure(0, 0)
|
||||
circuit.x(1).c_if(0, 1)
|
||||
circuit.measure(2, 0)
|
||||
|
||||
timed_circuit = self.time_conversion_pass(circuit)
|
||||
scheduled_circuit = self.scheduling_pass(timed_circuit, property_set={"time_unit": "dt"})
|
||||
aligned_circuit = self.align_measure_pass(
|
||||
scheduled_circuit, property_set={"time_unit": "dt"}
|
||||
)
|
||||
self.assertEqual(aligned_circuit.duration, 2032)
|
||||
|
||||
ref_circuit = QuantumCircuit(3, 1)
|
||||
ref_circuit.x(0)
|
||||
ref_circuit.delay(112, 0, unit="dt")
|
||||
ref_circuit.delay(1872, 1, unit="dt") # 2032 - 160
|
||||
ref_circuit.delay(432, 2, unit="dt") # 2032 - 1600
|
||||
ref_circuit.measure(0, 0)
|
||||
ref_circuit.x(1).c_if(0, 1)
|
||||
ref_circuit.delay(160, 0, unit="dt")
|
||||
ref_circuit.measure(2, 0)
|
||||
|
||||
self.assertEqual(aligned_circuit, ref_circuit)
|
||||
from qiskit.transpiler.passes import ValidatePulseGates
|
||||
|
||||
|
||||
class TestPulseGateValidation(QiskitTestCase):
|
||||
|
|
|
@ -1,811 +0,0 @@
|
|||
# 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.
|
||||
|
||||
"""Test the legacy Scheduling passes"""
|
||||
|
||||
import unittest
|
||||
|
||||
from ddt import ddt, data, unpack
|
||||
|
||||
from qiskit import QuantumCircuit
|
||||
from qiskit.circuit import Delay, Parameter
|
||||
from qiskit.circuit.library.standard_gates import XGate, YGate, CXGate
|
||||
from qiskit.test import QiskitTestCase
|
||||
from qiskit.transpiler.exceptions import TranspilerError
|
||||
from qiskit.transpiler.instruction_durations import InstructionDurations
|
||||
from qiskit.transpiler.passes import ASAPSchedule, ALAPSchedule, DynamicalDecoupling
|
||||
from qiskit.transpiler.passmanager import PassManager
|
||||
from qiskit.transpiler.target import Target, InstructionProperties
|
||||
|
||||
|
||||
@ddt
|
||||
class TestSchedulingPass(QiskitTestCase):
|
||||
"""Tests the Scheduling passes"""
|
||||
|
||||
def test_alap_agree_with_reverse_asap_reverse(self):
|
||||
"""Test if ALAP schedule agrees with doubly-reversed ASAP schedule."""
|
||||
qc = QuantumCircuit(2)
|
||||
qc.h(0)
|
||||
qc.delay(500, 1)
|
||||
qc.cx(0, 1)
|
||||
qc.measure_all()
|
||||
|
||||
durations = InstructionDurations(
|
||||
[("h", 0, 200), ("cx", [0, 1], 700), ("measure", None, 1000)]
|
||||
)
|
||||
|
||||
pm = PassManager(ALAPSchedule(durations))
|
||||
alap_qc = pm.run(qc)
|
||||
|
||||
pm = PassManager(ASAPSchedule(durations))
|
||||
new_qc = pm.run(qc.reverse_ops())
|
||||
new_qc = new_qc.reverse_ops()
|
||||
new_qc.name = new_qc.name
|
||||
|
||||
self.assertEqual(alap_qc, new_qc)
|
||||
|
||||
@data(ALAPSchedule, ASAPSchedule)
|
||||
def test_classically_controlled_gate_after_measure(self, schedule_pass):
|
||||
"""Test if ALAP/ASAP schedules circuits with c_if after measure with a common clbit.
|
||||
See: https://github.com/Qiskit/qiskit-terra/issues/7654
|
||||
|
||||
(input)
|
||||
┌─┐
|
||||
q_0: ┤M├───────────
|
||||
└╥┘ ┌───┐
|
||||
q_1: ─╫────┤ X ├───
|
||||
║ └─╥─┘
|
||||
║ ┌────╨────┐
|
||||
c: 1/═╩═╡ c_0 = T ╞
|
||||
0 └─────────┘
|
||||
|
||||
(scheduled)
|
||||
┌─┐┌────────────────┐
|
||||
q_0: ───────────────────┤M├┤ Delay(200[dt]) ├
|
||||
┌─────────────────┐└╥┘└─────┬───┬──────┘
|
||||
q_1: ┤ Delay(1000[dt]) ├─╫───────┤ X ├───────
|
||||
└─────────────────┘ ║ └─╥─┘
|
||||
║ ┌────╨────┐
|
||||
c: 1/════════════════════╩════╡ c_0=0x1 ╞════
|
||||
0 └─────────┘
|
||||
"""
|
||||
qc = QuantumCircuit(2, 1)
|
||||
qc.measure(0, 0)
|
||||
qc.x(1).c_if(0, True)
|
||||
|
||||
durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)])
|
||||
pm = PassManager(schedule_pass(durations))
|
||||
scheduled = pm.run(qc)
|
||||
|
||||
expected = QuantumCircuit(2, 1)
|
||||
expected.measure(0, 0)
|
||||
expected.delay(1000, 1) # x.c_if starts after measure
|
||||
expected.x(1).c_if(0, True)
|
||||
expected.delay(200, 0)
|
||||
|
||||
self.assertEqual(expected, scheduled)
|
||||
|
||||
@data(ALAPSchedule, ASAPSchedule)
|
||||
def test_measure_after_measure(self, schedule_pass):
|
||||
"""Test if ALAP/ASAP schedules circuits with measure after measure with a common clbit.
|
||||
See: https://github.com/Qiskit/qiskit-terra/issues/7654
|
||||
|
||||
(input)
|
||||
┌───┐┌─┐
|
||||
q_0: ┤ X ├┤M├───
|
||||
└───┘└╥┘┌─┐
|
||||
q_1: ──────╫─┤M├
|
||||
║ └╥┘
|
||||
c: 1/══════╩══╩═
|
||||
0 0
|
||||
|
||||
(scheduled)
|
||||
┌───┐ ┌─┐┌─────────────────┐
|
||||
q_0: ───────┤ X ├───────┤M├┤ Delay(1000[dt]) ├
|
||||
┌──────┴───┴──────┐└╥┘└───────┬─┬───────┘
|
||||
q_1: ┤ Delay(1200[dt]) ├─╫─────────┤M├────────
|
||||
└─────────────────┘ ║ └╥┘
|
||||
c: 1/════════════════════╩══════════╩═════════
|
||||
0 0
|
||||
"""
|
||||
qc = QuantumCircuit(2, 1)
|
||||
qc.x(0)
|
||||
qc.measure(0, 0)
|
||||
qc.measure(1, 0)
|
||||
|
||||
durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)])
|
||||
pm = PassManager(schedule_pass(durations))
|
||||
scheduled = pm.run(qc)
|
||||
|
||||
expected = QuantumCircuit(2, 1)
|
||||
expected.x(0)
|
||||
expected.measure(0, 0)
|
||||
expected.delay(1200, 1)
|
||||
expected.measure(1, 0)
|
||||
expected.delay(1000, 0)
|
||||
|
||||
self.assertEqual(expected, scheduled)
|
||||
|
||||
@data(ALAPSchedule, ASAPSchedule)
|
||||
def test_c_if_on_different_qubits(self, schedule_pass):
|
||||
"""Test if ALAP/ASAP schedules circuits with `c_if`s on different qubits.
|
||||
|
||||
(input)
|
||||
┌─┐
|
||||
q_0: ┤M├──────────────────────
|
||||
└╥┘ ┌───┐
|
||||
q_1: ─╫────┤ X ├──────────────
|
||||
║ └─╥─┘ ┌───┐
|
||||
q_2: ─╫──────╫────────┤ X ├───
|
||||
║ ║ └─╥─┘
|
||||
║ ┌────╨────┐┌────╨────┐
|
||||
c: 1/═╩═╡ c_0 = T ╞╡ c_0 = T ╞
|
||||
0 └─────────┘└─────────┘
|
||||
|
||||
(scheduled)
|
||||
|
||||
┌─┐┌────────────────┐
|
||||
q_0: ───────────────────┤M├┤ Delay(200[dt]) ├───────────
|
||||
┌─────────────────┐└╥┘└─────┬───┬──────┘
|
||||
q_1: ┤ Delay(1000[dt]) ├─╫───────┤ X ├──────────────────
|
||||
├─────────────────┤ ║ └─╥─┘ ┌───┐
|
||||
q_2: ┤ Delay(1000[dt]) ├─╫─────────╫────────────┤ X ├───
|
||||
└─────────────────┘ ║ ║ └─╥─┘
|
||||
║ ┌────╨────┐ ┌────╨────┐
|
||||
c: 1/════════════════════╩════╡ c_0=0x1 ╞════╡ c_0=0x1 ╞
|
||||
0 └─────────┘ └─────────┘
|
||||
"""
|
||||
qc = QuantumCircuit(3, 1)
|
||||
qc.measure(0, 0)
|
||||
qc.x(1).c_if(0, True)
|
||||
qc.x(2).c_if(0, True)
|
||||
|
||||
durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)])
|
||||
pm = PassManager(schedule_pass(durations))
|
||||
scheduled = pm.run(qc)
|
||||
|
||||
expected = QuantumCircuit(3, 1)
|
||||
expected.measure(0, 0)
|
||||
expected.delay(1000, 1)
|
||||
expected.delay(1000, 2)
|
||||
expected.x(1).c_if(0, True)
|
||||
expected.x(2).c_if(0, True)
|
||||
expected.delay(200, 0)
|
||||
|
||||
self.assertEqual(expected, scheduled)
|
||||
|
||||
@data(ALAPSchedule, ASAPSchedule)
|
||||
def test_shorter_measure_after_measure(self, schedule_pass):
|
||||
"""Test if ALAP/ASAP schedules circuits with shorter measure after measure with a common clbit.
|
||||
|
||||
(input)
|
||||
┌─┐
|
||||
q_0: ┤M├───
|
||||
└╥┘┌─┐
|
||||
q_1: ─╫─┤M├
|
||||
║ └╥┘
|
||||
c: 1/═╩══╩═
|
||||
0 0
|
||||
|
||||
(scheduled)
|
||||
┌─┐┌────────────────┐
|
||||
q_0: ───────────────────┤M├┤ Delay(700[dt]) ├
|
||||
┌─────────────────┐└╥┘└──────┬─┬───────┘
|
||||
q_1: ┤ Delay(1000[dt]) ├─╫────────┤M├────────
|
||||
└─────────────────┘ ║ └╥┘
|
||||
c: 1/════════════════════╩═════════╩═════════
|
||||
0 0
|
||||
"""
|
||||
qc = QuantumCircuit(2, 1)
|
||||
qc.measure(0, 0)
|
||||
qc.measure(1, 0)
|
||||
|
||||
durations = InstructionDurations([("measure", [0], 1000), ("measure", [1], 700)])
|
||||
pm = PassManager(schedule_pass(durations))
|
||||
scheduled = pm.run(qc)
|
||||
|
||||
expected = QuantumCircuit(2, 1)
|
||||
expected.measure(0, 0)
|
||||
expected.delay(1000, 1)
|
||||
expected.measure(1, 0)
|
||||
expected.delay(700, 0)
|
||||
|
||||
self.assertEqual(expected, scheduled)
|
||||
|
||||
@data(ALAPSchedule, ASAPSchedule)
|
||||
def test_measure_after_c_if(self, schedule_pass):
|
||||
"""Test if ALAP/ASAP schedules circuits with c_if after measure with a common clbit.
|
||||
|
||||
(input)
|
||||
┌─┐
|
||||
q_0: ┤M├──────────────
|
||||
└╥┘ ┌───┐
|
||||
q_1: ─╫────┤ X ├──────
|
||||
║ └─╥─┘ ┌─┐
|
||||
q_2: ─╫──────╫─────┤M├
|
||||
║ ┌────╨────┐└╥┘
|
||||
c: 1/═╩═╡ c_0 = T ╞═╩═
|
||||
0 └─────────┘ 0
|
||||
|
||||
(scheduled)
|
||||
┌─┐┌─────────────────┐
|
||||
q_0: ───────────────────┤M├┤ Delay(1000[dt]) ├──────────────────
|
||||
┌─────────────────┐└╥┘└──────┬───┬──────┘┌────────────────┐
|
||||
q_1: ┤ Delay(1000[dt]) ├─╫────────┤ X ├───────┤ Delay(800[dt]) ├
|
||||
├─────────────────┤ ║ └─╥─┘ └──────┬─┬───────┘
|
||||
q_2: ┤ Delay(1000[dt]) ├─╫──────────╫────────────────┤M├────────
|
||||
└─────────────────┘ ║ ┌────╨────┐ └╥┘
|
||||
c: 1/════════════════════╩═════╡ c_0=0x1 ╞════════════╩═════════
|
||||
0 └─────────┘ 0
|
||||
"""
|
||||
qc = QuantumCircuit(3, 1)
|
||||
qc.measure(0, 0)
|
||||
qc.x(1).c_if(0, 1)
|
||||
qc.measure(2, 0)
|
||||
|
||||
durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)])
|
||||
pm = PassManager(schedule_pass(durations))
|
||||
scheduled = pm.run(qc)
|
||||
|
||||
expected = QuantumCircuit(3, 1)
|
||||
expected.delay(1000, 1)
|
||||
expected.delay(1000, 2)
|
||||
expected.measure(0, 0)
|
||||
expected.x(1).c_if(0, 1)
|
||||
expected.measure(2, 0)
|
||||
expected.delay(1000, 0)
|
||||
expected.delay(800, 1)
|
||||
|
||||
self.assertEqual(expected, scheduled)
|
||||
|
||||
def test_parallel_gate_different_length(self):
|
||||
"""Test circuit having two parallel instruction with different length.
|
||||
|
||||
(input)
|
||||
┌───┐┌─┐
|
||||
q_0: ┤ X ├┤M├───
|
||||
├───┤└╥┘┌─┐
|
||||
q_1: ┤ X ├─╫─┤M├
|
||||
└───┘ ║ └╥┘
|
||||
c: 2/══════╩══╩═
|
||||
0 1
|
||||
|
||||
(expected, ALAP)
|
||||
┌────────────────┐┌───┐┌─┐
|
||||
q_0: ┤ Delay(200[dt]) ├┤ X ├┤M├
|
||||
└─────┬───┬──────┘└┬─┬┘└╥┘
|
||||
q_1: ──────┤ X ├────────┤M├──╫─
|
||||
└───┘ └╥┘ ║
|
||||
c: 2/════════════════════╩═══╩═
|
||||
1 0
|
||||
|
||||
(expected, ASAP)
|
||||
┌───┐┌─┐┌────────────────┐
|
||||
q_0: ┤ X ├┤M├┤ Delay(200[dt]) ├
|
||||
├───┤└╥┘└──────┬─┬───────┘
|
||||
q_1: ┤ X ├─╫────────┤M├────────
|
||||
└───┘ ║ └╥┘
|
||||
c: 2/══════╩═════════╩═════════
|
||||
0 1
|
||||
|
||||
"""
|
||||
qc = QuantumCircuit(2, 2)
|
||||
qc.x(0)
|
||||
qc.x(1)
|
||||
qc.measure(0, 0)
|
||||
qc.measure(1, 1)
|
||||
|
||||
durations = InstructionDurations(
|
||||
[("x", [0], 200), ("x", [1], 400), ("measure", None, 1000)]
|
||||
)
|
||||
pm = PassManager(ALAPSchedule(durations))
|
||||
qc_alap = pm.run(qc)
|
||||
|
||||
alap_expected = QuantumCircuit(2, 2)
|
||||
alap_expected.delay(200, 0)
|
||||
alap_expected.x(0)
|
||||
alap_expected.x(1)
|
||||
alap_expected.measure(0, 0)
|
||||
alap_expected.measure(1, 1)
|
||||
|
||||
self.assertEqual(qc_alap, alap_expected)
|
||||
|
||||
pm = PassManager(ASAPSchedule(durations))
|
||||
qc_asap = pm.run(qc)
|
||||
|
||||
asap_expected = QuantumCircuit(2, 2)
|
||||
asap_expected.x(0)
|
||||
asap_expected.x(1)
|
||||
asap_expected.measure(0, 0) # immediately start after X gate
|
||||
asap_expected.measure(1, 1)
|
||||
asap_expected.delay(200, 0)
|
||||
|
||||
self.assertEqual(qc_asap, asap_expected)
|
||||
|
||||
def test_parallel_gate_different_length_with_barrier(self):
|
||||
"""Test circuit having two parallel instruction with different length with barrier.
|
||||
|
||||
(input)
|
||||
┌───┐┌─┐
|
||||
q_0: ┤ X ├┤M├───
|
||||
├───┤└╥┘┌─┐
|
||||
q_1: ┤ X ├─╫─┤M├
|
||||
└───┘ ║ └╥┘
|
||||
c: 2/══════╩══╩═
|
||||
0 1
|
||||
|
||||
(expected, ALAP)
|
||||
┌────────────────┐┌───┐ ░ ┌─┐
|
||||
q_0: ┤ Delay(200[dt]) ├┤ X ├─░─┤M├───
|
||||
└─────┬───┬──────┘└───┘ ░ └╥┘┌─┐
|
||||
q_1: ──────┤ X ├─────────────░──╫─┤M├
|
||||
└───┘ ░ ║ └╥┘
|
||||
c: 2/═══════════════════════════╩══╩═
|
||||
0 1
|
||||
|
||||
(expected, ASAP)
|
||||
┌───┐┌────────────────┐ ░ ┌─┐
|
||||
q_0: ┤ X ├┤ Delay(200[dt]) ├─░─┤M├───
|
||||
├───┤└────────────────┘ ░ └╥┘┌─┐
|
||||
q_1: ┤ X ├───────────────────░──╫─┤M├
|
||||
└───┘ ░ ║ └╥┘
|
||||
c: 2/═══════════════════════════╩══╩═
|
||||
0 1
|
||||
"""
|
||||
qc = QuantumCircuit(2, 2)
|
||||
qc.x(0)
|
||||
qc.x(1)
|
||||
qc.barrier()
|
||||
qc.measure(0, 0)
|
||||
qc.measure(1, 1)
|
||||
|
||||
durations = InstructionDurations(
|
||||
[("x", [0], 200), ("x", [1], 400), ("measure", None, 1000)]
|
||||
)
|
||||
pm = PassManager(ALAPSchedule(durations))
|
||||
qc_alap = pm.run(qc)
|
||||
|
||||
alap_expected = QuantumCircuit(2, 2)
|
||||
alap_expected.delay(200, 0)
|
||||
alap_expected.x(0)
|
||||
alap_expected.x(1)
|
||||
alap_expected.barrier()
|
||||
alap_expected.measure(0, 0)
|
||||
alap_expected.measure(1, 1)
|
||||
|
||||
self.assertEqual(qc_alap, alap_expected)
|
||||
|
||||
pm = PassManager(ASAPSchedule(durations))
|
||||
qc_asap = pm.run(qc)
|
||||
|
||||
asap_expected = QuantumCircuit(2, 2)
|
||||
asap_expected.x(0)
|
||||
asap_expected.delay(200, 0)
|
||||
asap_expected.x(1)
|
||||
asap_expected.barrier()
|
||||
asap_expected.measure(0, 0)
|
||||
asap_expected.measure(1, 1)
|
||||
|
||||
self.assertEqual(qc_asap, asap_expected)
|
||||
|
||||
def test_measure_after_c_if_on_edge_locking(self):
|
||||
"""Test if ALAP/ASAP schedules circuits with c_if after measure with a common clbit.
|
||||
|
||||
The scheduler is configured to reproduce behavior of the 0.20.0,
|
||||
in which clbit lock is applied to the end-edge of measure instruction.
|
||||
See https://github.com/Qiskit/qiskit-terra/pull/7655
|
||||
|
||||
(input)
|
||||
┌─┐
|
||||
q_0: ┤M├──────────────
|
||||
└╥┘ ┌───┐
|
||||
q_1: ─╫────┤ X ├──────
|
||||
║ └─╥─┘ ┌─┐
|
||||
q_2: ─╫──────╫─────┤M├
|
||||
║ ┌────╨────┐└╥┘
|
||||
c: 1/═╩═╡ c_0 = T ╞═╩═
|
||||
0 └─────────┘ 0
|
||||
|
||||
(ASAP scheduled)
|
||||
┌─┐┌────────────────┐
|
||||
q_0: ───────────────────┤M├┤ Delay(200[dt]) ├─────────────────────
|
||||
┌─────────────────┐└╥┘└─────┬───┬──────┘
|
||||
q_1: ┤ Delay(1000[dt]) ├─╫───────┤ X ├────────────────────────────
|
||||
└─────────────────┘ ║ └─╥─┘ ┌─┐┌────────────────┐
|
||||
q_2: ────────────────────╫─────────╫─────────┤M├┤ Delay(200[dt]) ├
|
||||
║ ┌────╨────┐ └╥┘└────────────────┘
|
||||
c: 1/════════════════════╩════╡ c_0=0x1 ╞═════╩═══════════════════
|
||||
0 └─────────┘ 0
|
||||
|
||||
(ALAP scheduled)
|
||||
┌─┐┌────────────────┐
|
||||
q_0: ───────────────────┤M├┤ Delay(200[dt]) ├───
|
||||
┌─────────────────┐└╥┘└─────┬───┬──────┘
|
||||
q_1: ┤ Delay(1000[dt]) ├─╫───────┤ X ├──────────
|
||||
└┬────────────────┤ ║ └─╥─┘ ┌─┐
|
||||
q_2: ─┤ Delay(200[dt]) ├─╫─────────╫─────────┤M├
|
||||
└────────────────┘ ║ ┌────╨────┐ └╥┘
|
||||
c: 1/════════════════════╩════╡ c_0=0x1 ╞═════╩═
|
||||
0 └─────────┘ 0
|
||||
|
||||
"""
|
||||
qc = QuantumCircuit(3, 1)
|
||||
qc.measure(0, 0)
|
||||
qc.x(1).c_if(0, 1)
|
||||
qc.measure(2, 0)
|
||||
|
||||
durations = InstructionDurations([("x", None, 200), ("measure", None, 1000)])
|
||||
|
||||
# lock at the end edge
|
||||
actual_asap = PassManager(ASAPSchedule(durations, clbit_write_latency=1000)).run(qc)
|
||||
actual_alap = PassManager(ALAPSchedule(durations, clbit_write_latency=1000)).run(qc)
|
||||
|
||||
# start times of 2nd measure depends on ASAP/ALAP
|
||||
expected_asap = QuantumCircuit(3, 1)
|
||||
expected_asap.measure(0, 0)
|
||||
expected_asap.delay(1000, 1)
|
||||
expected_asap.x(1).c_if(0, 1)
|
||||
expected_asap.measure(2, 0)
|
||||
expected_asap.delay(200, 0)
|
||||
expected_asap.delay(200, 2)
|
||||
self.assertEqual(expected_asap, actual_asap)
|
||||
|
||||
expected_alap = QuantumCircuit(3, 1)
|
||||
expected_alap.measure(0, 0)
|
||||
expected_alap.delay(1000, 1)
|
||||
expected_alap.x(1).c_if(0, 1)
|
||||
expected_alap.delay(200, 2)
|
||||
expected_alap.measure(2, 0)
|
||||
expected_alap.delay(200, 0)
|
||||
self.assertEqual(expected_alap, actual_alap)
|
||||
|
||||
@data([100, 200], [500, 0], [1000, 200])
|
||||
@unpack
|
||||
def test_active_reset_circuit(self, write_lat, cond_lat):
|
||||
"""Test practical example of reset circuit.
|
||||
|
||||
Because of the stimulus pulse overlap with the previous XGate on the q register,
|
||||
measure instruction is always triggered after XGate regardless of write latency.
|
||||
Thus only conditional latency matters in the scheduling.
|
||||
|
||||
(input)
|
||||
┌─┐ ┌───┐ ┌─┐ ┌───┐ ┌─┐ ┌───┐
|
||||
q: ┤M├───┤ X ├───┤M├───┤ X ├───┤M├───┤ X ├───
|
||||
└╥┘ └─╥─┘ └╥┘ └─╥─┘ └╥┘ └─╥─┘
|
||||
║ ┌────╨────┐ ║ ┌────╨────┐ ║ ┌────╨────┐
|
||||
c: 1/═╩═╡ c_0=0x1 ╞═╩═╡ c_0=0x1 ╞═╩═╡ c_0=0x1 ╞
|
||||
0 └─────────┘ 0 └─────────┘ 0 └─────────┘
|
||||
|
||||
"""
|
||||
qc = QuantumCircuit(1, 1)
|
||||
qc.measure(0, 0)
|
||||
qc.x(0).c_if(0, 1)
|
||||
qc.measure(0, 0)
|
||||
qc.x(0).c_if(0, 1)
|
||||
qc.measure(0, 0)
|
||||
qc.x(0).c_if(0, 1)
|
||||
|
||||
durations = InstructionDurations([("x", None, 100), ("measure", None, 1000)])
|
||||
actual_asap = PassManager(
|
||||
ASAPSchedule(durations, clbit_write_latency=write_lat, conditional_latency=cond_lat)
|
||||
).run(qc)
|
||||
actual_alap = PassManager(
|
||||
ALAPSchedule(durations, clbit_write_latency=write_lat, conditional_latency=cond_lat)
|
||||
).run(qc)
|
||||
|
||||
expected = QuantumCircuit(1, 1)
|
||||
expected.measure(0, 0)
|
||||
if cond_lat > 0:
|
||||
expected.delay(cond_lat, 0)
|
||||
expected.x(0).c_if(0, 1)
|
||||
expected.measure(0, 0)
|
||||
if cond_lat > 0:
|
||||
expected.delay(cond_lat, 0)
|
||||
expected.x(0).c_if(0, 1)
|
||||
expected.measure(0, 0)
|
||||
if cond_lat > 0:
|
||||
expected.delay(cond_lat, 0)
|
||||
expected.x(0).c_if(0, 1)
|
||||
|
||||
self.assertEqual(expected, actual_asap)
|
||||
self.assertEqual(expected, actual_alap)
|
||||
|
||||
def test_random_complicated_circuit(self):
|
||||
"""Test scheduling complicated circuit with control flow.
|
||||
|
||||
(input)
|
||||
┌────────────────┐ ┌───┐ ░ ┌───┐ »
|
||||
q_0: ┤ Delay(100[dt]) ├───┤ X ├────░──────────────────┤ X ├───»
|
||||
└────────────────┘ └─╥─┘ ░ ┌───┐ └─╥─┘ »
|
||||
q_1: ───────────────────────╫──────░───────┤ X ├────────╫─────»
|
||||
║ ░ ┌─┐ └─╥─┘ ║ »
|
||||
q_2: ───────────────────────╫──────░─┤M├─────╫──────────╫─────»
|
||||
┌────╨────┐ ░ └╥┘┌────╨────┐┌────╨────┐»
|
||||
c: 1/══════════════════╡ c_0=0x1 ╞════╩═╡ c_0=0x0 ╞╡ c_0=0x0 ╞»
|
||||
└─────────┘ 0 └─────────┘└─────────┘»
|
||||
« ┌────────────────┐┌───┐
|
||||
«q_0: ┤ Delay(300[dt]) ├┤ X ├─────■─────
|
||||
« └────────────────┘└───┘ ┌─┴─┐
|
||||
«q_1: ────────■─────────────────┤ X ├───
|
||||
« ┌─┴─┐ ┌─┐ └─╥─┘
|
||||
«q_2: ──────┤ X ├────────┤M├──────╫─────
|
||||
« └───┘ └╥┘ ┌────╨────┐
|
||||
«c: 1/════════════════════╩══╡ c_0=0x0 ╞
|
||||
« 0 └─────────┘
|
||||
|
||||
(ASAP scheduled) duration = 2800 dt
|
||||
┌────────────────┐┌────────────────┐ ┌───┐ ░ ┌─────────────────┐»
|
||||
q_0: ┤ Delay(100[dt]) ├┤ Delay(100[dt]) ├───┤ X ├────░─┤ Delay(1400[dt]) ├»
|
||||
├────────────────┤└────────────────┘ └─╥─┘ ░ ├─────────────────┤»
|
||||
q_1: ┤ Delay(300[dt]) ├───────────────────────╫──────░─┤ Delay(1200[dt]) ├»
|
||||
├────────────────┤ ║ ░ └───────┬─┬───────┘»
|
||||
q_2: ┤ Delay(300[dt]) ├───────────────────────╫──────░─────────┤M├────────»
|
||||
└────────────────┘ ┌────╨────┐ ░ └╥┘ »
|
||||
c: 1/════════════════════════════════════╡ c_0=0x1 ╞════════════╩═════════»
|
||||
└─────────┘ 0 »
|
||||
« ┌───┐ ┌────────────────┐»
|
||||
«q_0: ────────────────────────────────┤ X ├───┤ Delay(300[dt]) ├»
|
||||
« ┌───┐ └─╥─┘ └────────────────┘»
|
||||
«q_1: ───┤ X ├──────────────────────────╫─────────────■─────────»
|
||||
« └─╥─┘ ┌────────────────┐ ║ ┌─┴─┐ »
|
||||
«q_2: ─────╫─────┤ Delay(300[dt]) ├─────╫───────────┤ X ├───────»
|
||||
« ┌────╨────┐└────────────────┘┌────╨────┐ └───┘ »
|
||||
«c: 1/╡ c_0=0x0 ╞══════════════════╡ c_0=0x0 ╞══════════════════»
|
||||
« └─────────┘ └─────────┘ »
|
||||
« ┌───┐ ┌────────────────┐
|
||||
«q_0: ──────┤ X ├────────────■─────┤ Delay(700[dt]) ├
|
||||
« ┌─────┴───┴──────┐ ┌─┴─┐ ├────────────────┤
|
||||
«q_1: ┤ Delay(400[dt]) ├───┤ X ├───┤ Delay(700[dt]) ├
|
||||
« ├────────────────┤ └─╥─┘ └──────┬─┬───────┘
|
||||
«q_2: ┤ Delay(300[dt]) ├─────╫────────────┤M├────────
|
||||
« └────────────────┘┌────╨────┐ └╥┘
|
||||
«c: 1/══════════════════╡ c_0=0x0 ╞════════╩═════════
|
||||
« └─────────┘ 0
|
||||
|
||||
(ALAP scheduled) duration = 3100
|
||||
┌────────────────┐┌────────────────┐ ┌───┐ ░ ┌─────────────────┐»
|
||||
q_0: ┤ Delay(100[dt]) ├┤ Delay(100[dt]) ├───┤ X ├────░─┤ Delay(1400[dt]) ├»
|
||||
├────────────────┤└────────────────┘ └─╥─┘ ░ ├─────────────────┤»
|
||||
q_1: ┤ Delay(300[dt]) ├───────────────────────╫──────░─┤ Delay(1200[dt]) ├»
|
||||
├────────────────┤ ║ ░ └───────┬─┬───────┘»
|
||||
q_2: ┤ Delay(300[dt]) ├───────────────────────╫──────░─────────┤M├────────»
|
||||
└────────────────┘ ┌────╨────┐ ░ └╥┘ »
|
||||
c: 1/════════════════════════════════════╡ c_0=0x1 ╞════════════╩═════════»
|
||||
└─────────┘ 0 »
|
||||
« ┌───┐ ┌────────────────┐»
|
||||
«q_0: ────────────────────────────────┤ X ├───┤ Delay(300[dt]) ├»
|
||||
« ┌───┐ ┌────────────────┐ └─╥─┘ └────────────────┘»
|
||||
«q_1: ───┤ X ├───┤ Delay(300[dt]) ├─────╫─────────────■─────────»
|
||||
« └─╥─┘ ├────────────────┤ ║ ┌─┴─┐ »
|
||||
«q_2: ─────╫─────┤ Delay(600[dt]) ├─────╫───────────┤ X ├───────»
|
||||
« ┌────╨────┐└────────────────┘┌────╨────┐ └───┘ »
|
||||
«c: 1/╡ c_0=0x0 ╞══════════════════╡ c_0=0x0 ╞══════════════════»
|
||||
« └─────────┘ └─────────┘ »
|
||||
« ┌───┐ ┌────────────────┐
|
||||
«q_0: ──────┤ X ├────────────■─────┤ Delay(700[dt]) ├
|
||||
« ┌─────┴───┴──────┐ ┌─┴─┐ ├────────────────┤
|
||||
«q_1: ┤ Delay(100[dt]) ├───┤ X ├───┤ Delay(700[dt]) ├
|
||||
« └──────┬─┬───────┘ └─╥─┘ └────────────────┘
|
||||
«q_2: ───────┤M├─────────────╫───────────────────────
|
||||
« └╥┘ ┌────╨────┐
|
||||
«c: 1/════════╩═════════╡ c_0=0x0 ╞══════════════════
|
||||
« 0 └─────────┘
|
||||
|
||||
"""
|
||||
qc = QuantumCircuit(3, 1)
|
||||
qc.delay(100, 0)
|
||||
qc.x(0).c_if(0, 1)
|
||||
qc.barrier()
|
||||
qc.measure(2, 0)
|
||||
qc.x(1).c_if(0, 0)
|
||||
qc.x(0).c_if(0, 0)
|
||||
qc.delay(300, 0)
|
||||
qc.cx(1, 2)
|
||||
qc.x(0)
|
||||
qc.cx(0, 1).c_if(0, 0)
|
||||
qc.measure(2, 0)
|
||||
|
||||
durations = InstructionDurations(
|
||||
[("x", None, 100), ("measure", None, 1000), ("cx", None, 200)]
|
||||
)
|
||||
|
||||
actual_asap = PassManager(
|
||||
ASAPSchedule(durations, clbit_write_latency=100, conditional_latency=200)
|
||||
).run(qc)
|
||||
actual_alap = PassManager(
|
||||
ALAPSchedule(durations, clbit_write_latency=100, conditional_latency=200)
|
||||
).run(qc)
|
||||
|
||||
expected_asap = QuantumCircuit(3, 1)
|
||||
expected_asap.delay(100, 0)
|
||||
expected_asap.delay(100, 0) # due to conditional latency of 200dt
|
||||
expected_asap.delay(300, 1)
|
||||
expected_asap.delay(300, 2)
|
||||
expected_asap.x(0).c_if(0, 1)
|
||||
expected_asap.barrier()
|
||||
expected_asap.delay(1400, 0)
|
||||
expected_asap.delay(1200, 1)
|
||||
expected_asap.measure(2, 0)
|
||||
expected_asap.x(1).c_if(0, 0)
|
||||
expected_asap.x(0).c_if(0, 0)
|
||||
expected_asap.delay(300, 0)
|
||||
expected_asap.x(0)
|
||||
expected_asap.delay(300, 2)
|
||||
expected_asap.cx(1, 2)
|
||||
expected_asap.delay(400, 1)
|
||||
expected_asap.cx(0, 1).c_if(0, 0)
|
||||
expected_asap.delay(700, 0) # creg is released at t0 of cx(0,1).c_if(0,0)
|
||||
expected_asap.delay(
|
||||
700, 1
|
||||
) # no creg write until 100dt. thus measure can move left by 300dt.
|
||||
expected_asap.delay(300, 2)
|
||||
expected_asap.measure(2, 0)
|
||||
self.assertEqual(expected_asap, actual_asap)
|
||||
self.assertEqual(actual_asap.duration, 3100)
|
||||
|
||||
expected_alap = QuantumCircuit(3, 1)
|
||||
expected_alap.delay(100, 0)
|
||||
expected_alap.delay(100, 0) # due to conditional latency of 200dt
|
||||
expected_alap.delay(300, 1)
|
||||
expected_alap.delay(300, 2)
|
||||
expected_alap.x(0).c_if(0, 1)
|
||||
expected_alap.barrier()
|
||||
expected_alap.delay(1400, 0)
|
||||
expected_alap.delay(1200, 1)
|
||||
expected_alap.measure(2, 0)
|
||||
expected_alap.x(1).c_if(0, 0)
|
||||
expected_alap.x(0).c_if(0, 0)
|
||||
expected_alap.delay(300, 0)
|
||||
expected_alap.x(0)
|
||||
expected_alap.delay(300, 1)
|
||||
expected_alap.delay(600, 2)
|
||||
expected_alap.cx(1, 2)
|
||||
expected_alap.delay(100, 1)
|
||||
expected_alap.cx(0, 1).c_if(0, 0)
|
||||
expected_alap.measure(2, 0)
|
||||
expected_alap.delay(700, 0)
|
||||
expected_alap.delay(700, 1)
|
||||
self.assertEqual(expected_alap, actual_alap)
|
||||
self.assertEqual(actual_alap.duration, 3100)
|
||||
|
||||
def test_dag_introduces_extra_dependency_between_conditionals(self):
|
||||
"""Test dependency between conditional operations in the scheduling.
|
||||
|
||||
In the below example circuit, the conditional x on q1 could start at time 0,
|
||||
however it must be scheduled after the conditional x on q0 in ASAP scheduling.
|
||||
That is because circuit model used in the transpiler passes (DAGCircuit)
|
||||
interprets instructions acting on common clbits must be run in the order
|
||||
given by the original circuit (QuantumCircuit).
|
||||
|
||||
(input)
|
||||
┌────────────────┐ ┌───┐
|
||||
q_0: ┤ Delay(100[dt]) ├───┤ X ├───
|
||||
└─────┬───┬──────┘ └─╥─┘
|
||||
q_1: ──────┤ X ├────────────╫─────
|
||||
└─╥─┘ ║
|
||||
┌────╨────┐ ┌────╨────┐
|
||||
c: 1/═══╡ c_0=0x1 ╞════╡ c_0=0x1 ╞
|
||||
└─────────┘ └─────────┘
|
||||
|
||||
(ASAP scheduled)
|
||||
┌────────────────┐ ┌───┐
|
||||
q_0: ┤ Delay(100[dt]) ├───┤ X ├──────────────
|
||||
├────────────────┤ └─╥─┘ ┌───┐
|
||||
q_1: ┤ Delay(100[dt]) ├─────╫────────┤ X ├───
|
||||
└────────────────┘ ║ └─╥─┘
|
||||
┌────╨────┐┌────╨────┐
|
||||
c: 1/══════════════════╡ c_0=0x1 ╞╡ c_0=0x1 ╞
|
||||
└─────────┘└─────────┘
|
||||
"""
|
||||
qc = QuantumCircuit(2, 1)
|
||||
qc.delay(100, 0)
|
||||
qc.x(0).c_if(0, True)
|
||||
qc.x(1).c_if(0, True)
|
||||
|
||||
durations = InstructionDurations([("x", None, 160)])
|
||||
pm = PassManager(ASAPSchedule(durations))
|
||||
scheduled = pm.run(qc)
|
||||
|
||||
expected = QuantumCircuit(2, 1)
|
||||
expected.delay(100, 0)
|
||||
expected.delay(100, 1) # due to extra dependency on clbits
|
||||
expected.x(0).c_if(0, True)
|
||||
expected.x(1).c_if(0, True)
|
||||
|
||||
self.assertEqual(expected, scheduled)
|
||||
|
||||
@data(ALAPSchedule, ASAPSchedule)
|
||||
def test_respect_target_instruction_constraints(self, schedule_pass):
|
||||
"""Test if ALAP/ASAP does not pad delays for qubits that do not support delay instructions.
|
||||
See: https://github.com/Qiskit/qiskit-terra/issues/9993
|
||||
"""
|
||||
target = Target(dt=1)
|
||||
target.add_instruction(XGate(), {(1,): InstructionProperties(duration=200)})
|
||||
# delays are not supported
|
||||
|
||||
qc = QuantumCircuit(2)
|
||||
qc.x(1)
|
||||
|
||||
pm = PassManager(schedule_pass(target=target))
|
||||
scheduled = pm.run(qc)
|
||||
|
||||
expected = QuantumCircuit(2)
|
||||
expected.x(1)
|
||||
# no delay on qubit 0
|
||||
|
||||
self.assertEqual(expected, scheduled)
|
||||
|
||||
def test_dd_respect_target_instruction_constraints(self):
|
||||
"""Test if DD pass does not pad delays for qubits that do not support delay instructions
|
||||
and does not insert DD gates for qubits that do not support necessary gates.
|
||||
See: https://github.com/Qiskit/qiskit-terra/issues/9993
|
||||
"""
|
||||
qc = QuantumCircuit(3)
|
||||
qc.cx(0, 1)
|
||||
qc.cx(1, 2)
|
||||
|
||||
target = Target(dt=1)
|
||||
# Y is partially supported (not supported on qubit 2)
|
||||
target.add_instruction(
|
||||
XGate(), {(q,): InstructionProperties(duration=100) for q in range(2)}
|
||||
)
|
||||
target.add_instruction(
|
||||
CXGate(),
|
||||
{
|
||||
(0, 1): InstructionProperties(duration=1000),
|
||||
(1, 2): InstructionProperties(duration=1000),
|
||||
},
|
||||
)
|
||||
# delays are not supported
|
||||
|
||||
# No DD instructions nor delays are padded due to no delay support in the target
|
||||
pm_xx = PassManager(
|
||||
[
|
||||
ALAPSchedule(target=target),
|
||||
DynamicalDecoupling(durations=None, dd_sequence=[XGate(), XGate()], target=target),
|
||||
]
|
||||
)
|
||||
scheduled = pm_xx.run(qc)
|
||||
self.assertEqual(qc, scheduled)
|
||||
|
||||
# Fails since Y is not supported in the target
|
||||
with self.assertRaises(TranspilerError):
|
||||
PassManager(
|
||||
[
|
||||
ALAPSchedule(target=target),
|
||||
DynamicalDecoupling(
|
||||
durations=None,
|
||||
dd_sequence=[XGate(), YGate(), XGate(), YGate()],
|
||||
target=target,
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
# Add delay support to the target
|
||||
target.add_instruction(Delay(Parameter("t")), {(q,): None for q in range(3)})
|
||||
# No error but no DD on qubit 2 (just delay is padded) since X is not supported on it
|
||||
scheduled = pm_xx.run(qc)
|
||||
|
||||
expected = QuantumCircuit(3)
|
||||
expected.delay(1000, [2])
|
||||
expected.cx(0, 1)
|
||||
expected.cx(1, 2)
|
||||
expected.delay(200, [0])
|
||||
expected.x([0])
|
||||
expected.delay(400, [0])
|
||||
expected.x([0])
|
||||
expected.delay(200, [0])
|
||||
self.assertEqual(expected, scheduled)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
|
@ -14,508 +14,9 @@
|
|||
|
||||
from qiskit import QuantumCircuit, pulse
|
||||
from qiskit.test import QiskitTestCase
|
||||
from qiskit.transpiler import InstructionDurations, PassManager
|
||||
from qiskit.transpiler import PassManager
|
||||
from qiskit.transpiler.exceptions import TranspilerError
|
||||
from qiskit.transpiler.passes import (
|
||||
AlignMeasures,
|
||||
InstructionDurationCheck,
|
||||
ConstrainedReschedule,
|
||||
ValidatePulseGates,
|
||||
ALAPScheduleAnalysis,
|
||||
ASAPScheduleAnalysis,
|
||||
ALAPSchedule,
|
||||
PadDelay,
|
||||
SetIOLatency,
|
||||
)
|
||||
|
||||
|
||||
class TestAlignMeasures(QiskitTestCase):
|
||||
"""A test for measurement alignment pass."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.instruction_durations = InstructionDurations(
|
||||
[
|
||||
("rz", (0,), 0),
|
||||
("rz", (1,), 0),
|
||||
("x", (0,), 160),
|
||||
("x", (1,), 160),
|
||||
("sx", (0,), 160),
|
||||
("sx", (1,), 160),
|
||||
("cx", (0, 1), 800),
|
||||
("cx", (1, 0), 800),
|
||||
("measure", None, 1600),
|
||||
]
|
||||
)
|
||||
|
||||
def test_t1_experiment_type(self):
|
||||
"""Test T1 experiment type circuit.
|
||||
|
||||
(input)
|
||||
|
||||
┌───┐┌────────────────┐┌─┐
|
||||
q_0: ┤ X ├┤ Delay(100[dt]) ├┤M├
|
||||
└───┘└────────────────┘└╥┘
|
||||
c: 1/════════════════════════╩═
|
||||
0
|
||||
|
||||
(aligned)
|
||||
|
||||
┌───┐┌────────────────┐┌─┐
|
||||
q_0: ┤ X ├┤ Delay(112[dt]) ├┤M├
|
||||
└───┘└────────────────┘└╥┘
|
||||
c: 1/════════════════════════╩═
|
||||
0
|
||||
|
||||
This type of experiment slightly changes delay duration of interest.
|
||||
However the quantization error should be less than alignment * dt.
|
||||
"""
|
||||
circuit = QuantumCircuit(1, 1)
|
||||
circuit.x(0)
|
||||
circuit.delay(100, 0, unit="dt")
|
||||
circuit.measure(0, 0)
|
||||
|
||||
pm = PassManager(
|
||||
[
|
||||
# reproduce old behavior of 0.20.0 before #7655
|
||||
# currently default write latency is 0
|
||||
SetIOLatency(clbit_write_latency=1600, conditional_latency=0),
|
||||
ALAPScheduleAnalysis(durations=self.instruction_durations),
|
||||
ConstrainedReschedule(acquire_alignment=16),
|
||||
PadDelay(),
|
||||
]
|
||||
)
|
||||
|
||||
aligned_circuit = pm.run(circuit)
|
||||
|
||||
ref_circuit = QuantumCircuit(1, 1)
|
||||
ref_circuit.x(0)
|
||||
ref_circuit.delay(112, 0, unit="dt")
|
||||
ref_circuit.measure(0, 0)
|
||||
|
||||
self.assertEqual(aligned_circuit, ref_circuit)
|
||||
|
||||
def test_hanh_echo_experiment_type(self):
|
||||
"""Test Hahn echo experiment type circuit.
|
||||
|
||||
(input)
|
||||
|
||||
┌────┐┌────────────────┐┌───┐┌────────────────┐┌────┐┌─┐
|
||||
q_0: ┤ √X ├┤ Delay(100[dt]) ├┤ X ├┤ Delay(100[dt]) ├┤ √X ├┤M├
|
||||
└────┘└────────────────┘└───┘└────────────────┘└────┘└╥┘
|
||||
c: 1/══════════════════════════════════════════════════════╩═
|
||||
0
|
||||
|
||||
(output)
|
||||
|
||||
┌────┐┌────────────────┐┌───┐┌────────────────┐┌────┐┌──────────────┐┌─┐
|
||||
q_0: ┤ √X ├┤ Delay(100[dt]) ├┤ X ├┤ Delay(100[dt]) ├┤ √X ├┤ Delay(8[dt]) ├┤M├
|
||||
└────┘└────────────────┘└───┘└────────────────┘└────┘└──────────────┘└╥┘
|
||||
c: 1/══════════════════════════════════════════════════════════════════════╩═
|
||||
0
|
||||
|
||||
This type of experiment doesn't change duration of interest (two in the middle).
|
||||
However induces slight delay less than alignment * dt before measurement.
|
||||
This might induce extra amplitude damping error.
|
||||
"""
|
||||
circuit = QuantumCircuit(1, 1)
|
||||
circuit.sx(0)
|
||||
circuit.delay(100, 0, unit="dt")
|
||||
circuit.x(0)
|
||||
circuit.delay(100, 0, unit="dt")
|
||||
circuit.sx(0)
|
||||
circuit.measure(0, 0)
|
||||
|
||||
pm = PassManager(
|
||||
[
|
||||
# reproduce old behavior of 0.20.0 before #7655
|
||||
# currently default write latency is 0
|
||||
SetIOLatency(clbit_write_latency=1600, conditional_latency=0),
|
||||
ALAPScheduleAnalysis(durations=self.instruction_durations),
|
||||
ConstrainedReschedule(acquire_alignment=16),
|
||||
PadDelay(),
|
||||
]
|
||||
)
|
||||
|
||||
aligned_circuit = pm.run(circuit)
|
||||
|
||||
ref_circuit = QuantumCircuit(1, 1)
|
||||
ref_circuit.sx(0)
|
||||
ref_circuit.delay(100, 0, unit="dt")
|
||||
ref_circuit.x(0)
|
||||
ref_circuit.delay(100, 0, unit="dt")
|
||||
ref_circuit.sx(0)
|
||||
ref_circuit.delay(8, 0, unit="dt")
|
||||
ref_circuit.measure(0, 0)
|
||||
|
||||
self.assertEqual(aligned_circuit, ref_circuit)
|
||||
|
||||
def test_mid_circuit_measure(self):
|
||||
"""Test circuit with mid circuit measurement.
|
||||
|
||||
(input)
|
||||
|
||||
┌───┐┌────────────────┐┌─┐┌───────────────┐┌───┐┌────────────────┐┌─┐
|
||||
q_0: ┤ X ├┤ Delay(100[dt]) ├┤M├┤ Delay(10[dt]) ├┤ X ├┤ Delay(120[dt]) ├┤M├
|
||||
└───┘└────────────────┘└╥┘└───────────────┘└───┘└────────────────┘└╥┘
|
||||
c: 2/════════════════════════╩══════════════════════════════════════════╩═
|
||||
0 1
|
||||
|
||||
(output)
|
||||
|
||||
┌───┐┌────────────────┐┌─┐┌───────────────┐┌───┐┌────────────────┐┌─┐
|
||||
q_0: ┤ X ├┤ Delay(112[dt]) ├┤M├┤ Delay(10[dt]) ├┤ X ├┤ Delay(134[dt]) ├┤M├
|
||||
└───┘└────────────────┘└╥┘└───────────────┘└───┘└────────────────┘└╥┘
|
||||
c: 2/════════════════════════╩══════════════════════════════════════════╩═
|
||||
0 1
|
||||
|
||||
Extra delay is always added to the existing delay right before the measurement.
|
||||
Delay after measurement is unchanged.
|
||||
"""
|
||||
circuit = QuantumCircuit(1, 2)
|
||||
circuit.x(0)
|
||||
circuit.delay(100, 0, unit="dt")
|
||||
circuit.measure(0, 0)
|
||||
circuit.delay(10, 0, unit="dt")
|
||||
circuit.x(0)
|
||||
circuit.delay(120, 0, unit="dt")
|
||||
circuit.measure(0, 1)
|
||||
|
||||
pm = PassManager(
|
||||
[
|
||||
# reproduce old behavior of 0.20.0 before #7655
|
||||
# currently default write latency is 0
|
||||
SetIOLatency(clbit_write_latency=1600, conditional_latency=0),
|
||||
ALAPScheduleAnalysis(durations=self.instruction_durations),
|
||||
ConstrainedReschedule(acquire_alignment=16),
|
||||
PadDelay(),
|
||||
]
|
||||
)
|
||||
|
||||
aligned_circuit = pm.run(circuit)
|
||||
|
||||
ref_circuit = QuantumCircuit(1, 2)
|
||||
ref_circuit.x(0)
|
||||
ref_circuit.delay(112, 0, unit="dt")
|
||||
ref_circuit.measure(0, 0)
|
||||
ref_circuit.delay(10, 0, unit="dt")
|
||||
ref_circuit.x(0)
|
||||
ref_circuit.delay(134, 0, unit="dt")
|
||||
ref_circuit.measure(0, 1)
|
||||
|
||||
self.assertEqual(aligned_circuit, ref_circuit)
|
||||
|
||||
def test_mid_circuit_multiq_gates(self):
|
||||
"""Test circuit with mid circuit measurement and multi qubit gates.
|
||||
|
||||
(input)
|
||||
|
||||
┌───┐┌────────────────┐┌─┐ ┌─┐
|
||||
q_0: ┤ X ├┤ Delay(100[dt]) ├┤M├──■───────■──┤M├
|
||||
└───┘└────────────────┘└╥┘┌─┴─┐┌─┐┌─┴─┐└╥┘
|
||||
q_1: ────────────────────────╫─┤ X ├┤M├┤ X ├─╫─
|
||||
║ └───┘└╥┘└───┘ ║
|
||||
c: 2/════════════════════════╩═══════╩═══════╩═
|
||||
0 1 0
|
||||
|
||||
(output)
|
||||
|
||||
┌───┐ ┌────────────────┐┌─┐ ┌─────────────────┐ ┌─┐»
|
||||
q_0: ───────┤ X ├───────┤ Delay(112[dt]) ├┤M├──■──┤ Delay(1600[dt]) ├──■──┤M├»
|
||||
┌──────┴───┴──────┐└────────────────┘└╥┘┌─┴─┐└───────┬─┬───────┘┌─┴─┐└╥┘»
|
||||
q_1: ┤ Delay(1872[dt]) ├───────────────────╫─┤ X ├────────┤M├────────┤ X ├─╫─»
|
||||
└─────────────────┘ ║ └───┘ └╥┘ └───┘ ║ »
|
||||
c: 2/══════════════════════════════════════╩═══════════════╩═══════════════╩═»
|
||||
0 1 0 »
|
||||
«
|
||||
«q_0: ───────────────────
|
||||
« ┌─────────────────┐
|
||||
«q_1: ┤ Delay(1600[dt]) ├
|
||||
« └─────────────────┘
|
||||
«c: 2/═══════════════════
|
||||
«
|
||||
|
||||
Delay for the other channel paired by multi-qubit instruction is also scheduled.
|
||||
Delay (1872dt) = X (160dt) + Delay (100dt + extra 12dt) + Measure (1600dt).
|
||||
"""
|
||||
circuit = QuantumCircuit(2, 2)
|
||||
circuit.x(0)
|
||||
circuit.delay(100, 0, unit="dt")
|
||||
circuit.measure(0, 0)
|
||||
circuit.cx(0, 1)
|
||||
circuit.measure(1, 1)
|
||||
circuit.cx(0, 1)
|
||||
circuit.measure(0, 0)
|
||||
|
||||
pm = PassManager(
|
||||
[
|
||||
# reproduce old behavior of 0.20.0 before #7655
|
||||
# currently default write latency is 0
|
||||
SetIOLatency(clbit_write_latency=1600, conditional_latency=0),
|
||||
ALAPScheduleAnalysis(durations=self.instruction_durations),
|
||||
ConstrainedReschedule(acquire_alignment=16),
|
||||
PadDelay(),
|
||||
]
|
||||
)
|
||||
|
||||
aligned_circuit = pm.run(circuit)
|
||||
|
||||
ref_circuit = QuantumCircuit(2, 2)
|
||||
ref_circuit.x(0)
|
||||
ref_circuit.delay(112, 0, unit="dt")
|
||||
ref_circuit.measure(0, 0)
|
||||
ref_circuit.delay(160 + 112 + 1600, 1, unit="dt")
|
||||
ref_circuit.cx(0, 1)
|
||||
ref_circuit.delay(1600, 0, unit="dt")
|
||||
ref_circuit.measure(1, 1)
|
||||
ref_circuit.cx(0, 1)
|
||||
ref_circuit.delay(1600, 1, unit="dt")
|
||||
ref_circuit.measure(0, 0)
|
||||
|
||||
self.assertEqual(aligned_circuit, ref_circuit)
|
||||
|
||||
def test_alignment_is_not_processed(self):
|
||||
"""Test avoid pass processing if delay is aligned."""
|
||||
circuit = QuantumCircuit(2, 2)
|
||||
circuit.x(0)
|
||||
circuit.delay(160, 0, unit="dt")
|
||||
circuit.measure(0, 0)
|
||||
circuit.cx(0, 1)
|
||||
circuit.measure(1, 1)
|
||||
circuit.cx(0, 1)
|
||||
circuit.measure(0, 0)
|
||||
|
||||
# pre scheduling is not necessary because alignment is skipped
|
||||
# this is to minimize breaking changes to existing code.
|
||||
pm = PassManager()
|
||||
|
||||
pm.append(InstructionDurationCheck(acquire_alignment=16))
|
||||
pm.run(circuit)
|
||||
|
||||
self.assertFalse(pm.property_set["reschedule_required"])
|
||||
|
||||
def test_circuit_using_clbit(self):
|
||||
"""Test a circuit with instructions using a common clbit.
|
||||
|
||||
(input)
|
||||
┌───┐┌────────────────┐┌─┐
|
||||
q_0: ┤ X ├┤ Delay(100[dt]) ├┤M├──────────────
|
||||
└───┘└────────────────┘└╥┘ ┌───┐
|
||||
q_1: ────────────────────────╫────┤ X ├──────
|
||||
║ └─╥─┘ ┌─┐
|
||||
q_2: ────────────────────────╫──────╫─────┤M├
|
||||
║ ┌────╨────┐└╥┘
|
||||
c: 1/════════════════════════╩═╡ c_0 = T ╞═╩═
|
||||
0 └─────────┘ 0
|
||||
|
||||
(aligned)
|
||||
┌───┐ ┌────────────────┐┌─┐┌────────────────┐
|
||||
q_0: ───────┤ X ├───────┤ Delay(112[dt]) ├┤M├┤ Delay(160[dt]) ├───
|
||||
┌──────┴───┴──────┐└────────────────┘└╥┘└─────┬───┬──────┘
|
||||
q_1: ┤ Delay(1872[dt]) ├───────────────────╫───────┤ X ├──────────
|
||||
└┬────────────────┤ ║ └─╥─┘ ┌─┐
|
||||
q_2: ─┤ Delay(432[dt]) ├───────────────────╫─────────╫─────────┤M├
|
||||
└────────────────┘ ║ ┌────╨────┐ └╥┘
|
||||
c: 1/══════════════════════════════════════╩════╡ c_0 = T ╞═════╩═
|
||||
0 └─────────┘ 0
|
||||
|
||||
Looking at the q_0, the total schedule length T becomes
|
||||
160 (x) + 112 (aligned delay) + 1600 (measure) + 160 (delay) = 2032.
|
||||
The last delay comes from ALAP scheduling called before the AlignMeasure pass,
|
||||
which aligns stop times as late as possible, so the start time of x(1).c_if(0)
|
||||
and the stop time of measure(0, 0) become T - 160.
|
||||
"""
|
||||
circuit = QuantumCircuit(3, 1)
|
||||
circuit.x(0)
|
||||
circuit.delay(100, 0, unit="dt")
|
||||
circuit.measure(0, 0)
|
||||
circuit.x(1).c_if(0, 1)
|
||||
circuit.measure(2, 0)
|
||||
|
||||
pm = PassManager(
|
||||
[
|
||||
# reproduce old behavior of 0.20.0 before #7655
|
||||
# currently default write latency is 0
|
||||
SetIOLatency(clbit_write_latency=1600, conditional_latency=0),
|
||||
ALAPScheduleAnalysis(durations=self.instruction_durations),
|
||||
ConstrainedReschedule(acquire_alignment=16),
|
||||
PadDelay(fill_very_end=False),
|
||||
]
|
||||
)
|
||||
|
||||
aligned_circuit = pm.run(circuit)
|
||||
|
||||
self.assertEqual(aligned_circuit.duration, 2032)
|
||||
|
||||
ref_circuit = QuantumCircuit(3, 1)
|
||||
ref_circuit.x(0)
|
||||
ref_circuit.delay(112, 0, unit="dt")
|
||||
ref_circuit.delay(1872, 1, unit="dt") # 2032 - 160
|
||||
ref_circuit.delay(432, 2, unit="dt") # 2032 - 1600
|
||||
ref_circuit.measure(0, 0)
|
||||
ref_circuit.x(1).c_if(0, 1)
|
||||
ref_circuit.measure(2, 0)
|
||||
|
||||
self.assertEqual(aligned_circuit, ref_circuit)
|
||||
|
||||
def test_programmed_delay_preserved(self):
|
||||
"""Intentionally programmed delay will be kept after reschedule.
|
||||
|
||||
No delay
|
||||
++++++++
|
||||
|
||||
(input)
|
||||
┌────────────────┐┌───┐ ░ ┌───┐
|
||||
q_0: ┤ Delay(100[dt]) ├┤ X ├─░─┤ X ├
|
||||
├────────────────┤└───┘ ░ └───┘
|
||||
q_1: ┤ Delay(272[dt]) ├──────░──────
|
||||
└────────────────┘ ░
|
||||
|
||||
(aligned)
|
||||
┌────────────────┐┌───┐ ░ ┌───┐
|
||||
q_0: ┤ Delay(112[dt]) ├┤ X ├─░─┤ X ├
|
||||
├────────────────┤└───┘ ░ └───┘
|
||||
q_1: ┤ Delay(272[dt]) ├──────░──────
|
||||
└────────────────┘ ░
|
||||
|
||||
With delay (intentional post buffer)
|
||||
++++++++++++++++++++++++++++++++++++
|
||||
|
||||
(input) ... this is identical to no delay pattern without reschedule
|
||||
┌────────────────┐┌───┐┌───────────────┐ ░ ┌───┐
|
||||
q_0: ┤ Delay(100[dt]) ├┤ X ├┤ Delay(10[dt]) ├─░─┤ X ├
|
||||
├────────────────┤└───┘└───────────────┘ ░ └───┘
|
||||
q_1: ┤ Delay(272[dt]) ├───────────────────────░──────
|
||||
└────────────────┘ ░
|
||||
|
||||
(aligned)
|
||||
┌────────────────┐┌───┐┌───────────────┐ ░ ┌──────────────┐┌───┐
|
||||
q_0: ┤ Delay(112[dt]) ├┤ X ├┤ Delay(10[dt]) ├─░─┤ Delay(6[dt]) ├┤ X ├
|
||||
├────────────────┤└───┘└───────────────┘ ░ └──────────────┘└───┘
|
||||
q_1: ┤ Delay(282[dt]) ├───────────────────────░──────────────────────
|
||||
└────────────────┘ ░
|
||||
|
||||
"""
|
||||
|
||||
pm = PassManager(
|
||||
[
|
||||
ASAPScheduleAnalysis(durations=self.instruction_durations),
|
||||
ConstrainedReschedule(pulse_alignment=16),
|
||||
PadDelay(fill_very_end=False),
|
||||
]
|
||||
)
|
||||
|
||||
pm_only_schedule = PassManager(
|
||||
[
|
||||
ASAPScheduleAnalysis(durations=self.instruction_durations),
|
||||
PadDelay(fill_very_end=False),
|
||||
]
|
||||
)
|
||||
|
||||
circuit_no_delay = QuantumCircuit(2)
|
||||
circuit_no_delay.delay(100, 0, unit="dt")
|
||||
circuit_no_delay.x(0) # q0 ends here at t = 260, t = 260 - 272 is free
|
||||
circuit_no_delay.delay(160 + 112, 1, unit="dt")
|
||||
circuit_no_delay.barrier() # q0 and q1 is aligned here at t = 272 dt
|
||||
circuit_no_delay.x(0)
|
||||
|
||||
ref_no_delay = QuantumCircuit(2)
|
||||
ref_no_delay.delay(112, 0, unit="dt")
|
||||
ref_no_delay.x(0)
|
||||
ref_no_delay.delay(160 + 100 + 12, 1, unit="dt") # this t0 doesn't change
|
||||
ref_no_delay.barrier()
|
||||
ref_no_delay.x(0) # no buffer
|
||||
|
||||
self.assertEqual(pm.run(circuit_no_delay), ref_no_delay)
|
||||
|
||||
circuit_with_delay = QuantumCircuit(2)
|
||||
circuit_with_delay.delay(100, 0, unit="dt")
|
||||
circuit_with_delay.x(0) # q0 ends here at t = 260
|
||||
circuit_with_delay.delay(10, 0, unit="dt") # intentional post buffer of 10 dt to next X(0)
|
||||
circuit_with_delay.delay(160 + 112, 1, unit="dt") # q0 and q1 is aligned here at t = 272 dt
|
||||
circuit_with_delay.barrier()
|
||||
circuit_with_delay.x(0)
|
||||
|
||||
ref_with_delay = QuantumCircuit(2)
|
||||
ref_with_delay.delay(112, 0, unit="dt")
|
||||
ref_with_delay.x(0)
|
||||
ref_with_delay.delay(10, 0, unit="dt") # this delay survive
|
||||
ref_with_delay.delay(160 + 100 + 12 + 10, 1, unit="dt")
|
||||
ref_with_delay.barrier()
|
||||
ref_with_delay.delay(6, 0, unit="dt") # extra delay for next X0
|
||||
ref_with_delay.x(0) # at least 10dt buffer is preserved
|
||||
|
||||
self.assertEqual(pm.run(circuit_with_delay), ref_with_delay)
|
||||
|
||||
# check if circuit is identical without reschedule
|
||||
self.assertEqual(
|
||||
pm_only_schedule.run(circuit_no_delay),
|
||||
pm_only_schedule.run(circuit_with_delay),
|
||||
)
|
||||
|
||||
def test_both_pulse_and_acquire_alignment(self):
|
||||
"""Test when both acquire and pulse alignment are specified.
|
||||
|
||||
(input)
|
||||
┌────────────────┐┌───┐┌───────────────┐┌─┐
|
||||
q: ┤ Delay(100[dt]) ├┤ X ├┤ Delay(10[dt]) ├┤M├
|
||||
└────────────────┘└───┘└───────────────┘└╥┘
|
||||
c: 1/═════════════════════════════════════════╩═
|
||||
0
|
||||
|
||||
(aligned)
|
||||
┌────────────────┐┌───┐┌───────────────┐┌─┐
|
||||
q: ┤ Delay(112[dt]) ├┤ X ├┤ Delay(16[dt]) ├┤M├
|
||||
└────────────────┘└───┘└───────────────┘└╥┘
|
||||
c: 1/═════════════════════════════════════════╩═
|
||||
0
|
||||
"""
|
||||
pm = PassManager(
|
||||
[
|
||||
ALAPScheduleAnalysis(durations=self.instruction_durations),
|
||||
ConstrainedReschedule(pulse_alignment=16, acquire_alignment=16),
|
||||
PadDelay(fill_very_end=False),
|
||||
]
|
||||
)
|
||||
|
||||
circuit = QuantumCircuit(1, 1)
|
||||
circuit.delay(100, 0, unit="dt")
|
||||
circuit.x(0)
|
||||
circuit.delay(10, 0, unit="dt")
|
||||
circuit.measure(0, 0)
|
||||
|
||||
ref_circ = QuantumCircuit(1, 1)
|
||||
ref_circ.delay(112, 0, unit="dt")
|
||||
ref_circ.x(0)
|
||||
ref_circ.delay(16, 0, unit="dt")
|
||||
ref_circ.measure(0, 0)
|
||||
|
||||
self.assertEqual(pm.run(circuit), ref_circ)
|
||||
|
||||
def test_deprecated_align_measure(self):
|
||||
"""Test if old AlignMeasures can be still used and warning is raised."""
|
||||
circuit = QuantumCircuit(1, 1)
|
||||
circuit.x(0)
|
||||
circuit.delay(100)
|
||||
circuit.measure(0, 0)
|
||||
|
||||
with self.assertWarns(PendingDeprecationWarning):
|
||||
pm_old = PassManager(
|
||||
[
|
||||
ALAPSchedule(durations=self.instruction_durations),
|
||||
AlignMeasures(alignment=16),
|
||||
]
|
||||
)
|
||||
|
||||
pm_new = PassManager(
|
||||
[
|
||||
ALAPSchedule(durations=self.instruction_durations),
|
||||
AlignMeasures(alignment=16),
|
||||
]
|
||||
)
|
||||
|
||||
self.assertEqual(pm_old.run(circuit), pm_new.run(circuit))
|
||||
from qiskit.transpiler.passes import ValidatePulseGates
|
||||
|
||||
|
||||
class TestPulseGateValidation(QiskitTestCase):
|
||||
|
|
Loading…
Reference in New Issue