OSCR

Centralized brain networks controlling antennal grooming coordination.

Code ↔ Paper

40 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 40 matches · 4 of them tie a paragraph to a whole file, not to given lines: weak matches, whose lines are not tinted
  1. [1] § Methods › Connectome analysis › Assigning neurons to groups ↔ src/prepare_data/Figure4_graph_tools.py, lines 253–315 · score 0.90 · MNfl, MNnm, T1 leg motor, neck premotor neurons, leg premotor neurons, leg motor neuron
  2. [2] § Results › A computational perturbation screen identifies key neurons for grooming ↔ src/Figure6.ipynb, lines 394–515 · score 0.83 · Global activity, bilateral neural silencing, unilateral neural activation, Neural perturbations, asymmetric JO, aMN4
  3. [3] § Methods › Data analysis › Analysis of perturbation experiments ↔ src/prepare_data/Figure3_prepare_data.py, lines 498–583 · score 0.80 · freely moving body, kinematic variable, head rotation, head fixed, body parts, head roll
  4. [4] § Methods › Connectome analysis › Constructing the antennal grooming network ↔ src/prepare_data/Figure4_generate_grooming_network.py, lines 340–402 · score 0.79 · super_class, FAFB connectome, foundational network, sensory neurons, brain, threshold
  5. [5] § Methods › Data analysis › Analysis of perturbation experiments ↔ src/prepare_data/Figure3_prepare_data.py, lines 352–436 · score 0.75 · freely moving body, pitched antenna, head fixed, amputee, body part, head roll
  6. [6] § Methods › Data processing › 2D & 3D pose estimation ↔ charuco_board/design/checkboard_config.py, the whole file · a weak match · score 0.72 · ChArUco board, OpenCV, bit, squares, dictionary
  7. [7] § Methods › Data processing › 2D & 3D pose estimation ↔ charuco_board/check_charuco.py, the whole file · a weak match · score 0.69 · ChArUco board, OpenCV, reconstruction, Anipose, calibrated
  8. [8] § Results › A computational perturbation screen identifies key neurons for grooming ↔ src/Figure6.ipynb, lines 394–515 · score 0.69 · neural silencing, neural activation, asymmetric JO, aMN4, clusters, activity
  9. [9] § Results › Proprioception does not play a major role in body-part coordination ↔ src/prepare_data/Figure3_prepare_data.py, lines 846–954 · score 0.69 · freely moving body, kinematic variable, head rotations, body part, head fixed, head roll
  10. [10] § Methods › Connectome analysis › Constructing the antennal grooming network ↔ src/common.py, lines 710–730 · score 0.67 · degree_centrality, eigenvector_centrality, NetworkX, scores
  11. [11] § Methods › Connectome analysis › Assigning neurons to groups ↔ src/prepare_data/Figure4_graph_tools.py, lines 253–315 · score 0.66 · neck premotor neurons, leg motor neurons, shared premotor neurons, neck motor neurons, VNC, foreleg
  12. [12] § Results › Body-part coordination enhances sustained foreleg-antennal contact ↔ src/EDFigure4.ipynb, lines 129–186 · score 0.66 · reduced collisions, left tarsus, right tibia, kinematic replay, head pitch, simulation
  13. [13] § Methods › Connectome analysis › Assigning neurons to groups ↔ src/Figure4.ipynb, lines 564–597 · score 0.65 · postsynaptic neurons, presynaptic neurons, leg premotor, modules, sensory, JO
  14. [14] § Methods › Kinematic replay and antennal contact detection ↔ src/EDFigure4.ipynb, lines 266–318 · score 0.65 · zero forces, contact force, collision pair, segments, tarsus, antennae
  15. [15] § Results › Simulating a connectome-derived antennal grooming network ↔ src/EDFigure13.ipynb, lines 50–85 · score 0.65 · unilateral selectivity, right neuron, left neuron, Neural responses, aDNs, aBNs
  16. [16] § Results › A centralized brain network links multiple motor modules ↔ src/EDFigure7.ipynb, lines 45–119 · score 0.64 · parameter sweep, monosynaptically connected, neck motor neurons, grooming neurons, descending, interneurons
  17. [17] § Results › Antennal grooming arises from tripartite coordination ↔ src/Figure1.ipynb, lines 424–503 · score 0.63 · uniR, grooming subtype, bilateral, Unilateral, positioned, behaviors
  18. [18] § Methods › Kinematic replay and antennal contact detection ↔ src/Figure2.ipynb, lines 57–177 · score 0.63 · collision diagrams, collision pair, leg segment, contact, replay, tarsus
  19. [19] § Methods › Connectome analysis › Graph visualizations ↔ src/prepare_data/Figure4_graph_tools.py, lines 38–71 · score 0.63 · source node, target node, NetworkX, graph, edge
  20. [20] § Results › Antennal grooming arises from tripartite coordination ↔ src/Figure1.ipynb, lines 424–503 · score 0.63 · uniR, subtypes, bilateral, unilateral, behavioral, grooming
  21. [21] § Results › A centralized brain network links multiple motor modules ↔ src/prepare_data/Figure4_graph_tools.py, lines 341–393 · score 0.62 · shared premotor neurons, antennal motor neurons, motor modules, synapses, neck, network
  22. [22] § Methods › Data processing › 3D pose alignment & inverse kinematics ↔ kinematics3d/constants.py, the whole file · a weak match · score 0.61 · Thorax Coxa, Tibia Tarsus, Claw, wing, tip
  23. [23] § Results › A centralized brain network links multiple motor modules ↔ src/prepare_data/Figure4_generate_grooming_network.py, lines 340–402 · score 0.61 · parameter sweep, brain connectome, sensory neurons, motor neurons, threshold, network
  24. [24] § Methods › Statistical analysis ↔ src/Figure7.ipynb, lines 73–128 · score 0.60 · Mann Whitney, stimulus transition, windows, median, fly
  25. [25] § Methods › Connectome-constrained neural network modeling › Adjacency matrix preparation ↔ src/prepare_data/Figure5_prepare_data.py, lines 34–47 · score 0.60 · distance matrix, cluster neurons, DBSCAN, algorithm
  26. [26] § Methods › Connectome-constrained neural network modeling › Model parameters and training ↔ src/prepare_data/connectome_utils.py, lines 389–410 · score 0.59 · pre synaptic, synaptic connection, post synaptic, neurotransmitter, neurons
  27. [27] § Methods › Data analysis › Dimensionality reduction using PCA ↔ src/EDFigure2.ipynb, lines 105–175 · score 0.59 · coxa trochanter, Tibia Tarsus joints, overlap, optogenetically, positions, foreleg
  28. [28] § Results › Simulating a connectome-derived antennal grooming network ↔ src/Figure5.ipynb, lines 185–219 · score 0.58 · unilateral selectivity, right neuron, left neuron, aDNs, aBNs, USI
  29. [29] § Results › Coupled circuit motifs enable robust unilateral coordination ↔ src/Figure6.ipynb, lines 779–908 · score 0.58 · neck premotor, leg premotor, shared premotor, DN33, clusters, motifs
  30. [30] § Methods › Data processing › 3D pose alignment & inverse kinematics ↔ src/EDFigure2.ipynb, lines 105–175 · score 0.57 · coxa trochanter, femur tibia, joint, positioning, leg, pose
  31. [31] § Results › Body-part coordination enhances sustained foreleg-antennal contact ↔ src/Figure2.ipynb, lines 225–365 · score 0.56 · Simes Hochberg, Contact duration, head pitch, boxes, segments, tarsus
  32. [32] § Methods › Data analysis › Transitions between behaviors ↔ src/prepare_data/Figure4_graph_tools.py, lines 495–608 · score 0.55 · directed graph, NetworkX, transition, edges, row, node
  33. [33] § Methods › Connectome analysis › Assigning neurons to groups ↔ src/prepare_data/Figure4_graph_tools.py, lines 396–442 · score 0.54 · identify central neurons, NetworkX, motor neurons, layers, synapses, premotor
  34. [34] § Methods › Connectome analysis › Graph visualizations ↔ src/Figure4.ipynb, lines 98–179 · score 0.54 · pre synaptic neuron, post synaptic neuron, node, exported, widths, connectivity
  35. [35] § Methods › Data analysis › Transitions between behaviors ↔ src/EDFigure9.ipynb, lines 52–167 · score 0.54 · directed graph, NetworkX, transition, edges, row, node
  36. [36] § Results › Coupled circuit motifs enable robust unilateral coordination ↔ src/Figure7.ipynb, lines 73–128 · score 0.53 · Mann Whitney, stimulus transition, windows, intervals, medians, Figure 7
  37. [37] § Methods › Kinematic replay and antennal contact detection ↔ src/Figure2.ipynb, lines 57–177 · score 0.53 · kinematic replay, collision pair, simulations, contact, antennal
  38. [38] § Methods › Data processing › 2D & 3D pose estimation ↔ dlc_fxns/06_extract_outlier_refine.py, the whole file · a weak match · score 0.53 · outlier frames, DeepLabCut, amputation, pose
  39. [39] § Methods › Kinematic replay and antennal contact detection ↔ src/common.py, lines 343–407 · score 0.52 · kinematic replay, collision pair, 0–1, simulations, contact, antennal
  40. [40] § Results › Simulating a connectome-derived antennal grooming network ↔ src/prepare_data/EDFigure9_random_graphs.py, lines 1–46 · score 0.52 · random seeds, adjacency matrix, edges, pre, post, neck

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 · 737 lines · 26 KB · Apache-2.0 · 6 matches

  1. """ Graph tools. """
  2. import copy
  3. import itertools
  4. import numpy as np
  5. import pandas as pd
  6. import networkx as nx
  7. import matplotlib.pyplot as plt
  8. import neuprint
  9. from neuprint import Client
  10. import Figure4_neurons as neurons
  11. MY_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InBnaXplbW96ZGlsQGdtYWlsLmNvbSIsImxldmVsIjoibm9hdXRoIiwiaW1hZ2UtdXJsIjoiaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tL2EvQUNnOG9jTENyNEoyQWozazFqTjFGTzQtcmpIVnhZU2xGV3Y5NHptSUtndDVicVRuPXM5Ni1jP3N6PTUwP3N6PTUwIiwiZXhwIjoxODkyMTQ5OTA0fQ.1jPbQ350OHMCitI6zQy5SQIBtc6A2hdY5ErwYwqpn-A'
  12. def signal_flow(A):
  13. """
  14. Implementation of the signal flow metric from Varshney et al 2011
  15. Source: A Connectome of an insect brain, Winding, Pedigo et al 2023
  16. """
  17. A = A.copy()
  18. # A = remove_loops(A)
  19. W = (A + A.T) / 2
  20. D = np.diag(np.sum(W, axis=1))
  21. L = D - W
  22. b = np.sum(W * np.sign(A - A.T), axis=1)
  23. L_pinv = np.linalg.pinv(L)
  24. z = L_pinv @ b
  25. return z
  26. def get_all_shortest_paths(
  27. graph, source_nodes, target_nodes, edge_property="syn_count_inv"
  28. ):
  29. """Get all simple paths between source and target nodes"""
  30. all_paths = []
  31. for source_n, target_n in itertools.product(source_nodes, target_nodes):
  32. if not source_n in graph.nodes or not target_n in graph.nodes:
  33. continue
  34. if nx.has_path(graph, source_n, target_n) and source_n != target_n:
  35. # If there is a direct edge, remove it and find the shortest path
  36. edge_list = []
  37. while graph.has_edge(source_n, target_n):
  38. edge_data = graph.get_edge_data(
  39. source_n, target_n
  40. ) # Store the edge data if needed
  41. edge_list.append((source_n, target_n, edge_data))
  42. graph.remove_edge(source_n, target_n)
  43. try:
  44. path = nx.shortest_path(
  45. graph, source=source_n, target=target_n, weight=edge_property
  46. )
  47. except nx.NetworkXNoPath:
  48. path = []
  49. # Add the edge back to the graph
  50. for edge in edge_list:
  51. source_n, target_n, edge_data = edge
  52. graph.add_edge(source_n, target_n, **edge_data)
  53. all_paths.append(path)
  54. return all_paths
  55. def get_all_simple_paths(graph, source_nodes, target_nodes, cutoff=4):
  56. """Get all simple paths between source and target nodes"""
  57. all_paths = []
  58. for source_n, target_n in itertools.product(source_nodes, target_nodes):
  59. if not source_n in graph.nodes or not target_n in graph.nodes:
  60. continue
  61. if nx.has_path(graph, source_n, target_n):
  62. all_paths.extend(
  63. [
  64. p
  65. for p in nx.all_simple_paths(
  66. graph, source_n, target_n, cutoff=cutoff
  67. )
  68. ]
  69. )
  70. return all_paths
  71. def threshold_prune_paths(
  72. graph,
  73. paths,
  74. threshold=0.05,
  75. edge_property="syn_count_perc",
  76. exclude_nodes=None,
  77. ):
  78. """Thresholds the paths based on the threshold value"""
  79. thresholded_paths = [
  80. p
  81. for p in paths
  82. if (len(p) > 1)
  83. and (
  84. nx.path_weight(graph, path=list(p), weight=edge_property) / (len(p) - 1)
  85. >= threshold
  86. )
  87. ]
  88. # we prune the paths that include specific nodes
  89. if exclude_nodes is None:
  90. return thresholded_paths
  91. return [p for p in thresholded_paths if not set(p).intersection(set(exclude_nodes))]
  92. def get_adj_matrix_from_conn(
  93. connectivity_df,
  94. neuron_groups,
  95. ):
  96. """Get the adjacency matrix from the connectivity dataframe between neuronal groups."""
  97. adjacency_matrix_perc = np.zeros((len(neuron_groups), len(neuron_groups)))
  98. adjacency_matrix_syn = np.zeros((len(neuron_groups), len(neuron_groups)))
  99. for i, (name_pre, nodes_pre) in enumerate(neuron_groups.items()):
  100. for j, (name_post, nodes_post) in enumerate(neuron_groups.items()):
  101. number_of_synapses_total = (
  102. connectivity_df[connectivity_df.pre_root_id.isin(nodes_pre)]
  103. .syn_count.abs()
  104. .sum()
  105. )
  106. number_of_synapses = (
  107. connectivity_df[
  108. (connectivity_df.pre_root_id.isin(nodes_pre))
  109. & (connectivity_df.post_root_id.isin(nodes_post))
  110. ]
  111. .syn_count.abs()
  112. .sum()
  113. )
  114. w_perc = (number_of_synapses / (number_of_synapses_total + 1e-5)) * 100
  115. adjacency_matrix_perc[i, j] = w_perc
  116. adjacency_matrix_syn[i, j] = number_of_synapses / (
  117. len(nodes_pre) * len(nodes_post)
  118. )
  119. return {
  120. "perc": adjacency_matrix_perc,
  121. "syn": adjacency_matrix_syn,
  122. }
  123. def get_upstream(connectivity_df, neuron_ids):
  124. """Gets the upstream partners of a list of neurons"""
  125. neuron_ids = list(neuron_ids) if not isinstance(neuron_ids, list) else neuron_ids
  126. return connectivity_df[
  127. connectivity_df.post_root_id.isin(neuron_ids)
  128. ].pre_root_id.unique()
  129. def threshold_connection(
  130. connectivity_df,
  131. pre_root_ids,
  132. post_root_ids,
  133. threshold_col_name="syn_count_perc",
  134. threshold=0.05,
  135. # verbose=False,
  136. ):
  137. """Thresholds the connections based on the threshold value"""
  138. pre_root_ids = (
  139. list(pre_root_ids) if not isinstance(pre_root_ids, list) else pre_root_ids
  140. )
  141. post_root_ids = (
  142. list(post_root_ids) if not isinstance(post_root_ids, list) else post_root_ids
  143. )
  144. thresholded_neurons = {}
  145. # for each pre root id, look at the synapse percentage
  146. for pre_id in pre_root_ids:
  147. sum_perc = connectivity_df[
  148. (connectivity_df.pre_root_id == pre_id)
  149. & (connectivity_df.post_root_id.isin(post_root_ids))
  150. ][threshold_col_name].sum()
  151. # if sum percentage is more than the threshold of the total synapses onto motor neurons
  152. if sum_perc >= threshold:
  153. thresholded_neurons[pre_id] = sum_perc
  154. # if verbose:
  155. # print(pre_id, neurons.ALL_NEURONS_REV_JO[pre_id]["name"], sum_perc)
  156. return thresholded_neurons
  157. def shuffle_postsyn_conn(adj_matrix, random_seed=0):
  158. """Shuffle the postsynaptic connection.
  159. This will ensure that a neuron will have
  160. the same number of synapses.
  161. """
  162. np.random.seed(random_seed)
  163. shuffled_adj = np.zeros_like(adj_matrix)
  164. for row in range(adj_matrix.shape[0]):
  165. shuffled_adj[row, :] = np.random.choice(
  166. adj_matrix[row, :], size=adj_matrix.shape[1], replace=False
  167. )
  168. return shuffled_adj
  169. def make_a_random_graph(
  170. original_connectivity,
  171. motor_neuron_ids,
  172. seed=1,
  173. ):
  174. """Randomize the connections in a graph."""
  175. np.random.seed(seed)
  176. all_segment_ids = set(original_connectivity.pre_root_id.tolist()).union(
  177. set(original_connectivity.post_root_id.tolist())
  178. )
  179. all_segment_ids_wo_mns = all_segment_ids.difference(set(motor_neuron_ids))
  180. random_conn = copy.deepcopy(original_connectivity)
  181. random_conn["pre_root_id"] = np.random.choice(
  182. list(all_segment_ids_wo_mns), len(random_conn)
  183. )
  184. random_conn["post_root_id"] = np.random.choice(
  185. list(all_segment_ids), len(random_conn)
  186. )
  187. random_conn["syn_count"] = np.random.permutation(random_conn["syn_count"].values)
  188. random_conn["total_synapses"] = random_conn.groupby("pre_root_id")[
  189. "syn_count"
  190. ].transform("sum")
  191. #  Normalize syn_count by dividing it with total_synapses
  192. random_conn["syn_count_perc"] = (
  193. random_conn["syn_count"] / random_conn["total_synapses"]
  194. )
  195. random_conn["syn_count_inv"] = 1 / random_conn["syn_count"].values
  196. return random_conn
  197. def get_premotor_neurons(connectivity_df, motor_neuron_ids, threshold=0.05):
  198. """Get the premotor neurons in the brain dataset, defined by a connectivity threshold."""
  199. mn_upstream = get_upstream(connectivity_df, motor_neuron_ids)
  200. mn_ups_percentage = threshold_connection(
  201. connectivity_df,
  202. mn_upstream,
  203. motor_neuron_ids,
  204. threshold=0.05,
  205. )
  206. return mn_ups_percentage
  207. def get_prem_neurons_vnc():
  208. """Get the premotor neurons in the VNC, returns three dataframes: connectivity of neck, foreleg, and shared premotor neurons with their respective motor neurons."""
  209. c = Client("neuprint.janelia.org", dataset="manc:v1.2.1", token=MY_TOKEN)
  210. c.fetch_version()
  211. # Get the leg motor neurons
  212. neuron_df_npt1_left, _ = neuprint.fetch_neurons(
  213. neuprint.NeuronCriteria(outputRois=["LegNp(T1)(L)"])
  214. )
  215. neuron_df_npt1_right, _ = neuprint.fetch_neurons(
  216. neuprint.NeuronCriteria(outputRois=["LegNp(T1)(R)"])
  217. )
  218. # Concat the two dfs
  219. neuron_df_npt1 = pd.concat(
  220. [neuron_df_npt1_left, neuron_df_npt1_right], axis=0
  221. ).dropna(subset=["instance", "type"])
  222. # Search for the motor neurons
  223. t1_leg_mn_df = neuron_df_npt1[neuron_df_npt1.instance.str.contains("MNfl")]
  224. # Find the leg premotor neurons
  225. # Example: Fetch all upstream connections TO a set of neurons
  226. leg_mns = t1_leg_mn_df.bodyId.values
  227. leg_prem_df, leg_prem_conn_df = neuprint.fetch_adjacencies(
  228. sources=None, targets=leg_mns, min_total_weight=10
  229. )
  230. # drop if the bodyId_pre is in the leg motor neurons
  231. leg_prem_conn_df = leg_prem_conn_df[~leg_prem_conn_df.bodyId_pre.isin(leg_mns)]
  232. leg_prem_conn_clean_df = (
  233. leg_prem_conn_df.groupby(["bodyId_pre", "bodyId_post"]).sum().reset_index()
  234. )
  235. # Get the neck motor neurons
  236. neuron_df, _ = neuprint.fetch_neurons(neuprint.NeuronCriteria())
  237. neuron_df = neuron_df.dropna(subset=["instance", "type"])
  238. # search for the neck motor neurons
  239. neck_mn_df = neuron_df[neuron_df.instance.str.contains("MNnm")]
  240. # Get the neck premotor neurons
  241. neck_mns = neck_mn_df.bodyId.values
  242. neck_prem_df, neck_prem_conn_df = neuprint.fetch_adjacencies(
  243. sources=None, targets=neck_mns, min_total_weight=10
  244. )
  245. neck_prem_conn_df = neck_prem_conn_df[~neck_prem_conn_df.bodyId_pre.isin(neck_mns)]
  246. neck_prem_conn_clean_df = (
  247. neck_prem_conn_df.groupby(["bodyId_pre", "bodyId_post"]).sum().reset_index()
  248. )
  249. neck_prem_neurons = neck_prem_conn_clean_df.bodyId_pre.unique()
  250. neck_prem_df = neuron_df[neuron_df.bodyId.isin(neck_prem_neurons)]
  251. leg_prem_neurons = leg_prem_conn_clean_df.bodyId_pre.unique()
  252. leg_prem_df = neuron_df[neuron_df.bodyId.isin(leg_prem_neurons)]
  253. both_prem_neurons = np.intersect1d(leg_prem_neurons, neck_prem_neurons)
  254. common_prem_df = neuron_df[neuron_df.bodyId.isin(both_prem_neurons)]
  255. print(
  256. f"Neurons ({len(both_prem_neurons)}) projecting onto leg and neck motor neurons in the VNC are: {both_prem_neurons}"
  257. )
  258. return neck_prem_df, leg_prem_df, common_prem_df
  259. def update_motor_modules(
  260. motor_module_neurons, anten_prem_set, neck_prem_set, leg_prem_set
  261. ):
  262. # Shared prem neurons: those at the intersection of at least two premotor neuron types
  263. neck_anten_leg_prem = neck_prem_set & anten_prem_set & leg_prem_set
  264. neck_anten_prem = neck_prem_set & anten_prem_set - neck_anten_leg_prem
  265. neck_leg_prem = neck_prem_set & leg_prem_set - neck_anten_leg_prem
  266. anten_leg_prem = anten_prem_set & leg_prem_set - neck_anten_leg_prem
  267. shared_prem = neck_anten_leg_prem.union(
  268. neck_anten_prem, neck_leg_prem, anten_leg_prem
  269. )
  270. all_prem_neurons = neck_prem_set.union(anten_prem_set, leg_prem_set)
  271. motor_module_neurons["leg_prem"] = list(leg_prem_set - shared_prem)
  272. motor_module_neurons["neck_prem"] = list(neck_prem_set - shared_prem)
  273. motor_module_neurons["anten_prem"] = list(anten_prem_set - shared_prem)
  274. motor_module_neurons["shared_prem"] = list(shared_prem)
  275. motor_module_neurons["all_prem"] = list(all_prem_neurons)
  276. return motor_module_neurons
  277. def get_panel_m(
  278. grooming_network, motor_module_neurons, shared_prem, anten_prem_set, neck_prem_set
  279. ):
  280. # Look at how much of information to MNs come from shared vs. other prem neurons
  281. # total number of synapses onto neck mns
  282. neck_mns_upstream_synapses = grooming_network[
  283. grooming_network.post_root_id.isin(motor_module_neurons["neck_mn"])
  284. ].syn_count.sum()
  285. # number of synapses from shared prem onto neck mns
  286. neck_mns_shared_prem_input = grooming_network[
  287. (grooming_network.post_root_id.isin(motor_module_neurons["neck_mn"]))
  288. & (grooming_network.pre_root_id.isin(shared_prem))
  289. ].syn_count.sum()
  290. # number of synapses from ind. prem onto neck mns
  291. neck_mns_ind_input = grooming_network[
  292. (grooming_network.post_root_id.isin(motor_module_neurons["neck_mn"]))
  293. & (grooming_network.pre_root_id.isin(neck_prem_set - shared_prem))
  294. ].syn_count.sum()
  295. neck_input_percen = {
  296. "shared": 100 * neck_mns_shared_prem_input / neck_mns_upstream_synapses,
  297. "ind": 100 * neck_mns_ind_input / neck_mns_upstream_synapses,
  298. }
  299. print(
  300. f"""Neck motor neurons receive {neck_input_percen["shared"]:.2f}% of their input from shared premotor neurons"""
  301. )
  302. # Same for the antennal motor neurons
  303. # total number of synapses onto anten mns
  304. anten_mns_upstream_synapses = grooming_network[
  305. grooming_network.post_root_id.isin(motor_module_neurons["anten_mn"])
  306. ].syn_count.sum()
  307. # number of synapses from shared prem onto anten mns
  308. anten_mns_shared_prem_input = grooming_network[
  309. (grooming_network.post_root_id.isin(motor_module_neurons["anten_mn"]))
  310. & (grooming_network.pre_root_id.isin(shared_prem))
  311. ].syn_count.sum()
  312. # number of synapses from ind prem onto anten mns
  313. anten_mns_ind_input = grooming_network[
  314. (grooming_network.post_root_id.isin(motor_module_neurons["anten_mn"]))
  315. & (grooming_network.pre_root_id.isin(anten_prem_set - shared_prem))
  316. ].syn_count.sum()
  317. anten_input_percen = {
  318. "shared": 100 * anten_mns_shared_prem_input / anten_mns_upstream_synapses,
  319. "ind": 100 * anten_mns_ind_input / anten_mns_upstream_synapses,
  320. }
  321. print(
  322. f"""Antennal motor neurons receive {anten_input_percen["shared"]:.2f}% of their input from shared premotor neurons"""
  323. )
  324. return neck_input_percen, anten_input_percen
  325. def identify_central_neurons(grooming_network, motor_module_neurons):
  326. """To identify central neurons, we look at all simple paths from the JOF to the premotor/motor neurons and take those with a connection strength more than a threshold."""
  327. graph_table = nx.from_pandas_edgelist(
  328. grooming_network,
  329. source="pre_root_id",
  330. target="post_root_id",
  331. edge_attr=["syn_count", "syn_count_perc"],
  332. create_using=nx.DiGraph(),
  333. )
  334. cutoff = 4 # based on the layer algorithm max depth
  335. threshold = 0.05 # average synapse count percentage
  336. jof2prem_simple_paths = get_all_simple_paths(
  337. graph_table,
  338. # From
  339. motor_module_neurons["jo_f"],
  340. # To
  341. motor_module_neurons["all_prem"]
  342. + motor_module_neurons["neck_mn"]
  343. + motor_module_neurons["anten_mn"],
  344. cutoff=cutoff,
  345. )
  346. # Prune those with low connection strength
  347. jof2prem_simple_paths_pruned = threshold_prune_paths(
  348. graph_table,
  349. jof2prem_simple_paths,
  350. threshold=threshold,
  351. edge_property="syn_count_perc",
  352. exclude_nodes=motor_module_neurons["jo_e"]
  353. + motor_module_neurons["jo_c"]
  354. + motor_module_neurons["bm_ant"],
  355. )
  356. # Exclude sensory and motor module neurons
  357. central_neurons = set(
  358. itertools.chain.from_iterable(jof2prem_simple_paths_pruned)
  359. ).difference(
  360. set(
  361. motor_module_neurons["jo_f"]
  362. + motor_module_neurons["all_prem"]
  363. + motor_module_neurons["neck_mn"]
  364. + motor_module_neurons["anten_mn"]
  365. )
  366. )
  367. return list(central_neurons)
  368. def get_conn_between_groups(grooming_network, motor_module_neurons):
  369. """Calculate the connectivity percentage between neuron groups."""
  370. input_dictionary = {
  371. "sensory": motor_module_neurons["jo_f"],
  372. "central": motor_module_neurons["central"],
  373. "anten prem": motor_module_neurons["anten_prem"],
  374. "neck prem": motor_module_neurons["neck_prem"],
  375. "leg prem": motor_module_neurons["leg_prem"],
  376. "shared prem": motor_module_neurons["shared_prem"],
  377. }
  378. prem_mn_dictionary = {
  379. "central": motor_module_neurons["central"],
  380. "anten prem": motor_module_neurons["anten_prem"],
  381. "neck prem": motor_module_neurons["neck_prem"],
  382. "leg prem": motor_module_neurons["leg_prem"],
  383. "shared prem": motor_module_neurons["shared_prem"],
  384. "anten mn": motor_module_neurons["anten_mn"],
  385. "neck mn": motor_module_neurons["neck_mn"],
  386. }
  387. input_central_to_prem_array = np.zeros(
  388. (len(input_dictionary) + 1, len(prem_mn_dictionary))
  389. )
  390. # For each premotor neuron type, get the percentage of synapses going to each premotor neuron type
  391. input_central_to_prem_perc = {}
  392. for j, to_prem_type in enumerate(prem_mn_dictionary):
  393. total_synapses = grooming_network[
  394. grooming_network.post_root_id.isin(prem_mn_dictionary[to_prem_type])
  395. ].syn_count.sum()
  396. for i, from_prem_type in enumerate(input_dictionary):
  397. btw_synapses = grooming_network[
  398. (grooming_network.pre_root_id.isin(input_dictionary[from_prem_type]))
  399. & (grooming_network.post_root_id.isin(prem_mn_dictionary[to_prem_type]))
  400. ].syn_count.sum()
  401. # percentage
  402. if total_synapses == 0:
  403. perc = 0
  404. else:
  405. perc = 100 * btw_synapses / total_synapses
  406. # prem_to_prem_perc[(from_prem_type, to_prem_type)] = perc
  407. input_central_to_prem_array[i, j] = perc
  408. # Other
  409. input_central_to_prem_array[-1, :] = 100 - input_central_to_prem_array.sum(axis=0)
  410. return input_central_to_prem_array
  411. def draw_graph(
  412. adjacency_matrix,
  413. neuron_group_names_pre,
  414. neuron_group_names_post,
  415. pos_custom,
  416. node_colors,
  417. title="",
  418. fig_name=None,
  419. edge_label=False,
  420. normalize_edge_weight=False,
  421. threshold=0,
  422. connectionstyle="arc3,rad=0.1",
  423. export_path=None,
  424. ):
  425. # Plot a graph from the adjacency matrix
  426. neuron_names_pre = [name.replace("_", " ") for name in neuron_group_names_pre]
  427. neuron_names_post = [name.replace("_", " ") for name in neuron_group_names_post]
  428. neuron_names = np.unique(neuron_names_pre + neuron_names_post)
  429. fig, ax = plt.subplots(figsize=(2.4, 2.8), dpi=300)
  430. edge_color = []
  431. edge_weight = []
  432. edges = []
  433. # create directed graph from transition dataframe
  434. G = nx.DiGraph()
  435. for neuron in neuron_names:
  436. G.add_node(neuron)
  437. for row in range(adjacency_matrix.shape[0]):
  438. for column in range(adjacency_matrix.shape[1]):
  439. if np.abs(adjacency_matrix[row, column]) > threshold:
  440. # print(
  441. # f"{neuron_names_pre[row]} -> {neuron_names_post[column]}: {adjacency_matrix[row, column]}"
  442. # )
  443. G.add_edge(
  444. neuron_names_pre[row],
  445. neuron_names_post[column],
  446. weight=np.abs(adjacency_matrix[row, column]),
  447. )
  448. edge_color.append(
  449. "darkblue" if adjacency_matrix[row, column] < 0 else "darkred"
  450. )
  451. edges.append((neuron_names_pre[row], neuron_names_post[column]))
  452. edge_weight.append(adjacency_matrix[row, column])
  453. edge_weight = np.array(edge_weight)
  454. # print(edge_weight.min(), edge_weight.max())
  455. if normalize_edge_weight:
  456. edge_weight -= edge_weight.min() - 0.2
  457. edge_weight /= edge_weight.max() - edge_weight.min() - 0.2
  458. edge_weight *= 4.5
  459. # create node and edge labels
  460. node_labels = {
  461. node: node.replace(" ", "\n").replace("OTHER", "").replace("NEURONS", "")
  462. for node in G.nodes()
  463. }
  464. edge_labels = {(u, v): round(d["weight"], 1) for u, v, d in G.edges(data=True)}
  465. # create graph layout and draw nodes, edges, and labels
  466. pos = pos_custom
  467. node_size = 300
  468. nx.draw_networkx_nodes(
  469. G,
  470. pos,
  471. nodelist=neuron_names,
  472. node_size=node_size,
  473. node_shape="s",
  474. node_color=[node_colors[name] for name in G.nodes()],
  475. alpha=1,
  476. edgecolors="black",
  477. )
  478. # print(G.edges())
  479. nx.draw_networkx_edges(
  480. G,
  481. pos,
  482. # node_size=node_size,
  483. edgelist=edges,
  484. width=edge_weight,
  485. arrows=True,
  486. edge_color="black", # edge_color,
  487. connectionstyle=connectionstyle,
  488. alpha=1,
  489. )
  490. nx.draw_networkx_labels(G, pos, labels=node_labels, font_size=5)
  491. if edge_label:
  492. nx.draw_networkx_edge_labels(
  493. G,
  494. pos,
  495. edge_labels=edge_labels,
  496. font_size=3,
  497. rotate=False,
  498. label_pos=0.35,
  499. )
  500. # display graph
  501. plt.axis("off")
  502. ax.margins(0.1)
  503. ax.set_title(title)
  504. if fig_name is not None and export_path is not None:
  505. fig.savefig(
  506. export_path / f"{fig_name}.png", bbox_inches="tight", facecolor="white"
  507. )
  508. # plt.show()
  509. return G, edge_weight
  510. def order_signal_flow(grooming_network):
  511. """Takes the connectivity table of a network, and orders nodes based on their signal flow score (i.e., input to output)."""
  512. graph_table = nx.from_pandas_edgelist(
  513. grooming_network,
  514. source="pre_root_id",
  515. target="post_root_id",
  516. edge_attr=["syn_count", "syn_count_perc"],
  517. create_using=nx.DiGraph(),
  518. )
  519. # make the adj matrix
  520. adj_matrix_unsigned = nx.to_numpy_array(graph_table, weight=None)
  521. # apply the signal flow algorithm
  522. z = signal_flow(adj_matrix_unsigned)
  523. sort_inds = np.argsort(z)[::-1]
  524. adj_sorted_unsigned = adj_matrix_unsigned[np.ix_(sort_inds, sort_inds)]
  525. # get list of node names
  526. nodes = np.array(list(graph_table.nodes()))[sort_inds]
  527. # sort z too
  528. z_sorted = z[sort_inds]
  529. return adj_sorted_unsigned, nodes, z_sorted
  530. def layer_neurons(nodes, signal_flow_score, no_layers=10):
  531. """Divide the neurons into layers based on their signal flow score."""
  532. layer_bounds = np.linspace(
  533. signal_flow_score.max(), signal_flow_score.min(), no_layers
  534. )
  535. layer_neurons = {}
  536. for layer_no in range(len(layer_bounds) - 1):
  537. layer_neurons[layer_no + 1] = nodes[
  538. np.logical_and(
  539. layer_bounds[layer_no] >= signal_flow_score,
  540. signal_flow_score >= layer_bounds[layer_no + 1],
  541. )
  542. ]
  543. return layer_neurons, layer_bounds
  544. def get_connectivity_between_layers(layer_neurons, grooming_network):
  545. """ Computes the number of synapses between layers. """
  546. layers = list(layer_neurons.keys())
  547. # connectivity between the layers
  548. layer_by_layer = np.zeros((len(layers), len(layers)))
  549. layer_by_layer_inh = np.zeros((len(layers), len(layers)))
  550. layer_by_layer_exc = np.zeros((len(layers), len(layers)))
  551. for layer1 in sorted(layers):
  552. layer1_neurons = layer_neurons[layer1]
  553. for layer2 in sorted(layers):
  554. layer2_neurons = layer_neurons[layer2]
  555. synapses = grooming_network[
  556. grooming_network.pre_root_id.isin(layer1_neurons)
  557. & grooming_network.post_root_id.isin(layer2_neurons)
  558. ]
  559. synapses_inh = synapses[synapses.nt_type.isin(["GABA", "GLUT"])].syn_count.sum()
  560. synapses_exc = synapses[
  561. synapses.nt_type.isin(["ACH", "SER", "DA", "OCT"])
  562. ].syn_count.sum()
  563. synapses_total = synapses.syn_count.sum()
  564. assert synapses_total == synapses_inh + synapses_exc
  565. layer_by_layer[layer1 - 1, layer2 - 1] = synapses_total
  566. layer_by_layer_inh[layer1 - 1, layer2 - 1] = synapses_inh
  567. layer_by_layer_exc[layer1 - 1, layer2 - 1] = synapses_exc
  568. return layer_by_layer, layer_by_layer_inh, layer_by_layer_exc
  569. def classify_central_neurons(grooming_network, neuron_groups):
  570. """ Classify central neurons based on their projections onto premotor neurons."""
  571. central_neurons = neuron_groups["central"]
  572. shared_prem = neuron_groups["shared_prem"]
  573. neck_prem = neuron_groups["neck_prem"]
  574. anten_prem = neuron_groups["anten_prem"]
  575. leg_prem = neuron_groups["leg_prem"]
  576. # neck upstream and central
  577. shared_prem_upstream = set(grooming_network[
  578. grooming_network.pre_root_id.isin(central_neurons) &
  579. grooming_network.post_root_id.isin(shared_prem)
  580. ].pre_root_id.unique())
  581. neck_prem_upstream = set(grooming_network[
  582. grooming_network.pre_root_id.isin(central_neurons) &
  583. grooming_network.post_root_id.isin(neck_prem)
  584. ].pre_root_id.unique()) - shared_prem_upstream
  585. anten_prem_upstream = set(grooming_network[
  586. grooming_network.pre_root_id.isin(central_neurons) &
  587. grooming_network.post_root_id.isin(anten_prem)
  588. ].pre_root_id.unique()) - shared_prem_upstream
  589. leg_prem_upstream = set(grooming_network[
  590. grooming_network.pre_root_id.isin(central_neurons) &
  591. grooming_network.post_root_id.isin(leg_prem)
  592. ].pre_root_id.unique()) - shared_prem_upstream
  593. central_array_projection = np.zeros((len(central_neurons), 5))
  594. for central_id, central_neuron in enumerate(central_neurons):
  595. if central_neuron in anten_prem_upstream:
  596. central_array_projection[central_id, 0] = 1
  597. if central_neuron in neck_prem_upstream:
  598. central_array_projection[central_id, 1] = 1
  599. if central_neuron in leg_prem_upstream:
  600. central_array_projection[central_id, 2] = 1
  601. if central_neuron in shared_prem_upstream:
  602. central_array_projection[central_id, 3] = 1
  603. # if not in any premotor neuron
  604. if (
  605. central_neuron not in shared_prem_upstream
  606. and central_neuron not in neck_prem_upstream
  607. and central_neuron not in anten_prem_upstream
  608. and central_neuron not in leg_prem_upstream
  609. ):
  610. central_array_projection[central_id, 4] = 1
  611. return central_array_projection

