Source code for qeg_nmr_qua.config.config

"""
OPX-1000 Configuration Module.

This module provides configuration utilities for the OPX-1000 LF-FEM
for solid state NMR experiments.
"""

from dataclasses import dataclass, field
from typing import Any
from collections.abc import Iterable

from qeg_nmr_qua.config.controller import ControllerConfig
from qeg_nmr_qua.config.element import ElementConfig, Element
from qeg_nmr_qua.config.pulse import PulseConfig, ControlPulse, MeasPulse
from qeg_nmr_qua.config.waveform import AnalogWaveformConfig, DigitalWaveformConfig
from qeg_nmr_qua.config.integration import IntegrationWeights


[docs] @dataclass class OPXConfig: """ Complete configuration for the OPX-1000 low-frequency front-end module. This is the main configuration class that aggregates all sub-configurations needed to operate the OPX-1000 for NMR experiments. It manages controllers, elements (physical connections), pulses, waveforms, digital waveforms, and integration weights. **Connection Settings:** - ``qop_ip``: IP address of the Quantum Orchestration Platform (default: "192.168.88.253") - ``cluster``: Name of the OPX cluster to use (default: "lex") **Configuration Components:** Each component is a nested configuration object: - ``controllers``: :class:`~ControllerConfig` - Hardware controllers (analog/digital I/O) - ``elements``: :class:`~ElementConfig` - Physical elements (resonators, amplifiers, etc.) - ``pulses``: :class:`~PulseConfig` - Pulse definitions (control and measurement) - ``waveforms``: :class:`~AnalogWaveformConfig` - Analog waveform shapes - ``digital_waveforms``: :class:`~DigitalWaveformConfig` - Digital marker waveforms - ``integration_weights``: :class:`~IntegrationWeights` - Integration weight sets for demodulation **Serialization:** Use :meth:`to_dict` and :meth:`from_dict` for programmatic serialization, or use :meth:`save_to_file` and :meth:`load_from_file` for JSON file I/O. **OPX Format:** Use :meth:`to_opx_config` to generate the configuration in OPX-compatible format for passing to the Quantum Orchestration Platform. Example: >>> config = OPXConfig(qop_ip="192.168.1.100") >>> config.elements.add_element("resonator", frequency=282.1901e6) >>> opx_config_dict = config.to_opx_config() """ qop_ip: str = "192.168.88.253" cluster: str = "lex" controllers: ControllerConfig = field(default_factory=ControllerConfig) elements: ElementConfig = field(default_factory=ElementConfig) pulses: PulseConfig = field(default_factory=PulseConfig) waveforms: AnalogWaveformConfig = field(default_factory=AnalogWaveformConfig) digital_waveforms: DigitalWaveformConfig = field( default_factory=DigitalWaveformConfig ) integration_weights: IntegrationWeights = field(default_factory=IntegrationWeights)
[docs] def to_dict(self) -> dict[str, Any]: """Convert the OPX configuration to a dictionary.""" return { "qop_ip": self.qop_ip, "cluster": self.cluster, "controllers": self.controllers.to_dict(), "elements": self.elements.to_dict(), "pulses": self.pulses.to_dict(), "waveforms": self.waveforms.to_dict(), "digital_waveforms": self.digital_waveforms.to_dict(), "integration_weights": self.integration_weights.to_dict(), }
[docs] @classmethod def from_dict(cls, d: dict[str, Any]) -> "OPXConfig": # Import here to avoid circular imports at module import time controllers = ControllerConfig.from_dict(d.get("controllers", {})) elements = ElementConfig.from_dict(d.get("elements", {})) pulses = PulseConfig.from_dict(d.get("pulses", {})) waveforms = AnalogWaveformConfig.from_dict(d.get("waveforms", {})) digital_waveforms = DigitalWaveformConfig.from_dict( d.get("digital_waveforms", {}) ) integration_weights = IntegrationWeights.from_dict( d.get("integration_weights", {}) ) return cls( qop_ip=d.get("qop_ip", "192.168.88.253"), cluster=d.get("cluster", "lex"), controllers=controllers, elements=elements, pulses=pulses, waveforms=waveforms, digital_waveforms=digital_waveforms, integration_weights=integration_weights, )
[docs] def save_to_file(self, filepath: str) -> None: """Save the OPXConfig to a JSON file at `filepath`.""" import json with open(filepath, "w", encoding="utf-8") as f: json.dump(self.to_dict(), f, indent=2)
[docs] @classmethod def load_from_file(cls, filepath: str) -> "OPXConfig": """Load an OPXConfig from a JSON file at `filepath`.""" import json with open(filepath, "r", encoding="utf-8") as f: d = json.load(f) return cls.from_dict(d)
[docs] def to_opx_config(self) -> dict[str, Any]: """Convert to OPX configuration format.""" return { "controllers": self.controllers.to_opx_config(), "elements": self.elements.to_opx_config(), "pulses": self.pulses.to_opx_config(), "waveforms": self.waveforms.to_opx_config(), "digital_waveforms": self.digital_waveforms.to_opx_config(), "integration_weights": self.integration_weights.to_opx_config(), }
[docs] def add_controller(self, controller_config: ControllerConfig): """Add a controller configuration.""" self.controllers = controller_config
[docs] def add_element(self, name: str, element: Element): self.elements.add_element(name, element)
[docs] def add_pulse(self, name: str, pulse: ControlPulse | MeasPulse): """Add a pulse configuration.""" self.pulses.add_pulse(name, pulse)
[docs] def add_control_pulse(self, pulse_name: str, element_name: str, amplitude: float, length: int, waveform: list[float]): """ Add a control pulse configuration. """ self.pulses.add_control_pulse( pulse_name, element_name, amplitude, length, waveform )
[docs] def add_waveform(self, name: str, waveform: float | list[float]): """Add an analog waveform configuration.""" self.waveforms.add_waveform(name, sample=waveform)
[docs] def add_digital_waveform(self, name: str, state: int = 0, length: int = 0): """Add a digital waveform (marker) configuration.""" self.digital_waveforms.add_waveform(name, state=state, length=length)
[docs] def add_integration_weight( self, name: str, length: int, real_weight: float = 1, imag_weight: float = 0 ): """Add an integration weight configuration.""" self.integration_weights.add_weight( name, length, real_weight=real_weight, imag_weight=imag_weight )
def __repr__(self) -> str: return f"OPXConfig(qop_ip={self.qop_ip}, cluster='{self.cluster}', num_modules={len(self.controllers.modules.keys())}, num_elements={len(self.elements.elements)})"