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. displacement calibration pulse sequence

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(
    control_df_arr=np.linspace(-19e6, 1e6, 201),
    memory_amp_arr=np.linspace(0, 0.5, 6),

presto_address = ""  # 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.


The peaks are separated by the dispersive coupling between the qubit and the memory \(\chi/2\pi\).


Depending on the Hamiltonian representation the peaks are either separated by \(\chi/2\pi\) or \(\chi/\pi\). We adopt the former Hamiltonian representation.


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.


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)