Two-tone spectroscopy of the qubit¶
Two-tone spectroscopy using lockin
mode. 2D sweep of amplitude and frequency of the qubit
drive called pump or control drive, with fixed frequency and amplitude of the readout resonator
drive, also called the probe drive.
The source code for the class TwoTonePower
that performs the two-tone spectroscopy experiment is
available at presto-measure/two_tone_power.py. Here, we first run the
experiment and extract a rough estimate of the qubit frequency, and we then have a deeper look at
what the code actually does.
You can create a new experiment and run it on your Presto. Be sure to change the parameters of
TwoTonePower
to match your experiment and change presto_address
to match the IP address of your
Presto.
from two_tone_power import TwoTonePower
import numpy as np
experiment = TwoTonePower(
readout_freq=6.2e9,
control_freq_center=4.2e9,
control_freq_span=200e6,
df=200e3,
readout_amp=0.2,
control_amp_arr=np.linspace(0.1, 1, 11),
readout_port=1,
control_port=4,
input_port=1,
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 = TwoTonePower.load("data/two_tone_power_20220422_063812.h5")
In either case, we analyze the data to get a nice plot:
experiment.analyze()
Code explanation¶
Let’s look at the main parts of the code in presto-measure/two_tone_power.py. Compared to what we saw in Power sweep of resonator spectroscopy, we output one more tone at one more port.
We still tune()
the integration bandwidth before outputting the tones:
_, self.df = lck.tune(0.0, self.df)
lck.set_df(self.df)
We need to configure three digital mixers: one for the down-conversion at the input, and two for
the up-conversion at the output. We end up with two calls to Hardware.configure_mixer()
. For
the pump tone on the qubit, we start with the first frequency in the array.
lck.hardware.configure_mixer(
freq=self.readout_freq,
in_ports=self.input_port,
out_ports=self.readout_port,
)
lck.hardware.configure_mixer(
freq=self.control_freq_arr[0],
out_ports=self.control_port,
)
We create separate OutputGroup
s for the drives to the resonator (readout, ogr
) and
qubit (control, ogc
). We also create an InputGroup
ig
. Like in the previous chapters
of the tutorial, we opt for using zero IF so the spectroscopy tones will coincide with the
local-oscillator frequency set in the respective mixers. We output and measure one frequency per
input/output group.
ogr = lck.add_output_group(self.readout_port, nr_freq=1)
ogr.set_frequencies(0.0)
ogr.set_amplitudes(self.readout_amp)
ogc = lck.add_output_group(self.control_port, nr_freq=1)
ogc.set_frequencies(0.0)
ogc.set_amplitudes(self.control_amp_arr[0])
ig = lck.add_input_group(self.input_port, nr_freq=1)
ig.set_frequencies(0.0)
We apply the settings and, at this point, Presto will start outputting both spectroscopy tones to the resonator and to the qubit.
lck.apply_settings()
Just like in the previous chapter, the core of the measurement
consists of two nested for
loops. The two loops update the settings for the qubit drive, while
the resonator drive is kept constant. The outer loop sweeps the qubit amplitude. In the inner loop,
we update the qubit frequency, wait for the settings to apply and the resonator response to
stabilize, and finally acquire measurement data with get_pixels()
. Notice that we do
not change the resonator drive tone within the loops since we already started outputting it before
the start of the first for loop.
for jj, control_amp in enumerate(self.control_amp_arr):
ogc.set_amplitudes(control_amp)
lck.apply_settings()
for ii, control_freq in enumerate(self.control_freq_arr):
lck.hardware.configure_mixer(control_freq, out_ports=self.control_port)
lck.apply_settings()
_d = lck.get_pixels(self.num_skip + self.num_averages, quiet=True)
data_i = _d[self.input_port][1][:, 0]
data_q = _d[self.input_port][2][:, 0]
data = data_i.real + 1j * data_q.real # using zero IF
self.resp_arr[jj, ii] = np.mean(data[-self.num_averages :])
Finally, after the experiment is complete, we set the amplitude of both outputs to zero.
ogr.set_amplitudes(0.0)
ogc.set_amplitudes(0.0)
lck.apply_settings()