Download

Download this file as Jupyter notebook: sweep_examples.ipynb.

Sweeping program variables

The Running a program on hardware tutorial covers the basic workflow for running experiments on hardware and extracting trace data and I/Q data. Here we expand on that and add sweeps to the programs.

Setting up

As before, we use two AWG channels and two digitizers.

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

# set the following to True when connected to hardware:
run_on_hw = False

# instantiate channels representing two AWGs and two digitizers
awgs = qcs.Channels(range(2), "readoutawg")
digs = qcs.Channels(range(2), "readoutreceiver")

# load our channel mapper to connect the virtual channes above to physical ones
mapper = qcs.load("../../assets/channel_mapper.qcs")

Single variable sweeps

We define a program that plays a single RF waveform on our AWG channels and performs an acquisition on the digitizer channels with an IntegrationFilterto perform I/Q demodulation of the trace data.

We are sweeping the frequency of the RF pulse but keep the frequency of the integration filter constant.

[3]:
# define the frequency and amplitude of our RF waveform as QCS variables
frequency = qcs.Scalar("rf", dtype=float)
amplitude = qcs.Scalar("amp", dtype=float, value=1)


def make_program():
    """Creates a simple program with a single RF waveform"""
    program = qcs.Program()
    # define the waveform using this frequency variable & add it to the program
    gauss = qcs.RFWaveform(80e-9, qcs.GaussianEnvelope(num_sigma=4), 1, frequency)
    program.add_waveform(gauss, awgs)

    # define an integration filter using the same RF waveform but with fixed frequency
    int_filter = qcs.RFWaveform(80e-9, qcs.GaussianEnvelope(num_sigma=4), 1, 4.15e9)
    program.add_acquisition(int_filter, digs)

    # specify the number of shots
    return program.n_shots(10)


program = make_program()

# specify the frequency values to be swept over
frequencies = qcs.Array(
    "frequencies", value=[4.05e9, 4.1e9, 4.15e9, 4.2e9, 4.25e9], dtype=float
)

# add the sweep, targeting the `frequency` variable
program.sweep(frequencies, frequency)

program.draw()
keysight-logo-svg
Program
Program
Duration 80 ns
Layers 1
Targets 4
Repetitions Sweep with 5 repetitions
Associations
rf Array(name=frequencies, shape=(5,), dtype=float, unit=none, value=[4.05 GHz, 4.1 GHz, 4.15 GHz, 4.2 GHz, 4.25 GHz])
Repeat with 10 repetitions
Layer #0
Layer #0
Duration 80 ns
readoutawg 0
RFWaveform on ('readoutawg', 0)

Parameters
Duration 80 ns
Amplitude 1
Frequency Scalar(name=rf, value=None, dtype=float, unit=Hz)
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
1
RFWaveform on ('readoutawg', 1)

Parameters
Duration 80 ns
Amplitude 1
Frequency Scalar(name=rf, value=None, dtype=float, unit=Hz)
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
readoutreceiver 0
Acquisition on ('readoutreceiver', 0)

Parameters
Duration 80 ns
Integration Filter
RFWaveform

Parameters
Duration 80 ns
Amplitude 1
Frequency 4.15 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
1
Acquisition on ('readoutreceiver', 1)

Parameters
Duration 80 ns
Integration Filter
RFWaveform

Parameters
Duration 80 ns
Amplitude 1
Frequency 4.15 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad

We can inspect the sweep settings on the program visualization by hovering over the table header. Alternatively, we can retrieve them through the repetitions attribute:

[4]:
print(program.repetitions.items)
[Sweep(rf=Array(name=frequencies, shape=(5,), dtype=float, unit=none)), Repeat(10)]

Next, we execute the program on hardware.

[5]:
if run_on_hw:
    # initialize the backend pass
    backend = qcs.HclBackend(channel_mapper=mapper)
    # the executor returns the program populated with results
    program = qcs.Executor(backend).execute(program)
    # (optional) export the data to an HDF5 file
    program.to_hdf5("program2.hdf5")

# we are loading a previously run program here for this example
program = qcs.load("program2.hdf5")

The results will now consist of five traces for each sweep point and for each channel.

[6]:
program.plot_trace()

We can retrieve the I/Q data by calling the get_iq() method, which returns a dataframe that has the sweep values in its column header.

