Skip to content

Commit 2efae2b

Browse files
authored
Merge pull request numpy#26724 from mtsokol/array-api-2023
ENH: Add Array API 2023.12 version support
2 parents 5c7b7b6 + b6fcc19 commit 2efae2b

File tree

18 files changed

+536
-50
lines changed

18 files changed

+536
-50
lines changed

.github/workflows/linux.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ jobs:
231231
uses: actions/checkout@v4
232232
with:
233233
repository: data-apis/array-api-tests
234-
ref: '3cf8ef654c456d9fd1633d64e67b4470465940e9' # Latest commit as of 2024-04-09
234+
ref: '809a1984414cfc0bca68a823aeaeba7df3900d17' # Latest commit as of 2024-06-26
235235
submodules: 'true'
236236
path: 'array-api-tests'
237237
- name: Set up Python
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
* `numpy.cumulative_sum` and `numpy.cumulative_prod` were added as Array API
2+
compatible alternatives for `numpy.cumsum` and `numpy.cumprod`. The new functions
3+
can include a fixed initial (zeros for ``sum`` and ones for ``prod``) in the result.
4+
* `numpy.clip` now supports ``max`` and ``min`` keyword arguments which are meant
5+
to replace ``a_min`` and ``a_max``. Also, for ``np.clip(a)`` or ``np.clip(a, None, None)``
6+
a copy of the input array will be returned instead of raising an error.
7+
* `numpy.astype` now supports ``device`` argument.

doc/source/reference/routines.math.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ Sums, products, differences
6363
sum
6464
nanprod
6565
nansum
66+
cumulative_sum
67+
cumulative_prod
6668
cumprod
6769
cumsum
6870
nancumprod

numpy/__init__.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -134,23 +134,23 @@
134134
can_cast, cbrt, cdouble, ceil, character, choose, clip, clongdouble,
135135
complex128, complex64, complexfloating, compress, concat, concatenate,
136136
conj, conjugate, convolve, copysign, copyto, correlate, cos, cosh,
137-
count_nonzero, cross, csingle, cumprod, cumsum,
138-
datetime64, datetime_as_string, datetime_data, deg2rad, degrees,
139-
diagonal, divide, divmod, dot, double, dtype, e, einsum, einsum_path,
140-
empty, empty_like, equal, errstate, euler_gamma, exp, exp2, expm1,
141-
fabs, finfo, flatiter, flatnonzero, flexible, float16, float32,
142-
float64, float_power, floating, floor, floor_divide, fmax, fmin, fmod,
143-
format_float_positional, format_float_scientific, frexp, from_dlpack,
144-
frombuffer, fromfile, fromfunction, fromiter, frompyfunc, fromstring,
145-
full, full_like, gcd, generic, geomspace, get_printoptions,
146-
getbufsize, geterr, geterrcall, greater, greater_equal, half,
147-
heaviside, hstack, hypot, identity, iinfo, iinfo, indices, inexact,
148-
inf, inner, int16, int32, int64, int8, int_, intc, integer, intp,
149-
invert, is_busday, isclose, isdtype, isfinite, isfortran, isinf,
150-
isnan, isnat, isscalar, issubdtype, lcm, ldexp, left_shift, less,
151-
less_equal, lexsort, linspace, little_endian, log, log10, log1p, log2,
152-
logaddexp, logaddexp2, logical_and, logical_not, logical_or,
153-
logical_xor, logspace, long, longdouble, longlong, matmul,
137+
count_nonzero, cross, csingle, cumprod, cumsum, cumulative_prod,
138+
cumulative_sum, datetime64, datetime_as_string, datetime_data,
139+
deg2rad, degrees, diagonal, divide, divmod, dot, double, dtype, e,
140+
einsum, einsum_path, empty, empty_like, equal, errstate, euler_gamma,
141+
exp, exp2, expm1, fabs, finfo, flatiter, flatnonzero, flexible,
142+
float16, float32, float64, float_power, floating, floor, floor_divide,
143+
fmax, fmin, fmod, format_float_positional, format_float_scientific,
144+
frexp, from_dlpack, frombuffer, fromfile, fromfunction, fromiter,
145+
frompyfunc, fromstring, full, full_like, gcd, generic, geomspace,
146+
get_printoptions, getbufsize, geterr, geterrcall, greater,
147+
greater_equal, half, heaviside, hstack, hypot, identity, iinfo, iinfo,
148+
indices, inexact, inf, inner, int16, int32, int64, int8, int_, intc,
149+
integer, intp, invert, is_busday, isclose, isdtype, isfinite,
150+
isfortran, isinf, isnan, isnat, isscalar, issubdtype, lcm, ldexp,
151+
left_shift, less, less_equal, lexsort, linspace, little_endian, log,
152+
log10, log1p, log2, logaddexp, logaddexp2, logical_and, logical_not,
153+
logical_or, logical_xor, logspace, long, longdouble, longlong, matmul,
154154
matrix_transpose, max, maximum, may_share_memory, mean, memmap, min,
155155
min_scalar_type, minimum, mod, modf, moveaxis, multiply, nan, ndarray,
156156
ndim, nditer, negative, nested_iters, newaxis, nextafter, nonzero,
@@ -290,7 +290,7 @@
290290
# import with `from numpy import *`.
291291
__future_scalars__ = {"str", "bytes", "object"}
292292

