Skip to content

ENH: Cache bad channel interpolation matrix for EEG (Part 1) (#4651) - #14455

Open
ishaaaan17 wants to merge 1 commit into
mne-tools:mainfrom
ishaaaan17:perf/cache-interpolation-matrix
Open

ishaaaan17 wants to merge 1 commit into
mne-tools:mainfrom
ishaaaan17:perf/cache-interpolation-matrix

Conversation

@ishaaaan17

Copy link
Copy Markdown
Contributor

what does this implement/fix?

addresses #4651 (part 1 of 3: EEG spherical spline caching).

when you preprocess multi-condition Evoked ERP datasets, batches of Epochs, or inside autoreject, calling .interpolate_bads() on instances with the same sensor configuration recomputes the Legendre polynomial expansion and the spherical spline matrix inversion again on every call.

as @christianbrodbeck proposed in #4651 and @larsoner approved, this PR does part 1:

  1. adds mne.channels.compute_bad_channel_interpolation_matrix(inst_or_info), which returns an InterpolationMatrix (a subclass of dict) with the channel metadata and the precalculated spherical spline mappings.
  2. adds a keyword-only parameter interpolation=None to .interpolate_bads(..., interpolation=None) on Raw, Epochs and Evoked.
  3. checks compatibility (same order of ch_names and matching bads set) before it is applied.
  4. keeps the results bit-exact (atol=1e-15) with the uncached path.

(part 2 will follow with caching for MEG field interpolation, and part 3 with the multi-modal extensions.)

example usage

import mne
from mne.channels import compute_bad_channel_interpolation_matrix

# Compute interpolation matrix once
interp = compute_bad_channel_interpolation_matrix(raw)

# Apply across multiple evoked conditions or epochs in microseconds
for evoked in evokeds:
    evoked.interpolate_bads(interpolation=interp)

benchmark results

interpolation_benchmark
channel configuration uncached (recompute) cached matrix speedup bit-exact parity
EEG (32 ch) 7.36 ms 0.36 ms 20.3x faster identical (atol=1e-15)
EEG (64 ch) 8.06 ms 0.45 ms 17.9x faster identical (atol=1e-15)
EEG (128 ch) 20.26 ms 0.79 ms 25.7x faster identical (atol=1e-15)
EEG (256 ch) 47.67 ms 1.35 ms 35.2x faster identical (atol=1e-15)

verification

  • all 47 tests in mne/channels/tests/test_interpolation.py pass.
  • ruff check, ruff format --check and codespell pass with no warnings.

additional information

i used Google Antigravity to help structure the first version of the helper refactor and the benchmark scripts. i investigated the bottleneck discussed in #4651 myself, designed the compatibility checks of the InterpolationMatrix dictionary subclass, and checked that the results are bit-exact down to machine precision (atol=1e-15) against the normal raw.interpolate_bads(). i also made sure all 47 tests in mne/channels/tests/test_interpolation.py pass with no regression, and that the linter and formatting checks (ruff check, ruff format, codespell) pass.

@ishaaaan17
ishaaaan17 force-pushed the perf/cache-interpolation-matrix branch from 0d6b2c7 to 1242f1b Compare October 24, 2026 00:46
@ishaaaan17
ishaaaan17 force-pushed the perf/cache-interpolation-matrix branch 2 times, most recently from c2a62f4 to ab3766c Compare October 24, 2026 01:29
@ishaaaan17
ishaaaan17 force-pushed the perf/cache-interpolation-matrix branch from ab3766c to 848d1c0 Compare October 24, 2026 01:42

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant