Skip to content

Commit 677d196

Browse files
committed
Sparse compute
1 parent 2428476 commit 677d196

1 file changed

Lines changed: 23 additions & 1 deletion

File tree

‎bindsnet/network/topology.py‎

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ def __init__(
414414
manual_update: bool = False,
415415
traces: bool = False,
416416
compute_dtype: Optional[torch.dtype] = None,
417+
sparse_compute: bool = False,
417418
**kwargs,
418419
) -> None:
419420
# language=rst
@@ -429,6 +430,8 @@ def __init__(
429430
:param traces: Set to :code:`True` to record history of connection activity (for monitors)
430431
:param compute_dtype: Optional low-precision dtype (e.g. ``torch.bfloat16``)
431432
for the matmul fast path only; ``None`` (default) keeps full ``float32``.
433+
:param sparse_compute: Set to :code:`True` to compute sparse activity (<~20% neurons active)
434+
efficiently
432435
"""
433436

434437
super().__init__(source, target, device, pipeline, **kwargs)
@@ -440,6 +443,7 @@ def __init__(
440443
self._w_eff = None # 'Folded' weight matrix for faster computing
441444
self._has_learning = None
442445
self.compute_dtype = compute_dtype
446+
self.sparse_compute = sparse_compute
443447

444448
def _pipeline_has_learning(self) -> bool:
445449
# language=rst
@@ -480,6 +484,20 @@ def _folded_weight(self) -> Optional[torch.Tensor]:
480484
self._w_eff = w_eff
481485
return w_eff
482486

487+
def _sparse_matmul(self, s_flat: torch.Tensor, w_eff: torch.Tensor) -> torch.Tensor:
488+
# language=rst
489+
"""
490+
Activity-sparse form of ``s_flat @ w_eff``. Read only
491+
the rows of ``w_eff`` for source neurons that spiked this step. Numerically
492+
identical to the dense matmul. More efficient when few source neurons are active.
493+
"""
494+
active = s_flat.any(dim=0).nonzero(as_tuple=False).squeeze(1)
495+
if active.numel() == 0:
496+
return torch.zeros(
497+
s_flat.size(0), self.target.n, device=w_eff.device, dtype=w_eff.dtype
498+
)
499+
return s_flat[:, active].to(w_eff.dtype) @ w_eff.index_select(0, active)
500+
483501
def compute(self, s: torch.Tensor) -> torch.Tensor:
484502
# language=rst
485503
"""
@@ -493,7 +511,11 @@ def compute(self, s: torch.Tensor) -> torch.Tensor:
493511
# Fast path: when the whole pipeline folds to a static elementwise-multiply
494512
w_eff = self._folded_weight()
495513
if w_eff is not None:
496-
out_signal = s.view(s.size(0), self.source.n).to(w_eff.dtype) @ w_eff
514+
s_flat = s.view(s.size(0), self.source.n)
515+
if self.sparse_compute:
516+
out_signal = self._sparse_matmul(s_flat, w_eff)
517+
else:
518+
out_signal = s_flat.to(w_eff.dtype) @ w_eff
497519
if out_signal.dtype != torch.float32:
498520
out_signal = out_signal.float()
499521
if self.traces:

0 commit comments

Comments
 (0)