Skip to content

Add VanillaSort external sorter - #4832

Merged
alejoe91 merged 10 commits into
SpikeInterface:mainfrom
KohakuKirisame:main
Oct 20, 2026
Merged

alejoe91 merged 10 commits into
SpikeInterface:mainfrom
KohakuKirisame:main

Conversation

@KohakuKirisame

@KohakuKirisame KohakuKirisame commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Add VanillaSort as an external sorter through its public vanillasort.sort() API (requires VanillaSort >= 0.2.1). The wrapper provides lightweight discovery/version checks, 17 serializable parameters, optional CUDA, progress logging, and reloadable multisegment results. Python gate_fn callbacks remain available through the direct VanillaSort API.

Detailed parameters and examples and wrapper-specific regression tests are maintained in VanillaSort. SpikeInterface retains the shared SorterCommonTestSuite subclass, its standard installation entry, and external documentation links.

Validation:

  • Shared sorter suite: 2 passed using the unchanged 60-second, 25 kHz fixture and published checkpoints on CPU.
  • VanillaSort package/integration suite: 21 passed, including direct/wrapper equivalence, saved results/logs, parameter forwarding, fixed-K policies, and 20/25/30 kHz round trips.
  • Focused Sphinx build, parameter/default consistency, Black/Ruff, and distribution checks passed.

The 25 kHz common-suite run used VanillaSort main at 46970dc. Its removal of the fixed 30 kHz check is unreleased; PyPI 0.2.1 retains that earlier check.

Repository · Models · Paper

Register VanillaSortSorter and map model selection, local checkpoint paths, CPU/CUDA devices, seed, mixture count, profile and batch sizes to vanillasort.sort(). Keep all detection and clustering code in the external package.

Use BaseSorter's recording serialization and metadata flow, preserve multi-segment results and main_channel_id with native sorting-folder storage, and capture package progress in the standard runtime log. Report package installation/version and optional GPU support without loading checkpoints during discovery.

Reuse SorterCommonTestSuite with a short synthetic recording and add one direct-versus-wrapper integration test. Check exact spike-vector/unit/property equivalence, result reload, recording metadata, version reporting, output cleanup and standard failure logging.

Validation: the new external tests and existing sorter registry/dependency checks pass (9 passed, 1 platform-specific skip). Black, targeted Ruff, compileall and git diff --check pass. The optional VanillaSort package is tested from its local 0.1.0 inference-package branch.
Add VanillaSort to the external sorter list and document source installation, the public package API, repository and paper citation, and a basic run_sorter example.

Describe 30 kHz and 2D geometry requirements, four-channel local neighborhoods, optional CPU/CUDA execution, configurable GMM K and local models. Explicitly identify experimental larger-probe behavior, independent segment units and full-segment memory requirements.

Validation: documentation changes are limited to the existing sorter list and installation guide; whitespace and diff checks pass. Submission and upstream discussion remain for human contributors under AGENTS.md.
Document that default pretrained models are downloaded lazily from a pinned Hugging Face release and cached separately from the Python package. Update the model parameter description and link the published model repository.

Generate small local VanillaDet/HuiduRep checkpoints for the existing common-suite and equivalence tests. Exercise the real inference path and local-model parameter without downloading pretrained weights or adding test cases.

Validation: HF_HUB_OFFLINE=1 run of the external sorter, registry and runner dependency tests reports 9 passed and 1 platform-specific skip. A separate smoke test with the actual Hub model and installed lightweight wheel confirms exact direct/wrapper equivalence. Black, targeted Ruff and git diff --check pass.
Replace the repository clone and editable-install commands with pip install vanillasort in the external sorter's installation message and the sorter installation guide. Retain the minimum supported version and the link to platform-specific PyTorch guidance.

Validation: Black and git diff --check pass. A targeted source/documentation search confirms both VanillaSort installation instructions now use the PyPI command. This changes instructional text only.
@alejoe91 alejoe91 added the sorters Related to sorters module label Oct 7, 2026

@zm711 zm711 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few comments to improve performance of our package overall as we add the features. Let me know if you need more guidance.

Comment thread src/spikeinterface/sorters/external/vanillasort.py Outdated
Comment thread src/spikeinterface/sorters/external/vanillasort.py Outdated
Comment thread src/spikeinterface/sorters/external/vanillasort.py Outdated
Comment thread src/spikeinterface/sorters/external/vanillasort.py Outdated
Address the maintainer review by checking module availability with importlib.util.find_spec and reading the installed distribution version with importlib.metadata.version. Preserve the minimum supported version without importing the optional VanillaSort package.

Make use_gpu report the requested device intent without importing Torch on the host. Use the recognized nvidia-optional capability so SpikeInterface's container runner handles hardware availability, while VanillaSort resolves auto in the inference environment.

Explain how redirect_stdout captures package progress in the standard runtime trace and restores stdout after success or failure. Add one focused import-guard regression test and extend the existing integration test to verify stdout restoration.

