NumPy 2.6.0 Release Notes#

Highlights#

We’ll choose highlights for this release near the end of the release cycle.

New functions#

New function numpy.top_k#

A new function np.top_k(array, k, axis=..., mode=..., sorted=...) was added, which returns the largest/smallest k values of an array along a given axis.

(gh-31659)

Deprecations#

numpy.linalg.lapack_lite is deprecated#

Importing numpy.linalg.lapack_lite is deprecated. The module is an internal implementation detail of numpy.linalg and was never intended as a public API. Users that need LAPACK or BLAS routines should use scipy.linalg.lapack or scipy.linalg.blas instead.

(gh-32015)

Expired deprecations#

  • The deprecated 'full', 'f', 'economic', and 'e' modes of numpy.linalg.qr have been removed. These were deprecated in NumPy 1.8. Use 'reduced' instead of 'full'/'f', and 'raw' instead of 'economic'/'e'.

    (gh-31387)

  • The numpy.typing.mypy_plugin mypy plugin (deprecated since NumPy 2.3) did not affect type-safety and has been removed in favor of platform- and type-checker-agnostic static typing.

    (gh-31931)

Compatibility notes#

numpy.insert out-of-bounds index detection#

numpy.insert now consistently raises IndexError for any out-of-bounds index, including when out-of-bounds and in-bounds indices are mixed in the same call. Previously such cases could silently succeed or produce incorrect results.

(gh-31782)

ufunc.reduce on a multi-output ufunc now raises TypeError#

Calling reduce on a ufunc with more than one output that does not register a reduction loop now raises a TypeError instead of a ValueError. Of NumPy’s own ufuncs only numpy.divmod is affected.

(gh-31816)

C API changes#

NumPy DTypes are heap-allocated and can use PyType_FromMetaType#

Most of NumPy DType classes, except np.dtype itself, are now heap-allocated types and downstream DTypes can now create their own DType classes using PyType_FromMetaType.

DType authors using PyArrayInitDTypeMeta_FromSpec may notice this if the DType has incorrect reference counting, since DTypes are no longer immortal. In theory this also means that you cannot subclass abstract DTypes anymore unless converting your DType to a heap-allocated type itself.

(gh-31364)

PyArray_StringDTypeObject is opaque under the abi3t stable ABI#

The PyArray_StringDTypeObject was accidentally exposed in NumPy 2.5 when targeting the free-threading-compatible stable ABI (Py_TARGET_ABI3T). PyArray_StringDTypeObject is now an opaque struct: extensions compiled that way cannot access its fields, since the struct layout depends on the size of the object header. Any code that accessed PyArray_StringDTypeObject fields in an abi3t build would have crashed, so we are making this API change in a bugfix release.

The NpyString allocator API remains usable by passing the descriptor object pointer, e.g. NpyString_acquire_allocator((PyArray_StringDTypeObject *)descr).

(gh-31771)

New mechanism to register reduction loops to ufuncs for multi-output reductions#

Ufuncs with more than one output can now support reduce by registering a dedicated reduction loop (and, optionally, per-output identities) on their ArrayMethod. The reduction returns one array per output. See Adding a reduction loop to a ufunc for a worked example.

(gh-31816)

New Features#

Recognize ISO Fortran Environment kinds in F2Py#

This adds the int16, int32, and int64 integer kinds and the real32 and real64 real kinds from the ISO Fortran Environment module to F2Py.

(gh-28574)

New descending keyword argument for numpy.partition and numpy.argpartition#

Users can now pass the descending=True keyword argument to numpy.partition and numpy.argpartition to partition and argpartition arrays in descending order. NaN values, if present, are partitioned to the end of the array in both ascending and descending sorts. This feature is available for all built-in dtypes except void and generic. Note that SIMD optimizations for partitioning are currently not available for descending order, so performance may be slower.

(gh-31511)

DType partitioning and argpartitioning supports the ArrayMethod API#

