Source code for qblox_scheduler.device_under_test.device_element

# SPDX-FileCopyrightText: Copyright 2020-2025, Quantify Consortium
# SPDX-FileCopyrightText: © 2022 Qblox <https://qblox.com>
# SPDX-License-Identifier: BSD-3-Clause

"""The module contains definitions for device elements."""

from __future__ import annotations

import abc
import warnings
from typing import TYPE_CHECKING, Annotated, Any, ClassVar
from typing_extensions import Self

from pydantic import AfterValidator, Field, ModelWrapValidatorHandler, model_validator

from qblox_scheduler.structure.model import SchedulerBaseModel, is_identifier

if TYPE_CHECKING:
    from qblox_scheduler.backends.graph_compilation import DeviceCompilationConfig


def is_identifier_without_underscore(value: str) -> str:
    """Pydantic validator for names that are valid identifiers but without underscore."""
    value = is_identifier(value)
    if "_" in value:
        raise ValueError(f"{value} may not contain underscores")
    return value


[docs] class DeviceElement(abc.ABC, SchedulerBaseModel): """ Create a device element for managing parameters. The :class:`~DeviceElement` is responsible for compiling operations applied to that specific device element from the composite to the quantum-device layer. """ __model_registry__: ClassVar[dict[str, type[DeviceElement]]] = {} element_type: str name: Annotated[str, AfterValidator(is_identifier_without_underscore)] = Field(kw_only=False) @classmethod def __pydantic_init_subclass__(cls, **kwargs: Any) -> None: # noqa: ANN401 """Store new subclasses of :class:`~DeviceElement` into a class-level registry.""" super().__pydantic_init_subclass__(**kwargs) DeviceElement.__model_registry__[cls.__name__] = cls def __getstate__(self) -> dict[str, Any]: """Get the state of :class:`~DeviceElement` (used for YAML serialization).""" return self.model_dump(exclude={"element_type"})
[docs] @model_validator(mode="before") @classmethod def include_submodule_names(cls, data: Any) -> Any: # noqa: ANN401 """ Fill in the ``name`` attribute of :class:`~DeviceElement` submodules when missing (used for YAML deserialization, they are omitted at serialization). """ if isinstance(data, dict): for submodule_name, submodule_data in data.items(): if submodule_name in ("name", "element_type"): continue if "name" not in submodule_data: submodule_data["name"] = submodule_name return data
[docs] @model_validator(mode="before") @classmethod def _drop_reference_magnitude(cls, data: Any) -> Any: # noqa: ANN401 if isinstance(data, dict) and "reference_magnitude" in data: data = dict(data) data.pop("reference_magnitude") warnings.warn( f"{cls.__name__!r} in this configuration contains a " "'reference_magnitude' entry. This option was removed and will be " "ignored. Remove it from your configuration to silence this warning; " "all other unrecognized keys remain errors.", DeprecationWarning, stacklevel=2, ) return data
[docs] @model_validator(mode="wrap") @classmethod def dispatch_concrete_model( cls, data: Any, # noqa: ANN401 handler: ModelWrapValidatorHandler[Self], ) -> Self: """ When deserializing a dict representation of a concrete :class:`~DeviceElement`, infer the matching class by looking its `element_type` into the model registry and return a validated instance of the concrete device element. """ if cls is DeviceElement and isinstance(data, dict): element_type = data.get("element_type") if element_type in cls.__model_registry__: return cls.__model_registry__[element_type].model_validate(data) # type: ignore[reportReturnType] else: raise ValueError(f"unknown element type '{element_type}'") return handler(data)
[docs] @abc.abstractmethod def generate_device_config(self) -> DeviceCompilationConfig: """Generate the device configuration.""" raise NotImplementedError