# Repository: https://gitlab.com/quantify-os/quantify-scheduler
# Licensed according to the LICENCE file on the main branch
"""Contains various examples of trace schedules."""
from __future__ import annotations
from typing import TYPE_CHECKING
import numpy as np
if TYPE_CHECKING:
    from quantify_scheduler.device_under_test.device_element import DeviceElement
from quantify_scheduler.enums import BinMode
from quantify_scheduler.operations.acquisition_library import (
    SSBIntegrationComplex,
    Trace,
)
from quantify_scheduler.operations.control_flow_library import LoopOperation
from quantify_scheduler.operations.gate_library import Measure
from quantify_scheduler.operations.pulse_library import (
    IdlePulse,
    SquarePulse,
    VoltageOffset,
)
from quantify_scheduler.resources import ClockResource
from quantify_scheduler.schedules.schedule import Schedule
[docs]
def trace_schedule(
    pulse_amp: float,
    pulse_duration: float,
    pulse_delay: float,
    frequency: float,
    acquisition_delay: float,
    integration_time: float,
    port: str,
    clock: str,
    init_duration: float = 200e-6,
    repetitions: int = 1,
) -> Schedule:
    """
    Generate a schedule to perform raw trace acquisition.
    Parameters
    ----------
    pulse_amp :
        The amplitude of the pulse in Volt.
    pulse_duration
        The duration of the pulse in seconds.
    pulse_delay
        The pulse delay in seconds.
    frequency
        The frequency of the pulse and of the data acquisition in Hertz.
    acquisition_delay
        The start of the data acquisition with respect to the start of the pulse in
        seconds.
    integration_time
        The time in seconds to integrate.
    port
        The location on the device where the
        pulse should be applied.
    clock
        The reference clock used to track the pulse frequency.
    init_duration
        The relaxation time or dead time.
    repetitions
        The amount of times the Schedule will be repeated.
    Returns
    -------
    :
        The Raw Trace acquisition Schedule.
    """
    schedule = Schedule("Raw trace acquisition", repetitions)
    schedule.add_resource(ClockResource(name=clock, freq=frequency))
    schedule.add(IdlePulse(duration=init_duration), label="Dead time")
    pulse = schedule.add(
        SquarePulse(
            duration=pulse_duration,
            amp=pulse_amp,
            port=port,
            clock=clock,
        ),
        label="trace_pulse",
        rel_time=pulse_delay,
    )
    schedule.add(
        Trace(
            duration=integration_time,
            port=port,
            clock=clock,
            acq_index=0,
            acq_channel=0,
        ),
        ref_op=pulse,
        ref_pt="start",
        rel_time=acquisition_delay,
        label="acquisition",
    )
    return schedule 
[docs]
def trace_schedule_circuit_layer(
    qubit_name: str,
    repetitions: int = 1,
) -> Schedule:
    """
    Generate a simple schedule at circuit layer to perform raw trace acquisition.
    Parameters
    ----------
    qubit_name
        Name of a device element.
    repetitions
        The amount of times the Schedule will be repeated.
    Returns
    -------
    :
        The Raw Trace acquisition Schedule.
    """
    schedule = Schedule("Raw trace acquisition", repetitions)
    schedule.add(Measure(qubit_name, acq_protocol="Trace"))
    return schedule 
[docs]
def two_tone_trace_schedule(
    qubit_pulse_amp: float,
    qubit_pulse_duration: float,
    qubit_pulse_frequency: float,
    qubit_pulse_port: str,
    qubit_pulse_clock: str,
    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 = 200e-6,
    repetitions: int = 1,
) -> Schedule:
    """
    Generate a schedule for performing a two-tone raw trace acquisition.
    Parameters
    ----------
    qubit_pulse_amp
        The amplitude of the pulse in Volt.
    qubit_pulse_duration
        The duration of the pulse in seconds.
    qubit_pulse_frequency
        The pulse frequency in Hertz.
    qubit_pulse_port
        The location on the device where the
        qubit pulse should be applied.
    qubit_pulse_clock
        The reference clock used to track the
        pulse frequency.
    ro_pulse_amp
        The amplitude of the readout pulse in Volt.
    ro_pulse_duration
        The duration of the readout pulse in seconds.
    ro_pulse_delay
        The time between the end of the pulse and the start
        of the readout pulse.
    ro_pulse_port
        The location on the device where the
        readout pulse should be applied.
    ro_pulse_clock
        The reference clock used to track the
        readout pulse frequency.
    ro_pulse_frequency
        The readout pulse frequency in Hertz.
    ro_acquisition_delay
        The start of the data acquisition with respect to
        the start of the pulse in seconds.
    ro_integration_time
        The integration time of the data acquisition in seconds.
    init_duration :
        The relaxation time or dead time.
    repetitions
        The amount of times the Schedule will be repeated.
    Returns
    -------
    :
        The Two-tone Trace acquisition Schedule.
    """
    schedule = Schedule("Two-tone Trace acquisition", repetitions)
    schedule.add_resource(ClockResource(name=qubit_pulse_clock, freq=qubit_pulse_frequency))
    schedule.add_resource(ClockResource(name=ro_pulse_clock, freq=ro_pulse_frequency))
    schedule.add(
        IdlePulse(duration=init_duration),
        label="Reset",
    )
    schedule.add(
        SquarePulse(
            duration=qubit_pulse_duration,
            amp=qubit_pulse_amp,
            port=qubit_pulse_port,
            clock=qubit_pulse_clock,
        ),
        label="qubit_pulse",
    )
    ro_pulse = schedule.add(
        SquarePulse(
            duration=ro_pulse_duration,
            amp=ro_pulse_amp,
            port=ro_pulse_port,
            clock=ro_pulse_clock,
        ),
        label="readout_pulse",
        rel_time=ro_pulse_delay,
    )
    schedule.add(
        Trace(
            duration=ro_integration_time,
            port=ro_pulse_port,
            clock=ro_pulse_clock,
            acq_index=0,
            acq_channel=0,
        ),
        ref_op=ro_pulse,
        ref_pt="start",
        rel_time=ro_acquisition_delay,
        label="acquisition",
    )
    return schedule 
