Spike-train distance and similarity metrics (Victor-Purpura, van Rossum, multi-unit van Rossum, Schreiber, Hunter-Milton, ISI-distance) in pure Python with zero dependencies.
Project description
spikedist
Spike-train distance and similarity metrics in pure Python with zero dependencies. Implements the Victor-Purpura and van Rossum distances, the ISI-distance, and the Schreiber and Hunter-Milton similarities on plain sequences of spike times.
Install
pip install spikedist
For the optional NumPy fast path:
pip install spikedist[fast]
30-second example
from spikedist import victor_purpura, van_rossum, isi_distance, schreiber, hunter_milton
a = [0.010, 0.025, 0.090] # spike times in seconds
b = [0.012, 0.030, 0.095]
victor_purpura(a, b, cost=100.0) # edit distance, cost is the q parameter
van_rossum(a, b, tau=0.012) # kernel distance, tau is the time constant
isi_distance(a, b, interval=[0.0, 0.1]) # ISI-distance in [0, 1]
schreiber(a, b, sigma=0.010) # Gaussian correlation similarity in [0, 1]
hunter_milton(a, b, tau=0.012) # nearest-neighbor similarity in (0, 1]
Spike times can be Python lists, tuples, or any sequence of numbers, including NumPy arrays. They are treated as an unordered set of event times and sorted internally. There is no NumPy requirement.
Why this exists
The Victor-Purpura and van Rossum distances are two of the most cited spike-train metrics, but every Python implementation lives inside a heavy framework or a compiled extension:
elephantimplements both, but requiresneoandquantitiesand works onneo.SpikeTrainobjects with units.pymuvris a fast multi-unit van Rossum implementation, but is a C++ extension and requires NumPy.pyspikeis excellent for ISI-distance, SPIKE-distance, and SPIKE-synchrony, but does not implement Victor-Purpura or van Rossum.spikedistnow also implements the ISI-distance matching pyspike's convention, with zero dependencies and no NumPy requirement.
spikedist is a small, typed, dependency-free package for when you just want the
distance between two spike trains.
Definitions
Victor-Purpura
victor_purpura(a, b, *, cost) is the minimum total cost to turn train a into
train b using three operations: insert a spike (cost 1), delete a spike
(cost 1), and shift a spike by dt (cost cost * abs(dt)). cost is the
parameter usually written q. It is computed with an O(n*m) dynamic program.
cost = 0counts only the difference in spike count.- As
costgrows, shifting becomes expensive and each unmatched spike approaches a cost of 2.
van Rossum
van_rossum(a, b, *, tau) convolves each train with a causal exponential kernel
exp(-t / tau) and returns the Euclidean distance between the filtered signals.
Using the closed form of the kernel inner products,
D^2 = 0.5 * (Saa + Sbb - 2 * Sab), Sxy = sum over spike pairs exp(-|xi - yj| / tau)
The kernel sums are computed in O(n + m) time using the Houghton-Kreuz markage recursion rather than the naive O(n*m) double loop.
With this normalization the distance between an empty train and a single spike is
sqrt(0.5), and as tau grows large the distance approaches
abs(len(a) - len(b)) / sqrt(2).
Both distances are true metrics: non-negative, symmetric, zero only between equal trains, and they satisfy the triangle inequality. These properties are tested.
ISI-distance
isi_distance(a, b, *, interval) measures the dissimilarity between two spike
trains using their instantaneous inter-spike-interval (ISI) functions. At each
time t the ISI function gives the length of the ISI that contains t. Boundary
intervals use an auxiliary ISI: before the first spike in a train with N > 1
it is max(first_spike - t_start, second_ISI); for a single-spike train it is
first_spike - t_start. After the last spike in a train with N > 1 it is
max(t_end - last_spike, last_ISI); for a single-spike train it is
t_end - last_spike. An empty train is treated as two auxiliary spikes at
t_start and t_end, giving a constant ISI of t_end - t_start.
The pointwise dissimilarity and time-averaged distance are
I(t) = |isi_a(t) - isi_b(t)| / max(isi_a(t), isi_b(t))
D_I = (1 / (t_end - t_start)) * integral_{t_start}^{t_end} I(t) dt
The result lies in [0, 1]. The interval parameter [t_start, t_end] is
required and has no default value. The algorithm runs in O(n + m) time.
The edge convention matches pyspike exactly, validated to floating-point identity (error == 0.0) on all reference cases from pyspike 0.9.0.
Reference: Kreuz T, Haas JS, Morelli A, Abarbanel HDI, Politi A (2007), "Measuring spike train synchrony," J Neurosci Methods 165:151-161.
Multi-unit van Rossum
van_rossum_multiunit(a, b, *, tau, c) compares two labeled populations of spike trains,
each given as a mapping from unit label to that unit's train. The parameter c in
[0, 1] sets how much spikes of different units interact: c = 0 treats the units as
independent (the Euclidean combination of the per-unit distances), c = 1 ignores the
labels (the pooled van Rossum distance), and a single unit reduces to van_rossum. It
reuses the O(n + m) markage cross-sum.
Schreiber similarity
schreiber(a, b, *, sigma) convolves each train with a Gaussian of width sigma
and returns the cosine similarity of the filtered signals, in [0, 1]. It is 1
for identical trains.
Hunter-Milton similarity
hunter_milton(a, b, *, tau) scores each spike by exp(-dt / tau) to its nearest
neighbor in the other train and averages over both trains, giving a value in
(0, 1]. It is 1 for identical trains.
By convention both similarities treat two empty trains as identical (1.0) and a non-empty train against an empty one as fully dissimilar (0.0).
Pairwise matrices
pairwise(trains, metric) builds the full matrix of any metric over a list of
trains. Parameterize the metric with functools.partial:
from functools import partial
from spikedist import pairwise, van_rossum
pairwise(trains, partial(van_rossum, tau=0.01))
NumPy fast path (optional)
When NumPy is installed (pip install spikedist[fast]), van_rossum_matrix
computes the full N x N pairwise van Rossum distance matrix using NumPy
broadcasting on the cross-kernel sums. It is faster than N^2 calls to
van_rossum for moderate to large N and returns numerically identical results.
from spikedist import van_rossum_matrix
trains = [[0.0, 0.1], [0.05, 0.2], [0.3]]
matrix = van_rossum_matrix(trains, tau=0.01)
# matrix[i][j] == van_rossum(trains[i], trains[j], tau=0.01)
NumPy remains strictly optional. The package imports and all other functions
work with zero dependencies when NumPy is not installed, and van_rossum_matrix
is simply not available.
Roadmap
- Additional spike-train metrics (SPIKE-distance, SPIKE-synchrony).
Testing
pip install -e ".[dev]"
pytest
Tests cover exact closed-form reference values and metric-property invariants (identity, symmetry, non-negativity, triangle inequality) via Hypothesis.
Contributing
Issues and pull requests are welcome. See CONTRIBUTING.md.
License
MIT. See LICENSE.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file spikedist-0.6.0.tar.gz.
File metadata
- Download URL: spikedist-0.6.0.tar.gz
- Upload date:
- Size: 768.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3a9dd95acdb851f35b76102e5a32444ec7317e3533a1f92ba77c894d2c74e56c
|
|
| MD5 |
1350529247a1494c29397c72ae9a1154
|
|
| BLAKE2b-256 |
ae7f437e5bc480dc4cf07f15ab21f051dff9fa7ff905a7a78876084a9036ffb4
|
File details
Details for the file spikedist-0.6.0-py3-none-any.whl.
File metadata
- Download URL: spikedist-0.6.0-py3-none-any.whl
- Upload date:
- Size: 17.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f75ede72d1af565f79d796f73a9e33a1245e0908af5c73d19fce5507e015f277
|
|
| MD5 |
db3ca8be3e24d143e5a25b2d7b41f908
|
|
| BLAKE2b-256 |
13eb047de5b45eb931a95cd2118c7e5f665c3f190116a31ab907d9cbd7562292
|