Source code for qblox_scheduler.device_under_test.edge
# Repository: https://gitlab.com/qblox/packages/software/qblox-scheduler
# Licensed according to the LICENSE file on the main branch
#
# Copyright 2020-2025, Quantify Consortium
# Copyright 2025, Qblox B.V.
"""The module contains definitions for edges."""
from __future__ import annotations
import abc
from typing import TYPE_CHECKING, Any, ClassVar
from typing_extensions import Self
from pydantic import ModelWrapValidatorHandler, PrivateAttr, model_validator
from qblox_scheduler.device_under_test.device_element import DeviceElement
from qblox_scheduler.structure.model import SchedulerBaseModel, SchedulerSubmodule
if TYPE_CHECKING:
from qblox_scheduler.backends.graph_compilation import OperationCompilationConfig
[docs]
class Edge(abc.ABC, SchedulerBaseModel):
"""
Create an Edge.
This class encapsulates the connection information between DeviceElements in the
QuantumDevice. It provides an interface for the QuantumDevice to generate the
edge information for use in the device compilation step. See
:class:`qblox_scheduler.device_under_test.composite_square_edge` for an example
edge implementation.
"""
__model_registry__: ClassVar[dict[str, type[Edge]]] = {}
[docs]
parent_element_name: str
[docs]
child_element_name: str
[docs]
_parent_device_element: DeviceElement | None = PrivateAttr(default=None)
[docs]
_child_device_element: DeviceElement | None = PrivateAttr(default=None)
@classmethod
def __pydantic_init_subclass__(cls, **kwargs: Any) -> None: # noqa: ANN401
"""Store new subclasses of :class:`~Edge` into a class-level registry."""
super().__pydantic_init_subclass__(**kwargs)
Edge.__model_registry__[cls.__name__] = cls
def __init__(
self,
parent_element: DeviceElement | str | None = None,
child_element: DeviceElement | str | None = None,
**data: Any, # noqa: ANN401
) -> None:
if not data.get("parent_element_name"):
data["parent_element_name"] = self._get_element_name(parent_element)
if not data.get("child_element_name"):
data["child_element_name"] = self._get_element_name(child_element)
if not data.get("name"):
data["name"] = f"{data['parent_element_name']}_{data['child_element_name']}"
super().__init__(**data)
if isinstance(parent_element, DeviceElement):
self._parent_device_element = parent_element
if isinstance(child_element, DeviceElement):
self._child_device_element = child_element
def __getstate__(self) -> dict[str, Any]:
"""Get the state of :class:`~Edge` (used for YAML serialization)."""
return self.model_dump(exclude={"edge_type"})
@model_validator(mode="before")
@classmethod
[docs]
def include_submodule_names(cls, data: Any) -> Any: # noqa: ANN401
"""
Fill in the ``name`` attribute of :class:`~Edge` 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",
"edge_type",
"parent_element_name",
"child_element_name",
):
continue
if "name" not in submodule_data:
submodule_data["name"] = submodule_name
return data
@model_validator(mode="wrap")
@classmethod
[docs]
def dispatch_concrete_model(
cls,
data: Any, # noqa: ANN401
handler: ModelWrapValidatorHandler[Self],
) -> Self:
"""
When deserializing a dict representation of a concrete :class:`~Edge`,
infer the matching class by looking its `edge_type` into the model registry
and return a validated instance of the concrete edge.
"""
if cls is Edge and isinstance(data, dict):
edge_type = data.get("edge_type")
if edge_type in cls.__model_registry__:
return cls.__model_registry__[edge_type].model_validate(data) # type: ignore[reportReturnType]
else:
raise ValueError(f"unknown edge type '{edge_type}'")
return handler(data)
@property
[docs]
def parent_element(self) -> DeviceElement | None:
"""Getter for the internal parent device element."""
return self._parent_device_element
@property
[docs]
def child_element(self) -> DeviceElement | None:
"""Getter for the internal child device element."""
return self._child_device_element
@property
[docs]
def submodules(self) -> dict[str, Any]:
"""Mapping of submodules of this edge."""
return {
field_name: field_value
for field_name, field_value in self
if isinstance(field_value, SchedulerSubmodule)
}
@staticmethod
[docs]
def _get_element_name(element: DeviceElement | dict | str | None) -> str:
"""
Get the name of an element represented as a `DeviceElement` instance,
dictionary or string.
"""
match element:
case DeviceElement():
return element.name
case dict():
return element["name"]
case str():
return element
case _:
raise TypeError(f"Invalid type for device element {element}")
@abc.abstractmethod
[docs]
def generate_edge_config(self) -> dict[str, dict[str, OperationCompilationConfig]]:
"""
Generate the device configuration for an edge.
This method is intended to be used when this object is part of a
device object containing multiple elements.
"""
raise NotImplementedError