victor_purpura_distance

victor_purpura_distance#

class braintools.analysis.spike.victor_purpura_distance(spike_times_1, spike_times_2, cost_factor=1.0)#

Calculate Victor-Purpura distance between two spike trains.

The Victor-Purpura distance quantifies the dissimilarity between two spike trains by computing the minimum cost to transform one spike train into another through spike insertions, deletions, and temporal shifts [1].

The distance is computed as:

\[D_{VP} = \min \sum_{ops} c_{op}\]

where the cost of moving a spike by time \(\Delta t\) is \(q|\Delta t|\), insertion/deletion costs are 1, and \(q\) is the cost factor.

Parameters:
  • spike_times_1 (Array | ndarray | bool | number | bool | int | float | complex | Quantity) – First spike train as a 1-D array of spike times. Entries equal to +inf are padding and are treated as absent spikes, so a row of SpikeTrains.times can be passed directly.

  • spike_times_2 (Array | ndarray | bool | number | bool | int | float | complex | Quantity) – Second spike train, same convention.

  • cost_factor (float) – Cost factor \(q\) for temporal shifts. Higher values penalize temporal differences more heavily.

Returns:

Victor-Purpura distance, a 0-d array in the float dtype of the inputs (float32 unless jax_enable_x64 is on).

Return type:

jax.Array

Raises:

ValueError – If a spike train is not 1-D.

See also

van_rossum_distance

Kernel-based distance between two spike trains.

isi_distance

Time-resolved ISI-distance between two populations.

spike_distance

Time-resolved SPIKE-distance between two populations.

Notes

The dynamic programme fills the (n_1 + 1) x (n_2 + 1) cost table with a jax.lax.scan() over its rows and an inner scan over its columns; a +inf entry leaves the row (or column) unchanged, which is the same as removing that spike. The function therefore works under jax.jit and jax.vmap (e.g. over the rows of a padded (N, K) array), and is differentiable almost everywhere with respect to the spike times and cost_factor (the distance is piecewise linear in them). It takes O(n_1 n_2) sequential steps.

cost_factor and the spike times must use consistent units (the distance has units of cost_factor * time + count); the magnitudes of Quantities are used as given.

Before version 0.4 this function returned a Python float; float(d) still works on the 0-d array.

Examples

>>> import jax
>>> import jax.numpy as jnp
>>> import braintools
>>> spikes1 = jnp.array([1.0, 2.0, 3.0])
>>> spikes2 = jnp.array([1.1, 2.1, 3.1])
>>> d = braintools.analysis.spike.victor_purpura_distance(spikes1, spikes2, cost_factor=10.0)
>>> round(float(d), 4)
3.0
>>>
>>> # Padded rows (``+inf`` = no spike), e.g. ``SpikeTrains.times``, under vmap.
>>> a = jnp.array([[1.0, 2.0, jnp.inf], [1.0, jnp.inf, jnp.inf]])
>>> b = jnp.array([[1.0, 2.0, 3.0], [jnp.inf, jnp.inf, jnp.inf]])
>>> jax.vmap(braintools.analysis.spike.victor_purpura_distance)(a, b)
Array([1., 1.], dtype=float32)

References