Skip to content

Channel Ports

In the section Channels, we have seen how to create analog channels and attach digital outputs to them. In these examples, the ports are defined by simple tuples like (controller, fem, port). However, for more advanced use cases and better organization, it is recommended to define ports using dedicated Port QUAM components managed through port containers. This approach provides several benefits:

  1. Centralized port management: Define all ports in one location and reference them from channels.
  2. Port sharing: Multiple channels can reference the same physical port with consistent properties.
  3. Advanced port properties: Access hardware-specific features like crosstalk compensation, filters, sampling rates, and frequency conversion settings.

QUAM supports ports for three types of quantum control hardware:

  • LF-FEM Ports: Low-frequency FEM modules using 3-element tuples (controller_id, fem_id, port_id) with support for high sampling rates and flexible output modes.
  • MW-FEM Ports: Microwave FEM modules using 3-element tuples (controller_id, fem_id, port_id) with built-in frequency conversion, replacing the need for separate IQ channels with Octave frequency converters.
  • OPX+ Ports: OPX+ controllers using 2-element tuples (controller_id, port_id) for simpler setups.

All port types are defined in the quam.components.ports module.

Port Containers with BasicQuam

The recommended way to work with ports is to use a FEMPortsContainer attached to your QuamRoot object. This provides centralized port management and enables port references.

Setting Up the Ports Container

Here's how to integrate a ports container into your QUAM state. For this example, we will be using BasicFEMQuam to create the QUAM instance:

from quam.components import BasicFEMQuam
from quam.components.ports import FEMPortsContainer

# Create a QUAM instance with ports container
machine = BasicFEMQuam(
    ports=FEMPortsContainer()
)

# Create ports using the container methods
machine.ports.get_analog_output(
    "con1", 1, 2,  # controller, fem_id, port_id
    create=True,
    offset=0.15,
    sampling_rate=2e9,  # 2 GHz sampling
)

machine.ports.get_mw_output(
    "con1", 1, 1,
    create=True,
    band=1,
    upconverter_frequency=5e9,  # 5 GHz upconverter
)

The create=True parameter automatically creates ports if they don't exist, simplifying setup code.

Basic Port Usage

LF-FEM Analog Output with Port References

For low-frequency control signals, use LF-FEM analog output ports referenced from your channels:

from quam.components import BasicFEMQuam, SingleChannel
from quam.components.ports import FEMPortsContainer

# Create QUAM instance with ports
machine = BasicFEMQuam(ports=FEMPortsContainer())

# Create the port
port = machine.ports.get_analog_output(
    "con1", 1, 2,  # controller, fem_id, port_id
    create=True,
    offset=0.15,
    sampling_rate=2e9,
    output_mode="amplified",
)

# Create channel that references this port
machine.channels["drive"] = SingleChannel(
    opx_output=port.get_reference()  # Get reference from port
)

Notice that the channel uses port.get_reference() to get a string reference instead of passing the port object directly. This is the recommended pattern for port usage.

MW-FEM for Microwave Control

For microwave applications, MW-FEM ports provide built-in frequency conversion, eliminating the need for separate IQ channels and external mixers:

from quam.components import BasicFEMQuam
from quam.components.channels import MWChannel
from quam.components.ports import FEMPortsContainer

# Create QUAM instance with ports
machine = BasicFEMQuam(ports=FEMPortsContainer())

# Create MW-FEM output port for microwave drive
mw_port = machine.ports.get_mw_output(
    "con1", 1, 1,
    create=True,
    band=1,
    upconverter_frequency=5.5e9,  # 5.5 GHz LO frequency
    sampling_rate=1e9,
)

# Create MW channel with reference to MW-FEM port
machine.channels["qubit_xy"] = MWChannel(
    opx_output=mw_port.get_reference(),
    intermediate_frequency=100e6,  # 100 MHz IF
)

The MW-FEM port handles both I and Q components internally. Use MWChannel for output-only, InMWChannel for input-only, or InOutMWChannel for input+output applications.

MW-FEM Readout with Linked Frequencies

For readout, link the downconverter frequency to the upconverter frequency:

from quam.components.channels import InOutMWChannel

mw_output = machine.ports.get_mw_output("con1", 1, 1, create=True, band=1, upconverter_frequency=6e9)
mw_input = machine.ports.get_mw_input(
    "con1", 1, 1, create=True, band=1,
    downconverter_frequency=mw_output.get_reference("upconverter_frequency"),
)

machine.channels["readout"] = InOutMWChannel(
    opx_output=mw_output.get_reference(),
    opx_input=mw_input.get_reference(),
)

Port Types and Hardware

LF-FEM Ports

Low-frequency FEM ports support high sampling rates and various output modes:

# LF-FEM analog output with advanced features
machine.ports.get_analog_output(
    "con1", 1, 2,  # controller, fem_id, port_id
    create=True,
    offset=0.15,
    sampling_rate=2e9,  # 1 GHz or 2 GHz
    output_mode="amplified",  # "direct" or "amplified"
    exponential_filter=[(10, 0.1), (20, 0.2)],  # For QOP >= 3.3.0
)

MW-FEM Ports

Microwave FEM ports integrate frequency conversion for direct RF/microwave control:

