Correct and speed up multi-segment synchrony metrics - #4770
Conversation
There was a problem hiding this comment.
Hi @JESUSROYETH this is great!!! Thank you!
I had a quick play and think the new implementation is numba jit compliant with a few changes (which I've made as suggestions in the review). Would it be possible to add these and jit the function following what is done with other metrics (e.g. use numba is HAVE_NUMBA is True). This case is slightly different because the function is the same if a user has numba or not.
Co-authored-by: Chris Halcrow <57948917+chrishalcrow@users.noreply.github.com>
Co-authored-by: Chris Halcrow <57948917+chrishalcrow@users.noreply.github.com>
Co-authored-by: Chris Halcrow <57948917+chrishalcrow@users.noreply.github.com>
|
Thanks @JESUSROYETH I'm curious now! What is the numba speedup???? |
|
Thanks! @alejoe91 @chrishalcrow |
| if HAVE_NUMBA: | ||
| import numba | ||
|
|
||
| _get_synchrony_counts = numba.jit(nopython=True, nogil=True, cache=False)(_get_synchrony_counts) |
|
Interesting result @chrishalcrow @alejoe91: on the same 20M-spike flow, warm Numba was 3.3% slower (0.668 vs 0.649 s), while a fresh process was 10.4x slower due to compilation. Five-run medians, with identical output hashes. The function is already vectorized and dominated by |
|
Interesting! I get a 2x speedup from numpy to numba on my data (macbook, 30hr 1-shank NP2 recording). @alejoe91 do you want to try on some of your real data? |
|
yeah, that makes sense @chrishalcrow. If this runs several times during merges/splits, then the compilation time is not really important. Also, the 2x on real NP2 data is more useful than my synthetic test. |
|
Let me test on a couple of datasets on my end! |
Small dataset: 142 units / ~2M spikescompute("quality_metrics", metric_names=["synchrony"], save=False) compute_synchrony_metrics(analyzer) Medium dataset: 325 units / ~15M spikescompute("quality_metrics", metric_names=["synchrony"], save=False) compute_synchrony_metrics(analyzer) Large dataset: 314 units / ~150M spikescompute("quality_metrics", metric_names=["synchrony"], save=False) compute_synchrony_metrics(analyzer) I ran the synchrony metric before the |
|
Yes, I think keeping Numba as default makes sense for the large repeated merge/split case. There is a regression on the small dataset, but the NumPy fallback is still available when Numba is not installed. Thanks for testing the different sizes |
Description
Synchrony metrics can credit the wrong units when a sorting has multiple segments. Equal sample indices from different segments get grouped together, and the code then slices the spikes as if those occurrences were contiguous. That can mark solitary units as synchronous, and it can also miss units that belong to a real synchronous event.
The spike vector is already ordered by segment and sample, so this change just detects the adjacent events and counts the unit pairs with NumPy instead. Single-segment results, duplicate-unit behaviour, and the ordering through the supported
periodspath stay the same, and it adds no dependency. This is the synchrony bottleneck discussed in #4310, and it follows the ordering contract from #4606.Benchmark
I measured this through
compute_synchrony_metrics, one thread, median of five fresh-process runs after warm-up:Peak RSS on the 20M-spike workload dropped from 1051.7 to 315.7 MiB, and the output hashes matched in both workloads.
Validation
I ran the quality-metrics module: 32 tests pass. The patch also matches an independent oracle across 400 randomised multi-segment cases and the public MEArec fixture. Black is clean, and I tested it on Linux.