From e7c159cad33f1b4d3af2092a90ca749a41c074ed Mon Sep 17 00:00:00 2001 From: brovelli Date: Fri, 18 Sep 2026 16:09:36 +0200 Subject: [PATCH 1/3] Pin zero_mean in conn_spec's TF decomposition; test CI on Python 3.10-3.12 - _tf_decomp: mne changed the default of `zero_mean` in tfr_array_morlet / tfr_array_multitaper from False (<=1.5) to True, so conn_spec results silently depended on the installed mne version. Set zero_mean=False explicitly (user values in kw_cwt / kw_mt take precedence) and pin it in the conn_spec reference tests as well. - CI: Python matrix 3.8/3.9 -> 3.10/3.11/3.12 (mne >= 1.13 requires Python >= 3.11), flake / doc jobs on 3.11, classifiers updated. - What's new: conn_spec fixes from PR #69 and the Python / mne range. Verified with the full test suite at both ends of the supported range: Python 3.10 + mne 1.5.0 and Python 3.12 + mne 1.13.2 + numpy 2.5.3. Co-Authored-By: Claude Fable 5.1 --- .github/workflows/flake.yml | 2 +- .github/workflows/pr.yml | 2 +- .github/workflows/test_doc.yml | 4 ++-- docs/source/overview/ovw_whatsnew.rst | 8 ++++++++ frites/conn/conn_tf.py | 6 ++++++ frites/conn/tests/test_conn_spec.py | 4 ++-- setup.py | 3 +-- 7 files changed, 21 insertions(+), 8 deletions(-) diff --git a/.github/workflows/flake.yml b/.github/workflows/flake.yml index 10071522e..34a389cdb 100644 --- a/.github/workflows/flake.yml +++ b/.github/workflows/flake.yml @@ -15,7 +15,7 @@ jobs: - name: Set up Python 🔧 uses: actions/setup-python@v4 with: - python-version: '3.9' + python-version: '3.11' - name: Install dependencies 🔧 run: | diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 070eb19a4..cf2211455 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.8, 3.9] + python-version: ["3.10", "3.11", "3.12"] steps: - name: Checkout 🛎️ diff --git a/.github/workflows/test_doc.yml b/.github/workflows/test_doc.yml index 0b05695d2..2b4849c42 100644 --- a/.github/workflows/test_doc.yml +++ b/.github/workflows/test_doc.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.10", "3.11", "3.12"] steps: - name: Checkout 🛎️ @@ -40,7 +40,7 @@ jobs: - name: Set up Python 🔧 uses: actions/setup-python@v4 with: - python-version: '3.8' + python-version: '3.11' - name: Install dependencies 🔧 run: | diff --git a/docs/source/overview/ovw_whatsnew.rst b/docs/source/overview/ovw_whatsnew.rst index 6a498c461..9eb5fb291 100644 --- a/docs/source/overview/ovw_whatsnew.rst +++ b/docs/source/overview/ovw_whatsnew.rst @@ -12,6 +12,14 @@ Bug fixes * Support mne-python versions newer than 1.5.0, up to the current release (:commit:`9a81a6dc`) * Restore a compatibility shim so mne keeps working with NumPy >= 2.4, which removed ``np.in1d`` while mne still relies on it internally (:commit:`ad7cfe65`) * Fix type comparison in :func:`frites.dataset.ds_utils.multi_to_uni_conditions` and in :func:`frites.plot.plot_conn_circle` (:commit:`cee7ed4a`) +* Fix :func:`frites.conn.conn_spec` in multitaper mode : the cross- and auto-spectra are now averaged over tapers (the complex coefficients used to be averaged before, cancelling most of the signal). Coherence and PLV values change for every multitaper user (:PR:`69`) +* Fix the hanning smoothing kernel of :func:`frites.conn.conn_spec` (``sm_times`` of 2 samples gave a NaN output, 3 samples no smoothing) (:PR:`69`) +* The cross-spectrum (``metric='sxy'``) of :func:`frites.conn.conn_spec` is now complex (the imaginary part used to be silently dropped) (:PR:`69`) +* :func:`frites.conn.conn_spec` sets ``zero_mean=False`` explicitly in the time-frequency decomposition : mne changed its default to ``True``, which silently changed the results between mne versions + +Dependencies +++++++++++++ +* Tested against mne 1.5 up to 1.13 (mne >= 1.13 requires Python >= 3.11) and Python 3.10 to 3.12 ; the continuous integration matrix moved from Python 3.8/3.9 to 3.10/3.11/3.12 v0.4.4 ------ diff --git a/frites/conn/conn_tf.py b/frites/conn/conn_tf.py index bce27cd5f..389c35c83 100644 --- a/frites/conn/conn_tf.py +++ b/frites/conn/conn_tf.py @@ -63,6 +63,12 @@ def _tf_decomp(data, sf, freqs, mode='morlet', n_cycles=7.0, mt_bandwidth=None, complex coefficients across tapers cancels most of the signal and does not give the multitaper estimator. """ + # mne changed the default of `zero_mean` from False (<=1.5) to True. Fix it + # explicitly so that the results do not depend on the mne version (a + # user-defined value in kw_cwt / kw_mt takes precedence) + kw_cwt = {'zero_mean': False, **kw_cwt} + kw_mt = {'zero_mean': False, **kw_mt} + if mode == 'morlet': out = tfr_array_morlet( data, sf, freqs, n_cycles=n_cycles, output='complex', decim=decim, diff --git a/frites/conn/tests/test_conn_spec.py b/frites/conn/tests/test_conn_spec.py index e1d341e34..fce30943f 100644 --- a/frites/conn/tests/test_conn_spec.py +++ b/frites/conn/tests/test_conn_spec.py @@ -150,7 +150,7 @@ def test_multitaper_reference(self): # reference : (trials, chans, tapers, freqs, times) w = tfr_array_multitaper(x.data, sfreq, freqs, n_cycles=n_cycles, time_bandwidth=tw, output='complex', - verbose=False) + zero_mean=False, verbose=False) s_xy = (w[:, 0] * np.conj(w[:, 1])).mean(1) s_xx = (np.abs(w[:, 0]) ** 2).mean(1) s_yy = (np.abs(w[:, 1]) ** 2).mean(1) @@ -183,7 +183,7 @@ def test_sxy_complex(self): assert np.abs(sxy.data.imag).max() > 0 # morlet cross-spectrum is exactly w_x * conj(w_y) w = tfr_array_morlet(x.data, sfreq, freqs, n_cycles=n_cycles, - output='complex', verbose=False) + output='complex', zero_mean=False, verbose=False) sxy = conn_spec(x, metric='sxy', dtype=np.complex128, **kw) np.testing.assert_allclose(sxy.sel(roi='x-y').data, w[:, 0] * np.conj(w[:, 1]), rtol=1e-6) diff --git a/setup.py b/setup.py index 040d1310a..eee78820c 100644 --- a/setup.py +++ b/setup.py @@ -67,8 +67,7 @@ def read(fname): 'Intended Audience :: Science/Research', 'Intended Audience :: Education', 'Intended Audience :: Developers', - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", ]) From 3d38c83b1cfd31a1f704d5b8905e1b70465790b4 Mon Sep 17 00:00:00 2001 From: brovelli Date: Fri, 18 Sep 2026 16:35:13 +0200 Subject: [PATCH 2/3] Fix the documentation build on Python >= 3.10 The doc build failed on the new CI matrix (Python 3.10 / 3.11 / 3.12) while pytest passed: Extension error: Could not import extension sphinxcontrib.bibtex (cannot import name 'MutableSet' from 'collections') sphinxcontrib-bibtex was pinned to 1.0.0 (2019), which depends on `oset` and its `collections.MutableSet` import, an alias removed in Python 3.10. This is why the build only ever passed on the old 3.8 / 3.9 matrix. - setup.py: sphinxcontrib-bibtex>=2.6 and sphinx>=7.4 (bibtex 2.7 needs it; the sphinx==7.0.0 pin is lifted, the build is verified with Sphinx 7.4.7 and 9.0.4) - conf.py: intersphinx_mapping in the named format required by Sphinx >= 8 (the pre-1.0 format was rejected with a ConfigError), language 'en' instead of None - what's new: the extlink role is :pull:, not :PR: Co-Authored-By: Claude Fable 5.1 --- .gitignore | 4 +++- docs/source/conf.py | 4 ++-- docs/source/overview/ovw_whatsnew.rst | 8 ++++---- setup.py | 4 ++-- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index e915b2ef8..0837652b9 100644 --- a/.gitignore +++ b/.gitignore @@ -122,4 +122,6 @@ docs/source/auto_examples/ node_modules/ package.json -yarn.lock \ No newline at end of file +yarn.lock +docs/source/sg_execution_times.rst + diff --git a/docs/source/conf.py b/docs/source/conf.py index f4851136d..2e3ac49bc 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -89,7 +89,7 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +language = 'en' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -276,7 +276,7 @@ # -- Options for intersphinx extension --------------------------------------- # Example configuration for intersphinx: refer to the Python standard library. -intersphinx_mapping = {"https://docs.python.org/": None} +intersphinx_mapping = {"python": ("https://docs.python.org/3", None)} # -- Options for todo extension ---------------------------------------------- diff --git a/docs/source/overview/ovw_whatsnew.rst b/docs/source/overview/ovw_whatsnew.rst index 9eb5fb291..07e976990 100644 --- a/docs/source/overview/ovw_whatsnew.rst +++ b/docs/source/overview/ovw_whatsnew.rst @@ -12,9 +12,9 @@ Bug fixes * Support mne-python versions newer than 1.5.0, up to the current release (:commit:`9a81a6dc`) * Restore a compatibility shim so mne keeps working with NumPy >= 2.4, which removed ``np.in1d`` while mne still relies on it internally (:commit:`ad7cfe65`) * Fix type comparison in :func:`frites.dataset.ds_utils.multi_to_uni_conditions` and in :func:`frites.plot.plot_conn_circle` (:commit:`cee7ed4a`) -* Fix :func:`frites.conn.conn_spec` in multitaper mode : the cross- and auto-spectra are now averaged over tapers (the complex coefficients used to be averaged before, cancelling most of the signal). Coherence and PLV values change for every multitaper user (:PR:`69`) -* Fix the hanning smoothing kernel of :func:`frites.conn.conn_spec` (``sm_times`` of 2 samples gave a NaN output, 3 samples no smoothing) (:PR:`69`) -* The cross-spectrum (``metric='sxy'``) of :func:`frites.conn.conn_spec` is now complex (the imaginary part used to be silently dropped) (:PR:`69`) +* Fix :func:`frites.conn.conn_spec` in multitaper mode : the cross- and auto-spectra are now averaged over tapers (the complex coefficients used to be averaged before, cancelling most of the signal). Coherence and PLV values change for every multitaper user (:pull:`69`) +* Fix the hanning smoothing kernel of :func:`frites.conn.conn_spec` (``sm_times`` of 2 samples gave a NaN output, 3 samples no smoothing) (:pull:`69`) +* The cross-spectrum (``metric='sxy'``) of :func:`frites.conn.conn_spec` is now complex (the imaginary part used to be silently dropped) (:pull:`69`) * :func:`frites.conn.conn_spec` sets ``zero_mean=False`` explicitly in the time-frequency decomposition : mne changed its default to ``True``, which silently changed the results between mne versions Dependencies @@ -30,7 +30,7 @@ New Features * New function :func:`frites.core.ent_nd_g` to compute entropy on tensors (:commit:`17587a15`) * New function :func:`frites.conn.conn_ii` to estimate the interaction information (:commit:`10938b46`) * New function :func:`frites.conn.conn_pid` to estimate the partial information decomposition (:commit:`ac9798dd`) -- New function :func:`frites.conn.conn_fit` to estimate the feature specific information transfer (:PR:`59`) - :author:`aopy` +- New function :func:`frites.conn.conn_fit` to estimate the feature specific information transfer (:pull:`59`) - :author:`aopy` Bug fixes +++++++++ diff --git a/setup.py b/setup.py index eee78820c..a28572513 100644 --- a/setup.py +++ b/setup.py @@ -30,8 +30,8 @@ def read(fname): core_deps = ['matplotlib', 'networkx', 'numba', 'dcor'] test_deps = ['pytest', 'pytest-sugar', 'pytest-cov', 'codecov'] doc_deps = [ - 'sphinx==7.0.0 ', 'sphinx-gallery', 'pydata-sphinx-theme>=0.6.3', - 'sphinxcontrib-bibtex==1.0.0', 'numpydoc', 'xlrd', 'openpyxl', 'seaborn', + 'sphinx>=7.4', 'sphinx-gallery', 'pydata-sphinx-theme>=0.6.3', + 'sphinxcontrib-bibtex>=2.6', 'numpydoc', 'xlrd', 'openpyxl', 'seaborn', 'memory-profiler', 'sphinx-panels', 'sphinx-copybutton' ] flake_deps = ['flake8', 'pep8-naming'] From 372ea45487d4cb10321ffd86224ecbaabc1034b6 Mon Sep 17 00:00:00 2001 From: brovelli Date: Fri, 18 Sep 2026 16:55:31 +0200 Subject: [PATCH 3/3] Drop pkg_resources from set_mpl_style (Python 3.12 doc build) On the Python 3.12 CI job, 31 of the 34 gallery examples failed with ModuleNotFoundError: No module named 'pkg_resources' raised by frites.set_mpl_style, which every example calls first. pkg_resources belongs to setuptools, which is no longer installed by default in Python >= 3.12 environments and whose pkg_resources module is deprecated. Locate the .mplstyle files with the stdlib importlib.resources instead (Python >= 3.9). Co-Authored-By: Claude Fable 5.1 --- frites/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/frites/__init__.py b/frites/__init__.py index 3d7edd201..640eed497 100644 --- a/frites/__init__.py +++ b/frites/__init__.py @@ -70,8 +70,10 @@ def set_mpl_style(style='frites'): Style name. Use either "frites" for a white background or "ggfrites" for a grey brackground like with ggplot. """ - from pkg_resources import resource_filename + # pkg_resources (setuptools) is no longer available by default on + # Python >= 3.12 and is deprecated; use the stdlib instead + from importlib.resources import files import matplotlib.pyplot as plt assert style in ["frites", "ggfrites"] - path_style = resource_filename('frites', f'data/{style}.mplstyle') - plt.style.use(path_style) + path_style = files('frites') / 'data' / f'{style}.mplstyle' + plt.style.use(str(path_style))