Source code for kosmos.topology.node

import math
from abc import ABC, abstractmethod
from collections.abc import Collection
from dataclasses import dataclass
from enum import Enum
from typing import ClassVar


[docs] @dataclass(frozen=True) class NodeId: """Identifier of a node. Attributes: value (str): Identifier string. """ value: str def __post_init__(self) -> None: """Validate that the value is non-empty.""" if not self.value or not self.value.strip(): msg = "NodeId must be a non-empty string." raise ValueError(msg) def __str__(self) -> str: """Return the string value.""" return self.value
[docs] class NodeType(Enum): """Type of node.""" CLASSICAL = "classical" QUANTUM = "quantum"
[docs] class NodeRole(Enum): """Role of a node.""" END_USER = "end_user" ROUTER = "router" REPEATER = "repeater"
[docs] @dataclass(frozen=True, kw_only=True) class Node(ABC): """Base for network nodes. Attributes: id (NodeId): Node identifier. label (str | None): Label of the node. Defaults to None. roles (Collection[NodeRole]): Role(s) of the node. """ id: NodeId label: str | None = None roles: Collection[NodeRole] def __post_init__(self) -> None: """Make roles immutable and validate the node.""" if self.label is not None and not self.label.strip(): msg = "label must be None or a non-empty string." raise ValueError(msg) roles_fs = frozenset(self.roles) object.__setattr__(self, "roles", roles_fs) if not all(isinstance(r, NodeRole) for r in roles_fs): msg = "roles must contain only NodeRole members." raise TypeError(msg) @property @abstractmethod def type(self) -> NodeType: """Type of the node."""
[docs] def has_role(self, role: NodeRole) -> bool: """Check if this node has a given role. Args: role: Role to check. Returns: True if the role is present. """ return role in self.roles
[docs] @dataclass(frozen=True, kw_only=True) class ClassicalNode(Node): """Classical node. Attributes: id (NodeId): Node identifier. label (str | None): Label of the node. Defaults to None. roles (Collection[NodeRole]): Role(s) of the node. """ _TYPE: ClassVar[NodeType] = NodeType.CLASSICAL @property def type(self) -> NodeType: """Type of the node.""" return self._TYPE
[docs] @dataclass(frozen=True, kw_only=True) class QuantumNode(Node): """Quantum node. Attributes: id (NodeId): Node identifier. label (str | None): Label of the node. Defaults to None. roles (Collection[NodeRole]): Role(s) of the node. num_qubits (int): Number of physical qubits. num_data_qubits (int): Number of data qubits. Defaults to -1, which sets it to half the total number of qubits (rounded up). gate_fid (float): Fidelity of multi-qubit gates. Defaults to 1.0. meas_fid (float): Fidelity of single-qubit measurements. Defaults to 1.0. coherence_time (float): Maximum storage time before decoherence in seconds. has_transceiver (bool): Whether the node has a quantum transceiver. Defaults to False. """ num_qubits: int num_data_qubits: int = -1 gate_fid: float = 1.0 meas_fid: float = 1.0 coherence_time: float has_transceiver: bool = False _TYPE: ClassVar[NodeType] = NodeType.QUANTUM def __post_init__(self) -> None: """Validate the node.""" super().__post_init__() if self.num_qubits < 0 or not math.isfinite(self.num_qubits): msg = "num_qubits must be >= 0 and finite." raise ValueError(msg) if not (0 <= self.gate_fid <= 1): msg = "gate_fid must be in [0, 1]." raise ValueError(msg) if not (0 <= self.meas_fid <= 1): msg = "meas_fid must be in [0, 1]." raise ValueError(msg) if self.coherence_time <= 0 or not math.isfinite(self.coherence_time): msg = "coherence_time must be > 0 and finite." raise ValueError(msg) if self.num_data_qubits == -1: object.__setattr__(self, "num_data_qubits", max(0, int((self.num_qubits + 1) // 2))) if ( not math.isfinite(self.num_data_qubits) or self.num_data_qubits < 0 or self.num_data_qubits > self.num_qubits ): msg = f"num_data_qubits must be between 0 and {self.num_qubits}." raise ValueError(msg) @property def type(self) -> NodeType: """Type of the node.""" return self._TYPE @property def data_qubits(self) -> int: """Number of qubits available for data operations. Returns: int: Number of data qubits. """ return self.num_data_qubits @property def communication_qubits(self) -> int: """Number of qubits reserved for communication/entanglement. Returns: int: Number of communication qubits. """ return self.num_qubits - self.num_data_qubits