Rabi time¶
Using pulsed
mode, we measure Rabi oscillations by driving the qubit with a square pulse of
variable duration. We also sweep the amplitude of the qubit pulse to get a 2D sweep. We fit the
amplitude-dependent Rabi rate.
The full source code for this experiment is available at presto-measure/rabi_time.py. Here, we first run the Rabi experiment and analyze the data in order to fit the amplitude-dependent Rabi rate, and then we have a more detailed look at the most important parts of the code.
You can create a new experiment and run it on your Presto. Be sure to change the parameters of
RabiTime
to match your experiment and change presto_address
to match the IP address of your
Presto:
from rabi_time import RabiTime
import numpy as np
experiment = RabiTime(
readout_freq=6.2e9,
control_freq=4.2e9,
readout_amp=0.1,
control_amp_arr=np.linspace(0.001, 0.01, 21),
readout_duration=2.1e-6,
control_duration_arr=np.linspace(100e-9, 10e-6, 100),
sample_duration=2.5e-6,
readout_port=1,
control_port=4,
sample_port=1,
wait_delay=100e-6,
readout_sample_delay=0e-9,
num_averages=100,
)
presto_address = "192.168.88.65" # your Presto IP address
save_filename = experiment.run(presto_address)
Or you can also load older data:
experiment = RabiTime.load("data/rabi_time_20230328_181549.h5")
In either case, we analyze the data to get a nice plot:
experiment.analyze()
The frequency of Rabi oscillations (left panel) is fitted for each drive amplitude individually. The linear dependence of the Rabi rate and drive amplitude is shown in the right panel.
Code explanation¶
Here we discuss the main parts of the code in presto-measure/rabi_time.py.
Note
If this is your first measurement in pulsed
mode, you might want to first have a look at
the Rabi amplitude chapter in this tutorial. There we describe the code more
pedagogically and in more detail.
We program all the control amplitudes we want to sweep in the scale look-up table (LUT), just like we did in Rabi amplitude.
pls.setup_scale_lut(self.readout_port, group=0, scales=self.readout_amp)
pls.setup_scale_lut(self.control_port, group=0, scales=self.control_amp_arr)
We want to change the duration of the qubit-control pulse for each iteration of the experiment. The
easiest way to achieve that is to create a LongDrive
using setup_long_drive()
:
control_pulse = pls.setup_long_drive(
self.control_port, group=0,
duration=self.control_duration_arr[0],
amplitude=1.0 + 1j,
envelope=False,
)
We initialize the pulse duration to the first value in control_duration_arr
, we will then update
the duration when we program the experimental sequence. A LongDrive
supports smooth rise and
fall, but in this case we’ll just use a square pulse shape.
The core of the measurement is the definition of the experimental sequence:
T = 0.0 # s, start at time zero ...
for control_duration in self.control_duration_arr:
control_pulse.set_total_duration(control_duration) # Set control pulse length
pls.output_pulse(T, control_pulse)
T += control_duration
pls.output_pulse(T, readout_pulse) # Readout
pls.store(T + self.readout_sample_delay)
T += self.readout_duration
T += self.wait_delay # Wait for decay
pls.next_scale(T, self.control_port, group=0)
T += self.wait_delay
We use a for
loop to repeat the measurement for each duration of the qubit-control pulse. We
update the length of the qubit-control pulse with set_total_duration()
, and then
we schedule its output.
As usual, the resonator-readout pulse and the data acquisition are scheduled right after the control pulse. After that, we wait for the qubit to decay back to the ground state.
When the for
loop is exhausted, we call next_scale()
to change the amplitude of
the qubit-control pulse to the next entry in the scale LUT.
To finally execute the measurement, we call run()
:
nr_amps = len(self.control_amp_arr)
pls.run(period=T, repeat_count=nr_amps, num_averages=self.num_averages)
A single sequence already contains len(control_duration_arr)
measurements due to the for
loop,
each with a different duration for the qubit-control pulse. This long sequence is then repeated
nr_amps
times due to the repeat_count
parameter, each with a different amplitude of the
qubit-control pulse. Finally, the whole 2D sweep is repeated num_averages
times to perform
interleaved averaging.