Skip to content

Channels

Channel

Bases: QuamComponent, ABC

Base QuAM component for a channel, can be output, input or both.

Parameters:

Name Type Description Default
operations Dict[str, Pulse]

A dictionary of pulses to be played on this channel. The key is the pulse label (e.g. "X90") and value is a Pulse.

required
id (str, int)

The id of the channel, used to generate the name. Can be a string, or an integer in which case it will add Channel._default_label.

required
sticky Sticky

Optional sticky parameters for the channel, i.e. defining whether successive pulses are applied w.r.t the previous pulse or w.r.t 0 V. If not specified, this channel is not sticky.

required
Source code in quam/components/channels.py
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
@quam_dataclass
class Channel(QuamComponent, ABC):
    """Base QuAM component for a channel, can be output, input or both.

    Args:
        operations (Dict[str, Pulse]): A dictionary of pulses to be played on this
            channel. The key is the pulse label (e.g. "X90") and value is a Pulse.
        id (str, int): The id of the channel, used to generate the name.
            Can be a string, or an integer in which case it will add
            `Channel._default_label`.
        sticky (Sticky): Optional sticky parameters for the channel, i.e. defining
            whether successive pulses are applied w.r.t the previous pulse or w.r.t 0 V.
            If not specified, this channel is not sticky.
    """

    operations: Dict[str, Pulse] = field(default_factory=dict)

    id: Union[str, int] = None
    _default_label: ClassVar[str] = "ch"  # Used to determine name from id

    digital_outputs: Dict[str, DigitalOutputChannel] = field(default_factory=dict)
    sticky: Optional[StickyChannelAddon] = None
    intermediate_frequency: Optional[float] = None
    thread: Optional[str] = None

    @property
    def name(self) -> str:
        cls_name = self.__class__.__name__

        if self.id is not None:
            if str_ref.is_reference(self.id):
                raise AttributeError(
                    f"{cls_name}.name cannot be determined. "
                    f"Please either set {cls_name}.id to a string or integer, "
                    f"or {cls_name} should be an attribute of another QuAM component."
                )
            if isinstance(self.id, str):
                return self.id
            else:
                return f"{self._default_label}{self.id}"
        if self.parent is None:
            raise AttributeError(
                f"{cls_name}.name cannot be determined. "
                f"Please either set {cls_name}.id to a string or integer, "
                f"or {cls_name} should be an attribute of another QuAM component with "
                "a name."
            )
        if isinstance(self.parent, QuamDict):
            return self.parent.get_attr_name(self)
        if not hasattr(self.parent, "name"):
            raise AttributeError(
                f"{cls_name}.name cannot be determined. "
                f"Please either set {cls_name}.id to a string or integer, "
                f"or {cls_name} should be an attribute of another QuAM component with "
                "a name."
            )
        return f"{self.parent.name}{str_ref.DELIMITER}{self.parent.get_attr_name(self)}"

    @property
    def pulse_mapping(self):
        return {label: pulse.pulse_name for label, pulse in self.operations.items()}

    def play(
        self,
        pulse_name: str,
        amplitude_scale: Union[float, AmpValuesType] = None,
        duration: QuaNumberType = None,
        condition: QuaExpressionType = None,
        chirp: ChirpType = None,
        truncate: QuaNumberType = None,
        timestamp_stream: StreamType = None,
        continue_chirp: bool = False,
        target: str = "",
        validate: bool = True,
    ):
        """Play a pulse on this channel.

        Args:
            pulse_name (str): The name of the pulse to play. Should be registered in
                `self.operations`.
            amplitude_scale (float, _PulseAmp): Amplitude scale of the pulse.
                Can be either a float, or qua.amp(float).
            duration (int): Duration of the pulse in units of the clock cycle (4ns).
                If not provided, the default pulse duration will be used. It is possible
                to dynamically change the duration of both constant and arbitrary
                pulses. Arbitrary pulses can only be stretched, not compressed.
            chirp (Union[(list[int], str), (int, str)]): Allows to perform
                piecewise linear sweep of the element's intermediate
                frequency in time. Input should be a tuple, with the 1st
                element being a list of rates and the second should be a
                string with the units. The units can be either: 'Hz/nsec',
                'mHz/nsec', 'uHz/nsec', 'pHz/nsec' or 'GHz/sec', 'MHz/sec',
                'KHz/sec', 'Hz/sec', 'mHz/sec'.
            truncate (Union[int, QUA variable of type int]): Allows playing
                only part of the pulse, truncating the end. If provided,
                will play only up to the given time in units of the clock
                cycle (4ns).
            condition (A logical expression to evaluate.): Will play analog
                pulse only if the condition's value is true. Any digital
                pulses associated with the operation will always play.
            timestamp_stream (Union[str, _ResultSource]): (Supported from
                QOP 2.2) Adding a `timestamp_stream` argument will save the
                time at which the operation occurred to a stream. If the
                `timestamp_stream` is a string ``label``, then the timestamp
                handle can be retrieved with
                `qm._results.JobResults.get` with the same ``label``.
            validate (bool): If True (default), validate that the pulse is registered
                in Channel.operations

        Note:
            The `element` argument from `qm.qua.play()`is not needed, as it is
            automatically set to `self.name`.

        """
        if validate and pulse_name not in self.operations:
            raise KeyError(
                f"Operation '{pulse_name}' not found in channel '{self.name}'"
            )

        if amplitude_scale is not None:
            if not isinstance(amplitude_scale, _PulseAmp):
                amplitude_scale = amp(amplitude_scale)
            pulse = pulse_name * amplitude_scale
        else:
            pulse = pulse_name

        # At the moment, self.name is not defined for Channel because it could
        # be a property or dataclass field in a subclass.
        # # TODO Find elegant solution for Channel.name.
        play(
            pulse=pulse,
            element=self.name,
            duration=duration,
            condition=condition,
            chirp=chirp,
            truncate=truncate,
            timestamp_stream=timestamp_stream,
            continue_chirp=continue_chirp,
            target=target,
        )

    def wait(self, duration: QuaNumberType, *other_elements: Union[str, "Channel"]):
        """Wait for the given duration on all provided elements without outputting anything.

        Duration is in units of the clock cycle (4ns)

        Args:
            duration (Union[int,QUA variable of type int]): time to wait in
                units of the clock cycle (4ns). Range: [4, $2^{31}-1$]
                in steps of 1.
            *other_elements (Union[str,sequence of str]): elements to wait on,
                in addition to this channel

        Warning:
            In case the value of this is outside the range above, unexpected results may occur.

        Note:
            The current channel element is always included in the wait operation.

        Note:
            The purpose of the `wait` operation is to add latency. In most cases, the
            latency added will be exactly the same as that specified by the QUA variable or
            the literal used. However, in some cases an additional computational latency may
            be added. If the actual wait time has significance, such as in characterization
            experiments, the actual wait time should always be verified with a simulator.
        """
        other_elements_str = [
            element if isinstance(element, str) else str(element)
            for element in other_elements
        ]
        wait(duration, self.name, *other_elements_str)

    def align(self, *other_elements):
        if not other_elements:
            align()
        else:
            other_elements_str = [
                element if isinstance(element, str) else str(element)
                for element in other_elements
            ]
            align(self.name, *other_elements_str)

    def update_frequency(
        self,
        new_frequency: QuaNumberType,
        units: str = "Hz",
        keep_phase: bool = False,
    ):
        """Dynamically update the frequency of the associated oscillator.

        This changes the frequency from the value defined in the channel.

        The behavior of the phase (continuous vs. coherent) is controlled by the
        ``keep_phase`` parameter and is discussed in the documentation.

        Args:
            new_frequency (int): The new frequency value to set in units set
                by ``units`` parameter. In steps of 1.
            units (str): units of new frequency. Useful when sub-Hz
                precision is required. Allowed units are "Hz", "mHz", "uHz",
                "nHz", "pHz"
            keep_phase (bool): Determine whether phase will be continuous
                through the change (if ``True``) or it will be coherent,
                only the frequency will change (if ``False``).

        Example:
            ```python
            with program() as prog:
                update_frequency("q1", 4e6) # will set the frequency to 4 MHz

                ### Example for sub-Hz resolution
                # will set the frequency to 100 Hz (due to casting to int)
                update_frequency("q1", 100.7)

                # will set the frequency to 100.7 Hz
                update_frequency("q1", 100700, units='mHz')
            ```
        """
        update_frequency(self.name, new_frequency, units, keep_phase)

    def frame_rotation(self, angle: QuaNumberType):
        r"""Shift the phase of the channel element's oscillator by the given angle.

        This is typically used for virtual z-rotations.

        Note:
            The fixed point format of QUA variables of type fixed is 4.28, meaning the
            phase must be between $-8$ and $8-2^{28}$. Otherwise the phase value will be
            invalid. It is therefore better to use `frame_rotation_2pi()` which avoids
            this issue.

        Note:
            The phase is accumulated with a resolution of 16 bit.
            Therefore, *N* changes to the phase can result in a phase (and amplitude)
            inaccuracy of about :math:`N \cdot 2^{-16}`. To null out this accumulated
            error, it is recommended to use `reset_frame(el)` from time to time.

        Args:
            angle (Union[float, QUA variable of type fixed]): The angle to
                add to the current phase (in radians)
            *elements (str): a single element whose oscillator's phase will
                be shifted. multiple elements can be given, in which case
                all of their oscillators' phases will be shifted

        """
        frame_rotation(angle, self.name)

    def frame_rotation_2pi(self, angle: QuaNumberType):
        r"""Shift the phase of the oscillator associated with an element by the given
        angle in units of 2pi radians.

        This is typically used for virtual z-rotations.

        Note:
            Unlike the case of frame_rotation(), this method performs the 2-pi radian
            wrap around of the angle automatically.

        Note:
            The phase is accumulated with a resolution of 16 bit.
            Therefore, *N* changes to the phase can result in a phase inaccuracy of
            about :math:`N \cdot 2^{-16}`. To null out this accumulated error, it is
            recommended to use `reset_frame(el)` from time to time.

        Args:
            angle (Union[float,QUA variable of type real]): The angle to add
                to the current phase (in $2\pi$ radians)
        """
        frame_rotation_2pi(angle, self.name)

    def _config_add_digital_outputs(self, config: Dict[str, dict]) -> None:
        """Adds the digital outputs to the QUA config.

        config.elements.<element_name>.digitalInputs will be updated with the digital
        outputs of this channel.

        Note that the digital outputs are added separately to the controller config in
        `DigitalOutputChannel.apply_to_config`.

        Args:
            config (dict): The QUA config that's in the process of being generated.
        """
        if not self.digital_outputs:
            return

        element_config = config["elements"][self.name]
        element_config.setdefault("digitalInputs", {})

        for name, digital_output in self.digital_outputs.items():
            digital_cfg = digital_output.generate_element_config()
            element_config["digitalInputs"][name] = digital_cfg

    def apply_to_config(self, config: Dict[str, dict]) -> None:
        """Adds this Channel to the QUA configuration.

        config.elements.<element_name> will be created, and the operations are added.

        Args:
            config (dict): The QUA config that's in the process of being generated.

        Raises:
            ValueError: If the channel already exists in the config.
        """
        if self.name in config["elements"]:
            raise ValueError(
                f"Cannot add channel '{self.name}' to the config because it already "
                f"exists. Existing entry: {config['elements'][self.name]}"
            )
        config["elements"][self.name] = {"operations": self.pulse_mapping}
        element_config = config["elements"][self.name]

        if self.intermediate_frequency is not None:
            element_config["intermediate_frequency"] = self.intermediate_frequency

        if self.thread is not None:
            element_config["thread"] = self.thread

        self._config_add_digital_outputs(config)

apply_to_config(config)

Adds this Channel to the QUA configuration.

config.elements. will be created, and the operations are added.

Parameters:

Name Type Description Default
config dict

The QUA config that's in the process of being generated.

required

Raises:

Type Description
ValueError

If the channel already exists in the config.

Source code in quam/components/channels.py
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
def apply_to_config(self, config: Dict[str, dict]) -> None:
    """Adds this Channel to the QUA configuration.

    config.elements.<element_name> will be created, and the operations are added.

    Args:
        config (dict): The QUA config that's in the process of being generated.

    Raises:
        ValueError: If the channel already exists in the config.
    """
    if self.name in config["elements"]:
        raise ValueError(
            f"Cannot add channel '{self.name}' to the config because it already "
            f"exists. Existing entry: {config['elements'][self.name]}"
        )
    config["elements"][self.name] = {"operations": self.pulse_mapping}
    element_config = config["elements"][self.name]

    if self.intermediate_frequency is not None:
        element_config["intermediate_frequency"] = self.intermediate_frequency

    if self.thread is not None:
        element_config["thread"] = self.thread

    self._config_add_digital_outputs(config)

frame_rotation(angle)

Shift the phase of the channel element's oscillator by the given angle.

This is typically used for virtual z-rotations.

Note

The fixed point format of QUA variables of type fixed is 4.28, meaning the phase must be between $-8$ and $8-2^{28}$. Otherwise the phase value will be invalid. It is therefore better to use frame_rotation_2pi() which avoids this issue.

Note

The phase is accumulated with a resolution of 16 bit. Therefore, N changes to the phase can result in a phase (and amplitude) inaccuracy of about :math:N \cdot 2^{-16}. To null out this accumulated error, it is recommended to use reset_frame(el) from time to time.

