From 38be4cbd32ce4192e8791416382d058948627e91 Mon Sep 17 00:00:00 2001 From: Charles Harris Date: Sun, 15 Aug 2021 13:59:16 -0600 Subject: [PATCH 01/14] REL: prepare 1.21.x for further development --- doc/source/release.rst | 1 + doc/source/release/1.21.3-notes.rst | 45 +++++++++++++++++++++++++++++ pavement.py | 2 +- 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 doc/source/release/1.21.3-notes.rst diff --git a/doc/source/release.rst b/doc/source/release.rst index 99856e990d96..bef6e8e4d6f7 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -5,6 +5,7 @@ Release Notes .. toctree:: :maxdepth: 3 + 1.21.3 1.21.2 1.21.1 1.21.0 diff --git a/doc/source/release/1.21.3-notes.rst b/doc/source/release/1.21.3-notes.rst new file mode 100644 index 000000000000..7db85672af26 --- /dev/null +++ b/doc/source/release/1.21.3-notes.rst @@ -0,0 +1,45 @@ +.. currentmodule:: numpy + +========================== +NumPy 1.21.3 Release Notes +========================== + + +Highlights +========== + + +New functions +============= + + +Deprecations +============ + + +Future Changes +============== + + +Expired deprecations +==================== + + +Compatibility notes +=================== + + +C API changes +============= + + +New Features +============ + + +Improvements +============ + + +Changes +======= diff --git a/pavement.py b/pavement.py index 572e9d8f6a24..e33695d99d5e 100644 --- a/pavement.py +++ b/pavement.py @@ -38,7 +38,7 @@ #----------------------------------- # Path to the release notes -RELEASE_NOTES = 'doc/source/release/1.21.2-notes.rst' +RELEASE_NOTES = 'doc/source/release/1.21.3-notes.rst' #------------------------------------------------------- From 35356c88aaf80d2d1e72036ab3bcd03923088d33 Mon Sep 17 00:00:00 2001 From: Bas van Beek Date: Mon, 31 May 2021 11:09:40 +0200 Subject: [PATCH 02/14] ENH: Add dtype-support to 3 `generic`/`ndarray` methods * `astype` * `getfield` * `view` --- numpy/__init__.pyi | 125 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 106 insertions(+), 19 deletions(-) diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index d07e32f1c276..dfec6af94b74 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -1254,20 +1254,9 @@ class _ArrayOrScalarCommon: def __deepcopy__(self: _ArraySelf, __memo: Optional[dict] = ...) -> _ArraySelf: ... def __eq__(self, other): ... def __ne__(self, other): ... - def astype( - self: _ArraySelf, - dtype: DTypeLike, - order: _OrderKACF = ..., - casting: _Casting = ..., - subok: bool = ..., - copy: bool = ..., - ) -> _ArraySelf: ... def copy(self: _ArraySelf, order: _OrderKACF = ...) -> _ArraySelf: ... def dump(self, file: str) -> None: ... def dumps(self) -> bytes: ... - def getfield( - self: _ArraySelf, dtype: DTypeLike, offset: int = ... - ) -> _ArraySelf: ... def tobytes(self, order: _OrderKACF = ...) -> bytes: ... # NOTE: `tostring()` is deprecated and therefore excluded # def tostring(self, order=...): ... @@ -1276,14 +1265,6 @@ class _ArrayOrScalarCommon: ) -> None: ... # generics and 0d arrays return builtin scalars def tolist(self) -> Any: ... - @overload - def view(self, type: Type[_NdArraySubClass]) -> _NdArraySubClass: ... - @overload - def view(self: _ArraySelf, dtype: DTypeLike = ...) -> _ArraySelf: ... - @overload - def view( - self, dtype: DTypeLike, type: Type[_NdArraySubClass] - ) -> _NdArraySubClass: ... # TODO: Add proper signatures def __getitem__(self, key) -> Any: ... @@ -1665,6 +1646,12 @@ _T_co = TypeVar("_T_co", covariant=True) _2Tuple = Tuple[_T, _T] _Casting = L["no", "equiv", "safe", "same_kind", "unsafe"] +_DTypeLike = Union[ + dtype[_ScalarType], + Type[_ScalarType], + _SupportsDType[dtype[_ScalarType]], +] + _ArrayUInt_co = NDArray[Union[bool_, unsignedinteger[Any]]] _ArrayInt_co = NDArray[Union[bool_, integer[Any]]] _ArrayFloat_co = NDArray[Union[bool_, integer[Any], floating[Any]]] @@ -1914,6 +1901,53 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeType, _DType_co]): self, *shape: SupportsIndex, order: _OrderACF = ... ) -> ndarray[Any, _DType_co]: ... + @overload + def astype( + self, + dtype: _DTypeLike[_ScalarType], + order: _OrderKACF = ..., + casting: _Casting = ..., + subok: bool = ..., + copy: bool = ..., + ) -> NDArray[_ScalarType]: ... + @overload + def astype( + self, + dtype: DTypeLike, + order: _OrderKACF = ..., + casting: _Casting = ..., + subok: bool = ..., + copy: bool = ..., + ) -> NDArray[Any]: ... + + @overload + def view(self: _ArraySelf) -> _ArraySelf: ... + @overload + def view(self, type: Type[_NdArraySubClass]) -> _NdArraySubClass: ... + @overload + def view(self, dtype: _DTypeLike[_ScalarType]) -> NDArray[_ScalarType]: ... + @overload + def view(self, dtype: DTypeLike) -> NDArray[Any]: ... + @overload + def view( + self, + dtype: DTypeLike, + type: Type[_NdArraySubClass], + ) -> _NdArraySubClass: ... + + @overload + def getfield( + self, + dtype: _DTypeLike[_ScalarType], + offset: SupportsIndex = ... + ) -> NDArray[_ScalarType]: ... + @overload + def getfield( + self, + dtype: DTypeLike, + offset: SupportsIndex = ... + ) -> NDArray[Any]: ... + # Dispatch to the underlying `generic` via protocols def __int__( self: ndarray[Any, dtype[SupportsInt]], # type: ignore[type-var] @@ -2886,6 +2920,59 @@ class generic(_ArrayOrScalarCommon): def byteswap(self: _ScalarType, inplace: L[False] = ...) -> _ScalarType: ... @property def flat(self: _ScalarType) -> flatiter[ndarray[Any, dtype[_ScalarType]]]: ... + + @overload + def astype( + self, + dtype: _DTypeLike[_ScalarType], + order: _OrderKACF = ..., + casting: _Casting = ..., + subok: bool = ..., + copy: bool = ..., + ) -> _ScalarType: ... + @overload + def astype( + self, + dtype: DTypeLike, + order: _OrderKACF = ..., + casting: _Casting = ..., + subok: bool = ..., + copy: bool = ..., + ) -> Any: ... + + # NOTE: `view` will perform a 0D->scalar cast, + # thus the array `type` is irrelevant to the output type + @overload + def view( + self: _ScalarType, + type: Type[ndarray[Any, Any]] = ..., + ) -> _ScalarType: ... + @overload + def view( + self, + dtype: _DTypeLike[_ScalarType], + type: Type[ndarray[Any, Any]] = ..., + ) -> _ScalarType: ... + @overload + def view( + self, + dtype: DTypeLike, + type: Type[ndarray[Any, Any]] = ..., + ) -> Any: ... + + @overload + def getfield( + self, + dtype: _DTypeLike[_ScalarType], + offset: SupportsIndex = ... + ) -> _ScalarType: ... + @overload + def getfield( + self, + dtype: DTypeLike, + offset: SupportsIndex = ... + ) -> Any: ... + def item( self, __args: Union[L[0], Tuple[()], Tuple[L[0]]] = ..., From 8c3d526b10bf0106593f5658c7d43988999edbb4 Mon Sep 17 00:00:00 2001 From: Bas van Beek Date: Mon, 31 May 2021 11:10:29 +0200 Subject: [PATCH 03/14] TST: Update the `ndarray`/`generic` typing tests --- .../tests/data/reveal/ndarray_conversion.py | 53 +++++++++---------- numpy/typing/tests/data/reveal/scalars.py | 12 +++++ 2 files changed, 37 insertions(+), 28 deletions(-) diff --git a/numpy/typing/tests/data/reveal/ndarray_conversion.py b/numpy/typing/tests/data/reveal/ndarray_conversion.py index 4ee637b752fa..03f2faf4337e 100644 --- a/numpy/typing/tests/data/reveal/ndarray_conversion.py +++ b/numpy/typing/tests/data/reveal/ndarray_conversion.py @@ -1,12 +1,13 @@ import numpy as np +import numpy.typing as npt -nd = np.array([[1, 2], [3, 4]]) +nd: npt.NDArray[np.int_] = np.array([[1, 2], [3, 4]]) # item -reveal_type(nd.item()) # E: Any -reveal_type(nd.item(1)) # E: Any -reveal_type(nd.item(0, 1)) # E: Any -reveal_type(nd.item((0, 1))) # E: Any +reveal_type(nd.item()) # E: int +reveal_type(nd.item(1)) # E: int +reveal_type(nd.item(0, 1)) # E: int +reveal_type(nd.item((0, 1))) # E: int # tolist reveal_type(nd.tolist()) # E: Any @@ -19,36 +20,32 @@ # dumps is pretty simple # astype -reveal_type(nd.astype("float")) # E: numpy.ndarray -reveal_type(nd.astype(float)) # E: numpy.ndarray -reveal_type(nd.astype(float, "K")) # E: numpy.ndarray -reveal_type(nd.astype(float, "K", "unsafe")) # E: numpy.ndarray -reveal_type(nd.astype(float, "K", "unsafe", True)) # E: numpy.ndarray -reveal_type(nd.astype(float, "K", "unsafe", True, True)) # E: numpy.ndarray +reveal_type(nd.astype("float")) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(nd.astype(float)) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(nd.astype(np.float64)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(nd.astype(np.float64, "K")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(nd.astype(np.float64, "K", "unsafe")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(nd.astype(np.float64, "K", "unsafe", True)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(nd.astype(np.float64, "K", "unsafe", True, True)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] # byteswap -reveal_type(nd.byteswap()) # E: numpy.ndarray -reveal_type(nd.byteswap(True)) # E: numpy.ndarray +reveal_type(nd.byteswap()) # E: numpy.ndarray[Any, numpy.dtype[{int_}]] +reveal_type(nd.byteswap(True)) # E: numpy.ndarray[Any, numpy.dtype[{int_}]] # copy -reveal_type(nd.copy()) # E: numpy.ndarray -reveal_type(nd.copy("C")) # E: numpy.ndarray +reveal_type(nd.copy()) # E: numpy.ndarray[Any, numpy.dtype[{int_}]] +reveal_type(nd.copy("C")) # E: numpy.ndarray[Any, numpy.dtype[{int_}]] -# view -class SubArray(np.ndarray): - pass - - -reveal_type(nd.view()) # E: numpy.ndarray -reveal_type(nd.view(np.int64)) # E: numpy.ndarray -# replace `Any` with `numpy.matrix` when `matrix` will be added to stubs -reveal_type(nd.view(np.int64, np.matrix)) # E: Any -reveal_type(nd.view(np.int64, SubArray)) # E: SubArray +reveal_type(nd.view()) # E: numpy.ndarray[Any, numpy.dtype[{int_}]] +reveal_type(nd.view(np.float64)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(nd.view(float)) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(nd.view(np.float64, np.matrix)) # E: numpy.matrix[Any, Any] # getfield -reveal_type(nd.getfield("float")) # E: numpy.ndarray -reveal_type(nd.getfield(float)) # E: numpy.ndarray -reveal_type(nd.getfield(float, 8)) # E: numpy.ndarray +reveal_type(nd.getfield("float")) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(nd.getfield(float)) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(nd.getfield(np.float64)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(nd.getfield(np.float64, 8)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] # setflags does not return a value # fill does not return a value diff --git a/numpy/typing/tests/data/reveal/scalars.py b/numpy/typing/tests/data/reveal/scalars.py index 8d1181f84607..ac6d2560f5e2 100644 --- a/numpy/typing/tests/data/reveal/scalars.py +++ b/numpy/typing/tests/data/reveal/scalars.py @@ -144,3 +144,15 @@ if sys.version_info >= (3, 9): reveal_type(f8.__ceil__()) # E: int reveal_type(f8.__floor__()) # E: int + +reveal_type(i8.astype(float)) # E: Any +reveal_type(i8.astype(np.float64)) # E: {float64} + +reveal_type(i8.view()) # E: {int64} +reveal_type(i8.view(np.float64)) # E: {float64} +reveal_type(i8.view(float)) # E: Any +reveal_type(i8.view(np.float64, np.ndarray)) # E: {float64} + +reveal_type(i8.getfield(float)) # E: Any +reveal_type(i8.getfield(np.float64)) # E: {float64} +reveal_type(i8.getfield(np.float64, 8)) # E: {float64} From c91443ba4998d84db352ba58a8e22db8cdd08370 Mon Sep 17 00:00:00 2001 From: Developer-Ecosystem-Engineering <65677710+Developer-Ecosystem-Engineering@users.noreply.github.com> Date: Sat, 25 Sep 2021 15:48:32 -0700 Subject: [PATCH 04/14] BUG: Resolve Divide by Zero on Apple silicon + test failures (#19926) * Resolve divide by zero in reciprocal #18555 clang has an optimization bug where a vector that is only partially loaded / stored will get narrowed to only the lanes used, which can be fine in some cases. However, in numpy's `reciprocal` function a partial load is explicitly extended to the full width of the register (filled with '1's) to avoid divide-by-zero. clang's optimization ignores the explicit filling with '1's. The changes here insert a dummy `volatile` variable. This convinces clang not to narrow the load and ignore the explicit filling of '1's. `volatile` can be expensive since it forces loads / stores to refresh contents whenever the variable is used. numpy has its own template / macro system that'll expand the loop function below for sqrt, absolute, square, and reciprocal. Additionally, the loop can be called on a full array if there's overlap between src and dst. Consequently, we try to limit the scope that we need to apply `volatile`. Intention is it should only be needed when compiling with clang, against Apple arm64, and only for the `reciprocal` function. Moreover, `volatile` is only needed when a vector is partially loaded. Testing: Beyond fixing the cases mentioned in the GitHub issue, the changes here also resolve several failures in numpy's test suite. Before: ``` FAILED numpy/core/tests/test_scalarmath.py::TestBaseMath::test_blocked - RuntimeWarning: divide by zero encountered in reciprocal FAILED numpy/core/tests/test_ufunc.py::TestUfuncGenericLoops::test_unary_PyUFunc_O_O_method_full[reciprocal] - AssertionError: FloatingPointError not raised FAILED numpy/core/tests/test_umath.py::TestPower::test_power_float - RuntimeWarning: divide by zero encountered in reciprocal FAILED numpy/core/tests/test_umath.py::TestSpecialFloats::test_tan - AssertionError: FloatingPointError not raised by tan FAILED numpy/core/tests/test_umath.py::TestAVXUfuncs::test_avx_based_ufunc - RuntimeWarning: divide by zero encountered in reciprocal FAILED numpy/linalg/tests/test_linalg.py::TestNormDouble::test_axis - RuntimeWarning: divide by zero encountered in reciprocal FAILED numpy/linalg/tests/test_linalg.py::TestNormSingle::test_axis - RuntimeWarning: divide by zero encountered in reciprocal FAILED numpy/linalg/tests/test_linalg.py::TestNormInt64::test_axis - RuntimeWarning: divide by zero encountered in reciprocal 8 failed, 14759 passed, 204 skipped, 1268 deselected, 34 xfailed in 69.90s (0:01:09) ``` After: ``` FAILED numpy/core/tests/test_umath.py::TestSpecialFloats::test_tan - AssertionError: FloatingPointError not raised by tan 1 failed, 14766 passed, 204 skipped, 1268 deselected, 34 xfailed in 70.37s (0:01:10) ``` * Enhancement on top of workaround for clang bug in reciprocal Enhancement on top of workaround for clang bug in reciprocal (numpy/numpy#18555) Numpy's FP unary loops use a partial load / store on every iteration. The partial load / store helpers each insert a switch statement to know how many elements to handle. This causes a lot of unnecessary branches to be inserted in the loops. The partial load / store is only needed on the final iteration of the loop if it isn't a full vector. The changes here breakout the final iteration to use the partial load / stores. The loop has been changed to use full load / stores. Additionally, this means we don't need to conditionalize the volatile workaround in the loop. * Address Azure CI failures with older versions of clang - -ftrapping-math is default enabled for Numpy, but support in clang is mainly for x86_64 - Apple Clang and Clang have different, but overlapping versions - Non-Apple Clang versions come from looking at when they started supporting -ftrapping-math for x86_64 Testing was done against Apple Clang versions - v11 / x86_64 - failed previously, now passes (azure failure) - v12+ / x86_64 - passes before and after - v13 / arm64 - failed before initial patch, passes after --- .../src/umath/loops_unary_fp.dispatch.c.src | 84 ++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/numpy/core/src/umath/loops_unary_fp.dispatch.c.src b/numpy/core/src/umath/loops_unary_fp.dispatch.c.src index 3a1ea82f9460..2d5917282c26 100644 --- a/numpy/core/src/umath/loops_unary_fp.dispatch.c.src +++ b/numpy/core/src/umath/loops_unary_fp.dispatch.c.src @@ -77,6 +77,56 @@ NPY_FINLINE double c_square_f64(double a) */ #define CONTIG 0 #define NCONTIG 1 + +/* + * clang has a bug that's present at -O1 or greater. When partially loading a + * vector register for a reciprocal operation, the remaining elements are set + * to 1 to avoid divide-by-zero. The partial load is paired with a partial + * store after the reciprocal operation. clang notices that the entire register + * is not needed for the store and optimizes out the fill of 1 to the remaining + * elements. This causes either a divide-by-zero or 0/0 with invalid exception + * that we were trying to avoid by filling. + * + * Using a dummy variable marked 'volatile' convinces clang not to ignore + * the explicit fill of remaining elements. If `-ftrapping-math` is + * supported, then it'll also avoid the bug. `-ftrapping-math` is supported + * on Apple clang v12+ for x86_64. It is not currently supported for arm64. + * `-ftrapping-math` is set by default of Numpy builds in + * numpy/distutils/ccompiler.py. + * + * Note: Apple clang and clang upstream have different versions that overlap + */ +#if defined(__clang__) + #if defined(__apple_build_version__) + // Apple Clang + #if __apple_build_version__ < 12000000 + // Apple Clang before v12 + #define WORKAROUND_CLANG_RECIPROCAL_BUG 1 + #elif defined(NPY_CPU_X86) || defined(NPY_CPU_AMD64) + // Apple Clang after v12, targeting i386 or x86_64 + #define WORKAROUND_CLANG_RECIPROCAL_BUG 0 + #else + // Apple Clang after v12, not targeting i386 or x86_64 + #define WORKAROUND_CLANG_RECIPROCAL_BUG 1 + #endif + #else + // Clang, not Apple Clang + #if __clang_major__ < 10 + // Clang before v10 + #define WORKAROUND_CLANG_RECIPROCAL_BUG 1 + #elif defined(NPY_CPU_X86) || defined(NPY_CPU_AMD64) + // Clang v10+, targeting i386 or x86_64 + #define WORKAROUND_CLANG_RECIPROCAL_BUG 0 + #else + // Clang v10+, not targeting i386 or x86_64 + #define WORKAROUND_CLANG_RECIPROCAL_BUG 1 + #endif + #endif +#else +// Not a Clang compiler +#define WORKAROUND_CLANG_RECIPROCAL_BUG 0 +#endif + /**begin repeat * #TYPE = FLOAT, DOUBLE# * #sfx = f32, f64# @@ -87,6 +137,7 @@ NPY_FINLINE double c_square_f64(double a) * #kind = sqrt, absolute, square, reciprocal# * #intr = sqrt, abs, square, recip# * #repl_0w1 = 0, 0, 0, 1# + * #RECIP_WORKAROUND = 0, 0, 0, WORKAROUND_CLANG_RECIPROCAL_BUG# */ /**begin repeat2 * #STYPE = CONTIG, NCONTIG, CONTIG, NCONTIG# @@ -101,6 +152,8 @@ static void simd_@TYPE@_@kind@_@STYPE@_@DTYPE@ const int vstep = npyv_nlanes_@sfx@; const int wstep = vstep * @unroll@; + + // unrolled iterations for (; len >= wstep; len -= wstep, src += ssrc*wstep, dst += sdst*wstep) { /**begin repeat3 * #N = 0, 1, 2, 3# @@ -126,7 +179,24 @@ static void simd_@TYPE@_@kind@_@STYPE@_@DTYPE@ #endif /**end repeat3**/ } - for (; len > 0; len -= vstep, src += ssrc*vstep, dst += sdst*vstep) { + + // vector-sized iterations + for (; len >= vstep; len -= vstep, src += ssrc*vstep, dst += sdst*vstep) { + #if @STYPE@ == CONTIG + npyv_@sfx@ v_src0 = npyv_load_@sfx@(src); + #else + npyv_@sfx@ v_src0 = npyv_loadn_@sfx@(src, ssrc); + #endif + npyv_@sfx@ v_unary0 = npyv_@intr@_@sfx@(v_src0); + #if @DTYPE@ == CONTIG + npyv_store_@sfx@(dst, v_unary0); + #else + npyv_storen_@sfx@(dst, sdst, v_unary0); + #endif + } + + // last partial iteration, if needed + if(len > 0){ #if @STYPE@ == CONTIG #if @repl_0w1@ npyv_@sfx@ v_src0 = npyv_load_till_@sfx@(src, len, 1); @@ -140,6 +210,15 @@ static void simd_@TYPE@_@kind@_@STYPE@_@DTYPE@ npyv_@sfx@ v_src0 = npyv_loadn_tillz_@sfx@(src, ssrc, len); #endif #endif + #if @RECIP_WORKAROUND@ + /* + * Workaround clang bug. We use a dummy variable marked 'volatile' + * to convince clang that the entire vector is needed. We only + * want to do this for the last iteration / partial load-store of + * the loop since 'volatile' forces a refresh of the contents. + */ + volatile npyv_@sfx@ unused_but_workaround_bug = v_src0; + #endif // @RECIP_WORKAROUND@ npyv_@sfx@ v_unary0 = npyv_@intr@_@sfx@(v_src0); #if @DTYPE@ == CONTIG npyv_store_till_@sfx@(dst, len, v_unary0); @@ -147,6 +226,7 @@ static void simd_@TYPE@_@kind@_@STYPE@_@DTYPE@ npyv_storen_till_@sfx@(dst, sdst, len, v_unary0); #endif } + npyv_cleanup(); } /**end repeat2**/ @@ -154,6 +234,8 @@ static void simd_@TYPE@_@kind@_@STYPE@_@DTYPE@ #endif // @VCHK@ /**end repeat**/ +#undef WORKAROUND_CLANG_RECIPROCAL_BUG + /******************************************************************************** ** Defining ufunc inner functions ********************************************************************************/ From 6fb30f9adfcb9793a9771cdf2a172eb3072e456c Mon Sep 17 00:00:00 2001 From: Bas van Beek Date: Thu, 9 Sep 2021 13:51:26 +0200 Subject: [PATCH 05/14] MAINT: Mark type-check-only ufunc subclasses as ufunc aliases during runtime --- numpy/typing/__init__.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/numpy/typing/__init__.py b/numpy/typing/__init__.py index 8e758b26cb6f..0cf534bc01b6 100644 --- a/numpy/typing/__init__.py +++ b/numpy/typing/__init__.py @@ -161,7 +161,8 @@ # NOTE: The API section will be appended with additional entries # further down in this file -from typing import TYPE_CHECKING, List, Any +from numpy import ufunc +from typing import TYPE_CHECKING, List if TYPE_CHECKING: import sys @@ -364,14 +365,16 @@ class _8Bit(_16Bit): ... # type: ignore[misc] _GUFunc_Nin2_Nout1, ) else: - _UFunc_Nin1_Nout1 = Any - _UFunc_Nin2_Nout1 = Any - _UFunc_Nin1_Nout2 = Any - _UFunc_Nin2_Nout2 = Any - _GUFunc_Nin2_Nout1 = Any + # Declare the (type-check-only) ufunc subclasses as ufunc aliases during + # runtime; this helps autocompletion tools such as Jedi (numpy/numpy#19834) + _UFunc_Nin1_Nout1 = ufunc + _UFunc_Nin2_Nout1 = ufunc + _UFunc_Nin1_Nout2 = ufunc + _UFunc_Nin2_Nout2 = ufunc + _GUFunc_Nin2_Nout1 = ufunc # Clean up the namespace -del TYPE_CHECKING, final, List, Any +del TYPE_CHECKING, final, List, ufunc if __doc__ is not None: from ._add_docstring import _docstrings From d169a12a7910a5e1e75212db99e191c9b4098c5e Mon Sep 17 00:00:00 2001 From: Developer-Ecosystem-Engineering <65677710+Developer-Ecosystem-Engineering@users.noreply.github.com> Date: Mon, 27 Sep 2021 10:37:07 -0700 Subject: [PATCH 06/14] BUG: np.tan(np.inf) test failure Numpy has a test failure where `np.tan(np.inf)` is not raising "invalid". The bug is due to an underlying bug in Apple's Libm. The bug is only present on arm64 and does not affect x86_64. The changes here apply to sin, cos, and tan. If the input is a non-finite value, then return `(x - x)`, which will raise "invalid" for `x=INFINITY` as well as return `NAN` for both `INFINITY` and `NAN` as input. Testing: (Note, the testing below is on top of another branch that fixes divide-by-zero failures in reciprocal) Before change: ``` FAILED numpy/core/tests/test_umath.py::TestSpecialFloats::test_tan - AssertionError: FloatingPointError not raised by tan 1 failed, 15589 passed, 302 skipped, 1268 deselected, 31 xfailed, 3 xpassed in 70.99s (0:01:10) ``` After change: ``` 15590 passed, 302 skipped, 1268 deselected, 31 xfailed, 3 xpassed in 71.38s (0:01:11) ``` --- .../core/src/npymath/npy_math_internal.h.src | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/numpy/core/src/npymath/npy_math_internal.h.src b/numpy/core/src/npymath/npy_math_internal.h.src index ff4663dc3e50..d692aa7953e9 100644 --- a/numpy/core/src/npymath/npy_math_internal.h.src +++ b/numpy/core/src/npymath/npy_math_internal.h.src @@ -483,21 +483,40 @@ NPY_INPLACE @type@ npy_frexp@c@(@type@ x, int* exp) * #c = l,,f# * #C = L,,F# */ + +/* + * On arm64 macOS, there's a bug with sin, cos, and tan where they don't + * raise "invalid" when given INFINITY as input. + */ +#if defined(__APPLE__) && defined(__arm64__) +#define WORKAROUND_APPLE_TRIG_BUG 1 +#else +#define WORKAROUND_APPLE_TRIG_BUG 0 +#endif + /**begin repeat1 * #kind = sin,cos,tan,sinh,cosh,tanh,fabs,floor,ceil,rint,trunc,sqrt,log10, * log,exp,expm1,asin,acos,atan,asinh,acosh,atanh,log1p,exp2,log2# * #KIND = SIN,COS,TAN,SINH,COSH,TANH,FABS,FLOOR,CEIL,RINT,TRUNC,SQRT,LOG10, * LOG,EXP,EXPM1,ASIN,ACOS,ATAN,ASINH,ACOSH,ATANH,LOG1P,EXP2,LOG2# + * #TRIG_WORKAROUND = WORKAROUND_APPLE_TRIG_BUG*3, 0*22# */ #ifdef HAVE_@KIND@@C@ NPY_INPLACE @type@ npy_@kind@@c@(@type@ x) { +#if @TRIG_WORKAROUND@ + if (!npy_isfinite(x)) { + return (x - x); + } +#endif return @kind@@c@(x); } #endif /**end repeat1**/ +#undef WORKAROUND_APPLE_TRIG_BUG + /**begin repeat1 * #kind = atan2,hypot,pow,copysign# * #KIND = ATAN2,HYPOT,POW,COPYSIGN# From a26be10ff1d1a4f64fb23ead017e4eff5b8f4e83 Mon Sep 17 00:00:00 2001 From: Kevin Sheppard Date: Wed, 6 Oct 2021 09:45:21 +0100 Subject: [PATCH 07/14] BUG: Correct incorrect advance in PCG with emulated int128 Correct incorrect implemetation of carry in PCG64 and PCG64DXSM when advancing more than 2**64 steps closes #20048 --- doc/release/upcoming_changes/20049.change.rst | 5 +++++ numpy/random/src/pcg64/pcg64.c | 3 +-- numpy/random/tests/test_direct.py | 22 +++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 doc/release/upcoming_changes/20049.change.rst diff --git a/doc/release/upcoming_changes/20049.change.rst b/doc/release/upcoming_changes/20049.change.rst new file mode 100644 index 000000000000..e1f08b3437c8 --- /dev/null +++ b/doc/release/upcoming_changes/20049.change.rst @@ -0,0 +1,5 @@ +Corrected ``advance`` in ``PCG64DSXM`` and ``PCG64`` +---------------------------------------------------- +Fixed a bug in the ``advance`` method of ``PCG64DSXM`` and ``PCG64``. The bug only +affects results when the step was larger than :math:`2^{64}` on platforms +that do not support 128-bit integers(e.g., Windows and 32-bit Linux). diff --git a/numpy/random/src/pcg64/pcg64.c b/numpy/random/src/pcg64/pcg64.c index c623c809b02e..b9be1e39d350 100644 --- a/numpy/random/src/pcg64/pcg64.c +++ b/numpy/random/src/pcg64/pcg64.c @@ -109,8 +109,7 @@ pcg128_t pcg_advance_lcg_128(pcg128_t state, pcg128_t delta, pcg128_t cur_mult, cur_plus = pcg128_mult(pcg128_add(cur_mult, PCG_128BIT_CONSTANT(0u, 1u)), cur_plus); cur_mult = pcg128_mult(cur_mult, cur_mult); - delta.low >>= 1; - delta.low += delta.high & 1; + delta.low = (delta.low >> 1) | (delta.high << 63); delta.high >>= 1; } return pcg128_add(pcg128_mult(acc_mult, state), acc_plus); diff --git a/numpy/random/tests/test_direct.py b/numpy/random/tests/test_direct.py index 29054b70b95a..ea1ebacb63c8 100644 --- a/numpy/random/tests/test_direct.py +++ b/numpy/random/tests/test_direct.py @@ -358,6 +358,17 @@ def test_advance_symmetry(self): assert val_neg == val_pos assert val_big == val_pos + def test_advange_large(self): + rs = Generator(self.bit_generator(38219308213743)) + pcg = rs.bit_generator + state = pcg.state["state"] + initial_state = 287608843259529770491897792873167516365 + assert state["state"] == initial_state + pcg.advance(sum(2**i for i in (96, 64, 32, 16, 8, 4, 2, 1))) + state = pcg.state["state"] + advanced_state = 135275564607035429730177404003164635391 + assert state["state"] == advanced_state + class TestPCG64DXSM(Base): @classmethod @@ -386,6 +397,17 @@ def test_advance_symmetry(self): assert val_neg == val_pos assert val_big == val_pos + def test_advange_large(self): + rs = Generator(self.bit_generator(38219308213743)) + pcg = rs.bit_generator + state = pcg.state + initial_state = 287608843259529770491897792873167516365 + assert state["state"]["state"] == initial_state + pcg.advance(sum(2**i for i in (96, 64, 32, 16, 8, 4, 2, 1))) + state = pcg.state["state"] + advanced_state = 277778083536782149546677086420637664879 + assert state["state"] == advanced_state + class TestMT19937(Base): @classmethod From 787e2cc0067921c104cc1ab9c58981aa6bf259e3 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 3 Aug 2021 15:02:46 -0600 Subject: [PATCH 08/14] BUG: Fix NaT handling in the PyArray_CompareFunc for datetime and timedelta In #12658 and #15068 the sort ordering for datetime and timedelta was changed so that NaT sorts to the end, but the internal compare array function was not updated. Fixes #19574. --- numpy/core/src/multiarray/arraytypes.c.src | 43 ++++++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src index ad74612272b2..20fe2ce0d37d 100644 --- a/numpy/core/src/multiarray/arraytypes.c.src +++ b/numpy/core/src/multiarray/arraytypes.c.src @@ -2805,11 +2805,9 @@ BOOL_compare(npy_bool *ip1, npy_bool *ip2, PyArrayObject *NPY_UNUSED(ap)) /**begin repeat * #TYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT, - * LONG, ULONG, LONGLONG, ULONGLONG, - * DATETIME, TIMEDELTA# + * LONG, ULONG, LONGLONG, ULONGLONG# * #type = npy_byte, npy_ubyte, npy_short, npy_ushort, npy_int, npy_uint, - * npy_long, npy_ulong, npy_longlong, npy_ulonglong, - * npy_datetime, npy_timedelta# + * npy_long, npy_ulong, npy_longlong, npy_ulonglong# */ static int @@ -2920,6 +2918,43 @@ C@TYPE@_compare(@type@ *pa, @type@ *pb) /**end repeat**/ +/**begin repeat + * #TYPE = DATETIME, TIMEDELTA# + * #type = npy_datetime, npy_timedelta# + */ + +#define LT(a,b) ((a) < (b) || ((b) != (b) && (a) ==(a))) + +static int +@TYPE@_compare(@type@ *pa, @type@ *pb) +{ + const @type@ a = *pa; + const @type@ b = *pb; + int ret; + + printf("Calling @TYPE@_compare\n"); + if (a == NPY_DATETIME_NAT) { + ret = 1; + } + else if (b == NPY_DATETIME_NAT) { + ret = -1; + } + else if (LT(a,b)) { + ret = -1; + } + else if (LT(b,a)) { + ret = 1; + } + else { + ret = 0; + } + return ret; +} + +#undef LT + +/**end repeat**/ + static int HALF_compare (npy_half *pa, npy_half *pb, PyArrayObject *NPY_UNUSED(ap)) { From 9416e593bf8029bbdc05c55c27c84dfd39effb2d Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 3 Aug 2021 16:19:23 -0600 Subject: [PATCH 09/14] Remove debug print --- numpy/core/src/multiarray/arraytypes.c.src | 1 - 1 file changed, 1 deletion(-) diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src index 20fe2ce0d37d..58dcb7bc023a 100644 --- a/numpy/core/src/multiarray/arraytypes.c.src +++ b/numpy/core/src/multiarray/arraytypes.c.src @@ -2932,7 +2932,6 @@ static int const @type@ b = *pb; int ret; - printf("Calling @TYPE@_compare\n"); if (a == NPY_DATETIME_NAT) { ret = 1; } From 73e7a1293745cbb8054fd019c1e03ade9e27638b Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 3 Aug 2021 16:23:38 -0600 Subject: [PATCH 10/14] Fix the implementation of DATETIME_compare I accidentally based it off the float compare template instead of the integer compare template. It also now properly handles the case when both arguments are NaT. --- numpy/core/src/multiarray/arraytypes.c.src | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src index 58dcb7bc023a..b3ea7544d974 100644 --- a/numpy/core/src/multiarray/arraytypes.c.src +++ b/numpy/core/src/multiarray/arraytypes.c.src @@ -2923,8 +2923,6 @@ C@TYPE@_compare(@type@ *pa, @type@ *pb) * #type = npy_datetime, npy_timedelta# */ -#define LT(a,b) ((a) < (b) || ((b) != (b) && (a) ==(a))) - static int @TYPE@_compare(@type@ *pa, @type@ *pb) { @@ -2933,25 +2931,22 @@ static int int ret; if (a == NPY_DATETIME_NAT) { - ret = 1; + if (b == NPY_DATETIME_NAT) { + ret = 0; + } + else { + ret = 1; + } } else if (b == NPY_DATETIME_NAT) { ret = -1; } - else if (LT(a,b)) { - ret = -1; - } - else if (LT(b,a)) { - ret = 1; - } else { - ret = 0; + ret = a < b ? -1 : a == b ? 0 : 1; } return ret; } -#undef LT - /**end repeat**/ static int From 2144b1c1cc625e58fe96691f1cad433f8b98eb66 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Wed, 14 Apr 2021 10:46:22 -0500 Subject: [PATCH 11/14] DOC: Ensure that we add documentation also as to the dict for types This ensures that `help(np.dtype)` produces a result. I am not exactly sure why it picks up `__doc__` from the dict instead of `tp_doc` right now. It probably is due to the combination of inheritance and the fact that the dict always includes `None` and gets preference during inheritance. (That probably makes a lot of sense to not inherit the `type` docstring by default.) Modifying the dictionary directly is not really good style, either, but hopefully works. Closes gh-18740 --- numpy/core/src/multiarray/compiled_base.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/numpy/core/src/multiarray/compiled_base.c b/numpy/core/src/multiarray/compiled_base.c index de793f87c156..485f87f0013a 100644 --- a/numpy/core/src/multiarray/compiled_base.c +++ b/numpy/core/src/multiarray/compiled_base.c @@ -1425,9 +1425,26 @@ arr_add_docstring(PyObject *NPY_UNUSED(dummy), PyObject *args) PyCFunctionObject *new = (PyCFunctionObject *)obj; _ADDDOC(new->m_ml->ml_doc, new->m_ml->ml_name); } - else if (Py_TYPE(obj) == &PyType_Type) { + else if (PyObject_TypeCheck(obj, &PyType_Type)) { + /* + * We add it to both `tp_doc` and `__doc__` here. Note that in theory + * `tp_doc` extracts the signature line, but we currently do not use + * it. It may make sense to only add it as `__doc__` and + * `__text_signature__` to the dict in the future. + * The dictionary path is only necessary for heaptypes (currently not + * used) and metaclasses. + * If `__doc__` as stored in `to_dict` is None, we assume this was + * filled in by `PyType_Read()` and should also be replaced. + */ PyTypeObject *new = (PyTypeObject *)obj; _ADDDOC(new->tp_doc, new->tp_name); + if (new->tp_dict != NULL && PyDict_CheckExact(new->tp_dict) && + PyDict_GetItemString(new->tp_dict, "__doc__") == Py_None) { + /* Warning: Modifying `tp_dict` is not generally safe! */ + if (PyDict_SetItemString(new->tp_dict, "__doc__", str) < 0) { + return NULL; + } + } } else if (Py_TYPE(obj) == &PyMemberDescr_Type) { PyMemberDescrObject *new = (PyMemberDescrObject *)obj; From 8f0c4b045d75e709ee1beeb5de33d9f61028f3ec Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Tue, 5 Oct 2021 09:24:48 -0500 Subject: [PATCH 12/14] Apply suggestions from code review Co-authored-by: Warren Weckesser --- numpy/core/src/multiarray/compiled_base.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/numpy/core/src/multiarray/compiled_base.c b/numpy/core/src/multiarray/compiled_base.c index 485f87f0013a..6b76e570f8e6 100644 --- a/numpy/core/src/multiarray/compiled_base.c +++ b/numpy/core/src/multiarray/compiled_base.c @@ -1433,8 +1433,8 @@ arr_add_docstring(PyObject *NPY_UNUSED(dummy), PyObject *args) * `__text_signature__` to the dict in the future. * The dictionary path is only necessary for heaptypes (currently not * used) and metaclasses. - * If `__doc__` as stored in `to_dict` is None, we assume this was - * filled in by `PyType_Read()` and should also be replaced. + * If `__doc__` as stored in `tp_dict` is None, we assume this was + * filled in by `PyType_Ready()` and should also be replaced. */ PyTypeObject *new = (PyTypeObject *)obj; _ADDDOC(new->tp_doc, new->tp_name); From b30f1891760f06f056925641d20f2f968c15ce9d Mon Sep 17 00:00:00 2001 From: Charles Harris Date: Tue, 12 Oct 2021 10:19:40 -0600 Subject: [PATCH 13/14] BUG: core: result_type(0, np.timedelta64(4)) would seg. fault. Backport of #20088. --- numpy/core/src/multiarray/convert_datatype.c | 61 +++++++++++++------- numpy/core/tests/test_dtype.py | 8 +++ 2 files changed, 49 insertions(+), 20 deletions(-) diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c index cd0e21098bcd..49e4550804b6 100644 --- a/numpy/core/src/multiarray/convert_datatype.c +++ b/numpy/core/src/multiarray/convert_datatype.c @@ -1536,6 +1536,40 @@ should_use_min_scalar(npy_intp narrs, PyArrayObject **arr, } +/* + * Utility function used only in PyArray_ResultType for value-based logic. + * See that function for the meaning and contents of the parameters. + */ +static PyArray_Descr * +get_descr_from_cast_or_value( + npy_intp i, + PyArrayObject *arrs[], + npy_intp ndtypes, + PyArray_Descr *descriptor, + PyArray_DTypeMeta *common_dtype) +{ + PyArray_Descr *curr; + if (NPY_LIKELY(i < ndtypes || + !(PyArray_FLAGS(arrs[i-ndtypes]) & _NPY_ARRAY_WAS_PYSCALAR))) { + curr = PyArray_CastDescrToDType(descriptor, common_dtype); + } + else { + /* + * Unlike `PyArray_CastToDTypeAndPromoteDescriptors`, deal with + * plain Python values "graciously". This recovers the original + * value the long route, but it should almost never happen... + */ + PyObject *tmp = PyArray_GETITEM(arrs[i-ndtypes], + PyArray_BYTES(arrs[i-ndtypes])); + if (tmp == NULL) { + return NULL; + } + curr = common_dtype->discover_descr_from_pyobject(common_dtype, tmp); + Py_DECREF(tmp); + } + return curr; +} + /*NUMPY_API * * Produces the result type of a bunch of inputs, using the same rules @@ -1672,28 +1706,15 @@ PyArray_ResultType( result = common_dtype->default_descr(common_dtype); } else { - result = PyArray_CastDescrToDType(all_descriptors[0], common_dtype); + result = get_descr_from_cast_or_value( + 0, arrs, ndtypes, all_descriptors[0], common_dtype); + if (result == NULL) { + goto error; + } for (npy_intp i = 1; i < ndtypes+narrs; i++) { - PyArray_Descr *curr; - if (NPY_LIKELY(i < ndtypes || - !(PyArray_FLAGS(arrs[i-ndtypes]) & _NPY_ARRAY_WAS_PYSCALAR))) { - curr = PyArray_CastDescrToDType(all_descriptors[i], common_dtype); - } - else { - /* - * Unlike `PyArray_CastToDTypeAndPromoteDescriptors` deal with - * plain Python values "graciously". This recovers the original - * value the long route, but it should almost never happen... - */ - PyObject *tmp = PyArray_GETITEM( - arrs[i-ndtypes], PyArray_BYTES(arrs[i-ndtypes])); - if (tmp == NULL) { - goto error; - } - curr = common_dtype->discover_descr_from_pyobject(common_dtype, tmp); - Py_DECREF(tmp); - } + PyArray_Descr *curr = get_descr_from_cast_or_value( + i, arrs, ndtypes, all_descriptors[i], common_dtype); if (curr == NULL) { goto error; } diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py index 3d15009ea765..1dcf1fb8ebd8 100644 --- a/numpy/core/tests/test_dtype.py +++ b/numpy/core/tests/test_dtype.py @@ -1539,3 +1539,11 @@ class mytype: # Tests that a dtype must have its type field set up to np.dtype # or in this case a builtin instance. create_custom_field_dtype(blueprint, mytype, 2) + + +def test_result_type_integers_and_unitless_timedelta64(): + # Regression test for gh-20077. The following call of `result_type` + # would cause a seg. fault. + td = np.timedelta64(4) + result = np.result_type(0, td) + assert_dtype_equal(result, td.dtype) From 649a2f6d040cc09d99913c5fc571e4e1e87fd49b Mon Sep 17 00:00:00 2001 From: Charles Harris Date: Tue, 19 Oct 2021 17:27:46 -0600 Subject: [PATCH 14/14] REL: Prepare for 1.21.3 release. --- doc/changelog/1.21.3-changelog.rst | 28 ++++++++++++++ doc/source/release/1.21.3-notes.rst | 57 ++++++++++++++--------------- 2 files changed, 56 insertions(+), 29 deletions(-) create mode 100644 doc/changelog/1.21.3-changelog.rst diff --git a/doc/changelog/1.21.3-changelog.rst b/doc/changelog/1.21.3-changelog.rst new file mode 100644 index 000000000000..7677947218d5 --- /dev/null +++ b/doc/changelog/1.21.3-changelog.rst @@ -0,0 +1,28 @@ + +Contributors +============ + +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Aaron Meurer +* Bas van Beek +* Charles Harris +* Developer-Ecosystem-Engineering + +* Kevin Sheppard +* Sebastian Berg +* Warren Weckesser + +Pull requests merged +==================== + +A total of 8 pull requests were merged for this release. + +* `#19745 `__: ENH: Add dtype-support to 3 `generic`/`ndarray` methods +* `#19955 `__: BUG: Resolve Divide by Zero on Apple silicon + test failures... +* `#19958 `__: MAINT: Mark type-check-only ufunc subclasses as ufunc aliases... +* `#19994 `__: BUG: np.tan(np.inf) test failure +* `#20080 `__: BUG: Correct incorrect advance in PCG with emulated int128 +* `#20081 `__: BUG: Fix NaT handling in the PyArray_CompareFunc for datetime... +* `#20082 `__: DOC: Ensure that we add documentation also as to the dict for... +* `#20106 `__: BUG: core: result_type(0, np.timedelta64(4)) would seg. fault. diff --git a/doc/source/release/1.21.3-notes.rst b/doc/source/release/1.21.3-notes.rst index 7db85672af26..c0ef5a498f85 100644 --- a/doc/source/release/1.21.3-notes.rst +++ b/doc/source/release/1.21.3-notes.rst @@ -4,42 +4,41 @@ NumPy 1.21.3 Release Notes ========================== +The NumPy 1.21.3 is a maintenance release the fixes a few bugs discovered after +1.21.2. It also provides 64 bit Python 3.10.0 wheels. Note a few oddities about +Python 3.10: -Highlights -========== +* There are no 32 bit wheels for Windows, Mac, or Linux. +* The Mac Intel builds are only available in universal2 wheels. +The Python versions supported in this release are 3.7-3.10. If you want to +compile your own version using gcc-11 you will need to use gcc-11.2+ to avoid +problems. -New functions -============= - - -Deprecations +Contributors ============ +A total of 7 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. -Future Changes -============== +* Aaron Meurer +* Bas van Beek +* Charles Harris +* Developer-Ecosystem-Engineering + +* Kevin Sheppard +* Sebastian Berg +* Warren Weckesser - -Expired deprecations +Pull requests merged ==================== +A total of 8 pull requests were merged for this release. -Compatibility notes -=================== - - -C API changes -============= - - -New Features -============ - - -Improvements -============ - - -Changes -======= +* `#19745 `__: ENH: Add dtype-support to 3 ```generic``/``ndarray`` methods +* `#19955 `__: BUG: Resolve Divide by Zero on Apple silicon + test failures... +* `#19958 `__: MAINT: Mark type-check-only ufunc subclasses as ufunc aliases... +* `#19994 `__: BUG: np.tan(np.inf) test failure +* `#20080 `__: BUG: Correct incorrect advance in PCG with emulated int128 +* `#20081 `__: BUG: Fix NaT handling in the PyArray_CompareFunc for datetime... +* `#20082 `__: DOC: Ensure that we add documentation also as to the dict for... +* `#20106 `__: BUG: core: result_type(0, np.timedelta64(4)) would seg. fault.