Source code for quchip.engine.slh

"""Gough-James algebra on resolved SLH triples: series product, concatenation, feedback.

Every function returns a new :class:`~quchip.engine.ir.ResolvedSLH` on the same
Hilbert space as its operands and does not change them. Hamiltonian corrections
that a composition generates are appended as network-origin static terms.
Scattering stays scalar. Traced values pass through the array module that they
arrive in.
"""

from __future__ import annotations

from collections.abc import Mapping
from dataclasses import dataclass, replace
from typing import TYPE_CHECKING, Any, Sequence

import numpy as np
from scipy.linalg import block_diag

from quchip.engine.ir import CanonicalOperator, CollapseTerm, HamiltonianProgram, ResolvedSLH, SLHChannel, StaticTerm
from quchip.engine.reference import ReferencePlane
from quchip.utils.jax_utils import contains_tracer, is_jax_array, maybe_concrete_scalar, select_array_module

if TYPE_CHECKING:
    from quchip.chip.port_network import NetworkPort

__all__ = ["series_product", "concatenate", "feedback_reduce"]

_SINGULAR_TOLERANCE = 1e-12


@dataclass(frozen=True)
class ModeResponse:
    """A removed harmonic pole attached to its original quantum-port locations."""

    label: str
    ports: tuple[str, ...]
    freq: Any
    weight: Any
    internal_rate: Any


@dataclass(frozen=True)
class CompiledChannel:
    """One exposure of a compiled network with its couplings by port and its reference plane."""

    exposure: NetworkPort
    coupling: Mapping[str, Any]
    reference: ReferencePlane


@dataclass(frozen=True)
class CompiledNetwork:
    """The Markovian core of a port network, compiled for one chip."""

    channels: tuple[CompiledChannel, ...]
    scattering: Any
    generated_pairs: tuple[tuple[str, str, Any], ...]
    support: np.ndarray
    output_network: Any = None


def cascade_hamiltonian(products: Sequence[tuple[Any, Any, Any]]) -> Any | None:
    """Return ``sum (L_d^dagger c L_u - h.c.) / 2i`` over ``(L_d, L_u, c)`` matrices on one space.

    A concrete result is ``None`` when each element is below ``64 n ε`` times
    the magnitude of the products that form it. This conservative threshold
    removes cancellation residue, e.g. the Lamb shift ``γ sin(π)`` of a giant
    atom, and also a genuine term that small. Each element has its own
    threshold, so the cancellation of one pair does not hide the small rate of
    another pair. A traced result keeps every term.
    """
    traced = contains_tracer(products)
    xp = select_array_module(traced)
    values: Any = 0.0
    scale: Any = 0.0
    for left, right, coefficient in products:
        left, right = xp.asarray(left), xp.asarray(right)
        product = xp.conj(xp.swapaxes(left, -1, -2)) @ (xp.asarray(coefficient) * right)
        values = values + (product - xp.conj(xp.swapaxes(product, -1, -2))) / (2j)
        if not traced:
            size = np.abs(coefficient) * (np.swapaxes(np.abs(left), -1, -2) @ np.abs(right))
            scale = scale + (size + np.swapaxes(size, -1, -2)) / 2
    if not traced and np.all(np.abs(values) <= 64 * np.finfo(float).eps * values.shape[-1] * scale):
        return None
    return values