Validation: the VanillaSort external suite and existing registry/dependency checks pass with HF_HUB_OFFLINE=1 (10 passed, 1 platform-specific skip) against the published vanillasort 0.1.0 package. A fresh-process check confirms discovery, version reporting and GPU intent leave both vanillasort and torch unloaded. Black, targeted Ruff, compileall and git diff --check pass.
@alejoe91

Copy link
Copy Markdown
Member

Hi @KohakuKirisame

I tested this on my laptop with a small GPU, but it seems to get stuck. I only see this output ofter more than one hour.

import spikeinterface.full as si

rec, sort = si.generate_ground_truth_recording(num_channels=64, durations=[60], sampling_frequency=30_000)
rec = rec.save(folder="rec_cache", n_jobs=-1, overwrite=True)
sorting_vanilla = si.run_sorter("vanillasort", rec, verbose=True, remove_existing_folder=True)

>>> Warning! The recording is already filtered, but vanillasort filter is enabled

Any idea what could be going wrong? I see my GPU being used...

Echo package progress to stdout while retaining vanillasort.log and the
standard runtime trace. Preserve quiet execution and restore stdout when
sorting fails.

Use pip install --upgrade vanillasort without a hard-coded version
requirement. Extend the existing integration test to check visible and
quiet progress, log retention, and exception handling without adding tests.

Validation: targeted sorter, discovery, and dependency tests passed
(10 passed, 1 Linux-only skip); direct/wrapper and discovery compatibility
checks also passed against the older package (2 passed). Targeted Ruff,
formatting, and git diff --check passed.
@KohakuKirisame

Copy link
Copy Markdown
Contributor Author

Hi @alejoe91 , the wrapper was redirecting all progress to a log even with verbose=True. Your example also processes 62 overlapping four-channel neighborhoods, each scanning the full recording, which makes inference expensive on smaller GPUs. I’ve updated the wrapper to display progress while retaining the log; new version of VanillaSort (0.2.0) provides the attention optimization and improved CUDA batch retries, try pip install --upgrade vanillasort. On Windows with an RTX 5080 (16 GB), Python 3.12.14, PyTorch 2.13.0+cu132, and SpikeInterface 0.105.1, your 64-channel/60-second example (fixed seed 0) completed in 279.90 s with VanillaSort 0.1.0 and 217.50 s with 0.2.0 (22.3% less time). All 62 neighborhoods and final outputs matched exactly, and CUDA retries under a restricted memory budget succeeded. I reproduced the hidden-progress issue, but not the hour-long runtime locally.

@alejoe91

alejoe91 commented Oct 19, 2026 •

Copy link
Copy Markdown
Member

Hi @alejoe91 , the wrapper was redirecting all progress to a log even with verbose=True. Your example also processes 62 overlapping four-channel neighborhoods, each scanning the full recording, which makes inference expensive on smaller GPUs. I’ve updated the wrapper to display progress while retaining the log; new version of VanillaSort (0.2.0) provides the attention optimization and improved CUDA batch retries, try pip install --upgrade vanillasort. On Windows with an RTX 5080 (16 GB), Python 3.12.14, PyTorch 2.13.0+cu132, and SpikeInterface 0.105.1, your 64-channel/60-second example (fixed seed 0) completed in 279.90 s with VanillaSort 0.1.0 and 217.50 s with 0.2.0 (22.3% less time). All 62 neighborhoods and final outputs matched exactly, and CUDA retries under a restricted memory budget succeeded. I reproduced the hidden-progress issue, but not the hour-long runtime locally.

I tested on my laptop, with a 4GB NVIDIA GeForce RTX 3050...So the new version runs relatively quickly, but I tested now on an 8-channel recording with 10 units. The issue now is that vanilla sort outputs 131 units versus the 10 ground truth ones.

The ground-truth units are found, with some oversplits. But is the extra number of units expected? Is it controlled by the components method?

image

Regarding the verbose output, I still got no std out:

sorting_vanilla = si.run_sorter("vanillasort", rec, verbose=True, remove_existing_folder=True)