[7]:
program.get_iq(stack_channels=True)
[7]:
(rf, 4.05 GHz) ... (rf, 4.25 GHz)
0 1 2 3 4 5 6 7 8 9 ... 0 1 2 3 4 5 6 7 8 9
(((Channels(labels=[0], name=readoutreceiver, absolute_phase=False)))) -0.000191-0.003899j 0.000077-0.003642j -0.000430-0.004168j 0.000012-0.004071j 0.000042-0.003658j -0.000510-0.004211j -0.000052-0.004175j 0.000084-0.003701j -0.000719-0.004324j -0.000124-0.004373j ... -0.000036+0.000368j 0.000318-0.000300j 0.000000-0.000124j 0.000142+0.000179j -0.000002-0.000144j -0.000265-0.000027j -0.000067+0.000153j -0.000051-0.000221j -0.000317+0.000065j 0.000106+0.000130j
(((Channels(labels=[1], name=readoutreceiver, absolute_phase=False)))) -0.004437-0.001820j -0.002133-0.001429j -0.002552-0.000963j -0.003809-0.001938j -0.002047-0.001594j -0.002233-0.000374j -0.004006-0.001622j -0.002390-0.001392j -0.002402-0.000332j -0.004073-0.001305j ... -0.000378-0.000470j -0.000537+0.000370j 0.000590-0.000199j -0.000435+0.000067j -0.000074+0.000787j 0.000421-0.000268j -0.000448-0.000644j -0.000327+0.000158j 0.000862-0.000466j -0.000263-0.000154j

2 rows × 50 columns

We can extract data for a sweep and repeat using the following

[8]:
df = program.get_iq(stack_channels=True)
channel0_rf_4_05_data = df["(rf, 4.05 GHz)"]["0"][digs[0]]
print(channel0_rf_4_05_data)
(-0.00019140228972316692-0.003898978610783353j)

This is the data for channel (labels=[0], name=readoutreceiver), 4.05 GHz frequency and 0th repeat.

Also, using the results class we can get the data directly as a numpy array:

[9]:
program.results.get_iq()[digs[0]]
[9]:
array([[-1.91402290e-04-3.89897861e-03j,  7.70289665e-05-3.64209390e-03j,
        -4.30160632e-04-4.16754846e-03j,  1.20284667e-05-4.07141253e-03j,
         4.15836104e-05-3.65753864e-03j, -5.09600539e-04-4.21130410e-03j,
        -5.21141851e-05-4.17486558e-03j,  8.39615156e-05-3.70079315e-03j,
        -7.19177847e-04-4.32388577e-03j, -1.23657266e-04-4.37293974e-03j],
       [-1.66186861e-02+3.47513473e-03j,  1.13629142e-02+1.27458832e-02j,
         5.42432161e-03-1.59696063e-02j, -1.65766110e-02+3.34303555e-03j,
         1.12454880e-02+1.26769889e-02j,  5.42309369e-03-1.61666690e-02j,
        -1.67368608e-02+3.48733935e-03j,  1.14300947e-02+1.26495161e-02j,
         5.36066815e-03-1.61397442e-02j, -1.67338263e-02+3.46158123e-03j],
       [ 1.68788959e-01-3.11123121e-02j,  1.68708986e-01-3.11430939e-02j,
         1.68702229e-01-3.09507697e-02j,  1.68736191e-01-3.11747065e-02j,
         1.69111285e-01-3.11912603e-02j,  1.68753332e-01-3.08776137e-02j,
         1.68991335e-01-3.12165456e-02j,  1.68833065e-01-3.11595782e-02j,
         1.68866133e-01-3.11388791e-02j,  1.68684933e-01-3.10410386e-02j],
       [-1.06401974e-02+1.71068065e-03j,  3.68136418e-03-1.04932203e-02j,
         6.64781888e-03+8.31739556e-03j, -1.06826902e-02+1.85181806e-03j,
         3.51248675e-03-1.01825651e-02j,  6.88053196e-03+8.33262053e-03j,
        -1.06986977e-02+1.90767454e-03j,  3.67344503e-03-1.01811858e-02j,
         6.70268812e-03+8.42696774e-03j, -1.06093989e-02+1.99493533e-03j],
       [-3.60093368e-05+3.68011214e-04j,  3.18209252e-04-2.99719457e-04j,
         2.27231128e-07-1.23874754e-04j,  1.41947574e-04+1.79278434e-04j,
        -1.97609124e-06-1.44342951e-04j, -2.64965381e-04-2.68446401e-05j,
        -6.71314234e-05+1.52929887e-04j, -5.11562025e-05-2.20667209e-04j,
        -3.16850598e-04+6.45891776e-05j,  1.06048549e-04+1.30306766e-04j]])

Since this program sweeps the frequency of the RF waveform but keeps the frequency in the integration filter constant, we expect a peak in I/Q magnitude at the center frequency, which can be visually confirmed by plotting the data:

[10]:
program.plot_iq(channel_subplots=False)

In typical applications, we typically have larger sweep ranges and storing all the trace data and performing the I/Q demodulation in software adds timing overhead. Instead, we can perform the demodulation on hardware, and only I/Q data will be stored.

[11]:
new_frequencies = qcs.Array(
    "rfs", value=np.linspace(4.05e9, 4.25e9, num=20), dtype=float
)

program = make_program()
program.sweep(new_frequencies, frequency)

