Simulation#

simulate#

minisim.simulate(spec, *, until=None, perf=None)[source]#

Run a full recording specification and return the typed Recording.

Seeds the RNG from spec.seed (so a spec + seed fully determines the output), sizes the motion margin, runs the steps in spec.steps order - already the canonical pipeline order, since Spec sorts the list on construction, so the order a caller listed them in is irrelevant - optionally snapshots each movie stage, and finalizes. until stops after the named stage: it matches either a step.name (e.g. "vignette") or the step’s spec kind (e.g. "composite", whose stage name is the less obvious "cells_only"), so both spellings work. An until that matches no step raises rather than silently running the whole pipeline.

Pass a PerfTracker as perf to record per-step (and finalize) wall time; it is a no-op when None (the default), so an un-profiled run pays nothing for the instrumentation.

Parameters:
  • spec (Spec)

  • until (str | None)

  • perf (PerfTracker | None)

Return type:

Recording

simulate_cached#

minisim.simulate_cached(spec, *, root=None)[source]

Return the recording for spec, loading from cache or simulating on a miss.

Parameters:
  • spec (Spec) – The recording spec; its cache_key is the cache key.

  • root (str | Path | None) – Cache directory. Defaults to cache_dir() ($MINISIM_CACHE or ~/.cache/minisim).

Return type:

Recording

Notes

The cache is keyed by filename ({cache_key}.zarr): a changed spec serializes to a new key, hence a new filename and a clean miss, so the cache never serves a recording for the wrong spec. A hit is served by Recording.load(); a miss runs simulate() and persists the result with Recording.save() before returning it.

simulate_video#

minisim.simulate_video(spec, path, *, chunk_frames=None, fps=None, vmin=0.0, vmax=None, codec='Y800', progress=True, perf=None)[source]#

Simulate spec straight to a grayscale video at path, streaming to disk.

Renders and digitizes the recording in frame chunks and writes each to an incrementally-opened cv2.VideoWriter, so the whole movie is never held in memory (unlike simulate(spec).observed, which is). The produced counts match simulate(spec).observed exactly.

vmax sets the count mapped to white (vmin → black); it defaults to the sensor’s full ADC range (2**bit_depth - 1) when the spec has a sensor step, so the file faithfully shows the true ADC utilization (a dim, honest frame). For a sensorless (continuous-intensity) spec there is no natural scale, so vmax must be given. codec is a 4-character opencv fourcc; it defaults to "Y800": uncompressed 8-bit grayscale, so the file carries the exact counts with no compression artifacts (large: ~``n_frames * H * W`` bytes). For a small lossy file pass "MJPG". Writing uses opencv’s bundled ffmpeg, so no system ffmpeg or mediapy extra is needed. Returns path.

Pass a PerfTracker as perf to record where the streamed write spends its time: the one-off setup (cell steps + footprint build), each per-chunk render sub-phase (composite, neuropil, motion_crop, photon_field, leakage, digitize), and the encode+write tail. It is a no-op when None (the default).

Parameters:
Return type:

Path

sweep#

minisim.sweep(base, axes)[source]#

Yield one validated SweptSpec per point in the Cartesian product of axes.

Parameters:
  • base (Spec) – The spec every combination starts from; never mutated.

  • axes (dict[str, list]) –

    Maps a dotted override path to the list of values to sweep it over. Path forms:

    • "acquisition.optics.na" - walk nested models;

    • "steps.<kind>.<field>" - address the step with that (unique) kind, e.g. "steps.place_neurons.density_per_mm3";

    • "seed" - a top-level field.

Yields:

SweptSpec – The base with this combination of overrides applied and all cross-field validators re-run - an axis value that yields an invalid combination (na=-1, a soma larger than the FOV, …) raises here. An empty axes yields the base once with axes={}.

Raises:

ValueError – For a path naming an unknown field, an unknown step kind, or one that descends into a non-model (scalar) field.

Return type:

Iterator[SweptSpec]

pydantic model minisim.SweptSpec[source]#

Bases: Spec

A Spec tagged with the sweep-axis values that produced it.

A genuine Spec subclass, so it drops into simulate() and Recording unchanged. axes is excluded from serialization, so it never reaches model_dump_json and therefore leaves Spec.cache_key() identical to the equivalent plain spec - sweeping does not perturb cache dedup, and the tag simply vanishes when a recording is persisted.

Fields:
  • axes (dict)

field axes: dict [Optional]#

Chosen value per swept dotted-path, for tidy benchmark rows.