OSCR

Cellular and subcellular specialization enables biology-constrained deep learning.

Code ↔ Paper

10 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 10 matches
  1. [1] § RESULTS › Dendritic target propagation can be flexibly combined with multiple biological synaptic plasticity rules ↔ EIANN/rules/btsp.py, lines 8 to 47 · score 0.71 · eligibility traces, instructive signals, presynaptic activity, sigmoidal, decaying, weight updates · Report a mismatch
  2. [2] § STAR★METHODS › METHOD DETAILS › Learning rules ↔ EIANN/rules/btsp.py, lines 8 to 47 · score 0.64 · eligibility traces, instructive signals, sigmoidal, BTSP, bounded, presynaptic · Report a mismatch
  3. [3] § RESULTS › Ineffective learning without top-down feedback ↔ EIANN/rules/hebbian.py, lines 8 to 89 · score 0.61 · postsynaptic activity, Hebbian learning rules, unsupervised learning, component, connections, EIANN · Report a mismatch
  4. [4] § RESULTS › Ineffective learning without top-down feedback ↔ EIANN/rules/hebbian.py, lines 8 to 89 · score 0.59 · principal component, unsupervised learning rule, correlation, Hebbian, weight update, postsynaptic · Report a mismatch
  5. [5] § RESULTS › Dendritic target propagation can be flexibly combined with multiple biological synaptic plasticity rules ↔ EIANN/rules/backprop_like.py, lines 356 to 468 · score 0.59 · nudge activities, firing rates, local error, weight updates, post, computation · Report a mismatch
  6. [6] § STAR★METHODS › METHOD DETAILS › Network architecture and dynamics ↔ EIANN/rules/backprop_like.py, lines 356 to 468 · score 0.56 · somatic states, firing rates, top layer, nudge, equilibrated, phase · Report a mismatch
  7. [7] § STAR★METHODS › METHOD DETAILS › Learning rules ↔ EIANN/network.py, lines 25 to 165 · score 0.55 · outgoing projections, loss function, incoming, global, error, networks · Report a mismatch
  8. [8] § STAR★METHODS › METHOD DETAILS › Network training and hyperparameter optimization ↔ EIANN/simulate/simulate_EIANN_fashion_mnist.py, lines 302 to 376 · score 0.54 · Fashion MNIST, random seeds, serially, class, network · Report a mismatch
  9. [9] § STAR★METHODS › METHOD DETAILS › Network architecture and dynamics ↔ EIANN/rules/backprop_like.py, lines 684 to 815 · score 0.53 · ReLU, firing rates, equilibrated, phase, activation, somatic · Report a mismatch
  10. [10] § STAR★METHODS › QUANTIFICATION AND STATISTICAL ANALYSIS › Analysis of neuronal stimulus selectivity ↔ EIANN/utils/representation_analysis.py, lines 1223 to 1256 · score 0.53 · spatial autocorrelation, receptive fields, Moran, dimensional · Report a mismatch

Paper

