Displacement calibration¶
Using pulsed
mode, we calibrate the amplitude of the memory drive (in FS units) and find the linear conversion factor to transform them into \(\alpha\) displacement units. We do a 2D sweep of qubit selective \(\pi\) pulse frequency and memory drive amplitude. We first displace the cavity with a pulse at the cavity frequency with a \(\sin^2\) envelope. Next we apply the qubit-control pulse. This pulse is a selective \(\pi\) pulse with a \(\sin^2\) envelope. The readout pulse is at the frequency of the readout resonator and has a square envelope. By probing the state of the qubit at different frequencies, we directly get the probablility that the cavity is in any of the Fock states \(|n\rangle\) when the frequency of the selective qubit pulse matches the \(f_q-n\chi\). Here \(f_q\) is the frequency of the qubit when memory is in the vacuum state (n=0), \(n\) is the Fock state number and \(\chi\) is the despersive shift between the memory and the qubit.
The class for performing the displacement calibration experiment is available at presto-measure/displacement_calibration.py. Here, we run the experiment and observe characteristic peaks that appear. We then have a look at the main parts of the code.
You can create a new experiment and run it on your Presto. Be sure to change the parameters of DisplacementCalibration
to match your experiment and change presto_address
to match the IP address of your Presto:
from displacement_calibration import DisplacementCalibration
import numpy as np
experiment = DisplacementCalibration(
readout_freq=6.2e9,
control_freq=4.2e9,
control_df_arr=np.linspace(-19e6, 1e6, 201),
memory_freq=3.5e9,
readout_amp=0.1,
control_amp=0.5,
memory_amp_arr=np.linspace(0, 0.5, 6),
readout_duration=2.5e-6,
control_duration=2000e-9,
memory_duration=50e-9,
sample_duration=2.5e-6,
readout_port=1,
control_port=2,
memory_port=5,
sample_port=1,
wait_delay=5e-3,
readout_sample_delay=0e-9,
num_averages=1,
)
presto_address = "192.168.88.65" # your Presto IP address
save_filename = experiment.run(presto_address)
Or you can also load older data:
experiment = DisplacementCalibration.load("../data/displacement_calibration_20240130_105117.h5")
In either case, we analyze the data to get a nice plot and fit the data.
experiment.analyze()
The peaks are separated by the dispersive coupling between the qubit and the memory \(\chi/2\pi\).
Note
Depending on the Hamiltonian representation the peaks are either separated by \(\chi/2\pi\) or \(\chi/\pi\). We adopt the former Hamiltonian representation.
Note
Depending on the strenght of the interaction between the qubit and the memory, the frequency of the peaks might have a correction to the linear dispersive shift \(\chi\). We do not fit for this correction in this example.
The area under the peaks reflects the probability that the memory is in Fock state \(|n\rangle\). Since the displacement \(D(\alpha)\) creates a coherent state \(|\alpha\rangle\) in the memory, the peaks reflect Poissonian distribution. Probability to find Fock state \(|n\rangle\) in a coherent state \(|\alpha\rangle\) is given by \(P(n)=e^{-|\alpha|^2}\frac{|\alpha|^{2n}}{n!}\). We fit the Poissonian distribution to find the amplitude of the displacemnt \(|\alpha|\) for each amplitude of the displacement pulse in FS units.
The complex-valued readout signal is rotated using utils.rotate_opt()
so that all the information about the qubit state is in the I quadrature.
Code explanation¶
Here we discuss the main part of the code of the DisplacementCalibration
class: the definition of the experiment
sequence. The full source is available at presto-measure/displacement_calibration.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 the qubit tutorial. There we describe the code more
pedagogically and in more detail.
Displacement calibration is a 2D sweep of qubit drive frequency and memory drive amplitude. The frequency sweep is realized with a for
loop and by calling the method select_frequency()
. The second dimmension of the sweep, namely the amplitude sweep, is implemented by using the next_scale()
after the for loop:
T = 0.0 # s, start at time zero ...
for i in range(len(self.control_freq_arr)):
pls.output_pulse(T, memory_pulse) # displace memory
T += self.memory_duration
pls.select_frequency(T, i, self.control_port, group=0)
pls.output_pulse(T, control_pulse) # pi pulse
T += self.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.memory_port)
T += self.wait_delay
We first displace the cavity. Then we output qubit-control \(\pi\) pulse that maps the population of Fock state \(|n\rangle\) in the cavity onto qubit population. Finally, we perform the readout.
run()
executes the experiment sequence. We set repeat_count=len(self.memory_amp_arr)
since we want to probe that many amplitudes of the memory drive.
pls.run(period=T, repeat_count=len(self.memory_amp_arr), num_averages=self.num_averages)