if run_on_hw:
    # initialize the backend pass
    # setting hw_demod=True enables hardware demodulation of trace data
    backend = qcs.HclBackend(channel_mapper=mapper, hw_demod=True)
    # the executor returns the program populated with results
    program = qcs.Executor(backend).execute(program)
    # (optional) export the data to an HDF5 file
    program.to_hdf5("program3.hdf5")

# we are loading a previously run program here for this example
program = qcs.load("program3.hdf5")

program.plot_iq(channel_subplots=False)

We can retrieve the classified data by calling the get_classified() method, which returns a dataframe that has the sweep values in its column header.

[12]:
program.get_classified()
[12]:
(((Channels(labels=[0], name=readoutreceiver, absolute_phase=False)))) ... (((Channels(labels=[1], name=readoutreceiver, absolute_phase=False))))
(rf, 4.05 GHz) ... (rf, 4.25 GHz)
0 1 2 3 4 5 6 7 8 9 ... 0 1 2 3 4 5 6 7 8 9
0 1 1 1 1 1 1 1 1 1 1 ... 1 0 1 1 0 1 0 0 1 1

1 rows × 400 columns

Plot classified data gives the count of occurences of 1’s and 0’s:

[13]:
program.plot_classified(channel_subplots=False)
[14]:
program = qcs.load("program4.hdf5")

Plot classified data as a 2d heat-map:

[15]:
program.plot_classified(avg=True, plot_type="2d")

Note

It is possible to perform variable sweeps both on hardware and software. To enable hardware sweeping, the program’s sweep() method needs to be called before the n_shots() method. This will execute both the sweep as well as the repetitions on hardware. Any other sweeps defined after those will be performed in software.

Nested sweeps

Sweeps can be nested by calling the sweep() method multiple times. For example, we can sweep the frequency and amplitude in our program from above:

[16]:
program = make_program()

# specify the frequency values to be swept over
frequencies = qcs.Array(
    "frequencies", value=[4.05e9, 4.1e9, 4.15e9, 4.2e9, 4.25e9], dtype=float
)

# specify the amplitude values to be swept over
amplitudes = qcs.Array("amplitude", value=[0.2, 0.4, 0.6, 0.8, 1], dtype=float)

program.sweep(frequencies, frequency)
program.sweep(amplitudes, amplitude)

print(program.repetitions.items)
[Sweep(amp=Array(name=amplitude, shape=(5,), dtype=float, unit=none)), Sweep(rf=Array(name=frequencies, shape=(5,), dtype=float, unit=none)), Repeat(10)]

We execute this program as before and load in the results:

[17]:
if run_on_hw:
    program = qcs.Executor(qcs.HclBackend(mapper, hw_demod=True)).apply(program)
    program.to_hdf5("program4.hdf5")

program = qcs.load("program4.hdf5")

program.get_iq(avg=True, stack_channels=True)
[17]:
(amp, 0.2) (amp, 0.4) ... (amp, 0.8) (amp, 1)
(rf, 4.05 GHz) (rf, 4.1 GHz) (rf, 4.15 GHz) (rf, 4.2 GHz) (rf, 4.25 GHz) (rf, 4.05 GHz) (rf, 4.1 GHz) (rf, 4.15 GHz) (rf, 4.2 GHz) (rf, 4.25 GHz) ... (rf, 4.05 GHz) (rf, 4.1 GHz) (rf, 4.15 GHz) (rf, 4.2 GHz) (rf, 4.25 GHz) (rf, 4.05 GHz) (rf, 4.1 GHz) (rf, 4.15 GHz) (rf, 4.2 GHz) (rf, 4.25 GHz)
(((Channels(labels=[0], name=readoutreceiver, absolute_phase=False)))) 0.000005+0.000003j -0.000331-0.000086j 0.037200-0.005492j -0.000265+0.000231j 0.000040+0.000042j 0.000073-0.000376j -0.000585-0.000206j 0.073275-0.011098j -0.000518+0.000330j -0.000065-0.000038j ... 0.000320-0.002474j -0.001304-0.000108j 0.140380-0.023565j -0.000949+0.000421j 0.000055+0.000037j -0.000223-0.004132j -0.001598+0.000318j 0.168689-0.031237j -0.001053+0.000202j -0.000034+0.000032j
(((Channels(labels=[1], name=readoutreceiver, absolute_phase=False)))) -0.000121-0.000029j 0.000231+0.000307j -0.035342-0.020230j 0.000409+0.000025j -0.000028-0.000068j -0.000321-0.000335j 0.000420+0.000575j -0.069978-0.039586j 0.000692+0.000120j -0.000064-0.000022j ... -0.002400-0.001459j 0.001120+0.000956j -0.135962-0.074133j 0.001217+0.000387j -0.000010+0.000088j -0.002938-0.001256j 0.001784+0.000920j -0.165303-0.087110j 0.000901+0.000468j -0.000023+0.000114j

2 rows × 25 columns

