Spike-based alignment learning solves the weight transport problem.
The 10 matches
- [1] § Methods › Symmetrization in deep neural networks › SymmNet experiments ↔ scripts/symm_net/main_salnet.py, lines 161–216 · score 0.82 · cross entropy, RDDNet, SALNet, ConvNet, weight decay, FA
- [2] § Methods › Symmetrization in deep neural networks › SymmNet experiments ↔ symmnet/src/symmnet/conv_net.py, lines 27–149 · score 0.79 · ReLU, convolutional blocks, convolutional layer, ConvNet, FC, batch
- [3] § Results › Deep neural networks › Symmetrization in deep networks ↔ symmnet/src/symmnet/conv_net.py, lines 27–149 · score 0.75 · convolutional neural network, FC layers, convolutional layers, ConvNet, fully connected, alignment
- [4] § Methods › Neuron model ↔ spiking_sampling_network/src/neuralsampling/network.py, lines 51–80 · score 0.69 · instantaneous firing rate, refractory period, PSP kernel, matched, syn, neuron
- [5] § Methods › Symmetrization in deep neural networks › SymmNet experiments ↔ scripts/symm_net/main_salnet.py, lines 161–216 · score 0.62 · RDDNet, SALNet, ConvNet, layers, weights
- [6] § Methods › Boltzmann machines and sampling with spikes ↔ spiking_sampling_network/src/neuralsampling/network.py, lines 216–233 · score 0.54 · gradient descent, sleep phase, optimized, training, network, spikes
- [7] § Methods › Symmetrization in deep neural networks › SymmNet experiments ↔ symmnet/src/symmnet/rdd_net.py, lines 11–71 · score 0.53 · RDDNet, spiking network, external, SymmNet, layers, weights
- [8] § Methods › Spiking cortical microcircuits › Mathematical description of the model ↔ spiking_microcircuits/src/microcircuits/model.py, lines 357–378 · score 0.51 · membrane potential, basal, apical, compartments, pyramidal, voltages
- [9] § Results › Symmetrization in spiking sampling networks ↔ spiking_sampling_network/src/neuralsampling/network.py, lines 216–233 · score 0.51 · gradient descent, sleep phase, weight update, symmetric, network, trained
- [10] § Results › Symmetrization in spiking sampling networks ↔ spiking_sampling_network/src/neuralsampling/network.py, lines 133–213 · score 0.50 · binary state, weight matrix, vector, biases, PSPs, network
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Python · 446 lines · 14 KB · MIT · 4 matches
- """Neural sampling implementation (Buesing et al. 2011)."""
- import time
- from datetime import datetime
- from typing import Any, Callable, TypeAlias
- import numba
- import numpy as np
- import numpy.typing as npt
- from .utils import (
- bm_to_probs,
- distr_from_states,
- get_states_from_spikes,
- list_of_states,
- ordered_spikes_to_list,
- )
- # declare my own types here:
- StdpFunc: TypeAlias = Callable[[npt.ArrayLike], npt.ArrayLike]
- SimParams: TypeAlias = dict[str, Any]
- @numba.njit(cache=False)
- def logistic(x: float, t_ref: float) -> float:
- """Logistic activation function shifted by log(t_ref)."""
- return 1.0 / (1.0 + np.exp(-(x - np.log(t_ref))))
- @numba.vectorize(cache=False)
- def heaviside(x: float) -> float:
- """Heaviside step function: 1 if x > 0, else 0."""
- if x > 0.0:
- return 1.0
- else:
- return 0.0
- @numba.njit(cache=False)
- def rect_kernel(x: float, tau_syn: float) -> float:
- """Rectangular PSP kernel: 1 within (0, tau_syn], else 0."""
- return heaviside(x) * heaviside(-x + tau_syn)
- @numba.njit(cache=False)
- def alpha_kernel(x: float, tau_ref: float, tau_syn: float) -> float:
- """Alpha-function PSP kernel."""
- return heaviside(tau_ref / tau_syn**2 * x * np.exp(-x / tau_syn))
- @numba.njit(cache=False)
- def calc_inst_rate(
- t: int,
- last_spikes: npt.NDArray,
- bias: npt.NDArray,
- weight_mat: npt.NDArray,
- t_ref: float,
- tau_syn: float,
- psp_kernel: Callable,
- ) -> npt.NDArray:
- """Compute instantaneous firing rates for all neurons at time t.
- Args:
- t: Current time step.
- last_spikes: (N, K) array of the K most recent spike times per neuron.
- bias: Neuron bias vector.
- weight_mat: Synaptic weight matrix.
- t_ref: Refractory period.
- tau_syn: Synaptic time constant.
- psp_kernel: PSP kernel function.
- Returns:
- Array of instantaneous firing rates, one per neuron.
- """
- psps = np.sum(psp_kernel(t - last_spikes, tau_syn), axis=1)
- mem_pot = bias + np.dot(weight_mat, psps)
- inst_rate = logistic(mem_pot, t_ref)
- # to match with buesing: substract log(t_ref) from mem_pot
- # (mind the unit of t_ref!)
- return inst_rate
- @numba.njit(cache=False)
- def sim_poisson_neurons(
- t_max: int,
- psp_kernel: Callable,
- bias: npt.NDArray,
- weights: npt.NDArray,
- t_ref: float,
- tau_syn: float,
- num_last_spikes: int = 10,
- ) -> npt.NDArray:
- """Simulate Poisson spiking neurons with PSP-based interactions.
- Args:
- t_max: Simulation duration in time steps.
- psp_kernel: PSP kernel function.
- bias: Neuron bias vector.
- weights: Synaptic weight matrix.
- t_ref: Refractory period.
- tau_syn: Synaptic time constant.
- num_last_spikes: Number of past spikes tracked per neuron.
- Returns:
- Ordered spike array of (time, neuron_id) tuples.
- """
- num_neurons = len(bias)
- ordered_spikes = []
- # how many past spikes do we take into account for the calculation of the
- # PSPs
- last_spikes = np.full((num_neurons, num_last_spikes), -100_000_000)
- for t in range(t_max):
- inst_rate = calc_inst_rate(
- t, last_spikes, bias, weights, t_ref, tau_syn, psp_kernel
- )
- # probability to spike in [t, t+dt]
- random_vals = np.random.random_sample(num_neurons)
- # check if the prob is smaller than the random value
- for i in np.nonzero(random_vals < inst_rate)[0]:
- # refractory machanism:
- if last_spikes[i, -1] < t - t_ref:
- # use nest/ssn convention
- ordered_spikes.append((t, i + 1.0))
- # push back the last_spikes-stack
- last_spikes[i, :-1] = last_spikes[i, 1:]
- last_spikes[i, -1] = t
- return np.array(ordered_spikes)
- class NeuralSampler:
- """Base neural sampler with wake and sleep phase simulation.
- Args:
- init_weight: Initial weight matrix (N, N).
- init_bias: Initial bias vector (N,).
- num_visible: Number of visible neurons.
- sim_params: Dict with keys psp_kernel, t_ref, tau_syn, num_last_spikes.
- rng_seed: Random seed.
- """
- def __init__(
- self,
- init_weight: npt.NDArray,
- init_bias: npt.NDArray,
- num_visible: int,
- sim_params: SimParams,
- rng_seed: int = 424242,
- ) -> None:
- np.random.seed(rng_seed)
- assert init_weight.shape[0] == init_weight.shape[1]
- assert init_weight.shape[0] == len(init_bias)
- self.num_nrns = len(init_bias)
- self.num_vis = num_visible
- self.num_hidden = self.num_nrns - self.num_vis
- assert self.num_hidden >= 0
- self.weight = init_weight
- self.bias = init_bias
- self.psp_kernel = sim_params["psp_kernel"]
- self.t_ref = sim_params["t_ref"]
- self.tau_syn = sim_params["tau_syn"]
- self.num_last_spikes = sim_params["num_last_spikes"]
- self.rng_seed = rng_seed
- def wake_phase(self, sim_dur: int, target: npt.NDArray) -> npt.NDArray:
- """Run a clamped simulation with target clamped to visible neurons."""
- bias = np.copy(self.bias)
- bias[: self.num_vis] = (target * 2.0 - 1.0) * 10.0
- spikes = sim_poisson_neurons(
- sim_dur, self.psp_kernel, bias, self.weight, self.t_ref, self.tau_syn
- )
- return spikes
- def sleep_phase(self, sim_dur: int) -> npt.NDArray:
- """Run a free simulation for `sim_dur` steps."""
- spikes = sim_poisson_neurons(
- sim_dur, self.psp_kernel, self.bias, self.weight, self.t_ref, self.tau_syn
- )
- return spikes
- def spikes_to_states(self, spikes: npt.NDArray, sim_dur: int) -> npt.NDArray:
- """Convert a spike array to a binary state matrix."""
- t_refs = np.full(self.num_nrns, self.t_ref)
- return get_states_from_spikes(
- self.num_nrns, spikes, t_refs, self.t_ref / 2.0, sim_dur
- )
- def restrict(self, arr: npt.NDArray) -> npt.NDArray:
- """Zero out visible-visible and hidden-hidden entries of arr."""
- arr[: self.num_vis, : self.num_vis] = 0.0
- arr[self.num_vis :, self.num_vis :] = 0.0
- return arr
- def restrict_weights(self) -> None:
- """Zero out visible-visible and hidden-hidden weights in-place."""
- self.weight[: self.num_vis, : self.num_vis] = 0.0
- self.weight[self.num_vis :, self.num_vis :] = 0.0
- def clip_weights(self, max_w: float) -> None:
- """Clip all weights to [-max_w, max_w] in-place."""
- self.weight[self.weight > max_w] = max_w
- self.weight[self.weight < -max_w] = -max_w
- def clip_bias(self, max_b: float) -> None:
- """Clip all biases to [-max_b, max_b] in-place."""
- self.bias[self.bias > max_b] = max_b
- self.bias[self.bias < -max_b] = -max_b
- class NeuralSamplerFullyConnected(NeuralSampler):
- """Fully connected neural sampler trained with STDP-based gradient descent.
- Args:
- init_weight: Initial weight matrix.
- init_bias: Initial bias vector.
- target_weight: Target BM weight matrix (used to compute target distribution).
- target_bias: Target BM bias vector.
- sim_params: Simulation parameters dict.
- dur_sleep: Sleep phase duration.
- optimizer_bias: Bias update optimizer.
- optimizer_weight: Weight update optimizer.
- optimizer_symm: Symmetrization optimizer (optional).
- max_w: Weight clip bound.
- max_b: Bias clip bound.
- rng_seed: Random seed.
- weight_decay: Per-step weight decay fraction.
- """
- def __init__(
- self,
- init_weight: npt.NDArray,
- init_bias: npt.NDArray,
- target_weight: npt.NDArray,
- target_bias: npt.NDArray,
- sim_params: SimParams,
- dur_sleep: int,
- optimizer_bias: Callable,
- optimizer_weight: Callable,
- optimizer_symm: Callable | None = None,
- max_w: float = 2.0,
- max_b: float = 2.0,
- rng_seed: int = 424242,
- weight_decay: float | npt.NDArray = 0.0,
- ):
- """Initialize sampler and compute target distribution analytically."""
- super().__init__(init_weight, init_bias, 0, sim_params, rng_seed=rng_seed)
- self.dur = dur_sleep
- self.optimizer_bias = optimizer_bias
- self.optimizer_weight = optimizer_weight
- self.optimizer_symm = optimizer_symm
- self.los = list_of_states(self.num_nrns)
- self.max_w = max_w
- self.max_b = max_b
- self.validation = False
- self.weight_decay = 1.0 - weight_decay
- self.target_distr, self.los, self.coact = bm_to_probs(
- target_weight, target_bias
- )
- self.marginals = np.diagonal(self.coact).copy()
- np.fill_diagonal(self.coact, 0.0)
- print("theoretical distribution", self.target_distr, flush=True)
- print("theoretical coactivation \n", self.coact, flush=True)
- print("theoretical marginals", self.marginals, flush=True)
- print(self.target_distr.shape)
- def spike_rates(self, spikes: npt.NDArray, dur: float | int) -> npt.NDArray:
- """Compute mean spike rates for all neurons.
- Args:
- spikes: Ordered spike array.
- dur: Simulation duration.
- Returns:
- Rate array, one per neuron.
- """
- rates = []
- list_of_spikes = ordered_spikes_to_list(
- spikes, list(range(1, self.num_nrns + 1))
- )
- for spks in list_of_spikes:
- rates.append(len(spks) / dur * self.t_ref)
- return np.array(rates)
- def sleep_phase(
- self, stdp_rule: StdpFunc, sal_rule: StdpFunc | None = None
- ) -> tuple[npt.NDArray, npt.NDArray, npt.NDArray, npt.NDArray | None]:
- """Run a sleep phase and return STDP, rates, sampled distribution, and SAL.
- Args:
- stdp_rule: STDP rule callable.
- sal_rule: SAL rule callable (optional).
- Returns:
- Tuple of (stdp, rates, sampled_distr, stdp_sal).
- """
- spikes = super().sleep_phase(self.dur)
- states = self.spikes_to_states(spikes, self.dur)
- sampled_distr = distr_from_states(states, self.los)
- rates = self.spike_rates(spikes, self.dur)
- stdp = stdp_rule(spikes) / self.dur * self.t_ref
- if sal_rule is not None:
- stdp_sal = sal_rule(spikes) / self.dur * self.t_ref
- else:
- stdp_sal = None
- return stdp, rates, sampled_distr, stdp_sal
- def training_iteration(
- self,
- stdp_rule: StdpFunc,
- sal_rule: StdpFunc | None = None,
- ) -> dict:
- """Run one training step: sleep phase → gradient → weight/bias update.
- Args:
- stdp_rule: STDP rule callable.
- sal_rule: SAL rule callable (optional).
- Returns:
- Dict with sampled_distr, weights, biases, sleep_stdp, sal_stdp, target_distr.
- """
- sleep_stdp, sleep_rates, sampled_distr, stdp_sal = self.sleep_phase(
- stdp_rule=stdp_rule,
- sal_rule=sal_rule,
- )
- corrected_coact = (
- self.coact * stdp_rule.noised_correlation_factors() / self.t_ref
- )
- grad_weight = corrected_coact - sleep_stdp
- grad_bias = self.marginals - sleep_rates
- delta_weight = self.optimizer_weight.update(grad_weight)
- delta_bias = self.optimizer_bias.update(grad_bias)
- # update params:
- self.bias = self.bias + delta_bias
- self.weight = self.weight + delta_weight
- # weight decay
- self.weight = self.weight * self.weight_decay
- # optional: symmetrization with sal
- if sal_rule is not None:
- delta_sal = self.optimizer_symm(stdp_sal)
- self.weight = self.weight + delta_sal
- # impose RBM restrictions:
- self.clip_weights(self.max_w)
- self.clip_bias(self.max_b)
- np.fill_diagonal(self.weight, 0.0)
- res = {
- "sampled_distr": sampled_distr,
- "weights": np.copy(self.weight),
- "biases": np.copy(self.bias),
- "sleep_stdp": sleep_stdp,
- "sal_stdp": stdp_sal,
- "target_distr": self.target_distr,
- }
- return res
- def train(
- self,
- num_iter: int,
- stdp_rule: StdpFunc,
- stdp_rule_symm: StdpFunc | None = None,
- callback: Callable | None = None,
- validation_step: int = 1,
- validation_factor: int = 10,
- ) -> None:
- """Train for `num_iter` iterations, calling `callback` after each step.
- Args:
- num_iter: Number of training iterations.
- stdp_rule: STDP rule callable.
- stdp_rule_symm: SAL rule callable (optional).
- callback: Called with (result_dict, step); return True to stop early.
- validation_step: Run a longer validation every this many steps.
- validation_factor: Multiply sleep duration during validation.
- """
- for step in range(num_iter):
- # every validation_step-th iteration change the sleep duration,
- # but not if validation_step == 1
- if step % validation_step == 0 and (validation_step - 1):
- tick = time.time()
- self.dur *= validation_factor
- print(
- f"TRAINING ITERATION NO. {step} -- {datetime.now().ctime()}",
- flush=True,
- )
- print("validation phase!")
- self.validation = True
- res = self.training_iteration(
- stdp_rule,
- sal_rule=stdp_rule_symm,
- )
- quit = False
- if callback is not None:
- quit = callback(res, step)
- if step % validation_step == 0 and (validation_step - 1):
- tock = time.time()
- self.dur /= validation_factor
- self.validation = False
- print(f"Time for iteration: {tock - tick}", flush=True)
- if quit:
- print(
- "Recieved signal to stop training from callback function!",
- flush=True,
- )
- break
- class GradDescent(object):
- """Gradient descent optimizer: update = lr * grad."""
- def __init__(self, lr: float) -> None:
- """Set learning rate."""
- self.lr = lr
- def update(self, grad: npt.NDArray) -> npt.NDArray:
- """Return lr * grad."""
- return self.lr * grad
- def __call__(self, grad: npt.NDArray) -> npt.NDArray:
- """Alias for `update`."""
- return self.update(grad)
network.py at commit f379585, under MIT · at the source
Overview
- Department of Physiology, Bern University,Bern, Switzerland
- Kirchhoff-Institut für Physik, Ruprecht-Karls-Universität Heidelberg,Heidelberg, Germany
- Neural Computation Unit, Okinawa Institute of Science and Technology,Okinawa, Japan
Abstract
Learning algorithms are often subject to symmetry constraints that are difficult to reconcile with local computation in physical neuronal networks. For example, contrastive Hebbian learning typically assumes symmetric connectivity, while error backpropagation requires knowledge of the forward weights in the backward pass. To solve this weight transport problem, we introduce spike-based alignment learning (SAL), a synapse-local learning rule that harnesses noise for weight alignment. This rule can operate simultaneously with other functional learning rules to maintain the necessary symmetry throughout learning and thereby ensure the correct local representation of gradients. SAL implicitly alleviates any discrepancy arising from the neuron and synapse variability that is ubiquitous in analog substrates, whether biological or artificial. We demonstrate the efficacy of our mechanism using different network models for spiking Bayesian inference and bio-plausible error backpropagation, and benchmark it in a deep learning computer vision task.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repositories
Its files are read in the Code ↔ Paper reader above, with 10 matches between paragraphs and lines of code.
zalandoresearch/fashion-mnist
b2617bb6d3ffa2e429640350f613e3291e10b141, 21 March 2022Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
14 files
- app.py, Python, 20 lines
- benchmark/
__init__.py , Python, 1 line - benchmark/
convnet.py , Python, 149 lines - benchmark/
runner.py , Python, 207 lines - configs.py, Python, 90 lines
- static/
js/ , JavaScript, 118 linesvue-binding.js - utils/
__init__.py , Python, 1 line - utils/
argparser.py , Python, 39 lines - utils/
helper.py , Python, 84 lines - utils/
mnist_reader.py , Python, 22 lines - visualization/
__init__.py , Python, 1 line - visualization/
project_zalando.py , Python, 43 lines - LICENSE, License, 7 lines
- README.md, Text, 283 lines
unibe-cns/sal-code
f37958586af3f8918da47c9dce2d10c756f1c024, 10 September 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
72 files
- scripts/
dales_law/ , Jupyter, 391 linesEI-system.ipynb - scripts/
microcircuits/ , Python, 22 linesbp/ change_params.py - scripts/
microcircuits/ , Shell, 49 linesbp/ run.sh - scripts/
microcircuits/ , Python, 22 linesfa/ change_params.py - scripts/
microcircuits/ , Shell, 49 linesfa/ run.sh - scripts/
microcircuits/ , Jupyter, 407 linesplot_fig.ipynb - scripts/
microcircuits/ , Python, 22 linessal/ change_params.py - scripts/
microcircuits/ , Shell, 49 linessal/ run.sh - scripts/
microcircuits/ , Python, 112 linestrain_mc.py - scripts/
psp_shapes/ , Jupyter, 500 linespsp_shapes.ipynb - scripts/
sal_principle/ , Jupyter, 207 linesplots_for_proof.ipynb - scripts/
sal_principle/ , Jupyter, 260 linesppd.ipynb - scripts/
sal_principle/ , Jupyter, 254 linesstdd.ipynb - scripts/
sal_principle/ , Jupyter, 155 linesstdd_ana_vs_sim.ipynb - scripts/
ssn/ , Python, 36 linesplast_noise/ change_params.py - scripts/
ssn/ , Shell, 47 linesplast_noise/ run.sh - scripts/
ssn/ , Python, 41 linesplast_noise_kp/ change_params.py - scripts/
ssn/ , Shell, 47 linesplast_noise_kp/ run.sh - scripts/
ssn/ , Python, 38 linesplast_noise_sal/ change_params.py - scripts/
ssn/ , Shell, 47 linesplast_noise_sal/ run.sh - scripts/
ssn/ , Jupyter, 895 linesplot_fig.ipynb - scripts/
ssn/ , Jupyter, 693 linesplot_fig_kp_si.ipynb - scripts/
ssn/ , Python, 18 linessyn_noise/ change_params.py - scripts/
ssn/ , Shell, 47 linessyn_noise/ run.sh - scripts/
ssn/ , Python, 22 linessyn_noise_kp/ change_params.py - scripts/
ssn/ , Shell, 47 linessyn_noise_kp/ run.sh - scripts/
ssn/ , Python, 18 linessyn_noise_sal/ change_params.py - scripts/
ssn/ , Shell, 47 linessyn_noise_sal/ run.sh - scripts/
ssn/ , Python, 281 linestrain_bm.py - scripts/
symm_net/ , Python, 154 linesload_utils.py - scripts/
symm_net/ , Python, 642 lines, 2 matchesmain_salnet.py - scripts/
symm_net/ , Jupyter, 192 linesplot_puresymm.ipynb - scripts/
symm_net/ , Jupyter, 370 linesplots.ipynb - scripts/
symm_net/ , Python, 230 linessalnet_symm.py - scripts/
symm_net/ , Jupyter, 538 linesscatter_stdwi_rdd_sal.ip ynb - scripts/
symm_net/ , Shell, 21 linesslurm.sh - scripts/
symm_net/ , Shell, 27 linesslurm_submit.sh - scripts/
symm_net/ , Python, 153 linessweep.py - scripts/
symm_net/ , Python, 126 linessweep_creator.py - scripts/
symm_net/ , Python, 128 linessweep_creator_symm.py - scripts/
symm_net/ , Python, 157 linessweep_symm.py - spiking_microcircuits/
src/ , Python, 1 linemicrocircuits/ __init__.py - spiking_microcircuits/
src/ , Python, 521 linesmicrocircuits/ experiment.py - spiking_microcircuits/
src/ , Python, 1,102 lines, 1 matchmicrocircuits/ model.py - spiking_microcircuits/
src/ , Python, 154 linesmicrocircuits/ utils.py - spiking_sampling_network
/ , Python, 3 linessrc/ neuralsampling/ __init__.py - spiking_sampling_network
/ , Python, 304 linessrc/ neuralsampling/ eisystem.py - spiking_sampling_network
/ , Python, 446 lines, 4 matchessrc/ neuralsampling/ network.py - spiking_sampling_network
/ , Python, 434 linessrc/ neuralsampling/ stdp_functions.py - spiking_sampling_network
/ , Python, 371 linessrc/ neuralsampling/ utils.py - stdd_calculator/
src/ , Python, 23 linesstddc/ __init__.py - stdd_calculator/
src/ , Python, 336 linesstddc/ distr.py - stdd_calculator/
src/ , Python, 76 linesstddc/ funcs.py - stdd_calculator/
src/ , Python, 145 linesstddc/ matrix.py - stdd_calculator/
src/ , Python, 209 linesstddc/ ppd.py - symmnet/
src/ , Python, 42 linessymmnet/ __init__.py - symmnet/
src/ , Python, 149 lines, 2 matchessymmnet/ conv_net.py - symmnet/
src/ , Python, 210 linessymmnet/ datasets.py - symmnet/
src/ , Python, 475 linessymmnet/ layers.py - symmnet/
src/ , Python, 316 linessymmnet/ rdd_layers.py - symmnet/
src/ , Python, 139 lines, 1 matchsymmnet/ rdd_net.py - symmnet/
src/ , Python, 436 linessymmnet/ sal_net.py - symmnet/
src/ , Python, 406 linessymmnet/ stdwi.py - symmnet/
src/ , Python, 28 linessymmnet/ utils.py - tests/
conftest.py , Python, 34 lines - tests/
test_imports.py , Python, 17 lines - tests/
test_main_salnet.py , Python, 77 lines - tests/
test_microcircuits.py , Python, 33 lines - tests/
test_salnet_symm.py , Python, 45 lines - tests/
test_ssn.py , Python, 40 lines - LICENSE, License, 21 lines
- README.md, Text, 158 lines
Code availability
The simulations were performed by custom code written in Python (v3.11), numpy (v2.0) and numba (v0.60). The SymmNet simulations were done using PyTorch (v2.7). All code is made available under https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 82 scripts, each with its path and the digest of its content;
- 10 matches between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
No dataset and no data link were found in the paper.
Data availability
The datasets used in this study are publicly available. CIFAR-10 is available at https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 5 authors, 4 keywords, 11 MeSH terms, 1 funder, 91 references.
Cite
This paper
Gierlich, T., Baumbach, A., Kungl, A. F., Max, K., & Petrovici, M. A. (2026). Spike-based alignment learning solves the weight transport problem. Nature communications, 17(1), 8699. https://
BibTeX
@article{gierlich2026spi
author = {Gierlich, Timo and Baumbach, Andreas and Kungl, Akos F. and Max, Kevin and Petrovici, Mihai A.},
title = {{Spike-based alignment learning solves the weight transport problem}},
journal = {Nature communications},
year = {2026},
month = jul,
volume = {17},
number = {1},
pages = {8699},
publisher = {Nature Publishing Group},
issn = {2041-1723},
doi = {10.1038/
url = {https://
pmid = {42414269},
pmcid = {PMC13490435}
}
RIS
TY - JOUR
AU - Gierlich, Timo
AU - Baumbach, Andreas
AU - Kungl, Akos F.
AU - Max, Kevin
AU - Petrovici, Mihai A.
TI - Spike-based alignment learning solves the weight transport problem
T2 - Nature communications
J2 - Nat Commun
PY - 2026
DA - 2026/
VL - 17
IS - 1
SP - 8699
SN - 2041-1723
PB - Nature Publishing Group
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "Spike-based alignment learning solves the weight transport problem",
"container-title": "Nature communications",
"author": [
{
"family": "Gierlich",
"given": "Timo"
},
{
"family": "Baumbach",
"given": "Andreas"
},
{
"family": "Kungl",
"given": "Akos F."
},
{
"family": "Max",
"given": "Kevin"
},
{
"family": "Petrovici",
"given": "Mihai A."
}
],
"container-title-short":
"volume": "17",
"issue": "1",
"page": "8699",
"DOI": "10.1038/
"PMID": "42414269",
"PMCID": "PMC13490435",
"ISSN": "2041-1723",
"publisher": "Nature Publishing Group",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
7
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.1371/journal.pcbi.1014164 [code]
- 'Backpropagation and the brain' realized in cortical error neuron microcircuits.Journal: PLoS computational biologyIn common: PyTorch, SciPy, Matplotlib, 1 other tool, computational modeling (no new data), 17 references, author Kevin Max
- [2] doi:10.7554/elife.105968 [code]
- Modeling the hallucinatory effects of classical psychedelics in terms of replay-dependent plasticity mechanisms.Journal: eLifeIn common: PyTorch, scikit-learn, Matplotlib, 1 other tool, 8 references
- [3] doi:10.7554/elife.111876 [code]
- Distinct sensorimotor encoding in tuft dendrites and somata associated with action, correction, and learning.Journal: eLifeIn common: PyTorch, seaborn, scikit-learn, 4 other tools, 5 references
- [4] doi:10.7554/elife.105953 [code]
- Top-down feedback in deep neural networks leads to functional differences during audiovisual integration.Journal: eLifeIn common: PyTorch, seaborn, scikit-learn, 4 other tools, 4 references
- [5] doi:10.1371/journal.pcbi.1014730 [code]
- A unified model of short- and long-term plasticity: Effects on network connectivity and information capacity.Journal: PLoS computational biologyIn common: seaborn, scikit-learn, pandas, 3 other tools, computational modeling (no new data), 4 references
- [6] doi:10.1038/s41467-026-74816-0 [code]
- Hierarchical optimization predicts plasticity in the macaque inferior temporal cortex following object training.Journal: Nature communicationsIn common: PyTorch, seaborn, scikit-learn, 4 other tools, 4 references
- [7] doi:10.1038/s41467-026-70354-x [code]
- Global error signal guides local optimization in mismatch calculation.Journal: Nature communicationsIn common: Numba, seaborn, scikit-learn, 3 other tools, computational modeling (no new data), 3 references
- [8] doi:10.1038/s41467-026-74358-5 [code]
- Brain-inspired spatial intelligence for embodied agents.Journal: Nature communicationsIn common: Numba, TensorFlow, PyTorch, 6 other tools, 1 reference
- [9] doi: [code]
- Going deeper with morphologically detailed neural networks by simulation-based gradient propagationJournal: Frontiers in computational neuroscienceIn common: TensorFlow, SciPy, Matplotlib, 1 other tool, computational modeling (no new data), 4 references
- [10] doi:10.1523/jneurosci.0987-25.2026 [code]
- Cell-Type-Specific Synaptic Scaling Mechanisms Differentially Contribute to Associative Learning.Journal: The Journal of neuroscience : the official journal of the Society for NeuroscienceIn common: Numba, seaborn, pandas, 3 other tools, 3 references
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 2 repositories of the authors' code, each at its verified commit and with its license, 82 scripts, and 10 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:480d12d7e0663f75…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
