Whitening
Neighbouring channels see much of the same signal, so they are correlated. Whitening is a
channels × channels matrix that undoes that: after it, every channel has unit variance and no
correlation with the others. Spike sorters (Kilosort) whiten so that each channel’s evidence
counts once. Whitening.fit_zca fits the matrix on the device (fit_local_knn: over each
channel’s nearest neighbours only, as Kilosort4).
import numpy as np
import dspkfrom plating import signals
s = signals.synthetic(channels=16)x = dspk.ops.filter.iir(s.data, s.sample_rate, "bandpass", (300.0, 6_000.0)) # the spike band, as sorters whiten it# Make neighbours share signal: each channel picks up a fraction of the next ones (volume conduction)mix = np.eye(s.channels) + sum(0.6**k * (np.eye(s.channels, k=k) + np.eye(s.channels, k=-k)) for k in (1, 2, 3))x = (mix @ x).astype(np.float32)
w = dspk.ops.spatial.Whitening.fit_zca(x)y = w.apply(x)before, after = np.corrcoef(x), np.corrcoef(y)off = ~np.eye(s.channels, dtype=bool)print(f"largest correlation between two channels: before {np.max(np.abs(before[off])):.2f}, after {np.max(np.abs(after[off])):.3f}")
The whole script is playground/05_spatial/01_whitening.py. Run it with uv run python playground/05_spatial/01_whitening.py.