OSCR

A vector-based strategy for olfactory navigation in Drosophila.

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] § A behavioural model of edge tracking ↔ state_space_model/simulation_agent.py, lines 6–68 · score 0.60 · state transitions, exit direction, exit memory, cross, state space model, fit
  2. [2] § A behavioural model of edge tracking ↔ state_space_model/sssm.py, lines 62–85 · score 0.56 · continuous latent variables, state space model, switching
  3. [3] § A behavioural model of edge tracking ↔ state_space_model/simulation_agent.py, lines 6–68 · score 0.55 · memory updates, exit memory, transition, crosses, state space model, fits

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 · 309 lines · 15 KB · MIT · 2 matches

  1. import os
  2. import numpy as np
  3. class Agent(object):
  4. def __init__(self, dt=None,
  5. dt_fit=None, r_011=None, p_011=None, r_100=None, p_100=None,
  6. _radius_bkw=1, _radius_fwd=1, _flter='average',
  7. _tau=None, _radius=None, sub_wind=False,
  8. a_m=None, b_m=None, sigma_m=None, init_m=None, downwind_thres=0,
  9. resample_exit_m=False,
  10. goal_speeds=None, a_v=None, sigma_v=None,
  11. tau_a1=9.8, tau_r1=0.72, beta=0.01, stim_thres=0.25, **kwargs):
  12. self.dt = dt
  13. ####### parameters for odor filtering and binarizing. (alvarez-salvado et al 2018)
  14. self.exp_tau_a1 = np.exp(- dt / tau_a1) # for odor adaptation factor
  15. self.exp_tau_r1 = np.exp(- dt / tau_r1) # for odor response
  16. self.beta = beta # for odor compression
  17. self.stim_thres = stim_thres # threshold to binarize odor
  18. ####### paramters for delay in state transition. negative binomial distribution
  19. ############ dt_fit: parameters are learned from data with time step dt_fit
  20. ############ r_011, p_011: duration of leaving state after exit
  21. ############ r_100, p_100: duration of returning state after entry
  22. self.r_arr = [r_011, r_100]
  23. self.p_arr = [p_011, p_100]
  24. self.delay_coef = int(dt_fit / dt)
  25. ####### whether to subtract wind direction from exit direction for memory update
  26. self.sub_wind = sub_wind
  27. ####### parameters for computing travel direction for memory update
  28. self._radius_bkw = _radius_bkw * int(dt_fit / dt)
  29. self._radius_fwd = _radius_fwd * int(dt_fit / dt)
  30. self._flter = _flter
  31. self._tau = _tau * int(dt_fit / dt) if _tau is not None else None
  32. self._radius = _radius * int(dt_fit / dt) if _radius is not None else None
  33. ####### parameters for memory update: m(t+1) = a_m * m(t) + b_m * v(t) + sqrt(sigma_m) * randn(2)
  34. self.a_m = a_m
  35. self.b_m = [(1 - a_mk) for a_mk in a_m] if b_m is None else b_m
  36. self.sigma_m = sigma_m
  37. ####### parameters for memory initialization
  38. self.init_m = init_m
  39. self.downwind_thres = downwind_thres
  40. ####### resample exit memory from initialization distribution
  41. self.resample_exit_m = resample_exit_m
  42. ####### goal speeds [returning, leaving]
  43. self.goal_speeds = goal_speeds
  44. ####### displacement update: step(t+1) = a_v * step(t) + (1 - a_v) * goal(t+1) + sqrt(sigma) * randn(2)
  45. ####### convert a_v, sigma_v from time step dt_fit to time step dt_sim
  46. a_v, sigma_v = a_v.copy(), sigma_v.copy()
  47. for io in range(2):
  48. a_v[io], _, sigma_v[io] = param_convert(a_v[io], 1 - a_v[io], sigma_v[io], dt_fit, dt)
  49. self.a_v = a_v
  50. self.sigma_v = sigma_v
  51. #######
  52. self.rs = None # random state
  53. self.modes = None # phase of the trial
  54. self.winds = None # wind direction
  55. self.odors = None # odor concentration
  56. self.oadpt = None # odor adaptation factor (equation 8 in alvarez-salvado et al 2018)
  57. self.ocmpr = None # odor compression (equation 9 in ...)
  58. self.orspn = None # odor response (equation 10 in ...)
  59. self.stims = None # stimuli (binarized odor)
  60. self.ichng = None # the time step when stimuli changes
  61. self.delay = None # delay of state transition
  62. self.stats = None # states (leaving/returning)
  63. self.xmems = None # crossing (entry and exit) memories.
  64. self.goals = None # goal direction
  65. self.steps = None # displacement
  66. self.xylocs = None # xy location
  67. def reset(self, nT, rs):
  68. self.rs = rs
  69. self.modes = np.zeros((nT,)).astype(int)
  70. self.winds = np.zeros((nT, 2))
  71. self.odors = np.zeros((nT,))
  72. self.oadpt = np.zeros((nT,))
  73. self.ocmpr = np.zeros((nT,))
  74. self.orspn = np.zeros((nT,))
  75. self.stims = np.zeros((nT,)).astype(int)
  76. self.ichng = -1
  77. self.delay = None
  78. self.stats = np.zeros((nT,), dtype=int)
  79. self.xmems = np.zeros((2, nT, 2))
  80. self.goals = np.zeros((nT, 2))
  81. self.steps = np.zeros((nT, 2))
  82. self.xylocs = np.zeros((nT + 1, 2))
  83. return
  84. def simulate(self, env, rs_, T, odor_filter=False):
  85. env.reset()
  86. nT = int(T / self.dt)
  87. self.reset(nT, np.random.RandomState(rs_))
  88. self.xylocs[0] = env.init_loc(self.rs)
  89. for it in range(nT):
  90. self.odors[it], self.winds[it], self.modes[it] = env.sense(xyloc=self.xylocs[it], it=it, odors=self.odors,
  91. steps=self.steps, ichng=self.ichng,
  92. xylocs=self.xylocs) # kwargs
  93. self.odor_response(it, odor_filter=odor_filter)
  94. self.state(it)
  95. self.memory(it)
  96. self.goal(it)
  97. self.step(it)
  98. return
  99. def odor_response(self, it, odor_filter=False):
  100. if odor_filter:
  101. self.oadpt[it] = self.exp_tau_a1 * self.oadpt[it - 1] + (1 - self.exp_tau_a1) * self.odors[it]
  102. self.ocmpr[it] = self.odors[it] / (self.odors[it] + self.beta + self.oadpt[it])
  103. self.orspn[it] = self.exp_tau_r1 * self.orspn[it - 1] + (1 - self.exp_tau_r1) * self.ocmpr[it]
  104. self.stims[it] = int(self.orspn[it] > self.stim_thres)
  105. else:
  106. self.stims[it] = self.odors[it]
  107. if self.stims[it] != self.stims[it - 1]:
  108. self.ichng = it - 1
  109. delay = self.rs.negative_binomial(self.r_arr[self.stims[it]], 1 - self.p_arr[self.stims[it]]) + 1
  110. self.delay = delay * self.delay_coef
  111. return
  112. def state(self, it):
  113. if it == 0:
  114. self.stats[it] = self.stims[it]
  115. elif (self.stats[it - 1] != self.stims[it]) and (it == self.ichng + self.delay):
  116. self.stats[it] = 1 - self.stats[it - 1]
  117. else:
  118. self.stats[it] = self.stats[it - 1]
  119. return
  120. def travel_direction(self, it):
  121. _radius = (self._radius_bkw + self._radius_fwd - 1) if self._radius is None else self._radius
  122. if self._flter in ['exponential']:
  123. arr = self.steps[max(0, it - _radius + 1): (it + 1)]
  124. arr = np.pad(arr, ((-min(0, it - _radius + 1), 0), (0, 0)), 'edge')
  125. weights = np.arange(it - _radius + 1, it + 1)
  126. weights = np.exp(-np.abs(weights - it) / self._tau)
  127. weights = weights / weights.sum()
  128. val = weights @ arr
  129. elif self._flter in ['average']:
  130. val = self.steps[max(0, it - _radius + 1): (it + 1)].mean(0)
  131. elif self._flter == 'none':
  132. val = self.steps[it]
  133. else:
  134. raise ValueError(f'{self._flter} not implemented.')
  135. val = val / np.linalg.norm(val) # travel direction
  136. return val
  137. def init_memory(self, it, k):
  138. ####### xmems[0]: memory of entry, initialized as the null vector
  139. ####### xmems[1]: memory of exit, initialized to be upwind with a random cross wind component
  140. wind_ortho = np.array([self.winds[it, 1], - self.winds[it, 0]]) # cross wind direction
  141. vec = (self.rs.uniform(*self.init_m[k][0]) * wind_ortho +
  142. self.rs.uniform(*self.init_m[k][1]) * self.winds[it])
  143. if k == 1 and self.sub_wind: # subtract wind direction from exit memory
  144. vec -= self.winds[it]
  145. return vec
  146. def memory(self, it):
  147. if it == 0:
  148. for k in range(2):
  149. self.xmems[k, it] = self.init_memory(it, k)
  150. return
  151. for k in range(2): # k=0: entry; k=1: exit
  152. cond_stim = np.concatenate([np.zeros(self._radius_bkw), np.ones(self._radius_fwd)])
  153. cond_stim = (1 - cond_stim) if k == 1 else cond_stim
  154. cond_learn = np.array_equal(cond_stim, self.stims[max(0, it - len(cond_stim) + 1): (it + 1)])
  155. if cond_learn: # update memory
  156. direction = self.travel_direction(it - 1)
  157. if k == 1 and (direction @ self.winds[it] < self.downwind_thres): # ignore downwind exit
  158. self.xmems[k][it] = self.xmems[k][it - 1]
  159. continue
  160. if k == 1 and self.resample_exit_m:
  161. self.xmems[k][it] = self.init_memory(it, k)
  162. continue
  163. if k == 1 and self.sub_wind: # subtract wind direction from exit direction
  164. direction = direction - self.winds[it]
  165. self.xmems[k][it] = self.a_m[k] * self.xmems[k][it - 1] + self.b_m[k] * direction
  166. self.xmems[k][it] += np.sqrt(self.sigma_m[k]) * self.rs.randn(2)
  167. else:
  168. self.xmems[k][it] = self.xmems[k][it - 1]
  169. return
  170. def goal(self, it):
  171. self.goals[it] = self.xmems[self.stats[it]][it] # returning: memory of entry; leaving: memory of exit;
  172. if self.stats[it] == 1 and self.sub_wind:
  173. self.goals[it] += self.winds[it]
  174. return
  175. def step(self, it):
  176. a = self.a_v[self.stats[it]]
  177. self.steps[it] = (a * self.steps[it - 1]
  178. + (1 - a) * self.goals[it] * self.goal_speeds[self.stats[it]] * self.dt
  179. + np.sqrt(self.sigma_v[self.stats[it]]) * self.rs.randn(2))
  180. self.xylocs[it + 1] = self.xylocs[it] + self.steps[it]
  181. return
  182. def param_convert(a, b, sigma, dt_fit, dt_sim):
  183. ####### for displacement update equation: step(t+1) = a * step(t) + b * m(t+1) + sqrt(sigma) * randn(2)
  184. ####### convert parameters under time step dt_fit to time step dt_sim
  185. if dt_fit == dt_sim:
  186. return a, b, sigma
  187. else:
  188. a_ = np.power(a, dt_sim / dt_fit)
  189. b_ = b / (1 - a) * (1 - a_)
  190. sigma = sigma / np.square(dt_fit) # velocity = step_fit / dt_fit
  191. sigma_ = sigma / (1 - np.square(a)) * (1 - np.square(a_))
  192. sigma_ = sigma_ * np.square(dt_sim) # step_sim = velocity * dt_sim
  193. return a_, b_, sigma_
  194. def param_from_sssm(model, single_trial=False, downwind_thres=0.5):
  195. param_dict = {'dt_fit': model._dt,
  196. 'r_011': model.r_011, 'p_011': model.p_011,
  197. 'r_100': model.r_100, 'p_100': model.p_100,
  198. 'a_m': model.a_m, 'sigma_m': np.zeros(2),
  199. 'goal_speeds': model.b_m / (1 - model.a_m),
  200. 'a_v': model.a_v, 'sigma_v': model.sigma_v,
  201. '_flter': model._flter, '_tau': model._tau,
  202. '_radius_bkw': model._radius_bkw, '_radius_fwd': model._radius_fwd,
  203. 'downwind_thres': downwind_thres}
  204. if single_trial:
  205. init_m = model.m0[0] / (model._dt * model.b_m / (1 - model.a_m))[:, None]
  206. param_dict['init_m'] = np.repeat(init_m[..., None], 2, axis=-1)
  207. else:
  208. init_m = np.zeros((2, 2, 2))
  209. for k in [1]:
  210. arr = model.m0[:, k, :] / (model.b_m[k] / (1 - model.a_m[k])) / model._dt
  211. xwind_abs_median = np.median(np.abs(arr[:, 0]))
  212. init_m[k, 0, :] = [-2 * xwind_abs_median, 2 * xwind_abs_median]
  213. init_m[k, 1, :] = np.median(arr[:, 1])
  214. param_dict['init_m'] = init_m
  215. return param_dict
  216. def param_from_fixedbias(model):
  217. param_dict = {'dt_fit': model._dt,
  218. 'r_011': model.r_011, 'p_011': model.p_011,
  219. 'r_100': model.r_100, 'p_100': model.p_100,
  220. 'a_m': np.ones(2), 'sigma_m': np.zeros(2), 'goal_speeds': np.ones(2),
  221. 'a_v': model.a_v, 'sigma_v': model.sigma_v}
  222. init_m = model.b_v / (1 - model.a_v[:, None]) / model._dt
  223. param_dict['init_m'] = np.repeat(init_m[..., None], 2, axis=-1)
  224. return param_dict
  225. if __name__ == '__main__':
  226. from simulation_envs import *
  227. import matplotlib.pyplot as plt
  228. import pickle
  229. from sssm import plot_traj_base, plot_edge
  230. plt.rcParams.update({'font.size': 6,
  231. 'font.family': 'arial',
  232. 'pdf.fonttype': 42,
  233. 'ps.fonttype': 42,
  234. 'savefig.dpi': 480})
  235. ################## param_from_sssm ##################
  236. name1, fld_str, fld_select = 'All', '0123', '0123'
  237. name2, init_str = 'N', '_initm01'
  238. save_file = (f'./files/models_ssm/fly_c_dt0pt2_fld{fld_str}_'
  239. f'sssm{init_str}_rb3rf3_drctAvgNorm_h0linear_trN200N1_sd0.pkl')
  240. with open(save_file, 'rb') as f:
  241. ifldlist_to_sssm = pickle.load(f)
  242. model = ifldlist_to_sssm[fld_select]
  243. param_str = f'Fit{name1}{name2}0'
  244. param_dict = param_from_sssm(model, downwind_thres=0.5)
  245. print(param_str)
  246. print(param_dict)
  247. ######################## setting time step and environment ##############
  248. dt = 0.2
  249. odor_filter = False
  250. # env, T = EnvEdge(deg=45, half_width=25*np.sqrt(2), ymin=-np.inf), 1000
  251. env, T = EnvEdge(deg=90, half_width=25, ymin=-np.inf), 1000
  252. # env, T = EnvEdge(deg=0, half_width=25, ymin=-np.inf), 1000
  253. # env, T = EnvJump(), 600
  254. # env, T = EnvReplay(deg=0, T1=600, T2=610, dt=dt), 1210
  255. # env, T = EnvZigzag(deg1=45, deg2=135, deg3=135, y1=200, nx=[0, 3, 11, None][0],
  256. # dt=dt, dur_thres=2.0, dist_thres=4.0), 2000
  257. # env, T = EnvZigzag(deg1=45, deg2=45, deg3=135, y1=200, nx=[0, 3, 11, None][2],
  258. # dt=dt, dur_thres=2.0, dist_thres=4.0), 2000
  259. ######################## agent ########################
  260. agent = Agent(dt=dt, **param_dict)
  261. ######################## run simulation ########################
  262. rerun = True
  263. save_file = f'./files/simulations/{param_str}_{env.env_name}.pkl'
  264. if os.path.isfile(save_file) and (not rerun):
  265. with open(save_file, 'rb') as f:
  266. res_list = pickle.load(f)
  267. else:
  268. res_list = {}
  269. for rs_ in range(5): # random seed
  270. agent.simulate(env, rs_, T, odor_filter=odor_filter)
  271. res_list[rs_] = {'steps': agent.steps, 'stims': agent.stims, 'modes': agent.modes,
  272. 'stats': agent.stats, 'xmems': agent.xmems, 'goals': agent.goals,
  273. 'xyloc0': agent.xylocs[0]}
  274. # with open(save_file, 'wb') as f:
  275. # pickle.dump(res_list, f)
  276. ####################### plot simulated trajectories #######################
  277. for rs_, res in res_list.items():
  278. figsize = (5, 5)
  279. fig, ax = plt.subplots(1, 1, figsize=figsize)
  280. stims = res['stims']
  281. nT = len(res['steps'])
  282. cumsum = np.cumsum(np.array([res['xyloc0']] + list(res['steps'][:nT])), axis=0)
  283. plot_traj_base(cumsum, res['stims'][:nT], ax,
  284. nT=None, init_xy=[0, 0], colors=['midnightblue', 'r'], linewidth=0.5)
  285. ax.axis('equal')
  286. ax.set_title(rs_)
  287. fig.show()
  288. # fig_file = f'./files/figures_ssm/leave_return/sim_{param_str}_{env.env_name}_example{rs_}.pdf'
  289. # fig.savefig(fig_file, bbox_inches='tight', transparent=True)

