Download

Download this file as Jupyter notebook: envelopes.ipynb.

Introduction to pulse envelopes

This guide gives an overview of the types of pulse envelopes available in Keysight’s Quantum Control System and demonstrates how to initialize them, add them to waveforms in a programs, and plot them.

Note

Throughout the examples in this tutorial we are using DCWaveforms to demonstrate the envelopes. Introduction to RF waveforms shows how to generate the more commonly used RFWaveforms with envelopes.

[2]:
import keysight.qcs as qcs
import numpy as np

Built-In Envelopes

Constant Envelope

A constant envelope is the simplest pulse shape. It realizes a constant amplitude for a duration. In this example we configure a program with one AWG channel and add a DC waveform with a constant envelope to it.

[3]:
# initialize a constant envelope
envelope = qcs.ConstantEnvelope()
dc_wav = qcs.DCWaveform(duration=2, envelope=envelope, amplitude=1)

# instantiate an empty program
program = qcs.Program()

# instantiate an awg channel
awg = qcs.Channels(range(1), "awg_channel")

# add the envelope to the program
program.add_waveform(dc_wav, awg)

# render
program.render(sample_rate=1e3)

Gaussian Envelope

In this example we define a DC waveform with a Gaussian envelope with a duration of 32 ns and standard deviation of 8 ns.

[4]:
# initialize a Gaussian
envelope = qcs.GaussianEnvelope(num_sigma=2)
dc_wav = qcs.DCWaveform(32e-9, envelope, 1)

# initialize the program and add the envelope
program = qcs.Program()
program.add_waveform(dc_wav, awg)

# render
program.render()

Flat-Top Gaussian Envelope

Here we realize a Flat-Top Gaussian envelope. During an initial rise and equal lower time, the pulse is Gaussian. In the middle, for a time duration, the envelope is constant and equal to its maximum value.

In this example we make use of the built-in Scalar class, which allows us to modify parameters after instantiation. We render the envelope once, then render it again with different parameters. This behavior is used for example in swept programs.

[5]:
# initialize parameters
duration = qcs.Scalar("duration", value=32e-9, dtype=float)
amplitude = qcs.Scalar("amplitude", value=0.8, dtype=float)
rise = qcs.Scalar("rise", value=8e-9, dtype=float)

# initialize a Gaussian DC Waveform
dc_wav = qcs.DCWaveform(
    duration=2 * rise, envelope=qcs.GaussianEnvelope(), amplitude=amplitude
)

# initialize the program and add the flattop using the to_flattop method
program = qcs.Program()
program.add_waveform(dc_wav.to_flattop(duration), awg)

# render
program.render()

We now change the parameters defining the envelope and re-render it.

[6]:
duration.value = 64e-9
amplitude.value = 0.6
rise.value = 20e-9

# render
program.render()

Derivative Envelope

The derivative envelope is specified by a base_envelope and optional step_size. The base_envelope can be any envelope defined by Keysight’s Quantum Control System.

[7]:
base_envelope = qcs.GaussianEnvelope(num_sigma=5)
envelope = qcs.DerivativeEnvelope(base_envelope)
dc_wav = qcs.DCWaveform(2, envelope, 0.7)

# initialize the program and add the envelope
program = qcs.Program()
program.add_waveform(dc_wav, awg)

# render
program.render(sample_rate=1e3)

DRAG Envelope

Derivative Removal by Adiabatic Gate (DRAG) envelopes were introduced in Motzoi et al. as a means of offsetting the noise introduced during the application of a pulse by applying another pulse in an orthogonal axis. The envelope applied orthogonal to the base envelope is described by the derivative of the base envelope scaled with some DRAG coefficient. Often, pulses applied simultaneously in different axes are stored in the real and imaginary parts of an envelope. We therefore describe the DRAG pulse of a base envelope \(E(t)\) as