[docs]
def long_time_trace(
    pulse_amp: complex,
    frequency: float,
    acquisition_delay: float,
    integration_time: float,
    port: str,
    clock: str,
    num_points: int,
    pulse_delay: float = 0,
    acq_index: int = 0,
    repetitions: int = 1,
) -> Schedule:
    """
    The function generates a :class:`~Schedule` for a time trace experiment
    where single side band integration (i.e. ``SSBIntegrationComplex``) is
    applied repeatedly. Compared to the Trace schedule which returns one point
    each ns for 16.4 μs (qblox instrument), the long time trace allows the
    user to customize the integration time and the number of data points in the trace (a minimum of
    300 ns (qblox instrument) for the integration time is needed in
    between two points). The resulting :class:`~Schedule` can be used for
    quantum experiments involving dynamic processes (charge transition) and
    time-dependent measurements (Elzerman Readout).
    Parameters
    ----------
    pulse_amp
        The amplitude of the pulse in Volt.
    frequency
        The frequency of the pulse and of the data acquisition in Hertz.
    acquisition_delay
        The start of the data acquisition with respect to the start of the pulse in
        seconds.
    integration_time
        Integration time per point in the trace. A minimum of 300ns is required du
        to the spacing of acquisition protocols in QbloxInstrument.
    port
        The location on the device where the
        pulse should be applied.
    clock
        The reference clock used to track the pulse frequency.
    num_points
        Number of points the output long_trace contains.
        The total time of the long_trace is then nb_pts*integration_time
    pulse_delay
        The pulse delay in seconds.
    acq_index
        The data register in which the acquisition is stored, by default 0.
        Describes the "when" information of the measurement, used to label or
        tag individual measurements in a large circuit. Typically corresponds
        to the setpoints of a schedule (e.g., tau in a T1 experiment).
    repetitions
        The amount of times the Schedule will be repeated.
    Returns
    -------
    :
        The custom long Trace acquisition Schedule.
    """
    schedule = Schedule("Long time trace acquisition", repetitions)
    schedule.add_resource(ClockResource(name=clock, freq=frequency))
    pulse_op = VoltageOffset(
        offset_path_I=np.real(pulse_amp),
        offset_path_Q=np.imag(pulse_amp),
        port=port,
        clock=clock,
    )
    schedule.add(pulse_op)
    op = SSBIntegrationComplex(
        port=port,
        clock=clock,
        duration=integration_time,
        acq_channel=0,
        acq_index=acq_index,
        bin_mode=BinMode.APPEND,
        t0=pulse_delay,
    )
    inner = Schedule("inner", repetitions=1)
    inner.add(op)
    schedule.add(
        LoopOperation(body=inner, repetitions=num_points),
        rel_time=acquisition_delay,
        ref_pt="start",
    )
    pulse_op_off = VoltageOffset(
        offset_path_I=0,
        offset_path_Q=0,
        port=port,
        clock=clock,
    )
    # Here rel_time = 4ns have to be add to order properly with the control flow.
    # Same think Idle pulse has to be added for loops
    # This has to be removed in a next version.
    schedule.add(pulse_op_off, rel_time=4e-9)
    schedule.add(IdlePulse(duration=4e-9))
    return schedule 
[docs]
def long_time_trace_with_qubit(
    qubit: DeviceElement,
    num_points: int,
    acq_index: int = 0,
    repetitions: int = 1,
) -> Schedule:
    """
    Generate a simple schedule similar to a circuit layer to perform long trace acquisition.
    Wrapper function for :func:~quantify_scheduler.schedules.long_time_trace to use
    with a quantum device element..
    Parameters
    ----------
    qubit
        Device Element.
    num_points
        Number of points the output custom_long_trace contains.
        The total time of the custom_long_trace is then nb_pts*integration_time
    acq_index :
        The data register in which the acquisition is stored, by default 0.
        Describes the "when" information of the measurement, used to label or
        tag individual measurements in a large circuit. Typically corresponds
        to the setpoints of a schedule (e.g., tau in a T1 experiment).
    repetitions
        The amount of times the Schedule will be repeated.
    Returns
    -------
    :
        The custom long Trace acquisition Schedule.
    """
    device_element = qubit
    schedule = long_time_trace(
        pulse_amp=device_element.measure.pulse_amp(),
        pulse_delay=0,
        frequency=device_element.clock_freqs.readout(),
        acquisition_delay=device_element.measure.acq_delay(),
        integration_time=device_element.measure.integration_time(),
        port=device_element.ports.readout(),
        clock=device_element.name + ".ro",
        num_points=num_points,
        acq_index=acq_index,
        repetitions=repetitions,
    )
    return schedule