OSCR

Machine learning discovers numerous new computational principles supporting elementary motion detection.

Code ↔ Paper

4 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 4 matches
  1. [1] § Results › Methods to identify circuit components participating in DS computations ↔ main.py, lines 1–59 · score 0.61 · RF structure, receptive field, presynaptic population, surround, postsynaptic, components
  2. [2] § Results › Machine learning framework to study motion computations ↔ main.py, lines 1–59 · score 0.58 · RF orientations, receptive field, presynaptic population, network, synapsing, kinetics
  3. [3] § Methods ↔ GA_RF.py, lines 44–113 · score 0.55 · RF component, Gaussian, fraction, sum, width, surround
  4. [4] § Results › Machine learning framework to study motion computations ↔ GA_RF.py, lines 44–113 · score 0.50 · temporal activation, standard deviation, shifted, dynamics, peak, RF

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 · 452 lines · 22 KB · no license · 2 matches

  1. from multiprocessing import Process, Queue, freeze_support, Pipe
  2. import numpy as np
  3. import time
  4. import matplotlib.pyplot as plt
  5. from neuron import h
  6. #from time import sleep
  7. from GA_RF import GA_Pre_activation , GA_StimTrajectory
  8. from GA_h5 import GA_SaveH5
  9. from neuron import h
  10. import copy
  11. import pickle
  12. #import sys
  13. import argparse
  14. h.load_file('stdrun.hoc')
  15. from GA_NEURON import NEURON_SetCells, NEURON_UpdateSynapses, NEURON_mutation, GA_RecordingVectors
  16. h.load_file('stdrun.hoc')
  17. # Receptive fields of Excitatory and Inhibitory presynaptic populations
  18. pre_cell_types = ["excitation", "inhibition"]
  19. pre_RF_components = ["center", "surround"]
  20. global_params = {
  21. 'randomStart': True, # random params or load from file
  22. 'numPop': 10, # population size
  23. 'numGen': 300, # number of generations
  24. 'activeChannels': '',#'Ka,CaN', #CaN,CaL,caLinear, Ka,Km,IH,Kdr
  25. 'cellType': 'RGC', #['RGC', 'L23', 'L5', 'SAC', 'SAC network'], # Cell type (RGC, L23, L5, SAC, SAC-simple morphology)
  26. 'numSpeed': 3, # number of probed speeds
  27. 'numContrast': 1, # number of probed contrasts
  28. 'numDir': 2, # number of probed directions, keep 2 or more
  29. 'numPreClus': 4, # number of different presynaptic clusters with different response profiles
  30. 'numPostComp': 4, # number of different postsynaptic compartments with possible different distributions of voltage gated channels
  31. 'numVoltagePoints': 20, # number of different postsynaptic voltage points to modify the voltage gated channels, starting at -80 and increse by 5mV
  32. #compartments with possible different distributions of voltage gated channels
  33. 'numSyn': 100, # number of synaptic inputs
  34. 'distSyn': 0, # distance in microns between synapses, set to zero or negative to use random numSyn only
  35. 'dt': 10, # Time step
  36. 'mutationRate': 0.1, # Change in param values between generations
  37. # RF structue
  38. "RF_constrains": {
  39. cell_type: {
  40. cs: {
  41. 'sameKinetics': False, # Presynaptic inputs that vary in their kinetics
  42. 'sameSize': False, # Presynaptic inputs that vary in their RF size
  43. 'sameAmplitude': False, # Presynaptic inputs that vary in their strength
  44. 'sameOrientation': True,# Presynaptic inputs that vary in their RF orientation
  45. 'doSurround': False, # mutate surround (does nothing for center components)
  46. }
  47. for cs in pre_RF_components
  48. }
  49. for cell_type in pre_cell_types
  50. },
  51. 'inhibition': True, # include inhibitory presynaptic inputs
  52. # SAC NETWORK ONLY
  53. 'numSAClayersX': 1, # how many SACs are arranged around the target cell; 0 will create one cell
  54. 'numSAClayersY': 1, # how many SACs are arranged around the target cell in Y; 1 is minimal number
  55. 'distSAC': 25, # Distance between cells
  56. 'maxSAC_syndist': 15, # highest possible distance between pre and post synaptic locations
  57. # SIMULATION
  58. 'debugger': {
  59. 'run_neuron': True, # Actually run the simulation
  60. 'multithread': False, # Use multuple threads or not
  61. 'stop_mutations': False, # Do not mutate the models
  62. 'set_speed': 1, # Use this speed only (set to zero/negative to disable)
  63. 'set_dir': -1, # Use this direction only (set to negative to disable)
  64. 'plot_outcome': True, # Plot simulation results
  65. 'plot_inputs': False, # Plot the inputs
  66. 'plot_positions': False, # Plot the cell positions
  67. 'print_dsi': True, # print DSI values for all cells
  68. },
  69. #'run_debugger': True, # RUN DEBUGGER
  70. 'run_debugger': False, # TRUE to run normal sim, otherwise apply the debugger
  71. 'job_id': 0,
  72. 'save_every_gen': 10,
  73. 'numCells': 1, # number of cells
  74. 'ca_present': False, # ca conductance exists
  75. 'k_present': False, # k conductance exists
  76. 'save_all': False, # Save all parameters (takes a lot of space)
  77. }
  78. if not global_params['run_debugger']:
  79. global_params['debugger']['run_neuron']= True
  80. global_params['debugger']['multithread']= True
  81. global_params['debugger']['stop_mutations']= False
  82. global_params['debugger']['set_speed']= -1
  83. global_params['debugger']['set_dir']= -1
  84. global_params['debugger']['plot_outcome']= False
  85. global_params['debugger']['plot_inputs']= False
  86. global_params['debugger']['plot_positions']= False
  87. global_params['debugger']['print_dsi']= False
  88. #----------------------------- arguments!
  89. parser = argparse.ArgumentParser(description="Simulation Configuration")
  90. parser.add_argument('--cellType', type=str)
  91. parser.add_argument('--activeChannels', type=str)
  92. parser.add_argument('--numSAClayersX', type=int)
  93. parser.add_argument('--distSAC', type=int)
  94. parser.add_argument('--job_id', type=int)
  95. global_params.update({key: val for key, val in vars(parser.parse_args()).items() if val is not None and key in global_params})
  96. if len(global_params['activeChannels']) > 0 :
  97. print(f"active channels: {global_params['activeChannels']}")
  98. stim_params = {
  99. 'dt': global_params['dt'],
  100. 'dx': 10,
  101. 'arena': 1000,
  102. 'tStop': 3000,
  103. 'speed': 1,
  104. 'contrast': 1,
  105. 'angle': 0,
  106. 'delay': 500,
  107. 'duration': 200
  108. }
  109. class Model:
  110. pass
  111. def __init__(self):
  112. self.InputE_synapses = [] # Synaptic inputs from presynaptic populations that can have difefrent RF properties
  113. self.InputI_synapses = []
  114. self.SAC_SAC_synapses = [] # Feedback inhibition from pther SACs
  115. self.cell = [] # Morhphology etc
  116. self.input_params = {}
  117. self.output_params = {}
  118. def GA_Run(model, stim_params, global_params):
  119. h(f"forall g_pas={float(model.input_params['passive_params']['pas'])}")
  120. h(f"forall Ra={float(model.input_params['passive_params']['Ra'])}")
  121. # Flexible active conductances
  122. v_vec= np.linspace(-80, -80+5*global_params['numVoltagePoints'], global_params['numVoltagePoints']) # Voltage stops
  123. V_Vec = h.Vector(v_vec)
  124. #mtau_vec= h.Vector(model.input_params['active_params']['mtau_caGA'])
  125. #print('presetn',V_Vec.size(), mtau_vec.size()) # Should return True
  126. if "caGA" in global_params['activeChannels']: # Flexible conductance present
  127. h.table_minf_caGA(h.Vector(model.input_params['active_params']['minf_caGA']), V_Vec)
  128. h.table_mtau_caGA(h.Vector(model.input_params['active_params']['mtau_caGA']), V_Vec)
  129. h.table_hinf_caGA(h.Vector(model.input_params['active_params']['hinf_caGA']), V_Vec)
  130. h.table_htau_caGA(h.Vector(model.input_params['active_params']['htau_caGA']), V_Vec)
  131. if "kGA" in global_params['activeChannels']: # Flexible conductance present
  132. h.table_ninf_kGA(h.Vector(model.input_params['active_params']['minf_kGA']), V_Vec)
  133. h.table_ntau_kGA(h.Vector(model.input_params['active_params']['mtau_kGA']), V_Vec)
  134. h.table_hinf_kGA(h.Vector(model.input_params['active_params']['hinf_kGA']), V_Vec)
  135. h.table_htau_kGA(h.Vector(model.input_params['active_params']['htau_kGA']), V_Vec)
  136. for cell in range(global_params['numCells']):
  137. for d, sec in enumerate(model.cell[cell].all):
  138. #sec.push()
  139. type= model.output_params['cell'][cell]['activeCompartment']['type'][d]
  140. if h.ismembrane("canrgc", sec=sec): # N-type conductance present
  141. for seg in sec:
  142. seg.gbar_canrgc = model.input_params['active_params']['gbar_canrgc'][type].item()
  143. seg.shift_canrgc = model.input_params['active_params']['shift_canrgc']
  144. if h.ismembrane("calrgcfix", sec=sec): # L-type conductance present
  145. for seg in sec:
  146. seg.gbar_calrgc = model.input_params['active_params']['gbar_calrgc'][type].item()
  147. seg.shift_calrgc = model.input_params['active_params']['shift_calrgc']
  148. if h.ismembrane("caGA", sec=sec): # Flexible conductance present
  149. for seg in sec:
  150. seg.gMax_caGA = model.input_params['active_params']['gMax_caGA'][type].item()
  151. seg.mN_caGA = model.input_params['active_params']['mN_caGA']
  152. if h.ismembrane("kap", sec=sec): # A-type conductance present
  153. for seg in sec:
  154. seg.gkabar_kap = model.input_params['active_params']['gbar_ka'][type].item()
  155. seg.shift_kap = model.input_params['active_params']['shift_ka']
  156. if h.ismembrane("km", sec=sec): # M-type conductance present
  157. for seg in sec:
  158. seg.gbar_km = model.input_params['active_params']['gbar_km'][type].item()
  159. seg.shift_km = model.input_params['active_params']['shift_km']
  160. if h.ismembrane("kSlow", sec=sec): # DR-type conductance present
  161. for seg in sec:
  162. seg.gkbar_kSlow = model.input_params['active_params']['gbar_kd'][type].item()
  163. seg.v_shift_kSlow = model.input_params['active_params']['shift_kd']
  164. if h.ismembrane("ih", sec=sec): # M-type conductance present
  165. for seg in sec:
  166. seg.ghdbar_ih = model.input_params['active_params']['gbar_ih'][type].item()
  167. seg.shift_ih = model.input_params['active_params']['shift_ih']
  168. if h.ismembrane("kGA", sec=sec): # Flexible conductance present
  169. for seg in sec:
  170. seg.gMax_kGA = model.input_params['active_params']['gMax_kGA'][type].item()
  171. seg.nN_kGA = model.input_params['active_params']['nN_kGA']
  172. GA_RecordingVectors(global_params, stim_params, model, prep= True)
  173. # Run multiple simulations for a range of contrasts and velocities
  174. for speed in range(global_params['numSpeed']):
  175. # Compute speed
  176. stim_params['speed']= 1
  177. if(global_params['numSpeed'] == 5):
  178. stim_params['speed']= 2**(speed - 2)
  179. if(global_params['numSpeed'] == 3):
  180. stim_params['speed']= 4**(speed - 1)
  181. if(global_params['numSpeed'] == 2):
  182. stim_params['speed']= 0.25 + 0.75 * speed
  183. if(global_params['debugger']['set_speed'] > 0):
  184. stim_params['speed']= global_params['debugger']['set_speed']
  185. # Computation of activation params
  186. model.input_params['trajectory'] = GA_StimTrajectory(stim_params)
  187. for cls in range(global_params['numPreClus']):
  188. model.input_params['InputE_syn_time'][cls, :] = GA_Pre_activation('excitation', model.input_params['RF_params'], model.input_params['trajectory'], stim_params, pop=0, cls=cls)
  189. if global_params['inhibition']:
  190. model.input_params['InputI_syn_time'][cls, :] = GA_Pre_activation('inhibition', model.input_params['RF_params'], model.input_params['trajectory'], stim_params, pop=0, cls=cls)
  191. for contrast in range(global_params['numContrast']):
  192. stim_params['contrast']= (3**(contrast + 1)) / 3**global_params['numContrast']
  193. # Run for different directions, compute DSI and store it
  194. score_right= []
  195. score_left= []
  196. model.output_params['cell'][cell]['max_soma_single_run']= []
  197. for cell in range(global_params['numCells']):
  198. model.output_params['cell'][cell]['dendrite_vectors']['max_right_single_run']= []
  199. model.output_params['cell'][cell]['dendrite_vectors']['max_left_single_run']= []
  200. for dr in range (global_params['numDir']):
  201. stim_params['angle'] = (dr / (global_params['numDir']-1)) * np.pi
  202. if(global_params['debugger']['set_dir'] >= 0):
  203. stim_params['angle']= global_params['debugger']['set_dir']
  204. #for cell in range(global_params['numCells']):
  205. score_right.append(np.cos(stim_params['angle']))
  206. score_left.append(-np.cos(stim_params['angle']))
  207. h.tstop= NEURON_UpdateSynapses(global_params, stim_params, model)
  208. if(not global_params['debugger']['run_neuron']):
  209. h.tstop= speed + 10
  210. h.run()
  211. GA_RecordingVectors(global_params, stim_params, model, populate= True)
  212. # Compute DSI after all dirs are done - from somatic responses for RGCs and dendritic calcium levels for SACs
  213. for cell in range(global_params['numCells']):
  214. if (global_params['cellType'] in ['RGC', 'L23', 'L5']): # Somatic
  215. dsi_list=np.dot(np.array(score_right), np.array(model.output_params['cell'][cell]['max_soma_single_run'])) / sum( model.output_params['cell'][cell]['max_soma_single_run'])
  216. else: # SAC
  217. dsi_list= np.dot(np.array(score_right), np.array(model.output_params['cell'][cell]['dendrite_vectors']['max_right_single_run'])) / sum( model.output_params['cell'][cell]['dendrite_vectors']['max_right_single_run']) / 2
  218. dsi_list+= np.dot(np.array(score_left), np.array(model.output_params['cell'][cell]['dendrite_vectors']['max_left_single_run'])) / sum( model.output_params['cell'][cell]['dendrite_vectors']['max_left_single_run']) / 2
  219. model.output_params['cell'][cell]['dsi_list'].append(dsi_list)
  220. if global_params['debugger']['print_dsi']: # Report DSI values from all cells
  221. print(np.array(model.output_params['cell'][cell]['max_soma_single_run']), dsi_list)
  222. for cell in range(global_params['numCells']):
  223. model.output_params['cell'][cell]['dsi']=np.mean(model.output_params['cell'][cell]['dsi_list'])
  224. def GA_Start(conn, model, stim_params, global_params):
  225. h('v_init= -60')
  226. h('stdinit()')
  227. NEURON_SetCells(global_params, stim_params, model)
  228. while True:
  229. if conn.poll(): # Check for incoming command
  230. msg = conn.recv()
  231. response = "---"
  232. if isinstance(msg, str):
  233. if msg == "input_params":
  234. response = model.input_params
  235. if msg == 'global_params':
  236. response= global_params
  237. if msg == 'I_syn':
  238. response= len(model.SAC_SAC_synapses)
  239. elif msg == "quit":
  240. #h.quit()
  241. conn.send("end of simulation")
  242. break
  243. elif msg == "show_inputs":
  244. maxx= -10000
  245. minx= 10000
  246. for syn in model.InputE_synapses:
  247. maxx= max(maxx, syn.x)
  248. minx= min(minx, syn.x)
  249. for syn in model.InputE_synapses:
  250. plt.plot(syn.shifted_drive_vector.to_python(), color= ((syn.x - minx) / (maxx - minx), 0, 0))
  251. plt.show()
  252. response= "done plotting"
  253. #break
  254. elif isinstance(msg, dict):
  255. # Apply the passive and active params, compute the synaptic activation and run the simulation
  256. model.input_params = msg
  257. GA_Run(model, stim_params, global_params)
  258. response = model.output_params
  259. conn.send(response)
  260. if __name__ == "__main__":
  261. freeze_support()
  262. start_time = time.time() # ⏱️ Start timer
  263. # Initialize the models
  264. models=[] # Container of the NEURON models
  265. for pop in range(global_params['numPop']):
  266. model = Model()
  267. models.append(model)
  268. sim_procs = []
  269. sim_conns = []
  270. # Initialize the NEURON threads
  271. for pop in range(global_params['numPop']): # Adjust number of NEURON simulations here
  272. if(global_params['debugger']['multithread']):
  273. parent_conn, child_conn = Pipe()
  274. p = Process(target=GA_Start, args=(child_conn, models[pop], stim_params, global_params))
  275. p.start()
  276. sim_procs.append(p)
  277. sim_conns.append(parent_conn)
  278. else: # Single thread
  279. h('load_file("nrngui.hoc")')
  280. NEURON_SetCells(global_params, stim_params, model)
  281. h('load_file("neuron.ses")')
  282. h('v_init= -60')
  283. # Save a copy of the created params
  284. if(global_params['debugger']['multithread']):
  285. for pop,conn in enumerate(sim_conns):
  286. conn.send("input_params")
  287. models[pop].input_params= conn.recv()
  288. sim_conns[0].send('global_params')
  289. global_params= sim_conns[0].recv()
  290. #sim_conns[0].send('I_syn')
  291. #nSynI= sim_conns[0].recv()
  292. if(global_params['numCells'] > 1):
  293. print(f"Number of SAC-SAC synapses - {global_params['numIsyn']} , Number of cells - {global_params['numCells']}")
  294. models[0].output_params['score']= []
  295. # Generation loop
  296. for gen in range(global_params['numGen']): # number of generations
  297. gen_time = time.time()
  298. # Send new input parameters and run the simulations
  299. if(global_params['debugger']['multithread']):
  300. for pop, conn in enumerate(sim_conns):
  301. #print('send', pop, models[pop].input_params['RF_params']['excitation']['center']['peak'])
  302. conn.send(models[pop].input_params)
  303. # Gather values
  304. for pop,conn in enumerate(sim_conns):
  305. models[pop].output_params= conn.recv()
  306. conn.send("input_params")
  307. models[pop].input_params= conn.recv()
  308. #print('back',pop, models[pop].input_params['RF_params']['excitation']['center']['peak'])
  309. else:
  310. for pop in range(global_params['numPop']):
  311. GA_Run(models[pop], stim_params, global_params)
  312. # Find the model with the largest DSI
  313. best_dsi= -1
  314. best_pos= 0
  315. for pop, model in enumerate(models):
  316. if(model.output_params['cell'][0]['dsi'] > best_dsi):
  317. best_dsi= model.output_params['cell'][0]['dsi']
  318. best_pos= pop
  319. if global_params['debugger']['print_dsi']:
  320. print(f"Model={pop}, DSI={model.output_params['cell'][0]['dsi']}. Best pos={best_pos} ({best_dsi})")
  321. print(f"Done generation {gen}, best score= {best_dsi}, time - {(time.time()-gen_time):.4f}")
  322. models[0].output_params['score'].append(best_dsi) # Save progress
  323. #print(f"Gen - {gen}, time - {(time.time()-gen_time):.4f}")
  324. if((gen%(global_params['save_every_gen'])) == (global_params['save_every_gen']-1)):
  325. GA_SaveH5(global_params, models, final= False)
  326. with open(f"params/input_params_{global_params['job_id']}.pkl", "wb") as f:
  327. pickle.dump(models[0].input_params, f)
  328. # Duplicate best model
  329. if gen < global_params['numGen'] - 1:
  330. for pop, model in enumerate(models):
  331. if pop != best_pos:
  332. models[pop] = copy.deepcopy(models[best_pos])
  333. if global_params['debugger']['print_dsi']:
  334. print(f'Copy {best_pos} into {pop}')
  335. # Mutations
  336. if (global_params['debugger']['stop_mutations'] == False):
  337. for pop, model in enumerate(models):
  338. if(pop > 0): # keep the first model intact
  339. NEURON_mutation(global_params, model.input_params)
  340. if global_params['debugger']['print_dsi']:
  341. print(f'Mutate {pop}')
  342. # End generation loop
  343. #models[0].output_params['score']= score # Save progress
  344. if (global_params['debugger']['plot_inputs']):
  345. sim_conns[0].send("show_inputs")
  346. dummy= conn.recv()
  347. # Cleanup
  348. if(global_params['debugger']['multithread']):
  349. for conn in sim_conns:
  350. conn.send("quit")
  351. dummy= conn.recv()
  352. for p in sim_procs:
  353. p.join()
  354. print("All NEURON simulations completed.")
  355. end_time = time.time() # ⏱️ End timer
  356. elapsed_time = end_time - start_time
  357. # Save to HDF5
  358. GA_SaveH5(global_params, models, final= True)
  359. print("Saved H5 File")
  360. with open(f"params/input_params_{global_params['job_id']}.pkl", "wb") as f:
  361. pickle.dump(models[0].input_params, f)
  362. print(f"Execution Time: {(elapsed_time / 60):.2f} minutes")
  363. if (global_params['debugger']['plot_outcome']):
  364. #print("op ",models[0].output_params)
  365. fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(8, 6), height_ratios=[1, 1, 1])
  366. ax3.plot(models[0].output_params['score'], color='black')
  367. for cell in range(global_params['numCells']):
  368. for v in model.output_params['cell'][cell]['somaV_all_angles']:
  369. ax1.plot(v)
  370. if (global_params['cellType'] in ['RGC', 'L23', 'L5']): # Somatic
  371. pass
  372. else:
  373. for ca in model.output_params['cell'][0]['dendrite_vectors']['ca_right_all_angles']:
  374. ax2.plot(ca, color= 'orange')
  375. for ca in model.output_params['cell'][0]['dendrite_vectors']['ca_left_all_angles']:
  376. ax2.plot(ca, color= 'black')
  377. plt.show()
  378. #for cell in range(global_params['numCells']):
  379. if (global_params['debugger']['plot_positions']):
  380. plt.plot(models[0].input_params['cellPos'][:][0],models[0].input_params['cellPos'][:][1])
  381. x_coords, y_coords = zip(*models[0].input_params['cellPos'])
  382. plt.scatter(x_coords, y_coords)
  383. plt.show()

