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
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:
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.
Here we discuss the main parts of the code in presto-measure/rabi_time.py.
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)
control_pulse = pls.setup_long_drive( self.control_port, group=0, duration=self.control_duration_arr, 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.
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
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
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