Skip to content
Merged
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ jobs:
name: Check sphinx log for warnings (which are treated as errors)
when: always
command: |
! grep "^.* WARNING: .*$" sphinx_log.txt
! grep "^.* (WARNING|ERROR): .*$" sphinx_log.txt
- run:
name: Show profiling output
when: always
Expand Down
20 changes: 9 additions & 11 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,24 +99,22 @@ jobs:
python-version: ${{ matrix.python }}
if: startswith(matrix.kind, 'pip')
# Python (if conda)
- name: Remove numba and dipy
run: | # TODO: Remove when numba 0.59 and dipy 1.8 land on conda-forge
sed -i '/numba/d' environment.yml
sed -i '/dipy/d' environment.yml
sed -i 's/- mne$/- mne-base/' environment.yml
if: matrix.os == 'ubuntu-latest' && startswith(matrix.kind, 'conda') && matrix.python == '3.12'
- name: Fixes for conda
run: |
# For some reason on Linux we get crashes
if [[ "$RUNNER_OS" == "Linux" ]]; then
sed -i "/numba/d" environment.yml
elif [[ "$RUNNER_OS" == "macOS" ]]; then
sed -i "" "s/ - PySide6 .*/ - PySide6 <6.8/g" environment.yml
fi
if: matrix.kind == 'conda' || matrix.kind == 'mamba'
- uses: mamba-org/setup-micromamba@v2
with:
environment-file: ${{ env.CONDA_ENV }}
environment-name: mne
create-args: >-
python=${{ env.PYTHON_VERSION }}
mamba
nomkl
if: ${{ !startswith(matrix.kind, 'pip') }}
# Make sure we have the right Python
- run: python -c "import platform; assert platform.machine() == 'arm64', platform.machine()"
if: matrix.os == 'macos-14'
- run: ./tools/github_actions_dependencies.sh
# Minimal commands on Linux (macOS stalls)
- run: ./tools/get_minimal_commands.sh
Expand Down
5 changes: 5 additions & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,10 @@ def fix_sklearn_inherited_docstrings(app, what, name, obj, options, lines):
r"https://scholar.google.com/scholar\?cites=12188330066413208874&as_ylo=2014",
r"https://scholar.google.com/scholar\?cites=1521584321377182930&as_ylo=2013",
"https://www.research.chop.edu/imaging",
"http://prdownloads.sourceforge.net/optipng/optipng-0.7.8-win64.zip?download",
"https://sourceforge.net/projects/aespa/files/",
"https://sourceforge.net/projects/ezwinports/files/",
"https://www.mathworks.com/products/compiler/matlab-runtime.html",
# 500 server error
"https://openwetware.org/wiki/Beauchamp:FreeSurfer",
# 503 Server error
Expand All @@ -688,6 +692,7 @@ def fix_sklearn_inherited_docstrings(app, what, name, obj, options, lines):
# SSL problems sometimes
"http://ilabs.washington.edu",
"https://psychophysiology.cpmc.columbia.edu",
"https://erc.easme-web.eu",
]
linkcheck_anchors = False # saves a bit of time
linkcheck_timeout = 15 # some can be quite slow
Expand Down
2 changes: 1 addition & 1 deletion doc/development/contributing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,6 @@ it can serve as a useful example of what to expect from the PR review process.
.. optipng

.. _optipng: http://optipng.sourceforge.net/
.. _optipng for Windows: http://prdownloads.sourceforge.net/optipng/optipng-0.7.7-win32.zip?download
.. _optipng for Windows: http://prdownloads.sourceforge.net/optipng/optipng-0.7.8-win64.zip?download

.. include:: ../links.inc
2 changes: 1 addition & 1 deletion doc/install/installers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ Platform-specific installers

.. We have to use a button-link here because button-ref doesn't properly nested parse the inline code