Parameters:

Name Type Description Default
angle Union[float, QUA variable of type fixed]

The angle to add to the current phase (in radians)

required
*elements str

a single element whose oscillator's phase will be shifted. multiple elements can be given, in which case all of their oscillators' phases will be shifted

required
Source code in quam/components/channels.py
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
def frame_rotation(self, angle: QuaNumberType):
    r"""Shift the phase of the channel element's oscillator by the given angle.

    This is typically used for virtual z-rotations.

    Note:
        The fixed point format of QUA variables of type fixed is 4.28, meaning the
        phase must be between $-8$ and $8-2^{28}$. Otherwise the phase value will be
        invalid. It is therefore better to use `frame_rotation_2pi()` which avoids
        this issue.

    Note:
        The phase is accumulated with a resolution of 16 bit.
        Therefore, *N* changes to the phase can result in a phase (and amplitude)
        inaccuracy of about :math:`N \cdot 2^{-16}`. To null out this accumulated
        error, it is recommended to use `reset_frame(el)` from time to time.

    Args:
        angle (Union[float, QUA variable of type fixed]): The angle to
            add to the current phase (in radians)
        *elements (str): a single element whose oscillator's phase will
            be shifted. multiple elements can be given, in which case
            all of their oscillators' phases will be shifted

    """
    frame_rotation(angle, self.name)

frame_rotation_2pi(angle)

Shift the phase of the oscillator associated with an element by the given angle in units of 2pi radians.

This is typically used for virtual z-rotations.

Note

Unlike the case of frame_rotation(), this method performs the 2-pi radian wrap around of the angle automatically.

Note

The phase is accumulated with a resolution of 16 bit. Therefore, N changes to the phase can result in a phase inaccuracy of about :math:N \cdot 2^{-16}. To null out this accumulated error, it is recommended to use reset_frame(el) from time to time.

Parameters:

Name Type Description Default
angle Union[float,QUA variable of type real]

The angle to add to the current phase (in $2\pi$ radians)

required
Source code in quam/components/channels.py
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
def frame_rotation_2pi(self, angle: QuaNumberType):
    r"""Shift the phase of the oscillator associated with an element by the given
    angle in units of 2pi radians.

    This is typically used for virtual z-rotations.

    Note:
        Unlike the case of frame_rotation(), this method performs the 2-pi radian
        wrap around of the angle automatically.

    Note:
        The phase is accumulated with a resolution of 16 bit.
        Therefore, *N* changes to the phase can result in a phase inaccuracy of
        about :math:`N \cdot 2^{-16}`. To null out this accumulated error, it is
        recommended to use `reset_frame(el)` from time to time.

    Args:
        angle (Union[float,QUA variable of type real]): The angle to add
            to the current phase (in $2\pi$ radians)
    """
    frame_rotation_2pi(angle, self.name)

play(pulse_name, amplitude_scale=None, duration=None, condition=None, chirp=None, truncate=None, timestamp_stream=None, continue_chirp=False, target='', validate=True)

Play a pulse on this channel.

Parameters:

Name Type Description Default
pulse_name str

The name of the pulse to play. Should be registered in self.operations.

required
amplitude_scale (float, _PulseAmp)

Amplitude scale of the pulse. Can be either a float, or qua.amp(float).

None
duration int

Duration of the pulse in units of the clock cycle (4ns). If not provided, the default pulse duration will be used. It is possible to dynamically change the duration of both constant and arbitrary pulses. Arbitrary pulses can only be stretched, not compressed.

None
chirp Union[(list[int], str), (int, str)]

Allows to perform piecewise linear sweep of the element's intermediate frequency in time. Input should be a tuple, with the 1st element being a list of rates and the second should be a string with the units. The units can be either: 'Hz/nsec', 'mHz/nsec', 'uHz/nsec', 'pHz/nsec' or 'GHz/sec', 'MHz/sec', 'KHz/sec', 'Hz/sec', 'mHz/sec'.

None
truncate Union[int, QUA variable of type int]

Allows playing only part of the pulse, truncating the end. If provided, will play only up to the given time in units of the clock cycle (4ns).

None
condition A logical expression to evaluate.

Will play analog pulse only if the condition's value is true. Any digital pulses associated with the operation will always play.

None
timestamp_stream Union[str, _ResultSource]

(Supported from QOP 2.2) Adding a timestamp_stream argument will save the time at which the operation occurred to a stream. If the timestamp_stream is a string label, then the timestamp handle can be retrieved with qm._results.JobResults.get with the same label.

None
validate bool

If True (default), validate that the pulse is registered in Channel.operations

True
Note

The element argument from qm.qua.play()is not needed, as it is automatically set to self.name.

Source code in quam/components/channels.py
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
def play(
    self,
    pulse_name: str,
    amplitude_scale: Union[float, AmpValuesType] = None,
    duration: QuaNumberType = None,
    condition: QuaExpressionType = None,
    chirp: ChirpType = None,
    truncate: QuaNumberType = None,
    timestamp_stream: StreamType = None,
    continue_chirp: bool = False,
    target: str = "",
    validate: bool = True,
):
    """Play a pulse on this channel.

    Args:
        pulse_name (str): The name of the pulse to play. Should be registered in
            `self.operations`.
        amplitude_scale (float, _PulseAmp): Amplitude scale of the pulse.
            Can be either a float, or qua.amp(float).
        duration (int): Duration of the pulse in units of the clock cycle (4ns).
            If not provided, the default pulse duration will be used. It is possible
            to dynamically change the duration of both constant and arbitrary
            pulses. Arbitrary pulses can only be stretched, not compressed.
        chirp (Union[(list[int], str), (int, str)]): Allows to perform
            piecewise linear sweep of the element's intermediate
            frequency in time. Input should be a tuple, with the 1st
            element being a list of rates and the second should be a
            string with the units. The units can be either: 'Hz/nsec',
            'mHz/nsec', 'uHz/nsec', 'pHz/nsec' or 'GHz/sec', 'MHz/sec',
            'KHz/sec', 'Hz/sec', 'mHz/sec'.
        truncate (Union[int, QUA variable of type int]): Allows playing
            only part of the pulse, truncating the end. If provided,
            will play only up to the given time in units of the clock
            cycle (4ns).
        condition (A logical expression to evaluate.): Will play analog
            pulse only if the condition's value is true. Any digital
            pulses associated with the operation will always play.
        timestamp_stream (Union[str, _ResultSource]): (Supported from
            QOP 2.2) Adding a `timestamp_stream` argument will save the
            time at which the operation occurred to a stream. If the
            `timestamp_stream` is a string ``label``, then the timestamp
            handle can be retrieved with
            `qm._results.JobResults.get` with the same ``label``.
        validate (bool): If True (default), validate that the pulse is registered
            in Channel.operations

    Note:
        The `element` argument from `qm.qua.play()`is not needed, as it is
        automatically set to `self.name`.

    """
    if validate and pulse_name not in self.operations:
        raise KeyError(
            f"Operation '{pulse_name}' not found in channel '{self.name}'"
        )

    if amplitude_scale is not None:
        if not isinstance(amplitude_scale, _PulseAmp):
            amplitude_scale = amp(amplitude_scale)
        pulse = pulse_name * amplitude_scale
    else:
        pulse = pulse_name

    # At the moment, self.name is not defined for Channel because it could
    # be a property or dataclass field in a subclass.
    # # TODO Find elegant solution for Channel.name.
    play(
        pulse=pulse,
        element=self.name,
        duration=duration,
        condition=condition,
        chirp=chirp,
        truncate=truncate,
        timestamp_stream=timestamp_stream,
        continue_chirp=continue_chirp,
        target=target,
    )

update_frequency(new_frequency, units='Hz', keep_phase=False)

Dynamically update the frequency of the associated oscillator.

This changes the frequency from the value defined in the channel.

The behavior of the phase (continuous vs. coherent) is controlled by the keep_phase parameter and is discussed in the documentation.

Parameters:

Name Type Description Default
new_frequency int

The new frequency value to set in units set by units parameter. In steps of 1.

required
units str

units of new frequency. Useful when sub-Hz precision is required. Allowed units are "Hz", "mHz", "uHz", "nHz", "pHz"

'Hz'
keep_phase bool

Determine whether phase will be continuous through the change (if True) or it will be coherent, only the frequency will change (if False).

False
Example
with program() as prog:
    update_frequency("q1", 4e6) # will set the frequency to 4 MHz

    ### Example for sub-Hz resolution
    # will set the frequency to 100 Hz (due to casting to int)
    update_frequency("q1", 100.7)

    # will set the frequency to 100.7 Hz
    update_frequency("q1", 100700, units='mHz')
Source code in quam/components/channels.py
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
def update_frequency(
    self,
    new_frequency: QuaNumberType,
    units: str = "Hz",
    keep_phase: bool = False,
):
    """Dynamically update the frequency of the associated oscillator.

    This changes the frequency from the value defined in the channel.

    The behavior of the phase (continuous vs. coherent) is controlled by the
    ``keep_phase`` parameter and is discussed in the documentation.

    Args:
        new_frequency (int): The new frequency value to set in units set
            by ``units`` parameter. In steps of 1.
        units (str): units of new frequency. Useful when sub-Hz
            precision is required. Allowed units are "Hz", "mHz", "uHz",
            "nHz", "pHz"
        keep_phase (bool): Determine whether phase will be continuous
            through the change (if ``True``) or it will be coherent,
            only the frequency will change (if ``False``).

    Example:
        ```python
        with program() as prog:
            update_frequency("q1", 4e6) # will set the frequency to 4 MHz

            ### Example for sub-Hz resolution
            # will set the frequency to 100 Hz (due to casting to int)
            update_frequency("q1", 100.7)

            # will set the frequency to 100.7 Hz
            update_frequency("q1", 100700, units='mHz')
        ```
    """
    update_frequency(self.name, new_frequency, units, keep_phase)

wait(duration, *other_elements)

Wait for the given duration on all provided elements without outputting anything.

Duration is in units of the clock cycle (4ns)

Parameters:

Name Type Description Default
duration Union[int,QUA variable of type int]

time to wait in units of the clock cycle (4ns). Range: [4, $2^{31}-1$] in steps of 1.

required
*other_elements Union[str,sequence of str]

elements to wait on, in addition to this channel

()
Warning

In case the value of this is outside the range above, unexpected results may occur.

Note

The current channel element is always included in the wait operation.

Note

The purpose of the wait operation is to add latency. In most cases, the latency added will be exactly the same as that specified by the QUA variable or the literal used. However, in some cases an additional computational latency may be added. If the actual wait time has significance, such as in characterization experiments, the actual wait time should always be verified with a simulator.

Source code in quam/components/channels.py
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
def wait(self, duration: QuaNumberType, *other_elements: Union[str, "Channel"]):
    """Wait for the given duration on all provided elements without outputting anything.

    Duration is in units of the clock cycle (4ns)

    Args:
        duration (Union[int,QUA variable of type int]): time to wait in
            units of the clock cycle (4ns). Range: [4, $2^{31}-1$]
            in steps of 1.
        *other_elements (Union[str,sequence of str]): elements to wait on,
            in addition to this channel

    Warning:
        In case the value of this is outside the range above, unexpected results may occur.

    Note:
        The current channel element is always included in the wait operation.

    Note:
        The purpose of the `wait` operation is to add latency. In most cases, the
        latency added will be exactly the same as that specified by the QUA variable or
        the literal used. However, in some cases an additional computational latency may
        be added. If the actual wait time has significance, such as in characterization
        experiments, the actual wait time should always be verified with a simulator.
    """
    other_elements_str = [
        element if isinstance(element, str) else str(element)
        for element in other_elements
    ]
    wait(duration, self.name, *other_elements_str)

DigitalOutputChannel

Bases: QuamComponent

QuAM component for a digital output channel (signal going out of the OPX)

Should be added to Channel.digital_outputs so that it's also added to the respective element in the QUA config.

Parameters:

Name Type Description Default
opx_output Tuple[str, int]

Channel output port from the OPX perspective, E.g. ("con1", 1)

required
delay int

Delay in nanoseconds. An intrinsic negative delay of 136 ns exists by default.

required
buffer int

Digital pulses played to this element will be convolved with a digital pulse of value 1 with this length [ns].

required
shareable bool

If True, the digital output can be shared with other QM instances. Default is False

required
inverted bool

If True, the digital output is inverted. Default is False.

required

.

