Testing helpers#
Convenience helpers for using minisim as test fixtures for an analysis pipeline.
Unlike the rest of the reference, these live in the minisim.testing submodule
(from minisim.testing import make_recording, score). See the how-to guide
Use minisim in your test suite for the recommended pytest wiring.
make_recording#
- minisim.testing.make_recording(*, n_cells=6, n_px=128, pixel_size_um=1.0, duration_s=2.0, fps=20.0, seed=0, depth_um=50.0, focal_depth_um='auto', soma_radius_um=5.0, morphology='soma', population=None, motion=False, activity=None, sensor=None, extra_steps=(), save_intermediates=False)[source]#
A small, fast, deterministic recording with ground truth, for CI tests.
Places
n_cellswell-separated somata on a jittered grid at a singledepth_umand runs the minimal forward chain (place_neurons → cell_activity → optics → composite → sensor) on a squaren_px × n_pxsensor atpixel_size_umper pixel. Cell count, positions, and every pixel are fully determined by the arguments, so the sameseed(and the same arguments) always yields the same recording - the property a fixture needs.Defaults are tuned for CI: a 128 px FOV at 1 µm/px (128 µm), six cells at 50 µm depth, two seconds at 20 fps, with a lively default activity and a fixed, well-chosen exposure (the focal plane is
"auto"), so every placed cell reliably fires, is in focus, and is brightly but non-saturatingly exposed - hence detectable - with no manual tuning. Shrinkn_px/duration_sfor an even faster fixture, or raisen_cellsfor a denser one.- Parameters:
n_cells (int) – Number of somata to place (exactly this many; grid-arranged and separated).
n_px (int) – Square sensor side, pixels (both height and width).
pixel_size_um (float) – Object-space size of one pixel, µm. Realized via a sensor pitch of
8.0µm and a matching magnification, so the FOV isn_px · pixel_size_um.duration_s (float) – Sampling and the master RNG seed.
fps (float) – Sampling and the master RNG seed.
seed (int) – Sampling and the master RNG seed.
depth_um (float) – Common depth of every soma below the tissue surface, µm. With the default
focal_depth_um="auto"the focal plane resolves here and the cells are in focus. Ignored whenpopulationis given (the population carries its own depth distribution).focal_depth_um (float | Literal['auto']) – Focal plane depth in tissue, µm, or
"auto"(the default) to let the simulator pick the most-recoverable plane. Pin it to a fixed depth (e.g.0.0, the surface) to hold the plane still while sweeping cell depth - the setup a recall-vs-depth study needs, since auto-focus refocuses on each band and would otherwise keep every cell in focus and detectable.soma_radius_um (float) – Cell shape:
"soma"(body only, fast) or"cytosolic"(soma plus proximal dendrites). Ignored whenpopulationis given.morphology (Literal['soma', 'cytosolic']) – Cell shape:
"soma"(body only, fast) or"cytosolic"(soma plus proximal dendrites). Ignored whenpopulationis given.population (NeuronPopulation | None) – A
NeuronPopulationto place verbatim, bypassing the well-separated grid. Use minisim’s native volumetric placement - adensity_per_mm3over adepth_range_um, optionally Poisson-disk-spaced bymin_distance_um- to get a realistic, variable layout where footprints overlap and a depth spread leaves some cells off the focal plane (so not every planted cell is detectable).None(the default) keeps the exact-count grid; when given,n_cells,depth_um,soma_radius_um, andmorphologyare ignored (the population sets them).motion (bool) – When
True, append a defaultBrainMotionstep, soground_truth.shiftsis populated (lets you exercise motion correction).activity (CellActivity | None) – Override the
CellActivitymodel or theSensorexposure step;Noneuses defaults (the default sensor uses a fixed, well-exposedphotons_per_unit). Pass an explicitSensor(photons_per_unit=...)for a dimmer or brighter recording.sensor (Sensor | None) – Override the
CellActivitymodel or theSensorexposure step;Noneuses defaults (the default sensor uses a fixed, well-exposedphotons_per_unit). Pass an explicitSensor(photons_per_unit=...)for a dimmer or brighter recording.extra_steps (Sequence[Annotated[PlaceNeurons | CellActivity | CellOptics | Composite | Neuropil | Vasculature | Bleaching | BrainMotion | IlluminationProfile | Vignette | Leakage | Sensor, Discriminator(discriminator=kind, custom_error_type=None, custom_error_message=None, custom_error_context=None)]]) – Additional steps to append (
Neuropil,Vignette,Leakage, …); the spec re-sorts into canonical order, so order here is free.save_intermediates (bool) – Persist per-stage snapshots (see
Output).
- Returns:
rec.observedis the movie,rec.ground_truththe exact truth.- Return type:
score#
- minisim.testing.score(estimate, ground_truth, *, match_metric='iou', match_threshold=0.5, restrict_to_detectable=True, footprint_shift='auto')[source]#
Score a pipeline’s
Estimateagainst the ground truth, in one call.Runs the standard recovery pipeline: match estimated footprints to true ones by spatial overlap (
hungarian_match()againstA_observed, the recoverable target), then score the temporal recovery of the matched pairs. The conventions the a-la-carte recipe asks you to remember are applied here:matches against
A_observed(not the optics-freeA_planted);scores recall over the detectable cells by default (the fair denominator; see
detectable_subset()). The returnedReportalways carriesn_requested(cells planted) andn_detectablealongsiden_true(the denominator used), so a highrecallover a shrunken denominator can never be mistaken for “recovered everything”;absorbs a global footprint offset before matching, so a uniform shift from the pipeline’s motion-correction frame is not scored as a miss (see
footprint_shift);reduces per-pair trace correlations with a nan-safe median;
scores the deconvolved activity without binarizing and up to an unknown scale (
activity_similarity()), reducing each quantity with a nan-safe median;treats an estimated motion trajectory as a correction (negated) and aligns away a constant origin offset when comparing to
GroundTruth.shifts.
- Parameters:
estimate (Estimate) – The pipeline output. Only
Estimate.Ais required.ground_truth (GroundTruth) – The recording’s
ground_truth.match_metric (str) – Footprint similarity for matching (see
SIMILARITY_METRICS):"iou"(binary, default),"cosine", or"weighted_jaccard"(the weighted metrics let pixel weights, not just lit pixels, drive the match).match_threshold (float) – Minimum similarity for a matched pair to count as a true positive.
restrict_to_detectable (bool) – Score against
detectable_subset()(default). SetFalseto score against every planted cell, detectable or not.footprint_shift (tuple[float, float] | str | None) – How to handle a global translational offset between the estimated (motion-corrected) footprints and the true ones.
"auto"(default) reads the offset off the motion trajectories when bothestimate.shiftsandground_truth.shiftsare present (exact), and otherwise estimates it by aligning the footprint centroids. Pass a(dy, dx)to force a known shift, orNoneto disable. The applied integer shift is recorded on theReport.
- Returns:
The scorecard. Unsupplied estimate fields score
nan/None.- Return type:
Estimate#
- class minisim.testing.Estimate(A=<object object>, C=None, S=None, shifts=None, *, footprints=<object object>, traces=None, activity=None)[source]#
Bases:
objectWhat a pipeline recovered, as the input to
score().The footprints are the only required field. Traces / activity / shifts are optional; when omitted, the matching score in the
Reportisnan(orNonefor motion). Arrays may benumpyorxarray(minian’s CNMF returnsxr.DataArray); both are accepted.Each field has two interchangeable spellings - the terse CNMF/minian symbol a pipeline already emits, and a spelled-out alias for anyone who does not speak that dialect. Both work as keyword arguments, and both are readable attributes:
Estimate(A=A, C=C, S=S) # CNMF names Estimate(footprints=A, traces=C, activity=S) # spelled out
A/footprints- spatial footprints,(n_units, height, width).C/traces- calcium traces,(n_units, frame).S/activity- the deconvolved estimate,(n_units, frame). Not a spike train: it is a non-negative estimate of neural activity rate, scaled by an unknown factor mapping activity to calcium-kernel amplitude.score()compares it without binarizing and up to that unknown scale (seeactivity_similarity()).shifts- estimated per-frame(dy, dx)motion,(frame, 2). A motion correction trajectory (the negation of the applied shift);score()negates it to compare againstGroundTruth.shiftsand to recover the global footprint offset.
A frozen dataclass (not a
NamedTuple) on purpose: keyword construction with no positional contract, so a future field can be added without breaking callers that pin the current ones - the property a long-lived public scoring contract needs.- Parameters:
Report#
- class minisim.testing.Report(n_true, n_est, n_matched, recall, precision, f1, mean_overlap, trace_corr, activity_corr, activity_variance_explained, activity_scale, shift_rmse, n_requested, n_detectable, match_metric='iou', footprint_shift=(0, 0))[source]#
Bases:
objectThe recovery scorecard
score()returns.Cell counts and spatial scores are always present; temporal scores are
nanwhen the matching estimate field was not supplied, andshift_rmseisNonewhen motion truth or a motion estimate is absent.The recall denominator is reported explicitly, because it is not obvious: by default
score()scores against the detectable cells, sorecallis overn_true(=n_detectable), which can be fewer than the cells that were planted. The three counts make that legible at a glance -recall = 1.0withn_detectable=4 < n_requested=6means “recovered every detectable cell, but two planted cells were too dim to detect”, not “recovered everything”. Seescore()’srestrict_to_detectable.