Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 37 additions & 11 deletions .github/workflows/MATLAB_unittests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,37 +7,63 @@ on:
paths:
- brainstat_matlab/*
- '**.m'
- .github/workflows/MATLAB_unittests.yml
pull_request:
paths:
- brainstat_matlab/*
- '**.m'
- .github/workflows/MATLAB_unittests.yml

jobs:
matlab_unit_test:
strategy:
fail-fast: false
matrix:
matlab-version: [R019b, latest]
include:
- matlab-version: R2022a
python-version: '3.9'
- matlab-version: latest
python-version: '3.10'

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Set up Python 3.8
uses: actions/setup-python@v2
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
id: setup-python
uses: actions/setup-python@v5
with:
python-version: 3.8
- name: Install dependencies
python-version: ${{ matrix.python-version }}

- name: Install Python dependencies for MATLAB tests
shell: bash
run: |
python -m pip install --upgrade pip setuptools wheel
# numpy + vtk are required by recursive_pkl_conversion in
# tests/@test_precomputed/test_precomputed.m, which loads pickle
# fixtures via MATLAB's Python bridge.
python -m pip install numpy vtk
[[ -z $(git remote show origin | grep "Fetch URL:" | grep git@github.com:MICA-MNI/BrainStat.git) ]] && git config remote.upstream.fetch refs/heads/*:refs/remotes/upstream/* || git config remote.origin.fetch refs/heads/*:refs/origin/upstream/*
git fetch origin test-data-2.0
git checkout origin/test-data-2.0 -- extern/test-data
git reset extern/test-data

- name: Install MATLAB
uses: matlab-actions/setup-matlab@v0
- name: Run tests
uses: matlab-actions/run-tests@v0
uses: matlab-actions/setup-matlab@v2
with:
release: ${{ matrix.matlab-version }}

- name: Run tests
uses: matlab-actions/run-command@v2
with:
source-folder: brainstat_matlab
# Configure MATLAB's Python interpreter explicitly before running the
# tests; otherwise test_precomputed/load_pkl errors with
# MATLAB:Python:PythonUnavailable on the GitHub runners. Also need to
# add brainstat_matlab to the MATLAB path explicitly — run-tests v2
# used to do this via source-folder, but run-command does not.
command: |
addpath(genpath('brainstat_matlab'));
pyenv('Version', '${{ steps.setup-python.outputs.python-path }}');
results = runtests('brainstat_matlab', 'IncludeSubfolders', true);
if any([results.Failed]); error('%d MATLAB test(s) failed.', sum([results.Failed])); end
25 changes: 19 additions & 6 deletions .github/workflows/python_unittests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,37 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.11', '3.12']
# Python 3.12 cannot be exercised while pandas is pinned <2.1 (the
# abagen groupby/pkg_resources workaround), because pandas 2.0.x
# ships no wheels for 3.12 and cannot be built from source there.
# Re-add 3.12 once abagen is patched and the pin is lifted.
python-version: ['3.10', '3.11']
os: [ubuntu-latest, windows-latest, macos-latest]

runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install Dependencies
shell: bash
run: |
python -m pip install --upgrade pip setuptools
run: |
# setuptools 81+ no longer ships the legacy `pkg_resources` module on
# import, but transitive deps (e.g. abagen 0.1.3 in
# brainstat/context/genetics.py) still rely on it. Pin <81 so test
# collection succeeds; revisit when abagen drops the pkg_resources
# import.
python -m pip install --upgrade pip
python -m pip install 'setuptools<81' wheel
[[ -z $(git remote show origin | grep "Fetch URL:" | grep git@github.com:MICA-MNI/BrainStat.git) ]] && git config remote.upstream.fetch refs/heads/*:refs/remotes/upstream/* || git config remote.origin.fetch refs/heads/*:refs/origin/upstream/*
git fetch origin test-data-2.0
python -m pip install -e .[dev]
# Reinstall pinned setuptools in case the editable install pulled it
# back up to 81+.
python -m pip install --force-reinstall 'setuptools<81'

- name: Test with pytest
shell: bash
Expand Down
4 changes: 2 additions & 2 deletions brainstat/stats/SLM.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# type: ignore
""" Standard Linear regression models. """
import warnings
from cmath import sqrt
from math import sqrt
from pathlib import Path
from pprint import pformat
from typing import Optional, Sequence, Tuple, Union
Expand All @@ -17,7 +17,7 @@

from brainstat._typing import ArrayLike
from brainstat.datasets import fetch_parcellation, fetch_template_surface
from brainstat.datasets.base import fetch_template_surface, fetch_yeo_networks_metadata
from brainstat.datasets.base import fetch_yeo_networks_metadata
from brainstat.mesh.utils import _mask_edges, mesh_edges
from brainstat.stats.terms import FixedEffect, MixedEffect
from brainstat.stats.utils import apply_mask, undo_mask
Expand Down
7 changes: 4 additions & 3 deletions brainstat/stats/_t_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import math
import sys
import warnings

import numpy as np
Expand Down Expand Up @@ -39,7 +38,7 @@ def _t_test(self) -> None:
if np.square(np.dot(null_space(self.X).T, c)).sum() / np.square(
c
).sum() > np.spacing(1):
sys.exit("Contrast is not estimable :-(")
raise ValueError("Contrast is not estimable.")

else:
c = np.dot(pinvX, self.contrast)
Expand Down Expand Up @@ -173,6 +172,8 @@ def _t_test(self) -> None:
M[M_ef] = ef_duplicate[:, i]
M[M_sse] = sse_matrix.flatten()

self.t[i] = float(np.sqrt(-np.linalg.det(M) / det_sse / vf))
# Use .item() rather than float(): numpy >=2.0 raises
# TypeError when float() is applied to size-1 ndarrays.
self.t[i] = np.sqrt(-np.linalg.det(M) / det_sse / vf).item()

self.t = np.atleast_2d(self.t)
8 changes: 6 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ neurosynth
nibabel
nilearn>=0.7.0
nimare
numpy>=1.21
pandas>=1.3
# numpy and pandas are pinned together: pandas<2.1 is needed because abagen
# 0.1.3 still calls DataFrame.groupby(axis=...) which pandas 2.1 removed; the
# resulting pandas 2.0.x is built against numpy 1.x ABI, so numpy must also
# be <2 to avoid `numpy.dtype size changed` runtime errors.
numpy>=1.21,<2
pandas>=1.3,<2.1
scikit_learn>=1.0
scipy>=1.7
statsmodels
Expand Down
7 changes: 5 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@
"nibabel",
"nilearn>=0.7.0",
"nimare",
"numpy>=1.21",
"pandas>=1.3",
# numpy<2 is required so the pinned pandas<2.1 (whose 2.0.x C
# extensions were built against the numpy 1.x ABI) loads cleanly.
# See requirements.txt for the abagen-driven rationale.
"numpy>=1.21,<2",
"pandas>=1.3,<2.1",
"scikit_learn>=1.0",
"scipy>=1.7",
"templateflow",
Expand Down
Loading