Loaded from Europe PMC by your browser; OSCR does not store it: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Python · 815 lines · 43 KB · MIT · 3 matches

  1. from .base_classes import LearningRule, BiasLearningRule
  2. import torch
  3. import math
  4. class BP_like_1E(LearningRule):
  5. def __init__(self, projection, max_pop_fraction=1., stochastic=False, learning_rate=None, relu_gate=False):
  6. """
  7. Output units are nudged to target. Hidden dendrites locally compute an error as the difference between
  8. forward and backward dendritic state.
  9. Weight updates are proportional to local error and (forward) presynaptic firing rate.
  10. If unit selection is stochastic, hidden units are selected for a weight update with a probability proportional
  11. to dendritic state by sampling a Bernoulli distribution. Otherwise, units are sorted by dendritic state. A fixed
  12. maximum fraction of hidden units are updated at each train step. Hidden units are "nudged" by dendritic state
  13. when selected for a weight update. Nudges to somatic state are applied instantaneously, rather than being
  14. subject to slow equilibration. Nudged activity is only passed top-down, but not laterally, with no
  15. equilibration.
  16. :param projection: :class:'nn.Linear'
  17. :param max_pop_fraction: float in [0, 1]
  18. :param stochastic: bool
  19. :param learning_rate: float
  20. :param relu_gate: bool
  21. """
  22. super().__init__(projection, learning_rate)
  23. self.max_pop_fraction = max_pop_fraction
  24. self.stochastic = stochastic
  25. self.relu_gate = relu_gate
  26. projection.post.register_attribute_history('plateau')
  27. projection.post.register_attribute_history('backward_activity')
  28. projection.post.register_attribute_history('backward_dendritic_state')
  29. def step(self):
  30. if self.projection.direction in ['forward', 'F']:
  31. delta_weight = torch.outer(self.projection.post.plateau,
  32. torch.clamp(self.projection.pre.forward_activity, min=0, max=1))
  33. elif self.projection.direction in ['recurrent', 'R']:
  34. delta_weight = torch.outer(self.projection.post.plateau,
  35. torch.clamp(self.projection.pre.forward_prev_activity, min=0, max=1))
  36. self.projection.weight.data += self.learning_rate * delta_weight
  37. @classmethod
  38. def backward_nudge_activity(cls, layer, store_dynamics=False):
  39. """
  40. Update somatic state and activity for all populations that receive a nudge.
  41. :param layer:
  42. :param store_dynamics: bool
  43. """
  44. for post_pop in layer:
  45. if post_pop.backward_projections or post_pop is post_pop.network.output_pop:
  46. if hasattr(post_pop, 'dend_to_soma'):
  47. post_pop.prev_activity = post_pop.activity
  48. post_pop.activity = post_pop.activation(post_pop.state + post_pop.dend_to_soma)
  49. if store_dynamics:
  50. post_pop.backward_steps_activity.append(post_pop.activity.detach().clone())
  51. @classmethod
  52. def backward_update_layer_dendritic_state(cls, layer):
  53. """
  54. Update dendritic state for all populations that receive projections that target the dendritic
  55. compartment.
  56. """
  57. for post_pop in layer:
  58. if post_pop.backward_projections:
  59. # update dendritic state
  60. init_dend_state = False
  61. for projection in post_pop:
  62. pre_pop = projection.pre
  63. if projection.compartment == 'dend':
  64. if not init_dend_state:
  65. post_pop.dendritic_state = torch.zeros(post_pop.size, device=post_pop.network.device)
  66. init_dend_state = True
  67. if projection.direction in ['forward', 'F']:
  68. post_pop.dendritic_state = post_pop.dendritic_state + projection(pre_pop.activity)
  69. elif projection.direction in ['recurrent', 'R']:
  70. post_pop.dendritic_state = post_pop.dendritic_state + projection(pre_pop.prev_activity)
  71. @classmethod
  72. def backward(cls, network, output, target, store_history=False, store_dynamics=False):
  73. """
  74. Integrate top-down inputs and update dendritic state variables.
  75. :param network:
  76. :param output:
  77. :param target:
  78. :param store_history: bool
  79. :param store_dynamics: bool
  80. """
  81. reversed_layers = list(network)[1:]
  82. reversed_layers.reverse()
  83. output_pop = network.output_pop
  84. # initialize populations that are updated during the backward phase
  85. for layer in reversed_layers:
  86. for pop in layer:
  87. if pop.backward_projections or pop is output_pop:
  88. if store_dynamics:
  89. pop.backward_steps_activity = []
  90. # initialize dendritic state variables
  91. for projection in pop:
  92. if projection.learning_rule.__class__ == cls:
  93. pop.plateau = torch.zeros(pop.size, device=network.device)
  94. pop.dend_to_soma = torch.zeros(pop.size, device=network.device)
  95. break
  96. for layer in reversed_layers:
  97. # update dendritic state variables
  98. cls.backward_update_layer_dendritic_state(layer)
  99. for pop in layer:
  100. for projection in pop:
  101. # compute plateau events and nudge somatic state
  102. if projection.learning_rule.__class__ == cls:
  103. if pop is output_pop:
  104. local_loss = torch.clamp(target - output_pop.activity, min=-1, max=1)
  105. output_pop.dendritic_state = local_loss.detach().clone()
  106. if projection.learning_rule.relu_gate:
  107. local_loss[output_pop.forward_activity == 0.] = 0.
  108. output_pop.plateau[:] = local_loss.detach().clone()
  109. output_pop.dend_to_soma[:] = local_loss.detach().clone()
  110. else:
  111. max_units = math.ceil(projection.learning_rule.max_pop_fraction * pop.size)
  112. local_loss = (pop.dendritic_state - pop.forward_dendritic_state).clamp_(-1., 1.)
  113. if projection.learning_rule.relu_gate:
  114. local_loss[pop.forward_activity == 0.] = 0.
  115. pos_avail_indexes = (local_loss > 0.).nonzero().squeeze(1)
  116. if projection.learning_rule.stochastic:
  117. pos_candidate_rel_indexes = (
  118. torch.bernoulli(local_loss[pos_avail_indexes])).nonzero().squeeze(1)
  119. else:
  120. sorted, pos_candidate_rel_indexes = torch.sort(local_loss[pos_avail_indexes],
  121. descending=True, stable=True)
  122. pos_event_indexes = pos_avail_indexes[pos_candidate_rel_indexes][:max_units]
  123. neg_avail_indexes = (local_loss < 0.).nonzero().squeeze(1)
  124. if projection.learning_rule.stochastic:
  125. neg_candidate_rel_indexes = (
  126. torch.bernoulli(-local_loss[neg_avail_indexes])).nonzero().squeeze(1)
  127. else:
  128. sorted, neg_candidate_rel_indexes = torch.sort(local_loss[neg_avail_indexes],
  129. descending=True, stable=True)
  130. neg_event_indexes = neg_avail_indexes[neg_candidate_rel_indexes][-max_units:]
  131. pop.plateau[pos_event_indexes] = local_loss[pos_event_indexes].detach().clone()
  132. pop.plateau[neg_event_indexes] = local_loss[neg_event_indexes].detach().clone()
  133. pop.dend_to_soma[pos_event_indexes] = (
  134. local_loss[pos_event_indexes].detach().clone())
  135. pop.dend_to_soma[neg_event_indexes] = (
  136. local_loss[neg_event_indexes].detach().clone())
  137. break
  138. # update activities
  139. cls.backward_nudge_activity(layer, store_dynamics=store_dynamics)
  140. if store_history:
  141. for layer in network:
  142. for pop in layer:
  143. for projection in pop:
  144. if projection.learning_rule.__class__ == cls:
  145. pop.append_attribute_history('plateau', pop.plateau.detach().clone())
  146. pop.append_attribute_history('backward_dendritic_state',
  147. pop.dendritic_state.detach().clone())
  148. break
  149. if pop.backward_projections or pop is output_pop:
  150. if store_dynamics:
  151. pop.append_attribute_history('backward_activity', pop.backward_steps_activity)
  152. else:
  153. pop.append_attribute_history('backward_activity', pop.activity.detach().clone())
  154. class BP_like_2E(LearningRule):
  155. def __init__(self, projection, max_pop_fraction=1., stochastic=False, learning_rate=None, relu_gate=False):
  156. """
  157. Initialize BP_like_2E learning rule.
  158. Output units are nudged to target. Hidden dendrites locally compute an error as the difference between
  159. excitation and inhibition. Weight updates are proportional to local error and (forward) presynaptic firing rate.
  160. If unit selection is stochastic, hidden units are selected for a weight update with a probability proportional
  161. to dendritic state by sampling a Bernoulli distribution. Otherwise, units are sorted by dendritic state. A fixed
  162. maximum fraction of hidden units are updated at each train step. Hidden units are "nudged" by dendritic state
  163. when selected for a weight update. Nudges to somatic state are applied instantaneously, rather than being
  164. subject to slow equilibration. Nudged activity is only passed top-down, but not laterally, with no
  165. equilibration.
  166. Parameters
  167. ----------
  168. projection : EIANN.Projection object (inherits from torch.nn.Linear)
  169. The neural projection/connection.
  170. max_pop_fraction : float, default=1.0
  171. Maximum fraction of population to update, must be in [0, 1].
  172. stochastic : bool, default=False
  173. Whether to use stochastic unit selection.
  174. learning_rate : float, optional
  175. Learning rate for weight updates.
  176. relu_gate : bool, default=False
  177. Whether to apply ReLU gating.
  178. """
  179. super().__init__(projection, learning_rate)
  180. self.max_pop_fraction = max_pop_fraction
  181. self.stochastic = stochastic
  182. self.relu_gate = relu_gate
  183. projection.post.register_attribute_history('plateau')
  184. projection.post.register_attribute_history('backward_activity')
  185. projection.post.register_attribute_history('backward_dendritic_state')
  186. def step(self):
  187. if self.projection.direction in ['forward', 'F']:
  188. delta_weight = torch.outer(self.projection.post.plateau,
  189. torch.clamp(self.projection.pre.forward_activity, min=0, max=1))
  190. elif self.projection.direction in ['recurrent', 'R']:
  191. delta_weight = torch.outer(self.projection.post.plateau,
  192. torch.clamp(self.projection.pre.forward_prev_activity, min=0, max=1))
  193. self.projection.weight.data += self.learning_rate * delta_weight
  194. @classmethod
  195. def backward_nudge_activity(cls, layer, store_dynamics=False):
  196. """
  197. Update somatic state and activity for all populations that receive a nudge.
  198. Parameters
  199. ----------
  200. layer : EIANN.Layer object
  201. The network layer to update.
  202. store_dynamics : bool, default=False
  203. Whether to store activity dynamics during the backward pass.
  204. """
  205. for post_pop in layer:
  206. if post_pop.backward_projections or post_pop is post_pop.network.output_pop:
  207. if hasattr(post_pop, 'dend_to_soma'):
  208. post_pop.prev_activity = post_pop.activity
  209. post_pop.activity = post_pop.activation(post_pop.state + post_pop.dend_to_soma)
  210. if store_dynamics:
  211. post_pop.backward_steps_activity.append(post_pop.activity.detach().clone())
  212. @classmethod
  213. def backward_update_layer_dendritic_state(cls, layer):
  214. """
  215. Update dendritic state for all populations that receive projections targeting the dendritic compartment.
  216. Parameters
  217. ----------
  218. layer : object
  219. The network layer to update.
  220. """
  221. for post_pop in layer:
  222. if post_pop.backward_projections:
  223. # update dendritic state
  224. init_dend_state = False
  225. for projection in post_pop:
  226. pre_pop = projection.pre
  227. if projection.compartment == 'dend':
  228. if not init_dend_state:
  229. post_pop.dendritic_state = torch.zeros(post_pop.size, device=post_pop.network.device)
  230. init_dend_state = True
  231. if projection.direction in ['forward', 'F']:
  232. post_pop.dendritic_state = post_pop.dendritic_state + projection(pre_pop.activity)
  233. elif projection.direction in ['recurrent', 'R']:
  234. post_pop.dendritic_state = post_pop.dendritic_state + projection(pre_pop.prev_activity)
  235. if init_dend_state:
  236. post_pop.dendritic_state.clamp_(min=-1, max=1)
  237. @classmethod
  238. def backward(cls, network, output, target, store_history=False, store_dynamics=False):
  239. """
  240. Integrate top-down inputs and update dendritic state variables.
  241. Parameters
  242. ----------
  243. network : EIANN.Network object (inherits from torch.nn.Module)
  244. The neural network.
  245. output : torch.Tensor
  246. Output of the network.forward() method
  247. target : torch.Tensor
  248. Target values.
  249. store_history : bool, default=False
  250. Whether to store computation history.
  251. store_dynamics : bool, default=False
  252. Whether to store activity dynamics.
  253. """
  254. reversed_layers = list(network)[1:]
  255. reversed_layers.reverse()
  256. output_pop = network.output_pop
  257. # Initialize populations that are updated during the backward phase
  258. for layer in reversed_layers:
  259. for pop in layer:
  260. if pop.backward_projections or pop is output_pop:
  261. if store_dynamics:
  262. pop.backward_steps_activity = []
  263. # Initialize dendritic state variables
  264. for projection in pop:
  265. if projection.learning_rule.__class__ == cls:
  266. pop.plateau = torch.zeros(pop.size, device=network.device)
  267. pop.dend_to_soma = torch.zeros(pop.size, device=network.device)
  268. break
  269. for layer in reversed_layers:
  270. # Update dendritic state variables
  271. cls.backward_update_layer_dendritic_state(layer)
  272. for pop in layer:
  273. for projection in pop:
  274. # Compute plateau events and nudge somatic state
  275. if projection.learning_rule.__class__ == cls:
  276. if pop is output_pop:
  277. local_loss = torch.clamp(target - output_pop.activity, min=-1, max=1)
  278. output_pop.dendritic_state = local_loss.detach().clone()
  279. if projection.learning_rule.relu_gate:
  280. local_loss[output_pop.forward_activity == 0.] = 0
  281. output_pop.plateau[:] = local_loss.detach().clone()
  282. output_pop.dend_to_soma[:] = local_loss.detach().clone()
  283. else:
  284. max_units = math.ceil(projection.learning_rule.max_pop_fraction * pop.size)
  285. local_loss = pop.dendritic_state.detach().clone()
  286. if projection.learning_rule.relu_gate:
  287. local_loss[pop.forward_activity == 0.] = 0
  288. pos_avail_indexes = (local_loss > 0.).nonzero().squeeze(1)
  289. if projection.learning_rule.stochastic:
  290. pos_candidate_rel_indexes = (
  291. torch.bernoulli(local_loss[pos_avail_indexes])).nonzero().squeeze(1)
  292. else:
  293. sorted, pos_candidate_rel_indexes = torch.sort(local_loss[pos_avail_indexes],
  294. descending=True, stable=True)
  295. pos_event_indexes = pos_avail_indexes[pos_candidate_rel_indexes][:max_units]
  296. neg_avail_indexes = (local_loss < 0.).nonzero().squeeze(1)
  297. if projection.learning_rule.stochastic:
  298. neg_candidate_rel_indexes = (
  299. torch.bernoulli(-local_loss[neg_avail_indexes])).nonzero().squeeze(1)
  300. else:
  301. sorted, neg_candidate_rel_indexes = torch.sort(local_loss[neg_avail_indexes],
  302. descending=True, stable=True)
  303. neg_event_indexes = neg_avail_indexes[neg_candidate_rel_indexes][-max_units:]
  304. pop.plateau[pos_event_indexes] = local_loss[pos_event_indexes]
  305. pop.plateau[neg_event_indexes] = local_loss[neg_event_indexes]
  306. pop.dend_to_soma[pos_event_indexes] = local_loss[pos_event_indexes]
  307. pop.dend_to_soma[neg_event_indexes] = local_loss[neg_event_indexes]
  308. break
  309. # Update activities
  310. cls.backward_nudge_activity(layer, store_dynamics=store_dynamics)
  311. if store_history:
  312. for layer in network:
  313. for pop in layer:
  314. for projection in pop:
  315. if projection.learning_rule.__class__ == cls:
  316. pop.append_attribute_history('plateau', pop.plateau.detach().clone())
  317. pop.append_attribute_history('backward_dendritic_state',
  318. pop.dendritic_state.detach().clone())
  319. break
  320. if pop.backward_projections or pop is output_pop:
  321. if store_dynamics:
  322. pop.append_attribute_history('backward_activity', pop.backward_steps_activity)
  323. else:
  324. pop.append_attribute_history('backward_activity', pop.activity.detach().clone())
  325. class Top_Layer_BP_like_2E(LearningRule):
  326. def __init__(self, projection, learning_rate=None, relu_gate=True):
  327. """
  328. Initialize Top_Layer_BP_like_2E learning rule.
  329. Output units are nudged to target. Weight updates are proportional to local error and presynaptic firing rate
  330. (no backward phase equilibration).
  331. Parameters
  332. ----------
  333. projection : EIANN.Projection (inherits from torch.nn.Linear)
  334. The neural projection/connection.
  335. learning_rate : float, optional
  336. Learning rate for weight updates.
  337. relu_gate : bool, default=True
  338. Whether to apply ReLU gating.
  339. """
  340. super().__init__(projection, learning_rate)
  341. self.relu_gate = relu_gate
  342. projection.post.register_attribute_history('plateau')
  343. projection.post.register_attribute_history('backward_activity')
  344. projection.post.register_attribute_history('backward_dendritic_state')
  345. def step(self):
  346. if self.projection.direction in ['forward', 'F']:
  347. delta_weight = torch.outer(self.projection.post.plateau,
  348. torch.clamp(self.projection.pre.forward_activity, min=0, max=1))
  349. elif self.projection.direction in ['recurrent', 'R']:
  350. delta_weight = torch.outer(self.projection.post.plateau,
  351. torch.clamp(self.projection.pre.forward_prev_activity, min=0, max=1))
  352. self.projection.weight.data += self.learning_rate * delta_weight
  353. @classmethod
  354. def backward_nudge_activity(cls, layer, store_dynamics=False):
  355. """
  356. Update somatic state and activity for all populations that receive a nudge.
  357. Parameters
  358. ----------
  359. layer : EIANN.Layer object
  360. The network layer to update.
  361. store_dynamics : bool, default=False
  362. Whether to store activity dynamics during the backward pass.
  363. """
  364. for post_pop in layer:
  365. if post_pop.backward_projections or post_pop is post_pop.network.output_pop:
  366. if hasattr(post_pop, 'dend_to_soma'):
  367. post_pop.prev_activity = post_pop.activity
  368. post_pop.activity = post_pop.activation(post_pop.state + post_pop.dend_to_soma)
  369. if store_dynamics:
  370. post_pop.backward_steps_activity.append(post_pop.activity.detach().clone())
  371. @classmethod
  372. def backward(cls, network, output, target, store_history=False, store_dynamics=False):
  373. """
  374. Integrate top-down inputs and update dendritic state variables.
  375. Parameters
  376. ----------
  377. network : EIANN.Network object (inherits from torch.nn.Module)
  378. The neural network.
  379. output : torch.Tensor
  380. Output from the network.forward() method
  381. target : torch.Tensor
  382. Target values.
  383. store_history : bool, default=False
  384. Whether to store computation history.
  385. store_dynamics : bool, default=False
  386. Whether to store activity dynamics.
  387. """
  388. output_pop = network.output_pop
  389. output_layer = output_pop.layer
  390. # Initialize populations that are updated during the backward phase
  391. for pop in output_layer:
  392. if pop.backward_projections or pop is output_pop:
  393. if store_dynamics:
  394. pop.backward_steps_activity = []
  395. # Initialize dendritic state variables
  396. for projection in pop:
  397. if projection.learning_rule.__class__ == cls:
  398. pop.plateau = torch.zeros(pop.size, device=network.device)
  399. pop.dend_to_soma = torch.zeros(pop.size, device=network.device)
  400. break
  401. for pop in output_layer:
  402. for projection in pop:
  403. # Compute plateau events and nudge somatic state
  404. if projection.learning_rule.__class__ == cls:
  405. if pop is output_pop:
  406. local_loss = torch.clamp(target - output_pop.activity, min=-1, max=1)
  407. output_pop.dendritic_state = local_loss.detach().clone()
  408. if projection.learning_rule.relu_gate:
  409. local_loss[output_pop.forward_activity == 0.] = 0
  410. output_pop.plateau[:] = local_loss.detach().clone()
  411. output_pop.dend_to_soma[:] = local_loss.detach().clone()
  412. break
  413. cls.backward_nudge_activity(output_layer, store_dynamics=store_dynamics)
  414. if store_history:
  415. for pop in output_layer:
  416. for projection in pop:
  417. if projection.learning_rule.__class__ == cls:
  418. pop.append_attribute_history('plateau', pop.plateau.detach().clone())
  419. pop.append_attribute_history('backward_dendritic_state',
  420. pop.dendritic_state.detach().clone())
  421. break
  422. if pop.backward_projections or pop is output_pop:
  423. if store_dynamics:
  424. pop.append_attribute_history('backward_activity', pop.backward_steps_activity)
  425. else:
  426. pop.append_attribute_history('backward_activity', pop.activity.detach().clone())
  427. class BP_like_2L(LearningRule):
  428. def __init__(self, projection, max_pop_fraction=1., stochastic=False, learning_rate=None, relu_gate=False, forward_only=False):
  429. """
  430. Initialize BP_like_2L learning rule.
  431. Output units are nudged to target. Hidden dendrites locally compute an error as the difference between
  432. excitation and inhibition. Weight updates are proportional to local error and presynaptic firing rate (after
  433. backward phase equilibration).
  434. If unit selection is stochastic, hidden units are selected for a weight update with a probability proportional
  435. to dendritic state by sampling a Bernoulli distribution. Otherwise, units are sorted by dendritic state. A fixed
  436. maximum fraction of hidden units are updated at each train step. Hidden units are "nudged" by dendritic state
  437. when selected for a weight update. Nudges to somatic state are applied instantaneously, rather than being
  438. subject to slow equilibration. During nudging, activities are re-equilibrated across all layers.
  439. Parameters
  440. ----------
  441. projection : EIANN.Projection object (inherits from torch.nn.Linear)
  442. The neural projection/connection.
  443. max_pop_fraction : float, default=1.0
  444. Maximum fraction of population to update, must be in [0, 1].
  445. stochastic : bool, default=False
  446. Whether to use stochastic unit selection.
  447. learning_rate : float, optional
  448. Learning rate for weight updates.
  449. relu_gate : bool, default=False
  450. Whether to apply ReLU gating.
  451. forward_only : bool, default=False
  452. Whether to consult forward activity in weight update.
  453. """
  454. super().__init__(projection, learning_rate)
  455. self.max_pop_fraction = max_pop_fraction
  456. self.stochastic = stochastic
  457. self.relu_gate = relu_gate
  458. self.forward_only = forward_only
  459. projection.post.register_attribute_history('plateau')
  460. projection.post.register_attribute_history('backward_activity')
  461. projection.post.register_attribute_history('backward_dendritic_state')
  462. def step(self):
  463. if self.projection.direction in ['forward', 'F']:
  464. if self.forward_only:
  465. pre_activity = torch.clamp(self.projection.pre.forward_activity, min=0, max=1)
  466. else:
  467. pre_activity = torch.clamp(self.projection.pre.activity, min=0, max=1)
  468. elif self.projection.direction in ['recurrent', 'R']:
  469. if self.forward_only:
  470. pre_activity = torch.clamp(self.projection.pre.forward_prev_activity, min=0, max=1)
  471. else:
  472. pre_activity = torch.clamp(self.projection.pre.prev_activity, min=0, max=1)
  473. delta_weight = torch.outer(self.projection.post.plateau, pre_activity)
  474. self.projection.weight.data += self.learning_rate * delta_weight
  475. @classmethod
  476. def backward_update_layer_activity(cls, layer, store_dynamics=False):
  477. """
  478. Update somatic state and activity for populations with backward projections.
  479. Updates somatic state and activity for all populations that receive projections with update_phase in
  480. ['B', 'backward', 'A', 'all'].
  481. Parameters
  482. ----------
  483. layer : EIANN.Layer object
  484. The network layer to update.
  485. store_dynamics : bool, default=False
  486. Whether to store activity dynamics during the backward pass.
  487. """
  488. for post_pop in layer:
  489. post_pop.prev_activity = post_pop.activity
  490. for post_pop in layer:
  491. if post_pop.backward_projections or post_pop is post_pop.network.output_pop or store_dynamics:
  492. # Update somatic state and activity
  493. delta_state = -post_pop.state + post_pop.bias
  494. for projection in post_pop:
  495. pre_pop = projection.pre
  496. if projection.compartment in [None, 'soma']:
  497. if projection.direction in ['forward', 'F']:
  498. delta_state = delta_state + projection(pre_pop.activity)
  499. elif projection.direction in ['recurrent', 'R']:
  500. delta_state = delta_state + projection(pre_pop.prev_activity)
  501. post_pop.state = post_pop.state + delta_state / post_pop.tau
  502. if hasattr(post_pop, 'dend_to_soma'):
  503. post_pop.activity = post_pop.activation(post_pop.state + post_pop.dend_to_soma)
  504. else:
  505. post_pop.activity = post_pop.activation(post_pop.state)
  506. if store_dynamics:
  507. post_pop.backward_steps_activity.append(post_pop.activity.detach().clone())
  508. @classmethod
  509. def backward_update_layer_dendritic_state(cls, layer):
  510. """
  511. Update dendritic state for all populations that receive projections targeting the dendritic compartment.
  512. Parameters
  513. ----------
  514. layer : EIANN.Layer object
  515. The network layer to update.
  516. """
  517. for post_pop in layer:
  518. if post_pop.backward_projections:
  519. # Update dendritic state
  520. init_dend_state = False
  521. for projection in post_pop:
  522. pre_pop = projection.pre
  523. if projection.compartment == 'dend':
  524. if not init_dend_state:
  525. post_pop.dendritic_state = torch.zeros(post_pop.size, device=post_pop.network.device)
  526. init_dend_state = True
  527. if projection.direction in ['forward', 'F']:
  528. post_pop.dendritic_state = post_pop.dendritic_state + projection(pre_pop.activity)
  529. elif projection.direction in ['recurrent', 'R']:
  530. post_pop.dendritic_state = post_pop.dendritic_state + projection(pre_pop.prev_activity)
  531. if init_dend_state:
  532. post_pop.dendritic_state.clamp_(min=-1, max=1)
  533. @classmethod
  534. def backward(cls, network, output, target, store_history=False, store_dynamics=False):
  535. """
  536. Integrate top-down inputs and update dendritic state variables.
  537. Parameters
  538. ----------
  539. network : EIANN.Network object (inherits from torch.nn.Module)
  540. The neural network.
  541. output : torch.Tensor
  542. Output from the network.forward() method
  543. target : torch.Tensor
  544. Target values.
  545. store_history : bool, default=False
  546. Whether to store computation history.
  547. store_dynamics : bool, default=False
  548. Whether to store activity dynamics.
  549. """
  550. reversed_layers = list(network)[1:]
  551. reversed_layers.reverse()
  552. output_pop = network.output_pop
  553. # Initialize populations that are updated during the backward phase
  554. for layer in reversed_layers:
  555. for pop in layer:
  556. if store_dynamics:
  557. pop.backward_steps_activity = []
  558. # Initialize dendritic state variables
  559. for projection in pop:
  560. if cls.shared_backward_methods(projection.learning_rule):
  561. pop.plateau = torch.zeros(pop.size, device=network.device)
  562. pop.dend_to_soma = torch.zeros(pop.size, device=network.device)
  563. break
  564. for t in range(network.forward_steps):
  565. for layer in reversed_layers:
  566. # Update dendritic state variables
  567. cls.backward_update_layer_dendritic_state(layer)
  568. if t == 0:
  569. for pop in layer:
  570. for projection in pop:
  571. # Compute plateau events and nudge somatic state
  572. if cls.shared_backward_methods(projection.learning_rule):
  573. if pop is output_pop:
  574. local_loss = torch.clamp(target - output_pop.activity, min=-1, max=1)
  575. output_pop.dendritic_state = local_loss.detach().clone()
  576. if projection.learning_rule.relu_gate:
  577. local_loss[output_pop.forward_activity == 0.] = 0
  578. output_pop.plateau[:] = local_loss.detach().clone()
  579. output_pop.dend_to_soma[:] = local_loss.detach().clone()
  580. else:
  581. max_units = math.ceil(projection.learning_rule.max_pop_fraction * pop.size)
  582. local_loss = pop.dendritic_state.detach().clone()
  583. if projection.learning_rule.relu_gate:
  584. local_loss[pop.forward_activity == 0.] = 0
  585. pos_avail_indexes = (local_loss > 0.).nonzero().squeeze(1)
  586. if projection.learning_rule.stochastic:
  587. pos_candidate_rel_indexes = (
  588. torch.bernoulli(local_loss[pos_avail_indexes])).nonzero().squeeze(1)
  589. else:
  590. sorted, pos_candidate_rel_indexes = torch.sort(local_loss[pos_avail_indexes],
  591. descending=True, stable=True)
  592. pos_event_indexes = pos_avail_indexes[pos_candidate_rel_indexes][:max_units]
  593. neg_avail_indexes = (local_loss < 0.).nonzero().squeeze(1)
  594. if projection.learning_rule.stochastic:
  595. neg_candidate_rel_indexes = (
  596. torch.bernoulli(-local_loss[neg_avail_indexes])).nonzero().squeeze(1)
  597. else:
  598. sorted, neg_candidate_rel_indexes = torch.sort(local_loss[neg_avail_indexes],
  599. descending=True, stable=True)
  600. neg_event_indexes = neg_avail_indexes[neg_candidate_rel_indexes][-max_units:]
  601. pop.plateau[pos_event_indexes] = local_loss[pos_event_indexes]
  602. pop.plateau[neg_event_indexes] = local_loss[neg_event_indexes]
  603. pop.dend_to_soma[pos_event_indexes] = local_loss[pos_event_indexes]
  604. pop.dend_to_soma[neg_event_indexes] = local_loss[neg_event_indexes]
  605. break
  606. # Update activities
  607. cls.backward_update_layer_activity(layer, store_dynamics=store_dynamics)
  608. if store_history:
  609. for layer in network:
  610. for pop in layer:
  611. for projection in pop:
  612. if projection.learning_rule.__class__ == cls:
  613. pop.append_attribute_history('plateau', pop.plateau.detach().clone())
  614. pop.append_attribute_history('backward_dendritic_state',
  615. pop.dendritic_state.detach().clone())
  616. break
  617. if pop.backward_projections or pop is output_pop:
  618. if store_dynamics:
  619. pop.append_attribute_history('backward_activity', pop.backward_steps_activity)
  620. else:
  621. pop.append_attribute_history('backward_activity', pop.activity.detach().clone())
  622. class Top_Layer_BP_like_2L(LearningRule):
  623. def __init__(self, projection, learning_rate=None, relu_gate=True):
  624. """
  625. Initialize Top_Layer_BP_like_2L learning rule.
  626. Output units are nudged to target. Weight updates are proportional to local error and presynaptic firing rate
  627. (after backward phase equilibration).
  628. Parameters
  629. ----------
  630. projection : EIANN.Projection (inherits from torch.nn.Linear)
  631. The neural projection/connection.
  632. learning_rate : float, optional
  633. Learning rate for weight updates.
  634. relu_gate : bool, default=True
  635. Whether to apply ReLU gating.
  636. """
  637. super().__init__(projection, learning_rate)
  638. self.relu_gate = relu_gate
  639. projection.post.register_attribute_history('plateau')
  640. projection.post.register_attribute_history('backward_activity')
  641. projection.post.register_attribute_history('backward_dendritic_state')
  642. def step(self):
  643. if self.projection.direction in ['forward', 'F']:
  644. delta_weight = torch.outer(self.projection.post.plateau,
  645. torch.clamp(self.projection.pre.activity, min=0, max=1))
  646. elif self.projection.direction in ['recurrent', 'R']:
  647. delta_weight = torch.outer(self.projection.post.plateau,
  648. torch.clamp(self.projection.pre.prev_activity, min=0, max=1))
  649. self.projection.weight.data += self.learning_rate * delta_weight
  650. @classmethod
  651. def backward_update_layer_activity(cls, layer, store_dynamics=False):
  652. """
  653. Update somatic state and activity for populations with backward projections.
  654. Updates somatic state and activity for all populations that receive projections with update_phase in
  655. ['B', 'backward', 'A', 'all'].
  656. Parameters
  657. ----------
  658. layer : EIANN.Layer object
  659. The network layer to update.
  660. store_dynamics : bool, default=False
  661. Whether to store activity dynamics during the backward pass.
  662. """
  663. for post_pop in layer:
  664. post_pop.prev_activity = post_pop.activity
  665. for post_pop in layer:
  666. if post_pop.backward_projections or post_pop is post_pop.network.output_pop:
  667. # update somatic state and activity
  668. delta_state = -post_pop.state + post_pop.bias
  669. for projection in post_pop:
  670. pre_pop = projection.pre
  671. if projection.compartment in [None, 'soma']:
  672. if projection.direction in ['forward', 'F']:
  673. delta_state = delta_state + projection(pre_pop.activity)
  674. elif projection.direction in ['recurrent', 'R']:
  675. delta_state = delta_state + projection(pre_pop.prev_activity)
  676. post_pop.state = post_pop.state + delta_state / post_pop.tau
  677. if hasattr(post_pop, 'dend_to_soma'):
  678. post_pop.activity = post_pop.activation(post_pop.state + post_pop.dend_to_soma)
  679. else:
  680. post_pop.activity = post_pop.activation(post_pop.state)
  681. if store_dynamics:
  682. post_pop.backward_steps_activity.append(post_pop.activity.detach().clone())
  683. @classmethod
  684. def backward(cls, network, output, target, store_history=False, store_dynamics=False):
  685. """
  686. Integrate top-down inputs and update dendritic state variables.
  687. Parameters
  688. ----------
  689. network : EIANN.Network object (inherits from torch.nn.Module)
  690. The neural network.
  691. output : torch.Tensor
  692. Output from the network.forward() method
  693. target : torch.Tensor
  694. Target values.
  695. store_history : bool, default=False
  696. Whether to store computation history.
  697. store_dynamics : bool, default=False
  698. Whether to store activity dynamics.
  699. """
  700. output_pop = network.output_pop
  701. output_layer = output_pop.layer
  702. # Initialize populations that are updated during the backward phase
  703. for pop in output_layer:
  704. if pop.backward_projections or pop is output_pop:
  705. if store_dynamics:
  706. pop.backward_steps_activity = []
  707. # Initialize dendritic state variables
  708. for projection in pop:
  709. if projection.learning_rule.__class__ == cls:
  710. pop.plateau = torch.zeros(pop.size, device=network.device)
  711. pop.dend_to_soma = torch.zeros(pop.size, device=network.device)
  712. break
  713. for t in range(network.forward_steps):
  714. if t == 0:
  715. for pop in output_layer:
  716. for projection in pop:
  717. # Compute plateau events and nudge somatic state
  718. if projection.learning_rule.__class__ == cls:
  719. if pop is output_pop:
  720. local_loss = torch.clamp(target - output_pop.activity, min=-1, max=1)
  721. output_pop.dendritic_state = local_loss.detach().clone()
  722. if projection.learning_rule.relu_gate:
  723. local_loss[output_pop.forward_activity == 0.] = 0
  724. output_pop.plateau[:] = local_loss.detach().clone()
  725. output_pop.dend_to_soma[:] = local_loss.detach().clone()
  726. break
  727. cls.backward_update_layer_activity(output_layer, store_dynamics=store_dynamics)
  728. if store_history:
  729. for pop in output_layer:
  730. for projection in pop:
  731. if projection.learning_rule.__class__ == cls:
  732. pop.append_attribute_history('plateau', pop.plateau.detach().clone())
  733. pop.append_attribute_history('backward_dendritic_state',
  734. pop.dendritic_state.detach().clone())
  735. break
  736. if pop.backward_projections or pop is output_pop:
  737. if store_dynamics:
  738. pop.append_attribute_history('backward_activity', pop.backward_steps_activity)
  739. else:
  740. pop.append_attribute_history('backward_activity', pop.activity.detach().clone())