Figure4_graph_tools.py at commit 65787ac, under Apache-2.0 · at the source

Overview

  1. Neuroengineering Laboratory, Brain Mind Institute & Interfaculty Institute of Bioengineering, EPFL, Lausanne, Switzerland
  2. Biorobotics Laboratory, Institute of Bioengineering, EPFL, Lausanne, Switzerland
  3. Kempner Institute, Harvard University, Allston, MA USA
Institutions: Harvard University (United States); École Polytechnique Fédérale de Lausanne (Switzerland)
Journal: Nature communications, volume 17, issue 1, article 5617
Dates: received 27 January 2025; accepted 7 April 2026; published online 23 April 2026
Type: Research article · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.1038/s41467-026-72152-x · PMID 42026054 · PMCID PMC13314972 · OpenAlex W7155399663
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: computational modeling (no new data) (modality), drosophila (organism), systems (subfield)
Methods: Statistics, Smoothing, state filtering, decompositions, Preprocessing, Evoked potentials, Connectivity, Machine learning, fMRI & imaging
Keywords: Motor control, Network models, Sensorimotor processing
MeSH: Arthropod Antennae*, Brain*, Drosophila melanogaster*, Grooming*, Nerve Net*, Animals, Biomechanical Phenomena, Connectome, Interneurons, Locomotion (* major topic)
Topic: Neural dynamics and brain function (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Funding: NIH HHS (P40 OD018537); Swiss National Science Foundation (181239, 175667); European Research Council (951477)
Citations: cited by 1 paper (Europe PMC); 119 references in the paper

Abstract

The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.

Repositories

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

gizemozd/anipose

License: BSD-2-Clause
State: the link answers, verified on 29 September 2026
Evidence: files inventoried
Commit: eb23f2ddde8d50b030c2195f475ab8aef370bfd5, 16 July 2022
Languages: Python (24), JavaScript (1), Shell (1)
Size: 77 files, 26 scripts
Software Heritage: archived
Found in: the text, “2D & 3D pose estimation”
Holds: README, license file, environment (setup.py, docs/sphinx/requirements.txt), documentation
Not found: CITATION.cff, tests, continuous integration
Tools: NumPy (19 files), pandas (18 files), OpenCV (15 files), SciPy (10 files), DeepLabCut (2 files), Matplotlib (2 files), scikit-learn (1 file)
Availability: 1 check, the latest on 29 September 2026: the link answers
  • 29 September 2026: the link answers
28 files

NeLy-EPFL/kinematics3d

License: Apache-2.0
State: the link answers, verified on 29 September 2026
Evidence: files inventoried
Commit: 7b2621bf9c677266b68d755822356ffe1868524d, 16 July 2025
Languages: Python (25), Shell (1)
Size: 51 files, 26 scripts
Software Heritage: not archived
Found in: the text, “2D & 3D pose estimation”
Holds: README, license file, environment (setup.py)
Not found: CITATION.cff, tests, continuous integration, documentation
Tools: DeepLabCut (10 files), TensorFlow (6 files), Matplotlib (4 files), NumPy (4 files), OpenCV (3 files), pandas (1 file)
Availability: 1 check, the latest on 29 September 2026: the link answers
  • 29 September 2026: the link answers
28 files

NeLy-EPFL/antennal-grooming

License: Apache-2.0
State: the link answers, verified on 29 September 2026
Evidence: files inventoried
Commit: 65787ac7fa35aa5520addb6d3e4ac27887bbaf60, 4 May 2026
Languages: Jupyter (18), Python (11), Shell (2)
Size: 57 files, 31 scripts
Software Heritage: archived
Found in: “Code availability”
Holds: README, license file, environment (requirements.txt), 18 notebooks
Not found: CITATION.cff, tests, continuous integration, documentation
Tools: NumPy (27 files), pandas (23 files), Matplotlib (22 files), seaborn (15 files), NetworkX (12 files), SciPy (11 files), scikit-learn (4 files), h5py (1 file), statsmodels (1 file)
Availability: 1 check, the latest on 29 September 2026: the link answers
  • 29 September 2026: the link answers
33 files

Code availability statement

The paper has a code availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

Read it in the paper: doi.org/10.1038/s41467-026-72152-x.

Tracing map

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

What the map holds:

  • 3 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 83 scripts, each with its path and the digest of its content;
  • 40 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 statement

The paper has a data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

Read it in the paper: doi.org/10.1038/s41467-026-72152-x.

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, 6 authors, 3 keywords, 10 MeSH terms, 3 funders, 99 references.

Cite

This paper

Özdil, P. G., Arreguit, J., Scherrer, C., Hurtak, F., Ijspeert, A., & Ramdya, P. (2026). Centralized brain networks controlling antennal grooming coordination. Nature communications, 17(1), 5617. https://doi.org/10.1038/s41467-026-72152-x

BibTeX

@article{ozdil2026centralized,
author = {Özdil, Pembe Gizem and Arreguit, Jonathan and Scherrer, Clara and Hurtak, Femke and Ijspeert, Auke and Ramdya, Pavan},
title = {{Centralized brain networks controlling antennal grooming coordination}},
journal = {Nature communications},
year = {2026},
month = apr,
volume = {17},
number = {1},
pages = {5617},
publisher = {Nature Publishing Group},
issn = {2041-1723},
doi = {10.1038/s41467-026-72152-x},
url = {https://doi.org/10.1038/s41467-026-72152-x},
pmid = {42026054},
pmcid = {PMC13314972}
}

RIS

TY - JOUR
AU - Özdil, Pembe Gizem
AU - Arreguit, Jonathan
AU - Scherrer, Clara
AU - Hurtak, Femke
AU - Ijspeert, Auke
AU - Ramdya, Pavan
TI - Centralized brain networks controlling antennal grooming coordination
T2 - Nature communications
J2 - Nat Commun
PY - 2026
DA - 2026/04/23
VL - 17
IS - 1
SP - 5617
SN - 2041-1723
PB - Nature Publishing Group
DO - 10.1038/s41467-026-72152-x
UR - https://doi.org/10.1038/s41467-026-72152-x
LA - en
ER -

CSL-JSON

{
"id": "10.1038/s41467-026-72152-x",
"type": "article-journal",
"title": "Centralized brain networks controlling antennal grooming coordination",
"container-title": "Nature communications",
"author": [
{
"family": "Özdil",
"given": "Pembe Gizem"
},
{
"family": "Arreguit",
"given": "Jonathan"
},
{
"family": "Scherrer",
"given": "Clara"
},
{
"family": "Hurtak",
"given": "Femke"
},
{
"family": "Ijspeert",
"given": "Auke"
},
{
"family": "Ramdya",
"given": "Pavan"
}
],
"container-title-short": "Nat Commun",
"volume": "17",
"issue": "1",
"page": "5617",
"DOI": "10.1038/s41467-026-72152-x",
"PMID": "42026054",
"PMCID": "PMC13314972",
"ISSN": "2041-1723",
"publisher": "Nature Publishing Group",
"URL": "https://doi.org/10.1038/s41467-026-72152-x",
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
23
]
]
}
}

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/s41586-026-10735-w [code]
Distributed control circuits across a brain-and-cord connectome.
Journal: Nature
In common: NetworkX, seaborn, scikit-learn, 4 other tools, drosophila, 26 references
[2] doi:10.7554/elife.96084 [code]
Organization of circuits linking descending input to motor output in the &lt;i&gt;Drosophila&lt;/i&gt; Male Adult Nerve Cord connectome.
Journal: eLife
In common: drosophila, systems, 19 references
[3] 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: TensorFlow, NetworkX, h5py, 6 other tools, systems, 10 references
[4] doi:10.7554/elife.107867 [code]
Peripheral anatomy and central connectivity of proprioceptive sensory neurons in the &lt;i&gt;Drosophila&lt;/i&gt; wing.
Journal: eLife
In common: seaborn, scikit-learn, pandas, 3 other tools, drosophila, 9 references
[5] doi:10.1371/journal.pbio.3003959 [code]
Recurrent synapses between CO2-sensitive olfactory sensory neurons enable robust CO2 detection in Aedes aegypti mosquitoes.
Journal: PLoS biology
In common: statsmodels, seaborn, scikit-learn, 4 other tools, drosophila, 8 references
[6] doi:10.1038/s41467-026-72057-9 [code]
Sex-specific behavioral feedback modulates sensorimotor processing and drives flexible social behavior.
Journal: Nature communications
In common: TensorFlow, OpenCV, h5py, 7 other tools, drosophila, systems, 4 references
[7] doi:10.1016/j.isci.2026.117359 [code]
Connectomic mapping of pharyngeal and gut sensory circuits in adult &lt;i&gt;Drosophila&lt;/i&gt;.
Journal: iScience
In common: statsmodels, seaborn, pandas, 3 other tools, drosophila, systems, 7 references
[8] doi:10.1038/s41593-026-02232-0 [code]
Entorhinal cortex represents task-relevant remote locations independently of CA1.
Journal: Nature neuroscience
In common: DeepLabCut, NetworkX, OpenCV, 8 other tools, systems, 1 reference
[9] doi:10.1038/s41593-026-02262-8 [code]
Cheese3D enables sensitive detection and analysis of whole-face movement in mice.
Journal: Nature neuroscience
In common: DeepLabCut, OpenCV, h5py, 6 other tools, systems, 2 references
[10] 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: TensorFlow, statsmodels, seaborn, 5 other tools, 4 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.