-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MRG: Fix the computation of the orientation in (..)MxNE and gamma-map #4633
Conversation
mne/inverse_sparse/_gamma_map.py
Outdated
for ii in range(len(active_src)): | ||
for jj in range(3): | ||
if in_pos >= len(active_set): | ||
break | ||
if (active_set[in_pos] + jj) % 3 == 0: | ||
X_xyz[3 * ii + jj] = X[in_pos] | ||
X_ori_[3 * ii + jj] = X_ori[in_pos] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
XXX: There is still a problem here, I have to check what is happening when we give xyz_same_gamma=False
Codecov Report
@@ Coverage Diff @@
## master #4633 +/- ##
==========================================
- Coverage 87.69% 87.68% -0.01%
==========================================
Files 351 351
Lines 66554 66635 +81
Branches 10343 11554 +1211
==========================================
+ Hits 58362 58429 +67
- Misses 5233 5256 +23
+ Partials 2959 2950 -9 |
mne/inverse_sparse/_gamma_map.py
Outdated
@@ -277,8 +277,10 @@ def gamma_map(evoked, forward, noise_cov, alpha, loose="auto", depth=0.8, | |||
|
|||
# Reapply weights to have correct unit | |||
n_dip_per_pos = 1 if is_fixed_orient(forward) else 3 | |||
X_ori = X.copy() | |||
X = _reapply_source_weighting(X, source_weighting, | |||
active_set, n_dip_per_pos) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
n_dip_per_pos is not used in _reapply_source_weighting :(
mne/inverse_sparse/_gamma_map.py
Outdated
X = _reapply_source_weighting(X, source_weighting, | ||
active_set, n_dip_per_pos) | ||
X_ori /= source_weighting[active_set][:, None] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this looks bogus. _reapply_source_weighting does `X *= source_weighting[active_set][:, None]``
so I doubt it's doing the right thing but it's just a guess
mne/inverse_sparse/_gamma_map.py
Outdated
|
||
tmin = evoked.times[0] | ||
tstep = 1.0 / evoked.info['sfreq'] | ||
|
||
if return_as_dipoles: | ||
out = _make_dipoles_sparse(X, active_set, forward, tmin, tstep, M, | ||
M_estimated, active_is_idx=True) | ||
M_estimated, active_is_idx=True, | ||
X_ori=X_ori) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's really a mystery to me why you need to pass both X and X_ori and why internally you use X to get amplitude and X_ori to get orientation.
@@ -124,5 +124,20 @@ def test_gamma_map_vol_sphere(): | |||
|
|||
assert_array_almost_equal(stc.times, evoked.times, 5) | |||
|
|||
# Compare orientation obtained using fit_dipole and mixed_norm |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gamma_map
pos2 = src[0]['rr'][stc.vertices[1]] | ||
evoked_dip = mne.simulation.simulate_evoked(fwd, stc, info, cov, nave=1e9) | ||
|
||
dip_mxne = gamma_map(evoked_dip, fwd, cov, alpha, return_as_dipoles=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dip_gmap
mne/inverse_sparse/mxne_inverse.py
Outdated
@@ -24,8 +24,7 @@ | |||
@verbose | |||
def _prepare_weights(forward, gain, source_weighting, weights, weights_min): | |||
mask = None | |||
if isinstance(weights, SourceEstimate): | |||
# weights = np.sqrt(np.sum(weights.data ** 2, axis=1)) | |||
if isinstance(weights, (SourceEstimate, VolSourceEstimate)): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if isinstance(weights, _BaseSourceEstimate):
|
||
amp_max = [np.max(d.amplitude) for d in dip_mxne] | ||
dip_mxne = dip_mxne[np.argmax(amp_max)] | ||
assert_true(dip_mxne[0].pos[0] in pos) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this does not pass on master but the assert below on ori does.
so this test is not enough to demonstrate the bug fix on orientation
estimation.
|
||
|
||
def plot_pos_ori(pos, ori, color=(0., 0., 0.)): | ||
mlab.points3d(pos[0], pos[1], pos[2], scale_factor=0.005, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this works if you pass pos of shape (3, n_times)
as is the example is broken
@@ -137,3 +138,19 @@ | |||
|
|||
fig.tight_layout() | |||
plt.show() | |||
|
|||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not plot_dipole_locations
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh okay.
In any case, please make this a separate section with ###########
so it renders nicer in SG
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok
I don't think it works without an MRI...
|
Are we good here after Eric's comment? |
let me just check tomorrow that the added tests don't work in master so
it's actually obvious as a bug fix
cc @joewalter
|
ok I did I pass @yousrabk have a look FYI @joewalter we realized that the depth weighting strategy for sparse solver was not optimal for orientation estimation and we now use the Frobenius normalization you had written in the past. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise LGTM
@@ -53,7 +53,7 @@ | |||
|
|||
############################################################################### | |||
# Run solver | |||
alpha = 50 # regularization parameter between 0 and 100 (100 is high) | |||
alpha = 55 # regularization parameter between 0 and 100 (100 is high) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you made this change because the example now otherwise looks bad, we'll have to make it clear in the whats_new
(please update) that the computations have changed and that changing alpha
might be necessary to partially compensate.
|
||
dip_fit = mne.fit_dipole(evoked_dip, cov, sphere)[0] | ||
assert_true(np.max(np.abs(np.dot(dip_fit.ori[0], | ||
dip_gmap.ori.T))) > 0.99) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is there a max
here? Aren't these single dipoles? If so you can just do np.abs(np.dot(dip_fit.ori[0], dip_gmap.ori[0]))
.
Something else just occurred to me -- if we guarantee our dipoles have positive amplitude values during fitting, then the abs
isn't even needed, because they should both point in the "outward" direction. But we can fix this bit later if you don't want to investigate (but please add an XXX
comment so we don't forget)
assert_true(dip_gmap[0].pos[0] in src[0]['rr'][stc.vertices]) | ||
|
||
dip_fit = mne.fit_dipole(evoked_dip, cov, sphere)[0] | ||
assert_true(np.abs(np.dot(dip_fit.ori[0], dip_gmap.ori[0])) > 0.99) # XXX |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@yousrabk why the XXX?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was in response to my request to add a # XXX
comment to signify that we should look into whether the abs
is even necessary at some point (if currents are always positive it shouldn't be), but without at least a few words saying this, it is tough to know that
yes it is necessary since there is no reason why the sign of the amplitude
of the dipoles should be fixed.
please remove
|
My point is that we could (for |
(and for |
ok but gamma map can be on a volume / discrete source space so... anyhow if
you keep the XXX please add a comment. I would just remove the XXX
|
Ahh that's true at least some of these are volume. Sure let's just remove (maybe do it in |
ok XXX removed and what's new updated. good to go on my end if CIs are happy. |
Thanks @yousrabk @agramfort |
No description provided.