Commit 41bdb768 authored by Robert Sokolewicz's avatar Robert Sokolewicz
Browse files

Merge branch 'configure_gain_from_input' into 'main'

Qblox backend: configure gain in separate method

See merge request !533
parents cbb4c8ef daa851b9
Loading
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
- Compilation - Can optionally provide a `QuantumDevice` to `QuantifyCompiler`. This will be used as default `CompilationConfig` in `QuantifyCompiler.compile()` (!535)
- NV centers - Avoid python warning when parsing docstring in nv_element (!562)
- QuantumDevice - `BasicTransmonElement` can now be serialized to json string and deserialized via ``__getstate__/__init__`` (!510)
- Qblox Backend - Added method for gain configuration, overriding gain now raises ValueError (!533)
- Qblox backend - add TriggerCount to `QRMAcquisitionManager` (!556)
- Deprecation - Refactored tests to remove deprecated `qcompile`, refactored to `SerialCompiler` (!529, #368)
- Waveforms - Fix `sudden_net_zero` waveform generation function misunderstands `amp_B` (!549, #390)
+69 −90
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ from quantify_scheduler.backends.qblox import (
    q1asm_instructions,
    register_manager,
)
from quantify_scheduler.backends.qblox.helpers import calc_from_units_volt
from quantify_scheduler.backends.qblox.operation_handling.acquisitions import (
    AcquisitionStrategyPartial,
)
@@ -1024,17 +1025,11 @@ class QbloxBaseModule(ControlDeviceCompiler, ABC):
        differences between the different modules.
        """

    def _construct_sequencers(self) -> Dict[str, Sequencer]:
    def _construct_sequencers(self):
        """
        Constructs `Sequencer` objects for each port and clock combination
        belonging to this device.

        Returns
        -------
        :
            A dictionary containing the sequencer objects, the keys correspond to the
            names of the sequencers.

        Raises
        ------
        ValueError
@@ -1154,7 +1149,7 @@ class QbloxBaseModule(ControlDeviceCompiler, ABC):
                f"{self.static_hw_properties.max_sequencers}!"
            )

        return sequencers
        self.sequencers = sequencers

    def distribute_data(self):
        """
@@ -1271,110 +1266,94 @@ class QbloxBaseModule(ControlDeviceCompiler, ABC):
        self._settings = self.settings_type.extract_settings_from_mapping(
            self.hw_mapping
        )
        self._settings = self._configure_mixer_offsets_and_gains(
            self._settings, self.hw_mapping
        )
        self.sequencers = self._construct_sequencers()
        self._configure_input_gains()
        self._configure_mixer_offsets()
        self._construct_sequencers()
        self.distribute_data()
        self._determine_scope_mode_acquisition_sequencer()
        for seq in self.sequencers.values():
            self.assign_frequencies(seq)
        self.assign_attenuation()

    def _configure_mixer_offsets_and_gains(
        self, settings: BaseModuleSettings, hw_mapping: Dict[str, Any]
    ) -> BaseModuleSettings:
    def _configure_input_gains(self):
        """
        We configure the mixer offsets, gains, attenuations
        after initializing the settings such we can account for
        the differences in the hardware. e.g. the V vs mV encountered here.

        Parameters
        ----------
        settings
            The settings dataclass to which to add the dc offsets and gains.
        hw_mapping
            The hardware configuration.

        Returns
        -------
        :
            The settings dataclass after adding the normalized offsets and gains.

        Raises
        ------
        ValueError
            An offset was used outside of the allowed range.
        Configures input gain of module settings.
        Loops through all valid ios and checks for gain values in hw config.
        Throws a ValueError if a gain value gets modified.
        """
        in0_gain, in1_gain = None, None
        for io_name in self.static_hw_properties.valid_ios:
            io_mapping = self.hw_mapping.get(io_name, None)
            if io_mapping is None:
                continue

        def calc_from_units_volt(
            param_name: str, cfg: Dict[str, Any]
        ) -> Optional[float]:
            if io_name.startswith("complex"):
                in0_gain = io_mapping.get("input_gain_I", None)
                in1_gain = io_mapping.get("input_gain_Q", None)

            offset_in_config = cfg.get(param_name, None)  # Always in volts
            if offset_in_config is None:
                return None
            elif io_name.startswith("real"):
                # The next code block is for backwards compatibility.
                in_gain = io_mapping.get("input_gain", None)
                if in_gain is None:
                    in0_gain = io_mapping.get("input_gain0", None)
                    in1_gain = io_mapping.get("input_gain1", None)
                else:
                    in0_gain = in_gain
                    in1_gain = in_gain

            conversion_factor = 1
            voltage_range = self.static_hw_properties.mixer_dc_offset_range
            if voltage_range.units == "mV":
                conversion_factor = 1e3
            elif voltage_range.units != "V":
                raise RuntimeError(
                    f"Parameter {param_name} of {self.name} specifies "
                    f"the units {voltage_range.units}, but this is not "
                    f"supported by the Qblox backend."
            if in0_gain is not None:
                if (
                    self._settings.in0_gain is None
                    or in0_gain == self._settings.in0_gain
                ):
                    self._settings.in0_gain = in0_gain
                else:
                    raise ValueError(
                        f"Overwriting gain of {io_name} of module {self.name} "
                        f"to in0_gain: {in0_gain}.\nIt was previously set to "
                        f"in0_gain: {self._settings.in0_gain}."
                    )

            calculated_offset = offset_in_config * conversion_factor
            if in1_gain is not None:
                if (
                calculated_offset < voltage_range.min_val
                or calculated_offset > voltage_range.max_val
                    self._settings.in1_gain is None
                    or in1_gain == self._settings.in1_gain
                ):
                    self._settings.in1_gain = in1_gain
                else:
                    raise ValueError(
                    f"Attempting to set {param_name} of {self.name} to "
                    f"{offset_in_config} V. {param_name} has to be between "
                    f"{voltage_range.min_val/ conversion_factor} and "
                    f"{voltage_range.max_val/ conversion_factor} V!"
                        f"Overwriting gain of {io_name} of module {self.name}"
                        f"to in1_gain: {in1_gain}.\nIt was previously set to "
                        f"in1_gain: {self._settings.in1_gain}."
                    )

            return calculated_offset

    def _configure_mixer_offsets(self):
        """
        Configures offset of input, uses calc_from_units_volt found in helper file.
        Raises an exception if a value outside the accepted voltage range is given.
        """
        supported_outputs = ("complex_output_0", "complex_output_1")
        for output_idx, output_label in enumerate(supported_outputs):
            if output_label not in hw_mapping:
            if output_label not in self.hw_mapping:
                continue

            output_cfg = hw_mapping[output_label]
            output_cfg = self.hw_mapping[output_label]
            voltage_range = self.static_hw_properties.mixer_dc_offset_range
            if output_idx == 0:
                settings.offset_ch0_path0 = calc_from_units_volt(
                    "dc_mixer_offset_I", output_cfg
                self._settings.offset_ch0_path0 = calc_from_units_volt(
                    voltage_range, self.name, "dc_mixer_offset_I", output_cfg
                )
                settings.offset_ch0_path1 = calc_from_units_volt(
                    "dc_mixer_offset_Q", output_cfg
                self._settings.offset_ch0_path1 = calc_from_units_volt(
                    voltage_range, self.name, "dc_mixer_offset_Q", output_cfg
                )
            else:
                settings.offset_ch1_path0 = calc_from_units_volt(
                    "dc_mixer_offset_I", output_cfg
                self._settings.offset_ch1_path0 = calc_from_units_volt(
                    voltage_range, self.name, "dc_mixer_offset_I", output_cfg
                )
                settings.offset_ch1_path1 = calc_from_units_volt(
                    "dc_mixer_offset_Q", output_cfg
                self._settings.offset_ch1_path1 = calc_from_units_volt(
                    voltage_range, self.name, "dc_mixer_offset_Q", output_cfg
                )

        complex_output_0 = hw_mapping.get("complex_output_0", None)
        if complex_output_0 is not None:
            settings.in0_gain = complex_output_0.get("input_gain_I", None)
            settings.in1_gain = complex_output_0.get("input_gain_Q", None)
        else:
            settings.in0_gain = hw_mapping.get("real_output_0", {}).get(
                "input_gain", None
            )
            settings.in1_gain = hw_mapping.get("real_output_1", {}).get(
                "input_gain", None
            )

        return settings

    def _determine_scope_mode_acquisition_sequencer(self) -> None:
        """
        Finds which sequencer has to perform raw trace acquisitions and adds it to the
+58 −0
Original line number Diff line number Diff line
@@ -645,3 +645,61 @@ def convert_hw_config_to_portclock_configs_spec(
    _update_hw_config(hw_config)

    return hw_config


def calc_from_units_volt(
    voltage_range, name: str, param_name: str, cfg: Dict[str, Any]
) -> Optional[float]:
    """
    Helper method to calculate the offset from mV or V.
    Then compares to given voltage range, and throws a ValueError if out of bounds.

    Parameters
    ----------
    voltage_range
        The range of the voltage levels of the device used.
    name
        The name of the device used.
    param_name
        The name of the current parameter the method is used for.
    cfg
        The hardware config of the device used.

    Returns
    -------
        The normalized offsets.

    Raises
    ------
    RuntimeError
        When a unit range is given that is not supported, or a value is given that falls
        outside the allowed range.

    """
    offset_in_config = cfg.get(param_name, None)  # Always in volts
    if offset_in_config is None:
        return None

    conversion_factor = 1
    if voltage_range.units == "mV":
        conversion_factor = 1e3
    elif voltage_range.units != "V":
        raise RuntimeError(
            f"Parameter {param_name} of {name} specifies "
            f"the units {voltage_range.units}, but the Qblox "
            f"backend only supports mV and V."
        )

    calculated_offset = offset_in_config * conversion_factor
    if (
        calculated_offset < voltage_range.min_val
        or calculated_offset > voltage_range.max_val
    ):
        raise ValueError(
            f"Attempting to set {param_name} of {name} to "
            f"{offset_in_config} V. {param_name} has to be between "
            f"{voltage_range.min_val / conversion_factor} and "
            f"{voltage_range.max_val / conversion_factor} V!"
        )

    return calculated_offset
+4 −4
Original line number Diff line number Diff line
@@ -367,9 +367,9 @@ class SequencerSettings(DataClassJsonMixin):
    """Specifies whether IQ mixing will be used or not."""
    sync_en: bool
    """Enables party-line synchronization."""
    connected_outputs: Union[Tuple[int], Tuple[int, int]]
    connected_outputs: Optional[Union[Tuple[int], Tuple[int, int]]]
    """Specifies which physical outputs this sequencer produces waveform data for."""
    connected_inputs: Union[Tuple[int], Tuple[int, int]]
    connected_inputs: Optional[Union[Tuple[int], Tuple[int, int]]]
    """Specifies which physical inputs this sequencer collects data for."""
    init_offset_awg_path_0: float = 0.0
    """Specifies what value the sequencer offset for AWG path 0 will be reset to
@@ -409,8 +409,8 @@ class SequencerSettings(DataClassJsonMixin):
    def initialize_from_config_dict(
        cls,
        seq_settings: Dict[str, Any],
        connected_outputs: Union[Tuple[int], Tuple[int, int]],
        connected_inputs: Union[Tuple[int], Tuple[int, int]],
        connected_outputs: Optional[Union[Tuple[int], Tuple[int, int]]],
        connected_inputs: Optional[Union[Tuple[int], Tuple[int, int]]],
    ) -> SequencerSettings:
        """
        Instantiates an instance of this class, with initial parameters determined from
+1 −1
Original line number Diff line number Diff line
@@ -743,7 +743,7 @@ class QRMRFComponent(QRMComponent):
            self._set_parameter(
                self.instrument, "out0_offset_path1", settings.offset_ch0_path1
            )
        # configure gain and attenuation
        # configure attenuation
        if settings.out0_att is not None:
            self._set_parameter(self.instrument, "out0_att", settings.out0_att)
        if settings.in0_att is not None:
Loading