simulation_agent.py at commit 225dc7d, under MIT · at the source

Overview

Authors: Andrew F Siliciano1, Sun Minni2, Chad Morton1, Charles K Dowell1, Noelle B Eghbali1, Silas E Busch1, Juliana Y Rhee1, L F Abbott2, Vanessa Ruta1
  1. Laboratory of Neurophysiology and Behavior and Howard Hughes Medical Institute, The Rockefeller University, New York, NY USA
  2. Mortimer B. Zuckerman Mind Brain Behavior Institute, Kavli Institute for Brain Science, Department of Neuroscience, Columbia University, New York, NY USA
Institutions: Howard Hughes Medical Institute (United States); Rockefeller University (United States); Mortimer B. Zuckerman Mind Brain Behavior Institute (United States); Columbia University (United States)
Journal: Nature, volume 657, issue 8131, pages 443-454
Dates: received 24 February 2025; accepted 18 June 2026; published online 22 July 2026; in print 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1038/s41586-026-10827-7 · PMID 42486983 · PMCID PMC13558081 · OpenAlex W7170092641
Open access: hybrid, a free copy (OpenAlex)
Status: code verified
Categories: drosophila (organism), cognitive (subfield)
Methods: Connectivity, Graphs, Single-unit activity, calcium imaging
Keywords: Learning and memory, Neural circuits, Olfactory system
MeSH: Drosophila melanogaster*, Odorants*, Spatial Navigation*, Animals, Calcium, Cues, Female, Male, Memory, Neurons, Smell, Virtual Reality (* major topic)
Topic: Neurobiology and Insect Physiology Research (Cellular and Molecular Neuroscience, Neuroscience), according to OpenAlex
Funding: NIGMS NIH HHS (T32 GM152349); NIH HHS (P40 OD018537)
Citations: cited by 10 papers (Europe PMC); 61 references in the paper