backprop_like.py at commit 5df3405, under MIT · at the source

Overview

Authors: Alessandro R. Galloni1, Ajay Peddada1, Yash Chennawar1,2, Aaron D. Milstein1,3
  1. Center for Advanced Biotechnology and Medicine and Department of Neuroscience and Biology, Rutgers Biomedical and Health Sciences, Rutgers, The State University of New Jersey, Piscataway, NJ 08854, USA
  2. Department of Computer Science, Rutgers, The State University of New Jersey, Piscataway, NJ 08854, USA
  3. Lead contact
Institutions: Rutgers, The State University of New Jersey (United States); Rutgers Health (United States)
Journal: Cell reports, volume 45, issue 4, article 117159
Dates: published online 26 March 2026; in print March 2026
Type: Research article · Language: English
License: CC BY-NC
Identifiers: DOI 10.1016/j.celrep.2026.117159 · PMID 41894388 · PMCID PMC13109822 · OpenAlex W7140314935
Open access: gold, a free copy (OpenAlex)
Status: code verified (how the code is found and verified)
Categories: cellular / molecular (subfield)
Methods: Machine learning, Single-unit activity, calcium imaging
Keywords: Learning, Synaptic plasticity, Dendrites, Interneurons, Computational Neuroscience, Cp: Neuroscience
Topic: Single-cell and spatial transcriptomics (Molecular Biology, Biochemistry, Genetics and Molecular Biology), according to OpenAlex
Funding: European Molecular Biology Organization; NIMH NIH HHS (RF1 MH135576, R01 MH121979); National Institute of Mental Health
Citations: cited by 4 papers (Europe PMC); 160 references in the paper

