Skip to main content

CMNE

Contextual Minimum-Norm Estimates for MEG and EEG

An LSTM learns how brain activity evolves over time and uses that context to sharpen standard source estimates such as dSPM.

CI PyPI Python License: MIT
Paper Preprint

Source time courses of dSPM, the LSTM prediction and CMNE on simulated data

Simulated burst travelling across five sources (red: true peak source). CMNE suppresses background activity that dSPM leaves in. Generated by examples/quickstart_simulation.py.


Contents

How it works

Methods such as MNE, dSPM and sLORETA estimate every time sample on its own. Brain activity, however, unfolds over time: what happens now depends on what happened just before. CMNE uses this context in three steps.

  1. Estimate. Compute a standard source estimate $\hat q_t$ (dSPM), rectify it and z-score each source.
  2. Predict. Give the last $k$ CMNE estimates $b_{t-k}, \dots, b_{t-1}$ to an LSTM network, which predicts the next estimate.
  3. Correct. Use the prediction, scaled to a maximum of one, as a per-source weight on the current estimate:

$$ b_t = w_t \odot \hat q_t, \qquad w_t = \frac{\left| \mathrm{LSTM}(b_{t-k}, \dots, b_{t-1}) \right|}{\max \left| \mathrm{LSTM}(b_{t-k}, \dots, b_{t-1}) \right|} $$

For the first $k$ samples there is no history yet, so $b_t = \hat q_t$. Each new $b_t$ is fed back into the LSTM, so the estimate is refined recursively.

In the paper, CMNE gave higher source-space SNR, lower spatial dispersion and smaller localisation errors than dSPM, both on simulated epileptiform spikes and on recorded auditory steady-state responses (ASSR).

Features

Hardware CPU, NVIDIA CUDA or Apple Silicon (MPS), chosen automatically; bfloat16 on CUDA
Memory Training windows are generated on the fly, so memory grows with the sensor data rather than the source space
Speed The inverse operator is folded into a single matrix; many trials run as one batch
Deployment Export to ONNX and run with ONNX Runtime, without PyTorch
MNE-Python Works directly with Epochs, SourceEstimate and inverse operators
Fidelity Implements Eqs. 9–13, the control estimate and the metrics of Eqs. 22–26, tested against literal reference implementations

Installation

pip install cmne            # PyTorch + MNE-Python
pip install "cmne[onnx]"    # adds ONNX export and ONNX Runtime
pip install "cmne[viz]"     # adds matplotlib for the examples

For GPU training, install the matching PyTorch build first; CMNE picks it up automatically.

Development install
git clone https://github.com/chdinh/cmne && cd cmne
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest

Quick start

Try it in seconds

cmne demo

This trains a small model on simulated EEG on the CPU and compares it with dSPM at the peak of the activity:

dSPM  peak error  46.2 mm   spatial dispersion  58.4 mm
CMNE  peak error   0.0 mm   spatial dispersion  40.0 mm

Python

import cmne
from mne.minimum_norm import apply_inverse

# epochs: mne.Epochs, inv: mne.minimum_norm.InverseOperator
kernel = cmne.inverse_kernel(epochs.info, inv, lambda2=1 / 9, method="dSPM")

# Train on single-trial dSPM estimates (paper settings: k = 80, d = 1280)
model = cmne.fit(
    epochs[train], kernel, look_back=80, num_units=1280, n_steps=250, validation=epochs[test]
)
model.save("cmne.pt")

# Apply to an averaged response
stc = apply_inverse(epochs[test].average(), inv, 1 / 9, "dSPM", pick_ori="normal")
result = cmne.apply_cmne(stc, model)
result.cmne.plot(subject="sample", subjects_dir=subjects_dir)

apply_cmne returns three estimates:

Field Content
result.sensing rectified, z-scored input estimate $\hat q_t$
result.prediction raw LSTM prediction
result.cmne contextual estimate $b_t$

They are SourceEstimate objects if the input was one, NumPy arrays otherwise. To process many signals at once, pass an array of shape (n_signals, n_sources, n_times).

ONNX Runtime

cmne.export_onnx(model, "cmne.onnx")
predictor = cmne.OnnxPredictor("cmne.onnx")  # PyTorch not required
result = cmne.apply_cmne(stc, predictor)

Command line

cmne train  --raw sub01_raw.fif --inv sub01-inv.fif --events sub01-eve.fif -o cmne.pt
cmne export cmne.pt -o cmne.onnx
cmne apply  --raw sub01_raw.fif --inv sub01-inv.fif --events sub01-eve.fif \
            --model cmne.onnx --n-average 20 -o results/

cmne <command> --help lists all options, including epoch window, rejection thresholds, MEG-only, look-back and number of units.

Reproducing the paper

The ASSR data set used in the paper (MEG + EEG, ~4.3 GB) is available on OSF. The script downloads it on first use:

