Source code for quchip.chip.ports

"""Physical Markovian input-output channels attached to a chip."""

from __future__ import annotations

from collections.abc import Sequence
from typing import TYPE_CHECKING, Any

import numpy as np

from quchip.declarative.dissipation import CollapseChannel, normalize_dissipation
from quchip.utils.jax_utils import maybe_concrete_scalar
from quchip.utils.labeling import auto_label, resolve_label

if TYPE_CHECKING:
    from quchip.chip.chip import Chip


[docs] class Port: """One accessible Markovian channel with a dimensionless coupling operator.""" _type_prefix = "port" _parameter_names = ("rate", "external_quality_factor", "phase") def __init__( self, target: Any | Sequence[Any], *, rate: Any = None, external_quality_factor: Any = None, operator: Any = None, phase: Any = 0.0, label: str | None = None, ) -> None: if isinstance(target, Sequence) and not isinstance(target, (str, bytes)): targets = tuple(target) else: targets = (target,) if not targets: raise ValueError("Port requires at least one target device.") if (rate is None) == (external_quality_factor is None): raise ValueError("Port requires exactly one of rate or external_quality_factor.") if external_quality_factor is not None and len(targets) != 1: raise ValueError("external_quality_factor is defined only for a single target device.") if operator is None and len(targets) != 1: raise ValueError("A multi-device Port requires an explicit coupling operator.") self._targets = targets self.rate = rate self.external_quality_factor = external_quality_factor self.operator = operator self.phase = phase self.label = label if label is not None else auto_label(self._type_prefix) @staticmethod def _validate_positive(name: str, value: Any) -> None: concrete = maybe_concrete_scalar(value) if concrete is not None and concrete <= 0: raise ValueError(f"{name} must be positive, got {value}") def __setattr__(self, name: str, value: Any) -> None: if name in ("rate", "external_quality_factor"): self._validate_positive(name, value) other_name = "external_quality_factor" if name == "rate" else "rate" if hasattr(self, other_name): other = getattr(self, other_name) if (value is None) == (other is None): raise ValueError("Port requires exactly one of rate or external_quality_factor.") super().__setattr__(name, value)
[docs] def resolve_targets(self, chip: "Chip") -> tuple[str, ...]: """Return target labels after checking that they belong to *chip*.""" labels = tuple(resolve_label(target) for target in self._targets) unknown = [label for label in labels if label not in chip.device_map] if unknown: raise ValueError(f"Port {self.label!r} targets unknown device(s) {unknown}.") if len(set(labels)) != len(labels): raise ValueError(f"Port {self.label!r} repeats a target device.") return labels
[docs] def rate_value(self, chip: "Chip") -> Any: """Return the external coupling rate in ``1/ns``.""" if self.rate is not None: return self.rate label = self.resolve_targets(chip)[0] target = chip[label] if not hasattr(target, "freq"): raise TypeError("external_quality_factor requires a target with a freq parameter.") return 2.0 * np.pi * target.freq / self.external_quality_factor
def _authored_operator(self, chip: "Chip") -> Any: labels = self.resolve_targets(chip) if self.operator is None: return chip[labels[0]].lowering_operator() if isinstance(self.operator, str): if len(labels) != 1: raise ValueError("A named Port operator requires exactly one target.") return chip[labels[0]].local_operator(self.operator) return self.operator def _collapse_channels_with_paths( self, chip: "Chip", ) -> tuple[tuple[CollapseChannel, tuple[str, ...]], ...]: labels = self.resolve_targets(chip) normalized = normalize_dissipation( (CollapseChannel(self._authored_operator(chip), self.rate_value(chip), "external_coupling"),), labels=labels, dims=tuple(chip[label].local_space().dimension for label in labels), owner=self, scope=f"port.{self.label}", ) rate_paths: tuple[str, ...] if self.rate is not None: rate_paths = (f"port.{self.label}.rate",) else: rate_paths = ( f"{labels[0]}.freq", f"port.{self.label}.external_quality_factor", ) return tuple((channel, tuple(dict.fromkeys((*paths, *rate_paths)))) for channel, paths in normalized)
[docs] def parameter_values(self) -> dict[str, Any]: """Return active sweepable port values.""" return { name: value for name in self._parameter_names if (value := getattr(self, name)) is not None }
[docs] def set_parameter_value(self, name: str, value: Any) -> None: """Set one port parameter on an isolated chip copy.""" if name not in self._parameter_names: raise KeyError(name) setattr(self, name, value)
[docs] def copy(self) -> "Port": """Return an independent port retaining label-based targets.""" operator = self.operator.copy() if isinstance(self.operator, np.ndarray) else self.operator return Port( tuple(resolve_label(target) for target in self._targets), rate=self.rate, external_quality_factor=self.external_quality_factor, operator=operator, phase=self.phase, label=self.label, )
[docs] def physics_notes(self) -> list[str]: """Return the input-output convention owned by this port.""" return [ "This accessible Markovian channel uses " "L = exp(i phase) sqrt(rate) A and b_out = b_in - L; " "the same L sets damping, coherent input coupling, and reported output." ]
[docs] def to_dict(self) -> dict[str, Any]: """Serialize port targets and scalar coupling data.""" operator: Any = self.operator if operator is not None and not isinstance(operator, str): if hasattr(operator, "matrix"): operator = operator.matrix() elif hasattr(operator, "to_jax"): operator = operator.to_jax() elif hasattr(operator, "full"): operator = operator.full() array = np.asarray(operator, dtype=complex) operator = { "kind": "dense", "real": array.real.tolist(), "imag": array.imag.tolist(), } elif isinstance(operator, str): operator = {"kind": "named", "name": operator} return { "targets": [resolve_label(target) for target in self._targets], "rate": self.rate, "external_quality_factor": self.external_quality_factor, "operator": operator, "phase": self.phase, "label": self.label, }
[docs] @classmethod def from_dict(cls, data: dict[str, Any]) -> "Port": """Reconstruct a port from label-based serialized data.""" targets = data["targets"] target: Any = targets[0] if len(targets) == 1 else targets operator = data.get("operator") if isinstance(operator, dict): if operator.get("kind") == "named": operator = operator["name"] elif operator.get("kind") == "dense": operator = np.asarray(operator["real"]) + 1j * np.asarray(operator["imag"]) else: raise TypeError(f"Unknown serialized Port operator kind: {operator.get('kind')!r}") return cls( target, rate=data.get("rate"), external_quality_factor=data.get("external_quality_factor"), operator=operator, phase=data.get("phase", 0.0), label=data.get("label"), )