.. button-link:: ./ides.html
.. button-link:: ides.html
:ref-type: ref
:color: success
:shadow:
Expand Down
2 changes: 2 additions & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@ dependencies:
- joblib
- jupyter
- lazy_loader >=0.3
- mamba
- matplotlib >=3.7
- mffpy >=0.5.7
- mne-qt-browser
- nibabel
- nilearn
- nomkl
- numba
- numpy >=1.25,<3
- openmeeg >=2.5.5
Expand Down
4 changes: 4 additions & 0 deletions mne/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ def pytest_configure(config):
ignore:The (non_)?interactive_bk attribute was deprecated.*:
# SWIG (via OpenMEEG)
ignore:.*builtin type swigvarlink has no.*:DeprecationWarning
# eeglabio
ignore:numpy\.core\.records is deprecated.*:DeprecationWarning
# joblib
ignore:process .* is multi-threaded, use of fork/exec.*:DeprecationWarning
""" # noqa: E501
for warning_line in warning_lines.split("\n"):
warning_line = warning_line.strip()
Expand Down
7 changes: 6 additions & 1 deletion mne/datasets/sleep_physionet/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.

import inspect
import os
import os.path as op

Expand Down Expand Up @@ -114,12 +115,16 @@ def _update_sleep_temazepam_records(fname=TEMAZEPAM_SLEEP_RECORDS):
data = data.set_index(("Subject - age - sex", "Nr"))
data.index.name = "subject"
data.columns.names = [None, None]
kwargs = dict()
# TODO VERSION can be removed once we require Pandas 2.1
if "future_stack" in inspect.getfullargspec(pd.DataFrame.stack).args:
kwargs["future_stack"] = True
data = (
data.set_index(
[("Subject - age - sex", "Age"), ("Subject - age - sex", "M1/F2")],
append=True,
)
.stack(level=0)
.stack(level=0, **kwargs)
.reset_index()
)

Expand Down
6 changes: 5 additions & 1 deletion mne/decoding/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,11 @@ def __sklearn_tags__(self):
"""Get sklearn tags."""
from sklearn.utils import get_tags # added in 1.6

return get_tags(self.model)
# fit method below does not allow sparse data via check_data, we could
# eventually make it smarter if we had to
tags = get_tags(self.model)
tags.input_tags.sparse = False
return tags

def __getattr__(self, attr):
"""Wrap to model for some attributes."""
Expand Down
8 changes: 4 additions & 4 deletions mne/decoding/transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ def inverse_transform(self, epochs_data):
return out


class Vectorizer(TransformerMixin):
class Vectorizer(TransformerMixin, BaseEstimator):
"""Transform n-dimensional array into 2D array of n_samples by n_features.

This class reshapes an n-dimensional array into an n_samples * n_features
Expand Down Expand Up @@ -343,7 +343,7 @@ def inverse_transform(self, X):


@fill_doc
class PSDEstimator(TransformerMixin):
class PSDEstimator(TransformerMixin, BaseEstimator):
"""Compute power spectral density (PSD) using a multi-taper method.

Parameters
Expand Down Expand Up @@ -452,7 +452,7 @@ def transform(self, epochs_data):


@fill_doc
class FilterEstimator(TransformerMixin):
class FilterEstimator(TransformerMixin, BaseEstimator):
"""Estimator to filter RtEpochs.

Applies a zero-phase low-pass, high-pass, band-pass, or band-stop
Expand Down Expand Up @@ -743,7 +743,7 @@ def _apply_method(self, X, method):


