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
IntegrationFilter
to 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()
Program
Program
|
||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
||||||||||||||||||
|
|
RFWaveform on ('readoutawg', 0)
Parameters
|
||||||||||||||||
|
RFWaveform on ('readoutawg', 1)
Parameters
|
|||||||||||||||||
|
|
Acquisition on ('readoutreceiver', 0)
Parameters
|
||||||||||||||||
|
Acquisition on ('readoutreceiver', 1)
Parameters
|
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 then_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])
Program
Program
|
||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
||||||||||||||||||
|
|
RFWaveform on ('readoutawg', 0)
Parameters
|
||||||||||||||||
|
RFWaveform on ('readoutawg', 1)
Parameters
|
|||||||||||||||||
|
|
Acquisition on ('readoutreceiver', 0)
Parameters
|
||||||||||||||||
|
Acquisition on ('readoutreceiver', 1)
Parameters
|
[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()
Program
Program
|
||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
||||||||||||||||||
|
|
RFWaveform on ('readoutawg', 0)
Parameters
|
||||||||||||||||
|
RFWaveform on ('readoutawg', 1)
Parameters
|
|||||||||||||||||
|
|
Acquisition on ('readoutreceiver', 0)
Parameters
|
||||||||||||||||
|
Acquisition on ('readoutreceiver', 1)
Parameters
|
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
Instrument |
Operation |
Software sweep variables |
Hardware sweep variables |
---|---|---|---|
M5300AWG and M5301AWG |
Support for sweeping |
Support for sweeping |
|
M5300AWG and M5301AWG |
Support for sweeping |
Support for sweeping |
|
M5200Digitizer |
Support for sweeping all waveform variables on the
Acqusition’s |
No support |
|
M5300AWG, M5301AWG, and M5200Digitizer |
Support for sweeping |
Support for sweeping |
|
M5300AWG |
Support for sweeping |
No support |
|
M5301AWG |
Support for sweeping |
No support |
|
M5200AWG |
Support for sweeping |
No support |
|
M5201AWG |
Support for sweeping |
No support |
Limitation |
Software sweep mode |
Hardware sweep mode |
---|---|---|
Supported channel settings |
Both |
Most parameters can only be swept on channels where |
Demodulation mode |
Both |
Only |
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)
Program
Program
|
||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
||||||||||||||
|
|
RFWaveform on ('readoutawg', 0)
Parameters
|
||||||||||||
|
RFWaveform on ('readoutawg', 1)
Parameters
|
[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)
Program
Program
|
||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
||||||||||||||
|
|
RFWaveform on ('readoutawg', 0)
Parameters
|
||||||||||||
|
RFWaveform on ('readoutawg', 1)
Parameters
|
[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)
Program
Program
|
||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
||||||||||||||||||||||||||||||||||||||||||||||
|
|
RFWaveform on ('readoutawg', 0)
Parameters
|
Delay on ('readoutawg', 0)
Parameters
|
RFWaveform on ('readoutawg', 0)
Parameters
|
Delay on ('readoutawg', 0)
Parameters
|
RFWaveform on ('readoutawg', 0)
Parameters
|
||||||||||||||||||||||||||||||||||||||||
|
RFWaveform on ('readoutawg', 1)
Parameters
|
Delay on ('readoutawg', 1)
Parameters
|
RFWaveform on ('readoutawg', 1)
Parameters
|
Delay on ('readoutawg', 1)
Parameters
|
RFWaveform on ('readoutawg', 1)
Parameters
|
[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)
Program
Program
|
|||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Layer #0
Layer #0
|
|||||||||||||||||||||||||||
|
|
RFWaveform on ('readoutawg', 0)
Parameters
|
RFWaveform on ('readoutawg', 0)
Parameters
|
||||||||||||||||||||||||
|
RFWaveform on ('readoutawg', 1)
Parameters
|
RFWaveform on ('readoutawg', 1)
Parameters
|
[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.