293-
__array_api_version__ = "2022.12"
293+
__array_api_version__ = "2023.12"
294294

295295
from ._array_api_info import __array_namespace_info__
296296

numpy/__init__.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,13 +260,15 @@ from numpy._core.fromnumeric import (
260260
all as all,
261261
any as any,
262262
cumsum as cumsum,
263+
cumulative_sum as cumulative_sum,
263264
ptp as ptp,
264265
max as max,
265266
min as min,
266267
amax as amax,
267268
amin as amin,
268269
prod as prod,
269270
cumprod as cumprod,
271+
cumulative_prod as cumulative_prod,
270272
ndim as ndim,
271273
size as size,
272274
around as around,

numpy/_core/_methods.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ def _count_reduce_items(arr, axis, keepdims=False, where=True):
9898

9999
def _clip(a, min=None, max=None, out=None, **kwargs):
100100
if min is None and max is None:
101-
raise ValueError("One of max or min must be given")
102-
101+
# return identity
102+
return um.positive(a, out=out, **kwargs)
103103
if min is None:
104104
return um.minimum(a, max, out=out, **kwargs)
105105
elif max is None:

numpy/_core/fromnumeric.py

Lines changed: 227 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121
__all__ = [
2222
'all', 'amax', 'amin', 'any', 'argmax',
2323
'argmin', 'argpartition', 'argsort', 'around', 'choose', 'clip',
24-
'compress', 'cumprod', 'cumsum', 'diagonal', 'mean',
25-
'max', 'min', 'matrix_transpose',
24+
'compress', 'cumprod', 'cumsum', 'cumulative_prod', 'cumulative_sum',
25+
'diagonal', 'mean', 'max', 'min', 'matrix_transpose',
2626
'ndim', 'nonzero', 'partition', 'prod', 'ptp', 'put',
2727
'ravel', 'repeat', 'reshape', 'resize', 'round',
2828
'searchsorted', 'shape', 'size', 'sort', 'squeeze',
@@ -2210,12 +2210,14 @@ def compress(condition, a, axis=None, out=None):
22102210
return _wrapfunc(a, 'compress', condition, axis=axis, out=out)
22112211

22122212

2213-
def _clip_dispatcher(a, a_min, a_max, out=None, **kwargs):
2214-
return (a, a_min, a_max)
2213+
def _clip_dispatcher(a, a_min=None, a_max=None, out=None, *, min=None,
2214+
max=None, **kwargs):
2215+
return (a, a_min, a_max, out, min, max)
22152216

22162217

22172218
@array_function_dispatch(_clip_dispatcher)
2218-
def clip(a, a_min, a_max, out=None, **kwargs):
2219+
def clip(a, a_min=np._NoValue, a_max=np._NoValue, out=None, *,
2220+
min=np._NoValue, max=np._NoValue, **kwargs):
22192221
"""
22202222
Clip (limit) the values in an array.
22212223
@@ -2234,12 +2236,19 @@ def clip(a, a_min, a_max, out=None, **kwargs):
22342236
Array containing elements to clip.
22352237
a_min, a_max : array_like or None
22362238
Minimum and maximum value. If ``None``, clipping is not performed on
2237-
the corresponding edge. Only one of `a_min` and `a_max` may be
2238-
``None``. Both are broadcast against `a`.
2239+
the corresponding edge. If both ``a_min`` and ``a_max`` are ``None``,
2240+
the elements of the returned array stay the same. Both are broadcasted
2241+
against ``a``.
22392242
out : ndarray, optional
22402243
The results will be placed in this array. It may be the input
22412244
array for in-place clipping. `out` must be of the right shape
22422245
to hold the output. Its type is preserved.
2246+
min, max : array_like or None
2247+
Array API compatible alternatives for ``a_min`` and ``a_max``
2248+
arguments. Either ``a_min`` and ``a_max`` or ``min`` and ``max``
2249+
can be passed at the same time. Default: ``None``.
2250+
2251+
.. versionadded:: 2.1.0
22432252
**kwargs
22442253
For other keyword-only arguments, see the
22452254
:ref:`ufunc docs <ufuncs.kwargs>`.
@@ -2283,6 +2292,19 @@ def clip(a, a_min, a_max, out=None, **kwargs):
22832292
array([3, 4, 2, 3, 4, 5, 6, 7, 8, 8])
22842293
22852294
"""
2295+
if a_min is np._NoValue and a_max is np._NoValue:
2296+
a_min = None if min is np._NoValue else min
2297+
a_max = None if max is np._NoValue else max
2298+
elif a_min is np._NoValue:
2299+
raise TypeError("clip() missing 1 required positional "
2300+
"argument: 'a_min'")
2301+
elif a_max is np._NoValue:
2302+
raise TypeError("clip() missing 1 required positional "
2303+
"argument: 'a_max'")
2304+
elif min is not np._NoValue or max is not np._NoValue:
2305+
raise ValueError("Passing `min` or `max` keyword argument when "
2306+
"`a_min` and `a_max` are provided is forbidden.")
2307+
22862308
return _wrapfunc(a, 'clip', a_min, a_max, out=out, **kwargs)
22872309

22882310

@@ -2643,6 +2665,202 @@ def all(a, axis=None, out=None, keepdims=np._NoValue, *, where=np._NoValue):
26432665
keepdims=keepdims, where=where)
26442666

26452667

2668+
def _cumulative_func(x, func, axis, dtype, out, include_initial):
2669+
x = np.atleast_1d(x)
2670+
x_ndim = x.ndim
2671+
if axis is None:
2672+
if x_ndim >= 2:
2673+
raise ValueError("For arrays which have more than one dimension "
2674+
"``axis`` argument is required.")
2675+
axis = 0
2676+
2677+
if out is not None and include_initial:
2678+
item = [slice(None)] * x_ndim
2679+
item[axis] = slice(1, None)
2680+
func.accumulate(x, axis=axis, dtype=dtype, out=out[tuple(item)])
2681+
item[axis] = 0
2682+
out[tuple(item)] = func.identity
2683+
return out
2684+
2685+
res = func.accumulate(x, axis=axis, dtype=dtype, out=out)
2686+
if include_initial:
2687+
initial_shape = list(x.shape)
2688+
initial_shape[axis] = 1
2689+
res = np.concat(
2690+
[np.full_like(res, func.identity, shape=initial_shape), res],
2691+
axis=axis,
2692+
)
2693+
2694+
return res
2695+
2696+
2697+
def _cumulative_prod_dispatcher(x, /, *, axis=None, dtype=None, out=None,
2698+
include_initial=None):
2699+
return (x, out)
2700+
2701+
2702+
@array_function_dispatch(_cumulative_prod_dispatcher)
2703+
def cumulative_prod(x, /, *, axis=None, dtype=None, out=None,
2704+
include_initial=False):
2705+
"""
2706+
Return the cumulative product of elements along a given axis.
2707+
2708+
This function is an Array API compatible alternative to `numpy.cumprod`.
2709+
2710+
Parameters
2711+
----------
2712+
x : array_like
2713+
Input array.
2714+
axis : int, optional
2715+
Axis along which the cumulative product is computed. The default
2716+
(None) is only allowed for one-dimensional arrays. For arrays
2717+
with more than one dimension ``axis`` is required.
2718+
dtype : dtype, optional
2719+
Type of the returned array, as well as of the accumulator in which
2720+
the elements are multiplied. If ``dtype`` is not specified, it
2721+
defaults to the dtype of ``x``, unless ``x`` has an integer dtype
2722+
with a precision less than that of the default platform integer.
2723+
In that case, the default platform integer is used instead.
2724+
out : ndarray, optional
2725+
Alternative output array in which to place the result. It must
2726+
have the same shape and buffer length as the expected output
2727+
but the type of the resulting values will be cast if necessary.
2728+
See :ref:`ufuncs-output-type` for more details.
2729+
include_initial : bool, optional
2730+
Boolean indicating whether to include the initial value (ones) as
2731+
the first value in the output. With ``include_initial=True``
2732+
the shape of the output is different than the shape of the input.
2733+
Default: ``False``.
2734+
2735+
Returns
2736+
-------
2737+
cumulative_prod_along_axis : ndarray
2738+
A new array holding the result is returned unless ``out`` is
2739+
specified, in which case a reference to ``out`` is returned. The
2740+
result has the same shape as ``x`` if ``include_initial=False``.
2741+
2742+
Notes
2743+
-----
2744+
Arithmetic is modular when using integer types, and no error is
2745+
raised on overflow.
2746+
2747+
Examples
2748+
--------
2749+
>>> a = np.array([1, 2, 3])
2750+
>>> np.cumulative_prod(a) # intermediate results 1, 1*2
2751+
... # total product 1*2*3 = 6
2752+
array([1, 2, 6])
2753+
>>> a = np.array([1, 2, 3, 4, 5, 6])
2754+
>>> np.cumulative_prod(a, dtype=float) # specify type of output
2755+
array([ 1., 2., 6., 24., 120., 720.])
2756+
2757+
The cumulative product for each column (i.e., over the rows) of ``b``:
2758+
2759+
>>> b = np.array([[1, 2, 3], [4, 5, 6]])
2760+
>>> np.cumulative_prod(b, axis=0)
2761+
array([[ 1, 2, 3],
2762+
[ 4, 10, 18]])
2763+
2764+
The cumulative product for each row (i.e. over the columns) of ``b``:
2765+
2766+
>>> np.cumulative_prod(b, axis=1)
2767+
array([[ 1, 2, 6],
2768+
[ 4, 20, 120]])
2769+
2770+
"""
2771+
return _cumulative_func(x, um.multiply, axis, dtype, out, include_initial)
2772+
2773+
2774+
def _cumulative_sum_dispatcher(x, /, *, axis=None, dtype=None, out=None,
2775+
include_initial=None):
2776+
return (x, out)
2777+
2778+
2779+
@array_function_dispatch(_cumulative_sum_dispatcher)
2780+
def cumulative_sum(x, /, *, axis=None, dtype=None, out=None,
2781+
include_initial=False):
2782+
"""
2783+
Return the cumulative sum of the elements along a given axis.
2784+
2785+
This function is an Array API compatible alternative to `numpy.cumsum`.
2786+
2787+
Parameters
2788+
----------
2789+
x : array_like
2790+
Input array.
2791+
axis : int, optional
2792+
Axis along which the cumulative sum is computed. The default
2793+
(None) is only allowed for one-dimensional arrays. For arrays
2794+
with more than one dimension ``axis`` is required.
2795+
dtype : dtype, optional
2796+
Type of the returned array and of the accumulator in which the
2797+
elements are summed. If ``dtype`` is not specified, it defaults
2798+
to the dtype of ``x``, unless ``x`` has an integer dtype with
2799+
a precision less than that of the default platform integer.
2800+
In that case, the default platform integer is used.
2801+
out : ndarray, optional
2802+
Alternative output array in which to place the result. It must
2803+
have the same shape and buffer length as the expected output
2804+
but the type will be cast if necessary. See :ref:`ufuncs-output-type`
2805+
for more details.
2806+
include_initial : bool, optional
2807+
Boolean indicating whether to include the initial value (ones) as
2808+
the first value in the output. With ``include_initial=True``
2809+
the shape of the output is different than the shape of the input.
2810+
Default: ``False``.
2811+
2812+
Returns
2813+
-------
2814+
cumulative_sum_along_axis : ndarray
2815+
A new array holding the result is returned unless ``out`` is
2816+
specified, in which case a reference to ``out`` is returned. The
2817+
result has the same shape as ``x`` if ``include_initial=False``.
2818+
2819+
See Also
2820+
--------
2821+
sum : Sum array elements.
2822+
trapezoid : Integration of array values using composite trapezoidal rule.
2823+
diff : Calculate the n-th discrete difference along given axis.
2824+
2825+
Notes
2826+
-----
2827+
Arithmetic is modular when using integer types, and no error is
2828+
raised on overflow.
2829+
2830+
``cumulative_sum(a)[-1]`` may not be equal to ``sum(a)`` for
2831+
floating-point values since ``sum`` may use a pairwise summation routine,
2832+
reducing the roundoff-error. See `sum` for more information.
2833+
2834+
Examples
2835+
--------
2836+
>>> a = np.array([1, 2, 3, 4, 5, 6])
2837+
>>> a
2838+
array([1, 2, 3, 4, 5, 6])
2839+
>>> np.cumulative_sum(a)
2840+
array([ 1, 3, 6, 10, 15, 21])
2841+
>>> np.cumulative_sum(a, dtype=float) # specifies type of output value(s)
2842+
array([ 1., 3., 6., 10., 15., 21.])
2843+
2844+
>>> b = np.array([[1, 2, 3], [4, 5, 6]])
2845+
>>> np.cumulative_sum(b,axis=0) # sum over rows for each of the 3 columns
2846+
array([[1, 2, 3],
2847+
[5, 7, 9]])
2848+
>>> np.cumulative_sum(b,axis=1) # sum over columns for each of the 2 rows
2849+
array([[ 1, 3, 6],
2850+
[ 4, 9, 15]])
2851+
2852+
``cumulative_sum(c)[-1]`` may not be equal to ``sum(c)``
2853+
2854+
>>> c = np.array([1, 2e-9, 3e-9] * 1000000)
2855+
>>> np.cumulative_sum(c)[-1]
2856+
1000000.0050045159
2857+
>>> c.sum()
2858+
1000000.0050000029
2859+
2860+
"""
2861+
return _cumulative_func(x, um.add, axis, dtype, out, include_initial)
2862+
2863+
26462864
def _cumsum_dispatcher(a, axis=None, dtype=None, out=None):
26472865
return (a, out)
26482866

@@ -2681,6 +2899,7 @@ def cumsum(a, axis=None, dtype=None, out=None):
26812899
26822900
See Also
26832901
--------
2902+
cumulative_sum : Array API compatible alternative for ``cumsum``.
26842903
sum : Sum array elements.
26852904
trapezoid : Integration of array values using composite trapezoidal rule.
26862905
diff : Calculate the n-th discrete difference along given axis.
@@ -3269,6 +3488,7 @@ def cumprod(a, axis=None, dtype=None, out=None):
32693488
32703489
See Also
32713490
--------
3491+
cumulative_prod : Array API compatible alternative for ``cumprod``.
32723492
:ref:`ufuncs-output-type`
32733493
32743494
Notes

0 commit comments

Comments
 (0)