forked from Qiskit/qiskit
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Revert "Revert pulse model (Qiskit#11348)"
This reverts commit c0702bf.
- Loading branch information
1 parent
d8ef5c3
commit ff1a82e
Showing
9 changed files
with
784 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
# This code is part of Qiskit. | ||
# | ||
# (C) Copyright IBM 2023. | ||
# | ||
# 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. | ||
|
||
r""" | ||
========================================================== | ||
Pulse Targets & Frames (:mod:`qiskit.pulse.model`) | ||
========================================================== | ||
Pulse is meant to be agnostic to the underlying hardware implementation, while still allowing | ||
low-level control. Qiskit Pulse's pulse targets and frames create a flexible framework | ||
to define where pulse instructions are applied, and what would be their carrier frequency and phase | ||
(because typically AC pulses are used). Each :class:`.PulseTarget` represents a separate component | ||
in the quantum computing system on which instructions could be applied. On the other hand, | ||
each :class:`.Frame` represents a frequency and phase duo for the carrier of the pulse. | ||
While :class:`.PulseTarget` includes a :class`.Port` variant allowing for direct control over | ||
hardware ports, an abstraction layer is provided by :class:`.LogicalElement`. | ||
The abstraction allows to write pulse level programs with less knowledge of the hardware, and in | ||
a level which is more similar to the circuit level programing. i.e., instead of specifying | ||
ports, one can use Qubits, Couplers, etc. | ||
This logical and virtual representation allows the user to write template pulse | ||
programs without worrying about the exact details of the hardware implementation | ||
(are the pulses to be played via the same port? Which NCO is used?), while still | ||
allowing for effective utilization of the quantum hardware. The burden of mapping | ||
the different combinations of :class:`.LogicalElement` and :class:`.Frame` | ||
to hardware aware objects is left to the Pulse Compiler. | ||
.. _pulse_targets: | ||
PulseTarget | ||
================ | ||
:class:`.PulseTarget` includes :class:`.Port` who's objects are identified by a unique string identifier | ||
defined by the control system, and :class:`.LogicalElement` who's objects are identified by their type | ||
and index. Currently, the most prominent example of a :class:`.LogicalElement` is the | ||
:class:`~.pulse.Qubit`. | ||
.. autosummary:: | ||
:toctree: ../stubs/ | ||
Port | ||
Qubit | ||
Coupler | ||
.. _frames: | ||
Frame | ||
============= | ||
:class:`.Frame` s are identified by their type and unique identifier. A :class:`.GenericFrame` is used to | ||
specify custom frequency | ||
and phase duos, while :class:`.QubitFrame` and :class:`.MeasurementFrame` are used to indicate that | ||
backend defaults are to be used (for the qubit's driving frequency and measurement frequency | ||
respectively). | ||
.. autosummary:: | ||
:toctree: ../stubs/ | ||
GenericFrame | ||
QubitFrame | ||
MeasurementFrame | ||
.. _mixed_frames: | ||
MixedFrame | ||
============= | ||
The combination of a :class:`.LogicalElement` and :class:`.Frame` is dubbed a :class:`.MixedFrame`. | ||
.. autosummary:: | ||
:toctree: ../stubs/ | ||
MixedFrame | ||
""" | ||
|
||
from .pulse_target import ( | ||
PulseTarget, | ||
Port, | ||
LogicalElement, | ||
Qubit, | ||
Coupler, | ||
) | ||
|
||
from .frames import ( | ||
Frame, | ||
GenericFrame, | ||
QubitFrame, | ||
MeasurementFrame, | ||
) | ||
|
||
from .mixed_frames import ( | ||
MixedFrame, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
# This code is part of Qiskit. | ||
# | ||
# (C) Copyright IBM 2023. | ||
# | ||
# 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. | ||
|
||
""" | ||
Frames | ||
""" | ||
|
||
from abc import ABC | ||
|
||
import numpy as np | ||
|
||
from qiskit.pulse.exceptions import PulseError | ||
|
||
|
||
class Frame(ABC): | ||
"""Base class for pulse module frame. | ||
Because pulses used in Quantum hardware are typically AC pulses, the carrier frequency and phase | ||
must be defined. The :class:`Frame` is the object which identifies the frequency and phase for | ||
the carrier. | ||
and each pulse and most other instructions are associated with a frame. The different types of frames | ||
dictate how the frequency and phase duo are defined. | ||
The default initial phase for every frame is 0. | ||
""" | ||
|
||
|
||
class GenericFrame(Frame): | ||
"""Pulse module GenericFrame. | ||
The :class:`GenericFrame` is used for custom user defined frames, which are not associated with any | ||
backend defaults. It is especially useful when the frame doesn't correspond to any frame of | ||
the typical qubit model, like qudit control for example. Because no backend defaults exist for | ||
these frames, during compilation an initial frequency and phase will need to be provided. | ||
:class:`GenericFrame` objects are identified by their unique name. | ||
""" | ||
|
||
def __init__(self, name: str): | ||
"""Create ``GenericFrame``. | ||
Args: | ||
name: A unique identifier used to identify the frame. | ||
""" | ||
self._name = name | ||
|
||
@property | ||
def name(self) -> str: | ||
"""Return the name of the frame.""" | ||
return self._name | ||
|
||
def __repr__(self) -> str: | ||
return f"GenericFrame({self._name})" | ||
|
||
def __eq__(self, other): | ||
return type(self) is type(other) and self._name == other._name | ||
|
||
def __hash__(self): | ||
return hash((type(self), self._name)) | ||
|
||
|
||
class QubitFrame(Frame): | ||
"""A frame associated with the driving of a qubit. | ||
:class:`QubitFrame` is a frame associated with the driving of a specific qubit. | ||
The initial frequency of | ||
the frame will be taken as the default driving frequency provided by the backend | ||
during compilation. | ||
""" | ||
|
||
def __init__(self, index: int): | ||
"""Create ``QubitFrame``. | ||
Args: | ||
index: The index of the qubit represented by the frame. | ||
""" | ||
self._validate_index(index) | ||
self._index = index | ||
|
||
@property | ||
def index(self) -> int: | ||
"""Return the qubit index of the qubit frame.""" | ||
return self._index | ||
|
||
def _validate_index(self, index) -> None: | ||
"""Raise a ``PulseError`` if the qubit index is invalid. Namely, check if the index is a | ||
non-negative integer. | ||
Raises: | ||
PulseError: If ``identifier`` (index) is a negative integer. | ||
""" | ||
if not isinstance(index, (int, np.integer)) or index < 0: | ||
raise PulseError("Qubit index must be a non-negative integer") | ||
|
||
def __repr__(self) -> str: | ||
return f"QubitFrame({self._index})" | ||
|
||
def __eq__(self, other): | ||
return type(self) is type(other) and self._index == other._index | ||
|
||
def __hash__(self): | ||
return hash((type(self), self._index)) | ||
|
||
|
||
class MeasurementFrame(Frame): | ||
"""A frame associated with the measurement of a qubit. | ||
``MeasurementFrame`` is a frame associated with the readout of a specific qubit, | ||
which requires a stimulus tone driven at frequency off resonant to qubit drive. | ||
If not set otherwise, the initial frequency of the frame will be taken as the default | ||
measurement frequency provided by the backend during compilation. | ||
""" | ||
|
||
def __init__(self, index: int): | ||
"""Create ``MeasurementFrame``. | ||
Args: | ||
index: The index of the qubit represented by the frame. | ||
""" | ||
self._validate_index(index) | ||
self._index = index | ||
|
||
@property | ||
def index(self) -> int: | ||
"""Return the qubit index of the measurement frame.""" | ||
return self._index | ||
|
||
def _validate_index(self, index) -> None: | ||
"""Raise a ``PulseError`` if the qubit index is invalid. Namely, check if the index is a | ||
non-negative integer. | ||
Raises: | ||
PulseError: If ``index`` is a negative integer. | ||
""" | ||
if not isinstance(index, (int, np.integer)) or index < 0: | ||
raise PulseError("Qubit index must be a non-negative integer") | ||
|
||
def __repr__(self) -> str: | ||
return f"MeasurementFrame({self._index})" | ||
|
||
def __eq__(self, other): | ||
return type(self) is type(other) and self._index == other._index | ||
|
||
def __hash__(self): | ||
return hash((type(self), self._index)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
# This code is part of Qiskit. | ||
# | ||
# (C) Copyright IBM 2023. | ||
# | ||
# 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. | ||
|
||
""" | ||
Mixed Frames | ||
""" | ||
|
||
from .frames import Frame | ||
from .pulse_target import PulseTarget | ||
|
||
|
||
class MixedFrame: | ||
"""Representation of a :class:`PulseTarget` and :class:`Frame` combination. | ||
Most instructions need to be associated with both a :class:`PulseTarget` and a | ||
:class:`Frame`. The combination | ||
of the two is called a mixed frame and is represented by a :class:`MixedFrame` object. | ||
In most cases the :class:`MixedFrame` is used more by the compiler, and a pulse program | ||
can be written without :class:`MixedFrame` s, by setting :class:`PulseTarget` and | ||
:class:`Frame` independently. However, in some cases using :class:`MixedFrame` s can | ||
better convey the meaning of the code, and change the compilation process. One example | ||
is the use of the shift/set frequency/phase instructions which are not broadcasted to other | ||
:class:`MixedFrame` s if applied on a specific :class:`MixedFrame` (unlike the behavior | ||
of :class:`Frame`). User can also use a subclass of :class:`MixedFrame` for a particular | ||
combination of logical elements and frames as if a syntactic sugar. This might | ||
increase the readability of a user pulse program. As an example consider the cross | ||
resonance architecture, in which a pulse is played on a target qubit frame and applied | ||
to a control qubit logical element. | ||
""" | ||
|
||
def __init__(self, pulse_target: PulseTarget, frame: Frame): | ||
"""Create ``MixedFrame``. | ||
Args: | ||
pulse_target: The ``PulseTarget`` associated with the mixed frame. | ||
frame: The frame associated with the mixed frame. | ||
""" | ||
self._pulse_target = pulse_target | ||
self._frame = frame | ||
|
||
@property | ||
def pulse_target(self) -> PulseTarget: | ||
"""Return the target of this mixed frame.""" | ||
return self._pulse_target | ||
|
||
@property | ||
def frame(self) -> Frame: | ||
"""Return the ``Frame`` of this mixed frame.""" | ||
return self._frame | ||
|
||
def __repr__(self) -> str: | ||
return f"MixedFrame({self.pulse_target},{self.frame})" | ||
|
||
def __eq__(self, other: "MixedFrame") -> bool: | ||
"""Return True iff self and other are equal, specifically, iff they have the same target | ||
and frame. | ||
Args: | ||
other: The mixed frame to compare to this one. | ||
Returns: | ||
True iff equal. | ||
""" | ||
return self._pulse_target == other._pulse_target and self._frame == other._frame | ||
|
||
def __hash__(self) -> int: | ||
return hash((self._pulse_target, self._frame, type(self))) |
Oops, something went wrong.