From b8c7a06c5740bc8583bf89531eba19b10fb4fe3e Mon Sep 17 00:00:00 2001 From: Serwan Asaad Date: Thu, 23 Oct 2025 21:20:10 +0200 Subject: [PATCH 1/2] Feat: Support IQCC cloud without explicit dependency --- .../superconducting/qpu/base_quam.py | 35 ++++++++++++++----- quam_builder/tools/__init__.py | 10 +++++- 2 files changed, 35 insertions(+), 10 deletions(-) diff --git a/quam_builder/architecture/superconducting/qpu/base_quam.py b/quam_builder/architecture/superconducting/qpu/base_quam.py index df654cdc..fa07684f 100644 --- a/quam_builder/architecture/superconducting/qpu/base_quam.py +++ b/quam_builder/architecture/superconducting/qpu/base_quam.py @@ -1,5 +1,5 @@ from dataclasses import field -from typing import List, Dict, ClassVar, Optional, Union +from typing import List, Dict, ClassVar, Optional, Type, Union, cast from qm import QuantumMachinesManager, QuantumMachine from qm.octave import QmOctaveConfig @@ -14,6 +14,7 @@ from quam_builder.architecture.superconducting.qubit_pair import AnyTransmonPair from quam_builder.architecture.superconducting.qubit import AnyTransmon +from quam_builder.tools import load_class_from_string from qualang_tools.results.data_handler import DataHandler @@ -89,14 +90,30 @@ def connect(self) -> QuantumMachinesManager: Returns: QuantumMachinesManager: The opened Quantum Machine Manager. """ - settings = dict( - host=self.network["host"], - cluster_name=self.network["cluster_name"], - octave=self.get_octave_config(), - ) - if "port" in self.network: - settings["port"] = self.network["port"] - self.qmm = QuantumMachinesManager(**settings) + + if self.network.get("cloud", False): + if "qmm_settings" not in self.network: + raise ValueError( + "machine.network.qmm_settings is required for cloud connection" + ) + if "qmm_class" not in self.network: + raise ValueError( + "machine.network.qmm_class is required for cloud connection" + ) + + qmm_cls = load_class_from_string(self.network["qmm_class"]) + qmm_cls = cast(Type[QuantumMachinesManager], qmm_cls) + qmm_settings = self.network["qmm_settings"] + else: + qmm_cls = QuantumMachinesManager + qmm_settings = dict( + host=self.network["host"], + cluster_name=self.network["cluster_name"], + octave=self.get_octave_config(), + ) + if "port" in self.network: + qmm_settings["port"] = self.network["port"] + self.qmm = qmm_cls(**qmm_settings) return self.qmm def calibrate_octave_ports(self, QM: QuantumMachine) -> None: diff --git a/quam_builder/tools/__init__.py b/quam_builder/tools/__init__.py index 813e17d9..605b33c9 100644 --- a/quam_builder/tools/__init__.py +++ b/quam_builder/tools/__init__.py @@ -5,7 +5,15 @@ get_output_power_iq_channel, get_output_power_mw_channel, ) +from quam_builder.tools.import_utils import ( + load_class_from_string, +) __all__ = [ - *power_tools.__all__, + "calculate_voltage_scaling_factor", + "set_output_power_mw_channel", + "set_output_power_iq_channel", + "get_output_power_iq_channel", + "get_output_power_mw_channel", + "load_class_from_string", ] From 6f104765302c59f1f65f98a0af0e689226ed1df9 Mon Sep 17 00:00:00 2001 From: Serwan Asaad Date: Thu, 23 Oct 2025 21:20:22 +0200 Subject: [PATCH 2/2] add loader function --- quam_builder/tools/import_utils.py | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 quam_builder/tools/import_utils.py diff --git a/quam_builder/tools/import_utils.py b/quam_builder/tools/import_utils.py new file mode 100644 index 00000000..ec911fc9 --- /dev/null +++ b/quam_builder/tools/import_utils.py @@ -0,0 +1,37 @@ +"""Utilities for dynamic class loading and imports.""" + +import importlib +from typing import Type, TypeVar + +__all__ = ["load_class_from_string"] + +T = TypeVar("T") + + +def load_class_from_string(class_path: str) -> Type[T]: + """Load a class dynamically from a string path. + + Args: + class_path: Full path to the class in the format 'module.ClassName'. + + Returns: + The class type. + + Raises: + ValueError: If the class_path format is invalid. + ImportError: If the module or class cannot be imported. + + Example: + >>> MyClass = load_class_from_string("my_module.MyClass") + >>> instance = MyClass() + """ + if "." not in class_path: + raise ValueError( + "class_path should be a full path in the format 'module.ClassName'" + ) + module_path, class_name = class_path.rsplit(".", 1) + try: + module = importlib.import_module(module_path) + return getattr(module, class_name) + except (ModuleNotFoundError, AttributeError) as e: + raise ImportError(f"Could not import class '{class_path}': {e}")