Source code in quam/components/channels.py
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
@quam_dataclass
class DigitalOutputChannel(QuamComponent):
    """QuAM component for a digital output channel (signal going out of the OPX)

    Should be added to `Channel.digital_outputs` so that it's also added to the
    respective element in the QUA config.

    Args:
        opx_output (Tuple[str, int]): Channel output port from the OPX perspective,
            E.g. ("con1", 1)
        delay (int, optional): Delay in nanoseconds. An intrinsic negative delay of
            136 ns exists by default.
        buffer (int, optional): Digital pulses played to this element will be convolved
            with a digital pulse of value 1 with this length [ns].
        shareable (bool, optional): If True, the digital output can be shared with other
            QM instances. Default is False
        inverted (bool, optional): If True, the digital output is inverted.
            Default is False.
    ."""

    opx_output: Union[Tuple[str, int], Tuple[str, int, int], DigitalOutputPort]
    delay: int = None
    buffer: int = None

    shareable: bool = None
    inverted: bool = None

    def generate_element_config(self) -> Dict[str, int]:
        """Generates the config entry for a digital channel in the QUA config.

        This config entry goes into:
        config.elements.<element_name>.digitalInputs.<opx_output[1]>

        Returns:
            Dict[str, int]: The digital channel config entry.
                Contains "port", and optionally "delay", "buffer" if specified
        """
        if isinstance(self.opx_output, DigitalOutputPort):
            opx_output = self.opx_output.port_tuple
        else:
            opx_output = tuple(self.opx_output)

        digital_cfg: Dict[str, Any] = {"port": opx_output}
        if self.delay is not None:
            digital_cfg["delay"] = self.delay
        if self.buffer is not None:
            digital_cfg["buffer"] = self.buffer
        return digital_cfg

    def apply_to_config(self, config: dict) -> None:
        """Adds this DigitalOutputChannel to the QUA configuration.

        config.controllers.<controller_name>.digital_outputs.<port> will be updated
        with the shareable and inverted settings of this channel if specified.

        See [`QuamComponent.apply_to_config`][quam.core.quam_classes.QuamComponent.apply_to_config]
        for details.
        """
        if isinstance(self.opx_output, DigitalOutputPort):
            if self.shareable is not None:
                warnings.warn(
                    f"Property {self.name}.shareable (={self.shareable}) is ignored "
                    "because it should be set in {self.name}.opx_output.shareable"
                )
            if self.inverted is not None:
                warnings.warn(
                    f"Property {self.name}.inverted (={self.inverted}) is ignored "
                    "because it should be set in {self.name}.opx_output.inverted"
                )
            return

        shareable = self.shareable if self.shareable is not None else False
        inverted = self.inverted if self.inverted is not None else False
        if len(self.opx_output) == 2:
            digital_output_port = OPXPlusDigitalOutputPort(
                *self.opx_output, shareable=shareable, inverted=inverted
            )
        else:
            digital_output_port = FEMDigitalOutputPort(
                *self.opx_output, shareable=shareable, inverted=inverted
            )
        digital_output_port.apply_to_config(config)

apply_to_config(config)

Adds this DigitalOutputChannel to the QUA configuration.

config.controllers..digital_outputs. will be updated with the shareable and inverted settings of this channel if specified.

See QuamComponent.apply_to_config for details.

Source code in quam/components/channels.py
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
def apply_to_config(self, config: dict) -> None:
    """Adds this DigitalOutputChannel to the QUA configuration.

    config.controllers.<controller_name>.digital_outputs.<port> will be updated
    with the shareable and inverted settings of this channel if specified.

    See [`QuamComponent.apply_to_config`][quam.core.quam_classes.QuamComponent.apply_to_config]
    for details.
    """
    if isinstance(self.opx_output, DigitalOutputPort):
        if self.shareable is not None:
            warnings.warn(
                f"Property {self.name}.shareable (={self.shareable}) is ignored "
                "because it should be set in {self.name}.opx_output.shareable"
            )
        if self.inverted is not None:
            warnings.warn(
                f"Property {self.name}.inverted (={self.inverted}) is ignored "
                "because it should be set in {self.name}.opx_output.inverted"
            )
        return

    shareable = self.shareable if self.shareable is not None else False
    inverted = self.inverted if self.inverted is not None else False
    if len(self.opx_output) == 2:
        digital_output_port = OPXPlusDigitalOutputPort(
            *self.opx_output, shareable=shareable, inverted=inverted
        )
    else:
        digital_output_port = FEMDigitalOutputPort(
            *self.opx_output, shareable=shareable, inverted=inverted
        )
    digital_output_port.apply_to_config(config)

generate_element_config()

Generates the config entry for a digital channel in the QUA config.

This config entry goes into: config.elements..digitalInputs.

Returns:

Type Description
Dict[str, int]

Dict[str, int]: The digital channel config entry. Contains "port", and optionally "delay", "buffer" if specified

Source code in quam/components/channels.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
def generate_element_config(self) -> Dict[str, int]:
    """Generates the config entry for a digital channel in the QUA config.

    This config entry goes into:
    config.elements.<element_name>.digitalInputs.<opx_output[1]>

    Returns:
        Dict[str, int]: The digital channel config entry.
            Contains "port", and optionally "delay", "buffer" if specified
    """
    if isinstance(self.opx_output, DigitalOutputPort):
        opx_output = self.opx_output.port_tuple
    else:
        opx_output = tuple(self.opx_output)

    digital_cfg: Dict[str, Any] = {"port": opx_output}
    if self.delay is not None:
        digital_cfg["delay"] = self.delay
    if self.buffer is not None:
        digital_cfg["buffer"] = self.buffer
    return digital_cfg

IQChannel

Bases: _OutComplexChannel

QuAM component for an IQ output channel.

Parameters:

Name Type Description Default
operations Dict[str, Pulse]

A dictionary of pulses to be played on this channel. The key is the pulse label (e.g. "X90") and value is a Pulse.

required
id (str, int)

The id of the channel, used to generate the name. Can be a string, or an integer in which case it will add Channel._default_label.

required
opx_output_I Tuple[str, int]

Channel I output port from the OPX perspective, a tuple of (controller_name, port).

required
opx_output_Q Tuple[str, int]

Channel Q output port from the OPX perspective, a tuple of (controller_name, port).

required
opx_output_offset_I float

The offset of the I channel. Default is 0.

required
opx_output_offset_Q float

The offset of the Q channel. Default is 0.

required
intermediate_frequency float

Intermediate frequency of the mixer. Default is 0.0

required
LO_frequency float

Local oscillator frequency. Default is the LO frequency of the frequency converter up component.

required
RF_frequency float

RF frequency of the mixer. By default, the RF frequency is inferred by adding the LO frequency and the intermediate frequency.

required
frequency_converter_up FrequencyConverter

Frequency converter QuAM component for the IQ output.

required
Source code in quam/components/channels.py
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
@quam_dataclass
class IQChannel(_OutComplexChannel):
    """QuAM component for an IQ output channel.

    Args:
        operations (Dict[str, Pulse]): A dictionary of pulses to be played on this
            channel. The key is the pulse label (e.g. "X90") and value is a Pulse.
        id (str, int): The id of the channel, used to generate the name.
            Can be a string, or an integer in which case it will add
            `Channel._default_label`.
        opx_output_I (Tuple[str, int]): Channel I output port from the OPX perspective,
            a tuple of (controller_name, port).
        opx_output_Q (Tuple[str, int]): Channel Q output port from the OPX perspective,
            a tuple of (controller_name, port).
        opx_output_offset_I float: The offset of the I channel. Default is 0.
        opx_output_offset_Q float: The offset of the Q channel. Default is 0.
        intermediate_frequency (float): Intermediate frequency of the mixer.
            Default is 0.0
        LO_frequency (float): Local oscillator frequency. Default is the LO frequency
            of the frequency converter up component.
        RF_frequency (float): RF frequency of the mixer. By default, the RF frequency
            is inferred by adding the LO frequency and the intermediate frequency.
        frequency_converter_up (FrequencyConverter): Frequency converter QuAM component
            for the IQ output.
    """

    opx_output_I: Union[Tuple[str, int], Tuple[str, int, int], LFAnalogOutputPort]
    opx_output_Q: Union[Tuple[str, int], Tuple[str, int, int], LFAnalogOutputPort]

    opx_output_offset_I: float = None
    opx_output_offset_Q: float = None

    frequency_converter_up: BaseFrequencyConverter

    LO_frequency: float = "#./frequency_converter_up/LO_frequency"
    RF_frequency: float = "#./inferred_RF_frequency"

    _default_label: ClassVar[str] = "IQ"

    @property
    def local_oscillator(self) -> Optional[LocalOscillator]:
        return getattr(self.frequency_converter_up, "local_oscillator", None)

    @property
    def mixer(self) -> Optional[Mixer]:
        return getattr(self.frequency_converter_up, "mixer", None)

    @property
    def rf_frequency(self):
        warnings.warn(
            "rf_frequency is deprecated, use RF_frequency instead", DeprecationWarning
        )
        return self.frequency_converter_up.LO_frequency + self.intermediate_frequency

    def set_dc_offset(self, offset: QuaNumberType, element_input: Literal["I", "Q"]):
        """Set the DC offset of an element's input to the given value.
        This value will remain the DC offset until changed or until the Quantum Machine
        is closed.

        Args:
            offset (QuaNumberType): The DC offset to set the input to.
                This is limited by the OPX output voltage range.
                The number can be a QUA variable
            element_input (Literal["I", "Q"]): The element input to set the offset for.

        Raises:
            ValueError: If element_input is not "I" or "Q"
        """
        if element_input not in ["I", "Q"]:
            raise ValueError(
                f"element_input should be either 'I' or 'Q', got {element_input}"
            )
        set_dc_offset(element=self.name, element_input=element_input, offset=offset)

    def apply_to_config(self, config: dict):
        """Adds this IQChannel to the QUA configuration.

        See [`QuamComponent.apply_to_config`][quam.core.quam_classes.QuamComponent.apply_to_config]
        for details.
        """
        # Add pulses & waveforms
        super().apply_to_config(config)

        if str_ref.is_reference(self.name):
            raise AttributeError(
                f"Channel {self.get_reference()} cannot be added to the config because"
                " it doesn't have a name. Either set channel.id to a string or"
                " integer, or channel should be an attribute of another QuAM component"
                " with a name."
            )

        element_config = config["elements"][self.name]

        from quam.components.octave import OctaveUpConverter

        if isinstance(self.frequency_converter_up, OctaveUpConverter):
            octave = self.frequency_converter_up.octave
            if octave is None:
                raise ValueError(
                    f"Error generating config: channel {self.name} has an "
                    f"OctaveUpConverter (id={self.frequency_converter_up.id}) without "
                    "an attached Octave"
                )
            element_config["RF_inputs"] = {
                "port": (octave.name, self.frequency_converter_up.id)
            }
        elif str_ref.is_reference(self.frequency_converter_up):
            raise ValueError(
                f"Error generating config: channel {self.name} could not determine "
                f'"frequency_converter_up", it seems to point to a non-existent '
                f"reference: {self.frequency_converter_up}"
            )
        else:

            element_config["mixInputs"] = {}  # To be filled in next section
            if self.mixer is not None:
                element_config["mixInputs"]["mixer"] = self.mixer.name
            if self.local_oscillator is not None:
                element_config["mixInputs"][
                    "lo_frequency"
                ] = self.local_oscillator.frequency

        opx_outputs = [self.opx_output_I, self.opx_output_Q]
        offsets = [self.opx_output_offset_I, self.opx_output_offset_Q]
        for I_or_Q, opx_output, offset in zip("IQ", opx_outputs, offsets):
            if isinstance(opx_output, LFAnalogOutputPort):
                opx_port = opx_output
            elif len(opx_output) == 2:
                opx_port = OPXPlusAnalogOutputPort(*opx_output, offset=offset)
                opx_port.apply_to_config(config)
            else:
                opx_port = LFFEMAnalogOutputPort(*opx_output, offset=offset)
                opx_port.apply_to_config(config)

            if "mixInputs" in element_config:
                element_config["mixInputs"][I_or_Q] = opx_port.port_tuple

apply_to_config(config)

Adds this IQChannel to the QUA configuration.

See QuamComponent.apply_to_config for details.

Source code in quam/components/channels.py
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
def apply_to_config(self, config: dict):
    """Adds this IQChannel to the QUA configuration.

    See [`QuamComponent.apply_to_config`][quam.core.quam_classes.QuamComponent.apply_to_config]
    for details.
    """
    # Add pulses & waveforms
    super().apply_to_config(config)

    if str_ref.is_reference(self.name):
        raise AttributeError(
            f"Channel {self.get_reference()} cannot be added to the config because"
            " it doesn't have a name. Either set channel.id to a string or"
            " integer, or channel should be an attribute of another QuAM component"
            " with a name."
        )

    element_config = config["elements"][self.name]

    from quam.components.octave import OctaveUpConverter

    if isinstance(self.frequency_converter_up, OctaveUpConverter):
        octave = self.frequency_converter_up.octave
        if octave is None:
            raise ValueError(
                f"Error generating config: channel {self.name} has an "
                f"OctaveUpConverter (id={self.frequency_converter_up.id}) without "
                "an attached Octave"
            )
        element_config["RF_inputs"] = {
            "port": (octave.name, self.frequency_converter_up.id)
        }
    elif str_ref.is_reference(self.frequency_converter_up):
        raise ValueError(
            f"Error generating config: channel {self.name} could not determine "
            f'"frequency_converter_up", it seems to point to a non-existent '
            f"reference: {self.frequency_converter_up}"
        )
    else:

        element_config["mixInputs"] = {}  # To be filled in next section
        if self.mixer is not None:
            element_config["mixInputs"]["mixer"] = self.mixer.name
        if self.local_oscillator is not None:
            element_config["mixInputs"][
                "lo_frequency"
            ] = self.local_oscillator.frequency

    opx_outputs = [self.opx_output_I, self.opx_output_Q]
    offsets = [self.opx_output_offset_I, self.opx_output_offset_Q]
    for I_or_Q, opx_output, offset in zip("IQ", opx_outputs, offsets):
        if isinstance(opx_output, LFAnalogOutputPort):
            opx_port = opx_output
        elif len(opx_output) == 2:
            opx_port = OPXPlusAnalogOutputPort(*opx_output, offset=offset)
            opx_port.apply_to_config(config)
        else:
            opx_port = LFFEMAnalogOutputPort(*opx_output, offset=offset)
            opx_port.apply_to_config(config)

        if "mixInputs" in element_config:
            element_config["mixInputs"][I_or_Q] = opx_port.port_tuple

