Dorsoventral gradient of theta sweeps in the medial entorhinal cortex.
The 9 matches
- [1] § MATERIALS AND METHODS › Computational modeling ↔ ComputationalModellingFig4/network_models.py, lines 248–268 · score 0.63 · offset length, phase offset, firing rate, conjunctive, GC, connectivity
- [2] § MATERIALS AND METHODS › Computational modeling ↔ ComputationalModellingFig4/Fig4_thetasweeps.ipynb, lines 510–623 · score 0.61 · phase offset, theta modulation, internal direction, theta phases, sensory, GC
- [3] § RESULTS › Efficient learning of hippocampal spatial maps via theta sweeps with varied angles ↔ ComputationalModellingFig4/network_models.py, lines 21–139 · score 0.60 · synaptic connectivity, bump center, Place cells, square, network, distance
- [4] § MATERIALS AND METHODS › Analyzing directional sweeps and locational sweeps across theta phase bins ↔ EmpiricalDataAnalysis/SI3_SweepAngle_across_phase_bins_forward_and_backward.ipynb, lines 601–722 · score 0.60 · phase bins, left sweeps, right sweep, theta phase, backward, locational
- [5] § MATERIALS AND METHODS › Statistical analysis ↔ EmpiricalDataAnalysis/Fig1_Bar_GridsweepAngle.ipynb, lines 14–60 · score 0.58 · likelihood ratio, full model, MixedLM, LMMs, fitted, sweep angle
- [6] § MATERIALS AND METHODS › Statistical analysis ↔ EmpiricalDataAnalysis/Fig3_Bar_IDmvl.ipynb, lines 8–51 · score 0.57 · likelihood ratio, full model, MixedLM, LMMs, fitted, animals
- [7] § MATERIALS AND METHODS › Decoding directional theta sweeps in tmDCs ↔ ComputationalModellingFig4/Fig4_tmDC_skipping.ipynb, lines 337–390 · score 0.54 · head direction bin, tuning curves, smoothed, circular, Gaussian, cell
- [8] § MATERIALS AND METHODS › Computational modeling ↔ ComputationalModellingFig4/Fig4_thetasweeps.ipynb, lines 168–237 · score 0.51 · angular speed, Theta modulation, oscillation, linearly, Computational
- [9] § MATERIALS AND METHODS › Computational modeling ↔ ComputationalModellingFig4/Fig4_tmDC_skipping.ipynb, lines 168–237 · score 0.51 · angular speed, Theta modulation, oscillation, linearly, Computational
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 · 504 lines · 20 KB · no license · 2 matches
- from dataclasses import dataclass
- import brainpy as bp
- import brainpy.math as bm
- import jax
- import numpy as np
- import networkx as nx
- @dataclass
- class PCParams:
- tau: float = 10.0
- tau_v: float = 100.0
- noise_strength: float = 0.0
- k: float = 0.2
- adaptation_strength: float = 15.0
- a: float = 0.2
- A: float = 5.0
- J0: float = 1.0
- g: float = 1.0
- conn_noise: float = 0.0
- class PCNet(bp.DynamicalSystem):
- """
- Graph-based continuous-attractor place cell network (linearized track version).
- Each node in the graph corresponds to a place cell.
- Synaptic connectivity is defined as a Gaussian function of the
- geodesic distance between nodes.
- If it is a linear track, the geodesic distance is the same as the Euclidean distance.
- If it is a T maze track or other more complex track, the geodesic distance is the shortest path distance along the track.
- This distance measurement is important for the network behaviours
- Parameters
- ----------
- Graph : networkx.Graph
- Graph where nodes represent place cells and edge weights represent distances.
- Must include 'dx' in Graph.graph.
- params : PCParams
- Parameter object containing model constants such as a, J0, tau, etc.
- Attributes
- ----------
- cell_num : int
- Number of place cells (nodes).
- D : brainpy.math array
- Matrix of geodesic distances between nodes.
- conn_mat : brainpy.math array
- Synaptic weight matrix.
- r, u, v : bm.Variable
- Neural rate, internal state, and adaptation state.
- center : bm.Variable
- Estimated bump centre position.
- """
- def __init__(self, Graph, params: PCParams = PCParams()):
- super().__init__()
- self.Graph = Graph
- self.params = params
- # number of cells = number of nodes in graph
- self.cell_num = len(Graph.nodes)
- self.node_list = list(Graph.nodes)
- dx = self.Graph.graph.get("dx", None)
- if dx is None:
- raise ValueError("Graph.graph['dx'] must be defined (spatial step size).")
- self.x = bm.asarray(np.arange(self.cell_num) * dx)
- # --- derived parameters ---
- self.m = params.adaptation_strength * params.tau / params.tau_v
- # --- compute geodesic distance matrix (cell_num×cell_num)
- geodist = dict(nx.all_pairs_dijkstra_path_length(Graph, weight='weight'))
- D = np.zeros((self.cell_num, self.cell_num))
- for i, ni in enumerate(self.node_list):
- for j, nj in enumerate(self.node_list):
- D[i, j] = geodist[ni][nj]
- self.D = bm.asarray(D)
- # --- build connectivity based on geodesic distance ---
- base_connection = self.make_connection(self.D)
- noise_connection = np.random.normal(0, params.conn_noise, size=(self.cell_num, self.cell_num))
- self.conn_mat = base_connection + noise_connection
- # --- state variables ---
- self.r = bm.Variable(bm.zeros(self.cell_num))
- self.u = bm.Variable(bm.zeros(self.cell_num))
- self.v = bm.Variable(bm.zeros(self.cell_num))
- self.center = bm.Variable(bm.zeros(1))
- # --- integrator ---
- self.integral = bp.odeint(method="exp_euler", f=self.derivative)
- @property
- def derivative(self):
- du = lambda u, t, input: (-u + input - self.v) / self.params.tau
- dv = lambda v, t: (-v + self.m * self.u) / self.params.tau_v
- return bp.JointEq([du, dv])
- # ===== connectivity based on geodesic distances =====
- def make_connection(self, D):
- """
- Gaussian weight kernel based on graph geodesic distances.
- """
- a = self.params.a
- J0 = self.params.J0
- W = J0 * bm.exp(-0.5 * (D / a) ** 2)
- # normalise or scale if desired
- W = W / (bm.sqrt(2 * bm.pi) * a)
- return W
- def get_bump_center(self, r, x):
- denom = bm.sum(r) + 1e-12
- center = bm.sum(r * x) / denom
- return center.reshape(-1,)
- # ===== external input based on animal position =====
- def input_bump(self, animal_pos_node_index):
- """
- Generate Gaussian bump centred on the node corresponding to the animal's current position.
- node_index: integer index into self.nodes
- """
- d = self.D[animal_pos_node_index]
- return self.params.A * bm.exp(-0.5 * (d / self.params.a) ** 2)
- # ===== update loop =====
- def update(self, animal_pos_node_index, ThetaInput):
- self.center.value = self.get_bump_center(r=self.r, x=self.x)
- Iext = ThetaInput * self.input_bump(animal_pos_node_index)
- Irec = bm.matmul(self.conn_mat, self.r)
- noise = bm.random.randn(self.cell_num) * self.params.noise_strength
- input_total = Iext + Irec + noise
- # integrate for current step
- u, v = self.integral(self.u, self.v, bp.share.load("t"), input_total)
- self.u.value = bm.where(u > 0, u, 0)
- self.v.value = v
- u_sq = bm.square(self.u)
- self.r.value = self.params.g * u_sq / (1.0 + self.params.k * bm.sum(u_sq))
- @dataclass
- class DCParams:
- tau: float = 10.0
- tau_v: float = 100.0
- noise_strength: float = 0.0
- k: float = 0.2
- adaptation_strength: float = 15.0
- a: float = 0.7
- A: float = 3.0
- J0: float = 1.0
- g: float = 1.0
- z_min: float = -bm.pi
- z_max: float = bm.pi
- conn_noise: float = 0.0
- class DCNet(bp.DynamicalSystem):
- """
- 1D continuous-attractor direction cell network
- """
- def __init__(self, cell_num: int, params: DCParams = DCParams()):
- super().__init__()
- self.cell_num = cell_num
- self.params = params
- # --- derived parameters ---
- self.m = params.adaptation_strength * params.tau / params.tau_v
- # --- feature space ---
- self.z_min = params.z_min
- self.z_max = params.z_max
- self.z_range = self.z_max - self.z_min
- x1 = bm.linspace(self.z_min, self.z_max, self.cell_num + 1)
- self.x = x1[:-1]
- # --- connectivity ---
- base_connection = self.make_connection()
- noise_connection = np.random.normal(0, params.conn_noise, size=(self.cell_num, self.cell_num))
- self.conn_mat = base_connection + noise_connection
- # --- state variables ---
- self.r = bm.Variable(bm.zeros(self.cell_num))
- self.u = bm.Variable(bm.zeros(self.cell_num))
- self.v = bm.Variable(bm.zeros(self.cell_num))
- self.center = bm.Variable(bm.zeros(1))
- # --- integrator ---
- self.integral = bp.odeint(method="exp_euler", f=self.derivative)
- @property
- def derivative(self):
- du = lambda u, t, input: (-u + input - self.v) / self.params.tau
- dv = lambda v, t: (-v + self.m * self.u) / self.params.tau_v
- return bp.JointEq([du, dv])
- # ===== utilities =====
- def handle_periodic_condition(self, A):
- B = bm.where(A > bm.pi, A - 2 * bm.pi, A)
- B = bm.where(B < -bm.pi, B + 2 * bm.pi, B)
- return B
- def calculate_dist(self, d):
- d = self.handle_periodic_condition(d)
- d = bm.where(d > 0.5 * self.z_range, d - self.z_range, d)
- return d
- # ===== connectivity =====
- def make_connection(self):
- @jax.vmap
- def get_J(xbins):
- d = self.calculate_dist(xbins - self.x)
- Jxx = (
- self.params.J0
- * bm.exp(-0.5 * bm.square(d / self.params.a))
- / (bm.sqrt(2 * bm.pi) * self.params.a)
- )
- return Jxx
- return get_J(self.x)
- # ===== dynamics =====
- def get_bump_center(self, r, x):
- exppos = bm.exp(1j * x)
- center = bm.angle(bm.sum(exppos * r))
- return center.reshape(-1,)
- def input_bump(self, head_direction):
- return self.params.A * bm.exp(
- -0.5 * bm.square(self.calculate_dist(self.x - head_direction) / self.params.a)
- )
- # ===== update loop =====
- def update(self, head_direction, ThetaInput):
- self.center.value = self.get_bump_center(r=self.r, x=self.x)
- Iext = ThetaInput * self.input_bump(head_direction)
- Irec = bm.matmul(self.conn_mat, self.r)
- noise = bm.random.randn(self.cell_num) * self.params.noise_strength
- input_total = Iext + Irec + noise
- # integrate for current step
- u, v = self.integral(self.u, self.v, bp.share.load("t"), input_total)
- self.u.value = bm.where(u > 0, u, 0)
- self.v.value = v
- u_sq = bm.square(self.u)
- self.r.value = self.params.g * u_sq / (1.0 + self.params.k * bm.sum(u_sq))
- @dataclass
- class GCParams:
- # dynamics
- tau: float = 10.0
- tau_v: float = 100.0
- noise_strength: float = 0.0 #activity noise
- conn_noise: float = 0.0 #connectivity noise
- k: float = 1.0
- adaptation_strength: float = 15.0 # (mbar)
- # connectivity / input
- a: float = 0.8
- A: float = 3.0
- J0: float = 5.0
- g: float = 1000.0 #scale the firing rate to make it reasonable, no biological meaning
- #controlling grid spacing, larger means smaller spacing
- mapping_ratio: float = 1
- #cntrolling offset length from conjunctive gc layer to gc layer, this is the key to drive the bump to move
- phase_offset: float = 1.0 / 20 #relative to -pi~pi range
- class GCNet(bp.DynamicalSystem):
- """
- 2D continuous-attractor grid cell network
- Grid size is num_gc_x x num_gc_x (total cells = num_gc_1side**2).
- """
- def __init__(self, num_dc: int = 100, num_gc_x: int = 100, envsize=1, params: GCParams = GCParams()):
- super().__init__()
- self.num_dc = num_dc
- self.num_gc_1side = num_gc_x
- self.envsize = envsize
- self.params = params
- # ----- derived parameters -----
- self.num = num_gc_x * num_gc_x
- self.m = params.adaptation_strength * params.tau / params.tau_v
- self.Lambda = 2 * bm.pi / params.mapping_ratio #grid spacing
- # ----- coordinate transforms (hex vs rect) -----
- # Note that coor_transform is to map a parallelogram with a 60-degree angle back to a square
- # The logic is to partition the 2D space into parallelograms, each of which contains one lattice of grid cells, and repeat the parallelogram to tile the whole space
- self.coor_transform = bm.array([[1.0, -1.0 / bm.sqrt(3.0)],
- [0.0, 2.0 / bm.sqrt(3.0)]])
- # inverse, which is bm.array([[1.0, 1.0 / 2],[0.0, bm.sqrt(3.0) / 2]])
- # Note that coor_transform_inv is to map a square to a parallelogram with a 60-degree angle
- self.coor_transform_inv = np.linalg.inv(np.array(self.coor_transform))
- # ----- feature space -----
- x_bins = bm.linspace(-bm.pi, bm.pi, num_gc_x + 1)
- x_grid, y_grid = bm.meshgrid(x_bins[:-1], x_bins[:-1])
- self.x_grid = x_grid.reshape(-1)
- self.y_grid = y_grid.reshape(-1)
- # positions in (x,y) space and transformed space
- self.value_grid = bm.stack([self.x_grid, self.y_grid], axis=1) # (N, 2)
- self.value_bump = self.value_grid * 4
- # ----- candidate centers (for center snapping) -----
- self.candidate_centers = self.make_candidate_centers(self.Lambda)
- # ----- connectivity -----
- base_connection = self.make_connection()
- noise_connection = np.random.normal(0.0, params.conn_noise, size=(self.num, self.num))
- self.conn_mat = base_connection + noise_connection
- # ----- state variables -----
- self.r = bm.Variable(bm.zeros(self.num))
- self.u = bm.Variable(bm.zeros(self.num))
- self.v = bm.Variable(bm.zeros(self.num))
- self.gc_bump = bm.Variable(bm.zeros(self.num))
- self.conj_input = bm.Variable(bm.zeros(self.num))
- self.sensory_input = bm.Variable(bm.zeros(self.num))
- self.center_phase = bm.Variable(bm.zeros(2))
- self.center_position = bm.Variable(bm.zeros(2))
- self.conj_center = bm.Variable(bm.zeros(2))
- self.sensory_center = bm.Variable(bm.zeros(2))
- # ----- integrator -----
- self.integral = bp.odeint(method="exp_euler", f=self.derivative)
- # ========================= Dynamics =========================
- @property
- def derivative(self):
- # pass total input (Irec + external + noise) as 'inp'
- du = lambda u, t, inp: (-u + inp - self.v) / self.params.tau
- dv = lambda v, t: (-v + self.m * self.u) / self.params.tau_v
- return bp.JointEq([du, dv])
- # ========================= Utilities =========================
- def handle_periodic_condition(self, d):
- d = bm.where(d > bm.pi, d - 2.0 * bm.pi, d)
- d = bm.where(d < -bm.pi, d + 2.0 * bm.pi, d)
- return d
- def calculate_dist(self, d):
- """
- d: (..., 2) displacement in original (x,y).
- Return Euclidean distance after transform (hex/rect).
- """
- #consider the periodic boundary condition
- d = self.handle_periodic_condition(d)
- # transform to lattice axes
- dist = (bm.matmul(self.coor_transform_inv, d.T)).T #This means the bump on the parallelogram lattice is a Gaussian, while in the square space it is a twisted Gaussian
- return bm.sqrt(dist[:, 0] ** 2 + dist[:, 1] ** 2)
- def make_candidate_centers(self, Lambda):
- #This will generate a massivly large number of candidate centers, so that cover the simulated environmental size sufficiently
- N_c = bm.int(bm.ceil(self.envsize/Lambda)+1) * 2 #number of candidates along one dimension
- cc = bm.zeros((N_c, N_c, 2))
- for i in range(N_c):
- for j in range(N_c):
- cc = cc.at[i, j, 0].set((-N_c // 2 + i) * Lambda)
- cc = cc.at[i, j, 1].set((-N_c // 2 + j) * Lambda)
- cc_tranformed = bm.dot(self.coor_transform_inv, cc.reshape(N_c * N_c, 2).T).T
- return cc_tranformed
- # ========================= Connectivity =========================
- def make_connection(self):
- @jax.vmap
- def kernel(v):
- # v: (2,) location in (x,y)
- d = self.calculate_dist(v - self.value_grid) # (N,)
- return (
- (self.params.J0 / self.params.g)
- * bm.exp(-0.5 * bm.square(d / self.params.a))
- / (bm.sqrt(2.0 * bm.pi) * self.params.a)
- )
- return kernel(self.value_grid) # (N, N)
- # ========================= Inputs =========================
- def position2phase(self, position):
- """
- map position->phase; phase is wrapped to [-pi, pi] per-axis
- """
- mapped_pos = position * self.params.mapping_ratio
- phase = bm.matmul(self.coor_transform, mapped_pos) + bm.pi
- px = bm.mod(phase[0], 2.0 * bm.pi) - bm.pi
- py = bm.mod(phase[1], 2.0 * bm.pi) - bm.pi
- return bm.array([px, py])
- def calculate_input_from_conjgc(self, animal_pos, direction_activity, theta_modulation):
- """Get input from conjunctive grid cell layer → grid cell layer; returns (N,) vector."""
- assert bm.size(animal_pos) == 2
- num_dc = self.num_dc
- num_gc = self.num
- direction_bin = bm.linspace(-bm.pi, bm.pi, num_dc)
- # # # lag relative to head direction
- # lagvec = -bm.array([bm.cos(head_direction), bm.sin(head_direction)]) * self.params.phase_offset * 0.0
- # offset = bm.array([bm.cos(direction_bin), bm.sin(direction_bin)]) * self.params.phase_offset + lagvec.reshape(-1, 1)
- offset = bm.array([bm.cos(direction_bin), bm.sin(direction_bin)]) * self.params.phase_offset
- center_conj = self.position2phase(animal_pos.reshape(-1, 1) + offset.reshape(-1, num_dc))
- conj_input = bm.zeros((num_dc, num_gc))
- for i in range(num_dc):
- d = self.calculate_dist(bm.asarray(center_conj[:, i]) - self.value_grid)
- conj_input = conj_input.at[i].set(self.params.A * bm.exp(-0.5 * bm.square(d / self.params.a)))
- # weighting by direction bump activity: keep top one-third (by max) then normalize, I thinking using all direction_activity should also be fine
- weight = bm.where(direction_activity > bm.max(direction_activity) / 3, direction_activity, 0.0)
- weight = weight / (bm.sum(weight) + 1e-12) # avoid div-by-zero, dim: (num_dc,)
- return (bm.matmul(conj_input.T, weight).reshape(-1) * theta_modulation) #dim: (num_gc, num_dc) x (num_dc,) -> (num_gc,)
- def calculate_sensory_input(self, animal_pos, theta_modulation):
- """Get sensory input to grid cell layer based on animal position; returns (N,) vector."""
- center_phase = self.position2phase(animal_pos)
- d = self.calculate_dist(center_phase - self.value_grid)
- sensory_input = self.params.A * bm.exp(-0.5 * bm.square(d / self.params.a))
- return sensory_input* theta_modulation #dim: (num_gc,)
- # ========================= Bump center (phase/pos) =========================
- def get_unique_activity_bump(self, network_activity, animal_position):
- """
- Estimate a unique bump (activity peak) from the current network state,
- given the animal's actual position.
- Returns
- -------
- center_phase : (2,) array
- Phase coordinates of bump center on the manifold.
- center_position : (2,) array
- Real-space position of the bump (nearest candidate).
- bump : (N,) array
- Gaussian bump template centered at center_position.
- """
- # --- find bump center in phase space ---
- exppos_x = bm.exp(1j * self.x_grid)
- exppos_y = bm.exp(1j * self.y_grid)
- activity_masked = bm.where(network_activity > bm.max(network_activity) * 0.1, network_activity, 0.0)
- center_phase = bm.zeros((2,))
- center_phase = center_phase.at[0].set(bm.angle(bm.sum(exppos_x * activity_masked)))
- center_phase = center_phase.at[1].set(bm.angle(bm.sum(exppos_y * activity_masked)))
- # --- map back to real space, snap to nearest candidate ---
- center_phase_residual = bm.matmul(self.coor_transform_inv, center_phase) / self.params.mapping_ratio
- candidate_pos_all = self.candidate_centers + center_phase_residual
- distances = bm.linalg.norm(candidate_pos_all - animal_position, axis=1)
- center_position = candidate_pos_all[bm.argmin(distances)]
- # --- build Gaussian bump template ---
- d = bm.asarray(center_position) - self.value_bump
- dist = bm.sqrt(d[:, 0] ** 2 + d[:, 1] ** 2)
- gc_bump = self.params.A * bm.exp(-bm.square(dist / self.params.a))
- return center_phase, center_position, gc_bump
- # one-step update (main)
- def update(self, animal_posistion, direction_activity, theta_modulation_conj2gc, theta_modulation_sensory2gc):
- # get bump activity in real space info from network activity on the manifold ---
- center_phase, center_position, gc_bump = self.get_unique_activity_bump(self.r, animal_posistion)
- self.center_phase.value = center_phase
- self.center_position.value = center_position
- self.gc_bump.value = gc_bump
- # get external input to grid cell layer from conjunctive grid cell layer
- # note that this conjunctive input will be theta modulated. When speed is high, theta modulation is high, thus input is stronger
- # This is how we get longer theta sweeps when speed is high
- conj_input = self.calculate_input_from_conjgc(animal_posistion, direction_activity, theta_modulation_conj2gc)
- sensory_input = self.calculate_sensory_input(animal_posistion, theta_modulation_sensory2gc)
- self.conj_input.value = conj_input
- self.sensory_input.value = sensory_input
- #get conj_input center and sensory_input center for monitoring
- _, conj_center_pos, _ = self.get_unique_activity_bump(conj_input, animal_posistion)
- _, sensory_center_pos, _ = self.get_unique_activity_bump(sensory_input, animal_posistion)
- self.conj_center.value = conj_center_pos
- self.sensory_center.value = sensory_center_pos
- # recurrent + noise
- Irec = bm.matmul(self.conn_mat, self.r)
- input_noise = bm.random.randn(self.num) * self.params.noise_strength
- total_net_input = Irec + conj_input + sensory_input + input_noise
- # integrate
- u, v = self.integral(self.u, self.v, bp.share.load("t"), total_net_input)
- self.u.value = bm.where(u > 0.0, u, 0.0)
- self.v.value = v
- # get neuron firing by global inhibition
- u_sq = bm.square(self.u)
- self.r.value = self.params.g * u_sq / (1.0 + self.params.k * bm.sum(u_sq))
network_models.py at commit 8c1af28, no license · at the source
Overview
- UCL Institute of Cognitive Neuroscience, University College London, London, UK
- UCL Queen Square Institute of Neurology, University College London, London, UK
- UCL Sainsbury Wellcome Centre for Neural Circuits and Behaviour, University College London, London, UK
Abstract
During random foraging, the positional signal decoded from entorhinal grid cells exhibits left-right theta sweeps, alternating from one side of the head direction to the other across successive theta cycles. Here, we report that theta sweeps are topographically organized along the dorsoventral axis of the medial entorhinal cortex, with the angular deviation from head direction increasing gradually from dorsal (smaller scale) to ventral (larger scale) modules. This gradient coexists with a corresponding dorsoventral increase in angular deviation decoded from theta-modulated direction cells, which drive grid cell theta sweeps. These phenomena parallel a broadening of head direction tuning and increasing occurrence of theta cycle skipping in single-cell firing along the dorsoventral axis. Computational modeling demonstrates that these patterns are consistent with continuous attractor dynamics and a dorsoventral gradient in firing rate adaptation. These results highlight how theta sweeps can simultaneously represent multiple potential future locations and reveal a clear neural mechanism underlying this process.
Reproduced under the paper's license (CC BY-NC), from the paper cited above.
Repositories
Its files are read in the Code ↔ Paper reader above, with 9 matches between paragraphs and lines of code.
Zenodo 21427119
Availability: 1 check, the latest on 26 September 2026: the link answers (HTTP 200)
- 26 September 2026: the link answers (HTTP 200)
zilongji/topographythetasweeps
8c1af287e1110d396ca3a20afa107343d15697f6, 31 May 2026Availability: 1 check, the latest on 26 September 2026: the link answers
- 26 September 2026: the link answers
39 files
- ComputationalModellingFi
g4/ , Jupyter, 1,085 linesFig4_palmTreePattern.ipy nb - ComputationalModellingFi
g4/ , Jupyter, 1,360 lines, 2 matchesFig4_thetasweeps.ipynb - ComputationalModellingFi
g4/ , Jupyter, 815 lines, 2 matchesFig4_tmDC_skipping.ipynb - ComputationalModellingFi
g4/ , Python, 504 lines, 2 matchesnetwork_models.py - ComputationalModellingFi
g4/ , Python, 176 linesplotting.py - ComputationalModellingFi
g5/ , Jupyter, 607 linesFig5.ipynb - ComputationalModellingFi
g5/ , Jupyter, 310 linespanels.ipynb - EmpiricalDataAnalysis/
Fig1_Bar_GridsweepAngle. , Jupyter, 197 lines, 1 matchipynb - EmpiricalDataAnalysis/
Fig1_GridSweepMod_AlongT , Jupyter, 287 linesrajectory.ipynb - EmpiricalDataAnalysis/
Fig1_GridSweep_AlongTraj , Jupyter, 347 linesectory.ipynb - EmpiricalDataAnalysis/
Fig1_GridSweep_examples. , Jupyter, 364 linesipynb - EmpiricalDataAnalysis/
Fig2_Bar_IDsweepAngle.ip , Jupyter, 196 linesynb - EmpiricalDataAnalysis/
Fig2_Example_ID_GC_Align , Jupyter, 213 lines.ipynb - EmpiricalDataAnalysis/
Fig2_Example_ID_PolarHis , Jupyter, 581 linest.ipynb - EmpiricalDataAnalysis/
Fig2_Example_tmDC_nonthe , Jupyter, 274 linestaskipping.ipynb - EmpiricalDataAnalysis/
Fig2_GS_HDMVL_TMI_x25843 , Jupyter, 115 lines_1.ipynb - EmpiricalDataAnalysis/
Fig3_Bar_HDmvl.ipynb , Jupyter, 487 lines - EmpiricalDataAnalysis/
Fig3_Bar_IDmvl.ipynb , Jupyter, 362 lines, 1 match - EmpiricalDataAnalysis/
Fig3_Bar_TSindex.ipynb , Jupyter, 508 lines - EmpiricalDataAnalysis/
Fig3_Example_tmDC_Mod1& , Jupyter, 300 linesMod2& Mod3.ipynb - EmpiricalDataAnalysis/
Fig4NIPS2026_GridSweep_A , Jupyter, 240 lineslongTrajectory.ipynb - EmpiricalDataAnalysis/
Fig4_AdaptationvsDVaxis_ , Jupyter, 145 linesfromYoshidaData.ipynb - EmpiricalDataAnalysis/
Fig4_Example_PalmTreeSwe , Jupyter, 162 linesep.ipynb - EmpiricalDataAnalysis/
Fig4_Heatmap_PPM_Mod_ave , Jupyter, 175 lines_all.ipynb - EmpiricalDataAnalysis/
Fig4_SweepAngleDiffvsMod , Jupyter, 60 linesule.ipynb - EmpiricalDataAnalysis/
SI1_Bar_GridsweepAngle_D , Jupyter, 185 linesownsample.ipynb - EmpiricalDataAnalysis/
SI1_Bar_IDsweepAngle_Dow , Jupyter, 196 linesnsample.ipynb - EmpiricalDataAnalysis/
SI2_ThetaFreqPerModule.i , Jupyter, 352 linespynb - EmpiricalDataAnalysis/
SI3_(related)_Heatmap_PP , Jupyter, 230 linesM_ave_all.ipynb - EmpiricalDataAnalysis/
SI3_Example_PalmTreeSwee , Jupyter, 65 linesp.ipynb - EmpiricalDataAnalysis/
SI3_Heatmap_PPM_Mod_ave_ , Jupyter, 413 linesall.ipynb - EmpiricalDataAnalysis/
SI3_SweepAngle_across_ph , Jupyter, 279 linesase_bins.ipynb - EmpiricalDataAnalysis/
SI3_SweepAngle_across_ph , Jupyter, 722 lines, 1 matchase_bins_forward_and_bac kward.ipynb - EmpiricalDataAnalysis/
SI4_GS_HDMVL_TMI_4animal , Jupyter, 123 liness.ipynb - EmpiricalDataAnalysis/
SI5_Bar_IDsweepAngle_the , Jupyter, 190 linestaCenterByModule.ipynb - EmpiricalDataAnalysis/
SI5_ModularActivity_vs_T , Jupyter, 196 lineshetaphase.ipynb - EmpiricalDataAnalysis/
SIxxx_Startpoints.ipynb , Jupyter, 411 lines - EmpiricalDataAnalysis/
SIxxx_Video_SweepsAlongT , Jupyter, 660 linesraj.ipynb - README.md, Text, 18 lines
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:
- 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 38 scripts, each with its path and the digest of its content;
- 9 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
- doi:10.25493/
r5fr-edg , at the source; found in “Data, code, and materials availability:”
Data, code, and materials availability
All data and code needed to evaluate and reproduce the results in the paper are present in the paper and/
Reproduced under the paper's license (CC BY-NC), from the paper cited above.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 5 authors, 7 MeSH terms, 1 funder, 35 references.
Cite
This paper
Ji, Z., Zhang, H., Marshall, C., Stonis, R., & Burgess, N. (2026). Dorsoventral gradient of theta sweeps in the medial entorhinal cortex. Science advances, 12(36), eaeg6797. https://
BibTeX
@article{ji2026dorsovent
author = {Ji, Zilong and Zhang, Huiwen and Marshall, Callum and Stonis, Rokas and Burgess, Neil},
title = {{Dorsoventral gradient of theta sweeps in the medial entorhinal cortex}},
journal = {Science advances},
year = {2026},
month = sep,
volume = {12},
number = {36},
pages = {eaeg6797},
publisher = {American Association for the Advancement of Science},
issn = {2375-2548},
doi = {10.1126/
url = {https://
pmid = {42696579},
pmcid = {PMC13544234}
}
RIS
TY - JOUR
AU - Ji, Zilong
AU - Zhang, Huiwen
AU - Marshall, Callum
AU - Stonis, Rokas
AU - Burgess, Neil
TI - Dorsoventral gradient of theta sweeps in the medial entorhinal cortex
T2 - Science advances
J2 - Sci Adv
PY - 2026
DA - 2026/
VL - 12
IS - 36
SP - eaeg6797
SN - 2375-2548
PB - American Association for the Advancement of Science
DO - 10.1126/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1126/
"type": "article-journal",
"title": "Dorsoventral gradient of theta sweeps in the medial entorhinal cortex",
"container-title": "Science advances",
"author": [
{
"family": "Ji",
"given": "Zilong"
},
{
"family": "Zhang",
"given": "Huiwen"
},
{
"family": "Marshall",
"given": "Callum"
},
{
"family": "Stonis",
"given": "Rokas"
},
{
"family": "Burgess",
"given": "Neil"
}
],
"container-title-short":
"volume": "12",
"issue": "36",
"page": "eaeg6797",
"DOI": "10.1126/
"PMID": "42696579",
"PMCID": "PMC13544234",
"ISSN": "2375-2548",
"publisher": "American Association for the Advancement of Science",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
9,
4
]
]
}
}
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.1002/hipo.70131 [code]
- Decoding Medial Entorhinal Cortical Dynamics Produces Planning-Like Alternations in Hippocampal theta Sequences.Journal: HippocampusIn common: JAX, h5py, scikit-learn, 4 other tools, DOI 10.25493/r5fr-edg, systems, 13 references
- [2] doi:10.1038/s41593-026-02365-2 [code]
- Hippocampal theta sweeps indicate goal direction during navigation.Journal: Nature neuroscienceIn common: JAX, SciPy, Matplotlib, 1 other tool, 9 references, 2 authors
- [3] doi:10.1038/s41467-026-74357-6 [code]
- Hippocampo-neocortical interaction as compressive retrieval-augmented generation.Journal: Nature communicationsIn common: UMAP, NetworkX, statsmodels, 6 other tools, author Neil Burgess
- [4] doi: [code]
- Naturalistic behavior and self-generated neural activity predictive of self-correctionJournal: bioRxiv : the preprint server for biologyIn common: JAX, NetworkX, statsmodels, 6 other tools, 3 references
- [5] doi:10.1038/s41593-026-02232-0 [code]
- Entorhinal cortex represents task-relevant remote locations independently of CA1.Journal: Nature neuroscienceIn common: NetworkX, h5py, statsmodels, 6 other tools, systems, 3 references
- [6] doi:10.1038/s42003-026-10957-8 [code]
- Brain defence by the extracellular matrix protein Cochlin.Journal: Communications biologyIn common: JAX, UMAP, NetworkX, 7 other tools
- [7] doi:10.1016/j.isci.2026.116825 [code]
- Social hierarchy shapes behavioral and transcriptional responses to chronic stress and ketamine in male mice.Journal: iScienceIn common: UMAP, NetworkX, h5py, 7 other tools, systems
- [8] doi:10.3389/fncom.2026.1786996 [code]
- Schumann-anchored golden ratio organization of human neural oscillations.Journal: Frontiers in computational neuroscienceIn common: UMAP, NetworkX, h5py, 7 other tools, systems
- [9] doi:10.1038/s41467-026-75959-w [code]
- Charting higher-order models of brain function beyond pairwise interactions.Journal: Nature communicationsIn common: JAX, NetworkX, h5py, 7 other tools
- [10] doi:10.1093/nar/gkag706 [code]
- scDifformer: diffusion-based post-training for virtual cell modeling across large-scale single-cell data.Journal: Nucleic acids researchIn common: UMAP, NetworkX, h5py, 7 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.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 2 repositories of the authors' code, each at its verified commit and with its license, 38 scripts, and 9 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:52bc9730aeeefc7e…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
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.
