Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Abstraction for LO frequency to MWChannel that matches the IQChannel #75

Merged
merged 8 commits into from
Oct 11, 2024
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## [Unreleased]
### Changed
- Modified `MWChannel` to also have `RF_frequency` and `LO_frequency` to match the signature of `IQChannel`.
This is done by letting both inherit from a new base class `_OutComplexChannel`.

## [0.3.5]
### Added
- Added `DragCosinePulse`.
Expand Down
91 changes: 50 additions & 41 deletions quam/components/channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -882,43 +882,11 @@ def measure_sliced(


@quam_dataclass
class IQChannel(Channel):
"""QuAM component for an IQ output channel.
class _OutComplexChannel(Channel, ABC):
"""Base class for IQ and MW output channels."""

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"
LO_frequency: float
RF_frequency: float

@property
def inferred_RF_frequency(self) -> float:
Expand Down Expand Up @@ -986,6 +954,46 @@ def inferred_LO_frequency(self) -> float:
)
return self.RF_frequency - self.intermediate_frequency


@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)
Expand Down Expand Up @@ -1571,7 +1579,7 @@ class InIQOutSingleChannel(SingleChannel, InIQChannel):


@quam_dataclass
class MWChannel(Channel):
class MWChannel(_OutComplexChannel):
"""QuAM component for a MW FEM output channel

Args:
Expand All @@ -1592,13 +1600,16 @@ class MWChannel(Channel):
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
"upconverter": self.upconverter,
}


Expand Down Expand Up @@ -1626,9 +1637,7 @@ 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["MWOutput"] = {"port": self.opx_input.port_tuple}
element_config["smearing"] = self.smearing
element_config["time_of_flight"] = self.time_of_flight

Expand Down
15 changes: 15 additions & 0 deletions tests/components/channels/test_mw_channel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from quam.components.channels import MWChannel
from quam.components.ports.analog_outputs import MWFEMAnalogOutputPort


def test_mw_channel():
opx_output = MWFEMAnalogOutputPort(
controller_id="con1", fem_id=1, port_id=1, band=1, upconverter_frequency=1e9
)
channel = MWChannel(opx_output=opx_output, upconverter=1, intermediate_frequency=0)

assert channel.LO_frequency == 1e9
assert channel.RF_frequency == 1e9

channel.intermediate_frequency = 100e6
assert channel.RF_frequency == 1.1e9