main.py at commit 6534bcd, no license · at the source

Overview

  1. Department of Physiology and Biophysics, and Neuroscience Program, University of Colorado School of Medicine,Aurora, CO USA
Institutions: University of Colorado Anschutz (United States)
Journal: Nature communications, volume 17, issue 1, article 3424
Dates: received 28 March 2025; accepted 24 February 2026; published online 3 March 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1038/s41467-026-70288-4 · PMID 41776191 · PMCID PMC13076616 · OpenAlex W7133356062
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: human (organism)
Methods: Preprocessing, Evoked potentials, Statistics, fMRI & imaging
Keywords: Learning algorithms, Motion detection, Sensory processing
MeSH: Machine Learning*, Models, Neurological*, Motion Perception*, Animals, Humans, Motion, Retina, Soft Computing, Visual Cortex (* major topic)
Topic: Visual perception and processing mechanisms (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Funding: NIH (R01EY030841, R01EY035293)
Citations: cited by 1 paper (Europe PMC); 95 references in the paper

Abstract

Motion direction detection is a fundamental visual computation that transforms spatial luminance patterns into directionally tuned outputs. Classical models of direction selectivity rely on temporal asymmetry, where motion detection arises through either delayed excitation or inhibition. Here, I used biologically inspired machine learning applied to retinal and cortical circuits to uncover receptive field architectures capable of direction selectivity. These include mechanisms based on asymmetric synaptic properties, spatial receptive field variations, new roles for pre- and postsynaptic inhibition, and previously unrecognized kinetic implementations. Conceptually, these circuit architectures cluster into eight computational primitives underlying motion detection, four of which are previously undescribed. Many of the solutions rival or outperform classical models in both robustness and precision, and several exhibit enhanced noise tolerance. All mechanisms are biologically plausible and correspond to known physiological and anatomical motifs, offering fresh insights into motion processing and illustrating how machine learning can uncover general principles of neural computation.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repository

Its files are read in the Code ↔ Paper reader above, with 4 matches between paragraphs and lines of code.

PolegPolskyLab/DS-mechanisms

License: none: the authors keep all their rights
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Commit: 6534bcd01294c027960c5eaf35e71ba4a69d4f68, 14 June 2025
Languages: NEURON (19), Python (4), C (1)
Size: 27 files, 24 scripts
Software Heritage: not archived
Found in: “Code availability”
Holds: README
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NEURON (21 files), NumPy (4 files), Matplotlib (2 files), h5py (1 file)
Availability: 1 check, the latest on 30 September 2026: the link answers
  • 30 September 2026: the link answers
25 files

Code availability

Simulation code can be found in the following repository: https://github.com/PolegPolskyLab/DS-mechanisms.

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:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 24 scripts, each with its path and the digest of its content;
  • 4 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

Source Data are provided with this paper.

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, 30 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 1 author, 3 keywords, 9 MeSH terms, 1 funder, 95 references.

Cite

This paper

Poleg-Polsky, A. (2026). Machine learning discovers numerous new computational principles supporting elementary motion detection. Nature communications, 17(1), 3424. https://doi.org/10.1038/s41467-026-70288-4

BibTeX

@article{polegpolsky2026machine,
author = {Poleg-Polsky, Alon},
title = {{Machine learning discovers numerous new computational principles supporting elementary motion detection}},
journal = {Nature communications},
year = {2026},
month = mar,
volume = {17},
number = {1},
pages = {3424},
publisher = {Nature Publishing Group},
issn = {2041-1723},
doi = {10.1038/s41467-026-70288-4},
url = {https://doi.org/10.1038/s41467-026-70288-4},
pmid = {41776191},
pmcid = {PMC13076616}
}

RIS

TY - JOUR
AU - Poleg-Polsky, Alon
TI - Machine learning discovers numerous new computational principles supporting elementary motion detection
T2 - Nature communications
J2 - Nat Commun
PY - 2026
DA - 2026/03/03
VL - 17
IS - 1
SP - 3424
SN - 2041-1723
PB - Nature Publishing Group
DO - 10.1038/s41467-026-70288-4
UR - https://doi.org/10.1038/s41467-026-70288-4
LA - en
ER -

CSL-JSON

{
"id": "10.1038/s41467-026-70288-4",
"type": "article-journal",
"title": "Machine learning discovers numerous new computational principles supporting elementary motion detection",
"container-title": "Nature communications",
"author": [
{
"family": "Poleg-Polsky",
"given": "Alon"
}
],
"container-title-short": "Nat Commun",
"volume": "17",
"issue": "1",
"page": "3424",
"DOI": "10.1038/s41467-026-70288-4",
"PMID": "41776191",
"PMCID": "PMC13076616",
"ISSN": "2041-1723",
"publisher": "Nature Publishing Group",
"URL": "https://doi.org/10.1038/s41467-026-70288-4",
"language": "en",
"issued": {
"date-parts": [
[
2026,
3,
3
]
]
}
}

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.7554/elife.108529 [code]
Inhibitory columnar feedback neurons are involved in motion processing in &lt;i&gt;Drosophila&lt;/i&gt;.
Journal: eLife
In common: h5py, Matplotlib, NumPy, 8 references
[2] doi:10.1016/j.celrep.2026.117476 [code]
Retinal waves shape starburst amacrine cell dendrite development through a direction-selective dendritic computation.
Journal: Cell reports
In common: Matplotlib, NumPy, 6 references
[3] doi:10.1126/sciadv.aec3961 [code]
Linking functional and structural dendritic spine remodeling during fear learning and extinction in vivo.
Journal: Science advances
In common: NEURON, Matplotlib, NumPy, 3 references
[4] doi:10.7554/elife.89629 [code]
Active dendrites enable robust spiking computations despite timing jitter.
Journal: eLife
In common: NEURON, Matplotlib, NumPy, 2 references
[5] doi:10.1073/pnas.2533168123 [code]
Dendritic morphology and synaptic nonlinearities enhance functional complexity in human cortical neurons.
Journal: Proceedings of the National Academy of Sciences of the United States of America
In common: NEURON, h5py, Matplotlib, 1 other tool, 1 reference
[6] doi:10.1126/sciadv.adz4123 [code]
Highly attenuated dendritic propagation of isolated synaptic potentials in vivo.
Journal: Science advances
In common: 4 references
[7] doi:10.1371/journal.pcbi.1013138 [code]
Hierarchical recurrent temporal prediction as a model of the mammalian dorsal visual pathway.
Journal: PLoS computational biology
In common: h5py, Matplotlib, NumPy, 2 references
[8] doi:10.7554/elife.95562 [code]
Spatially targeted inhibitory rhythms differentially affect neuronal integration.
Journal: eLife
In common: h5py, Matplotlib, NumPy, 2 references
[9] doi:10.1371/journal.pcbi.1014283 [code]
Spatial richness of neural magnetic fields.
Journal: PLoS computational biology
In common: NEURON, h5py, Matplotlib, 1 other tool
[10] doi:10.1016/j.isci.2026.115488 [code]
An integrated &lt;i&gt;i&lt;/i&gt; &lt;i&gt;n vitro&lt;/i&gt; platform and biophysical modeling approach for studying synaptic transmission in isolated neuronal pairs.
Journal: iScience
In common: NEURON, h5py, Matplotlib, 1 other tool

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.

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.