Repository: mne-tools/mne-python

Issue:
test_xdawn_regularization fails on Fedora 34 (rawhide)
Hey all!

the test: `test_dawn::test_xdawn_regularization` is currently failing in Fedora 34, with no reason per se. The test works fine on Fedora 33.

#### Describe the bug

The build logs are below:

1. [Fedora 33](https://major.fedorapeople.org/python-mne/f33_build.log)
2. [Fedora 34 (failing)](https://major.fedorapeople.org/python-mne/f34_build.log)
 
Failure:

```
mne/preprocessing/tests/test_xdawn.py:203: in test_xdawn_regularization
    xd.fit(epochs)
E   Failed: DID NOT RAISE <class 'ValueError'>
```
There's a bunch of log statements as well:

```
----------------------------- Captured stdout call -----------------------------
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using OAS
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using OAS
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using OAS
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using SHRINKAGE
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using SHRINKAGE
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using SHRINKAGE
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using SHRINKAGE
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using LEDOIT_WOLF
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using LEDOIT_WOLF
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using OAS
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using OAS
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using SHRINKAGE
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using SHRINKAGE
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using SHRINKAGE
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using DIAGONAL_FIXED
    MAG regularization : 0.1
    GRAD regularization : 0.1
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using DIAGONAL_FIXED
    MAG regularization : 0.1
    GRAD regularization : 0.1
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using DIAGONAL_FIXED
    MAG regularization : 0.1
    GRAD regularization : 0.1
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using EMPIRICAL
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using EMPIRICAL
Done.
Computing rank from data with rank='full'
    MEG: rank 39 from info
    Created an SSP operator (subspace dimension = 3)
Reducing data rank from 39 -> 39
Estimating covariance using EMPIRICAL
Done.
```


#### Steps to reproduce

Run the test in a Fedora 34 environment

#### Expected results

The test should not fail. It should throw a ValueError

#### Additional information

Since Fedora 34 is the bleeding edge, and is not stable, it is likely that it might get fixed in a later release. It is also possible it is due to some internal library (probably sklearn) and their compatibility with F34. I will be using this tracker to keep an eye on it, and will update this ticket regularly.



Discussion:
We should fix the XDawn code. Currently we rely on the LAPACK function to detect rank deficiency but it's unreliable. There are two existing open PRs that just skip this part of the test but it's better if we properly fix it.

I can do this on Monday, checking the rank should be a fast operation anyway.

Resolving patch:
diff --git a/examples/inverse/plot_label_source_activations.py b/examples/inverse/plot_label_source_activations.py
--- a/examples/inverse/plot_label_source_activations.py
+++ b/examples/inverse/plot_label_source_activations.py
@@ -41,7 +41,7 @@
 ###############################################################################
 # Compute inverse solution
 # ------------------------
-pick_ori = "normal"  # Get signed values to see the effect of sign filp
+pick_ori = "normal"  # Get signed values to see the effect of sign flip
 stc = apply_inverse(evoked, inverse_operator, lambda2, method,
                     pick_ori=pick_ori)
 
diff --git a/mne/viz/ica.py b/mne/viz/ica.py
--- a/mne/viz/ica.py
+++ b/mne/viz/ica.py
@@ -190,7 +190,7 @@ def _plot_ica_properties(pick, ica, inst, psds_mean, freqs, n_trials,
     else:
         x = np.linspace(ymin, ymax, 50)
         kde_ = kde(x)
-        kde_ /= kde_.max()
+        kde_ /= kde_.max() or 1.
         kde_ *= hist_ax.get_xlim()[-1] * .9
         hist_ax.plot(kde_, x, color="k")
         hist_ax.set_ylim(ymin, ymax)


Test patch:
diff --git a/mne/channels/tests/test_interpolation.py b/mne/channels/tests/test_interpolation.py
--- a/mne/channels/tests/test_interpolation.py
+++ b/mne/channels/tests/test_interpolation.py
@@ -157,7 +157,7 @@ def test_interpolation_meg():
     """Test interpolation of MEG channels."""
     # speed accuracy tradeoff: channel subselection is faster but the
     # correlation drops
-    thresh = 0.7
+    thresh = 0.68
 
     raw, epochs_meg = _load_data('meg')
 
diff --git a/mne/preprocessing/tests/test_maxwell.py b/mne/preprocessing/tests/test_maxwell.py
--- a/mne/preprocessing/tests/test_maxwell.py
+++ b/mne/preprocessing/tests/test_maxwell.py
@@ -28,7 +28,7 @@
     _sh_real_to_complex, _sh_negate, _bases_complex_to_real, _trans_sss_basis,
     _bases_real_to_complex, _prep_mf_coils, find_bad_channels_maxwell)
 from mne.rank import _get_rank_sss, _compute_rank_int
-from mne.utils import (assert_meg_snr, run_tests_if_main, catch_logging,
+from mne.utils import (assert_meg_snr, catch_logging,
                        object_diff, buggy_mkl_svd, use_log_level)
 
 data_path = testing.data_path(download=False)
@@ -1339,14 +1339,11 @@ def test_find_bad_channels_maxwell(fname, bads, annot, add_ch, ignore_ref,
         # Check "flat" scores.
         scores_flat = got_scores['scores_flat']
         limits_flat = got_scores['limits_flat']
-        # The following essentially is just this:
-        #     n_segments_below_limit = (scores_flat < limits_flat).sum(-1)
-        # made to work with NaN's in the scores.
-        n_segments_below_limit = np.less(
-            scores_flat, limits_flat,
-            where=np.equal(np.isnan(scores_flat), False),
-            out=np.full_like(scores_flat, fill_value=False)).sum(-1)
-
+        # Deal with NaN's in the scores (can't use np.less directly because of
+        # https://github.com/numpy/numpy/issues/17198)
+        scores_flat[np.isnan(scores_flat)] = np.inf
+        limits_flat[np.isnan(limits_flat)] = -np.inf
+        n_segments_below_limit = (scores_flat < limits_flat).sum(-1)
         ch_idx = np.where(n_segments_below_limit >=
                           min(min_count, len(got_scores['bins'])))
         flats = set(got_scores['ch_names'][ch_idx])
@@ -1355,18 +1352,10 @@ def test_find_bad_channels_maxwell(fname, bads, annot, add_ch, ignore_ref,
         # Check "noisy" scores.
         scores_noisy = got_scores['scores_noisy']
         limits_noisy = got_scores['limits_noisy']
-        # The following essentially is just this:
-        #     n_segments_beyond_limit = (scores_noisy > limits_noisy).sum(-1)
-        # made to work with NaN's in the scores.
-        n_segments_beyond_limit = np.greater(
-            scores_noisy, limits_noisy,
-            where=np.equal(np.isnan(scores_noisy), False),
-            out=np.full_like(scores_noisy, fill_value=False)).sum(-1)
-
+        scores_noisy[np.isnan(scores_noisy)] = -np.inf
+        limits_noisy[np.isnan(limits_noisy)] = np.inf
+        n_segments_beyond_limit = (scores_noisy > limits_noisy).sum(-1)
         ch_idx = np.where(n_segments_beyond_limit >=
                           min(min_count, len(got_scores['bins'])))
         bads = set(got_scores['ch_names'][ch_idx])
         assert bads == set(want_bads)
-
-
-run_tests_if_main()
diff --git a/mne/preprocessing/tests/test_xdawn.py b/mne/preprocessing/tests/test_xdawn.py
--- a/mne/preprocessing/tests/test_xdawn.py
+++ b/mne/preprocessing/tests/test_xdawn.py
@@ -5,7 +5,6 @@
 
 import numpy as np
 import os.path as op
-import sys
 
 from numpy.testing import (assert_array_equal, assert_array_almost_equal,
                            assert_allclose)
@@ -16,7 +15,7 @@
                  create_info, EpochsArray)
 from mne.decoding import Vectorizer
 from mne.io import read_raw_fif
-from mne.utils import requires_sklearn, run_tests_if_main, check_version
+from mne.utils import requires_sklearn, check_version
 from mne.preprocessing.xdawn import Xdawn, _XdawnTransformer
 
 base_dir = op.join(op.dirname(__file__), '..', '..', 'io', 'tests', 'data')
@@ -194,8 +193,7 @@ def test_xdawn_regularization():
     xd.fit(epochs)
     xd = Xdawn(correct_overlap=False, reg='diagonal_fixed')
     xd.fit(epochs)
-    bad_eig = (sys.platform.startswith('win') and
-               check_version('numpy', '1.16.5'))  # some problem with on Win
+    bad_eig = check_version('numpy', '1.16.5')  # some problem with newer NumPy
     if bad_eig:
         pytest.skip('Unknown MKL+Windows error fails for eig check')
     xd = Xdawn(correct_overlap=False, reg=None)
@@ -353,6 +351,3 @@ def test_xdawn_decoding_performance():
         for i in range(len(relev_patterns)):
             r, _ = stats.pearsonr(relev_patterns[i, :], mixing_mat[0, :])
             assert np.abs(r) > 0.99
-
-
-run_tests_if_main()