# MW-FEM output with upconverter
machine.ports.get_mw_output(
    "con1", 1, 1,  # controller, fem_id, port_id
    create=True,
    band=1,  # Required: 1 or 2
    upconverter_frequency=5.5e9,  # Upconversion LO frequency (Hz)
)

MW-FEM ports eliminate the need for external mixers and frequency converters.

OPX+ Ports

For systems using OPX+ controllers, ports use a simpler 2-tuple addressing scheme.

OPX+ Port Types

# Analog output with filters
machine.ports.get_analog_output(
    "con1", 3,  # controller, port_id (note: only 2 elements)
    create=True,
    offset=0.2,
    feedforward_filter=[0.7, 0.2, 0.1],  # FIR filter
    feedback_filter=[0.3, 0.4, 0.5],  # IIR filter
)

OPX+ with IQ Channels

When using OPX+ hardware, you may need IQ channels with separate I and Q ports:

from quam.components import BasicOPXPlusQuam, IQChannel
from quam.components.ports import OPXPlusPortsContainer
from quam.components.hardware import FrequencyConverter, LocalOscillator, Mixer

# For OPX+ systems, use OPXPlusPortsContainer
machine = BasicOPXPlusQuam(ports=OPXPlusPortsContainer())

# Create I and Q ports
port_I = machine.ports.get_analog_output("con1", 1, create=True, offset=0.1)
port_Q = machine.ports.get_analog_output("con1", 2, create=True, offset=-0.05)

# Create IQ channel with port references
machine.channels["qubit_xy"] = IQChannel(
    opx_output_I=port_I.get_reference(),
    opx_output_Q=port_Q.get_reference(),
    intermediate_frequency=100e6,
    frequency_converter_up=FrequencyConverter(
        local_oscillator=LocalOscillator(frequency=6e9, power=10),
        mixer=Mixer(),
    )
)

Note: MW-FEM ports are preferred over this approach for microwave applications as they integrate frequency conversion.

Port Container Methods

The FEMPortsContainer provides methods for each port type:

# LF-FEM ports (3 parameters: controller, fem_id, port_id)
machine.ports.get_analog_output("con1", 1, 2, create=True)
machine.ports.get_analog_input("con1", 1, 1, create=True)
machine.ports.get_digital_output("con1", 1, 1, create=True)

# MW-FEM ports
machine.ports.get_mw_output("con1", 1, 1, create=True, band=1, upconverter_frequency=5e9)
machine.ports.get_mw_input("con1", 1, 1, create=True, band=1, downconverter_frequency=5e9)

For OPXPlusPortsContainer, use 2 parameters (controller, port_id):

machine.ports.get_analog_output("con1", 3, create=True)
machine.ports.get_digital_input("con1", 1, create=True)

Port References

Use port.get_reference() to get string references for ports. See basic examples in Basic Port Usage above.

To reference specific attributes, use port.get_reference("attribute_name"). This is useful for linking converter frequencies:

mw_output = machine.ports.get_mw_output("con1", 1, 1, create=True, upconverter_frequency=5e9)
mw_input = machine.ports.get_mw_input(
    "con1", 1, 1, create=True,
    downconverter_frequency=mw_output.get_reference("upconverter_frequency"),
)

Advanced Features

Ports support crosstalk compensation, FIR/IIR filters (OPX+), and exponential filters (LF-FEM). Refer to the API documentation for complete parameter details.

Best Practices

  • Use port references: Always use port.get_reference() instead of passing port objects directly
  • Centralized management: Create all ports before channels for better organization
  • Configure properties on ports: Set properties like offset, shareable, inverted, and filters directly on Port objects, not on channels. Channel-level port properties are deprecated and will be removed in a future version.
  • Avoid deprecated channel properties: Do not use opx_output_offset, opx_input_offset, filter_fir_taps, filter_iir_taps, shareable, or inverted on channel objects. These emit deprecation warnings and will be removed.
  • Port sharing: Set shareable=True when multiple channels use the same port
  • Choose the right type: MW-FEM for microwave/RF, LF-FEM for high-rate baseband/IF

Migrating from Channel-Level Port Properties

!!! warning "Deprecated Channel Properties" As of QUAM v0.5.0, setting port properties on channels is deprecated. Runtime warnings are emitted to help you migrate your code. See the Channels Migration Guide for detailed examples.

Quick Migration Guide

If you're currently setting port properties on channels, migrate to explicit Port objects:

Channel Property Port Property Port Class
opx_output_offset offset OPXPlusAnalogOutputPort, LFFEMAnalogOutputPort
opx_input_offset offset OPXPlusAnalogInputPort, LFFEMAnalogInputPort
filter_fir_taps feedforward_filter LFAnalogOutputPort (all output ports)
filter_iir_taps feedback_filter (OPX+) or exponential_filter (LF-FEM) LFAnalogOutputPort (all output ports)
shareable (any channel) shareable All port types
inverted (digital) inverted DigitalOutputPort types

Example:

# Old (deprecated - emits warning)
channel = SingleChannel(opx_output=("con1", 1), opx_output_offset=0.15)

# New (recommended)
from quam.components.ports import OPXPlusAnalogOutputPort
port = OPXPlusAnalogOutputPort("con1", 1, offset=0.15)
channel = SingleChannel(opx_output=port)

See the complete migration guide in the Channels documentation for more examples.