Skip to main contentIBM Quantum Documentation

SymbolicPulse

qiskit.pulse.library.SymbolicPulse(pulse_type, duration, parameters=None, name=None, limit_amplitude=None, envelope=None, constraints=None, valid_amp_conditions=None) GitHub(opens in a new tab)

Bases: Pulse

The pulse representation model with parameters and symbolic expressions.

A symbolic pulse instance can be defined with an envelope and parameter constraints. Envelope and parameter constraints should be provided as symbolic expressions. Rather than creating a subclass, different pulse shapes can be distinguished by the instance attributes SymbolicPulse.envelope and SymbolicPulse.pulse_type.

The symbolic expressions must be defined either with SymPy(opens in a new tab) or Symengine(opens in a new tab). Usually Symengine-based expression is much more performant for instantiation of the SymbolicPulse, however, it doesn’t support every functions available in SymPy. You may need to choose proper library depending on how you define your pulses. Symengine works in the most envelopes and constraints, and thus it is recommended to use this library especially when your program contains a lot of pulses. Also note that Symengine has the limited platform support and may not be available for your local system. Symengine is a required dependency for Qiskit on platforms that support it will always be installed along with Qiskit on macOS x86_64 and arm64, and Linux x86_64, aarch64, and ppc64le. For 64-bit Windows users they will need to manual install it. For 32-bit platforms such as i686 and armv7 Linux, and on Linux s390x there are no pre-compiled packages available and to use symengine you’ll need to compile it from source. If Symengine is not available in your environment SymPy will be used.

Envelope function

The waveform at time tt is generated by the get_waveform() according to

F(t,Θ)=×F(t,duration,params)F(t, \Theta) = \times F(t, {\rm duration}, \overline{\rm params})

where Θ\Theta is the set of full pulse parameters in the SymbolicPulse.parameters dictionary which must include the duration\rm duration. Note that the FF is an envelope of the waveform, and a programmer must provide this as a symbolic expression. params\overline{\rm params} can be arbitrary complex values as long as they pass validate_parameters() and your quantum backend can accept. The time tt and duration\rm duration are in units of dt, i.e. sample time resolution, and this function is sampled with a discrete time vector in [0,duration][0, {\rm duration}] sampling the pulse envelope at every 0.5 dt (middle sampling strategy) when the SymbolicPulse.get_waveform() method is called. The sample data is not generated until this method is called thus a symbolic pulse instance only stores parameter values and waveform shape, which greatly reduces memory footprint during the program generation.

Pulse validation

When a symbolic pulse is instantiated, the method validate_parameters() is called, and performs validation of the pulse. The validation process involves testing the constraint functions and the maximal amplitude of the pulse (see below). While the validation process will improve code stability, it will reduce performance and might create compatibility issues (particularly with JAX). Therefore, it is possible to disable the validation by setting the class attribute disable_validation to True.

Constraint functions

Constraints on the parameters are defined with an instance attribute SymbolicPulse.constraints which can be provided through the constructor. The constraints value must be a symbolic expression, which is a function of parameters to be validated and must return a boolean value being True when parameters are valid. If there are multiple conditions to be evaluated, these conditions can be concatenated with logical expressions such as And and Or in SymPy or Symengine. The symbolic pulse instance can be played only when the constraint function returns True. The constraint is evaluated when validate_parameters() is called.

Maximum amplitude validation

When you play a pulse in a quantum backend, you might face the restriction on the power that your waveform generator can handle. Usually, the pulse amplitude is normalized by this maximum power, namely maxF1\max |F| \leq 1. This condition is evaluated along with above constraints when you set limit_amplitude = True in the constructor. To evaluate maximum amplitude of the waveform, we need to call get_waveform(). However, this introduces a significant overhead in the validation, and this cannot be ignored when you repeatedly instantiate symbolic pulse instances. SymbolicPulse.valid_amp_conditions provides a condition to skip this waveform validation, and the waveform is not generated as long as this condition returns True, so that healthy symbolic pulses are created very quick. For example, for a simple pulse shape like amp * cos(f * t), we know that pulse amplitude is valid as long as amp remains less than magnitude 1.0. So abs(amp) <= 1 could be passed as SymbolicPulse.valid_amp_conditions to skip doing a full waveform evaluation for amplitude validation. This expression is provided through the constructor. If this is not provided, the waveform is generated everytime when validate_parameters() is called.

Examples

This is how a user can instantiate a symbolic pulse instance. In this example, we instantiate a custom Sawtooth envelope.

from qiskit.pulse.library import SymbolicPulse
 
my_pulse = SymbolicPulse(
    pulse_type="Sawtooth",
    duration=100,
    parameters={"amp": 0.1, "freq": 0.05},
    name="pulse1",
)

Note that SymbolicPulse can be instantiated without providing the envelope and constraints. However, this instance cannot generate waveforms without knowing the envelope definition. Now you need to provide the envelope.

import sympy
from qiskit.pulse.library import SymbolicPulse
 
t, amp, freq = sympy.symbols("t, amp, freq")
envelope = 2 * amp * (freq * t - sympy.floor(1 / 2 + freq * t))
 
my_pulse = SymbolicPulse(
    pulse_type="Sawtooth",
    duration=100,
    parameters={"amp": 0.1, "freq": 0.05},
    envelope=envelope,
    name="pulse1",
)
 
my_pulse.draw()
../_images/qiskit-pulse-library-SymbolicPulse-1.png

