Skip to content

Commit 00d870d

Browse files
Merge pull request #766 from BindsNET/neuroeval/wo-04-05-citation-version
NeuroEval reproducibility/transparency improvements (#765)
2 parents e120b08 + 09f4577 commit 00d870d

16 files changed

Lines changed: 680 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
# Changelog
2+
3+
All notable changes to BindsNET are documented here. The format is based on
4+
[Keep a Changelog](https://keepachangelog.com/). For releases prior to the entries below,
5+
see the [GitHub releases / tags](https://github.com/BindsNET/bindsnet/releases).
6+
7+
## [Unreleased]
8+
9+
### Added
10+
- Reproducibility/transparency docs: `DATA.md` (dataset & stimulus declaration),
11+
`REPRODUCING.md` (model→script→command→seed map), and a
12+
`docs/source/models_spec.rst` neural-model specification page.
13+
- `CITATION.cff` with the paper citation and the Zenodo software DOI.
14+
- `CHANGELOG.md`.
15+
- `examples/breakout/README.md` documenting the `trained_shallow_ANN.pt` provenance.
16+
17+
### Changed
18+
- README Python requirement aligned to `>=3.11,<3.14`; added a reproducible-install note.
19+
- `pyproject.toml` version bumped to 0.3.4 to match the released tag.
20+
21+
## [0.3.4] - 2026-06-15
22+
23+
Archived on Zenodo — concept DOI [10.5281/zenodo.20695115](https://doi.org/10.5281/zenodo.20695115),
24+
version DOI [10.5281/zenodo.20695116](https://doi.org/10.5281/zenodo.20695116).
25+
26+
### Added
27+
- Sparse-tensor support for additional learning rules (plus a batch dimension and docs
28+
for `sparse=True`).
29+
- Validation tests for the reward-modulated learning rules `MSTDP` and `MSTDPET`.
30+
- Regression test for a preallocated `Monitor` short-run bug (PR #761).
31+
- Read the Docs configuration for documentation builds.
32+
33+
### Changed
34+
- `assign_labels` / evaluation: handle abstention for inactive samples, mark
35+
never-firing neurons with `-1`, and accuracy/performance improvements.
36+
- CI: dropped Python 3.10 (project requires `>=3.11`); upgraded GitHub Actions; test on
37+
Python 3.11/3.12/3.13.
38+
- Routine dependency updates via Poetry.
39+
40+
### Fixed
41+
- `bernoulli_loader` now honors the `max_prob` kwarg (PR #743).
42+
- Bug with preallocated buffers and `torch.cat`.
43+
- `torch.save` compatibility for PyTorch 2.6.0.
44+
- Python 3.13 support / tests.
45+
- `eth_mnist` example.
46+
47+
## [0.3.3] - 2024-10-18
48+
49+
Baseline for this changelog. See the
50+
[releases page](https://github.com/BindsNET/bindsnet/releases) for the history of
51+
0.1.x–0.3.3.

CITATION.cff

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
cff-version: 1.2.0
2+
message: "If you use BindsNET, please cite the article below."
3+
title: "BindsNET: A Machine Learning-Oriented Spiking Neural Networks Library in Python"
4+
type: software
5+
version: 0.3.4
6+
license: AGPL-3.0-only
7+
doi: 10.5281/zenodo.20695115
8+
repository-code: "https://github.com/BindsNET/bindsnet"
9+
url: "https://bindsnet-docs.readthedocs.io/"
10+
keywords:
11+
- spiking
12+
- neural
13+
- networks
14+
- pytorch
15+
authors:
16+
- family-names: Hazan
17+
given-names: Hananel
18+
- family-names: Saunders
19+
given-names: Daniel J.
20+
- family-names: Khan
21+
given-names: Hassaan
22+
- family-names: Patel
23+
given-names: Devdhar
24+
- family-names: Sanghavi
25+
given-names: Darpan T.
26+
- family-names: Siegelmann
27+
given-names: Hava T.
28+
- family-names: Kozma
29+
given-names: Robert
30+
identifiers:
31+
- type: doi
32+
value: 10.5281/zenodo.20695115
33+
description: Concept DOI (resolves to the latest archived release)
34+
- type: doi
35+
value: 10.5281/zenodo.20695116
36+
description: Archived software release v0.3.4
37+
preferred-citation:
38+
type: article
39+
title: "BindsNET: A Machine Learning-Oriented Spiking Neural Networks Library in Python"
40+
doi: 10.3389/fninf.2018.00089
41+
url: "https://www.frontiersin.org/article/10.3389/fninf.2018.00089"
42+
journal: "Frontiers in Neuroinformatics"
43+
volume: 12
44+
year: 2018
45+
authors:
46+
- family-names: Hazan
47+
given-names: Hananel
48+
- family-names: Saunders
49+
given-names: Daniel J.
50+
- family-names: Khan
51+
given-names: Hassaan
52+
- family-names: Patel
53+
given-names: Devdhar
54+
- family-names: Sanghavi
55+
given-names: Darpan T.
56+
- family-names: Siegelmann
57+
given-names: Hava T.
58+
- family-names: Kozma
59+
given-names: Robert

CONTRIBUTING.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ Run the tests, they all should pass
2626
poetry run pytest
2727
```
2828

29+
Notable changes are recorded in [`CHANGELOG.md`](CHANGELOG.md); please add an entry to the
30+
`Unreleased` section in your pull request.
31+
2932
All development should take place on a branch separate from master. To create a branch, issue
3033

3134
```shell

DATA.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Datasets & Stimuli used in BindsNET
2+
3+
BindsNET ships **no** third-party datasets. Its dataset loaders fetch data from the
4+
upstream sources declared below; all licenses are the upstream providers' and BindsNET
5+
does not redistribute the data. This file declares every dataset and synthetic stimulus
6+
referenced by the shipped examples and benchmarks, plus the additional dataset loaders
7+
the library provides.
8+
9+
> Licenses below are pointers to the upstream source, not assertions by BindsNET.
10+
> Confirm the current license at the source before using a dataset in your own work.
11+
12+
---
13+
14+
## 1. Datasets used by the shipped examples
15+
16+
### MNIST
17+
- **Loader:** `from bindsnet.datasets import MNIST` — a thin wrapper over
18+
`torchvision.datasets.MNIST` (`bindsnet/datasets/torchvision_wrapper.py`).
19+
- **Upstream source:** torchvision → http://yann.lecun.com/exdb/mnist/
20+
- **Version/snapshot:** whatever the installed `torchvision` resolves (mirror-hosted).
21+
- **Obtained by:** automatic download on first run (`download=True` in the examples).
22+
- **License:** as published by the upstream/torchvision mirror (verify upstream).
23+
- **Used in:** `examples/mnist/*.py`
24+
(e.g. `eth_mnist.py`, `batch_eth_mnist.py`, `supervised_mnist.py`, `conv_mnist.py`,
25+
`reservoir.py`, `MCC_reservoir.py`, `conv1d_MNIST.py`, `conv3d_MNIST.py`,
26+
`loc1d_mnist.py`, `loc2d_mnist.py`, `loc3d_mnist.py`, `SOM_LM-SNNs.py`).
27+
- **Preprocessing → spikes:** `transforms.ToTensor()` then scaling by `--intensity`
28+
(default 128 in `eth_mnist.py`), then rate coding via
29+
`bindsnet.encoding.PoissonEncoder(time, dt)` — pixel intensities become Poisson
30+
spike trains over `time` ms at step `dt`.
31+
32+
### Atari — Breakout (and Space Invaders)
33+
- **Loader:** `bindsnet.environment.GymEnvironment("BreakoutDeterministic-v4")`
34+
(see `examples/breakout/*.py`).
35+
- **Upstream source:** Arcade Learning Environment via `gymnasium[atari]` + `ale-py`
36+
(declared in `pyproject.toml`). ROMs are provided through the ALE/AutoROM tooling.
37+
- **Obtained by:** the Gymnasium/ALE runtime; not stored in this repo.
38+
- **License:** ALE/ROM licensing applies (verify via ale-py / AutoROM).
39+
- **Used in:** `examples/breakout/breakout.py`, `breakout_stdp.py`,
40+
`play_breakout_from_ANN.py`, `random_baseline.py`, `random_network_baseline.py`.
41+
- **Preprocessing → spikes:** Atari observations are converted to network input by the
42+
example pipelines (see each script and `bindsnet/encoding/`).
43+
- **Pretrained artifact:** `examples/breakout/trained_shallow_ANN.pt` (a Breakout
44+
Q-network transplanted into an SNN) — provenance in
45+
[examples/breakout/README.md](examples/breakout/README.md).
46+
47+
---
48+
49+
## 2. Synthetic stimuli (no external dataset)
50+
51+
### Scaling-benchmark Poisson drive
52+
Used by `examples/benchmark/benchmark.py` and reported in the README "Benchmarking"
53+
section and Hazan et al. 2018:
54+
- Population of **n** Poisson input neurons, firing rates drawn from **U(0, 100) Hz**.
55+
- Connected all-to-all to an equally sized population of LIF neurons; connection
56+
weights sampled from **N(0, 1)**.
57+
- **n** varied 250 → 10,000 in steps of 250; each run simulated **1,000 ms** at
58+
**dt = 1.0 ms**.
59+
60+
This stimulus is generated programmatically; there is no dataset to download.
61+
62+
---
63+
64+
## 3. Additional dataset loaders provided by the library
65+
66+
These loaders are part of `bindsnet.datasets` and are available to users, though not
67+
every one is exercised by a shipped example. Sources are taken directly from the loader
68+
modules.
69+
70+
| Dataset | Loader | Upstream source | Notes |
71+
|---------|--------|-----------------|-------|
72+
| Spoken MNIST (Free Spoken Digit Dataset) | `bindsnet.datasets.SpokenMNIST` (`spoken_mnist.py`) | https://github.com/Jakobovski/free-spoken-digit-dataset (downloads `master.zip`) | License per upstream repo |
73+
| ALOV300++ | `bindsnet.datasets.ALOV300` (`alov300.py`) | frames `http://isis-data.science.uva.nl/alov/alov300++_frames.zip`, GT text `http://isis-data.science.uva.nl/alov/alov300++GT_txtFiles.zip`; info `http://alov300pp.joomlafree.it/dataset-resources.html` | Visual-tracking dataset |
74+
| DAVIS 2017 | `bindsnet.datasets.Davis` (`davis.py`) | https://davischallenge.org/davis2017/code.html | Video object segmentation |
75+
| Other torchvision datasets | `create_torchvision_dataset_wrapper(...)` (`torchvision_wrapper.py`) | torchvision | Wrappers exported for CIFAR10/100, FashionMNIST, EMNIST, KMNIST, SVHN, STL10, Omniglot, VOC*, COCO*, etc. — each downloads from its torchvision-declared source |
76+
77+
---
78+
79+
## Data handling notes
80+
- Datasets download to a user-specified `root` directory (the examples typically use a
81+
local `data/` path); they are **not** committed to this repository.
82+
- BindsNET does not modify or redistribute upstream data; it applies encodings
83+
(`bindsnet/encoding/`) to turn inputs into spike trains at simulation time.
84+
- If a download URL has moved, consult the loader module in `bindsnet/datasets/` and the
85+
upstream project page listed above.

README.md

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,28 @@ This package is used as part of ongoing research on applying SNNs, machine learn
99

1010
Check out the [BindsNET examples](https://github.com/BindsNET/bindsnet/tree/master/examples) for a collection of experiments, functions for the analysis of results, plots of experiment outcomes, and more. Documentation for the package can be found [here](https://bindsnet-docs.readthedocs.io).
1111

12+
[![Build Status](https://github.com/BindsNET/bindsnet/actions/workflows/python-app.yml/badge.svg?branch=master)](https://github.com/BindsNET/bindsnet/actions/workflows/python-app.yml)
1213
[![CodeQL](https://github.com/BindsNET/bindsnet/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/BindsNET/bindsnet/actions/workflows/github-code-scanning/codeql)
1314
[![Documentation Status](https://readthedocs.org/projects/bindsnet-docs/badge/?version=latest)](https://bindsnet-docs.readthedocs.io/?badge=latest)
1415
[![Neuromorphic Computing](https://img.shields.io/badge/Collaboration_Network-Open_Neuromorphic-blue)](https://open-neuromorphic.org/neuromorphic-computing/)
16+
[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20695115.svg)](https://doi.org/10.5281/zenodo.20695115)
1517

1618
## Requirements
1719

18-
- Python >=3.9,<3.12
20+
- Python >=3.11,<3.14 (continuously tested on 3.11, 3.12, and 3.13)
1921

2022
## Setting things up
2123

24+
### Reproducible install
25+
For a byte-for-byte reproducible environment, install the pinned dependency set from
26+
the committed `poetry.lock`:
27+
28+
```
29+
poetry install
30+
```
31+
32+
Alternatively, the provided `Dockerfile` builds the full pinned stack (see *Using Docker* below).
33+
2234
## Using Pip
2335
To install the most recent stable release from the GitHub repository
2436

@@ -84,6 +96,20 @@ python -m pytest test/
8496

8597
Some tests will fail if Open AI `gym` is not installed on your machine.
8698

99+
## Datasets
100+
101+
BindsNET ships no third-party datasets; its loaders fetch them from upstream sources.
102+
Every dataset and synthetic stimulus used by the examples, benchmarks, and dataset
103+
loaders — with source, retrieval method, license pointer, and spike-encoding
104+
preprocessing — is declared in [DATA.md](DATA.md).
105+
106+
## Reproducing results
107+
108+
[REPRODUCING.md](REPRODUCING.md) maps each shipped model and published claim to its
109+
model class, example script, exact command, seed, and expected output (e.g. the
110+
Diehl & Cook 2015 MNIST replication via `examples/mnist/eth_mnist.py`, and the
111+
Hazan et al. 2018 scaling benchmark).
112+
87113
## Background
88114

89115
The simulation of biologically plausible spiking neuron dynamics can be challenging. It is typically done by solving ordinary differential equations (ODEs) which describe said dynamics. PyTorch does not explicitly support the solution of differential equations (as opposed to [`brian2`](https://github.com/brian-team/brian2), for example), but we can convert the ODEs defining the dynamics into difference equations and solve them at regular, short intervals (a `dt` on the order of 1 millisecond) as an approximation. Of course, under the hood, packages like `brian2` are doing the same thing. Doing this in [`PyTorch`](http://pytorch.org/) is exciting for a few reasons:
@@ -128,6 +154,17 @@ If you use BindsNET in your research, please cite the following [article](https:
128154
129155
```
130156

157+
### Citing the software
158+
159+
To cite a specific version of the BindsNET software, use the archived release on Zenodo.
160+
The concept DOI below always resolves to the latest version:
161+
162+
> BindsNET contributors. *BindsNET*. Zenodo. https://doi.org/10.5281/zenodo.20695115
163+
164+
(For the exact release used, cite its version DOI; e.g. v0.3.4 is
165+
[10.5281/zenodo.20695116](https://doi.org/10.5281/zenodo.20695116).) A machine-readable
166+
citation is provided in [`CITATION.cff`](CITATION.cff).
167+
131168
## Contributors
132169

133170
- Hava Siegelmann - Director of BINDS lab at UMass

REPRODUCING.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Reproducing results with BindsNET
2+
3+
This table traces each model BindsNET describes or ships back to executable code: the
4+
model class, the example script, an exact command, the seed, the expected output, and
5+
the data it needs (declared in [DATA.md](DATA.md)).
6+
7+
> **Honesty note.** Commands, defaults, seeds, and model classes below are verified
8+
> against the source. The **Expected output** cells describe *what the script reports*
9+
> and the qualitative trend; cells marked *(not measured here)* have **not** been run to
10+
> a final metric in producing this table — run the command to obtain the number for your
11+
> hardware. No accuracy/timing figure is asserted that was not measured.
12+
13+
## Model → code → command map
14+
15+
| Claim / source | Model class | Script | Command (defaults shown) | Seed | Expected output | Data |
16+
|----------------|-------------|--------|--------------------------|------|-----------------|------|
17+
| Diehl & Cook 2015 MNIST replication (DOI `10.3389/fncom.2015.00099`) | `DiehlAndCook2015` | `examples/mnist/eth_mnist.py` | `python examples/mnist/eth_mnist.py --n_neurons 100 --n_epochs 1 --time 250 --seed 0` | `--seed 0` (`torch.manual_seed`) | Prints test accuracy at end. **Measured:** all-activity **0.81**, proportion-weighting **0.82** at seed 0 with `--n_train 20000 --n_test 10000` (GPU, torch 2.6, ~7.8 h on an RTX 2070). Accuracy rises with `--n_neurons` and with the full 60000-sample train set (Diehl & Cook report up to ~95% at 6400 neurons). | MNIST |
18+
| Batched ETH MNIST | `DiehlAndCook2015` | `examples/mnist/batch_eth_mnist.py` | `python examples/mnist/batch_eth_mnist.py --n_neurons 100 --batch_size 32 --time 100 --seed 0` | `--seed 0` | Prints test accuracy; faster per-epoch via batching. *(not measured here)* | MNIST |
19+
| Supervised MNIST (label-clamped) | `DiehlAndCook2015` | `examples/mnist/supervised_mnist.py` | `python examples/mnist/supervised_mnist.py --n_neurons 100 --time 250 --intensity 32 --seed 0` | `--seed 0` | Prints test accuracy. *(not measured here)* | MNIST |
20+
| Convolutional SNN on MNIST | (in-script conv network) | `examples/mnist/conv_mnist.py` | `python examples/mnist/conv_mnist.py --time 50 --batch_size 1 --seed 0` | `--seed 0` | Prints accuracy. *(not measured here)* | MNIST |
21+
| Reservoir / liquid-state MNIST | (in-script reservoir) | `examples/mnist/reservoir.py` | `python examples/mnist/reservoir.py --n_neurons 500 --n_epochs 100 --time 250 --seed 0` | `--seed 0` | Prints accuracy after readout training. *(not measured here)* | MNIST |
22+
| Scaling benchmark (Hazan et al. 2018, DOI `10.3389/fninf.2018.00089`) | `Input` + `LIFNodes` via `Connection` | `examples/benchmark/benchmark.py` | **Not single-command** — see note below | n/a (timing study) | Runtime-vs-`n` curve; published figure is `docs/BindsNET benchmark.png` | synthetic Poisson drive (DATA.md) |
23+
| Atari Breakout (ANN→SNN demo) | trained ANN + SNN pipeline | `examples/breakout/play_breakout_from_ANN.py` | `python examples/breakout/play_breakout_from_ANN.py` | set in script | Plays Breakout from the shipped `trained_shallow_ANN.pt` | Atari Breakout (DATA.md) |
24+
25+
## Notes
26+
27+
### Determinism
28+
- Each MNIST example accepts `--seed` (default `0`) and calls `torch.manual_seed(seed)`
29+
and `torch.cuda.manual_seed_all(seed)`. Pass the same `--seed` to repeat a run.
30+
- Residual nondeterminism can come from CUDA atomic operations and first-run dataset
31+
download ordering. For stricter determinism run on CPU and, where feasible, set
32+
`torch.use_deterministic_algorithms(True)`.
33+
- An automated, seeded smoke-reproduction test
34+
(`test/repro/test_smoke_repro.py`) runs a tiny network end-to-end on CPU and asserts
35+
an exact pre-measured output, so determinism is checked continuously in CI.
36+
37+
### Scaling benchmark is a multi-simulator study
38+
`examples/benchmark/benchmark.py` compares BindsNET against **BRIAN2, PyNEST, ANNarchy,
39+
BRIAN2genn, and Nengo**, and imports those packages plus an `experiments` helper module.
40+
It is therefore **not** a single-command reproduction: it requires those external
41+
simulators installed and the benchmark harness. The published BindsNET result is the
42+
figure `docs/BindsNET benchmark.png` and the parameters in the README "Benchmarking"
43+
section (Poisson inputs U(0,100) Hz, weights N(0,1), dt = 1.0 ms, 1000 ms/run, n from
44+
250 to 10,000). A BindsNET-only timing reproduction (no external simulators) can be
45+
built from `Input` + `LIFNodes` + `Connection`.
46+
47+
### Data
48+
All datasets and synthetic stimuli these scripts use are declared in
49+
[DATA.md](DATA.md), including how they are downloaded and the spike encoding applied.

bindsnet/encoding/loaders.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def bernoulli_loader(
2626
:param float max_prob: Maximum probability of spike per Bernoulli trial.
2727
"""
2828
# Setting kwargs.
29-
max_prob = kwargs.get("dt", 1.0)
29+
max_prob = kwargs.get("max_prob", 1.0)
3030

3131
for i in range(len(data)):
3232
# Encode datum as Bernoulli spike trains.

bindsnet/network/topology.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,36 @@ def remove_pipeline(self, feature) -> None:
261261
self.pipeline.remove(feature)
262262
del self.feature_index[feature.name]
263263

264+
def _apply(self, fn):
265+
# language=rst
266+
"""
267+
Relocate pipeline features (and their learning rules) along with the connection
268+
on ``.to()`` / ``.cuda()`` / ``.cpu()``.
269+
270+
Feature tensors (e.g. ``Weight.value``) and the per-feature learning-rule state
271+
live on non-``Module`` objects in ``self.pipeline``, so they are not moved by
272+
``torch.nn.Module._apply``. Without this, ``network.to(device)`` leaves them on
273+
their original device and ``compute``/``update`` raise a cpu/cuda device
274+
mismatch. The feature value is moved in place (via ``.data``) so it stays
275+
aliased to the learning rule's cached reference.
276+
"""
277+
super()._apply(fn)
278+
for feature in self.pipeline:
279+
value = getattr(feature, "value", None)
280+
if isinstance(value, torch.Tensor):
281+
value.data = fn(value.data)
282+
self.device = value.device
283+
for attr, val in list(vars(feature).items()):
284+
if attr != "value" and isinstance(val, torch.Tensor):
285+
setattr(feature, attr, fn(val))
286+
rule = getattr(feature, "learning_rule", None)
287+
if rule is not None:
288+
for attr, val in list(vars(rule).items()):
289+
# feature_value is aliased to (the already-moved) feature.value.
290+
if attr != "feature_value" and isinstance(val, torch.Tensor):
291+
setattr(rule, attr, fn(val))
292+
return self
293+
264294

265295
class Connection(AbstractConnection):
266296
# language=rst

0 commit comments

Comments
 (0)