Skip to content

Conversation

@wmvanvliet
Copy link
Contributor

@wmvanvliet wmvanvliet commented Jan 21, 2025

This adds a GUI to perform guided dipole modeling in the spirit of MEGIN's XFit program. This PR contains the base functionality needed to make the GUI useful. Useful enough to include in the next release of MNE-Python. The plan is to keep adding features in future PRs as well as some sorely needed speed improvements.

See here for a list of currently supported features: #11977
This PR depends on: #12071

Screenshot 2025-01-21 183100

Minimal example:

import mne

data_path = mne.datasets.sample.data_path()
evoked = mne.read_evokeds(
    f"{data_path}/MEG/sample/sample_audvis-ave.fif", condition="Left Auditory"
)
evoked.apply_baseline((-0.2, 0))
trans = mne.read_trans(f"{data_path}/MEG/sample/sample_audvis_raw-trans.fif")
cov = mne.read_cov(f"{data_path}/MEG/sample/sample_audvis-cov.fif")
bem = mne.read_bem_solution(f"{data_path}/subjects/sample/bem/sample-5120-5120-5120-bem-sol.fif")
subject = "sample"
subjects_dir = data_path / "subjects"

evoked.pick("grad")

# Quick and dirty. No MRI whatsoever.
g = mne.gui.DipoleFitUI(evoked)

# Slow and proper. With proper BEM model.
# g = mne.gui.DipoleFitUI(evoked, trans=trans, cov=cov, bem=bem, subject=subject, subjects_dir=subjects_dir)

Todo:

  • Basic unit tests
  • In-depth unit tests
  • Proper documentation page
  • Towncrier

@wmvanvliet
Copy link
Contributor Author

The situation has improved somewhat. I've split-off some of the changes into a separate PR and the unit tests are now passing. We still need more unit tests, but hopefully this PR is becoming a little more manageable.

@larsoner
Copy link
Member

larsoner commented Jan 8, 2026

Want me to review? (And push a commit if the circle failure is somehow related?)

@wmvanvliet
Copy link
Contributor Author

That would be great!

@larsoner
Copy link
Member

Didn't get to it this week, might have time Monday. If not, then it'll probably be another week because next week is full of meetings for me!

@wmvanvliet
Copy link
Contributor Author

There's no hurry :)

Copy link
Member

@larsoner larsoner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks pretty reasonable so far. Can you update + flesh out the top comment? For example I think towncrier is done and you have some unit tests, so not clear to me what is still missing.

I still need to actually do some manual testing to see how it works as well, which could be a next step (you can put it as a TODO in the top comment if you want 😄 )

)
parser.add_option(
"-b",
"--bem",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Someday we should maybe allow --sphere... I think MNE-C had some way to do this. Can you add a TODO comment in case someone wants to try?

@larsoner
Copy link
Member

@wmvanvliet this is in draft mode, is this good to go from your end? If so feel free to mark as ready, ping me, and I'll try actually using it and see if there are any show-stopping issues.

CircleCI failure is real:

        ../tutorials/inverse/21_interactive_dipole_fit.py failed leaving traceback:
    
        Traceback (most recent call last):
          File "/home/circleci/project/tutorials/inverse/21_interactive_dipole_fit.py", line 41, in <module>
            mne.gui.dipolefit()
        TypeError: dipolefit() missing 1 required positional argument: 'evoked'

so is its style complaint

/home/circleci/project/doc/generated/commands.rst:457: WARNING: Option list ends without a blank line; unexpected unindent. [docutils]
/home/circleci/project/doc/generated/commands.rst:461: WARNING: Definition list ends without a blank line; unexpected unindent. [docutils]

in case you want to test locally, I can replicate the example error with

larsoner:~/python/mne-python/doc$ PATTERN=interactive_dipole make html-pattern

I'll look to see if I can see what docutils is angry about

Copy link
Member

@larsoner larsoner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uggghh when I run locally I get a reference cycle. Working on fixing it and will push if I figure it out

@larsoner
Copy link
Member

Fixed ref cycle (need weakrefs to all callbacks) and improved scraping to use GUI-style scraping

),
)
self._actors["sensors"] = list(sensors.values())
self._actors["sensors"] = sum(sensors.values(), [])
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this magic?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sum just adds things... and you can add lists to concatenate them. The default starting value is 0 though and that won't work for a list so you have to provide [] as the starting value. So

>>> sum([["a", "b"], ["c"]], [])
['a', 'b', 'c']

It's just a one-liner equiv for what you had before

self._actors["sensors"] = list()
for val in sensors.values():
    self._actors.extend(val)

@wmvanvliet
Copy link
Contributor Author

Only thing blocking this IMO is faster unit tests with better coverage.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants