summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
Diffstat (limited to 'numpy')
-rw-r--r--numpy/__config__.py.in34
-rw-r--r--numpy/__init__.py8
-rw-r--r--numpy/__init__.pyi73
-rw-r--r--numpy/_typing/_scalars.py10
-rw-r--r--numpy/array_api/__init__.py4
-rw-r--r--numpy/array_api/_array_object.py2
-rw-r--r--numpy/array_api/_indexing_functions.py18
-rw-r--r--numpy/array_api/tests/test_indexing_functions.py24
-rw-r--r--numpy/core/__init__.py1
-rw-r--r--numpy/core/_asarray.pyi8
-rw-r--r--numpy/core/_type_aliases.pyi10
-rw-r--r--numpy/core/code_generators/generate_umath.py125
-rw-r--r--numpy/core/code_generators/ufunc_docstrings.py10
-rw-r--r--numpy/core/config.h.in16
-rw-r--r--numpy/core/fromnumeric.py193
-rw-r--r--numpy/core/getlimits.py21
-rw-r--r--numpy/core/include/meson.build1
-rw-r--r--numpy/core/include/numpy/_dtype_api.h408
-rw-r--r--numpy/core/include/numpy/experimental_dtype_api.h192
-rw-r--r--numpy/core/include/numpy/npy_3kcompat.h26
-rw-r--r--numpy/core/include/numpy/npy_common.h50
-rw-r--r--numpy/core/meson.build76
-rw-r--r--numpy/core/numeric.py5
-rw-r--r--numpy/core/setup.py30
-rw-r--r--numpy/core/setup_common.py56
-rw-r--r--numpy/core/src/common/common.hpp11
-rw-r--r--numpy/core/src/common/half.hpp63
-rw-r--r--numpy/core/src/common/meta.hpp54
-rw-r--r--numpy/core/src/common/npstd.hpp54
-rw-r--r--numpy/core/src/common/npy_config.h1
-rw-r--r--numpy/core/src/common/npy_cpu_features.c7
-rw-r--r--numpy/core/src/common/npy_cpu_features.h3
-rw-r--r--numpy/core/src/common/numpyos.c22
-rw-r--r--numpy/core/src/common/simd/avx2/memory.h16
-rw-r--r--numpy/core/src/common/simd/sse/memory.h53
-rw-r--r--numpy/core/src/common/simd/vec/arithmetic.h3
-rw-r--r--numpy/core/src/common/simd/vec/conversion.h8
-rw-r--r--numpy/core/src/common/simd/vec/math.h2
-rw-r--r--numpy/core/src/common/simd/vec/memory.h10
-rw-r--r--numpy/core/src/common/simd/vec/misc.h28
-rw-r--r--numpy/core/src/multiarray/array_coercion.c164
-rw-r--r--numpy/core/src/multiarray/array_coercion.h10
-rw-r--r--numpy/core/src/multiarray/array_method.c45
-rw-r--r--numpy/core/src/multiarray/array_method.h187
-rw-r--r--numpy/core/src/multiarray/arrayobject.c9
-rw-r--r--numpy/core/src/multiarray/convert.c19
-rw-r--r--numpy/core/src/multiarray/convert_datatype.c41
-rw-r--r--numpy/core/src/multiarray/convert_datatype.h2
-rw-r--r--numpy/core/src/multiarray/ctors.c3
-rw-r--r--numpy/core/src/multiarray/datetime.c8
-rw-r--r--numpy/core/src/multiarray/descriptor.c136
-rw-r--r--numpy/core/src/multiarray/descriptor.h23
-rw-r--r--numpy/core/src/multiarray/dtype_transfer.c395
-rw-r--r--numpy/core/src/multiarray/dtype_transfer.h1
-rw-r--r--numpy/core/src/multiarray/dtype_traversal.c474
-rw-r--r--numpy/core/src/multiarray/dtype_traversal.h82
-rw-r--r--numpy/core/src/multiarray/dtypemeta.c10
-rw-r--r--numpy/core/src/multiarray/dtypemeta.h63
-rw-r--r--numpy/core/src/multiarray/experimental_public_dtype_api.c127
-rw-r--r--numpy/core/src/multiarray/item_selection.c299
-rw-r--r--numpy/core/src/multiarray/methods.c15
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c6
-rw-r--r--numpy/core/src/multiarray/nditer_api.c71
-rw-r--r--numpy/core/src/multiarray/nditer_constr.c42
-rw-r--r--numpy/core/src/multiarray/nditer_impl.h3
-rw-r--r--numpy/core/src/multiarray/refcount.c113
-rw-r--r--numpy/core/src/multiarray/refcount.h8
-rw-r--r--numpy/core/src/multiarray/textreading/tokenize.cpp2
-rw-r--r--numpy/core/src/multiarray/usertypes.c16
-rw-r--r--numpy/core/src/npysort/heapsort.cpp3
-rw-r--r--numpy/core/src/npysort/mergesort.cpp1
-rw-r--r--numpy/core/src/npysort/npysort_heapsort.h4
-rw-r--r--numpy/core/src/npysort/quicksort.cpp127
-rw-r--r--numpy/core/src/npysort/simd_qsort.dispatch.cpp44
-rw-r--r--numpy/core/src/npysort/simd_qsort.hpp19
-rw-r--r--numpy/core/src/npysort/simd_qsort_16bit.dispatch.cpp31
-rw-r--r--numpy/core/src/npysort/x86-qsort.dispatch.cpp835
-rw-r--r--numpy/core/src/npysort/x86-qsort.h28
m---------numpy/core/src/npysort/x86-simd-sort0
-rw-r--r--numpy/core/src/umath/_scaled_float_dtype.c2
-rw-r--r--numpy/core/src/umath/_umath_tests.c.src7
-rw-r--r--numpy/core/src/umath/fast_loop_macros.h17
-rw-r--r--numpy/core/src/umath/legacy_array_method.c2
-rw-r--r--numpy/core/src/umath/loops.c.src241
-rw-r--r--numpy/core/src/umath/loops.h.src127
-rw-r--r--numpy/core/src/umath/loops_autovec.dispatch.c.src287
-rw-r--r--numpy/core/src/umath/loops_exponent_log.dispatch.c.src2
-rw-r--r--numpy/core/src/umath/wrapping_array_method.c7
-rw-r--r--numpy/core/tests/test_array_coercion.py2
-rw-r--r--numpy/core/tests/test_cpu_features.py6
-rw-r--r--numpy/core/tests/test_custom_dtypes.py14
-rw-r--r--numpy/core/tests/test_deprecations.py21
-rw-r--r--numpy/core/tests/test_dtype.py20
-rw-r--r--numpy/core/tests/test_getlimits.py60
-rw-r--r--numpy/core/tests/test_indexing.py2
-rw-r--r--numpy/core/tests/test_item_selection.py79
-rw-r--r--numpy/core/tests/test_mem_overlap.py2
-rw-r--r--numpy/core/tests/test_memmap.py4
-rw-r--r--numpy/core/tests/test_multiarray.py87
-rw-r--r--numpy/core/tests/test_nditer.py32
-rw-r--r--numpy/core/tests/test_numeric.py8
-rw-r--r--numpy/core/tests/test_regression.py23
-rw-r--r--numpy/ctypeslib.pyi4
-rw-r--r--numpy/distutils/ccompiler_opt.py16
-rw-r--r--numpy/distutils/checks/cpu_avx512_spr.c22
-rw-r--r--numpy/fft/_pocketfft.c21
-rw-r--r--numpy/fft/helper.pyi8
-rw-r--r--numpy/lib/__init__.pyi1
-rw-r--r--numpy/lib/function_base.py4
-rw-r--r--numpy/lib/index_tricks.py19
-rw-r--r--numpy/lib/index_tricks.pyi2
-rw-r--r--numpy/lib/tests/test_function_base.py8
-rw-r--r--numpy/lib/tests/test_type_check.py2
-rw-r--r--numpy/lib/twodim_base.py2
-rw-r--r--numpy/linalg/linalg.py22
-rw-r--r--numpy/linalg/umath_linalg.cpp80
-rw-r--r--numpy/ma/core.py14
-rw-r--r--numpy/ma/core.pyi4
-rw-r--r--numpy/ma/extras.py4
-rw-r--r--numpy/ma/tests/test_core.py53
-rw-r--r--numpy/ma/tests/test_extras.py4
-rw-r--r--numpy/ma/tests/test_old_ma.py8
-rw-r--r--numpy/ma/testutils.py2
-rw-r--r--numpy/meson.build7
-rw-r--r--numpy/random/_bounded_integers.pyx.in88
-rw-r--r--numpy/random/_generator.pyi99
-rw-r--r--numpy/random/_generator.pyx56
-rw-r--r--numpy/random/bit_generator.pyi3
-rw-r--r--numpy/random/bit_generator.pyx59
-rw-r--r--numpy/random/meson.build2
-rw-r--r--numpy/random/mtrand.pyx2
-rw-r--r--numpy/random/tests/test_direct.py40
-rw-r--r--numpy/random/tests/test_generator_mt19937.py2
-rw-r--r--numpy/testing/_private/utils.py35
-rw-r--r--numpy/testing/overrides.py6
-rw-r--r--numpy/tests/test_public_api.py4
136 files changed, 3939 insertions, 3018 deletions
diff --git a/numpy/__config__.py.in b/numpy/__config__.py.in
index 659a09b26..6c6c21cb8 100644
--- a/numpy/__config__.py.in
+++ b/numpy/__config__.py.in
@@ -21,10 +21,8 @@ def _cleanup(d):
Removes empty values in a `dict` recursively
This ensures we remove values that Meson could not provide to CONFIG
"""
- if type(d) is dict:
- return dict(
- (k, _cleanup(v)) for k, v in d.items() if v and _cleanup(v)
- )
+ if isinstance(d, dict):
+ return {k: _cleanup(v) for k, v in d.items() if v and _cleanup(v)}
else:
return d
@@ -64,45 +62,41 @@ CONFIG = _cleanup(
"endian": "@BUILD_CPU_ENDIAN@",
"system": "@BUILD_CPU_SYSTEM@",
},
- "cross-compiled": "@CROSS_COMPILED@",
+ "cross-compiled": bool("@CROSS_COMPILED@".lower().replace("false", "")),
},
"Build Dependencies": {
"blas": {
"name": "@BLAS_NAME@",
- "found": "@BLAS_FOUND@",
+ "found": bool("@BLAS_FOUND@".lower().replace("false", "")),
"version": "@BLAS_VERSION@",
"detection method": "@BLAS_TYPE_NAME@",
- "include directory": "@BLAS_INCLUDEDIR@",
- "lib directory": "@BLAS_LIBDIR@",
+ "include directory": r"@BLAS_INCLUDEDIR@",
+ "lib directory": r"@BLAS_LIBDIR@",
"openblas configuration": "@BLAS_OPENBLAS_CONFIG@",
- "pc file directory": "@BLAS_PCFILEDIR@",
+ "pc file directory": r"@BLAS_PCFILEDIR@",
},
"lapack": {
"name": "@LAPACK_NAME@",
- "found": "@LAPACK_FOUND@",
+ "found": bool("@LAPACK_FOUND@".lower().replace("false", "")),
"version": "@LAPACK_VERSION@",
"detection method": "@LAPACK_TYPE_NAME@",
- "include directory": "@LAPACK_INCLUDEDIR@",
- "lib directory": "@LAPACK_LIBDIR@",
+ "include directory": r"@LAPACK_INCLUDEDIR@",
+ "lib directory": r"@LAPACK_LIBDIR@",
"openblas configuration": "@LAPACK_OPENBLAS_CONFIG@",
- "pc file directory": "@LAPACK_PCFILEDIR@",
+ "pc file directory": r"@LAPACK_PCFILEDIR@",
},
},
"Python Information": {
- "path": "@PYTHON_PATH@",
+ "path": r"@PYTHON_PATH@",
"version": "@PYTHON_VERSION@",
},
"SIMD Extensions": {
"baseline": __cpu_baseline__,
"found": [
- feature
- for feature in __cpu_dispatch__
- if __cpu_features__[feature]
+ feature for feature in __cpu_dispatch__ if __cpu_features__[feature]
],
"not found": [
- feature
- for feature in __cpu_dispatch__
- if not __cpu_features__[feature]
+ feature for feature in __cpu_dispatch__ if not __cpu_features__[feature]
],
},
}
diff --git a/numpy/__init__.py b/numpy/__init__.py
index e1666a8d2..e41f7eae6 100644
--- a/numpy/__init__.py
+++ b/numpy/__init__.py
@@ -122,6 +122,9 @@ except NameError:
if __NUMPY_SETUP__:
sys.stderr.write('Running from numpy source directory.\n')
else:
+ # Allow distributors to run custom init code before importing numpy.core
+ from . import _distributor_init
+
try:
from numpy.__config__ import show as show_config
except ImportError as e:
@@ -137,9 +140,6 @@ else:
# mapping of {name: (value, deprecation_msg)}
__deprecated_attrs__ = {}
- # Allow distributors to run custom init code
- from . import _distributor_init
-
from . import core
from .core import *
from . import compat
@@ -221,7 +221,7 @@ else:
del _msg, _type_info
- from .core import round, abs, max, min
+ from .core import abs
# now that numpy modules are imported, can initialize limits
core.getlimits._register_known_types()
diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi
index fb041ddc4..f909005b1 100644
--- a/numpy/__init__.pyi
+++ b/numpy/__init__.pyi
@@ -254,6 +254,8 @@ from numpy.core.fromnumeric import (
any as any,
cumsum as cumsum,
ptp as ptp,
+ max as max,
+ min as min,
amax as amax,
amin as amin,
prod as prod,
@@ -261,6 +263,7 @@ from numpy.core.fromnumeric import (
ndim as ndim,
size as size,
around as around,
+ round as round,
mean as mean,
std as std,
var as var,
@@ -458,7 +461,6 @@ from numpy.lib.function_base import (
digitize as digitize,
cov as cov,
corrcoef as corrcoef,
- msort as msort,
median as median,
sinc as sinc,
hamming as hamming,
@@ -667,10 +669,7 @@ test: PytestTester
# Placeholders for classes
# Some of these are aliases; others are wrappers with an identical signature
-round = around
round_ = around
-max = amax
-min = amin
product = prod
cumproduct = cumprod
sometrue = any
@@ -678,7 +677,7 @@ alltrue = all
def show_config() -> None: ...
-_NdArraySubClass = TypeVar("_NdArraySubClass", bound=ndarray)
+_NdArraySubClass = TypeVar("_NdArraySubClass", bound=ndarray[Any, Any])
_DTypeScalar_co = TypeVar("_DTypeScalar_co", covariant=True, bound=generic)
_ByteOrder = L["S", "<", ">", "=", "|", "L", "B", "N", "I"]
@@ -804,7 +803,7 @@ class dtype(Generic[_DTypeScalar_co]):
@overload
def __new__(cls, dtype: _VoidCodes, align: bool = ..., copy: bool = ...) -> dtype[void]: ...
@overload
- def __new__(cls, dtype: _ObjectCodes | type[ct.py_object], align: bool = ..., copy: bool = ...) -> dtype[object_]: ...
+ def __new__(cls, dtype: _ObjectCodes | type[ct.py_object[Any]], align: bool = ..., copy: bool = ...) -> dtype[object_]: ...
# dtype of a dtype is the same dtype
@overload
@@ -928,13 +927,13 @@ class dtype(Generic[_DTypeScalar_co]):
_ArrayLikeInt = Union[
int,
- integer,
- Sequence[Union[int, integer]],
+ integer[Any],
+ Sequence[Union[int, integer[Any]]],
Sequence[Sequence[Any]], # TODO: wait for support for recursive types
- ndarray
+ ndarray[Any, Any]
]
-_FlatIterSelf = TypeVar("_FlatIterSelf", bound=flatiter)
+_FlatIterSelf = TypeVar("_FlatIterSelf", bound=flatiter[Any])
@final
class flatiter(Generic[_NdArraySubClass]):
@@ -952,7 +951,7 @@ class flatiter(Generic[_NdArraySubClass]):
@overload
def __getitem__(
self: flatiter[ndarray[Any, dtype[_ScalarType]]],
- key: int | integer | tuple[int | integer],
+ key: int | integer[Any] | tuple[int | integer[Any]],
) -> _ScalarType: ...
@overload
def __getitem__(
@@ -1147,7 +1146,7 @@ class _ArrayOrScalarCommon:
axis: None | SupportsIndex = ...,
kind: None | _SortKind = ...,
order: None | str | Sequence[str] = ...,
- ) -> ndarray: ...
+ ) -> ndarray[Any, Any]: ...
@overload
def choose(
@@ -1155,7 +1154,7 @@ class _ArrayOrScalarCommon:
choices: ArrayLike,
out: None = ...,
mode: _ModeKind = ...,
- ) -> ndarray: ...
+ ) -> ndarray[Any, Any]: ...
@overload
def choose(
self,
@@ -1171,7 +1170,7 @@ class _ArrayOrScalarCommon:
max: None | ArrayLike = ...,
out: None = ...,
**kwargs: Any,
- ) -> ndarray: ...
+ ) -> ndarray[Any, Any]: ...
@overload
def clip(
self,
@@ -1179,7 +1178,7 @@ class _ArrayOrScalarCommon:
max: ArrayLike = ...,
out: None = ...,
**kwargs: Any,
- ) -> ndarray: ...
+ ) -> ndarray[Any, Any]: ...
@overload
def clip(
self,
@@ -1203,7 +1202,7 @@ class _ArrayOrScalarCommon:
a: ArrayLike,
axis: None | SupportsIndex = ...,
out: None = ...,
- ) -> ndarray: ...
+ ) -> ndarray[Any, Any]: ...
@overload
def compress(
self,
@@ -1222,7 +1221,7 @@ class _ArrayOrScalarCommon:
axis: None | SupportsIndex = ...,
dtype: DTypeLike = ...,
out: None = ...,
- ) -> ndarray: ...
+ ) -> ndarray[Any, Any]: ...
@overload
def cumprod(
self,
@@ -1237,7 +1236,7 @@ class _ArrayOrScalarCommon:
axis: None | SupportsIndex = ...,
dtype: DTypeLike = ...,
out: None = ...,
- ) -> ndarray: ...
+ ) -> ndarray[Any, Any]: ...
@overload
def cumsum(
self,
@@ -1482,7 +1481,7 @@ class _SupportsImag(Protocol[_T_co]):
class ndarray(_ArrayOrScalarCommon, Generic[_ShapeType, _DType_co]):
__hash__: ClassVar[None]
@property
- def base(self) -> None | ndarray: ...
+ def base(self) -> None | ndarray[Any, Any]: ...
@property
def ndim(self) -> int: ...
@property
@@ -1649,7 +1648,7 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeType, _DType_co]):
# 1D + 1D returns a scalar;
# all other with at least 1 non-0D array return an ndarray.
@overload
- def dot(self, b: _ScalarLike_co, out: None = ...) -> ndarray: ...
+ def dot(self, b: _ScalarLike_co, out: None = ...) -> ndarray[Any, Any]: ...
@overload
def dot(self, b: ArrayLike, out: None = ...) -> Any: ... # type: ignore[misc]
@overload
@@ -2826,20 +2825,20 @@ class integer(number[_NBit1]): # type: ignore
def __index__(self) -> int: ...
__truediv__: _IntTrueDiv[_NBit1]
__rtruediv__: _IntTrueDiv[_NBit1]
- def __mod__(self, value: _IntLike_co) -> integer: ...
- def __rmod__(self, value: _IntLike_co) -> integer: ...
+ def __mod__(self, value: _IntLike_co) -> integer[Any]: ...
+ def __rmod__(self, value: _IntLike_co) -> integer[Any]: ...
def __invert__(self: _IntType) -> _IntType: ...
# Ensure that objects annotated as `integer` support bit-wise operations
- def __lshift__(self, other: _IntLike_co) -> integer: ...
- def __rlshift__(self, other: _IntLike_co) -> integer: ...
- def __rshift__(self, other: _IntLike_co) -> integer: ...
- def __rrshift__(self, other: _IntLike_co) -> integer: ...
- def __and__(self, other: _IntLike_co) -> integer: ...
- def __rand__(self, other: _IntLike_co) -> integer: ...
- def __or__(self, other: _IntLike_co) -> integer: ...
- def __ror__(self, other: _IntLike_co) -> integer: ...
- def __xor__(self, other: _IntLike_co) -> integer: ...
- def __rxor__(self, other: _IntLike_co) -> integer: ...
+ def __lshift__(self, other: _IntLike_co) -> integer[Any]: ...
+ def __rlshift__(self, other: _IntLike_co) -> integer[Any]: ...
+ def __rshift__(self, other: _IntLike_co) -> integer[Any]: ...
+ def __rrshift__(self, other: _IntLike_co) -> integer[Any]: ...
+ def __and__(self, other: _IntLike_co) -> integer[Any]: ...
+ def __rand__(self, other: _IntLike_co) -> integer[Any]: ...
+ def __or__(self, other: _IntLike_co) -> integer[Any]: ...
+ def __ror__(self, other: _IntLike_co) -> integer[Any]: ...
+ def __xor__(self, other: _IntLike_co) -> integer[Any]: ...
+ def __rxor__(self, other: _IntLike_co) -> integer[Any]: ...
class signedinteger(integer[_NBit1]):
def __init__(self, value: _IntValue = ..., /) -> None: ...
@@ -2964,8 +2963,8 @@ ulonglong = unsignedinteger[_NBitLongLong]
class inexact(number[_NBit1]): # type: ignore
def __getnewargs__(self: inexact[_64Bit]) -> tuple[float, ...]: ...
-_IntType = TypeVar("_IntType", bound=integer)
-_FloatType = TypeVar('_FloatType', bound=floating)
+_IntType = TypeVar("_IntType", bound=integer[Any])
+_FloatType = TypeVar('_FloatType', bound=floating[Any])
class floating(inexact[_NBit1]):
def __init__(self, value: _FloatValue = ..., /) -> None: ...
@@ -3139,10 +3138,6 @@ infty: Final[float]
nan: Final[float]
pi: Final[float]
-CLIP: L[0]
-WRAP: L[1]
-RAISE: L[2]
-
ERR_IGNORE: L[0]
ERR_WARN: L[1]
ERR_RAISE: L[2]
@@ -3211,7 +3206,7 @@ class ufunc:
# can't type them very precisely.
reduce: Any
accumulate: Any
- reduce: Any
+ reduceat: Any
outer: Any
# Similarly at won't be defined for ufuncs that return multiple
# outputs, so we can't type it very precisely.
diff --git a/numpy/_typing/_scalars.py b/numpy/_typing/_scalars.py
index 1ea88e957..e46ff04a0 100644
--- a/numpy/_typing/_scalars.py
+++ b/numpy/_typing/_scalars.py
@@ -10,13 +10,13 @@ _CharLike_co = Union[str, bytes]
# The 6 `<X>Like_co` type-aliases below represent all scalars that can be
# coerced into `<X>` (with the casting rule `same_kind`)
_BoolLike_co = Union[bool, np.bool_]
-_UIntLike_co = Union[_BoolLike_co, np.unsignedinteger]
-_IntLike_co = Union[_BoolLike_co, int, np.integer]
-_FloatLike_co = Union[_IntLike_co, float, np.floating]
-_ComplexLike_co = Union[_FloatLike_co, complex, np.complexfloating]
+_UIntLike_co = Union[_BoolLike_co, np.unsignedinteger[Any]]
+_IntLike_co = Union[_BoolLike_co, int, np.integer[Any]]
+_FloatLike_co = Union[_IntLike_co, float, np.floating[Any]]
+_ComplexLike_co = Union[_FloatLike_co, complex, np.complexfloating[Any, Any]]
_TD64Like_co = Union[_IntLike_co, np.timedelta64]
-_NumberLike_co = Union[int, float, complex, np.number, np.bool_]
+_NumberLike_co = Union[int, float, complex, np.number[Any], np.bool_]
_ScalarLike_co = Union[
int,
float,
diff --git a/numpy/array_api/__init__.py b/numpy/array_api/__init__.py
index 5e58ee0a8..e154b9952 100644
--- a/numpy/array_api/__init__.py
+++ b/numpy/array_api/__init__.py
@@ -333,6 +333,10 @@ __all__ += [
"trunc",
]
+from ._indexing_functions import take
+
+__all__ += ["take"]
+
# linalg is an extension in the array API spec, which is a sub-namespace. Only
# a subset of functions in it are imported into the top-level namespace.
from . import linalg
diff --git a/numpy/array_api/_array_object.py b/numpy/array_api/_array_object.py
index c4746fad9..eee117be6 100644
--- a/numpy/array_api/_array_object.py
+++ b/numpy/array_api/_array_object.py
@@ -56,7 +56,7 @@ class Array:
functions, such as asarray().
"""
- _array: np.ndarray
+ _array: np.ndarray[Any, Any]
# Use a custom constructor instead of __init__, as manually initializing
# this class is not supported API.
diff --git a/numpy/array_api/_indexing_functions.py b/numpy/array_api/_indexing_functions.py
new file mode 100644
index 000000000..ba56bcd6f
--- /dev/null
+++ b/numpy/array_api/_indexing_functions.py
@@ -0,0 +1,18 @@
+from __future__ import annotations
+
+from ._array_object import Array
+from ._dtypes import _integer_dtypes
+
+import numpy as np
+
+def take(x: Array, indices: Array, /, *, axis: int) -> Array:
+ """
+ Array API compatible wrapper for :py:func:`np.take <numpy.take>`.
+
+ See its docstring for more information.
+ """
+ if indices.dtype not in _integer_dtypes:
+ raise TypeError("Only integer dtypes are allowed in indexing")
+ if indices.ndim != 1:
+ raise ValueError("Only 1-dim indices array is supported")
+ return Array._new(np.take(x._array, indices._array, axis=axis))
diff --git a/numpy/array_api/tests/test_indexing_functions.py b/numpy/array_api/tests/test_indexing_functions.py
new file mode 100644
index 000000000..9e05c6386
--- /dev/null
+++ b/numpy/array_api/tests/test_indexing_functions.py
@@ -0,0 +1,24 @@
+import pytest
+
+from numpy import array_api as xp
+
+
+@pytest.mark.parametrize(
+ "x, indices, axis, expected",
+ [
+ ([2, 3], [1, 1, 0], 0, [3, 3, 2]),
+ ([2, 3], [1, 1, 0], -1, [3, 3, 2]),
+ ([[2, 3]], [1], -1, [[3]]),
+ ([[2, 3]], [0, 0], 0, [[2, 3], [2, 3]]),
+ ],
+)
+def test_take_function(x, indices, axis, expected):
+ """
+ Indices respect relative order of a descending stable-sort
+
+ See https://github.com/numpy/numpy/issues/20778
+ """
+ x = xp.asarray(x)
+ indices = xp.asarray(indices)
+ out = xp.take(x, indices, axis=axis)
+ assert xp.all(out == xp.asarray(expected))
diff --git a/numpy/core/__init__.py b/numpy/core/__init__.py
index 1079c41df..142c24ee0 100644
--- a/numpy/core/__init__.py
+++ b/numpy/core/__init__.py
@@ -92,7 +92,6 @@ from . import einsumfunc
from .einsumfunc import *
del nt
-from .fromnumeric import amax as max, amin as min, round_ as round
from .numeric import absolute as abs
# do this after everything else, to minimize the chance of this misleadingly
diff --git a/numpy/core/_asarray.pyi b/numpy/core/_asarray.pyi
index 473bc037c..69d1528d4 100644
--- a/numpy/core/_asarray.pyi
+++ b/numpy/core/_asarray.pyi
@@ -1,10 +1,10 @@
from collections.abc import Iterable
-from typing import TypeVar, Union, overload, Literal
+from typing import Any, TypeVar, Union, overload, Literal
from numpy import ndarray
from numpy._typing import DTypeLike, _SupportsArrayFunc
-_ArrayType = TypeVar("_ArrayType", bound=ndarray)
+_ArrayType = TypeVar("_ArrayType", bound=ndarray[Any, Any])
_Requirements = Literal[
"C", "C_CONTIGUOUS", "CONTIGUOUS",
@@ -31,7 +31,7 @@ def require(
requirements: _E | Iterable[_RequirementsWithE] = ...,
*,
like: _SupportsArrayFunc = ...
-) -> ndarray: ...
+) -> ndarray[Any, Any]: ...
@overload
def require(
a: object,
@@ -39,4 +39,4 @@ def require(
requirements: None | _Requirements | Iterable[_Requirements] = ...,
*,
like: _SupportsArrayFunc = ...
-) -> ndarray: ...
+) -> ndarray[Any, Any]: ...
diff --git a/numpy/core/_type_aliases.pyi b/numpy/core/_type_aliases.pyi
index bbead0cb5..c0b6f1a80 100644
--- a/numpy/core/_type_aliases.pyi
+++ b/numpy/core/_type_aliases.pyi
@@ -1,12 +1,12 @@
-from typing import TypedDict
+from typing import Any, TypedDict
from numpy import generic, signedinteger, unsignedinteger, floating, complexfloating
class _SCTypes(TypedDict):
- int: list[type[signedinteger]]
- uint: list[type[unsignedinteger]]
- float: list[type[floating]]
- complex: list[type[complexfloating]]
+ int: list[type[signedinteger[Any]]]
+ uint: list[type[unsignedinteger[Any]]]
+ float: list[type[floating[Any]]]
+ complex: list[type[complexfloating[Any, Any]]]
others: list[type]
sctypeDict: dict[int | str, type[generic]]
diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py
index 34fd0c9d1..1fe2e43c0 100644
--- a/numpy/core/code_generators/generate_umath.py
+++ b/numpy/core/code_generators/generate_umath.py
@@ -61,9 +61,6 @@ class TypeDescription:
cfunc_alias : str or none, optional
Appended to inner loop C function name, e.g., FLOAT_{cfunc_alias}. See make_arrays.
NOTE: it doesn't support 'astype'
- simd : list
- Available SIMD ufunc loops, dispatched at runtime in specified order
- Currently only supported for simples types (see make_arrays)
dispatch : str or None, optional
Dispatch-able source name without its extension '.dispatch.c' that
contains the definition of ufunc, dispatched at runtime depending on the
@@ -71,7 +68,7 @@ class TypeDescription:
NOTE: it doesn't support 'astype'
"""
def __init__(self, type, f=None, in_=None, out=None, astype=None, cfunc_alias=None,
- simd=None, dispatch=None):
+ dispatch=None):
self.type = type
self.func_data = f
if astype is None:
@@ -84,7 +81,6 @@ class TypeDescription:
out = out.replace('P', type)
self.out = out
self.cfunc_alias = cfunc_alias
- self.simd = simd
self.dispatch = dispatch
def finish_signature(self, nin, nout):
@@ -146,8 +142,9 @@ def build_func_data(types, f):
return func_data
def TD(types, f=None, astype=None, in_=None, out=None, cfunc_alias=None,
- simd=None, dispatch=None):
- """Generate a TypeDescription instance for each item in types
+ dispatch=None):
+ """
+ Generate a TypeDescription instance for each item in types
"""
if f is not None:
if isinstance(f, str):
@@ -172,12 +169,6 @@ def TD(types, f=None, astype=None, in_=None, out=None, cfunc_alias=None,
raise ValueError("Number of types and outputs do not match")
tds = []
for t, fd, i, o in zip(types, func_data, in_, out):
- # [(simd-name, list of types)]
- if simd is not None:
- simdt = [k for k, v in simd if t in v]
- else:
- simdt = []
-
# [(dispatch file name without extension '.dispatch.c*', list of types)]
if dispatch:
dispt = ([k for k, v in dispatch if t in v]+[None])[0]
@@ -185,7 +176,7 @@ def TD(types, f=None, astype=None, in_=None, out=None, cfunc_alias=None,
dispt = None
tds.append(TypeDescription(
t, f=fd, in_=i, out=o, astype=astype, cfunc_alias=cfunc_alias,
- simd=simdt, dispatch=dispt
+ dispatch=dispt
))
return tds
@@ -352,8 +343,10 @@ defdict = {
docstrings.get('numpy.core.umath.add'),
'PyUFunc_AdditionTypeResolver',
TD('?', cfunc_alias='logical_or', dispatch=[('loops_logical', '?')]),
- TD(no_bool_times_obj, simd=[('avx2', ints)],
- dispatch=[('loops_arithm_fp', 'fdFD')]),
+ TD(no_bool_times_obj, dispatch=[
+ ('loops_arithm_fp', 'fdFD'),
+ ('loops_autovec', ints),
+ ]),
[TypeDescription('M', FullTypeDescr, 'Mm', 'M'),
TypeDescription('m', FullTypeDescr, 'mm', 'm'),
TypeDescription('M', FullTypeDescr, 'mM', 'M'),
@@ -365,8 +358,10 @@ defdict = {
Ufunc(2, 1, None, # Zero is only a unit to the right, not the left
docstrings.get('numpy.core.umath.subtract'),
'PyUFunc_SubtractionTypeResolver',
- TD(no_bool_times_obj, simd=[('avx2', ints)],
- dispatch=[('loops_arithm_fp', 'fdFD')]),
+ TD(no_bool_times_obj, dispatch=[
+ ('loops_arithm_fp', 'fdFD'),
+ ('loops_autovec', ints),
+ ]),
[TypeDescription('M', FullTypeDescr, 'Mm', 'M'),
TypeDescription('m', FullTypeDescr, 'mm', 'm'),
TypeDescription('M', FullTypeDescr, 'MM', 'm'),
@@ -380,8 +375,10 @@ defdict = {
'PyUFunc_MultiplicationTypeResolver',
TD('?', cfunc_alias='logical_and',
dispatch=[('loops_logical', '?')]),
- TD(no_bool_times_obj, simd=[('avx2', ints)],
- dispatch=[('loops_arithm_fp', 'fdFD')]),
+ TD(no_bool_times_obj, dispatch=[
+ ('loops_arithm_fp', 'fdFD'),
+ ('loops_autovec', ints),
+ ]),
[TypeDescription('m', FullTypeDescr, 'mq', 'm'),
TypeDescription('m', FullTypeDescr, 'qm', 'm'),
TypeDescription('m', FullTypeDescr, 'md', 'm'),
@@ -421,8 +418,10 @@ defdict = {
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.conjugate'),
None,
- TD(ints+flts+cmplx, simd=[('avx2', ints)],
- dispatch=[('loops_arithm_fp', 'FD')]),
+ TD(ints+flts+cmplx, dispatch=[
+ ('loops_arithm_fp', 'FD'),
+ ('loops_autovec', ints),
+ ]),
TD(P, f='conjugate'),
),
'fmod':
@@ -437,15 +436,21 @@ defdict = {
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.square'),
None,
- TD(ints+inexact, simd=[('avx2', ints)],
- dispatch=[('loops_unary_fp', 'fd'), ('loops_arithm_fp', 'FD')]),
+ TD(ints+inexact, dispatch=[
+ ('loops_unary_fp', 'fd'),
+ ('loops_arithm_fp', 'FD'),
+ ('loops_autovec', ints),
+ ]),
TD(O, f='Py_square'),
),
'reciprocal':
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.reciprocal'),
None,
- TD(ints+inexact, simd=[('avx2', ints)], dispatch=[('loops_unary_fp', 'fd')]),
+ TD(ints+inexact, dispatch=[
+ ('loops_unary_fp', 'fd'),
+ ('loops_autovec', ints),
+ ]),
TD(O, f='Py_reciprocal'),
),
# This is no longer used as numpy.ones_like, however it is
@@ -477,8 +482,11 @@ defdict = {
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.absolute'),
'PyUFunc_AbsoluteTypeResolver',
- TD(bints+flts+timedeltaonly, dispatch=[('loops_unary_fp', 'fd'),
- ('loops_logical', '?')]),
+ TD(bints+flts+timedeltaonly, dispatch=[
+ ('loops_unary_fp', 'fd'),
+ ('loops_logical', '?'),
+ ('loops_autovec', ints + 'e'),
+ ]),
TD(cmplx, dispatch=[('loops_unary_complex', 'FD')],
out=('f', 'd', 'g')),
TD(O, f='PyNumber_Absolute'),
@@ -509,7 +517,7 @@ defdict = {
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.sign'),
'PyUFunc_SimpleUniformOperationTypeResolver',
- TD(nobool_or_datetime),
+ TD(nobool_or_datetime, dispatch=[('loops_autovec', ints)]),
),
'greater':
Ufunc(2, 1, None,
@@ -563,24 +571,30 @@ defdict = {
Ufunc(2, 1, True_,
docstrings.get('numpy.core.umath.logical_and'),
'PyUFunc_SimpleBinaryComparisonTypeResolver',
- TD(nodatetime_or_obj, out='?', simd=[('avx2', ints)],
- dispatch=[('loops_logical', '?')]),
+ TD(nodatetime_or_obj, out='?', dispatch=[
+ ('loops_logical', '?'),
+ ('loops_autovec', ints),
+ ]),
TD(O, f='npy_ObjectLogicalAnd'),
),
'logical_not':
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.logical_not'),
None,
- TD(nodatetime_or_obj, out='?', simd=[('avx2', ints)],
- dispatch=[('loops_logical', '?')]),
+ TD(nodatetime_or_obj, out='?', dispatch=[
+ ('loops_logical', '?'),
+ ('loops_autovec', ints),
+ ]),
TD(O, f='npy_ObjectLogicalNot'),
),
'logical_or':
Ufunc(2, 1, False_,
docstrings.get('numpy.core.umath.logical_or'),
'PyUFunc_SimpleBinaryComparisonTypeResolver',
- TD(nodatetime_or_obj, out='?', simd=[('avx2', ints)],
- dispatch=[('loops_logical', '?')]),
+ TD(nodatetime_or_obj, out='?', dispatch=[
+ ('loops_logical', '?'),
+ ('loops_autovec', ints),
+ ]),
TD(O, f='npy_ObjectLogicalOr'),
),
'logical_xor':
@@ -589,7 +603,9 @@ defdict = {
'PyUFunc_SimpleBinaryComparisonTypeResolver',
TD('?', out='?', cfunc_alias='not_equal',
dispatch=[('loops_comparison', '?')]),
- TD(no_bool_times_obj, out='?'),
+ TD(no_bool_times_obj, out='?', dispatch=[
+ ('loops_autovec', ints),
+ ]),
# TODO: using obj.logical_xor() seems pretty much useless:
TD(P, f='logical_xor'),
),
@@ -656,7 +672,7 @@ defdict = {
None,
TD('?', cfunc_alias='logical_and',
dispatch=[('loops_logical', '?')]),
- TD(ints, simd=[('avx2', ints)]),
+ TD(ints, dispatch=[('loops_autovec', ints)]),
TD(O, f='PyNumber_And'),
),
'bitwise_or':
@@ -664,7 +680,7 @@ defdict = {
docstrings.get('numpy.core.umath.bitwise_or'),
None,
TD('?', cfunc_alias='logical_or', dispatch=[('loops_logical', '?')]),
- TD(ints, simd=[('avx2', ints)]),
+ TD(ints, dispatch=[('loops_autovec', ints)]),
TD(O, f='PyNumber_Or'),
),
'bitwise_xor':
@@ -673,7 +689,7 @@ defdict = {
None,
TD('?', cfunc_alias='not_equal',
dispatch=[('loops_comparison', '?')]),
- TD(ints, simd=[('avx2', ints)]),
+ TD(ints, dispatch=[('loops_autovec', ints)]),
TD(O, f='PyNumber_Xor'),
),
'invert':
@@ -682,21 +698,21 @@ defdict = {
None,
TD('?', cfunc_alias='logical_not',
dispatch=[('loops_logical', '?')]),
- TD(ints, simd=[('avx2', ints)]),
+ TD(ints, dispatch=[('loops_autovec', ints)]),
TD(O, f='PyNumber_Invert'),
),
'left_shift':
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.left_shift'),
None,
- TD(ints, simd=[('avx2', ints)]),
+ TD(ints, dispatch=[('loops_autovec', ints)]),
TD(O, f='PyNumber_Lshift'),
),
'right_shift':
Ufunc(2, 1, None,
docstrings.get('numpy.core.umath.right_shift'),
None,
- TD(ints, simd=[('avx2', ints)]),
+ TD(ints, dispatch=[('loops_autovec', ints)]),
TD(O, f='PyNumber_Rshift'),
),
'heaviside':
@@ -986,7 +1002,10 @@ defdict = {
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.isnan'),
'PyUFunc_IsFiniteTypeResolver',
- TD(noobj, out='?', dispatch=[('loops_unary_fp_le', inexactvec)]),
+ TD(noobj, out='?', dispatch=[
+ ('loops_unary_fp_le', inexactvec),
+ ('loops_autovec', bints),
+ ]),
),
'isnat':
Ufunc(1, 1, None,
@@ -998,13 +1017,19 @@ defdict = {
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.isinf'),
'PyUFunc_IsFiniteTypeResolver',
- TD(noobj, out='?', dispatch=[('loops_unary_fp_le', inexactvec)]),
+ TD(noobj, out='?', dispatch=[
+ ('loops_unary_fp_le', inexactvec),
+ ('loops_autovec', bints + 'mM'),
+ ]),
),
'isfinite':
Ufunc(1, 1, None,
docstrings.get('numpy.core.umath.isfinite'),
'PyUFunc_IsFiniteTypeResolver',
- TD(noobj, out='?', dispatch=[('loops_unary_fp_le', inexactvec)]),
+ TD(noobj, out='?', dispatch=[
+ ('loops_unary_fp_le', inexactvec),
+ ('loops_autovec', bints),
+ ]),
),
'signbit':
Ufunc(1, 1, None,
@@ -1156,18 +1181,6 @@ def make_arrays(funcdict):
datalist.append('(void *)NULL')
tname = english_upper(chartoname[t.type])
cfunc_fname = f"{tname}_{cfunc_alias}"
- if t.simd is not None:
- for vt in t.simd:
- code2list.append(textwrap.dedent("""\
- #ifdef HAVE_ATTRIBUTE_TARGET_{ISA}
- if (NPY_CPU_HAVE({ISA})) {{
- {fname}_functions[{idx}] = {cname}_{isa};
- }}
- #endif
- """).format(
- ISA=vt.upper(), isa=vt,
- fname=name, cname=cfunc_fname, idx=k
- ))
else:
try:
thedict = arity_lookup[uf.nin, uf.nout]
diff --git a/numpy/core/code_generators/ufunc_docstrings.py b/numpy/core/code_generators/ufunc_docstrings.py
index ecfc5affe..05f947c15 100644
--- a/numpy/core/code_generators/ufunc_docstrings.py
+++ b/numpy/core/code_generators/ufunc_docstrings.py
@@ -2021,7 +2021,7 @@ add_newdoc('numpy.core.umath', 'log',
has a branch cut `[-inf, 0]` and is continuous from above on it. `log`
handles the floating-point negative zero as an infinitesimal negative
number, conforming to the C99 standard.
-
+
In the cases where the input has a negative real part and a very small
negative complex part (approaching 0), the result is so close to `-pi`
that it evaluates to exactly `-pi`.
@@ -2457,7 +2457,7 @@ add_newdoc('numpy.core.umath', 'maximum',
"""
Element-wise maximum of array elements.
- Compare two arrays and returns a new array containing the element-wise
+ Compare two arrays and return a new array containing the element-wise
maxima. If one of the elements being compared is a NaN, then that
element is returned. If both elements are NaNs then the first is
returned. The latter distinction is important for complex NaNs, which
@@ -2516,7 +2516,7 @@ add_newdoc('numpy.core.umath', 'minimum',
"""
Element-wise minimum of array elements.
- Compare two arrays and returns a new array containing the element-wise
+ Compare two arrays and return a new array containing the element-wise
minima. If one of the elements being compared is a NaN, then that
element is returned. If both elements are NaNs then the first is
returned. The latter distinction is important for complex NaNs, which
@@ -2575,7 +2575,7 @@ add_newdoc('numpy.core.umath', 'fmax',
"""
Element-wise maximum of array elements.
- Compare two arrays and returns a new array containing the element-wise
+ Compare two arrays and return a new array containing the element-wise
maxima. If one of the elements being compared is a NaN, then the
non-nan element is returned. If both elements are NaNs then the first
is returned. The latter distinction is important for complex NaNs,
@@ -2633,7 +2633,7 @@ add_newdoc('numpy.core.umath', 'fmin',
"""
Element-wise minimum of array elements.
- Compare two arrays and returns a new array containing the element-wise
+ Compare two arrays and return a new array containing the element-wise
minima. If one of the elements being compared is a NaN, then the
non-nan element is returned. If both elements are NaNs then the first
is returned. The latter distinction is important for complex NaNs,
diff --git a/numpy/core/config.h.in b/numpy/core/config.h.in
index 943a90cc8..e3b559753 100644
--- a/numpy/core/config.h.in
+++ b/numpy/core/config.h.in
@@ -29,28 +29,12 @@
#mesondefine HAVE___BUILTIN_BSWAP64
#mesondefine HAVE___BUILTIN_EXPECT
#mesondefine HAVE___BUILTIN_MUL_OVERFLOW
-#mesondefine HAVE__M_FROM_INT64
-#mesondefine HAVE__MM_LOAD_PS
-#mesondefine HAVE__MM_PREFETCH
-#mesondefine HAVE__MM_LOAD_PD
#mesondefine HAVE___BUILTIN_PREFETCH
-#mesondefine HAVE_LINK_AVX
-#mesondefine HAVE_LINK_AVX2
-#mesondefine HAVE_LINK_AVX512F
-#mesondefine HAVE_LINK_AVX512_SKX
-#mesondefine HAVE_XGETBV
#mesondefine HAVE_ATTRIBUTE_OPTIMIZE_UNROLL_LOOPS
#mesondefine HAVE_ATTRIBUTE_OPTIMIZE_OPT_3
#mesondefine HAVE_ATTRIBUTE_OPTIMIZE_OPT_2
#mesondefine HAVE_ATTRIBUTE_NONNULL
-#mesondefine HAVE_ATTRIBUTE_TARGET_AVX
-#mesondefine HAVE_ATTRIBUTE_TARGET_AVX2
-#mesondefine HAVE_ATTRIBUTE_TARGET_AVX512F
-#mesondefine HAVE_ATTRIBUTE_TARGET_AVX512_SKX
-#mesondefine HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS
-#mesondefine HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS
-#mesondefine HAVE_ATTRIBUTE_TARGET_AVX512_SKX_WITH_INTRINSICS
/* C99 complex support and complex.h are not universal */
#mesondefine HAVE_COMPLEX_H
diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py
index e7366898e..7ea2313d1 100644
--- a/numpy/core/fromnumeric.py
+++ b/numpy/core/fromnumeric.py
@@ -6,6 +6,7 @@ import types
import warnings
import numpy as np
+from .._utils import set_module
from . import multiarray as mu
from . import overrides
from . import umath as um
@@ -20,8 +21,9 @@ __all__ = [
'all', 'alltrue', 'amax', 'amin', 'any', 'argmax',
'argmin', 'argpartition', 'argsort', 'around', 'choose', 'clip',
'compress', 'cumprod', 'cumproduct', 'cumsum', 'diagonal', 'mean',
+ 'max', 'min',
'ndim', 'nonzero', 'partition', 'prod', 'product', 'ptp', 'put',
- 'ravel', 'repeat', 'reshape', 'resize', 'round_',
+ 'ravel', 'repeat', 'reshape', 'resize', 'round', 'round_',
'searchsorted', 'shape', 'size', 'sometrue', 'sort', 'squeeze',
'std', 'sum', 'swapaxes', 'take', 'trace', 'transpose', 'var',
]
@@ -2695,13 +2697,14 @@ def ptp(a, axis=None, out=None, keepdims=np._NoValue):
return _methods._ptp(a, axis=axis, out=out, **kwargs)
-def _amax_dispatcher(a, axis=None, out=None, keepdims=None, initial=None,
- where=None):
+def _max_dispatcher(a, axis=None, out=None, keepdims=None, initial=None,
+ where=None):
return (a, out)
-@array_function_dispatch(_amax_dispatcher)
-def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
+@array_function_dispatch(_max_dispatcher)
+@set_module('numpy')
+def max(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
where=np._NoValue):
"""
Return the maximum of an array or maximum along an axis.
@@ -2729,7 +2732,7 @@ def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
the result will broadcast correctly against the input array.
If the default value is passed, then `keepdims` will not be
- passed through to the `amax` method of sub-classes of
+ passed through to the ``max`` method of sub-classes of
`ndarray`, however any non-default value will be. If the
sub-class' method does not implement `keepdims` any
exceptions will be raised.
@@ -2748,7 +2751,7 @@ def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
Returns
-------
- amax : ndarray or scalar
+ max : ndarray or scalar
Maximum of `a`. If `axis` is None, the result is a scalar value.
If `axis` is an int, the result is an array of dimension
``a.ndim - 1``. If `axis` is a tuple, the result is an array of
@@ -2775,9 +2778,9 @@ def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
corresponding max value will be NaN as well. To ignore NaN values
(MATLAB behavior), please use nanmax.
- Don't use `amax` for element-wise comparison of 2 arrays; when
+ Don't use `~numpy.max` for element-wise comparison of 2 arrays; when
``a.shape[0]`` is 2, ``maximum(a[0], a[1])`` is faster than
- ``amax(a, axis=0)``.
+ ``max(a, axis=0)``.
Examples
--------
@@ -2785,19 +2788,19 @@ def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
>>> a
array([[0, 1],
[2, 3]])
- >>> np.amax(a) # Maximum of the flattened array
+ >>> np.max(a) # Maximum of the flattened array
3
- >>> np.amax(a, axis=0) # Maxima along the first axis
+ >>> np.max(a, axis=0) # Maxima along the first axis
array([2, 3])
- >>> np.amax(a, axis=1) # Maxima along the second axis
+ >>> np.max(a, axis=1) # Maxima along the second axis
array([1, 3])
- >>> np.amax(a, where=[False, True], initial=-1, axis=0)
+ >>> np.max(a, where=[False, True], initial=-1, axis=0)
array([-1, 3])
>>> b = np.arange(5, dtype=float)
>>> b[2] = np.NaN
- >>> np.amax(b)
+ >>> np.max(b)
nan
- >>> np.amax(b, where=~np.isnan(b), initial=-1)
+ >>> np.max(b, where=~np.isnan(b), initial=-1)
4.0
>>> np.nanmax(b)
4.0
@@ -2805,14 +2808,14 @@ def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
You can use an initial value to compute the maximum of an empty slice, or
to initialize it to a different value:
- >>> np.amax([[-50], [10]], axis=-1, initial=0)
+ >>> np.max([[-50], [10]], axis=-1, initial=0)
array([ 0, 10])
Notice that the initial value is used as one of the elements for which the
maximum is determined, unlike for the default argument Python's max
function, which is only used for empty iterables.
- >>> np.amax([5], initial=6)
+ >>> np.max([5], initial=6)
6
>>> max([5], default=6)
5
@@ -2821,14 +2824,31 @@ def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
keepdims=keepdims, initial=initial, where=where)
-def _amin_dispatcher(a, axis=None, out=None, keepdims=None, initial=None,
- where=None):
+@array_function_dispatch(_max_dispatcher)
+def amax(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
+ where=np._NoValue):
+ """
+ Return the maximum of an array or maximum along an axis.
+
+ `amax` is an alias of `~numpy.max`.
+
+ See Also
+ --------
+ max : alias of this function
+ ndarray.max : equivalent method
+ """
+ return _wrapreduction(a, np.maximum, 'max', axis, None, out,
+ keepdims=keepdims, initial=initial, where=where)
+
+
+def _min_dispatcher(a, axis=None, out=None, keepdims=None, initial=None,
+ where=None):
return (a, out)
-@array_function_dispatch(_amin_dispatcher)
-def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
- where=np._NoValue):
+@array_function_dispatch(_min_dispatcher)
+def min(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
+ where=np._NoValue):
"""
Return the minimum of an array or minimum along an axis.
@@ -2855,7 +2875,7 @@ def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
the result will broadcast correctly against the input array.
If the default value is passed, then `keepdims` will not be
- passed through to the `amin` method of sub-classes of
+ passed through to the ``min`` method of sub-classes of
`ndarray`, however any non-default value will be. If the
sub-class' method does not implement `keepdims` any
exceptions will be raised.
@@ -2874,7 +2894,7 @@ def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
Returns
-------
- amin : ndarray or scalar
+ min : ndarray or scalar
Minimum of `a`. If `axis` is None, the result is a scalar value.
If `axis` is an int, the result is an array of dimension
``a.ndim - 1``. If `axis` is a tuple, the result is an array of
@@ -2901,9 +2921,9 @@ def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
corresponding min value will be NaN as well. To ignore NaN values
(MATLAB behavior), please use nanmin.
- Don't use `amin` for element-wise comparison of 2 arrays; when
+ Don't use `~numpy.min` for element-wise comparison of 2 arrays; when
``a.shape[0]`` is 2, ``minimum(a[0], a[1])`` is faster than
- ``amin(a, axis=0)``.
+ ``min(a, axis=0)``.
Examples
--------
@@ -2911,25 +2931,25 @@ def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
>>> a
array([[0, 1],
[2, 3]])
- >>> np.amin(a) # Minimum of the flattened array
+ >>> np.min(a) # Minimum of the flattened array
0
- >>> np.amin(a, axis=0) # Minima along the first axis
+ >>> np.min(a, axis=0) # Minima along the first axis
array([0, 1])
- >>> np.amin(a, axis=1) # Minima along the second axis
+ >>> np.min(a, axis=1) # Minima along the second axis
array([0, 2])
- >>> np.amin(a, where=[False, True], initial=10, axis=0)
+ >>> np.min(a, where=[False, True], initial=10, axis=0)
array([10, 1])
>>> b = np.arange(5, dtype=float)
>>> b[2] = np.NaN
- >>> np.amin(b)
+ >>> np.min(b)
nan
- >>> np.amin(b, where=~np.isnan(b), initial=10)
+ >>> np.min(b, where=~np.isnan(b), initial=10)
0.0
>>> np.nanmin(b)
0.0
- >>> np.amin([[-50], [10]], axis=-1, initial=0)
+ >>> np.min([[-50], [10]], axis=-1, initial=0)
array([-50, 0])
Notice that the initial value is used as one of the elements for which the
@@ -2938,7 +2958,7 @@ def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
Notice that this isn't the same as Python's ``default`` argument.
- >>> np.amin([6], initial=5)
+ >>> np.min([6], initial=5)
5
>>> min([6], default=5)
6
@@ -2947,6 +2967,23 @@ def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
keepdims=keepdims, initial=initial, where=where)
+@array_function_dispatch(_min_dispatcher)
+def amin(a, axis=None, out=None, keepdims=np._NoValue, initial=np._NoValue,
+ where=np._NoValue):
+ """
+ Return the minimum of an array or minimum along an axis.
+
+ `amin` is an alias of `~numpy.min`.
+
+ See Also
+ --------
+ min : alias of this function
+ ndarray.min : equivalent method
+ """
+ return _wrapreduction(a, np.minimum, 'min', axis, None, out,
+ keepdims=keepdims, initial=initial, where=where)
+
+
def _prod_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None,
initial=None, where=None):
return (a, out)
@@ -3238,12 +3275,12 @@ def size(a, axis=None):
return asarray(a).shape[axis]
-def _around_dispatcher(a, decimals=None, out=None):
+def _round_dispatcher(a, decimals=None, out=None):
return (a, out)
-@array_function_dispatch(_around_dispatcher)
-def around(a, decimals=0, out=None):
+@array_function_dispatch(_round_dispatcher)
+def round(a, decimals=0, out=None):
"""
Evenly round to the given number of decimals.
@@ -3274,18 +3311,17 @@ def around(a, decimals=0, out=None):
See Also
--------
ndarray.round : equivalent method
+ around : an alias for this function
ceil, fix, floor, rint, trunc
Notes
-----
- `~numpy.round` is often used as an alias for `~numpy.around`.
-
For values exactly halfway between rounded decimal values, NumPy
rounds to the nearest even value. Thus 1.5 and 2.5 round to 2.0,
-0.5 and 0.5 round to 0.0, etc.
- ``np.around`` uses a fast but sometimes inexact algorithm to round
+ ``np.round`` uses a fast but sometimes inexact algorithm to round
floating-point datatypes. For positive `decimals` it is equivalent to
``np.true_divide(np.rint(a * 10**decimals), 10**decimals)``, which has
error due to the inexact representation of decimal fractions in the IEEE
@@ -3322,21 +3358,38 @@ def around(a, decimals=0, out=None):
Examples
--------
- >>> np.around([0.37, 1.64])
+ >>> np.round([0.37, 1.64])
array([0., 2.])
- >>> np.around([0.37, 1.64], decimals=1)
+ >>> np.round([0.37, 1.64], decimals=1)
array([0.4, 1.6])
- >>> np.around([.5, 1.5, 2.5, 3.5, 4.5]) # rounds to nearest even value
+ >>> np.round([.5, 1.5, 2.5, 3.5, 4.5]) # rounds to nearest even value
array([0., 2., 2., 4., 4.])
- >>> np.around([1,2,3,11], decimals=1) # ndarray of ints is returned
+ >>> np.round([1,2,3,11], decimals=1) # ndarray of ints is returned
array([ 1, 2, 3, 11])
- >>> np.around([1,2,3,11], decimals=-1)
+ >>> np.round([1,2,3,11], decimals=-1)
array([ 0, 0, 0, 10])
"""
return _wrapfunc(a, 'round', decimals=decimals, out=out)
+@array_function_dispatch(_round_dispatcher)
+def around(a, decimals=0, out=None):
+ """
+ Round an array to the given number of decimals.
+
+ `around` is an alias of `~numpy.round`.
+
+ See Also
+ --------
+ ndarray.round : equivalent method
+ round : alias for this function
+ ceil, fix, floor, rint, trunc
+
+ """
+ return _wrapfunc(a, 'round', decimals=decimals, out=out)
+
+
def _mean_dispatcher(a, axis=None, dtype=None, out=None, keepdims=None, *,
where=None):
return (a, where, out)
@@ -3748,18 +3801,30 @@ def var(a, axis=None, dtype=None, out=None, ddof=0, keepdims=np._NoValue, *,
**kwargs)
-# Aliases of other functions. These have their own definitions only so that
-# they can have unique docstrings.
+# Aliases of other functions. Provided unique docstrings
+# are reference purposes only. Wherever possible,
+# avoid using them.
-@array_function_dispatch(_around_dispatcher)
+@array_function_dispatch(_round_dispatcher)
def round_(a, decimals=0, out=None):
"""
Round an array to the given number of decimals.
+ `~numpy.round_` is a disrecommended backwards-compatibility
+ alias of `~numpy.around` and `~numpy.round`.
+
+ .. deprecated:: 1.25.0
+ ``round_`` is deprecated as of NumPy 1.25.0, and will be
+ removed in NumPy 2.0. Please use `round` instead.
+
See Also
--------
around : equivalent function; see for details.
"""
+ # 2023-02-28, 1.25.0
+ warnings.warn("`round_` is deprecated as of NumPy 1.25.0, and will be "
+ "removed in NumPy 2.0. Please use `round` instead.",
+ DeprecationWarning, stacklevel=2)
return around(a, decimals=decimals, out=out)
@@ -3768,10 +3833,18 @@ def product(*args, **kwargs):
"""
Return the product of array elements over a given axis.
+ .. deprecated:: 1.25.0
+ ``product`` is deprecated as of NumPy 1.25.0, and will be
+ removed in NumPy 2.0. Please use `prod` instead.
+
See Also
--------
prod : equivalent function; see for details.
"""
+ # 2023-03-02, 1.25.0
+ warnings.warn("`product` is deprecated as of NumPy 1.25.0, and will be "
+ "removed in NumPy 2.0. Please use `prod` instead.",
+ DeprecationWarning, stacklevel=2)
return prod(*args, **kwargs)
@@ -3780,10 +3853,18 @@ def cumproduct(*args, **kwargs):
"""
Return the cumulative product over the given axis.
+ .. deprecated:: 1.25.0
+ ``cumproduct`` is deprecated as of NumPy 1.25.0, and will be
+ removed in NumPy 2.0. Please use `cumprod` instead.
+
See Also
--------
cumprod : equivalent function; see for details.
"""
+ # 2023-03-02, 1.25.0
+ warnings.warn("`cumproduct` is deprecated as of NumPy 1.25.0, and will be "
+ "removed in NumPy 2.0. Please use `cumprod` instead.",
+ DeprecationWarning, stacklevel=2)
return cumprod(*args, **kwargs)
@@ -3794,10 +3875,18 @@ def sometrue(*args, **kwargs):
Refer to `any` for full documentation.
+ .. deprecated:: 1.25.0
+ ``sometrue`` is deprecated as of NumPy 1.25.0, and will be
+ removed in NumPy 2.0. Please use `any` instead.
+
See Also
--------
any : equivalent function; see for details.
"""
+ # 2023-03-02, 1.25.0
+ warnings.warn("`sometrue` is deprecated as of NumPy 1.25.0, and will be "
+ "removed in NumPy 2.0. Please use `any` instead.",
+ DeprecationWarning, stacklevel=2)
return any(*args, **kwargs)
@@ -3806,8 +3895,16 @@ def alltrue(*args, **kwargs):
"""
Check if all elements of input array are true.
+ .. deprecated:: 1.25.0
+ ``alltrue`` is deprecated as of NumPy 1.25.0, and will be
+ removed in NumPy 2.0. Please use `all` instead.
+
See Also
--------
numpy.all : Equivalent function; see for details.
"""
+ # 2023-03-02, 1.25.0
+ warnings.warn("`alltrue` is deprecated as of NumPy 1.25.0, and will be "
+ "removed in NumPy 2.0. Please use `all` instead.",
+ DeprecationWarning, stacklevel=2)
return all(*args, **kwargs)
diff --git a/numpy/core/getlimits.py b/numpy/core/getlimits.py
index f848af085..da9e1d7f3 100644
--- a/numpy/core/getlimits.py
+++ b/numpy/core/getlimits.py
@@ -482,6 +482,10 @@ class finfo:
_finfo_cache = {}
def __new__(cls, dtype):
+ obj = cls._finfo_cache.get(dtype) # most common path
+ if obj is not None:
+ return obj
+
if dtype is None:
# Deprecated in NumPy 1.25, 2023-01-16
warnings.warn(
@@ -497,7 +501,7 @@ class finfo:
# In case a float instance was given
dtype = numeric.dtype(type(dtype))
- obj = cls._finfo_cache.get(dtype, None)
+ obj = cls._finfo_cache.get(dtype)
if obj is not None:
return obj
dtypes = [dtype]
@@ -507,17 +511,24 @@ class finfo:
dtype = newdtype
if not issubclass(dtype, numeric.inexact):
raise ValueError("data type %r not inexact" % (dtype))
- obj = cls._finfo_cache.get(dtype, None)
+ obj = cls._finfo_cache.get(dtype)
if obj is not None:
return obj
if not issubclass(dtype, numeric.floating):
newdtype = _convert_to_float[dtype]
if newdtype is not dtype:
+ # dtype changed, for example from complex128 to float64
dtypes.append(newdtype)
dtype = newdtype
- obj = cls._finfo_cache.get(dtype, None)
- if obj is not None:
- return obj
+
+ obj = cls._finfo_cache.get(dtype, None)
+ if obj is not None:
+ # the original dtype was not in the cache, but the new
+ # dtype is in the cache. we add the original dtypes to
+ # the cache and return the result
+ for dt in dtypes:
+ cls._finfo_cache[dt] = obj
+ return obj
obj = object.__new__(cls)._init(dtype)
for dt in dtypes:
cls._finfo_cache[dt] = obj
diff --git a/numpy/core/include/meson.build b/numpy/core/include/meson.build
index 0f9fbe76b..be07a07b8 100644
--- a/numpy/core/include/meson.build
+++ b/numpy/core/include/meson.build
@@ -2,6 +2,7 @@ installed_headers = [
'numpy/_neighborhood_iterator_imp.h',
'numpy/arrayobject.h',
'numpy/arrayscalars.h',
+ 'numpy/_dtype_api.h',
'numpy/experimental_dtype_api.h',
'numpy/halffloat.h',
'numpy/ndarrayobject.h',
diff --git a/numpy/core/include/numpy/_dtype_api.h b/numpy/core/include/numpy/_dtype_api.h
new file mode 100644
index 000000000..2f801eace
--- /dev/null
+++ b/numpy/core/include/numpy/_dtype_api.h
@@ -0,0 +1,408 @@
+/*
+ * DType related API shared by the (experimental) public API And internal API.
+ */
+
+#ifndef NUMPY_CORE_INCLUDE_NUMPY___DTYPE_API_H_
+#define NUMPY_CORE_INCLUDE_NUMPY___DTYPE_API_H_
+
+#define __EXPERIMENTAL_DTYPE_API_VERSION 9
+
+struct PyArrayMethodObject_tag;
+
+/*
+ * Largely opaque struct for DType classes (i.e. metaclass instances).
+ * The internal definition is currently in `ndarraytypes.h` (export is a bit
+ * more complex because `PyArray_Descr` is a DTypeMeta internall but not
+ * externally).
+ */
+#if !(defined(NPY_INTERNAL_BUILD) && NPY_INTERNAL_BUILD)
+
+ typedef struct PyArray_DTypeMeta_tag {
+ PyHeapTypeObject super;
+
+ /*
+ * Most DTypes will have a singleton default instance, for the
+ * parametric legacy DTypes (bytes, string, void, datetime) this
+ * may be a pointer to the *prototype* instance?
+ */
+ PyArray_Descr *singleton;
+ /* Copy of the legacy DTypes type number, usually invalid. */
+ int type_num;
+
+ /* The type object of the scalar instances (may be NULL?) */
+ PyTypeObject *scalar_type;
+ /*
+ * DType flags to signal legacy, parametric, or
+ * abstract. But plenty of space for additional information/flags.
+ */
+ npy_uint64 flags;
+
+ /*
+ * Use indirection in order to allow a fixed size for this struct.
+ * A stable ABI size makes creating a static DType less painful
+ * while also ensuring flexibility for all opaque API (with one
+ * indirection due the pointer lookup).
+ */
+ void *dt_slots;
+ /* Allow growing (at the moment also beyond this) */
+ void *reserved[3];
+ } PyArray_DTypeMeta;
+
+#endif /* not internal build */
+
+/*
+ * ******************************************************
+ * ArrayMethod API (Casting and UFuncs)
+ * ******************************************************
+ */
+/*
+ * NOTE: Expected changes:
+ * * probably split runtime and general flags into two
+ * * should possibly not use an enum for typedef for more stable ABI?
+ */
+typedef enum {
+ /* Flag for whether the GIL is required */
+ NPY_METH_REQUIRES_PYAPI = 1 << 0,
+ /*
+ * Some functions cannot set floating point error flags, this flag
+ * gives us the option (not requirement) to skip floating point error
+ * setup/check. No function should set error flags and ignore them
+ * since it would interfere with chaining operations (e.g. casting).
+ */
+ NPY_METH_NO_FLOATINGPOINT_ERRORS = 1 << 1,
+ /* Whether the method supports unaligned access (not runtime) */
+ NPY_METH_SUPPORTS_UNALIGNED = 1 << 2,
+ /*
+ * Used for reductions to allow reordering the operation. At this point
+ * assume that if set, it also applies to normal operations though!
+ */
+ NPY_METH_IS_REORDERABLE = 1 << 3,
+ /*
+ * Private flag for now for *logic* functions. The logical functions
+ * `logical_or` and `logical_and` can always cast the inputs to booleans
+ * "safely" (because that is how the cast to bool is defined).
+ * @seberg: I am not sure this is the best way to handle this, so its
+ * private for now (also it is very limited anyway).
+ * There is one "exception". NA aware dtypes cannot cast to bool
+ * (hopefully), so the `??->?` loop should error even with this flag.
+ * But a second NA fallback loop will be necessary.
+ */
+ _NPY_METH_FORCE_CAST_INPUTS = 1 << 17,
+
+ /* All flags which can change at runtime */
+ NPY_METH_RUNTIME_FLAGS = (
+ NPY_METH_REQUIRES_PYAPI |
+ NPY_METH_NO_FLOATINGPOINT_ERRORS),
+} NPY_ARRAYMETHOD_FLAGS;
+
+
+typedef struct PyArrayMethod_Context_tag {
+ /* The caller, which is typically the original ufunc. May be NULL */
+ PyObject *caller;
+ /* The method "self". Publically currentl an opaque object. */
+ struct PyArrayMethodObject_tag *method;
+
+ /* Operand descriptors, filled in by resolve_descriptors */
+ PyArray_Descr **descriptors;
+ /* Structure may grow (this is harmless for DType authors) */
+} PyArrayMethod_Context;
+
+
+/*
+ * The main object for creating a new ArrayMethod. We use the typical `slots`
+ * mechanism used by the Python limited API (see below for the slot defs).
+ */
+typedef struct {
+ const char *name;
+ int nin, nout;
+ NPY_CASTING casting;
+ NPY_ARRAYMETHOD_FLAGS flags;
+ PyArray_DTypeMeta **dtypes;
+ PyType_Slot *slots;
+} PyArrayMethod_Spec;
+
+
+/*
+ * ArrayMethod slots
+ * -----------------
+ *
+ * SLOTS IDs For the ArrayMethod creation, once fully public, IDs are fixed
+ * but can be deprecated and arbitrarily extended.
+ */
+#define NPY_METH_resolve_descriptors 1
+/* We may want to adapt the `get_loop` signature a bit: */
+#define _NPY_METH_get_loop 2
+#define NPY_METH_get_reduction_initial 3
+/* specific loops for constructions/default get_loop: */
+#define NPY_METH_strided_loop 4
+#define NPY_METH_contiguous_loop 5
+#define NPY_METH_unaligned_strided_loop 6
+#define NPY_METH_unaligned_contiguous_loop 7
+#define NPY_METH_contiguous_indexed_loop 8
+
+/*
+ * The resolve descriptors function, must be able to handle NULL values for
+ * all output (but not input) `given_descrs` and fill `loop_descrs`.
+ * Return -1 on error or 0 if the operation is not possible without an error
+ * set. (This may still be in flux.)
+ * Otherwise must return the "casting safety", for normal functions, this is
+ * almost always "safe" (or even "equivalent"?).
+ *
+ * `resolve_descriptors` is optional if all output DTypes are non-parametric.
+ */
+typedef NPY_CASTING (resolve_descriptors_function)(
+ /* "method" is currently opaque (necessary e.g. to wrap Python) */
+ struct PyArrayMethodObject_tag *method,
+ /* DTypes the method was created for */
+ PyArray_DTypeMeta **dtypes,
+ /* Input descriptors (instances). Outputs may be NULL. */
+ PyArray_Descr **given_descrs,
+ /* Exact loop descriptors to use, must not hold references on error */
+ PyArray_Descr **loop_descrs,
+ npy_intp *view_offset);
+
+
+typedef int (PyArrayMethod_StridedLoop)(PyArrayMethod_Context *context,
+ char *const *data, const npy_intp *dimensions, const npy_intp *strides,
+ NpyAuxData *transferdata);
+
+
+typedef int (get_loop_function)(
+ PyArrayMethod_Context *context,
+ int aligned, int move_references,
+ const npy_intp *strides,
+ PyArrayMethod_StridedLoop **out_loop,
+ NpyAuxData **out_transferdata,
+ NPY_ARRAYMETHOD_FLAGS *flags);
+
+/**
+ * Query an ArrayMethod for the initial value for use in reduction.
+ *
+ * @param context The arraymethod context, mainly to access the descriptors.
+ * @param reduction_is_empty Whether the reduction is empty. When it is, the
+ * value returned may differ. In this case it is a "default" value that
+ * may differ from the "identity" value normally used. For example:
+ * - `0.0` is the default for `sum([])`. But `-0.0` is the correct
+ * identity otherwise as it preserves the sign for `sum([-0.0])`.
+ * - We use no identity for object, but return the default of `0` and `1`
+ * for the empty `sum([], dtype=object)` and `prod([], dtype=object)`.
+ * This allows `np.sum(np.array(["a", "b"], dtype=object))` to work.
+ * - `-inf` or `INT_MIN` for `max` is an identity, but at least `INT_MIN`
+ * not a good *default* when there are no items.
+ * @param initial Pointer to initial data to be filled (if possible)
+ *
+ * @returns -1, 0, or 1 indicating error, no initial value, and initial being
+ * successfully filled. Errors must not be given where 0 is correct, NumPy
+ * may call this even when not strictly necessary.
+ */
+typedef int (get_reduction_initial_function)(
+ PyArrayMethod_Context *context, npy_bool reduction_is_empty,
+ char *initial);
+
+
+/*
+ * The following functions are only used be the wrapping array method defined
+ * in umath/wrapping_array_method.c
+ */
+
+/*
+ * The function to convert the given descriptors (passed in to
+ * `resolve_descriptors`) and translates them for the wrapped loop.
+ * The new descriptors MUST be viewable with the old ones, `NULL` must be
+ * supported (for outputs) and should normally be forwarded.
+ *
+ * The function must clean up on error.
+ *
+ * NOTE: We currently assume that this translation gives "viewable" results.
+ * I.e. there is no additional casting related to the wrapping process.
+ * In principle that could be supported, but not sure it is useful.
+ * This currently also means that e.g. alignment must apply identically
+ * to the new dtypes.
+ *
+ * TODO: Due to the fact that `resolve_descriptors` is also used for `can_cast`
+ * there is no way to "pass out" the result of this function. This means
+ * it will be called twice for every ufunc call.
+ * (I am considering including `auxdata` as an "optional" parameter to
+ * `resolve_descriptors`, so that it can be filled there if not NULL.)
+ */
+typedef int translate_given_descrs_func(int nin, int nout,
+ PyArray_DTypeMeta *wrapped_dtypes[],
+ PyArray_Descr *given_descrs[], PyArray_Descr *new_descrs[]);
+
+/**
+ * The function to convert the actual loop descriptors (as returned by the
+ * original `resolve_descriptors` function) to the ones the output array
+ * should use.
+ * This function must return "viewable" types, it must not mutate them in any
+ * form that would break the inner-loop logic. Does not need to support NULL.
+ *
+ * The function must clean up on error.
+ *
+ * @param nargs Number of arguments
+ * @param new_dtypes The DTypes of the output (usually probably not needed)
+ * @param given_descrs Original given_descrs to the resolver, necessary to
+ * fetch any information related to the new dtypes from the original.
+ * @param original_descrs The `loop_descrs` returned by the wrapped loop.
+ * @param loop_descrs The output descriptors, compatible to `original_descrs`.
+ *
+ * @returns 0 on success, -1 on failure.
+ */
+typedef int translate_loop_descrs_func(int nin, int nout,
+ PyArray_DTypeMeta *new_dtypes[], PyArray_Descr *given_descrs[],
+ PyArray_Descr *original_descrs[], PyArray_Descr *loop_descrs[]);
+
+
+/*
+ * A traverse loop working on a single array. This is similar to the general
+ * strided-loop function. This is designed for loops that need to visit every
+ * element of a single array.
+ *
+ * Currently this is only used for array clearing, via the
+ * NPY_DT_get_clear_loop, api hook, particularly for arrays storing embedded
+ * references to python objects or heap-allocated data. If you define a dtype
+ * that uses embedded references, the NPY_ITEM_REFCOUNT flag must be set on the
+ * dtype instance.
+ *
+ * The `void *traverse_context` is passed in because we may need to pass in
+ * Intepreter state or similar in the futurem, but we don't want to pass in
+ * a full context (with pointers to dtypes, method, caller which all make
+ * no sense for a traverse function).
+ *
+ * We assume for now that this context can be just passed through in the
+ * the future (for structured dtypes).
+ *
+ */
+typedef int (traverse_loop_function)(
+ void *traverse_context, PyArray_Descr *descr, char *data,
+ npy_intp size, npy_intp stride, NpyAuxData *auxdata);
+
+
+/*
+ * Simplified get_loop function specific to dtype traversal
+ *
+ * Currently this is only used for clearing arrays. It should set the flags
+ * needed for the traversal loop and set out_loop to the loop function, which
+ * must be a valid traverse_loop_function pointer.
+ *
+ */
+typedef int (get_traverse_loop_function)(
+ void *traverse_context, PyArray_Descr *descr,
+ int aligned, npy_intp fixed_stride,
+ traverse_loop_function **out_loop, NpyAuxData **out_auxdata,
+ NPY_ARRAYMETHOD_FLAGS *flags);
+
+
+/*
+ * ****************************
+ * DTYPE API
+ * ****************************
+ */
+
+#define NPY_DT_ABSTRACT 1 << 1
+#define NPY_DT_PARAMETRIC 1 << 2
+#define NPY_DT_NUMERIC 1 << 3
+
+/*
+ * These correspond to slots in the NPY_DType_Slots struct and must
+ * be in the same order as the members of that struct. If new slots
+ * get added or old slots get removed NPY_NUM_DTYPE_SLOTS must also
+ * be updated
+ */
+
+#define NPY_DT_discover_descr_from_pyobject 1
+// this slot is considered private because its API hasn't beed decided
+#define _NPY_DT_is_known_scalar_type 2
+#define NPY_DT_default_descr 3
+#define NPY_DT_common_dtype 4
+#define NPY_DT_common_instance 5
+#define NPY_DT_ensure_canonical 6
+#define NPY_DT_setitem 7
+#define NPY_DT_getitem 8
+#define NPY_DT_get_clear_loop 9
+
+// These PyArray_ArrFunc slots will be deprecated and replaced eventually
+// getitem and setitem can be defined as a performance optimization;
+// by default the user dtypes call `legacy_getitem_using_DType` and
+// `legacy_setitem_using_DType`, respectively. This functionality is
+// only supported for basic NumPy DTypes.
+
+
+// used to separate dtype slots from arrfuncs slots
+// intended only for internal use but defined here for clarity
+#define _NPY_DT_ARRFUNCS_OFFSET (1 << 10)
+
+// Cast is disabled
+// #define NPY_DT_PyArray_ArrFuncs_cast 0 + _NPY_DT_ARRFUNCS_OFFSET
+
+#define NPY_DT_PyArray_ArrFuncs_getitem 1 + _NPY_DT_ARRFUNCS_OFFSET
+#define NPY_DT_PyArray_ArrFuncs_setitem 2 + _NPY_DT_ARRFUNCS_OFFSET
+
+#define NPY_DT_PyArray_ArrFuncs_copyswapn 3 + _NPY_DT_ARRFUNCS_OFFSET
+#define NPY_DT_PyArray_ArrFuncs_copyswap 4 + _NPY_DT_ARRFUNCS_OFFSET
+#define NPY_DT_PyArray_ArrFuncs_compare 5 + _NPY_DT_ARRFUNCS_OFFSET
+#define NPY_DT_PyArray_ArrFuncs_argmax 6 + _NPY_DT_ARRFUNCS_OFFSET
+#define NPY_DT_PyArray_ArrFuncs_dotfunc 7 + _NPY_DT_ARRFUNCS_OFFSET
+#define NPY_DT_PyArray_ArrFuncs_scanfunc 8 + _NPY_DT_ARRFUNCS_OFFSET
+#define NPY_DT_PyArray_ArrFuncs_fromstr 9 + _NPY_DT_ARRFUNCS_OFFSET
+#define NPY_DT_PyArray_ArrFuncs_nonzero 10 + _NPY_DT_ARRFUNCS_OFFSET
+#define NPY_DT_PyArray_ArrFuncs_fill 11 + _NPY_DT_ARRFUNCS_OFFSET
+#define NPY_DT_PyArray_ArrFuncs_fillwithscalar 12 + _NPY_DT_ARRFUNCS_OFFSET
+#define NPY_DT_PyArray_ArrFuncs_sort 13 + _NPY_DT_ARRFUNCS_OFFSET
+#define NPY_DT_PyArray_ArrFuncs_argsort 14 + _NPY_DT_ARRFUNCS_OFFSET
+
+// Casting related slots are disabled. See
+// https://github.com/numpy/numpy/pull/23173#discussion_r1101098163
+// #define NPY_DT_PyArray_ArrFuncs_castdict 15 + _NPY_DT_ARRFUNCS_OFFSET
+// #define NPY_DT_PyArray_ArrFuncs_scalarkind 16 + _NPY_DT_ARRFUNCS_OFFSET
+// #define NPY_DT_PyArray_ArrFuncs_cancastscalarkindto 17 + _NPY_DT_ARRFUNCS_OFFSET
+// #define NPY_DT_PyArray_ArrFuncs_cancastto 18 + _NPY_DT_ARRFUNCS_OFFSET
+
+// These are deprecated in NumPy 1.19, so are disabled here.
+// #define NPY_DT_PyArray_ArrFuncs_fastclip 19 + _NPY_DT_ARRFUNCS_OFFSET
+// #define NPY_DT_PyArray_ArrFuncs_fastputmask 20 + _NPY_DT_ARRFUNCS_OFFSET
+// #define NPY_DT_PyArray_ArrFuncs_fasttake 21 + _NPY_DT_ARRFUNCS_OFFSET
+#define NPY_DT_PyArray_ArrFuncs_argmin 22 + _NPY_DT_ARRFUNCS_OFFSET
+
+// TODO: These slots probably still need some thought, and/or a way to "grow"?
+typedef struct {
+ PyTypeObject *typeobj; /* type of python scalar or NULL */
+ int flags; /* flags, including parametric and abstract */
+ /* NULL terminated cast definitions. Use NULL for the newly created DType */
+ PyArrayMethod_Spec **casts;
+ PyType_Slot *slots;
+ /* Baseclass or NULL (will always subclass `np.dtype`) */
+ PyTypeObject *baseclass;
+} PyArrayDTypeMeta_Spec;
+
+
+typedef PyArray_Descr *(discover_descr_from_pyobject_function)(
+ PyArray_DTypeMeta *cls, PyObject *obj);
+
+/*
+ * Before making this public, we should decide whether it should pass
+ * the type, or allow looking at the object. A possible use-case:
+ * `np.array(np.array([0]), dtype=np.ndarray)`
+ * Could consider arrays that are not `dtype=ndarray` "scalars".
+ */
+typedef int (is_known_scalar_type_function)(
+ PyArray_DTypeMeta *cls, PyTypeObject *obj);
+
+typedef PyArray_Descr *(default_descr_function)(PyArray_DTypeMeta *cls);
+typedef PyArray_DTypeMeta *(common_dtype_function)(
+ PyArray_DTypeMeta *dtype1, PyArray_DTypeMeta *dtype2);
+typedef PyArray_Descr *(common_instance_function)(
+ PyArray_Descr *dtype1, PyArray_Descr *dtype2);
+typedef PyArray_Descr *(ensure_canonical_function)(PyArray_Descr *dtype);
+
+/*
+ * TODO: These two functions are currently only used for experimental DType
+ * API support. Their relation should be "reversed": NumPy should
+ * always use them internally.
+ * There are open points about "casting safety" though, e.g. setting
+ * elements is currently always unsafe.
+ */
+typedef int(setitemfunction)(PyArray_Descr *, PyObject *, char *);
+typedef PyObject *(getitemfunction)(PyArray_Descr *, char *);
+
+
+#endif /* NUMPY_CORE_INCLUDE_NUMPY___DTYPE_API_H_ */
diff --git a/numpy/core/include/numpy/experimental_dtype_api.h b/numpy/core/include/numpy/experimental_dtype_api.h
index 9ecb582d0..120d33324 100644
--- a/numpy/core/include/numpy/experimental_dtype_api.h
+++ b/numpy/core/include/numpy/experimental_dtype_api.h
@@ -3,6 +3,9 @@
* NEPs 41 to 43. For background, please check these NEPs. Otherwise,
* this header also serves as documentation for the time being.
*
+ * The header includes `_dtype_api.h` which holds most definition while this
+ * header mainly wraps functions for public consumption.
+ *
* Please do not hesitate to contact @seberg with questions. This is
* developed together with https://github.com/seberg/experimental_user_dtypes
* and those interested in experimenting are encouraged to contribute there.
@@ -114,7 +117,13 @@
#include <Python.h>
#include "ndarraytypes.h"
+#include "_dtype_api.h"
+/*
+ * The contents of PyArrayMethodObject are currently opaque (is there a way
+ * good way to make them be `PyObject *`?)
+ */
+typedef struct PyArrayMethodObject_tag PyArrayMethodObject;
/*
* There must be a better way?! -- Oh well, this is experimental
@@ -154,72 +163,6 @@
#endif
-/*
- * DTypeMeta struct, the content may be made fully opaque (except the size).
- * We may also move everything into a single `void *dt_slots`.
- */
-typedef struct {
- PyHeapTypeObject super;
- PyArray_Descr *singleton;
- int type_num;
- PyTypeObject *scalar_type;
- npy_uint64 flags;
- void *dt_slots;
- void *reserved[3];
-} PyArray_DTypeMeta;
-
-
-/*
- * ******************************************************
- * ArrayMethod API (Casting and UFuncs)
- * ******************************************************
- */
-/*
- * NOTE: Expected changes:
- * * invert logic of floating point error flag
- * * probably split runtime and general flags into two
- * * should possibly not use an enum for typedef for more stable ABI?
- */
-typedef enum {
- /* Flag for whether the GIL is required */
- NPY_METH_REQUIRES_PYAPI = 1 << 0,
- /*
- * Some functions cannot set floating point error flags, this flag
- * gives us the option (not requirement) to skip floating point error
- * setup/check. No function should set error flags and ignore them
- * since it would interfere with chaining operations (e.g. casting).
- */
- NPY_METH_NO_FLOATINGPOINT_ERRORS = 1 << 1,
- /* Whether the method supports unaligned access (not runtime) */
- NPY_METH_SUPPORTS_UNALIGNED = 1 << 2,
- /*
- * Used for reductions to allow reordering the operation. At this point
- * assume that if set, it also applies to normal operations though!
- */
- NPY_METH_IS_REORDERABLE = 1 << 3,
-
- /* All flags which can change at runtime */
- NPY_METH_RUNTIME_FLAGS = (
- NPY_METH_REQUIRES_PYAPI |
- NPY_METH_NO_FLOATINGPOINT_ERRORS),
-} NPY_ARRAYMETHOD_FLAGS;
-
-
-
-/*
- * The main object for creating a new ArrayMethod. We use the typical `slots`
- * mechanism used by the Python limited API (see below for the slot defs).
- */
-typedef struct {
- const char *name;
- int nin, nout;
- NPY_CASTING casting;
- NPY_ARRAYMETHOD_FLAGS flags;
- PyArray_DTypeMeta **dtypes;
- PyType_Slot *slots;
-} PyArrayMethod_Spec;
-
-
typedef int _ufunc_addloop_fromspec_func(
PyObject *ufunc, PyArrayMethod_Spec *spec);
/*
@@ -273,117 +216,6 @@ typedef int _ufunc_addpromoter_func(
#define PyUFunc_AddPromoter \
(*(_ufunc_addpromoter_func *)(__experimental_dtype_api_table[1]))
-
-/*
- * The resolve descriptors function, must be able to handle NULL values for
- * all output (but not input) `given_descrs` and fill `loop_descrs`.
- * Return -1 on error or 0 if the operation is not possible without an error
- * set. (This may still be in flux.)
- * Otherwise must return the "casting safety", for normal functions, this is
- * almost always "safe" (or even "equivalent"?).
- *
- * `resolve_descriptors` is optional if all output DTypes are non-parametric.
- */
-#define NPY_METH_resolve_descriptors 1
-typedef NPY_CASTING (resolve_descriptors_function)(
- /* "method" is currently opaque (necessary e.g. to wrap Python) */
- PyObject *method,
- /* DTypes the method was created for */
- PyArray_DTypeMeta **dtypes,
- /* Input descriptors (instances). Outputs may be NULL. */
- PyArray_Descr **given_descrs,
- /* Exact loop descriptors to use, must not hold references on error */
- PyArray_Descr **loop_descrs,
- npy_intp *view_offset);
-
-/* NOT public yet: Signature needs adapting as external API. */
-#define _NPY_METH_get_loop 2
-
-/*
- * Current public API to define fast inner-loops. You must provide a
- * strided loop. If this is a cast between two "versions" of the same dtype
- * you must also provide an unaligned strided loop.
- * Other loops are useful to optimize the very common contiguous case.
- *
- * NOTE: As of now, NumPy will NOT use unaligned loops in ufuncs!
- */
-#define NPY_METH_strided_loop 4
-#define NPY_METH_contiguous_loop 5
-#define NPY_METH_unaligned_strided_loop 6
-#define NPY_METH_unaligned_contiguous_loop 7
-#define NPY_METH_contiguous_indexed_loop 8
-
-
-typedef struct {
- PyObject *caller; /* E.g. the original ufunc, may be NULL */
- PyObject *method; /* The method "self". Currently an opaque object */
-
- /* Operand descriptors, filled in by resolve_descriptors */
- PyArray_Descr **descriptors;
- /* Structure may grow (this is harmless for DType authors) */
-} PyArrayMethod_Context;
-
-typedef int (PyArrayMethod_StridedLoop)(PyArrayMethod_Context *context,
- char *const *data, const npy_intp *dimensions, const npy_intp *strides,
- NpyAuxData *transferdata);
-
-
-/**
- * Query an ArrayMethod for the initial value for use in reduction.
- *
- * @param context The arraymethod context, mainly to access the descriptors.
- * @param reduction_is_empty Whether the reduction is empty. When it is, the
- * value returned may differ. In this case it is a "default" value that
- * may differ from the "identity" value normally used. For example:
- * - `0.0` is the default for `sum([])`. But `-0.0` is the correct
- * identity otherwise as it preserves the sign for `sum([-0.0])`.
- * - We use no identity for object, but return the default of `0` and `1`
- * for the empty `sum([], dtype=object)` and `prod([], dtype=object)`.
- * This allows `np.sum(np.array(["a", "b"], dtype=object))` to work.
- * - `-inf` or `INT_MIN` for `max` is an identity, but at least `INT_MIN`
- * not a good *default* when there are no items.
- * @param initial Pointer to initial data to be filled (if possible)
- *
- * @returns -1, 0, or 1 indicating error, no initial value, and initial being
- * successfully filled. Errors must not be given where 0 is correct, NumPy
- * may call this even when not strictly necessary.
- */
-#define NPY_METH_get_reduction_initial 3
-typedef int (get_reduction_initial_function)(
- PyArrayMethod_Context *context, npy_bool reduction_is_empty,
- char *initial);
-
-/*
- * ****************************
- * DTYPE API
- * ****************************
- */
-
-#define NPY_DT_ABSTRACT 1 << 1
-#define NPY_DT_PARAMETRIC 1 << 2
-
-#define NPY_DT_discover_descr_from_pyobject 1
-#define _NPY_DT_is_known_scalar_type 2
-#define NPY_DT_default_descr 3
-#define NPY_DT_common_dtype 4
-#define NPY_DT_common_instance 5
-#define NPY_DT_ensure_canonical 6
-#define NPY_DT_setitem 7
-#define NPY_DT_getitem 8
-
-
-// TODO: These slots probably still need some thought, and/or a way to "grow"?
-typedef struct{
- PyTypeObject *typeobj; /* type of python scalar or NULL */
- int flags; /* flags, including parametric and abstract */
- /* NULL terminated cast definitions. Use NULL for the newly created DType */
- PyArrayMethod_Spec **casts;
- PyType_Slot *slots;
- /* Baseclass or NULL (will always subclass `np.dtype`) */
- PyTypeObject *baseclass;
-} PyArrayDTypeMeta_Spec;
-
-
#define PyArrayDTypeMeta_Type \
(*(PyTypeObject *)__experimental_dtype_api_table[2])
typedef int __dtypemeta_fromspec(
@@ -489,16 +321,14 @@ PyArray_GetDefaultDescr(PyArray_DTypeMeta *DType)
*/
#if !defined(NO_IMPORT) && !defined(NO_IMPORT_ARRAY)
-#define __EXPERIMENTAL_DTYPE_VERSION 7
-
static int
import_experimental_dtype_api(int version)
{
- if (version != __EXPERIMENTAL_DTYPE_VERSION) {
+ if (version != __EXPERIMENTAL_DTYPE_API_VERSION) {
PyErr_Format(PyExc_RuntimeError,
"DType API version %d did not match header version %d. Please "
"update the import statement and check for API changes.",
- version, __EXPERIMENTAL_DTYPE_VERSION);
+ version, __EXPERIMENTAL_DTYPE_API_VERSION);
return -1;
}
if (__experimental_dtype_api_table != __uninitialized_table) {
diff --git a/numpy/core/include/numpy/npy_3kcompat.h b/numpy/core/include/numpy/npy_3kcompat.h
index 8e5e4000a..62fde943a 100644
--- a/numpy/core/include/numpy/npy_3kcompat.h
+++ b/numpy/core/include/numpy/npy_3kcompat.h
@@ -167,6 +167,26 @@ static inline int PyInt_Check(PyObject *op) {
#endif /* NPY_PY3K */
+/*
+ * Macros to protect CRT calls against instant termination when passed an
+ * invalid parameter (https://bugs.python.org/issue23524).
+ */
+#if defined _MSC_VER && _MSC_VER >= 1900
+
+#include <stdlib.h>
+
+extern _invalid_parameter_handler _Py_silent_invalid_parameter_handler;
+#define NPY_BEGIN_SUPPRESS_IPH { _invalid_parameter_handler _Py_old_handler = \
+ _set_thread_local_invalid_parameter_handler(_Py_silent_invalid_parameter_handler);
+#define NPY_END_SUPPRESS_IPH _set_thread_local_invalid_parameter_handler(_Py_old_handler); }
+
+#else
+
+#define NPY_BEGIN_SUPPRESS_IPH
+#define NPY_END_SUPPRESS_IPH
+
+#endif /* _MSC_VER >= 1900 */
+
static inline void
PyUnicode_ConcatAndDel(PyObject **left, PyObject *right)
@@ -242,13 +262,17 @@ npy_PyFile_Dup2(PyObject *file, char *mode, npy_off_t *orig_pos)
/* Convert to FILE* handle */
#ifdef _WIN32
+ NPY_BEGIN_SUPPRESS_IPH
handle = _fdopen(fd2, mode);
+ NPY_END_SUPPRESS_IPH
#else
handle = fdopen(fd2, mode);
#endif
if (handle == NULL) {
PyErr_SetString(PyExc_IOError,
- "Getting a FILE* from a Python file object failed");
+ "Getting a FILE* from a Python file object via "
+ "_fdopen failed. If you built NumPy, you probably "
+ "linked with the wrong debug/release runtime");
return NULL;
}
diff --git a/numpy/core/include/numpy/npy_common.h b/numpy/core/include/numpy/npy_common.h
index ea4a818c8..3b31bcf2d 100644
--- a/numpy/core/include/numpy/npy_common.h
+++ b/numpy/core/include/numpy/npy_common.h
@@ -40,39 +40,6 @@
#define NPY_GCC_OPT_3
#endif
-/* compile target attributes */
-#if defined HAVE_ATTRIBUTE_TARGET_AVX && defined HAVE_LINK_AVX
-#define NPY_GCC_TARGET_AVX __attribute__((target("avx")))
-#else
-#define NPY_GCC_TARGET_AVX
-#endif
-
-#if defined HAVE_ATTRIBUTE_TARGET_AVX2_WITH_INTRINSICS
-#define HAVE_ATTRIBUTE_TARGET_FMA
-#define NPY_GCC_TARGET_FMA __attribute__((target("avx2,fma")))
-#endif
-
-#if defined HAVE_ATTRIBUTE_TARGET_AVX2 && defined HAVE_LINK_AVX2
-#define NPY_GCC_TARGET_AVX2 __attribute__((target("avx2")))
-#else
-#define NPY_GCC_TARGET_AVX2
-#endif
-
-#if defined HAVE_ATTRIBUTE_TARGET_AVX512F && defined HAVE_LINK_AVX512F
-#define NPY_GCC_TARGET_AVX512F __attribute__((target("avx512f")))
-#elif defined HAVE_ATTRIBUTE_TARGET_AVX512F_WITH_INTRINSICS
-#define NPY_GCC_TARGET_AVX512F __attribute__((target("avx512f")))
-#else
-#define NPY_GCC_TARGET_AVX512F
-#endif
-
-#if defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX && defined HAVE_LINK_AVX512_SKX
-#define NPY_GCC_TARGET_AVX512_SKX __attribute__((target("avx512f,avx512dq,avx512vl,avx512bw,avx512cd")))
-#elif defined HAVE_ATTRIBUTE_TARGET_AVX512_SKX_WITH_INTRINSICS
-#define NPY_GCC_TARGET_AVX512_SKX __attribute__((target("avx512f,avx512dq,avx512vl,avx512bw,avx512cd")))
-#else
-#define NPY_GCC_TARGET_AVX512_SKX
-#endif
/*
* mark an argument (starting from 1) that must not be NULL and is not checked
* DO NOT USE IF FUNCTION CHECKS FOR NULL!! the compiler will remove the check
@@ -83,21 +50,6 @@
#define NPY_GCC_NONNULL(n)
#endif
-#if defined HAVE_XMMINTRIN_H && defined HAVE__MM_LOAD_PS
-#define NPY_HAVE_SSE_INTRINSICS
-#endif
-
-#if defined HAVE_EMMINTRIN_H && defined HAVE__MM_LOAD_PD
-#define NPY_HAVE_SSE2_INTRINSICS
-#endif
-
-#if defined HAVE_IMMINTRIN_H && defined HAVE_LINK_AVX2
-#define NPY_HAVE_AVX2_INTRINSICS
-#endif
-
-#if defined HAVE_IMMINTRIN_H && defined HAVE_LINK_AVX512F
-#define NPY_HAVE_AVX512F_INTRINSICS
-#endif
/*
* give a hint to the compiler which branch is more likely or unlikely
* to occur, e.g. rare error cases:
@@ -120,7 +72,7 @@
/* unlike _mm_prefetch also works on non-x86 */
#define NPY_PREFETCH(x, rw, loc) __builtin_prefetch((x), (rw), (loc))
#else
-#ifdef HAVE__MM_PREFETCH
+#ifdef NPY_HAVE_SSE
/* _MM_HINT_ET[01] (rw = 1) unsupported, only available in gcc >= 4.9 */
#define NPY_PREFETCH(x, rw, loc) _mm_prefetch((x), loc == 0 ? _MM_HINT_NTA : \
(loc == 1 ? _MM_HINT_T2 : \
diff --git a/numpy/core/meson.build b/numpy/core/meson.build
index 7cbfe6dc3..646dc0597 100644
--- a/numpy/core/meson.build
+++ b/numpy/core/meson.build
@@ -109,11 +109,19 @@ cdata.set('NPY_SIZEOF_PY_LONG_LONG',
if cc.has_header('complex.h')
cdata.set10('HAVE_COMPLEX_H', true)
cdata.set10('NPY_USE_C99_COMPLEX', true)
- complex_types_to_check = [
- ['NPY_HAVE_COMPLEX_FLOAT', 'NPY_SIZEOF_COMPLEX_FLOAT', 'complex float', 'float'],
- ['NPY_HAVE_COMPLEX_DOUBLE', 'NPY_SIZEOF_COMPLEX_DOUBLE', 'complex double', 'double'],
- ['NPY_HAVE_COMPLEX_LONG_DOUBLE', 'NPY_SIZEOF_COMPLEX_LONGDOUBLE', 'complex long double', 'long double'],
- ]
+ if cc.get_id() == 'msvc'
+ complex_types_to_check = [
+ ['NPY_HAVE_COMPLEX_FLOAT', 'NPY_SIZEOF_COMPLEX_FLOAT', '_Fcomplex', 'float'],
+ ['NPY_HAVE_COMPLEX_DOUBLE', 'NPY_SIZEOF_COMPLEX_DOUBLE', '_Dcomplex', 'double'],
+ ['NPY_HAVE_COMPLEX_LONG_DOUBLE', 'NPY_SIZEOF_COMPLEX_LONGDOUBLE', '_Lcomplex', 'long double'],
+ ]
+ else
+ complex_types_to_check = [
+ ['NPY_HAVE_COMPLEX_FLOAT', 'NPY_SIZEOF_COMPLEX_FLOAT', 'complex float', 'float'],
+ ['NPY_HAVE_COMPLEX_DOUBLE', 'NPY_SIZEOF_COMPLEX_DOUBLE', 'complex double', 'double'],
+ ['NPY_HAVE_COMPLEX_LONG_DOUBLE', 'NPY_SIZEOF_COMPLEX_LONGDOUBLE', 'complex long double', 'long double'],
+ ]
+ endif
foreach symbol_type: complex_types_to_check
if cc.has_type(symbol_type[2], prefix: '#include <complex.h>')
cdata.set10(symbol_type[0], true)
@@ -250,7 +258,9 @@ else
# function is not available in CI. For the latter there is a fallback path,
# but that is broken because we don't have the exact long double
# representation checks.
- cdata.set10('HAVE_STRTOLD_L', false)
+ if cc.get_id() != 'msvc'
+ cdata.set10('HAVE_STRTOLD_L', false)
+ endif
endif
# Other optional functions
@@ -293,16 +303,7 @@ optional_function_attributes = [
['optimize("O3")', 'OPTIMIZE_OPT_3'],
['optimize("O2")', 'OPTIMIZE_OPT_2'],
['optimize("nonnull (1)")', 'NONNULL'],
- ]
-if host_machine.cpu_family() in ['x86', 'x86_64']
- optional_function_attributes += [
- ['target("avx")', 'TARGET_AVX'],
- ['target("avx2")', 'TARGET_AVX2'],
- ['target("avx512f")', 'TARGET_AVX512F'],
- ['target("avx512f,avx512dq,avx512bw,avx512vl,avx512cd")', 'TARGET_AVX512_SKX'],
- ]
- # TODO: add the _WITH_INTRINSICS_AVX list
-endif
+]
#foreach attr: optional_function_attributes
# if cc.has_function_attribute(attr[0])
# cdata.set10('HAVE_ATTRIBUTE_' + attr[1], true)
@@ -321,26 +322,8 @@ optional_intrinsics = [
['__builtin_expect', '5, 0', [], []],
# Test `long long` for arm+clang 13 (gh-22811, but we use all versions):
['__builtin_mul_overflow', '(long long)5, 5, (int*)5', [], []],
+ ['__builtin_prefetch', '(float*)0, 0, 3', [], []],
]
-if host_machine.cpu_family() in ['x86', 'x86_64']
- optional_intrinsics += [
- # MMX only needed for icc, but some clang's don't have it
- ['_m_from_int64', '0', ['emmintrin.h'], []],
- ['_mm_load_ps', '(float*)0', ['xmmintrin.h'], []], # SSE
- ['_mm_prefetch', '(float*)0, _MM_HINT_NTA', ['xmmintrin.h'], []], # SSE
- ['_mm_load_pd', '(double*)0', ['emmintrin.h'], []], # SSE2
- ['__builtin_prefetch', '(float*)0, 0, 3', [], []],
- # Check that the linker can handle AVX
- ['__asm__ volatile', '"vpand %xmm1, %xmm2, %xmm3"', ['stdio.h'], ['HAVE_LINK_AVX']],
- ['__asm__ volatile', '"vpand %ymm1, %ymm2, %ymm3"', ['stdio.h'], ['HAVE_LINK_AVX2']],
- ['__asm__ volatile', '"vpaddd %zmm1, %zmm2, %zmm3"', ['stdio.h'], ['HAVE_LINK_AVX512F']],
- ['__asm__ volatile',
- '"vfpclasspd $0x40, %zmm15, %k6\\n vmovdqu8 %xmm0, %xmm1\\n vpbroadcastmb2q %k0, %xmm0"',
- ['stdio.h'], ['HAVE_LINK_AVX512_SKX']
- ],
- ['__asm__ volatile', '"xgetbv"', ['stdio.h'], ['HAVE_XGETBV']],
- ]
-endif
foreach intrin: optional_intrinsics
func = intrin[0]
func_args = intrin[1]
@@ -451,7 +434,12 @@ if cc.get_id() == 'msvc'
# libnpymath and libnpyrandom)
if cc.has_argument('-d2VolatileMetadata-')
staticlib_cflags += '-d2VolatileMetadata-'
- endif
+ endif
+endif
+# TODO: change to "feature" option in meson_options.txt? See
+# https://mesonbuild.com/Build-options.html#build-options
+if get_option('disable-simd-optimizations')
+ staticlib_cflags += '-DNPY_DISABLE_OPTIMIZATION'
endif
npy_math_internal_h = custom_target(
@@ -576,9 +564,13 @@ c_args_common = [
# Same as NPY_CXX_FLAGS (TODO: extend for what ccompiler_opt adds)
cpp_args_common = c_args_common + [
'-D__STDC_VERSION__=0', # for compatibility with C headers
- '-fno-exceptions', # no exception support
- '-fno-rtti', # no runtime type information
]
+if cc.get_id() != 'msvc'
+ cpp_args_common += [
+ '-fno-exceptions', # no exception support
+ '-fno-rtti', # no runtime type information
+ ]
+endif
# Other submodules depend on generated headers and include directories from
# core, wrap those up into a reusable dependency. Also useful for some test
@@ -594,7 +586,8 @@ np_core_dep = declare_dependency(
'.',
'include',
'src/common',
- ]
+ ],
+ compile_args: disable_simd_optimizations
)
@@ -689,6 +682,7 @@ src_multiarray = [
'src/multiarray/dtypemeta.c',
'src/multiarray/dragon4.c',
'src/multiarray/dtype_transfer.c',
+ 'src/multiarray/dtype_traversal.c',
src_file.process('src/multiarray/einsum.c.src'),
src_file.process('src/multiarray/einsum_sumprod.c.src'),
'src/multiarray/experimental_public_dtype_api.c',
@@ -718,7 +712,8 @@ src_multiarray = [
'src/multiarray/usertypes.c',
'src/multiarray/vdot.c',
src_file.process('src/common/npy_sort.h.src'),
- 'src/npysort/x86-qsort.dispatch.cpp',
+ 'src/npysort/simd_qsort.dispatch.cpp',
+ 'src/npysort/simd_qsort_16bit.dispatch.cpp',
'src/npysort/quicksort.cpp',
'src/npysort/mergesort.cpp',
'src/npysort/timsort.cpp',
@@ -760,6 +755,7 @@ src_umath = [
src_file.process('src/umath/loops_unary_fp.dispatch.c.src'),
src_file.process('src/umath/loops_unary_fp_le.dispatch.c.src'),
src_file.process('src/umath/loops_unary_complex.dispatch.c.src'),
+ src_file.process('src/umath/loops_autovec.dispatch.c.src'),
src_file.process('src/umath/matmul.c.src'),
src_file.process('src/umath/matmul.h.src'),
'src/umath/ufunc_type_resolution.c',
diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py
index fbb284696..1687e2dbf 100644
--- a/numpy/core/numeric.py
+++ b/numpy/core/numeric.py
@@ -4,6 +4,7 @@ import operator
import sys
import warnings
import numbers
+import builtins
import numpy as np
from . import multiarray
@@ -2023,7 +2024,7 @@ def binary_repr(num, width=None):
binary = bin(num)[2:]
binwidth = len(binary)
outwidth = (binwidth if width is None
- else max(binwidth, width))
+ else builtins.max(binwidth, width))
warn_if_insufficient(width, binwidth)
return binary.zfill(outwidth)
@@ -2043,7 +2044,7 @@ def binary_repr(num, width=None):
binary = bin(twocomp)[2:]
binwidth = len(binary)
- outwidth = max(binwidth, width)
+ outwidth = builtins.max(binwidth, width)
warn_if_insufficient(width, binwidth)
return '1' * (outwidth - binwidth) + binary
diff --git a/numpy/core/setup.py b/numpy/core/setup.py
index c7e28af9c..77e1ebf99 100644
--- a/numpy/core/setup.py
+++ b/numpy/core/setup.py
@@ -171,18 +171,6 @@ def check_math_capabilities(config, ext, moredefs, mathlibs):
else:
return 1
- # NOTE: not needed in Meson build, we set the minimum
- # compiler version to 8.4 to avoid this bug
- # GH-14787: Work around GCC<8.4 bug when compiling with AVX512
- # support on Windows-based platforms
- def check_gh14787(fn):
- if fn == 'attribute_target_avx512f':
- if (sys.platform in ('win32', 'cygwin') and
- config.check_compiler_gcc() and
- not config.check_gcc_version_at_least(8, 4)):
- ext.extra_compile_args.extend(
- ['-ffixed-xmm%s' % n for n in range(16, 32)])
-
#use_msvc = config.check_decl("_MSC_VER")
if not check_funcs_once(MANDATORY_FUNCS, add_to_moredefs=False):
raise SystemError("One of the required function to build numpy is not"
@@ -233,20 +221,8 @@ def check_math_capabilities(config, ext, moredefs, mathlibs):
for dec, fn in OPTIONAL_FUNCTION_ATTRIBUTES:
if config.check_gcc_function_attribute(dec, fn):
moredefs.append((fname2def(fn), 1))
- check_gh14787(fn)
platform = sysconfig.get_platform()
- if ("x86_64" in platform):
- for dec, fn in OPTIONAL_FUNCTION_ATTRIBUTES_AVX:
- if config.check_gcc_function_attribute(dec, fn):
- moredefs.append((fname2def(fn), 1))
- check_gh14787(fn)
- for dec, fn, code, header in (
- OPTIONAL_FUNCTION_ATTRIBUTES_WITH_INTRINSICS_AVX):
- if config.check_gcc_function_attribute_with_intrinsics(
- dec, fn, code, header):
- moredefs.append((fname2def(fn), 1))
-
for fn in OPTIONAL_VARIABLE_ATTRIBUTES:
if config.check_gcc_variable_attribute(fn):
m = fn.replace("(", "_").replace(")", "_")
@@ -840,6 +816,7 @@ def configuration(parent_package='',top_path=None):
join('src', 'multiarray', 'descriptor.h'),
join('src', 'multiarray', 'dtypemeta.h'),
join('src', 'multiarray', 'dtype_transfer.h'),
+ join('src', 'multiarray', 'dtype_traversal.h'),
join('src', 'multiarray', 'dragon4.h'),
join('src', 'multiarray', 'einsum_debug.h'),
join('src', 'multiarray', 'einsum_sumprod.h'),
@@ -913,6 +890,7 @@ def configuration(parent_package='',top_path=None):
join('src', 'multiarray', 'dtypemeta.c'),
join('src', 'multiarray', 'dragon4.c'),
join('src', 'multiarray', 'dtype_transfer.c'),
+ join('src', 'multiarray', 'dtype_traversal.c'),
join('src', 'multiarray', 'einsum.c.src'),
join('src', 'multiarray', 'einsum_sumprod.c.src'),
join('src', 'multiarray', 'experimental_public_dtype_api.c'),
@@ -942,7 +920,6 @@ def configuration(parent_package='',top_path=None):
join('src', 'multiarray', 'usertypes.c'),
join('src', 'multiarray', 'vdot.c'),
join('src', 'common', 'npy_sort.h.src'),
- join('src', 'npysort', 'x86-qsort.dispatch.cpp'),
join('src', 'npysort', 'quicksort.cpp'),
join('src', 'npysort', 'mergesort.cpp'),
join('src', 'npysort', 'timsort.cpp'),
@@ -964,6 +941,8 @@ def configuration(parent_package='',top_path=None):
# links to the arm64 npymath library,
# see gh-22673
join('src', 'npymath', 'arm64_exports.c'),
+ join('src', 'npysort', 'simd_qsort.dispatch.cpp'),
+ join('src', 'npysort', 'simd_qsort_16bit.dispatch.cpp'),
]
#######################################################################
@@ -1018,6 +997,7 @@ def configuration(parent_package='',top_path=None):
join('src', 'umath', 'loops_modulo.dispatch.c.src'),
join('src', 'umath', 'loops_comparison.dispatch.c.src'),
join('src', 'umath', 'loops_unary_complex.dispatch.c.src'),
+ join('src', 'umath', 'loops_autovec.dispatch.c.src'),
join('src', 'umath', 'matmul.h.src'),
join('src', 'umath', 'matmul.c.src'),
join('src', 'umath', 'clip.h'),
diff --git a/numpy/core/setup_common.py b/numpy/core/setup_common.py
index 0512457f4..ef8d21fa7 100644
--- a/numpy/core/setup_common.py
+++ b/numpy/core/setup_common.py
@@ -183,25 +183,7 @@ OPTIONAL_INTRINSICS = [("__builtin_isnan", '5.'),
# Test `long long` for arm+clang 13 (gh-22811,
# but we use all versions of __builtin_mul_overflow):
("__builtin_mul_overflow", '(long long)5, 5, (int*)5'),
- # MMX only needed for icc, but some clangs don't have it
- ("_m_from_int64", '0', "emmintrin.h"),
- ("_mm_load_ps", '(float*)0', "xmmintrin.h"), # SSE
- ("_mm_prefetch", '(float*)0, _MM_HINT_NTA',
- "xmmintrin.h"), # SSE
- ("_mm_load_pd", '(double*)0', "emmintrin.h"), # SSE2
("__builtin_prefetch", "(float*)0, 0, 3"),
- # check that the linker can handle avx
- ("__asm__ volatile", '"vpand %xmm1, %xmm2, %xmm3"',
- "stdio.h", "LINK_AVX"),
- ("__asm__ volatile", '"vpand %ymm1, %ymm2, %ymm3"',
- "stdio.h", "LINK_AVX2"),
- ("__asm__ volatile", '"vpaddd %zmm1, %zmm2, %zmm3"',
- "stdio.h", "LINK_AVX512F"),
- ("__asm__ volatile", '"vfpclasspd $0x40, %zmm15, %k6\\n"\
- "vmovdqu8 %xmm0, %xmm1\\n"\
- "vpbroadcastmb2q %k0, %xmm0\\n"',
- "stdio.h", "LINK_AVX512_SKX"),
- ("__asm__ volatile", '"xgetbv"', "stdio.h", "XGETBV"),
]
# function attributes
@@ -216,44 +198,6 @@ OPTIONAL_FUNCTION_ATTRIBUTES = [('__attribute__((optimize("unroll-loops")))',
('__attribute__((nonnull (1)))',
'attribute_nonnull'),
]
-
-OPTIONAL_FUNCTION_ATTRIBUTES_AVX = [('__attribute__((target ("avx")))',
- 'attribute_target_avx'),
- ('__attribute__((target ("avx2")))',
- 'attribute_target_avx2'),
- ('__attribute__((target ("avx512f")))',
- 'attribute_target_avx512f'),
- ('__attribute__((target ("avx512f,avx512dq,avx512bw,avx512vl,avx512cd")))',
- 'attribute_target_avx512_skx'),
- ]
-
-# function attributes with intrinsics
-# To ensure your compiler can compile avx intrinsics with just the attributes
-# gcc 4.8.4 support attributes but not with intrisics
-# tested via "#include<%s> int %s %s(void *){code; return 0;};" % (header, attribute, name, code)
-# function name will be converted to HAVE_<upper-case-name> preprocessor macro
-# The _mm512_castps_si512 instruction is specific check for AVX-512F support
-# in gcc-4.9 which is missing a subset of intrinsics. See
-# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61878
-OPTIONAL_FUNCTION_ATTRIBUTES_WITH_INTRINSICS_AVX = [
- ('__attribute__((target("avx2,fma")))',
- 'attribute_target_avx2_with_intrinsics',
- '__m256 temp = _mm256_set1_ps(1.0); temp = \
- _mm256_fmadd_ps(temp, temp, temp)',
- 'immintrin.h'),
- ('__attribute__((target("avx512f")))',
- 'attribute_target_avx512f_with_intrinsics',
- '__m512i temp = _mm512_castps_si512(_mm512_set1_ps(1.0))',
- 'immintrin.h'),
- ('__attribute__((target ("avx512f,avx512dq,avx512bw,avx512vl,avx512cd")))',
- 'attribute_target_avx512_skx_with_intrinsics',
- '__mmask8 temp = _mm512_fpclass_pd_mask(_mm512_set1_pd(1.0), 0x01);\
- __m512i unused_temp = \
- _mm512_castps_si512(_mm512_set1_ps(1.0));\
- _mm_mask_storeu_epi8(NULL, 0xFF, _mm_broadcastmb_epi64(temp))',
- 'immintrin.h'),
- ]
-
def fname2def(name):
return "HAVE_%s" % name.upper()
diff --git a/numpy/core/src/common/common.hpp b/numpy/core/src/common/common.hpp
new file mode 100644
index 000000000..47d790bcf
--- /dev/null
+++ b/numpy/core/src/common/common.hpp
@@ -0,0 +1,11 @@
+#ifndef NUMPY_CORE_SRC_COMMON_COMMON_HPP
+#define NUMPY_CORE_SRC_COMMON_COMMON_HPP
+/*
+ * The following C++ headers are safe to be used standalone, however,
+ * they are gathered to make it easy for us and for the future need to support PCH.
+ */
+#include "npstd.hpp"
+#include "half.hpp"
+#include "meta.hpp"
+
+#endif // NUMPY_CORE_SRC_COMMON_COMMON_HPP
diff --git a/numpy/core/src/common/half.hpp b/numpy/core/src/common/half.hpp
new file mode 100644
index 000000000..399f2fa79
--- /dev/null
+++ b/numpy/core/src/common/half.hpp
@@ -0,0 +1,63 @@
+#ifndef NUMPY_CORE_SRC_COMMON_HALF_HPP
+#define NUMPY_CORE_SRC_COMMON_HALF_HPP
+
+#include "npstd.hpp"
+
+// TODO(@seiko2plus):
+// - covers half-precision operations that being supported by numpy/halffloat.h
+// - support __fp16
+// - optimize x86 half<->single via cpu_fp16
+// - optimize ppc64 half<->single via cpu_vsx3
+
+namespace np {
+
+/// @addtogroup cpp_core_types
+/// @{
+
+/// Provides a type that implements 16-bit floating point (half-precision).
+/// This type is ensured to be 16-bit size.
+class Half final {
+ public:
+ /// @name Public Constructors
+ /// @{
+
+ /// Default constructor. initialize nothing.
+ Half() = default;
+ /// Copy.
+ Half(const Half &r)
+ {
+ data_.u = r.data_.u;
+ }
+
+ /// @}
+
+ /// Returns a new Half constracted from the IEEE 754 binary16.
+ /// @param b the value of binary16.
+ static Half FromBits(uint16_t b)
+ {
+ Half f;
+ f.data_.u = b;
+ return f;
+ }
+ /// Returns the IEEE 754 binary16 representation.
+ uint16_t Bits() const
+ {
+ return data_.u;
+ }
+
+ private:
+ union {
+ uint16_t u;
+/*
+TODO(@seiko2plus): support __fp16
+#ifdef NPY_HAVE_HW_FP16
+ __fp16 f;
+#endif
+*/
+ } data_;
+};
+
+/// @} cpp_core_types
+
+} // namespace np
+#endif // NUMPY_CORE_SRC_COMMON_HALF_HPP
diff --git a/numpy/core/src/common/meta.hpp b/numpy/core/src/common/meta.hpp
new file mode 100644
index 000000000..27ea1857e
--- /dev/null
+++ b/numpy/core/src/common/meta.hpp
@@ -0,0 +1,54 @@
+#ifndef NUMPY_CORE_SRC_COMMON_META_HPP
+#define NUMPY_CORE_SRC_COMMON_META_HPP
+
+#include "npstd.hpp"
+
+namespace np { namespace meta {
+/// @addtogroup cpp_core_meta
+/// @{
+
+namespace details {
+template<int size, bool unsig>
+struct IntBySize;
+
+template<bool unsig>
+struct IntBySize<sizeof(uint8_t), unsig> {
+ using Type = typename std::conditional<
+ unsig, uint8_t, int8_t>::type;
+};
+template<bool unsig>
+struct IntBySize<sizeof(uint16_t), unsig> {
+ using Type = typename std::conditional<
+ unsig, uint16_t, int16_t>::type;
+};
+template<bool unsig>
+struct IntBySize<sizeof(uint32_t), unsig> {
+ using Type = typename std::conditional<
+ unsig, uint32_t, int32_t>::type;
+};
+template<bool unsig>
+struct IntBySize<sizeof(uint64_t), unsig> {
+ using Type = typename std::conditional<
+ unsig, uint64_t, int64_t>::type;
+};
+} // namespace details
+
+/// Provides safe conversion of any integer type synonyms
+/// to a fixed-width integer type.
+template<typename T>
+struct FixedWidth {
+ using TF_ = typename details::IntBySize<
+ sizeof(T), std::is_unsigned<T>::value
+ >::Type;
+
+ using Type = typename std::conditional<
+ std::is_integral<T>::value, TF_, T
+ >::type;
+};
+
+/// @} cpp_core_meta
+
+}} // namespace np::meta
+
+#endif // NUMPY_CORE_SRC_COMMON_META_HPP
+
diff --git a/numpy/core/src/common/npstd.hpp b/numpy/core/src/common/npstd.hpp
new file mode 100644
index 000000000..71993bd7c
--- /dev/null
+++ b/numpy/core/src/common/npstd.hpp
@@ -0,0 +1,54 @@
+#ifndef NUMPY_CORE_SRC_COMMON_NPSTD_HPP
+#define NUMPY_CORE_SRC_COMMON_NPSTD_HPP
+
+#include <cstddef>
+#include <cstring>
+#include <cctype>
+
+#include <string>
+#include <algorithm>
+#include <utility>
+#include <cstdlib>
+#include <cmath>
+#include <complex>
+#include <type_traits>
+
+#include <numpy/npy_common.h>
+
+#include "npy_config.h"
+
+namespace np {
+/// @addtogroup cpp_core_types
+/// @{
+using std::uint8_t;
+using std::int8_t;
+using std::uint16_t;
+using std::int16_t;
+using std::uint32_t;
+using std::int32_t;
+using std::uint64_t;
+using std::int64_t;
+using std::uintptr_t;
+using std::intptr_t;
+using std::complex;
+
+/** Guard for long double.
+ *
+ * The C implementation defines long double as double
+ * on MinGW to provide compatibility with MSVC to unify
+ * one behavior under Windows OS, which makes npy_longdouble
+ * not fit to be used with template specialization or overloading.
+ *
+ * This type will be set to `void` when `npy_longdouble` is not defined
+ * as `long double`.
+ */
+using LongDouble = typename std::conditional<
+ !std::is_same<npy_longdouble, long double>::value,
+ void, npy_longdouble
+>::type;
+/// @} cpp_core_types
+
+} // namespace np
+
+#endif // NUMPY_CORE_SRC_COMMON_NPSTD_HPP
+
diff --git a/numpy/core/src/common/npy_config.h b/numpy/core/src/common/npy_config.h
index d6886c5ea..715b17777 100644
--- a/numpy/core/src/common/npy_config.h
+++ b/numpy/core/src/common/npy_config.h
@@ -2,6 +2,7 @@
#define NUMPY_CORE_SRC_COMMON_NPY_CONFIG_H_
#include "config.h"
+#include "npy_cpu_dispatch.h" // brings NPY_HAVE_[CPU features]
#include "numpy/numpyconfig.h"
#include "numpy/utils.h"
#include "numpy/npy_os.h"
diff --git a/numpy/core/src/common/npy_cpu_features.c b/numpy/core/src/common/npy_cpu_features.c
index 949c75ad5..92a4e432b 100644
--- a/numpy/core/src/common/npy_cpu_features.c
+++ b/numpy/core/src/common/npy_cpu_features.c
@@ -81,12 +81,14 @@ static struct {
{NPY_CPU_FEATURE_AVX512VBMI, "AVX512VBMI"},
{NPY_CPU_FEATURE_AVX512VBMI2, "AVX512VBMI2"},
{NPY_CPU_FEATURE_AVX512BITALG, "AVX512BITALG"},
+ {NPY_CPU_FEATURE_AVX512FP16 , "AVX512FP16"},
{NPY_CPU_FEATURE_AVX512_KNL, "AVX512_KNL"},
{NPY_CPU_FEATURE_AVX512_KNM, "AVX512_KNM"},
{NPY_CPU_FEATURE_AVX512_SKX, "AVX512_SKX"},
{NPY_CPU_FEATURE_AVX512_CLX, "AVX512_CLX"},
{NPY_CPU_FEATURE_AVX512_CNL, "AVX512_CNL"},
{NPY_CPU_FEATURE_AVX512_ICL, "AVX512_ICL"},
+ {NPY_CPU_FEATURE_AVX512_SPR, "AVX512_SPR"},
{NPY_CPU_FEATURE_VSX, "VSX"},
{NPY_CPU_FEATURE_VSX2, "VSX2"},
{NPY_CPU_FEATURE_VSX3, "VSX3"},
@@ -506,6 +508,11 @@ npy__cpu_init_features(void)
npy__cpu_have[NPY_CPU_FEATURE_AVX512VBMI2] &&
npy__cpu_have[NPY_CPU_FEATURE_AVX512BITALG] &&
npy__cpu_have[NPY_CPU_FEATURE_AVX512VPOPCNTDQ];
+ // Sapphire Rapids
+ npy__cpu_have[NPY_CPU_FEATURE_AVX512FP16] = (reg[3] & (1 << 23)) != 0;
+ npy__cpu_have[NPY_CPU_FEATURE_AVX512_SPR] = npy__cpu_have[NPY_CPU_FEATURE_AVX512_ICL] &&
+ npy__cpu_have[NPY_CPU_FEATURE_AVX512FP16];
+
}
}
diff --git a/numpy/core/src/common/npy_cpu_features.h b/numpy/core/src/common/npy_cpu_features.h
index 96c543e70..b49aea247 100644
--- a/numpy/core/src/common/npy_cpu_features.h
+++ b/numpy/core/src/common/npy_cpu_features.h
@@ -43,6 +43,7 @@ enum npy_cpu_features
NPY_CPU_FEATURE_AVX512VNNI = 42,
NPY_CPU_FEATURE_AVX512VBMI2 = 43,
NPY_CPU_FEATURE_AVX512BITALG = 44,
+ NPY_CPU_FEATURE_AVX512FP16 = 45,
// X86 CPU Groups
// Knights Landing (F,CD,ER,PF)
@@ -57,6 +58,8 @@ enum npy_cpu_features
NPY_CPU_FEATURE_AVX512_CNL = 105,
// Ice Lake (F,CD,BW,DQ,VL,IFMA,VBMI,VNNI,VBMI2,BITALG,VPOPCNTDQ)
NPY_CPU_FEATURE_AVX512_ICL = 106,
+ // Sapphire Rapids (Ice Lake, AVX512FP16)
+ NPY_CPU_FEATURE_AVX512_SPR = 107,
// IBM/POWER VSX
// POWER7
diff --git a/numpy/core/src/common/numpyos.c b/numpy/core/src/common/numpyos.c
index 5ee95191d..2fec06e1c 100644
--- a/numpy/core/src/common/numpyos.c
+++ b/numpy/core/src/common/numpyos.c
@@ -779,3 +779,25 @@ NumPyOS_strtoull(const char *str, char **endptr, int base)
return strtoull(str, endptr, base);
}
+#ifdef _MSC_VER
+
+#include <stdlib.h>
+
+#if _MSC_VER >= 1900
+/* npy3k_compat.h uses this function in the _Py_BEGIN/END_SUPPRESS_IPH
+ * macros. It does not need to be defined when building using MSVC
+ * earlier than 14.0 (_MSC_VER == 1900).
+ */
+
+static void __cdecl _silent_invalid_parameter_handler(
+ wchar_t const* expression,
+ wchar_t const* function,
+ wchar_t const* file,
+ unsigned int line,
+ uintptr_t pReserved) { }
+
+_invalid_parameter_handler _Py_silent_invalid_parameter_handler = _silent_invalid_parameter_handler;
+
+#endif
+
+#endif
diff --git a/numpy/core/src/common/simd/avx2/memory.h b/numpy/core/src/common/simd/avx2/memory.h
index 64692b54c..81144a36b 100644
--- a/numpy/core/src/common/simd/avx2/memory.h
+++ b/numpy/core/src/common/simd/avx2/memory.h
@@ -215,7 +215,7 @@ NPY_FINLINE npyv_s64 npyv_load_till_s64(const npy_int64 *ptr, npy_uintp nlane, n
const __m256i steps = npyv_set_s64(0, 1, 2, 3);
__m256i vnlane = npyv_setall_s64(nlane > 4 ? 4 : (int)nlane);
__m256i mask = _mm256_cmpgt_epi64(vnlane, steps);
- __m256i payload = _mm256_maskload_epi64((const void*)ptr, mask);
+ __m256i payload = _mm256_maskload_epi64((const long long*)ptr, mask);
return _mm256_blendv_epi8(vfill, payload, mask);
}
// fill zero to rest lanes
@@ -225,7 +225,7 @@ NPY_FINLINE npyv_s64 npyv_load_tillz_s64(const npy_int64 *ptr, npy_uintp nlane)
const __m256i steps = npyv_set_s64(0, 1, 2, 3);
__m256i vnlane = npyv_setall_s64(nlane > 4 ? 4 : (int)nlane);
__m256i mask = _mm256_cmpgt_epi64(vnlane, steps);
- return _mm256_maskload_epi64((const void*)ptr, mask);
+ return _mm256_maskload_epi64((const long long*)ptr, mask);
}
//// 64-bit nlane
@@ -240,7 +240,7 @@ NPY_FINLINE npyv_s32 npyv_load2_till_s32(const npy_int32 *ptr, npy_uintp nlane,
const __m256i steps = npyv_set_s64(0, 1, 2, 3);
__m256i vnlane = npyv_setall_s64(nlane > 4 ? 4 : (int)nlane);
__m256i mask = _mm256_cmpgt_epi64(vnlane, steps);
- __m256i payload = _mm256_maskload_epi64((const void*)ptr, mask);
+ __m256i payload = _mm256_maskload_epi64((const long long*)ptr, mask);
return _mm256_blendv_epi8(vfill, payload, mask);
}
// fill zero to rest lanes
@@ -253,7 +253,7 @@ NPY_FINLINE npyv_u64 npyv_load2_tillz_s64(const npy_int64 *ptr, npy_uintp nlane)
assert(nlane > 0);
npy_int64 m = -((npy_int64)(nlane > 1));
__m256i mask = npyv_set_s64(-1, -1, m, m);
- return _mm256_maskload_epi64((const void*)ptr, mask);
+ return _mm256_maskload_epi64((const long long*)ptr, mask);
}
// fill zero to rest lanes
NPY_FINLINE npyv_u64 npyv_load2_till_s64(const npy_int64 *ptr, npy_uintp nlane,
@@ -262,7 +262,7 @@ NPY_FINLINE npyv_u64 npyv_load2_till_s64(const npy_int64 *ptr, npy_uintp nlane,
const __m256i vfill = npyv_set_s64(0, 0, fill_lo, fill_hi);
npy_int64 m = -((npy_int64)(nlane > 1));
__m256i mask = npyv_set_s64(-1, -1, m, m);
- __m256i payload = _mm256_maskload_epi64((const void*)ptr, mask);
+ __m256i payload = _mm256_maskload_epi64((const long long*)ptr, mask);
return _mm256_blendv_epi8(vfill, payload, mask);
}
/*********************************
@@ -295,7 +295,7 @@ npyv_loadn_till_s64(const npy_int64 *ptr, npy_intp stride, npy_uintp nlane, npy_
const __m256i steps = npyv_set_s64(0, 1, 2, 3);
__m256i vnlane = npyv_setall_s64(nlane > 4 ? 4 : (int)nlane);
__m256i mask = _mm256_cmpgt_epi64(vnlane, steps);
- return _mm256_mask_i64gather_epi64(vfill, (const void*)ptr, idx, mask, 8);
+ return _mm256_mask_i64gather_epi64(vfill, (const long long*)ptr, idx, mask, 8);
}
// fill zero to rest lanes
NPY_FINLINE npyv_s64
@@ -315,7 +315,7 @@ NPY_FINLINE npyv_s64 npyv_loadn2_till_s32(const npy_int32 *ptr, npy_intp stride,
const __m256i steps = npyv_set_s64(0, 1, 2, 3);
__m256i vnlane = npyv_setall_s64(nlane > 4 ? 4 : (int)nlane);
__m256i mask = _mm256_cmpgt_epi64(vnlane, steps);
- return _mm256_mask_i64gather_epi64(vfill, (const void*)ptr, idx, mask, 4);
+ return _mm256_mask_i64gather_epi64(vfill, (const long long*)ptr, idx, mask, 4);
}
// fill zero to rest lanes
NPY_FINLINE npyv_s32 npyv_loadn2_tillz_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane)
@@ -361,7 +361,7 @@ NPY_FINLINE void npyv_store_till_s64(npy_int64 *ptr, npy_uintp nlane, npyv_s64 a
const __m256i steps = npyv_set_s64(0, 1, 2, 3);
__m256i vnlane = npyv_setall_s64(nlane > 8 ? 8 : (int)nlane);
__m256i mask = _mm256_cmpgt_epi64(vnlane, steps);
- _mm256_maskstore_epi64((void*)ptr, mask, a);
+ _mm256_maskstore_epi64((long long*)ptr, mask, a);
}
//// 64-bit nlane
diff --git a/numpy/core/src/common/simd/sse/memory.h b/numpy/core/src/common/simd/sse/memory.h
index b97f3ec2e..4c8e86a6f 100644
--- a/numpy/core/src/common/simd/sse/memory.h
+++ b/numpy/core/src/common/simd/sse/memory.h
@@ -244,13 +244,14 @@ NPY_FINLINE npyv_s32 npyv_load_tillz_s32(const npy_int32 *ptr, npy_uintp nlane)
return _mm_cvtsi32_si128(*ptr);
case 2:
return _mm_loadl_epi64((const __m128i*)ptr);
- case 3:;
- npyv_s32 a = _mm_loadl_epi64((const __m128i*)ptr);
- #ifdef NPY_HAVE_SSE41
- return _mm_insert_epi32(a, ptr[2], 2);
- #else
- return _mm_unpacklo_epi64(a, _mm_cvtsi32_si128(ptr[2]));
- #endif
+ case 3: {
+ npyv_s32 a = _mm_loadl_epi64((const __m128i*)ptr);
+ #ifdef NPY_HAVE_SSE41
+ return _mm_insert_epi32(a, ptr[2], 2);
+ #else
+ return _mm_unpacklo_epi64(a, _mm_cvtsi32_si128(ptr[2]));
+ #endif
+ }
default:
return npyv_load_s32(ptr);
}
@@ -371,23 +372,27 @@ npyv_loadn_tillz_s32(const npy_int32 *ptr, npy_intp stride, npy_uintp nlane)
case 1:
return _mm_cvtsi32_si128(ptr[0]);
case 2:;
- npyv_s32 a = _mm_cvtsi32_si128(ptr[0]);
-#ifdef NPY_HAVE_SSE41
- return _mm_insert_epi32(a, ptr[stride], 1);
-#else
- return _mm_unpacklo_epi32(a, _mm_cvtsi32_si128(ptr[stride]));
-#endif // NPY_HAVE_SSE41
- case 3:;
- a = _mm_cvtsi32_si128(ptr[0]);
-#ifdef NPY_HAVE_SSE41
- a = _mm_insert_epi32(a, ptr[stride], 1);
- a = _mm_insert_epi32(a, ptr[stride*2], 2);
- return a;
-#else
- a = _mm_unpacklo_epi32(a, _mm_cvtsi32_si128(ptr[stride]));
- a = _mm_unpacklo_epi64(a, _mm_cvtsi32_si128(ptr[stride*2]));
- return a;
-#endif // NPY_HAVE_SSE41
+ {
+ npyv_s32 a = _mm_cvtsi32_si128(ptr[0]);
+ #ifdef NPY_HAVE_SSE41
+ return _mm_insert_epi32(a, ptr[stride], 1);
+ #else
+ return _mm_unpacklo_epi32(a, _mm_cvtsi32_si128(ptr[stride]));
+ #endif // NPY_HAVE_SSE41
+ }
+ case 3:
+ {
+ npyv_s32 a = _mm_cvtsi32_si128(ptr[0]);
+ #ifdef NPY_HAVE_SSE41
+ a = _mm_insert_epi32(a, ptr[stride], 1);
+ a = _mm_insert_epi32(a, ptr[stride*2], 2);
+ return a;
+ #else
+ a = _mm_unpacklo_epi32(a, _mm_cvtsi32_si128(ptr[stride]));
+ a = _mm_unpacklo_epi64(a, _mm_cvtsi32_si128(ptr[stride*2]));
+ return a;
+ #endif // NPY_HAVE_SSE41
+ }
default:
return npyv_loadn_s32(ptr, stride);
}
diff --git a/numpy/core/src/common/simd/vec/arithmetic.h b/numpy/core/src/common/simd/vec/arithmetic.h
index 3c13ab06c..85f4d6b26 100644
--- a/numpy/core/src/common/simd/vec/arithmetic.h
+++ b/numpy/core/src/common/simd/vec/arithmetic.h
@@ -366,6 +366,7 @@ NPY_FINLINE float npyv_sum_f32(npyv_f32 a)
{
npyv_f32 sum = vec_add(a, npyv_combineh_f32(a, a));
return vec_extract(sum, 0) + vec_extract(sum, 1);
+ (void)sum;
}
#endif
@@ -386,6 +387,7 @@ NPY_FINLINE npy_uint16 npyv_sumup_u8(npyv_u8 a)
npyv_u32 four = vec_sum4s(a, zero);
npyv_s32 one = vec_sums((npyv_s32)four, (npyv_s32)zero);
return (npy_uint16)vec_extract(one, 3);
+ (void)one;
#endif
}
@@ -400,6 +402,7 @@ NPY_FINLINE npy_uint32 npyv_sumup_u16(npyv_u16 a)
npyv_u32 four = vec_add(eight.val[0], eight.val[1]);
npyv_s32 one = vec_sums((npyv_s32)four, zero);
return (npy_uint32)vec_extract(one, 3);
+ (void)one;
#endif
}
diff --git a/numpy/core/src/common/simd/vec/conversion.h b/numpy/core/src/common/simd/vec/conversion.h
index 0c0d5b3ac..922109f7b 100644
--- a/numpy/core/src/common/simd/vec/conversion.h
+++ b/numpy/core/src/common/simd/vec/conversion.h
@@ -96,6 +96,8 @@ npyv_pack_b8_b64(npyv_b64 a, npyv_b64 b, npyv_b64 c, npyv_b64 d,
#else
return vec_extract(r, 4);
#endif
+ // to suppress ambiguous warning: variable `r` but not used [-Wunused-but-set-variable]
+ (void)r;
}
NPY_FINLINE npy_uint64 npyv_tobits_b16(npyv_b16 a)
{
@@ -106,6 +108,8 @@ npyv_pack_b8_b64(npyv_b64 a, npyv_b64 b, npyv_b64 c, npyv_b64 d,
#else
return vec_extract(r, 8);
#endif
+ // to suppress ambiguous warning: variable `r` but not used [-Wunused-but-set-variable]
+ (void)r;
}
NPY_FINLINE npy_uint64 npyv_tobits_b32(npyv_b32 a)
{
@@ -120,6 +124,8 @@ npyv_pack_b8_b64(npyv_b64 a, npyv_b64 b, npyv_b64 c, npyv_b64 d,
#else
return vec_extract(r, 8);
#endif
+ // to suppress ambiguous warning: variable `r` but not used [-Wunused-but-set-variable]
+ (void)r;
}
NPY_FINLINE npy_uint64 npyv_tobits_b64(npyv_b64 a)
{
@@ -134,6 +140,8 @@ npyv_pack_b8_b64(npyv_b64 a, npyv_b64 b, npyv_b64 c, npyv_b64 d,
#else
return vec_extract(r, 8);
#endif
+ // to suppress ambiguous warning: variable `r` but not used [-Wunused-but-set-variable]
+ (void)r;
}
#else
NPY_FINLINE npy_uint64 npyv_tobits_b8(npyv_b8 a)
diff --git a/numpy/core/src/common/simd/vec/math.h b/numpy/core/src/common/simd/vec/math.h
index 95b16fdf7..85690f76c 100644
--- a/numpy/core/src/common/simd/vec/math.h
+++ b/numpy/core/src/common/simd/vec/math.h
@@ -186,6 +186,7 @@ NPY_IMPL_VEC_REDUCE_MINMAX(max, int32, s32)
{ \
npyv_##SFX r = vec_##INTRIN(a, vec_sld(a, a, 8)); \
return (npy_##STYPE)vec_extract(r, 0); \
+ (void)r; \
}
NPY_IMPL_VEC_REDUCE_MINMAX(min, uint64, u64)
NPY_IMPL_VEC_REDUCE_MINMAX(max, uint64, u64)
@@ -225,6 +226,7 @@ NPY_IMPL_VEC_REDUCE_MINMAX(max, int64, s64)
{ \
npyv_f64 r = vec_##INTRIN(a, vec_sld(a, a, 8)); \
return vec_extract(r, 0); \
+ (void)r; \
} \
NPY_FINLINE double npyv_reduce_##INTRIN##n_f64(npyv_f64 a) \
{ \
diff --git a/numpy/core/src/common/simd/vec/memory.h b/numpy/core/src/common/simd/vec/memory.h
index 3c17617b1..de78d02e3 100644
--- a/numpy/core/src/common/simd/vec/memory.h
+++ b/numpy/core/src/common/simd/vec/memory.h
@@ -46,14 +46,8 @@
#endif
// avoid aliasing rules
-#ifdef __cplusplus
- template<typename T_PTR>
- NPY_FINLINE npy_uint64 *npyv__ptr2u64(const T_PTR *ptr)
- { npy_uint64 *ptr64 = (npy_uint64*)ptr; return ptr64; }
-#else
- NPY_FINLINE npy_uint64 *npyv__ptr2u64(const void *ptr)
- { npy_uint64 *ptr64 = (npy_uint64*)ptr; return ptr64; }
-#endif // __cplusplus
+NPY_FINLINE npy_uint64 *npyv__ptr2u64(const void *ptr)
+{ npy_uint64 *ptr64 = (npy_uint64*)ptr; return ptr64; }
// load lower part
NPY_FINLINE npyv_u64 npyv__loadl(const void *ptr)
diff --git a/numpy/core/src/common/simd/vec/misc.h b/numpy/core/src/common/simd/vec/misc.h
index 7ea0f21f6..79c194d90 100644
--- a/numpy/core/src/common/simd/vec/misc.h
+++ b/numpy/core/src/common/simd/vec/misc.h
@@ -26,28 +26,28 @@
#define NPYV_IMPL_VEC_SPLTW(T_VEC, V) ((T_VEC){V, V, V, V})
#define NPYV_IMPL_VEC_SPLTD(T_VEC, V) ((T_VEC){V, V})
-#define npyv_setall_u8(VAL) NPYV_IMPL_VEC_SPLTB(npyv_u8, (unsigned char)VAL)
-#define npyv_setall_s8(VAL) NPYV_IMPL_VEC_SPLTB(npyv_s8, (signed char)VAL)
-#define npyv_setall_u16(VAL) NPYV_IMPL_VEC_SPLTH(npyv_u16, (unsigned short)VAL)
-#define npyv_setall_s16(VAL) NPYV_IMPL_VEC_SPLTH(npyv_s16, (short)VAL)
-#define npyv_setall_u32(VAL) NPYV_IMPL_VEC_SPLTW(npyv_u32, (unsigned int)VAL)
-#define npyv_setall_s32(VAL) NPYV_IMPL_VEC_SPLTW(npyv_s32, (int)VAL)
+#define npyv_setall_u8(VAL) NPYV_IMPL_VEC_SPLTB(npyv_u8, (unsigned char)(VAL))
+#define npyv_setall_s8(VAL) NPYV_IMPL_VEC_SPLTB(npyv_s8, (signed char)(VAL))
+#define npyv_setall_u16(VAL) NPYV_IMPL_VEC_SPLTH(npyv_u16, (unsigned short)(VAL))
+#define npyv_setall_s16(VAL) NPYV_IMPL_VEC_SPLTH(npyv_s16, (short)(VAL))
+#define npyv_setall_u32(VAL) NPYV_IMPL_VEC_SPLTW(npyv_u32, (unsigned int)(VAL))
+#define npyv_setall_s32(VAL) NPYV_IMPL_VEC_SPLTW(npyv_s32, (int)(VAL))
#if NPY_SIMD_F32
- #define npyv_setall_f32(VAL) NPYV_IMPL_VEC_SPLTW(npyv_f32, VAL)
+ #define npyv_setall_f32(VAL) NPYV_IMPL_VEC_SPLTW(npyv_f32, (VAL))
#endif
-#define npyv_setall_u64(VAL) NPYV_IMPL_VEC_SPLTD(npyv_u64, (npy_uint64)VAL)
-#define npyv_setall_s64(VAL) NPYV_IMPL_VEC_SPLTD(npyv_s64, (npy_int64)VAL)
+#define npyv_setall_u64(VAL) NPYV_IMPL_VEC_SPLTD(npyv_u64, (npy_uint64)(VAL))
+#define npyv_setall_s64(VAL) NPYV_IMPL_VEC_SPLTD(npyv_s64, (npy_int64)(VAL))
#define npyv_setall_f64(VAL) NPYV_IMPL_VEC_SPLTD(npyv_f64, VAL)
// vector with specific values set to each lane and
// set a specific value to all remained lanes
-#define npyv_setf_u8(FILL, ...) ((npyv_u8){NPYV__SET_FILL_16(char, FILL, __VA_ARGS__)})
-#define npyv_setf_s8(FILL, ...) ((npyv_s8){NPYV__SET_FILL_16(char, FILL, __VA_ARGS__)})
-#define npyv_setf_u16(FILL, ...) ((npyv_u16){NPYV__SET_FILL_8(short, FILL, __VA_ARGS__)})
+#define npyv_setf_u8(FILL, ...) ((npyv_u8){NPYV__SET_FILL_16(unsigned char, FILL, __VA_ARGS__)})
+#define npyv_setf_s8(FILL, ...) ((npyv_s8){NPYV__SET_FILL_16(signed char, FILL, __VA_ARGS__)})
+#define npyv_setf_u16(FILL, ...) ((npyv_u16){NPYV__SET_FILL_8(unsigned short, FILL, __VA_ARGS__)})
#define npyv_setf_s16(FILL, ...) ((npyv_s16){NPYV__SET_FILL_8(short, FILL, __VA_ARGS__)})
-#define npyv_setf_u32(FILL, ...) ((npyv_u32){NPYV__SET_FILL_4(int, FILL, __VA_ARGS__)})
+#define npyv_setf_u32(FILL, ...) ((npyv_u32){NPYV__SET_FILL_4(unsigned int, FILL, __VA_ARGS__)})
#define npyv_setf_s32(FILL, ...) ((npyv_s32){NPYV__SET_FILL_4(int, FILL, __VA_ARGS__)})
-#define npyv_setf_u64(FILL, ...) ((npyv_u64){NPYV__SET_FILL_2(npy_int64, FILL, __VA_ARGS__)})
+#define npyv_setf_u64(FILL, ...) ((npyv_u64){NPYV__SET_FILL_2(npy_uint64, FILL, __VA_ARGS__)})
#define npyv_setf_s64(FILL, ...) ((npyv_s64){NPYV__SET_FILL_2(npy_int64, FILL, __VA_ARGS__)})
#if NPY_SIMD_F32
#define npyv_setf_f32(FILL, ...) ((npyv_f32){NPYV__SET_FILL_4(float, FILL, __VA_ARGS__)})
diff --git a/numpy/core/src/multiarray/array_coercion.c b/numpy/core/src/multiarray/array_coercion.c
index f77a4a898..d6bee1a7b 100644
--- a/numpy/core/src/multiarray/array_coercion.c
+++ b/numpy/core/src/multiarray/array_coercion.c
@@ -16,6 +16,7 @@
#include "common_dtype.h"
#include "dtypemeta.h"
+#include "npy_argparse.h"
#include "abstractdtypes.h"
#include "array_coercion.h"
#include "ctors.h"
@@ -873,42 +874,61 @@ find_descriptor_from_array(
/**
* Given a dtype or DType object, find the correct descriptor to cast the
- * array to.
+ * array to. In some places, this function is used with dtype=NULL which
+ * means that legacy behavior is used: The dtype instances "S0", "U0", and
+ * "V0" are converted to mean the DType classes instead.
+ * When dtype != NULL, this path is ignored, and the function does nothing
+ * unless descr == NULL.
*
* This function is identical to normal casting using only the dtype, however,
* it supports inspecting the elements when the array has object dtype
* (and the given datatype describes a parametric DType class).
*
* @param arr
- * @param dtype A dtype instance or class.
+ * @param dtype NULL or a dtype class
+ * @param descr A dtype instance, if the dtype is NULL the dtype class is
+ * found and e.g. "S0" is converted to denote only String.
* @return A concrete dtype instance or NULL
*/
NPY_NO_EXPORT PyArray_Descr *
-PyArray_AdaptDescriptorToArray(PyArrayObject *arr, PyObject *dtype)
+PyArray_AdaptDescriptorToArray(
+ PyArrayObject *arr, PyArray_DTypeMeta *dtype, PyArray_Descr *descr)
{
/* If the requested dtype is flexible, adapt it */
- PyArray_Descr *new_dtype;
- PyArray_DTypeMeta *new_DType;
+ PyArray_Descr *new_descr;
int res;
- res = PyArray_ExtractDTypeAndDescriptor((PyObject *)dtype,
- &new_dtype, &new_DType);
- if (res < 0) {
- return NULL;
+ if (dtype != NULL && descr != NULL) {
+ /* descr was given and no special logic, return (call not necessary) */
+ Py_INCREF(descr);
+ return descr;
}
- if (new_dtype == NULL) {
- res = find_descriptor_from_array(arr, new_DType, &new_dtype);
+ if (dtype == NULL) {
+ res = PyArray_ExtractDTypeAndDescriptor(descr, &new_descr, &dtype);
if (res < 0) {
- Py_DECREF(new_DType);
return NULL;
}
- if (new_dtype == NULL) {
- /* This is an object array but contained no elements, use default */
- new_dtype = NPY_DT_CALL_default_descr(new_DType);
+ if (new_descr != NULL) {
+ Py_DECREF(dtype);
+ return new_descr;
}
}
- Py_DECREF(new_DType);
- return new_dtype;
+ else {
+ assert(descr == NULL); /* gueranteed above */
+ Py_INCREF(dtype);
+ }
+
+ res = find_descriptor_from_array(arr, dtype, &new_descr);
+ if (res < 0) {
+ Py_DECREF(dtype);
+ return NULL;
+ }
+ if (new_descr == NULL) {
+ /* This is an object array but contained no elements, use default */
+ new_descr = NPY_DT_CALL_default_descr(dtype);
+ }
+ Py_DECREF(dtype);
+ return new_descr;
}
@@ -1380,111 +1400,25 @@ PyArray_DiscoverDTypeAndShape(
}
-
-/**
- * Check the descriptor is a legacy "flexible" DType instance, this is
- * an instance which is (normally) not attached to an array, such as a string
- * of length 0 or a datetime with no unit.
- * These should be largely deprecated, and represent only the DType class
- * for most `dtype` parameters.
- *
- * TODO: This function should eventually receive a deprecation warning and
- * be removed.
- *
- * @param descr
- * @return 1 if this is not a concrete dtype instance 0 otherwise
- */
-static int
-descr_is_legacy_parametric_instance(PyArray_Descr *descr,
- PyArray_DTypeMeta *DType)
-{
- if (!NPY_DT_is_legacy(DType)) {
- return 0;
- }
-
- if (PyDataType_ISUNSIZED(descr)) {
- return 1;
- }
- /* Flexible descr with generic time unit (which can be adapted) */
- if (PyDataType_ISDATETIME(descr)) {
- PyArray_DatetimeMetaData *meta;
- meta = get_datetime_metadata_from_dtype(descr);
- if (meta->base == NPY_FR_GENERIC) {
- return 1;
- }
- }
- return 0;
-}
-
-
-/**
- * Given either a DType instance or class, (or legacy flexible instance),
- * ands sets output dtype instance and DType class. Both results may be
- * NULL, but if `out_descr` is set `out_DType` will always be the
- * corresponding class.
- *
- * @param dtype
- * @param out_descr
- * @param out_DType
- * @return 0 on success -1 on failure
- */
-NPY_NO_EXPORT int
-PyArray_ExtractDTypeAndDescriptor(PyObject *dtype,
- PyArray_Descr **out_descr, PyArray_DTypeMeta **out_DType)
-{
- *out_DType = NULL;
- *out_descr = NULL;
-
- if (dtype != NULL) {
- if (PyObject_TypeCheck(dtype, (PyTypeObject *)&PyArrayDTypeMeta_Type)) {
- assert(dtype != (PyObject * )&PyArrayDescr_Type); /* not np.dtype */
- *out_DType = (PyArray_DTypeMeta *)dtype;
- Py_INCREF(*out_DType);
- }
- else if (PyObject_TypeCheck((PyObject *)Py_TYPE(dtype),
- (PyTypeObject *)&PyArrayDTypeMeta_Type)) {
- *out_DType = NPY_DTYPE(dtype);
- Py_INCREF(*out_DType);
- if (!descr_is_legacy_parametric_instance((PyArray_Descr *)dtype,
- *out_DType)) {
- *out_descr = (PyArray_Descr *)dtype;
- Py_INCREF(*out_descr);
- }
- }
- else {
- PyErr_SetString(PyExc_TypeError,
- "dtype parameter must be a DType instance or class.");
- return -1;
- }
- }
- return 0;
-}
-
-
/*
* Python API function to expose the dtype+shape discovery functionality
* directly.
*/
NPY_NO_EXPORT PyObject *
_discover_array_parameters(PyObject *NPY_UNUSED(self),
- PyObject *args, PyObject *kwargs)
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
- static char *kwlist[] = {"obj", "dtype", NULL};
-
PyObject *obj;
- PyObject *dtype = NULL;
- PyArray_Descr *fixed_descriptor = NULL;
- PyArray_DTypeMeta *fixed_DType = NULL;
+ npy_dtype_info dt_info = {NULL, NULL};
npy_intp shape[NPY_MAXDIMS];
- if (!PyArg_ParseTupleAndKeywords(
- args, kwargs, "O|O:_discover_array_parameters", kwlist,
- &obj, &dtype)) {
- return NULL;
- }
-
- if (PyArray_ExtractDTypeAndDescriptor(dtype,
- &fixed_descriptor, &fixed_DType) < 0) {
+ NPY_PREPARE_ARGPARSER;
+ if (npy_parse_arguments(
+ "_discover_array_parameters", args, len_args, kwnames,
+ "", NULL, &obj,
+ "|dtype", &PyArray_DTypeOrDescrConverterOptional, &dt_info,
+ NULL, NULL, NULL) < 0) {
+ /* fixed is last to parse, so never necessary to clean up */
return NULL;
}
@@ -1493,9 +1427,9 @@ _discover_array_parameters(PyObject *NPY_UNUSED(self),
int ndim = PyArray_DiscoverDTypeAndShape(
obj, NPY_MAXDIMS, shape,
&coercion_cache,
- fixed_DType, fixed_descriptor, (PyArray_Descr **)&out_dtype, 0);
- Py_XDECREF(fixed_DType);
- Py_XDECREF(fixed_descriptor);
+ dt_info.dtype, dt_info.descr, (PyArray_Descr **)&out_dtype, 0);
+ Py_XDECREF(dt_info.dtype);
+ Py_XDECREF(dt_info.descr);
if (ndim < 0) {
return NULL;
}
diff --git a/numpy/core/src/multiarray/array_coercion.h b/numpy/core/src/multiarray/array_coercion.h
index 63d543cf7..0757c1cb8 100644
--- a/numpy/core/src/multiarray/array_coercion.h
+++ b/numpy/core/src/multiarray/array_coercion.h
@@ -26,7 +26,8 @@ NPY_NO_EXPORT int
PyArray_Pack(PyArray_Descr *descr, char *item, PyObject *value);
NPY_NO_EXPORT PyArray_Descr *
-PyArray_AdaptDescriptorToArray(PyArrayObject *arr, PyObject *dtype);
+PyArray_AdaptDescriptorToArray(
+ PyArrayObject *arr, PyArray_DTypeMeta *dtype, PyArray_Descr *descr);
NPY_NO_EXPORT int
PyArray_DiscoverDTypeAndShape(
@@ -36,14 +37,9 @@ PyArray_DiscoverDTypeAndShape(
PyArray_DTypeMeta *fixed_DType, PyArray_Descr *requested_descr,
PyArray_Descr **out_descr, int never_copy);
-NPY_NO_EXPORT int
-PyArray_ExtractDTypeAndDescriptor(PyObject *dtype,
- PyArray_Descr **out_descr, PyArray_DTypeMeta **out_DType);
-
NPY_NO_EXPORT PyObject *
_discover_array_parameters(PyObject *NPY_UNUSED(self),
- PyObject *args, PyObject *kwargs);
-
+ PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames);
/* Would make sense to inline the freeing functions everywhere */
/* Frees the coercion cache object recursively. */
diff --git a/numpy/core/src/multiarray/array_method.c b/numpy/core/src/multiarray/array_method.c
index 5001a67e9..87515bb03 100644
--- a/numpy/core/src/multiarray/array_method.c
+++ b/numpy/core/src/multiarray/array_method.c
@@ -264,7 +264,7 @@ fill_arraymethod_from_slots(
case NPY_METH_resolve_descriptors:
meth->resolve_descriptors = slot->pfunc;
continue;
- case NPY_METH_get_loop:
+ case _NPY_METH_get_loop:
/*
* NOTE: get_loop is considered "unstable" in the public API,
* I do not like the signature, and the `move_references`
@@ -344,28 +344,39 @@ fill_arraymethod_from_slots(
}
/* Check whether the provided loops make sense. */
+ if (meth->flags & NPY_METH_SUPPORTS_UNALIGNED) {
+ if (meth->unaligned_strided_loop == NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "Must provide unaligned strided inner loop when using "
+ "NPY_METH_SUPPORTS_UNALIGNED flag (in method: %s)",
+ spec->name);
+ return -1;
+ }
+ }
+ else {
+ if (meth->unaligned_strided_loop != NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "Must not provide unaligned strided inner loop when not "
+ "using NPY_METH_SUPPORTS_UNALIGNED flag (in method: %s)",
+ spec->name);
+ return -1;
+ }
+ }
+ /* Fill in the blanks: */
+ if (meth->unaligned_contiguous_loop == NULL) {
+ meth->unaligned_contiguous_loop = meth->unaligned_strided_loop;
+ }
if (meth->strided_loop == NULL) {
- PyErr_Format(PyExc_TypeError,
- "Must provide a strided inner loop function. (method: %s)",
- spec->name);
- return -1;
+ meth->strided_loop = meth->unaligned_strided_loop;
}
if (meth->contiguous_loop == NULL) {
meth->contiguous_loop = meth->strided_loop;
}
- if (meth->unaligned_contiguous_loop != NULL &&
- meth->unaligned_strided_loop == NULL) {
- PyErr_Format(PyExc_TypeError,
- "Must provide unaligned strided inner loop when providing "
- "a contiguous version. (method: %s)", spec->name);
- return -1;
- }
- if ((meth->unaligned_strided_loop == NULL) !=
- !(meth->flags & NPY_METH_SUPPORTS_UNALIGNED)) {
+
+ if (meth->strided_loop == NULL) {
PyErr_Format(PyExc_TypeError,
- "Must provide unaligned strided inner loop when casting spec "
- "has the NPY_METH_SUPPORTS_UNALIGNED flag. "
- "(method: %s)", spec->name);
+ "Must provide a strided inner loop function. (method: %s)",
+ spec->name);
return -1;
}
diff --git a/numpy/core/src/multiarray/array_method.h b/numpy/core/src/multiarray/array_method.h
index 138df69fa..c82a968cd 100644
--- a/numpy/core/src/multiarray/array_method.h
+++ b/numpy/core/src/multiarray/array_method.h
@@ -11,40 +11,7 @@
extern "C" {
#endif
-typedef enum {
- /* Flag for whether the GIL is required */
- NPY_METH_REQUIRES_PYAPI = 1 << 0,
- /*
- * Some functions cannot set floating point error flags, this flag
- * gives us the option (not requirement) to skip floating point error
- * setup/check. No function should set error flags and ignore them
- * since it would interfere with chaining operations (e.g. casting).
- */
- NPY_METH_NO_FLOATINGPOINT_ERRORS = 1 << 1,
- /* Whether the method supports unaligned access (not runtime) */
- NPY_METH_SUPPORTS_UNALIGNED = 1 << 2,
- /*
- * Used for reductions to allow reordering the operation. At this point
- * assume that if set, it also applies to normal operations though!
- */
- NPY_METH_IS_REORDERABLE = 1 << 3,
- /*
- * Private flag for now for *logic* functions. The logical functions
- * `logical_or` and `logical_and` can always cast the inputs to booleans
- * "safely" (because that is how the cast to bool is defined).
- * @seberg: I am not sure this is the best way to handle this, so its
- * private for now (also it is very limited anyway).
- * There is one "exception". NA aware dtypes cannot cast to bool
- * (hopefully), so the `??->?` loop should error even with this flag.
- * But a second NA fallback loop will be necessary.
- */
- _NPY_METH_FORCE_CAST_INPUTS = 1 << 17,
-
- /* All flags which can change at runtime */
- NPY_METH_RUNTIME_FLAGS = (
- NPY_METH_REQUIRES_PYAPI |
- NPY_METH_NO_FLOATINGPOINT_ERRORS),
-} NPY_ARRAYMETHOD_FLAGS;
+#include "numpy/_dtype_api.h"
/*
@@ -60,143 +27,6 @@ typedef enum {
((flags1 | flags2) & ~PyArrayMethod_MINIMAL_FLAGS) \
| (flags1 & flags2)))
-
-struct PyArrayMethodObject_tag;
-
-/*
- * This struct is specific to an individual (possibly repeated) call of
- * the ArrayMethods strided operator, and as such is passed into the various
- * methods of the ArrayMethod object (the resolve_descriptors function,
- * the get_loop function and the individual lowlevel strided operator calls).
- * It thus has to be persistent for one end-user call, and then be discarded.
- *
- * TODO: Before making this public, we should review which information should
- * be stored on the Context/BoundArrayMethod vs. the ArrayMethod.
- */
-typedef struct PyArrayMethod_Context_tag {
- PyObject *caller; /* E.g. the original ufunc, may be NULL */
- struct PyArrayMethodObject_tag *method;
-
- /* Operand descriptors, filled in by resolve_descriptors */
- PyArray_Descr **descriptors;
-} PyArrayMethod_Context;
-
-
-typedef int (PyArrayMethod_StridedLoop)(PyArrayMethod_Context *context,
- char *const *data, const npy_intp *dimensions, const npy_intp *strides,
- NpyAuxData *transferdata);
-
-
-typedef NPY_CASTING (resolve_descriptors_function)(
- struct PyArrayMethodObject_tag *method,
- PyArray_DTypeMeta **dtypes,
- PyArray_Descr **given_descrs,
- PyArray_Descr **loop_descrs,
- npy_intp *view_offset);
-
-
-typedef int (get_loop_function)(
- PyArrayMethod_Context *context,
- int aligned, int move_references,
- const npy_intp *strides,
- PyArrayMethod_StridedLoop **out_loop,
- NpyAuxData **out_transferdata,
- NPY_ARRAYMETHOD_FLAGS *flags);
-
-
-/**
- * Query an ArrayMethod for the initial value for use in reduction.
- *
- * @param context The arraymethod context, mainly to access the descriptors.
- * @param reduction_is_empty Whether the reduction is empty. When it is, the
- * value returned may differ. In this case it is a "default" value that
- * may differ from the "identity" value normally used. For example:
- * - `0.0` is the default for `sum([])`. But `-0.0` is the correct
- * identity otherwise as it preserves the sign for `sum([-0.0])`.
- * - We use no identity for object, but return the default of `0` and `1`
- * for the empty `sum([], dtype=object)` and `prod([], dtype=object)`.
- * This allows `np.sum(np.array(["a", "b"], dtype=object))` to work.
- * - `-inf` or `INT_MIN` for `max` is an identity, but at least `INT_MIN`
- * not a good *default* when there are no items.
- * @param initial Pointer to initial data to be filled (if possible)
- *
- * @returns -1, 0, or 1 indicating error, no initial value, and initial being
- * successfully filled. Errors must not be given where 0 is correct, NumPy
- * may call this even when not strictly necessary.
- */
-typedef int (get_reduction_initial_function)(
- PyArrayMethod_Context *context, npy_bool reduction_is_empty,
- char *initial);
-
-/*
- * The following functions are only used be the wrapping array method defined
- * in umath/wrapping_array_method.c
- */
-
-/*
- * The function to convert the given descriptors (passed in to
- * `resolve_descriptors`) and translates them for the wrapped loop.
- * The new descriptors MUST be viewable with the old ones, `NULL` must be
- * supported (for outputs) and should normally be forwarded.
- *
- * The function must clean up on error.
- *
- * NOTE: We currently assume that this translation gives "viewable" results.
- * I.e. there is no additional casting related to the wrapping process.
- * In principle that could be supported, but not sure it is useful.
- * This currently also means that e.g. alignment must apply identically
- * to the new dtypes.
- *
- * TODO: Due to the fact that `resolve_descriptors` is also used for `can_cast`
- * there is no way to "pass out" the result of this function. This means
- * it will be called twice for every ufunc call.
- * (I am considering including `auxdata` as an "optional" parameter to
- * `resolve_descriptors`, so that it can be filled there if not NULL.)
- */
-typedef int translate_given_descrs_func(int nin, int nout,
- PyArray_DTypeMeta *wrapped_dtypes[],
- PyArray_Descr *given_descrs[], PyArray_Descr *new_descrs[]);
-
-/**
- * The function to convert the actual loop descriptors (as returned by the
- * original `resolve_descriptors` function) to the ones the output array
- * should use.
- * This function must return "viewable" types, it must not mutate them in any
- * form that would break the inner-loop logic. Does not need to support NULL.
- *
- * The function must clean up on error.
- *
- * @param nargs Number of arguments
- * @param new_dtypes The DTypes of the output (usually probably not needed)
- * @param given_descrs Original given_descrs to the resolver, necessary to
- * fetch any information related to the new dtypes from the original.
- * @param original_descrs The `loop_descrs` returned by the wrapped loop.
- * @param loop_descrs The output descriptors, compatible to `original_descrs`.
- *
- * @returns 0 on success, -1 on failure.
- */
-typedef int translate_loop_descrs_func(int nin, int nout,
- PyArray_DTypeMeta *new_dtypes[], PyArray_Descr *given_descrs[],
- PyArray_Descr *original_descrs[], PyArray_Descr *loop_descrs[]);
-
-
-/*
- * This struct will be public and necessary for creating a new ArrayMethod
- * object (casting and ufuncs).
- * We could version the struct, although since we allow passing arbitrary
- * data using the slots, and have flags, that may be enough?
- * (See also PyBoundArrayMethodObject.)
- */
-typedef struct {
- const char *name;
- int nin, nout;
- NPY_CASTING casting;
- NPY_ARRAYMETHOD_FLAGS flags;
- PyArray_DTypeMeta **dtypes;
- PyType_Slot *slots;
-} PyArrayMethod_Spec;
-
-
/*
* Structure of the ArrayMethod. This structure should probably not be made
* public. If necessary, we can make certain operations on it public
@@ -254,21 +84,6 @@ typedef struct {
extern NPY_NO_EXPORT PyTypeObject PyArrayMethod_Type;
extern NPY_NO_EXPORT PyTypeObject PyBoundArrayMethod_Type;
-/*
- * SLOTS IDs For the ArrayMethod creation, one public, the IDs are fixed.
- * TODO: Before making it public, consider adding a large constant to private
- * slots.
- */
-#define NPY_METH_resolve_descriptors 1
-#define NPY_METH_get_loop 2
-#define NPY_METH_get_reduction_initial 3
-/* specific loops for constructions/default get_loop: */
-#define NPY_METH_strided_loop 4
-#define NPY_METH_contiguous_loop 5
-#define NPY_METH_unaligned_strided_loop 6
-#define NPY_METH_unaligned_contiguous_loop 7
-#define NPY_METH_contiguous_indexed_loop 8
-
/*
* Used internally (initially) for real to complex loops only
diff --git a/numpy/core/src/multiarray/arrayobject.c b/numpy/core/src/multiarray/arrayobject.c
index a4e49ce89..0434e8676 100644
--- a/numpy/core/src/multiarray/arrayobject.c
+++ b/numpy/core/src/multiarray/arrayobject.c
@@ -56,6 +56,7 @@ maintainer email: oliphant.travis@ieee.org
#include "alloc.h"
#include "mem_overlap.h"
#include "numpyos.h"
+#include "refcount.h"
#include "strfuncs.h"
#include "binop_override.h"
@@ -452,9 +453,11 @@ array_dealloc(PyArrayObject *self)
}
if ((fa->flags & NPY_ARRAY_OWNDATA) && fa->data) {
- /* Free internal references if an Object array */
- if (PyDataType_FLAGCHK(fa->descr, NPY_ITEM_REFCOUNT)) {
- PyArray_XDECREF(self);
+ /* Free any internal references */
+ if (PyDataType_REFCHK(fa->descr)) {
+ if (PyArray_ClearArray(self) < 0) {
+ PyErr_WriteUnraisable(NULL);
+ }
}
if (fa->mem_handler == NULL) {
char *env = getenv("NUMPY_WARN_IF_NO_MEM_POLICY");
diff --git a/numpy/core/src/multiarray/convert.c b/numpy/core/src/multiarray/convert.c
index ef231587d..e99bc3fe4 100644
--- a/numpy/core/src/multiarray/convert.c
+++ b/numpy/core/src/multiarray/convert.c
@@ -157,7 +157,24 @@ PyArray_ToFile(PyArrayObject *self, FILE *fp, char *sep, char *format)
NPY_BEGIN_ALLOW_THREADS;
#if defined (_MSC_VER) && defined(_WIN64)
- /* Workaround Win64 fwrite() bug. Ticket #1660 */
+ /* Workaround Win64 fwrite() bug. Issue gh-2556
+ * If you touch this code, please run this test which is so slow
+ * it was removed from the test suite
+ *
+ * fourgbplus = 2**32 + 2**16
+ * testbytes = np.arange(8, dtype=np.int8)
+ * n = len(testbytes)
+ * flike = tempfile.NamedTemporaryFile()
+ * f = flike.file
+ * np.tile(testbytes, fourgbplus // testbytes.nbytes).tofile(f)
+ * flike.seek(0)
+ * a = np.fromfile(f, dtype=np.int8)
+ * flike.close()
+ * assert_(len(a) == fourgbplus)
+ * # check only start and end for speed:
+ * assert_((a[:n] == testbytes).all())
+ * assert_((a[-n:] == testbytes).all())
+ */
{
npy_intp maxsize = 2147483648 / PyArray_DESCR(self)->elsize;
npy_intp chunksize;
diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c
index 14341c103..53db5c577 100644
--- a/numpy/core/src/multiarray/convert_datatype.c
+++ b/numpy/core/src/multiarray/convert_datatype.c
@@ -18,6 +18,7 @@
#include "can_cast_table.h"
#include "common.h"
#include "ctors.h"
+#include "descriptor.h"
#include "dtypemeta.h"
#include "common_dtype.h"
#include "scalartypes.h"
@@ -31,6 +32,7 @@
#include "array_method.h"
#include "usertypes.h"
#include "dtype_transfer.h"
+#include "dtype_traversal.h"
#include "arrayobject.h"
@@ -152,7 +154,7 @@ PyArray_GetCastingImpl(PyArray_DTypeMeta *from, PyArray_DTypeMeta *to)
{
PyObject *res;
if (from == to) {
- res = NPY_DT_SLOTS(from)->within_dtype_castingimpl;
+ res = (PyObject *)NPY_DT_SLOTS(from)->within_dtype_castingimpl;
}
else {
res = PyDict_GetItemWithError(NPY_DT_SLOTS(from)->castingimpls, (PyObject *)to);
@@ -329,7 +331,7 @@ PyArray_CastToType(PyArrayObject *arr, PyArray_Descr *dtype, int is_f_order)
return NULL;
}
- Py_SETREF(dtype, PyArray_AdaptDescriptorToArray(arr, (PyObject *)dtype));
+ Py_SETREF(dtype, PyArray_AdaptDescriptorToArray(arr, NULL, dtype));
if (dtype == NULL) {
return NULL;
}
@@ -1148,7 +1150,7 @@ PyArray_CastDescrToDType(PyArray_Descr *descr, PyArray_DTypeMeta *given_DType)
*/
NPY_NO_EXPORT PyArray_Descr *
PyArray_FindConcatenationDescriptor(
- npy_intp n, PyArrayObject **arrays, PyObject *requested_dtype)
+ npy_intp n, PyArrayObject **arrays, PyArray_Descr *requested_dtype)
{
if (requested_dtype == NULL) {
return PyArray_ResultType(n, arrays, 0, NULL);
@@ -2414,8 +2416,7 @@ PyArray_AddCastingImplementation(PyBoundArrayMethodObject *meth)
return -1;
}
Py_INCREF(meth->method);
- NPY_DT_SLOTS(meth->dtypes[0])->within_dtype_castingimpl = (
- (PyObject *)meth->method);
+ NPY_DT_SLOTS(meth->dtypes[0])->within_dtype_castingimpl = meth->method;
return 0;
}
@@ -2668,7 +2669,7 @@ add_numeric_cast(PyArray_DTypeMeta *from, PyArray_DTypeMeta *to)
* consider moving this warning into the inner-loop at some point
* for simplicity (this requires ensuring it is only emitted once).
*/
- slots[5].slot = NPY_METH_get_loop;
+ slots[5].slot = _NPY_METH_get_loop;
slots[5].pfunc = &complex_to_noncomplex_get_loop;
slots[6].slot = 0;
slots[6].pfunc = NULL;
@@ -2689,7 +2690,7 @@ add_numeric_cast(PyArray_DTypeMeta *from, PyArray_DTypeMeta *to)
/* When there is no casting (equivalent C-types) use byteswap loops */
slots[0].slot = NPY_METH_resolve_descriptors;
slots[0].pfunc = &legacy_same_dtype_resolve_descriptors;
- slots[1].slot = NPY_METH_get_loop;
+ slots[1].slot = _NPY_METH_get_loop;
slots[1].pfunc = &get_byteswap_loop;
slots[2].slot = 0;
slots[2].pfunc = NULL;
@@ -2873,7 +2874,7 @@ add_other_to_and_from_string_cast(
*/
PyArray_DTypeMeta *dtypes[2] = {other, string};
PyType_Slot slots[] = {
- {NPY_METH_get_loop, &legacy_cast_get_strided_loop},
+ {_NPY_METH_get_loop, &legacy_cast_get_strided_loop},
{NPY_METH_resolve_descriptors, &cast_to_string_resolve_descriptors},
{0, NULL}};
PyArrayMethod_Spec spec = {
@@ -3012,7 +3013,7 @@ PyArray_InitializeStringCasts(void)
/* string<->string and unicode<->unicode have their own specialized casts */
PyArray_DTypeMeta *dtypes[2];
PyType_Slot slots[] = {
- {NPY_METH_get_loop, &string_to_string_get_loop},
+ {_NPY_METH_get_loop, &string_to_string_get_loop},
{NPY_METH_resolve_descriptors, &string_to_string_resolve_descriptors},
{0, NULL}};
PyArrayMethod_Spec spec = {
@@ -3711,7 +3712,7 @@ PyArray_InitializeVoidToVoidCast(void)
PyArray_DTypeMeta *Void = PyArray_DTypeFromTypeNum(NPY_VOID);
PyArray_DTypeMeta *dtypes[2] = {Void, Void};
PyType_Slot slots[] = {
- {NPY_METH_get_loop, &void_to_void_get_loop},
+ {_NPY_METH_get_loop, &void_to_void_get_loop},
{NPY_METH_resolve_descriptors, &void_to_void_resolve_descriptors},
{0, NULL}};
PyArrayMethod_Spec spec = {
@@ -3894,7 +3895,7 @@ PyArray_InitializeObjectToObjectCast(void)
PyArray_DTypeMeta *Object = PyArray_DTypeFromTypeNum(NPY_OBJECT);
PyArray_DTypeMeta *dtypes[2] = {Object, Object};
PyType_Slot slots[] = {
- {NPY_METH_get_loop, &object_to_object_get_loop},
+ {_NPY_METH_get_loop, &object_to_object_get_loop},
{0, NULL}};
PyArrayMethod_Spec spec = {
.name = "object_to_object_cast",
@@ -3912,6 +3913,21 @@ PyArray_InitializeObjectToObjectCast(void)
}
+static int
+PyArray_SetClearFunctions(void)
+{
+ PyArray_DTypeMeta *Object = PyArray_DTypeFromTypeNum(NPY_OBJECT);
+ NPY_DT_SLOTS(Object)->get_clear_loop = &npy_get_clear_object_strided_loop;
+ Py_DECREF(Object); /* use borrowed */
+
+ PyArray_DTypeMeta *Void = PyArray_DTypeFromTypeNum(NPY_VOID);
+ NPY_DT_SLOTS(Void)->get_clear_loop = &npy_get_clear_void_and_legacy_user_dtype_loop;
+ Py_DECREF(Void); /* use borrowed */
+ return 0;
+}
+
+
+
NPY_NO_EXPORT int
PyArray_InitializeCasts()
{
@@ -3931,5 +3947,8 @@ PyArray_InitializeCasts()
if (PyArray_InitializeDatetimeCasts() < 0) {
return -1;
}
+ if (PyArray_SetClearFunctions() < 0) {
+ return -1;
+ }
return 0;
}
diff --git a/numpy/core/src/multiarray/convert_datatype.h b/numpy/core/src/multiarray/convert_datatype.h
index 1a23965f8..a68c669ee 100644
--- a/numpy/core/src/multiarray/convert_datatype.h
+++ b/numpy/core/src/multiarray/convert_datatype.h
@@ -82,7 +82,7 @@ PyArray_CastDescrToDType(PyArray_Descr *descr, PyArray_DTypeMeta *given_DType);
NPY_NO_EXPORT PyArray_Descr *
PyArray_FindConcatenationDescriptor(
- npy_intp n, PyArrayObject **arrays, PyObject *requested_dtype);
+ npy_intp n, PyArrayObject **arrays, PyArray_Descr *requested_dtype);
NPY_NO_EXPORT int
PyArray_AddCastingImplementation(PyBoundArrayMethodObject *meth);
diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c
index fa3b103dd..38af60427 100644
--- a/numpy/core/src/multiarray/ctors.c
+++ b/numpy/core/src/multiarray/ctors.c
@@ -20,6 +20,7 @@
#include "common.h"
#include "ctors.h"
#include "convert_datatype.h"
+#include "descriptor.h"
#include "dtypemeta.h"
#include "shape.h"
#include "npy_buffer.h"
@@ -1621,7 +1622,7 @@ PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth,
PyArray_Descr *fixed_descriptor;
PyArray_DTypeMeta *fixed_DType;
- if (PyArray_ExtractDTypeAndDescriptor((PyObject *)newtype,
+ if (PyArray_ExtractDTypeAndDescriptor(newtype,
&fixed_descriptor, &fixed_DType) < 0) {
Py_XDECREF(newtype);
return NULL;
diff --git a/numpy/core/src/multiarray/datetime.c b/numpy/core/src/multiarray/datetime.c
index 695b696c2..820f40fd8 100644
--- a/numpy/core/src/multiarray/datetime.c
+++ b/numpy/core/src/multiarray/datetime.c
@@ -4100,7 +4100,7 @@ PyArray_InitializeDatetimeCasts()
};
slots[0].slot = NPY_METH_resolve_descriptors;
slots[0].pfunc = &time_to_time_resolve_descriptors;
- slots[1].slot = NPY_METH_get_loop;
+ slots[1].slot = _NPY_METH_get_loop;
slots[1].pfunc = &time_to_time_get_loop;
slots[2].slot = 0;
slots[2].pfunc = NULL;
@@ -4130,7 +4130,7 @@ PyArray_InitializeDatetimeCasts()
slots[0].slot = NPY_METH_resolve_descriptors;
slots[0].pfunc = &datetime_to_timedelta_resolve_descriptors;
- slots[1].slot = NPY_METH_get_loop;
+ slots[1].slot = _NPY_METH_get_loop;
slots[1].pfunc = &legacy_cast_get_strided_loop;
slots[2].slot = 0;
slots[2].pfunc = NULL;
@@ -4203,7 +4203,7 @@ PyArray_InitializeDatetimeCasts()
slots[0].slot = NPY_METH_resolve_descriptors;
slots[0].pfunc = &time_to_string_resolve_descriptors;
/* Strided loop differs for the two */
- slots[1].slot = NPY_METH_get_loop;
+ slots[1].slot = _NPY_METH_get_loop;
slots[2].slot = 0;
slots[2].pfunc = NULL;
@@ -4252,7 +4252,7 @@ PyArray_InitializeDatetimeCasts()
/* The default type resolution should work fine. */
slots[0].slot = NPY_METH_resolve_descriptors;
slots[0].pfunc = &string_to_datetime_cast_resolve_descriptors;
- slots[1].slot = NPY_METH_get_loop;
+ slots[1].slot = _NPY_METH_get_loop;
slots[1].pfunc = &string_to_datetime_cast_get_loop;
slots[2].slot = 0;
slots[2].pfunc = NULL;
diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c
index ac8aeef1a..68d398d64 100644
--- a/numpy/core/src/multiarray/descriptor.c
+++ b/numpy/core/src/multiarray/descriptor.c
@@ -1390,6 +1390,141 @@ PyArray_DescrConverter2(PyObject *obj, PyArray_Descr **at)
}
}
+
+/**
+ * Check the descriptor is a legacy "flexible" DType instance, this is
+ * an instance which is (normally) not attached to an array, such as a string
+ * of length 0 or a datetime with no unit.
+ * These should be largely deprecated, and represent only the DType class
+ * for most `dtype` parameters.
+ *
+ * TODO: This function should eventually receive a deprecation warning and
+ * be removed.
+ *
+ * @param descr
+ * @return 1 if this is not a concrete dtype instance 0 otherwise
+ */
+static int
+descr_is_legacy_parametric_instance(PyArray_Descr *descr,
+ PyArray_DTypeMeta *DType)
+{
+ if (!NPY_DT_is_legacy(DType)) {
+ return 0;
+ }
+
+ if (PyDataType_ISUNSIZED(descr)) {
+ return 1;
+ }
+ /* Flexible descr with generic time unit (which can be adapted) */
+ if (PyDataType_ISDATETIME(descr)) {
+ PyArray_DatetimeMetaData *meta;
+ meta = get_datetime_metadata_from_dtype(descr);
+ if (meta->base == NPY_FR_GENERIC) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * Given a descriptor (dtype instance), handles conversion of legacy flexible
+ * "unsized" descriptors to their DType. It returns the DType and descriptor
+ * both results can be NULL (if the input is). But it always sets the DType
+ * when a descriptor is set.
+ *
+ * @param dtype
+ * @param out_descr
+ * @param out_DType
+ * @return 0 on success -1 on failure
+ */
+NPY_NO_EXPORT int
+PyArray_ExtractDTypeAndDescriptor(PyArray_Descr *dtype,
+ PyArray_Descr **out_descr, PyArray_DTypeMeta **out_DType)
+{
+ *out_DType = NULL;
+ *out_descr = NULL;
+
+ if (dtype != NULL) {
+ *out_DType = NPY_DTYPE(dtype);
+ Py_INCREF(*out_DType);
+ if (!descr_is_legacy_parametric_instance((PyArray_Descr *)dtype,
+ *out_DType)) {
+ *out_descr = (PyArray_Descr *)dtype;
+ Py_INCREF(*out_descr);
+ }
+ }
+ return 0;
+}
+
+
+/**
+ * Converter function filling in an npy_dtype_info struct on success.
+ *
+ * @param obj representing a dtype instance (descriptor) or DType class.
+ * @param[out] npy_dtype_info filled with the DType class and dtype/descriptor
+ * instance. The class is always set while the instance may be NULL.
+ * On error, both will be NULL.
+ * @return 0 on failure and 1 on success (as a converter)
+ */
+NPY_NO_EXPORT int
+PyArray_DTypeOrDescrConverterRequired(PyObject *obj, npy_dtype_info *dt_info)
+{
+ /*
+ * Allow dtype classes pass, this could also be generalized to at least
+ * some scalar types (right now most of these give instances or)
+ */
+ dt_info->dtype = NULL;
+ dt_info->descr = NULL;
+
+ if (PyObject_TypeCheck(obj, &PyArrayDTypeMeta_Type)) {
+ Py_INCREF(obj);
+ dt_info->dtype = (PyArray_DTypeMeta *)obj;
+ dt_info->descr = NULL;
+ return NPY_SUCCEED;
+ }
+ PyArray_Descr *descr;
+ if (PyArray_DescrConverter(obj, &descr) != NPY_SUCCEED) {
+ return NPY_FAIL;
+ }
+ /*
+ * The above converts e.g. "S" or "S0" to the prototype instance, we make
+ * it behave the same as the DType. This is not fully correct, "S0" should
+ * be considered an instance with actual 0 length.
+ * TODO: It would be nice to fix that eventually.
+ */
+ int res = PyArray_ExtractDTypeAndDescriptor(
+ descr, &dt_info->descr, &dt_info->dtype);
+ Py_DECREF(descr);
+ if (res < 0) {
+ return NPY_FAIL;
+ }
+ return NPY_SUCCEED;
+}
+
+
+/**
+ * Converter function filling in an npy_dtype_info struct on success. It
+ * accepts `None` and does nothing in that case (user must initialize to
+ * NULL anyway).
+ *
+ * @param obj None or obj representing a dtype instance (descr) or DType class.
+ * @param[out] npy_dtype_info filled with the DType class and dtype/descriptor
+ * instance. If `obj` is None, is not modified. Otherwise the class
+ * is always set while the instance may be NULL.
+ * On error, both will be NULL.
+ * @return 0 on failure and 1 on success (as a converter)
+ */
+NPY_NO_EXPORT int
+PyArray_DTypeOrDescrConverterOptional(PyObject *obj, npy_dtype_info *dt_info)
+{
+ if (obj == Py_None) {
+ /* caller must have initialized for the optional version */
+ return NPY_SUCCEED;
+ }
+ return PyArray_DTypeOrDescrConverterRequired(obj, dt_info);
+}
+
/**
* Get a dtype instance from a python type
*/
@@ -2312,7 +2447,6 @@ arraydescr_new(PyTypeObject *subtype,
PyErr_NoMemory();
return NULL;
}
- PyObject_Init((PyObject *)descr, subtype);
descr->f = &NPY_DT_SLOTS(DType)->f;
Py_XINCREF(DType->scalar_type);
descr->typeobj = DType->scalar_type;
diff --git a/numpy/core/src/multiarray/descriptor.h b/numpy/core/src/multiarray/descriptor.h
index 7e6f212f2..b14edc3aa 100644
--- a/numpy/core/src/multiarray/descriptor.h
+++ b/numpy/core/src/multiarray/descriptor.h
@@ -1,6 +1,29 @@
#ifndef NUMPY_CORE_SRC_MULTIARRAY_DESCRIPTOR_H_
#define NUMPY_CORE_SRC_MULTIARRAY_DESCRIPTOR_H_
+
+/*
+ * In some API calls we wish to allow users to pass a DType class or a
+ * dtype instances with different meanings.
+ * This struct is mainly used for the argument parsing in
+ * `PyArray_DTypeOrDescrConverter`.
+ */
+typedef struct {
+ PyArray_DTypeMeta *dtype;
+ PyArray_Descr *descr;
+} npy_dtype_info;
+
+
+NPY_NO_EXPORT int
+PyArray_DTypeOrDescrConverterOptional(PyObject *, npy_dtype_info *dt_info);
+
+NPY_NO_EXPORT int
+PyArray_DTypeOrDescrConverterRequired(PyObject *, npy_dtype_info *dt_info);
+
+NPY_NO_EXPORT int
+PyArray_ExtractDTypeAndDescriptor(PyArray_Descr *dtype,
+ PyArray_Descr **out_descr, PyArray_DTypeMeta **out_DType);
+
NPY_NO_EXPORT PyObject *arraydescr_protocol_typestr_get(
PyArray_Descr *, void *);
NPY_NO_EXPORT PyObject *arraydescr_protocol_descr_get(
diff --git a/numpy/core/src/multiarray/dtype_transfer.c b/numpy/core/src/multiarray/dtype_transfer.c
index 549300f73..9e77d456d 100644
--- a/numpy/core/src/multiarray/dtype_transfer.c
+++ b/numpy/core/src/multiarray/dtype_transfer.c
@@ -32,6 +32,7 @@
#include "shape.h"
#include "dtype_transfer.h"
+#include "dtype_traversal.h"
#include "alloc.h"
#include "dtypemeta.h"
#include "array_method.h"
@@ -73,18 +74,6 @@ _safe_print(PyObject *obj)
}
#endif
-/*
- * Returns a transfer function which DECREFs any references in src_type.
- *
- * Returns NPY_SUCCEED or NPY_FAIL.
- */
-static int
-get_decref_transfer_function(int aligned,
- npy_intp src_stride,
- PyArray_Descr *src_dtype,
- NPY_cast_info *cast_info,
- int *out_needs_api);
-
/*************************** COPY REFERENCES *******************************/
@@ -159,7 +148,7 @@ typedef struct {
NpyAuxData base;
PyArray_GetItemFunc *getitem;
PyArrayObject_fields arr_fields;
- NPY_cast_info decref_src;
+ NPY_traverse_info decref_src;
} _any_to_object_auxdata;
@@ -169,7 +158,7 @@ _any_to_object_auxdata_free(NpyAuxData *auxdata)
_any_to_object_auxdata *data = (_any_to_object_auxdata *)auxdata;
Py_DECREF(data->arr_fields.descr);
- NPY_cast_info_xfree(&data->decref_src);
+ NPY_traverse_info_xfree(&data->decref_src);
PyMem_Free(data);
}
@@ -187,7 +176,7 @@ _any_to_object_auxdata_clone(NpyAuxData *auxdata)
Py_INCREF(res->arr_fields.descr);
if (data->decref_src.func != NULL) {
- if (NPY_cast_info_copy(&res->decref_src, &data->decref_src) < 0) {
+ if (NPY_traverse_info_copy(&res->decref_src, &data->decref_src) < 0) {
NPY_AUXDATA_FREE((NpyAuxData *)res);
return NULL;
}
@@ -228,8 +217,8 @@ _strided_to_strided_any_to_object(
}
if (data->decref_src.func != NULL) {
/* If necessary, clear the input buffer (`move_references`) */
- if (data->decref_src.func(&data->decref_src.context,
- &orig_src, &N, &src_stride, data->decref_src.auxdata) < 0) {
+ if (data->decref_src.func(NULL, data->decref_src.descr,
+ orig_src, N, src_stride, data->decref_src.auxdata) < 0) {
return -1;
}
}
@@ -265,18 +254,18 @@ any_to_object_get_loop(
data->arr_fields.nd = 0;
data->getitem = context->descriptors[0]->f->getitem;
- NPY_cast_info_init(&data->decref_src);
+ NPY_traverse_info_init(&data->decref_src);
if (move_references && PyDataType_REFCHK(context->descriptors[0])) {
- int needs_api;
- if (get_decref_transfer_function(
+ NPY_ARRAYMETHOD_FLAGS clear_flags;
+ if (PyArray_GetClearFunction(
aligned, strides[0], context->descriptors[0],
- &data->decref_src,
- &needs_api) == NPY_FAIL) {
+ &data->decref_src, &clear_flags) < 0) {
NPY_AUXDATA_FREE(*out_transferdata);
*out_transferdata = NULL;
return -1;
}
+ *flags = PyArrayMethod_COMBINED_FLAGS(*flags, clear_flags);
}
return 0;
}
@@ -1393,7 +1382,7 @@ typedef struct {
npy_intp N;
NPY_cast_info wrapped;
/* If finish->func is non-NULL the source needs a decref */
- NPY_cast_info decref_src;
+ NPY_traverse_info decref_src;
} _one_to_n_data;
/* transfer data free function */
@@ -1401,7 +1390,7 @@ static void _one_to_n_data_free(NpyAuxData *data)
{
_one_to_n_data *d = (_one_to_n_data *)data;
NPY_cast_info_xfree(&d->wrapped);
- NPY_cast_info_xfree(&d->decref_src);
+ NPY_traverse_info_xfree(&d->decref_src);
PyMem_Free(data);
}
@@ -1420,7 +1409,7 @@ static NpyAuxData *_one_to_n_data_clone(NpyAuxData *data)
newdata->base.clone = &_one_to_n_data_clone;
newdata->N = d->N;
/* Initialize in case of error, or if it is unused */
- NPY_cast_info_init(&newdata->decref_src);
+ NPY_traverse_info_init(&newdata->decref_src);
if (NPY_cast_info_copy(&newdata->wrapped, &d->wrapped) < 0) {
_one_to_n_data_free((NpyAuxData *)newdata);
@@ -1430,7 +1419,7 @@ static NpyAuxData *_one_to_n_data_clone(NpyAuxData *data)
return (NpyAuxData *)newdata;
}
- if (NPY_cast_info_copy(&newdata->decref_src, &d->decref_src) < 0) {
+ if (NPY_traverse_info_copy(&newdata->decref_src, &d->decref_src) < 0) {
_one_to_n_data_free((NpyAuxData *)newdata);
return NULL;
}
@@ -1490,8 +1479,8 @@ _strided_to_strided_one_to_n_with_finish(
return -1;
}
- if (d->decref_src.func(&d->decref_src.context,
- &src, &one_item, &zero_stride, d->decref_src.auxdata) < 0) {
+ if (d->decref_src.func(NULL, d->decref_src.descr,
+ src, one_item, zero_stride, d->decref_src.auxdata) < 0) {
return -1;
}
@@ -1522,7 +1511,7 @@ get_one_to_n_transfer_function(int aligned,
data->base.free = &_one_to_n_data_free;
data->base.clone = &_one_to_n_data_clone;
data->N = N;
- NPY_cast_info_init(&data->decref_src); /* In case of error */
+ NPY_traverse_info_init(&data->decref_src); /* In case of error */
/*
* move_references is set to 0, handled in the wrapping transfer fn,
@@ -1542,15 +1531,14 @@ get_one_to_n_transfer_function(int aligned,
/* If the src object will need a DECREF, set src_dtype */
if (move_references && PyDataType_REFCHK(src_dtype)) {
- *out_flags |= NPY_METH_REQUIRES_PYAPI;
- if (get_decref_transfer_function(aligned,
- src_stride,
- src_dtype,
- &data->decref_src,
- NULL) != NPY_SUCCEED) {
+ NPY_ARRAYMETHOD_FLAGS clear_flags;
+ if (PyArray_GetClearFunction(
+ aligned, src_stride, src_dtype,
+ &data->decref_src, &clear_flags) < 0) {
NPY_AUXDATA_FREE((NpyAuxData *)data);
return NPY_FAIL;
}
+ *out_flags = PyArrayMethod_COMBINED_FLAGS(*out_flags, clear_flags);
}
if (data->decref_src.func == NULL) {
@@ -1741,8 +1729,8 @@ typedef struct {
typedef struct {
NpyAuxData base;
NPY_cast_info wrapped;
- NPY_cast_info decref_src;
- NPY_cast_info decref_dst; /* The use-case should probably be deprecated */
+ NPY_traverse_info decref_src;
+ NPY_traverse_info decref_dst; /* The use-case should probably be deprecated */
npy_intp src_N, dst_N;
/* This gets a run-length encoded representation of the transfer */
npy_intp run_count;
@@ -1755,8 +1743,8 @@ static void _subarray_broadcast_data_free(NpyAuxData *data)
{
_subarray_broadcast_data *d = (_subarray_broadcast_data *)data;
NPY_cast_info_xfree(&d->wrapped);
- NPY_cast_info_xfree(&d->decref_src);
- NPY_cast_info_xfree(&d->decref_dst);
+ NPY_traverse_info_xfree(&d->decref_src);
+ NPY_traverse_info_xfree(&d->decref_dst);
PyMem_Free(data);
}
@@ -1780,21 +1768,21 @@ static NpyAuxData *_subarray_broadcast_data_clone(NpyAuxData *data)
newdata->run_count = d->run_count;
memcpy(newdata->offsetruns, d->offsetruns, offsetruns_size);
- NPY_cast_info_init(&newdata->decref_src);
- NPY_cast_info_init(&newdata->decref_dst);
+ NPY_traverse_info_init(&newdata->decref_src);
+ NPY_traverse_info_init(&newdata->decref_dst);
if (NPY_cast_info_copy(&newdata->wrapped, &d->wrapped) < 0) {
_subarray_broadcast_data_free((NpyAuxData *)newdata);
return NULL;
}
if (d->decref_src.func != NULL) {
- if (NPY_cast_info_copy(&newdata->decref_src, &d->decref_src) < 0) {
+ if (NPY_traverse_info_copy(&newdata->decref_src, &d->decref_src) < 0) {
_subarray_broadcast_data_free((NpyAuxData *) newdata);
return NULL;
}
}
if (d->decref_dst.func != NULL) {
- if (NPY_cast_info_copy(&newdata->decref_dst, &d->decref_dst) < 0) {
+ if (NPY_traverse_info_copy(&newdata->decref_dst, &d->decref_dst) < 0) {
_subarray_broadcast_data_free((NpyAuxData *) newdata);
return NULL;
}
@@ -1883,8 +1871,8 @@ _strided_to_strided_subarray_broadcast_withrefs(
}
else {
if (d->decref_dst.func != NULL) {
- if (d->decref_dst.func(&d->decref_dst.context,
- &dst_ptr, &count, &dst_subitemsize,
+ if (d->decref_dst.func(NULL, d->decref_dst.descr,
+ dst_ptr, count, dst_subitemsize,
d->decref_dst.auxdata) < 0) {
return -1;
}
@@ -1895,8 +1883,8 @@ _strided_to_strided_subarray_broadcast_withrefs(
}
if (d->decref_src.func != NULL) {
- if (d->decref_src.func(&d->decref_src.context,
- &src, &d->src_N, &src_subitemsize,
+ if (d->decref_src.func(NULL, d->decref_src.descr,
+ src, d->src_N, src_subitemsize,
d->decref_src.auxdata) < 0) {
return -1;
}
@@ -1939,8 +1927,8 @@ get_subarray_broadcast_transfer_function(int aligned,
data->src_N = src_size;
data->dst_N = dst_size;
- NPY_cast_info_init(&data->decref_src);
- NPY_cast_info_init(&data->decref_dst);
+ NPY_traverse_info_init(&data->decref_src);
+ NPY_traverse_info_init(&data->decref_dst);
/*
* move_references is set to 0, handled in the wrapping transfer fn,
@@ -1959,12 +1947,9 @@ get_subarray_broadcast_transfer_function(int aligned,
/* If the src object will need a DECREF */
if (move_references && PyDataType_REFCHK(src_dtype)) {
- if (PyArray_GetDTypeTransferFunction(aligned,
- src_dtype->elsize, 0,
- src_dtype, NULL,
- 1,
- &data->decref_src,
- out_flags) != NPY_SUCCEED) {
+ if (PyArray_GetClearFunction(aligned,
+ src_dtype->elsize, src_dtype,
+ &data->decref_src, out_flags) < 0) {
NPY_AUXDATA_FREE((NpyAuxData *)data);
return NPY_FAIL;
}
@@ -1972,12 +1957,9 @@ get_subarray_broadcast_transfer_function(int aligned,
/* If the dst object needs a DECREF to set it to NULL */
if (PyDataType_REFCHK(dst_dtype)) {
- if (PyArray_GetDTypeTransferFunction(aligned,
- dst_dtype->elsize, 0,
- dst_dtype, NULL,
- 1,
- &data->decref_dst,
- out_flags) != NPY_SUCCEED) {
+ if (PyArray_GetClearFunction(aligned,
+ dst_dtype->elsize, dst_dtype,
+ &data->decref_dst, out_flags) < 0) {
NPY_AUXDATA_FREE((NpyAuxData *)data);
return NPY_FAIL;
}
@@ -2182,6 +2164,7 @@ typedef struct {
typedef struct {
NpyAuxData base;
npy_intp field_count;
+ NPY_traverse_info decref_src;
_single_field_transfer fields[];
} _field_transfer_data;
@@ -2190,6 +2173,7 @@ typedef struct {
static void _field_transfer_data_free(NpyAuxData *data)
{
_field_transfer_data *d = (_field_transfer_data *)data;
+ NPY_traverse_info_xfree(&d->decref_src);
for (npy_intp i = 0; i < d->field_count; ++i) {
NPY_cast_info_xfree(&d->fields[i].info);
@@ -2213,6 +2197,10 @@ static NpyAuxData *_field_transfer_data_clone(NpyAuxData *data)
}
newdata->base = d->base;
newdata->field_count = 0;
+ if (NPY_traverse_info_copy(&newdata->decref_src, &d->decref_src) < 0) {
+ PyMem_Free(newdata);
+ return NULL;
+ }
/* Copy all the fields transfer data */
for (npy_intp i = 0; i < field_count; ++i) {
@@ -2254,6 +2242,11 @@ _strided_to_strided_field_transfer(
return -1;
}
}
+ if (d->decref_src.func != NULL && d->decref_src.func(
+ NULL, d->decref_src.descr, src, blocksize, src_stride,
+ d->decref_src.auxdata) < 0) {
+ return -1;
+ }
N -= NPY_LOWLEVEL_BUFFER_BLOCKSIZE;
src += NPY_LOWLEVEL_BUFFER_BLOCKSIZE*src_stride;
dst += NPY_LOWLEVEL_BUFFER_BLOCKSIZE*dst_stride;
@@ -2267,6 +2260,11 @@ _strided_to_strided_field_transfer(
return -1;
}
}
+ if (d->decref_src.func != NULL && d->decref_src.func(
+ NULL, d->decref_src.descr, src, blocksize, src_stride,
+ d->decref_src.auxdata) < 0) {
+ return -1;
+ }
return 0;
}
}
@@ -2313,6 +2311,7 @@ get_fields_transfer_function(int NPY_UNUSED(aligned),
data->base.free = &_field_transfer_data_free;
data->base.clone = &_field_transfer_data_clone;
data->field_count = 0;
+ NPY_traverse_info_init(&data->decref_src);
*out_flags = PyArrayMethod_MINIMAL_FLAGS;
for (i = 0; i < field_count; ++i) {
@@ -2340,23 +2339,17 @@ get_fields_transfer_function(int NPY_UNUSED(aligned),
}
/*
- * If references should be decrefd in src, add another transfer
- * function to do that. Since a decref function only uses a single
- * input, the second one (normally output) just does not matter here.
+ * If references should be decrefd in src, add a clear function.
*/
if (move_references && PyDataType_REFCHK(src_dtype)) {
- *out_flags |= NPY_METH_REQUIRES_PYAPI;
- if (get_decref_transfer_function(0,
- src_stride,
- src_dtype,
- &data->fields[field_count].info,
- NULL) != NPY_SUCCEED) {
+ NPY_ARRAYMETHOD_FLAGS clear_flags;
+ if (PyArray_GetClearFunction(
+ 0, src_stride, src_dtype, &data->decref_src,
+ &clear_flags) < 0) {
NPY_AUXDATA_FREE((NpyAuxData *)data);
return NPY_FAIL;
}
- data->fields[field_count].src_offset = 0;
- data->fields[field_count].dst_offset = 0;
- data->field_count = field_count;
+ *out_flags = PyArrayMethod_COMBINED_FLAGS(*out_flags, clear_flags);
}
*out_stransfer = &_strided_to_strided_field_transfer;
@@ -2384,6 +2377,7 @@ get_fields_transfer_function(int NPY_UNUSED(aligned),
}
data->base.free = &_field_transfer_data_free;
data->base.clone = &_field_transfer_data_clone;
+ NPY_traverse_info_init(&data->decref_src);
key = PyTuple_GET_ITEM(src_dtype->names, 0);
tup = PyDict_GetItem(src_dtype->fields, key);
@@ -2432,6 +2426,7 @@ get_fields_transfer_function(int NPY_UNUSED(aligned),
data->base.free = &_field_transfer_data_free;
data->base.clone = &_field_transfer_data_clone;
data->field_count = 0;
+ NPY_traverse_info_init(&data->decref_src);
*out_flags = PyArrayMethod_MINIMAL_FLAGS;
/* set up the transfer function for each field */
@@ -2473,69 +2468,6 @@ get_fields_transfer_function(int NPY_UNUSED(aligned),
return NPY_SUCCEED;
}
-static int
-get_decref_fields_transfer_function(int NPY_UNUSED(aligned),
- npy_intp src_stride,
- PyArray_Descr *src_dtype,
- PyArrayMethod_StridedLoop **out_stransfer,
- NpyAuxData **out_transferdata,
- int *out_needs_api)
-{
- PyObject *names, *key, *tup, *title;
- PyArray_Descr *src_fld_dtype;
- npy_int i, structsize;
- Py_ssize_t field_count;
- int src_offset;
-
- names = src_dtype->names;
- field_count = PyTuple_GET_SIZE(src_dtype->names);
-
- /* Over-allocating here: less fields may be used */
- structsize = sizeof(_field_transfer_data) +
- field_count * sizeof(_single_field_transfer);
- /* Allocate the data and populate it */
- _field_transfer_data *data = PyMem_Malloc(structsize);
- if (data == NULL) {
- PyErr_NoMemory();
- return NPY_FAIL;
- }
- data->base.free = &_field_transfer_data_free;
- data->base.clone = &_field_transfer_data_clone;
- data->field_count = 0;
-
- _single_field_transfer *field = data->fields;
- for (i = 0; i < field_count; ++i) {
- key = PyTuple_GET_ITEM(names, i);
- tup = PyDict_GetItem(src_dtype->fields, key);
- if (!PyArg_ParseTuple(tup, "Oi|O", &src_fld_dtype,
- &src_offset, &title)) {
- NPY_AUXDATA_FREE((NpyAuxData *)data);
- return NPY_FAIL;
- }
- if (PyDataType_REFCHK(src_fld_dtype)) {
- if (out_needs_api) {
- *out_needs_api = 1;
- }
- if (get_decref_transfer_function(0,
- src_stride,
- src_fld_dtype,
- &field->info,
- out_needs_api) != NPY_SUCCEED) {
- NPY_AUXDATA_FREE((NpyAuxData *)data);
- return NPY_FAIL;
- }
- field->src_offset = src_offset;
- data->field_count++;
- field++;
- }
- }
-
- *out_stransfer = &_strided_to_strided_field_transfer;
- *out_transferdata = (NpyAuxData *)data;
-
- return NPY_SUCCEED;
-}
-
/************************* MASKED TRANSFER WRAPPER *************************/
@@ -2544,7 +2476,7 @@ typedef struct {
/* The transfer function being wrapped (could likely be stored directly) */
NPY_cast_info wrapped;
/* The src decref function if necessary */
- NPY_cast_info decref_src;
+ NPY_traverse_info decref_src;
} _masked_wrapper_transfer_data;
/* transfer data free function */
@@ -2553,7 +2485,7 @@ _masked_wrapper_transfer_data_free(NpyAuxData *data)
{
_masked_wrapper_transfer_data *d = (_masked_wrapper_transfer_data *)data;
NPY_cast_info_xfree(&d->wrapped);
- NPY_cast_info_xfree(&d->decref_src);
+ NPY_traverse_info_xfree(&d->decref_src);
PyMem_Free(data);
}
@@ -2576,7 +2508,7 @@ _masked_wrapper_transfer_data_clone(NpyAuxData *data)
return NULL;
}
if (d->decref_src.func != NULL) {
- if (NPY_cast_info_copy(&newdata->decref_src, &d->decref_src) < 0) {
+ if (NPY_traverse_info_copy(&newdata->decref_src, &d->decref_src) < 0) {
NPY_AUXDATA_FREE((NpyAuxData *)newdata);
return NULL;
}
@@ -2586,7 +2518,7 @@ _masked_wrapper_transfer_data_clone(NpyAuxData *data)
}
static int
-_strided_masked_wrapper_decref_transfer_function(
+_strided_masked_wrapper_clear_function(
PyArrayMethod_Context *NPY_UNUSED(context), char *const *args,
const npy_intp *dimensions, const npy_intp *strides,
npy_bool *mask, npy_intp mask_stride,
@@ -2603,8 +2535,8 @@ _strided_masked_wrapper_decref_transfer_function(
/* Skip masked values, still calling decref for move_references */
mask = (npy_bool*)npy_memchr((char *)mask, 0, mask_stride, N,
&subloopsize, 1);
- if (d->decref_src.func(&d->decref_src.context,
- &src, &subloopsize, &src_stride, d->decref_src.auxdata) < 0) {
+ if (d->decref_src.func(NULL, d->decref_src.descr,
+ src, subloopsize, src_stride, d->decref_src.auxdata) < 0) {
return -1;
}
dst += subloopsize * dst_stride;
@@ -2670,139 +2602,18 @@ _strided_masked_wrapper_transfer_function(
}
-/*************************** CLEAR SRC *******************************/
+/* A no-op function (currently only used for cleanup purposes really) */
static int
-_dec_src_ref_nop(
+_cast_no_op(
PyArrayMethod_Context *NPY_UNUSED(context),
char *const *NPY_UNUSED(args), const npy_intp *NPY_UNUSED(dimensions),
const npy_intp *NPY_UNUSED(strides), NpyAuxData *NPY_UNUSED(auxdata))
{
- /* NOP */
+ /* Do nothing */
return 0;
}
-static int
-_strided_to_null_dec_src_ref_reference(
- PyArrayMethod_Context *NPY_UNUSED(context),
- char *const *args, const npy_intp *dimensions,
- const npy_intp *strides, NpyAuxData *NPY_UNUSED(auxdata))
-{
- char *src = args[0];
- npy_intp N = dimensions[0];
- npy_intp stride = strides[0];
-
- PyObject *src_ref = NULL;
- while (N > 0) {
- /* Release the reference in src and set it to NULL */
- NPY_DT_DBG_REFTRACE("dec src ref (null dst)", src_ref);
- memcpy(&src_ref, src, sizeof(src_ref));
- Py_XDECREF(src_ref);
- memset(src, 0, sizeof(PyObject *));
-
- src += stride;
- --N;
- }
- return 0;
-}
-
-
-/*
- * Get a function to decref. Currently, this uses a cast info slot, which
- * means that the second (destination) descriptor is always set to NULL
- * and generally does not have to be passed.
- * Since we do not currently have an `ArrayMethod` representing this, the
- * method is also set to NULL.
- *
- * TODO: this function should probably be moved onto the DType eventually,
- * which would allow for user DTypes to include dynamic allocated
- * memory or Python objects.
- */
-static int
-get_decref_transfer_function(int aligned,
- npy_intp src_stride,
- PyArray_Descr *src_dtype,
- NPY_cast_info *cast_info,
- int *out_needs_api)
-{
- NPY_cast_info_init(cast_info);
-
- /* If there are no references, it's a nop */
- if (!PyDataType_REFCHK(src_dtype)) {
- cast_info->func = &_dec_src_ref_nop;
- cast_info->auxdata = NULL;
- goto finalize;
- }
- /* If it's a single reference, it's one decref */
- else if (src_dtype->type_num == NPY_OBJECT) {
- if (out_needs_api) {
- *out_needs_api = 1;
- }
-
- cast_info->func = &_strided_to_null_dec_src_ref_reference;
- cast_info->auxdata = NULL;
- goto finalize;
- }
- /* If there are subarrays, need to wrap it */
- else if (PyDataType_HASSUBARRAY(src_dtype)) {
- PyArray_Dims src_shape = {NULL, -1};
- npy_intp src_size;
-
- if (out_needs_api) {
- *out_needs_api = 1;
- }
-
- if (!(PyArray_IntpConverter(src_dtype->subarray->shape,
- &src_shape))) {
- PyErr_SetString(PyExc_ValueError,
- "invalid subarray shape");
- return NPY_FAIL;
- }
- src_size = PyArray_MultiplyList(src_shape.ptr, src_shape.len);
- npy_free_cache_dim_obj(src_shape);
-
- NPY_ARRAYMETHOD_FLAGS ignored_flags;
- if (get_n_to_n_transfer_function(aligned,
- src_stride, 0,
- src_dtype->subarray->base, NULL, 1, src_size,
- &cast_info->func, &cast_info->auxdata,
- &ignored_flags) != NPY_SUCCEED) {
- return NPY_FAIL;
- }
-
- goto finalize;
- }
- /* If there are fields, need to do each field */
- else if (PyDataType_HASFIELDS(src_dtype)) {
- if (out_needs_api) {
- *out_needs_api = 1;
- }
-
- if (get_decref_fields_transfer_function(aligned,
- src_stride, src_dtype,
- &cast_info->func, &cast_info->auxdata,
- out_needs_api) < 0) {
- return NPY_FAIL;
- }
- goto finalize;
- }
- else {
- PyErr_Format(PyExc_RuntimeError,
- "Internal error, tried to fetch decref function for the "
- "unsupported DType '%S'.", src_dtype);
- return NPY_FAIL;
- }
-
- finalize:
- /* Make sure all important fields are either set or cleared */
- Py_INCREF(src_dtype);
- cast_info->descriptors[0] = src_dtype;
- cast_info->descriptors[1] = NULL;
- cast_info->context.method = NULL;
- cast_info->context.caller = NULL;
- return NPY_SUCCEED;
-}
-
/*
* ********************* Generalized Multistep Cast ************************
@@ -3083,14 +2894,14 @@ _clear_cast_info_after_get_loop_failure(NPY_cast_info *cast_info)
/* As public API we could choose to clear auxdata != NULL */
assert(cast_info->auxdata == NULL);
/* Set func to be non-null so that `NPY_cats_info_xfree` does not skip */
- cast_info->func = &_dec_src_ref_nop;
+ cast_info->func = &_cast_no_op;
NPY_cast_info_xfree(cast_info);
}
/*
* Helper for PyArray_GetDTypeTransferFunction, which fetches a single
- * transfer function from the each casting implementation (ArrayMethod).
+ * transfer function from the each casting implementation (ArrayMethod)
* May set the transfer function to NULL when the cast can be achieved using
* a view.
* TODO: Expand the view functionality for general offsets, not just 0:
@@ -3120,6 +2931,8 @@ define_cast_for_descrs(
int move_references,
NPY_cast_info *cast_info, NPY_ARRAYMETHOD_FLAGS *out_flags)
{
+ assert(dst_dtype != NULL); /* Was previously used for decref */
+
/* Storage for all cast info in case multi-step casting is necessary */
_multistep_castdata castdata;
/* Initialize funcs to NULL to simplify cleanup on error. */
@@ -3164,7 +2977,7 @@ define_cast_for_descrs(
/* Prepare the actual cast (if necessary): */
if (from_view_offset == 0 && !must_wrap) {
/* This step is not necessary and can be skipped */
- castdata.from.func = &_dec_src_ref_nop; /* avoid NULL */
+ castdata.from.func = &_cast_no_op; /* avoid NULL */
NPY_cast_info_xfree(&castdata.from);
}
else {
@@ -3203,7 +3016,7 @@ define_cast_for_descrs(
/* Prepare the actual cast (if necessary): */
if (to_view_offset == 0 && !must_wrap) {
/* This step is not necessary and can be skipped. */
- castdata.to.func = &_dec_src_ref_nop; /* avoid NULL */
+ castdata.to.func = &_cast_no_op; /* avoid NULL */
NPY_cast_info_xfree(&castdata.to);
}
else {
@@ -3279,33 +3092,6 @@ PyArray_GetDTypeTransferFunction(int aligned,
NPY_cast_info *cast_info,
NPY_ARRAYMETHOD_FLAGS *out_flags)
{
- assert(src_dtype != NULL);
-
- /*
- * If one of the dtypes is NULL, we give back either a src decref
- * function or a dst setzero function
- *
- * TODO: Eventually, we may wish to support user dtype with references
- * (including and beyond bare `PyObject *` this may require extending
- * the ArrayMethod API and those paths should likely be split out
- * from this function.)
- */
- if (dst_dtype == NULL) {
- assert(move_references);
- int needs_api = 0;
- int res = get_decref_transfer_function(aligned,
- src_dtype->elsize,
- src_dtype,
- cast_info,
- &needs_api);
- /* decref'ing never creates floating point errors, so just ignore it */
- *out_flags = PyArrayMethod_MINIMAL_FLAGS;
- if (needs_api) {
- *out_flags |= NPY_METH_REQUIRES_PYAPI;
- }
- return res;
- }
-
if (define_cast_for_descrs(aligned,
src_stride, dst_stride,
src_dtype, dst_dtype, move_references,
@@ -3563,20 +3349,19 @@ PyArray_GetMaskedDTypeTransferFunction(int aligned,
/* If the src object will need a DECREF, get a function to handle that */
if (move_references && PyDataType_REFCHK(src_dtype)) {
- *out_flags |= NPY_METH_REQUIRES_PYAPI;
- if (get_decref_transfer_function(aligned,
- src_stride,
- src_dtype,
- &data->decref_src,
- NULL) != NPY_SUCCEED) {
+ NPY_ARRAYMETHOD_FLAGS clear_flags;
+ if (PyArray_GetClearFunction(
+ aligned, src_stride, src_dtype,
+ &data->decref_src, &clear_flags) < 0) {
NPY_AUXDATA_FREE((NpyAuxData *)data);
return NPY_FAIL;
}
+ *out_flags = PyArrayMethod_COMBINED_FLAGS(*out_flags, clear_flags);
cast_info->func = (PyArrayMethod_StridedLoop *)
- &_strided_masked_wrapper_decref_transfer_function;
+ &_strided_masked_wrapper_clear_function;
}
else {
- NPY_cast_info_init(&data->decref_src);
+ NPY_traverse_info_init(&data->decref_src);
cast_info->func = (PyArrayMethod_StridedLoop *)
&_strided_masked_wrapper_transfer_function;
}
diff --git a/numpy/core/src/multiarray/dtype_transfer.h b/numpy/core/src/multiarray/dtype_transfer.h
index 6f7aa2837..04df5cb64 100644
--- a/numpy/core/src/multiarray/dtype_transfer.h
+++ b/numpy/core/src/multiarray/dtype_transfer.h
@@ -146,7 +146,6 @@ object_to_any_get_loop(
NpyAuxData **out_transferdata,
NPY_ARRAYMETHOD_FLAGS *flags);
-
NPY_NO_EXPORT int
wrap_aligned_transferfunction(
int aligned, int must_wrap,
diff --git a/numpy/core/src/multiarray/dtype_traversal.c b/numpy/core/src/multiarray/dtype_traversal.c
new file mode 100644
index 000000000..cefa7d6e1
--- /dev/null
+++ b/numpy/core/src/multiarray/dtype_traversal.c
@@ -0,0 +1,474 @@
+/*
+ * This file is simlar to the low-level loops for data type transfer
+ * in `dtype_transfer.c` but for those which only require visiting
+ * a single array (and mutating it in-place).
+ *
+ * As of writing, it is only used for CLEARing, which means mainly
+ * Python object DECREF/dealloc followed by NULL'ing the data
+ * (to support double clearing and ensure data is again in a usable state).
+ * However, memory initialization and traverse follows similar
+ * protocols (although traversal needs additional arguments).
+ */
+
+#define NPY_NO_DEPRECATED_API NPY_API_VERSION
+#define _MULTIARRAYMODULE
+#define _UMATHMODULE
+
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+#include <structmember.h>
+
+#include "numpy/ndarraytypes.h"
+#include "numpy/arrayobject.h"
+
+#include "alloc.h"
+#include "array_method.h"
+#include "dtypemeta.h"
+
+#include "dtype_traversal.h"
+
+
+/* Buffer size with the same use case as the one in dtype_transfer.c */
+#define NPY_LOWLEVEL_BUFFER_BLOCKSIZE 128
+
+
+/*
+ * Generic Clear function helpers:
+ */
+
+static int
+get_clear_function(
+ void *traverse_context, PyArray_Descr *dtype, int aligned,
+ npy_intp stride, NPY_traverse_info *clear_info,
+ NPY_ARRAYMETHOD_FLAGS *flags)
+{
+ NPY_traverse_info_init(clear_info);
+ /* not that cleanup code bothers to check e.g. for floating point flags */
+ *flags = PyArrayMethod_MINIMAL_FLAGS;
+
+ get_traverse_loop_function *get_clear = NPY_DT_SLOTS(NPY_DTYPE(dtype))->get_clear_loop;
+ if (get_clear == NULL) {
+ PyErr_Format(PyExc_RuntimeError,
+ "Internal error, `get_clear_loop` not set for the DType '%S'",
+ dtype);
+ return -1;
+ }
+
+ if (get_clear(traverse_context, dtype, aligned, stride,
+ &clear_info->func, &clear_info->auxdata, flags) < 0) {
+ /* callee should clean up, but make sure outside debug mode */
+ assert(clear_info->func == NULL);
+ clear_info->func = NULL;
+ return -1;
+ }
+ Py_INCREF(dtype);
+ clear_info->descr = dtype;
+
+ return 0;
+}
+
+/*
+ * Helper to set up a strided loop used for clearing. Clearing means
+ * deallocating any references (e.g. via Py_DECREF) and resetting the data
+ * back into a usable/initialized state (e.g. by NULLing any references).
+ *
+ * The function will error when called on a dtype which does not have
+ * references (and thus the get_clear_loop slot NULL).
+ * Note that old-style user-dtypes use the "void" version.
+ *
+ * NOTE: This function may have a use for a `traverse_context` at some point
+ * but right now, it is always NULL and only exists to allow adding it
+ * in the future without changing the strided-loop signature.
+ */
+NPY_NO_EXPORT int
+PyArray_GetClearFunction(
+ int aligned, npy_intp stride, PyArray_Descr *dtype,
+ NPY_traverse_info *clear_info, NPY_ARRAYMETHOD_FLAGS *flags)
+{
+ return get_clear_function(NULL, dtype, aligned, stride, clear_info, flags);
+}
+
+
+/****************** Python Object clear ***********************/
+
+static int
+clear_object_strided_loop(
+ void *NPY_UNUSED(traverse_context), PyArray_Descr *NPY_UNUSED(descr),
+ char *data, npy_intp size, npy_intp stride,
+ NpyAuxData *NPY_UNUSED(auxdata))
+{
+ PyObject *aligned_copy = NULL;
+ while (size > 0) {
+ /* Release the reference in src and set it to NULL */
+ memcpy(&aligned_copy, data, sizeof(PyObject *));
+ Py_XDECREF(aligned_copy);
+ memset(data, 0, sizeof(PyObject *));
+
+ data += stride;
+ --size;
+ }
+ return 0;
+}
+
+
+NPY_NO_EXPORT int
+npy_get_clear_object_strided_loop(
+ void *NPY_UNUSED(traverse_context), PyArray_Descr *NPY_UNUSED(descr),
+ int NPY_UNUSED(aligned), npy_intp NPY_UNUSED(fixed_stride),
+ traverse_loop_function **out_loop, NpyAuxData **out_auxdata,
+ NPY_ARRAYMETHOD_FLAGS *flags)
+{
+ *flags = NPY_METH_REQUIRES_PYAPI|NPY_METH_NO_FLOATINGPOINT_ERRORS;
+ *out_loop = &clear_object_strided_loop;
+ return 0;
+}
+
+
+/**************** Structured DType clear funcationality ***************/
+
+/*
+ * Note that legacy user dtypes also make use of this. Someone managed to
+ * hack objects into them by adding a field that contains objects and this
+ * remains (somewhat) valid.
+ * (Unlike our voids, those fields must be hardcoded probably, but...)
+ *
+ * The below functionality mirrors the casting functionality relatively
+ * closely.
+ */
+
+typedef struct {
+ npy_intp src_offset;
+ NPY_traverse_info info;
+} single_field_clear_data;
+
+typedef struct {
+ NpyAuxData base;
+ npy_intp field_count;
+ single_field_clear_data fields[];
+} fields_clear_data;
+
+
+/* traverse data free function */
+static void
+fields_clear_data_free(NpyAuxData *data)
+{
+ fields_clear_data *d = (fields_clear_data *)data;
+
+ for (npy_intp i = 0; i < d->field_count; ++i) {
+ NPY_traverse_info_xfree(&d->fields[i].info);
+ }
+ PyMem_Free(d);
+}
+
+
+/* traverse data copy function (untested due to no direct use currently) */
+static NpyAuxData *
+fields_clear_data_clone(NpyAuxData *data)
+{
+ fields_clear_data *d = (fields_clear_data *)data;
+
+ npy_intp field_count = d->field_count;
+ npy_intp structsize = sizeof(fields_clear_data) +
+ field_count * sizeof(single_field_clear_data);
+
+ /* Allocate the data and populate it */
+ fields_clear_data *newdata = PyMem_Malloc(structsize);
+ if (newdata == NULL) {
+ return NULL;
+ }
+ newdata->base = d->base;
+ newdata->field_count = 0;
+
+ /* Copy all the fields transfer data */
+ single_field_clear_data *in_field = d->fields;
+ single_field_clear_data *new_field = newdata->fields;
+
+ for (; newdata->field_count < field_count;
+ newdata->field_count++, in_field++, new_field++) {
+ new_field->src_offset = in_field->src_offset;
+
+ if (NPY_traverse_info_copy(&new_field->info, &in_field->info) < 0) {
+ fields_clear_data_free((NpyAuxData *)newdata);
+ return NULL;
+ }
+ }
+
+ return (NpyAuxData *)newdata;
+}
+
+
+static int
+traverse_fields_function(
+ void *traverse_context, PyArray_Descr *NPY_UNUSED(descr),
+ char *data, npy_intp N, npy_intp stride,
+ NpyAuxData *auxdata)
+{
+ fields_clear_data *d = (fields_clear_data *)auxdata;
+ npy_intp i, field_count = d->field_count;
+
+ /* Do the traversing a block at a time for better memory caching */
+ const npy_intp blocksize = NPY_LOWLEVEL_BUFFER_BLOCKSIZE;
+
+ for (;;) {
+ if (N > blocksize) {
+ for (i = 0; i < field_count; ++i) {
+ single_field_clear_data field = d->fields[i];
+ if (field.info.func(traverse_context,
+ field.info.descr, data + field.src_offset,
+ blocksize, stride, field.info.auxdata) < 0) {
+ return -1;
+ }
+ }
+ N -= blocksize;
+ data += blocksize * stride;
+ }
+ else {
+ for (i = 0; i < field_count; ++i) {
+ single_field_clear_data field = d->fields[i];
+ if (field.info.func(traverse_context,
+ field.info.descr, data + field.src_offset,
+ N, stride, field.info.auxdata) < 0) {
+ return -1;
+ }
+ }
+ return 0;
+ }
+ }
+}
+
+
+static int
+get_clear_fields_transfer_function(
+ void *traverse_context, PyArray_Descr *dtype, int NPY_UNUSED(aligned),
+ npy_intp stride, traverse_loop_function **out_func,
+ NpyAuxData **out_auxdata, NPY_ARRAYMETHOD_FLAGS *flags)
+{
+ PyObject *names, *key, *tup, *title;
+ PyArray_Descr *fld_dtype;
+ npy_int i, structsize;
+ Py_ssize_t field_count;
+
+ names = dtype->names;
+ field_count = PyTuple_GET_SIZE(dtype->names);
+
+ /* Over-allocating here: less fields may be used */
+ structsize = (sizeof(fields_clear_data) +
+ field_count * sizeof(single_field_clear_data));
+ /* Allocate the data and populate it */
+ fields_clear_data *data = PyMem_Malloc(structsize);
+ if (data == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ data->base.free = &fields_clear_data_free;
+ data->base.clone = &fields_clear_data_clone;
+ data->field_count = 0;
+
+ single_field_clear_data *field = data->fields;
+ for (i = 0; i < field_count; ++i) {
+ int offset;
+
+ key = PyTuple_GET_ITEM(names, i);
+ tup = PyDict_GetItem(dtype->fields, key);
+ if (!PyArg_ParseTuple(tup, "Oi|O", &fld_dtype, &offset, &title)) {
+ NPY_AUXDATA_FREE((NpyAuxData *)data);
+ return -1;
+ }
+ if (PyDataType_REFCHK(fld_dtype)) {
+ NPY_ARRAYMETHOD_FLAGS clear_flags;
+ if (get_clear_function(
+ traverse_context, fld_dtype, 0,
+ stride, &field->info, &clear_flags) < 0) {
+ NPY_AUXDATA_FREE((NpyAuxData *)data);
+ return -1;
+ }
+ *flags = PyArrayMethod_COMBINED_FLAGS(*flags, clear_flags);
+ field->src_offset = offset;
+ data->field_count++;
+ field++;
+ }
+ }
+
+ *out_func = &traverse_fields_function;
+ *out_auxdata = (NpyAuxData *)data;
+
+ return 0;
+}
+
+
+typedef struct {
+ NpyAuxData base;
+ npy_intp count;
+ NPY_traverse_info info;
+} subarray_clear_data;
+
+
+/* traverse data free function */
+static void
+subarray_clear_data_free(NpyAuxData *data)
+{
+ subarray_clear_data *d = (subarray_clear_data *)data;
+
+ NPY_traverse_info_xfree(&d->info);
+ PyMem_Free(d);
+}
+
+
+/*
+ * We seem to be neither using nor exposing this right now, so leave it NULL.
+ * (The implementation below should be functional.)
+ */
+#define subarray_clear_data_clone NULL
+
+#ifndef subarray_clear_data_clone
+/* traverse data copy function */
+static NpyAuxData *
+subarray_clear_data_clone(NpyAuxData *data)
+{
+ subarray_clear_data *d = (subarray_clear_data *)data;
+
+ /* Allocate the data and populate it */
+ subarray_clear_data *newdata = PyMem_Malloc(sizeof(subarray_clear_data));
+ if (newdata == NULL) {
+ return NULL;
+ }
+ newdata->base = d->base;
+ newdata->count = d->count;
+
+ if (NPY_traverse_info_copy(&newdata->info, &d->info) < 0) {
+ PyMem_Free(newdata);
+ return NULL;
+ }
+
+ return (NpyAuxData *)newdata;
+}
+#endif
+
+
+static int
+traverse_subarray_func(
+ void *traverse_context, PyArray_Descr *NPY_UNUSED(descr),
+ char *data, npy_intp N, npy_intp stride,
+ NpyAuxData *auxdata)
+{
+ subarray_clear_data *subarr_data = (subarray_clear_data *)auxdata;
+
+ traverse_loop_function *func = subarr_data->info.func;
+ PyArray_Descr *sub_descr = subarr_data->info.descr;
+ npy_intp sub_N = subarr_data->count;
+ NpyAuxData *sub_auxdata = subarr_data->info.auxdata;
+ npy_intp sub_stride = sub_descr->elsize;
+
+ while (N--) {
+ if (func(traverse_context, sub_descr, data,
+ sub_N, sub_stride, sub_auxdata) < 0) {
+ return -1;
+ }
+ data += stride;
+ }
+ return 0;
+}
+
+
+static int
+get_subarray_clear_func(
+ void *traverse_context, PyArray_Descr *dtype, int aligned,
+ npy_intp size, npy_intp stride, traverse_loop_function **out_func,
+ NpyAuxData **out_auxdata, NPY_ARRAYMETHOD_FLAGS *flags)
+{
+ subarray_clear_data *auxdata = PyMem_Malloc(sizeof(subarray_clear_data));
+ if (auxdata == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
+
+ auxdata->count = size;
+ auxdata->base.free = &subarray_clear_data_free;
+ auxdata->base.clone = subarray_clear_data_clone;
+
+ if (get_clear_function(
+ traverse_context, dtype, aligned,
+ dtype->elsize, &auxdata->info, flags) < 0) {
+ PyMem_Free(auxdata);
+ return -1;
+ }
+ *out_func = &traverse_subarray_func;
+ *out_auxdata = (NpyAuxData *)auxdata;
+
+ return 0;
+}
+
+
+static int
+clear_no_op(
+ void *NPY_UNUSED(traverse_context), PyArray_Descr *NPY_UNUSED(descr),
+ char *NPY_UNUSED(data), npy_intp NPY_UNUSED(size),
+ npy_intp NPY_UNUSED(stride), NpyAuxData *NPY_UNUSED(auxdata))
+{
+ return 0;
+}
+
+
+NPY_NO_EXPORT int
+npy_get_clear_void_and_legacy_user_dtype_loop(
+ void *traverse_context, PyArray_Descr *dtype, int aligned,
+ npy_intp stride, traverse_loop_function **out_func,
+ NpyAuxData **out_auxdata, NPY_ARRAYMETHOD_FLAGS *flags)
+{
+ /*
+ * If there are no references, it's a nop. This path should not be hit
+ * but structured dtypes are tricky when a dtype which included references
+ * was sliced to not include any.
+ */
+ if (!PyDataType_REFCHK(dtype)) {
+ *out_func = &clear_no_op;
+ return 0;
+ }
+
+ if (PyDataType_HASSUBARRAY(dtype)) {
+ PyArray_Dims shape = {NULL, -1};
+ npy_intp size;
+
+ if (!(PyArray_IntpConverter(dtype->subarray->shape, &shape))) {
+ PyErr_SetString(PyExc_ValueError,
+ "invalid subarray shape");
+ return -1;
+ }
+ size = PyArray_MultiplyList(shape.ptr, shape.len);
+ npy_free_cache_dim_obj(shape);
+
+ if (get_subarray_clear_func(
+ traverse_context, dtype->subarray->base, aligned, size, stride,
+ out_func, out_auxdata, flags) < 0) {
+ return -1;
+ }
+
+ return 0;
+ }
+ /* If there are fields, need to do each field */
+ else if (PyDataType_HASFIELDS(dtype)) {
+ if (get_clear_fields_transfer_function(
+ traverse_context, dtype, aligned, stride,
+ out_func, out_auxdata, flags) < 0) {
+ return -1;
+ }
+ return 0;
+ }
+ else if (dtype->type_num == NPY_VOID) {
+ /*
+ * Void dtypes can have "ghosts" of objects marking the dtype because
+ * holes (or the raw bytes if fields are gone) may include objects.
+ * Paths that need those flags should probably be considered incorrect.
+ * But as long as this can happen (a V8 that indicates references)
+ * we need to make it a no-op here.
+ */
+ *out_func = &clear_no_op;
+ return 0;
+ }
+
+ PyErr_Format(PyExc_RuntimeError,
+ "Internal error, tried to fetch clear function for the "
+ "user dtype '%S' without fields or subarray (legacy support).",
+ dtype);
+ return -1;
+}
diff --git a/numpy/core/src/multiarray/dtype_traversal.h b/numpy/core/src/multiarray/dtype_traversal.h
new file mode 100644
index 000000000..fd060a0f0
--- /dev/null
+++ b/numpy/core/src/multiarray/dtype_traversal.h
@@ -0,0 +1,82 @@
+#ifndef NUMPY_CORE_SRC_MULTIARRAY_DTYPE_TRAVERSAL_H_
+#define NUMPY_CORE_SRC_MULTIARRAY_DTYPE_TRAVERSAL_H_
+
+#include "array_method.h"
+
+/* NumPy DType clear (object DECREF + NULLing) implementations */
+
+NPY_NO_EXPORT int
+npy_get_clear_object_strided_loop(
+ void *traverse_context, PyArray_Descr *descr, int aligned,
+ npy_intp fixed_stride,
+ traverse_loop_function **out_loop, NpyAuxData **out_traversedata,
+ NPY_ARRAYMETHOD_FLAGS *flags);
+
+NPY_NO_EXPORT int
+npy_get_clear_void_and_legacy_user_dtype_loop(
+ void *traverse_context, PyArray_Descr *descr, int aligned,
+ npy_intp fixed_stride,
+ traverse_loop_function **out_loop, NpyAuxData **out_traversedata,
+ NPY_ARRAYMETHOD_FLAGS *flags);
+
+
+/* Helper to deal with calling or nesting simple strided loops */
+
+typedef struct {
+ traverse_loop_function *func;
+ NpyAuxData *auxdata;
+ PyArray_Descr *descr;
+} NPY_traverse_info;
+
+
+static inline void
+NPY_traverse_info_init(NPY_traverse_info *cast_info)
+{
+ cast_info->func = NULL; /* mark as uninitialized. */
+ cast_info->auxdata = NULL; /* allow leaving auxdata untouched */
+}
+
+
+static inline void
+NPY_traverse_info_xfree(NPY_traverse_info *traverse_info)
+{
+ if (traverse_info->func == NULL) {
+ return;
+ }
+ traverse_info->func = NULL;
+ NPY_AUXDATA_FREE(traverse_info->auxdata);
+ Py_DECREF(traverse_info->descr);
+}
+
+
+static inline int
+NPY_traverse_info_copy(
+ NPY_traverse_info *traverse_info, NPY_traverse_info *original)
+{
+ traverse_info->func = NULL;
+ if (original->func == NULL) {
+ /* Allow copying also of unused clear info */
+ return 0;
+ }
+ traverse_info->auxdata = NULL;
+ if (original->auxdata != NULL) {
+ traverse_info->auxdata = NPY_AUXDATA_CLONE(original->auxdata);
+ if (traverse_info->auxdata == NULL) {
+ return -1;
+ }
+ }
+ Py_INCREF(original->descr);
+ traverse_info->descr = original->descr;
+ traverse_info->func = original->func;
+
+ return 0;
+}
+
+
+NPY_NO_EXPORT int
+PyArray_GetClearFunction(
+ int aligned, npy_intp stride, PyArray_Descr *dtype,
+ NPY_traverse_info *clear_info, NPY_ARRAYMETHOD_FLAGS *flags);
+
+
+#endif /* NUMPY_CORE_SRC_MULTIARRAY_DTYPE_TRAVERSAL_H_ */ \ No newline at end of file
diff --git a/numpy/core/src/multiarray/dtypemeta.c b/numpy/core/src/multiarray/dtypemeta.c
index 437319b3b..f268ba2cb 100644
--- a/numpy/core/src/multiarray/dtypemeta.c
+++ b/numpy/core/src/multiarray/dtypemeta.c
@@ -899,6 +899,10 @@ dtypemeta_wrap_legacy_descriptor(PyArray_Descr *descr)
}
}
+ if (PyTypeNum_ISNUMBER(descr->type_num)) {
+ dtype_class->flags |= NPY_DT_NUMERIC;
+ }
+
if (_PyArray_MapPyTypeToDType(dtype_class, descr->typeobj,
PyTypeNum_ISUSERDEF(dtype_class->type_num)) < 0) {
Py_DECREF(dtype_class);
@@ -927,6 +931,11 @@ dtypemeta_get_parametric(PyArray_DTypeMeta *self) {
return PyBool_FromLong(NPY_DT_is_parametric(self));
}
+static PyObject *
+dtypemeta_get_is_numeric(PyArray_DTypeMeta *self) {
+ return PyBool_FromLong(NPY_DT_is_numeric(self));
+}
+
/*
* Simple exposed information, defined for each DType (class).
*/
@@ -934,6 +943,7 @@ static PyGetSetDef dtypemeta_getset[] = {
{"_abstract", (getter)dtypemeta_get_abstract, NULL, NULL, NULL},
{"_legacy", (getter)dtypemeta_get_legacy, NULL, NULL, NULL},
{"_parametric", (getter)dtypemeta_get_parametric, NULL, NULL, NULL},
+ {"_is_numeric", (getter)dtypemeta_get_is_numeric, NULL, NULL, NULL},
{NULL, NULL, NULL, NULL, NULL}
};
diff --git a/numpy/core/src/multiarray/dtypemeta.h b/numpy/core/src/multiarray/dtypemeta.h
index ef702f923..3b4dbad24 100644
--- a/numpy/core/src/multiarray/dtypemeta.h
+++ b/numpy/core/src/multiarray/dtypemeta.h
@@ -1,44 +1,18 @@
#ifndef NUMPY_CORE_SRC_MULTIARRAY_DTYPEMETA_H_
#define NUMPY_CORE_SRC_MULTIARRAY_DTYPEMETA_H_
+#include "array_method.h"
+#include "dtype_traversal.h"
+
#ifdef __cplusplus
extern "C" {
#endif
-/* DType flags, currently private, since we may just expose functions */
-#define NPY_DT_LEGACY 1 << 0
-#define NPY_DT_ABSTRACT 1 << 1
-#define NPY_DT_PARAMETRIC 1 << 2
-
-
-typedef PyArray_Descr *(discover_descr_from_pyobject_function)(
- PyArray_DTypeMeta *cls, PyObject *obj);
-
-/*
- * Before making this public, we should decide whether it should pass
- * the type, or allow looking at the object. A possible use-case:
- * `np.array(np.array([0]), dtype=np.ndarray)`
- * Could consider arrays that are not `dtype=ndarray` "scalars".
- */
-typedef int (is_known_scalar_type_function)(
- PyArray_DTypeMeta *cls, PyTypeObject *obj);
-
-typedef PyArray_Descr *(default_descr_function)(PyArray_DTypeMeta *cls);
-typedef PyArray_DTypeMeta *(common_dtype_function)(
- PyArray_DTypeMeta *dtype1, PyArray_DTypeMeta *dtype2);
-typedef PyArray_Descr *(common_instance_function)(
- PyArray_Descr *dtype1, PyArray_Descr *dtype2);
-typedef PyArray_Descr *(ensure_canonical_function)(PyArray_Descr *dtype);
+#include "numpy/_dtype_api.h"
-/*
- * TODO: These two functions are currently only used for experimental DType
- * API support. Their relation should be "reversed": NumPy should
- * always use them internally.
- * There are open points about "casting safety" though, e.g. setting
- * elements is currently always unsafe.
- */
-typedef int(setitemfunction)(PyArray_Descr *, PyObject *, char *);
-typedef PyObject *(getitemfunction)(PyArray_Descr *, char *);
+/* DType flags, currently private, since we may just expose functions
+ Other publicly visible flags are in _dtype_api.h */
+#define NPY_DT_LEGACY 1 << 0
typedef struct {
@@ -55,10 +29,23 @@ typedef struct {
setitemfunction *setitem;
getitemfunction *getitem;
/*
+ * Either NULL or fetches a clearing function. Clearing means deallocating
+ * any referenced data and setting it to a safe state. For Python objects
+ * this means using `Py_CLEAR` which is equivalent to `Py_DECREF` and
+ * setting the `PyObject *` to NULL.
+ * After the clear, the data must be fillable via cast/copy and calling
+ * clear a second time must be safe.
+ * If the DType class does not implement `get_clear_loop` setting
+ * NPY_ITEM_REFCOUNT on its dtype instances is invalid. Note that it is
+ * acceptable for NPY_ITEM_REFCOUNT to inidicate references that are not
+ * Python objects.
+ */
+ get_traverse_loop_function *get_clear_loop;
+ /*
* The casting implementation (ArrayMethod) to convert between two
* instances of this DType, stored explicitly for fast access:
*/
- PyObject *within_dtype_castingimpl;
+ PyArrayMethodObject *within_dtype_castingimpl;
/*
* Dictionary of ArrayMethods representing most possible casts
* (structured and object are exceptions).
@@ -74,6 +61,13 @@ typedef struct {
PyArray_ArrFuncs f;
} NPY_DType_Slots;
+// This must be updated if new slots before within_dtype_castingimpl
+// are added
+#define NPY_NUM_DTYPE_SLOTS 9
+#define NPY_NUM_DTYPE_PYARRAY_ARRFUNCS_SLOTS 22
+#define NPY_DT_MAX_ARRFUNCS_SLOT \
+ NPY_NUM_DTYPE_PYARRAY_ARRFUNCS_SLOTS + _NPY_DT_ARRFUNCS_OFFSET
+
#define NPY_DTYPE(descr) ((PyArray_DTypeMeta *)Py_TYPE(descr))
#define NPY_DT_SLOTS(dtype) ((NPY_DType_Slots *)(dtype)->dt_slots)
@@ -81,6 +75,7 @@ typedef struct {
#define NPY_DT_is_legacy(dtype) (((dtype)->flags & NPY_DT_LEGACY) != 0)
#define NPY_DT_is_abstract(dtype) (((dtype)->flags & NPY_DT_ABSTRACT) != 0)
#define NPY_DT_is_parametric(dtype) (((dtype)->flags & NPY_DT_PARAMETRIC) != 0)
+#define NPY_DT_is_numeric(dtype) (((dtype)->flags & NPY_DT_NUMERIC) != 0)
#define NPY_DT_is_user_defined(dtype) (((dtype)->type_num == -1))
/*
diff --git a/numpy/core/src/multiarray/experimental_public_dtype_api.c b/numpy/core/src/multiarray/experimental_public_dtype_api.c
index 1870a726b..c0da0a7b7 100644
--- a/numpy/core/src/multiarray/experimental_public_dtype_api.c
+++ b/numpy/core/src/multiarray/experimental_public_dtype_api.c
@@ -16,19 +16,6 @@
#include "common_dtype.h"
-#define EXPERIMENTAL_DTYPE_API_VERSION 7
-
-
-typedef struct{
- PyTypeObject *typeobj; /* type of python scalar or NULL */
- int flags; /* flags, including parametric and abstract */
- /* NULL terminated cast definitions. Use NULL for the newly created DType */
- PyArrayMethod_Spec **casts;
- PyType_Slot *slots;
-} PyArrayDTypeMeta_Spec;
-
-
-
static PyArray_DTypeMeta *
dtype_does_not_promote(
PyArray_DTypeMeta *NPY_UNUSED(self), PyArray_DTypeMeta *NPY_UNUSED(other))
@@ -116,10 +103,6 @@ PyArray_ArrFuncs default_funcs = {
};
-/* other slots are in order, so keep only last around: */
-#define NUM_DTYPE_SLOTS 8
-
-
int
PyArrayInitDTypeMeta_FromSpec(
PyArray_DTypeMeta *DType, PyArrayDTypeMeta_Spec *spec)
@@ -155,10 +138,12 @@ PyArrayInitDTypeMeta_FromSpec(
}
/* Check and handle flags: */
- if (spec->flags & ~(NPY_DT_PARAMETRIC|NPY_DT_ABSTRACT)) {
+ int allowed_flags = NPY_DT_PARAMETRIC | NPY_DT_ABSTRACT | NPY_DT_NUMERIC;
+ if (spec->flags & ~(allowed_flags)) {
PyErr_SetString(PyExc_RuntimeError,
- "invalid DType flags specified, only parametric and abstract "
- "are valid flags for user DTypes.");
+ "invalid DType flags specified, only NPY_DT_PARAMETRIC, "
+ "NPY_DT_ABSTRACT, and NPY_DT_NUMERIC are valid flags for "
+ "user DTypes.");
return -1;
}
@@ -178,6 +163,8 @@ PyArrayInitDTypeMeta_FromSpec(
NPY_DT_SLOTS(DType)->common_instance = NULL;
NPY_DT_SLOTS(DType)->setitem = NULL;
NPY_DT_SLOTS(DType)->getitem = NULL;
+ NPY_DT_SLOTS(DType)->get_clear_loop = NULL;
+ NPY_DT_SLOTS(DType)->f = default_funcs;
PyType_Slot *spec_slot = spec->slots;
while (1) {
@@ -187,20 +174,100 @@ PyArrayInitDTypeMeta_FromSpec(
if (slot == 0) {
break;
}
- if (slot > NUM_DTYPE_SLOTS || slot < 0) {
+ if ((slot < 0) ||
+ ((slot > NPY_NUM_DTYPE_SLOTS) &&
+ (slot <= _NPY_DT_ARRFUNCS_OFFSET)) ||
+ (slot > NPY_DT_MAX_ARRFUNCS_SLOT)) {
PyErr_Format(PyExc_RuntimeError,
"Invalid slot with value %d passed in.", slot);
return -1;
}
/*
- * It is up to the user to get this right, and slots are sorted
- * exactly like they are stored right now:
+ * It is up to the user to get this right, the slots in the public API
+ * are sorted exactly like they are stored in the NPY_DT_Slots struct
+ * right now:
*/
- void **current = (void **)(&(
- NPY_DT_SLOTS(DType)->discover_descr_from_pyobject));
- current += slot - 1;
- *current = pfunc;
+ if (slot <= NPY_NUM_DTYPE_SLOTS) {
+ // slot > NPY_NUM_DTYPE_SLOTS are PyArray_ArrFuncs
+ void **current = (void **)(&(
+ NPY_DT_SLOTS(DType)->discover_descr_from_pyobject));
+ current += slot - 1;
+ *current = pfunc;
+ }
+ else {
+ int f_slot = slot - _NPY_DT_ARRFUNCS_OFFSET;
+ if (1 <= f_slot && f_slot <= NPY_NUM_DTYPE_PYARRAY_ARRFUNCS_SLOTS) {
+ switch (f_slot) {
+ case 1:
+ NPY_DT_SLOTS(DType)->f.getitem = pfunc;
+ break;
+ case 2:
+ NPY_DT_SLOTS(DType)->f.setitem = pfunc;
+ break;
+ case 3:
+ NPY_DT_SLOTS(DType)->f.copyswapn = pfunc;
+ break;
+ case 4:
+ NPY_DT_SLOTS(DType)->f.copyswap = pfunc;
+ break;
+ case 5:
+ NPY_DT_SLOTS(DType)->f.compare = pfunc;
+ break;
+ case 6:
+ NPY_DT_SLOTS(DType)->f.argmax = pfunc;
+ break;
+ case 7:
+ NPY_DT_SLOTS(DType)->f.dotfunc = pfunc;
+ break;
+ case 8:
+ NPY_DT_SLOTS(DType)->f.scanfunc = pfunc;
+ break;
+ case 9:
+ NPY_DT_SLOTS(DType)->f.fromstr = pfunc;
+ break;
+ case 10:
+ NPY_DT_SLOTS(DType)->f.nonzero = pfunc;
+ break;
+ case 11:
+ NPY_DT_SLOTS(DType)->f.fill = pfunc;
+ break;
+ case 12:
+ NPY_DT_SLOTS(DType)->f.fillwithscalar = pfunc;
+ break;
+ case 13:
+ *NPY_DT_SLOTS(DType)->f.sort = pfunc;
+ break;
+ case 14:
+ *NPY_DT_SLOTS(DType)->f.argsort = pfunc;
+ break;
+ case 15:
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "PyArray_ArrFunc casting slot with value %d is disabled.",
+ f_slot
+ );
+ return -1;
+ case 22:
+ NPY_DT_SLOTS(DType)->f.argmin = pfunc;
+ break;
+ }
+ } else {
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "Invalid PyArray_ArrFunc slot with value %d passed in.",
+ f_slot
+ );
+ return -1;
+ }
+ }
}
+
if (NPY_DT_SLOTS(DType)->setitem == NULL
|| NPY_DT_SLOTS(DType)->getitem == NULL) {
PyErr_SetString(PyExc_RuntimeError,
@@ -229,7 +296,7 @@ PyArrayInitDTypeMeta_FromSpec(
return -1;
}
}
- NPY_DT_SLOTS(DType)->f = default_funcs;
+
/* invalid type num. Ideally, we get away with it! */
DType->type_num = -1;
@@ -443,12 +510,12 @@ _get_experimental_dtype_api(PyObject *NPY_UNUSED(mod), PyObject *arg)
if (error_converting(version)) {
return NULL;
}
- if (version != EXPERIMENTAL_DTYPE_API_VERSION) {
+ if (version != __EXPERIMENTAL_DTYPE_API_VERSION) {
PyErr_Format(PyExc_RuntimeError,
"Experimental DType API version %d requested, but NumPy "
"is exporting version %d. Recompile your DType and/or upgrade "
"NumPy to match.",
- version, EXPERIMENTAL_DTYPE_API_VERSION);
+ version, __EXPERIMENTAL_DTYPE_API_VERSION);
return NULL;
}
diff --git a/numpy/core/src/multiarray/item_selection.c b/numpy/core/src/multiarray/item_selection.c
index e5331b2b4..508b830f0 100644
--- a/numpy/core/src/multiarray/item_selection.c
+++ b/numpy/core/src/multiarray/item_selection.c
@@ -17,10 +17,12 @@
#include "multiarraymodule.h"
#include "common.h"
+#include "dtype_transfer.h"
#include "arrayobject.h"
#include "ctors.h"
#include "lowlevel_strided_loops.h"
#include "array_assign.h"
+#include "refcount.h"
#include "npy_sort.h"
#include "npy_partition.h"
@@ -39,7 +41,26 @@ npy_fasttake_impl(
PyArray_Descr *dtype, int axis)
{
NPY_BEGIN_THREADS_DEF;
- NPY_BEGIN_THREADS_DESCR(dtype);
+
+ NPY_cast_info cast_info;
+ NPY_ARRAYMETHOD_FLAGS flags;
+ NPY_cast_info_init(&cast_info);
+
+ if (!needs_refcounting) {
+ /* if "refcounting" is not needed memcpy is safe for a simple copy */
+ NPY_BEGIN_THREADS;
+ }
+ else {
+ if (PyArray_GetDTypeTransferFunction(
+ 1, itemsize, itemsize, dtype, dtype, 0,
+ &cast_info, &flags) < 0) {
+ return -1;
+ }
+ if (!(flags & NPY_METH_REQUIRES_PYAPI)) {
+ NPY_BEGIN_THREADS;
+ }
+ }
+
switch (clipmode) {
case NPY_RAISE:
for (npy_intp i = 0; i < n; i++) {
@@ -47,22 +68,23 @@ npy_fasttake_impl(
npy_intp tmp = indices[j];
if (check_and_adjust_index(&tmp, max_item, axis,
_save) < 0) {
- return -1;
+ goto fail;
}
char *tmp_src = src + tmp * chunk;
if (needs_refcounting) {
- for (npy_intp k = 0; k < nelem; k++) {
- PyArray_Item_INCREF(tmp_src, dtype);
- PyArray_Item_XDECREF(dest, dtype);
- memmove(dest, tmp_src, itemsize);
- dest += itemsize;
- tmp_src += itemsize;
+ char *data[2] = {tmp_src, dest};
+ npy_intp strides[2] = {itemsize, itemsize};
+ if (cast_info.func(
+ &cast_info.context, data, &nelem, strides,
+ cast_info.auxdata) < 0) {
+ NPY_END_THREADS;
+ goto fail;
}
}
else {
- memmove(dest, tmp_src, chunk);
- dest += chunk;
+ memcpy(dest, tmp_src, chunk);
}
+ dest += chunk;
}
src += chunk*max_item;
}
@@ -83,18 +105,19 @@ npy_fasttake_impl(
}
char *tmp_src = src + tmp * chunk;
if (needs_refcounting) {
- for (npy_intp k = 0; k < nelem; k++) {
- PyArray_Item_INCREF(tmp_src, dtype);
- PyArray_Item_XDECREF(dest, dtype);
- memmove(dest, tmp_src, itemsize);
- dest += itemsize;
- tmp_src += itemsize;
+ char *data[2] = {tmp_src, dest};
+ npy_intp strides[2] = {itemsize, itemsize};
+ if (cast_info.func(
+ &cast_info.context, data, &nelem, strides,
+ cast_info.auxdata) < 0) {
+ NPY_END_THREADS;
+ goto fail;
}
}
else {
- memmove(dest, tmp_src, chunk);
- dest += chunk;
+ memcpy(dest, tmp_src, chunk);
}
+ dest += chunk;
}
src += chunk*max_item;
}
@@ -111,18 +134,19 @@ npy_fasttake_impl(
}
char *tmp_src = src + tmp * chunk;
if (needs_refcounting) {
- for (npy_intp k = 0; k < nelem; k++) {
- PyArray_Item_INCREF(tmp_src, dtype);
- PyArray_Item_XDECREF(dest, dtype);
- memmove(dest, tmp_src, itemsize);
- dest += itemsize;
- tmp_src += itemsize;
+ char *data[2] = {tmp_src, dest};
+ npy_intp strides[2] = {itemsize, itemsize};
+ if (cast_info.func(
+ &cast_info.context, data, &nelem, strides,
+ cast_info.auxdata) < 0) {
+ NPY_END_THREADS;
+ goto fail;
}
}
else {
- memmove(dest, tmp_src, chunk);
- dest += chunk;
+ memcpy(dest, tmp_src, chunk);
}
+ dest += chunk;
}
src += chunk*max_item;
}
@@ -130,7 +154,13 @@ npy_fasttake_impl(
}
NPY_END_THREADS;
+ NPY_cast_info_xfree(&cast_info);
return 0;
+
+ fail:
+ /* NPY_END_THREADS already ensured. */
+ NPY_cast_info_xfree(&cast_info);
+ return -1;
}
@@ -322,11 +352,17 @@ PyArray_PutTo(PyArrayObject *self, PyObject* values0, PyObject *indices0,
NPY_CLIPMODE clipmode)
{
PyArrayObject *indices, *values;
- npy_intp i, chunk, ni, max_item, nv, tmp;
+ npy_intp i, itemsize, ni, max_item, nv, tmp;
char *src, *dest;
int copied = 0;
int overlap = 0;
+ NPY_BEGIN_THREADS_DEF;
+ NPY_cast_info cast_info;
+ NPY_ARRAYMETHOD_FLAGS flags;
+
+ NPY_cast_info_init(&cast_info);
+
indices = NULL;
values = NULL;
if (!PyArray_Check(self)) {
@@ -372,25 +408,51 @@ PyArray_PutTo(PyArrayObject *self, PyObject* values0, PyObject *indices0,
}
max_item = PyArray_SIZE(self);
dest = PyArray_DATA(self);
- chunk = PyArray_DESCR(self)->elsize;
+ itemsize = PyArray_DESCR(self)->elsize;
+
+ int has_references = PyDataType_REFCHK(PyArray_DESCR(self));
+
+ if (!has_references) {
+ /* if has_references is not needed memcpy is safe for a simple copy */
+ NPY_BEGIN_THREADS_THRESHOLDED(ni);
+ }
+ else {
+ PyArray_Descr *dtype = PyArray_DESCR(self);
+ if (PyArray_GetDTypeTransferFunction(
+ PyArray_ISALIGNED(self), itemsize, itemsize, dtype, dtype, 0,
+ &cast_info, &flags) < 0) {
+ goto fail;
+ }
+ if (!(flags & NPY_METH_REQUIRES_PYAPI)) {
+ NPY_BEGIN_THREADS_THRESHOLDED(ni);
+ }
+ }
+
+
+ if (has_references) {
+ const npy_intp one = 1;
+ const npy_intp strides[2] = {itemsize, itemsize};
- if (PyDataType_REFCHK(PyArray_DESCR(self))) {
switch(clipmode) {
case NPY_RAISE:
for (i = 0; i < ni; i++) {
- src = PyArray_BYTES(values) + chunk*(i % nv);
+ src = PyArray_BYTES(values) + itemsize*(i % nv);
tmp = ((npy_intp *)(PyArray_DATA(indices)))[i];
- if (check_and_adjust_index(&tmp, max_item, 0, NULL) < 0) {
+ if (check_and_adjust_index(&tmp, max_item, 0, _save) < 0) {
+ goto fail;
+ }
+ char *data[2] = {src, dest + tmp*itemsize};
+ if (cast_info.func(
+ &cast_info.context, data, &one, strides,
+ cast_info.auxdata) < 0) {
+ NPY_END_THREADS;
goto fail;
}
- PyArray_Item_INCREF(src, PyArray_DESCR(self));
- PyArray_Item_XDECREF(dest+tmp*chunk, PyArray_DESCR(self));
- memmove(dest + tmp*chunk, src, chunk);
}
break;
case NPY_WRAP:
for (i = 0; i < ni; i++) {
- src = PyArray_BYTES(values) + chunk * (i % nv);
+ src = PyArray_BYTES(values) + itemsize * (i % nv);
tmp = ((npy_intp *)(PyArray_DATA(indices)))[i];
if (tmp < 0) {
while (tmp < 0) {
@@ -402,14 +464,18 @@ PyArray_PutTo(PyArrayObject *self, PyObject* values0, PyObject *indices0,
tmp -= max_item;
}
}
- PyArray_Item_INCREF(src, PyArray_DESCR(self));
- PyArray_Item_XDECREF(dest+tmp*chunk, PyArray_DESCR(self));
- memmove(dest + tmp * chunk, src, chunk);
+ char *data[2] = {src, dest + tmp*itemsize};
+ if (cast_info.func(
+ &cast_info.context, data, &one, strides,
+ cast_info.auxdata) < 0) {
+ NPY_END_THREADS;
+ goto fail;
+ }
}
break;
case NPY_CLIP:
for (i = 0; i < ni; i++) {
- src = PyArray_BYTES(values) + chunk * (i % nv);
+ src = PyArray_BYTES(values) + itemsize * (i % nv);
tmp = ((npy_intp *)(PyArray_DATA(indices)))[i];
if (tmp < 0) {
tmp = 0;
@@ -417,30 +483,32 @@ PyArray_PutTo(PyArrayObject *self, PyObject* values0, PyObject *indices0,
else if (tmp >= max_item) {
tmp = max_item - 1;
}
- PyArray_Item_INCREF(src, PyArray_DESCR(self));
- PyArray_Item_XDECREF(dest+tmp*chunk, PyArray_DESCR(self));
- memmove(dest + tmp * chunk, src, chunk);
+ char *data[2] = {src, dest + tmp*itemsize};
+ if (cast_info.func(
+ &cast_info.context, data, &one, strides,
+ cast_info.auxdata) < 0) {
+ NPY_END_THREADS;
+ goto fail;
+ }
}
break;
}
}
else {
- NPY_BEGIN_THREADS_DEF;
- NPY_BEGIN_THREADS_THRESHOLDED(ni);
switch(clipmode) {
case NPY_RAISE:
for (i = 0; i < ni; i++) {
- src = PyArray_BYTES(values) + chunk * (i % nv);
+ src = PyArray_BYTES(values) + itemsize * (i % nv);
tmp = ((npy_intp *)(PyArray_DATA(indices)))[i];
if (check_and_adjust_index(&tmp, max_item, 0, _save) < 0) {
goto fail;
}
- memmove(dest + tmp * chunk, src, chunk);
+ memmove(dest + tmp * itemsize, src, itemsize);
}
break;
case NPY_WRAP:
for (i = 0; i < ni; i++) {
- src = PyArray_BYTES(values) + chunk * (i % nv);
+ src = PyArray_BYTES(values) + itemsize * (i % nv);
tmp = ((npy_intp *)(PyArray_DATA(indices)))[i];
if (tmp < 0) {
while (tmp < 0) {
@@ -452,12 +520,12 @@ PyArray_PutTo(PyArrayObject *self, PyObject* values0, PyObject *indices0,
tmp -= max_item;
}
}
- memmove(dest + tmp * chunk, src, chunk);
+ memmove(dest + tmp * itemsize, src, itemsize);
}
break;
case NPY_CLIP:
for (i = 0; i < ni; i++) {
- src = PyArray_BYTES(values) + chunk * (i % nv);
+ src = PyArray_BYTES(values) + itemsize * (i % nv);
tmp = ((npy_intp *)(PyArray_DATA(indices)))[i];
if (tmp < 0) {
tmp = 0;
@@ -465,14 +533,16 @@ PyArray_PutTo(PyArrayObject *self, PyObject* values0, PyObject *indices0,
else if (tmp >= max_item) {
tmp = max_item - 1;
}
- memmove(dest + tmp * chunk, src, chunk);
+ memmove(dest + tmp * itemsize, src, itemsize);
}
break;
}
- NPY_END_THREADS;
}
+ NPY_END_THREADS;
finish:
+ NPY_cast_info_xfree(&cast_info);
+
Py_XDECREF(values);
Py_XDECREF(indices);
if (copied) {
@@ -482,6 +552,8 @@ PyArray_PutTo(PyArrayObject *self, PyObject* values0, PyObject *indices0,
Py_RETURN_NONE;
fail:
+ NPY_cast_info_xfree(&cast_info);
+
Py_XDECREF(indices);
Py_XDECREF(values);
if (copied) {
@@ -562,11 +634,12 @@ PyArray_PutMask(PyArrayObject *self, PyObject* values0, PyObject* mask0)
{
PyArrayObject *mask, *values;
PyArray_Descr *dtype;
- npy_intp chunk, ni, nv;
+ npy_intp itemsize, ni, nv;
char *src, *dest;
npy_bool *mask_data;
int copied = 0;
int overlap = 0;
+ NPY_BEGIN_THREADS_DEF;
mask = NULL;
values = NULL;
@@ -627,31 +700,49 @@ PyArray_PutMask(PyArrayObject *self, PyObject* values0, PyObject* mask0)
self = obj;
}
- chunk = PyArray_DESCR(self)->elsize;
+ itemsize = PyArray_DESCR(self)->elsize;
dest = PyArray_DATA(self);
if (PyDataType_REFCHK(PyArray_DESCR(self))) {
+ NPY_cast_info cast_info;
+ NPY_ARRAYMETHOD_FLAGS flags;
+ const npy_intp one = 1;
+ const npy_intp strides[2] = {itemsize, itemsize};
+
+ NPY_cast_info_init(&cast_info);
+ if (PyArray_GetDTypeTransferFunction(
+ PyArray_ISALIGNED(self), itemsize, itemsize, dtype, dtype, 0,
+ &cast_info, &flags) < 0) {
+ goto fail;
+ }
+ if (!(flags & NPY_METH_REQUIRES_PYAPI)) {
+ NPY_BEGIN_THREADS;
+ }
+
for (npy_intp i = 0, j = 0; i < ni; i++, j++) {
if (j >= nv) {
j = 0;
}
if (mask_data[i]) {
- char *src_ptr = src + j*chunk;
- char *dest_ptr = dest + i*chunk;
-
- PyArray_Item_INCREF(src_ptr, PyArray_DESCR(self));
- PyArray_Item_XDECREF(dest_ptr, PyArray_DESCR(self));
- memmove(dest_ptr, src_ptr, chunk);
+ char *data[2] = {src + j*itemsize, dest + i*itemsize};
+ if (cast_info.func(
+ &cast_info.context, data, &one, strides,
+ cast_info.auxdata) < 0) {
+ NPY_END_THREADS;
+ NPY_cast_info_xfree(&cast_info);
+ goto fail;
+ }
}
}
+ NPY_cast_info_xfree(&cast_info);
}
else {
- NPY_BEGIN_THREADS_DEF;
- NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(self));
- npy_fastputmask(dest, src, mask_data, ni, nv, chunk);
- NPY_END_THREADS;
+ NPY_BEGIN_THREADS;
+ npy_fastputmask(dest, src, mask_data, ni, nv, itemsize);
}
+ NPY_END_THREADS;
+
Py_XDECREF(values);
Py_XDECREF(mask);
if (copied) {
@@ -946,7 +1037,7 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort,
npy_intp astride = PyArray_STRIDE(op, axis);
int swap = PyArray_ISBYTESWAPPED(op);
int needcopy = !IsAligned(op) || swap || astride != elsize;
- int hasrefs = PyDataType_REFCHK(PyArray_DESCR(op));
+ int needs_api = PyDataType_FLAGCHK(PyArray_DESCR(op), NPY_NEEDS_PYAPI);
PyArray_CopySwapNFunc *copyswapn = PyArray_DESCR(op)->f->copyswapn;
char *buffer = NULL;
@@ -980,6 +1071,9 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort,
ret = -1;
goto fail;
}
+ if (PyDataType_FLAGCHK(PyArray_DESCR(op), NPY_NEEDS_INIT)) {
+ memset(buffer, 0, N * elsize);
+ }
}
NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(op));
@@ -988,25 +1082,7 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort,
char *bufptr = it->dataptr;
if (needcopy) {
- if (hasrefs) {
- /*
- * For dtype's with objects, copyswapn Py_XINCREF's src
- * and Py_XDECREF's dst. This would crash if called on
- * an uninitialized buffer, or leak a reference to each
- * object if initialized.
- *
- * So, first do the copy with no refcounting...
- */
- _unaligned_strided_byte_copy(buffer, elsize,
- it->dataptr, astride, N, elsize);
- /* ...then swap in-place if needed */
- if (swap) {
- copyswapn(buffer, elsize, NULL, 0, N, swap, op);
- }
- }
- else {
- copyswapn(buffer, elsize, it->dataptr, astride, N, swap, op);
- }
+ copyswapn(buffer, elsize, it->dataptr, astride, N, swap, op);
bufptr = buffer;
}
/*
@@ -1019,7 +1095,7 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort,
if (part == NULL) {
ret = sort(bufptr, N, op);
- if (hasrefs && PyErr_Occurred()) {
+ if (needs_api && PyErr_Occurred()) {
ret = -1;
}
if (ret < 0) {
@@ -1032,7 +1108,7 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort,
npy_intp i;
for (i = 0; i < nkth; ++i) {
ret = part(bufptr, N, kth[i], pivots, &npiv, op);
- if (hasrefs && PyErr_Occurred()) {
+ if (needs_api && PyErr_Occurred()) {
ret = -1;
}
if (ret < 0) {
@@ -1042,16 +1118,7 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort,
}
if (needcopy) {
- if (hasrefs) {
- if (swap) {
- copyswapn(buffer, elsize, NULL, 0, N, swap, op);
- }
- _unaligned_strided_byte_copy(it->dataptr, astride,
- buffer, elsize, N, elsize);
- }
- else {
- copyswapn(it->dataptr, astride, buffer, elsize, N, swap, op);
- }
+ copyswapn(it->dataptr, astride, buffer, elsize, N, swap, op);
}
PyArray_ITER_NEXT(it);
@@ -1060,7 +1127,10 @@ _new_sortlike(PyArrayObject *op, int axis, PyArray_SortFunc *sort,
fail:
NPY_END_THREADS_DESCR(PyArray_DESCR(op));
/* cleanup internal buffer */
- PyDataMem_UserFREE(buffer, N * elsize, mem_handler);
+ if (needcopy) {
+ PyArray_ClearBuffer(PyArray_DESCR(op), buffer, elsize, N, 1);
+ PyDataMem_UserFREE(buffer, N * elsize, mem_handler);
+ }
if (ret < 0 && !PyErr_Occurred()) {
/* Out of memory during sorting or buffer creation */
PyErr_NoMemory();
@@ -1081,7 +1151,7 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort,
npy_intp astride = PyArray_STRIDE(op, axis);
int swap = PyArray_ISBYTESWAPPED(op);
int needcopy = !IsAligned(op) || swap || astride != elsize;
- int hasrefs = PyDataType_REFCHK(PyArray_DESCR(op));
+ int needs_api = PyDataType_FLAGCHK(PyArray_DESCR(op), NPY_NEEDS_PYAPI);
int needidxbuffer;
PyArray_CopySwapNFunc *copyswapn = PyArray_DESCR(op)->f->copyswapn;
@@ -1134,6 +1204,9 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort,
ret = -1;
goto fail;
}
+ if (PyDataType_FLAGCHK(PyArray_DESCR(op), NPY_NEEDS_INIT)) {
+ memset(valbuffer, 0, N * elsize);
+ }
}
if (needidxbuffer) {
@@ -1153,26 +1226,7 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort,
npy_intp *iptr, i;
if (needcopy) {
- if (hasrefs) {
- /*
- * For dtype's with objects, copyswapn Py_XINCREF's src
- * and Py_XDECREF's dst. This would crash if called on
- * an uninitialized valbuffer, or leak a reference to
- * each object item if initialized.
- *
- * So, first do the copy with no refcounting...
- */
- _unaligned_strided_byte_copy(valbuffer, elsize,
- it->dataptr, astride, N, elsize);
- /* ...then swap in-place if needed */
- if (swap) {
- copyswapn(valbuffer, elsize, NULL, 0, N, swap, op);
- }
- }
- else {
- copyswapn(valbuffer, elsize,
- it->dataptr, astride, N, swap, op);
- }
+ copyswapn(valbuffer, elsize, it->dataptr, astride, N, swap, op);
valptr = valbuffer;
}
@@ -1188,7 +1242,7 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort,
if (argpart == NULL) {
ret = argsort(valptr, idxptr, N, op);
/* Object comparisons may raise an exception in Python 3 */
- if (hasrefs && PyErr_Occurred()) {
+ if (needs_api && PyErr_Occurred()) {
ret = -1;
}
if (ret < 0) {
@@ -1202,7 +1256,7 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort,
for (i = 0; i < nkth; ++i) {
ret = argpart(valptr, idxptr, N, kth[i], pivots, &npiv, op);
/* Object comparisons may raise an exception in Python 3 */
- if (hasrefs && PyErr_Occurred()) {
+ if (needs_api && PyErr_Occurred()) {
ret = -1;
}
if (ret < 0) {
@@ -1228,7 +1282,10 @@ _new_argsortlike(PyArrayObject *op, int axis, PyArray_ArgSortFunc *argsort,
fail:
NPY_END_THREADS_DESCR(PyArray_DESCR(op));
/* cleanup internal buffers */
- PyDataMem_UserFREE(valbuffer, N * elsize, mem_handler);
+ if (needcopy) {
+ PyArray_ClearBuffer(PyArray_DESCR(op), valbuffer, elsize, N, 1);
+ PyDataMem_UserFREE(valbuffer, N * elsize, mem_handler);
+ }
PyDataMem_UserFREE(idxbuffer, N * sizeof(npy_intp), mem_handler);
if (ret < 0) {
if (!PyErr_Occurred()) {
@@ -2642,7 +2699,7 @@ PyArray_Nonzero(PyArrayObject *self)
}
}
/*
- * Fallback to a branchless strategy to avoid branch misprediction
+ * Fallback to a branchless strategy to avoid branch misprediction
* stalls that are very expensive on most modern processors.
*/
else {
diff --git a/numpy/core/src/multiarray/methods.c b/numpy/core/src/multiarray/methods.c
index 56225eb52..f518f3a02 100644
--- a/numpy/core/src/multiarray/methods.c
+++ b/numpy/core/src/multiarray/methods.c
@@ -21,6 +21,7 @@
#include "ctors.h"
#include "calculation.h"
#include "convert_datatype.h"
+#include "descriptor.h"
#include "item_selection.h"
#include "conversion_utils.h"
#include "shape.h"
@@ -851,29 +852,35 @@ static PyObject *
array_astype(PyArrayObject *self,
PyObject *const *args, Py_ssize_t len_args, PyObject *kwnames)
{
- PyArray_Descr *dtype = NULL;
/*
* TODO: UNSAFE default for compatibility, I think
* switching to SAME_KIND by default would be good.
*/
+ npy_dtype_info dt_info;
NPY_CASTING casting = NPY_UNSAFE_CASTING;
NPY_ORDER order = NPY_KEEPORDER;
_PyArray_CopyMode forcecopy = 1;
int subok = 1;
+
NPY_PREPARE_ARGPARSER;
if (npy_parse_arguments("astype", args, len_args, kwnames,
- "dtype", &PyArray_DescrConverter, &dtype,
+ "dtype", &PyArray_DTypeOrDescrConverterRequired, &dt_info,
"|order", &PyArray_OrderConverter, &order,
"|casting", &PyArray_CastingConverter, &casting,
"|subok", &PyArray_PythonPyIntFromInt, &subok,
"|copy", &PyArray_CopyConverter, &forcecopy,
NULL, NULL, NULL) < 0) {
- Py_XDECREF(dtype);
+ Py_XDECREF(dt_info.descr);
+ Py_XDECREF(dt_info.dtype);
return NULL;
}
/* If it is not a concrete dtype instance find the best one for the array */
- Py_SETREF(dtype, PyArray_AdaptDescriptorToArray(self, (PyObject *)dtype));
+ PyArray_Descr *dtype;
+
+ dtype = PyArray_AdaptDescriptorToArray(self, dt_info.dtype, dt_info.descr);
+ Py_XDECREF(dt_info.descr);
+ Py_DECREF(dt_info.dtype);
if (dtype == NULL) {
return NULL;
}
diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c
index 4fa58c4df..e85f8affa 100644
--- a/numpy/core/src/multiarray/multiarraymodule.c
+++ b/numpy/core/src/multiarray/multiarraymodule.c
@@ -473,7 +473,7 @@ PyArray_ConcatenateArrays(int narrays, PyArrayObject **arrays, int axis,
/* Get the priority subtype for the array */
PyTypeObject *subtype = PyArray_GetSubType(narrays, arrays);
PyArray_Descr *descr = PyArray_FindConcatenationDescriptor(
- narrays, arrays, (PyObject *)dtype);
+ narrays, arrays, dtype);
if (descr == NULL) {
return NULL;
}
@@ -589,7 +589,7 @@ PyArray_ConcatenateFlattenedArrays(int narrays, PyArrayObject **arrays,
PyTypeObject *subtype = PyArray_GetSubType(narrays, arrays);
PyArray_Descr *descr = PyArray_FindConcatenationDescriptor(
- narrays, arrays, (PyObject *)dtype);
+ narrays, arrays, dtype);
if (descr == NULL) {
return NULL;
}
@@ -4614,7 +4614,7 @@ static struct PyMethodDef array_module_methods[] = {
{"set_legacy_print_mode", (PyCFunction)set_legacy_print_mode,
METH_VARARGS, NULL},
{"_discover_array_parameters", (PyCFunction)_discover_array_parameters,
- METH_VARARGS | METH_KEYWORDS, NULL},
+ METH_FASTCALL | METH_KEYWORDS, NULL},
{"_get_castingimpl", (PyCFunction)_get_castingimpl,
METH_VARARGS | METH_KEYWORDS, NULL},
{"_get_experimental_dtype_api", (PyCFunction)_get_experimental_dtype_api,
diff --git a/numpy/core/src/multiarray/nditer_api.c b/numpy/core/src/multiarray/nditer_api.c
index 37e8acd84..28b7bf6e6 100644
--- a/numpy/core/src/multiarray/nditer_api.c
+++ b/numpy/core/src/multiarray/nditer_api.c
@@ -17,6 +17,7 @@
#include "nditer_impl.h"
#include "templ_common.h"
#include "ctors.h"
+#include "refcount.h"
/* Internal helper functions private to this file */
static npy_intp
@@ -1945,8 +1946,7 @@ npyiter_copy_from_buffers(NpyIter *iter)
* only copy back when this flag is on.
*/
if ((transferinfo[iop].write.func != NULL) &&
- (op_itflags[iop]&(NPY_OP_ITFLAG_WRITE|NPY_OP_ITFLAG_USINGBUFFER))
- == (NPY_OP_ITFLAG_WRITE|NPY_OP_ITFLAG_USINGBUFFER)) {
+ (op_itflags[iop]&NPY_OP_ITFLAG_USINGBUFFER)) {
npy_intp op_transfersize;
npy_intp src_stride, *dst_strides, *dst_coords, *dst_shape;
@@ -2059,27 +2059,18 @@ npyiter_copy_from_buffers(NpyIter *iter)
* The flag USINGBUFFER is set when the buffer was used, so
* only decrement refs when this flag is on.
*/
- else if (transferinfo[iop].write.func != NULL &&
- (op_itflags[iop]&NPY_OP_ITFLAG_USINGBUFFER) != 0) {
- NPY_IT_DBG_PRINT1("Iterator: Freeing refs and zeroing buffer "
- "of operand %d\n", (int)iop);
- /* Decrement refs */
+ else if (transferinfo[iop].clear.func != NULL &&
+ (op_itflags[iop]&NPY_OP_ITFLAG_USINGBUFFER)) {
+ NPY_IT_DBG_PRINT1(
+ "Iterator: clearing refs of operand %d\n", (int)iop);
npy_intp buf_stride = dtypes[iop]->elsize;
- if (transferinfo[iop].write.func(
- &transferinfo[iop].write.context,
- &buffer, &transfersize, &buf_stride,
- transferinfo[iop].write.auxdata) < 0) {
+ if (transferinfo[iop].clear.func(
+ NULL, transferinfo[iop].clear.descr, buffer, transfersize,
+ buf_stride, transferinfo[iop].clear.auxdata) < 0) {
/* Since this should only decrement, it should never error */
assert(0);
return -1;
}
- /*
- * Zero out the memory for safety. For instance,
- * if during iteration some Python code copied an
- * array pointing into the buffer, it will get None
- * values for its references after this.
- */
- memset(buffer, 0, dtypes[iop]->elsize*transfersize);
}
}
@@ -2626,54 +2617,36 @@ npyiter_clear_buffers(NpyIter *iter)
return;
}
- if (!(NIT_ITFLAGS(iter) & NPY_ITFLAG_NEEDSAPI)) {
- /* Buffers do not require clearing, but should not be copied back */
- NBF_SIZE(bufferdata) = 0;
- return;
- }
-
/*
- * The iterator may be using a dtype with references, which always
- * requires the API. In that case, further cleanup may be necessary.
- *
- * TODO: At this time, we assume that a dtype having references
- * implies the need to hold the GIL at all times. In theory
- * we could broaden this definition for a new
- * `PyArray_Item_XDECREF` API and the assumption may become
- * incorrect.
+ * The iterator may be using a dtype with references (as of writing this
+ * means Python objects, but does not need to stay that way).
+ * In that case, further cleanup may be necessary and we clear buffers
+ * explicitly.
*/
PyObject *type, *value, *traceback;
PyErr_Fetch(&type, &value, &traceback);
/* Cleanup any buffers with references */
char **buffers = NBF_BUFFERS(bufferdata);
+
+ NpyIter_TransferInfo *transferinfo = NBF_TRANSFERINFO(bufferdata);
PyArray_Descr **dtypes = NIT_DTYPES(iter);
npyiter_opitflags *op_itflags = NIT_OPITFLAGS(iter);
for (int iop = 0; iop < nop; ++iop, ++buffers) {
- /*
- * We may want to find a better way to do this, on the other hand,
- * this cleanup seems rare and fairly special. A dtype using
- * references (right now only us) must always keep the buffer in
- * a well defined state (either NULL or owning the reference).
- * Only we implement cleanup
- */
- if (!PyDataType_REFCHK(dtypes[iop]) ||
- !(op_itflags[iop]&NPY_OP_ITFLAG_USINGBUFFER)) {
+ if (transferinfo[iop].clear.func == NULL ||
+ !(op_itflags[iop]&NPY_OP_ITFLAG_USINGBUFFER)) {
continue;
}
if (*buffers == 0) {
continue;
}
int itemsize = dtypes[iop]->elsize;
- for (npy_intp i = 0; i < NBF_SIZE(bufferdata); i++) {
- /*
- * See above comment, if this API is expanded the GIL assumption
- * could become incorrect.
- */
- PyArray_Item_XDECREF(*buffers + (itemsize * i), dtypes[iop]);
+ if (transferinfo[iop].clear.func(NULL,
+ dtypes[iop], *buffers, NBF_SIZE(bufferdata), itemsize,
+ transferinfo[iop].clear.auxdata) < 0) {
+ /* This should never fail; if it does write it out */
+ PyErr_WriteUnraisable(NULL);
}
- /* Clear out the buffer just to be sure */
- memset(*buffers, 0, NBF_SIZE(bufferdata) * itemsize);
}
/* Signal that the buffers are empty */
NBF_SIZE(bufferdata) = 0;
diff --git a/numpy/core/src/multiarray/nditer_constr.c b/numpy/core/src/multiarray/nditer_constr.c
index b969c9f1d..dfa84c51c 100644
--- a/numpy/core/src/multiarray/nditer_constr.c
+++ b/numpy/core/src/multiarray/nditer_constr.c
@@ -19,6 +19,8 @@
#include "array_coercion.h"
#include "templ_common.h"
#include "array_assign.h"
+#include "dtype_traversal.h"
+
/* Internal helper functions private to this file */
static int
@@ -630,6 +632,18 @@ NpyIter_Copy(NpyIter *iter)
}
}
}
+
+ if (transferinfo[iop].clear.func != NULL) {
+ if (out_of_memory) {
+ transferinfo[iop].clear.func = NULL; /* No cleanup */
+ }
+ else {
+ if (NPY_traverse_info_copy(&transferinfo[iop].clear,
+ &transferinfo[iop].clear) < 0) {
+ out_of_memory = 1;
+ }
+ }
+ }
}
/* Initialize the buffers to the current iterindex */
@@ -706,6 +720,7 @@ NpyIter_Deallocate(NpyIter *iter)
for (iop = 0; iop < nop; ++iop, ++transferinfo) {
NPY_cast_info_xfree(&transferinfo->read);
NPY_cast_info_xfree(&transferinfo->write);
+ NPY_traverse_info_xfree(&transferinfo->clear);
}
}
@@ -1106,7 +1121,8 @@ npyiter_prepare_one_operand(PyArrayObject **op,
NPY_ITEM_IS_POINTER))) != 0)) {
PyErr_SetString(PyExc_TypeError,
"Iterator operand or requested dtype holds "
- "references, but the REFS_OK flag was not enabled");
+ "references, but the NPY_ITER_REFS_OK flag was not "
+ "enabled");
return 0;
}
}
@@ -1118,7 +1134,7 @@ npyiter_prepare_one_operand(PyArrayObject **op,
if (op_request_dtype != NULL) {
/* We just have a borrowed reference to op_request_dtype */
Py_SETREF(*op_dtype, PyArray_AdaptDescriptorToArray(
- *op, (PyObject *)op_request_dtype));
+ *op, NULL, op_request_dtype));
if (*op_dtype == NULL) {
return 0;
}
@@ -1133,7 +1149,7 @@ npyiter_prepare_one_operand(PyArrayObject **op,
PyArray_DescrNewByteorder(*op_dtype, NPY_NATIVE));
if (*op_dtype == NULL) {
return 0;
- }
+ }
NPY_IT_DBG_PRINT("Iterator: Setting NPY_OP_ITFLAG_CAST "
"because of NPY_ITER_NBO\n");
/* Indicate that byte order or alignment needs fixing */
@@ -3186,31 +3202,33 @@ npyiter_allocate_transfer_functions(NpyIter *iter)
cflags = PyArrayMethod_COMBINED_FLAGS(cflags, nc_flags);
}
}
- /* If no write back but there are references make a decref fn */
- else if (PyDataType_REFCHK(op_dtype[iop])) {
+ else {
+ transferinfo[iop].write.func = NULL;
+ }
+
+ /* Get the decref function, used for no-writeback and on error */
+ if (PyDataType_REFCHK(op_dtype[iop])) {
/*
* By passing NULL to dst_type and setting move_references
* to 1, we get back a function that just decrements the
* src references.
*/
- if (PyArray_GetDTypeTransferFunction(
+ if (PyArray_GetClearFunction(
(flags & NPY_OP_ITFLAG_ALIGNED) != 0,
- op_dtype[iop]->elsize, 0,
- op_dtype[iop], NULL,
- 1,
- &transferinfo[iop].write,
- &nc_flags) != NPY_SUCCEED) {
+ op_dtype[iop]->elsize, op_dtype[iop],
+ &transferinfo[iop].clear, &nc_flags) < 0) {
goto fail;
}
cflags = PyArrayMethod_COMBINED_FLAGS(cflags, nc_flags);
}
else {
- transferinfo[iop].write.func = NULL;
+ transferinfo[iop].clear.func = NULL;
}
}
else {
transferinfo[iop].read.func = NULL;
transferinfo[iop].write.func = NULL;
+ transferinfo[iop].clear.func = NULL;
}
}
diff --git a/numpy/core/src/multiarray/nditer_impl.h b/numpy/core/src/multiarray/nditer_impl.h
index 5bf47fabd..a33e4f90f 100644
--- a/numpy/core/src/multiarray/nditer_impl.h
+++ b/numpy/core/src/multiarray/nditer_impl.h
@@ -23,6 +23,8 @@
#include "lowlevel_strided_loops.h"
#include "dtype_transfer.h"
+#include "dtype_traversal.h"
+
/********** ITERATOR CONSTRUCTION TIMING **************/
#define NPY_IT_CONSTRUCTION_TIMING 0
@@ -242,6 +244,7 @@ typedef npy_int16 npyiter_opitflags;
struct NpyIter_TransferInfo_tag {
NPY_cast_info read;
NPY_cast_info write;
+ NPY_traverse_info clear;
/* Probably unnecessary, but make sure what follows is intp aligned: */
npy_intp _unused_ensure_alignment[];
};
diff --git a/numpy/core/src/multiarray/refcount.c b/numpy/core/src/multiarray/refcount.c
index a1c310700..20527f7af 100644
--- a/numpy/core/src/multiarray/refcount.c
+++ b/numpy/core/src/multiarray/refcount.c
@@ -2,6 +2,10 @@
* This module corresponds to the `Special functions for NPY_OBJECT`
* section in the numpy reference for C-API.
*/
+#include "array_method.h"
+#include "dtype_traversal.h"
+#include "lowlevel_strided_loops.h"
+#include "pyerrors.h"
#define NPY_NO_DEPRECATED_API NPY_API_VERSION
#define _MULTIARRAYMODULE
@@ -12,6 +16,7 @@
#include "numpy/arrayobject.h"
#include "numpy/arrayscalars.h"
#include "iterators.h"
+#include "dtypemeta.h"
#include "npy_config.h"
@@ -21,6 +26,96 @@ static void
_fillobject(char *optr, PyObject *obj, PyArray_Descr *dtype);
+/*
+ * Helper function to clear a strided memory (normally or always contiguous)
+ * from all Python (or other) references. The function does nothing if the
+ * array dtype does not indicate holding references.
+ *
+ * It is safe to call this function more than once, failing here is usually
+ * critical (during cleanup) and should be set up to minimize the risk or
+ * avoid it fully.
+ */
+NPY_NO_EXPORT int
+PyArray_ClearBuffer(
+ PyArray_Descr *descr, char *data,
+ npy_intp stride, npy_intp size, int aligned)
+{
+ if (!PyDataType_REFCHK(descr)) {
+ return 0;
+ }
+
+ NPY_traverse_info clear_info;
+ /* Flags unused: float errors do not matter and we do not release GIL */
+ NPY_ARRAYMETHOD_FLAGS flags_unused;
+ if (PyArray_GetClearFunction(
+ aligned, stride, descr, &clear_info, &flags_unused) < 0) {
+ return -1;
+ }
+
+ int res = clear_info.func(
+ NULL, clear_info.descr, data, size, stride, clear_info.auxdata);
+ NPY_traverse_info_xfree(&clear_info);
+ return res;
+}
+
+
+/*
+ * Helper function to clear whole array. It seems plausible that we should
+ * be able to get away with assuming the the array is contiguous.
+ *
+ * Must only be called on arrays which own their data (and asserts this).
+ */
+ NPY_NO_EXPORT int
+ PyArray_ClearArray(PyArrayObject *arr)
+ {
+ assert(PyArray_FLAGS(arr) & NPY_ARRAY_OWNDATA);
+
+ PyArray_Descr *descr = PyArray_DESCR(arr);
+ if (!PyDataType_REFCHK(descr)) {
+ return 0;
+ }
+ /*
+ * The contiguous path should cover practically all important cases since
+ * it is difficult to create a non-contiguous array which owns its memory
+ * and only arrays which own their memory should clear it.
+ */
+ int aligned = PyArray_ISALIGNED(arr);
+ if (PyArray_ISCONTIGUOUS(arr)) {
+ return PyArray_ClearBuffer(
+ descr, PyArray_BYTES(arr), descr->elsize,
+ PyArray_SIZE(arr), aligned);
+ }
+ int idim, ndim;
+ npy_intp shape_it[NPY_MAXDIMS], strides_it[NPY_MAXDIMS];
+ npy_intp coord[NPY_MAXDIMS];
+ char *data_it;
+ if (PyArray_PrepareOneRawArrayIter(
+ PyArray_NDIM(arr), PyArray_DIMS(arr),
+ PyArray_BYTES(arr), PyArray_STRIDES(arr),
+ &ndim, shape_it, &data_it, strides_it) < 0) {
+ return -1;
+ }
+ npy_intp inner_stride = strides_it[0];
+ npy_intp inner_shape = shape_it[0];
+ NPY_traverse_info clear_info;
+ /* Flags unused: float errors do not matter and we do not release GIL */
+ NPY_ARRAYMETHOD_FLAGS flags_unused;
+ if (PyArray_GetClearFunction(
+ aligned, inner_stride, descr, &clear_info, &flags_unused) < 0) {
+ return -1;
+ }
+ NPY_RAW_ITER_START(idim, ndim, coord, shape_it) {
+ /* Process the innermost dimension */
+ if (clear_info.func(NULL, clear_info.descr,
+ data_it, inner_shape, inner_stride, clear_info.auxdata) < 0) {
+ return -1;
+ }
+ } NPY_RAW_ITER_ONE_NEXT(idim, ndim, coord,
+ shape_it, data_it, strides_it);
+ return 0;
+}
+
+
/*NUMPY_API
* XINCREF all objects in a single array item. This is complicated for
* structured datatypes where the position of objects needs to be extracted.
@@ -204,6 +299,10 @@ PyArray_INCREF(PyArrayObject *mp)
/*NUMPY_API
Decrement all internal references for object arrays.
(or arrays with object fields)
+
+ The use of this function is strongly discouraged, within NumPy
+ use PyArray_Clear, which DECREF's and sets everything to NULL and can
+ work with any dtype.
*/
NPY_NO_EXPORT int
PyArray_XDECREF(PyArrayObject *mp)
@@ -260,9 +359,17 @@ PyArray_XDECREF(PyArrayObject *mp)
NPY_NO_EXPORT void
PyArray_FillObjectArray(PyArrayObject *arr, PyObject *obj)
{
+ PyArray_Descr* descr = PyArray_DESCR(arr);
+
+ // non-legacy dtypes are responsible for initializing
+ // their own internal references
+ if (!NPY_DT_is_legacy(NPY_DTYPE(descr))) {
+ return;
+ }
+
npy_intp i,n;
n = PyArray_SIZE(arr);
- if (PyArray_DESCR(arr)->type_num == NPY_OBJECT) {
+ if (descr->type_num == NPY_OBJECT) {
PyObject **optr;
optr = (PyObject **)(PyArray_DATA(arr));
n = PyArray_SIZE(arr);
@@ -282,8 +389,8 @@ PyArray_FillObjectArray(PyArrayObject *arr, PyObject *obj)
char *optr;
optr = PyArray_DATA(arr);
for (i = 0; i < n; i++) {
- _fillobject(optr, obj, PyArray_DESCR(arr));
- optr += PyArray_DESCR(arr)->elsize;
+ _fillobject(optr, obj, descr);
+ optr += descr->elsize;
}
}
}
diff --git a/numpy/core/src/multiarray/refcount.h b/numpy/core/src/multiarray/refcount.h
index 959eef5ba..16d34e292 100644
--- a/numpy/core/src/multiarray/refcount.h
+++ b/numpy/core/src/multiarray/refcount.h
@@ -1,6 +1,14 @@
#ifndef NUMPY_CORE_SRC_MULTIARRAY_REFCOUNT_H_
#define NUMPY_CORE_SRC_MULTIARRAY_REFCOUNT_H_
+NPY_NO_EXPORT int
+PyArray_ClearBuffer(
+ PyArray_Descr *descr, char *data,
+ npy_intp stride, npy_intp size, int aligned);
+
+NPY_NO_EXPORT int
+PyArray_ClearArray(PyArrayObject *arr);
+
NPY_NO_EXPORT void
PyArray_Item_INCREF(char *data, PyArray_Descr *descr);
diff --git a/numpy/core/src/multiarray/textreading/tokenize.cpp b/numpy/core/src/multiarray/textreading/tokenize.cpp
index cc5e621d6..210428813 100644
--- a/numpy/core/src/multiarray/textreading/tokenize.cpp
+++ b/numpy/core/src/multiarray/textreading/tokenize.cpp
@@ -290,7 +290,7 @@ NPY_NO_EXPORT int
npy_tokenize(stream *s, tokenizer_state *ts, parser_config *const config)
{
assert(ts->fields_size >= 2);
- assert(ts->field_buffer_length >= 2*(ssize_t)sizeof(Py_UCS4));
+ assert(ts->field_buffer_length >= 2*(Py_ssize_t)sizeof(Py_UCS4));
int finished_reading_file = 0;
diff --git a/numpy/core/src/multiarray/usertypes.c b/numpy/core/src/multiarray/usertypes.c
index a338d712d..a172343f1 100644
--- a/numpy/core/src/multiarray/usertypes.c
+++ b/numpy/core/src/multiarray/usertypes.c
@@ -41,6 +41,7 @@ maintainer email: oliphant.travis@ieee.org
#include "scalartypes.h"
#include "array_method.h"
#include "convert_datatype.h"
+#include "dtype_traversal.h"
#include "legacy_dtype_implementation.h"
@@ -223,6 +224,8 @@ PyArray_RegisterDataType(PyArray_Descr *descr)
PyErr_SetString(PyExc_ValueError, "missing typeobject");
return -1;
}
+
+ int use_void_clearimpl = 0;
if (descr->flags & (NPY_ITEM_IS_POINTER | NPY_ITEM_REFCOUNT)) {
/*
* User dtype can't actually do reference counting, however, there
@@ -231,6 +234,8 @@ PyArray_RegisterDataType(PyArray_Descr *descr)
* so we have to support this. But such a structure must be constant
* (i.e. fixed at registration time, this is the case for `xpress`).
*/
+ use_void_clearimpl = 1;
+
if (descr->names == NULL || descr->fields == NULL ||
!PyDict_CheckExact(descr->fields)) {
PyErr_Format(PyExc_ValueError,
@@ -264,6 +269,13 @@ PyArray_RegisterDataType(PyArray_Descr *descr)
NPY_NUMUSERTYPES--;
return -1;
}
+ if (use_void_clearimpl) {
+ /* See comment where use_void_clearimpl is set... */
+ PyArray_DTypeMeta *Void = PyArray_DTypeFromTypeNum(NPY_VOID);
+ NPY_DT_SLOTS(NPY_DTYPE(descr))->get_clear_loop = (
+ &npy_get_clear_void_and_legacy_user_dtype_loop);
+ Py_DECREF(Void);
+ }
return typenum;
}
@@ -597,7 +609,7 @@ PyArray_AddLegacyWrapping_CastingImpl(
if (from == to) {
spec.flags = NPY_METH_REQUIRES_PYAPI | NPY_METH_SUPPORTS_UNALIGNED;
PyType_Slot slots[] = {
- {NPY_METH_get_loop, &legacy_cast_get_strided_loop},
+ {_NPY_METH_get_loop, &legacy_cast_get_strided_loop},
{NPY_METH_resolve_descriptors, &legacy_same_dtype_resolve_descriptors},
{0, NULL}};
spec.slots = slots;
@@ -606,7 +618,7 @@ PyArray_AddLegacyWrapping_CastingImpl(
else {
spec.flags = NPY_METH_REQUIRES_PYAPI;
PyType_Slot slots[] = {
- {NPY_METH_get_loop, &legacy_cast_get_strided_loop},
+ {_NPY_METH_get_loop, &legacy_cast_get_strided_loop},
{NPY_METH_resolve_descriptors, &simple_cast_resolve_descriptors},
{0, NULL}};
spec.slots = slots;
diff --git a/numpy/core/src/npysort/heapsort.cpp b/numpy/core/src/npysort/heapsort.cpp
index 3956de51f..77a4bda74 100644
--- a/numpy/core/src/npysort/heapsort.cpp
+++ b/numpy/core/src/npysort/heapsort.cpp
@@ -55,6 +55,9 @@ npy_heapsort(void *start, npy_intp num, void *varr)
PyArrayObject *arr = (PyArrayObject *)varr;
npy_intp elsize = PyArray_ITEMSIZE(arr);
PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare;
+ if (elsize == 0) {
+ return 0; /* no need for sorting elements of no size */
+ }
char *tmp = (char *)malloc(elsize);
char *a = (char *)start - elsize;
npy_intp i, j, l;
diff --git a/numpy/core/src/npysort/mergesort.cpp b/numpy/core/src/npysort/mergesort.cpp
index 60d89ddb7..f53feb320 100644
--- a/numpy/core/src/npysort/mergesort.cpp
+++ b/numpy/core/src/npysort/mergesort.cpp
@@ -171,6 +171,7 @@ amergesort_(type *v, npy_intp *tosort, npy_intp num)
}
/*
+
*****************************************************************************
** STRING SORTS **
*****************************************************************************
diff --git a/numpy/core/src/npysort/npysort_heapsort.h b/numpy/core/src/npysort/npysort_heapsort.h
index 442320094..16750b817 100644
--- a/numpy/core/src/npysort/npysort_heapsort.h
+++ b/numpy/core/src/npysort/npysort_heapsort.h
@@ -128,6 +128,10 @@ int string_heapsort_(type *start, npy_intp n, void *varr)
{
PyArrayObject *arr = (PyArrayObject *)varr;
size_t len = PyArray_ITEMSIZE(arr) / sizeof(type);
+ if (len == 0) {
+ return 0; /* no need for sorting if strings are empty */
+ }
+
type *tmp = (type *)malloc(PyArray_ITEMSIZE(arr));
type *a = (type *)start - len;
npy_intp i, j, l;
diff --git a/numpy/core/src/npysort/quicksort.cpp b/numpy/core/src/npysort/quicksort.cpp
index 3e351dd84..7497ebaa3 100644
--- a/numpy/core/src/npysort/quicksort.cpp
+++ b/numpy/core/src/npysort/quicksort.cpp
@@ -54,15 +54,11 @@
#include "npysort_common.h"
#include "npysort_heapsort.h"
#include "numpy_tag.h"
+#include "simd_qsort.hpp"
-#include "x86-qsort.h"
#include <cstdlib>
#include <utility>
-#ifndef NPY_DISABLE_OPTIMIZATION
-#include "x86-qsort.dispatch.h"
-#endif
-
#define NOT_USED NPY_UNUSED(unused)
/*
* pushing largest partition has upper bound of log2(n) space
@@ -73,70 +69,50 @@
#define SMALL_MERGESORT 20
#define SMALL_STRING 16
+// Temporarily disable AVX512 sorting on WIN32 and CYGWIN until we can figure
+// out why it has test failures
+#if defined(_MSC_VER) || defined(__CYGWIN__)
+template<typename T>
+inline bool quicksort_dispatch(T*, npy_intp)
+{
+ return false;
+}
+#else
+template<typename T>
+inline bool quicksort_dispatch(T *start, npy_intp num)
+{
+ using TF = typename np::meta::FixedWidth<T>::Type;
+ void (*dispfunc)(TF*, intptr_t) = nullptr;
+ if (sizeof(T) == sizeof(uint16_t)) {
+ #ifndef NPY_DISABLE_OPTIMIZATION
+ #include "simd_qsort_16bit.dispatch.h"
+ #endif
+ NPY_CPU_DISPATCH_CALL_XB(dispfunc = np::qsort_simd::template QSort, <TF>);
+ }
+ else if (sizeof(T) == sizeof(uint32_t) || sizeof(T) == sizeof(uint64_t)) {
+ #ifndef NPY_DISABLE_OPTIMIZATION
+ #include "simd_qsort.dispatch.h"
+ #endif
+ NPY_CPU_DISPATCH_CALL_XB(dispfunc = np::qsort_simd::template QSort, <TF>);
+ }
+ if (dispfunc) {
+ (*dispfunc)(reinterpret_cast<TF*>(start), static_cast<intptr_t>(num));
+ return true;
+ }
+ return false;
+}
+#endif // _MSC_VER || CYGWIN
+
/*
*****************************************************************************
** NUMERIC SORTS **
*****************************************************************************
*/
-namespace {
-
-template <typename Tag>
-struct x86_dispatch {
- static bool quicksort(typename Tag::type *, npy_intp) { return false; }
-};
-
-template <>
-struct x86_dispatch<npy::int_tag> {
- static bool quicksort(npy_int *start, npy_intp num)
- {
- void (*dispfunc)(void *, npy_intp) = nullptr;
- NPY_CPU_DISPATCH_CALL_XB(dispfunc = x86_quicksort_int);
- if (dispfunc) {
- (*dispfunc)(start, num);
- return true;
- }
- return false;
- }
-};
-
-template <>
-struct x86_dispatch<npy::uint_tag> {
- static bool quicksort(npy_uint *start, npy_intp num)
- {
- void (*dispfunc)(void *, npy_intp) = nullptr;
- NPY_CPU_DISPATCH_CALL_XB(dispfunc = x86_quicksort_uint);
- if (dispfunc) {
- (*dispfunc)(start, num);
- return true;
- }
- return false;
- }
-};
-
-template <>
-struct x86_dispatch<npy::float_tag> {
- static bool quicksort(npy_float *start, npy_intp num)
- {
- void (*dispfunc)(void *, npy_intp) = nullptr;
- NPY_CPU_DISPATCH_CALL_XB(dispfunc = x86_quicksort_float);
- if (dispfunc) {
- (*dispfunc)(start, num);
- return true;
- }
- return false;
- }
-};
-
-} // namespace
-
template <typename Tag, typename type>
static int
quicksort_(type *start, npy_intp num)
{
- if (x86_dispatch<Tag>::quicksort(start, num))
- return 0;
-
type vp;
type *pl = start;
type *pr = pl + num - 1;
@@ -729,56 +705,89 @@ quicksort_ubyte(void *start, npy_intp n, void *NPY_UNUSED(varr))
NPY_NO_EXPORT int
quicksort_short(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
+ if (quicksort_dispatch((npy_short *)start, n)) {
+ return 0;
+ }
return quicksort_<npy::short_tag>((npy_short *)start, n);
}
NPY_NO_EXPORT int
quicksort_ushort(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
+ if (quicksort_dispatch((npy_ushort *)start, n)) {
+ return 0;
+ }
return quicksort_<npy::ushort_tag>((npy_ushort *)start, n);
}
NPY_NO_EXPORT int
quicksort_int(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
+ if (quicksort_dispatch((npy_int *)start, n)) {
+ return 0;
+ }
return quicksort_<npy::int_tag>((npy_int *)start, n);
}
NPY_NO_EXPORT int
quicksort_uint(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
+ if (quicksort_dispatch((npy_uint *)start, n)) {
+ return 0;
+ }
return quicksort_<npy::uint_tag>((npy_uint *)start, n);
}
NPY_NO_EXPORT int
quicksort_long(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
+ if (quicksort_dispatch((npy_long *)start, n)) {
+ return 0;
+ }
return quicksort_<npy::long_tag>((npy_long *)start, n);
}
NPY_NO_EXPORT int
quicksort_ulong(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
+ if (quicksort_dispatch((npy_ulong *)start, n)) {
+ return 0;
+ }
return quicksort_<npy::ulong_tag>((npy_ulong *)start, n);
}
NPY_NO_EXPORT int
quicksort_longlong(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
+ if (quicksort_dispatch((npy_longlong *)start, n)) {
+ return 0;
+ }
return quicksort_<npy::longlong_tag>((npy_longlong *)start, n);
}
NPY_NO_EXPORT int
quicksort_ulonglong(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
+ if (quicksort_dispatch((npy_ulonglong *)start, n)) {
+ return 0;
+ }
return quicksort_<npy::ulonglong_tag>((npy_ulonglong *)start, n);
}
NPY_NO_EXPORT int
quicksort_half(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
+ if (quicksort_dispatch((np::Half *)start, n)) {
+ return 0;
+ }
return quicksort_<npy::half_tag>((npy_half *)start, n);
}
NPY_NO_EXPORT int
quicksort_float(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
+ if (quicksort_dispatch((npy_float *)start, n)) {
+ return 0;
+ }
return quicksort_<npy::float_tag>((npy_float *)start, n);
}
NPY_NO_EXPORT int
quicksort_double(void *start, npy_intp n, void *NPY_UNUSED(varr))
{
+ if (quicksort_dispatch((npy_double *)start, n)) {
+ return 0;
+ }
return quicksort_<npy::double_tag>((npy_double *)start, n);
}
NPY_NO_EXPORT int
diff --git a/numpy/core/src/npysort/simd_qsort.dispatch.cpp b/numpy/core/src/npysort/simd_qsort.dispatch.cpp
new file mode 100644
index 000000000..101bb3dcc
--- /dev/null
+++ b/numpy/core/src/npysort/simd_qsort.dispatch.cpp
@@ -0,0 +1,44 @@
+/*@targets
+ * $maxopt $keep_baseline avx512_skx
+ */
+// policy $keep_baseline is used to avoid skip building avx512_skx
+// when its part of baseline features (--cpu-baseline), since
+// 'baseline' option isn't specified within targets.
+
+#include "simd_qsort.hpp"
+
+#if defined(NPY_HAVE_AVX512_SKX) && !defined(_MSC_VER)
+ #include "x86-simd-sort/src/avx512-32bit-qsort.hpp"
+ #include "x86-simd-sort/src/avx512-64bit-qsort.hpp"
+#endif
+
+namespace np { namespace qsort_simd {
+
+#if defined(NPY_HAVE_AVX512_SKX) && !defined(_MSC_VER)
+template<> void NPY_CPU_DISPATCH_CURFX(QSort)(int32_t *arr, intptr_t size)
+{
+ avx512_qsort(arr, size);
+}
+template<> void NPY_CPU_DISPATCH_CURFX(QSort)(uint32_t *arr, intptr_t size)
+{
+ avx512_qsort(arr, size);
+}
+template<> void NPY_CPU_DISPATCH_CURFX(QSort)(int64_t *arr, intptr_t size)
+{
+ avx512_qsort(arr, size);
+}
+template<> void NPY_CPU_DISPATCH_CURFX(QSort)(uint64_t *arr, intptr_t size)
+{
+ avx512_qsort(arr, size);
+}
+template<> void NPY_CPU_DISPATCH_CURFX(QSort)(float *arr, intptr_t size)
+{
+ avx512_qsort(arr, size);
+}
+template<> void NPY_CPU_DISPATCH_CURFX(QSort)(double *arr, intptr_t size)
+{
+ avx512_qsort(arr, size);
+}
+#endif // NPY_HAVE_AVX512_SKX
+
+}} // namespace np::simd
diff --git a/numpy/core/src/npysort/simd_qsort.hpp b/numpy/core/src/npysort/simd_qsort.hpp
new file mode 100644
index 000000000..7cdee774d
--- /dev/null
+++ b/numpy/core/src/npysort/simd_qsort.hpp
@@ -0,0 +1,19 @@
+#ifndef NUMPY_SRC_COMMON_NPYSORT_SIMD_QSORT_HPP
+#define NUMPY_SRC_COMMON_NPYSORT_SIMD_QSORT_HPP
+
+#include "common.hpp"
+
+namespace np { namespace qsort_simd {
+
+#ifndef NPY_DISABLE_OPTIMIZATION
+ #include "simd_qsort.dispatch.h"
+#endif
+NPY_CPU_DISPATCH_DECLARE(template <typename T> void QSort, (T *arr, intptr_t size))
+
+#ifndef NPY_DISABLE_OPTIMIZATION
+ #include "simd_qsort_16bit.dispatch.h"
+#endif
+NPY_CPU_DISPATCH_DECLARE(template <typename T> void QSort, (T *arr, intptr_t size))
+
+} } // np::qsort_simd
+#endif // NUMPY_SRC_COMMON_NPYSORT_SIMD_QSORT_HPP
diff --git a/numpy/core/src/npysort/simd_qsort_16bit.dispatch.cpp b/numpy/core/src/npysort/simd_qsort_16bit.dispatch.cpp
new file mode 100644
index 000000000..a6465a883
--- /dev/null
+++ b/numpy/core/src/npysort/simd_qsort_16bit.dispatch.cpp
@@ -0,0 +1,31 @@
+/*@targets
+ * $maxopt $keep_baseline avx512_icl
+ */
+// policy $keep_baseline is used to avoid skip building avx512_skx
+// when its part of baseline features (--cpu-baseline), since
+// 'baseline' option isn't specified within targets.
+
+#include "simd_qsort.hpp"
+
+#if defined(NPY_HAVE_AVX512_ICL) && !defined(_MSC_VER)
+ #include "x86-simd-sort/src/avx512-16bit-qsort.hpp"
+#endif
+
+namespace np { namespace qsort_simd {
+
+#if defined(NPY_HAVE_AVX512_ICL) && !defined(_MSC_VER)
+template<> void NPY_CPU_DISPATCH_CURFX(QSort)(Half *arr, intptr_t size)
+{
+ avx512_qsort_fp16(reinterpret_cast<uint16_t*>(arr), size);
+}
+template<> void NPY_CPU_DISPATCH_CURFX(QSort)(uint16_t *arr, intptr_t size)
+{
+ avx512_qsort(arr, size);
+}
+template<> void NPY_CPU_DISPATCH_CURFX(QSort)(int16_t *arr, intptr_t size)
+{
+ avx512_qsort(arr, size);
+}
+#endif // NPY_HAVE_AVX512_ICL
+
+}} // namespace np::qsort_simd
diff --git a/numpy/core/src/npysort/x86-qsort.dispatch.cpp b/numpy/core/src/npysort/x86-qsort.dispatch.cpp
deleted file mode 100644
index 8e88cc667..000000000
--- a/numpy/core/src/npysort/x86-qsort.dispatch.cpp
+++ /dev/null
@@ -1,835 +0,0 @@
-/*@targets
- * $maxopt $keep_baseline avx512_skx
- */
-// policy $keep_baseline is used to avoid skip building avx512_skx
-// when its part of baseline features (--cpu-baseline), since
-// 'baseline' option isn't specified within targets.
-
-#include "x86-qsort.h"
-#define NPY_NO_DEPRECATED_API NPY_API_VERSION
-
-#ifdef NPY_HAVE_AVX512_SKX
-#include "numpy/npy_math.h"
-
-#include "npy_sort.h"
-#include "numpy_tag.h"
-
-#include "simd/simd.h"
-#include <immintrin.h>
-
-template <typename Tag, typename type>
-NPY_NO_EXPORT int
-heapsort_(type *start, npy_intp n);
-
-/*
- * Quicksort using AVX-512 for int, uint32 and float. The ideas and code are
- * based on these two research papers:
- * (1) Fast and Robust Vectorized In-Place Sorting of Primitive Types
- * https://drops.dagstuhl.de/opus/volltexte/2021/13775/
- * (2) A Novel Hybrid Quicksort Algorithm Vectorized using AVX-512 on Intel
- * Skylake https://arxiv.org/pdf/1704.08579.pdf
- *
- * High level idea: Vectorize the quicksort partitioning using AVX-512
- * compressstore instructions. The algorithm to pick the pivot is to use median
- * of 72 elements picked at random. If the array size is < 128, then use
- * Bitonic sorting network. Good resource for bitonic sorting network:
- * http://mitp-content-server.mit.edu:18180/books/content/sectbyfn?collid=books_pres_0&fn=Chapter%2027.pdf&id=8030
- *
- * Refer to https://github.com/numpy/numpy/pull/20133#issuecomment-958110340
- * for potential problems when converting this code to universal intrinsics
- * framework.
- */
-
-/*
- * Constants used in sorting 16 elements in a ZMM registers. Based on Bitonic
- * sorting network (see
- * https://en.wikipedia.org/wiki/Bitonic_sorter#/media/File:BitonicSort.svg)
- */
-#define NETWORK1 14, 15, 12, 13, 10, 11, 8, 9, 6, 7, 4, 5, 2, 3, 0, 1
-#define NETWORK2 12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3
-#define NETWORK3 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7
-#define NETWORK4 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2
-#define NETWORK5 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
-#define NETWORK6 11, 10, 9, 8, 15, 14, 13, 12, 3, 2, 1, 0, 7, 6, 5, 4
-#define NETWORK7 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8
-#define ZMM_MAX_FLOAT _mm512_set1_ps(NPY_INFINITYF)
-#define ZMM_MAX_UINT _mm512_set1_epi32(NPY_MAX_UINT32)
-#define ZMM_MAX_INT _mm512_set1_epi32(NPY_MAX_INT32)
-#define SHUFFLE_MASK(a, b, c, d) (a << 6) | (b << 4) | (c << 2) | d
-#define SHUFFLE_ps(ZMM, MASK) _mm512_shuffle_ps(zmm, zmm, MASK)
-#define SHUFFLE_epi32(ZMM, MASK) _mm512_shuffle_epi32(zmm, MASK)
-
-#define MAX(x, y) (((x) > (y)) ? (x) : (y))
-#define MIN(x, y) (((x) < (y)) ? (x) : (y))
-
-/*
- * Vectorized random number generator xoroshiro128+. Broken into 2 parts:
- * (1) vnext generates 2 64-bit random integers
- * (2) rnd_epu32 converts this to 4 32-bit random integers and bounds it to
- * the length of the array
- */
-#define VROTL(x, k) /* rotate each uint64_t value in vector */ \
- _mm256_or_si256(_mm256_slli_epi64((x), (k)), \
- _mm256_srli_epi64((x), 64 - (k)))
-
-static inline __m256i
-vnext(__m256i *s0, __m256i *s1)
-{
- *s1 = _mm256_xor_si256(*s0, *s1); /* modify vectors s1 and s0 */
- *s0 = _mm256_xor_si256(_mm256_xor_si256(VROTL(*s0, 24), *s1),
- _mm256_slli_epi64(*s1, 16));
- *s1 = VROTL(*s1, 37);
- return _mm256_add_epi64(*s0, *s1); /* return random vector */
-}
-
-/* transform random numbers to the range between 0 and bound - 1 */
-static inline __m256i
-rnd_epu32(__m256i rnd_vec, __m256i bound)
-{
- __m256i even = _mm256_srli_epi64(_mm256_mul_epu32(rnd_vec, bound), 32);
- __m256i odd = _mm256_mul_epu32(_mm256_srli_epi64(rnd_vec, 32), bound);
- return _mm256_blend_epi32(odd, even, 0b01010101);
-}
-
-template <typename type>
-struct vector;
-
-template <>
-struct vector<npy_int> {
- using tag = npy::int_tag;
- using type_t = npy_int;
- using zmm_t = __m512i;
- using ymm_t = __m256i;
-
- static type_t type_max() { return NPY_MAX_INT32; }
- static type_t type_min() { return NPY_MIN_INT32; }
- static zmm_t zmm_max() { return _mm512_set1_epi32(type_max()); }
-
- static __mmask16 ge(zmm_t x, zmm_t y)
- {
- return _mm512_cmp_epi32_mask(x, y, _MM_CMPINT_NLT);
- }
- template <int scale>
- static ymm_t i64gather(__m512i index, void const *base)
- {
- return _mm512_i64gather_epi32(index, base, scale);
- }
- static zmm_t loadu(void const *mem) { return _mm512_loadu_si512(mem); }
- static zmm_t max(zmm_t x, zmm_t y) { return _mm512_max_epi32(x, y); }
- static void mask_compressstoreu(void *mem, __mmask16 mask, zmm_t x)
- {
- return _mm512_mask_compressstoreu_epi32(mem, mask, x);
- }
- static zmm_t mask_loadu(zmm_t x, __mmask16 mask, void const *mem)
- {
- return _mm512_mask_loadu_epi32(x, mask, mem);
- }
- static zmm_t mask_mov(zmm_t x, __mmask16 mask, zmm_t y)
- {
- return _mm512_mask_mov_epi32(x, mask, y);
- }
- static void mask_storeu(void *mem, __mmask16 mask, zmm_t x)
- {
- return _mm512_mask_storeu_epi32(mem, mask, x);
- }
- static zmm_t min(zmm_t x, zmm_t y) { return _mm512_min_epi32(x, y); }
- static zmm_t permutexvar(__m512i idx, zmm_t zmm)
- {
- return _mm512_permutexvar_epi32(idx, zmm);
- }
- static type_t reducemax(zmm_t v) { return npyv_reduce_max_s32(v); }
- static type_t reducemin(zmm_t v) { return npyv_reduce_min_s32(v); }
- static zmm_t set1(type_t v) { return _mm512_set1_epi32(v); }
- template<__mmask16 mask>
- static zmm_t shuffle(zmm_t zmm)
- {
- return _mm512_shuffle_epi32(zmm, (_MM_PERM_ENUM)mask);
- }
- static void storeu(void *mem, zmm_t x)
- {
- return _mm512_storeu_si512(mem, x);
- }
-
- static ymm_t max(ymm_t x, ymm_t y) { return _mm256_max_epi32(x, y); }
- static ymm_t min(ymm_t x, ymm_t y) { return _mm256_min_epi32(x, y); }
-};
-template <>
-struct vector<npy_uint> {
- using tag = npy::uint_tag;
- using type_t = npy_uint;
- using zmm_t = __m512i;
- using ymm_t = __m256i;
-
- static type_t type_max() { return NPY_MAX_UINT32; }
- static type_t type_min() { return 0; }
- static zmm_t zmm_max() { return _mm512_set1_epi32(type_max()); }
-
- template<int scale>
- static ymm_t i64gather(__m512i index, void const *base)
- {
- return _mm512_i64gather_epi32(index, base, scale);
- }
- static __mmask16 ge(zmm_t x, zmm_t y)
- {
- return _mm512_cmp_epu32_mask(x, y, _MM_CMPINT_NLT);
- }
- static zmm_t loadu(void const *mem) { return _mm512_loadu_si512(mem); }
- static zmm_t max(zmm_t x, zmm_t y) { return _mm512_max_epu32(x, y); }
- static void mask_compressstoreu(void *mem, __mmask16 mask, zmm_t x)
- {
- return _mm512_mask_compressstoreu_epi32(mem, mask, x);
- }
- static zmm_t mask_loadu(zmm_t x, __mmask16 mask, void const *mem)
- {
- return _mm512_mask_loadu_epi32(x, mask, mem);
- }
- static zmm_t mask_mov(zmm_t x, __mmask16 mask, zmm_t y)
- {
- return _mm512_mask_mov_epi32(x, mask, y);
- }
- static void mask_storeu(void *mem, __mmask16 mask, zmm_t x)
- {
- return _mm512_mask_storeu_epi32(mem, mask, x);
- }
- static zmm_t min(zmm_t x, zmm_t y) { return _mm512_min_epu32(x, y); }
- static zmm_t permutexvar(__m512i idx, zmm_t zmm)
- {
- return _mm512_permutexvar_epi32(idx, zmm);
- }
- static type_t reducemax(zmm_t v) { return npyv_reduce_max_u32(v); }
- static type_t reducemin(zmm_t v) { return npyv_reduce_min_u32(v); }
- static zmm_t set1(type_t v) { return _mm512_set1_epi32(v); }
- template<__mmask16 mask>
- static zmm_t shuffle(zmm_t zmm)
- {
- return _mm512_shuffle_epi32(zmm, (_MM_PERM_ENUM)mask);
- }
- static void storeu(void *mem, zmm_t x)
- {
- return _mm512_storeu_si512(mem, x);
- }
-
- static ymm_t max(ymm_t x, ymm_t y) { return _mm256_max_epu32(x, y); }
- static ymm_t min(ymm_t x, ymm_t y) { return _mm256_min_epu32(x, y); }
-};
-template <>
-struct vector<npy_float> {
- using tag = npy::float_tag;
- using type_t = npy_float;
- using zmm_t = __m512;
- using ymm_t = __m256;
-
- static type_t type_max() { return NPY_INFINITYF; }
- static type_t type_min() { return -NPY_INFINITYF; }
- static zmm_t zmm_max() { return _mm512_set1_ps(type_max()); }
-
- static __mmask16 ge(zmm_t x, zmm_t y)
- {
- return _mm512_cmp_ps_mask(x, y, _CMP_GE_OQ);
- }
- template<int scale>
- static ymm_t i64gather(__m512i index, void const *base)
- {
- return _mm512_i64gather_ps(index, base, scale);
- }
- static zmm_t loadu(void const *mem) { return _mm512_loadu_ps(mem); }
- static zmm_t max(zmm_t x, zmm_t y) { return _mm512_max_ps(x, y); }
- static void mask_compressstoreu(void *mem, __mmask16 mask, zmm_t x)
- {
- return _mm512_mask_compressstoreu_ps(mem, mask, x);
- }
- static zmm_t mask_loadu(zmm_t x, __mmask16 mask, void const *mem)
- {
- return _mm512_mask_loadu_ps(x, mask, mem);
- }
- static zmm_t mask_mov(zmm_t x, __mmask16 mask, zmm_t y)
- {
- return _mm512_mask_mov_ps(x, mask, y);
- }
- static void mask_storeu(void *mem, __mmask16 mask, zmm_t x)
- {
- return _mm512_mask_storeu_ps(mem, mask, x);
- }
- static zmm_t min(zmm_t x, zmm_t y) { return _mm512_min_ps(x, y); }
- static zmm_t permutexvar(__m512i idx, zmm_t zmm)
- {
- return _mm512_permutexvar_ps(idx, zmm);
- }
- static type_t reducemax(zmm_t v) { return npyv_reduce_max_f32(v); }
- static type_t reducemin(zmm_t v) { return npyv_reduce_min_f32(v); }
- static zmm_t set1(type_t v) { return _mm512_set1_ps(v); }
- template<__mmask16 mask>
- static zmm_t shuffle(zmm_t zmm)
- {
- return _mm512_shuffle_ps(zmm, zmm, (_MM_PERM_ENUM)mask);
- }
- static void storeu(void *mem, zmm_t x) { return _mm512_storeu_ps(mem, x); }
-
- static ymm_t max(ymm_t x, ymm_t y) { return _mm256_max_ps(x, y); }
- static ymm_t min(ymm_t x, ymm_t y) { return _mm256_min_ps(x, y); }
-};
-
-/*
- * COEX == Compare and Exchange two registers by swapping min and max values
- */
-template <typename vtype, typename mm_t>
-void
-COEX(mm_t &a, mm_t &b)
-{
- mm_t temp = a;
- a = vtype::min(a, b);
- b = vtype::max(temp, b);
-}
-
-template <typename vtype, typename zmm_t = typename vtype::zmm_t>
-static inline zmm_t
-cmp_merge(zmm_t in1, zmm_t in2, __mmask16 mask)
-{
- zmm_t min = vtype::min(in2, in1);
- zmm_t max = vtype::max(in2, in1);
- return vtype::mask_mov(min, mask, max); // 0 -> min, 1 -> max
-}
-
-/*
- * Assumes zmm is random and performs a full sorting network defined in
- * https://en.wikipedia.org/wiki/Bitonic_sorter#/media/File:BitonicSort.svg
- */
-template <typename vtype, typename zmm_t = typename vtype::zmm_t>
-static inline zmm_t
-sort_zmm(zmm_t zmm)
-{
- zmm = cmp_merge<vtype>(zmm, vtype::template shuffle<SHUFFLE_MASK(2, 3, 0, 1)>(zmm),
- 0xAAAA);
- zmm = cmp_merge<vtype>(zmm, vtype::template shuffle<SHUFFLE_MASK(0, 1, 2, 3)>(zmm),
- 0xCCCC);
- zmm = cmp_merge<vtype>(zmm, vtype::template shuffle<SHUFFLE_MASK(2, 3, 0, 1)>(zmm),
- 0xAAAA);
- zmm = cmp_merge<vtype>(
- zmm, vtype::permutexvar(_mm512_set_epi32(NETWORK3), zmm), 0xF0F0);
- zmm = cmp_merge<vtype>(zmm, vtype::template shuffle<SHUFFLE_MASK(1, 0, 3, 2)>(zmm),
- 0xCCCC);
- zmm = cmp_merge<vtype>(zmm, vtype::template shuffle<SHUFFLE_MASK(2, 3, 0, 1)>(zmm),
- 0xAAAA);
- zmm = cmp_merge<vtype>(
- zmm, vtype::permutexvar(_mm512_set_epi32(NETWORK5), zmm), 0xFF00);
- zmm = cmp_merge<vtype>(
- zmm, vtype::permutexvar(_mm512_set_epi32(NETWORK6), zmm), 0xF0F0);
- zmm = cmp_merge<vtype>(zmm, vtype::template shuffle<SHUFFLE_MASK(1, 0, 3, 2)>(zmm),
- 0xCCCC);
- zmm = cmp_merge<vtype>(zmm, vtype::template shuffle<SHUFFLE_MASK(2, 3, 0, 1)>(zmm),
- 0xAAAA);
- return zmm;
-}
-
-// Assumes zmm is bitonic and performs a recursive half cleaner
-template <typename vtype, typename zmm_t = typename vtype::zmm_t>
-static inline zmm_t
-bitonic_merge_zmm(zmm_t zmm)
-{
- // 1) half_cleaner[16]: compare 1-9, 2-10, 3-11 etc ..
- zmm = cmp_merge<vtype>(
- zmm, vtype::permutexvar(_mm512_set_epi32(NETWORK7), zmm), 0xFF00);
- // 2) half_cleaner[8]: compare 1-5, 2-6, 3-7 etc ..
- zmm = cmp_merge<vtype>(
- zmm, vtype::permutexvar(_mm512_set_epi32(NETWORK6), zmm), 0xF0F0);
- // 3) half_cleaner[4]
- zmm = cmp_merge<vtype>(zmm, vtype::template shuffle<SHUFFLE_MASK(1, 0, 3, 2)>(zmm),
- 0xCCCC);
- // 3) half_cleaner[1]
- zmm = cmp_merge<vtype>(zmm, vtype::template shuffle<SHUFFLE_MASK(2, 3, 0, 1)>(zmm),
- 0xAAAA);
- return zmm;
-}
-
-// Assumes zmm1 and zmm2 are sorted and performs a recursive half cleaner
-template <typename vtype, typename zmm_t = typename vtype::zmm_t>
-static inline void
-bitonic_merge_two_zmm(zmm_t *zmm1, zmm_t *zmm2)
-{
- // 1) First step of a merging network: coex of zmm1 and zmm2 reversed
- *zmm2 = vtype::permutexvar(_mm512_set_epi32(NETWORK5), *zmm2);
- zmm_t zmm3 = vtype::min(*zmm1, *zmm2);
- zmm_t zmm4 = vtype::max(*zmm1, *zmm2);
- // 2) Recursive half cleaner for each
- *zmm1 = bitonic_merge_zmm<vtype>(zmm3);
- *zmm2 = bitonic_merge_zmm<vtype>(zmm4);
-}
-
-// Assumes [zmm0, zmm1] and [zmm2, zmm3] are sorted and performs a recursive
-// half cleaner
-template <typename vtype, typename zmm_t = typename vtype::zmm_t>
-static inline void
-bitonic_merge_four_zmm(zmm_t *zmm)
-{
- zmm_t zmm2r = vtype::permutexvar(_mm512_set_epi32(NETWORK5), zmm[2]);
- zmm_t zmm3r = vtype::permutexvar(_mm512_set_epi32(NETWORK5), zmm[3]);
- zmm_t zmm_t1 = vtype::min(zmm[0], zmm3r);
- zmm_t zmm_t2 = vtype::min(zmm[1], zmm2r);
- zmm_t zmm_t3 = vtype::permutexvar(_mm512_set_epi32(NETWORK5),
- vtype::max(zmm[1], zmm2r));
- zmm_t zmm_t4 = vtype::permutexvar(_mm512_set_epi32(NETWORK5),
- vtype::max(zmm[0], zmm3r));
- zmm_t zmm0 = vtype::min(zmm_t1, zmm_t2);
- zmm_t zmm1 = vtype::max(zmm_t1, zmm_t2);
- zmm_t zmm2 = vtype::min(zmm_t3, zmm_t4);
- zmm_t zmm3 = vtype::max(zmm_t3, zmm_t4);
- zmm[0] = bitonic_merge_zmm<vtype>(zmm0);
- zmm[1] = bitonic_merge_zmm<vtype>(zmm1);
- zmm[2] = bitonic_merge_zmm<vtype>(zmm2);
- zmm[3] = bitonic_merge_zmm<vtype>(zmm3);
-}
-
-template <typename vtype, typename zmm_t = typename vtype::zmm_t>
-static inline void
-bitonic_merge_eight_zmm(zmm_t *zmm)
-{
- zmm_t zmm4r = vtype::permutexvar(_mm512_set_epi32(NETWORK5), zmm[4]);
- zmm_t zmm5r = vtype::permutexvar(_mm512_set_epi32(NETWORK5), zmm[5]);
- zmm_t zmm6r = vtype::permutexvar(_mm512_set_epi32(NETWORK5), zmm[6]);
- zmm_t zmm7r = vtype::permutexvar(_mm512_set_epi32(NETWORK5), zmm[7]);
- zmm_t zmm_t1 = vtype::min(zmm[0], zmm7r);
- zmm_t zmm_t2 = vtype::min(zmm[1], zmm6r);
- zmm_t zmm_t3 = vtype::min(zmm[2], zmm5r);
- zmm_t zmm_t4 = vtype::min(zmm[3], zmm4r);
- zmm_t zmm_t5 = vtype::permutexvar(_mm512_set_epi32(NETWORK5),
- vtype::max(zmm[3], zmm4r));
- zmm_t zmm_t6 = vtype::permutexvar(_mm512_set_epi32(NETWORK5),
- vtype::max(zmm[2], zmm5r));
- zmm_t zmm_t7 = vtype::permutexvar(_mm512_set_epi32(NETWORK5),
- vtype::max(zmm[1], zmm6r));
- zmm_t zmm_t8 = vtype::permutexvar(_mm512_set_epi32(NETWORK5),
- vtype::max(zmm[0], zmm7r));
- COEX<vtype>(zmm_t1, zmm_t3);
- COEX<vtype>(zmm_t2, zmm_t4);
- COEX<vtype>(zmm_t5, zmm_t7);
- COEX<vtype>(zmm_t6, zmm_t8);
- COEX<vtype>(zmm_t1, zmm_t2);
- COEX<vtype>(zmm_t3, zmm_t4);
- COEX<vtype>(zmm_t5, zmm_t6);
- COEX<vtype>(zmm_t7, zmm_t8);
- zmm[0] = bitonic_merge_zmm<vtype>(zmm_t1);
- zmm[1] = bitonic_merge_zmm<vtype>(zmm_t2);
- zmm[2] = bitonic_merge_zmm<vtype>(zmm_t3);
- zmm[3] = bitonic_merge_zmm<vtype>(zmm_t4);
- zmm[4] = bitonic_merge_zmm<vtype>(zmm_t5);
- zmm[5] = bitonic_merge_zmm<vtype>(zmm_t6);
- zmm[6] = bitonic_merge_zmm<vtype>(zmm_t7);
- zmm[7] = bitonic_merge_zmm<vtype>(zmm_t8);
-}
-
-template <typename vtype, typename type_t>
-static inline void
-sort_16(type_t *arr, npy_int N)
-{
- __mmask16 load_mask = (0x0001 << N) - 0x0001;
- typename vtype::zmm_t zmm =
- vtype::mask_loadu(vtype::zmm_max(), load_mask, arr);
- vtype::mask_storeu(arr, load_mask, sort_zmm<vtype>(zmm));
-}
-
-template <typename vtype, typename type_t>
-static inline void
-sort_32(type_t *arr, npy_int N)
-{
- if (N <= 16) {
- sort_16<vtype>(arr, N);
- return;
- }
- using zmm_t = typename vtype::zmm_t;
- zmm_t zmm1 = vtype::loadu(arr);
- __mmask16 load_mask = (0x0001 << (N - 16)) - 0x0001;
- zmm_t zmm2 = vtype::mask_loadu(vtype::zmm_max(), load_mask, arr + 16);
- zmm1 = sort_zmm<vtype>(zmm1);
- zmm2 = sort_zmm<vtype>(zmm2);
- bitonic_merge_two_zmm<vtype>(&zmm1, &zmm2);
- vtype::storeu(arr, zmm1);
- vtype::mask_storeu(arr + 16, load_mask, zmm2);
-}
-
-template <typename vtype, typename type_t>
-static inline void
-sort_64(type_t *arr, npy_int N)
-{
- if (N <= 32) {
- sort_32<vtype>(arr, N);
- return;
- }
- using zmm_t = typename vtype::zmm_t;
- zmm_t zmm[4];
- zmm[0] = vtype::loadu(arr);
- zmm[1] = vtype::loadu(arr + 16);
- __mmask16 load_mask1 = 0xFFFF, load_mask2 = 0xFFFF;
- if (N < 48) {
- load_mask1 = (0x0001 << (N - 32)) - 0x0001;
- load_mask2 = 0x0000;
- }
- else if (N < 64) {
- load_mask2 = (0x0001 << (N - 48)) - 0x0001;
- }
- zmm[2] = vtype::mask_loadu(vtype::zmm_max(), load_mask1, arr + 32);
- zmm[3] = vtype::mask_loadu(vtype::zmm_max(), load_mask2, arr + 48);
- zmm[0] = sort_zmm<vtype>(zmm[0]);
- zmm[1] = sort_zmm<vtype>(zmm[1]);
- zmm[2] = sort_zmm<vtype>(zmm[2]);
- zmm[3] = sort_zmm<vtype>(zmm[3]);
- bitonic_merge_two_zmm<vtype>(&zmm[0], &zmm[1]);
- bitonic_merge_two_zmm<vtype>(&zmm[2], &zmm[3]);
- bitonic_merge_four_zmm<vtype>(zmm);
- vtype::storeu(arr, zmm[0]);
- vtype::storeu(arr + 16, zmm[1]);
- vtype::mask_storeu(arr + 32, load_mask1, zmm[2]);
- vtype::mask_storeu(arr + 48, load_mask2, zmm[3]);
-}
-
-template <typename vtype, typename type_t>
-static inline void
-sort_128(type_t *arr, npy_int N)
-{
- if (N <= 64) {
- sort_64<vtype>(arr, N);
- return;
- }
- using zmm_t = typename vtype::zmm_t;
- zmm_t zmm[8];
- zmm[0] = vtype::loadu(arr);
- zmm[1] = vtype::loadu(arr + 16);
- zmm[2] = vtype::loadu(arr + 32);
- zmm[3] = vtype::loadu(arr + 48);
- zmm[0] = sort_zmm<vtype>(zmm[0]);
- zmm[1] = sort_zmm<vtype>(zmm[1]);
- zmm[2] = sort_zmm<vtype>(zmm[2]);
- zmm[3] = sort_zmm<vtype>(zmm[3]);
- __mmask16 load_mask1 = 0xFFFF, load_mask2 = 0xFFFF;
- __mmask16 load_mask3 = 0xFFFF, load_mask4 = 0xFFFF;
- if (N < 80) {
- load_mask1 = (0x0001 << (N - 64)) - 0x0001;
- load_mask2 = 0x0000;
- load_mask3 = 0x0000;
- load_mask4 = 0x0000;
- }
- else if (N < 96) {
- load_mask2 = (0x0001 << (N - 80)) - 0x0001;
- load_mask3 = 0x0000;
- load_mask4 = 0x0000;
- }
- else if (N < 112) {
- load_mask3 = (0x0001 << (N - 96)) - 0x0001;
- load_mask4 = 0x0000;
- }
- else {
- load_mask4 = (0x0001 << (N - 112)) - 0x0001;
- }
- zmm[4] = vtype::mask_loadu(vtype::zmm_max(), load_mask1, arr + 64);
- zmm[5] = vtype::mask_loadu(vtype::zmm_max(), load_mask2, arr + 80);
- zmm[6] = vtype::mask_loadu(vtype::zmm_max(), load_mask3, arr + 96);
- zmm[7] = vtype::mask_loadu(vtype::zmm_max(), load_mask4, arr + 112);
- zmm[4] = sort_zmm<vtype>(zmm[4]);
- zmm[5] = sort_zmm<vtype>(zmm[5]);
- zmm[6] = sort_zmm<vtype>(zmm[6]);
- zmm[7] = sort_zmm<vtype>(zmm[7]);
- bitonic_merge_two_zmm<vtype>(&zmm[0], &zmm[1]);
- bitonic_merge_two_zmm<vtype>(&zmm[2], &zmm[3]);
- bitonic_merge_two_zmm<vtype>(&zmm[4], &zmm[5]);
- bitonic_merge_two_zmm<vtype>(&zmm[6], &zmm[7]);
- bitonic_merge_four_zmm<vtype>(zmm);
- bitonic_merge_four_zmm<vtype>(zmm + 4);
- bitonic_merge_eight_zmm<vtype>(zmm);
- vtype::storeu(arr, zmm[0]);
- vtype::storeu(arr + 16, zmm[1]);
- vtype::storeu(arr + 32, zmm[2]);
- vtype::storeu(arr + 48, zmm[3]);
- vtype::mask_storeu(arr + 64, load_mask1, zmm[4]);
- vtype::mask_storeu(arr + 80, load_mask2, zmm[5]);
- vtype::mask_storeu(arr + 96, load_mask3, zmm[6]);
- vtype::mask_storeu(arr + 112, load_mask4, zmm[7]);
-}
-
-template <typename type_t>
-static inline void
-swap(type_t *arr, npy_intp ii, npy_intp jj)
-{
- type_t temp = arr[ii];
- arr[ii] = arr[jj];
- arr[jj] = temp;
-}
-
-// Median of 3 strategy
-// template<typename type_t>
-// static inline
-// npy_intp get_pivot_index(type_t *arr, const npy_intp left, const npy_intp
-// right) {
-// return (rand() % (right + 1 - left)) + left;
-// //npy_intp middle = ((right-left)/2) + left;
-// //type_t a = arr[left], b = arr[middle], c = arr[right];
-// //if ((b >= a && b <= c) || (b <= a && b >= c))
-// // return middle;
-// //if ((a >= b && a <= c) || (a <= b && a >= c))
-// // return left;
-// //else
-// // return right;
-//}
-
-/*
- * Picking the pivot: Median of 72 array elements chosen at random.
- */
-
-template <typename vtype, typename type_t>
-static inline type_t
-get_pivot(type_t *arr, const npy_intp left, const npy_intp right)
-{
- /* seeds for vectorized random number generator */
- __m256i s0 = _mm256_setr_epi64x(8265987198341093849, 3762817312854612374,
- 1324281658759788278, 6214952190349879213);
- __m256i s1 = _mm256_setr_epi64x(2874178529384792648, 1257248936691237653,
- 7874578921548791257, 1998265912745817298);
- s0 = _mm256_add_epi64(s0, _mm256_set1_epi64x(left));
- s1 = _mm256_sub_epi64(s1, _mm256_set1_epi64x(right));
-
- npy_intp arrsize = right - left + 1;
- __m256i bound =
- _mm256_set1_epi32(arrsize > INT32_MAX ? INT32_MAX : arrsize);
- __m512i left_vec = _mm512_set1_epi64(left);
- __m512i right_vec = _mm512_set1_epi64(right);
- using ymm_t = typename vtype::ymm_t;
- ymm_t v[9];
- /* fill 9 vectors with random numbers */
- for (npy_int i = 0; i < 9; ++i) {
- __m256i rand_64 = vnext(&s0, &s1); /* vector with 4 random uint64_t */
- __m512i rand_32 = _mm512_cvtepi32_epi64(rnd_epu32(
- rand_64, bound)); /* random numbers between 0 and bound - 1 */
- __m512i indices;
- if (i < 5)
- indices =
- _mm512_add_epi64(left_vec, rand_32); /* indices for arr */
- else
- indices =
- _mm512_sub_epi64(right_vec, rand_32); /* indices for arr */
-
- v[i] = vtype::template i64gather<sizeof(type_t)>(indices, arr);
- }
-
- /* median network for 9 elements */
- COEX<vtype>(v[0], v[1]);
- COEX<vtype>(v[2], v[3]);
- COEX<vtype>(v[4], v[5]);
- COEX<vtype>(v[6], v[7]);
- COEX<vtype>(v[0], v[2]);
- COEX<vtype>(v[1], v[3]);
- COEX<vtype>(v[4], v[6]);
- COEX<vtype>(v[5], v[7]);
- COEX<vtype>(v[0], v[4]);
- COEX<vtype>(v[1], v[2]);
- COEX<vtype>(v[5], v[6]);
- COEX<vtype>(v[3], v[7]);
- COEX<vtype>(v[1], v[5]);
- COEX<vtype>(v[2], v[6]);
- COEX<vtype>(v[3], v[5]);
- COEX<vtype>(v[2], v[4]);
- COEX<vtype>(v[3], v[4]);
- COEX<vtype>(v[3], v[8]);
- COEX<vtype>(v[4], v[8]);
-
- // technically v[4] needs to be sorted before we pick the correct median,
- // picking the 4th element works just as well for performance
- type_t *temp = (type_t *)&v[4];
-
- return temp[4];
-}
-
-/*
- * Partition one ZMM register based on the pivot and returns the index of the
- * last element that is less than equal to the pivot.
- */
-template <typename vtype, typename type_t, typename zmm_t>
-static inline npy_int
-partition_vec(type_t *arr, npy_intp left, npy_intp right, const zmm_t curr_vec,
- const zmm_t pivot_vec, zmm_t *smallest_vec, zmm_t *biggest_vec)
-{
- /* which elements are larger than the pivot */
- __mmask16 gt_mask = vtype::ge(curr_vec, pivot_vec);
- npy_int amount_gt_pivot = _mm_popcnt_u32((npy_int)gt_mask);
- vtype::mask_compressstoreu(arr + left, _mm512_knot(gt_mask), curr_vec);
- vtype::mask_compressstoreu(arr + right - amount_gt_pivot, gt_mask,
- curr_vec);
- *smallest_vec = vtype::min(curr_vec, *smallest_vec);
- *biggest_vec = vtype::max(curr_vec, *biggest_vec);
- return amount_gt_pivot;
-}
-
-/*
- * Partition an array based on the pivot and returns the index of the
- * last element that is less than equal to the pivot.
- */
-template <typename vtype, typename type_t>
-static inline npy_intp
-partition_avx512(type_t *arr, npy_intp left, npy_intp right, type_t pivot,
- type_t *smallest, type_t *biggest)
-{
- /* make array length divisible by 16 , shortening the array */
- for (npy_int i = (right - left) % 16; i > 0; --i) {
- *smallest = MIN(*smallest, arr[left]);
- *biggest = MAX(*biggest, arr[left]);
- if (arr[left] > pivot) {
- swap(arr, left, --right);
- }
- else {
- ++left;
- }
- }
-
- if (left == right)
- return left; /* less than 16 elements in the array */
-
- using zmm_t = typename vtype::zmm_t;
- zmm_t pivot_vec = vtype::set1(pivot);
- zmm_t min_vec = vtype::set1(*smallest);
- zmm_t max_vec = vtype::set1(*biggest);
-
- if (right - left == 16) {
- zmm_t vec = vtype::loadu(arr + left);
- npy_int amount_gt_pivot = partition_vec<vtype>(
- arr, left, left + 16, vec, pivot_vec, &min_vec, &max_vec);
- *smallest = vtype::reducemin(min_vec);
- *biggest = vtype::reducemax(max_vec);
- return left + (16 - amount_gt_pivot);
- }
-
- // first and last 16 values are partitioned at the end
- zmm_t vec_left = vtype::loadu(arr + left);
- zmm_t vec_right = vtype::loadu(arr + (right - 16));
- // store points of the vectors
- npy_intp r_store = right - 16;
- npy_intp l_store = left;
- // indices for loading the elements
- left += 16;
- right -= 16;
- while (right - left != 0) {
- zmm_t curr_vec;
- /*
- * if fewer elements are stored on the right side of the array,
- * then next elements are loaded from the right side,
- * otherwise from the left side
- */
- if ((r_store + 16) - right < left - l_store) {
- right -= 16;
- curr_vec = vtype::loadu(arr + right);
- }
- else {
- curr_vec = vtype::loadu(arr + left);
- left += 16;
- }
- // partition the current vector and save it on both sides of the array
- npy_int amount_gt_pivot =
- partition_vec<vtype>(arr, l_store, r_store + 16, curr_vec,
- pivot_vec, &min_vec, &max_vec);
- ;
- r_store -= amount_gt_pivot;
- l_store += (16 - amount_gt_pivot);
- }
-
- /* partition and save vec_left and vec_right */
- npy_int amount_gt_pivot =
- partition_vec<vtype>(arr, l_store, r_store + 16, vec_left,
- pivot_vec, &min_vec, &max_vec);
- l_store += (16 - amount_gt_pivot);
- amount_gt_pivot =
- partition_vec<vtype>(arr, l_store, l_store + 16, vec_right,
- pivot_vec, &min_vec, &max_vec);
- l_store += (16 - amount_gt_pivot);
- *smallest = vtype::reducemin(min_vec);
- *biggest = vtype::reducemax(max_vec);
- return l_store;
-}
-
-template <typename vtype, typename type_t>
-static inline void
-qsort_(type_t *arr, npy_intp left, npy_intp right, npy_int max_iters)
-{
- /*
- * Resort to heapsort if quicksort isn't making any progress
- */
- if (max_iters <= 0) {
- heapsort_<typename vtype::tag>(arr + left, right + 1 - left);
- return;
- }
- /*
- * Base case: use bitonic networks to sort arrays <= 128
- */
- if (right + 1 - left <= 128) {
- sort_128<vtype>(arr + left, (npy_int)(right + 1 - left));
- return;
- }
-
- type_t pivot = get_pivot<vtype>(arr, left, right);
- type_t smallest = vtype::type_max();
- type_t biggest = vtype::type_min();
- npy_intp pivot_index = partition_avx512<vtype>(arr, left, right + 1, pivot,
- &smallest, &biggest);
- if (pivot != smallest)
- qsort_<vtype>(arr, left, pivot_index - 1, max_iters - 1);
- if (pivot != biggest)
- qsort_<vtype>(arr, pivot_index, right, max_iters - 1);
-}
-
-static inline npy_intp
-replace_nan_with_inf(npy_float *arr, npy_intp arrsize)
-{
- npy_intp nan_count = 0;
- __mmask16 loadmask = 0xFFFF;
- while (arrsize > 0) {
- if (arrsize < 16) {
- loadmask = (0x0001 << arrsize) - 0x0001;
- }
- __m512 in_zmm = _mm512_maskz_loadu_ps(loadmask, arr);
- __mmask16 nanmask = _mm512_cmp_ps_mask(in_zmm, in_zmm, _CMP_NEQ_UQ);
- nan_count += _mm_popcnt_u32((npy_int)nanmask);
- _mm512_mask_storeu_ps(arr, nanmask, ZMM_MAX_FLOAT);
- arr += 16;
- arrsize -= 16;
- }
- return nan_count;
-}
-
-static inline void
-replace_inf_with_nan(npy_float *arr, npy_intp arrsize, npy_intp nan_count)
-{
- for (npy_intp ii = arrsize - 1; nan_count > 0; --ii) {
- arr[ii] = NPY_NANF;
- nan_count -= 1;
- }
-}
-
-/***************************************
- * C > C++ dispatch
- ***************************************/
-
-NPY_NO_EXPORT void
-NPY_CPU_DISPATCH_CURFX(x86_quicksort_int)(void *arr, npy_intp arrsize)
-{
- if (arrsize > 1) {
- qsort_<vector<npy_int>, npy_int>((npy_int *)arr, 0, arrsize - 1,
- 2 * (npy_int)log2(arrsize));
- }
-}
-
-NPY_NO_EXPORT void
-NPY_CPU_DISPATCH_CURFX(x86_quicksort_uint)(void *arr, npy_intp arrsize)
-{
- if (arrsize > 1) {
- qsort_<vector<npy_uint>, npy_uint>((npy_uint *)arr, 0, arrsize - 1,
- 2 * (npy_int)log2(arrsize));
- }
-}
-
-NPY_NO_EXPORT void
-NPY_CPU_DISPATCH_CURFX(x86_quicksort_float)(void *arr, npy_intp arrsize)
-{
- if (arrsize > 1) {
- npy_intp nan_count = replace_nan_with_inf((npy_float *)arr, arrsize);
- qsort_<vector<npy_float>, npy_float>((npy_float *)arr, 0, arrsize - 1,
- 2 * (npy_int)log2(arrsize));
- replace_inf_with_nan((npy_float *)arr, arrsize, nan_count);
- }
-}
-
-#endif // NPY_HAVE_AVX512_SKX
diff --git a/numpy/core/src/npysort/x86-qsort.h b/numpy/core/src/npysort/x86-qsort.h
deleted file mode 100644
index 6340e2bc7..000000000
--- a/numpy/core/src/npysort/x86-qsort.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#include "numpy/npy_common.h"
-
-#include "npy_cpu_dispatch.h"
-
-#ifndef NPY_NO_EXPORT
-#define NPY_NO_EXPORT NPY_VISIBILITY_HIDDEN
-#endif
-
-#ifndef NPY_DISABLE_OPTIMIZATION
-#include "x86-qsort.dispatch.h"
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void x86_quicksort_int,
- (void *start, npy_intp num))
-
-NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void x86_quicksort_uint,
- (void *start, npy_intp num))
-
-NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void x86_quicksort_float,
- (void *start, npy_intp num))
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/numpy/core/src/npysort/x86-simd-sort b/numpy/core/src/npysort/x86-simd-sort
new file mode 160000
+Subproject 7d7591cf5927e83e4a1e7c4b6f2c4dc91a97889
diff --git a/numpy/core/src/umath/_scaled_float_dtype.c b/numpy/core/src/umath/_scaled_float_dtype.c
index 9bcac8114..c26ace9f1 100644
--- a/numpy/core/src/umath/_scaled_float_dtype.c
+++ b/numpy/core/src/umath/_scaled_float_dtype.c
@@ -248,7 +248,7 @@ static PyArray_DTypeMeta PyArray_SFloatDType = {{{
}},
.type_num = -1,
.scalar_type = NULL,
- .flags = NPY_DT_PARAMETRIC,
+ .flags = NPY_DT_PARAMETRIC | NPY_DT_NUMERIC,
.dt_slots = &sfloat_slots,
};
diff --git a/numpy/core/src/umath/_umath_tests.c.src b/numpy/core/src/umath/_umath_tests.c.src
index 624dcefda..b427991e5 100644
--- a/numpy/core/src/umath/_umath_tests.c.src
+++ b/numpy/core/src/umath/_umath_tests.c.src
@@ -24,7 +24,7 @@
#include "numpy/npy_cpu.h"
#include "npy_import.h"
#include "numpy/experimental_dtype_api.h"
-#include "dtypemeta.h"
+
/*
*****************************************************************************
@@ -723,7 +723,7 @@ err:
static int
add_INT32_negative_indexed(PyObject *module, PyObject *dict) {
- if (import_experimental_dtype_api(__EXPERIMENTAL_DTYPE_VERSION) < 0) {
+ if (import_experimental_dtype_api(__EXPERIMENTAL_DTYPE_API_VERSION) < 0) {
return -1;
}
@@ -732,8 +732,7 @@ add_INT32_negative_indexed(PyObject *module, PyObject *dict) {
if (negative == NULL) {
return -1;
}
- PyArray_DTypeMeta *dtype = PyArray_DTypeFromTypeNum(NPY_INT32);
- PyArray_DTypeMeta *dtypes[] = {dtype, dtype};
+ PyArray_DTypeMeta *dtypes[] = {&PyArray_Int32DType, &PyArray_Int32DType};
PyType_Slot slots[] = {
{NPY_METH_contiguous_indexed_loop, INT32_negative_indexed},
diff --git a/numpy/core/src/umath/fast_loop_macros.h b/numpy/core/src/umath/fast_loop_macros.h
index cade26db4..b8c1926b2 100644
--- a/numpy/core/src/umath/fast_loop_macros.h
+++ b/numpy/core/src/umath/fast_loop_macros.h
@@ -12,6 +12,19 @@
#include <assert.h>
+#include "simd/simd.h"
+
+/*
+ * largest simd vector size in bytes numpy supports
+ * it is currently a extremely large value as it is only used for memory
+ * overlap checks
+ */
+#if NPY_SIMD > 0
+ // Enough for compiler unroll
+ #define AUTOVEC_OVERLAP_SIZE NPY_SIMD_WIDTH*4
+#else
+ #define AUTOVEC_OVERLAP_SIZE 1024
+#endif
/*
* MAX_STEP_SIZE is used to determine if we need to use SIMD version of the ufunc.
* Very large step size can be as slow as processing it using scalar. The
@@ -219,11 +232,11 @@ abs_ptrdiff(char *a, char *b)
/* condition allows compiler to optimize the generic macro */ \
if (IS_BINARY_CONT(tin, tout)) { \
if (abs_ptrdiff(args[2], args[0]) == 0 && \
- abs_ptrdiff(args[2], args[1]) >= NPY_MAX_SIMD_SIZE) { \
+ abs_ptrdiff(args[2], args[1]) >= AUTOVEC_OVERLAP_SIZE) { \
BASE_BINARY_LOOP_INP(tin, tout, op) \
} \
else if (abs_ptrdiff(args[2], args[1]) == 0 && \
- abs_ptrdiff(args[2], args[0]) >= NPY_MAX_SIMD_SIZE) { \
+ abs_ptrdiff(args[2], args[0]) >= AUTOVEC_OVERLAP_SIZE) { \
BASE_BINARY_LOOP_INP(tin, tout, op) \
} \
else { \
diff --git a/numpy/core/src/umath/legacy_array_method.c b/numpy/core/src/umath/legacy_array_method.c
index dd56e13a1..965a0eb83 100644
--- a/numpy/core/src/umath/legacy_array_method.c
+++ b/numpy/core/src/umath/legacy_array_method.c
@@ -398,7 +398,7 @@ PyArray_NewLegacyWrappingArrayMethod(PyUFuncObject *ufunc,
}
PyType_Slot slots[4] = {
- {NPY_METH_get_loop, &get_wrapped_legacy_ufunc_loop},
+ {_NPY_METH_get_loop, &get_wrapped_legacy_ufunc_loop},
{NPY_METH_resolve_descriptors, &simple_legacy_resolve_descriptors},
{NPY_METH_get_reduction_initial, get_reduction_intial},
{0, NULL},
diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src
index 5684f26a5..397ebaca2 100644
--- a/numpy/core/src/umath/loops.c.src
+++ b/numpy/core/src/umath/loops.c.src
@@ -32,16 +32,6 @@
*/
#define PW_BLOCKSIZE 128
-
-/*
- * largest simd vector size in bytes numpy supports
- * it is currently a extremely large value as it is only used for memory
- * overlap checks
- */
-#ifndef NPY_MAX_SIMD_SIZE
-#define NPY_MAX_SIMD_SIZE 1024
-#endif
-
/** Provides the various *_LOOP macros */
#include "fast_loop_macros.h"
@@ -417,24 +407,6 @@ BOOL__ones_like(char **args, npy_intp const *dimensions, npy_intp const *steps,
}
}
-
-/**begin repeat
- * #kind = isnan, isinf, isfinite#
- * #func = npy_isnan, npy_isinf, npy_isfinite#
- * #val = NPY_FALSE, NPY_FALSE, NPY_TRUE#
- **/
-NPY_NO_EXPORT void
-BOOL_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
-{
- /*
- * The (void)in; suppresses an unused variable warning raised by gcc and allows
- * us to re-use this macro even though we do not depend on in
- */
- UNARY_LOOP_FAST(npy_bool, npy_bool, (void)in; *out = @val@);
-}
-
-/**end repeat**/
-
/*
*****************************************************************************
** INTEGER LOOPS
@@ -466,82 +438,16 @@ NPY_NO_EXPORT void
*((@type@ *)op1) = 1;
}
}
-
-NPY_NO_EXPORT void
-@TYPE@_positive(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
-{
- UNARY_LOOP_FAST(@type@, @type@, *out = +in);
-}
-
/**begin repeat1
- * #isa = , _avx2#
- * #CHK = 1, defined(HAVE_ATTRIBUTE_TARGET_AVX2)#
- * #ATTR = , NPY_GCC_TARGET_AVX2#
- */
-
-#if @CHK@
-NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void
-@TYPE@_square@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data))
-{
- UNARY_LOOP_FAST(@type@, @type@, *out = in * in);
-}
-#endif
-
-#if @CHK@
-NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void
-@TYPE@_reciprocal@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data))
-{
- UNARY_LOOP_FAST(@type@, @type@, *out = 1.0 / in);
-}
-#endif
-
-#if @CHK@
-NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void
-@TYPE@_conjugate@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
-{
- UNARY_LOOP_FAST(@type@, @type@, *out = in);
-}
-#endif
-
-#if @CHK@
-NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void
-@TYPE@_logical_not@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
-{
- UNARY_LOOP_FAST(@type@, npy_bool, *out = !in);
-}
-#endif
-
-#if @CHK@
-NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void
-@TYPE@_invert@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
-{
- UNARY_LOOP_FAST(@type@, @type@, *out = ~in);
-}
-#endif
-
-/**begin repeat2
* Arithmetic
* #kind = add, subtract, multiply, bitwise_and, bitwise_or, bitwise_xor#
* #OP = +, -, *, &, |, ^#
*/
-#if @CHK@
-NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void
-@TYPE@_@kind@@isa@(char **args, npy_intp const *dimensions,
- npy_intp const *steps, void *NPY_UNUSED(func))
-{
- if (IS_BINARY_REDUCE) {
- BINARY_REDUCE_LOOP_FAST(@type@, io1 @OP@= in2);
- }
- else {
- BINARY_LOOP_FAST(@type@, @type@, *out = in1 @OP@ in2);
- }
-}
-
-NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ int
-@TYPE@_@kind@@isa@_indexed(PyArrayMethod_Context *NPY_UNUSED(context),
- char **args, npy_intp const *dimensions, npy_intp const *steps,
- void *NPY_UNUSED(func))
+NPY_NO_EXPORT NPY_GCC_OPT_3 int
+@TYPE@_@kind@_indexed(PyArrayMethod_Context *NPY_UNUSED(context),
+ char **args, npy_intp const *dimensions, npy_intp const *steps,
+ void *NPY_UNUSED(func))
{
char *ip1 = args[0];
char *indx = args[1];
@@ -556,86 +462,6 @@ NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ int
}
return 0;
}
-
-#endif
-
-/**end repeat2**/
-
-/*
- * Arithmetic bit shift operations.
- *
- * Intel hardware masks bit shift values, so large shifts wrap around
- * and can produce surprising results. The special handling ensures that
- * behavior is independent of compiler or hardware.
- * TODO: We could implement consistent behavior for negative shifts,
- * which is undefined in C.
- */
-
-#define INT_left_shift_needs_clear_floatstatus
-#define UINT_left_shift_needs_clear_floatstatus
-
-#if @CHK@
-NPY_NO_EXPORT NPY_GCC_OPT_3 void
-@TYPE@_left_shift@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps,
- void *NPY_UNUSED(func))
-{
- BINARY_LOOP_FAST(@type@, @type@, *out = npy_lshift@c@(in1, in2));
-
-#ifdef @TYPE@_left_shift_needs_clear_floatstatus
- // For some reason, our macOS CI sets an "invalid" flag here, but only
- // for some types.
- npy_clear_floatstatus_barrier((char*)dimensions);
-#endif
-}
-#endif
-
-#undef INT_left_shift_needs_clear_floatstatus
-#undef UINT_left_shift_needs_clear_floatstatus
-
-#if @CHK@
-NPY_NO_EXPORT
-#ifndef NPY_DO_NOT_OPTIMIZE_@TYPE@_right_shift
-NPY_GCC_OPT_3
-#endif
-void
-@TYPE@_right_shift@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps,
- void *NPY_UNUSED(func))
-{
- BINARY_LOOP_FAST(@type@, @type@, *out = npy_rshift@c@(in1, in2));
-}
-#endif
-
-/**begin repeat2
- * #kind = logical_and, logical_or#
- * #OP = &&, ||#
- */
-
-#if @CHK@
-NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void
-@TYPE@_@kind@@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
-{
- /*
- * gcc vectorization of this is not good (PR60575) but manual integer
- * vectorization is too tedious to be worthwhile
- */
- BINARY_LOOP_FAST(@type@, npy_bool, *out = in1 @OP@ in2);
-}
-#endif
-
-/**end repeat2**/
-
-#if @CHK@
-NPY_NO_EXPORT NPY_GCC_OPT_3 @ATTR@ void
-@TYPE@_logical_xor@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
-{
- BINARY_LOOP {
- const int t1 = !!*(@type@ *)ip1;
- const int t2 = !!*(@type@ *)ip2;
- *((npy_bool *)op1) = (t1 != t2);
- }
-}
-#endif
-
/**end repeat1**/
NPY_NO_EXPORT void
@@ -677,23 +503,6 @@ NPY_NO_EXPORT void
*((@type@ *) op1) = out;
}
}
-
-/**begin repeat1
- * #kind = isnan, isinf, isfinite#
- * #func = npy_isnan, npy_isinf, npy_isfinite#
- * #val = NPY_FALSE, NPY_FALSE, NPY_TRUE#
- **/
-NPY_NO_EXPORT void
-@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
-{
- /*
- * The (void)in; suppresses an unused variable warning raised by gcc and allows
- * us to re-use this macro even though we do not depend on in
- */
- UNARY_LOOP_FAST(@type@, npy_bool, (void)in; *out = @val@);
-}
-/**end repeat1**/
-
/**end repeat**/
/**begin repeat
@@ -701,19 +510,6 @@ NPY_NO_EXPORT void
* #type = npy_byte, npy_short, npy_int, npy_long, npy_longlong#
* #c = ,,,l,ll#
*/
-
-NPY_NO_EXPORT NPY_GCC_OPT_3 void
-@TYPE@_absolute(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
-{
- UNARY_LOOP_FAST(@type@, @type@, *out = (in >= 0) ? in : -in);
-}
-
-NPY_NO_EXPORT NPY_GCC_OPT_3 void
-@TYPE@_sign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
-{
- UNARY_LOOP_FAST(@type@, @type@, *out = in > 0 ? 1 : (in < 0 ? -1 : 0));
-}
-
/**begin repeat1
* #kind = gcd, lcm#
**/
@@ -727,7 +523,6 @@ NPY_NO_EXPORT void
}
}
/**end repeat1**/
-
/**end repeat**/
/**begin repeat
@@ -735,19 +530,6 @@ NPY_NO_EXPORT void
* #type = npy_ubyte, npy_ushort, npy_uint, npy_ulong, npy_ulonglong#
* #c = u,u,u,ul,ull#
*/
-
-NPY_NO_EXPORT NPY_GCC_OPT_3 void
-@TYPE@_absolute(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
-{
- UNARY_LOOP_FAST(@type@, @type@, *out = in);
-}
-
-NPY_NO_EXPORT NPY_GCC_OPT_3 void
-@TYPE@_sign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
-{
- UNARY_LOOP_FAST(@type@, @type@, *out = in > 0 ? 1 : 0);
-}
-
/**begin repeat1
* #kind = gcd, lcm#
**/
@@ -761,7 +543,6 @@ NPY_NO_EXPORT void
}
}
/**end repeat1**/
-
/**end repeat**/
/*
@@ -840,12 +621,6 @@ NPY_NO_EXPORT void
}
NPY_NO_EXPORT void
-@TYPE@_isinf(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
-{
- UNARY_LOOP_FAST(npy_bool, npy_bool, (void)in; *out = NPY_FALSE);
-}
-
-NPY_NO_EXPORT void
@TYPE@__ones_like(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data))
{
OUTPUT_LOOP {
@@ -1714,7 +1489,7 @@ HALF_@kind@_indexed(void *NPY_UNUSED(context),
const float v = npy_half_to_float(*(npy_half *)value);
*indexed = npy_float_to_half(npy_half_to_float(*indexed) @OP@ v);
}
- return 0;
+ return 0;
}
/**end repeat**/
@@ -1974,12 +1749,6 @@ HALF_conjugate(char **args, npy_intp const *dimensions, npy_intp const *steps, v
}
}
-NPY_NO_EXPORT NPY_GCC_OPT_3 void
-HALF_absolute(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
-{
- UNARY_LOOP_FAST(npy_half, npy_half, *out = in&0x7fffu);
-}
-
NPY_NO_EXPORT void
HALF_negative(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
{
diff --git a/numpy/core/src/umath/loops.h.src b/numpy/core/src/umath/loops.h.src
index e393b8310..e36067822 100644
--- a/numpy/core/src/umath/loops.h.src
+++ b/numpy/core/src/umath/loops.h.src
@@ -68,6 +68,17 @@ NPY_NO_EXPORT void
BOOL_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
/**end repeat**/
+
+#ifndef NPY_DISABLE_OPTIMIZATION
+ #include "loops_autovec.dispatch.h"
+#endif
+/**begin repeat
+ * #kind = isnan, isinf, isfinite#
+ */
+NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void BOOL_@kind@,
+ (char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)))
+/**end repeat**/
+
/*
*****************************************************************************
** INTEGER LOOPS
@@ -123,16 +134,34 @@ NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void @TYPE@_@kind@,
/**end repeat1**/
/**end repeat**/
+
+#ifndef NPY_DISABLE_OPTIMIZATION
+ #include "loops_autovec.dispatch.h"
+#endif
/**begin repeat
- * #TYPE = BYTE, SHORT, INT, LONG, LONGLONG#
+ * #TYPE = UBYTE, USHORT, UINT, ULONG, ULONGLONG,
+ BYTE, SHORT, INT, LONG, LONGLONG#
+ */
+/**begin repeat1
+ * #kind = invert, logical_not, conjugate, reciprocal, square, add,
+ * subtract, multiply, bitwise_and, bitwise_or, bitwise_xor,
+ * left_shift, right_shift, logical_and, logical_or,
+ * logical_xor, isnan, isinf, isfinite,
+ * absolute, sign#
*/
+NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void @TYPE@_@kind@,
+ (char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)))
+/**end repeat1**/
+/**end repeat**/
+/**begin repeat
+ * #TYPE = BYTE, SHORT, INT, LONG, LONGLONG#
+ */
/**begin repeat1
* both signed and unsigned integer types
* #s = , u#
* #S = , U#
*/
-
#define @S@@TYPE@_floor_divide @S@@TYPE@_divide
#define @S@@TYPE@_floor_divide_indexed @S@@TYPE@_divide_indexed
#define @S@@TYPE@_fmax @S@@TYPE@_maximum
@@ -147,49 +176,15 @@ NPY_NO_EXPORT void
@S@@TYPE@_positive(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
/**begin repeat2
- * #isa = , _avx2#
- */
-
-NPY_NO_EXPORT void
-@S@@TYPE@_square@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data));
-
-NPY_NO_EXPORT void
-@S@@TYPE@_reciprocal@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data));
-
-NPY_NO_EXPORT void
-@S@@TYPE@_conjugate@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
-
-NPY_NO_EXPORT void
-@S@@TYPE@_logical_not@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
-
-NPY_NO_EXPORT void
-@S@@TYPE@_invert@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
-
-/**begin repeat3
* Arithmetic
* #kind = add, subtract, multiply, bitwise_and, bitwise_or, bitwise_xor,
* left_shift, right_shift#
* #OP = +, -,*, &, |, ^, <<, >>#
*/
-NPY_NO_EXPORT void
-@S@@TYPE@_@kind@@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
-
NPY_NO_EXPORT int
-@S@@TYPE@_@kind@@isa@_indexed(PyArrayMethod_Context *NPY_UNUSED(context), char *const *args, npy_intp const *dimensions, npy_intp const *steps, NpyAuxData *NPY_UNUSED(func));
-
-/**end repeat3**/
-
-/**begin repeat3
- * #kind = logical_and, logical_or#
- * #OP = &&, ||#
- */
-NPY_NO_EXPORT void
-@S@@TYPE@_@kind@@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
-
-/**end repeat3**/
+@S@@TYPE@_@kind@_indexed(PyArrayMethod_Context *NPY_UNUSED(context), char *const *args,
+ npy_intp const *dimensions, npy_intp const *steps, NpyAuxData *NPY_UNUSED(func));
-NPY_NO_EXPORT void
-@S@@TYPE@_logical_xor@isa@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
/**end repeat2**/
/**begin repeat2
@@ -207,26 +202,13 @@ NPY_NO_EXPORT void
@S@@TYPE@_power(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
-@S@@TYPE@_absolute(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
-
-NPY_NO_EXPORT void
-@S@@TYPE@_sign(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
-
-NPY_NO_EXPORT void
@S@@TYPE@_gcd(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
NPY_NO_EXPORT void
@S@@TYPE@_lcm(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
-
-/**begin repeat2
- * #kind = isnan, isinf, isfinite#
- **/
-NPY_NO_EXPORT void
-@S@@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
/**end repeat2**/
/**end repeat1**/
-
/**end repeat**/
@@ -375,18 +357,6 @@ NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void @TYPE@_@func@,
/**end repeat1**/
/**end repeat**/
-/**begin repeat
- * #TYPE = FLOAT, DOUBLE#
- */
-/**begin repeat1
- * #func = maximum, minimum#
- */
-NPY_NO_EXPORT void
-@TYPE@_@func@_avx512f(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
-
-/**end repeat1**/
-/**end repeat**/
-
#ifndef NPY_DISABLE_OPTIMIZATION
#include "loops_trigonometric.dispatch.h"
#endif
@@ -562,6 +532,19 @@ NPY_NO_EXPORT void
/**end repeat1**/
/**end repeat**/
+#ifndef NPY_DISABLE_OPTIMIZATION
+ #include "loops_autovec.dispatch.h"
+#endif
+/**begin repeat
+ * #TYPE = HALF#
+ */
+/**begin repeat1
+ * #kind = absolute#
+ */
+NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void @TYPE@_@kind@,
+ (char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)))
+/**end repeat1**/
+/**end repeat**/
/*
*****************************************************************************
** COMPLEX LOOPS **
@@ -728,9 +711,6 @@ NPY_NO_EXPORT void
NPY_NO_EXPORT void
@TYPE@_isfinite(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
-NPY_NO_EXPORT void
-@TYPE@_isinf(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func));
-
#define @TYPE@_isnan @TYPE@_isnat
NPY_NO_EXPORT void
@@ -806,6 +786,21 @@ TIMEDELTA_mm_qm_divmod(char **args, npy_intp const *dimensions, npy_intp const *
#define TIMEDELTA_md_m_floor_divide TIMEDELTA_md_m_divide
/* #define TIMEDELTA_mm_d_floor_divide TIMEDELTA_mm_d_divide */
+
+#ifndef NPY_DISABLE_OPTIMIZATION
+ #include "loops_autovec.dispatch.h"
+#endif
+/**begin repeat
+ * #TYPE = TIMEDELTA, DATETIME#
+ */
+/**begin repeat1
+ * #kind = isinf#
+ */
+NPY_CPU_DISPATCH_DECLARE(NPY_NO_EXPORT void @TYPE@_@kind@,
+ (char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func)))
+/**end repeat1**/
+/**end repeat**/
+
/*
*****************************************************************************
** OBJECT LOOPS **
diff --git a/numpy/core/src/umath/loops_autovec.dispatch.c.src b/numpy/core/src/umath/loops_autovec.dispatch.c.src
new file mode 100644
index 000000000..bdbfa0f86
--- /dev/null
+++ b/numpy/core/src/umath/loops_autovec.dispatch.c.src
@@ -0,0 +1,287 @@
+/*@targets
+ ** $maxopt $autovec baseline
+ ** sse2 avx2
+ ** neon
+ ** vsx2
+ ** vx
+ **/
+#define _UMATHMODULE
+#define _MULTIARRAYMODULE
+#define NPY_NO_DEPRECATED_API NPY_API_VERSION
+
+#include "simd/simd.h"
+#include "loops_utils.h"
+#include "loops.h"
+// Provides the various *_LOOP macros
+#include "fast_loop_macros.h"
+
+/*
+ *****************************************************************************
+ ** INTEGER LOOPS
+ *****************************************************************************
+ */
+/*
+ * Arithmetic bit shift operations.
+ *
+ * Intel hardware masks bit shift values, so large shifts wrap around
+ * and can produce surprising results. The special handling ensures that
+ * behavior is independent of compiler or hardware.
+ * TODO: We could implement consistent behavior for negative shifts,
+ * which is undefined in C.
+ */
+#define INT_left_shift_needs_clear_floatstatus
+#define UINT_left_shift_needs_clear_floatstatus
+
+/**begin repeat
+ * #TYPE = BYTE, UBYTE, SHORT, USHORT, INT, UINT,
+ * 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#
+ * #ftype = npy_float, npy_float, npy_float, npy_float, npy_double, npy_double,
+ * npy_double, npy_double, npy_double, npy_double#
+ * #SIGNED = 1, 0, 1, 0, 1, 0, 1, 0, 1, 0#
+ * #c = hh,uhh,h,uh,,u,l,ul,ll,ull#
+ */
+
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_positive)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
+{
+ UNARY_LOOP_FAST(@type@, @type@, *out = +in);
+}
+
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_square)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data))
+{
+ UNARY_LOOP_FAST(@type@, @type@, *out = in * in);
+}
+
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_reciprocal)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(data))
+{
+ UNARY_LOOP_FAST(@type@, @type@, *out = 1.0 / in);
+}
+
+/**begin repeat1
+ * Arithmetic
+ * #kind = add, subtract, multiply#
+ * #OP = +, -, *#
+ */
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_@kind@)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
+{
+ if (IS_BINARY_REDUCE) {
+ BINARY_REDUCE_LOOP_FAST(@type@, io1 @OP@= in2);
+ }
+ else {
+ BINARY_LOOP_FAST(@type@, @type@, *out = in1 @OP@ in2);
+ }
+}
+/**end repeat1**/
+
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_left_shift)
+(char **args, npy_intp const *dimensions, npy_intp const *steps,
+ void *NPY_UNUSED(func))
+{
+ BINARY_LOOP_FAST(@type@, @type@, *out = npy_lshift@c@(in1, in2));
+#ifdef @TYPE@_left_shift_needs_clear_floatstatus
+ // For some reason, our macOS CI sets an "invalid" flag here, but only
+ // for some types.
+ npy_clear_floatstatus_barrier((char*)dimensions);
+#endif
+}
+
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_right_shift)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
+{
+#ifndef NPY_DO_NOT_OPTIMIZE_@TYPE@_right_shift
+ BINARY_LOOP_FAST(@type@, @type@, *out = npy_rshift@c@(in1, in2));
+#else
+ BINARY_LOOP {
+ @type@ in1 = *(@type@ *)ip1;
+ @type@ in2 = *(@type@ *)ip2;
+ *(@type@ *)op1 = npy_rshift@c@(in1, in2);
+ }
+#endif
+}
+/**end repeat**/
+
+/*
+ *****************************************************************************
+ ** UNSIGNED INTEGER LOOPS
+ *****************************************************************************
+ */
+/**begin repeat
+ * #TYPE = UBYTE, USHORT, UINT, ULONG, ULONGLONG#
+ * #type = npy_ubyte, npy_ushort, npy_uint, npy_ulong, npy_ulonglong#
+ * #c = u,u,u,ul,ull#
+ */
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_absolute)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
+{
+ UNARY_LOOP_FAST(@type@, @type@, *out = in);
+}
+
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_sign)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
+{
+ UNARY_LOOP_FAST(@type@, @type@, *out = in > 0 ? 1 : 0);
+}
+
+/**begin repeat1
+ * Arithmetic
+ * #kind = bitwise_and, bitwise_or, bitwise_xor#
+ * #OP = &, |, ^#
+ */
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_@kind@)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
+{
+ if (IS_BINARY_REDUCE) {
+ BINARY_REDUCE_LOOP_FAST(@type@, io1 @OP@= in2);
+ }
+ else {
+ BINARY_LOOP_FAST(@type@, @type@, *out = in1 @OP@ in2);
+ }
+}
+/**end repeat1**/
+
+/**begin repeat1
+ * #kind = logical_and, logical_or#
+ * #OP = &&, ||#
+ */
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_@kind@)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
+{
+ /*
+ * gcc vectorization of this is not good (PR60575) but manual integer
+ * vectorization is too tedious to be worthwhile
+ */
+ BINARY_LOOP_FAST(@type@, npy_bool, *out = in1 @OP@ in2);
+}
+/**end repeat1**/
+
+NPY_FINLINE npy_bool @TYPE@_logical_xor_(@type@ in1, @type@ in2)
+{ return (!!in1) != (!!in2); }
+
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_logical_xor)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
+{
+ BINARY_LOOP_FAST(@type@, npy_bool, *out = @TYPE@_logical_xor_(in1, in2));
+}
+
+/**begin repeat1
+ * #kind = isnan, isinf, isfinite#
+ * #func = npy_isnan, npy_isinf, npy_isfinite#
+ * #val = NPY_FALSE, NPY_FALSE, NPY_TRUE#
+ **/
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_@kind@)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
+{
+ /*
+ * The (void)in; suppresses an unused variable warning raised by gcc and allows
+ * us to re-use this macro even though we do not depend on in
+ */
+ UNARY_LOOP_FAST(@type@, npy_bool, (void)in; *out = @val@);
+}
+/**end repeat1**/
+
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_conjugate)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
+{
+ UNARY_LOOP_FAST(@type@, @type@, *out = in);
+}
+
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_logical_not)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
+{
+ UNARY_LOOP_FAST(@type@, npy_bool, *out = !in);
+}
+
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_invert)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
+{
+ UNARY_LOOP_FAST(@type@, @type@, *out = ~in);
+}
+/**end repeat**/
+
+/*
+ *****************************************************************************
+ ** SIGNED! INTEGER LOOPS
+ *****************************************************************************
+ */
+
+/**begin repeat
+ * #TYPE = BYTE, SHORT, INT, LONG, LONGLONG#
+ * #type = npy_byte, npy_short, npy_int, npy_long, npy_longlong#
+ * #c = ,,,l,ll#
+ */
+
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_absolute)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
+{
+ UNARY_LOOP_FAST(@type@, @type@, *out = (in >= 0) ? in : -in);
+}
+
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_sign)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
+{
+ UNARY_LOOP_FAST(@type@, @type@, *out = in > 0 ? 1 : (in < 0 ? -1 : 0));
+}
+
+/**begin repeat1
+ * #kind = conjugate, invert, isnan, isinf, isfinite,
+ * logical_and, logical_or, logical_xor, logical_not,
+ * bitwise_and, bitwise_or, bitwise_xor#
+ **/
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_@kind@)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)
+{
+ NPY_CPU_DISPATCH_CURFX(U@TYPE@_@kind@)(args, dimensions, steps, func);
+}
+/**end repeat1**/
+/**end repeat**/
+
+/*
+ *****************************************************************************
+ ** BOOLEAN LOOPS **
+ *****************************************************************************
+ */
+/**begin repeat
+ * #kind = isnan, isinf, isfinite#
+ * #func = npy_isnan, npy_isinf, npy_isfinite#
+ * #val = NPY_FALSE, NPY_FALSE, NPY_TRUE#
+ **/
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(BOOL_@kind@)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)
+{
+ NPY_CPU_DISPATCH_CURFX(UBYTE_@kind@)(args, dimensions, steps, func);
+}
+/**end repeat**/
+
+/*
+ *****************************************************************************
+ ** HALF-FLOAT LOOPS **
+ *****************************************************************************
+ */
+
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(HALF_absolute)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
+{
+ UNARY_LOOP_FAST(npy_half, npy_half, *out = in&0x7fffu);
+}
+
+/*
+ *****************************************************************************
+ ** DATETIME LOOPS **
+ *****************************************************************************
+ */
+
+/**begin repeat
+ * #type = npy_datetime, npy_timedelta#
+ * #TYPE = DATETIME, TIMEDELTA#
+ */
+NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_isinf)
+(char **args, npy_intp const *dimensions, npy_intp const *steps, void *func)
+{
+ NPY_CPU_DISPATCH_CURFX(ULONGLONG_isinf)(args, dimensions, steps, func);
+}
+/**end repeat**/
diff --git a/numpy/core/src/umath/loops_exponent_log.dispatch.c.src b/numpy/core/src/umath/loops_exponent_log.dispatch.c.src
index 182c57b01..1fac3c150 100644
--- a/numpy/core/src/umath/loops_exponent_log.dispatch.c.src
+++ b/numpy/core/src/umath/loops_exponent_log.dispatch.c.src
@@ -239,7 +239,7 @@ fma_scalef_ps(__m256 poly, __m256 quadrant)
#ifdef SIMD_AVX512F
-NPY_FINLINE NPY_GCC_OPT_3 NPY_GCC_TARGET_AVX512F __mmask16
+NPY_FINLINE __mmask16
avx512_get_full_load_mask_ps(void)
{
return 0xFFFF;
diff --git a/numpy/core/src/umath/wrapping_array_method.c b/numpy/core/src/umath/wrapping_array_method.c
index c32b5c714..64fea7aeb 100644
--- a/numpy/core/src/umath/wrapping_array_method.c
+++ b/numpy/core/src/umath/wrapping_array_method.c
@@ -268,14 +268,15 @@ PyUFunc_AddWrappingLoop(PyObject *ufunc_obj,
break;
}
if (wrapped_meth == NULL) {
- PyErr_SetString(PyExc_TypeError,
- "Did not find the to-be-wrapped loop in the ufunc.");
+ PyErr_Format(PyExc_TypeError,
+ "Did not find the to-be-wrapped loop in the ufunc with given "
+ "DTypes. Received wrapping types: %S", wrapped_dt_tuple);
goto finish;
}
PyType_Slot slots[] = {
{NPY_METH_resolve_descriptors, &wrapping_method_resolve_descriptors},
- {NPY_METH_get_loop, &wrapping_method_get_loop},
+ {_NPY_METH_get_loop, &wrapping_method_get_loop},
{NPY_METH_get_reduction_initial,
&wrapping_method_get_identity_function},
{0, NULL}
diff --git a/numpy/core/tests/test_array_coercion.py b/numpy/core/tests/test_array_coercion.py
index fa3468179..0ba736c05 100644
--- a/numpy/core/tests/test_array_coercion.py
+++ b/numpy/core/tests/test_array_coercion.py
@@ -163,6 +163,8 @@ class TestStringDiscovery:
assert np.array(arr, dtype="S").dtype == expected
# Check that .astype() behaves identical
assert arr.astype("S").dtype == expected
+ # The DType class is accepted by `.astype()`
+ assert arr.astype(type(np.dtype("S"))).dtype == expected
@pytest.mark.parametrize("obj",
[object(), 1.2, 10**43, None, "string"],
diff --git a/numpy/core/tests/test_cpu_features.py b/numpy/core/tests/test_cpu_features.py
index 1a76897e2..8c1c25ed4 100644
--- a/numpy/core/tests/test_cpu_features.py
+++ b/numpy/core/tests/test_cpu_features.py
@@ -116,7 +116,7 @@ class Test_X86_Features(AbstractTest):
"AVX", "F16C", "XOP", "FMA4", "FMA3", "AVX2", "AVX512F", "AVX512CD",
"AVX512ER", "AVX512PF", "AVX5124FMAPS", "AVX5124VNNIW", "AVX512VPOPCNTDQ",
"AVX512VL", "AVX512BW", "AVX512DQ", "AVX512VNNI", "AVX512IFMA",
- "AVX512VBMI", "AVX512VBMI2", "AVX512BITALG",
+ "AVX512VBMI", "AVX512VBMI2", "AVX512BITALG", "AVX512FP16",
]
features_groups = dict(
AVX512_KNL = ["AVX512F", "AVX512CD", "AVX512ER", "AVX512PF"],
@@ -128,6 +128,10 @@ class Test_X86_Features(AbstractTest):
"AVX512VBMI"],
AVX512_ICL = ["AVX512F", "AVX512CD", "AVX512BW", "AVX512DQ", "AVX512VL", "AVX512IFMA",
"AVX512VBMI", "AVX512VNNI", "AVX512VBMI2", "AVX512BITALG", "AVX512VPOPCNTDQ"],
+ AVX512_SPR = ["AVX512F", "AVX512CD", "AVX512BW", "AVX512DQ",
+ "AVX512VL", "AVX512IFMA", "AVX512VBMI", "AVX512VNNI",
+ "AVX512VBMI2", "AVX512BITALG", "AVX512VPOPCNTDQ",
+ "AVX512FP16"],
)
features_map = dict(
SSE3="PNI", SSE41="SSE4_1", SSE42="SSE4_2", FMA3="FMA",
diff --git a/numpy/core/tests/test_custom_dtypes.py b/numpy/core/tests/test_custom_dtypes.py
index 5d01fc49d..1a34c6fa3 100644
--- a/numpy/core/tests/test_custom_dtypes.py
+++ b/numpy/core/tests/test_custom_dtypes.py
@@ -219,6 +219,16 @@ class TestSFloat:
expected = np.hypot.reduce(float_equiv, keepdims=True)
assert res.view(np.float64) * 2 == expected
+ def test_astype_class(self):
+ # Very simple test that we accept `.astype()` also on the class.
+ # ScaledFloat always returns the default descriptor, but it does
+ # check the relevant code paths.
+ arr = np.array([1., 2., 3.], dtype=object)
+
+ res = arr.astype(SF) # passing the class class
+ expected = arr.astype(SF(1.)) # above will have discovered 1. scaling
+ assert_array_equal(res.view(np.float64), expected.view(np.float64))
+
def test_type_pickle():
# can't actually unpickle, but we can pickle (if in namespace)
@@ -231,3 +241,7 @@ def test_type_pickle():
assert res is SF
del np._ScaledFloatTestDType
+
+
+def test_is_numeric():
+ assert SF._is_numeric
diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py
index ac4761b50..96ae4b2a8 100644
--- a/numpy/core/tests/test_deprecations.py
+++ b/numpy/core/tests/test_deprecations.py
@@ -901,3 +901,24 @@ class TestDeprecatedFinfo(_DeprecationTestCase):
# Deprecated in NumPy 1.25, 2023-01-16
def test_deprecated_none(self):
self.assert_deprecated(np.finfo, args=(None,))
+
+class TestFromnumeric(_DeprecationTestCase):
+ # 2023-02-28, 1.25.0
+ def test_round_(self):
+ self.assert_deprecated(lambda: np.round_(np.array([1.5, 2.5, 3.5])))
+
+ # 2023-03-02, 1.25.0
+ def test_cumproduct(self):
+ self.assert_deprecated(lambda: np.cumproduct(np.array([1, 2, 3])))
+
+ # 2023-03-02, 1.25.0
+ def test_product(self):
+ self.assert_deprecated(lambda: np.product(np.array([1, 2, 3])))
+
+ # 2023-03-02, 1.25.0
+ def test_sometrue(self):
+ self.assert_deprecated(lambda: np.sometrue(np.array([True, False])))
+
+ # 2023-03-02, 1.25.0
+ def test_alltrue(self):
+ self.assert_deprecated(lambda: np.alltrue(np.array([True, False])))
diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py
index a20b4ecc2..9b44367e6 100644
--- a/numpy/core/tests/test_dtype.py
+++ b/numpy/core/tests/test_dtype.py
@@ -551,6 +551,14 @@ class TestRecord:
assert_equal(np.zeros((1, 2), dtype=[]) == a,
np.ones((1, 2), dtype=bool))
+ def test_nonstructured_with_object(self):
+ # See gh-23277, the dtype here thinks it contain objects, if the
+ # assert about that fails, the test becomes meaningless (which is OK)
+ arr = np.recarray((0,), dtype="O")
+ assert arr.dtype.names is None # no fields
+ assert arr.dtype.hasobject # but claims to contain objects
+ del arr # the deletion failed previously.
+
class TestSubarray:
def test_single_subarray(self):
@@ -1579,6 +1587,18 @@ class TestDTypeClasses:
assert type(np.dtype).__module__ == "numpy"
assert np.dtype._abstract
+ def test_is_numeric(self):
+ all_codes = set(np.typecodes['All'])
+ numeric_codes = set(np.typecodes['AllInteger'] +
+ np.typecodes['AllFloat'] + '?')
+ non_numeric_codes = all_codes - numeric_codes
+
+ for code in numeric_codes:
+ assert type(np.dtype(code))._is_numeric
+
+ for code in non_numeric_codes:
+ assert not type(np.dtype(code))._is_numeric
+
class TestFromCTypes:
diff --git a/numpy/core/tests/test_getlimits.py b/numpy/core/tests/test_getlimits.py
index b8aaba386..63217c38c 100644
--- a/numpy/core/tests/test_getlimits.py
+++ b/numpy/core/tests/test_getlimits.py
@@ -3,6 +3,7 @@
"""
import warnings
import numpy as np
+import pytest
from numpy.core import finfo, iinfo
from numpy import half, single, double, longdouble
from numpy.testing import assert_equal, assert_, assert_raises
@@ -40,20 +41,38 @@ class TestLongdouble:
ftype2 = finfo(longdouble)
assert_equal(id(ftype), id(ftype2))
+def assert_finfo_equal(f1, f2):
+ # assert two finfo instances have the same attributes
+ for attr in ('bits', 'eps', 'epsneg', 'iexp', 'machep',
+ 'max', 'maxexp', 'min', 'minexp', 'negep', 'nexp',
+ 'nmant', 'precision', 'resolution', 'tiny',
+ 'smallest_normal', 'smallest_subnormal'):
+ assert_equal(getattr(f1, attr), getattr(f2, attr),
+ f'finfo instances {f1} and {f2} differ on {attr}')
+
+def assert_iinfo_equal(i1, i2):
+ # assert two iinfo instances have the same attributes
+ for attr in ('bits', 'min', 'max'):
+ assert_equal(getattr(i1, attr), getattr(i2, attr),
+ f'iinfo instances {i1} and {i2} differ on {attr}')
+
class TestFinfo:
def test_basic(self):
dts = list(zip(['f2', 'f4', 'f8', 'c8', 'c16'],
[np.float16, np.float32, np.float64, np.complex64,
np.complex128]))
for dt1, dt2 in dts:
- for attr in ('bits', 'eps', 'epsneg', 'iexp', 'machep',
- 'max', 'maxexp', 'min', 'minexp', 'negep', 'nexp',
- 'nmant', 'precision', 'resolution', 'tiny',
- 'smallest_normal', 'smallest_subnormal'):
- assert_equal(getattr(finfo(dt1), attr),
- getattr(finfo(dt2), attr), attr)
+ assert_finfo_equal(finfo(dt1), finfo(dt2))
+
assert_raises(ValueError, finfo, 'i4')
+ def test_regression_gh23108(self):
+ # np.float32(1.0) and np.float64(1.0) have the same hash and are
+ # equal under the == operator
+ f1 = np.finfo(np.float32(1.0))
+ f2 = np.finfo(np.float64(1.0))
+ assert f1 != f2
+
class TestIinfo:
def test_basic(self):
dts = list(zip(['i1', 'i2', 'i4', 'i8',
@@ -61,9 +80,8 @@ class TestIinfo:
[np.int8, np.int16, np.int32, np.int64,
np.uint8, np.uint16, np.uint32, np.uint64]))
for dt1, dt2 in dts:
- for attr in ('bits', 'min', 'max'):
- assert_equal(getattr(iinfo(dt1), attr),
- getattr(iinfo(dt2), attr), attr)
+ assert_iinfo_equal(iinfo(dt1), iinfo(dt2))
+
assert_raises(ValueError, iinfo, 'f4')
def test_unsigned_max(self):
@@ -85,8 +103,28 @@ class TestRepr:
def test_instances():
- iinfo(10)
- finfo(3.0)
+ # Test the finfo and iinfo results on numeric instances agree with
+ # the results on the corresponding types
+
+ for c in [int, np.int16, np.int32, np.int64]:
+ class_iinfo = iinfo(c)
+ instance_iinfo = iinfo(c(12))
+
+ assert_iinfo_equal(class_iinfo, instance_iinfo)
+
+ for c in [float, np.float16, np.float32, np.float64]:
+ class_finfo = finfo(c)
+ instance_finfo = finfo(c(1.2))
+ assert_finfo_equal(class_finfo, instance_finfo)
+
+ with pytest.raises(ValueError):
+ iinfo(10.)
+
+ with pytest.raises(ValueError):
+ iinfo('hi')
+
+ with pytest.raises(ValueError):
+ finfo(np.int64(1))
def assert_ma_equal(discovered, ma_like):
diff --git a/numpy/core/tests/test_indexing.py b/numpy/core/tests/test_indexing.py
index 74075639c..042936702 100644
--- a/numpy/core/tests/test_indexing.py
+++ b/numpy/core/tests/test_indexing.py
@@ -1062,7 +1062,7 @@ class TestMultiIndexingAutomated:
if np.any(_indx >= _size) or np.any(_indx < -_size):
raise IndexError
if len(indx[1:]) == len(orig_slice):
- if np.product(orig_slice) == 0:
+ if np.prod(orig_slice) == 0:
# Work around for a crash or IndexError with 'wrap'
# in some 0-sized cases.
try:
diff --git a/numpy/core/tests/test_item_selection.py b/numpy/core/tests/test_item_selection.py
index 3c35245a3..5660ef583 100644
--- a/numpy/core/tests/test_item_selection.py
+++ b/numpy/core/tests/test_item_selection.py
@@ -1,5 +1,7 @@
import sys
+import pytest
+
import numpy as np
from numpy.testing import (
assert_, assert_raises, assert_array_equal, HAS_REFCOUNT
@@ -84,3 +86,80 @@ class TestTake:
b = np.array([0, 1, 2, 3, 4, 5])
assert_array_equal(a, b)
+
+
+class TestPutMask:
+ @pytest.mark.parametrize("dtype", list(np.typecodes["All"]) + ["i,O"])
+ def test_simple(self, dtype):
+ if dtype.lower() == "m":
+ dtype += "8[ns]"
+
+ # putmask is weird and doesn't care about value length (even shorter)
+ vals = np.arange(1001).astype(dtype=dtype)
+
+ mask = np.random.randint(2, size=1000).astype(bool)
+ # Use vals.dtype in case of flexible dtype (i.e. string)
+ arr = np.zeros(1000, dtype=vals.dtype)
+ zeros = arr.copy()
+
+ np.putmask(arr, mask, vals)
+ assert_array_equal(arr[mask], vals[:len(mask)][mask])
+ assert_array_equal(arr[~mask], zeros[~mask])
+
+ @pytest.mark.parametrize("dtype", list(np.typecodes["All"])[1:] + ["i,O"])
+ @pytest.mark.parametrize("mode", ["raise", "wrap", "clip"])
+ def test_empty(self, dtype, mode):
+ arr = np.zeros(1000, dtype=dtype)
+ arr_copy = arr.copy()
+ mask = np.random.randint(2, size=1000).astype(bool)
+
+ # Allowing empty values like this is weird...
+ np.put(arr, mask, [])
+ assert_array_equal(arr, arr_copy)
+
+
+class TestPut:
+ @pytest.mark.parametrize("dtype", list(np.typecodes["All"])[1:] + ["i,O"])
+ @pytest.mark.parametrize("mode", ["raise", "wrap", "clip"])
+ def test_simple(self, dtype, mode):
+ if dtype.lower() == "m":
+ dtype += "8[ns]"
+
+ # put is weird and doesn't care about value length (even shorter)
+ vals = np.arange(1001).astype(dtype=dtype)
+
+ # Use vals.dtype in case of flexible dtype (i.e. string)
+ arr = np.zeros(1000, dtype=vals.dtype)
+ zeros = arr.copy()
+
+ if mode == "clip":
+ # Special because 0 and -1 value are "reserved" for clip test
+ indx = np.random.permutation(len(arr) - 2)[:-500] + 1
+
+ indx[-1] = 0
+ indx[-2] = len(arr) - 1
+ indx_put = indx.copy()
+ indx_put[-1] = -1389
+ indx_put[-2] = 1321
+ else:
+ # Avoid duplicates (for simplicity) and fill half only
+ indx = np.random.permutation(len(arr) - 3)[:-500]
+ indx_put = indx
+ if mode == "wrap":
+ indx_put = indx_put + len(arr)
+
+ np.put(arr, indx_put, vals, mode=mode)
+ assert_array_equal(arr[indx], vals[:len(indx)])
+ untouched = np.ones(len(arr), dtype=bool)
+ untouched[indx] = False
+ assert_array_equal(arr[untouched], zeros[:untouched.sum()])
+
+ @pytest.mark.parametrize("dtype", list(np.typecodes["All"])[1:] + ["i,O"])
+ @pytest.mark.parametrize("mode", ["raise", "wrap", "clip"])
+ def test_empty(self, dtype, mode):
+ arr = np.zeros(1000, dtype=dtype)
+ arr_copy = arr.copy()
+
+ # Allowing empty values like this is weird...
+ np.put(arr, [1, 2, 3], [])
+ assert_array_equal(arr, arr_copy)
diff --git a/numpy/core/tests/test_mem_overlap.py b/numpy/core/tests/test_mem_overlap.py
index d66decfda..1fd4c4d41 100644
--- a/numpy/core/tests/test_mem_overlap.py
+++ b/numpy/core/tests/test_mem_overlap.py
@@ -55,7 +55,7 @@ def _indices(ndims):
def _check_assignment(srcidx, dstidx):
"""Check assignment arr[dstidx] = arr[srcidx] works."""
- arr = np.arange(np.product(shape)).reshape(shape)
+ arr = np.arange(np.prod(shape)).reshape(shape)
cpy = arr.copy()
diff --git a/numpy/core/tests/test_memmap.py b/numpy/core/tests/test_memmap.py
index 914f86f14..ad074b312 100644
--- a/numpy/core/tests/test_memmap.py
+++ b/numpy/core/tests/test_memmap.py
@@ -6,7 +6,7 @@ from pathlib import Path
from tempfile import NamedTemporaryFile, TemporaryFile
from numpy import (
- memmap, sum, average, product, ndarray, isscalar, add, subtract, multiply)
+ memmap, sum, average, prod, ndarray, isscalar, add, subtract, multiply)
from numpy import arange, allclose, asarray
from numpy.testing import (
@@ -153,7 +153,7 @@ class TestMemmap:
with suppress_warnings() as sup:
sup.filter(FutureWarning, "np.average currently does not preserve")
- for unary_op in [sum, average, product]:
+ for unary_op in [sum, average, prod]:
result = unary_op(fp)
assert_(isscalar(result))
assert_(result.__class__ is self.data[0, 0].__class__)
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index 1911bd1d2..ac4bd42d3 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -2116,19 +2116,26 @@ class TestMethods:
c.sort(kind=kind)
assert_equal(c, a, msg)
- def test_sort_structured(self):
+ @pytest.mark.parametrize("dt", [
+ np.dtype([('f', float), ('i', int)]),
+ np.dtype([('f', float), ('i', object)])])
+ @pytest.mark.parametrize("step", [1, 2])
+ def test_sort_structured(self, dt, step):
# test record array sorts.
- dt = np.dtype([('f', float), ('i', int)])
- a = np.array([(i, i) for i in range(101)], dtype=dt)
+ a = np.array([(i, i) for i in range(101*step)], dtype=dt)
b = a[::-1]
for kind in ['q', 'h', 'm']:
msg = "kind=%s" % kind
- c = a.copy()
+ c = a.copy()[::step]
+ indx = c.argsort(kind=kind)
c.sort(kind=kind)
- assert_equal(c, a, msg)
- c = b.copy()
+ assert_equal(c, a[::step], msg)
+ assert_equal(a[::step][indx], a[::step], msg)
+ c = b.copy()[::step]
+ indx = c.argsort(kind=kind)
c.sort(kind=kind)
- assert_equal(c, a, msg)
+ assert_equal(c, a[step-1::step], msg)
+ assert_equal(b[::step][indx], a[step-1::step], msg)
@pytest.mark.parametrize('dtype', ['datetime64[D]', 'timedelta64[D]'])
def test_sort_time(self, dtype):
@@ -5552,33 +5559,6 @@ class TestIO:
tmp_filename,
dtype='<f4')
- @pytest.mark.slow # takes > 1 minute on mechanical hard drive
- def test_big_binary(self):
- """Test workarounds for 32-bit limit for MSVC fwrite, fseek, and ftell
-
- These normally would hang doing something like this.
- See : https://github.com/numpy/numpy/issues/2256
- """
- if sys.platform != 'win32' or '[GCC ' in sys.version:
- return
- try:
- # before workarounds, only up to 2**32-1 worked
- fourgbplus = 2**32 + 2**16
- testbytes = np.arange(8, dtype=np.int8)
- n = len(testbytes)
- flike = tempfile.NamedTemporaryFile()
- f = flike.file
- np.tile(testbytes, fourgbplus // testbytes.nbytes).tofile(f)
- flike.seek(0)
- a = np.fromfile(f, dtype=np.int8)
- flike.close()
- assert_(len(a) == fourgbplus)
- # check only start and end for speed:
- assert_((a[:n] == testbytes).all())
- assert_((a[-n:] == testbytes).all())
- except (MemoryError, ValueError):
- pass
-
def test_string(self, tmp_filename):
self._check_from(b'1,2,3,4', [1., 2., 3., 4.], tmp_filename, sep=',')
@@ -9859,39 +9839,48 @@ class TestViewDtype:
@pytest.mark.xfail(_SUPPORTS_SVE, reason="gh-22982")
# Test various array sizes that hit different code paths in quicksort-avx512
-@pytest.mark.parametrize("N", [8, 16, 24, 32, 48, 64, 96, 128, 151, 191,
- 256, 383, 512, 1023, 2047])
-def test_sort_float(N):
+@pytest.mark.parametrize("N", np.arange(1, 512))
+@pytest.mark.parametrize("dtype", ['e', 'f', 'd'])
+def test_sort_float(N, dtype):
# Regular data with nan sprinkled
np.random.seed(42)
- arr = -0.5 + np.random.sample(N).astype('f')
+ arr = -0.5 + np.random.sample(N).astype(dtype)
arr[np.random.choice(arr.shape[0], 3)] = np.nan
assert_equal(np.sort(arr, kind='quick'), np.sort(arr, kind='heap'))
# (2) with +INF
- infarr = np.inf*np.ones(N, dtype='f')
+ infarr = np.inf*np.ones(N, dtype=dtype)
infarr[np.random.choice(infarr.shape[0], 5)] = -1.0
assert_equal(np.sort(infarr, kind='quick'), np.sort(infarr, kind='heap'))
# (3) with -INF
- neginfarr = -np.inf*np.ones(N, dtype='f')
+ neginfarr = -np.inf*np.ones(N, dtype=dtype)
neginfarr[np.random.choice(neginfarr.shape[0], 5)] = 1.0
assert_equal(np.sort(neginfarr, kind='quick'),
np.sort(neginfarr, kind='heap'))
# (4) with +/-INF
- infarr = np.inf*np.ones(N, dtype='f')
+ infarr = np.inf*np.ones(N, dtype=dtype)
infarr[np.random.choice(infarr.shape[0], (int)(N/2))] = -np.inf
assert_equal(np.sort(infarr, kind='quick'), np.sort(infarr, kind='heap'))
-
-def test_sort_int():
- # Random data with NPY_MAX_INT32 and NPY_MIN_INT32 sprinkled
- rng = np.random.default_rng(42)
- N = 2047
- minv = np.iinfo(np.int32).min
- maxv = np.iinfo(np.int32).max
- arr = rng.integers(low=minv, high=maxv, size=N).astype('int32')
+def test_sort_float16():
+ arr = np.arange(65536, dtype=np.int16)
+ temp = np.frombuffer(arr.tobytes(), dtype=np.float16)
+ data = np.copy(temp)
+ np.random.shuffle(data)
+ data_backup = data
+ assert_equal(np.sort(data, kind='quick'),
+ np.sort(data_backup, kind='heap'))
+
+
+@pytest.mark.parametrize("N", np.arange(1, 512))
+@pytest.mark.parametrize("dtype", ['h', 'H', 'i', 'I', 'l', 'L'])
+def test_sort_int(N, dtype):
+ # Random data with MAX and MIN sprinkled
+ minv = np.iinfo(dtype).min
+ maxv = np.iinfo(dtype).max
+ arr = np.random.randint(low=minv, high=maxv-1, size=N, dtype=dtype)
arr[np.random.choice(arr.shape[0], 10)] = minv
arr[np.random.choice(arr.shape[0], 10)] = maxv
assert_equal(np.sort(arr, kind='quick'), np.sort(arr, kind='heap'))
diff --git a/numpy/core/tests/test_nditer.py b/numpy/core/tests/test_nditer.py
index b4dd864f0..9f639c4c1 100644
--- a/numpy/core/tests/test_nditer.py
+++ b/numpy/core/tests/test_nditer.py
@@ -1457,6 +1457,38 @@ def test_iter_copy_casts_structured():
assert_array_equal(res2["b"][field], expected)
+def test_iter_copy_casts_structured2():
+ # Similar to the above, this is a fairly arcane test to cover internals
+ in_dtype = np.dtype([("a", np.dtype("O,O")),
+ ("b", np.dtype("(5)O,(3)O,(1,)O,(1,)i,(1,)O"))])
+ out_dtype = np.dtype([("a", np.dtype("O")),
+ ("b", np.dtype("O,(3)i,(4)O,(4)O,(4)i"))])
+
+ arr = np.ones(1, dtype=in_dtype)
+ it = np.nditer((arr,), ["buffered", "external_loop", "refs_ok"],
+ op_dtypes=[out_dtype], casting="unsafe")
+ it_copy = it.copy()
+
+ res1 = next(it)
+ del it
+ res2 = next(it_copy)
+ del it_copy
+
+ # Array of two structured scalars:
+ for res in res1, res2:
+ # Cast to tuple by getitem, which may be weird and changable?:
+ assert type(res["a"][0]) == tuple
+ assert res["a"][0] == (1, 1)
+
+ for res in res1, res2:
+ assert_array_equal(res["b"]["f0"][0], np.ones(5, dtype=object))
+ assert_array_equal(res["b"]["f1"], np.ones((1, 3), dtype="i"))
+ assert res["b"]["f2"].shape == (1, 4)
+ assert_array_equal(res["b"]["f2"][0], np.ones(4, dtype=object))
+ assert_array_equal(res["b"]["f3"][0], np.ones(4, dtype=object))
+ assert_array_equal(res["b"]["f3"][0], np.ones(4, dtype="i"))
+
+
def test_iter_allocate_output_simple():
# Check that the iterator will properly allocate outputs
diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py
index 3cc168b34..f81f563cd 100644
--- a/numpy/core/tests/test_numeric.py
+++ b/numpy/core/tests/test_numeric.py
@@ -114,7 +114,9 @@ class TestNonarrayArgs:
def test_cumproduct(self):
A = [[1, 2, 3], [4, 5, 6]]
- assert_(np.all(np.cumproduct(A) == np.array([1, 2, 6, 24, 120, 720])))
+ with assert_warns(DeprecationWarning):
+ expected = np.array([1, 2, 6, 24, 120, 720])
+ assert_(np.all(np.cumproduct(A) == expected))
def test_diagonal(self):
a = [[0, 1, 2, 3],
@@ -1193,8 +1195,8 @@ class TestFromiter:
expected = np.array(list(self.makegen()))
a = np.fromiter(self.makegen(), int)
a20 = np.fromiter(self.makegen(), int, 20)
- assert_(np.alltrue(a == expected, axis=0))
- assert_(np.alltrue(a20 == expected[:20], axis=0))
+ assert_(np.all(a == expected, axis=0))
+ assert_(np.all(a20 == expected[:20], axis=0))
def load_data(self, n, eindex):
# Utility method for the issue 2592 tests.
diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py
index 413ece045..fdd536bb9 100644
--- a/numpy/core/tests/test_regression.py
+++ b/numpy/core/tests/test_regression.py
@@ -516,22 +516,15 @@ class TestRegression:
def test_method_args(self):
# Make sure methods and functions have same default axis
# keyword and arguments
- funcs1 = ['argmax', 'argmin', 'sum', ('product', 'prod'),
- ('sometrue', 'any'),
- ('alltrue', 'all'), 'cumsum', ('cumproduct', 'cumprod'),
- 'ptp', 'cumprod', 'prod', 'std', 'var', 'mean',
- 'round', 'min', 'max', 'argsort', 'sort']
+ funcs1 = ['argmax', 'argmin', 'sum', 'any', 'all', 'cumsum',
+ 'ptp', 'cumprod', 'prod', 'std', 'var', 'mean',
+ 'round', 'min', 'max', 'argsort', 'sort']
funcs2 = ['compress', 'take', 'repeat']
for func in funcs1:
arr = np.random.rand(8, 7)
arr2 = arr.copy()
- if isinstance(func, tuple):
- func_meth = func[1]
- func = func[0]
- else:
- func_meth = func
- res1 = getattr(arr, func_meth)()
+ res1 = getattr(arr, func)()
res2 = getattr(np, func)(arr2)
if res1 is None:
res1 = arr
@@ -1336,8 +1329,8 @@ class TestRegression:
# Ticket #1058
a = np.fromiter(list(range(10)), dtype='b')
b = np.fromiter(list(range(10)), dtype='B')
- assert_(np.alltrue(a == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])))
- assert_(np.alltrue(b == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])))
+ assert_(np.all(a == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])))
+ assert_(np.all(b == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])))
def test_array_from_sequence_scalar_array(self):
# Ticket #1078: segfaults when creating an array with a sequence of
@@ -1515,8 +1508,8 @@ class TestRegression:
def test_fromiter_comparison(self):
a = np.fromiter(list(range(10)), dtype='b')
b = np.fromiter(list(range(10)), dtype='B')
- assert_(np.alltrue(a == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])))
- assert_(np.alltrue(b == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])))
+ assert_(np.all(a == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])))
+ assert_(np.all(b == np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])))
def test_fromstring_crash(self):
# Ticket #1345: the following should not cause a crash
diff --git a/numpy/ctypeslib.pyi b/numpy/ctypeslib.pyi
index 0313cd82a..3edf98e14 100644
--- a/numpy/ctypeslib.pyi
+++ b/numpy/ctypeslib.pyi
@@ -91,10 +91,10 @@ class _ndptr(ctypes.c_void_p, Generic[_DTypeOptional]):
@overload
@classmethod
- def from_param(cls: type[_ndptr[None]], obj: ndarray[Any, Any]) -> _ctypes: ...
+ def from_param(cls: type[_ndptr[None]], obj: ndarray[Any, Any]) -> _ctypes[Any]: ...
@overload
@classmethod
- def from_param(cls: type[_ndptr[_DType]], obj: ndarray[Any, _DType]) -> _ctypes: ...
+ def from_param(cls: type[_ndptr[_DType]], obj: ndarray[Any, _DType]) -> _ctypes[Any]: ...
class _concrete_ndptr(_ndptr[_DType]):
_dtype_: ClassVar[_DType]
diff --git a/numpy/distutils/ccompiler_opt.py b/numpy/distutils/ccompiler_opt.py
index da60205ec..4904dd3dd 100644
--- a/numpy/distutils/ccompiler_opt.py
+++ b/numpy/distutils/ccompiler_opt.py
@@ -300,6 +300,10 @@ class _Config:
group="AVX512VBMI2 AVX512BITALG AVX512VPOPCNTDQ",
detect="AVX512_ICL", implies_detect=False
),
+ AVX512_SPR = dict(
+ interest=46, implies="AVX512_ICL", group="AVX512FP16",
+ detect="AVX512_SPR", implies_detect=False
+ ),
# IBM/Power
## Power7/ISA 2.06
VSX = dict(interest=1, headers="altivec.h", extra_checks="VSX_ASM"),
@@ -370,7 +374,8 @@ class _Config:
AVX512_CNL = dict(flags="-mavx512ifma -mavx512vbmi"),
AVX512_ICL = dict(
flags="-mavx512vbmi2 -mavx512bitalg -mavx512vpopcntdq"
- )
+ ),
+ AVX512_SPR = dict(flags="-mavx512fp16"),
)
if on_x86 and self.cc_is_icc: return dict(
SSE = dict(flags="-msse"),
@@ -402,6 +407,7 @@ class _Config:
AVX512_CLX = dict(flags="-xCASCADELAKE"),
AVX512_CNL = dict(flags="-xCANNONLAKE"),
AVX512_ICL = dict(flags="-xICELAKE-CLIENT"),
+ AVX512_SPR = dict(disable="Not supported yet")
)
if on_x86 and self.cc_is_iccw: return dict(
SSE = dict(flags="/arch:SSE"),
@@ -434,7 +440,8 @@ class _Config:
AVX512_SKX = dict(flags="/Qx:SKYLAKE-AVX512"),
AVX512_CLX = dict(flags="/Qx:CASCADELAKE"),
AVX512_CNL = dict(flags="/Qx:CANNONLAKE"),
- AVX512_ICL = dict(flags="/Qx:ICELAKE-CLIENT")
+ AVX512_ICL = dict(flags="/Qx:ICELAKE-CLIENT"),
+ AVX512_SPR = dict(disable="Not supported yet")
)
if on_x86 and self.cc_is_msvc: return dict(
SSE = dict(flags="/arch:SSE") if self.cc_on_x86 else {},
@@ -472,7 +479,10 @@ class _Config:
AVX512_SKX = dict(flags="/arch:AVX512"),
AVX512_CLX = {},
AVX512_CNL = {},
- AVX512_ICL = {}
+ AVX512_ICL = {},
+ AVX512_SPR= dict(
+ disable="MSVC compiler doesn't support it"
+ )
)
on_power = self.cc_on_ppc64le or self.cc_on_ppc64
diff --git a/numpy/distutils/checks/cpu_avx512_spr.c b/numpy/distutils/checks/cpu_avx512_spr.c
new file mode 100644
index 000000000..3c9575a57
--- /dev/null
+++ b/numpy/distutils/checks/cpu_avx512_spr.c
@@ -0,0 +1,22 @@
+#if defined(DETECT_FEATURES) && defined(__INTEL_COMPILER)
+ /*
+ * Unlike GCC and CLANG, Intel Compiler exposes all supported intrinsics,
+ * whether or not the build options for those features are specified.
+ * Therefore, we must test #definitions of CPU features when option native/host
+ * is enabled via `--cpu-baseline` or through env var `CFLAGS` otherwise
+ * the test will be broken and leads to enable all possible features.
+ */
+ #if !defined(__AVX512FP16__)
+ #error "HOST/ARCH doesn't support Sapphire Rapids AVX512FP16 features"
+ #endif
+#endif
+
+#include <immintrin.h>
+
+int main(int argc, char **argv)
+{
+ __m512h a = _mm512_loadu_ph((void*)argv[argc-1]);
+ __m512h temp = _mm512_fmadd_ph(a, a, a);
+ _mm512_storeu_ph((void*)(argv[argc-1]), temp);
+ return 0;
+}
diff --git a/numpy/fft/_pocketfft.c b/numpy/fft/_pocketfft.c
index 86de57bd3..37649386f 100644
--- a/numpy/fft/_pocketfft.c
+++ b/numpy/fft/_pocketfft.c
@@ -23,7 +23,7 @@
#include <stdlib.h>
#define RALLOC(type,num) \
- ((type *)malloc((num)*sizeof(type)))
+ (assert(num != 0), ((type *)malloc((num)*sizeof(type))))
#define DEALLOC(ptr) \
do { free(ptr); (ptr)=NULL; } while(0)
@@ -1056,8 +1056,10 @@ static cfftp_plan make_cfftp_plan (size_t length)
if (length==1) return plan;
if (cfftp_factorize(plan)!=0) { DEALLOC(plan); return NULL; }
size_t tws=cfftp_twsize(plan);
- plan->mem=RALLOC(cmplx,tws);
- if (!plan->mem) { DEALLOC(plan); return NULL; }
+ if (tws != 0) {
+ plan->mem=RALLOC(cmplx,tws);
+ if (!plan->mem) { DEALLOC(plan); return NULL; }
+ }
if (cfftp_comp_twiddle(plan)!=0)
{ DEALLOC(plan->mem); DEALLOC(plan); return NULL; }
return plan;
@@ -1820,7 +1822,6 @@ static size_t rfftp_twsize(rfftp_plan plan)
l1*=ip;
}
return twsize;
- return 0;
}
WARN_UNUSED_RESULT NOINLINE static int rfftp_comp_twiddle (rfftp_plan plan)
@@ -1876,8 +1877,10 @@ NOINLINE static rfftp_plan make_rfftp_plan (size_t length)
if (length==1) return plan;
if (rfftp_factorize(plan)!=0) { DEALLOC(plan); return NULL; }
size_t tws=rfftp_twsize(plan);
- plan->mem=RALLOC(double,tws);
- if (!plan->mem) { DEALLOC(plan); return NULL; }
+ if (tws != 0) {
+ plan->mem=RALLOC(double,tws);
+ if (!plan->mem) { DEALLOC(plan); return NULL; }
+ }
if (rfftp_comp_twiddle(plan)!=0)
{ DEALLOC(plan->mem); DEALLOC(plan); return NULL; }
return plan;
@@ -2229,6 +2232,8 @@ execute_real_forward(PyObject *a1, double fct)
{
rfft_plan plan=NULL;
int fail = 0;
+ npy_intp tdim[NPY_MAXDIMS];
+
PyArrayObject *data = (PyArrayObject *)PyArray_FromAny(a1,
PyArray_DescrFromType(NPY_DOUBLE), 1, 0,
NPY_ARRAY_DEFAULT | NPY_ARRAY_ENSUREARRAY | NPY_ARRAY_FORCECAST,
@@ -2238,15 +2243,11 @@ execute_real_forward(PyObject *a1, double fct)
int ndim = PyArray_NDIM(data);
const npy_intp *odim = PyArray_DIMS(data);
int npts = odim[ndim - 1];
- npy_intp *tdim=(npy_intp *)malloc(ndim*sizeof(npy_intp));
- if (!tdim)
- { Py_XDECREF(data); return NULL; }
for (int d=0; d<ndim-1; ++d)
tdim[d] = odim[d];
tdim[ndim-1] = npts/2 + 1;
PyArrayObject *ret = (PyArrayObject *)PyArray_Empty(ndim,
tdim, PyArray_DescrFromType(NPY_CDOUBLE), 0);
- free(tdim);
if (!ret) fail=1;
if (!fail) {
int rstep = PyArray_DIM(ret, PyArray_NDIM(ret) - 1)*2;
diff --git a/numpy/fft/helper.pyi b/numpy/fft/helper.pyi
index b49fc88f7..9b6525190 100644
--- a/numpy/fft/helper.pyi
+++ b/numpy/fft/helper.pyi
@@ -27,21 +27,21 @@ def ifftshift(x: ArrayLike, axes: None | _ShapeLike = ...) -> NDArray[Any]: ...
@overload
def fftfreq(
n: int | integer[Any],
- d: _ArrayLikeFloat_co,
+ d: _ArrayLikeFloat_co = ...,
) -> NDArray[floating[Any]]: ...
@overload
def fftfreq(
n: int | integer[Any],
- d: _ArrayLikeComplex_co,
+ d: _ArrayLikeComplex_co = ...,
) -> NDArray[complexfloating[Any, Any]]: ...
@overload
def rfftfreq(
n: int | integer[Any],
- d: _ArrayLikeFloat_co,
+ d: _ArrayLikeFloat_co = ...,
) -> NDArray[floating[Any]]: ...
@overload
def rfftfreq(
n: int | integer[Any],
- d: _ArrayLikeComplex_co,
+ d: _ArrayLikeComplex_co = ...,
) -> NDArray[complexfloating[Any, Any]]: ...
diff --git a/numpy/lib/__init__.pyi b/numpy/lib/__init__.pyi
index 1fa2d226e..d3553bbcc 100644
--- a/numpy/lib/__init__.pyi
+++ b/numpy/lib/__init__.pyi
@@ -64,7 +64,6 @@ from numpy.lib.function_base import (
digitize as digitize,
cov as cov,
corrcoef as corrcoef,
- msort as msort,
median as median,
sinc as sinc,
hamming as hamming,
diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py
index f5af314b5..405790025 100644
--- a/numpy/lib/function_base.py
+++ b/numpy/lib/function_base.py
@@ -4937,7 +4937,7 @@ def _meshgrid_dispatcher(*xi, copy=None, sparse=None, indexing=None):
@array_function_dispatch(_meshgrid_dispatcher)
def meshgrid(*xi, copy=True, sparse=False, indexing='xy'):
"""
- Return coordinate matrices from coordinate vectors.
+ Return a list of coordinate matrices from coordinate vectors.
Make N-D coordinate arrays for vectorized evaluations of
N-D scalar/vector fields over N-D grids, given
@@ -4978,7 +4978,7 @@ def meshgrid(*xi, copy=True, sparse=False, indexing='xy'):
Returns
-------
- X1, X2,..., XN : ndarray
+ X1, X2,..., XN : list of ndarrays
For vectors `x1`, `x2`,..., `xn` with lengths ``Ni=len(xi)``,
returns ``(N1, N2, N3,..., Nn)`` shaped arrays if indexing='ij'
or ``(N2, N1, N3,..., Nn)`` shaped arrays if indexing='xy'
diff --git a/numpy/lib/index_tricks.py b/numpy/lib/index_tricks.py
index fa3a90e4f..abf9e1090 100644
--- a/numpy/lib/index_tricks.py
+++ b/numpy/lib/index_tricks.py
@@ -3,11 +3,10 @@ import sys
import math
import warnings
+import numpy as np
from .._utils import set_module
import numpy.core.numeric as _nx
-from numpy.core.numeric import (
- asarray, ScalarType, array, alltrue, cumprod, arange, ndim
-)
+from numpy.core.numeric import ScalarType, array
from numpy.core.numerictypes import find_common_type, issubdtype
import numpy.matrixlib as matrixlib
@@ -94,7 +93,7 @@ def ix_(*args):
nd = len(args)
for k, new in enumerate(args):
if not isinstance(new, _nx.ndarray):
- new = asarray(new)
+ new = np.asarray(new)
if new.size == 0:
# Explicitly type empty arrays to avoid float default
new = new.astype(_nx.intp)
@@ -396,7 +395,7 @@ class AxisConcatenator:
scalar = True
scalartypes.append(newobj.dtype)
else:
- item_ndim = ndim(item)
+ item_ndim = np.ndim(item)
newobj = array(item, copy=False, subok=True, ndmin=ndmin)
if trans1d != -1 and item_ndim < ndmin:
k2 = ndmin - item_ndim
@@ -596,7 +595,7 @@ class ndenumerate:
"""
def __init__(self, arr):
- self.iter = asarray(arr).flat
+ self.iter = np.asarray(arr).flat
def __next__(self):
"""
@@ -909,9 +908,9 @@ def fill_diagonal(a, val, wrap=False):
else:
# For more than d=2, the strided formula is only valid for arrays with
# all dimensions equal, so we check first.
- if not alltrue(diff(a.shape) == 0):
+ if not np.all(diff(a.shape) == 0):
raise ValueError("All dimensions of input must be of equal length")
- step = 1 + (cumprod(a.shape[:-1])).sum()
+ step = 1 + (np.cumprod(a.shape[:-1])).sum()
# Write the value out into the diagonal.
a.flat[:end:step] = val
@@ -982,7 +981,7 @@ def diag_indices(n, ndim=2):
[0, 1]]])
"""
- idx = arange(n)
+ idx = np.arange(n)
return (idx,) * ndim
@@ -1041,7 +1040,7 @@ def diag_indices_from(arr):
raise ValueError("input array must be at least 2-d")
# For more than d=2, the strided formula is only valid for arrays with
# all dimensions equal, so we check first.
- if not alltrue(diff(arr.shape) == 0):
+ if not np.all(diff(arr.shape) == 0):
raise ValueError("All dimensions of input must be of equal length")
return diag_indices(arr.shape[0], arr.ndim)
diff --git a/numpy/lib/index_tricks.pyi b/numpy/lib/index_tricks.pyi
index c9251abd1..29a6b9e2b 100644
--- a/numpy/lib/index_tricks.pyi
+++ b/numpy/lib/index_tricks.pyi
@@ -119,7 +119,7 @@ class AxisConcatenator:
@staticmethod
def makemat(
data: ArrayLike, dtype: DTypeLike = ..., copy: bool = ...
- ) -> _Matrix: ...
+ ) -> _Matrix[Any, Any]: ...
# TODO: Sort out this `__getitem__` method
def __getitem__(self, key: Any) -> Any: ...
diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py
index cc8003f61..3ec46735c 100644
--- a/numpy/lib/tests/test_function_base.py
+++ b/numpy/lib/tests/test_function_base.py
@@ -229,8 +229,8 @@ class TestAny:
def test_nd(self):
y1 = [[0, 0, 0], [0, 1, 0], [1, 1, 0]]
assert_(np.any(y1))
- assert_array_equal(np.sometrue(y1, axis=0), [1, 1, 0])
- assert_array_equal(np.sometrue(y1, axis=1), [0, 1, 1])
+ assert_array_equal(np.any(y1, axis=0), [1, 1, 0])
+ assert_array_equal(np.any(y1, axis=1), [0, 1, 1])
class TestAll:
@@ -247,8 +247,8 @@ class TestAll:
def test_nd(self):
y1 = [[0, 0, 1], [0, 1, 1], [1, 1, 1]]
assert_(not np.all(y1))
- assert_array_equal(np.alltrue(y1, axis=0), [0, 0, 1])
- assert_array_equal(np.alltrue(y1, axis=1), [0, 0, 1])
+ assert_array_equal(np.all(y1, axis=0), [0, 0, 1])
+ assert_array_equal(np.all(y1, axis=1), [0, 0, 1])
class TestCopy:
diff --git a/numpy/lib/tests/test_type_check.py b/numpy/lib/tests/test_type_check.py
index 3f4ca6309..ea0326139 100644
--- a/numpy/lib/tests/test_type_check.py
+++ b/numpy/lib/tests/test_type_check.py
@@ -155,7 +155,7 @@ class TestIscomplex:
def test_fail(self):
z = np.array([-1, 0, 1])
res = iscomplex(z)
- assert_(not np.sometrue(res, axis=0))
+ assert_(not np.any(res, axis=0))
def test_pass(self):
z = np.array([-1j, 1, 0])
diff --git a/numpy/lib/twodim_base.py b/numpy/lib/twodim_base.py
index ed4f98704..6dcb65651 100644
--- a/numpy/lib/twodim_base.py
+++ b/numpy/lib/twodim_base.py
@@ -754,7 +754,7 @@ def histogram2d(x, y, bins=10, range=None, density=None, weights=None):
>>> xcenters = (xedges[:-1] + xedges[1:]) / 2
>>> ycenters = (yedges[:-1] + yedges[1:]) / 2
>>> im.set_data(xcenters, ycenters, H)
- >>> ax.images.append(im)
+ >>> ax.add_image(im)
>>> plt.show()
It is also possible to construct a 2-D histogram without specifying bin
diff --git a/numpy/linalg/linalg.py b/numpy/linalg/linalg.py
index 92878228d..78927d1ae 100644
--- a/numpy/linalg/linalg.py
+++ b/numpy/linalg/linalg.py
@@ -23,7 +23,7 @@ from numpy.core import (
array, asarray, zeros, empty, empty_like, intc, single, double,
csingle, cdouble, inexact, complexfloating, newaxis, all, Inf, dot,
add, multiply, sqrt, sum, isfinite,
- finfo, errstate, geterrobj, moveaxis, amin, amax, product, abs,
+ finfo, errstate, geterrobj, moveaxis, amin, amax, prod, abs,
atleast_2d, intp, asanyarray, object_, matmul,
swapaxes, divide, count_nonzero, isnan, sign, argsort, sort,
reciprocal
@@ -138,24 +138,24 @@ def _commonType(*arrays):
result_type = single
is_complex = False
for a in arrays:
- if issubclass(a.dtype.type, inexact):
- if isComplexType(a.dtype.type):
+ type_ = a.dtype.type
+ if issubclass(type_, inexact):
+ if isComplexType(type_):
is_complex = True
- rt = _realType(a.dtype.type, default=None)
- if rt is None:
+ rt = _realType(type_, default=None)
+ if rt is double:
+ result_type = double
+ elif rt is None:
# unsupported inexact scalar
raise TypeError("array type %s is unsupported in linalg" %
(a.dtype.name,))
else:
- rt = double
- if rt is double:
result_type = double
if is_complex:
- t = cdouble
result_type = _complex_types_map[result_type]
+ return cdouble, result_type
else:
- t = double
- return t, result_type
+ return double, result_type
def _to_native_byte_order(*arrays):
@@ -196,7 +196,7 @@ def _assert_finite(*arrays):
def _is_empty_2d(arr):
# check size first for efficiency
- return arr.size == 0 and product(arr.shape[-2:]) == 0
+ return arr.size == 0 and prod(arr.shape[-2:]) == 0
def transpose(a):
diff --git a/numpy/linalg/umath_linalg.cpp b/numpy/linalg/umath_linalg.cpp
index 639de6ee0..68db2b2f1 100644
--- a/numpy/linalg/umath_linalg.cpp
+++ b/numpy/linalg/umath_linalg.cpp
@@ -1149,7 +1149,7 @@ slogdet(char **args,
void *NPY_UNUSED(func))
{
fortran_int m;
- npy_uint8 *tmp_buff = NULL;
+ char *tmp_buff = NULL;
size_t matrix_size;
size_t pivot_size;
size_t safe_m;
@@ -1163,10 +1163,11 @@ slogdet(char **args,
*/
INIT_OUTER_LOOP_3
m = (fortran_int) dimensions[0];
- safe_m = m;
+ /* avoid empty malloc (buffers likely unused) and ensure m is `size_t` */
+ safe_m = m != 0 ? m : 1;
matrix_size = safe_m * safe_m * sizeof(typ);
pivot_size = safe_m * sizeof(fortran_int);
- tmp_buff = (npy_uint8 *)malloc(matrix_size + pivot_size);
+ tmp_buff = (char *)malloc(matrix_size + pivot_size);
if (tmp_buff) {
LINEARIZE_DATA_t lin_data;
@@ -1183,6 +1184,13 @@ slogdet(char **args,
free(tmp_buff);
}
+ else {
+ /* TODO: Requires use of new ufunc API to indicate error return */
+ NPY_ALLOW_C_API_DEF
+ NPY_ALLOW_C_API;
+ PyErr_NoMemory();
+ NPY_DISABLE_C_API;
+ }
}
template<typename typ, typename basetyp>
@@ -1193,7 +1201,7 @@ det(char **args,
void *NPY_UNUSED(func))
{
fortran_int m;
- npy_uint8 *tmp_buff;
+ char *tmp_buff;
size_t matrix_size;
size_t pivot_size;
size_t safe_m;
@@ -1207,10 +1215,11 @@ det(char **args,
*/
INIT_OUTER_LOOP_2
m = (fortran_int) dimensions[0];
- safe_m = m;
+ /* avoid empty malloc (buffers likely unused) and ensure m is `size_t` */
+ safe_m = m != 0 ? m : 1;
matrix_size = safe_m * safe_m * sizeof(typ);
pivot_size = safe_m * sizeof(fortran_int);
- tmp_buff = (npy_uint8 *)malloc(matrix_size + pivot_size);
+ tmp_buff = (char *)malloc(matrix_size + pivot_size);
if (tmp_buff) {
LINEARIZE_DATA_t lin_data;
@@ -1231,6 +1240,13 @@ det(char **args,
free(tmp_buff);
}
+ else {
+ /* TODO: Requires use of new ufunc API to indicate error return */
+ NPY_ALLOW_C_API_DEF
+ NPY_ALLOW_C_API;
+ PyErr_NoMemory();
+ NPY_DISABLE_C_API;
+ }
}
@@ -3738,16 +3754,16 @@ scalar_trait)
fortran_int lda = fortran_int_max(1, m);
fortran_int ldb = fortran_int_max(1, fortran_int_max(m,n));
- mem_buff = (npy_uint8 *)malloc(a_size + b_size + s_size);
-
- if (!mem_buff)
- goto error;
+ size_t msize = a_size + b_size + s_size;
+ mem_buff = (npy_uint8 *)malloc(msize != 0 ? msize : 1);
+ if (!mem_buff) {
+ goto no_memory;
+ }
a = mem_buff;
b = a + a_size;
s = b + b_size;
-
params->M = m;
params->N = n;
params->NRHS = nrhs;
@@ -3767,9 +3783,9 @@ scalar_trait)
params->RWORK = NULL;
params->LWORK = -1;
- if (call_gelsd(params) != 0)
+ if (call_gelsd(params) != 0) {
goto error;
-
+ }
work_count = (fortran_int)work_size_query;
work_size = (size_t) work_size_query * sizeof(ftyp);
@@ -3777,9 +3793,9 @@ scalar_trait)
}
mem_buff2 = (npy_uint8 *)malloc(work_size + iwork_size);
- if (!mem_buff2)
- goto error;
-
+ if (!mem_buff2) {
+ goto no_memory;
+ }
work = mem_buff2;
iwork = work + work_size;
@@ -3789,12 +3805,18 @@ scalar_trait)
params->LWORK = work_count;
return 1;
+
+ no_memory:
+ NPY_ALLOW_C_API_DEF
+ NPY_ALLOW_C_API;
+ PyErr_NoMemory();
+ NPY_DISABLE_C_API;
+
error:
TRACE_TXT("%s failed init\n", __FUNCTION__);
free(mem_buff);
free(mem_buff2);
memset(params, 0, sizeof(*params));
-
return 0;
}
@@ -3858,16 +3880,17 @@ using frealtyp = basetype_t<ftyp>;
fortran_int lda = fortran_int_max(1, m);
fortran_int ldb = fortran_int_max(1, fortran_int_max(m,n));
- mem_buff = (npy_uint8 *)malloc(a_size + b_size + s_size);
+ size_t msize = a_size + b_size + s_size;
+ mem_buff = (npy_uint8 *)malloc(msize != 0 ? msize : 1);
- if (!mem_buff)
- goto error;
+ if (!mem_buff) {
+ goto no_memory;
+ }
a = mem_buff;
b = a + a_size;
s = b + b_size;
-
params->M = m;
params->N = n;
params->NRHS = nrhs;
@@ -3888,8 +3911,9 @@ using frealtyp = basetype_t<ftyp>;
params->RWORK = &rwork_size_query;
params->LWORK = -1;
- if (call_gelsd(params) != 0)
+ if (call_gelsd(params) != 0) {
goto error;
+ }
work_count = (fortran_int)work_size_query.r;
@@ -3899,8 +3923,9 @@ using frealtyp = basetype_t<ftyp>;
}
mem_buff2 = (npy_uint8 *)malloc(work_size + rwork_size + iwork_size);
- if (!mem_buff2)
- goto error;
+ if (!mem_buff2) {
+ goto no_memory;
+ }
work = mem_buff2;
rwork = work + work_size;
@@ -3912,6 +3937,13 @@ using frealtyp = basetype_t<ftyp>;
params->LWORK = work_count;
return 1;
+
+ no_memory:
+ NPY_ALLOW_C_API_DEF
+ NPY_ALLOW_C_API;
+ PyErr_NoMemory();
+ NPY_DISABLE_C_API;
+
error:
TRACE_TXT("%s failed init\n", __FUNCTION__);
free(mem_buff);
diff --git a/numpy/ma/core.py b/numpy/ma/core.py
index b76ff8ea9..9584e56d2 100644
--- a/numpy/ma/core.py
+++ b/numpy/ma/core.py
@@ -2870,7 +2870,13 @@ class MaskedArray(ndarray):
_data._mask = _data._mask.copy()
# Reset the shape of the original mask
if getmask(data) is not nomask:
- data._mask.shape = data.shape
+ # gh-21022 encounters an issue here
+ # because data._mask.shape is not writeable, but
+ # the op was also pointless in that case, because
+ # the shapes were the same, so we can at least
+ # avoid that path
+ if data._mask.shape != data.shape:
+ data._mask.shape = data.shape
else:
# Case 2. : With a mask in input.
# If mask is boolean, create an array of True or False
@@ -6300,6 +6306,12 @@ class MaskedArray(ndarray):
memo[id(self)] = copied
for (k, v) in self.__dict__.items():
copied.__dict__[k] = deepcopy(v, memo)
+ # as clearly documented for np.copy(), you need to use
+ # deepcopy() directly for arrays of object type that may
+ # contain compound types--you cannot depend on normal
+ # copy semantics to do the right thing here
+ if self.dtype.hasobject:
+ copied._data[...] = deepcopy(copied._data)
return copied
diff --git a/numpy/ma/core.pyi b/numpy/ma/core.pyi
index 58d73a231..15f37c422 100644
--- a/numpy/ma/core.pyi
+++ b/numpy/ma/core.pyi
@@ -219,6 +219,10 @@ class MaskedArray(ndarray[_ShapeType, _DType_co]):
def compress(self, condition, axis=..., out=...): ...
def __eq__(self, other): ...
def __ne__(self, other): ...
+ def __ge__(self, other): ...
+ def __gt__(self, other): ...
+ def __le__(self, other): ...
+ def __lt__(self, other): ...
def __add__(self, other): ...
def __radd__(self, other): ...
def __sub__(self, other): ...
diff --git a/numpy/ma/extras.py b/numpy/ma/extras.py
index 41bce0f22..4abe2107a 100644
--- a/numpy/ma/extras.py
+++ b/numpy/ma/extras.py
@@ -398,7 +398,7 @@ def apply_along_axis(func1d, axis, arr, *args, **kwargs):
dtypes.append(np.asarray(res).dtype)
outarr = zeros(outshape, object)
outarr[tuple(ind)] = res
- Ntot = np.product(outshape)
+ Ntot = np.prod(outshape)
k = 1
while k < Ntot:
# increment the index
@@ -418,7 +418,7 @@ def apply_along_axis(func1d, axis, arr, *args, **kwargs):
j = i.copy()
j[axis] = ([slice(None, None)] * res.ndim)
j.put(indlist, ind)
- Ntot = np.product(outshape)
+ Ntot = np.prod(outshape)
holdshape = outshape
outshape = list(arr.shape)
outshape[axis] = res.shape
diff --git a/numpy/ma/tests/test_core.py b/numpy/ma/tests/test_core.py
index 7d23e32ec..5db01b74a 100644
--- a/numpy/ma/tests/test_core.py
+++ b/numpy/ma/tests/test_core.py
@@ -8,6 +8,7 @@ __author__ = "Pierre GF Gerard-Marchant"
import sys
import warnings
+import copy
import operator
import itertools
import textwrap
@@ -1335,16 +1336,16 @@ class TestMaskedArrayArithmetic:
assert_equal(np.sum(x, axis=0), sum(x, axis=0))
assert_equal(np.sum(filled(xm, 0), axis=0), sum(xm, axis=0))
assert_equal(np.sum(x, 0), sum(x, 0))
- assert_equal(np.product(x, axis=0), product(x, axis=0))
- assert_equal(np.product(x, 0), product(x, 0))
- assert_equal(np.product(filled(xm, 1), axis=0), product(xm, axis=0))
+ assert_equal(np.prod(x, axis=0), product(x, axis=0))
+ assert_equal(np.prod(x, 0), product(x, 0))
+ assert_equal(np.prod(filled(xm, 1), axis=0), product(xm, axis=0))
s = (3, 4)
x.shape = y.shape = xm.shape = ym.shape = s
if len(s) > 1:
assert_equal(np.concatenate((x, y), 1), concatenate((xm, ym), 1))
assert_equal(np.add.reduce(x, 1), add.reduce(x, 1))
assert_equal(np.sum(x, 1), sum(x, 1))
- assert_equal(np.product(x, 1), product(x, 1))
+ assert_equal(np.prod(x, 1), product(x, 1))
def test_binops_d2D(self):
# Test binary operations on 2D data
@@ -5570,3 +5571,47 @@ note
original note"""
assert_equal(np.ma.core.doc_note(method.__doc__, "note"), expected_doc)
+
+
+def test_gh_22556():
+ source = np.ma.array([0, [0, 1, 2]], dtype=object)
+ deepcopy = copy.deepcopy(source)
+ deepcopy[1].append('this should not appear in source')
+ assert len(source[1]) == 3
+
+
+def test_gh_21022():
+ # testing for absence of reported error
+ source = np.ma.masked_array(data=[-1, -1], mask=True, dtype=np.float64)
+ axis = np.array(0)
+ result = np.prod(source, axis=axis, keepdims=False)
+ result = np.ma.masked_array(result,
+ mask=np.ones(result.shape, dtype=np.bool_))
+ array = np.ma.masked_array(data=-1, mask=True, dtype=np.float64)
+ copy.deepcopy(array)
+ copy.deepcopy(result)
+
+
+def test_deepcopy_2d_obj():
+ source = np.ma.array([[0, "dog"],
+ [1, 1],
+ [[1, 2], "cat"]],
+ mask=[[0, 1],
+ [0, 0],
+ [0, 0]],
+ dtype=object)
+ deepcopy = copy.deepcopy(source)
+ deepcopy[2, 0].extend(['this should not appear in source', 3])
+ assert len(source[2, 0]) == 2
+ assert len(deepcopy[2, 0]) == 4
+ assert_equal(deepcopy._mask, source._mask)
+ deepcopy._mask[0, 0] = 1
+ assert source._mask[0, 0] == 0
+
+
+def test_deepcopy_0d_obj():
+ source = np.ma.array(0, mask=[0], dtype=object)
+ deepcopy = copy.deepcopy(source)
+ deepcopy[...] = 17
+ assert_equal(source, 0)
+ assert_equal(deepcopy, 17)
diff --git a/numpy/ma/tests/test_extras.py b/numpy/ma/tests/test_extras.py
index 38603fb84..e59ba3656 100644
--- a/numpy/ma/tests/test_extras.py
+++ b/numpy/ma/tests/test_extras.py
@@ -387,8 +387,8 @@ class TestConcatenator:
# Tests mr_ on 2D arrays.
a_1 = np.random.rand(5, 5)
a_2 = np.random.rand(5, 5)
- m_1 = np.round_(np.random.rand(5, 5), 0)
- m_2 = np.round_(np.random.rand(5, 5), 0)
+ m_1 = np.round(np.random.rand(5, 5), 0)
+ m_2 = np.round(np.random.rand(5, 5), 0)
b_1 = masked_array(a_1, mask=m_1)
b_2 = masked_array(a_2, mask=m_2)
# append columns
diff --git a/numpy/ma/tests/test_old_ma.py b/numpy/ma/tests/test_old_ma.py
index 8465b1153..7b892ad23 100644
--- a/numpy/ma/tests/test_old_ma.py
+++ b/numpy/ma/tests/test_old_ma.py
@@ -194,16 +194,16 @@ class TestMa:
assert_(eq(np.sum(x, axis=0), sum(x, axis=0)))
assert_(eq(np.sum(filled(xm, 0), axis=0), sum(xm, axis=0)))
assert_(eq(np.sum(x, 0), sum(x, 0)))
- assert_(eq(np.product(x, axis=0), product(x, axis=0)))
- assert_(eq(np.product(x, 0), product(x, 0)))
- assert_(eq(np.product(filled(xm, 1), axis=0),
+ assert_(eq(np.prod(x, axis=0), product(x, axis=0)))
+ assert_(eq(np.prod(x, 0), product(x, 0)))
+ assert_(eq(np.prod(filled(xm, 1), axis=0),
product(xm, axis=0)))
if len(s) > 1:
assert_(eq(np.concatenate((x, y), 1),
concatenate((xm, ym), 1)))
assert_(eq(np.add.reduce(x, 1), add.reduce(x, 1)))
assert_(eq(np.sum(x, 1), sum(x, 1)))
- assert_(eq(np.product(x, 1), product(x, 1)))
+ assert_(eq(np.prod(x, 1), product(x, 1)))
def test_testCI(self):
# Test of conversions and indexing
diff --git a/numpy/ma/testutils.py b/numpy/ma/testutils.py
index 2dd479abe..7a633906b 100644
--- a/numpy/ma/testutils.py
+++ b/numpy/ma/testutils.py
@@ -233,7 +233,7 @@ def fail_if_array_equal(x, y, err_msg='', verbose=True):
"""
def compare(x, y):
- return (not np.alltrue(approx(x, y)))
+ return (not np.all(approx(x, y)))
assert_array_compare(compare, x, y, err_msg=err_msg, verbose=verbose,
header='Arrays are not equal')
diff --git a/numpy/meson.build b/numpy/meson.build
index dbb5fd301..b366b7b05 100644
--- a/numpy/meson.build
+++ b/numpy/meson.build
@@ -10,6 +10,7 @@ endif
# Platform detection
is_windows = host_machine.system() == 'windows'
is_mingw = is_windows and cc.get_id() == 'gcc'
+is_msvc = is_windows and cc.get_id() == 'msvc'
if is_windows
# For mingw-w64, link statically against the UCRT.
@@ -76,6 +77,12 @@ dependency_map = {
# TODO: add ILP64 support
have_blas = blas.found() # TODO: and blas.has_cblas()
have_lapack = lapack.found()
+if have_blas
+ # TODO: this is a shortcut - it needs to be checked rather than used
+ # unconditionally, and then added to the extension modules that need the
+ # macro, rather than as a project argument.
+ add_project_arguments('-DHAVE_CBLAS', language: ['c', 'cpp'])
+endif
# Copy the main __init__.py|pxd files to the build dir (needed for Cython)
__init__py = fs.copyfile('__init__.py')
diff --git a/numpy/random/_bounded_integers.pyx.in b/numpy/random/_bounded_integers.pyx.in
index 7eb6aff1e..6743001d6 100644
--- a/numpy/random/_bounded_integers.pyx.in
+++ b/numpy/random/_bounded_integers.pyx.in
@@ -99,8 +99,12 @@ cdef object _rand_{{nptype}}_broadcast(np.ndarray low, np.ndarray high, object s
is_open = not closed
low_arr = <np.ndarray>low
high_arr = <np.ndarray>high
- if np.any(np.less(low_arr, {{lb}})):
+
+ if np.can_cast(low_arr, np.{{otype}}):
+ pass # cannot be out-of-bounds
+ elif np.any(np.less(low_arr, np.{{otype}}({{lb}}))):
raise ValueError('low is out of bounds for {{nptype}}')
+
if closed:
high_comp = np.greater_equal
low_high_comp = np.greater
@@ -108,8 +112,11 @@ cdef object _rand_{{nptype}}_broadcast(np.ndarray low, np.ndarray high, object s
high_comp = np.greater
low_high_comp = np.greater_equal
- if np.any(high_comp(high_arr, {{ub}})):
+ if np.can_cast(high_arr, np.{{otype}}):
+ pass # cannot be out-of-bounds
+ elif np.any(high_comp(high_arr, np.{{nptype_up}}({{ub}}))):
raise ValueError('high is out of bounds for {{nptype}}')
+
if np.any(low_high_comp(low_arr, high_arr)):
raise ValueError(format_bounds_error(closed, low_arr))
@@ -165,50 +172,69 @@ cdef object _rand_{{nptype}}_broadcast(object low, object high, object size,
64 bit integer type.
"""
- cdef np.ndarray low_arr, high_arr, out_arr, highm1_arr
+ cdef np.ndarray low_arr, low_arr_orig, high_arr, high_arr_orig, out_arr
cdef np.npy_intp i, cnt, n
cdef np.broadcast it
cdef object closed_upper
cdef uint64_t *out_data
cdef {{nptype}}_t *highm1_data
cdef {{nptype}}_t low_v, high_v
- cdef uint64_t rng, last_rng, val, mask, off, out_val
+ cdef uint64_t rng, last_rng, val, mask, off, out_val, is_open
- low_arr = <np.ndarray>low
- high_arr = <np.ndarray>high
+ low_arr_orig = <np.ndarray>low
+ high_arr_orig = <np.ndarray>high
- if np.any(np.less(low_arr, {{lb}})):
- raise ValueError('low is out of bounds for {{nptype}}')
- dt = high_arr.dtype
- if closed or np.issubdtype(dt, np.integer):
- # Avoid object dtype path if already an integer
- high_lower_comp = np.less if closed else np.less_equal
- if np.any(high_lower_comp(high_arr, {{lb}})):
- raise ValueError(format_bounds_error(closed, low_arr))
- high_m1 = high_arr if closed else high_arr - dt.type(1)
- if np.any(np.greater(high_m1, {{ub}})):
- raise ValueError('high is out of bounds for {{nptype}}')
- highm1_arr = <np.ndarray>np.PyArray_FROM_OTF(high_m1, np.{{npctype}}, np.NPY_ALIGNED | np.NPY_FORCECAST)
+ is_open = not closed
+
+ # The following code tries to cast safely, but failing that goes via
+ # Python `int()` because it is very difficult to cast integers in a
+ # truly safe way (i.e. so it raises on out-of-bound).
+ # We correct if the interval is not closed in this step if we go the long
+ # route. (Not otherwise, since the -1 could overflow in theory.)
+ if np.can_cast(low_arr_orig, np.{{otype}}):
+ low_arr = <np.ndarray>np.PyArray_FROM_OTF(low_arr_orig, np.{{npctype}}, np.NPY_ALIGNED)
+ else:
+ low_arr = <np.ndarray>np.empty_like(low_arr_orig, dtype=np.{{otype}})
+ flat = low_arr_orig.flat
+ low_data = <{{nptype}}_t *>np.PyArray_DATA(low_arr)
+ cnt = np.PyArray_SIZE(low_arr)
+ for i in range(cnt):
+ lower = int(flat[i])
+ if lower < {{lb}} or lower > {{ub}}:
+ raise ValueError('low is out of bounds for {{nptype}}')
+ low_data[i] = lower
+
+ del low_arr_orig
+
+ if np.can_cast(high_arr_orig, np.{{otype}}):
+ high_arr = <np.ndarray>np.PyArray_FROM_OTF(high_arr_orig, np.{{npctype}}, np.NPY_ALIGNED)
else:
- # If input is object or a floating type
- highm1_arr = <np.ndarray>np.empty_like(high_arr, dtype=np.{{otype}})
- highm1_data = <{{nptype}}_t *>np.PyArray_DATA(highm1_arr)
+ high_arr = np.empty_like(high_arr_orig, dtype=np.{{otype}})
+ flat = high_arr_orig.flat
+ high_data = <{{nptype}}_t *>np.PyArray_DATA(high_arr)
cnt = np.PyArray_SIZE(high_arr)
- flat = high_arr.flat
for i in range(cnt):
- # Subtract 1 since generator produces values on the closed int [off, off+rng]
- closed_upper = int(flat[i]) - 1
+ closed_upper = int(flat[i]) - is_open
if closed_upper > {{ub}}:
raise ValueError('high is out of bounds for {{nptype}}')
if closed_upper < {{lb}}:
raise ValueError(format_bounds_error(closed, low_arr))
- highm1_data[i] = <{{nptype}}_t>closed_upper
+ high_data[i] = closed_upper
- if np.any(np.greater(low_arr, highm1_arr)):
- raise ValueError(format_bounds_error(closed, low_arr))
+ is_open = 0 # we applied is_open in this path already
- high_arr = highm1_arr
- low_arr = <np.ndarray>np.PyArray_FROM_OTF(low, np.{{npctype}}, np.NPY_ALIGNED | np.NPY_FORCECAST)
+ del high_arr_orig
+
+ # Since we have the largest supported integer dtypes, they must be within
+ # range at this point; otherwise conversion would have failed. Check that
+ # it is never true that `high <= low`` if closed and `high < low` if not
+ if not is_open:
+ low_high_comp = np.greater
+ else:
+ low_high_comp = np.greater_equal
+
+ if np.any(low_high_comp(low_arr, high_arr)):
+ raise ValueError(format_bounds_error(closed, low_arr))
if size is not None:
out_arr = <np.ndarray>np.empty(size, np.{{otype}})
@@ -224,8 +250,8 @@ cdef object _rand_{{nptype}}_broadcast(object low, object high, object size,
for i in range(n):
low_v = (<{{nptype}}_t*>np.PyArray_MultiIter_DATA(it, 0))[0]
high_v = (<{{nptype}}_t*>np.PyArray_MultiIter_DATA(it, 1))[0]
- # Generator produces values on the closed int [off, off+rng], -1 subtracted above
- rng = <{{utype}}_t>(high_v - low_v)
+ # Generator produces values on the closed int [off, off+rng]
+ rng = <{{utype}}_t>((high_v - is_open) - low_v)
off = <{{utype}}_t>(<{{nptype}}_t>low_v)
if rng != last_rng:
diff --git a/numpy/random/_generator.pyi b/numpy/random/_generator.pyi
index f0d814fef..e1cdefb15 100644
--- a/numpy/random/_generator.pyi
+++ b/numpy/random/_generator.pyi
@@ -29,6 +29,7 @@ from numpy._typing import (
_DTypeLikeUInt,
_Float32Codes,
_Float64Codes,
+ _FloatLike_co,
_Int8Codes,
_Int16Codes,
_Int32Codes,
@@ -72,6 +73,7 @@ class Generator:
def __reduce__(self) -> tuple[Callable[[str], Generator], tuple[str], dict[str, Any]]: ...
@property
def bit_generator(self) -> BitGenerator: ...
+ def spawn(self, n_children: int) -> list[Generator]: ...
def bytes(self, length: int) -> bytes: ...
@overload
def standard_normal( # type: ignore[misc]
@@ -187,13 +189,18 @@ class Generator:
out: None | ndarray[Any, dtype[float64]] = ...,
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def beta(self, a: float, b: float, size: None = ...) -> float: ... # type: ignore[misc]
+ def beta(
+ self,
+ a: _FloatLike_co,
+ b: _FloatLike_co,
+ size: None = ...,
+ ) -> float: ... # type: ignore[misc]
@overload
def beta(
self, a: _ArrayLikeFloat_co, b: _ArrayLikeFloat_co, size: None | _ShapeLike = ...
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def exponential(self, scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc]
+ def exponential(self, scale: _FloatLike_co = ..., size: None = ...) -> float: ... # type: ignore[misc]
@overload
def exponential(
self, scale: _ArrayLikeFloat_co = ..., size: None | _ShapeLike = ...
@@ -370,7 +377,12 @@ class Generator:
shuffle: bool = ...,
) -> ndarray[Any, Any]: ...
@overload
- def uniform(self, low: float = ..., high: float = ..., size: None = ...) -> float: ... # type: ignore[misc]
+ def uniform(
+ self,
+ low: _FloatLike_co = ...,
+ high: _FloatLike_co = ...,
+ size: None = ...,
+ ) -> float: ... # type: ignore[misc]
@overload
def uniform(
self,
@@ -379,7 +391,12 @@ class Generator:
size: None | _ShapeLike = ...,
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def normal(self, loc: float = ..., scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc]
+ def normal(
+ self,
+ loc: _FloatLike_co = ...,
+ scale: _FloatLike_co = ...,
+ size: None = ...,
+ ) -> float: ... # type: ignore[misc]
@overload
def normal(
self,
@@ -390,7 +407,7 @@ class Generator:
@overload
def standard_gamma( # type: ignore[misc]
self,
- shape: float,
+ shape: _FloatLike_co,
size: None = ...,
dtype: _DTypeLikeFloat32 | _DTypeLikeFloat64 = ...,
out: None = ...,
@@ -425,7 +442,7 @@ class Generator:
out: None | ndarray[Any, dtype[float64]] = ...,
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def gamma(self, shape: float, scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc]
+ def gamma(self, shape: _FloatLike_co, scale: _FloatLike_co = ..., size: None = ...) -> float: ... # type: ignore[misc]
@overload
def gamma(
self,
@@ -434,13 +451,13 @@ class Generator:
size: None | _ShapeLike = ...,
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def f(self, dfnum: float, dfden: float, size: None = ...) -> float: ... # type: ignore[misc]
+ def f(self, dfnum: _FloatLike_co, dfden: _FloatLike_co, size: None = ...) -> float: ... # type: ignore[misc]
@overload
def f(
self, dfnum: _ArrayLikeFloat_co, dfden: _ArrayLikeFloat_co, size: None | _ShapeLike = ...
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def noncentral_f(self, dfnum: float, dfden: float, nonc: float, size: None = ...) -> float: ... # type: ignore[misc]
+ def noncentral_f(self, dfnum: _FloatLike_co, dfden: _FloatLike_co, nonc: _FloatLike_co, size: None = ...) -> float: ... # type: ignore[misc]
@overload
def noncentral_f(
self,
@@ -450,19 +467,19 @@ class Generator:
size: None | _ShapeLike = ...,
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def chisquare(self, df: float, size: None = ...) -> float: ... # type: ignore[misc]
+ def chisquare(self, df: _FloatLike_co, size: None = ...) -> float: ... # type: ignore[misc]
@overload
def chisquare(
self, df: _ArrayLikeFloat_co, size: None | _ShapeLike = ...
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def noncentral_chisquare(self, df: float, nonc: float, size: None = ...) -> float: ... # type: ignore[misc]
+ def noncentral_chisquare(self, df: _FloatLike_co, nonc: _FloatLike_co, size: None = ...) -> float: ... # type: ignore[misc]
@overload
def noncentral_chisquare(
self, df: _ArrayLikeFloat_co, nonc: _ArrayLikeFloat_co, size: None | _ShapeLike = ...
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def standard_t(self, df: float, size: None = ...) -> float: ... # type: ignore[misc]
+ def standard_t(self, df: _FloatLike_co, size: None = ...) -> float: ... # type: ignore[misc]
@overload
def standard_t(
self, df: _ArrayLikeFloat_co, size: None = ...
@@ -472,25 +489,25 @@ class Generator:
self, df: _ArrayLikeFloat_co, size: _ShapeLike = ...
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def vonmises(self, mu: float, kappa: float, size: None = ...) -> float: ... # type: ignore[misc]
+ def vonmises(self, mu: _FloatLike_co, kappa: _FloatLike_co, size: None = ...) -> float: ... # type: ignore[misc]
@overload
def vonmises(
self, mu: _ArrayLikeFloat_co, kappa: _ArrayLikeFloat_co, size: None | _ShapeLike = ...
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def pareto(self, a: float, size: None = ...) -> float: ... # type: ignore[misc]
+ def pareto(self, a: _FloatLike_co, size: None = ...) -> float: ... # type: ignore[misc]
@overload
def pareto(
self, a: _ArrayLikeFloat_co, size: None | _ShapeLike = ...
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def weibull(self, a: float, size: None = ...) -> float: ... # type: ignore[misc]
+ def weibull(self, a: _FloatLike_co, size: None = ...) -> float: ... # type: ignore[misc]
@overload
def weibull(
self, a: _ArrayLikeFloat_co, size: None | _ShapeLike = ...
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def power(self, a: float, size: None = ...) -> float: ... # type: ignore[misc]
+ def power(self, a: _FloatLike_co, size: None = ...) -> float: ... # type: ignore[misc]
@overload
def power(
self, a: _ArrayLikeFloat_co, size: None | _ShapeLike = ...
@@ -500,7 +517,12 @@ class Generator:
@overload
def standard_cauchy(self, size: _ShapeLike = ...) -> ndarray[Any, dtype[float64]]: ...
@overload
- def laplace(self, loc: float = ..., scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc]
+ def laplace(
+ self,
+ loc: _FloatLike_co = ...,
+ scale: _FloatLike_co = ...,
+ size: None = ...,
+ ) -> float: ... # type: ignore[misc]
@overload
def laplace(
self,
@@ -509,7 +531,12 @@ class Generator:
size: None | _ShapeLike = ...,
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def gumbel(self, loc: float = ..., scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc]
+ def gumbel(
+ self,
+ loc: _FloatLike_co = ...,
+ scale: _FloatLike_co = ...,
+ size: None = ...,
+ ) -> float: ... # type: ignore[misc]
@overload
def gumbel(
self,
@@ -518,7 +545,12 @@ class Generator:
size: None | _ShapeLike = ...,
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def logistic(self, loc: float = ..., scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc]
+ def logistic(
+ self,
+ loc: _FloatLike_co = ...,
+ scale: _FloatLike_co = ...,
+ size: None = ...,
+ ) -> float: ... # type: ignore[misc]
@overload
def logistic(
self,
@@ -527,7 +559,12 @@ class Generator:
size: None | _ShapeLike = ...,
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def lognormal(self, mean: float = ..., sigma: float = ..., size: None = ...) -> float: ... # type: ignore[misc]
+ def lognormal(
+ self,
+ mean: _FloatLike_co = ...,
+ sigma: _FloatLike_co = ...,
+ size: None = ...,
+ ) -> float: ... # type: ignore[misc]
@overload
def lognormal(
self,
@@ -536,19 +573,25 @@ class Generator:
size: None | _ShapeLike = ...,
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def rayleigh(self, scale: float = ..., size: None = ...) -> float: ... # type: ignore[misc]
+ def rayleigh(self, scale: _FloatLike_co = ..., size: None = ...) -> float: ... # type: ignore[misc]
@overload
def rayleigh(
self, scale: _ArrayLikeFloat_co = ..., size: None | _ShapeLike = ...
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def wald(self, mean: float, scale: float, size: None = ...) -> float: ... # type: ignore[misc]
+ def wald(self, mean: _FloatLike_co, scale: _FloatLike_co, size: None = ...) -> float: ... # type: ignore[misc]
@overload
def wald(
self, mean: _ArrayLikeFloat_co, scale: _ArrayLikeFloat_co, size: None | _ShapeLike = ...
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def triangular(self, left: float, mode: float, right: float, size: None = ...) -> float: ... # type: ignore[misc]
+ def triangular(
+ self,
+ left: _FloatLike_co,
+ mode: _FloatLike_co,
+ right: _FloatLike_co,
+ size: None = ...,
+ ) -> float: ... # type: ignore[misc]
@overload
def triangular(
self,
@@ -558,31 +601,31 @@ class Generator:
size: None | _ShapeLike = ...,
) -> ndarray[Any, dtype[float64]]: ...
@overload
- def binomial(self, n: int, p: float, size: None = ...) -> int: ... # type: ignore[misc]
+ def binomial(self, n: int, p: _FloatLike_co, size: None = ...) -> int: ... # type: ignore[misc]
@overload
def binomial(
self, n: _ArrayLikeInt_co, p: _ArrayLikeFloat_co, size: None | _ShapeLike = ...
) -> ndarray[Any, dtype[int64]]: ...
@overload
- def negative_binomial(self, n: float, p: float, size: None = ...) -> int: ... # type: ignore[misc]
+ def negative_binomial(self, n: _FloatLike_co, p: _FloatLike_co, size: None = ...) -> int: ... # type: ignore[misc]
@overload
def negative_binomial(
self, n: _ArrayLikeFloat_co, p: _ArrayLikeFloat_co, size: None | _ShapeLike = ...
) -> ndarray[Any, dtype[int64]]: ...
@overload
- def poisson(self, lam: float = ..., size: None = ...) -> int: ... # type: ignore[misc]
+ def poisson(self, lam: _FloatLike_co = ..., size: None = ...) -> int: ... # type: ignore[misc]
@overload
def poisson(
self, lam: _ArrayLikeFloat_co = ..., size: None | _ShapeLike = ...
) -> ndarray[Any, dtype[int64]]: ...
@overload
- def zipf(self, a: float, size: None = ...) -> int: ... # type: ignore[misc]
+ def zipf(self, a: _FloatLike_co, size: None = ...) -> int: ... # type: ignore[misc]
@overload
def zipf(
self, a: _ArrayLikeFloat_co, size: None | _ShapeLike = ...
) -> ndarray[Any, dtype[int64]]: ...
@overload
- def geometric(self, p: float, size: None = ...) -> int: ... # type: ignore[misc]
+ def geometric(self, p: _FloatLike_co, size: None = ...) -> int: ... # type: ignore[misc]
@overload
def geometric(
self, p: _ArrayLikeFloat_co, size: None | _ShapeLike = ...
@@ -598,7 +641,7 @@ class Generator:
size: None | _ShapeLike = ...,
) -> ndarray[Any, dtype[int64]]: ...
@overload
- def logseries(self, p: float, size: None = ...) -> int: ... # type: ignore[misc]
+ def logseries(self, p: _FloatLike_co, size: None = ...) -> int: ... # type: ignore[misc]
@overload
def logseries(
self, p: _ArrayLikeFloat_co, size: None | _ShapeLike = ...
diff --git a/numpy/random/_generator.pyx b/numpy/random/_generator.pyx
index 83a4b2ad5..4cc8d42f5 100644
--- a/numpy/random/_generator.pyx
+++ b/numpy/random/_generator.pyx
@@ -238,6 +238,58 @@ cdef class Generator:
"""
return self._bit_generator
+ def spawn(self, int n_children):
+ """
+ Create new independent child generators.
+
+ See :ref:`seedsequence-spawn` for additional notes on spawning
+ children.
+
+ .. versionadded:: 1.25.0
+
+ Returns
+ -------
+ child_generators : list of Generators
+
+ Raises
+ ------
+ TypeError
+ When the underlying SeedSequence does not implement spawning.
+
+ See Also
+ --------
+ random.BitGenerator.spawn, random.SeedSequence.spawn :
+ Equivalent method on the bit generator and seed sequence.
+ bit_generator :
+ The bit generator instance used by the generator.
+
+ Examples
+ --------
+ Starting from a seeded default generator:
+
+ >>> # High quality entropy created with: f"0x{secrets.randbits(128):x}"
+ >>> entropy = 0x3034c61a9ae04ff8cb62ab8ec2c4b501
+ >>> rng = np.random.default_rng(entropy)
+
+ Create two new generators for example for parallel executation:
+
+ >>> child_rng1, child_rng2 = rng.spawn(2)
+
+ Drawn numbers from each are independent but derived from the initial
+ seeding entropy:
+
+ >>> rng.uniform(), child_rng1.uniform(), child_rng2.uniform()
+ (0.19029263503854454, 0.9475673279178444, 0.4702687338396767)
+
+ It is safe to spawn additional children from the original ``rng`` or
+ the children:
+
+ >>> more_child_rngs = rng.spawn(20)
+ >>> nested_spawn = child_rng1.spawn(20)
+
+ """
+ return [type(self)(g) for g in self._bit_generator.spawn(n_children)]
+
def random(self, size=None, dtype=np.float64, out=None):
"""
random(size=None, dtype=np.float64, out=None)
@@ -2576,7 +2628,7 @@ cdef class Generator:
>>> b = []
>>> for i in range(1000):
... a = 10. + rng.standard_normal(100)
- ... b.append(np.product(a))
+ ... b.append(np.prod(a))
>>> b = np.array(b) / np.min(b) # scale values to be positive
>>> count, bins, ignored = plt.hist(b, 100, density=True, align='mid')
@@ -4825,6 +4877,8 @@ def default_rng(seed=None):
-----
If ``seed`` is not a `BitGenerator` or a `Generator`, a new `BitGenerator`
is instantiated. This function does not manage a default global instance.
+
+ See :ref:`seeding_and_entropy` for more information about seeding.
Examples
--------
diff --git a/numpy/random/bit_generator.pyi b/numpy/random/bit_generator.pyi
index e6e3b10cd..8b9779cad 100644
--- a/numpy/random/bit_generator.pyi
+++ b/numpy/random/bit_generator.pyi
@@ -96,6 +96,9 @@ class BitGenerator(abc.ABC):
def state(self) -> Mapping[str, Any]: ...
@state.setter
def state(self, value: Mapping[str, Any]) -> None: ...
+ @property
+ def seed_seq(self) -> ISeedSequence: ...
+ def spawn(self, n_children: int) -> list[BitGenerator]: ...
@overload
def random_raw(self, size: None = ..., output: Literal[True] = ...) -> int: ... # type: ignore[misc]
@overload
diff --git a/numpy/random/bit_generator.pyx b/numpy/random/bit_generator.pyx
index 47804c487..06f8c9753 100644
--- a/numpy/random/bit_generator.pyx
+++ b/numpy/random/bit_generator.pyx
@@ -212,6 +212,9 @@ class ISpawnableSeedSequence(ISeedSequence):
Spawn a number of child `SeedSequence` s by extending the
`spawn_key`.
+ See :ref:`seedsequence-spawn` for additional notes on spawning
+ children.
+
Parameters
----------
n_children : int
@@ -451,6 +454,9 @@ cdef class SeedSequence():
Spawn a number of child `SeedSequence` s by extending the
`spawn_key`.
+ See :ref:`seedsequence-spawn` for additional notes on spawning
+ children.
+
Parameters
----------
n_children : int
@@ -458,6 +464,12 @@ cdef class SeedSequence():
Returns
-------
seqs : list of `SeedSequence` s
+
+ See Also
+ --------
+ random.Generator.spawn, random.BitGenerator.spawn :
+ Equivalent method on the generator and bit generator.
+
"""
cdef uint32_t i
@@ -551,6 +563,53 @@ cdef class BitGenerator():
def state(self, value):
raise NotImplementedError('Not implemented in base BitGenerator')
+ @property
+ def seed_seq(self):
+ """
+ Get the seed sequence used to initialize the bit generator.
+
+ .. versionadded:: 1.25.0
+
+ Returns
+ -------
+ seed_seq : ISeedSequence
+ The SeedSequence object used to initialize the BitGenerator.
+ This is normally a `np.random.SeedSequence` instance.
+
+ """
+ return self._seed_seq
+
+ def spawn(self, int n_children):
+ """
+ Create new independent child bit generators.
+
+ See :ref:`seedsequence-spawn` for additional notes on spawning
+ children. Some bit generators also implement ``jumped``
+ as a different approach for creating independent streams.
+
+ .. versionadded:: 1.25.0
+
+ Returns
+ -------
+ child_bit_generators : list of BitGenerators
+
+ Raises
+ ------
+ TypeError
+ When the underlying SeedSequence does not implement spawning.
+
+ See Also
+ --------
+ random.Generator.spawn, random.SeedSequence.spawn :
+ Equivalent method on the generator and seed sequence.
+
+ """
+ if not isinstance(self._seed_seq, ISpawnableSeedSequence):
+ raise TypeError(
+ "The underlying SeedSequence does not implement spawning.")
+
+ return [type(self)(seed=s) for s in self._seed_seq.spawn(n_children)]
+
def random_raw(self, size=None, output=True):
"""
random_raw(self, size=None)
diff --git a/numpy/random/meson.build b/numpy/random/meson.build
index cc61c66dd..036cd81b9 100644
--- a/numpy/random/meson.build
+++ b/numpy/random/meson.build
@@ -66,7 +66,7 @@ random_pyx_sources = [
fs.copyfile('mtrand.pyx'),
'src/distributions/distributions.c',
'src/legacy/legacy-distributions.c'
- ], ['-DNPY_RANDOM_LEGACY=1'], npymath_lib,
+ ], ['-DNP_RANDOM_LEGACY=1'], npymath_lib,
],
]
foreach gen: random_pyx_sources
diff --git a/numpy/random/mtrand.pyx b/numpy/random/mtrand.pyx
index ca6ba9de8..dfa553ee4 100644
--- a/numpy/random/mtrand.pyx
+++ b/numpy/random/mtrand.pyx
@@ -3066,7 +3066,7 @@ cdef class RandomState:
>>> b = []
>>> for i in range(1000):
... a = 10. + np.random.standard_normal(100)
- ... b.append(np.product(a))
+ ... b.append(np.prod(a))
>>> b = np.array(b) / np.min(b) # scale values to be positive
>>> count, bins, ignored = plt.hist(b, 100, density=True, align='mid')
diff --git a/numpy/random/tests/test_direct.py b/numpy/random/tests/test_direct.py
index 58d966adf..fa2ae866b 100644
--- a/numpy/random/tests/test_direct.py
+++ b/numpy/random/tests/test_direct.py
@@ -148,6 +148,46 @@ def test_seedsequence():
assert len(dummy.spawn(10)) == 10
+def test_generator_spawning():
+ """ Test spawning new generators and bit_generators directly.
+ """
+ rng = np.random.default_rng()
+ seq = rng.bit_generator.seed_seq
+ new_ss = seq.spawn(5)
+ expected_keys = [seq.spawn_key + (i,) for i in range(5)]
+ assert [c.spawn_key for c in new_ss] == expected_keys
+
+ new_bgs = rng.bit_generator.spawn(5)
+ expected_keys = [seq.spawn_key + (i,) for i in range(5, 10)]
+ assert [bg.seed_seq.spawn_key for bg in new_bgs] == expected_keys
+
+ new_rngs = rng.spawn(5)
+ expected_keys = [seq.spawn_key + (i,) for i in range(10, 15)]
+ found_keys = [rng.bit_generator.seed_seq.spawn_key for rng in new_rngs]
+ assert found_keys == expected_keys
+
+ # Sanity check that streams are actually different:
+ assert new_rngs[0].uniform() != new_rngs[1].uniform()
+
+
+def test_non_spawnable():
+ from numpy.random.bit_generator import ISeedSequence
+
+ class FakeSeedSequence:
+ def generate_state(self, n_words, dtype=np.uint32):
+ return np.zeros(n_words, dtype=dtype)
+
+ ISeedSequence.register(FakeSeedSequence)
+
+ rng = np.random.default_rng(FakeSeedSequence())
+
+ with pytest.raises(TypeError, match="The underlying SeedSequence"):
+ rng.spawn(5)
+
+ with pytest.raises(TypeError, match="The underlying SeedSequence"):
+ rng.bit_generator.spawn(5)
+
+
class Base:
dtype = np.uint64
data2 = data1 = {}
diff --git a/numpy/random/tests/test_generator_mt19937.py b/numpy/random/tests/test_generator_mt19937.py
index 54a5b73a3..5c4c2cbf9 100644
--- a/numpy/random/tests/test_generator_mt19937.py
+++ b/numpy/random/tests/test_generator_mt19937.py
@@ -345,6 +345,8 @@ class TestIntegers:
endpoint=endpoint, dtype=dt)
assert_raises(ValueError, self.rfunc, 1, [0],
endpoint=endpoint, dtype=dt)
+ assert_raises(ValueError, self.rfunc, [ubnd+1], [ubnd],
+ endpoint=endpoint, dtype=dt)
def test_bounds_checking_array(self, endpoint):
for dt in self.itype:
diff --git a/numpy/testing/_private/utils.py b/numpy/testing/_private/utils.py
index 3ebc6a31f..b9ea703d0 100644
--- a/numpy/testing/_private/utils.py
+++ b/numpy/testing/_private/utils.py
@@ -19,7 +19,7 @@ import pprint
import sysconfig
import numpy as np
-from numpy.core import(
+from numpy.core import (
intp, float32, empty, arange, array_repr, ndarray, isnat, array)
import numpy.linalg.lapack_lite
@@ -470,7 +470,7 @@ def print_assert_equal(test_string, actual, desired):
@np._no_nep50_warning()
-def assert_almost_equal(actual,desired,decimal=7,err_msg='',verbose=True):
+def assert_almost_equal(actual, desired, decimal=7, err_msg='', verbose=True):
"""
Raises an AssertionError if two items are not equal up to desired
precision.
@@ -597,7 +597,8 @@ def assert_almost_equal(actual,desired,decimal=7,err_msg='',verbose=True):
@np._no_nep50_warning()
-def assert_approx_equal(actual,desired,significant=7,err_msg='',verbose=True):
+def assert_approx_equal(actual, desired, significant=7, err_msg='',
+ verbose=True):
"""
Raises an AssertionError if two items are not equal up to significant
digits.
@@ -701,7 +702,8 @@ def assert_array_compare(comparison, x, y, err_msg='', verbose=True, header='',
precision=6, equal_nan=True, equal_inf=True,
*, strict=False):
__tracebackhide__ = True # Hide traceback for py.test
- from numpy.core import array, array2string, isnan, inf, bool_, errstate, all, max, object_
+ from numpy.core import (array2string, isnan, inf, bool_, errstate,
+ all, max, object_)
x = np.asanyarray(x)
y = np.asanyarray(y)
@@ -827,10 +829,10 @@ def assert_array_compare(comparison, x, y, err_msg='', verbose=True, header='',
max_abs_error = max(error)
if getattr(error, 'dtype', object_) == object_:
remarks.append('Max absolute difference: '
- + str(max_abs_error))
+ + str(max_abs_error))
else:
remarks.append('Max absolute difference: '
- + array2string(max_abs_error))
+ + array2string(max_abs_error))
# note: this definition of relative error matches that one
# used by assert_allclose (found in np.isclose)
@@ -842,10 +844,10 @@ def assert_array_compare(comparison, x, y, err_msg='', verbose=True, header='',
max_rel_error = max(error[nonzero] / abs(y[nonzero]))
if getattr(error, 'dtype', object_) == object_:
remarks.append('Max relative difference: '
- + str(max_rel_error))
+ + str(max_rel_error))
else:
remarks.append('Max relative difference: '
- + array2string(max_rel_error))
+ + array2string(max_rel_error))
err_msg += '\n' + '\n'.join(remarks)
msg = build_err_msg([ox, oy], err_msg,
@@ -1058,13 +1060,13 @@ def assert_array_almost_equal(x, y, decimal=6, err_msg='', verbose=True):
"""
__tracebackhide__ = True # Hide traceback for py.test
- from numpy.core import number, float_, result_type, array
+ from numpy.core import number, float_, result_type
from numpy.core.numerictypes import issubdtype
from numpy.core.fromnumeric import any as npany
def compare(x, y):
try:
- if npany(gisinf(x)) or npany( gisinf(y)):
+ if npany(gisinf(x)) or npany(gisinf(y)):
xinfid = gisinf(x)
yinfid = gisinf(y)
if not (xinfid == yinfid).all():
@@ -1106,8 +1108,6 @@ def assert_array_less(x, y, err_msg='', verbose=True):
compared, no assertion is raised if both objects have NaNs in the same
positions.
-
-
Parameters
----------
x : array_like
@@ -1129,8 +1129,6 @@ def assert_array_less(x, y, err_msg='', verbose=True):
assert_array_equal: tests objects for equality
assert_array_almost_equal: test objects for equality up to precision
-
-
Examples
--------
>>> np.testing.assert_array_less([1.0, 1.0, np.nan], [1.1, 2.0, np.nan])
@@ -1323,8 +1321,10 @@ class _Dummy(unittest.TestCase):
def nop(self):
pass
+
_d = _Dummy('nop')
+
def assert_raises(*args, **kwargs):
"""
assert_raises(exception_class, callable, *args, **kwargs)
@@ -1351,7 +1351,7 @@ def assert_raises(*args, **kwargs):
"""
__tracebackhide__ = True # Hide traceback for py.test
- return _d.assertRaises(*args,**kwargs)
+ return _d.assertRaises(*args, **kwargs)
def assert_raises_regex(exception_class, expected_regexp, *args, **kwargs):
@@ -1502,7 +1502,7 @@ def assert_allclose(actual, desired, rtol=1e-7, atol=0, equal_nan=True,
Given two array_like objects, check that their shapes and all elements
are equal (but see the Notes for the special handling of a scalar). An
- exception is raised if the shapes mismatch or any values conflict. In
+ exception is raised if the shapes mismatch or any values conflict. In
contrast to the standard usage in numpy, NaNs are compared like numbers,
no assertion is raised if both objects have NaNs in the same positions.
@@ -2424,6 +2424,7 @@ def assert_no_gc_cycles(*args, **kwargs):
with _assert_no_gc_cycles_context(name=func.__name__):
func(*args, **kwargs)
+
def break_cycles():
"""
Break reference cycles by calling gc.collect
@@ -2556,7 +2557,7 @@ def _no_tracing(func):
def _get_glibc_version():
try:
ver = os.confstr('CS_GNU_LIBC_VERSION').rsplit(' ')[1]
- except Exception as inst:
+ except Exception:
ver = '0.0'
return ver
diff --git a/numpy/testing/overrides.py b/numpy/testing/overrides.py
index 781f7d100..edc7132c2 100644
--- a/numpy/testing/overrides.py
+++ b/numpy/testing/overrides.py
@@ -38,9 +38,9 @@ def allows_array_ufunc_override(func):
`True` if `func` is overridable via `__array_ufunc__` and
`False` otherwise.
- Note
- ----
- This function is equivalent to `isinstance(func, np.ufunc)` and
+ Notes
+ -----
+ This function is equivalent to ``isinstance(func, np.ufunc)`` and
will work correctly for ufuncs defined outside of Numpy.
"""
diff --git a/numpy/tests/test_public_api.py b/numpy/tests/test_public_api.py
index c2df50494..108ba0b74 100644
--- a/numpy/tests/test_public_api.py
+++ b/numpy/tests/test_public_api.py
@@ -472,6 +472,10 @@ def test_api_importable():
@pytest.mark.xfail(
+ hasattr(np.__config__, "_built_with_meson"),
+ reason = "Meson does not yet support entry points via pyproject.toml",
+)
+@pytest.mark.xfail(
sysconfig.get_config_var("Py_DEBUG") is not None,
reason=(
"NumPy possibly built with `USE_DEBUG=True ./tools/travis-test.sh`, "