Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions qiskit/transpiler/basepasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ def __call__(self, circuit, property_set=None):

if self.property_set["layout"]:
result_circuit._layout = self.property_set["layout"]
if self.property_set["clbit_write_latency"] is not None:
result_circuit._clbit_write_latency = self.property_set["clbit_write_latency"]
if self.property_set["conditional_latency"] is not None:
result_circuit._conditional_latency = self.property_set["conditional_latency"]

return result_circuit

Expand Down
8 changes: 7 additions & 1 deletion qiskit/transpiler/passes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,11 @@
ALAPSchedule
ASAPSchedule
DynamicalDecoupling
ConstrainedReschedule
AlignMeasures
ValidatePulseGates
InstructionDurationCheck
SetIOLatency

Circuit Analysis
================
Expand Down Expand Up @@ -227,9 +230,12 @@
from .scheduling import ALAPSchedule
from .scheduling import ASAPSchedule
from .scheduling import DynamicalDecoupling
from .scheduling import AlignMeasures
from .scheduling import AlignMeasures # Deprecated
from .scheduling import ValidatePulseGates
from .scheduling import PadDelay
from .scheduling import ConstrainedReschedule
from .scheduling import InstructionDurationCheck
from .scheduling import SetIOLatency

# additional utility passes
from .utils import CheckMap
Expand Down
11 changes: 8 additions & 3 deletions qiskit/transpiler/passes/scheduling/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@

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

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

# For backward compability
from . import alignments as instruction_alignments

# TODO Deprecated pass. Will be removed after deprecation period.
from .alignments import AlignMeasures
81 changes: 81 additions & 0 deletions qiskit/transpiler/passes/scheduling/alignments/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2022.
#
# 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.

"""Validation and optimization for hardware instruction alignment constraints.

This is a control electronics aware analysis pass group.

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.

A control electronics, i.e. micro-architecture, of the real quantum backend may
impose some constraints on the start time of microinstructions.
In Qiskit SDK, the duration of :class:`qiskit.circuit.Delay` can take arbitrary
value in units of dt, thus circuits involving delays may violate the constraints,
which may result in failure in the circuit execution on the backend.

There are two alignment constraint values reported by your quantum backend.
In addition, if you want to define a custom instruction as a pulse gate, i.e. calibration,
the underlying pulse instruction should satisfy other two waveform constraints.

Pulse alignment constraint

This value is reported by ``timing_constraints["pulse_alignment"]`` in the backend
configuration in units of dt. The start time of the all pulse instruction should be
multiple of this value. Violation of this constraint may result in the
backend execution failure.

In most of the senarios, the scheduled start time of ``DAGOpNode`` corresponds to the
start time of the underlying pulse instruction composing the node operation.
However, this assumption can be intentionally broken by defining a pulse gate,
i.e. calibration, with the schedule involving pre-buffer, i.e. some random pulse delay
followed by a pulse instruction. Because this pass is not aware of such edge case,
the user must take special care of pulse gates if any.

Acquire alignment constraint

This value is reported by ``timing_constraints["acquire_alignment"]`` in the backend
configuration in units of dt. The start time of the :class:`~qiskit.circuit.Measure`
instruction should be multiple of this value.

Granularity constraint

This value is reported by ``timing_constraints["granularity"]`` in the backend
configuration in units of dt. This is the constraint for a single pulse :class:`Play`
instruction that may constitute your pulse gate.
The length of waveform samples should be multipel of this constraint value.
Violation of this constraint may result in failue in backend execution.

Minimum pulse length constraint

This value is reported by ``timing_constraints["min_length"]`` in the backend
configuration in units of dt. This is the constraint for a single pulse :class:`Play`
instruction that may constitute your pulse gate.
The length of waveform samples should be greater than this constraint value.
Violation of this constraint may result in failue in backend execution.

"""

from .check_durations import InstructionDurationCheck
from .pulse_gate_validation import ValidatePulseGates
from .reschedule import ConstrainedReschedule
from .align_measures import AlignMeasures
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# 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.

"""Deprecated. Measurement alignment."""

import warnings

from qiskit.transpiler.passes.scheduling.alignments.reschedule import ConstrainedReschedule


class AlignMeasures:
"""Deprecated. Measurement alignment."""

def __new__(cls, alignment=1) -> ConstrainedReschedule:
"""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.

Returns:
ConstrainedReschedule instance that is a drop-in-replacement of this class.
"""
warnings.warn(
f"{cls.__name__} has been deprecated as of Qiskit 20.0. "
f"Use ConstrainedReschedule pass instead.",
FutureWarning,
)
return ConstrainedReschedule(acquire_alignment=alignment)
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# 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.
"""A pass to check if input circuit requires reschedule."""