[docs] def series_product(first: ResolvedSLH, second: ResolvedSLH) -> ResolvedSLH: """Feed every output of ``first`` into the matching input of ``second``. Implements ``G1 ▷ G2 = (S2 S1, L2 + S2 L1, H1 + H2 + (L2† S2 L1 − h.c.) / 2i)``. Paired channels must agree in accessibility and carrier. Reference runs are joined leg by leg, so the outbound run of ``first`` and the inbound run of ``second`` must be empty. Parameters ---------- first, second : ResolvedSLH Systems with equal channel counts on one Hilbert space. Returns ------- ResolvedSLH The cascaded system. Raises ------ ValueError Channel counts differ, Hilbert spaces differ, or a joined leg carries a reference run. """ _require_common_space(first, second) if len(first.channels) != len(second.channels): raise ValueError( "series_product needs equal channel counts; " f"got {len(first.channels)} outputs feeding {len(second.channels)} inputs." ) for upstream, downstream in zip(first.channels, second.channels, strict=True): _require_joinable("series_product", upstream, downstream) template = _template(first) xp, ((s1, l1), (s2, l2)) = _arrays(first, second) routed = xp.tensordot(s2, l1, axes=1) couplings = l2 + routed cross = xp.sum(_dag(l2, xp) @ routed, axis=0) correction = _correction_term(cross, template, xp, tag="slh-series") support = (np.asarray(second.support, dtype=int) @ np.asarray(first.support, dtype=int)) > 0 channels = tuple( _channel( replace(downstream, input_occupation=upstream.input_occupation), couplings[index], template, reference=ReferencePlane(inbound=upstream.reference.inbound, outbound=downstream.reference.outbound), ) for index, (upstream, downstream) in enumerate(zip(first.channels, second.channels, strict=True)) ) return ResolvedSLH( scattering=s2 @ s1, hamiltonian=_hamiltonian((first, second), correction), channels=channels, support=support, )
[docs] def concatenate(*systems: ResolvedSLH, prefixes: Sequence[str] | None = None) -> ResolvedSLH: """Place systems side by side with block-diagonal scattering. Implements ``G1 ⊞ G2 = (diag(S1, S2), (L1, L2), H1 + H2)``. Channel keys must stay unique. To rename each system's keys as ``prefix/key``, pass ``prefixes``. Exposed channels are ordered before hidden channels. Parameters ---------- *systems : ResolvedSLH One or more systems on one Hilbert space. prefixes : sequence of str, optional One prefix per system. Returns ------- ResolvedSLH The concatenated system. Raises ------ ValueError No systems, mismatched prefixes, duplicate keys, or differing Hilbert spaces. """ if not systems: raise ValueError("concatenate needs at least one system.") _require_common_space(*systems) if prefixes is not None and len(prefixes) != len(systems): raise ValueError(f"concatenate got {len(prefixes)} prefixes for {len(systems)} systems.") entries: list[SLHChannel] = [] for system_index, system in enumerate(systems): for channel in system.channels: key = channel.key if prefixes is None else f"{prefixes[system_index]}/{channel.key}" entries.append(replace(channel, key=key)) keys = [channel.key for channel in entries] duplicates = sorted({key for key in keys if keys.count(key) > 1}) if duplicates: raise ValueError(f"concatenate needs prefixes= to disambiguate duplicate channel keys {duplicates}.") order = np.asarray( [index for index, channel in enumerate(entries) if channel.accessibility == "exposed"] + [index for index, channel in enumerate(entries) if channel.accessibility == "hidden"], dtype=int, ) if any(system.output_network is not None for system in systems): raise ValueError("Compose the physical PortNetwork before resolving branched output reference sections.") xp = _array_module(systems) assemble_blocks = block_diag if xp is not np: from jax.scipy.linalg import block_diag as assemble_blocks grid = np.ix_(order, order) scattering = assemble_blocks(*(xp.asarray(system.scattering, dtype=complex) for system in systems))[grid] support = block_diag(*(np.asarray(system.support, dtype=bool) for system in systems))[grid] channels = tuple(replace(entries[index], coupling_operator=entries[index].coupling) for index in order) return ResolvedSLH( scattering=scattering, hamiltonian=_hamiltonian(systems, None), channels=channels, support=support, )
[docs] def feedback_reduce(slh: ResolvedSLH, *, output: str | SLHChannel, input: str | SLHChannel) -> ResolvedSLH: """Connect one output channel back into one input channel. For output ``x`` and input ``y`` with ``g = (1 − S_xy)⁻¹``, this implements ``S̃ = S_x̄ȳ + S_x̄y g S_xȳ``, ``L̃ = L_x̄ + S_x̄y g L_x`` and ``H̃ = H + ((Σ_j L_j† S_jy) g L_x − h.c.) / 2i``. When ``x`` and ``y`` differ, the remaining input of ``x`` and output of ``y`` form one channel, keyed ``"<x>-><y>"`` and is placed at the earlier of the two positions. Parameters ---------- slh : ResolvedSLH The system to close. output, input : str or SLHChannel The output channel that is fed back and the input channel that receives it. Returns ------- ResolvedSLH The reduced system with one channel fewer. Raises ------ ValueError The loop is concretely singular, the two channels differ in accessibility, or a closed leg carries a reference run. KeyError An unknown channel key. """ x = _index(slh, output) y = _index(slh, input) closed_output, closed_input = slh.channels[x], slh.channels[y] _require_joinable("feedback_reduce", closed_output, closed_input) template = _template(slh) xp, ((scattering, couplings),) = _arrays(slh) loop = scattering[x, y] if not contains_tracer(loop) and abs(1.0 - complex(np.asarray(loop))) < _SINGULAR_TOLERANCE: raise ValueError( f"Feedback from {closed_output.key!r} into {closed_input.key!r} is singular: 1 - S[x, y] vanishes." ) gain = 1.0 / (1.0 - loop) merged = min(x, y) if x != y else None positions = [index for index in range(len(slh.channels)) if index not in (x, y) or index == merged] row_index = np.asarray([y if index == merged else index for index in positions], dtype=int) column_index = np.asarray([x if index == merged else index for index in positions], dtype=int) channels = [ replace( closed_input, key=f"{closed_output.key}->{closed_input.key}", input_occupation=closed_output.input_occupation, reference=ReferencePlane(inbound=closed_output.reference.inbound, outbound=closed_input.reference.outbound), ) if index == merged else slh.channels[index] for index in positions ] reduced_scattering = ( scattering[np.ix_(row_index, column_index)] + xp.outer(scattering[row_index, y], scattering[x, column_index]) * gain ) reduced_couplings = couplings[row_index] + (scattering[row_index, y] * gain)[:, None, None] * couplings[x] inbound = xp.sum(_dag(couplings, xp) * scattering[:, y][:, None, None], axis=0) correction = _correction_term(inbound @ (gain * couplings[x]), template, xp, tag="slh-feedback") support = np.asarray(slh.support, dtype=bool) reduced_support = support[np.ix_(row_index, column_index)] | np.outer( support[row_index, y], support[x, column_index] ) return ResolvedSLH( scattering=reduced_scattering, hamiltonian=_hamiltonian((slh,), correction), channels=tuple( _channel(channel, reduced_couplings[index], template, reference=channel.reference) for index, channel in enumerate(channels) ), support=reduced_support, )
def _index(slh: ResolvedSLH, channel: str | SLHChannel) -> int: key = channel.key if isinstance(channel, SLHChannel) else channel for index, candidate in enumerate(slh.channels): if candidate.key == key: return index raise KeyError(f"No channel {key!r}; available: {[item.key for item in slh.channels]}") def _template(slh: ResolvedSLH) -> CanonicalOperator: if not slh.channels: raise ValueError("SLH algebra needs at least one channel to define the Hilbert space.") return slh.channels[0].coupling def _require_common_space(*systems: ResolvedSLH) -> None: layouts = { (template.shape, template.dims, template.basis, template.subsystem_labels) for template in (_template(system) for system in systems) } if len(layouts) != 1: raise ValueError(f"SLH algebra needs operands on one Hilbert space; got layouts {sorted(map(str, layouts))}.") def _arrays(*systems: ResolvedSLH) -> tuple[Any, list[tuple[Any, Any]]]: """Return the shared array module and each system's dense ``(S, stacked L)``.""" if any(system.output_network is not None for system in systems): raise ValueError("Compose the physical PortNetwork before resolving branched output reference sections.") xp = _array_module(systems) return xp, [ ( xp.asarray(system.scattering, dtype=complex), xp.stack([xp.asarray(operator.to_dense(), dtype=complex) for operator in system.L]), ) for system in systems ] def _array_module(systems: Sequence[ResolvedSLH]) -> Any: """Return JAX when any operand payload is traced or already a JAX array, else NumPy.""" leaves = [leaf for system in systems for leaf in (system.scattering, *(operator.values for operator in system.L))] return select_array_module(contains_tracer(leaves) or any(is_jax_array(leaf) for leaf in leaves)) def _require_joinable(operation: str, upstream: SLHChannel, downstream: SLHChannel) -> None: """Reject joining an output leg to an input leg whose metadata cannot be merged.""" prefix = f"{operation} cannot join {upstream.key!r} to {downstream.key!r}: " if upstream.accessibility != downstream.accessibility: raise ValueError(prefix + "both channels must share accessibility.") if not _same_carrier(upstream.carrier, downstream.carrier): raise ValueError(prefix + "carriers are not statically known to be equal.") if downstream.input_occupation is not None: raise ValueError(prefix + "the connected input cannot retain an independent thermal bath declaration.") if upstream.reference.outbound or downstream.reference.inbound: raise ValueError(prefix + "reference runs must be empty on the joined legs.") def _same_carrier(first: Any, second: Any) -> bool: """Allow concrete roundoff in static SLH composition; traced carriers require identity.""" first_concrete = maybe_concrete_scalar(first) second_concrete = maybe_concrete_scalar(second) if first_concrete is not None and second_concrete is not None: return bool(np.isclose(first_concrete, second_concrete, rtol=1e-12, atol=1e-12)) return first is second def _dag(matrices: Any, xp: Any) -> Any: return xp.conj(xp.swapaxes(matrices, -1, -2)) def _correction_term(cross: Any, template: CanonicalOperator, xp: Any, *, tag: str) -> StaticTerm | None: values = (cross - _dag(cross, xp)) / 2j if not contains_tracer(values) and np.allclose(np.asarray(values), 0.0): return None operator = CanonicalOperator.from_dense( values, dims=template.dims, basis=template.basis, subsystem_labels=template.subsystem_labels, tag=tag ) return StaticTerm(operator=operator, origin="network", metadata={"operation": tag}) def _hamiltonian(systems: Sequence[ResolvedSLH], correction: StaticTerm | None) -> HamiltonianProgram: static = tuple(term for system in systems for term in system.H.static_terms) dynamic = tuple(term for system in systems for term in system.H.dynamic_terms) if correction is not None: static = (*static, correction) return HamiltonianProgram(static_terms=static, dynamic_terms=dynamic) def _channel(source: SLHChannel, values: Any, template: CanonicalOperator, *, reference: ReferencePlane) -> SLHChannel: coupling = CanonicalOperator.from_dense( values, dims=template.dims, basis=template.basis, subsystem_labels=template.subsystem_labels, tag=f"slh:{source.key}", ) collapse = CollapseTerm( operator=coupling, rate=1.0, source=source.key, channel="resolved_slh", frame_frequency=source.collapse.frame_frequency, ) return SLHChannel( key=source.key, accessibility=source.accessibility, collapse=collapse, coupling_operator=coupling, reference=reference, input_occupation=source.input_occupation, )