Abstract

For many species, odours serve as key navigational cues1,2. Although tracking an odour plume has been modelled as a reflexive process3–5, it remains unclear whether animals can use memories of their past odour encounters to infer the spatial structure of their chemical environment or their location within it. Here we developed a virtual-reality olfactory paradigm that allows head-fixed Drosophila to explore structured chemical landscapes, offering insight into how memory mechanisms shape their navigational strategies. We found that flies track an appetitive odour corridor by following its boundary, alternating between rapid counter-turns to exit the plume and directed returns to its edge. Using a combination of behavioural modelling, functional calcium imaging and neural perturbations, we show that this ‘edge tracking’ strategy relies on vector-based computations within the Drosophila central complex, in which flies store and dynamically update memories of the direction to return to the plume’s boundary. Consistent with this, we find that FC2 neurons within the fan-shaped body, which encode a fly’s navigational goal6, signal the direction back to the odour boundary when flies are outside the plume. Plume tracking thus engages components of a conserved navigational toolkit, in which flies can use directional memories to navigate through complex and shifting chemical landscapes.

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

Repositories

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

rutalaboratory/Edge-Tracking-Model

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 225dc7ddbebb30d5f092f7144db02216fd1271b6, 31 March 2025
Languages: Python (4)
Size: 16 files, 4 scripts
Software Heritage: not archived
Found in: “Code availability”
Holds: README, license file
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (4 files), Matplotlib (2 files), SciPy (2 files), pandas (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
6 files

Zenodo 2075181

License: CC0-1.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Size: 1 file, 0 scripts
Software Heritage: not checked
Found in: the references
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
  • 27 September 2026: the link answers (HTTP 200)
At the source:

Code availability

Code for the switching state-space model is available at https://github.com/rutalaboratory/Edge-Tracking-Model.

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

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

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

Datasets cited

Data availability

Data supporting the findings of this study are available via Zenodo at https://doi.org/10.5281/zenodo.20751812 (ref. 61). Additional requests will be fulfilled by the corresponding author.

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 2, 28 September 2026

  • Publisher: n/a → Nature Portfolio

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 9 authors, 3 keywords, 12 MeSH terms, 2 funders, 47 references.

Cite

This paper

Siliciano, A. F., Minni, S., Morton, C., Dowell, C. K., Eghbali, N. B., Busch, S. E., Rhee, J. Y., Abbott, L. F., & Ruta, V. (2026). A vector-based strategy for olfactory navigation in Drosophila. Nature, 657(8131), 443-454. https://doi.org/10.1038/s41586-026-10827-7

BibTeX

@article{siliciano2026vector,
author = {Siliciano, Andrew F and Minni, Sun and Morton, Chad and Dowell, Charles K and Eghbali, Noelle B and Busch, Silas E and Rhee, Juliana Y and Abbott, L F and Ruta, Vanessa},
title = {{A vector-based strategy for olfactory navigation in Drosophila}},
journal = {Nature},
year = {2026},
month = jul,
volume = {657},
number = {8131},
pages = {443--454},
publisher = {Nature Portfolio},
issn = {0028-0836},
doi = {10.1038/s41586-026-10827-7},
url = {https://doi.org/10.1038/s41586-026-10827-7},
pmid = {42486983},
pmcid = {PMC13558081}
}

RIS

TY - JOUR
AU - Siliciano, Andrew F
AU - Minni, Sun
AU - Morton, Chad
AU - Dowell, Charles K
AU - Eghbali, Noelle B
AU - Busch, Silas E
AU - Rhee, Juliana Y
AU - Abbott, L F
AU - Ruta, Vanessa
TI - A vector-based strategy for olfactory navigation in Drosophila
T2 - Nature
J2 - Nature
PY - 2026
DA - 2026/07/22
VL - 657
IS - 8131
SP - 443
EP - 454
SN - 0028-0836
PB - Nature Portfolio
DO - 10.1038/s41586-026-10827-7
UR - https://doi.org/10.1038/s41586-026-10827-7
LA - en
ER -

CSL-JSON

{
"id": "10.1038/s41586-026-10827-7",
"type": "article-journal",
"title": "A vector-based strategy for olfactory navigation in Drosophila",
"container-title": "Nature",
"author": [
{
"family": "Siliciano",
"given": "Andrew F"
},
{
"family": "Minni",
"given": "Sun"
},
{
"family": "Morton",
"given": "Chad"
},
{
"family": "Dowell",
"given": "Charles K"
},
{
"family": "Eghbali",
"given": "Noelle B"
},
{
"family": "Busch",
"given": "Silas E"
},
{
"family": "Rhee",
"given": "Juliana Y"
},
{
"family": "Abbott",
"given": "L F"
},
{
"family": "Ruta",
"given": "Vanessa"
}
],
"container-title-short": "Nature",
"volume": "657",
"issue": "8131",
"page": "443-454",
"DOI": "10.1038/s41586-026-10827-7",
"PMID": "42486983",
"PMCID": "PMC13558081",
"ISSN": "0028-0836",
"publisher": "Nature Portfolio",
"URL": "https://doi.org/10.1038/s41586-026-10827-7",
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
22
]
]
}
}

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.1038/s41467-026-75945-2 [code]
Neural dynamics for working memory and evidence integration during olfactory navigation in Drosophila.
Journal: Nature communications
In common: drosophila, cognitive, 20 references
[2] doi:10.1126/sciadv.aeh7220 [code]
Central complex representations of self-movement are sufficient to compute wind direction in flight.
Journal: Science advances
In common: pandas, SciPy, Matplotlib, 1 other tool, 16 references
[3] doi:10.7554/elife.107589 [code]
Distinct evolutionary trajectories of two integration centres, the central complex and mushroom bodies, across Heliconiini butterflies.
Journal: eLife
In common: 11 references
[4] doi:10.1038/s41467-025-67545-3 [code]
Updating an allocentric goal from lateralised egocentric visual memories.
Journal: Nature communications
In common: cognitive, 10 references
[5] doi:10.1073/pnas.2609141123
Odor tracking in flying &lt;i&gt;Drosophila&lt;/i&gt; requires visual reafference and compass neurons.
Journal: Proceedings of the National Academy of Sciences of the United States of America
In common: drosophila, cognitive, 9 references
[6] doi:10.3389/fnsys.2026.1822122 [code]
Convergence-divergence circuits for multimodal integration of innate and learned opponent valences.
Journal: Frontiers in systems neuroscience
In common: pandas, SciPy, Matplotlib, 1 other tool, 6 references
[7] doi:10.1038/s41586-026-10735-w [code]
Distributed control circuits across a brain-and-cord connectome.
Journal: Nature
In common: pandas, SciPy, Matplotlib, 1 other tool, drosophila, 5 references
[8] doi:10.1126/sciadv.aeg3535 [code]
Undoing of firing rate adaptation enables invariant population codes.
Journal: Science advances
In common: SciPy, Matplotlib, NumPy, 4 references
[9] doi:10.1371/journal.pcbi.1014577 [code]
Combining sampling and attractor dynamics in spiking models of head direction systems.
Journal: PLoS computational biology
In common: SciPy, Matplotlib, NumPy, 4 references
[10] doi:10.1038/s41593-026-02257-5 [code]
Neural sequences underlying directed turning in Caenorhabditis elegans.
Journal: Nature neuroscience
In common: pandas, SciPy, Matplotlib, 1 other tool, 3 references

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

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.