User-defined dtypes can now implement custom partitioning and argpartitioning using the ArrayMethod API in a fashion similar to sorting and argsorting. These methods are used by numpy.partition and numpy.argpartition when called with arrays of the user-defined dtype.

The partitioning and argpartitioning methods are registered by passing the arraymethod specs that implement the operations to the PyUFunc_AddLoopsFromSpecs function. See the ArrayMethod API documentation for details.

(gh-31614)

Add DLPack support to NumPy scalars#

scalar.__dlpack__ and scalar.__dlpack_device__ methods have been added to NumPy scalars to allow exporting them to DLPack, similarly to ndarrays.

(gh-32029)

numpy.ma.unwrap has been added#

numpy.ma.unwrap is the mask-aware equivalent of numpy.unwrap. It skips over the masked elements, computing each correction from the delta to the closest preceding unmasked element rather than from the data underlying the mask, and carries the mask through to the output.

(gh-32091)

Improvements#

  • StringDType comparisons now correctly handle embedded NULL bytes.

    (gh-31662)

numpy.common_type now raises a clear error for non-array input#

Passing a dtype or scalar type to numpy.common_type, such as np.common_type(np.dtype("f4")), used to raise a confusing AttributeError. It now raises a TypeError that points to numpy.result_type and numpy.promote_types, which are the tools meant for combining dtypes and scalar types.

(gh-30890)

Object array sorting supports descending=True and consistently sorts NaN-like objects#

np.sort and np.argsort with arrays of dtype object now support passing descending=True to sort in descending order. Objects that compare as not equal to themselves (obj != obj), such as NaN-like objects, are considered unordered and are sorted to the end of the array, regardless of the value of descending.

(gh-31431)

np.linspace no longer returns NaN for equal infinite endpoints#

np.linspace(np.inf, np.inf, N) (and -np.inf) now correctly returns an array filled with inf instead of nan.

(gh-31620)

numpy.unwrap is now implemented as a generalized ufunc and preserves array subclasses#

The core of numpy.unwrap is now a generalized ufunc in C++ (signature (n),(),()->(n)) covering the floating point and signed integer dtypes while performing the operation in a single pass without the intermediate arrays the previous Python implementation had to allocate. As a result, numpy.unwrap now preserves the ndarray subclasses rather than always returning a base ndarray.

(gh-31848)

DataSource accepts path-like local paths#

numpy.lib._datasource.DataSource and Repository now accept os.PathLike local paths in the same places that accepted string paths. This includes open, exists, and abspath methods, as well as the module-level numpy.lib._datasource.open helper.

(gh-31906)

f2py handles platform-specific separators in --include-paths#

f2py --include-paths now splits include directories using the platform path separator, fixing parsing of Windows paths containing drive letters.

(gh-31934)

Ensure F2Py defines required typedefs in generated C wrapper#

Ensure F2Py includes the typedefs required for kinds in the ISO C Binding module and the ISO Fortran Environment module in the generated C wrapper.

This also allows the int8 integer kind from the ISO Fortran Environment module and the c_int8_t integer kind from the ISO C Binding module to work.

(gh-32043)

Performance improvements and changes#

Faster ufunc calls through inner-loop caching#

Ufuncs implemented through the legacy ufunc API (which includes most ufuncs provided by NumPy itself) now cache the selected inner-loop function instead of looking it up on every call. This speeds up ufunc calls, most notably for scalar and small-array inputs (roughly 10% faster). Note that as a consequence, loops must be registered through the official API (e.g. PyUFunc_ReplaceLoopBySignature); directly modifying the functions member of a ufunc no longer takes effect.

(gh-31068)

Faster selected numpy.pad modes for zero-width axes#

numpy.pad is now faster for axes with pad width (0, 0) when using mode="linear_ramp" or one of the statistic modes "maximum", "mean", "median", and "minimum". These modes now skip unnecessary mode-specific work for axes where no values are added.

