Source code for pdft.circuit.cache
"""In-memory cache for jit-compiled jnp.einsum closures.
Mirror of upstream src/einsum_cache.jl. Upstream persists TreeSA-optimized
paths to disk; XLA path optimization is cheap enough that an in-process
dict is sufficient for Phase 1.
"""
from __future__ import annotations
from collections.abc import Callable
import jax
import jax.numpy as jnp
_CACHE: dict[tuple, Callable] = {}
[docs]
def optimize_code_cached(subscripts: str, *shapes: tuple[int, ...]) -> Callable[..., jax.Array]:
"""Return a jit-compiled einsum closure bound to fixed subscripts and shapes.
On first call for a given (subscripts, shapes) key, computes an optimal
contraction path via jnp.einsum_path on dummy arrays of the supplied
shapes, then returns a jit-compiled closure. Subsequent calls return the
identical cached closure.
Parameters
----------
subscripts : str
einsum subscripts string, e.g., "ijk,jlk->ilk".
*shapes : tuple[int, ...]
Shape of each operand, in order matching the subscripts.
Returns
-------
Callable accepting the operands and returning the contraction result.
"""
key = (subscripts, tuple(tuple(s) for s in shapes))
if key in _CACHE:
return _CACHE[key]
dummies = [jnp.zeros(shape, dtype=jnp.complex128) for shape in shapes]
# "greedy" scales polynomially in tensor count; "optimal" is exponential
# and becomes impractical beyond ~10 tensors (a 3x3 QFT has 12).
# For circuits of this size "greedy" matches "optimal" in practice.
path, _info = jnp.einsum_path(subscripts, *dummies, optimize="greedy")
@jax.jit
def _contract(*operands):
return jnp.einsum(subscripts, *operands, optimize=path)
_CACHE[key] = _contract
return _contract