Abstract

Learning depends on changes in neuronal synaptic strengths. While cellular mechanisms of plasticity have been extensively studied experimentally, our understanding of how plasticity is organized across neural populations comes largely from training artificial neural networks (ANNs). However, most modern ANN architectures and algorithms are not compatible with fundamental principles of neuroscience. Here we leverage recent experimental evidence to test an emergent theory that biological learning depends on neuronal cell type specialization and compartmentalized signaling within neuronal dendrites. We demonstrate that multi-layer ANNs comprised of separate excitatory and inhibitory cell types, and neuronal units with separate dendrite compartments, can be trained to accurately classify images using a fully biology-compatible deep learning algorithm called dendritic target propagation. By adhering to strict biological constraints, this model provides insight into biological mechanisms of learning and makes experimentally testable predictions regarding the roles of specific cell types in coordinating learning across multiple circuit layers.

Reproduced under the paper's license (CC BY-NC), 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.

milstein-lab.github.io/eiann

License: none: the authors keep all their rights
State: the link answers, verified on 30 September 2026
Evidence: the link answers
Software Heritage: not checked
Found in: the text, “Additional resources”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 30 September 2026: the link answers (HTTP 200)
  • 30 September 2026: the link answers (HTTP 200)

Milstein-Lab/EIANN

