nstat.extrasOpt-in Python bridges between the MATLAB-faithful nSTAT core and the modern Python systems-neuroscience ecosystem — without coupling the core parity contract.
nstat.extras is
The core nstat.* namespace mirrors the MATLAB nSTAT toolbox method-for-method and is under a strict parity contract — class names, method names, and numerical conventions match upstream MATLAB exactly so users can port code without surprises. That contract is the reason core nstat has zero dependencies outside NumPy / SciPy / SymPy / matplotlib.
nstat.extras.* is the home for everything that doesn't fit the parity contract: bridges to the wider Python neural-data stack, modern decoders, validation oracles, and metrics with no MATLAB counterpart. Every extras module is opt-in: it requires an explicit pip install nstat-toolbox[…] and follows a different stability tier (minor-version evolution, not major-version).
Pattern modeled on scikit-learn-contrib, but in-tree rather than as a separate package — preserves discoverability and lets the test suite enforce the install-hint contract.
nstat.extras.validation.* — Python-side cross-validation oracles. Fits the same statistical model in nstat and an independent reference library, then returns a comparison object with assert_agree(atol, rtol) hooks for parity tests.
Strongest available cross-check on nstat's GLM & Kalman paths short of running MATLAB-engine.
nstat.extras.interop.* — nspikeTrain / SpikeTrainCollection / Trial converters for Neo, pynapple, and NWB. Unlocks Spike2 / NEX / Blackrock / Plexon / TDT readers via Neo; epoch-math via pynapple; BRAIN-Initiative NWB:N format.
Bridges the file-format gap between MATLAB workflows and modern Python pipelines.
nstat.extras.metrics.* — modern spike-train distance metrics with no MATLAB counterpart. Thin wrappers around PySpike for ISI / SPIKE / SPIKE-synchronization (Kreuz family, parameter-free).
C/Cython-accelerated; suitable for population-level distance matrices.
| Group | Pulls | Backs |
|---|---|---|
[neo] | neo, quantities | extras.interop.neo |
[pynapple] | pynapple | extras.interop.pynapple |
[nwb] | pynwb (and hdmf) | extras.interop.nwb |
[metrics] | pyspike | extras.metrics.spike_distances |
[nemos] | nemos (incl. JAX, ~200 MB) | extras.validation.nemos_bridge |
[test-parity] | nemos + pykalman + statsmodels + nitime | All cross-validation bridges |
[dynamax] | dynamax (incl. JAX, ~200 MB) | extras.em.dynamax_bridge |
[all-extras] | Every functional group above except [dynamax] | One-shot install (heavy [dynamax] opt-out documented in tests/test_pyproject_consistency.py::HEAVY_OPT_OUT_OF_ALL_EXTRAS) |
The [all-extras] union is CI-enforced — adding a new group without updating [all-extras] fails the test suite.
pip install nstat-toolbox[neo]
Round-trip between nspikeTrain and neo.SpikeTrain; build neo.Segment from a collection. Unlocks Spike2 / NEX / Blackrock / Plexon / TDT via Neo readers.
from nstat.extras.interop.neo import to_neo_spiketrain, from_neo_spiketrain
neo_st = to_neo_spiketrain(nst) # nstat → Neo
nst_back = from_neo_spiketrain(neo_st) # Neo → nstat
Details: interop_neo.html · Demo: interop_neo_demo.py
pip install nstat-toolbox[pynapple]
Round-trip between nspikeTrain and pynapple.Ts; preserves recording window via IntervalSet; supports epoch math like ts.restrict(IntervalSet).
import pynapple as nap
from nstat.extras.interop.pynapple import to_pynapple_with_support, from_pynapple_ts
ts, support = to_pynapple_with_support(nst)
ts_sub = ts.restrict(nap.IntervalSet(start=2.0, end=5.0))
nst_sub = from_pynapple_ts(ts_sub, name="sub", sample_rate=30_000, support=...)
Details: interop_pynapple.html · Demo: interop_pynapple_demo.py
pip install nstat-toolbox[nwb]
Read NWB:N files into SpikeTrainCollection. Resolves per-unit observation windows via obs_intervals (NWB standard) or an explicit time_window= override; emits UserWarning when neither is available.
from nstat.extras.interop.nwb import read_nwb_path
coll = read_nwb_path("/path/to/session.nwb", sample_rate=30_000.0)
# Or: nwb_units_to_collection(nwbfile, time_window=(0.0, 600.0))
Details: interop_nwb.html · Demo: interop_nwb_demo.py
pip install nstat-toolbox[nemos] (or [test-parity])
Cross-validate nstat.fit_poisson_glm against Flatiron's NeMoS GLM. Returns a GLMComparison with assert_agree hook for parity tests.
from nstat.extras.validation.nemos_bridge import cross_validate_poisson_glm
cmp = cross_validate_poisson_glm(X, y)
cmp.assert_agree(atol=5e-2, rtol=5e-2)
Details: validation_nemos.html · Demo: validation_nemos_demo.py
pip install nstat-toolbox[test-parity]
Cross-validate nstat.fit_poisson_glm against statsmodels' IRLS. Because both use IRLS, agreement is typically ~1e-9 (machine precision) — the tightest oracle in nstat.extras.validation.
from nstat.extras.validation.statsmodels_bridge import cross_validate_poisson_glm
cmp = cross_validate_poisson_glm(X, y)
cmp.assert_agree(atol=1e-6, rtol=1e-6) # tight by design
Details: validation_statsmodels.html · Demo: validation_statsmodels_demo.py
pip install nstat-toolbox[test-parity]
Cross-validate DecodingAlgorithms.kalman_filter + kalman_fixedIntervalSmoother against pykalman. Documents the AUDIT D3 smoother-gap empirically (~0.4 unit baseline on a 100×2 LG fixture).
from nstat.extras.validation.pykalman_bridge import cross_validate_kalman
cmp = cross_validate_kalman(y, A, C, Q, R, x0, P0)
cmp.assert_filtered_agree(atol=1e-2) # filter agrees
# Smoother gap is documented & known — see AUDIT D3.
Details: validation_pykalman.html · Demo: validation_pykalman_demo.py
pip install nstat-toolbox[metrics]
ISI-distance (Kreuz 2007), SPIKE-distance (Kreuz 2013), SPIKE-synchronization (Kreuz 2015) and pairwise N×N distance matrix. Parameter-free — no kernel bandwidth, no binning.
from nstat.extras.metrics.spike_distances import (
spike_distance, pairwise_spike_distance_matrix,
)
d = spike_distance(a, b) # scalar in [0, 1]
D = pairwise_spike_distance_matrix(trains) # (N, N)
Details: metrics_spike_distances.html · Demo: metrics_spike_distances_demo.py
pip install nstat-toolbox[dynamax] (pulls JAX, ~200 MB)
State-space estimation closing the unported MATLAB KF_EM / PP_EM / mPPCO_EM families (AUDIT_REPORT.md §3.2 — 19 methods / ~7,500 LOC of MATLAB). Three EM trainers + two point-process inference routines:
fit_linear_gaussian_em — KF_EM equivalent (thin Dynamax LinearGaussianSSM.fit_em wrapper).cmgf_poisson_filter / cmgf_poisson_smoother — point-process Kalman filter/smoother under the CMGF Gaussian approximation (PPDecodeFilter / PP_fixedIntervalSmoother).fit_point_process_em — PP_EM equivalent (CMGF E-step + closed-form/Newton M-step, Smith & Brown 2003 PPLDS).fit_hybrid_em — mPPCO_EM equivalent (IRLS-pseudo-observation augmented Kalman smoother E-step + closed-form/Newton M-step).from nstat.extras.em.dynamax_bridge import (
fit_linear_gaussian_em, fit_point_process_em, fit_hybrid_em,
)
# Poisson point-process state-space EM (PP_EM)
pp = fit_point_process_em(spike_counts, state_dim=2, n_iter=30)
print(pp.observation_matrix, pp.marginal_log_likelihoods[-1])
# Hybrid Poisson + Gaussian observations sharing one latent (mPPCO_EM)
hy = fit_hybrid_em(spike_counts, lfp, state_dim=2, n_iter=30)
Details: em_dynamax.html · Demo: em_dynamax_demo.py
parity/integration_opportunities.md):
fit_point_process_em / fit_hybrid_em with the exact smoother lag-one covariance, and exact Laplace E[exp(Cx)] correction, to tighten MATLAB-parity from ~1-3% to <1e-4.nstat.extras.spectral.synchrosqueeze — ssqueezepy wrapper, closes the "no synchrosqueeze" admission.nstat.extras.deep_learning — PyTorch decoder bridges (planned).nstat.extras.interop.spikeinterface — spike-sorting-output bridge.
Every extras module is Python-side only. No bridge introduces runtime coupling to the MATLAB cajigaslab/nSTAT repository. The cleanroom-boundary CI gate (tests/test_cleanroom_boundary.py) scans every nstat/extras/**/*.py for the forbidden matlab.engine pattern and fails the build on any match.
License: GPL-2.0 (matches core nSTAT-python). Every extras dep listed above is MIT / BSD-2 / BSD-3 / Apache-2 / GPL-2 — all GPL-2 compatible. The license audit lives in integration_opportunities.md.