Model a structural connectome

This tutorial will demonstrate how to generate a structural connectivity matrix \(G\) using the Normand et al. (2025) model,

\[ \displaystyle G_{ij}=\sum_{m=1}^k \frac{\psi_m(i)\psi_m^+(j)}{1+r_s^2\lambda_m}, \]

where:

  • \(G_{ij}\) denotes the connectivity strength between cortical vertices \(i\) and \(j\)

  • \(\psi_m(i)\) denotes the amplitude of the \(m^{th}\) geometric eigenmode at vertex \(i\)

  • \(\lambda_m\) denotes the \(m^{th}\) eigenvalue

  • \([\ \ ]^+\) denotes the Moore–Penrose pseudoinverse

  • \(r_s\) denotes the spatial length scale parameter

  • \(k\) denotes the number of modes used

As in the previous tutorials, we begin by initialising the EigenSolver with a cortical surface and medial wall mask, then solving for the first 120 eigenmodes. To reduce computation and plotting time, we can use the fsLR-4k mesh.

from importlib.resources import files, as_file
from neuromodes import EigenSolver
from neuromodes.io import fetch_surf
from nsbutils.plotting import plot_surf, plot_heatmap
from nsbutils.utils import unmask

mesh, medmask = fetch_surf(density='4k', hemi='R')
solver = EigenSolver(mesh, mask=medmask).solve(120)

We can then call the model_connectome class method:

G = solver.model_connectome()

Note that \(r_s=9.53\) and \(k=108\) are used by default, in line with the paper.

We can visualise the structural connectivity matrix using plot_heatmap, from our nsbutils package:

plot_heatmap(G, cmap='inferno', cbar=True)
<Axes: >
../_images/b605783892ef4c525ea17994574f90a064fd06b229833cfe0d0c7b335dc08050.png

We can also visualise the vertex-averaged connectivity on the cortical surface, this time using plot_surf from nsbutils:

nodal_sc = G.sum(axis=0)

# Path to surface
rh_surfpath = files('neuromodes.data') / 'sp-human_tpl-fsLR_den-4k_hemi-R_midthickness.surf.gii'

with as_file(rh_surfpath) as rh_surfpath: 
    plot_surf(
        rh_surfpath,
        unmask(nodal_sc, medmask),
        cmap='inferno',
        cbar=True
    )
../_images/87c50aef38a8e6f30136176aea86b780a9b0bc23be6567bb7f4b1b21a0b6a937.png

To explore the model further, we can generate another connectome but only use 5 modes instead of the default 108:

G_k5 = solver.model_connectome(k=5)

plot_heatmap(G_k5, cmap='inferno', cbar=True)

with as_file(rh_surfpath) as rh_surfpath: 
    plot_surf(
        rh_surfpath,
        unmask(G_k5.sum(axis=0), medmask),
        cmap='inferno',
        cbar=True
    )
../_images/28d020ec6432bf7bdb65b81b38bf27fd1227c8eafd6bae3778154a4f83c991ce.png ../_images/61b02b5d84c5d31b1ecdfd21f7d06d3ddf5a192ff242eabb38ea49f2f2bf20b4.png

What about if we instead increase the spatial length scale parameter from 9.53 to 95.3?

G_r95 = solver.model_connectome(r=95.3)

plot_heatmap(G_r95, cmap='inferno', cbar=True)

with as_file(rh_surfpath) as rh_surfpath: 
    plot_surf(
        rh_surfpath,
        unmask(G_r95.sum(axis=0), medmask),
        cmap='inferno',
        cbar=True
    )
../_images/71f0e12d9d173692aeaa4dc2005924c93cc102747d8681d053bbaf8d4bec0c84.png ../_images/2c831c18635b8fb5a86064ed0ccc824672529ae8096faf2dd00a98ee1103c025.png