License: MIT
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Commit: 5df3405ff38229ab0a10b830570de3638bf86f54, 26 May 2026
Languages: Shell (45), Python (41), Jupyter (29)
Size: 371 files, 115 scripts
Software Heritage: not archived
Found in: the references
Holds: README, license file, environment (pyproject.toml, requirements.txt), tests, continuous integration, documentation, 29 notebooks
Not found: CITATION.cff
Tools: PyTorch (29 files), NumPy (25 files), Matplotlib (22 files), h5py (19 files), SciPy (6 files), scikit-learn (2 files), pandas (1 file), scikit-image (1 file)
Availability: 1 check, the latest on 30 September 2026: the link answers
  • 30 September 2026: the link answers
109 files

neurosutras/nested

License: none: the authors keep all their rights
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Commit: 42d484faf91a9339417ecff7cd2a0c4fd810fd15, 19 August 2026
Languages: Python (23), Shell (7), Jupyter (2)
Size: 44 files, 32 scripts
Software Heritage: archived
Found in: the references
Holds: README, tests, 2 notebooks
Not found: license file, CITATION.cff, environment file, continuous integration, documentation
Tools: NumPy (7 files), Matplotlib (6 files), SciPy (5 files), h5py (3 files), NEURON (2 files), scikit-learn (2 files), seaborn (1 file)
Availability: 1 check, the latest on 30 September 2026: the link answers
  • 30 September 2026: the link answers
