-
Notifications
You must be signed in to change notification settings - Fork 26
Support NumPy 2.x and NiBabel 5.x #137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
KenyaOtsuka
merged 9 commits into
KamitaniLab:dev
from
KenyaOtsuka:chore/numpy-2x-nibabel-support
Jun 24, 2026
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
11662c5
chore: relax bdpy[mri] pins for NumPy 2.x and NiBabel 5.x
KenyaOtsuka ad94696
fix: support NiBabel 5.x and NumPy 2.x in bdpy/mri
KenyaOtsuka 2fdc424
fix: read MATLAB v7.3 files with h5py instead of hdf5storage (#106)
KenyaOtsuka 1de6e61
fix: replace SparseArray.save fallback with NumPy-2 h5py writer
KenyaOtsuka cb63440
chore: cap NumPy <2 on legacy Python (<3.10)
KenyaOtsuka f123d47
fix: avoid full-volume reads in load_array and _check_xyz
KenyaOtsuka 887258b
test: make v7.3 feature loader test deterministic
KenyaOtsuka 5fee425
fix: do not treat MATLAB_empty as Python.Empty in read_dataset
KenyaOtsuka 3053edb
chore: require NumPy-2-capable hdf5storage on Python >= 3.10
KenyaOtsuka File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| """Read MATLAB v7.3 (HDF5) ``.mat`` files with h5py. | ||
|
|
||
| bdpy historically relied on :func:`hdf5storage.loadmat` for the load path, but | ||
| that broke under NumPy 2.0, which removed ``np.unicode_`` that older hdf5storage | ||
| referenced (see issue #106). This module reimplements the *read* side on top of | ||
| h5py for the data layouts bdpy actually uses (dense numeric arrays and the | ||
| sparse-array struct). Saving still goes through hdf5storage. | ||
|
|
||
| This file is a part of BdPy. | ||
| """ | ||
|
|
||
| import h5py | ||
| import numpy as np | ||
| import scipy.io as sio | ||
|
|
||
| __all__ = ["load_array", "loadmat_key", "read_cell", "read_dataset"] | ||
|
|
||
|
|
||
| def read_dataset(dset: h5py.Dataset) -> np.ndarray: | ||
| """Read an h5py dataset, undoing MATLAB v7.3 / hdf5storage conventions. | ||
|
|
||
| MATLAB stores arrays in Fortran (column-major) order, so multi-dimensional | ||
| datasets are written transposed relative to NumPy's C order. hdf5storage | ||
| additionally records the original Python shape and empty-array flags as | ||
| ``Python.*`` attributes, which we honor to reproduce ``hdf5storage.loadmat``. | ||
| Only ``Python.Empty`` is special-cased; a bare ``MATLAB_empty`` (set by | ||
| MATLAB without ``Python.Shape``) is read through the normal path so that | ||
| empty non-scalar arrays such as ``(0, 3)`` keep their shape. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| dset : h5py.Dataset | ||
| Dataset to read. | ||
|
|
||
| Returns | ||
| ------- | ||
| numpy.ndarray | ||
| The array with its original shape restored. | ||
| """ | ||
| attrs = dset.attrs | ||
| if "Python.Empty" in attrs: | ||
| # Only Python.Empty (written by hdf5storage) implies a Python.Shape we | ||
| # can trust; fall back to the stored dataset shape if it is missing. A | ||
| # bare MATLAB_empty (written by MATLAB without Python.Shape) must NOT be | ||
| # treated this way -- np.empty(()) would collapse e.g. (0, 3) to 0-d -- | ||
| # so it falls through to the normal read/transpose path below. | ||
| shape = tuple(int(x) for x in attrs.get("Python.Shape", dset.shape)) | ||
| return np.empty(shape, dtype=dset.dtype) | ||
| arr = dset[()] | ||
| if "MATLAB_class" in attrs and isinstance(arr, np.ndarray) and arr.ndim >= 2: | ||
| arr = np.transpose(arr) | ||
| if "Python.Shape" in attrs: | ||
| arr = np.asarray(arr).reshape(tuple(int(x) for x in attrs["Python.Shape"])) | ||
| return np.asarray(arr) | ||
|
|
||
|
|
||
| def read_cell(f: h5py.File, dset: h5py.Dataset) -> list: | ||
| """Read a MATLAB cell array (or a plain matrix) into a list of arrays. | ||
|
|
||
| hdf5storage stores Python tuples/lists as MATLAB cell arrays, i.e. an object | ||
| dataset of HDF5 references to the individual elements. Files written by other | ||
| tools (e.g. MATLAB or Julia) may instead store the same information as a plain | ||
| 2-D matrix whose rows are the elements; both layouts are handled here. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| f : h5py.File | ||
| Open file, used to dereference cell-array element references. | ||
| dset : h5py.Dataset | ||
| The cell (object) dataset or a plain matrix. | ||
|
|
||
| Returns | ||
| ------- | ||
| list of numpy.ndarray | ||
| One array per cell element / matrix row. | ||
| """ | ||
| data = dset[()] | ||
| if isinstance(data, np.ndarray) and data.dtype == object: | ||
| return [read_dataset(f[ref]) for ref in data.ravel()] | ||
| arr = np.asarray(data) | ||
| return [arr[i] for i in range(arr.shape[0])] | ||
|
|
||
|
|
||
| def load_array(path: str, key: str) -> np.ndarray: | ||
| """Load a single dense numeric array from a v7.3 ``.mat`` file. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| path : str | ||
| Path to the ``.mat`` file. | ||
| key : str | ||
| Variable name to load. | ||
|
|
||
| Returns | ||
| ------- | ||
| numpy.ndarray | ||
| The loaded array. | ||
| """ | ||
| with h5py.File(path, "r") as f: | ||
| return read_dataset(f[key]) | ||
|
|
||
|
|
||
| def loadmat_key(path: str, key: str) -> np.ndarray: | ||
| """Load one variable from a ``.mat`` file, handling both v5 and v7.3. | ||
|
|
||
| MATLAB v5 (and earlier) files are read with :func:`scipy.io.loadmat`; v7.3 | ||
| (HDF5) files, which scipy cannot read, fall back to the h5py reader. This | ||
| preserves v5 support while avoiding hdf5storage on the load path (which | ||
| breaks under NumPy 2.0). | ||
|
|
||
| Parameters | ||
| ---------- | ||
| path : str | ||
| Path to the ``.mat`` file. | ||
| key : str | ||
| Variable name to load. | ||
|
|
||
| Returns | ||
| ------- | ||
| numpy.ndarray | ||
| The loaded array. | ||
| """ | ||
| try: | ||
| array = sio.loadmat(path)[key] | ||
| except (NotImplementedError, ValueError): | ||
| return load_array(path, key) | ||
| return np.asarray(array) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Addressed in f123d47.