# SPDX-FileCopyrightText: Copyright 2020-2025, Quantify Consortium
# SPDX-FileCopyrightText: © 2021 Qblox <https://qblox.com>
# SPDX-License-Identifier: BSD-3-Clause
"""Standard acquisition protocols for use with the qblox_scheduler."""
from __future__ import annotations
from copy import copy
from typing import TYPE_CHECKING
import numpy as np
from qblox_scheduler.enums import (
BinMode,
DualThresholdedTriggerCountLabels,
TimeRef,
TimeSource,
TriggerCondition,
)
from qblox_scheduler.operations.operation import Operation
from qblox_scheduler.resources import DigitalClockResource
if TYPE_CHECKING:
from collections.abc import Hashable
[docs]
class Acquisition(Operation):
"""
An operation representing data acquisition at the quantum-device abstraction layer.
An Acquisition must consist of (at least) an AcquisitionProtocol specifying how the
acquired signal is to be processed, and an AcquisitionLabel and AcquisitionIndex
specifying where the acquired data is to be stored in the RawDataset.
N.B. This class helps differentiate an acquisition operation from the regular
operations. This enables us to use
:func:`~.qblox_scheduler.schedules._visualization.pulse_diagram.plot_acquisition_operations`
to highlight acquisition pulses in the pulse diagrams.
"""
[docs]
class Trace(Acquisition):
"""
The Trace acquisition protocol measures a signal s(t).
Only processing performed is rescaling and adding
units based on a calibrated scale. Values are returned
as a raw trace (numpy array of float datatype). Length of
this array depends on the sampling rate of the acquisition
device.
.. important::
The exact duration of this operation, and the possible bin modes may depend on
the control hardware. Please consult your hardware vendor's :ref:`Reference
guide` for more information.
Parameters
----------
port
The acquisition port.
clock
The clock used to demodulate the acquisition.
duration
The acquisition duration in seconds.
acq_label
The data channel in which the acquisition is stored, is by default 0.
Describes the "where" information of the measurement, which typically
corresponds to a device element idx.
coords
Coords for the acquisition.
These coordinates for the measured value for this operation
appear in the retrieved acquisition data.
For example ``coords={"amp": 0.1}`` has the effect, that the measured
value for this acquisition will be associated with ``amp==0.1``.
By default ``None``, no coords are added.
"""
def __init__(
self,
duration: float,
port: str,
clock: str,
acq_label: Hashable = 0,
coords: dict | None = None,
) -> None:
if not isinstance(duration, float):
duration = float(duration)
super().__init__(name=self.__class__.__name__)
self.data["acquisition_info"] = {
"waveforms": [],
"duration": duration,
"port": port,
"clock": clock,
"acq_label": acq_label,
"coords": coords,
"bin_mode": BinMode.AVERAGE,
"protocol": "Trace",
"acq_return_type": np.ndarray,
}
def __str__(self) -> str:
acq_info = self.data["acquisition_info"]
return self._get_signature(acq_info)
[docs]
class IQIntegration(Acquisition):
r"""
Weighted integration acquisition protocol returning integrated I and Q as one complex number.
If no weights (``weights_a``/``weights_b``) are given, a square window is used as the
integration weights (single sideband integration); ``duration`` then defaults to 50 ms
and may not exceed 50 ms. If weights are given, the specified weights are used for the
integration (with interpolation where necessary), and the acquisition length is limited
to the length of the weight memory of 16384 ns.
Weights are applied as:
.. math::
\widetilde{A} = \int \mathrm{Re}(S(t))\cdot W_A(t) \mathrm{d}t
.. math::
\widetilde{B} = \int \mathrm{Im}(S(t))\cdot W_B(t) \mathrm{d}t
Parameters
----------
port
The acquisition port.
clock
The clock used to demodulate the acquisition.
duration
The acquisition duration in seconds. When no weights are given, defaults to 50 ms
and is capped at 50 ms. When weights are given, ``duration`` is the time the weight
arrays span (the weight sampling rate is derived as ``len(weights_a) / duration``);
if omitted, the weights are assumed to be sampled at 1 GHz, so
``duration = len(weights_a) / 1e9`` (capped at 16384 ns).
weights_a
The list of complex values used as weights :math:`W_A(t)` on the incoming
signal's real part. By default ``None``, in which case a square window is used
(single sideband integration).
weights_b
The list of complex values used as weights :math:`W_B(t)` on the incoming
signal's imaginary part. By default ``None``.
interpolation
Specifies the kind of interpolation to use.
The string has to be one of 'linear', 'nearest', 'nearest-up', 'zero',
'slinear', 'quadratic', 'cubic', 'previous', or 'next'. 'zero',
'slinear', 'quadratic' and 'cubic' refer to a spline interpolation of
zeroth, first, second or third order; 'previous' and 'next' simply
return the previous or next value of the point; 'nearest-up' and
'nearest' differ when interpolating half-integers (e.g. 0.5, 1.5)
in that 'nearest-up' rounds up and 'nearest' rounds down. Default
is 'linear'. Only used if
weights are given.
acq_label
The data channel in which the acquisition is stored, by default 0.
Describes the "where" information of the measurement, which typically
corresponds to a device element idx.
coords
Coords for the acquisition.
These coordinates for the measured value for this operation
appear in the retrieved acquisition data.
For example ``coords={"amp": 0.1}`` has the effect, that the measured
value for this acquisition will be associated with ``amp==0.1``.
By default ``None``, no coords are added.
average_segment_repetitions
Specifies whether acquisitions in each segment are averaged or kept separately/appended.
Raises
------
ValueError
If exactly one of ``weights_a``/``weights_b`` is given, or if the (derived)
duration exceeds the allowed maximum.
"""
_DEFAULT_DURATION_NO_WEIGHTS = 50e-3
_MAX_DURATION_NO_WEIGHTS = 50e-3
_MAX_DURATION_WEIGHTED = 16384e-9
_DEFAULT_WEIGHTS_SAMPLING_RATE = 1e9
def __init__(
self,
port: str,
clock: str,
duration: float | None = None,
weights_a: list[complex] | list[float] | np.ndarray | None = None,
weights_b: list[complex] | list[float] | np.ndarray | None = None,
interpolation: str = "linear",
acq_label: Hashable = 0,
coords: dict | None = None,
average_segment_repetitions: bool = True,
) -> None:
bin_mode = BinMode.AVERAGE_APPEND
if (weights_a is None) != (weights_b is None):
raise ValueError("Both `weights_a` and `weights_b` must be given, or neither.")
super().__init__(name=self.__class__.__name__)
if weights_a is None:
if duration is None:
duration = self._DEFAULT_DURATION_NO_WEIGHTS
if duration > self._MAX_DURATION_NO_WEIGHTS:
raise ValueError(
f"Acquisition duration of {duration} s exceeds the maximum duration "
f"of {self._MAX_DURATION_NO_WEIGHTS} s for unweighted (square window) "
f"integration."
)
waveform_i = {
"port": port,
"clock": clock,
"duration": duration,
"wf_func": "qblox_scheduler.waveforms.square",
"amplitude": 1,
}
waveform_q = {
"port": port,
"clock": clock,
"duration": duration,
"wf_func": "qblox_scheduler.waveforms.square",
"amplitude": (0 + 1j),
}
waveforms = [waveform_i, waveform_q]
protocol = "SSBIntegrationComplex"
else:
weights_a = np.array(weights_a)
weights_b = np.array(weights_b)
if duration is not None:
weights_sampling_rate = len(weights_a) / duration
else:
weights_sampling_rate = self._DEFAULT_WEIGHTS_SAMPLING_RATE
t_samples = np.arange(len(weights_a)) / weights_sampling_rate
duration = len(t_samples) / weights_sampling_rate
if duration > self._MAX_DURATION_WEIGHTED:
raise ValueError(
f"Acquisition duration of {duration} s exceeds the maximum duration "
f"of {self._MAX_DURATION_WEIGHTED} s (16384 ns weight memory length) "
f"for weighted integration."
)
waveforms_a = {
"wf_func": "qblox_scheduler.waveforms.interpolated_complex_waveform",
"amplitude": 1,
"samples": weights_a,
"t_samples": t_samples,
"interpolation": interpolation,
}
waveforms_b = {
"wf_func": "qblox_scheduler.waveforms.interpolated_complex_waveform",
"amplitude": 1,
"samples": weights_b,
"t_samples": t_samples,
"interpolation": interpolation,
}
waveforms = [waveforms_a, waveforms_b]
protocol = "NumericalSeparatedWeightedIntegration"
self.data["acquisition_info"] = {
"waveforms": waveforms,
"clock": clock,
"port": port,
"duration": duration,
"acq_label": acq_label,
"coords": coords,
"bin_mode": bin_mode,
"average_segment_repetitions": average_segment_repetitions,
"acq_return_type": complex,
"protocol": protocol,
}
def __str__(self) -> str:
acq_info = self.data["acquisition_info"]
port = acq_info["port"]
clock = acq_info["clock"]
acq_label = acq_info["acq_label"]
coords = acq_info["coords"]
average_segment_repetitions = acq_info["average_segment_repetitions"]
duration = acq_info["duration"]
if "samples" in acq_info["waveforms"][0]:
weights_a = np.array2string(
acq_info["waveforms"][0]["samples"], separator=", ", precision=9
)
weights_b = np.array2string(
acq_info["waveforms"][1]["samples"], separator=", ", precision=9
)
interpolation = acq_info["waveforms"][0]["interpolation"]
return (
f"{self.__class__.__name__}(weights_a={weights_a}, weights_b={weights_b}, "
f"{duration=}, {port=}, {clock=}, {interpolation=}, "
f"{acq_label=}, {coords=}, {average_segment_repetitions=})"
)
return (
f"{self.__class__.__name__}({port=}, {clock=}, {duration=}, "
f"{acq_label=}, {coords=}, {average_segment_repetitions=})"
)
def __repr__(self) -> str:
return str(self)
[docs]
class IQIntegrationMixed(IQIntegration):
"""
Subclass of :class:`~IQIntegration` returning a real number, combining weighted I and Q.
Returns the integrated (I*w_a + Q*w_b) based on the integration weights, rather than
the separate I and Q components as one complex number.
Parameters
----------
port
The acquisition port.
clock
The clock used to demodulate the acquisition.
weights_a
The list of complex values used as weights :math:`A(t)` on
the incoming complex signal.
weights_b
The list of complex values used as weights :math:`B(t)` on
the incoming complex signal.
duration
The time in seconds spanned by the weight arrays. If omitted, the weights are
assumed to be sampled at 1 GHz. Capped at 16384 ns.
interpolation
Specifies the kind of interpolation to use.
The string has to be one of 'linear', 'nearest', 'nearest-up', 'zero',
'slinear', 'quadratic', 'cubic', 'previous', or 'next'. 'zero',
'slinear', 'quadratic' and 'cubic' refer to a spline interpolation of
zeroth, first, second or third order; 'previous' and 'next' simply
return the previous or next value of the point; 'nearest-up' and
'nearest' differ when interpolating half-integers (e.g. 0.5, 1.5)
in that 'nearest-up' rounds up and 'nearest' rounds down. Default
is 'linear'. Only used if
weights are given.
acq_label
The data channel in which the acquisition is stored, by default 0.
Describes the "where" information of the measurement, which typically
corresponds to a device element idx.
coords
Coords for the acquisition.
These coordinates for the measured value for this operation
appear in the retrieved acquisition data.
For example ``coords={"amp": 0.1}`` has the effect, that the measured
value for this acquisition will be associated with ``amp==0.1``.
By default ``None``, no coords are added.
average_segment_repetitions
Specifies whether acquisitions in each segment are averaged or kept separately/appended.
"""
def __init__(
self,
port: str,
clock: str,
weights_a: list[complex] | np.ndarray,
weights_b: list[complex] | np.ndarray,
duration: float | None = None,
interpolation: str = "linear",
acq_label: Hashable = 0,
coords: dict | None = None,
average_segment_repetitions: bool = True,
) -> None:
super().__init__(
port=port,
clock=clock,
weights_a=weights_a,
weights_b=weights_b,
duration=duration,
interpolation=interpolation,
acq_label=acq_label,
coords=coords,
average_segment_repetitions=average_segment_repetitions,
)
self.data["acquisition_info"]["protocol"] = "IQIntegrationMixed"
[docs]
class ThresholdedIQIntegration(Acquisition):
"""
Acquisition protocol allowing to control rotation and threshold.
This acquisition protocol is similar to the :class:`~.IQIntegration`
acquisition protocol, but the complex result is now rotated and thresholded
to produce a "0" or a "1", as controlled by the parameters for rotation
angle `<device_element>.measure.acq_rotation` and threshold value
`<device_element>.measure.acq_threshold` in the device configuration (see example
below).
The rotation angle and threshold value for each qubit can be set through
the device configuration.
.. admonition:: Note
Thresholded acquisition is currently only supported by the Qblox
backend.
.. admonition:: Examples
.. jupyter-execute::
from qblox_scheduler.schedules.schedule import TimeableSchedule
from qblox_applications.qblox_scheduler.superconducting.transmon.device_element import (
transmon_element,
)
from qblox_scheduler import ThresholdedIQIntegration
# set up qubit
device_element = transmon_element.BasicTransmonElement("q0")
device_element.clock_freqs.readout = 8.0e9
# set rotation and threshold value
rotation, threshold = 20, -0.1
device_element.measure.acq_rotation = rotation
device_element.measure.acq_threshold = threshold
# basic schedule
schedule = TimeableSchedule("thresholded acquisition")
schedule.add(ThresholdedIQIntegration(port="q0:res", clock="q0.ro", duration=1e-6))
Parameters
----------
port : str
The acquisition port.
clock : str
The clock used to demodulate the acquisition.
duration : float
The acquisition duration in seconds.
acq_label
The data channel in which the acquisition is stored, by default 0.
Describes the "where" information of the measurement, which
typically corresponds to a device element idx.
coords
Coords for the acquisition.
These coordinates for the measured value for this operation
appear in the retrieved acquisition data.
For example ``coords={"amp": 0.1}`` has the effect, that the measured
value for this acquisition will be associated with ``amp==0.1``.
By default ``None``, no coords are added.
average_segment_repetitions
Specifies whether acquisitions in each segment are averaged or kept separately/appended.
feedback_trigger_label : str
The label corresponding to the feedback trigger, which is mapped by the
compiler to a feedback trigger address on hardware, by default None.
"""
def __init__(
self,
port: str,
clock: str,
duration: float,
acq_label: Hashable = 0,
coords: dict | None = None,
average_segment_repetitions: bool = True,
feedback_trigger_label: str | None = None,
acq_rotation: float = 0,
acq_threshold: float = 0,
) -> None:
bin_mode = BinMode.AVERAGE_APPEND
waveform_i = {
"port": port,
"clock": clock,
"duration": duration,
"wf_func": "qblox_scheduler.waveforms.square",
"amplitude": 1,
}
waveform_q = {
"port": port,
"clock": clock,
"duration": duration,
"wf_func": "qblox_scheduler.waveforms.square",
"amplitude": (0 + 1j),
}
super().__init__(name=self.__class__.__name__)
self.data["acquisition_info"] = {
"waveforms": [waveform_i, waveform_q],
"clock": clock,
"port": port,
"duration": duration,
"acq_label": acq_label,
"coords": coords,
"bin_mode": bin_mode,
"average_segment_repetitions": average_segment_repetitions,
"acq_return_type": float if average_segment_repetitions else np.int32,
"protocol": "ThresholdedIQIntegration",
"feedback_trigger_label": feedback_trigger_label,
"acq_threshold": acq_threshold,
"acq_rotation": acq_rotation,
}
def __str__(self) -> str:
acq_info = self.data["acquisition_info"]
return self._get_signature(acq_info)
[docs]
class ThresholdedIQIntegrationMixed(IQIntegrationMixed):
"""
Subclass of :class:`~IQIntegrationMixed` but Thresholded.
Acquisition protocol allowing to control rotation and threshold.
This acquisition protocol is similar to the :class:`~.IQIntegration`
acquisition protocol, but the complex result is now rotated and thresholded
to produce a "0" or a "1", as controlled by the parameters for rotation
angle `<qubit>.measure.acq_rotation` and threshold value
`<qubit>.measure.acq_threshold` in the device configuration (see example
below).
The rotation angle and threshold value for each qubit can be set through
the device configuration.
.. admonition:: Note
Thresholded acquisition is currently only supported by the Qblox
backend.
Parameters
----------
port
The acquisition port.
clock
The clock used to demodulate the acquisition.
weights_a
The list of complex values used as weights :math:`A(t)` on
the incoming complex signal.
weights_b
The list of complex values used as weights :math:`B(t)` on
the incoming complex signal.
duration
The time in seconds spanned by the weight arrays. If omitted, the weights are
assumed to be sampled at 1 GHz. Capped at 16384 ns.
interpolation
Specifies the kind of interpolation to use.
The string has to be one of 'linear', 'nearest', 'nearest-up', 'zero',
'slinear', 'quadratic', 'cubic', 'previous', or 'next'. 'zero',
'slinear', 'quadratic' and 'cubic' refer to a spline interpolation of
zeroth, first, second or third order; 'previous' and 'next' simply
return the previous or next value of the point; 'nearest-up' and
'nearest' differ when interpolating half-integers (e.g. 0.5, 1.5)
in that 'nearest-up' rounds up and 'nearest' rounds down. Default
is 'linear'. Only used if
weights are given.
acq_label
The data channel in which the acquisition is stored, by default 0.
Describes the "where" information of the measurement, which typically
corresponds to a device element idx.
coords
Coords for the acquisition.
These coordinates for the measured value for this operation
appear in the retrieved acquisition data.
For example ``coords={"amp": 0.1}`` has the effect, that the measured
value for this acquisition will be associated with ``amp==0.1``.
By default ``None``, no coords are added.
average_segment_repetitions
Specifies whether acquisitions in each segment are averaged or kept separately/appended.
feedback_trigger_label : str
The label corresponding to the feedback trigger, which is mapped by the
compiler to a feedback trigger address on hardware, by default None.
"""
def __init__(
self,
port: str,
clock: str,
weights_a: list[complex] | np.ndarray,
weights_b: list[complex] | np.ndarray,
duration: float | None = None,
interpolation: str = "linear",
acq_label: Hashable = 0,
coords: dict | None = None,
average_segment_repetitions: bool = True,
feedback_trigger_label: str | None = None,
acq_threshold: float = 0,
) -> None:
super().__init__(
port=port,
clock=clock,
weights_a=weights_a,
weights_b=weights_b,
duration=duration,
interpolation=interpolation,
acq_label=acq_label,
coords=coords,
average_segment_repetitions=average_segment_repetitions,
)
self.data["acquisition_info"]["protocol"] = "ThresholdedIQIntegrationMixed"
self.data["acquisition_info"]["feedback_trigger_label"] = feedback_trigger_label
self.data["acquisition_info"]["acq_threshold"] = acq_threshold
self.data["acquisition_info"]["acq_rotation"] = 360 - 45
self.data["acquisition_info"]["acq_return_type"] = (
float if average_segment_repetitions else np.int32
)
[docs]
class TriggerCount(Acquisition):
"""
Trigger counting acquisition protocol returning an integer.
The trigger acquisition mode is used to measure how
many times the trigger level is surpassed. The level is set
in the hardware configuration.
.. important::
The exact duration of this operation, and the possible bin modes may depend on
the control hardware. Please consult your hardware vendor's :ref:`Reference
guide` for more information.
Parameters
----------
port
The acquisition port.
clock
The clock used to demodulate the acquisition.
duration
The duration of the operation in seconds.
acq_label
The data channel in which the acquisition is stored, by default 0.
Describes the "where" information of the measurement, which typically
corresponds to a device element idx.
coords
Coords for the acquisition.
These coordinates for the measured value for this operation
appear in the retrieved acquisition data.
For example ``coords={"amp": 0.1}`` has the effect, that the measured
value for this acquisition will be associated with ``amp==0.1``.
By default ``None``, no coords are added.
average_segment_repetitions
Specifies whether acquisitions in each segment are averaged or kept separately/appended.
fine_start_delay
Delays the start of the acquisition by the given amount in seconds. Does
not delay the start time of the operation in the schedule. If the
hardware supports it, this parameter can be used to shift the
acquisition window by a small amount of time, independent of the
hardware instruction timing grid. Currently only implemented for Qblox
QTM modules, which allow only positive values for this parameter. By
default 0.
fine_end_delay
Delays the end of the pulse by the given amount in seconds. Does not
delay the end time of the operation in the schedule. If the hardware
supports it, this parameter can be used to shift the acquisition window
by a small amount of time, independent of the hardware instruction
timing grid. Currently only implemented for Qblox QTM modules, which
allow only positive values for this parameter. By default 0.
"""
def __init__(
self,
port: str,
clock: str,
duration: float,
acq_label: Hashable = 0,
coords: dict | None = None,
average_segment_repetitions: bool = True,
fine_start_delay: float = 0,
fine_end_delay: float = 0,
) -> None:
bin_mode = BinMode.AVERAGE_APPEND
super().__init__(name=self.__class__.__name__)
self.data["acquisition_info"] = {
"waveforms": [],
"clock": clock,
"port": port,
"duration": duration,
"acq_label": acq_label,
"coords": coords,
"bin_mode": bin_mode,
"average_segment_repetitions": average_segment_repetitions,
"acq_return_type": int,
"protocol": "TriggerCount",
"fine_start_delay": fine_start_delay,
"fine_end_delay": fine_end_delay,
}
def __str__(self) -> str:
acq_info = self.data["acquisition_info"]
return self._get_signature(acq_info)
[docs]
class TriggerCountDistribution(Acquisition):
"""
Trigger counting distribution acquisition protocol returning an integer.
The trigger acquisition mode is used to measure how
many times the trigger level is surpassed. The level is set
in the hardware configuration.
Parameters
----------
port
The acquisition port.
clock
The clock used to demodulate the acquisition.
duration
The duration of the operation in seconds.
acq_label
The data channel in which the acquisition is stored, by default 0.
Describes the "where" information of the measurement, which typically
corresponds to a device element idx.
coords
Coords for the acquisition.
These coordinates for the measured value for this operation
appear in the retrieved acquisition data.
For example ``coords={"amp": 0.1}`` has the effect, that the measured
value for this acquisition will be associated with ``amp==0.1``.
By default ``None``, no coords are added.
fine_start_delay
Delays the start of the acquisition by the given amount in seconds. Does
not delay the start time of the operation in the schedule. If the
hardware supports it, this parameter can be used to shift the
acquisition window by a small amount of time, independent of the
hardware instruction timing grid. Currently only implemented for Qblox
QTM modules, which allow only positive values for this parameter. By
default 0.
fine_end_delay
Delays the end of the pulse by the given amount in seconds. Does not
delay the end time of the operation in the schedule. If the hardware
supports it, this parameter can be used to shift the acquisition window
by a small amount of time, independent of the hardware instruction
timing grid. Currently only implemented for Qblox QTM modules, which
allow only positive values for this parameter. By default 0.
"""
def __init__(
self,
port: str,
clock: str,
duration: float,
acq_label: Hashable = 0,
coords: dict | None = None,
fine_start_delay: float = 0,
fine_end_delay: float = 0,
) -> None:
super().__init__(name=self.__class__.__name__)
self.data["acquisition_info"] = {
"waveforms": [],
"clock": clock,
"port": port,
"duration": duration,
"acq_label": acq_label,
"coords": coords,
"bin_mode": BinMode.DISTRIBUTION,
"acq_return_type": int,
"protocol": "TriggerCountDistribution",
"fine_start_delay": fine_start_delay,
"fine_end_delay": fine_end_delay,
}
def __str__(self) -> str:
acq_info = self.data["acquisition_info"]
return self._get_signature(acq_info)
[docs]
class ThresholdedTriggerCount(Acquisition):
"""
Thresholded trigger counting acquisition protocol returning the comparison result with a
threshold.
If the number of triggers counted is less than the threshold, a 0 is returned, otherwise a 1.
The analog threshold for registering a single count is set in the hardware configuration.
.. important::
The exact duration of this operation, and the possible bin modes may depend on the control
hardware. Please consult your hardware vendor's :ref:`Reference guide` for more information.
Parameters
----------
port
The acquisition port.
clock
The clock used to demodulate the acquisition.
duration
The duration of the operation in seconds.
threshold
The threshold of the ThresholdedTriggerCount acquisition.
feedback_trigger_label
The label corresponding to the feedback trigger, which is mapped by the compiler to a
feedback trigger address on hardware, by default None.
Note: this label is merely used to link this acquisition together with a
ConditionalOperation. It does not affect the acquisition result.
feedback_trigger_condition
The comparison condition (greater-equal, less-than) for the ThresholdedTriggerCount
acquisition.
acq_label
The data channel in which the acquisition is stored, by default 0. Describes the "where"
information of the measurement, which typically corresponds to a qubit idx.
coords
Coords for the acquisition.
These coordinates for the measured value for this operation
appear in the retrieved acquisition data.
For example ``coords={"amp": 0.1}`` has the effect, that the measured
value for this acquisition will be associated with ``amp==0.1``.
By default ``None``, no coords are added.
average_segment_repetitions
Specifies whether acquisitions in each segment are averaged or kept separately/appended.
"""
def __init__(
self,
port: str,
clock: str,
duration: float,
threshold: int,
*,
feedback_trigger_label: str | None = None,
feedback_trigger_condition: (
str | TriggerCondition
) = TriggerCondition.GREATER_THAN_EQUAL_TO,
acq_label: Hashable = 0,
coords: dict | None = None,
average_segment_repetitions: bool = True,
) -> None:
bin_mode = BinMode.AVERAGE_APPEND
super().__init__(name=self.__class__.__name__)
self.data["acquisition_info"] = {
"waveforms": [],
"clock": clock,
"port": port,
"duration": duration,
"acq_label": acq_label,
"coords": coords,
"bin_mode": bin_mode,
"average_segment_repetitions": average_segment_repetitions,
"thresholded_trigger_count": {
"threshold": threshold,
"condition": feedback_trigger_condition,
},
"feedback_trigger_label": feedback_trigger_label,
"feedback_trigger_address": None,
"acq_return_type": int,
"protocol": "ThresholdedTriggerCount",
}
def __str__(self) -> str:
acq_info = copy(self.data["acquisition_info"])
# The __str__ method is used for serialization. The keys must match the input parameters.
acq_info["threshold"] = acq_info["thresholded_trigger_count"]["threshold"]
acq_info["feedback_trigger_condition"] = acq_info["thresholded_trigger_count"]["condition"]
return self._get_signature(acq_info)
[docs]
class DualThresholdedTriggerCount(Acquisition):
"""
Thresholded trigger count protocol that uses two thresholds.
Four outcomes are possible for this measurement, and each of those four can be assigned a label
to use in a :class:`~qblox_scheduler.operations.control_flow_library.ConditionalOperation`:
- "low" if ``counts < threshold_low``,
- "mid" if ``threshold_low <= counts < threshold_high``,
- "high" if ``counts >= threshold_high``,
- "invalid" if the counts are invalid (can occur in very rare cases, e.g. when the counter
overflows).
The returned acquisition data is the raw number of counts.
.. important::
The exact duration of this operation, and the possible bin modes may depend on the control
hardware. Please consult your hardware vendor's :ref:`Reference guide` for more information.
Parameters
----------
port
The acquisition port.
clock
The clock used to demodulate the acquisition.
duration
The duration of the operation in seconds.
threshold_low
The lower counts threshold of the ThresholdedTriggerCount acquisition.
threshold_high
The upper counts threshold of the ThresholdedTriggerCount acquisition.
label_low
The label that can be used to link a result of `counts < threshold_low` to a
ConditionalOperation, by default None.
label_mid
The label that can be used to link a result of `threshold_low <= counts < threshold_high` to
a ConditionalOperation, by default None.
label_high
The label that can be used to link a result of `counts >= threshold_high` to a
ConditionalOperation, by default None.
label_invalid
The label that can be used to link an invalid counts result (e.g. a counter overflow) to a
ConditionalOperation, by default None.
feedback_trigger_condition
The comparison condition (greater-equal, less-than) for the ThresholdedTriggerCount
acquisition.
acq_label
The data channel in which the acquisition is stored, by default 0. Describes the "where"
information of the measurement, which typically corresponds to a qubit idx.
coords
Coords for the acquisition.
These coordinates for the measured value for this operation
appear in the retrieved acquisition data.
For example ``coords={"amp": 0.1}`` has the effect, that the measured
value for this acquisition will be associated with ``amp==0.1``.
By default ``None``, no coords are added.
average_segment_repetitions
Specifies whether acquisitions in each segment are averaged or kept separately/appended.
"""
def __init__(
self,
port: str,
clock: str,
duration: float,
threshold_low: int,
threshold_high: int,
*,
label_low: str | None = None,
label_mid: str | None = None,
label_high: str | None = None,
label_invalid: str | None = None,
acq_label: Hashable = 0,
coords: dict | None = None,
average_segment_repetitions: bool = True,
) -> None:
bin_mode = BinMode.AVERAGE_APPEND
super().__init__(name=self.__class__.__name__)
self.data["acquisition_info"] = {
"waveforms": [],
"clock": clock,
"port": port,
"duration": duration,
"acq_label": acq_label,
"coords": coords,
"bin_mode": bin_mode,
"average_segment_repetitions": average_segment_repetitions,
"thresholded_trigger_count": {
"threshold_low": threshold_low,
"threshold_high": threshold_high,
},
"feedback_trigger_labels": {
DualThresholdedTriggerCountLabels.LOW: label_low,
DualThresholdedTriggerCountLabels.MID: label_mid,
DualThresholdedTriggerCountLabels.HIGH: label_high,
DualThresholdedTriggerCountLabels.INVALID: label_invalid,
},
"acq_return_type": int,
"protocol": "DualThresholdedTriggerCount",
}
def __str__(self) -> str:
acq_info = copy(self.data["acquisition_info"])
acq_info["threshold_low"] = acq_info["thresholded_trigger_count"]["threshold_low"]
acq_info["threshold_high"] = acq_info["thresholded_trigger_count"]["threshold_high"]
# Pyright thinks custom StrEnum is not iterable.
for suffix in DualThresholdedTriggerCountLabels: # type: ignore
acq_info[f"label_{suffix}"] = acq_info["feedback_trigger_labels"][suffix]
return self._get_signature(acq_info)