Likewise, you can define SymbolicPulse.constraints for my_pulse. After providing the envelope definition, you can generate the waveform data. Note that it would be convenient to define a factory function that automatically accomplishes this procedure.

def Sawtooth(duration, amp, freq, name):
    t, amp, freq = sympy.symbols("t, amp, freq")
 
    instance = SymbolicPulse(
        pulse_type="Sawtooth",
        duration=duration,
        parameters={"amp": amp, "freq": freq},
        envelope=2 * amp * (freq * t - sympy.floor(1 / 2 + freq * t)),
        name=name,
    )
 
    return instance

You can also provide a Parameter object in the parameters dictionary, or define duration with a Parameter object when you instantiate the symbolic pulse instance. A waveform cannot be generated until you assign all unbounded parameters. Note that parameters will be assigned through the schedule playing the pulse.

Serialization

The SymbolicPulse subclass can be serialized along with the symbolic expressions through qiskit.qpy. A user can therefore create a custom pulse subclass with a novel envelope and constraints, and then one can instantiate the class with certain parameters to run on a backend. This pulse instance can be saved in the QPY binary, which can be loaded afterwards even within the environment not having original class definition loaded. This mechanism also allows us to easily share a pulse program including custom pulse instructions with collaborators.

Create a parametric pulse.

Parameters

  • pulse_type (str(opens in a new tab)) – Display name of this pulse shape.
  • duration (ParameterExpression |int(opens in a new tab)) – Duration of pulse.
  • parameters (Mapping[str(opens in a new tab), ParameterExpression |complex(opens in a new tab)] | None) – Dictionary of pulse parameters that defines the pulse envelope.
  • name (str(opens in a new tab) | None) – Display name for this particular pulse envelope.
  • limit_amplitude (bool(opens in a new tab) | None) – If True, then limit the absolute value of the amplitude of the waveform to 1. The default is True and the amplitude is constrained to 1.
  • envelope (sym.Expr | None) – Pulse envelope expression.
  • constraints (sym.Expr | None) – Pulse parameter constraint expression.
  • valid_amp_conditions (sym.Expr | None) – Extra conditions to skip a full-waveform check for the amplitude limit. If this condition is not met, then the validation routine will investigate the full-waveform and raise an error when the amplitude norm of any data point exceeds 1.0. If not provided, the validation always creates a full-waveform.

Raises

PulseError – When not all parameters are listed in the attribute PARAM_DEF.


Attributes

constraints

Return symbolic expression for the pulse parameter constraints.

disable_validation

= False

duration

envelope

Return symbolic expression for the pulse envelope.

id

Unique identifier for this pulse.

limit_amplitude

= True

name

parameters

pulse_type

Return display name of the pulse shape.

valid_amp_conditions

Return symbolic expression for the pulse amplitude constraints.


Methods

draw

draw(style=None, backend=None, time_range=None, time_unit='dt', show_waveform_info=True, plotter='mpl2d', axis=None)

Plot the interpolated envelope of pulse.

Parameters

  • style (dict(opens in a new tab)[str(opens in a new tab), Any] | None) – Stylesheet options. This can be dictionary or preset stylesheet classes. See IQXStandard, IQXSimple, and IQXDebugging for details of preset stylesheets.

  • backend (Optional[BaseBackend]) – Backend object to play the input pulse program. If provided, the plotter may use to make the visualization hardware aware.

  • time_range (tuple(opens in a new tab)[int(opens in a new tab), int(opens in a new tab)] | None) – Set horizontal axis limit. Tuple (tmin, tmax).

  • time_unit (str(opens in a new tab)) – The unit of specified time range either dt or ns. The unit of ns is available only when backend object is provided.

  • show_waveform_info (bool(opens in a new tab)) – Show waveform annotations, i.e. name, of waveforms. Set True to show additional information about waveforms.

  • plotter (str(opens in a new tab)) –

    Name of plotter API to generate an output image. One of following APIs should be specified:

    mpl2d: Matplotlib API for 2D image generation.
        Matplotlib API to generate 2D image. Charts are placed along y axis with
        vertical offset. This API takes matplotlib.axes.Axes as `axis` input.

    axis and style kwargs may depend on the plotter.

  • axis (Any | None) – Arbitrary object passed to the plotter. If this object is provided, the plotters use a given axis instead of internally initializing a figure object. This object format depends on the plotter. See plotter argument for details.

Returns

Visualization output data. The returned data type depends on the plotter. If matplotlib family is specified, this will be a matplotlib.pyplot.Figure data.

get_waveform

get_waveform()

Return a Waveform with samples filled according to the formula that the pulse represents and the parameter values it contains.

Since the returned array is a discretized time series of the continuous function, this method uses a midpoint sampler. For duration, return:

{f(t+0.5)CtZ0<=t<duration}\{f(t+0.5) \in \mathbb{C} | t \in \mathbb{Z} \wedge 0<=t<\texttt{duration}\}

Returns

A waveform representation of this pulse.

Raises

  • PulseError – When parameters are not assigned.
  • PulseError – When expression for pulse envelope is not assigned.

Return type

Waveform

is_parameterized

is_parameterized()

Return True iff the instruction is parameterized.

Return type

bool(opens in a new tab)

validate_parameters

validate_parameters()

Validate parameters.

Raises

PulseError – If the parameters passed are not valid.

Was this page helpful?