Source code for qblox_scheduler.schedules.spectroscopy_schedules

# Repository: https://gitlab.com/qblox/packages/software/qblox-scheduler
# Licensed according to the LICENSE file on the main branch
#
# Copyright 2020-2025, Quantify Consortium
# Copyright 2025, Qblox B.V.
"""Module containing schedules for common spectroscopy experiments."""

from __future__ import annotations

from typing import TYPE_CHECKING

from qblox_scheduler.enums import BinMode
from qblox_scheduler.operations.acquisition_library import IQIntegration
from qblox_scheduler.operations.gate_library import Measure, Reset
from qblox_scheduler.operations.nv_native_library import ChargeReset, CRCount
from qblox_scheduler.operations.pulse_library import (
    IdlePulse,
    SetClockFrequency,
    SquarePulse,
)
from qblox_scheduler.operations.shared_native_library import SpectroscopyOperation
from qblox_scheduler.resources import ClockResource
from qblox_scheduler.schedules.schedule import TimeableSchedule

if TYPE_CHECKING:
    import numpy as np


[docs] def heterodyne_spec_sched( pulse_amp: float, pulse_duration: float, frequency: float, acquisition_delay: float, integration_time: float, port: str, clock: str, init_duration: float = 10e-6, repetitions: int = 1, port_out: str | None = None, ) -> TimeableSchedule: """ Generate a schedule for performing heterodyne spectroscopy. Parameters ---------- pulse_amp Amplitude of the spectroscopy pulse in Volt. pulse_duration Duration of the spectroscopy pulse in seconds. frequency Frequency of 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 TimeableSchedule 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 = TimeableSchedule("Heterodyne spectroscopy", repetitions) sched.add_resource(ClockResource(name=clock, freq=frequency)) sched.add(IdlePulse(duration=init_duration), label="buffer") if port_out is None: port_out = port pulse = sched.add( SquarePulse( duration=pulse_duration, amplitude=pulse_amp, port=port_out, clock=clock, ), label="spec_pulse", ) sched.add( IQIntegration( duration=integration_time, port=port, clock=clock, acq_label=0, ), ref_op=pulse, ref_pt="start", rel_time=acquisition_delay, label="acquisition", ) return sched
[docs] 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, ) -> TimeableSchedule: """ Generate a batched schedule for performing fast heterodyne spectroscopy using the :class:`~qblox_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 TimeableSchedule 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 = TimeableSchedule("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 i, freq in enumerate(frequencies): sched.add(IdlePulse(duration=init_duration), label=f"buffer {i}") sched.add( SetClockFrequency(clock=clock, frequency=freq), label=f"set_freq {i} ({clock} {freq:e} Hz)", ) spec_pulse = sched.add( SquarePulse( duration=pulse_duration, amplitude=pulse_amp, port=port_out, clock=clock, ), label=f"spec_pulse {i})", ) sched.add( IQIntegration( duration=integration_time, port=port, clock=clock, acq_label=0, bin_mode=BinMode.AVERAGE_APPEND, ), ref_op=spec_pulse, ref_pt="start", rel_time=acquisition_delay, label=f"acquisition {i})", ) return sched
[docs] def two_tone_spec_sched( spec_pulse_amp: float, spec_pulse_duration: float, spec_pulse_port: str, spec_pulse_clock: str, spec_pulse_frequency: float, ro_pulse_amp: float, ro_pulse_duration: float, ro_pulse_delay: float, ro_pulse_port: str, ro_pulse_clock: str, ro_pulse_frequency: float, ro_acquisition_delay: float, ro_integration_time: float, init_duration: float = 10e-6, repetitions: int = 1, ) -> TimeableSchedule: """ Generate a schedule for performing two-tone spectroscopy. Parameters ---------- spec_pulse_amp Amplitude of the spectroscopy pulse in Volt. spec_pulse_duration Duration of the spectroscopy pulse in seconds. spec_pulse_port Location on the device where the spectroscopy pulse should be applied. spec_pulse_clock Reference clock used to track the spectroscopy frequency. spec_pulse_frequency Frequency of the spectroscopy pulse in Hertz. ro_pulse_amp Amplitude of the readout (spectroscopy) pulse in Volt. ro_pulse_duration Duration of the readout (spectroscopy) pulse in seconds. ro_pulse_delay Time between the end of the spectroscopy pulse and the start of the readout (spectroscopy) pulse. ro_pulse_port Location on the device where the readout (spectroscopy) pulse should be applied. ro_pulse_clock Reference clock used to track the readout (spectroscopy) frequency. ro_pulse_frequency Frequency of the readout (spectroscopy) pulse in Hertz. ro_acquisition_delay Start of the data acquisition with respect to the start of the readout pulse in seconds. ro_integration_time Integration time of the data acquisition in seconds. init_duration The relaxation time or dead time. repetitions The amount of times the TimeableSchedule will be repeated. """ sched = TimeableSchedule("Two-tone spectroscopy", repetitions) sched.add_resource(ClockResource(name=spec_pulse_clock, freq=spec_pulse_frequency)) sched.add_resource(ClockResource(name=ro_pulse_clock, freq=ro_pulse_frequency)) sched.add( IdlePulse(duration=init_duration), label="buffer", ) sched.add( SquarePulse( duration=spec_pulse_duration, amplitude=spec_pulse_amp, port=spec_pulse_port, clock=spec_pulse_clock, ), label="spec_pulse", ) ro_pulse = sched.add( SquarePulse( duration=ro_pulse_duration, amplitude=ro_pulse_amp, port=ro_pulse_port, clock=ro_pulse_clock, ), label="readout_pulse", rel_time=ro_pulse_delay, ) sched.add( IQIntegration( duration=ro_integration_time, port=ro_pulse_port, clock=ro_pulse_clock, acq_label=0, ), ref_op=ro_pulse, ref_pt="start", rel_time=ro_acquisition_delay, label="acquisition", ) return sched
[docs] def two_tone_spec_sched_nco( spec_pulse_amp: float, spec_pulse_duration: float, spec_pulse_port: str, spec_pulse_clock: str, spec_pulse_frequencies: np.ndarray, ro_pulse_amp: float, ro_pulse_duration: float, ro_pulse_delay: float, ro_pulse_port: str, ro_pulse_clock: str, ro_pulse_frequency: float, ro_acquisition_delay: float, ro_integration_time: float, init_duration: float, repetitions: int = 1, ) -> TimeableSchedule: """ Generate a batched schedule for performing fast two-tone spectroscopy using the :class:`~qblox_scheduler.operations.pulse_library.SetClockFrequency` operation for doing an NCO sweep. For long-lived qubits, it is advisable to use a small number of repetitions and compensate by doing continuous spectroscopy (low amplitude, long duration pulse with simultaneous long readout). The "dead-time" between two data points needs to be sufficient to properly reset the qubit. That means that `init_duration` should be >> T1 (so typically >200us). Parameters ---------- spec_pulse_amp Amplitude of the spectroscopy pulse in Volt. spec_pulse_duration Duration of the spectroscopy pulse in seconds. spec_pulse_port Location on the device where the spectroscopy pulse should be applied. spec_pulse_clock Reference clock used to track the spectroscopy frequency. spec_pulse_frequencies Sample frequencies for the spectroscopy pulse in Hertz. ro_pulse_amp Amplitude of the readout (spectroscopy) pulse in Volt. ro_pulse_duration Duration of the readout (spectroscopy) pulse in seconds. ro_pulse_delay Time between the end of the spectroscopy pulse and the start of the readout (spectroscopy) pulse. ro_pulse_port Location on the device where the readout (spectroscopy) pulse should be applied. ro_pulse_clock Reference clock used to track the readout (spectroscopy) frequency. ro_pulse_frequency Frequency of the readout (spectroscopy) pulse in Hertz. ro_acquisition_delay Start of the data acquisition with respect to the start of the readout pulse in seconds. ro_integration_time Integration time of the data acquisition in seconds. init_duration The relaxation time or dead time. repetitions The amount of times the TimeableSchedule will be repeated. """ sched = TimeableSchedule("Fast two-tone spectroscopy (NCO sweep)", repetitions) sched.add_resources( [ ClockResource(name=spec_pulse_clock, freq=spec_pulse_frequencies.flat[0]), ClockResource(name=ro_pulse_clock, freq=ro_pulse_frequency), ] ) for i, spec_pulse_freq in enumerate(spec_pulse_frequencies): sched.add(IdlePulse(duration=init_duration), label=f"buffer {i}") sched.add( SetClockFrequency(clock=spec_pulse_clock, frequency=spec_pulse_freq), label=f"set_freq {i} ({spec_pulse_clock} {spec_pulse_freq:e} Hz)", ) spec_pulse = sched.add( SquarePulse( duration=spec_pulse_duration, amplitude=spec_pulse_amp, port=spec_pulse_port, clock=spec_pulse_clock, ), label=f"spec_pulse {i}", ) ro_pulse = sched.add( SquarePulse( duration=ro_pulse_duration, amplitude=ro_pulse_amp, port=ro_pulse_port, clock=ro_pulse_clock, ), ref_op=spec_pulse, ref_pt="end", rel_time=ro_pulse_delay, label=f"readout_pulse {i}", ) sched.add( IQIntegration( duration=ro_integration_time, port=ro_pulse_port, clock=ro_pulse_clock, acq_label=0, bin_mode=BinMode.AVERAGE_APPEND, ), ref_op=ro_pulse, ref_pt="start", rel_time=ro_acquisition_delay, label=f"acquisition {i}", ) return sched
[docs] def nv_dark_esr_sched( qubit: str, repetitions: int = 1, ) -> TimeableSchedule: """ Generates a schedule for a dark ESR experiment on an NV-center. The spectroscopy frequency is taken from the device element. Please use the clock specified in the `spectroscopy_operation` entry of the device config. Parameters ---------- qubit Name of the `DeviceElement` representing the NV-center. repetitions Number of schedule repetitions. Returns ------- : TimeableSchedule with a single frequency """ device_element = qubit sched = TimeableSchedule("Dark ESR Schedule", repetitions=repetitions) sched.add(ChargeReset(device_element), label="Charge reset") sched.add(CRCount(device_element), label="CRCount pre") sched.add(Reset(device_element), label="Reset") sched.add(SpectroscopyOperation(device_element), label="Spectroscopy") sched.add(Measure(device_element), label="Measure") sched.add(CRCount(device_element), label="CRCount post") return sched
[docs] def nv_dark_esr_sched_nco( qubit: str, spec_clock: str, spec_frequencies: np.ndarray, repetitions: int = 1, ) -> TimeableSchedule: """ Generates a schedule for a dark ESR experiment on an NV-center, in which the NCO frequency is swept. Parameters ---------- qubit Name of the `DeviceElement` representing the NV-center. spec_clock Reference clock of the spectroscopy operation. spec_frequencies Sample frequencies for the spectroscopy pulse in Hertz. repetitions Number of schedule repetitions. Returns ------- : TimeableSchedule with NCO frequency sweeping for spectroscopy operation. """ device_element = qubit sched = TimeableSchedule("Dark ESR TimeableSchedule (NCO sweep)", repetitions=repetitions) sched.add(ChargeReset(device_element), label="Charge reset pre 0") sched.add(CRCount(device_element), label="CRCount pre 0") for idx, spec_freq in enumerate(spec_frequencies): sched.add( SetClockFrequency(clock=spec_clock, frequency=spec_freq), label=f"set_freq ({spec_clock} {spec_freq:e} Hz)", ) sched.add(Reset(device_element), label=f"Reset {idx}") sched.add(SpectroscopyOperation(device_element), label=f"Spectroscopy ({spec_freq:e} Hz)") sched.add(Measure(device_element), label=f"Measure {idx}") sched.add(CRCount(device_element), label=f"CRCount post {idx}") return sched