33 files

The paper's code and data availability statement is in the Data section.

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:

  • 3 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 139 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 and code availability

All large data files containing simulation results from training networks with optimized hyperparameters are available from the lead contact upon request.

All code required to reproduce the results in this paper have been deposited on GitHub155 and archived on Zenodo and are publicly available as of the date of publication. Accession numbers are listed in the key resources table.

Any additional information required to reanalyze the data reported in this paper is available from the lead contact upon request.

Reproduced under the paper's license (CC BY-NC), 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, 30 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 4 authors, 6 keywords, 3 funders, 137 references.

Cite

This paper

Galloni, A. R., Peddada, A., Chennawar, Y., & Milstein, A. D. (2026). Cellular and subcellular specialization enables biology-constrained deep learning. Cell reports, 45(4), 117159. https://doi.org/10.1016/j.celrep.2026.117159

BibTeX

@article{galloni2026cellular,
author = {Galloni, Alessandro R. and Peddada, Ajay and Chennawar, Yash and Milstein, Aaron D.},
title = {{Cellular and subcellular specialization enables biology-constrained deep learning}},
journal = {Cell reports},
year = {2026},
month = mar,
volume = {45},
number = {4},
pages = {117159},
publisher = {Cell Press},
issn = {2211-1247},
doi = {10.1016/j.celrep.2026.117159},
url = {https://doi.org/10.1016/j.celrep.2026.117159},
pmid = {41894388},
pmcid = {PMC13109822}
}

RIS

TY - JOUR
AU - Galloni, Alessandro R.
AU - Peddada, Ajay
AU - Chennawar, Yash
AU - Milstein, Aaron D.
TI - Cellular and subcellular specialization enables biology-constrained deep learning
T2 - Cell reports
J2 - Cell Rep
PY - 2026
DA - 2026/03/26
VL - 45
IS - 4
SP - 117159
SN - 2211-1247
PB - Cell Press
DO - 10.1016/j.celrep.2026.117159
UR - https://doi.org/10.1016/j.celrep.2026.117159
LA - en
ER -

CSL-JSON

{
"id": "10.1016/j.celrep.2026.117159",
"type": "article-journal",
"title": "Cellular and subcellular specialization enables biology-constrained deep learning",
"container-title": "Cell reports",
"author": [
{
"family": "Galloni",
"given": "Alessandro R."
},
{
"family": "Peddada",
"given": "Ajay"
},
{
"family": "Chennawar",
"given": "Yash"
},
{
"family": "Milstein",
"given": "Aaron D."
}
],
"container-title-short": "Cell Rep",
"volume": "45",
"issue": "4",
"page": "117159",
"DOI": "10.1016/j.celrep.2026.117159",
"PMID": "41894388",
"PMCID": "PMC13109822",
"ISSN": "2211-1247",
"publisher": "Cell Press",
"URL": "https://doi.org/10.1016/j.celrep.2026.117159",
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
26
]
]
}
}

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.1016/j.celrep.2022.111962 [code]
Lateral entorhinal cortex inputs modulate hippocampal dendritic excitability by recruiting a local disinhibitory microcircuit.
Journal: Cell reports
In common: NEURON, seaborn, pandas, 3 other tools, 11 references
[2] doi:10.1038/s41467-025-64859-0 [code]
Hippocampal OLM interneurons regulate CA1 place cell plasticity and remapping.
Journal: Nature communications
In common: h5py, seaborn, pandas, 3 other tools, 12 references
[3] doi:10.1038/s41586-026-10190-7 [code]
Vectorized instructive signals in cortical dendrites.
Journal: Nature
In common: h5py, NumPy, 14 references
[4] doi:10.7554/elife.89674 [code]
A neuronal least-action principle for real-time learning in cortical circuits.
Journal: eLife
In common: PyTorch, seaborn, scikit-learn, 3 other tools, 12 references
[5] doi:10.1038/s41467-026-74460-8 [code]
Spike-based alignment learning solves the weight transport problem.
Journal: Nature communications
In common: PyTorch, seaborn, scikit-learn, 4 other tools, 11 references
[6] doi:10.1038/s41467-025-56297-9 [code]
Dendrites endow artificial neural networks with accurate, robust and parameter-efficient learning.
Journal: Nature communications
In common: PyTorch, seaborn, scikit-learn, 4 other tools, 10 references
[7] doi:10.1101/2025.05.12.653626 [code]
Breaking Balance: Encoding local error signals in perturbations of excitation-inhibition balance
Journal: bioRxiv (preprint)
In common: cellular / molecular, 13 references
[8] doi:10.1371/journal.pcbi.1012754 [code]
The calcitron: A simple neuron model that implements many learning rules via the calcium control hypothesis.
Journal: PLoS computational biology
In common: seaborn, scikit-learn, pandas, 3 other tools, cellular / molecular, 11 references
[9] doi:10.1038/s41467-024-50546-z [code]
Functional architecture of intracellular oscillations in hippocampal dendrites.
Journal: Nature communications
In common: 14 references
[10] doi:10.1016/j.celrep.2026.117793 [code]
Clustered inputs engage dendritic nonlinearities and calcium signaling to support efficient place-field formation in CA1 pyramidal neurons.
Journal: Cell reports
In common: NEURON, seaborn, scikit-learn, 4 other tools, cellular / molecular, 9 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.

