Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a97b1e9
Merge pull request #750 from BindsNET:master
Hananel-Hazan Apr 17, 2026
8ce7d48
poetry update
Hananel-Hazan Apr 24, 2026
86a9fe3
poetry update
Hananel-Hazan Apr 27, 2026
a64be52
poetry update
Hananel-Hazan Apr 29, 2026
ac4afcf
poetry update
Hananel-Hazan May 1, 2026
cd5e14c
poetry update
Hananel-Hazan May 3, 2026
a301fde
poetry update
Hananel-Hazan May 7, 2026
95108a8
poetry update
Hananel-Hazan May 14, 2026
68076a1
Merge branch 'hananel' of https://github.com/BindsNET/bindsnet into h…
Hananel-Hazan May 14, 2026
152ddec
Merge branch 'master' into hananel
Hananel-Hazan May 14, 2026
71fbe2b
poetry update
Hananel-Hazan May 14, 2026
1a04631
poetry update
Hananel-Hazan May 18, 2026
1809413
poetry update
Hananel-Hazan May 24, 2026
cc3c275
poetry update
Hananel-Hazan May 31, 2026
57d1251
poetry update
Hananel-Hazan Jun 1, 2026
e38ed72
poetry update
Hananel-Hazan Jun 3, 2026
70d389f
poetry update
Hananel-Hazan Jun 5, 2026
1090e30
poetry update
Hananel-Hazan Jun 12, 2026
426a8de
Add validation tests for reward-modulated learning rules MSTDP and MS…
Hananel-Hazan Jun 14, 2026
f0ba78a
Handle neurons with no firing activity in assign_labels by marking th…
Hananel-Hazan Jun 14, 2026
5070302
Merge origin/master into hananel
Copilot Jun 14, 2026
101cd23
Stop tracking .pytest_cache and .claude tooling artifacts
Hananel-Hazan Jun 14, 2026
be8e84d
Improve assign_labels accuracy and performance
Machuka Feb 11, 2026
85c533d
Reverted variable names to original
Machuka Feb 11, 2026
8a5923b
Reverted accidental removal of docstring
Machuka Feb 11, 2026
eeb7e27
Improve evaluation function with noise for assignments
Machuka Feb 18, 2026
09d9878
Enhance label assignment functions to handle abstention for inactive …
Hananel-Hazan Jun 14, 2026
516a2d5
Merge branch 'master' into hananel
Hananel-Hazan Jun 14, 2026
056dc4d
black and pip updates
Hananel-Hazan Jun 14, 2026
8371707
Refactor assign_labels function to remove redundant noise handling an…
Hananel-Hazan Jun 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ dist/*
logs/*
.pytest_cache/*
.vscode/*
.claude/
data/*
4 changes: 0 additions & 4 deletions .pytest_cache/CACHEDIR.TAG

This file was deleted.

8 changes: 0 additions & 8 deletions .pytest_cache/README.md

This file was deleted.

1 change: 0 additions & 1 deletion .pytest_cache/v/cache/lastfailed

This file was deleted.

23 changes: 0 additions & 23 deletions .pytest_cache/v/cache/nodeids

This file was deleted.

1 change: 0 additions & 1 deletion .pytest_cache/v/cache/stepwise

This file was deleted.

46 changes: 29 additions & 17 deletions bindsnet/evaluation/evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ def assign_labels(
``assign_labels()`` call.
:param alpha: Rate of decay of label assignments.
:return: Tuple of class assignments, per-class spike proportions, and per-class
firing rates.
firing rates. Neurons that never fired are assigned ``-1`` (unassigned) so
they do not bias predictions toward class ``0``.
"""

n_neurons = spikes.size(2)
Expand All @@ -35,10 +36,10 @@ def assign_labels(

# Sum over time dimension (spike ordering doesn't matter).
spikes = spikes.sum(1)

for i in range(n_labels):
# Create mask.
mask = (labels == i)
mask = labels == i
# Count the number of samples with this label.
n_labeled = mask.sum().float()

Expand All @@ -50,24 +51,23 @@ def assign_labels(

# Compute proportions of spike activity per class.
total_activity = rates.sum(1, keepdim=True)
proportions = torch.where(total_activity > 0, rates / total_activity, torch.zeros_like(rates))
proportions = torch.where(
total_activity > 0, rates / total_activity, torch.zeros_like(rates)
)

# Noise for random tie breaking.
eps = 1e-6 # Small enough not to distort real decisions
eps = 1e-6 # Small enough not to distort real decisions
noise = eps * torch.randn_like(proportions)

# Neuron assignments are the labels they fire most for.
assignments = torch.argmax(proportions + noise, dim=1)

# Uniform assignment for silent neurons
silent_mask = total_activity.squeeze() == 0
n_silent = silent_mask.sum()
# Neurons that never fired have no class preference; mark them with -1 so
# they are masked out of downstream voting (issue #736: silent neurons must
# not default to a vote for class 0). Downstream schemes only match
# ``assignments == i`` for i >= 0, so -1 neurons are excluded.
assignments[rates.sum(1) == 0] = -1

if n_silent > 0:
assignments[silent_mask] = torch.randint(
0, n_labels, (n_silent,), device=spikes.device
)

return assignments, proportions, rates


Expand Down Expand Up @@ -118,7 +118,9 @@ def all_activity(
:param assignments: A vector of shape ``(n_neurons,)`` of neuron label assignments.
:param n_labels: The number of target labels in the data.
:return: Predictions tensor of shape ``(n_samples,)`` resulting from the "all
activity" classification scheme.
activity" classification scheme. Samples that elicit no activity from any
assigned neuron are predicted as ``-1`` (abstain) rather than defaulting to
class ``0``.
"""
n_samples = spikes.size(0)

Expand All @@ -140,7 +142,12 @@ def all_activity(
rates[:, i] = torch.sum(spikes[:, indices], 1) / n_assigns

# Predictions are arg-max of layer-wise firing rates.
return torch.sort(rates, dim=1, descending=True)[1][:, 0]
predictions = torch.sort(rates, dim=1, descending=True)[1][:, 0]

# Abstain (-1) on samples with no activity, avoiding a biased vote for class 0.
predictions[rates.sum(1) == 0] = -1

return predictions


def proportion_weighting(
Expand All @@ -161,7 +168,9 @@ def proportion_weighting(
proportions of neuron spiking activity.
:param n_labels: The number of target labels in the data.
:return: Predictions tensor of shape ``(n_samples,)`` resulting from the "proportion
weighting" classification scheme.
weighting" classification scheme. Samples that elicit no weighted activity from
any assigned neuron are predicted as ``-1`` (abstain) rather than defaulting to
class ``0``.
"""
n_samples = spikes.size(0)

Expand All @@ -187,6 +196,9 @@ def proportion_weighting(
# Predictions are arg-max of layer-wise firing rates.
predictions = torch.sort(rates, dim=1, descending=True)[1][:, 0]

# Abstain (-1) on samples with no activity, avoiding a biased vote for class 0.
predictions[rates.sum(1) == 0] = -1

return predictions


Expand Down
Loading
Loading