Source code for quchip.declarative.dissipation

"""Authored Lindblad dissipation values."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from quchip.declarative.expr import as_operator_expr, as_scalar_expr
from quchip.utils.jax_utils import maybe_concrete_scalar


[docs] def collapse_parameter_paths(operator: Any, rate: Any) -> tuple[str, ...]: """Return expression parameter paths in authored operator-then-rate order.""" paths: list[str] = [] for value in (operator, rate): parameter_paths = getattr(value, "parameter_paths", None) if parameter_paths is not None: paths.extend(parameter_paths()) return tuple(dict.fromkeys(paths))
[docs] @dataclass(frozen=True) class CollapseChannel: """One unscaled Lindblad operator and its rate in inverse nanoseconds.""" operator: Any rate: Any name: str def __post_init__(self) -> None: concrete_rate = maybe_concrete_scalar(self.rate) if concrete_rate is not None and concrete_rate < 0: raise ValueError("CollapseChannel.rate must be non-negative.") if not isinstance(self.name, str) or not self.name: raise ValueError("CollapseChannel.name must be a non-empty string.")
[docs] def normalize_dissipation( authored: Any, *, labels: tuple[str, ...], dims: tuple[int, ...], owner: Any, scope: str, allowed: dict[str, Any] | None = None, bindings: dict[str, Any] | None = None, ) -> tuple[tuple[CollapseChannel, tuple[str, ...]], ...]: """Normalize one owner's authored local dissipation and infer dependencies.""" if not isinstance(authored, tuple): raise TypeError( f"{type(owner).__name__}.dissipation() must return a tuple of " f"CollapseChannel values; got {type(authored).__name__}." ) normalized: list[tuple[CollapseChannel, tuple[str, ...]]] = [] for channel in authored: if not isinstance(channel, CollapseChannel): raise TypeError( f"{type(owner).__name__}.dissipation() must return " f"CollapseChannel values; got {type(channel).__name__}." ) operator = as_operator_expr( channel.operator, labels=labels, dims=dims, name=rf"\hat L_{{{scope},{channel.name}}}", owner=owner, scope=scope, allowed=allowed, ) rate = as_scalar_expr( channel.rate, name=rf"\gamma_{{{scope},{channel.name}}}", owner=owner, scope=scope, allowed=allowed, ) if bindings: operator = operator.with_bindings(bindings) rate = rate.with_bindings(bindings) normalized.append( ( CollapseChannel(operator, rate, channel.name), collapse_parameter_paths(operator, rate), ) ) return tuple(normalized)