We can see that the dataframe now has an additional column index for the second sweep. When plotting the I/Q data, we can easily switch between sweep axes by specifying the plot_axis argument. The default, plot_axis=0 will plot the data over the first sweep (zeroth index of the dataframe columns). Note that this can also be used to plot the I/Q data as a function of shots, when the method is called with avg=False.

[18]:
# plot over amplitude for each frequency
program.plot_iq(channel_subplots=False)
[19]:
# plot over frequency for each amplitude
program.plot_iq(channel_subplots=False, plot_axis=1)

Simultaneous sweeps

Sweeps can also be performed simultaneously by passing the sweep values and targets as lists:

[20]:
program = make_program()

# specify the frequency values to be swept over
frequencies = qcs.Array(
    "frequencies", value=[4.05e9, 4.1e9, 4.15e9, 4.2e9, 4.25e9], dtype=float
)

# specify the amplitude values to be swept over
amplitudes = qcs.Array("amplitude", value=[0.2, 0.4, 0.6, 0.8, 1], dtype=float)

program.sweep([frequencies, amplitudes], [frequency, amplitude])
keysight-logo-svg
Program
Program
Duration 80 ns
Layers 1
Targets 4
Repetitions Sweep with 5 repetitions
Associations
rf Array(name=frequencies, shape=(5,), dtype=float, unit=none, value=[4.05 GHz, 4.1 GHz, 4.15 GHz, 4.2 GHz, 4.25 GHz])
amp Array(name=amplitude, shape=(5,), dtype=float, unit=none, value=[0.2, 0.4, 0.6, 0.8, 1])
Repeat with 10 repetitions
Layer #0
Layer #0
Duration 80 ns
readoutawg 0
RFWaveform on ('readoutawg', 0)

Parameters
Duration 80 ns
Amplitude 1
Frequency Scalar(name=rf, value=None, dtype=float, unit=Hz)
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
1
RFWaveform on ('readoutawg', 1)

Parameters
Duration 80 ns
Amplitude 1
Frequency Scalar(name=rf, value=None, dtype=float, unit=Hz)
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
readoutreceiver 0
Acquisition on ('readoutreceiver', 0)

Parameters
Duration 80 ns
Integration Filter
RFWaveform

Parameters
Duration 80 ns
Amplitude 1
Frequency 4.15 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
1
Acquisition on ('readoutreceiver', 1)

Parameters
Duration 80 ns
Integration Filter
RFWaveform

Parameters
Duration 80 ns
Amplitude 1
Frequency 4.15 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
[20]:
Program([Layer(Channels(labels=[0, 1], name=readoutawg, absolute_phase=False)=[RFWaveform(duration=Scalar(name=_implicit, value=8e-08, dtype=float, unit=s), envelope={GaussianEnvelope(4.0): Scalar(name=_implicit, value=1.0, dtype=float, unit=none)}, rf_frequency=Scalar(name=rf, value=None, dtype=float, unit=Hz), instantaneous_phase={GaussianEnvelope(4.0): Scalar(name=_implicit, value=0.0, dtype=float, unit=rad)}, post_phase=Scalar(name=_implicit, value=0.0, dtype=float, unit=rad))], Channels(labels=[0, 1], name=readoutreceiver, absolute_phase=False)=[Acquisition(IntegrationFilter(RFWaveform(duration=Scalar(name=_implicit, value=8e-08, dtype=float, unit=s), envelope={GaussianEnvelope(4.0): Scalar(name=_implicit, value=1.0, dtype=float, unit=none)}, rf_frequency=Scalar(name=_implicit, value=4150000000.0, dtype=float, unit=Hz), instantaneous_phase={GaussianEnvelope(4.0): Scalar(name=_implicit, value=0.0, dtype=float, unit=rad)}, post_phase=Scalar(name=_implicit, value=0.0, dtype=float, unit=rad))))])])

We execute this program as before and load in the results:

[21]:
if run_on_hw:
    program = qcs.Executor(qcs.HclBackend(mapper, hw_demod=True)).apply(program)
    program.to_hdf5("program5.hdf5")

program = qcs.load("program5.hdf5")

program.get_iq(avg=True, stack_channels=True)
[21]:
(rf, 4.05 GHz), (amp, 0.2) (rf, 4.1 GHz), (amp, 0.4) (rf, 4.15 GHz), (amp, 0.6) (rf, 4.2 GHz), (amp, 0.8) (rf, 4.25 GHz), (amp, 1)
(((Channels(labels=[0], name=readoutreceiver, absolute_phase=False)))) 0.000013-0.000013j -0.000613-0.000192j 0.107115-0.017246j -0.000988+0.000395j 0.000078-0.000041j
(((Channels(labels=[1], name=readoutreceiver, absolute_phase=False)))) -0.000073-0.000024j 0.000425+0.000622j -0.103357-0.057106j 0.001224+0.000277j -0.000104-0.000037j
[22]:
# plot the results
program.plot_iq(channel_subplots=False)

Hardware Sweeps