set_dc_offset(offset, element_input)

Set the DC offset of an element's input to the given value. This value will remain the DC offset until changed or until the Quantum Machine is closed.

Parameters:

Name Type Description Default
offset QuaNumberType

The DC offset to set the input to. This is limited by the OPX output voltage range. The number can be a QUA variable

required
element_input Literal['I', 'Q']

The element input to set the offset for.

required

Raises:

Type Description
ValueError

If element_input is not "I" or "Q"

Source code in quam/components/channels.py
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
def set_dc_offset(self, offset: QuaNumberType, element_input: Literal["I", "Q"]):
    """Set the DC offset of an element's input to the given value.
    This value will remain the DC offset until changed or until the Quantum Machine
    is closed.

    Args:
        offset (QuaNumberType): The DC offset to set the input to.
            This is limited by the OPX output voltage range.
            The number can be a QUA variable
        element_input (Literal["I", "Q"]): The element input to set the offset for.

    Raises:
        ValueError: If element_input is not "I" or "Q"
    """
    if element_input not in ["I", "Q"]:
        raise ValueError(
            f"element_input should be either 'I' or 'Q', got {element_input}"
        )
    set_dc_offset(element=self.name, element_input=element_input, offset=offset)

InIQChannel

Bases: _InComplexChannel

QuAM component for an IQ input channel

Parameters:

Name Type Description Default
operations Dict[str, Pulse]

A dictionary of pulses to be played on this channel. The key is the pulse label (e.g. "readout") and value is a ReadoutPulse.

required
id (str, int)

The id of the channel, used to generate the name. Can be a string, or an integer in which case it will add Channel._default_label.

required
opx_input_I Tuple[str, int]

Channel I input port from the OPX perspective, a tuple of (controller_name, port).

required
opx_input_Q Tuple[str, int]

Channel Q input port from the OPX perspective, a tuple of (controller_name, port).

required
opx_input_offset_I float

The offset of the I channel. Default is 0.

required
opx_input_offset_Q float

The offset of the Q channel. Default is 0.

required
frequency_converter_down Optional[FrequencyConverter]

Frequency converter QuAM component for the IQ input port. Only needed for the old Octave.

required
time_of_flight int

Round-trip signal duration in nanoseconds.

required
smearing int

Additional window of ADC integration in nanoseconds. Used to account for signal smearing.

required
input_gain float

The gain of the input channel. Default is None.

required
Source code in quam/components/channels.py
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
@quam_dataclass
class InIQChannel(_InComplexChannel):
    """QuAM component for an IQ input channel

    Args:
        operations (Dict[str, Pulse]): A dictionary of pulses to be played on this
            channel. The key is the pulse label (e.g. "readout") and value is a
            ReadoutPulse.
        id (str, int): The id of the channel, used to generate the name.
            Can be a string, or an integer in which case it will add
            `Channel._default_label`.
        opx_input_I (Tuple[str, int]): Channel I input port from the OPX perspective,
            a tuple of (controller_name, port).
        opx_input_Q (Tuple[str, int]): Channel Q input port from the OPX perspective,
            a tuple of (controller_name, port).
        opx_input_offset_I float: The offset of the I channel. Default is 0.
        opx_input_offset_Q float: The offset of the Q channel. Default is 0.
        frequency_converter_down (Optional[FrequencyConverter]): Frequency converter
            QuAM component for the IQ input port. Only needed for the old Octave.
        time_of_flight (int): Round-trip signal duration in nanoseconds.
        smearing (int): Additional window of ADC integration in nanoseconds.
            Used to account for signal smearing.
        input_gain (float): The gain of the input channel. Default is None.
    """

    opx_input_I: Union[Tuple[str, int], Tuple[str, int, int], LFAnalogInputPort]
    opx_input_Q: Union[Tuple[str, int], Tuple[str, int, int], LFAnalogInputPort]

    time_of_flight: int = 24
    smearing: int = 0

    opx_input_offset_I: float = None
    opx_input_offset_Q: float = None

    input_gain: Optional[int] = None

    frequency_converter_down: BaseFrequencyConverter = None

    _default_label: ClassVar[str] = "IQ"

    def apply_to_config(self, config: dict):
        """Adds this InOutIQChannel to the QUA configuration.

        See [`QuamComponent.apply_to_config`][quam.core.quam_classes.QuamComponent.apply_to_config]
        for details.
        """
        super().apply_to_config(config)

        # Note outputs instead of inputs because it's w.r.t. the QPU
        element_config = config["elements"][self.name]
        element_config["smearing"] = self.smearing
        element_config["time_of_flight"] = self.time_of_flight

        from quam.components.octave import OctaveDownConverter

        if isinstance(self.frequency_converter_down, OctaveDownConverter):
            octave = self.frequency_converter_down.octave
            if octave is None:
                raise ValueError(
                    f"Error generating config: channel {self.name} has an "
                    f"OctaveDownConverter (id={self.frequency_converter_down.id}) "
                    "without an attached Octave"
                )
            element_config["RF_outputs"] = {
                "port": (octave.name, self.frequency_converter_down.id)
            }
        elif str_ref.is_reference(self.frequency_converter_down):
            raise ValueError(
                f"Error generating config: channel {self.name} could not determine "
                f'"frequency_converter_down", it seems to point to a non-existent '
                f"reference: {self.frequency_converter_down}"
            )
        else:
            # To be filled in next section
            element_config["outputs"] = {}

        opx_inputs = [self.opx_input_I, self.opx_input_Q]
        offsets = [self.opx_input_offset_I, self.opx_input_offset_Q]
        input_gain = int(self.input_gain if self.input_gain is not None else 0)
        for k, (opx_input, offset) in enumerate(zip(opx_inputs, offsets), start=1):
            if isinstance(opx_input, LFAnalogInputPort):
                opx_port = opx_input
            elif len(opx_input) == 2:
                opx_port = OPXPlusAnalogInputPort(
                    *opx_input, offset=offset, gain_db=input_gain
                )
                opx_port.apply_to_config(config)
            else:
                opx_port = LFFEMAnalogInputPort(
                    *opx_input, offset=offset, gain_db=input_gain
                )
                opx_port.apply_to_config(config)
            if not isinstance(self.frequency_converter_down, OctaveDownConverter):
                element_config["outputs"][f"out{k}"] = opx_port.port_tuple

apply_to_config(config)

Adds this InOutIQChannel to the QUA configuration.

See QuamComponent.apply_to_config for details.

Source code in quam/components/channels.py
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
def apply_to_config(self, config: dict):
    """Adds this InOutIQChannel to the QUA configuration.

    See [`QuamComponent.apply_to_config`][quam.core.quam_classes.QuamComponent.apply_to_config]
    for details.
    """
    super().apply_to_config(config)

    # Note outputs instead of inputs because it's w.r.t. the QPU
    element_config = config["elements"][self.name]
    element_config["smearing"] = self.smearing
    element_config["time_of_flight"] = self.time_of_flight

    from quam.components.octave import OctaveDownConverter

    if isinstance(self.frequency_converter_down, OctaveDownConverter):
        octave = self.frequency_converter_down.octave
        if octave is None:
            raise ValueError(
                f"Error generating config: channel {self.name} has an "
                f"OctaveDownConverter (id={self.frequency_converter_down.id}) "
                "without an attached Octave"
            )
        element_config["RF_outputs"] = {
            "port": (octave.name, self.frequency_converter_down.id)
        }
    elif str_ref.is_reference(self.frequency_converter_down):
        raise ValueError(
            f"Error generating config: channel {self.name} could not determine "
            f'"frequency_converter_down", it seems to point to a non-existent '
            f"reference: {self.frequency_converter_down}"
        )
    else:
        # To be filled in next section
        element_config["outputs"] = {}

    opx_inputs = [self.opx_input_I, self.opx_input_Q]
    offsets = [self.opx_input_offset_I, self.opx_input_offset_Q]
    input_gain = int(self.input_gain if self.input_gain is not None else 0)
    for k, (opx_input, offset) in enumerate(zip(opx_inputs, offsets), start=1):
        if isinstance(opx_input, LFAnalogInputPort):
            opx_port = opx_input
        elif len(opx_input) == 2:
            opx_port = OPXPlusAnalogInputPort(
                *opx_input, offset=offset, gain_db=input_gain
            )
            opx_port.apply_to_config(config)
        else:
            opx_port = LFFEMAnalogInputPort(
                *opx_input, offset=offset, gain_db=input_gain
            )
            opx_port.apply_to_config(config)
        if not isinstance(self.frequency_converter_down, OctaveDownConverter):
            element_config["outputs"][f"out{k}"] = opx_port.port_tuple

InIQOutSingleChannel

Bases: SingleChannel, InIQChannel

QuAM component for an IQ input channel with a single output.

Parameters:

Name Type Description Default
operations Dict[str, Pulse]

A dictionary of pulses to be played on this channel. The key is the pulse label (e.g. "readout") and value is a ReadoutPulse.

required
id (str, int)

The id of the channel, used to generate the name. Can be a string, or an integer in which case it will add Channel._default_label.

required
opx_output Tuple[str, int]

Channel output port from the OPX perspective, a tuple of (controller_name, port).

required
opx_output_offset float

DC offset for the output port.

required
opx_input_I Tuple[str, int]

Channel I input port from the OPX perspective, a tuple of (controller_name, port).

required
opx_input_Q Tuple[str, int]

Channel Q input port from the OPX perspective, a tuple of (controller_name, port).

required
opx_input_offset_I float

The offset of the I channel. Default is 0.

required
opx_input_offset_Q float

The offset of the Q channel. Default is 0.

required
filter_fir_taps List[float]

FIR filter taps for the output port.

required
filter_iir_taps List[float]

IIR filter taps for the output port.

required
intermediate_frequency float

Intermediate frequency of OPX output, default is None.

required
time_of_flight int

Round-trip signal duration in nanoseconds.

required
smearing int

Additional window of ADC integration in nanoseconds. Used to account for signal smearing.

required
Source code in quam/components/channels.py
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
@quam_dataclass
class InIQOutSingleChannel(SingleChannel, InIQChannel):
    """QuAM component for an IQ input channel with a single output.

    Args:
        operations (Dict[str, Pulse]): A dictionary of pulses to be played on this
            channel. The key is the pulse label (e.g. "readout") and value is a
            ReadoutPulse.
        id (str, int): The id of the channel, used to generate the name.
            Can be a string, or an integer in which case it will add
            `Channel._default_label`.
        opx_output (Tuple[str, int]): Channel output port from the OPX perspective,
            a tuple of (controller_name, port).
        opx_output_offset (float): DC offset for the output port.
        opx_input_I (Tuple[str, int]): Channel I input port from the OPX perspective,
            a tuple of (controller_name, port).
        opx_input_Q (Tuple[str, int]): Channel Q input port from the OPX perspective,
            a tuple of (controller_name, port).
        opx_input_offset_I float: The offset of the I channel. Default is 0.
        opx_input_offset_Q float: The offset of the Q channel. Default is 0.
        filter_fir_taps (List[float]): FIR filter taps for the output port.
        filter_iir_taps (List[float]): IIR filter taps for the output port.
        intermediate_frequency (float): Intermediate frequency of OPX output, default
            is None.
        time_of_flight (int): Round-trip signal duration in nanoseconds.
        smearing (int): Additional window of ADC integration in nanoseconds.
            Used to account for signal smearing.
    """

    pass

InMWChannel

Bases: _InComplexChannel

QuAM component for a MW FEM input channel

Parameters:

Name Type Description Default
operations Dict[str, Pulse]

A dictionary of pulses to be played on this channel. The key is the pulse label (e.g. "X90") and value is a Pulse.

required
id (str, int)

The id of the channel, used to generate the name. Can be a string, or an integer in which case it will add Channel._default_label.

required
opx_input MWFEMAnalogInputPort

Channel input port from the OPX perspective.

required
intermediate_frequency float

Intermediate frequency of OPX output, default is None.

required
Source code in quam/components/channels.py
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
@quam_dataclass
class InMWChannel(_InComplexChannel):
    """QuAM component for a MW FEM input channel

    Args:
        operations (Dict[str, Pulse]): A dictionary of pulses to be played on this
            channel. The key is the pulse label (e.g. "X90") and value is a Pulse.
        id (str, int): The id of the channel, used to generate the name.
            Can be a string, or an integer in which case it will add
            `Channel._default_label`.
        opx_input (MWFEMAnalogInputPort)): Channel input port from the OPX perspective.
        intermediate_frequency (float): Intermediate frequency of OPX output, default
            is None.
    """

    opx_input: MWFEMAnalogInputPort

    time_of_flight: int = 24
    smearing: int = 0

    def apply_to_config(self, config: Dict) -> None:
        super().apply_to_config(config)

        element_config = config["elements"][self.name]
        element_config["MWOutput"] = {"port": self.opx_input.port_tuple}
        element_config["smearing"] = self.smearing
        element_config["time_of_flight"] = self.time_of_flight

