Skip to content

QuAM Channels API

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
203
204
205
206
207
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
262
263
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
@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
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
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
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
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
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
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
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
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
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
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
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
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
 73
 74
 75
 76
 77
 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
@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
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
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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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
 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
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
@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
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
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
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
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
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
@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
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
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
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
@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
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
@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
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
@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
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
@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
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
@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
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
666
667
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
@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

    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)

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
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
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
663
664
665
666
667
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
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
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
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
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
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)

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
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
@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
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
@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
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
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
@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
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
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
548
549
550
551
552
553
554
555
556
557
558
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
157
158
159
160
161
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
@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.