OSCR

Electrophysiologically informed spiking neural networks for fish-inspired navigation with boundary vector cells and hydrostatic pressure cues.

Code ↔ Paper

3 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 3 matches
  1. [1] § STAR★Methods › Method details › Motor control system ↔ mdl_NAVIGATE.py, lines 272–353 · score 0.51 · Basal ganglia, BG, Thalamus, filtering, noise, maximal
  2. [2] § STAR★Methods › Method details › Motor control system ↔ mdl_NAVIGATE_AZ.py, lines 259–341 · score 0.51 · Basal ganglia, BG, Thalamus, filtering, noise, maximal
  3. [3] § Results ↔ run_EXPERIMENT.py, lines 89–159 · score 0.50 · HP_max, navigation strategies, inter, Azimuth, agent, simulated

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 · 802 lines · 40 KB · no license · 1 match

  1. import random
  2. import numpy as np
  3. import nengo
  4. import nengo_spa as spa
  5. import math, pickle, time
  6. import cv2
  7. from numpy import ndarray
  8. from scipy import stats
  9. from utils import mypause
  10. import matplotlib, matplotlib.pyplot as plt
  11. from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
  12. from scipy.io import savemat
  13. import nengo_ocl
  14. import os
  15. class BVC_NAV:
  16. def __init__(self,Path,m_tau,steer_thres,telen_neurons,ang_bin,corals_pos,Goal_pos,agent_init_pos,df,eps,free_turn_bins,
  17. max_bins_turn,seed,sim_time,sim_num,plt,vid):
  18. self.sig_thrsh = 10 ** steer_thres
  19. self.m_tau = m_tau
  20. #force-steer
  21. # self.forceSteer=None
  22. #model parameters
  23. self.goal_reached=0 #this will be 1 when simulation is over
  24. self.stop_sim = False # this will be TRUE when simulation is over
  25. self.seed=None
  26. self.sim_time=sim_time #maximal simulation time in sec
  27. # default parameters
  28. self.telen_neurons = telen_neurons # ~total units in the telencephalon
  29. self.init_pos=agent_init_pos #start maze here
  30. self.x = agent_init_pos[0]
  31. self.y = agent_init_pos[1]
  32. self.amp_gain = 1 / 100 #gain for 'simple' neurons (so FR is in range 1-4)
  33. self.ang_bin_size_deg = ang_bin #BVC directional resolution
  34. self.n_bins_max_turn = max_bins_turn #maximal direction (in bins of ang_bin_size_deg each) to consider when turning
  35. self.n_bins_clear_path = free_turn_bins #how many bins to test if path is clear
  36. self.turn_angle_CCW=0 #initial turn angle
  37. self.smooth_coeff=.9 #for each turn, take into account 0.25 from previous turn
  38. self.steer_out=float(0) #by default, do not turn
  39. self.df=df #factor that modulates the minimal distance for slowing down from 20 cm
  40. self.eps=eps #distance from coral to fully stop
  41. #arena parameters
  42. self.arena_size=[4, 4] #arena size in [m]
  43. self.corals_pos = corals_pos
  44. self.n_corals=len(corals_pos)
  45. self.Goal_pos = Goal_pos #feeding position
  46. # self.GoalDirection=np.arctan2( (Goal_pos[1]-self.init_pos[1]),
  47. # (Goal_pos[0]-self.init_pos[0]) )/np.pi #initial goal-direction
  48. # self.GoalDirection=float(random.randrange(-10,10))/10 # random direction -1:0.1:1
  49. ####################### below should be replaced with automatic wall avoidance!#########
  50. x=np.sign(agent_init_pos[0])
  51. y=np.sign(agent_init_pos[1])
  52. if (x < 0 and y < 0):
  53. q=3
  54. elif (x < 0 and y > 0):
  55. q=2
  56. elif (x > 0 and y > 0):
  57. q=1
  58. else:
  59. q=4
  60. self.GoalDirection = float(random.randrange(-15+5*q, -10+5*q)) / 10 # random direction -1:0.1:1
  61. ######################################################################################
  62. #probe-plot parameters
  63. self.plot_config=plt
  64. self.vid_flag=vid
  65. # if self.plot_config:
  66. # self.num_plots=0
  67. # plt.ion()
  68. # plt.show()
  69. self.probes={}
  70. #save data parameters
  71. folder_path = "Matlab Output/" + Path + "/" # output directory
  72. os.makedirs(folder_path, exist_ok=True) # create output directory if it doesn't exist already
  73. mat_folder_name = folder_path + "/" + Path + " mat" # output directory for mat files
  74. os.makedirs(mat_folder_name, exist_ok=True) # create output directory if it doesn't exist already
  75. self.mat_name = mat_folder_name + "/SIM_" + str("%.3d" % sim_num)
  76. self.pic_name = "Matlab Output/SIM_" + str("%.3d"%sim_num)
  77. self.input_params_dict = {"telen_neurons": telen_neurons, "sim_time": sim_time,
  78. "FOV_res":ang_bin,"seed":seed,"arena_size":self.arena_size,
  79. "Goal_pos":Goal_pos,"corals_pos":corals_pos,"DistSlow":df*20,
  80. "DistStop":eps,"FrontFOV":max_bins_turn*2*ang_bin,
  81. "FreePathFOV":ang_bin*(1+free_turn_bins),"SmoothTurnCoef":self.smooth_coeff}
  82. # construct neuromorphic model
  83. self.dt = 0.001 #simulation step size
  84. self.buildModel()
  85. def buildModel(self):
  86. # service function to add named probes
  87. def _add_probe(element, label):
  88. self.probes[label] = nengo.Probe(element, label=label, synapse=0.01)
  89. # if self.plot_config:
  90. # plt.figure(self.num_plots)
  91. # self.num_plots += 1
  92. # limit simulation time function
  93. def _max_stop_time(t):
  94. if t>self.sim_time: #maximal simulation time if goal wasn't reached
  95. self.stop_sim = True
  96. return self.stop_sim
  97. ################### Boundary encoding parameters ###########################
  98. ''' #optional- load exsiting distributions
  99. save_path = 'C:/Users\Dell/anaconda3\envs/nengo\Scripts\Lear/'
  100. BVC_intercepts = np.load(save_path + 'BVC_intercepts.npy')
  101. BVC_Rates = np.load(save_path + 'BVC_Rates.npy')
  102. '''
  103. ################## Fish BVC intercepts Distribution #####################
  104. fish_receptive_fields = \
  105. [0.303565699399023, 0.150651881887490, 0.612954089261763,
  106. 1.32902305046175, 0.237358465697817, 0.572593575787876,
  107. 0.804672645959743, 0.468032848207433, 0.465103040497480,
  108. 0.538975230941517, 0.131637644359929, 1.79196669576041,
  109. 0.702770519299926, 0.346483106391552, 0.415090154920481,
  110. 1.42381073514439, 0.218941453593792, 1.28263930362589,
  111. 0.319611957288709, 0.296690418408916, 0.127691451940062,
  112. 0.821042813569916, 0.611300555983888, 0.0706456431783326,
  113. 1.83412056590551, 1.78588767232961, 0.543818191887459,
  114. 0.820062851642834, 1.00724116911013, 0.189768947502303,
  115. 0.900946696238341, 0.228138317181491, 0.0195893010264571,
  116. 1.86946161637729, 1.70685573025602]
  117. fish_receptive_fields_cm = np.array(fish_receptive_fields,
  118. dtype=float) * 0.7 # make it in cm
  119. # estimate distribution
  120. ae, loce, scalee = stats.skewnorm.fit(fish_receptive_fields_cm)
  121. ################### Fish BVC Rate Distribution #####################
  122. PeakRateBVC_Fish = \
  123. [1.23179012004551, 0.652634682539281, 4.09152522548310,
  124. 1.59885112929847, 1.14081018374045, 6.54274691116549,
  125. 2.21902173988490, 1.53838548171947, 10.2606854396450,
  126. 0.326835293519020, 0.107522721333319, 2.38158063055244,
  127. 0.199934324014590, 2.04147978313486, 2.09880178896422,
  128. 1.99088166689020, 2.76220348861795, 5.51890082912580,
  129. 4.23048845852580, 2.48385349846702, 6.14630410033045,
  130. 0.392574798790659, 1.25628115518902, 1.05271181026214,
  131. 0.529461993040073, 0.470916168665174, 3.25946160652742,
  132. 0.557688406685955, 3.30565839449426, 1.51284640149477,
  133. 2.06711978413453, 0.610938792413799, 0.441433752122931,
  134. 2.15427182373612, 6.68880488440973]
  135. ae_rate, loce_rate, scalee_rate = stats.skewnorm.fit(PeakRateBVC_Fish)
  136. ################### Fish HP cells Rate Distribution #####################
  137. HP_rate = [0.2004, 0.2493, 0.2421, 0.4544, 0.2421, 0.4690,
  138. 0.4583, 0.5749, 2.2806, 0.9800]
  139. hp_rate, hploc_rate, hpscale_rate = stats.skewnorm.fit(HP_rate)
  140. '''
  141. ################### Fish velocity Rate Distribution #####################
  142. Vel_rate = [0.197338698747390, 0.380581776155509, 0.00502881088493350,
  143. 0.0364070833883619,
  144. 0.0274849640740761, 0.0399434968347826, 0.0959080169846669,
  145. 0.0626902630243608,
  146. 0.984263214195281, 0.141873793252625, 0.0179027099720430,
  147. 0.278798500697151,
  148. 0.0272255916653539, 4.40721626968568, 0.0839577370743340,
  149. 11.9883355299775,
  150. 0.248319274692277, 0.328447548766507, 0.230424234436725,
  151. 0.164223774383254,
  152. 0.866626664817651, 0.817386513407557, 1.41039241529147,
  153. 0.199830486833172]
  154. v_rate, vloc_rate, vscale_rate = stats.skewnorm.fit(Vel_rate)
  155. ################### Fish velocity receptive field Distribution #####################
  156. vel_rec = [8.01242153733998, 8.25676942317176, 5, 1, 5,
  157. 3.74763034523908,
  158. 0.559199852102832,
  159. 13.0357053721238, 0.196123594968107, 0.503285030846904, 5,
  160. 5.85266779897490,
  161. 0.829365741476824, 8.28690525620088, 0.0326276173643798,
  162. 22.9369932190041,
  163. 12.3153891189659, 10.1130683005553, 2.79621194110437,
  164. 2.43798519662587,
  165. 11.1893592815419, 8.91421085953175, 8.06612542053255,
  166. 0.281776502680117]
  167. vel_rec = np.array(vel_rec, dtype=float) * 0.01 # make it in cm
  168. v_rec, vloc_rec, vscale_rec = stats.skewnorm.fit(vel_rec)
  169. '''
  170. ##########################################################################
  171. # values to test
  172. telen_neurons = self.telen_neurons
  173. ang_bin_size_deg = self.ang_bin_size_deg
  174. n_bins_max_turn = self.n_bins_max_turn
  175. n_bins_clear_path=self.n_bins_clear_path
  176. corals_pos = self.corals_pos
  177. n_corals = len(corals_pos) # total numer of corals
  178. Goal_pos = self.Goal_pos
  179. arena_size = self.arena_size # arena size in meter
  180. #constant parameters
  181. tau = 0.1 #synaptic time for path integration
  182. tau2=2*tau/10 #default synaptic time for other units
  183. attractor_scale = 1#0.1 # scale attracotr ensemble
  184. max_speed = 0.5 # up to 50cm/s
  185. speed_shift = -max_speed / 2 # shift for symmetricity purposes
  186. v0 = max_speed # desired (initial) speed
  187. n_neuron_prop = np.round(np.array([20/132,
  188. 35/196])*telen_neurons) #proportional n: vel/hd/speed, BVC
  189. n_neurons=int(n_neuron_prop[0]) #for the speed/hd/velocity units
  190. self.max_angle = np.pi # normalization factor for angles
  191. fish_size = 0.1 # fish radius to avoid collisions - 10cm
  192. self.coral_size = 0.15 # coral radius to avoid collisions - 15cm
  193. feed_visual_dist = 2 * self.coral_size # distance in which food is visual for sure
  194. #BVC parameters:
  195. self.BVC_rec_field = 1.4 + self.coral_size # BVC max receptive field
  196. n_LIDAR_pts = int(360 / ang_bin_size_deg) #n bvc directions
  197. neurons_per_angle = int(n_neuron_prop[1]/n_LIDAR_pts) #neurons per dim
  198. angle_array = np.linspace(0,
  199. 2 * self.max_angle - 2 * self.max_angle / n_LIDAR_pts,
  200. n_LIDAR_pts) #LIDAR array 0 to 2pi
  201. ang_bin_size = angle_array[1] - angle_array[0] #effectively ang_bin_size_deg in [rad]
  202. # Speed control parameters
  203. momentum_coeff = 0.1 # how fish shape distores its breaks ability
  204. eps = self.eps # distance threshold for full-brakes - 1 cm
  205. deceleration_coeff = 3 # [tested in matlab]
  206. v_eps = deceleration_coeff * eps
  207. D = (momentum_coeff * max_speed ** 2 + max_speed / deceleration_coeff +
  208. eps + fish_size + self.coral_size) * self.df # *3 # Effective Distance (df*20cm) to start slow down:
  209. # define initial position
  210. def init_pos(t):
  211. if t < tau:
  212. return self.init_pos
  213. else:
  214. return [0, 0]
  215. # angle/distance functions
  216. def calc_dist(x):
  217. return (x[0] ** 2 + x[1] ** 2) ** 0.5
  218. def calc_angle(t, x):
  219. return np.arctan2(x[1], x[0]) / self.max_angle
  220. # speed and direction --> velocity
  221. def get_VxVy(t, x):
  222. return [x[1] * np.cos(x[0]), x[1] * np.sin(x[0])]
  223. def read_LIDAR(t, x):
  224. X, Y, HD = x
  225. distArray = self.BVC_rec_field * np.ones(n_LIDAR_pts)
  226. #implement arena edges into array
  227. self.x_out=1 #variable that helps tune the ydirection
  228. m_eps = 3 #how many epsilons from the wall to allow freely
  229. n_bins_quartile = int(n_LIDAR_pts / 4) # how many bins to block entire side
  230. bin_hd = int((np.abs(angle_array - (HD + 2 * np.pi) % (2 * np.pi) )).argmin()) # index of current HD
  231. if self.x + self.arena_size[0] / 2 < m_eps * self.eps: # wall on the left
  232. self.x_out = 0.8
  233. Q = int(0.5 * n_LIDAR_pts)
  234. wall_inds = list( (np.arange(Q - n_bins_quartile - bin_hd + 1, Q + n_bins_quartile - bin_hd))%(n_LIDAR_pts) )
  235. distArray[wall_inds] = m_eps * self.eps
  236. if -self.x + self.arena_size[0] / 2 < m_eps * self.eps: # wall on the right
  237. self.x_out = 1.2
  238. Q = int(0)
  239. wall_inds = list( (np.arange(Q - n_bins_quartile - bin_hd + 1, Q + n_bins_quartile - bin_hd))%(n_LIDAR_pts) )
  240. distArray[wall_inds] = m_eps * self.eps
  241. if self.y + self.arena_size[1] / 2 < m_eps * self.eps: # wall on the bottom
  242. Q = int(0.75 * n_LIDAR_pts)
  243. wall_inds = list( (np.arange(Q - n_bins_quartile - bin_hd + 1, Q + n_bins_quartile - bin_hd))%(n_LIDAR_pts) )
  244. distArray[wall_inds] = m_eps * self.eps
  245. if -self.y + self.arena_size[1] / 2 < m_eps * self.eps: # wall on the top
  246. Q = int(0.25 * n_LIDAR_pts)
  247. wall_inds = list( (np.arange(Q - n_bins_quartile - bin_hd + 1, Q + n_bins_quartile - bin_hd))%(n_LIDAR_pts) )
  248. distArray[wall_inds] = m_eps * self.eps
  249. # distArray+=white_noise
  250. angle_all= np.ones(n_corals) #allocate memory (default values)
  251. dist_all=self.BVC_rec_field * np.ones(n_corals) #allocate memory (default values)
  252. for i in range(0, n_corals):
  253. dy = corals_pos[i][1] - Y
  254. dx = corals_pos[i][0] - X
  255. angle = np.arctan2(dy, dx)-HD
  256. angle_all[i] = (angle + 2 * np.pi) % (
  257. 2 * np.pi) # shift to [0,2pi] representation
  258. dist_all[i]=np.clip(calc_dist([dx, dy]), 0,self.BVC_rec_field) # fit to max receptive field
  259. #get coral index by sorted distance from far to near:
  260. Coral_sort_ind = np.argsort(dist_all)
  261. Coral_sort_ind=Coral_sort_ind[::-1] #sort descending order
  262. for i in range(0, n_corals): #now go coral by coral from further to nearer
  263. #read angle and distance from previous loop
  264. angle=angle_all[Coral_sort_ind[i]]
  265. dist=dist_all[Coral_sort_ind[i]]
  266. #take coral width into account for LIDAR read:
  267. coral_Aperture = 2*np.arctan(self.coral_size/dist) #portion of visual field for the coral
  268. coral_LIDAR_pts = np.round(coral_Aperture/ang_bin_size) #how many bins represented by this coral
  269. angle_array_idx = (np.abs(angle_array - angle)).argmin() #angle argument index in LIDAR array
  270. if coral_LIDAR_pts==2:
  271. if angle_array[angle_array_idx]>angle:
  272. angle_array_idx=np.arange(angle_array_idx-1,angle_array_idx+1,1) #add angle to the right
  273. else:
  274. angle_array_idx = np.arange(angle_array_idx, angle_array_idx+2, 1) # add angle to the left
  275. elif coral_LIDAR_pts>2: #maximal 3 angles per coral, add 1 from each side
  276. angle_array_idx = np.arange(angle_array_idx-1, angle_array_idx + 2, 1)
  277. if coral_LIDAR_pts>1:
  278. angle_array_idx=list(np.mod(angle_array_idx,n_LIDAR_pts)) #fold to arrays range, make list
  279. distArray[angle_array_idx] = dist #implement to array
  280. #this defines priorities in cases there are many optional turning angles
  281. priority_noise=np.abs((np.round(n_LIDAR_pts/2)-np.arange(0,n_LIDAR_pts))/20000)
  282. return distArray + priority_noise - self.BVC_rec_field #return array shifted to [-maxFiels, 0]
  283. # define turning function
  284. def Turn(V, angle):
  285. """
  286. Rotate a point counterclockwise by a given angle around [0,0].
  287. The angle should be given in radians.
  288. """
  289. vx, vy = V
  290. vx_rot = math.cos(angle) * (vx) - math.sin(angle) * (vy)
  291. vy_rot = math.sin(angle) * (vx) + math.cos(angle) * (vy)
  292. return vx_rot, vy_rot
  293. def Steer(t,x):
  294. def _set_steer(t,DA):
  295. # sig_thrsh = 0.1 # threshold for recognizing steering direction
  296. ind = np.argmax(x) # select max. steer direction
  297. if DA[ind] < self.sig_thrsh: # if under threshold, drive forward
  298. steer = 0
  299. else:
  300. steer = ang_bin_size*(ind-n_bins_max_turn)
  301. # self._ipc_state.steer = self.steer # set steer command
  302. return steer
  303. def getBVC_net(x):
  304. with nengo.Network() as BVC_net:
  305. BVC_net.input=nengo.Node(x)
  306. BVC_net.output=nengo.Node(output=None, size_in=len(x))
  307. nengo.Connection(BVC_net.input,BVC_net.output)
  308. return BVC_net
  309. with nengo.Network() as tmp_net:
  310. BVC_array=getBVC_net(x) #get BVC array as input node
  311. n_sample_filter = len(x) # number of channels remaining
  312. bg = nengo.networks.BasalGanglia(
  313. n_sample_filter) # basal ganglia, use default number of neurons/ensemble
  314. # if self.steer_model:
  315. # nengo.Connection(ext.output, bg.input, transform=0.75)
  316. # else:
  317. nengo.Connection(BVC_array.output, bg.input)#, transform=1)
  318. # _add_probe(bg.output, "bg_output")
  319. th = nengo.networks.Thalamus(n_sample_filter) # thalamus, use default number of neurons/ensemble
  320. nengo.Connection(bg.output, th.input)
  321. # _add_probe(th.output, "th_output")
  322. steer_out = nengo.Node(output=_set_steer, size_in=n_sample_filter, size_out=1) # connect to output
  323. nengo.Connection(th.output, steer_out)
  324. # Integrate steer over time. Using longer synapse to smooth-out noise
  325. steer_tau_i = self.m_tau*tau
  326. steer_sum_enc = nengo.Ensemble(n_neurons=n_neurons, dimensions=1, radius=ang_bin_size*n_bins_max_turn)
  327. nengo.Connection(steer_out, steer_sum_enc, transform=steer_tau_i)
  328. nengo.Connection(steer_sum_enc, steer_sum_enc, function=lambda x: x, synapse=steer_tau_i)
  329. steer_sum=nengo.Node(size_in=1)
  330. nengo.Connection(steer_sum_enc,steer_sum)
  331. self.steer_out=0 if steer_sum.output is None else steer_sum.output
  332. # nengo.Probe(steer_sum_enc,'Steer', synapse=0.01)
  333. # self.simSteer = nengo.Simulator(tmp_net, dt=0.01, seed=self.seed, optimize=True)
  334. return self.steer_out
  335. # conditioned speed function
  336. def _get_potential_velocity(t, bvns_data):
  337. # default outputs
  338. v = bvns_data[:2] # inital velocity
  339. min_dist = self.eps # minimal distance to enable speed
  340. dist_per_angle = bvns_data[
  341. 2:] + self.BVC_rec_field # distance per egocentric angle [0,2pi]
  342. dist_FRONT = np.min(np.concatenate(
  343. (dist_per_angle[:n_bins_clear_path+1],
  344. dist_per_angle[-n_bins_clear_path:]))
  345. ) # front distance
  346. dist_0 = dist_per_angle[0]- fish_size/2 - self.coral_size #avoid f2f collision
  347. d = dist_FRONT - fish_size/2 - self.coral_size # effective distance from boundary
  348. # do I need to slow down? get proportion:
  349. relative_D = (d - min_dist) / (
  350. D - min_dist) # relative distance
  351. # go axis by axis and slow/speed
  352. v1 = [0, 0] # initialize
  353. for ax in [0, 1]:
  354. curr_speed = np.abs(v[ax])
  355. curr_dir = np.sign(v[ax])
  356. # if getting close
  357. if d < D:
  358. speed = min((curr_speed + v_eps) * relative_D,
  359. curr_speed)
  360. # if far enough, accelerate to max_speed
  361. else:
  362. speed = min(v[ax] + deceleration_coeff * tau,
  363. max_speed)
  364. # v1[ax] = curr_dir * np.abs(speed)
  365. v1[ax] = curr_dir * np.clip(np.abs(speed),0,max_speed)
  366. v = v1 # set desired velocity
  367. if 1:# try to go desired direction
  368. des_HD = np.mod(bvns_data[-1],
  369. 2 * np.pi) # shift to [0,2pi] representation
  370. LIDAR_des_HD_ind = (np.abs(angle_array - des_HD)).argmin()
  371. all_des_HD_inds = list(np.mod(
  372. range(LIDAR_des_HD_ind - n_bins_clear_path,
  373. LIDAR_des_HD_ind + n_bins_clear_path + 1), n_LIDAR_pts
  374. ))
  375. # all_des_HD_inds = np.mod(all_des_HD_inds,
  376. # n_LIDAR_pts) # shift list to 0,n_LIDAR_pts-1 range
  377. dist_des_HD = dist_per_angle[list(all_des_HD_inds)]
  378. if np.max(dist_des_HD)>D: #if path is clear
  379. max_dir_ind=np.argmax(dist_des_HD) #choose clearest path
  380. turn_angle_CCW=all_des_HD_inds[max_dir_ind]*ang_bin_size
  381. # turn_angle_CCW=des_HD
  382. # v=Turn(v,turn_angle_CCW)
  383. # if this fails, maximize distance in front:
  384. else:
  385. # best turning in range [-self.max_angle_turn,self.max_angle_turn],
  386. dist_array_wide_front = np.concatenate(
  387. (dist_per_angle[-(n_bins_max_turn+1):],
  388. dist_per_angle[:n_bins_max_turn+1+1]))
  389. # # #average with neighbours
  390. # dist_array_wide_front= (dist_array_wide_front[0:-2]+
  391. # dist_array_wide_front[1:-1]+
  392. # dist_array_wide_front[2:])/3
  393. turn_angle_CCW=Steer(t, dist_array_wide_front) #assign steer_out to self ###self.steer_out
  394. # turn_angle_ind = np.argmax(
  395. # dist_array_wide_front) - n_bins_max_turn # ind in [-n/2,n/2]
  396. # # use the bin as direction, but make turn smooth (size/10)
  397. # turn_angle_CCW = turn_angle_ind * ang_bin_size # actual angel to turn CCW
  398. #perform turn
  399. # smooth with previous turn
  400. self.turn_angle_CCW = self.smooth_coeff*turn_angle_CCW+(1-self.smooth_coeff)*self.turn_angle_CCW
  401. v = Turn(v, self.turn_angle_CCW)
  402. if np.mod(t,1)<self.dt:
  403. self.GoalDirection = calc_angle(t, v) #set new direction to the current (only for this model!)
  404. return np.array([v[0], v[1], dist_FRONT, self.turn_angle_CCW])
  405. # def feed_or_not(t, x):
  406. # if calc_dist(x) < feed_visual_dist*attractor_scale:
  407. # attract=[1]
  408. # else:
  409. # attract=[0]
  410. # return attract
  411. threshold_attract=0.1
  412. def Food_Stim(t, x):
  413. if x[0]>threshold_attract:
  414. return x[0] * x[1], x[0] * x[2]
  415. else:
  416. return 0* x[1],0* x[2]
  417. def getGoalDirection(t):
  418. return self.GoalDirection
  419. # stop simulation variable
  420. def goal_reached(t, x):
  421. if calc_dist(x) < fish_size and t>0.1: #delay initial condition
  422. self.stop_sim = True
  423. return 1
  424. else:
  425. return 0
  426. self.model = spa.Network("BVC_NAV", seed=self.seed)
  427. with self.model:
  428. # time variable to flag 'simulation end'
  429. Time = nengo.Node(_max_stop_time,size_out=1)
  430. #probe steer
  431. SteerNode=nengo.Node(self.steer_out)
  432. _add_probe(SteerNode,'Steer')
  433. # speed
  434. Agent_speed = nengo.Node([v0]) # speed of agent
  435. sp_shift_norm = nengo.Node([speed_shift])
  436. n_speed = nengo.Ensemble(n_neurons,
  437. dimensions=1, radius=max_speed/2,
  438. neuron_type=nengo.LIF(amplitude=self.amp_gain))
  439. nengo.Connection(Agent_speed, n_speed)
  440. nengo.Connection(sp_shift_norm, n_speed)
  441. # head direction
  442. Agent_HD = nengo.Node(getGoalDirection)
  443. n_HD = nengo.Ensemble(n_neurons, dimensions=1,
  444. neuron_type=nengo.LIF(amplitude=self.amp_gain))
  445. nengo.Connection(Agent_HD, n_HD,synapse=tau)
  446. _add_probe(n_HD,'HeadDirection')
  447. # Velocity
  448. Agent_vel = nengo.Node(get_VxVy,size_in=2) # Agent's velocity node
  449. nengo.Connection(n_HD, Agent_vel[0],
  450. transform=self.max_angle,synapse=tau2) # get angle [-pi,pi]
  451. nengo.Connection(n_speed,
  452. Agent_vel[1],synapse=tau2) # get speed [-max_speed/2,max_speed/2]
  453. nengo.Connection(sp_shift_norm, Agent_vel[1],transform=-1) # set speed to [0,.5]
  454. n_vel = nengo.Ensemble(n_neurons,
  455. dimensions=2,radius=max_speed*np.sqrt(2),
  456. neuron_type=nengo.LIF(amplitude=self.amp_gain))
  457. nengo.Connection(Agent_vel, n_vel[:2],synapse=tau2)
  458. # position X,Y integrator
  459. initial_pos=nengo.Node(init_pos,size_out=2)
  460. # define the position variable during roaming
  461. # HP_Rates = stats.skewnorm(hp_rate, hploc_rate,
  462. # hpscale_rate).rvs(n_neurons)
  463. # scale_rate = 400 / np.max(
  464. # HP_Rates) - 1 # just so the neurons would work properly
  465. # npos = nengo.Ensemble(n_neurons=n_neurons,
  466. # dimensions=2, radius=arena_size[1] / (2 ** 0.5),
  467. # max_rates=HP_Rates * scale_rate,
  468. # neuron_type=nengo.SpikingRectifiedLinear(amplitude=2 / scale_rate)
  469. # )
  470. npos = nengo.Ensemble(n_neurons*2, dimensions=2,
  471. radius=arena_size[0] * .5 * np.sqrt(
  472. 2)) # x,y position up2:2m|2D
  473. nengo.Connection(initial_pos,npos,synapse=tau) #connect initial position to pos
  474. _add_probe(npos,'position')
  475. # calculate current position using integrator
  476. nengo.Connection(npos[:2], npos[:2], synapse=tau)
  477. # visual cues
  478. # for i in range(0, len(corals_pos)):
  479. # track_boundaries = nengo.Node(corals_pos[i])
  480. Goal = nengo.Node(Goal_pos)
  481. # BVC neurons definition:
  482. BVC_intercepts = stats.skewnorm(ae, loce, scalee).rvs(n_neurons)
  483. BVC_intercepts /= np.max(BVC_intercepts)*1.001
  484. BVC_Rates = stats.skewnorm(ae_rate, loce_rate, scalee_rate).rvs(
  485. n_neurons)
  486. scale_rate = 90 # just so the neurons would work properly
  487. # make the emsemble array for each angle in different index
  488. # EA.EnsembleArray
  489. ea_BVC = nengo.networks.EnsembleArray(neurons_per_angle,
  490. n_LIDAR_pts,
  491. radius=self.BVC_rec_field,
  492. encoders=[-1] * neurons_per_angle,
  493. intercepts=np.random.choice(
  494. BVC_intercepts,size=neurons_per_angle),
  495. max_rates=np.random.choice(
  496. BVC_Rates,size=neurons_per_angle) * scale_rate,
  497. neuron_type=
  498. # nengo.PoissonSpiking(
  499. # nengo.RectifiedLinear(amplitude=1 / scale_rate)
  500. nengo.SpikingRectifiedLinear(amplitude=1 / scale_rate)
  501. # )
  502. )
  503. # connect position and HD and encode BVC(LIDAR) data
  504. ea_BVC_input = nengo.Node(read_LIDAR, size_in=3,
  505. size_out=n_LIDAR_pts)
  506. nengo.Connection(npos[:2], ea_BVC_input[:2],synapse=2*tau2)
  507. nengo.Connection(n_HD, ea_BVC_input[2], transform=self.max_angle,synapse=2*tau2)
  508. nengo.Connection(ea_BVC_input, ea_BVC.input, synapse=2*tau2)
  509. #probe lidar array:
  510. lidar_probe = nengo.Node(size_in=n_LIDAR_pts)
  511. nengo.Connection(ea_BVC_input,lidar_probe)
  512. _add_probe(lidar_probe, 'LIDAR_ARRAY')
  513. # Goal encoding: feed and fast!
  514. # check goal path is clear and set as attractor when visual
  515. ############### TBD using nengo SPA ##################################
  516. # food visibility node by distance
  517. # goal_to_agent_diff = nengo.Node(feed_or_not, size_in=2, size_out=1)
  518. def feed_or_not(x):
  519. att=np.array([0,1]) #optional outputs
  520. dx,dy=x
  521. self.goal_dist=calc_dist(np.array([dx,dy]))
  522. if self.goal_dist < feed_visual_dist * attractor_scale:
  523. attract = att[1]
  524. else:
  525. attract = att[0]
  526. return attract
  527. goal_to_agent_diff = nengo.Ensemble(n_neurons=3000, dimensions=2,radius = self.arena_size[1]/2)
  528. nengo.Connection(npos[:2], goal_to_agent_diff, synapse=tau, transform=attractor_scale)
  529. nengo.Connection(Goal, goal_to_agent_diff, synapse=tau, transform=-attractor_scale)
  530. Food_stimulus = nengo.Node(Food_Stim, size_in=3, size_out=2)
  531. feed_bolean=nengo.Connection(goal_to_agent_diff, Food_stimulus[0], function=feed_or_not, synapse=tau)
  532. nengo.Connection(Goal, Food_stimulus[1:])
  533. nengo.Connection(npos, Food_stimulus[1:], transform=-1, synapse=tau)
  534. _add_probe(feed_bolean, 'Go2Goal')
  535. # above yields X_food-X, Yfood-Y when visual, 0,0 when not
  536. # therefore, adding it to npos will yield X+0,Y+0 when not visual,
  537. # and X+X_food-X, Y+Yfood-Y when visual (i.e. attractor):
  538. nengo.Connection(Food_stimulus, npos, synapse=tau)
  539. # speed control: if distance from boundar is getting smaller, decrease speed.
  540. potential_velocity = nengo.Node(_get_potential_velocity,
  541. size_in=2 + n_LIDAR_pts + 1,
  542. size_out=4)
  543. nengo.Connection(ea_BVC.output, potential_velocity[2:-1],
  544. synapse=tau)
  545. nengo.Connection(Agent_vel, potential_velocity[:2],synapse=tau)
  546. # translate speed output into HD shift
  547. potential_HD = nengo.Node(calc_angle, size_in=2, size_out=1)
  548. nengo.Connection(potential_velocity[:2], potential_HD,synapse=2*tau)
  549. HD_error = nengo.Node(size_in=1)
  550. nengo.Connection(Agent_HD, HD_error, transform=-1,synapse=tau2)
  551. nengo.Connection(potential_HD, HD_error,synapse=tau2)
  552. nengo.Connection(HD_error, n_HD,synapse=tau)
  553. # send HD error to potential speed to check availability
  554. nengo.Connection(HD_error, potential_velocity[-1],
  555. transform=-self.max_angle,synapse=tau)
  556. ################ probe pot_vel_output#############
  557. probe_v_pot=nengo.Node(size_in=2)
  558. nengo.Connection(potential_velocity[:2],probe_v_pot)
  559. # _add_probe(probe_v_pot,'V_potential')
  560. probe_dist_front=nengo.Node(size_in=1)
  561. nengo.Connection(potential_velocity[2],probe_dist_front)
  562. # _add_probe(probe_dist_front, 'Front_Dist')
  563. probe_turn=nengo.Node(size_in=1)
  564. nengo.Connection(potential_velocity[-1],probe_turn)
  565. # _add_probe(probe_turn, 'Turn_Angle')
  566. # apply velocity error to agent
  567. velocity_error = nengo.Ensemble(n_neurons=n_neurons,
  568. dimensions=2,
  569. radius=max_speed*np.sqrt(2),
  570. neuron_type=nengo.LIF(amplitude=self.amp_gain))
  571. # displacement_error=nengo.Node(size_in=2,size_out=2)
  572. nengo.Connection(n_vel[:2], velocity_error, transform=-1,synapse=tau2*5)
  573. nengo.Connection(potential_velocity[:2], velocity_error,synapse=tau2*5)
  574. # Actual velocity output from the motor system
  575. actual_velocity = nengo.Ensemble(n_neurons=n_neurons,
  576. dimensions=2,
  577. radius=max_speed*np.sqrt(2),
  578. neuron_type=nengo.LIF(amplitude=self.amp_gain))
  579. # _add_probe(actual_velocity, 'Vel_out')
  580. nengo.Connection(n_vel[:2], actual_velocity[:2],synapse=tau2)
  581. nengo.Connection(velocity_error, actual_velocity[:2],synapse=tau2)
  582. # connect the controlled speed to npos
  583. nengo.Connection(actual_velocity, npos[:2],
  584. synapse=tau, transform=tau)
  585. # stop simulation when reaching goal
  586. self.goal_reached = nengo.Node(goal_reached, size_in=2, size_out=1)
  587. _add_probe(self.goal_reached, 'GOAL')
  588. nengo.Connection(Goal, self.goal_reached)
  589. nengo.Connection(npos, self.goal_reached, transform=-1)
  590. self.sim = nengo_ocl.Simulator(self.model, dt=self.dt, #1ms step size
  591. seed=self.seed)
  592. def Start(self):
  593. # step + optional: live plot (currently doesn't work)
  594. t=0
  595. step_counter=int(1) #count steps of sim to plot every other step
  596. # create video
  597. FPS = 10
  598. vid_size = [300, 300]
  599. my_dpi = 100
  600. if self.vid_flag:
  601. vidOut=cv2.VideoWriter('{}_video.avi'.format(self.pic_name)
  602. ,cv2.VideoWriter_fourcc('M','J','P','G'), FPS, (vid_size[0], vid_size[1]))
  603. #define stop_time 1 sec after stop_sim
  604. self.StopT = self.sim_time
  605. stopflag=False
  606. # while not self.stop_sim:
  607. while t < self.StopT+0.1:
  608. t+=self.dt #dummy variable to get current time
  609. step_counter+=1 #count steps
  610. self.sim.run_steps(steps=1, progress_bar=False) # single simulation step
  611. if self.stop_sim and not stopflag:
  612. self.StopT = t + 1 #stop in 1 sec
  613. stopflag=True #don't update this value anymore
  614. goal_reached=self.sim.data[self.probes['GOAL']][-1]
  615. #if out of arena change direction
  616. xy = self.sim.data[self.probes['position']][-1:]
  617. self.x=xy[:, 0]
  618. self.y=xy[:, 1]
  619. # if abs(x)>self.arena_size[0]*.95/2 or abs(y)>self.arena_size[1]*.95/2:
  620. # # do this only once a second:
  621. # if np.mod(t,1)<self.dt:
  622. # self.forceSteer=.67
  623. # else:
  624. # self.forceSteer=None
  625. if goal_reached>.9 and t>0.1: #if reached destination (skip initialization time), stop simulation
  626. self.stop_sim=True
  627. # Live plot
  628. if self.plot_config:
  629. if np.mod(step_counter,100)==0: #plot every other 100 steps (100ms):
  630. # create plot
  631. plt.ion()
  632. plt.show()
  633. fig = plt.figure(0, figsize=(vid_size[0] / my_dpi, vid_size[1] / my_dpi),
  634. dpi=my_dpi) # set fig size to match video
  635. # iterate over all the probes, and plot last few samples
  636. pos = self.sim.data[self.probes['position']][-300:]
  637. hd= self.sim.data[self.probes['HeadDirection']][-1]
  638. hd_rad=hd*self.max_angle #range -pi,pi
  639. bvc=self.sim.data[self.probes['LIDAR_ARRAY']][-1]
  640. bvc=bvc+self.BVC_rec_field
  641. plt.cla()
  642. #recent trajectory
  643. plt.plot(pos[:,0],pos[:,1],color='blue')
  644. #fish pos
  645. x,y=pos[-1,0],pos[-1,1]
  646. fish = plt.Circle((x,y), 0.03, color='k')
  647. plt.gca().add_patch(fish)
  648. #corals
  649. for r in range(0,self.n_corals):
  650. Coral = plt.Circle((self.corals_pos[r]), self.coral_size, color='g')
  651. plt.gca().add_patch(Coral)
  652. Goal = plt.Circle((self.Goal_pos), 0.02, color='r')
  653. plt.gca().add_patch(Goal)
  654. #BVC:
  655. ang_arr=np.arange(0,360,self.ang_bin_size_deg)
  656. for ang in range(0,len(ang_arr)):
  657. #define angle and end-point for bvc lines
  658. th=hd_rad + np.deg2rad(ang_arr[ang])
  659. x2=float(x+bvc[ang]*np.cos(th))
  660. y2=float(y+bvc[ang]*np.sin(th))
  661. #define linestyle and color
  662. if ang == 0:
  663. LS = '-'
  664. else:
  665. LS = '--'
  666. if bvc[ang]>self.BVC_rec_field-.05:
  667. LC='k'
  668. else:
  669. LC='r'
  670. xlin=np.array([x,x2])
  671. ylin=np.array([y,y2])
  672. plt.plot(xlin,ylin,color=LC,linestyle=LS,linewidth=0.5)
  673. plt.xlim(-self.arena_size[0]/2,self.arena_size[0]/2)
  674. plt.ylim(-self.arena_size[0] / 2, self.arena_size[0] / 2)
  675. plt.xticks([])
  676. plt.yticks([])
  677. # plt.title('BVC_Nav noMemory t = ' + str(round(t,2)) + ' [s]')
  678. plt.title('ref t = ' + str(round(t,2)) + ' [s], GD= ' + str(self.GoalDirection) )
  679. mypause(0.002)
  680. if self.vid_flag:
  681. # with vidOut.saving(fig, '{}_video.mp4'.format(self.mat_name), dpi=my_dpi):
  682. # write frame into viedo
  683. canvas = FigureCanvas(plt.gcf())
  684. canvas.draw()
  685. mat = np.array(canvas.renderer._renderer)
  686. mat = cv2.cvtColor(mat, cv2.COLOR_RGB2BGR)
  687. vidOut.write(mat)
  688. # vidOut.grab_frame()
  689. # End of simulation plot and data dump
  690. if t >= self.StopT and stopflag==True:
  691. print('simulation finished after ' + str(self.StopT) + ' sec')
  692. mat_dict = {'t': self.sim.trange()}
  693. # iterate over all the probes, and plot with different style
  694. for key in self.probes.keys():
  695. # plt.figure()
  696. arr = self.sim.data[self.probes[key]]
  697. # plots = plt.plot(arr)
  698. # for i, p in enumerate(plots):
  699. # p.set_label(str(i))
  700. # p.set_color(comb_styles[i%len(comb_styles)][0])
  701. # p.set_linestyle(comb_styles[i%len(comb_styles)][1])
  702. # plt.title(key)
  703. # plt.legend()
  704. mat_dict[key] = arr
  705. # plt.show()
  706. # Save probe data
  707. savemat('{}_data.mat'.format(self.mat_name), mat_dict) # matlab format
  708. with open('{}_data.pkl'.format(self.pic_name), 'wb') as f: # pickle format
  709. pickle.dump(mat_dict, f)
  710. # save input params data
  711. savemat('{}_input_params.mat'.format(self.mat_name), self.input_params_dict) # matlab format
  712. if self.vid_flag:
  713. cv2.destroyAllWindows()
  714. vidOut.release()