InOutIQChannel

Bases: IQChannel, InIQChannel

QuAM component for an IQ channel with both input and output.

An example of such a channel is a readout resonator, where you may want to apply a readout tone and then measure the response.

Parameters:

Name Type Description Default
operations Dict[str, Pulse]

A dictionary of pulses to be played on this channel. The key is the pulse label (e.g. "readout") and value is a ReadoutPulse.

required
id (str, int)

The id of the channel, used to generate the name. Can be a string, or an integer in which case it will add Channel._default_label.

required
opx_output_I Tuple[str, int]

Channel I output port from the OPX perspective, a tuple of (controller_name, port).

required
opx_output_Q Tuple[str, int]

Channel Q output port from the OPX perspective, a tuple of (controller_name, port).

required
opx_output_offset_I float

The offset of the I channel. Default is 0.

required
opx_output_offset_Q float

The offset of the Q channel. Default is 0.

required
opx_input_I Tuple[str, int]

Channel I input port from the OPX perspective, a tuple of (controller_name, port).

required
opx_input_Q Tuple[str, int]

Channel Q input port from the OPX perspective, a tuple of (controller_name, port).

required
opx_input_offset_I float

The offset of the I channel. Default is 0.

required
opx_input_offset_Q float

The offset of the Q channel. Default is 0.

required
intermediate_frequency float

Intermediate frequency of the mixer. Default is 0.0

required
LO_frequency float

Local oscillator frequency. Default is the LO frequency of the frequency converter up component.

required
RF_frequency float

RF frequency of the mixer. By default, the RF frequency is inferred by adding the LO frequency and the intermediate frequency.

required
frequency_converter_up FrequencyConverter

Frequency converter QuAM component for the IQ output.

required
frequency_converter_down Optional[FrequencyConverter]

Frequency converter QuAM component for the IQ input port. Only needed for the old Octave.

required
time_of_flight int

Round-trip signal duration in nanoseconds.

required
smearing int

Additional window of ADC integration in nanoseconds. Used to account for signal smearing.

required
Source code in quam/components/channels.py
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
@quam_dataclass
class InOutIQChannel(IQChannel, InIQChannel):
    """QuAM component for an IQ channel with both input and output.

    An example of such a channel is a readout resonator, where you may want to
    apply a readout tone and then measure the response.

    Args:
        operations (Dict[str, Pulse]): A dictionary of pulses to be played on this
            channel. The key is the pulse label (e.g. "readout") and value is a
            ReadoutPulse.
        id (str, int): The id of the channel, used to generate the name.
            Can be a string, or an integer in which case it will add
            `Channel._default_label`.
        opx_output_I (Tuple[str, int]): Channel I output port from the OPX perspective,
            a tuple of (controller_name, port).
        opx_output_Q (Tuple[str, int]): Channel Q output port from the OPX perspective,
            a tuple of (controller_name, port).
        opx_output_offset_I float: The offset of the I channel. Default is 0.
        opx_output_offset_Q float: The offset of the Q channel. Default is 0.
        opx_input_I (Tuple[str, int]): Channel I input port from the OPX perspective,
            a tuple of (controller_name, port).
        opx_input_Q (Tuple[str, int]): Channel Q input port from the OPX perspective,
            a tuple of (controller_name, port).
        opx_input_offset_I float: The offset of the I channel. Default is 0.
        opx_input_offset_Q float: The offset of the Q channel. Default is 0.
        intermediate_frequency (float): Intermediate frequency of the mixer.
            Default is 0.0
        LO_frequency (float): Local oscillator frequency. Default is the LO frequency
            of the frequency converter up component.
        RF_frequency (float): RF frequency of the mixer. By default, the RF frequency
            is inferred by adding the LO frequency and the intermediate frequency.
        frequency_converter_up (FrequencyConverter): Frequency converter QuAM component
            for the IQ output.
        frequency_converter_down (Optional[FrequencyConverter]): Frequency converter
            QuAM component for the IQ input port. Only needed for the old Octave.
        time_of_flight (int): Round-trip signal duration in nanoseconds.
        smearing (int): Additional window of ADC integration in nanoseconds.
            Used to account for signal smearing.
    """

    pass

InOutMWChannel

Bases: MWChannel, InMWChannel

QuAM component for a MW FEM input channel

Parameters:

Name Type Description Default
operations Dict[str, Pulse]

A dictionary of pulses to be played on this channel. The key is the pulse label (e.g. "X90") and value is a Pulse.

required
id (str, int)

The id of the channel, used to generate the name. Can be a string, or an integer in which case it will add Channel._default_label.

required
opx_output MWFEMAnalogOutputPort

Channel output port from the OPX perspective.

required
opx_input MWFEMAnalogInputPort

Channel input port from the OPX perspective.

required
intermediate_frequency float

Intermediate frequency of OPX output, default is None.

required
upconverter int

The upconverter to use. Default is 1.

required
time_of_flight int

Round-trip signal duration in nanoseconds.

required
smearing int

Additional window of ADC integration in nanoseconds. Used to account for signal smearing.

required
Source code in quam/components/channels.py
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
@quam_dataclass
class InOutMWChannel(MWChannel, InMWChannel):
    """QuAM component for a MW FEM input channel

    Args:
        operations (Dict[str, Pulse]): A dictionary of pulses to be played on this
            channel. The key is the pulse label (e.g. "X90") and value is a Pulse.
        id (str, int): The id of the channel, used to generate the name.
            Can be a string, or an integer in which case it will add
            `Channel._default_label`.
        opx_output (MWFEMAnalogOutputPort): Channel output port from the OPX perspective.
        opx_input (MWFEMAnalogInputPort)): Channel input port from the OPX perspective.
        intermediate_frequency (float): Intermediate frequency of OPX output, default
            is None.
        upconverter (int): The upconverter to use. Default is 1.
        time_of_flight (int): Round-trip signal duration in nanoseconds.
        smearing (int): Additional window of ADC integration in nanoseconds.
            Used to account for signal smearing.
    """

    pass

InOutSingleChannel

Bases: SingleChannel, InSingleChannel

QuAM component for a single (not IQ) input + output channel.

Parameters:

Name Type Description Default
operations Dict[str, Pulse]

A dictionary of pulses to be played on this channel. The key is the pulse label (e.g. "X90") and value is a Pulse.

required
id (str, int)

The id of the channel, used to generate the name. Can be a string, or an integer in which case it will add Channel._default_label.

required
opx_output Tuple[str, int]

Channel output port from the OPX perspective, a tuple of (controller_name, port).

required
opx_output_offset float

DC offset for the output port.

required
opx_input Tuple[str, int]

Channel input port from OPX perspective, a tuple of (controller_name, port).

required
opx_input_offset float

DC offset for the input port.

required
filter_fir_taps List[float]

FIR filter taps for the output port.

required
filter_iir_taps List[float]

IIR filter taps for the output port.

required
intermediate_frequency float

Intermediate frequency of OPX output, default is None.

required
time_of_flight int

Round-trip signal duration in nanoseconds.

required
smearing int

Additional window of ADC integration in nanoseconds. Used to account for signal smearing.

required
Source code in quam/components/channels.py
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
@quam_dataclass
class InOutSingleChannel(SingleChannel, InSingleChannel):
    """QuAM component for a single (not IQ) input + output channel.

    Args:
        operations (Dict[str, Pulse]): A dictionary of pulses to be played on this
            channel. The key is the pulse label (e.g. "X90") and value is a Pulse.
        id (str, int): The id of the channel, used to generate the name.
            Can be a string, or an integer in which case it will add
            `Channel._default_label`.
        opx_output (Tuple[str, int]): Channel output port from the OPX perspective,
            a tuple of (controller_name, port).
        opx_output_offset (float): DC offset for the output port.
        opx_input (Tuple[str, int]): Channel input port from OPX perspective,
            a tuple of (controller_name, port).
        opx_input_offset (float): DC offset for the input port.
        filter_fir_taps (List[float]): FIR filter taps for the output port.
        filter_iir_taps (List[float]): IIR filter taps for the output port.
        intermediate_frequency (float): Intermediate frequency of OPX output, default
            is None.
        time_of_flight (int): Round-trip signal duration in nanoseconds.
        smearing (int): Additional window of ADC integration in nanoseconds.
            Used to account for signal smearing.
    """

    pass

InSingleChannel

Bases: Channel

QuAM component for a single (not IQ) input channel.

Parameters:

Name Type Description Default
operations Dict[str, Pulse]

A dictionary of pulses to be played on this channel. The key is the pulse label (e.g. "X90") and value is a Pulse.

required
id (str, int)

The id of the channel, used to generate the name. Can be a string, or an integer in which case it will add Channel._default_label.

required
opx_input Tuple[str, int]

Channel input port from OPX perspective, a tuple of (controller_name, port).

required
opx_input_offset float

DC offset for the input port.

required
intermediate_frequency float

Intermediate frequency of OPX input, default is None.

required
time_of_flight int

Round-trip signal duration in nanoseconds.

required
smearing int

Additional window of ADC integration in nanoseconds. Used to account for signal smearing.

required
Source code in quam/components/channels.py
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
@quam_dataclass
class InSingleChannel(Channel):
    """QuAM component for a single (not IQ) input channel.

    Args:
        operations (Dict[str, Pulse]): A dictionary of pulses to be played on this
            channel. The key is the pulse label (e.g. "X90") and value is a Pulse.
        id (str, int): The id of the channel, used to generate the name.
            Can be a string, or an integer in which case it will add
            `Channel._default_label`.
        opx_input (Tuple[str, int]): Channel input port from OPX perspective,
            a tuple of (controller_name, port).
        opx_input_offset (float): DC offset for the input port.
        intermediate_frequency (float): Intermediate frequency of OPX input,
            default is None.
        time_of_flight (int): Round-trip signal duration in nanoseconds.
        smearing (int): Additional window of ADC integration in nanoseconds.
            Used to account for signal smearing.
    """

    opx_input: Union[Tuple[str, int], Tuple[str, int, int], LFAnalogInputPort]
    opx_input_offset: float = None

    time_of_flight: int = 24
    smearing: int = 0

    time_tagging: Optional[TimeTaggingAddon] = None

    def apply_to_config(self, config: dict):
        """Adds this InSingleChannel to the QUA configuration.

        See [`QuamComponent.apply_to_config`][quam.core.quam_classes.QuamComponent.apply_to_config]
        for details.
        """
        # Add output to config
        super().apply_to_config(config)

        # Note outputs instead of inputs because it's w.r.t. the QPU
        element_config = config["elements"][self.name]
        element_config["smearing"] = self.smearing
        element_config["time_of_flight"] = self.time_of_flight

        if isinstance(self.opx_input, LFAnalogInputPort):
            opx_port = self.opx_input
        elif len(self.opx_input) == 2:
            opx_port = OPXPlusAnalogInputPort(
                *self.opx_input, offset=self.opx_input_offset
            )
            opx_port.apply_to_config(config)
        else:
            opx_port = LFFEMAnalogInputPort(
                *self.opx_input, offset=self.opx_input_offset
            )
            opx_port.apply_to_config(config)

        element_config["outputs"] = {"out1": opx_port.port_tuple}

    def measure(
        self,
        pulse_name: str,
        amplitude_scale: Union[float, AmpValuesType] = None,
        qua_vars: Tuple[QuaVariableType, ...] = None,
        stream=None,
    ) -> Tuple[QuaVariableType, QuaVariableType]:
        """Perform a full demodulation measurement on this channel.

        Args:
            pulse_name (str): The name of the pulse to play. Should be registered in
                `self.operations`.
            amplitude_scale (float, _PulseAmp): Amplitude scale of the pulse.
                Can be either a float, or qua.amp(float).
            qua_vars (Tuple[QuaVariableType, ...], optional): Two QUA
                variables to store the I, Q measurement results.
                If not provided, new variables will be declared and returned.
            stream (Optional[StreamType]): The stream to save the measurement result to.
                If not provided, the raw ADC signal will not be streamed.

        Returns:
            I, Q: The QUA variables used to store the measurement results.
                If provided as input, the same variables will be returned.
                If not provided, new variables will be declared and returned.

        Raises:
            ValueError: If `qua_vars` is provided and is not a tuple of two QUA
                variables.
        """

        pulse: BaseReadoutPulse = self.operations[pulse_name]

        if qua_vars is not None:
            if not isinstance(qua_vars, Sequence) or len(qua_vars) != 2:
                raise ValueError(
                    f"InOutSingleChannel.measure received kwarg 'qua_vars' "
                    f"which is not a tuple of two QUA variables. Received {qua_vars=}"
                )
        else:
            qua_vars = [declare(fixed) for _ in range(2)]

        if amplitude_scale is not None:
            if not isinstance(amplitude_scale, _PulseAmp):
                amplitude_scale = amp(amplitude_scale)
            pulse_name *= amplitude_scale

        integration_weight_labels = list(pulse.integration_weights_mapping)
        measure(
            pulse_name,
            self.name,
            stream,
            demod.full(integration_weight_labels[0], qua_vars[0], "out1"),
            demod.full(integration_weight_labels[1], qua_vars[1], "out1"),
        )
        return tuple(qua_vars)

    def measure_accumulated(
        self,
        pulse_name: str,
        amplitude_scale: Union[float, AmpValuesType] = None,
        num_segments: int = None,
        segment_length: int = None,
        qua_vars: Tuple[QuaVariableType, ...] = None,
        stream=None,
    ) -> Tuple[QuaVariableType, QuaVariableType]:
        """Perform an accumulated demodulation measurement on this channel.

        Args:
            pulse_name (str): The name of the pulse to play. Should be registered in
                `self.operations`.
            amplitude_scale (float, _PulseAmp): Amplitude scale of the pulse.
                Can be either a float, or qua.amp(float).
            num_segments (int): The number of segments to accumulate.
                Should either specify this or `segment_length`.
            segment_length (int): The length of the segment to accumulate.
                Should either specify this or `num_segments`.
            qua_vars (Tuple[QuaVariableType, ...], optional): Two QUA
                variables to store the I, Q measurement results.
                If not provided, new variables will be declared and returned.
            stream (Optional[StreamType]): The stream to save the measurement result to.
                If not provided, the raw ADC signal will not be streamed.

        Returns:
            I, Q: The QUA variables used to store the measurement results.
                If provided as input, the same variables will be returned.
                If not provided, new variables will be declared and returned.

        Raises:
            ValueError: If both `num_segments` and `segment_length` are provided, or if
                neither are provided.
            ValueError: If `qua_vars` is provided and is not a tuple of two QUA
                variables.
        """
        pulse: BaseReadoutPulse = self.operations[pulse_name]

        if num_segments is None and segment_length is None:
            raise ValueError(
                "InOutSingleChannel.measure_accumulated requires either 'segment_length' "
                "or 'num_segments' to be provided."
            )
        elif num_segments is not None and segment_length is not None:
            raise ValueError(
                "InOutSingleChannel.measure_accumulated received both 'segment_length' "
                "and 'num_segments'. Please provide only one."
            )
        elif num_segments is None:
            num_segments = int(pulse.length / (4 * segment_length))  # Number of slices
        elif segment_length is None:
            segment_length = int(pulse.length / (4 * num_segments))

        if qua_vars is not None:
            if not isinstance(qua_vars, Sequence) or len(qua_vars) != 2:
                raise ValueError(
                    f"InOutSingleChannel.measure_accumulated received kwarg 'qua_vars' "
                    f"which is not a tuple of two QUA variables. Received {qua_vars=}"
                )
        else:
            qua_vars = [declare(fixed, size=num_segments) for _ in range(2)]

        if amplitude_scale is not None:
            if not isinstance(amplitude_scale, _PulseAmp):
                amplitude_scale = amp(amplitude_scale)
            pulse_name *= amplitude_scale

        integration_weight_labels = list(pulse.integration_weights_mapping)
        measure(
            pulse_name,
            self.name,
            stream,
            demod.accumulated(
                integration_weight_labels[0], qua_vars[0], segment_length, "out1"
            ),
            demod.accumulated(
                integration_weight_labels[1], qua_vars[1], segment_length, "out1"
            ),
        )
        return tuple(qua_vars)

    def measure_sliced(
        self,
        pulse_name: str,
        amplitude_scale: Union[float, AmpValuesType] = None,
        num_segments: int = None,
        segment_length: int = None,
        qua_vars: Tuple[QuaVariableType, ...] = None,
        stream=None,
    ) -> Tuple[QuaVariableType, QuaVariableType]:
        """Perform an accumulated demodulation measurement on this channel.

        Args:
            pulse_name (str): The name of the pulse to play. Should be registered in
                `self.operations`.
            amplitude_scale (float, _PulseAmp): Amplitude scale of the pulse.
                Can be either a float, or qua.amp(float).
            num_segments (int): The number of segments to accumulate.
                Should either specify this or `segment_length`.
            segment_length (int): The length of the segment to accumulate.
                Should either specify this or `num_segments`.
            qua_vars (Tuple[QuaVariableType, ...], optional): Two QUA
                variables to store the I, Q measurement results.
                If not provided, new variables will be declared and returned.
            stream (Optional[StreamType]): The stream to save the measurement result to.
                If not provided, the raw ADC signal will not be streamed.

        Returns:
            I, Q: The QUA variables used to store the measurement results.
                If provided as input, the same variables will be returned.
                If not provided, new variables will be declared and returned.

        Raises:
            ValueError: If both `num_segments` and `segment_length` are provided, or if
                neither are provided.
            ValueError: If `qua_vars` is provided and is not a tuple of two QUA
                variables.
        """
        pulse: BaseReadoutPulse = self.operations[pulse_name]

        if num_segments is None and segment_length is None:
            raise ValueError(
                "InOutSingleChannel.measure_sliced requires either 'segment_length' "
                "or 'num_segments' to be provided."
            )
        elif num_segments is not None and segment_length is not None:
            raise ValueError(
                "InOutSingleChannel.measure_sliced received both 'segment_length' "
                "and 'num_segments'. Please provide only one."
            )
        elif num_segments is None:
            num_segments = int(pulse.length / (4 * segment_length))  # Number of slices
        elif segment_length is None:
            segment_length = int(pulse.length / (4 * num_segments))

        if qua_vars is not None:
            if not isinstance(qua_vars, Sequence) or len(qua_vars) != 2:
                raise ValueError(
                    f"InOutSingleChannel.measure_sliced received kwarg 'qua_vars' "
                    f"which is not a tuple of two QUA variables. Received {qua_vars=}"
                )
        else:
            qua_vars = [declare(fixed, size=num_segments) for _ in range(2)]

        if amplitude_scale is not None:
            if not isinstance(amplitude_scale, _PulseAmp):
                amplitude_scale = amp(amplitude_scale)
            pulse_name *= amplitude_scale

        integration_weight_labels = list(pulse.integration_weights_mapping)
        measure(
            pulse_name,
            self.name,
            stream,
            demod.sliced(
                integration_weight_labels[0], qua_vars[0], segment_length, "out1"
            ),
            demod.sliced(
                integration_weight_labels[1], qua_vars[1], segment_length, "out1"
            ),
        )
        return tuple(qua_vars)

    def measure_time_tagging(
        self,
        pulse_name: str,
        size: int,
        max_time: QuaNumberType,
        qua_vars: Optional[Tuple[QuaVariableType, QuaNumberType]] = None,
        stream: Optional[StreamType] = None,
        mode: Literal["analog", "high_res", "digital"] = "analog",
    ) -> Tuple[QuaVariableType, QuaNumberType]:
        """Perform a time tagging measurement on this channel.

        For details see https://docs.quantum-machines.co/latest/docs/Guides/features/#time-tagging

        Args:
            pulse_name (str): The name of the pulse to play. Should be registered in
                `self.operations`.
            size (int): The size of the QUA array to store the times of the detected
                pulses. Ignored if `qua_vars` is provided.
            max_time (QuaNumberType): The maximum time to search for pulses.
            qua_vars (Tuple[QuaVariableType, QuaNumberType], optional): QUA variables
                to store the times and counts of the detected pulses. If not provided,
                new variables will be declared and returned.
            stream (Optional[StreamType]): The stream to save the measurement result to.
                If not provided, the raw ADC signal will not be streamed.
            mode (Literal["analog", "high_res", "digital"]): The time tagging mode.

        Returns:
            times (QuaVariableType): The QUA variable to store the times of the detected
                pulses.
            counts (QuaNumberType): The number of detected pulses.

        Example:
            ```python
            times, counts = channel.measure_time_tagging("readout", size=1000, max_time=1000)
            ```
        """
        if mode == "analog":
            time_tagging_func = time_tagging.analog
        elif mode == "high_res":
            time_tagging_func = time_tagging.high_res
        elif mode == "digital":
            time_tagging_func = time_tagging.digital
        else:
            raise ValueError(f"Invalid time tagging mode: {mode}")

        if qua_vars is None:
            times = declare(int, size=size)
            counts = declare(int)
        else:
            times, counts = qua_vars

        measure(
            pulse_name,
            self.name,
            stream,
            time_tagging_func(target=times, max_time=max_time, targetLen=counts),
        )
        return times, counts

apply_to_config(config)

Adds this InSingleChannel to the QUA configuration.

See QuamComponent.apply_to_config for details.

Source code in quam/components/channels.py
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
def apply_to_config(self, config: dict):
    """Adds this InSingleChannel to the QUA configuration.

    See [`QuamComponent.apply_to_config`][quam.core.quam_classes.QuamComponent.apply_to_config]
    for details.
    """
    # Add output to config
    super().apply_to_config(config)

    # Note outputs instead of inputs because it's w.r.t. the QPU
    element_config = config["elements"][self.name]
    element_config["smearing"] = self.smearing
    element_config["time_of_flight"] = self.time_of_flight

    if isinstance(self.opx_input, LFAnalogInputPort):
        opx_port = self.opx_input
    elif len(self.opx_input) == 2:
        opx_port = OPXPlusAnalogInputPort(
            *self.opx_input, offset=self.opx_input_offset
        )
        opx_port.apply_to_config(config)
    else:
        opx_port = LFFEMAnalogInputPort(
            *self.opx_input, offset=self.opx_input_offset
        )
        opx_port.apply_to_config(config)

    element_config["outputs"] = {"out1": opx_port.port_tuple}

measure(pulse_name, amplitude_scale=None, qua_vars=None, stream=None)

Perform a full demodulation measurement on this channel.

Parameters:

Name Type Description Default
pulse_name str

The name of the pulse to play. Should be registered in self.operations.

required
amplitude_scale (float, _PulseAmp)

Amplitude scale of the pulse. Can be either a float, or qua.amp(float).

None
qua_vars Tuple[QuaVariableType, ...]

Two QUA variables to store the I, Q measurement results. If not provided, new variables will be declared and returned.

None
stream Optional[StreamType]

The stream to save the measurement result to. If not provided, the raw ADC signal will not be streamed.

None

Returns:

Type Description
Tuple[QuaVariableType, QuaVariableType]

I, Q: The QUA variables used to store the measurement results. If provided as input, the same variables will be returned. If not provided, new variables will be declared and returned.

Raises:

Type Description
ValueError

If qua_vars is provided and is not a tuple of two QUA variables.

Source code in quam/components/channels.py
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
def measure(
    self,
    pulse_name: str,
    amplitude_scale: Union[float, AmpValuesType] = None,
    qua_vars: Tuple[QuaVariableType, ...] = None,
    stream=None,
) -> Tuple[QuaVariableType, QuaVariableType]:
    """Perform a full demodulation measurement on this channel.

    Args:
        pulse_name (str): The name of the pulse to play. Should be registered in
            `self.operations`.
        amplitude_scale (float, _PulseAmp): Amplitude scale of the pulse.
            Can be either a float, or qua.amp(float).
        qua_vars (Tuple[QuaVariableType, ...], optional): Two QUA
            variables to store the I, Q measurement results.
            If not provided, new variables will be declared and returned.
        stream (Optional[StreamType]): The stream to save the measurement result to.
            If not provided, the raw ADC signal will not be streamed.

    Returns:
        I, Q: The QUA variables used to store the measurement results.
            If provided as input, the same variables will be returned.
            If not provided, new variables will be declared and returned.

    Raises:
        ValueError: If `qua_vars` is provided and is not a tuple of two QUA
            variables.
    """

    pulse: BaseReadoutPulse = self.operations[pulse_name]

    if qua_vars is not None:
        if not isinstance(qua_vars, Sequence) or len(qua_vars) != 2:
            raise ValueError(
                f"InOutSingleChannel.measure received kwarg 'qua_vars' "
                f"which is not a tuple of two QUA variables. Received {qua_vars=}"
            )
    else:
        qua_vars = [declare(fixed) for _ in range(2)]

    if amplitude_scale is not None:
        if not isinstance(amplitude_scale, _PulseAmp):
            amplitude_scale = amp(amplitude_scale)
        pulse_name *= amplitude_scale

    integration_weight_labels = list(pulse.integration_weights_mapping)
    measure(
        pulse_name,
        self.name,
        stream,
        demod.full(integration_weight_labels[0], qua_vars[0], "out1"),
        demod.full(integration_weight_labels[1], qua_vars[1], "out1"),
    )
    return tuple(qua_vars)

measure_accumulated(pulse_name, amplitude_scale=None, num_segments=None, segment_length=None, qua_vars=None, stream=None)

Perform an accumulated demodulation measurement on this channel.

Parameters:

Name Type Description Default
pulse_name str

The name of the pulse to play. Should be registered in self.operations.

required
amplitude_scale (float, _PulseAmp)

Amplitude scale of the pulse. Can be either a float, or qua.amp(float).

None
num_segments int

The number of segments to accumulate. Should either specify this or segment_length.

None
segment_length int

The length of the segment to accumulate. Should either specify this or num_segments.

None
qua_vars Tuple[QuaVariableType, ...]

Two QUA variables to store the I, Q measurement results. If not provided, new variables will be declared and returned.

None
stream Optional[StreamType]

The stream to save the measurement result to. If not provided, the raw ADC signal will not be streamed.

None

Returns:

Type Description
Tuple[QuaVariableType, QuaVariableType]

I, Q: The QUA variables used to store the measurement results. If provided as input, the same variables will be returned. If not provided, new variables will be declared and returned.

Raises:

Type Description
ValueError

If both num_segments and segment_length are provided, or if neither are provided.

ValueError

If qua_vars is provided and is not a tuple of two QUA variables.

Source code in quam/components/channels.py
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
def measure_accumulated(
    self,
    pulse_name: str,
    amplitude_scale: Union[float, AmpValuesType] = None,
    num_segments: int = None,
    segment_length: int = None,
    qua_vars: Tuple[QuaVariableType, ...] = None,
    stream=None,
) -> Tuple[QuaVariableType, QuaVariableType]:
    """Perform an accumulated demodulation measurement on this channel.

    Args:
        pulse_name (str): The name of the pulse to play. Should be registered in
            `self.operations`.
        amplitude_scale (float, _PulseAmp): Amplitude scale of the pulse.
            Can be either a float, or qua.amp(float).
        num_segments (int): The number of segments to accumulate.
            Should either specify this or `segment_length`.
        segment_length (int): The length of the segment to accumulate.
            Should either specify this or `num_segments`.
        qua_vars (Tuple[QuaVariableType, ...], optional): Two QUA
            variables to store the I, Q measurement results.
            If not provided, new variables will be declared and returned.
        stream (Optional[StreamType]): The stream to save the measurement result to.
            If not provided, the raw ADC signal will not be streamed.

    Returns:
        I, Q: The QUA variables used to store the measurement results.
            If provided as input, the same variables will be returned.
            If not provided, new variables will be declared and returned.

    Raises:
        ValueError: If both `num_segments` and `segment_length` are provided, or if
            neither are provided.
        ValueError: If `qua_vars` is provided and is not a tuple of two QUA
            variables.
    """
    pulse: BaseReadoutPulse = self.operations[pulse_name]

    if num_segments is None and segment_length is None:
        raise ValueError(
            "InOutSingleChannel.measure_accumulated requires either 'segment_length' "
            "or 'num_segments' to be provided."
        )
    elif num_segments is not None and segment_length is not None:
        raise ValueError(
            "InOutSingleChannel.measure_accumulated received both 'segment_length' "
            "and 'num_segments'. Please provide only one."
        )
    elif num_segments is None:
        num_segments = int(pulse.length / (4 * segment_length))  # Number of slices
    elif segment_length is None:
        segment_length = int(pulse.length / (4 * num_segments))

    if qua_vars is not None:
        if not isinstance(qua_vars, Sequence) or len(qua_vars) != 2:
            raise ValueError(
                f"InOutSingleChannel.measure_accumulated received kwarg 'qua_vars' "
                f"which is not a tuple of two QUA variables. Received {qua_vars=}"
            )
    else:
        qua_vars = [declare(fixed, size=num_segments) for _ in range(2)]

    if amplitude_scale is not None:
        if not isinstance(amplitude_scale, _PulseAmp):
            amplitude_scale = amp(amplitude_scale)
        pulse_name *= amplitude_scale

    integration_weight_labels = list(pulse.integration_weights_mapping)
    measure(
        pulse_name,
        self.name,
        stream,
        demod.accumulated(
            integration_weight_labels[0], qua_vars[0], segment_length, "out1"
        ),
        demod.accumulated(
            integration_weight_labels[1], qua_vars[1], segment_length, "out1"
        ),
    )
    return tuple(qua_vars)

measure_sliced(pulse_name, amplitude_scale=None, num_segments=None, segment_length=None, qua_vars=None, stream=None)

Perform an accumulated demodulation measurement on this channel.

Parameters:

Name Type Description Default
pulse_name str

The name of the pulse to play. Should be registered in self.operations.

required
amplitude_scale (float, _PulseAmp)

Amplitude scale of the pulse. Can be either a float, or qua.amp(float).

None
num_segments int

The number of segments to accumulate. Should either specify this or segment_length.

None
segment_length int

The length of the segment to accumulate. Should either specify this or num_segments.

None
qua_vars Tuple[QuaVariableType, ...]

Two QUA variables to store the I, Q measurement results. If not provided, new variables will be declared and returned.

None
stream Optional[StreamType]

The stream to save the measurement result to. If not provided, the raw ADC signal will not be streamed.

None

Returns:

Type Description
Tuple[QuaVariableType, QuaVariableType]

I, Q: The QUA variables used to store the measurement results. If provided as input, the same variables will be returned. If not provided, new variables will be declared and returned.

Raises:

Type Description
ValueError

If both num_segments and segment_length are provided, or if neither are provided.

ValueError

If qua_vars is provided and is not a tuple of two QUA variables.

Source code in quam/components/channels.py
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
def measure_sliced(
    self,
    pulse_name: str,
    amplitude_scale: Union[float, AmpValuesType] = None,
    num_segments: int = None,
    segment_length: int = None,
    qua_vars: Tuple[QuaVariableType, ...] = None,
    stream=None,
) -> Tuple[QuaVariableType, QuaVariableType]:
    """Perform an accumulated demodulation measurement on this channel.

    Args:
        pulse_name (str): The name of the pulse to play. Should be registered in
            `self.operations`.
        amplitude_scale (float, _PulseAmp): Amplitude scale of the pulse.
            Can be either a float, or qua.amp(float).
        num_segments (int): The number of segments to accumulate.
            Should either specify this or `segment_length`.
        segment_length (int): The length of the segment to accumulate.
            Should either specify this or `num_segments`.
        qua_vars (Tuple[QuaVariableType, ...], optional): Two QUA
            variables to store the I, Q measurement results.
            If not provided, new variables will be declared and returned.
        stream (Optional[StreamType]): The stream to save the measurement result to.
            If not provided, the raw ADC signal will not be streamed.

    Returns:
        I, Q: The QUA variables used to store the measurement results.
            If provided as input, the same variables will be returned.
            If not provided, new variables will be declared and returned.

    Raises:
        ValueError: If both `num_segments` and `segment_length` are provided, or if
            neither are provided.
        ValueError: If `qua_vars` is provided and is not a tuple of two QUA
            variables.
    """
    pulse: BaseReadoutPulse = self.operations[pulse_name]

    if num_segments is None and segment_length is None:
        raise ValueError(
            "InOutSingleChannel.measure_sliced requires either 'segment_length' "
            "or 'num_segments' to be provided."
        )
    elif num_segments is not None and segment_length is not None:
        raise ValueError(
            "InOutSingleChannel.measure_sliced received both 'segment_length' "
            "and 'num_segments'. Please provide only one."
        )
    elif num_segments is None:
        num_segments = int(pulse.length / (4 * segment_length))  # Number of slices
    elif segment_length is None:
        segment_length = int(pulse.length / (4 * num_segments))

    if qua_vars is not None:
        if not isinstance(qua_vars, Sequence) or len(qua_vars) != 2:
            raise ValueError(
                f"InOutSingleChannel.measure_sliced received kwarg 'qua_vars' "
                f"which is not a tuple of two QUA variables. Received {qua_vars=}"
            )
    else:
        qua_vars = [declare(fixed, size=num_segments) for _ in range(2)]

    if amplitude_scale is not None:
        if not isinstance(amplitude_scale, _PulseAmp):
            amplitude_scale = amp(amplitude_scale)
        pulse_name *= amplitude_scale

    integration_weight_labels = list(pulse.integration_weights_mapping)
    measure(
        pulse_name,
        self.name,
        stream,
        demod.sliced(
            integration_weight_labels[0], qua_vars[0], segment_length, "out1"
        ),
        demod.sliced(
            integration_weight_labels[1], qua_vars[1], segment_length, "out1"
        ),
    )
    return tuple(qua_vars)

measure_time_tagging(pulse_name, size, max_time, qua_vars=None, stream=None, mode='analog')

Perform a time tagging measurement on this channel.

For details see https://docs.quantum-machines.co/latest/docs/Guides/features/#time-tagging

Parameters:

Name Type Description Default
pulse_name str

The name of the pulse to play. Should be registered in self.operations.

required
size int

The size of the QUA array to store the times of the detected pulses. Ignored if qua_vars is provided.

required
max_time QuaNumberType

The maximum time to search for pulses.

required
qua_vars Tuple[QuaVariableType, QuaNumberType]

QUA variables to store the times and counts of the detected pulses. If not provided, new variables will be declared and returned.

None
stream Optional[StreamType]

The stream to save the measurement result to. If not provided, the raw ADC signal will not be streamed.

None
mode Literal['analog', 'high_res', 'digital']

The time tagging mode.

'analog'

Returns:

Name Type Description
times QuaVariableType

The QUA variable to store the times of the detected pulses.

counts QuaNumberType

The number of detected pulses.

Example
times, counts = channel.measure_time_tagging("readout", size=1000, max_time=1000)
Source code in quam/components/channels.py
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
def measure_time_tagging(
    self,
    pulse_name: str,
    size: int,
    max_time: QuaNumberType,
    qua_vars: Optional[Tuple[QuaVariableType, QuaNumberType]] = None,
    stream: Optional[StreamType] = None,
    mode: Literal["analog", "high_res", "digital"] = "analog",
) -> Tuple[QuaVariableType, QuaNumberType]:
    """Perform a time tagging measurement on this channel.

    For details see https://docs.quantum-machines.co/latest/docs/Guides/features/#time-tagging

    Args:
        pulse_name (str): The name of the pulse to play. Should be registered in
            `self.operations`.
        size (int): The size of the QUA array to store the times of the detected
            pulses. Ignored if `qua_vars` is provided.
        max_time (QuaNumberType): The maximum time to search for pulses.
        qua_vars (Tuple[QuaVariableType, QuaNumberType], optional): QUA variables
            to store the times and counts of the detected pulses. If not provided,
            new variables will be declared and returned.
        stream (Optional[StreamType]): The stream to save the measurement result to.
            If not provided, the raw ADC signal will not be streamed.
        mode (Literal["analog", "high_res", "digital"]): The time tagging mode.

    Returns:
        times (QuaVariableType): The QUA variable to store the times of the detected
            pulses.
        counts (QuaNumberType): The number of detected pulses.

    Example:
        ```python
        times, counts = channel.measure_time_tagging("readout", size=1000, max_time=1000)
        ```
    """
    if mode == "analog":
        time_tagging_func = time_tagging.analog
    elif mode == "high_res":
        time_tagging_func = time_tagging.high_res
    elif mode == "digital":
        time_tagging_func = time_tagging.digital
    else:
        raise ValueError(f"Invalid time tagging mode: {mode}")

    if qua_vars is None:
        times = declare(int, size=size)
        counts = declare(int)
    else:
        times, counts = qua_vars

    measure(
        pulse_name,
        self.name,
        stream,
        time_tagging_func(target=times, max_time=max_time, targetLen=counts),
    )
    return times, counts

InSingleOutIQChannel

Bases: IQChannel, InSingleChannel

QuAM component for an IQ output channel with a single input.

Parameters:

Name Type Description Default
operations Dict[str, Pulse]

A dictionary of pulses to be played on this channel. The key is the pulse label (e.g. "readout") and value is a ReadoutPulse.

required
id (str, int)

The id of the channel, used to generate the name. Can be a string, or an integer in which case it will add Channel._default_label.

required
opx_output_I Tuple[str, int]

Channel I output port from the OPX perspective, a tuple of (controller_name, port).

required
opx_output_Q Tuple[str, int]

Channel Q output port from the OPX perspective, a tuple of (controller_name, port).

required
opx_output_offset_I float

The offset of the I channel. Default is 0.

required
opx_output_offset_Q float

The offset of the Q channel. Default is 0.

required
opx_input Tuple[str, int]

Channel input port from OPX perspective, a tuple of (controller_name, port).

required
opx_input_offset float

DC offset for the input port.

required
intermediate_frequency float

Intermediate frequency of the mixer. Default is 0.0

required
LO_frequency float

Local oscillator frequency. Default is the LO frequency of the frequency converter up component.

required
RF_frequency float

RF frequency of the mixer. By default, the RF frequency is inferred by adding the LO frequency and the intermediate frequency.

required
frequency_converter_up FrequencyConverter

Frequency converter QuAM component for the IQ output.

required
time_of_flight int

Round-trip signal duration in nanoseconds.

required
smearing int

Additional window of ADC integration in nanoseconds. Used to account for signal smearing.

required
Source code in quam/components/channels.py
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
@quam_dataclass
class InSingleOutIQChannel(IQChannel, InSingleChannel):
    """QuAM component for an IQ output channel with a single input.

    Args:
        operations (Dict[str, Pulse]): A dictionary of pulses to be played on this
            channel. The key is the pulse label (e.g. "readout") and value is a
            ReadoutPulse.
        id (str, int): The id of the channel, used to generate the name.
            Can be a string, or an integer in which case it will add
            `Channel._default_label`.
        opx_output_I (Tuple[str, int]): Channel I output port from the OPX perspective,
            a tuple of (controller_name, port).
        opx_output_Q (Tuple[str, int]): Channel Q output port from the OPX perspective,
            a tuple of (controller_name, port).
        opx_output_offset_I float: The offset of the I channel. Default is 0.
        opx_output_offset_Q float: The offset of the Q channel. Default is 0.
        opx_input (Tuple[str, int]): Channel input port from OPX perspective,
            a tuple of (controller_name, port).
        opx_input_offset (float): DC offset for the input port.
        intermediate_frequency (float): Intermediate frequency of the mixer.
            Default is 0.0
        LO_frequency (float): Local oscillator frequency. Default is the LO frequency
            of the frequency converter up component.
        RF_frequency (float): RF frequency of the mixer. By default, the RF frequency
            is inferred by adding the LO frequency and the intermediate frequency.
        frequency_converter_up (FrequencyConverter): Frequency converter QuAM component
            for the IQ output.
        time_of_flight (int): Round-trip signal duration in nanoseconds.
        smearing (int): Additional window of ADC integration in nanoseconds.
            Used to account for signal smearing.
    """

    pass

