Source code for quchip.engine.conditional_response

"""Weak-probe commutator sources for a frozen dressed reference."""
from __future__ import annotations

from typing import Any

from quchip.engine.ir import CoherenceResponseProblem, LinearResponseProblem
from quchip.engine.linear_response import (
    _UnsupportedLinearModel, _build_vacuum_response_problem, _sector_response_model,
)
from quchip.utils.constants import TWO_PI
from quchip.utils.jax_utils import contains_tracer


[docs] def build_conditional_response( chip: Any, frequencies: Any, labels: tuple[str, ...], reference: tuple[int, ...], ) -> tuple[LinearResponseProblem | CoherenceResponseProblem, dict[str, Any]]: """Build the closed adjacent-sector response, with the reference frozen only in its source.""" try: model = _sector_response_model(chip, labels) except _UnsupportedLinearModel as error: raise ValueError( "Conditional VNA requires a static excitation-conserving model, lowering ports, " "lowering or conserving loss channels, vacuum inputs and no downstream output network." ) from error sectors, xp = model.sectors, chip.backend.array_module if any(level >= dim for level, dim in zip(reference, sectors.dims, strict=True)): raise ValueError("Conditional VNA reference exceeds a device's resolved level count.") total = sum(reference) metadata: dict[str, Any] = { "reference": tuple(zip((d.label for d in chip.devices), reference, strict=True)), "approximation": "none" if total == 0 else "frozen_reference", } if total == 0: problem = _build_vacuum_response_problem(chip, frequencies, plane_labels=labels) metadata.update(solver="vacuum_response", reference_overlap=1., reference_escape_rate=0., reference_stationarity_residual=0., mode_count=len(problem.mode_labels)) return problem, metadata if chip.port_network._mode_responses: raise NotImplementedError( "Retained multiport mode responses support the vacuum reference only. " "Keep the boundary mode for an excited-reference VNA sweep." ) system = sectors.eigensystem(total) row = system.space.row(reference) index = system.labeling.indices[row] overlap = xp.asarray(system.labeling.overlaps[row]) vector = xp.asarray(system.eigenvectors)[:, index] invalid = (overlap <= 0.5 + 32 * xp.finfo(overlap.dtype).eps) | xp.any(system.labeling.duplicates) message = "Conditional VNA requires an unambiguous dressed reference with bare-state overlap greater than one half." if contains_tracer(invalid): import equinox as eqx vector = eqx.error_if(vector, invalid, message) elif bool(invalid): raise ValueError(message) rho = xp.outer(vector, vector.conj()) energy = system.eigenvalues[index] sizes = tuple(sectors.space(n).size for n in range(total + 2)) from quchip.utils.memory import dense_bytes, require_memory channel_count = len(model.channels) block_entries = sum(size**2 for size in sizes) * (len(model.conserving_indices) + 4) block_entries += sum(sizes[n] * sizes[n - 1] for n in range(1, len(sizes))) * (channel_count + len(labels)) require_memory(dense_bytes(block_entries, copies=2), task="Conditional VNA sector operators", remedy="Reduce the model or its local level counts.") hamiltonians = tuple(TWO_PI * (xp.asarray(sectors.hamiltonian(n)) - energy * xp.eye(size)) for n, size in enumerate(sizes)) count = len(model.channels) lowering = (xp.zeros((count, 0, sizes[0]), dtype=complex),) + tuple( xp.stack([model.channel_block(k, n - 1, n) for k in range(count)]) for n in range(1, total + 2)) conserving = tuple(xp.stack([model.channel_block(k, n, n) for k in model.conserving_indices]) if model.conserving_indices else xp.zeros((0, size, size), dtype=complex) for n, size in enumerate(sizes)) indices = list(model.plane_indices) mixing = model.scattering[:, indices] sources = [xp.zeros((sizes[0], 0, len(indices)), dtype=complex)] for n in range(1, total + 2): # K_i^dagger = sum_k S_ki L_k^dagger. The source is -[K_i^dagger, rho]. dagger = xp.einsum("kab,ki->bai", lowering[n].conj(), mixing) sources.append(-xp.einsum("abi,bc->aci", dagger, rho) if n == total + 1 else xp.einsum("ab,bci->aci", rho, dagger)) lower = lowering[total] kept = conserving[total] lower_state, kept_state = lower @ vector, kept @ vector means = xp.einsum("a,ka->k", vector.conj(), kept_state) escape = xp.real(xp.sum(xp.abs(lower_state)**2) + xp.sum(xp.abs(kept_state)**2) - xp.sum(xp.abs(means)**2)) damping = xp.einsum("kab,kac->bc", lower.conj(), lower) + xp.einsum("kab,kac->bc", kept.conj(), kept) retained_derivative = xp.einsum("kab,bc,kdc->ad", kept, rho, kept.conj()) - (damping @ rho + rho @ damping) / 2 escaped_derivative = xp.einsum("kab,bc,kdc->ad", lower, rho, lower.conj()) metadata.update( solver="conditional_response", reference_overlap=overlap, reference_escape_rate=xp.maximum(escape, 0.), reference_stationarity_residual=xp.sqrt(xp.sum(xp.abs(retained_derivative)**2) + xp.sum(xp.abs(escaped_derivative)**2)), sector_dimensions=sizes, coherence_count=sum(sizes[n] * sizes[n - 1] for n in range(1, len(sizes))), ) inbound, outbound = model.transfers(frequencies) return CoherenceResponseProblem( frequencies=frequencies, hamiltonians=hamiltonians, lowering=lowering, conserving=conserving, sources=tuple(sources), scattering=model.scattering, plane_indices=model.plane_indices, inbound_transfer=inbound, outbound_transfer=outbound, ), metadata