Resonator Spectroscopy

As a first step, we want to find the resonance frequency of the readout resonator of our qubit.

When creating an experiment, we first define some experimental parameters. In this case this is the sweep range of the frequency, the number of sweep points and the number of averages per point. We also define some sweep parameters to be used in the experiment. Here, we are using a linear sweep of the resonator frequency, in the range specified by the experiment parameters above. Finally, we specify the waveform envelope of a readout pulse which is used to excite the resonator. Here, this is a simple square pulse, with length and amplitude as defined in the qubit parameters dictionary.

# frequency range of spectroscopy scan - around centre frequency
spec_range = 200e6
# how many frequency points to measure
spec_num = 50

# how many averages per point: 2^n_average
n_average = 10

# define sweep parameters
freq_sweep_q0 = LinearSweepParameter(uid="res_freq", start=qubit_parameters['ro_freq_q0'] - spec_range / 2, stop=qubit_parameters['ro_freq_q0'] + spec_range / 2, count=spec_num)
freq_sweep_q1 = LinearSweepParameter(uid="res_freq", start=qubit_parameters['ro_freq_q1'] - spec_range / 2, stop=qubit_parameters['ro_freq_q1'] + spec_range / 2, count=spec_num)

# spectroscopy excitation pulse
readout_pulse_spec = pulse_library.const(
    uid="readout_pulse_spec", length=qubit_parameters['ro_len_spec'], amplitude=qubit_parameters['ro_amp_spec']

Next, we create the actual experiment. In this example, we define an experiment without any direct reference to the qubit parameters, which we can re-use for the two different qubits connected to our setup. To this end, we define a python function that takes a sweep parameter as argument, so that we can specify the different frequency sweeps for the two qubits. Within the function, we define an experiment object with two experimental signal lines: a resonator drive line and a measurement return from the fridge.

Then we define the experimental pulse sequence: In an outer, near-time loop we iterate the oscillator frequency of the resonator excitation pulse, and for each sweep point we average in real-time over multiple single shots. For each single shot, we first play an excitation pulse on the resonator drive line. The pulse itself has been defined above. Note that we only defined the waveform envelope here, the modulation at the intermediate frequency is done automatically, in this case by the hardware oscillator on the UHFQA itself. After the excitation pulse, we trigger the signal acquisition on the acquisition line, here in spectroscopy mode, since we are doing resonator spectroscopy. Finally, we play a delay on the excitation line, effectively inserting a holdoff time between consecutive readouts. This is required to give the UHFQA enough time for data processing so as not to produce an overflow. Good practice is a delay of a minimum 1 μs between consecutive shots, as we do here.

def res_spectroscopy(freq_sweep):
    # Create resonator spectroscopy experiment - uses only readout drive and signal acquisition
    exp_spec = Experiment(
        uid="Resonator Spectroscopy",

    ## define experimental sequence
    # outer loop - vary drive frequency
    with exp_spec.sweep(uid="res_freq", parameter=freq_sweep):
        # inner loop - average for each frequency - UHFQA in spectroscopy mode
        with exp_spec.acquire_loop_rt(
                count=pow(2, n_average),
            # readout pulse and data acquisition
            with exp_spec.section(uid="spectroscopy"):
                # resonator excitation pulse
                # resonator signal readout
                exp_spec.acquire(signal="acquire", handle="res_spec", length=qubit_parameters['ro_len_spec'])
            with exp_spec.section(uid="delay"):
                # holdoff time after signal acquisition - min 1us required for data processing
                exp_spec.delay(signal="measure", time=1e-6)

    return exp_spec

Finally, we define a function that takes a sweep parameter as an argument and returns a calibration object, where the sweep parameter is assigned to the hardware oscillator modulating the readout resonator drive line in the UHFQA. In this way, the internal digital oscillator of the UHFQA is used for the pulse modulation. We also define two signal maps, which are dictionaries that contain the mapping of experimental signal lines, used in the definition of the experiment, to logical signal lines, defined in the device setup. This enables the re-use of an experiment for multiple qubits, simply by switching out the mapping of experimental signals to match the various logical signals connected to individual qubits.

# calibration of readout oscillator
def res_spec_calib(freq_sweep):
        exp_calibration = Calibration()

        exp_calibration["measure"] = SignalCalibration(
                oscillator = Oscillator(
        return exp_calibration

# signal maps
res_spec_map_q0 = {
    "measure": "/logical_signal_groups/q0/measure_line",
    "acquire": "/logical_signal_groups/q0/acquire_line",
res_spec_map_q1 = {
    "measure": "/logical_signal_groups/q1/measure_line",
    "acquire": "/logical_signal_groups/q1/acquire_line",

The resonator spectroscopy experiment for the first qubit is then run on the open session with

exp_spec = res_spectroscopy(freq_sweep_q0)

# set signal calibration and signal map for experiment

# run the experiment
my_results =

Here, we first generate the experiment object, with the correct sweep parameter for the first qubit. Next, we apply the calibration, which sets the sweep parameter to the hardware oscillator on the UHFQA and then set the signal map, identifying which logical signals to use for the first qubit. The final command executes the experiment on the session.

After the successful execution, the acquired measurement results can be obtained from the session:

spec_res = my_results.get_data('res_spec')

The specified string corresponds to the handle given as part of the acquire command in the experiment.

Figure 1 shows example data as can be expected from a resonator spectroscopy experiment. We plot the absolute value of the transmission (not normalized) as a function of the oscillator frequency. The resonance frequency of the resonator shows as a dip in the transmission with a Lorentzian line shape, whose width is given by the photon decay rate of the oscillator. The red trace in Figure 1 shows a fit to the expected functional form, which can be used to extract the resonator parameters, like resonance frequency, and internal and external coupling factors. In the example notebook, we only use the position of the resonance to update the readout frequency parameter of the qubit_parameters dictionary.

Resonator spectroscopy
Figure 1. Example data from resonator spectroscopy.

Having determined the readout resonator frequency, next we use this knowledge to find the resonance frequency of the qubit.