>>> Warning! The recording is already filtered, but vanillasort filter is enabled
vanillasort run time 150.47s
[/home/alessio/Documents/Codes/spike_sorting/spikeinterface/spikeinterface/src/spikeinterface/core/core_tools.py:947](http://localhost:8888/home/alessio/Documents/Codes/spike_sorting/spikeinterface/spikeinterface/src/spikeinterface/core/core_tools.py#line=946): UserWarning: The extractor is not serializable to file. The provenance will not be saved.
  warnings.warn("The extractor is not serializable to file. The provenance will not be saved.")

Add comprehensive VanillaSort 0.2.1 documentation covering wrapper parameters, direct API options, gating, custom callbacks, fixed-K postprocessing, outputs, and usage examples. Link the new guide from sorter listings and installation instructions.
Register the nine JSON-serializable detection, neighborhood, and fixed-K
postprocessing options with the same defaults as vanillasort.sort(). Keep
gate_fn on the direct Python API, while preserving the existing inference
implementation and stdout logging behavior.

Check the installed distribution version before inference and provide an
upgrade message for VanillaSort versions older than 0.2.1. Version checks,
sorter discovery, and GPU intent checks continue to avoid heavy imports.

Update the installation and parameter guides for all 17 wrapper options,
including run_sorter examples for threshold control and per-segment fixed-K
merging, shortfall policies, and the direct-only callback boundary.

Validation:
- Focused offline wrapper suite: 5 passed.
- Eight-channel, two-segment direct/wrapper results match exactly.
- Parameter JSON, empty results, and strict-shortfall diagnostics verified.
- Fresh-process discovery and version checks import neither torch nor vanillasort.
- Black, targeted Ruff, diff checks, and focused Sphinx build pass.
- Documentation defaults and Python examples match the package API.
@KohakuKirisame

Copy link
Copy Markdown
Contributor Author

Hi @alejoe91 , the wrapper was redirecting all progress to a log even with verbose=True. Your example also processes 62 overlapping four-channel neighborhoods, each scanning the full recording, which makes inference expensive on smaller GPUs. I’ve updated the wrapper to display progress while retaining the log; new version of VanillaSort (0.2.0) provides the attention optimization and improved CUDA batch retries, try pip install --upgrade vanillasort. On Windows with an RTX 5080 (16 GB), Python 3.12.14, PyTorch 2.13.0+cu132, and SpikeInterface 0.105.1, your 64-channel/60-second example (fixed seed 0) completed in 279.90 s with VanillaSort 0.1.0 and 217.50 s with 0.2.0 (22.3% less time). All 62 neighborhoods and final outputs matched exactly, and CUDA retries under a restricted memory budget succeeded. I reproduced the hidden-progress issue, but not the hour-long runtime locally.

I tested on my laptop, with a 4GB NVIDIA GeForce RTX 3050...So the new version runs relatively quickly, but I tested now on an 8-channel recording with 10 units. The issue now is that vanilla sort outputs 131 units versus the 10 ground truth ones.

The ground-truth units are found, with some oversplits. But is the extra number of units expected? Is it controlled by the components method?

image Regarding the verbose output, I still got no std out:
sorting_vanilla = si.run_sorter("vanillasort", rec, verbose=True, remove_existing_folder=True)

>>> Warning! The recording is already filtered, but vanillasort filter is enabled
vanillasort run time 150.47s
[/home/alessio/Documents/Codes/spike_sorting/spikeinterface/spikeinterface/src/spikeinterface/core/core_tools.py:947](http://localhost:8888/home/alessio/Documents/Codes/spike_sorting/spikeinterface/spikeinterface/src/spikeinterface/core/core_tools.py#line=946): UserWarning: The extractor is not serializable to file. The provenance will not be saved.
  warnings.warn("The extractor is not serializable to file. The provenance will not be saved.")

Hi @alejoe91, components sets K independently for each four-channel neighborhood, so the combined output can contain excessive units and oversplits as the channel count increases. VanillaSort 0.2.1 is now available (pip install --upgrade vanillasort) and adds optional fixed-K merging. With the updated wrapper, you can pass target_units=10 through run_sorter(); postprocessing={"on_shortfall": "return_available"} allows fewer units when reliable anchors are insufficient. Adjusting components may also help, though these settings need tuning for each recording. I’ve updated the parameter documentation to explain all available options. Regarding stdout, please update your SpikeInterface checkout to the latest PR revision: 72a80d3 already forwards progress to the console with verbose=True. Updating VanillaSort alone does not update the wrapper.

Comment thread doc/modules/vanillasort.rst Outdated
Comment thread src/spikeinterface/sorters/external/tests/test_vanillasort.py
Comment thread src/spikeinterface/sorters/external/tests/test_vanillasort.py Outdated

@alejoe91 alejoe91 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @KohakuKirisame

Can you remove the vanillasort-specific tests and docs? Those should live in the vanillasort repo

Follow the review on PR SpikeInterface#4832: remove the dedicated parameter page and its hidden toctree, link installation and sorter settings to the VanillaSort-owned guide, and reduce the test module to the shared SorterCommonTestSuite subclass.

The migrated tiny-model regressions remain in VanillaSort. Keep the 17-parameter wrapper and common test infrastructure unchanged; align the sorter description with the current multichannel package API.

Validation: both common tests pass on the default 60-second, 25 kHz fixture with published checkpoints and current VanillaSort main; all 21 package/integration tests pass in VanillaSort; focused Sphinx, parameter-default consistency, Black/Ruff, and diff checks pass.
@KohakuKirisame

Copy link
Copy Markdown
Contributor Author

Thanks @alejoe91! I've updated the PR as suggested. The detailed documentation and sorter-specific tests now live in the VanillaSort repository, with links from SpikeInterface.

The test module now only inherits SorterCommonTestSuite, without custom fixtures or setUp overrides. Both common-suite tests pass against the current VanillaSort main using the default recording fixture and published checkpoints. All 21 VanillaSort package/integration tests also pass.

@alejoe91
alejoe91 merged commit 98aeee6 into SpikeInterface:main Oct 20, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

sorters Related to sorters module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants