Qubit spectroscopies

In this section we are going to present how to run with qibocal some qubit spectroscopy experiments.

Qubit Spectroscopy

To measure the resonance frequency of the qubit it is possible to perform a qubit spectroscopy experiment. After having obtained an initial guess for the readout amplitude and the readout frequency through a Resonator punchout this experiment aims at extracting the frequency of the qubit.

In this protocol the qubit is probed by sending a drive pulse at variable frequency \(w\) before measuring. When \(w\) is close to the transition frequency \(w_{01}\) some of the population will move to the excited state. If the drive pulse is long enough it will be generated a maximally mixed state with \(\rho \propto I\) [2, 9].

When the frequency bandwidth measured exceeds the IF bandwidth range (+/- 300 MHz), the routine automatically splits the sweep into multiple batches, adjusting the LO frequency accordingly for each batch.

The sweep can be specified directly with frequency or by setting freq_width and freq_step relative to the configured qubit drive frequency. The drive pulse amplitude and duration can be set with drive_amplitude and drive_duration. “””

Parameters

class qibocal.protocols.qubit_spectroscopies.qubit_spectroscopy.QubitSpectroscopyParameters(frequency: tuple[float, float, float] | tuple[Literal['linspace'], float, float, int] | tuple[Literal['window'], float, float, float] | tuple[Literal['linwindow'], float, float, int] | tuple[Literal['center'], float, float] | tuple[Literal['lincenter'], float, int] | tuple[Literal['asym'], tuple[float, float], float] | tuple[Literal['linasym'], tuple[float, float], int] | None = None, freq_width: int | None = None, freq_step: int | None = None, drive_duration: int = 4000, drive_amplitude: float = 1.0)[source]

QubitSpectroscopy runcard inputs.

frequency: tuple[float, float, float] | tuple[Literal['linspace'], float, float, int] | tuple[Literal['window'], float, float, float] | tuple[Literal['linwindow'], float, float, int] | tuple[Literal['center'], float, float] | tuple[Literal['lincenter'], float, int] | tuple[Literal['asym'], tuple[float, float], float] | tuple[Literal['linasym'], tuple[float, float], int] | None = None

Drive frequency [Hz] range for sweep.

freq_width: int | None = None

Width [Hz] for frequency sweep relative to the qubit frequency.

freq_step: int | None = None

Frequency [Hz] step for sweep.

drive_duration: int = 4000

Drive pulse duration [ns]. Same for all qubits.

drive_amplitude: float = 1.0

Drive pulse amplitude (optional). Same for all qubits.

frequency_range(center: float = 0.0) → tuple[float, float, float][source]
f0(center: float = 0.0) → float[source]

Return the center frequency of the sweep.

nshots: int

Number of executions on hardware.

relaxation_time: float

Wait time for the qubit to decohere back to the ground state.

How to execute a qubit spectroscopy experiment

A possible runcard to launch a qubit spectroscopy experiment could be the following:

- id: qubit spectroscopy 01
  operation: qubit_spectroscopy
  parameters:
    drive_amplitude: 0.01 # drive power
    drive_duration: 4000 # ns
    freq_width: 20_000_000
    freq_step: 1_000_000
    nshots: 1024
    relaxation_time: 20_000

The report shows the signal magnitude and phase, with a Lorentzian fit performed on the signal magnitude to extract the qubit frequency. The image below shows the fitted magnitude trace:

../../_images/qubit_spec.png

To extract the qubit frequency, a Lorentzian fit is performed on the signal magnitude.

Requirements

Qubit power spectroscopy

Qubit power spectroscopy sweeps the drive frequency and amplitude to inspect how spectral peaks change with drive power. Increasing the amplitude can make higher-energy transitions visible, and can help distinguish a qubit transition from other spectral features.

The protocol does not perform a fit. The PCA_VARIANCE_THRESHOLD constant, currently set to 0.85, determines which plot is shown. If the first principal component explains more than 85% of the total IQ-data variance, the plot uses that component; otherwise, it shows signal magnitude and phase as separate heatmaps.