So far, all examples shown were constructed as a Software sweep. This means that after each point in the sweep, the hardware stops and waits for the software to compile the next steps and load all the information into the FPGAs. For large sweeps or for nested sweeps, this can be a slow process.

Hardware Sweep provides a way to pre-load every step of the sweep into the hardware, so it can execute the whole sweep without interruptions.

To enable this, we only need to change the order of the sweeps: The repetitions (n_shots()) needs to be placed after the sweeps like in the example below.

[23]:
frequency = qcs.Scalar("rf", dtype=float)

"""Creates a simple program with a single RF waveform"""
program = qcs.Program()
# define the waveform using this frequency variable & add it to the program
gauss = qcs.RFWaveform(80e-9, qcs.GaussianEnvelope(num_sigma=4), 1, frequency)
program.add_waveform(gauss, awgs)

# define an integration filter using the same RF waveform but with fixed frequency
int_filter = qcs.RFWaveform(80e-9, qcs.GaussianEnvelope(num_sigma=4), 1, 4.15e9)
program.add_acquisition(int_filter, digs)

# specify the frequency values to be swept over
frequencies = qcs.Array(
    "frequencies", value=[4.05e9, 4.1e9, 4.15e9, 4.2e9, 4.25e9], dtype=float
)

# add the sweep, targeting the `frequency` variable
program.sweep(frequencies, frequency)

# specify the number of shots
program.n_shots(10)

program.draw()
keysight-logo-svg
Program
Program
Duration 80 ns
Layers 1
Targets 4
Repetitions Repeat with 10 repetitions
Sweep with 5 repetitions
Associations
rf Array(name=frequencies, shape=(5,), dtype=float, unit=none, value=[4.05 GHz, 4.1 GHz, 4.15 GHz, 4.2 GHz, 4.25 GHz])
Layer #0
Layer #0
Duration 80 ns
readoutawg 0
RFWaveform on ('readoutawg', 0)

Parameters
Duration 80 ns
Amplitude 1
Frequency Scalar(name=rf, value=None, dtype=float, unit=Hz)
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
1
RFWaveform on ('readoutawg', 1)

Parameters
Duration 80 ns
Amplitude 1
Frequency Scalar(name=rf, value=None, dtype=float, unit=Hz)
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
readoutreceiver 0
Acquisition on ('readoutreceiver', 0)

Parameters
Duration 80 ns
Integration Filter
RFWaveform

Parameters
Duration 80 ns
Amplitude 1
Frequency 4.15 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
1
Acquisition on ('readoutreceiver', 1)

Parameters
Duration 80 ns
Integration Filter
RFWaveform

Parameters
Duration 80 ns
Amplitude 1
Frequency 4.15 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad

This constructs the program to have the sweep of frequencies as the inner-loop, and then the repeat (i.e. n_shots) as the outer loop. Both the sweep and the repeat are executed as a hardware loop, so the entire program is executed without interruption.

It is possible to perform multi-dimensional sweeps. More than one sweep can be executed as a software sweep or a hardware sweep.

The inner-most Repeat on a program’s repetition structure serves as the boundary between hardware and software sweeps. To check which operations will be performed in hardware-time, the repetitions property of the program can be inspected

[24]:
program.repetitions.hw_items
[24]:
[Repeat(10),
 Sweep(rf=Array(name=frequencies, shape=(5,), dtype=float, unit=none))]

Limitations

Sweepable parameters

Sweepable Parameters

Instrument

Operation

Software sweep variables

Hardware sweep variables

M5300AWG and M5301AWG

RFWaveform

Support for sweeping amplitude, frequency, instantenous_phase, post_phase, and duration

Support for sweeping amplitude, frequency, instantenous_phase, and post_phase The duration variable is only supported for the hold duration on flat-top waveforms.

M5300AWG and M5301AWG

DCWaveform

Support for sweeping amplitude and duration

Support for sweeping amplitude. The duration variable is only supported for the hold duration on flat-top waveforms

M5200Digitizer

Acquisition

Support for sweeping all waveform variables on the Acqusition’s IntegrationFilter. Support for sweeping the Acquisition’s Classifier reference points. Support for changing the acquisition duration is not supported when hw_demod==False

No support

M5300AWG, M5301AWG, and M5200Digitizer

Delay

Support for sweeping duration

Support for sweeping duration

M5300AWG

RFAWGChannelSettings

Support for sweeping delay and lo_frequency

No support

M5301AWG

BasebandAWGChannelSettings

Support for sweeping delay and offset

No support

M5200AWG

DigitizerChannelSettings

Support for sweeping delay and range

No support

M5201AWG

DownconverterChannelSettings

Support for sweeping lo_frequency

No support

Other limitations per sweep type

Limitation

Software sweep mode

Hardware sweep mode

Supported channel settings

Both absolute_phase=True and absolute_phase=False

Most parameters can only be swept on channels where absolute_phase=False. The exception is Delay duration.

Demodulation mode

