See also
A Jupyter notebook version of this tutorial can be downloaded here
.
Fixed Frequency Transmon#
This notebook shows a typical tuneup sequence for fixed frequency transmon qubits. The notebook aims to be as compact as possible, for more details, please refer to the respective experiments.
[1]:
from __future__ import annotations
from functools import partial
from pathlib import Path
from typing import Literal
import matplotlib.pyplot as plt
import numpy as np
import rich # noqa:F401
from qcodes.parameters import ManualParameter
from simulated_data import get_simulated_res_spec_data, get_simulated_tof_data
from sklearn.metrics import ConfusionMatrixDisplay
import quantify_core.data.handling as dh
from quantify_core.analysis import RabiAnalysis
from quantify_core.analysis.base_analysis import Basic2DAnalysis
from quantify_core.analysis.readout_calibration_analysis import ReadoutCalibrationAnalysis
from quantify_core.analysis.single_qubit_timedomain import RamseyAnalysis, T1Analysis
from quantify_core.analysis.spectroscopy_analysis import (
QubitSpectroscopyAnalysis,
ResonatorSpectroscopyAnalysis,
)
from quantify_core.analysis.time_of_flight_analysis import TimeOfFlightAnalysis
from quantify_core.data.handling import load_dataset
from quantify_scheduler import QuantumDevice, Schedule, ScheduleGettable
from quantify_scheduler.backends.qblox import constants
from quantify_scheduler.device_under_test.transmon_element import BasicTransmonElement
from quantify_scheduler.enums import BinMode
from quantify_scheduler.math import closest_number_ceil
from quantify_scheduler.operations import X90, IdlePulse, Measure, Reset, Rxy, X
from quantify_scheduler.operations.acquisition_library import SSBIntegrationComplex
from quantify_scheduler.operations.pulse_library import (
SetClockFrequency,
SquarePulse,
)
from quantify_scheduler.resources import ClockResource
from utils import display_dict, initialize_hardware, run_schedule, show_connectivity # noqa:F401
[2]:
hw_config_path = "configs/tuning_transmon_coupled_pair_hardware_config.json"
device_path = "devices/transmon_device_2q.json"
[3]:
# Enter your own dataset directory here!
dh.set_datadir(dh.default_datadir())
Data will be saved in:
/root/quantify-data
[4]:
quantum_device = QuantumDevice("transmon_device")
quantum_device.hardware_config.load_from_json_file(hw_config_path)
qubit = BasicTransmonElement("q0")
qubit.measure.acq_channel(0)
quantum_device.add_element(qubit)
cluster_ip = None
meas_ctrl, inst_coord, cluster = initialize_hardware(quantum_device, ip=cluster_ip)
Time of flight measurement#
Here show how to measure time of flight for your system. This is useful to calibrate the acquisition delay for subsequent experiments.
Schedule definition#
[5]:
def tof_trace_schedule(
qubit_name: str,
repetitions: int = 1,
) -> Schedule:
schedule = Schedule("Trace measurement schedule", repetitions=repetitions)
schedule.add(Measure(qubit_name, acq_protocol="Trace"))
return schedule
Measuring time of flight with trace acquisition#
[6]:
def set_readout_attenuation_hardware_config(attenuation_dB: int):
hwcfg = quantum_device.hardware_config()
output_att = hwcfg["hardware_options"]["output_att"]
output_att[f"{qubit.ports.readout()}-{qubit.name}.ro"] = attenuation_dB
quantum_device.hardware_config(hwcfg)
set_readout_attenuation_hardware_config(0)
qubit.measure.pulse_duration(300e-9)
qubit.measure.integration_time(1e-6)
qubit.measure.pulse_amp(0.1)
qubit.measure.acq_delay(4e-9)
qubit.clock_freqs.readout(7.2e9)
[7]:
tof_t = ManualParameter(name="tof_t", unit="ns", label="Trace acquisition sample")
tof_t.batched = True
tof_t.batch_size = round(qubit.measure.integration_time() * constants.SAMPLING_RATE)
tof_sched_kwargs = dict(
qubit_name=qubit.name,
)
# set gettable
gettable = ScheduleGettable(
quantum_device,
schedule_function=tof_trace_schedule,
schedule_kwargs=tof_sched_kwargs,
real_imag=False,
batched=True,
)
# set measurement control
meas_ctrl.gettables(gettable)
[8]:
tof_t_setpoints = np.arange(tof_t.batch_size)
meas_ctrl.settables(tof_t)
meas_ctrl.setpoints(tof_t_setpoints)
if cluster_ip is None:
def dummy_gettable():
gettable.initialize()
return get_simulated_tof_data()
gettable.get = dummy_gettable
tof_ds = dh.to_gridded_dataset(meas_ctrl.run("Time of flight measurement " + qubit.name))
Starting batched measurement...
Iterative settable(s) [outer loop(s)]:
--- (None) ---
Batched settable(s):
tof_t
Batch size limit: 1000
Analysis#
[9]:
# use fake data in case the cluster is a dummy
tof_analysis = TimeOfFlightAnalysis(tuid=dh.get_latest_tuid())
tof_analysis.run(playback_delay=149e-9).display_figs_mpl()

