All notable changes to BindsNET are documented here. The format is based on Keep a Changelog. For releases prior to the entries below, see the GitHub releases / tags.
--seednow makes GPU runs repeatable ineth_mnist.py,batch_eth_mnist.py,supervised_mnist.py,conv_mnist.py,conv1d_MNIST.py,conv3d_MNIST.py,SOM_LM-SNNs.pyandbreakout/random_network_baseline.py: they calltorch.manual_seed(seed)unconditionally, which seeds the CPU generator (weight initialisation, Poisson encoding, data shuffling) and every GPU. Before, the GPU path called onlytorch.cuda.manual_seed_all; two runs ofbatch_eth_mnist.py --seed 0 --n_train 640 --n_test 320gave 0.37 and 0.47.dot_tracing.pynow uses its--seedfor torch and the dot simulator (default seed 1, since the simulator reads 0 as "seed from the clock").supervised_mnist.pyplots input-to-excitatory weights throughfeature_index["weight"](MulticompartmentConnectionhas no.w), so it runs to the end.reservoir.pyscores its readout on the MNIST test set (it reused the training loader before) and reports the number of images it actually scored.conv_mnist.pynow tests the network: it labels output neurons from training spikes (assign_labels) and reports all-activity and proportion-weighting test accuracy, likeeth_mnist.py.--plotworks as a switch ineth_mnist.py,supervised_mnist.py,conv_mnist.py,reservoir.py,conv1d_MNIST.py,conv3d_MNIST.pyandSOM_LM-SNNs.py: plotting is off unless--plotis given (it wasstore_truewith a default ofTrue).conv_mnist.pyno longer pauses 1 s per sample.- Training and test loops in the MNIST examples run exactly
--n_train/--n_testsamples (>=instead of>, which ran one more). - The Breakout examples run with the locked
ale-py0.12.1:GymEnvironmentmapsBreakoutDeterministic-v4to its ale-py 0.8.1 definition,Breakout-v4withframeskip=4(newer ale-py no longer register that id). play_breakout_from_ANN.pyloadstrained_shallow_ANN.ptunder PyTorch 2.6+: the file is now astate_dictwith identical tensors, loaded withweights_only=True(it was a pickledNetobject).publish.ymlreads the version from[project], where it has lived since 0.3.4, so a published GitHub release passes its tag check (it read[tool.poetry]; the last PyPI upload was started by hand, which skips the check).- Dockerfile on
python:3.13-slim, insiderequires-python = ">=3.11,<3.14"(#810 had moved it to 3.14); Dependabot no longer proposes minor or major Python bumps for the image. - API reference on Read the Docs was empty: the build never installed
bindsnet, so everyautomodulefailed to import (No module named 'matplotlib'), anddocs/pyproject.tomldowngraded Sphinx to 7.2.6..readthedocs.yamlnow installs a CPU build of torch and the package (Python 3.13, Ubuntu 24.04);docs/pyproject.tomlremoved. - The docs build has no warnings (was 48 on Read the Docs, 53 with the package
installed): docstring markup fixed in
topology.py,topology_features.py,monitors.py,learning.py,nodes.py,encoders.py,plotting.py,conversion.py,davis.py,preprocess.py,cue_reward.py,dot_simulator.py; broken links inindex.rstand the guide;conf.pytakes the version from the installed package. Docstring text only; no code changed. - API reference now includes
learning.MCC_learning,network.topology_features,environment.cue_reward,environment.dot_simulatorandanalysis.dotTrace_plotter, which were missing. - Normalizing a kernel whose weights sum to zero (for example an all-zero kernel)
in
LocalConnectionand the 1D/2D/3D convolutional and locally connected connections leaves it unchanged instead of introducing NaNs or infinities.
test/repro/test_packaging_consistency.py: checks that the Dockerfile's Python is inrequires-python, that CI tests every supported Python, thatCITATION.cffandpyproject.tomlcarry the same version, thatpublish.ymlreads that version, and that every example with--seedseeds torch unconditionally..github/workflows/docker.yml: builds the image and runstest/reproinside it when the Dockerfile,pyproject.tomlorpoetry.lockchange.examples/benchmark/bindsnet_scaling.py: the README scaling benchmark for BindsNET alone (no other simulator needed).examples/benchmark/benchmark.pyrewritten: every simulator (BindsNET CPU/GPU, BRIAN2, brian2genn, PyNEST, ANNarchy CPU/GPU) gets the network the README describes, each in its own process, and a simulator that is not installed is skipped. It replaces the 2018 script, which imported anexperimentshelper package that was not released and used its own input settings per simulator: BindsNET 0-1 Hz with U(0, 1) weights, BRIAN2 15 Hz with U(0, 0.01) weights and noon_pre(so its synapses did not drive the membrane), PyNEST 60 Hz connected one-to-one, ANNarchy 50 Hz with U(0, 1) weights.annarchy.pyandgpu_annarchy.pymerged into it;plot_benchmark.pyreads its CSV.test/environment/test_gym_environment.py.
- Convolutional and locally connected connections normalize all kernels in one operation instead of a Python loop (6 to 45 times faster on CPU). Results are identical on CPU; on CUDA, float32 kernels of 81 or more weights can differ by float32 rounding (about 1e-7) because the sum is taken in a different order.
- CI: one test workflow (
python-app.yml: jobbuildon Python 3.13 plus a 3.11/3.12 matrix, Poetry 2.4.3 with dependency caching, superseded runs cancelled);pythonpackage.ymlremoved (it ran on every push to every branch and itsblack .step reformatted instead of checking).black.ymlchecks with the black version frompoetry.lockinstead of the floatingpsf/black@stable. - Dependabot also updates the Dockerfile base image.
- Imports sorted with isort (settings already in
pyproject.toml, never applied; 20 files, import order only).black.ymland the pre-commit hook now also check isort. - Removed from git: 59 TensorBoard event files under
logs/(test output;logs/*was already in.gitignore),.vscode/launch.json(a local interpreter path) anddocs/Makefile.old,docs/make.bat.old. - Added
.pre-commit-config.yaml(black from Poetry);CONTRIBUTING.mdalready told contributors to install pre-commit, but there was no configuration. [tool.black] target-versionispy311-py313(waspy38); no file changes.- README: dead link to Markram et al. (1997) replaced with its DOI; RL example named correctly (Breakout, not Space Invaders); OpenAI gym text replaced (Gymnasium and ale-py install with BindsNET); benchmark marked as from the 2018 paper; PyPI badge refreshes hourly.
First PyPI upload since 0.2.7. It is built from the master branch on this date, not
from the GitHub tag 0.3.4, so pip install bindsnet==0.3.4 contains everything in
this section in addition to the tag. The Zenodo archive
10.5281/zenodo.20695116 is the tag only.
Results can differ between the two: see the MCC_learning.PostPre entry under Changed.
- Published to PyPI by
.github/workflows/publish.yml(PyPI trusted publishing; runs when a GitHub Release is published, or by hand). - Removed install requirements that no module in
bindsnet/orexamples/imports:Cython,scikit-build,foolbox,numba. torchis now>=2.14,<3andtorchvision>=0.29,<1instead of exact pins;poetry.lockstill pins the tested versions (torch 2.14.0, torchvision 0.29.0).- README:
pip install bindsnet, PyPI badge, and absolute links and logo URL so the PyPI project page renders. pyproject.tomlpackage metadata moved from[tool.poetry]to the standard[project]table (Poetry 2 deprecates the old one);[tool.poetry]now only routes torch/torchvision to the CUDA 13.0 wheel index.poetry.lockresolves to the same packages and versions. Build backendpoetry-core>=2.0; the unusedsetup.pyis removed.- Poetry 2.4.3 in CI (was 2.1.2) and in
CONTRIBUTING.md(said 1.1.8;poetry shellreplaced bypoetry env activate, which Poetry 2 uses). Dockerfilerewritten. The old one could not build: its CUDA 11.1 base image, theget-poetry.pyinstaller and the.python-versionfile it copied no longer exist. The new one usespython:3.13-slim, Poetry 2.4.3 andpoetry.lock. The README no longer links the Docker Hub imagehqkhan/bindsnet(last updated 2019-01-28).- Remaining links to the old
Hananel-Hazan/bindsnetrepository point toBindsNET/bindsnet.
test_perf_equivalence.py: the batch-1 checks of the fusedaddmm_STDP update required bit-for-bit equality with the un-fused formula. That holds on some CPUs and not on GitHub's CI runners, where 5 tests failed. They now accept float32 rounding (the tolerance already used for batch>1) and warn with the size of any difference.
- Reproducibility/transparency docs:
DATA.md(dataset & stimulus declaration),REPRODUCING.md(model→script→command→seed map), and adocs/source/models_spec.rstneural-model specification page. CITATION.cffwith the paper citation and the Zenodo software DOI.CHANGELOG.md.examples/breakout/README.mddocumenting thetrained_shallow_ANN.ptprovenance.
bindsnet.learning.DiehlAndCookandbindsnet.learning.MCC_learning.DiehlAndCook: the post-spike-only STDP of Diehl & Cook (2015), Sect. 2.3,dw = eta (x_pre - x_tar)(w_max - w)^mu(keyword argumentsx_tar,mu).DiehlAndCook2015(learning_rule=..., learning_rule_kwargs=...)selects it; the model's default staysPostPreso published results are unchanged.- Multicompartment
Weightfeatures forward extra keyword arguments to their learning rule. bindsnet/learning/README.md: rules, source papers, equation numbers, tests and pitfalls (moved from the top-level README).
MCC_learning.PostPreno longer multiplies its update by the simulation stepdt; like the classicPostPreand Morrison et al. (2008) eqs. 13-14 it is a per-spike increment. Identical atdt = 1; at other steps the effective learning rate is nownuinstead ofnu * dt.- README Python requirement aligned to
>=3.11,<3.14; added a reproducible-install note. pyproject.tomlversion bumped to 0.3.4 to match the released tag.- Performance pass on the per-timestep hot paths (numerics unchanged; every item
is pinned by
test/network/test_perf_equivalence.py, and a seeded old-vs-new comparison of 59 networks was bit-identical except three batch>1 weight matrices that differ by one float32 rounding step):PostPre/Hebbianon denseConnectionandMulticompartmentConnectionapply the STDP update with one fusedaddmm_instead of materialising the[batch, source.n, target.n]outer product (dense 784->1000 STDP, batch 16, 250 steps on CPU: 9.6 s -> 0.42 s; Diehl & Cook 784->400, batch 1: 1.6 s -> 0.26 s).LearningRule.updateno longer multiplies the whole weight matrix by1.0every step when no weight decay is configured.LocalConnection1D/2D/3Dlearning rules scale rows directly instead of building an[n, n]identity matrix per step (64-filter local connection on GPU: 61 MiB -> 2.8 MiB of per-step temporaries).MSTDP/MSTDPETkeep their post-synaptic trace as a[batch, n, 1]vector instead of a diagonal matrix.MSTDP/MSTDPETcacheexp(-dt / tc)and the default learning-rate tensors instead of recomputing / re-copying them to the device each step.- Neuron models update
v,refrac_count,theta,x, ... in place with the same operations in the same order, avoiding aModule.__setattr__round-trip per assignment per step. rank_orderencoding is vectorised.
- Benchmark script for the above:
examples/benchmark/hot_path_bench.py. - Learning rules validated against their source papers, with the equations cited in
docs/source/models_spec.rstand pinned bytest/network/test_learning_rule_specs.py:PostPre/WeightDependentPostPre/Hebbianagainst Morrison, Diesmann & Gerstner (2008) eqs. (11)-(14);MSTDP/MSTDPETagainst Florian (2007) eqs. (3.9)-(3.12) and (2.7)-(2.8) (equation numbers added totest_mstdp_florian.py);Rmaxagainst Vasilaki et al. (2009) eqs. (7), (8), (13). TheMCC_learningPostPre/Hebbianare checked to match the classic rules. - Docstrings corrected:
Rmaxtc_climits were stated backwards (0is the strict policy-gradient rule,infthe naive Hebbian rule, Vasilaki et al. eq. 8); theMSTDP/MSTDPETzero_lagcomments called the un-lagged variant "exact Florian", whereas the default one-step lag is Florian's discrete-time eq. (3.9).
network.run(clamp=...)/unclampare now applied insideNodes.forwardbefore the spike trace is updated, so a forced spike leaves a trace (and a suppressed one does not). Previously the clamp was applied after the trace update, so clamped spikes entered the same-step potentiation term of STDP rules but never the trace used by later depression terms (affectedexamples/mnist/supervised_mnist.py). Pinned byTestClampEntersTracesand the clamp-driven STDP window test.network.to(device)crashed on anyMulticompartmentConnection(used byDiehlAndCook2015) with_apply() takes 2 positional arguments but 3 were given;AbstractMulticompartmentConnection._applynow acceptsrecurse.Network.clone()was broken outright: it calledtorch.loadwithoutweights_only=False, so it raisedUnpicklingErrorunder PyTorch 2.6+, which changed that default toTrue. It had no test and no caller in the tree, so the breakage went unnoticed. Pinned byTestNetwork.test_clone.Network.save()calledtorch.serialization.add_safe_globals([self])with a network instance where PyTorch expects a class. It did nothing useful and corrupted PyTorch's safe-globals registry, so any later load in the same process failed with'Network' object has no attribute '__qualname__'. Removed. Pinned byTestNetwork.test_clone_after_save.bindsnet.conversion.ann_to_snnanddata_based_normalizationwere broken when given a path instead of atorch.nn.Module, for the same PyTorch 2.6 reason asNetwork.clone(). Only the in-memory form was tested. Pinned bytest_conversion_from_pathandtest_data_based_normalization_from_path.
- Documented that loading a saved network runs code.
bindsnet.network.load,bindsnet.conversion.ann_to_snnandbindsnet.conversion.data_based_normalizationread Python pickle files viatorch.load, so a file from an untrusted source can execute arbitrary code on load. This is the standard behaviour oftorch.loadacross the PyTorch ecosystem and is not a defect specific to BindsNET, but it was undocumented. Added warnings to each function's docstring and a "Loading saved networks and models" section toSECURITY.md. bindsnet.network.loadgained aweights_onlyparameter, passed through totorch.load. It defaults toFalse, which is required to read files written byNetwork.save(those store the whole network object, not a tensor state dict), so behaviour is unchanged.weights_only=Truerefuses code execution and is usable only for files holding plain tensors.SpokenMNISTnow reads its processed-data cache withweights_only=True. That cache holds only tensors, so refusing code execution there costs nothing. Pinned bytest/datasets/test_cache_serialization.py.- Reported by Gavin Branaa gbranaa4@gmail.com, who also prompted the three
torch.loadfixes listed under Fixed above. Thank you.
Archived on Zenodo — concept DOI 10.5281/zenodo.20695115, version DOI 10.5281/zenodo.20695116.
- Sparse-tensor support for additional learning rules (plus a batch dimension and docs
for
sparse=True). - Validation tests for the reward-modulated learning rules
MSTDPandMSTDPET. - Regression test for a preallocated
Monitorshort-run bug (PR #761). - Read the Docs configuration for documentation builds.
assign_labels/ evaluation: handle abstention for inactive samples, mark never-firing neurons with-1, and accuracy/performance improvements.- CI: dropped Python 3.10 (project requires
>=3.11); upgraded GitHub Actions; test on Python 3.11/3.12/3.13. - Routine dependency updates via Poetry.
bernoulli_loadernow honors themax_probkwarg (PR #743).- Bug with preallocated buffers and
torch.cat. torch.savecompatibility for PyTorch 2.6.0.- Python 3.13 support / tests.
eth_mnistexample.
Baseline for this changelog. See the releases page for the history of 0.1.x–0.3.3.