Add the badge to the code's README

OSCR: the badge of this paper

If you are an author or a maintainer of this paper's code, you can add this badge. It says what OSCR knows of the paper (its code, its validated tracing map) and links the code to this page. Paste one of these snippets under the README's title. Keep the two marks around it, so that the badge can be replaced in place and is never added twice. OSCR changes nothing in your repository.

Markdown
<!-- oscr-badge:start -->
[![OSCR](https://openscicode.org/badge/doi/10.1016/j.celrep.2026.117159.svg)](https://openscicode.org/badge/doi/10.1016/j.celrep.2026.117159)
<!-- oscr-badge:end -->
reStructuredText
.. oscr-badge:start

.. image:: https://openscicode.org/badge/doi/10.1016/j.celrep.2026.117159.svg
   :target: https://openscicode.org/badge/doi/10.1016/j.celrep.2026.117159
   :alt: OSCR

.. oscr-badge:end
HTML
<!-- oscr-badge:start -->
<a href="https://openscicode.org/badge/doi/10.1016/j.celrep.2026.117159"><img src="https://openscicode.org/badge/doi/10.1016/j.celrep.2026.117159.svg" alt="OSCR"></a>
<!-- oscr-badge:end -->

On GitHub, open the README, choose Edit (the pencil), paste the snippet under the title, then "Commit changes".

If you choose to, you can have the badge kept up to date for you, with oscr badge --write from the command line or with the GitHub Action in your repository's workflows. Every other form and option (a card, AsciiDoc, LaTeX, shields.io) is on the badge page.

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: code errors and mismatches between the code and the paper about this paper and its code, from readers and its authors, beside the paper and its tracing map.

Report a mismatch between the code and the paper · Report a code error (signed in)

Reproductions: reports from readers who ran the authors' code and did not get the paper's result, what they ran, with which environment, commit and data.

Report a reproduction failure (signed in)

Versions of its code: the releases of its code tied to a version of this paper (the preprint, the accepted manuscript, the version of record…), each with the tracing map versioned with it.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.