[10]:
fit_results = tof_analysis.quantities_of_interest
nco_prop_delay = fit_results["nco_prop_delay"]
measured_tof = fit_results["tof"]
qubit.measure.acq_delay(
closest_number_ceil(
measured_tof * constants.SAMPLING_RATE, constants.MIN_TIME_BETWEEN_OPERATIONS
)
/ constants.SAMPLING_RATE
)
[11]:
quantum_device.to_json_file("devices/")
[11]:
'devices/transmon_device_2025-03-12_18-03-50_UTC.json'
Resonator Spectroscopy#
Here we go through resonator discovery and punchout spectroscopy for identifying the resonator and measuring it’s resonant frequency.
[12]:
def set_readout_attenuation_hardware_config(attenuation_dB: int):
hwcfg = quantum_device.hardware_config()
output_att = hwcfg["hardware_options"]["output_att"]
output_att[f"{qubit.ports.readout()}-{qubit.name}.ro"] = attenuation_dB
quantum_device.hardware_config(hwcfg)
set_readout_attenuation_hardware_config(32)
qubit.measure.acq_delay(452e-9)
Resonator Spectroscopy#
[13]:
# This schedule can also be imported rom quantify_scheduler.schedules
def heterodyne_spec_sched_nco(
pulse_amp: float,
pulse_duration: float,
frequencies: np.ndarray,
acquisition_delay: float,
integration_time: float,
port: str,
clock: str,
init_duration: float = 10e-6,
repetitions: int = 1,
port_out: str | None = None,
) -> Schedule:
"""
Generate a batched schedule for performing fast heterodyne spectroscopy.
Using the :class:`~quantify_scheduler.operations.pulse_library.SetClockFrequency`
operation for doing an NCO sweep.
Parameters
----------
pulse_amp
Amplitude of the spectroscopy pulse in Volt.
pulse_duration
Duration of the spectroscopy pulse in seconds.
frequencies
Sample frequencies for the spectroscopy pulse in Hertz.
acquisition_delay
Start of the data acquisition with respect to the start of the spectroscopy
pulse in seconds.
integration_time
Integration time of the data acquisition in seconds.
port
Location on the device where the acquisition is performed.
clock
Reference clock used to track the spectroscopy frequency.
init_duration
The relaxation time or dead time.
repetitions
The amount of times the Schedule will be repeated.
port_out
Output port on the device where the pulse should be applied. If `None`, then use
the same as `port`.
"""
sched = Schedule("Fast heterodyne spectroscopy (NCO sweep)", repetitions)
sched.add_resource(ClockResource(name=clock, freq=frequencies.flat[0]))
if port_out is None:
port_out = port
for acq_idx, freq in enumerate(frequencies):
sched.add(IdlePulse(duration=init_duration), label=f"buffer {acq_idx}")
sched.add(
SetClockFrequency(clock=clock, clock_freq_new=freq),
label=f"set_freq {acq_idx} ({clock} {freq:e} Hz)",
)
spec_pulse = sched.add(
SquarePulse(
duration=pulse_duration,
amp=pulse_amp,
port=port_out,
clock=clock,
),
label=f"spec_pulse {acq_idx})",
)
sched.add(
SSBIntegrationComplex(
duration=integration_time,
port=port,
clock=clock,
acq_index=acq_idx,
acq_channel=0,
bin_mode=BinMode.AVERAGE,
),
ref_op=spec_pulse,
ref_pt="start",
rel_time=acquisition_delay,
label=f"acquisition {acq_idx})",
)
return sched
[14]:
freqs = ManualParameter(name="freq", unit="Hz", label="Frequency")
freqs.batched = True
freqs.batch_size = 100
center = 7.364e9
frequency_setpoints = np.linspace(center - 10e6, center + 10e6, 300)
spec_sched_kwargs = dict(
pulse_amp=1 / 6,
pulse_duration=2e-6,
frequencies=freqs,
acquisition_delay=196e-9,
integration_time=2e-6,
init_duration=10e-6,
port=qubit.ports.readout(),
clock=qubit.name + ".ro",
)
gettable = ScheduleGettable(
quantum_device,
schedule_function=heterodyne_spec_sched_nco,
schedule_kwargs=spec_sched_kwargs,
real_imag=False,
batched=True,
)
def simulated_get():
gettable.old_get()
return get_simulated_res_spec_data(f=frequency_setpoints, fr=center)
if cluster_ip is None:
gettable.old_get = gettable.get
gettable.get = simulated_get
meas_ctrl.gettables(gettable)
[15]:
quantum_device.cfg_sched_repetitions(400)
meas_ctrl.settables(freqs)
meas_ctrl.setpoints(frequency_setpoints)
rs_ds = meas_ctrl.run("resonator spectroscopy")
rs_ds
Starting batched measurement...
Iterative settable(s) [outer loop(s)]:
--- (None) ---
Batched settable(s):
freq
Batch size limit: 100
/builds/qblox/packages/software/qblox_instruments_docs/.venv/lib/python3.9/site-packages/quantify_scheduler/backends/circuit_to_device.py:414: RuntimeWarning: Clock 'q0.ro' has conflicting frequency definitions: 7354000000.0 Hz in the schedule and 7200000000.0 Hz in the device config. The clock is set to '7354000000.0'. Ensure the schedule clock resource matches the device config clock frequency or set the clock frequency in the device config to np.NaN to omit this warning.
warnings.warn(
[15]:
<xarray.Dataset> Size: 7kB Dimensions: (dim_0: 300) Coordinates: x0 (dim_0) float64 2kB 7.354e+09 7.354e+09 ... 7.374e+09 7.374e+09 Dimensions without coordinates: dim_0 Data variables: y0 (dim_0) float64 2kB 13.06 13.2 13.38 13.28 ... 15.17 14.74 14.69 y1 (dim_0) float64 2kB -134.4 -129.5 -124.9 ... 6.948 12.39 17.69 Attributes: tuid: 20250312-180350-353-12cc7d name: resonator spectroscopy grid_2d: False grid_2d_uniformly_spaced: False 1d_2_settables_uniformly_spaced: False
[16]:
rs_analysis = ResonatorSpectroscopyAnalysis(
dataset=rs_ds, settings_overwrite={"mpl_transparent_background": False}
)
rs_analysis.run().display_figs_mpl()



[17]:
qubit.clock_freqs.readout(rs_analysis.quantities_of_interest["fr"].nominal_value)
Resonator punchout#
[18]:
def resonator_punchout_schedule(
qubit, # noqa: ANN001
freqs: np.array,
ro_pulse_amps: np.array,
repetitions: int = 1,
) -> Schedule:
"""Schedule to sweep the resonator frequency."""
sched = Schedule("schedule", repetitions=repetitions)
index = 0
freqs, ro_pulse_amps = np.unique(freqs), np.unique(ro_pulse_amps)
for freq in freqs:
for amp in ro_pulse_amps:
sched.add(Measure(qubit.name, acq_index=index, freq=freq, pulse_amp=amp))
sched.add(IdlePulse(8e-9))
index += 1
return sched
freqs = ManualParameter(name="freq", unit="Hz", label="Frequency")
freqs.batched = True
ro_pulse_amps = ManualParameter(name="ro_pulse_amp", unit="", label="Readout pulse amplitude")
ro_pulse_amps.batched = False
spec_sched_kwargs = dict(
qubit=qubit,
freqs=freqs,
ro_pulse_amps=ro_pulse_amps,
)
gettable = ScheduleGettable(
quantum_device,
schedule_function=resonator_punchout_schedule,
schedule_kwargs=spec_sched_kwargs,
real_imag=False,
batched=True,
)
meas_ctrl.gettables(gettable)
[19]:
quantum_device.cfg_sched_repetitions(80)
center = qubit.clock_freqs.readout()
frequency_setpoints = np.linspace(center - 7e6, center + 3e6, 400)
amplitude_setpoints = np.linspace(0.01, 0.5, 10)
meas_ctrl.settables([freqs, ro_pulse_amps])
meas_ctrl.setpoints_grid((frequency_setpoints, amplitude_setpoints))
punchout_ds = meas_ctrl.run("resonator punchout")
punchout_ds
Starting batched measurement...
Iterative settable(s) [outer loop(s)]:
ro_pulse_amp
Batched settable(s):
freq
Batch size limit: 1024
[19]:
<xarray.Dataset> Size: 128kB Dimensions: (dim_0: 4000) Coordinates: x0 (dim_0) float64 32kB 7.357e+09 7.357e+09 ... 7.367e+09 7.367e+09 x1 (dim_0) float64 32kB 0.01 0.01 0.01 0.01 0.01 ... 0.5 0.5 0.5 0.5 Dimensions without coordinates: dim_0 Data variables: y0 (dim_0) float64 32kB nan nan nan nan nan ... nan nan nan nan nan y1 (dim_0) float64 32kB nan nan nan nan nan ... nan nan nan nan nan Attributes: tuid: 20250312-180353-831-1abfae name: resonator punchout grid_2d: True grid_2d_uniformly_spaced: True 1d_2_settables_uniformly_spaced: False xlen: 400 ylen: 10
[20]:
if cluster_ip is None:
dh.set_datadir(Path.cwd() / "data")
punchout_ds = load_dataset("20250204-234241-297-0b4720")
b2a = Basic2DAnalysis(dataset=punchout_ds)
b2a.run().display_figs_mpl()




[21]:
quantum_device.to_json_file("devices/")
[21]:
'devices/transmon_device_2025-03-12_18-04-03_UTC.json'
Qubit Spectroscopy#
Here we will carry out qubit spectroscopy on a single transmon in order to find the
Qubit spectroscopy#
[22]:
def two_tone_spec_sched_nco(
qubit, # noqa: ANN001
spec_pulse_frequencies: np.array,
repetitions: int = 1,
) -> Schedule:
"""
Generate a batched schedule for performing fast two-tone spectroscopy.
Using the X gate to perform the frequency sweep on the qubit.
Parameters
----------
qubit
qubit that should be used.
spec_pulse_frequencies
Sample frequencies for the spectroscopy pulse in Hertz.
repetitions
The amount of times the Schedule will be repeated.
"""
sched = Schedule("two-tone", repetitions)
sched.add_resource(ClockResource(name=qubit.name + ".01", freq=spec_pulse_frequencies.flat[0]))
for acq_idx, spec_pulse_freq in enumerate(spec_pulse_frequencies):
sched.add(Reset(qubit.name))
sched.add(SetClockFrequency(clock=qubit.name + ".01", clock_freq_new=spec_pulse_freq))
sched.add(X(qubit.name, freq=spec_pulse_freq))
sched.add(Measure(qubit.name, acq_index=acq_idx), rel_time=200e-9)
return sched
[23]:
freqs = ManualParameter(name="freq", unit="Hz", label="Frequency")
freqs.batched = True
qubit_spec_sched_kwargs = dict(
qubit=qubit,
spec_pulse_frequencies=freqs,
)
gettable = ScheduleGettable(
quantum_device,
schedule_function=two_tone_spec_sched_nco,
schedule_kwargs=qubit_spec_sched_kwargs,
real_imag=False,
batched=True,
)
meas_ctrl.gettables(gettable)
[24]:
quantum_device.cfg_sched_repetitions(300)
center = 6.1e9
frequency_setpoints = np.linspace(center - 20e6, center + 20e6, 300)
meas_ctrl.settables(freqs)
meas_ctrl.setpoints(frequency_setpoints)
qs_ds = meas_ctrl.run("Two-tone")
qs_ds
Starting batched measurement...
Iterative settable(s) [outer loop(s)]:
--- (None) ---
Batched settable(s):
freq
Batch size limit: 300
[24]:
<xarray.Dataset> Size: 7kB Dimensions: (dim_0: 300) Coordinates: x0 (dim_0) float64 2kB 6.08e+09 6.08e+09 ... 6.12e+09 6.12e+09 Dimensions without coordinates: dim_0 Data variables: y0 (dim_0) float64 2kB nan nan nan nan nan nan ... nan nan nan nan nan y1 (dim_0) float64 2kB nan nan nan nan nan nan ... nan nan nan nan nan Attributes: tuid: 20250312-180403-693-96bb79 name: Two-tone grid_2d: False grid_2d_uniformly_spaced: False 1d_2_settables_uniformly_spaced: False
[25]:
if cluster_ip is None:
notebook_dir = Path.cwd()
dh.set_datadir(notebook_dir / "data")
qs_ds = load_dataset("20250204-234600-751-7ad233")
qs_analysis = QubitSpectroscopyAnalysis(dataset=qs_ds)
qs_analysis.run().display_figs_mpl()

[26]:
qubit.clock_freqs.f01(
qs_analysis.quantities_of_interest["frequency_01"].nominal_value
) # Store the qubit frequency.
print(f"{qubit.clock_freqs.f01():.6e}")
4.579629e+09
[27]:
quantum_device.to_json_file("devices/")
[27]:
'devices/transmon_device_2025-03-12_18-04-05_UTC.json'
Rabi Oscillations#
Here we will carry out an experiment to measure the Rabi frequency that is required to excite the qubit to
Rabi Oscillations#
[28]:
pulse_amps = np.linspace(-0.14, 0.14, 200)
sched = Schedule("rabi_amplitude", 400)
for acq_idx, pulse_amp in enumerate(pulse_amps):
sched.add(Reset(qubit.name))
sched.add(X(qubit.name, amp180=pulse_amp))
sched.add(Measure(qubit.name, acq_index=acq_idx), rel_time=20e-9) # wait 20ns before measuring
rabi_ds = run_schedule(schedule=sched, quantum_device=quantum_device)
rabi_ds
[28]:
<xarray.Dataset> Size: 5kB Dimensions: (acq_index_0: 200) Coordinates: * acq_index_0 (acq_index_0) int64 2kB 0 1 2 3 4 5 ... 194 195 196 197 198 199 Data variables: 0 (acq_index_0) complex128 3kB (nan+nanj) ... (nan+nanj)
[29]:
if cluster_ip is None:
notebook_dir = Path.cwd()
dh.set_datadir(notebook_dir / "data")
rabi_ds = load_dataset("20250205-003934-203-4f4791")
rabi_analysis = RabiAnalysis(tuid=rabi_ds.attrs["tuid"], dataset=rabi_ds)
rabi_analysis.run().display_figs_mpl()

[30]:
qubit.rxy.amp180(rabi_analysis.quantities_of_interest["Pi-pulse amplitude"].nominal_value)
print(f"Pi pulse amplitude: {qubit.rxy.amp180()}")
Pi pulse amplitude: 0.5430058585105821
[31]:
quantum_device.to_json_file("devices/")
[31]:
'devices/transmon_device_2025-03-12_18-04-06_UTC.json'
T1#
Here we measure the
T1#
[32]:
# This schedule can also be imported from quantify_scheduler.schedules.
def t1_sched(
times: np.ndarray | float,
qubit: str,
repetitions: int = 1,
) -> Schedule:
"""
Generate a schedule for performing a :math:`T_1` experiment.
This measures the qubit relaxation time.
Schedule sequence
.. centered:: Reset -- pi -- Idle(tau) -- Measure
See section III.B.2. of :cite:t:`krantz_quantum_2019` for an explanation of the Bloch-Redfield
model of decoherence and the :math:`T_1` experiment.
Parameters
----------
times
an array of wait times tau between the start of pi-pulse and the measurement.
qubit
the name of the device element e.g., :code:`"q0"` to perform the T1 experiment on.
repetitions
The amount of times the Schedule will be repeated.
Returns
-------
:
An experiment schedule.
"""
device_element = qubit
# ensure times is an iterable when passing floats.
times = np.asarray(times)
times = times.reshape(times.shape or (1,))
schedule = Schedule("T1", repetitions)
for i, tau in enumerate(times):
schedule.add(Reset(device_element), label=f"Reset {i}")
schedule.add(X(device_element), label=f"pi {i}")
schedule.add(
Measure(device_element, acq_index=i),
ref_pt="start",
rel_time=tau,
label=f"Measurement {i}",
)
return schedule
[33]:
tau = ManualParameter(name="tau_delay", unit="s", label="Delay")
tau.batched = True
t1_sched_kwargs = {"qubit": qubit.name, "times": tau}
gettable = ScheduleGettable(
quantum_device,
schedule_function=t1_sched,
schedule_kwargs=t1_sched_kwargs,
real_imag=False,
batched=True,
)
meas_ctrl.gettables(gettable)
[34]:
delay_setpoints = np.arange(40e-9, 200e-6, 500e-9)
meas_ctrl.settables(tau)
meas_ctrl.setpoints(delay_setpoints)
quantum_device.cfg_sched_repetitions(300)
t1_ds = meas_ctrl.run("T1 experiment")
t1_ds
Starting batched measurement...
Iterative settable(s) [outer loop(s)]:
--- (None) ---
Batched settable(s):
tau_delay
Batch size limit: 400
[34]:
<xarray.Dataset> Size: 10kB Dimensions: (dim_0: 400) Coordinates: x0 (dim_0) float64 3kB 4e-08 5.4e-07 1.04e-06 ... 0.000199 0.0001995 Dimensions without coordinates: dim_0 Data variables: y0 (dim_0) float64 3kB nan nan nan nan nan nan ... nan nan nan nan nan y1 (dim_0) float64 3kB nan nan nan nan nan nan ... nan nan nan nan nan Attributes: tuid: 20250312-180406-769-ef541c name: T1 experiment grid_2d: False grid_2d_uniformly_spaced: False 1d_2_settables_uniformly_spaced: False
[35]:
if cluster_ip is None:
notebook_dir = Path.cwd()
dh.set_datadir(notebook_dir / "data")
t1_ds = load_dataset("20250205-203226-565-47a4c6")
t1_analysis = T1Analysis(dataset=t1_ds)
t1_analysis.run().display_figs_mpl()

[36]:
quantum_device.to_json_file("devices/")
[36]:
'devices/transmon_device_2025-03-12_18-04-08_UTC.json'
Ramsey Spectroscopy#
Here we demonstrate Ramsey Spectroscopy, which is used to tune the
Ramsey oscillations#
[37]:
# This schedule can also be imported from from quantify_scheduler.schedules
def ramsey_sched(
times: np.ndarray | float,
qubit: str,
artificial_detuning: float = 0,
repetitions: int = 1,
) -> Schedule:
r"""
Generate a schedule for performing a Ramsey experiment.
This measures the dephasing time :math:`T_2^{\star}`.
Schedule sequence
.. centered:: Reset -- pi/2 -- Idle(tau) -- pi/2 -- Measure
See section III.B.2. of :cite:t:`krantz_quantum_2019` for an explanation of the Bloch-Redfield
model of decoherence and the Ramsey experiment.
Parameters
----------
times
an array of wait times tau between the start of the first pi/2 pulse and
the start of the second pi/2 pulse.
artificial_detuning
frequency in Hz of the software emulated, or ``artificial`` qubit detuning, which is
implemented by changing the phase of the second pi/2 (recovery) pulse. The
artificial detuning changes the observed frequency of the Ramsey oscillation,
which can be useful to distinguish a slow oscillation due to a small physical
detuning from the decay of the dephasing noise.
qubit
the name of the device element e.g., :code:`"q0"` to perform the Ramsey experiment on.
repetitions
The amount of times the Schedule will be repeated.
Returns
-------
:
An experiment schedule.
"""
device_element = qubit
# ensure times is an iterable when passing floats.
times = np.asarray(times)
times = times.reshape(times.shape or (1,))
schedule = Schedule("Ramsey", repetitions)
if isinstance(times, float):
times = [times]
for i, tau in enumerate(times):
schedule.add(Reset(device_element), label=f"Reset {i}")
schedule.add(X90(device_element))
# the phase of the second pi/2 phase progresses to propagate
recovery_phase = np.rad2deg(2 * np.pi * artificial_detuning * tau)
schedule.add(
Rxy(theta=90, phi=recovery_phase, qubit=device_element), ref_pt="start", rel_time=tau
)
schedule.add(Measure(device_element, acq_index=i), label=f"Measurement {i}")
return schedule
[38]:
tau = ManualParameter(name="tau", unit="s", label="Time")
tau.batched = True
ramsey_sched_kwargs = {
"qubit": qubit.name,
"times": tau,
"artificial_detuning": 0.0,
}
gettable = ScheduleGettable(
quantum_device,
schedule_function=ramsey_sched,
schedule_kwargs=ramsey_sched_kwargs,
real_imag=False,
batched=True,
)
meas_ctrl.gettables(gettable)
[39]:
tau_setpoints = np.arange(20e-9, 4e-6, 32e-9)
meas_ctrl.settables(tau)
meas_ctrl.setpoints(tau_setpoints)
quantum_device.cfg_sched_repetitions(500)
ramsey_ds = meas_ctrl.run("ramsey")
ramsey_ds
Starting batched measurement...
Iterative settable(s) [outer loop(s)]:
--- (None) ---
Batched settable(s):
tau
Batch size limit: 125
[39]:
<xarray.Dataset> Size: 3kB Dimensions: (dim_0: 125) Coordinates: x0 (dim_0) float64 1kB 2e-08 5.2e-08 8.4e-08 ... 3.956e-06 3.988e-06 Dimensions without coordinates: dim_0 Data variables: y0 (dim_0) float64 1kB nan nan nan nan nan nan ... nan nan nan nan nan y1 (dim_0) float64 1kB nan nan nan nan nan nan ... nan nan nan nan nan Attributes: tuid: 20250312-180408-712-65f4c7 name: ramsey grid_2d: False grid_2d_uniformly_spaced: False 1d_2_settables_uniformly_spaced: False
[40]:
if cluster_ip is None:
notebook_dir = Path.cwd()
dh.set_datadir(notebook_dir / "data")
ramsey_ds = load_dataset("20250206-230423-264-3c48f3")
ramsey_analysis = RamseyAnalysis(ramsey_ds)
ramsey_analysis.run(
artificial_detuning=ramsey_sched_kwargs["artificial_detuning"]
).display_figs_mpl()

[41]:
detuning = ramsey_analysis.quantities_of_interest["detuning"].nominal_value
qubit.clock_freqs.f01(qubit.clock_freqs.f01() - detuning)
print(f"New qubit frequency: {qubit.clock_freqs.f01} Hz")
New qubit frequency: q0_clock_freqs_f01 Hz
[42]:
quantum_device.to_json_file("devices/")
[42]:
'devices/transmon_device_2025-03-12_18-04-10_UTC.json'
Discriminated Single Shot Readout#
Here we show how to run a readout calibration experiment and fit a discriminator with a linear discriminant analysis. This experiment is sometimes called multi-state discrimination.
Schedule definition#
[43]:
def readout_calibration_sched(
qubit: str,
prepared_states: list[int],
repetitions: int = 1,
acq_protocol: Literal[
"SSBIntegrationComplex", "ThresholdedAcquisition"
] = "SSBIntegrationComplex",
) -> Schedule:
"""
Make a schedule for readout calibration.
Parameters
----------
qubit
The name of the qubit e.g., :code:`"q0"` to perform the experiment on.
prepared_states
A list of integers indicating which state to prepare the qubit in before measuring.
The ground state corresponds to 0 and the first-excited state to 1.
repetitions
The number of times the schedule will be repeated. Fixed to 1 for this schedule.
acq_protocol
The acquisition protocol used for the readout calibration. By default
"SSBIntegrationComplex", but "ThresholdedAcquisition" can be
used for verifying thresholded acquisition parameters.
Returns
-------
:
An experiment schedule.
Raises
------
NotImplementedError
If the prepared state is > 1.
"""
schedule = Schedule(f"Readout calibration {qubit}", repetitions=1)
for i, prep_state in enumerate(prepared_states):
schedule.add(Reset(qubit), label=f"Reset {i}")
if prep_state == 0:
pass
elif prep_state == 1:
schedule.add(Rxy(qubit=qubit, theta=180, phi=0))
else:
raise NotImplementedError(
"Preparing the qubit in the higher excited states is not supported yet."
)
schedule.add(
Measure(qubit, acq_index=i, bin_mode=BinMode.APPEND, acq_protocol=acq_protocol),
label=f"Measurement {i}",
)
return schedule
SSRO with single side band (SSB) integration#
[44]:
states = ManualParameter(name="states", unit="", label="Prepared state")
states.batch_size = 400
states.batched = True
readout_calibration_sched_kwargs = dict(
qubit=qubit.name, prepared_states=states, acq_protocol="SSBIntegrationComplex"
)
# set gettable
ssro_gettable = ScheduleGettable(
quantum_device,
schedule_function=readout_calibration_sched,
schedule_kwargs=readout_calibration_sched_kwargs,
real_imag=True,
batched=True,
)
# set measurement control
meas_ctrl.gettables(ssro_gettable)
[45]:
num_shots = 1000
state_setpoints = np.asarray([0, 1] * num_shots)
# replace the get method for the gettable in case the cluster is a dummy
if "dummy" in str(cluster._transport):
from simulated_data import get_simulated_ssro_data
ssro_gettable.get = partial(get_simulated_ssro_data, num_shots=num_shots)
meas_ctrl.settables(states)
meas_ctrl.setpoints(state_setpoints)
ssro_ds = dh.to_gridded_dataset(meas_ctrl.run("Single shot readout experiment"))
ssro_ds
Starting batched measurement...
Iterative settable(s) [outer loop(s)]:
--- (None) ---
Batched settable(s):
states
Batch size limit: 400
[45]:
<xarray.Dataset> Size: 48kB Dimensions: (x0: 2000) Coordinates: * x0 (x0) int64 16kB 0 1 0 1 0 1 0 1 0 1 0 1 ... 0 1 0 1 0 1 0 1 0 1 0 1 Data variables: y0 (x0) float64 16kB 0.02724 0.03581 -0.007418 ... -0.001242 0.0332 y1 (x0) float64 16kB 0.01264 0.00428 0.02087 ... 0.007679 0.03984 Attributes: tuid: 20250312-180410-135-f6bc40 name: Single shot readout experiment grid_2d: False grid_2d_uniformly_spaced: False 1d_2_settables_uniformly_spaced: False
Fit line discriminator with linear discriminant analysis (LDA)#
[46]:
ssro_analysis = ReadoutCalibrationAnalysis(tuid=dh.get_latest_tuid())
ssro_analysis.run().display_figs_mpl()

[47]:
fit_results = ssro_analysis.fit_results["linear_discriminator"].params
acq_threshold = fit_results["acq_threshold"].value
acq_rotation = (np.rad2deg(fit_results["acq_rotation_rad"].value)) % 360
qubit.measure.acq_threshold(acq_threshold)
qubit.measure.acq_rotation(acq_rotation)
SSRO with thresholded acquisition#
[48]:
disc_ssro_gettable_kwargs = dict(
qubit=qubit.name, prepared_states=states, acq_protocol="ThresholdedAcquisition"
)
# set gettable
disc_ssro_gettable = ScheduleGettable(
quantum_device,
schedule_function=readout_calibration_sched,
schedule_kwargs=disc_ssro_gettable_kwargs,
real_imag=True,
batched=True,
)
# set measurement control
meas_ctrl.gettables(disc_ssro_gettable)
[49]:
num_shots = 10_000
state_setpoints = np.asarray([0, 1] * num_shots)
# replace the get method for the gettable in case the cluster is a dummy
if "dummy" in str(cluster._transport):
from simulated_data import get_simulated_binary_ssro_data
def dummy_gettable():
disc_ssro_gettable.initialize()
return get_simulated_binary_ssro_data(num_shots)
disc_ssro_gettable.get = dummy_gettable
meas_ctrl.settables(states)
meas_ctrl.setpoints(state_setpoints)
disc_ssro_ds = dh.to_gridded_dataset(meas_ctrl.run("Discriminated single shot readout experiment"))
disc_ssro_ds
Starting batched measurement...
Iterative settable(s) [outer loop(s)]:
--- (None) ---
Batched settable(s):
states
Batch size limit: 400
[49]:
<xarray.Dataset> Size: 480kB Dimensions: (x0: 20000) Coordinates: * x0 (x0) int64 160kB 0 1 0 1 0 1 0 1 0 1 0 1 ... 1 0 1 0 1 0 1 0 1 0 1 Data variables: y0 (x0) float64 160kB 1.0 1.0 0.0 1.0 0.0 1.0 ... 1.0 0.0 1.0 0.0 1.0 y1 (x0) float64 160kB nan nan nan nan nan nan ... nan nan nan nan nan Attributes: tuid: 20250312-180411-352-43bbcc name: Discriminated single shot readout exper... grid_2d: False grid_2d_uniformly_spaced: False 1d_2_settables_uniformly_spaced: False
[50]:
ConfusionMatrixDisplay.from_predictions(disc_ssro_ds.x0.data, disc_ssro_ds.y0.data)
plt.title("Confusion Matrix")
plt.xlabel("Measured State")
plt.ylabel("Prepared State")
[50]:
Text(0, 0.5, 'Prepared State')

[51]:
quantum_device.to_json_file("devices/")
[51]:
'devices/transmon_device_2025-03-12_18-04-12_UTC.json'