From 9df84e69cb4ba92e71104dcfabcbfa3928fcb893 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Mon, 26 Jan 2026 16:19:10 -0500 Subject: [PATCH 1/2] MAINT: Fix for latest SciPy --- mne/utils/linalg.py | 55 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/mne/utils/linalg.py b/mne/utils/linalg.py index 9382aad50f2..5b12d7b67f3 100644 --- a/mne/utils/linalg.py +++ b/mne/utils/linalg.py @@ -49,18 +49,61 @@ def _get_lapack_funcs(dtype, names): ############################################################################### # linalg.svd and linalg.pinv2 +# TODO VERSION these can be removed once we use SciPy 1.18+ with batched processing +# (otherwise call overhead for `lwork` is too high). Can't use NumPy because +# it doesn't provide an interface to GESVD (only uses GESDD, which can be buggy). + + +# Vendored from scipy: _compute_lwork and _check_work_float + + +def _compute_lwork(routine, *args, **kwargs): # pragma: no cover + dtype = getattr(routine, "dtype", None) + int_dtype = getattr(routine, "int_dtype", None) + ret = routine(*args, **kwargs) + if ret[-1] != 0: + raise ValueError(f"Internal work array size computation failed: {ret[-1]}") + if len(ret) == 2: + return _check_work_float(ret[0].real, dtype, int_dtype) + else: + return tuple(_check_work_float(x.real, dtype, int_dtype) for x in ret[:-1]) + + +_int32_max = np.iinfo(np.int32).max +_int64_max = np.iinfo(np.int64).max + + +def _check_work_float(value, dtype, int_dtype): # pragma: no cover + if dtype == np.float32 or dtype == np.complex64: + # Single-precision routine -- take next fp value to work + # around possible truncation in LAPACK code + value = np.nextafter(value, np.inf, dtype=np.float32) + + value = int(value) + if int_dtype.itemsize == 4: + if value < 0 or value > _int32_max: + raise ValueError( + "Too large work array required -- computation " + "cannot be performed with standard 32-bit" + " LAPACK." + ) + elif int_dtype.itemsize == 8: + if value < 0 or value > _int64_max: + raise ValueError( + "Too large work array required -- computation" + " cannot be performed with standard 64-bit" + " LAPACK." + ) + return value + def _svd_lwork(shape, dtype=np.float64): """Set up SVD calculations on identical-shape float64/complex128 arrays.""" - try: - ds = linalg._decomp_svd - except AttributeError: # < 1.8.0 - ds = linalg.decomp_svd gesdd_lwork, gesvd_lwork = _get_lapack_funcs(dtype, ("gesdd_lwork", "gesvd_lwork")) - sdd_lwork = ds._compute_lwork( + sdd_lwork = _compute_lwork( gesdd_lwork, *shape, compute_uv=True, full_matrices=False ) - svd_lwork = ds._compute_lwork( + svd_lwork = _compute_lwork( gesvd_lwork, *shape, compute_uv=True, full_matrices=False ) return sdd_lwork, svd_lwork From a183110d320e5cdd79d4c43cc89ef81d1c9518d7 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Mon, 26 Jan 2026 20:40:01 -0500 Subject: [PATCH 2/2] MAINT: Fix --- azure-pipelines.yml | 1 + mne/viz/tests/test_raw.py | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e83c8db3dae..553e30a4204 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -245,6 +245,7 @@ stages: PYTHONIOENCODING: 'utf-8' AZURE_CI_WINDOWS: 'true' PYTHON_ARCH: 'x64' + MNE_CI_KIND: $(TEST_MODE) timeoutInMinutes: 95 strategy: maxParallel: 4 diff --git a/mne/viz/tests/test_raw.py b/mne/viz/tests/test_raw.py index dc50dfbf53d..c1022b7cf4d 100644 --- a/mne/viz/tests/test_raw.py +++ b/mne/viz/tests/test_raw.py @@ -321,6 +321,9 @@ def test_scale_bar(browser_backend): def test_plot_raw_selection(raw, browser_backend): """Test selection mode of plot_raw().""" ismpl = browser_backend.name == "matplotlib" + if ismpl and os.getenv("MNE_CI_KIND") == "pip-pre": + # TODO VERSION FIX SOON AFTER 2026/01/26! + pytest.xfail("Needs mpl gh-31031") with raw.info._unlock(): raw.info["lowpass"] = 10.0 # allow heavy decim during plotting browser_backend._close_all() # ensure all are closed @@ -765,6 +768,9 @@ def test_plot_misc_auto(browser_backend): def test_plot_annotations(raw, browser_backend): """Test annotation mode of the plotter.""" ismpl = browser_backend.name == "matplotlib" + if ismpl and os.getenv("MNE_CI_KIND") == "pip-pre": + # TODO VERSION FIX SOON AFTER 2026/01/26! + pytest.xfail("Needs mpl gh-31031") with raw.info._unlock(): raw.info["lowpass"] = 10.0 _annotation_helper(raw, browser_backend)