Source code for semi_cr.core.quam_components.cryo_switch
from __future__ import annotations
from quam.components.channels import InOutSingleChannel
from quam.components.pulses import SquareReadoutPulse
from quam.core import QuamComponent, quam_dataclass
from quam.utils.qua_types import ScalarInt
__all__ = ["CryoSwitch"]
[docs]
@quam_dataclass
class CryoSwitch(QuamComponent):
"""QUAM component for a cryogenic RF switch / multiplexer.
Groups multiple :class:`~quam.components.channels.InOutSingleChannel` instances
and exposes helpers to drive them together inside QUA programs (frequency sweeps,
alignment, simultaneous measurements, etc.).
Attributes:
channels: Mapping from logical channel names to in/out reflectometry channels.
"""
in_channel: InOutSingleChannel
channels: dict[str, InOutSingleChannel]
amplitude: float = 0.1
intermediate_frequency: int = 100_000_000
readout_length: int = 1000
@property
def valid_channel_names(self) -> list[str]:
return list(self.channels.keys())
[docs]
def all_channels(self) -> list[InOutSingleChannel]:
if self.in_channel is None:
raise ValueError("In channel is not set")
return [self.in_channel] + list(self.channels.values())
[docs]
def setup_switch(self) -> None:
if self.in_channel is None:
raise ValueError("In channel is not set")
# Update intermediate frequency of all channels to match self.intermediate_frequency
self.in_channel.intermediate_frequency = self.intermediate_frequency
for channel in self.channels.values():
channel.intermediate_frequency = self.intermediate_frequency
inop = SquareReadoutPulse(
id="cryo_switch_readout",
length=self.readout_length,
amplitude=self.amplitude,
)
self.in_channel.operations["cryo_switch_readout"] = inop
for channel in self.channels.values():
outop = SquareReadoutPulse(
id="cryo_switch_readout",
length=self.readout_length,
amplitude=0,
)
channel.operations["cryo_switch_readout"] = outop
[docs]
def validate_switch(self) -> None:
# Validate all channels' intermediate frequency matches
for channel in self.all_channels():
if channel.intermediate_frequency != self.intermediate_frequency:
raise ValueError(
f"Channel '{channel.name}' has intermediate_frequency={channel.intermediate_frequency}, "
f"expected {self.intermediate_frequency}"
)
# Validate 'cryo_switch_readout' operations
for channel in self.all_channels():
if "cryo_switch_readout" not in channel.operations:
raise ValueError(
f"Channel {channel.name} does not have a 'cryo_switch_readout' operation"
)
op = channel.operations["cryo_switch_readout"]
# Ensure op meets specs
if op.length != self.readout_length:
raise ValueError(
f"Channel {channel.name} has a 'cryo_switch_readout' operation with a length of {op.length}, "
f"but the required readout length is {self.readout_length}"
)
expected_amplitude = self.amplitude if channel is self.in_channel else 0
if op.amplitude != expected_amplitude:
raise ValueError(
f"Channel {channel.name} has a 'cryo_switch_readout' amplitude {op.amplitude}, "
f"expected {expected_amplitude} (amplitude only on in_channel)"
)
[docs]
def align(self) -> None:
"""Align timing across the selected channels (default: all)."""
self.in_channel.align(*self.channels)
[docs]
def wait(
self,
duration: ScalarInt,
) -> None:
"""Wait on all selected channels without outputting a pulse."""
self.in_channel.wait(duration, *self.channels)
[docs]
def update_frequency(
self,
new_frequency: ScalarInt,
units: str = "Hz",
keep_phase: bool = False,
) -> None:
"""Update the intermediate frequency on all selected channels."""
for channel in self.all_channels():
channel.update_frequency(new_frequency, units=units, keep_phase=keep_phase)
[docs]
def reset_phase(self) -> None:
"""Reset phase on all channels"""
for channel in self.all_channels():
channel.reset_if_phase()
[docs]
def measure(
self,
):
self.validate_switch()
self.align()
results = {channel.name: {} for channel in self.all_channels()}
for channel in self.all_channels():
results[channel.name]["I"], results[channel.name]["Q"] = channel.measure(
pulse_name="cryo_switch_readout"
)
return results