Source code for minisim.cache

"""Spec-hash → zarr caching, kept as a thin library concern over :func:`simulate`.

A realistic spec takes tens of seconds to simulate, and parameter
sweeps re-request the same recordings many times. :func:`simulate_cached` keys a
recording by its :attr:`~minisim.spec.Spec.cache_key` (a hash of the
canonical spec JSON): a cache hit is loaded from disk, a miss simulates once and
saves the result. ``simulate()`` itself stays pure - it has no I/O and no disk
side effects - so caching composes on top rather than being baked in.

The cache directory defaults to ``~/.cache/minisim`` and is overridable with
the ``MINISIM_CACHE`` environment variable. Because ``output.save_intermediates``
(and every other knob) is part of the spec, it folds into the cache key - a
recording cached without snapshots can never falsely satisfy a request that wants
them; the keys simply differ.
"""

from __future__ import annotations

import os
from pathlib import Path

from minisim.recording import Recording
from minisim.simulate import simulate
from minisim.spec import Spec

#: Default cache root, used when ``$MINISIM_CACHE`` is unset.
DEFAULT_CACHE_DIR = "~/.cache/minisim"


[docs] def cache_dir() -> Path: """The resolved cache root: ``$MINISIM_CACHE`` if set, else :data:`DEFAULT_CACHE_DIR`. The returned path is user-expanded (``~``) but not created - :func:`simulate_cached` makes it on first write. """ return Path(os.environ.get("MINISIM_CACHE", DEFAULT_CACHE_DIR)).expanduser()
[docs] def cache_path(spec: Spec, root: str | Path | None = None) -> Path: """The on-disk path a recording for ``spec`` is cached at: ``{root}/{cache_key}.zarr``.""" base = Path(root).expanduser() if root is not None else cache_dir() return base / f"{spec.cache_key()}.zarr"
[docs] def simulate_cached(spec: Spec, *, root: str | Path | None = None) -> Recording: """Return the recording for ``spec``, loading from cache or simulating on a miss. Parameters ---------- spec The recording spec; its :attr:`~minisim.spec.Spec.cache_key` is the cache key. root Cache directory. Defaults to :func:`cache_dir` (``$MINISIM_CACHE`` or ``~/.cache/minisim``). Notes ----- A hit is served by :meth:`Recording.load`, which re-verifies the stored spec hash; a miss runs :func:`simulate` and persists the result with :meth:`Recording.save` before returning it. """ path = cache_path(spec, root) if path.exists(): return Recording.load(path) rec = simulate(spec) path.parent.mkdir(parents=True, exist_ok=True) rec.save(path) return rec