exg-source
EEG source localization in pure Rust — MNE / dSPM / sLORETA / eLORETA
exg-source implements the complete minimum-norm source localization pipeline
ported from MNE-Python, with no C, BLAS, or Python
dependencies. Linear algebra is handled by
faer (pure Rust).
Part of the exg workspace.
Can be used standalone or via exg's default source feature.
See also exg-luna for the LUNA
seizure-detection preprocessing pipeline.
Quick start
[]
= "0.0.3"
use *;
use Regularization;
use Array2;
// 1. Source space — 162 dipoles on a cortical sphere
let = ico_source_space;
// 2. Forward model — 3-shell spherical head (Berg & Scherg)
let electrodes: = /* your electrode positions [n_elec, 3] */
# zeros;
let fwd = make_sphere_forward;
// 3. Noise covariance from baseline data
let baseline: = /* [n_elec, n_samples] */
# zeros;
let noise_cov = compute_covariance;
// 4. Inverse operator
let inv = make_inverse_operator.unwrap;
// 5. Source estimate
let data: = /* [n_elec, n_times] */
# zeros;
let stc = apply_inverse.unwrap;
println!;
Pipeline overview
Electrode positions Source positions
│ │
▼ ▼
┌──────────────┐ ┌────────────────┐
│ SphereModel │ │ ico/grid │
│ (3-shell) │ │ source_space │
└──────┬───────┘ └───────┬────────┘
│ │
└─────────┬─────────────────┘
▼
┌────────────────┐
│ make_sphere_ │
│ forward() │──→ ForwardOperator (gain matrix)
└────────┬───────┘
│
Baseline ─→ compute_covariance() ──→ NoiseCov
│
▼
┌────────────────┐
│ make_inverse_ │
│ operator() │──→ InverseOperator (SVD + priors)
└────────┬───────┘
│
EEG data ──→ apply_inverse() ──→ SourceEstimate
│
├──→ estimate_snr()
└──→ make_resolution_matrix()
Modules
Source space — source_space
Generate source positions where dipoles are placed.
| Function | Description |
|---|---|
ico_source_space(n, r, center) |
Icosahedron subdivided n times, projected onto sphere of radius r |
grid_source_space(spacing, r, center) |
Regular 3-D grid inside a sphere |
ico_n_vertices(n) |
Expected vertex count: 10 × 4^n + 2 |
Subdivision levels: 0 → 12, 1 → 42, 2 → 162, 3 → 642, 4 → 2562, 5 → 10242 vertices.
Forward model — forward
Compute the EEG lead-field (gain) matrix using a multi-shell spherical head.
| Function | Description |
|---|---|
make_sphere_forward(elec, src, nn, sphere) |
Fixed-orientation forward [n_elec, n_src] |
make_sphere_forward_free(elec, src, sphere) |
Free-orientation forward [n_elec, n_src × 3] |
SphereModel::default() |
3-shell: brain (0.067 m, 0.33 S/m), skull (0.070 m, 0.0042 S/m), scalp (0.075 m, 0.33 S/m) |
SphereModel::single_shell(r, σ, c) |
Homogeneous sphere |
Based on the Berg & Scherg (1994) approximation with Prony's method for parameter fitting.
Noise covariance — covariance
| Function | Description |
|---|---|
compute_covariance(data, reg) |
From continuous [n_ch, n_t] data |
compute_covariance_epochs(epochs, reg) |
From epoched [n_ep, n_ch, n_t] data |
Regularisation options:
Empirical— raw sample covarianceShrunkIdentity(None)— Ledoit–Wolf automatic shrinkageShrunkIdentity(Some(α))— manual shrinkage, α ∈ [0, 1]Diagonal— channel variances only
Inverse operator — inverse
| Function | Description |
|---|---|
make_inverse_operator(fwd, cov, depth) |
Build from forward + noise covariance |
apply_inverse(data, inv, λ², method) |
Apply to [n_ch, n_t] data |
apply_inverse_full(data, inv, λ², method, pick_ori, opts) |
With orientation picking |
apply_inverse_epochs(epochs, inv, λ², method) |
Batch over [n_ep, n_ch, n_t] |
Methods: InverseMethod::MNE, DSPM, SLORETA, ELORETA
Orientation picking (PickOri):
None— combine XYZ as√(x² + y² + z²)(default)Normal— cortical normal component onlyVector— all three components[n_src × 3, n_t]
Resolution analysis — resolution
| Function | Returns |
|---|---|
make_resolution_matrix(inv, fwd, λ², method) |
R = K @ G [n_src, n_src] |
get_point_spread(R, idx) |
PSF for source idx |
get_cross_talk(R, idx) |
CTF for source idx |
peak_localisation_error(R) |
Index offset of PSF peak from true source |
spatial_spread(R) |
Number of sources within half-max of PSF |
relative_amplitude(R) |
Peak / total ratio (1.0 = perfect) |
SNR estimation — snr
| Function | Returns |
|---|---|
estimate_snr(data, inv) |
(snr, snr_est) — whitened GFP and regularisation-based SNR per time point |
eLORETA — eloreta
Iterative solver for exact zero-bias source localisation. Configure via EloretaOptions:
let opts = EloretaOptions ;
Linear algebra — linalg
Pure-Rust helpers using faer:
| Function | Description |
|---|---|
svd_thin(A) |
Thin SVD: (U, s, Vt) |
eigh_sorted(A) |
Symmetric eigendecomposition, descending order |
compute_whitener(C) |
Whitening matrix from noise covariance |
sqrtm_sym(A) |
Matrix square root of symmetric PSD matrix |
inv_sqrtm_sym(A) |
Inverse matrix square root |
Choosing λ² and method
| Parameter | Typical value | Meaning |
|---|---|---|
lambda2 = 1.0 / 9.0 |
SNR = 3 | Good default for evoked responses |
lambda2 = 1.0 / 1.0 |
SNR = 1 | More regularisation (noisy data) |
lambda2 = 1.0 / 100.0 |
SNR = 10 | Less regularisation (clean data) |
| Method | When to use |
|---|---|
| MNE | Raw current estimates, custom normalisation |
| dSPM | Statistical inference, group analysis |
| sLORETA | Best localisation accuracy for focal sources |
| eLORETA | Guaranteed zero localisation bias (slower) |
References
- Hämäläinen, M. S. & Ilmoniemi, R. J. (1994). Interpreting magnetic fields of the brain. Medical & Biological Engineering & Computing, 32(1), 35-42.
- Dale, A. M. et al. (2000). Dynamic statistical parametric mapping. Neuron, 26(1), 55-67.
- Pascual-Marqui, R. D. (2002). Standardized low-resolution brain electromagnetic tomography (sLORETA). Methods and Findings in Experimental and Clinical Pharmacology, 24D, 5-12.
- Pascual-Marqui, R. D. (2011). Discrete, 3D distributed, linear imaging methods of electric neuronal activity. arXiv:0710.3341.
- Berg, P. & Scherg, M. (1994). A fast method for forward computation of multiple-shell spherical head models. Electroencephalography and Clinical Neurophysiology, 90(1), 58-64.
- Ledoit, O. & Wolf, M. (2004). A well-conditioned estimator for large-dimensional covariance matrices. Journal of Multivariate Analysis, 88(2), 365-411.