from qiskit.circuit.delay import Delay
from qiskit.dagcircuit import DAGCircuit
from qiskit.transpiler.basepasses import AnalysisPass


class InstructionDurationCheck(AnalysisPass):
"""Duration validation pass for reschedule.

This pass investigates the input quantum circuit and checks if the circuit requres
rescheduling for execution. Note that this pass can be triggered without scheduling.
This pass only checks the duration of delay instructions and user defined pulse gates,
which report duration values without pre-scheduling.

This pass assumes backend supported instructions, i.e. basis gates, have no violation
of the hardware alignment constraints, which is true in general.
"""

def __init__(
self,
acquire_alignment: int = 1,
pulse_alignment: int = 1,
):
"""Create new duration validation pass.

The alignment values depend on the control electronics of your quantum processor.

Args:
acquire_alignment: Integer number representing the minimum time resolution to
trigger acquisition instruction in units of ``dt``.
pulse_alignment: Integer number representing the minimum time resolution to
trigger gate instruction in units of ``dt``.
"""
super().__init__()
self.acquire_align = acquire_alignment
self.pulse_align = pulse_alignment

def run(self, dag: DAGCircuit):
"""Run duration validation passes.

Args:
dag: DAG circuit to check instruction durations.
"""
self.property_set["reschedule_required"] = False

# Rescheduling is not necessary
if self.acquire_align == 1 and self.pulse_align == 1:
return

# Check delay durations
for delay_node in dag.op_nodes(Delay):
dur = delay_node.op.duration
if not (dur % self.acquire_align == 0 and dur % self.pulse_align == 0):
self.property_set["reschedule_required"] = True
return

# Check custom gate durations
for inst_defs in dag.calibrations.values():
for caldef in inst_defs.values():
dur = caldef.duration
if not (dur % self.acquire_align == 0 and dur % self.pulse_align == 0):
self.property_set["reschedule_required"] = True
return
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# 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.

"""Analysis passes for hardware alignment constraints."""

from qiskit.dagcircuit import DAGCircuit
from qiskit.pulse import Play
from qiskit.transpiler.basepasses import AnalysisPass
from qiskit.transpiler.exceptions import TranspilerError


class ValidatePulseGates(AnalysisPass):
"""Check custom gate length.

This is a control electronics aware analysis pass.

Quantum gates (instructions) are often implemented with shaped analog stimulus signals.
These signals may be digitally stored in the waveform memory of the control electronics
and converted into analog voltage signals by electronic components known as
digital to analog converters (DAC).

In Qiskit SDK, we can define the pulse-level implementation of custom quantum gate
instructions, as a `pulse gate
<https://qiskit.org/documentation/tutorials/circuits_advanced/05_pulse_gates.html>`__,
thus user gates should satisfy all waveform memory constraints imposed by the backend.

This pass validates all attached calibration entries and raises ``TranspilerError`` to
kill the transpilation process if any invalid calibration entry is found.
This pass saves users from waiting until job execution time to get an invalid pulse error from
the backend control electronics.
"""

def __init__(
self,
granularity: int = 1,
min_length: int = 1,
):
"""Create new pass.

Args:
granularity: Integer number representing the minimum time resolution to
define the pulse gate length in units of ``dt``. This value depends on
the control electronics of your quantum processor.
min_length: Integer number representing the minimum data point length to
define the pulse gate in units of ``dt``. This value depends on
the control electronics of your quantum processor.
"""
super().__init__()
self.granularity = granularity
self.min_length = min_length

def run(self, dag: DAGCircuit):
"""Run the pulse gate validation attached to ``dag``.

Args:
dag: DAG to be validated.

Returns:
DAGCircuit: DAG with consistent timing and op nodes annotated with duration.

Raises:
TranspilerError: When pulse gate violate pulse controller constraints.
"""
if self.granularity == 1 and self.min_length == 1:
# we can define arbitrary length pulse with dt resolution
return

for gate, insts in dag.calibrations.items():
for qubit_param_pair, schedule in insts.items():
for _, inst in schedule.instructions:
if isinstance(inst, Play):
pulse = inst.pulse
if pulse.duration % self.granularity != 0:
raise TranspilerError(
f"Pulse duration is not multiple of {self.granularity}. "
"This pulse cannot be played on the specified backend. "
f"Please modify the duration of the custom gate pulse {pulse.name} "
f"which is associated with the gate {gate} of "
f"qubit {qubit_param_pair[0]}."
)
if pulse.duration < self.min_length:
raise TranspilerError(
f"Pulse gate duration is less than {self.min_length}. "
"This pulse cannot be played on the specified backend. "
f"Please modify the duration of the custom gate pulse {pulse.name} "
f"which is associated with the gate {gate} of "
"qubit {qubit_param_pair[0]}."
)
Loading