Both hw_demod=False and hw_demod=True

Only hw_demod=True

Maximum number of parameter swept on a single waveform.

No limit

Three parameters

Maximum number of nested sweeping dimensions.

No limit

Eight dimensions

Sweep length limitation

No limit

The sum of all sweep lenghts must be less than 24,576 points (more details below)

Minimum value for a delay duration sweep

0 ns

13.333 ns

Hardware sweep size limitations

During compilation, each swept variable is translated into a separate array stored in FPGA memory. Each instrument channel supports up to eight FPGA arrays. All eight arrarys share the same memory space that can hold 24,576 sweep points.

When designing an experiment, it can be complicated to determine if all the sweeps fit inside the hardware due to multiple optimization steps. This section will go over some common scenarios. This won’t be an exhaustive list.

Example 1: Simple 1D sweep

In the following example, amplitude will use one FPGA array that contains 10,000 data points. The total number of points is 10,000 which is less than the 24,576 maximum.

[25]:
amplitude = qcs.Scalar("amp", dtype=float)

program = qcs.Program()

# define the waveform using this frequency variable & add it to the program
gauss = qcs.RFWaveform(80e-9, qcs.GaussianEnvelope(num_sigma=4), amplitude, 4e9)
program.add_waveform(gauss, awgs)

# define the values for the sweep
amplitudes = qcs.Array("amplitudes", value=np.linspace(0.1, 0.2, 10000), dtype=float)

# sweep the program
program.sweep(amplitudes, amplitude)
program.n_shots(10)
keysight-logo-svg
Program
Program
Duration 80 ns
Layers 1
Targets 2
Repetitions Repeat with 10 repetitions
Sweep with 10000 repetitions
Associations
amp Array(name=amplitudes, shape=(10000,), dtype=float, unit=none, value=[0.1, 0.1, 0.1, 0.1, 0.1, 0.1001, 0.1001, 0.1001, 0.1001, 0.1001, ... ])
Layer #0
Layer #0
Duration 80 ns
readoutawg 0
RFWaveform on ('readoutawg', 0)

Parameters
Duration 80 ns
Amplitude Scalar(name=amp, value=None, dtype=float, unit=none)
Frequency 4 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
1
RFWaveform on ('readoutawg', 1)

Parameters
Duration 80 ns
Amplitude Scalar(name=amp, value=None, dtype=float, unit=none)
Frequency 4 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
[25]:
Program([Layer([RFWaveform(duration=Scalar(name=_implicit, value=8e-08, dtype=float, unit=s), envelope={GaussianEnvelope(4.0): Scalar(name=amp, value=None, dtype=float, unit=none)}, rf_frequency=Scalar(name=_implicit, value=4000000000.0, dtype=float, unit=Hz), instantaneous_phase={GaussianEnvelope(4.0): Scalar(name=_implicit, value=0.0, dtype=float, unit=rad)}, post_phase=Scalar(name=_implicit, value=0.0, dtype=float, unit=rad))])])

Example 2: 2D Hardware Sweep

In the following example, amplitude and frequency will each take one FPGA array and each will use 10,000 data points. The total number of points is 20,000 which is less than the 24,576 maximum.

[26]:
frequency = qcs.Scalar("rf", dtype=float)
amplitude = qcs.Scalar("amp", dtype=float)

program = qcs.Program()

# define the waveform using this frequency variable & add it to the program
gauss = qcs.RFWaveform(80e-9, qcs.GaussianEnvelope(num_sigma=4), amplitude, frequency)
program.add_waveform(gauss, awgs)

# define the values for each sweep
frequencies = qcs.Array(
    "frequencies", value=np.linspace(4e9, 4.1e9, 10000), dtype=float
)
amplitudes = qcs.Array("amplitudes", value=np.linspace(0.1, 0.2, 10000), dtype=float)

# sweep the program
program.sweep(frequencies, frequency)
program.sweep(amplitudes, amplitude)
program.n_shots(10)
keysight-logo-svg
Program
Program
Duration 80 ns
Layers 1
Targets 2
Repetitions Repeat with 10 repetitions
Sweep with 10000 repetitions
Associations
amp Array(name=amplitudes, shape=(10000,), dtype=float, unit=none, value=[0.1, 0.1, 0.1, 0.1, 0.1, 0.1001, 0.1001, 0.1001, 0.1001, 0.1001, ... ])
Sweep with 10000 repetitions
Associations
rf Array(name=frequencies, shape=(10000,), dtype=float, unit=none, value=[4 GHz, 4.00001 GHz, 4.00002 GHz, 4.00003 GHz, 4.00004 GHz, 4.00005 GHz, 4.00006 GHz, 4.00007 GHz, 4.00008 GHz, 4.00009 GHz, ... ])
Layer #0
Layer #0
Duration 80 ns
readoutawg 0
RFWaveform on ('readoutawg', 0)