mdl_NAVIGATE.py, no license · at the source

Overview

Authors: Lear Cohen1, Hadar Cohen Duwek1, Elishai Ezra Tsur1
  1. The Neuro-biomorphic Engineering Lab (NBEL), The Open University of Israel, Raanana, Israel
Institutions: Open University of Israel (Israel)
Journal: iScience, volume 29, issue 5, article 115824
Dates: received 31 October 2025; accepted 16 April 2026; published online 20 April 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1016/j.isci.2026.115824 · PMID 42111205 · PMCID PMC13157163 · OpenAlex W7154938980
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: computational (subfield)
Methods: Connectivity, Statistics, Machine learning, Single-unit activity, calcium imaging, Smoothing, state filtering, decompositions
Keywords: behavioral neuroscience, systems neuroscience, cognitive neuroscience
Topic: Neurobiology and Insect Physiology Research (Cellular and Molecular Neuroscience, Neuroscience), according to OpenAlex
Funding: Israel Innovation Authority; NeMo Consortium; NeuroBiomorphic Engineering Lab; NBEL
Citations: not cited yet (Europe PMC); 48 references in the paper

Abstract

Spatial memory is a fundamental cognitive capacity governing the mental encoding of spatial information, which can be used to navigate through intricate terrains. While most navigation models rely on place- and grid cells as key building blocks, in fish, the largest vertebrate class, the neural basis of navigation was suggested to primarily comprise boundary vector cells (BVCs) and hydrostatic pressure (HP) cues. In this work, we used experimental neural data recordings from the telencephalon of the goldfish to implement a neuromorphic (brain-inspired) spiking neural network-based navigation framework. BVCs were used for collision avoidance and HP for trajectory control toward the target. We show that the model supports reliable goal-directed navigation in a depth-constrained task without requiring explicit place-cell position encoding. Navigation performance emerges from the interaction between boundary-related signals, HP cues, and a fixed initial directional bias. These results provide a computational account of how biologically grounded cues can support efficient navigation under constrained sensory assumptions.

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 3 matches between paragraphs and lines of code.

