Repository: mne-tools/mne-python

Issue:
One test failure on aarch64
#### Describe the bug

While packaging version 1.0.3 for Fedora linux, we found that the test `test_mxne_inverse_sure_synthetic[3-0.75-4-20-60-20]` fails on `aarch64` due to division by zero.

#### Steps to reproduce

Run the unit tests on an `aarch64` system. There is no reason to believe this error is specific to the Fedora Linux RPM build environment.

#### Expected results

All tests pass.

#### Actual results

```
=================================== FAILURES ===================================
_____________ test_mxne_inverse_sure_synthetic[3-0.75-4-20-60-20] ______________
mne/inverse_sparse/tests/test_mxne_inverse.py:372: in test_mxne_inverse_sure_synthetic
    _, active_set, _ = _compute_mxne_sure(M, G, alpha_grid, sigma=sigma,
<decorator-gen-507>:10: in _compute_mxne_sure
    ???
mne/inverse_sparse/mxne_inverse.py:881: in _compute_mxne_sure
    coefs_grid_1, coefs_grid_2, active_sets = _fit_on_grid(gain, M, eps, delta)
mne/inverse_sparse/mxne_inverse.py:858: in _fit_on_grid
    X_eps, a_set_eps = _run_solver(alpha, M_eps,
mne/inverse_sparse/mxne_inverse.py:819: in _run_solver
    X, active_set, _ = iterative_mixed_norm_solver(
<decorator-gen-497>:10: in iterative_mixed_norm_solver
    ???
mne/inverse_sparse/mxne_optim.py:584: in iterative_mixed_norm_solver
    X, _active_set, _ = mixed_norm_solver(
<decorator-gen-496>:10: in mixed_norm_solver
    ???
mne/inverse_sparse/mxne_optim.py:485: in mixed_norm_solver
    X, active_set, E = l21_solver(M, G, alpha, lc, maxit=maxit,
<decorator-gen-495>:12: in _mixed_norm_solver_bcd
    ???
mne/inverse_sparse/mxne_optim.py:233: in _mixed_norm_solver_bcd
    c = z / z.sum()
E   RuntimeWarning: divide by zero encountered in true_divide
```

#### Additional information

```
Platform:         Linux-5.18.11-200.fc36.aarch64-aarch64-with-glibc2.35.9000
Python:           3.11.0b5 (main, Jul 26 2022, 00:00:00) [GCC 12.1.1 20220628 (Red Hat 12.1.1-3)]
Executable:       /usr/bin/python3
CPU:              aarch64: 5 cores
Memory:           Unavailable (requires "psutil" package)
mne:              1.0.3
numpy:            1.22.0 {blas=flexiblas, lapack=flexiblas}
scipy:            1.8.1
matplotlib:       3.5.2 {backend=agg}
sklearn:          1.0.2
numba:            Not found
nibabel:          3.2.2
nilearn:          0.9.1
dipy:             1.5.0
cupy:             Not found
pandas:           1.3.5
pyvista:          Not found
pyvistaqt:        Not found
ipyvtklink:       Not found
vtk:              9.1.0
PyQt5:            5.15.6
ipympl:           Not found
pooch:            v1.5.2
mne_bids:         Not found
mne_nirs:         Not found
mne_features:     Not found
mne_qt_browser:   Not found
mne_connectivity: Not found
```

I am happy to run any additional tests that may be helpful, or try any candidate fixes, on real or emulated `aarch64` hardware.


Discussion:
Hello! 👋 Thanks for opening your first issue here! ❤️ We will try to get back to you soon. 🚴🏽‍♂️

@sanjayankur31