When the explained-variance ratio is above the threshold, the plot also shows the IQ data and principal axes:

IQ plane and power spectroscopy heatmap using the first principal component

At or below the threshold, magnitude and phase are plotted separately:

Signal magnitude and phase heatmaps for qubit power spectroscopy

Parameters

class qibocal.protocols.qubit_spectroscopies.qubit_power_spectroscopy.QubitPowerSpectroscopyParameters(frequency: tuple[float, float, float] | tuple[Literal['linspace'], float, float, int] | tuple[Literal['window'], float, float, float] | tuple[Literal['linwindow'], float, float, int] | tuple[Literal['center'], float, float] | tuple[Literal['lincenter'], float, int] | tuple[Literal['asym'], tuple[float, float], float] | tuple[Literal['linasym'], tuple[float, float], int] | None = None, freq_width: int | None = None, freq_step: int | None = None, amplitude: tuple[float, float, float] | tuple[Literal['linspace'], float, float, int] | tuple[Literal['window'], float, float, float] | tuple[Literal['linwindow'], float, float, int] | tuple[Literal['center'], float, float] | tuple[Literal['lincenter'], float, int] | tuple[Literal['asym'], tuple[float, float], float] | tuple[Literal['linasym'], tuple[float, float], int] | None = None, min_amp: float | None = None, max_amp: float | None = None, step_amp: float | None = None, duration: int = 4000)[source]

QubitPowerSpectroscopy runcard inputs.

frequency: tuple[float, float, float] | tuple[Literal['linspace'], float, float, int] | tuple[Literal['window'], float, float, float] | tuple[Literal['linwindow'], float, float, int] | tuple[Literal['center'], float, float] | tuple[Literal['lincenter'], float, int] | tuple[Literal['asym'], tuple[float, float], float] | tuple[Literal['linasym'], tuple[float, float], int] | None = None

Frequencies for the sweep [Hz].

freq_width: int | None = None

Width for frequency sweep relative to the drive frequency [Hz].

freq_step: int | None = None

Frequency step for sweep [Hz].

amplitude: tuple[float, float, float] | tuple[Literal['linspace'], float, float, int] | tuple[Literal['window'], float, float, float] | tuple[Literal['linwindow'], float, float, int] | tuple[Literal['center'], float, float] | tuple[Literal['lincenter'], float, int] | tuple[Literal['asym'], tuple[float, float], float] | tuple[Literal['linasym'], tuple[float, float], int] | None = None

Amplitudes for the sweep [a.u.].

min_amp: float | None = None

Minimum amplitude.

max_amp: float | None = None

Maximum amplitude.

step_amp: float | None = None

Step amplitude.

duration: int = 4000

Drive duration.

nshots: int

Number of executions on hardware.

relaxation_time: float

Wait time for the qubit to decohere back to the ground state.

frequency_range(q: Annotated[int | str, FieldInfo(annotation=NoneType, required=True, metadata=[_PydanticGeneralMetadata(union_mode='left_to_right')])], platform: CalibrationPlatform) → tuple[float, float, float][source]
amplitude_range() → tuple[float, float, float][source]

How to execute a qubit power spectroscopy experiment

A possible runcard to launch a qubit power spectroscopy experiment could be the following:

- id: qubit power spectroscopy
  operation: qubit_power_spectroscopy
  parameters:
    freq_width: 20_000_000
    freq_step: 1_000_000
    min_amp: 0.001
    max_amp: 0.05
    step_amp: 0.001
    duration: 4000 # ns
    nshots: 1024
    relaxation_time: 20_000

The frequency range is centered on the configured drive frequency. Alternatively, the frequency and amplitude parameters can each specify a range directly.

Qubit spectroscopy for higher excited states

Through a qubit spectroscopy experiment it is possible to target also the transition frequencies towards higher energy level other than the first excited state.

To visualize these secondary excitations it is necessary to provide a considerable amount of drive power, which might be outside the limit of the experimental setup.

Another way to address the higher levels is to first excite the qubit to state \(\ket{1}\) followed by the sequence previously presented for the qubit spectroscopy. In this way it is possible to induce a transition between \(\ket{1}\leftrightarrow\ket{2}\).

The protocol applies an \(RX\) pulse to prepare \(\ket{1}\), then sweeps a spectroscopy tone on the qubit’s 1-to-2 drive channel before readout. By default, the sweep is centered on the frequency configured for that channel. Set frequency to provide an explicit sweep range, or use freq_width and freq_step for a range centered on the configured frequency. As with the ground-state experiment, drive_duration and drive_amplitude control the spectroscopy pulse.

Such frequency \(\omega_{12}\) should be below \(\omega_{01}\) by around \(200 - 300\) MHz for flux tunable transmons. From \(\omega_{12}\) and \(\omega_{01}\) it is possible to compute the anharmonicity \(\alpha\) as [15]:

\[\alpha = \omega_{12} - \omega_{01}\]

In the literature the energy levels can be expressed as \(\ket{g}, \ket{e}, \ket{f}\), to address the ground state, the excited state and the first excited state above the excited state. For this reason the experiments has been labelled qubit_spectroscopy_ef.

Parameters

class qibocal.protocols.qubit_spectroscopies.qubit_spectroscopy_ef.QubitSpectroscopyEFParameters(frequency: tuple[float, float, float] | tuple[Literal['linspace'], float, float, int] | tuple[Literal['window'], float, float, float] | tuple[Literal['linwindow'], float, float, int] | tuple[Literal['center'], float, float] | tuple[Literal['lincenter'], float, int] | tuple[Literal['asym'], tuple[float, float], float] | tuple[Literal['linasym'], tuple[float, float], int] | None = None, freq_width: int | None = None, freq_step: int | None = None, drive_duration: int = 4000, drive_amplitude: float = 1.0)[source]

QubitSpectroscopyEF runcard inputs.

drive_amplitude: float = 1.0

Drive pulse amplitude (optional). Same for all qubits.

drive_duration: int = 4000

Drive pulse duration [ns]. Same for all qubits.

f0(center: float = 0.0) → float

Return the center frequency of the sweep.

freq_step: int | None = None

Frequency [Hz] step for sweep.

freq_width: int | None = None

Width [Hz] for frequency sweep relative to the qubit frequency.

frequency: tuple[float, float, float] | tuple[Literal['linspace'], float, float, int] | tuple[Literal['window'], float, float, float] | tuple[Literal['linwindow'], float, float, int] | tuple[Literal['center'], float, float] | tuple[Literal['lincenter'], float, int] | tuple[Literal['asym'], tuple[float, float], float] | tuple[Literal['linasym'], tuple[float, float], int] | None = None

Drive frequency [Hz] range for sweep.

frequency_range(center: float = 0.0) → tuple[float, float, float]
nshots: int

Number of executions on hardware.

relaxation_time: float

Wait time for the qubit to decohere back to the ground state.

How to execute a qubit spectroscopy EF experiment

A possible runcard to launch a qubit spectroscopy experiment could be the following:

- id: qubit spectroscopy 12
  operation: qubit_spectroscopy_ef
  parameters:
    drive_amplitude: 0.01 # drive power
    drive_duration: 4000 # ns
    freq_width: 20_000_000
    freq_step: 1_000_000
    nshots: 1024
    relaxation_time: 20_000

The report displays the same signal magnitude and phase panels as the standard qubit spectroscopy. A Lorentzian fit to the magnitude trace extracts \(\omega_{12}\), and the report additionally shows the fitted frequency and the resulting anharmonicity.

After fitting, the anharmonicity is calculated as \(\alpha = \omega_{12} - \omega_{01}\). When updates are enabled, both the calibrated 1-to-2 transition frequency and the frequency of the RX12 pulse are updated.

Requirements