MWChannel

Bases: _OutComplexChannel

QuAM component for a MW FEM output channel

Parameters:

Name Type Description Default
operations Dict[str, Pulse]

A dictionary of pulses to be played on this channel. The key is the pulse label (e.g. "X90") and value is a Pulse.

required
id (str, int)

The id of the channel, used to generate the name. Can be a string, or an integer in which case it will add Channel._default_label.

required
opx_output MWFEMAnalogOutputPort

Channel output port from the OPX perspective.

required
intermediate_frequency float

Intermediate frequency of OPX output, default is None.

required
upconverter int

The upconverter to use. Default is 1.

required
time_of_flight int

Round-trip signal duration in nanoseconds.

required
smearing int

Additional window of ADC integration in nanoseconds. Used to account for signal smearing.

required
Source code in quam/components/channels.py
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
@quam_dataclass
class MWChannel(_OutComplexChannel):
    """QuAM component for a MW FEM output channel

    Args:
        operations (Dict[str, Pulse]): A dictionary of pulses to be played on this
            channel. The key is the pulse label (e.g. "X90") and value is a Pulse.
        id (str, int): The id of the channel, used to generate the name.
            Can be a string, or an integer in which case it will add
            `Channel._default_label`.
        opx_output (MWFEMAnalogOutputPort): Channel output port from the OPX perspective.
        intermediate_frequency (float): Intermediate frequency of OPX output, default
            is None.
        upconverter (int): The upconverter to use. Default is 1.
        time_of_flight (int): Round-trip signal duration in nanoseconds.
        smearing (int): Additional window of ADC integration in nanoseconds.
            Used to account for signal smearing.
    """

    opx_output: MWFEMAnalogOutputPort
    upconverter: int = 1

    LO_frequency: float = "#./opx_output/upconverter_frequency"
    RF_frequency: float = "#./inferred_RF_frequency"

    def apply_to_config(self, config: Dict) -> None:
        super().apply_to_config(config)

        element_config = config["elements"][self.name]
        element_config["MWInput"] = {
            "port": self.opx_output.port_tuple,
            "upconverter": self.upconverter,
        }

SingleChannel

Bases: Channel

QuAM component for a single (not IQ) output channel.

Parameters:

Name Type Description Default
operations Dict[str, Pulse]

A dictionary of pulses to be played on this channel. The key is the pulse label (e.g. "X90") and value is a Pulse.

required
id (str, int)

The id of the channel, used to generate the name. Can be a string, or an integer in which case it will add Channel._default_label.

required
opx_output Tuple[str, int]

Channel output port from the OPX perspective, a tuple of (controller_name, port).

required
filter_fir_taps List[float]

FIR filter taps for the output port.

required
filter_iir_taps List[float]

IIR filter taps for the output port.

required
opx_output_offset float

DC offset for the output port.

required
intermediate_frequency float

Intermediate frequency of OPX output, default is None.

required
Source code in quam/components/channels.py
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
@quam_dataclass
class SingleChannel(Channel):
    """QuAM component for a single (not IQ) output channel.

    Args:
        operations (Dict[str, Pulse]): A dictionary of pulses to be played on this
            channel. The key is the pulse label (e.g. "X90") and value is a Pulse.
        id (str, int): The id of the channel, used to generate the name.
            Can be a string, or an integer in which case it will add
            `Channel._default_label`.
        opx_output (Tuple[str, int]): Channel output port from the OPX perspective,
            a tuple of (controller_name, port).
        filter_fir_taps (List[float]): FIR filter taps for the output port.
        filter_iir_taps (List[float]): IIR filter taps for the output port.
        opx_output_offset (float): DC offset for the output port.
        intermediate_frequency (float): Intermediate frequency of OPX output, default
            is None.
    """

    opx_output: Union[Tuple[str, int], Tuple[str, int, int], LFAnalogOutputPort]
    filter_fir_taps: List[float] = None
    filter_iir_taps: List[float] = None

    opx_output_offset: float = None

    def set_dc_offset(self, offset: QuaNumberType):
        """Set the DC offset of an element's input to the given value.
        This value will remain the DC offset until changed or until the Quantum Machine
        is closed.

        Args:
            offset (QuaNumberType): The DC offset to set the input to.
                This is limited by the OPX output voltage range.
                The number can be a QUA variable
        """
        set_dc_offset(element=self.name, element_input="single", offset=offset)

    def apply_to_config(self, config: dict):
        """Adds this SingleChannel to the QUA configuration.

        See [`QuamComponent.apply_to_config`][quam.core.quam_classes.QuamComponent.apply_to_config]
        for details.
        """
        # Add pulses & waveforms
        super().apply_to_config(config)

        if str_ref.is_reference(self.name):
            raise AttributeError(
                f"Channel {self.get_reference()} cannot be added to the config because"
                " it doesn't have a name. Either set channel.id to a string or"
                " integer, or channel should be an attribute of another QuAM component"
                " with a name."
            )

        element_config = config["elements"][self.name]

        filter_fir_taps = self.filter_fir_taps
        if filter_fir_taps is not None:
            filter_fir_taps = list(filter_fir_taps)
        filter_iir_taps = self.filter_iir_taps
        if filter_iir_taps is not None:
            filter_iir_taps = list(filter_iir_taps)

        if isinstance(self.opx_output, LFAnalogOutputPort):
            opx_port = self.opx_output
        elif len(self.opx_output) == 2:
            opx_port = OPXPlusAnalogOutputPort(
                *self.opx_output,
                offset=self.opx_output_offset,
                feedforward_filter=filter_fir_taps,
                feedback_filter=filter_iir_taps,
            )
            opx_port.apply_to_config(config)
        else:
            opx_port = LFFEMAnalogOutputPort(
                *self.opx_output,
                offset=self.opx_output_offset,
                feedforward_filter=filter_fir_taps,
                feedback_filter=filter_iir_taps,
            )
            opx_port.apply_to_config(config)

        element_config["singleInput"] = {"port": opx_port.port_tuple}

apply_to_config(config)

Adds this SingleChannel to the QUA configuration.

See QuamComponent.apply_to_config for details.

Source code in quam/components/channels.py
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
def apply_to_config(self, config: dict):
    """Adds this SingleChannel to the QUA configuration.

    See [`QuamComponent.apply_to_config`][quam.core.quam_classes.QuamComponent.apply_to_config]
    for details.
    """
    # Add pulses & waveforms
    super().apply_to_config(config)

    if str_ref.is_reference(self.name):
        raise AttributeError(
            f"Channel {self.get_reference()} cannot be added to the config because"
            " it doesn't have a name. Either set channel.id to a string or"
            " integer, or channel should be an attribute of another QuAM component"
            " with a name."
        )

    element_config = config["elements"][self.name]

    filter_fir_taps = self.filter_fir_taps
    if filter_fir_taps is not None:
        filter_fir_taps = list(filter_fir_taps)
    filter_iir_taps = self.filter_iir_taps
    if filter_iir_taps is not None:
        filter_iir_taps = list(filter_iir_taps)

    if isinstance(self.opx_output, LFAnalogOutputPort):
        opx_port = self.opx_output
    elif len(self.opx_output) == 2:
        opx_port = OPXPlusAnalogOutputPort(
            *self.opx_output,
            offset=self.opx_output_offset,
            feedforward_filter=filter_fir_taps,
            feedback_filter=filter_iir_taps,
        )
        opx_port.apply_to_config(config)
    else:
        opx_port = LFFEMAnalogOutputPort(
            *self.opx_output,
            offset=self.opx_output_offset,
            feedforward_filter=filter_fir_taps,
            feedback_filter=filter_iir_taps,
        )
        opx_port.apply_to_config(config)

    element_config["singleInput"] = {"port": opx_port.port_tuple}

set_dc_offset(offset)

Set the DC offset of an element's input to the given value. This value will remain the DC offset until changed or until the Quantum Machine is closed.

Parameters:

Name Type Description Default
offset QuaNumberType

The DC offset to set the input to. This is limited by the OPX output voltage range. The number can be a QUA variable

required
Source code in quam/components/channels.py
608
609
610
611
612
613
614
615
616
617
618
def set_dc_offset(self, offset: QuaNumberType):
    """Set the DC offset of an element's input to the given value.
    This value will remain the DC offset until changed or until the Quantum Machine
    is closed.

    Args:
        offset (QuaNumberType): The DC offset to set the input to.
            This is limited by the OPX output voltage range.
            The number can be a QUA variable
    """
    set_dc_offset(element=self.name, element_input="single", offset=offset)

StickyChannelAddon

Bases: QuamComponent

Addon to make channels sticky.

Parameters:

Name Type Description Default
duration int

The ramp to zero duration, in ns.

required
enabled bool

If False, the sticky parameters are not applied. Default is True.

required
analog bool

If False, the sticky parameters are not applied to analog outputs. Default is True.

required
digital bool

If False, the sticky parameters are not applied to digital outputs. Default is True.

required
Source code in quam/components/channels.py
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
@quam_dataclass
class StickyChannelAddon(QuamComponent):
    """Addon to make channels sticky.

    Args:
        duration (int): The ramp to zero duration, in ns.
        enabled (bool, optional): If False, the sticky parameters are not applied.
            Default is True.
        analog (bool, optional): If False, the sticky parameters are not applied to
            analog outputs. Default is True.
        digital (bool, optional): If False, the sticky parameters are not applied to
            digital outputs. Default is True.
    """

    duration: int
    enabled: bool = True
    analog: bool = True
    digital: bool = True

    @property
    def channel(self) -> Optional["Channel"]:
        """If the parent is a channel, returns the parent, otherwise returns None."""
        if isinstance(self.parent, Channel):
            return self.parent
        else:
            return

    @property
    def config_settings(self):
        if self.channel is not None:
            return {"after": [self.channel]}

    def apply_to_config(self, config: dict) -> None:
        if self.channel is None:
            return

        if not self.enabled:
            return

        config["elements"][self.channel.name]["sticky"] = {
            "analog": self.analog,
            "digital": self.digital,
            "duration": self.duration,
        }

channel: Optional[Channel] property

If the parent is a channel, returns the parent, otherwise returns None.

TimeTaggingAddon

Bases: QuamComponent

Addon to perform time tagging on a channel.

Parameters:

Name Type Description Default
signal_threshold float

The signal threshold in volts. If not specified, the default value is 800 / 4096 ≈ 0.195 V.

required
signal_polarity Literal['above', 'below']

The polarity of the signal threshold. Default is "below".

required
derivative_threshold float

The derivative threshold in volts/ns. If not specified, the default value is 300 / 4096 ≈ 0.073 V/ns.

required
derivative_polarity Literal['above', 'below']

The polarity of the derivative threshold. Default is "below".

required

For details see Time Tagging

Source code in quam/components/channels.py
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
@quam_dataclass
class TimeTaggingAddon(QuamComponent):
    """Addon to perform time tagging on a channel.

    Args:
        signal_threshold (float, optional): The signal threshold in volts.
            If not specified, the default value is 800 / 4096 ≈ 0.195 V.
        signal_polarity (Literal["above", "below"]): The polarity of the signal
            threshold. Default is "below".
        derivative_threshold (float, optional): The derivative threshold in volts/ns.
            If not specified, the default value is 300 / 4096 ≈ 0.073 V/ns.
        derivative_polarity (Literal["above", "below"]): The polarity of the derivative
            threshold. Default is "below".

    For details see [Time Tagging](https://docs.quantum-machines.co/latest/docs/Guides/features/#time-tagging)
    """

    signal_threshold: float = 800 / 4096
    signal_polarity: Literal["above", "below"] = "below"
    derivative_threshold: float = 300 / 4096
    derivative_polarity: Literal["above", "below"] = "below"
    enabled: bool = True

    @property
    def channel(self) -> Optional["Channel"]:
        """If the parent is a channel, returns the parent, otherwise returns None."""
        if isinstance(self.parent, Channel):
            return self.parent
        else:
            return

    @property
    def config_settings(self):
        if self.channel is not None:
            return {"after": [self.channel]}

    def apply_to_config(self, config: dict) -> None:
        if self.channel is None:
            return

        if not self.enabled:
            return

        if self.signal_threshold is not None and abs(self.signal_threshold) > 1:
            raise ValueError("TimeTaggingAddon.signal_threshold must be a voltage")
        # TODO should we also check derivative threshold? What should the max value be?

        ch_cfg = config["elements"][self.channel.name]
        ch_cfg["outputPulseParameters"] = {
            "signalThreshold": int(self.signal_threshold * 4096),
            "signalPolarity": self.signal_polarity,
            "derivativeThreshold": int(self.derivative_threshold * 4096),
            "derivativePolarity": self.derivative_polarity,
        }

channel: Optional[Channel] property

If the parent is a channel, returns the parent, otherwise returns None.