@fill_doc
class TemporalFilter(TransformerMixin):
class TemporalFilter(TransformerMixin, BaseEstimator):
"""Estimator to filter data array along the last dimension.

Applies a zero-phase low-pass, high-pass, band-pass, or band-stop
Expand Down
4 changes: 2 additions & 2 deletions mne/export/tests/test_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def _create_raw_for_edf_tests(stim_channel_index=None):


edfio_mark = pytest.mark.skipif(
not _check_edfio_installed(strict=False), reason="unsafe use of private module"
not _check_edfio_installed(strict=False), reason="requires edfio"
)


Expand Down Expand Up @@ -476,7 +476,7 @@ def test_export_epochs_eeglab(tmp_path, preload):
with ctx():
epochs.export(temp_fname)
epochs.drop_channels([ch for ch in ["epoc", "STI 014"] if ch in epochs.ch_names])
epochs_read = read_epochs_eeglab(temp_fname)
epochs_read = read_epochs_eeglab(temp_fname, verbose="error") # head radius
assert epochs.ch_names == epochs_read.ch_names
cart_coords = np.array([d["loc"][:3] for d in epochs.info["chs"]]) # just xyz
cart_coords_read = np.array([d["loc"][:3] for d in epochs_read.info["chs"]])
Expand Down
13 changes: 13 additions & 0 deletions mne/fixes.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,3 +720,16 @@ def minimum_phase(h, method="homomorphic", n_fft=None, *, half=True):

n_out = (n_half + len(h) % 2) if half else len(h)
return h_minimum[:n_out]


# SciPy 1.15 deprecates sph_harm for sph_harm_y and using it will trigger a
# DeprecationWarning. This is a backport of the new function for older SciPy versions.
def sph_harm_y(n, m, theta, phi, *, diff_n=0):
"""Wrap scipy.special.sph_harm for sph_harm_y."""
# Can be removed once we no longer support scipy < 1.15.0
from scipy import special

if "sph_harm_y" in special.__dict__:
return special.sph_harm_y(n, m, theta, phi, diff_n=diff_n)
else:
return special.sph_harm(m, n, phi, theta)
8 changes: 4 additions & 4 deletions mne/preprocessing/maxwell.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import numpy as np
from scipy import linalg
from scipy.special import lpmv, sph_harm
from scipy.special import lpmv

from .. import __version__
from .._fiff.compensator import make_compensator
Expand All @@ -24,7 +24,7 @@
from ..annotations import _annotations_starts_stops
from ..bem import _check_origin
from ..channels.channels import _get_T1T2_mag_inds, fix_mag_coil_types
from ..fixes import _safe_svd, bincount
from ..fixes import _safe_svd, bincount, sph_harm_y
from ..forward import _concatenate_coils, _create_meg_coils, _prep_meg_channels
from ..io import BaseRaw, RawArray
from ..surface import _normalize_vectors
Expand Down Expand Up @@ -436,7 +436,7 @@ def _prep_maxwell_filter(
# we purposefully stay away from shorthand notation in both and use
# explicit terms (like 'azimuth' and 'polar') to avoid confusion.
# See mathworld.wolfram.com/SphericalHarmonic.html for more discussion.
# Our code follows the same standard that ``scipy`` uses for ``sph_harm``.
# Our code follows the same standard that ``scipy`` uses for ``sph_harm_y``.

# triage inputs ASAP to avoid late-thrown errors
_validate_type(raw, BaseRaw, "raw")
Expand Down Expand Up @@ -1487,7 +1487,7 @@ def _sss_basis_basic(exp, coils, mag_scale=100.0, method="standard"):
S_in_out = list()
grads_in_out = list()
# Same spherical harmonic is used for both internal and external
sph = sph_harm(order, degree, az, pol)
sph = sph_harm_y(degree, order, pol, az)
sph_norm = _sph_harm_norm(order, degree)
# Compute complex gradient for all integration points
# in spherical coordinates (Eq. 6). The gradient for rad, az, pol
Expand Down
6 changes: 3 additions & 3 deletions mne/preprocessing/tests/test_maxwell.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
import pytest
from numpy.testing import assert_allclose, assert_array_equal
from scipy import sparse
from scipy.special import sph_harm

import mne
from mne import compute_raw_covariance, concatenate_raws, pick_info, pick_types
from mne._fiff.constants import FIFF
from mne.annotations import _annotations_starts_stops
from mne.chpi import filter_chpi, read_head_pos
from mne.datasets import testing
from mne.fixes import sph_harm_y
from mne.forward import _prep_meg_channels, use_coil_def
from mne.io import (
BaseRaw,
Expand Down Expand Up @@ -431,9 +431,9 @@ def test_spherical_conversions():
az, pol = np.meshgrid(np.linspace(0, 2 * np.pi, 30), np.linspace(0, np.pi, 20))
for degree in range(1, int_order):
for order in range(0, degree + 1):
sph = sph_harm(order, degree, az, pol)
sph = sph_harm_y(degree, order, pol, az)
# ensure that we satisfy the conjugation property
assert_allclose(_sh_negate(sph, order), sph_harm(-order, degree, az, pol))
assert_allclose(_sh_negate(sph, order), sph_harm_y(degree, -order, pol, az))
# ensure our conversion functions work
sph_real_pos = _sh_complex_to_real(sph, order)
sph_real_neg = _sh_complex_to_real(sph, -order)
Expand Down
5 changes: 2 additions & 3 deletions mne/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@
import numpy as np
from scipy import linalg
from scipy.spatial.distance import cdist
from scipy.special import sph_harm

from ._fiff.constants import FIFF
from ._fiff.open import fiff_open
from ._fiff.tag import read_tag
from ._fiff.write import start_and_end_file, write_coord_trans
from .defaults import _handle_default
from .fixes import _get_img_fdata, jit
from .fixes import _get_img_fdata, jit, sph_harm_y
from .utils import (
_check_fname,
_check_option,
Expand Down Expand Up @@ -926,7 +925,7 @@ def _compute_sph_harm(order, az, pol):
# _deg_ord_idx(0, 0) = -1 so we're actually okay to use it here
for degree in range(order + 1):
for order_ in range(degree + 1):
sph = sph_harm(order_, degree, az, pol)
sph = sph_harm_y(degree, order_, pol, az)
out[:, _deg_ord_idx(degree, order_)] = _sh_complex_to_real(sph, order_)
if order_ > 0:
out[:, _deg_ord_idx(degree, -order_)] = _sh_complex_to_real(
Expand Down
2 changes: 1 addition & 1 deletion tools/github_actions_dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ if [ ! -z "$CONDA_ENV" ]; then
elif [[ "${MNE_CI_KIND}" == "pip" ]]; then
# Only used for 3.13 at the moment, just get test deps plus a few extras
# that we know are available
INSTALL_ARGS="nibabel scikit-learn numpydoc PySide6 mne-qt-browser"
INSTALL_ARGS="nibabel scikit-learn numpydoc PySide6 mne-qt-browser pandas h5io mffpy defusedxml"
INSTALL_KIND="test"
else
test "${MNE_CI_KIND}" == "pip-pre"
Expand Down
Loading
Loading