Parameters
Duration 80 ns
Amplitude Scalar(name=amp, value=None, dtype=float, unit=none)
Frequency Scalar(name=rf, value=None, dtype=float, unit=Hz)
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
1
RFWaveform on ('readoutawg', 1)

Parameters
Duration 80 ns
Amplitude Scalar(name=amp, value=None, dtype=float, unit=none)
Frequency Scalar(name=rf, value=None, dtype=float, unit=Hz)
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
[26]:
Program([Layer([RFWaveform(duration=Scalar(name=_implicit, value=8e-08, dtype=float, unit=s), envelope={GaussianEnvelope(4.0): Scalar(name=amp, value=None, dtype=float, unit=none)}, rf_frequency=Scalar(name=rf, value=None, dtype=float, unit=Hz), instantaneous_phase={GaussianEnvelope(4.0): Scalar(name=_implicit, value=0.0, dtype=float, unit=rad)}, post_phase=Scalar(name=_implicit, value=0.0, dtype=float, unit=rad))])])

Example 3: 1D Sweep with Array reuse

In the following example, the program plays three identical waveforms, all of which use the same sweep values. The amplitudes values for the waveforms will be reused. This results in one FPGA array with 10,000 sweep points. If the FPGA array was not reused, the sweep points would have exceeded the 24,576 maximum. Note: FPGA array reuse is only possible if there is at least 13.333ns delay between each waveform.

[27]:
amplitude1 = qcs.Scalar("amp", dtype=float)

program = qcs.Program()

# define the waveform using this frequency variable & add it to the program
gauss = qcs.RFWaveform(80e-9, qcs.GaussianEnvelope(num_sigma=4), amplitude, 4e9)
program.add_waveform(gauss, awgs)
program.add_waveform(gauss, awgs, pre_delay=20e-9)
program.add_waveform(gauss, awgs, pre_delay=20e-9)
# define the values for the sweep
amplitudes = qcs.Array("amplitudes", value=np.linspace(0.1, 0.2, 10000), dtype=float)

# sweep the program
program.sweep(amplitudes, amplitude)
program.n_shots(10)
keysight-logo-svg
Program
Program
Duration 280 ns
Layers 1
Targets 2
Repetitions Repeat with 10 repetitions
Sweep with 10000 repetitions
Associations
amp Array(name=amplitudes, shape=(10000,), dtype=float, unit=none, value=[0.1, 0.1, 0.1, 0.1, 0.1, 0.1001, 0.1001, 0.1001, 0.1001, 0.1001, ... ])
Layer #0
Layer #0
Duration 280 ns
readoutawg 0
RFWaveform on ('readoutawg', 0)

Parameters
Duration 80 ns
Amplitude Scalar(name=amp, value=None, dtype=float, unit=none)
Frequency 4 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
Delay on ('readoutawg', 0)

Parameters
Duration 20 ns
RFWaveform on ('readoutawg', 0)

Parameters
Duration 80 ns
Amplitude Scalar(name=amp, value=None, dtype=float, unit=none)
Frequency 4 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
Delay on ('readoutawg', 0)

Parameters
Duration 20 ns
RFWaveform on ('readoutawg', 0)

Parameters
Duration 80 ns
Amplitude Scalar(name=amp, value=None, dtype=float, unit=none)
Frequency 4 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
1
RFWaveform on ('readoutawg', 1)

Parameters
Duration 80 ns
Amplitude Scalar(name=amp, value=None, dtype=float, unit=none)
Frequency 4 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
Delay on ('readoutawg', 1)

Parameters
Duration 20 ns
RFWaveform on ('readoutawg', 1)

Parameters
Duration 80 ns
Amplitude Scalar(name=amp, value=None, dtype=float, unit=none)
Frequency 4 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
Delay on ('readoutawg', 1)

Parameters
Duration 20 ns
RFWaveform on ('readoutawg', 1)

Parameters
Duration 80 ns
Amplitude Scalar(name=amp, value=None, dtype=float, unit=none)
Frequency 4 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
[27]:
Program([Layer([RFWaveform(duration=Scalar(name=_implicit, value=8e-08, dtype=float, unit=s), envelope={GaussianEnvelope(4.0): Scalar(name=amp, value=None, dtype=float, unit=none)}, rf_frequency=Scalar(name=_implicit, value=4000000000.0, dtype=float, unit=Hz), instantaneous_phase={GaussianEnvelope(4.0): Scalar(name=_implicit, value=0.0, dtype=float, unit=rad)}, post_phase=Scalar(name=_implicit, value=0.0, dtype=float, unit=rad)), Delay(Scalar(name=_implicit, value=2e-08, dtype=float, unit=s)), RFWaveform(duration=Scalar(name=_implicit, value=8e-08, dtype=float, unit=s), envelope={GaussianEnvelope(4.0): Scalar(name=amp, value=None, dtype=float, unit=none)}, rf_frequency=Scalar(name=_implicit, value=4000000000.0, dtype=float, unit=Hz), instantaneous_phase={GaussianEnvelope(4.0): Scalar(name=_implicit, value=0.0, dtype=float, unit=rad)}, post_phase=Scalar(name=_implicit, value=0.0, dtype=float, unit=rad)), Delay(Scalar(name=_implicit, value=2e-08, dtype=float, unit=s)), RFWaveform(duration=Scalar(name=_implicit, value=8e-08, dtype=float, unit=s), envelope={GaussianEnvelope(4.0): Scalar(name=amp, value=None, dtype=float, unit=none)}, rf_frequency=Scalar(name=_implicit, value=4000000000.0, dtype=float, unit=Hz), instantaneous_phase={GaussianEnvelope(4.0): Scalar(name=_implicit, value=0.0, dtype=float, unit=rad)}, post_phase=Scalar(name=_implicit, value=0.0, dtype=float, unit=rad))])])