Resolving patch:
diff --git a/mne/inverse_sparse/mxne_optim.py b/mne/inverse_sparse/mxne_optim.py
--- a/mne/inverse_sparse/mxne_optim.py
+++ b/mne/inverse_sparse/mxne_optim.py
@@ -225,7 +225,7 @@ def _mixed_norm_solver_bcd(M, G, alpha, lipschitz_constant, maxit=200,
                 # equivalent to:
                 # z = np.linalg.solve(C, np.ones(K))
                 u, s, _ = np.linalg.svd(C, hermitian=True)
-                if s[-1] <= 1e-6 * s[0]:
+                if s[-1] <= 1e-6 * s[0] or not np.isfinite(s).all():
                     logger.debug("Iteration %d: LinAlg Error" % (i + 1))
                     continue
                 z = ((u * 1 / s) @ u.T).sum(0)
diff --git a/mne/time_frequency/tfr.py b/mne/time_frequency/tfr.py
--- a/mne/time_frequency/tfr.py
+++ b/mne/time_frequency/tfr.py
@@ -248,7 +248,12 @@ def _cwt_gen(X, Ws, *, fsize=0, mode="same", decim=1, use_fft=True):
             if use_fft:
                 ret = ifft(fft_x * fft_Ws[ii])[:n_times + W.size - 1]
             else:
-                ret = np.convolve(x, W, mode=mode)
+                # Work around multarray.correlate->OpenBLAS bug on ppc64le
+                # ret = np.correlate(x, W, mode=mode)
+                ret = (
+                    np.convolve(x, W.real, mode=mode) +
+                    1j * np.convolve(x, W.imag, mode=mode)
+                )
 
             # Center and decimate decomposition
             if mode == 'valid':


Test patch:
diff --git a/mne/time_frequency/tests/test_tfr.py b/mne/time_frequency/tests/test_tfr.py
--- a/mne/time_frequency/tests/test_tfr.py
+++ b/mne/time_frequency/tests/test_tfr.py
@@ -99,6 +99,8 @@ def test_time_frequency():
         tfr_morlet(epochs_nopicks,
                    freqs=freqs, n_cycles=n_cycles, use_fft=True,
                    return_itc=False, picks=picks, average=False)
+    assert_allclose(
+        epochs_power_picks.data[0, 0, 0, 0], 9.130315e-23, rtol=1e-4)
     power_picks_avg = epochs_power_picks.average()
     # the actual data arrays here are equivalent, too...
     assert_allclose(power.data, power_picks.data)
diff --git a/mne/utils/tests/test_misc.py b/mne/utils/tests/test_misc.py
--- a/mne/utils/tests/test_misc.py
+++ b/mne/utils/tests/test_misc.py
@@ -3,7 +3,6 @@
 import subprocess
 import sys
 
-import numpy as np
 import pytest
 
 import mne
@@ -57,8 +56,6 @@ def test_run_subprocess(tmp_path, capsys, kind, do_raise):
 import time
 print('foo', file=sys.{kind})
 print('bar', file=sys.{kind})
-sys.{kind}.flush()
-time.sleep(0.02)
 """ + extra)
     with catch_logging() as log, raise_context:
         stdout, stderr = run_subprocess(
@@ -75,18 +72,14 @@ def test_run_subprocess(tmp_path, capsys, kind, do_raise):
     stderr = stderr.replace('\r\n', '\n')
     if do_raise:  # remove traceback
 
-        def truncate_before_traceback(log):
-            tb_line = np.where(
-                [line.startswith('Traceback') for line in log.split('\n')])[0]
-            assert len(tb_line) == 1
-            tb_line = tb_line[0]
-            log = '\n'.join(log.split('\n')[:tb_line])
-            if log:
-                log += '\n'
-            return log
+        def remove_traceback(log):
+            return '\n'.join(
+                line for line in log.split('\n')
+                if not line.strip().startswith(
+                    ('File ', 'raise ', 'RuntimeError: ', 'Traceback ')))
 
-        log = truncate_before_traceback(log)
-        stderr = truncate_before_traceback(stderr)
+        log = remove_traceback(log)
+        stderr = remove_traceback(stderr)
     want = 'foo\nbar\n'
     assert log == want, orig_log
     if kind == 'stdout':