\[E(t) + i\beta E'(t),\]

where \(\beta\) is the drag coefficient and \(E'(t)\) is the derivative of \(E(t)\) with respect to time.

Most DRAG envelopes are not explicitly defined in Keysight’s Quantum Control System. However, a method that automatically constructs the relevant DRAG envelope given a base envelope using numerical differentiation is provided.

In this example, we assemble a DRAG envelope with a Gaussian as the base shape. Here, the DRAG coefficient is \(\beta=0.1\).

[8]:
# initialize a 30ns Gaussian RF Waveform at 5GHz
gauss = qcs.RFWaveform(
    duration=30e-9, envelope=qcs.GaussianEnvelope(), amplitude=0.8, rf_frequency=5e9
)

# initialize the program and add the drag pulse with a coefficient of 0.05
program = qcs.Program()
program.add_waveform(gauss.drag(0.05), awg)

# render
program.render(sample_rate=5e9, plot_imaginary=True)

Arbitrary envelopes

An arbitrary envelope allows a user to specify any shape of pulse.

Example 1: linear interpolation

In this example we implement a flat-top pulse with linear interpolation from zero to the maximum value during the interval [0, rise_time]. The amplitude then falls back to zero on the interval [duration-fall_time, duration]. Arbitrary envelopes can be specified as a list of sample times and corresponding amplitudes, in which case linear interpolation is applied between specified points.

[9]:
duration = 3
rise_time = 0.5
fall_time = 1
amplitude = 0.6

# specify the samples for linear interpolation
times = [0, rise_time, duration - fall_time, duration]
samples = [0, amplitude, amplitude, 0]

# initialize the envelope
envelope = qcs.ArbitraryEnvelope(times, samples)
dc_wav = qcs.DCWaveform(duration, envelope, amplitude)

# initialize the program and add the envelope
program = qcs.Program()
program.add_waveform(dc_wav, awg)

# render
program.render(sample_rate=1e3)

Example 2: functional definition with sech envelope

Rather than specifying an envelope with a collection of discrete points, an envelope can be specified with a function that defines the envelope’s shape.

In this example we implement a sech envelope. The envelope \(E(t)\) is given by

\[E(t) = A\text{sech}\left(\frac{t-\mu}{\sigma}\right)\]

where \(A\) is the scaling parameter, \(\mu\) is the mean and \(\sigma\) is the standard deviation.

[10]:
# define the sech envelope
def sech(times, A, sigma):
    return A / np.cosh((times - times[-1] / 2) / sigma)


A = 0.45
sigma = 0.15

# initialize the envelope
envelope = qcs.ArbitraryEnvelope.sample(lambda t: sech(t, A, sigma), n_samples=100)

duration = 1
dc_wav = qcs.DCWaveform(duration, envelope, 1)

# initialize the program and add the envelope
program = qcs.Program()
program.add_waveform(dc_wav, awg)

program.render(sample_rate=1e3)

Example 3: functional definition with sine envelope

In this example we implement a sinusoidal envelope. The envelope \(E(t)\) is given by

\[E(t) = A\sin(2\pi f_{\mathrm{int}} t + \phi)\]

where \(A\) is the amplitude, \(f_{\mathrm{int}}\) is the frequency, and \(\phi\) is the phase of the envelope. Note that as envelopes do not explicitly depend on time, parameters such as the frequency are unitless and can be specified as integer multiples of periods. Here we specify a sine wave that covers half a period:

[11]:
# define the sine envelope
def sine(input_times, amplitude, freq, phase):
    return amplitude * np.sin(2 * np.pi * freq * input_times + phase)


amplitude = 0.4
phase = 0
freq = 0.5

# initialize the envelope
envelope = qcs.ArbitraryEnvelope.sample(
    lambda t: sine(t, amplitude, freq, phase), n_samples=100
)

duration = 1
dc_wav = qcs.DCWaveform(duration, envelope, amplitude)

# initialize the program and add the envelope
program = qcs.Program()
program.add_waveform(dc_wav, awg)

program.render(sample_rate=1e3)

Download

Download this file as Jupyter notebook: envelopes.ipynb.

On this page