Example 4: 1D Simultaneous Sweep

In the following example, the program plays two waveforms, each with its own set of amplitudes being swept. The amplitudes will be compiled into one interleaved array with a total of 20,000 data points

[28]:
amplitude1 = qcs.Scalar("amp1", dtype=float)
amplitude2 = qcs.Scalar("amp2", dtype=float)

program = qcs.Program()

# define the waveform using this frequency variable & add it to the program
gauss1 = qcs.RFWaveform(80e-9, qcs.GaussianEnvelope(num_sigma=4), amplitude1, 4e9)
gauss2 = qcs.RFWaveform(80e-9, qcs.GaussianEnvelope(num_sigma=4), amplitude2, 4e9)
program.add_waveform(gauss1, awgs)
program.add_waveform(gauss2, awgs)

# define the values for the sweep
amplitudes1 = qcs.Array("amplitudes1", value=np.linspace(0.1, 0.2, 10000), dtype=float)
amplitudes2 = qcs.Array("amplitudes2", value=np.linspace(0.3, 0.4, 10000), dtype=float)

# sweep the program
program.sweep([amplitudes1, amplitudes2], [amplitude1, amplitude2])
program.n_shots(10)
keysight-logo-svg
Program
Program
Duration 160 ns
Layers 1
Targets 2
Repetitions Repeat with 10 repetitions
Sweep with 10000 repetitions
Associations
amp1 Array(name=amplitudes1, shape=(10000,), dtype=float, unit=none, value=[0.1, 0.1, 0.1, 0.1, 0.1, 0.1001, 0.1001, 0.1001, 0.1001, 0.1001, ... ])
amp2 Array(name=amplitudes2, shape=(10000,), dtype=float, unit=none, value=[0.3, 0.3, 0.3, 0.3, 0.3, 0.3001, 0.3001, 0.3001, 0.3001, 0.3001, ... ])
Layer #0
Layer #0
Duration 160 ns
readoutawg 0
RFWaveform on ('readoutawg', 0)

Parameters
Duration 80 ns
Amplitude Scalar(name=amp1, value=None, dtype=float, unit=none)
Frequency 4 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
RFWaveform on ('readoutawg', 0)

Parameters
Duration 80 ns
Amplitude Scalar(name=amp2, value=None, dtype=float, unit=none)
Frequency 4 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
1
RFWaveform on ('readoutawg', 1)

Parameters
Duration 80 ns
Amplitude Scalar(name=amp1, value=None, dtype=float, unit=none)
Frequency 4 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
RFWaveform on ('readoutawg', 1)

Parameters
Duration 80 ns
Amplitude Scalar(name=amp2, value=None, dtype=float, unit=none)
Frequency 4 GHz
Envelope GaussianEnvelope(4.0)
Instantaneous Phase 0 rad
Post-phase 0 rad
[28]:
Program([Layer([RFWaveform(duration=Scalar(name=_implicit, value=8e-08, dtype=float, unit=s), envelope={GaussianEnvelope(4.0): Scalar(name=amp1, value=None, dtype=float, unit=none)}, rf_frequency=Scalar(name=_implicit, value=4000000000.0, dtype=float, unit=Hz), instantaneous_phase={GaussianEnvelope(4.0): Scalar(name=_implicit, value=0.0, dtype=float, unit=rad)}, post_phase=Scalar(name=_implicit, value=0.0, dtype=float, unit=rad)), RFWaveform(duration=Scalar(name=_implicit, value=8e-08, dtype=float, unit=s), envelope={GaussianEnvelope(4.0): Scalar(name=amp2, value=None, dtype=float, unit=none)}, rf_frequency=Scalar(name=_implicit, value=4000000000.0, dtype=float, unit=Hz), instantaneous_phase={GaussianEnvelope(4.0): Scalar(name=_implicit, value=0.0, dtype=float, unit=rad)}, post_phase=Scalar(name=_implicit, value=0.0, dtype=float, unit=rad))])])

Download

Download this file as Jupyter notebook: sweep_examples.ipynb.

On this page