Skip to content

Commit

Permalink
Added pulses to the SymbolicPulse library (#9625)
Browse files Browse the repository at this point in the history
* Corrected library pulses return types.

* Added Sin and Cos Pulses

* Add sawtooth pulse

* Add triangle pulse

* Add new pulses to _init_ and some corrections

* Add tests, release notes and some corrections.

* Release notes correction

* Documentation correction

* Update qiskit/pulse/library/symbolic_pulses.py

Co-authored-by: Naoki Kanazawa <nkanazawa1989@gmail.com>

* Some corrections and modifications

---------

Co-authored-by: Naoki Kanazawa <nkanazawa1989@gmail.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
3 people committed Feb 21, 2023
1 parent e2d31ff commit 3512bbd
Show file tree
Hide file tree
Showing 5 changed files with 440 additions and 5 deletions.
4 changes: 4 additions & 0 deletions qiskit/pulse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@
Gaussian,
GaussianSquare,
GaussianSquareDrag,
Sin,
Cos,
Sawtooth,
Triangle,
ParametricPulse,
SymbolicPulse,
ScalableSymbolicPulse,
Expand Down
8 changes: 8 additions & 0 deletions qiskit/pulse/library/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@
Gaussian
GaussianSquare
GaussianSquareDrag
Sin
Cos
Sawtooth
Triangle
"""

Expand Down Expand Up @@ -120,6 +124,10 @@
GaussianSquareDrag,
Drag,
Constant,
Sin,
Cos,
Sawtooth,
Triangle,
)
from .pulse import Pulse
from .waveform import Waveform
286 changes: 281 additions & 5 deletions qiskit/pulse/library/symbolic_pulses.py
Original file line number Diff line number Diff line change
Expand Up @@ -739,7 +739,7 @@ def __new__(
angle: Optional[Union[float, ParameterExpression]] = None,
name: Optional[str] = None,
limit_amplitude: Optional[bool] = None,
) -> SymbolicPulse:
) -> ScalableSymbolicPulse:
"""Create new pulse instance.
Args:
Expand Down Expand Up @@ -837,7 +837,7 @@ def __new__(
risefall_sigma_ratio: Optional[Union[float, ParameterExpression]] = None,
name: Optional[str] = None,
limit_amplitude: Optional[bool] = None,
) -> SymbolicPulse:
) -> ScalableSymbolicPulse:
"""Create new pulse instance.
Args:
Expand Down Expand Up @@ -924,7 +924,7 @@ def GaussianSquareDrag(
risefall_sigma_ratio: Optional[Union[float, ParameterExpression]] = None,
name: Optional[str] = None,
limit_amplitude: Optional[bool] = None,
) -> SymbolicPulse:
) -> ScalableSymbolicPulse:
"""A square pulse with a Drag shaped rise and fall
This pulse shape is similar to :class:`~.GaussianSquare` but uses
Expand Down Expand Up @@ -1112,7 +1112,7 @@ def __new__(
angle: Optional[Union[float, ParameterExpression]] = None,
name: Optional[str] = None,
limit_amplitude: Optional[bool] = None,
) -> SymbolicPulse:
) -> ScalableSymbolicPulse:
"""Create new pulse instance.
Args:
Expand Down Expand Up @@ -1181,7 +1181,7 @@ def __new__(
angle: Optional[Union[float, ParameterExpression]] = None,
name: Optional[str] = None,
limit_amplitude: Optional[bool] = None,
) -> SymbolicPulse:
) -> ScalableSymbolicPulse:
"""Create new pulse instance.
Args:
Expand Down Expand Up @@ -1227,3 +1227,279 @@ def __new__(
instance.validate_parameters()

return instance


def Sin(
duration: Union[int, ParameterExpression],
amp: Union[float, ParameterExpression],
phase: Union[float, ParameterExpression],
freq: Optional[Union[float, ParameterExpression]] = None,
angle: Optional[Union[float, ParameterExpression]] = 0.0,
name: Optional[str] = None,
limit_amplitude: Optional[bool] = None,
) -> ScalableSymbolicPulse:
"""A sinusoidal pulse.
The envelope of the pulse is given by:
.. math::
f(x) &= \\text{A}\\sin\\left(2\\pi\text{freq}x+\\text{phase}\\right) , 0 <= x < duration
where :math:`\\text{A} = \\text{amp} \\times\\exp\\left(i\\times\\text{angle}\\right)`.
Args:
duration: Pulse length in terms of the sampling period `dt`.
amp: The magnitude of the amplitude of the sinusoidal wave. Wave range is [-`amp`,`amp`].
phase: The phase of the sinusoidal wave (note that this is not equivalent to the angle of
the complex amplitude)
freq: The frequency of the sinusoidal wave, in terms of 1 over sampling period.
If not provided defaults to a single cycle (i.e :math:'\\frac{1}{\\text{duration}}').
The frequency is limited to the range :math:`\\left(0,0.5\\right]` (the Nyquist frequency).
angle: The angle in radians of the complex phase factor uniformly
scaling the pulse. Default value 0.
name: Display name for this pulse envelope.
limit_amplitude: If ``True``, then limit the amplitude of the
waveform to 1. The default is ``True`` and the amplitude is constrained to 1.
Returns:
ScalableSymbolicPulse instance.
"""
if freq is None:
freq = 1 / duration
parameters = {"freq": freq, "phase": phase}

# Prepare symbolic expressions
_t, _duration, _amp, _angle, _freq, _phase = sym.symbols("t, duration, amp, angle, freq, phase")

envelope_expr = _amp * sym.exp(sym.I * _angle) * sym.sin(2 * sym.pi * _freq * _t + _phase)

consts_expr = sym.And(_freq > 0, _freq < 0.5)

# This might fail for waves shorter than a single cycle
valid_amp_conditions_expr = sym.Abs(_amp) <= 1.0

instance = ScalableSymbolicPulse(
pulse_type="Sin",
duration=duration,
amp=amp,
angle=angle,
parameters=parameters,
name=name,
limit_amplitude=limit_amplitude,
envelope=envelope_expr,
constraints=consts_expr,
valid_amp_conditions=valid_amp_conditions_expr,
)
instance.validate_parameters()

return instance


def Cos(
duration: Union[int, ParameterExpression],
amp: Union[float, ParameterExpression],
phase: Union[float, ParameterExpression],
freq: Optional[Union[float, ParameterExpression]] = None,
angle: Optional[Union[float, ParameterExpression]] = 0.0,
name: Optional[str] = None,
limit_amplitude: Optional[bool] = None,
) -> ScalableSymbolicPulse:
"""A cosine pulse.
The envelope of the pulse is given by:
.. math::
f(x) &= \\text{A}\\cos\\left(2\\pi\text{freq}x+\\text{phase}\\right) , 0 <= x < duration
where :math:`\\text{A} = \\text{amp} \\times\\exp\\left(i\\times\\text{angle}\\right)`.
Args:
duration: Pulse length in terms of the sampling period `dt`.
amp: The magnitude of the amplitude of the cosine wave. Wave range is [-`amp`,`amp`].
phase: The phase of the cosine wave (note that this is not equivalent to the angle
of the complex amplitude).
freq: The frequency of the cosine wave, in terms of 1 over sampling period.
If not provided defaults to a single cycle (i.e :math:'\\frac{1}{\\text{duration}}').
The frequency is limited to the range :math:`\\left(0,0.5\\right]` (the Nyquist frequency).
angle: The angle in radians of the complex phase factor uniformly
scaling the pulse. Default value 0.
name: Display name for this pulse envelope.
limit_amplitude: If ``True``, then limit the amplitude of the
waveform to 1. The default is ``True`` and the amplitude is constrained to 1.
Returns:
ScalableSymbolicPulse instance.
"""
if freq is None:
freq = 1 / duration
parameters = {"freq": freq, "phase": phase}

# Prepare symbolic expressions
_t, _duration, _amp, _angle, _freq, _phase = sym.symbols("t, duration, amp, angle, freq, phase")

envelope_expr = _amp * sym.exp(sym.I * _angle) * sym.cos(2 * sym.pi * _freq * _t + _phase)

consts_expr = sym.And(_freq > 0, _freq < 0.5)

# This might fail for waves shorter than a single cycle
valid_amp_conditions_expr = sym.Abs(_amp) <= 1.0

instance = ScalableSymbolicPulse(
pulse_type="Cos",
duration=duration,
amp=amp,
angle=angle,
parameters=parameters,
name=name,
limit_amplitude=limit_amplitude,
envelope=envelope_expr,
constraints=consts_expr,
valid_amp_conditions=valid_amp_conditions_expr,
)
instance.validate_parameters()

return instance


def Sawtooth(
duration: Union[int, ParameterExpression],
amp: Union[float, ParameterExpression],
phase: Union[float, ParameterExpression],
freq: Optional[Union[float, ParameterExpression]] = None,
angle: Optional[Union[float, ParameterExpression]] = 0.0,
name: Optional[str] = None,
limit_amplitude: Optional[bool] = None,
) -> ScalableSymbolicPulse:
"""A sawtooth pulse.
The envelope of the pulse is given by:
.. math::
f(x) &= 2\\text{A}\\left[g\\left(x\\right)-
\\lfloor g\\left(x\\right)+\\frac{1}{2}\\rfloor\\right]
where :math:`\\text{A} = \\text{amp} \\times\\exp\\left(i\\times\\text{angle}\\right)`,
:math:`g\\left(x\\right)=x\\times\\text{freq}+\\frac{\\text{phase}}{2\\pi}`,
and :math:`\\lfloor ...\\rfloor` is the floor operation.
Args:
duration: Pulse length in terms of the sampling period `dt`.
amp: The magnitude of the amplitude of the sawtooth wave. Wave range is [-`amp`,`amp`].
phase: The phase of the sawtooth wave (note that this is not equivalent to the angle
of the complex amplitude)
freq: The frequency of the sawtooth wave, in terms of 1 over sampling period.
If not provided defaults to a single cycle (i.e :math:'\\frac{1}{\\text{duration}}').
The frequency is limited to the range :math:`\\left(0,0.5\\right]` (the Nyquist frequency).
angle: The angle in radians of the complex phase factor uniformly
scaling the pulse. Default value 0.
name: Display name for this pulse envelope.
limit_amplitude: If ``True``, then limit the amplitude of the
waveform to 1. The default is ``True`` and the amplitude is constrained to 1.
Returns:
ScalableSymbolicPulse instance.
"""
if freq is None:
freq = 1 / duration
parameters = {"freq": freq, "phase": phase}

# Prepare symbolic expressions
_t, _duration, _amp, _angle, _freq, _phase = sym.symbols("t, duration, amp, angle, freq, phase")
lin_expr = _t * _freq + _phase / (2 * sym.pi)

envelope_expr = 2 * _amp * sym.exp(sym.I * _angle) * (lin_expr - sym.floor(lin_expr + 1 / 2))

consts_expr = sym.And(_freq > 0, _freq < 0.5)

# This might fail for waves shorter than a single cycle
valid_amp_conditions_expr = sym.Abs(_amp) <= 1.0

instance = ScalableSymbolicPulse(
pulse_type="Sawtooth",
duration=duration,
amp=amp,
angle=angle,
parameters=parameters,
name=name,
limit_amplitude=limit_amplitude,
envelope=envelope_expr,
constraints=consts_expr,
valid_amp_conditions=valid_amp_conditions_expr,
)
instance.validate_parameters()

return instance


def Triangle(
duration: Union[int, ParameterExpression],
amp: Union[float, ParameterExpression],
phase: Union[float, ParameterExpression],
freq: Optional[Union[float, ParameterExpression]] = None,
angle: Optional[Union[float, ParameterExpression]] = 0.0,
name: Optional[str] = None,
limit_amplitude: Optional[bool] = None,
) -> ScalableSymbolicPulse:
"""A triangle pulse.
The envelope of the pulse is given by:
.. math::
f(x) &= \\text{A}\\left[\\text{sawtooth}\\left(x\\right)right] , 0 <= x < duration
where :math:`\\text{A} = \\text{amp} \\times\\exp\\left(i\\times\\text{angle}\\right)`,
and :math:`\\text{sawtooth}\\left(x\\right)` is a sawtooth wave with the same frequency
as the triangle wave, but a phase shifted by :math:`\\frac{\\pi}{2}`.
Args:
duration: Pulse length in terms of the sampling period `dt`.
amp: The magnitude of the amplitude of the triangle wave. Wave range is [-`amp`,`amp`].
phase: The phase of the triangle wave (note that this is not equivalent to the angle
of the complex amplitude)
freq: The frequency of the triangle wave, in terms of 1 over sampling period.
If not provided defaults to a single cycle (i.e :math:'\\frac{1}{\\text{duration}}').
The frequency is limited to the range :math:`\\left(0,0.5\\right]` (the Nyquist frequency).
angle: The angle in radians of the complex phase factor uniformly
scaling the pulse. Default value 0.
name: Display name for this pulse envelope.
limit_amplitude: If ``True``, then limit the amplitude of the
waveform to 1. The default is ``True`` and the amplitude is constrained to 1.
Returns:
ScalableSymbolicPulse instance.
"""
if freq is None:
freq = 1 / duration
parameters = {"freq": freq, "phase": phase}

# Prepare symbolic expressions
_t, _duration, _amp, _angle, _freq, _phase = sym.symbols("t, duration, amp, angle, freq, phase")
lin_expr = _t * _freq + _phase / (2 * sym.pi) - 0.25
sawtooth_expr = 2 * (lin_expr - sym.floor(lin_expr + 1 / 2))

envelope_expr = _amp * sym.exp(sym.I * _angle) * (-2 * sym.Abs(sawtooth_expr) + 1)

consts_expr = sym.And(_freq > 0, _freq < 0.5)

# This might fail for waves shorter than a single cycle
valid_amp_conditions_expr = sym.Abs(_amp) <= 1.0

instance = ScalableSymbolicPulse(
pulse_type="Triangle",
duration=duration,
amp=amp,
angle=angle,
parameters=parameters,
name=name,
limit_amplitude=limit_amplitude,
envelope=envelope_expr,
constraints=consts_expr,
valid_amp_conditions=valid_amp_conditions_expr,
)
instance.validate_parameters()

return instance
13 changes: 13 additions & 0 deletions releasenotes/notes/add-new-symbolic-pulses-4dc46ecaaa1ba928.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
features:
- |
The ``SymbolicPulse`` library was extended. The new pulses in the library are:
* :class:``~qiskit.pulse.library.Sin``
* :class:``~qiskit.pulse.library.Cos``
* :class:``~qiskit.pulse.library.Sawtooth``
* :class:``~qiskit.pulse.library.Triangle``
The new functions return a ``ScalableSymbolicPulse``. With the exception of the ``Sawtooth`` phase,
behaviour is identical to that of the corresponding waveform generators (:class:``~qiskit.pulse.library.sin`` etc).
The ``Sawtooth`` phase is defined such that a phase of :math:``2\\pi`` shifts by a full cycle.
Loading

0 comments on commit 3512bbd

Please sign in to comment.