Skip to content

Commit b78f9d8

Browse files
authored
Merge branch 'main' into TestAbsentMaskWarning
2 parents b11dd8d + 72ae039 commit b78f9d8

34 files changed

+609
-175
lines changed

.github/workflows/benchmark.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Benchmark
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
- maint/*
8+
pull_request:
9+
branches:
10+
- main
11+
- maint/*
12+
# Allow job to be triggered manually from GitHub interface
13+
workflow_dispatch:
14+
15+
defaults:
16+
run:
17+
shell: bash
18+
19+
concurrency:
20+
group: ${{ github.workflow }}-${{ github.ref }}
21+
cancel-in-progress: true
22+
23+
permissions:
24+
contents: read
25+
26+
jobs:
27+
benchmark:
28+
name: Linux
29+
runs-on: ubuntu-latest
30+
defaults:
31+
run:
32+
shell: bash
33+
strategy:
34+
fail-fast: false
35+
matrix:
36+
python-version: [ '3.11' ]
37+
38+
steps:
39+
- name: Set up system
40+
uses: actions/checkout@v4
41+
with:
42+
fetch-depth: 0
43+
- name: Set up Python ${{ matrix.python-version }}
44+
uses: actions/setup-python@v5
45+
with:
46+
python-version: ${{ matrix.python-version }}
47+
- name: Install dependencies
48+
run: |
49+
python -m pip install --upgrade pip
50+
pip install .[antsopt,benchmark]
51+
- name: Set threading parameters for reliable benchmarking
52+
run: |
53+
export OPENBLAS_NUM_THREADS=1
54+
export MKL_NUM_THREADS=1
55+
export OMP_NUM_THREADS=1
56+
- name: Run benchmarks
57+
run: |
58+
asv machine --yes --config benchmarks/asv.conf.json
59+
asv run --config benchmarks/asv.conf.json --show-stderr

.github/workflows/test.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ jobs:
7070
with:
7171
path: /home/runner/nifreeze-tests/
7272
key: data-v0
73+
- name: Install TeX Live
74+
run: |
75+
sudo apt-get update
76+
sudo apt install texlive texlive-latex-extra texlive-fonts-recommended cm-super dvipng
7377
- name: Install tox
7478
run: |
7579
python -m pip install --upgrade pip
@@ -91,8 +95,7 @@ jobs:
9195
continue-on-error: true
9296
strategy:
9397
matrix:
94-
check: ['spellcheck']
95-
98+
check: ['spellcheck', 'typecheck']
9699
steps:
97100
- uses: actions/checkout@v4
98101
- name: Install the latest version of uv

benchmarks/README.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
.. -*- rst -*-
2+
3+
===================
4+
NiFreeze benchmarks
5+
===================
6+
Benchmarking NiFreeze with Airspeed Velocity.
7+
8+
Usage
9+
-----
10+
Airspeed Velocity manages building and Python environments by itself,
11+
unless told otherwise.
12+
To run the benchmarks, you do not need to install
13+
a development version of *NiFreeze* on your current
14+
*Python* environment.
15+
16+
To run all benchmarks for the latest commit, navigate to *NiFreeze*'s root
17+
``benchmarks`` directory and execute::
18+
19+
asv run
20+
21+
For testing benchmarks locally, it may be better to run these without
22+
replications::
23+
24+
export REGEXP="bench.*Ufunc"
25+
asv run --dry-run --show-stderr --python=same --quick -b $REGEXP
26+
27+
All of the commands above display the results in plain text in the console,
28+
and the results are not saved for comparison with future commits.
29+
For greater control, a graphical view, and to have results saved for future
30+
comparisons, you can run ASV as follows to record results and generate
31+
the HTML reports::
32+
33+
asv run --skip-existing-commits --steps 10 ALL
34+
asv publish
35+
asv preview
36+
37+
More on how to use ``asv`` can be found in the `ASV documentation`_.
38+
Command-line help is available as usual via ``asv --help`` and
39+
``asv run --help``.
40+
41+
.. _ASV documentation: https://asv.readthedocs.io/

benchmarks/asv.conf.json

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
{
2+
// The version of the config file format. Do not change, unless
3+
// you know what you are doing.
4+
"version": 1,
5+
6+
// The name of the project being benchmarked
7+
"project": "nifreeze",
8+
9+
// The project's homepage
10+
"project_url": "https://www.nipreps.org/nifreeze/",
11+
12+
// The URL or local path of the source code repository for the
13+
// project being benchmarked
14+
"repo": "..",
15+
16+
// List of branches to benchmark. If not provided, defaults to "master"
17+
// (for git) or "tip" (for mercurial).
18+
"branches": ["HEAD"],
19+
20+
"build_command": [
21+
"python -m build --wheel -o {build_cache_dir} {build_dir}"
22+
],
23+
24+
// The DVCS being used. If not set, it will be automatically
25+
// determined from "repo" by looking at the protocol in the URL
26+
// (if remote), or by looking for special directories, such as
27+
// ".git" (if local).
28+
"dvcs": "git",
29+
30+
// The tool to use to create environments. May be "conda",
31+
// "virtualenv" or other value depending on the plugins in use.
32+
// If missing or the empty string, the tool will be automatically
33+
// determined by looking for tools on the PATH environment
34+
// variable.
35+
"environment_type": "virtualenv",
36+
37+
// the base URL to show a commit for the project.
38+
"show_commit_url": "https://github.com/nipreps/nifreeze/commit/",
39+
40+
// The Pythons you'd like to test against. If not provided, defaults
41+
// to the current version of Python used to run `asv`.
42+
// "pythons": ["3.12"],
43+
44+
// The matrix of dependencies to test. Each key is the name of a
45+
// package (in PyPI) and the values are version numbers. An empty
46+
// list indicates to just test against the default (latest)
47+
// version.
48+
"matrix": {
49+
"dipy": [],
50+
"nipype": [],
51+
"nest-asyncio": [],
52+
"nitransforms": [],
53+
"numpy": [],
54+
"scikit_learn": [],
55+
"scipy": []
56+
},
57+
58+
// The directory (relative to the current directory) that benchmarks are
59+
// stored in. If not provided, defaults to "benchmarks"
60+
"benchmark_dir": "benchmarks",
61+
62+
// The directory (relative to the current directory) to cache the Python
63+
// environments in. If not provided, defaults to "env"
64+
"env_dir": "env",
65+
66+
67+
// The directory (relative to the current directory) that raw benchmark
68+
// results are stored in. If not provided, defaults to "results".
69+
"results_dir": "results",
70+
71+
// The directory (relative to the current directory) that the html tree
72+
// should be written to. If not provided, defaults to "html".
73+
"html_dir": "html",
74+
75+
// The number of characters to retain in the commit hashes.
76+
// "hash_length": 8,
77+
78+
// `asv` will cache wheels of the recent builds in each
79+
// environment, making them faster to install next time. This is
80+
// number of builds to keep, per environment.
81+
"build_cache_size": 8,
82+
83+
// The commits after which the regression search in `asv publish`
84+
// should start looking for regressions. Dictionary whose keys are
85+
// regexps matching to benchmark names, and values corresponding to
86+
// the commit (exclusive) after which to start looking for
87+
// regressions. The default is to start from the first commit
88+
// with results. If the commit is `null`, regression detection is
89+
// skipped for the matching benchmark.
90+
//
91+
// "regressions_first_commits": {
92+
// "some_benchmark": "352cdf", // Consider regressions only after this commit
93+
// "another_benchmark": null, // Skip regression detection altogether
94+
// }
95+
96+
// Maximum time in seconds that a benchmark is allowed to run before it is terminated.
97+
"default_benchmark_timeout": 240
98+
}

benchmarks/benchmarks/__init__.py

Whitespace-only changes.

benchmarks/benchmarks/bench_model.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
# emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
2+
# vi: set ft=python sts=4 ts=4 sw=4 et:
3+
#
4+
# Copyright The NiPreps Developers <[email protected]>
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
# We support and encourage derived works from this project, please read
19+
# about our expectations at
20+
#
21+
# https://www.nipreps.org/community/licensing/
22+
#
23+
"""Benchmarking for nifreeze's models."""
24+
25+
from abc import ABC
26+
27+
import dipy.data as dpd
28+
import nibabel as nb
29+
import numpy as np
30+
from dipy.core.gradients import get_bval_indices
31+
from dipy.io import read_bvals_bvecs
32+
from dipy.segment.mask import median_otsu
33+
from scipy.ndimage import binary_dilation
34+
from skimage.morphology import ball
35+
36+
from nifreeze.model.gpr import DiffusionGPR, SphericalKriging
37+
38+
39+
class DiffusionGPRBenchmark(ABC):
40+
def __init__(self):
41+
self._estimator = None
42+
self._X_train = None
43+
self._y_train = None
44+
self._X_test = None
45+
self._y_test = None
46+
47+
def setup(self, *args, **kwargs):
48+
beta_a = 1.38
49+
beta_l = 1 / 2.1
50+
alpha = 0.1
51+
disp = True
52+
optimizer = None
53+
self.make_estimator((beta_a, beta_l, alpha, disp, optimizer))
54+
self.make_data()
55+
56+
def make_estimator(self, params):
57+
beta_a, beta_l, alpha, disp, optimizer = params
58+
kernel = SphericalKriging(beta_a=beta_a, beta_l=beta_l)
59+
self._estimator = DiffusionGPR(
60+
kernel=kernel,
61+
alpha=alpha,
62+
disp=disp,
63+
optimizer=optimizer,
64+
)
65+
66+
def make_data(self):
67+
name = "sherbrooke_3shell"
68+
69+
dwi_fname, bval_fname, bvec_fname = dpd.get_fnames(name=name)
70+
dwi_data = nb.load(dwi_fname).get_fdata()
71+
bvals, bvecs = read_bvals_bvecs(bval_fname, bvec_fname)
72+
73+
_, brain_mask = median_otsu(dwi_data, vol_idx=[0])
74+
brain_mask = binary_dilation(brain_mask, ball(8))
75+
76+
bval = 1000
77+
indices = get_bval_indices(bvals, bval, tol=20)
78+
79+
bvecs_shell = bvecs[indices]
80+
shell_data = dwi_data[..., indices]
81+
dwi_vol_idx = len(indices) // 2
82+
83+
# Prepare a train/test mask (False for all directions except the left-out where it's true)
84+
train_test_mask = np.zeros(bvecs_shell.shape[0], dtype=bool)
85+
train_test_mask[dwi_vol_idx] = True
86+
87+
# Generate train/test bvecs
88+
self._X_train = bvecs_shell[~train_test_mask, :]
89+
self._X_test = bvecs_shell[train_test_mask, :]
90+
91+
# Select voxels within brain mask
92+
y = shell_data[brain_mask]
93+
94+
# Generate train/test data
95+
self._y_train = y[:, ~train_test_mask]
96+
self._y_test = y[:, train_test_mask]
97+
98+
def time_fit(self, *args):
99+
self._estimator = self._estimator.fit(self._X_train, self._y_train.T)
100+
101+
def time_predict(self):
102+
self._estimator.predict(self._X_test)

docs/conf.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
"nipype",
5555
"nitime",
5656
"nitransforms",
57-
"numpy",
5857
"pandas",
5958
"scipy",
6059
"seaborn",
@@ -154,20 +153,20 @@
154153

155154
# -- Options for LaTeX output ------------------------------------------------
156155

157-
latex_elements = {
158-
# The paper size ('letterpaper' or 'a4paper').
159-
#
160-
# 'papersize': 'letterpaper',
161-
# The font size ('10pt', '11pt' or '12pt').
162-
#
163-
# 'pointsize': '10pt',
164-
# Additional stuff for the LaTeX preamble.
165-
#
166-
# 'preamble': '',
167-
# Latex figure (float) alignment
168-
#
169-
# 'figure_align': 'htbp',
170-
}
156+
# latex_elements = {
157+
# # The paper size ('letterpaper' or 'a4paper').
158+
# #
159+
# # 'papersize': 'letterpaper',
160+
# # The font size ('10pt', '11pt' or '12pt').
161+
# #
162+
# # 'pointsize': '10pt',
163+
# # Additional stuff for the LaTeX preamble.
164+
# #
165+
# # 'preamble': '',
166+
# # Latex figure (float) alignment
167+
# #
168+
# # 'figure_align': 'htbp',
169+
# }
171170

172171
# Grouping the document tree into LaTeX files. List of tuples
173172
# (source start file, target name, title,

docs/notebooks/bold_realignment.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"WORKDIR = Path.home() / \"tmp\" / \"nifreezedev\" / \"ismrm25\"\n",
3535
"WORKDIR.mkdir(parents=True, exist_ok=True)\n",
3636
"\n",
37-
"OUTPUT_DIR = Path(\"/data/derivatives\") / \"nifreeze-ismrm25-exp2\"\n",
37+
"OUTPUT_DIR = Path.home() / \"tmp\" / \"nifreezedev\" / \"ismrm25\" / \"nifreeze-ismrm25-exp2\"\n",
3838
"OUTPUT_DIR.mkdir(exist_ok=True, parents=True)"
3939
]
4040
},

0 commit comments

Comments
 (0)