| name | geometry-aware-brain-dynamics-mapping |
| description | Geometry-Aware Framework for noninvasive whole human brain dynamics mapping. Incorporates individual cortical geometry into electrophysiology for accurate spatiotemporal reconstruction of brain activity. Activation: geometry-aware, brain dynamics mapping, cortical geometry, noninvasive electrophysiology. |
Geometry-Aware Framework for Noninvasive Whole Human Brain Dynamics Mapping
A geometry-aware framework that incorporates individual cortical geometry into noninvasive electrophysiology to accurately reconstruct whole-brain spatiotemporal dynamics from EEG/MEG.
Metadata
- Source: arXiv:2604.25592v1
- Authors: Youssuf Saleh, Camille Gontier, Jonathan Arreguit, Denis Rivière, Pamela Villagrán, Bertrand Thirion, Alain Destexhe
- Published: 2026-04-28
- Categories: q-bio.NC, cs.LG
Core Methodology
Problem Statement
Non-invasive electrophysiology (EEG/MEG) lacks methods that accurately reconstruct whole-brain spatiotemporal dynamics while incorporating individual cortical geometry. Current approaches suffer from:
- Spatial Smearing: Standard source localization blurs activity across regions
- Geometry Neglect: Flattening or ignoring individual cortical folding patterns
- Temporal Limitations: Poor temporal resolution in fMRI-based methods
- Inverse Problem Ill-Posedness: Non-unique solutions in EEG/MEG source localization
Key Innovation
Geometry-Aware Framework integrates:
- Individual Cortical Geometry: Uses subject-specific cortical surface meshes
- Geometric Deep Learning: GNN/CNN architectures operating on cortical surfaces
- Physics-Informed Regularization: Enforces biophysical constraints from electromagnetic theory
- Spatiotemporal Modeling: Joint spatial and temporal dynamics reconstruction
Technical Framework
1. Cortical Geometry Representation
Cortical Surface Representation:
├── High-resolution triangular mesh (vertex ~1-2mm)
├── Sulcal-gyral pattern encoding
├── Local curvature features
├── Distance along cortical surface (geodesic)
└── Normal vectors for orientation
Individual Geometry Processing:
1. T1-weighted MRI → Freesurfer reconstruction
2. White matter surface extraction
3. Mesh decimation (100k-200k vertices)
4. Geodesic distance matrix computation
5. Curvature and thickness feature extraction
2. Forward Physics Model
The EEG/MEG forward problem maps neural currents to sensor measurements:
Y = L · J + ε
Where:
- Y: Sensor measurements [n_sensors × n_timepoints]
- L: Leadfield matrix [n_sensors × n_sources]
- J: Source currents [n_sources × n_timepoints]
- ε: Noise
Leadfield computation (boundary element method):
L_ij = ∫ G(r_sensor_i, r_source_j) · n_j dA
Where G is the Green's function for the head model
3. Geometry-Aware Inverse Solver
J* = argmin_J ||Y - L·J||² + λ₁·R_geometry(J) + λ₂·R_temporal(J) + λ₃·R_spatial(J)
Geometry Regularization:
R_geometry(J) = Σᵢ Σⱼ wᵢⱼ · ||Jᵢ - Jⱼ||² · d_geodesic(i,j)⁻¹
Where wᵢⱼ encodes cortical connectivity strength
and d_geodesic is geodesic distance along cortex
4. Graph Neural Network Architecture
Input: Source activity J on cortical mesh vertices
GNN Layers:
1. Spatial Graph Convolution:
J' = σ( D⁻¹ᐟ² A D⁻¹ᐟ² J W )
Where A is adjacency matrix based on geodesic distance
D is degree matrix
W is learnable weights
σ is activation (e.g., ReLU)
2. Temporal Convolution:
J'' = TemporalConv(J', kernel_size=K)
3. Geometry-Aware Pooling:
- Pool across hierarchical cortical parcellations
- Preserve sulcal-gyral boundaries
Output: Reconstructed spatiotemporal dynamics
Implementation Guide
Prerequisites
- Python >= 3.8
- PyTorch >= 1.10
- PyTorch Geometric (for GNNs)
- Freesurfer (for cortical reconstruction)
- MNE-Python (for EEG/MEG processing)
- NumPy, SciPy
Step-by-Step Implementation
1. Cortical Geometry Processing
import nibabel as nib
import numpy as np
from scipy.sparse import csr_matrix
from scipy.spatial.distance import cdist
class CorticalGeometry:
"""
Process individual cortical geometry for brain mapping
"""
def __init__(self, surface_file, curvature_file=None):
"""
Initialize with Freesurfer surface
Args:
surface_file: Path to .pial or .white surface file
curvature_file: Optional curvature map
"""
self.surf = nib.freesurfer.read_geometry(surface_file)
self.vertices = self.surf[0]
self.faces = self.surf[1]
self.n_vertices = len(self.vertices)
self.geo_dist = self._compute_geodesic_distance()
if curvature_file:
self.curvature = nib.freesurfer.read_morph_data(curvature_file)
else:
self.curvature = self._estimate_curvature()
self.normals = ._compute_normals()
():
scipy.sparse.csgraph dijkstra
adj = np.zeros((.n_vertices, .n_vertices))
face .faces:
i ():
j (i+, ):
v1, v2 = face[i], face[j]
dist = np.linalg.norm(.vertices[v1] - .vertices[v2])
adj[v1, v2] = dist
adj[v2, v1] = dist
geo_dist = dijkstra(adj, directed=, limit=max_dist)
geo_dist
():
curvature = np.zeros(.n_vertices)
i (.n_vertices):
neighbors = np.unique(.faces[
np.(.faces == i, axis=)
].flatten())
neighbors = neighbors[neighbors != i]
(neighbors) > :
local_pts = .vertices[neighbors]
centroid = np.mean(local_pts, axis=)
curvature[i] = np.linalg.norm(.vertices[i] - centroid)
curvature
():
normals = np.zeros((.n_vertices, ))
face .faces:
v1, v2, v3 = .vertices[face]
fnormal = np.cross(v2 - v1, v3 - v1)
fnormal = fnormal / (np.linalg.norm(fnormal) + )
v face:
normals[v] += fnormal
normals = normals / (np.linalg.norm(normals, axis=, keepdims=) + )
normals
():
{
: .vertices,
: .curvature,
: .normals,
: .geo_dist,
: ._estimate_sulcal_depth()
}
():
scipy.spatial ConvexHull
hull = ConvexHull(.vertices)
hull_pts = .vertices[hull.vertices]
depths = np.(cdist(.vertices, hull_pts), axis=)
depths
2. Leadfield Computation
import mne
import numpy as np
class ForwardModel:
"""
Compute EEG/MEG forward model with geometry
"""
def __init__(self, info, trans, bem, src):
"""
Args:
info: MNE info structure with sensor positions
trans: MRI-to-head transformation
bem: Boundary element model
src: Source space (cortical surface)
"""
self.info = info
self.trans = trans
self.bem = bem
self.src = src
self.leadfield = self._compute_leadfield()
def _compute_leadfield(self):
"""
Compute leadfield matrix using BEM
"""
from mne.forward import make_forward_solution
fwd = make_forward_solution(
self.info, self.trans, self.src, self.bem,
meg=True, eeg=True, mindist=5.0
)
leadfield = fwd['sol']['data']
n_sources = len(self.src[0]['vertno'])
leadfield_normal = np.zeros((leadfield.shape[], n_sources))
i (n_sources):
nn = .src[][][i]
idx = (i*, (i+)*)
leadfield_normal[:, i] = leadfield[:, idx] @ nn
leadfield_normal
():
.leadfield @ source_activity
():
noise_var = np.diag(noise_cov)
weights = / (noise_var + )
weights
3. Geometry-Aware GNN
import torch
import torch.nn as nn
from torch_geometric.nn import GCNConv, GATConv, global_mean_pool
from torch_geometric.data import Data
class GeometryAwareGNN(nn.Module):
"""
Graph Neural Network operating on cortical surface
"""
def __init__(self, in_channels=1, hidden_channels=64, num_layers=4):
super().__init__()
self.convs = nn.ModuleList()
self.batch_norms = nn.ModuleList()
self.convs.append(GATConv(in_channels, hidden_channels, heads=4, concat=False))
self.batch_norms.append(nn.BatchNorm1d(hidden_channels))
for _ in range(num_layers - 1):
self.convs.append(GATConv(hidden_channels, hidden_channels, heads=4, concat=False))
self.batch_norms.append(nn.BatchNorm1d(hidden_channels))
self.temporal_conv = nn.Conv1d(hidden_channels, hidden_channels, kernel_size=3, padding=1)
def forward(self, x, edge_index, edge_attr=None, batch=None):
"""
Args:
x: Node features [n_nodes, in_channels, n_time]
edge_index: Graph connectivity [2, n_edges]
edge_attr: Edge weights (geodesic distance) [n_edges]
"""
n_time = x.shape[]
outputs = []
t (n_time):
x_t = x[:, :, t]
conv, bn (.convs, .batch_norms):
x_t = conv(x_t, edge_index, edge_attr)
x_t = bn(x_t)
x_t = torch.relu(x_t)
outputs.append(x_t)
x = torch.stack(outputs, dim=)
x = .temporal_conv(x)
x
(nn.Module):
():
().__init__()
.n_sensors = n_sensors
.n_sources = n_sources
.sensor_encoder = nn.Sequential(
nn.Linear(n_sensors, ),
nn.ReLU(),
nn.Linear(, )
)
.gnn = GeometryAwareGNN(in_channels=, hidden_channels=)
.source_decoder = nn.Sequential(
nn.Linear(, ),
nn.ReLU(),
nn.Linear(, )
)
.leadfield_correction = nn.Linear(n_sources, n_sources, bias=)
.edge_index = geometry_info[]
.edge_attr = geometry_info.get()
():
batch_size, n_time = sensor_data.shape[], sensor_data.shape[]
sensor_encoded = []
t (n_time):
enc = .sensor_encoder(sensor_data[:, :, t])
sensor_encoded.append(enc)
sensor_encoded = torch.stack(sensor_encoded, dim=)
all_sources = []
b (batch_size):
x = sensor_encoded[b].unsqueeze().repeat(.n_sources, , )
gnn_out = .gnn(x, .edge_index, .edge_attr)
source_t = []
t (n_time):
dec = .source_decoder(gnn_out[:, :, t])
source_t.append(dec)
source_activity = torch.stack(source_t, dim=)
all_sources.append(source_activity)
torch.stack(all_sources, dim=)
4. Training with Physics Constraints
class GeometryAwareLoss(nn.Module):
"""
Multi-component loss with geometry constraints
"""
def __init__(self, leadfield, geo_dist, lambda_data=1.0,
lambda_smooth=0.1, lambda_geodesic=0.05):
super().__init__()
self.leadfield = leadfield
self.geo_dist = geo_dist
self.lambda_data = lambda_data
self.lambda_smooth = lambda_smooth
self.lambda_geodesic = lambda_geodesic
def forward(self, pred_sources, true_sources, sensor_data):
"""
Compute geometry-aware loss
Args:
pred_sources: [batch, n_sources, n_time]
true_sources: [batch, n_sources, n_time]
sensor_data: [batch, n_sensors, n_time]
"""
pred_sensors = torch.matmul(self.leadfield, pred_sources)
loss_data = torch.mean((pred_sensors - sensor_data)**2)
loss_source = torch.mean((pred_sources - true_sources)**2)
loss_smooth = self._geometry_smoothness(pred_sources)
loss_geodesic = self._geodesic_regularization(pred_sources)
total_loss = (self.lambda_data * loss_data +
loss_source +
self.lambda_smooth * loss_smooth +
self.lambda_geodesic * loss_geodesic)
{
: total_loss,
: loss_data,
: loss_source,
: loss_smooth,
: loss_geodesic
}
():
batch_size, n_sources, n_time = sources.shape
diff = sources.unsqueeze() - sources.unsqueeze()
weight = torch.exp(-.geo_dist / torch.median(.geo_dist))
weight = weight.unsqueeze().unsqueeze(-)
loss = torch.mean(weight * diff**)
loss
():
torch.tensor()
Applications
- Precision Neuromedicine: Subject-specific brain activity mapping
- Real-time Neuroimaging: Online dynamics reconstruction
- Brain-Computer Interfaces: Accurate cortical state estimation
- Cognitive Neuroscience: High-resolution spatiotemporal analysis
- Clinical Diagnostics: Patient-specific brain dynamics for epilepsy, stroke
Key Features
- Individual Geometry: Subject-specific cortical surface incorporation
- End-to-end Learning: Direct sensor-to-source mapping
- Physics Constraints: Biophysically plausible reconstructions
- Geodesic Regularization: Anatomically-informed smoothness
- Multi-scale: Operates at multiple spatial resolutions
Pitfalls
- Mesh Quality: Requires high-quality individual cortical surfaces
- Computational Cost: Geodesic distance computation is expensive
- Registration: MRI-to-head coordinate alignment critical
- Head Model: BEM accuracy affects forward solution
- Training Data: Needs paired source-sensor data (simulated or intra-cranial)
Related Skills
- geometric-brain-dynamics-mapping
- eeg-visual-attention-decoding
- brain-dit-fmri-foundation-model
References
Saleh, Y., et al. (2026). A geometry aware framework enhances noninvasive
mapping of whole human brain dynamics.
arXiv preprint arXiv:2604.25592v1.