Use minisim in your test suite#
If you maintain a calcium-imaging analysis pipeline (minian, CaImAn, suite2p,
…), minisim can supply small, reproducible recordings whose answer you already
know, so a test can assert that your pipeline recovers them. The
minisim.testing module gives you a one-call fixture and a one-call
scorecard built for exactly this.
A recording in one call#
make_recording() returns a small, fast, deterministic
Recording: the same arguments (and seed) always produce
the same movie and the same ground truth.
from minisim.testing import make_recording
rec = make_recording(n_cells=6, n_px=128, duration_s=2.0, seed=0)
movie = rec.observed # (frame, height, width) sensor counts
truth = rec.ground_truth # exact cells, traces, spikes
The defaults are tuned for CI (a 128 px field at 1 µm/px, six cells at 50 µm
depth, two seconds at 20 fps). Shrink n_px / duration_s for an even faster
fixture, raise n_cells for a denser one, pass motion=True to exercise motion
correction, or hand in your own activity= / sensor= / extra_steps=.
Scoring your pipeline in one call#
Wrap your pipeline’s output in an Estimate and pass
it to score(). It applies the conventions the
benchmarking guide spells out (match against A_observed,
score recall over the detectable cells, nan-safe trace median, treat the motion
estimate as a correction) and returns a Report.
from minisim.testing import Estimate, score
A_est, C_est, S_est = run_my_pipeline(rec.observed)
report = score(Estimate(A=A_est, C=C_est, S=S_est), rec.ground_truth)
assert report.recall > 0.8, report.summary()
A is the only required field of Estimate; leave C / S / shifts out and
the matching scores come back as nan / None. The Report carries recall,
precision, mean_iou, trace_corr (median Pearson r), spike_precision,
spike_recall, and shift_rmse. Arrays may be numpy or xarray (minian’s CNMF
returns xr.DataArray); both are accepted.
When you need more than the common case, the underlying primitives
(hungarian_match(), trace_pearson(),
spike_precision_recall(), shift_rmse()) stay
fully available; score is just the 90%-path on top of them.
A pytest fixture#
Drop a fixture in your conftest.py. Build the recording once per test (or per
session, since a Recording is immutable and safe to share):
# conftest.py
import pytest
from minisim.testing import make_recording
@pytest.fixture(scope="session")
def sim_recording():
return make_recording(n_cells=8, duration_s=3.0, seed=0)
# test_recovery.py
from minisim.testing import Estimate, score
def test_pipeline_recovers_cells(sim_recording):
A, C, S = run_my_pipeline(sim_recording.observed)
report = score(Estimate(A=A, C=C, S=S), sim_recording.ground_truth)
assert report.recall > 0.8, report.summary()
Use plain make_recording / simulate() in tests. The on-disk
simulate_cached() is meant for parameter sweeps, not CI: in a
fresh CI environment its cache is cold (no speedup, just disk writes).
Add minisim as a test-only dependency#
minisim never imports an analysis pipeline, so the dependency is strictly
one-directional and adding minisim to your test extra cannot create an import
cycle. In your pyproject.toml:
[project.optional-dependencies]
test = ["minisim", "pytest"]
minisim is then present when your tests run, but is not a runtime dependency of your package.