OSF ypmfq

License: none: the authors keep all their rights
State: the link answers, verified on 29 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: “Data and code availability”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: Matplotlib (8 files), NumPy (8 files), Nengo (7 files), OpenCV (7 files), SciPy (7 files), TensorFlow (1 file)
Availability: 1 check, the latest on 29 September 2026: the link answers (HTTP 200)
  • 29 September 2026: the link answers (HTTP 200)
11 files
At the source: osf.io/ypmfq/overview

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:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 10 scripts, each with its path and the digest of its content;
  • 3 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 simulation scripts and model implementations are available in the repository: https://osf.io/ypmfq/overview (DOI: https://doi.org/10.17605/OSF.IO/YPMFQ).

Any additional information required to reproduce this work is available from the lead contact upon request.

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

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

Cite

This paper

Cohen, L., Duwek, H. C., & Ezra Tsur, E. (2026). Electrophysiologically informed spiking neural networks for fish-inspired navigation with boundary vector cells and hydrostatic pressure cues. iScience, 29(5), 115824. https://doi.org/10.1016/j.isci.2026.115824

BibTeX

@article{cohen2026electrophysiological,
author = {Cohen, Lear and Duwek, Hadar Cohen and Ezra Tsur, Elishai},
title = {{Electrophysiologically informed spiking neural networks for fish-inspired navigation with boundary vector cells and hydrostatic pressure cues}},
journal = {iScience},
year = {2026},
month = apr,
volume = {29},
number = {5},
pages = {115824},
publisher = {Elsevier},
issn = {2589-0042},
doi = {10.1016/j.isci.2026.115824},
url = {https://doi.org/10.1016/j.isci.2026.115824},
pmid = {42111205},
pmcid = {PMC13157163}
}

RIS

TY - JOUR
AU - Cohen, Lear
AU - Duwek, Hadar Cohen
AU - Ezra Tsur, Elishai
TI - Electrophysiologically informed spiking neural networks for fish-inspired navigation with boundary vector cells and hydrostatic pressure cues
T2 - iScience
J2 - iScience
PY - 2026
DA - 2026/04/20
VL - 29
IS - 5
SP - 115824
SN - 2589-0042
PB - Elsevier
DO - 10.1016/j.isci.2026.115824
UR - https://doi.org/10.1016/j.isci.2026.115824
LA - en
ER -

CSL-JSON

{
"id": "10.1016/j.isci.2026.115824",
"type": "article-journal",
"title": "Electrophysiologically informed spiking neural networks for fish-inspired navigation with boundary vector cells and hydrostatic pressure cues",
"container-title": "iScience",
"author": [
{
"family": "Cohen",
"given": "Lear"
},
{
"family": "Duwek",
"given": "Hadar Cohen"
},
{
"family": "Ezra Tsur",
"given": "Elishai"
}
],
"container-title-short": "iScience",
"volume": "29",
"issue": "5",
"page": "115824",
"DOI": "10.1016/j.isci.2026.115824",
"PMID": "42111205",
"PMCID": "PMC13157163",
"ISSN": "2589-0042",
"publisher": "Elsevier",
"URL": "https://doi.org/10.1016/j.isci.2026.115824",
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
20
]
]
}
}

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.pbio.3003755 [code]
Action information is integrated into entorhinal representations of conceptual space and is reflected in eye movements.
Journal: PLoS biology
In common: TensorFlow, SciPy, Matplotlib, 1 other tool, 3 references
[2] doi:10.1038/s42256-026-01254-4 [code]
Neural sampling from cognitive maps enables goal-directed imagination and planning.
Journal: Nature machine intelligence
In common: Matplotlib, NumPy, 3 references
[3] doi:10.1038/s41467-026-69633-4 [code]
Visuomotor decision-making through multifeature convergence in the larval zebrafish hindbrain.
Journal: Nature communications
In common: SciPy, Matplotlib, NumPy, 2 references
[4] doi:10.1038/s41598-026-51212-8 [code]
Uncertainty aware machine learning for bridging simulation and experiment in high throughput materials characterization.
Journal: Scientific reports
In common: TensorFlow, OpenCV, Matplotlib, 1 other tool, computational
[5] doi: [code]
Real-time closed-loop feedback system for mouse mesoscale cortical signal and movement control
Journal: eLife
In common: TensorFlow, OpenCV, SciPy, 2 other tools
[6] doi:10.1038/s42003-026-10957-8 [code]
Brain defence by the extracellular matrix protein Cochlin.
Journal: Communications biology
In common: TensorFlow, OpenCV, SciPy, 2 other tools
[7] doi:10.1126/sciadv.aee6952 [code]
Wafer-scale SOT-MRAM for analog crossbar array applications.
Journal: Science advances
In common: TensorFlow, OpenCV, SciPy, 2 other tools
[8] doi:10.1126/sciadv.aed3650 [code]
Truthful visualizations for mass spectrometry imaging enable high-spatial-resolution interactive &lt;i&gt;m/z&lt;/i&gt; mapping and exploration.
Journal: Science advances
In common: TensorFlow, OpenCV, SciPy, 2 other tools
[9] doi:10.1038/s41467-026-76878-6 [code]
Temporal coding enables hyperacuity in event-based vision.
Journal: Nature communications
In common: TensorFlow, OpenCV, SciPy, 2 other tools
[10] doi:10.1038/s41592-026-03179-7 [code]
Voltage imaging of neurons distributed across entire brains of larval zebrafish.
Journal: Nature methods
In common: TensorFlow, OpenCV, SciPy, 2 other tools

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.