|
| 1 | +# Changelog |
| 2 | + |
| 3 | +All notable changes to BrainStat are recorded here. The project adopts |
| 4 | +[Semantic Versioning](https://semver.org/spec/v2.0.0.html). |
| 5 | + |
| 6 | +## [0.6.0] — 2026-05 |
| 7 | + |
| 8 | +This release re-greens CI after a long stretch of upstream-driven breakage, |
| 9 | +fixes a cluster of high-severity correctness bugs in the statistics core, |
| 10 | +adds the FreeSurfer Destrieux atlas to `fetch_parcellation`, hardens the |
| 11 | +Neurosynth fetcher, and adopts a literature-aligned default for BigBrain |
| 12 | +histology profiles. It also closes the longest-standing maintenance items |
| 13 | +in the issue tracker. |
| 14 | + |
| 15 | +### Backwards-incompatible changes |
| 16 | + |
| 17 | +- **`brainstat.context.histology.read_histology_profile` now inverts BigBrain |
| 18 | + intensities by default** (`invert=True`) so that higher = darker = more |
| 19 | + cell-dense, matching Paquola et al. 2021 (eLife) and most downstream |
| 20 | + BigBrain analyses. Pass `invert=False` to recover the previous behaviour. |
| 21 | + MPC and gradient computations are correlation-based and therefore |
| 22 | + invariant to this flip; only direct inspection of profile intensities is |
| 23 | + affected. (#274) |
| 24 | +- **`SLM.__init__` now coerces `contrast` to numeric and `mask` to |
| 25 | + boolean.** Pandas Series of object/categorical dtype now raise a clear |
| 26 | + `TypeError` instead of producing the cryptic `can't multiply sequence by |
| 27 | + non-int of type 'float'` deep inside `_t_test`. Integer 0/1 masks (the |
| 28 | + natural shape of `nib.load(...).get_fdata().astype(int)`) are now |
| 29 | + converted to bool rather than degenerating into fancy indexing inside |
| 30 | + `_fdr`. (#342) |
| 31 | +- **Pin numpy<2 and pandas<2.1.** `abagen 0.1.3` still imports |
| 32 | + `pkg_resources` and calls `DataFrame.groupby(axis=...)`; until upstream is |
| 33 | + patched, BrainStat must stay on the numpy 1.x ABI to avoid runtime |
| 34 | + binary-incompatibility errors. Will be lifted once abagen ships a |
| 35 | + compatible release. |
| 36 | + |
| 37 | +### Bug fixes — statistics core |
| 38 | + |
| 39 | +- `SLM._surfstat_to_brainstat_rft`: fix off-by-one in Yeo7 indexing. |
| 40 | + `peak_clus` returns 1-based vertex IDs (a SurfStat carry-over); these |
| 41 | + were being used to index a 0-based numpy array, raising `IndexError` |
| 42 | + when a peak landed on the final mesh vertex and silently mis-labelling |
| 43 | + Yeo networks for every other peak. (#347) |
| 44 | +- `SLM.py`: `from cmath import sqrt` was importing the **complex** square |
| 45 | + root; switched to `math.sqrt`. The expression was always real but the |
| 46 | + surrounding code would have silently propagated complex values had the |
| 47 | + branch ever been negative. |
| 48 | +- `SLM.py`: removed a duplicate `fetch_template_surface` import. |
| 49 | +- `_t_test.py`: `sys.exit("Contrast is not estimable :-(")` would kill the |
| 50 | + host Python process when invoked from a notebook or pipeline. Replaced |
| 51 | + with `raise ValueError(...)`. |
| 52 | +- `_t_test.py`: replace `float(...)` with `.item()` on the t-statistic |
| 53 | + computation. `numpy>=2.0` raises `TypeError: only 0-dimensional arrays |
| 54 | + can be converted to Python scalars` for `float()` on size-1 ndarrays. |
| 55 | + |
| 56 | +### New features |
| 57 | + |
| 58 | +- `brainstat.datasets.fetch_parcellation(template, "destrieux", ...)` |
| 59 | + now returns the FreeSurfer `aparc.a2009s` (Destrieux 2009) |
| 60 | + parcellation, sourced via |
| 61 | + `nilearn.datasets.fetch_atlas_surf_destrieux`. Native `fsaverage5`; |
| 62 | + other `fsaverage*` templates resampled with nearest-neighbour |
| 63 | + interpolation. `fslr32k` is rejected (FreeSurfer-specific). (#343) |
| 64 | +- The `surf=None + correction='rft'` error message now points users at |
| 65 | + `correction='fdr'` for volumetric data instead of just refusing. |
| 66 | + |
| 67 | +### Maintenance |
| 68 | + |
| 69 | +- **CI fully restored.** Both Python and MATLAB suites had been red on |
| 70 | + `master` for a long stretch. Root causes addressed: |
| 71 | + - `setuptools >= 81` no longer ships `pkg_resources`, breaking |
| 72 | + `abagen` import → pinned `setuptools<81` in CI and reinstalled it |
| 73 | + after the editable install. |
| 74 | + - `numpy.dtype size changed` from mixing pinned `pandas<2.1` (built |
| 75 | + against numpy 1.x ABI) with `numpy>=2` → pinned `numpy<2` to keep |
| 76 | + the stack consistent. |
| 77 | + - `pandas 2.0.x` has no Python 3.12 wheels → temporarily dropped 3.12 |
| 78 | + from the matrix until abagen is patched and the pin can be lifted. |
| 79 | + - `MATLAB:Python:PythonUnavailable` from the precomputed-pickle tests |
| 80 | + → bumped to `matlab-actions/setup-matlab@v2` + |
| 81 | + `matlab-actions/run-command@v2`, configure `pyenv()` before |
| 82 | + `runtests`, add explicit `addpath(genpath('brainstat_matlab'))`. |
| 83 | + - GitHub Actions versions: `actions/checkout@v4`, |
| 84 | + `actions/setup-python@v5`. (PR #379) |
| 85 | +- **Drop `# type: ignore` from `SLM.py` and `_multiple_comparisons.py`.** |
| 86 | + The class-body imports that were tripping |
| 87 | + [python/mypy#10521](https://github.com/python/mypy/issues/10521) have |
| 88 | + been lifted to module level and bound as class attributes (`_linear_model |
| 89 | + = _linear_model`, etc.). mypy now runs to completion on both files |
| 90 | + instead of crashing. The pre-existing untyped-code errors that the |
| 91 | + blanket ignore was hiding are surfaced but tracked separately; the |
| 92 | + `mypy` CI job remains commented out pending that triage. (#199) |
| 93 | +- **MATLAB R2019b/R2020a compatibility.** `peak_clus.column_vector` |
| 94 | + replaced the `mustBeVector` validator (introduced in R2020b) with an |
| 95 | + explicit `isvector` check. (#285) |
| 96 | +- **Resilient Neurosynth download.** `meta_analytic_decoder/fetch_neurosynth_data` |
| 97 | + validates the downloaded zip via `java.util.zip.ZipFile` before |
| 98 | + `unzip`, retries up to 3 times, cleans up partial zips between |
| 99 | + attempts, and verifies the post-extract file count. Truncated |
| 100 | + downloads no longer leave the toolbox in a permanently broken state. |
| 101 | + (#341) |
| 102 | + |
| 103 | +### Issues closed |
| 104 | + |
| 105 | +- #199 — Remove `# type: ignore` from `SLM` class |
| 106 | +- #274 — Flip the bits on the histological profiles |
| 107 | +- #285 — `column_vector` uses `mustBeVector` from MATLAB 2020b+ |
| 108 | +- #341 — Neurosynth data failed to get fetched |
| 109 | +- #342 — Volumetric mixed effects (input contract bugs; cluster-level |
| 110 | + RFT/TFCE on volumes remains tracked separately as a feature request) |
| 111 | +- #343 — Atlas coverage for FreeSurfer GLM workflows (Destrieux added; |
| 112 | + hosted Desikan-Killiany remains tracked for a follow-up) |
| 113 | +- #347 — Retrieval of yeo7 information has off-by-1 error |
| 114 | + |
| 115 | +### Known follow-ups |
| 116 | + |
| 117 | +- **Lift the numpy/pandas/3.12 pins** once `abagen` drops `pkg_resources` |
| 118 | + and the `groupby(axis=)` call. |
| 119 | +- **Re-enable the commented-out `mypy` CI job** once the ~380 latent |
| 120 | + typing errors uncovered by removing `# type: ignore` are triaged. |
| 121 | +- **Volumetric cluster-level RFT/TFCE** for `SLM` — present in SurfStat, |
| 122 | + not yet ported. |
| 123 | +- **Hosted Desikan-Killiany (`aparc`) atlas** in `fetch_parcellation` |
| 124 | + — needs a stable mirror (OSF / Zenodo) for the `.annot` files. |
| 125 | + |
| 126 | +## [0.5.2] — 2026-01 |
| 127 | + |
| 128 | +Hotfixes only; no formal changelog was kept for this release. |
| 129 | + |
| 130 | +## [0.5.1] and earlier |
| 131 | + |
| 132 | +See `git log` and the merged-PR history for releases prior to the |
| 133 | +introduction of this changelog. |
0 commit comments