python examples/assr_paper.py --data ~/cmne_data              # paper settings
python examples/assr_paper.py --data ~/cmne_data --units 256   # faster, for laptops
Setting Value (paper and default)
Inverse dSPM, SNR 3, loose orientation 0.2, normal component
Normalisation rectified, z-scored per source (Eq. 9)
Look-back $k$, units $d$ 80, 1280
Training 250 minibatches of 30 epochs × 20 windows
Split 85 % training, 15 % test (indices shipped with the data)
Evaluation average of 20 held-out epochs

Running on modest hardware

Most parameters sit in the LSTM input layer, which grows with the number of units $d$ times the number of sources. For 5,124 sources:

num_units Parameters Size (fp32) Suitable for
128 3.4 M 13 MB any laptop
256 6.8 M 27 MB laptop CPU, Apple Silicon
640 18.0 M 72 MB entry-level GPU, M-series Mac
1280 (paper) 39.4 M 157 MB GPU with ≥ 6 GB

If memory is still tight, lower epochs_per_batch and windows_per_epoch (default 30 × 20 = 600 windows per step).

Inference speed at paper scale on an Apple M-series CPU (python benchmarks/bench_inference.py):

Backend 1 signal 20 signals, batched
PyTorch 43 ms per step 7.6 ms per step and signal
ONNX Runtime 41 ms per step 18.9 ms per step and signal

API overview

Function / class Purpose
inverse_kernel Inverse operator as one (n_sources, n_channels) matrix
fit → CMNEModel Train the LSTM
CMNEModel.save / load / predict Store, load and run a model
apply_cmne → CMNEResult Contextual estimate (Eqs. 10–13)
control_estimate The paper's control estimate without LSTM
export_onnx, OnnxPredictor ONNX export and inference
peak_localization_error, spatial_dispersion, source_snr Metrics (Eqs. 22–26)
simulate_data, fetch_assr Simulated data and the ASSR data set

Changes from the original code

Version 0.2 is a rewrite of the 2017–2022 research code, which is kept in archive/. Besides moving from TensorFlow/Keras to PyTorch, it fixes:

  • Normalisation mismatch. The network was trained on signed z-scored dSPM but applied to rectified, max-normalised dSPM. Both now use Eq. 9.
  • Weighting. The estimate was multiplied by the raw, possibly negative prediction instead of the normalised weight of Eq. 11. The old behaviour remains available with normalize_weights=False.
  • Windowing. One valid training window per epoch was dropped.
  • Crashes. train() used undefined variables and returned nothing; Data indexed a method and compared arrays with == None; Settings failed if the results folder did not exist.
  • Hard-coded sizes. The evaluation assumed 5,124 sources and $k$ = 80; both now come from the model.
  • Robustness. Division by zero for constant sources; the data download relied on shell mv/rm.

Citation

If CMNE is useful for your work, please cite:

Dinh C, Samuelsson JG, Hunold A, Hämäläinen MS, Khan S. Contextual MEG and EEG Source Estimates Using Spatiotemporal LSTM Networks. Frontiers in Neuroscience 15:552666 (2021). doi:10.3389/fnins.2021.552666

@article{dinh2021cmne,
  title   = {Contextual {MEG} and {EEG} Source Estimates Using Spatiotemporal {LSTM} Networks},
  author  = {Dinh, Christoph and Samuelsson, John G. and Hunold, Alexander and
             H{\"a}m{\"a}l{\"a}inen, Matti S. and Khan, Sheraz},
  journal = {Frontiers in Neuroscience},
  volume  = {15},
  pages   = {552666},
  year    = {2021},
  doi     = {10.3389/fnins.2021.552666}
}

An earlier version is available as a preprint: arXiv:1909.02636.

License

MIT © 2017–2026 the CMNE authors.

Metadata

Release files for cmne 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cmne 0.2.1
File Size Uploaded
cmne-0.2.1.tar.gz 28.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cmne 0.2.1
File Interpreter ABI Platform
cmne-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 51.9 kB

Release files / cmne-0.2.1.tar.gz

Download URL cmne-0.2.1.tar.gz
Size 28.8 kB
Tags Source
SHA-256 checksum
How to use checksums
05be219477d276219d24e8e4c0033f0b54a16970672d41fb1cb7ed87a720080c
BLAKE2b-256 checksum
How to use checksums
f88cc90d97dc1c817a2525fc41b5ff93aff43f1670b05681a4fa89566c9c16b3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

Release files / cmne-0.2.1-py3-none-any.whl

Download URL cmne-0.2.1-py3-none-any.whl
Size 23.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
09fa65f65c09c7b51f90cc72002d88b0616c599017ffc07c4048e65196d83904
BLAKE2b-256 checksum
How to use checksums
ad8b77eef6fad6d3e24d06e085d20174ada37e4e69b63359684552bd52ac28af
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 4, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

0.1.0

2 release files

0.0.16

2 release files

0.0.15

2 release files

0.0.14

2 release files

0.0.13

2 release files

0.0.12

2 release files

0.0.11

2 release files

0.0.10

2 release files

0.0.9

2 release files

0.0.8

2 release files

0.0.7

2 release files

0.0.6

2 release files

0.0.5

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page