Pulsed two-tone spectroscopy

Two-tone spectroscopy using pulsed mode. 1D sweep of qubit drive frequency with fixed amplitude, and fixed resonator drive frequency and amplitude. This is the pulsed version of the Resonator spectroscopy chapter in this tutorial on qubit calibration.

pulsed two tone pulse sequence

The source code for the class TwoTonePulsed that performs this experiment is available at presto-measure/two_tone_pulsed.py. Here, we first run the experiment and extract an 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 TwoTonePulsed to match your experiment and change presto_address to match the IP address of your Presto.

from two_tone_pulsed import TwoTonePulsed

experiment = TwoTonePulsed(
    readout_freq=6.2e9,
    control_freq_center=4.2e9,
    control_freq_span=250e6,
    control_freq_nr=101,
    readout_amp=0.2,
    control_amp=0.1,
    readout_duration=2.5e-6,
    control_duration=2.5e-6,
    sample_duration=2.5e-6,
    readout_port=1,
    control_port=4,
    sample_port=1,
    wait_delay=100e-6,
    readout_sample_delay=0,
    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 = TwoTonePulsed.load("data/two_tone_pulsed_20220331_164030.h5")

In either case, we analyze the data to get a nice plot:

experiment.analyze()
../_images/two_tone_pulsed_light.svg ../_images/two_tone_pulsed_dark.svg

Code explanation

Here we discuss the main parts of the code in the TwoTonePulsed class, you can see the full source code at presto-measure/two_tone_pulsed.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 want to sweep the frequency of the qubit-control pulse. We do that by changing the intermediate frequency (IF) and keeping the NCO frequency constant, just like we already did in Ramsey fringes. Note that this is the opposite to what we did in the continuous-wave Resonator spectroscopy chapter of this tutorial.

pls.hardware.configure_mixer(
    self.readout_freq,  # <-- output frequency (zero IF)
    in_ports=self.sample_port,
    out_ports=self.readout_port,
)
pls.hardware.configure_mixer(
    control_nco,  # <-- up-conversion frequency (nonzero IF)
    out_ports=self.control_port,
)

pls.setup_freq_lut(
    self.control_port, group=0,
    frequencies=control_if_arr,
    phases=np.full_like(control_if_arr, 0.0),
    phases_q=np.full_like(control_if_arr, -np.pi / 2),  # upper sideband
)

And we make sure to use the IF generator with the qubit-control pulse by setting envelope=True:

control_pulse = pls.setup_template(
    self.control_port, group=0,
    template=control_envelope + 1j * control_envelope,
    envelope=True,  # <-- multiply by IF generator
)

The pulse sequence is rather simple:

T = 0.0  # s, start at time zero ...

pls.reset_phase(T, self.control_port)
pls.output_pulse(T, control_pulse)  # Control pulse
T += self.control_duration

pls.output_pulse(T, readout_pulse)  # Readout pulse
pls.store(T + self.readout_sample_delay)  # Sampling window
T += self.readout_duration

pls.next_frequency(T, self.control_port)  # Move to next control frequency
T += self.wait_delay  # Wait for decay

We readout the resonator right after outputting the qubit-control pulse. Before waiting for the qubit to decay, we call next_frequency() to proceed to the next control frequency for the next iteration of the experiment.


We finish up by running the experiment, setting repeat_count to the number of frequencies in the qubit-control sweep:

pls.run(period=T, repeat_count=self.control_freq_nr, num_averages=self.num_averages)