(gh-31791)

Faster reductions on small arrays#

numpy.sum, numpy.prod, numpy.min, numpy.max, numpy.any, and numpy.all are now faster for small arrays. This reduces the Python-level overhead of calling these reductions, which is most noticeable when the reduction itself is cheap.

(gh-31845)

Faster reductions for exact ndarrays#

numpy.sum, numpy.prod, numpy.min, numpy.max, numpy.amin, numpy.amax, numpy.any, and numpy.all now avoid Python dispatch overhead for exact numpy.ndarray inputs, reducing call times for benchmarked small-array reductions by approximately 30%-50%. Subclasses and custom array types continue to use the original __array_function__ dispatch path.

(gh-32041)

Typing improvements and changes#

Promotion-aware ufunc return types#

All ufuncs in the main namespace are now individually annotated, instead of sharing one generic numpy.ufunc type. Their __call__, reduce, accumulate, reduceat, outer, and at methods now follow NumPy’s promotion rules, so that the output dtype is inferred instead of Any.

import numpy as np
import numpy.typing as npt

x: npt.NDArray[np.float32]
y: npt.NDArray[np.int16]

reveal_type(np.sqrt(y))
# before: npt.NDArray[Any]
# after:  npt.NDArray[np.float32]

reveal_type(np.multiply(x, y))
# before: npt.NDArray[Any]
# after:  npt.NDArray[np.float32]

This took 44 pull requests and over 20,000 lines of hand-written code, spread over 1,905 overloads.

(gh-32198)

Changes#

  • The minimum supported GCC version has been updated from 9.3.0 to 10.3.0

    (gh-31843)

  • Structured dtypes and subarray dtypes containing StringDType now consistently raise TypeError. Formerly some structured dtpes containing StringDType could be created, but this could lead to data corruption or crashes on array data accesses.

    (gh-32027)

numpy.unwrap behavior changes for edge cases#

An explicitly typed discont argument passed to numpy.unwrap wider than the result dtype is now compared at the result dtype rather than promoting. This may change the result of numpy.unwrap by ~1 ULP.

Calling numpy.unwrap with an unsigned integer period that cannot represent the values needed internally now raises a TypeError (“no loop found” ufunc error, reporting the mismatched dtypes) instead of the OverflowError raised by the previous Python implementation.

(gh-9959)

f2py line wrapping no longer produces invalid continuation lines#

Lines exceeding the column limit in f2py-generated Fortran wrappers are now split correctly for both fixed-form and free-form source.

(gh-30967)

f2py allocatable character arrays now work correctly#

Allocatable character arrays in f2py-wrapped Fortran 90 modules no longer raise ValueError when accessed after allocation.

(gh-30971)

NumPy’s internal memory allocations now use PyMem_RawMalloc#

NumPy’s internal memory allocations now use PyMem_RawMalloc instead of malloc and can be tracked by tracemalloc.

(gh-31503)

ufunc.outer now follows NEP 50 promotion#

Python scalars passed to ufunc.outer were converted to arrays and thus treated as strongly typed, unlike for a normal ufunc call. They are now weak, so that e.g. np.add.outer(1., np.zeros(3, dtype="float32")) returns a float32 rather than a float64 array. As a consequence, huge Python integers now raise an OverflowError here as they do for ufunc.__call__.

(gh-32090)

ufunc.at now follows NEP 50 promotion#

Python scalars passed as the value operand of ufunc.at were converted to arrays and thus treated as strongly typed, unlike for a normal ufunc call. They are now weak, so the operation is computed in the promoted dtype of the target array: e.g. np.add.at(arr, idx, 1) with a uint64 array now uses the uint64 loop rather than float64, which rounded large values. As a consequence, out-of-bounds Python integers now raise an OverflowError here, as they do for ufunc.__call__; previously they wrapped silently (e.g. adding -1 to a uint8 array produced 255).

(gh-32094)