From 012343dec5599418b77512733fc5b8db6bc14c4c Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 11 Jan 2021 16:03:43 -0700 Subject: Add initial array_api sub-namespace This is based on the function stubs from the array API test suite, and is currently based on the assumption that NumPy already follows the array API standard. Now it needs to be modified to fix it in the places where NumPy deviates (for example, different function names for inverse trigonometric functions). --- numpy/_array_api/__init__.py | 45 ++++++ numpy/_array_api/constants.py | 3 + numpy/_array_api/creation_functions.py | 45 ++++++ numpy/_array_api/elementwise_functions.py | 221 +++++++++++++++++++++++++++ numpy/_array_api/linear_algebra_functions.py | 91 +++++++++++ numpy/_array_api/manipulation_functions.py | 29 ++++ numpy/_array_api/searching_functions.py | 17 +++ numpy/_array_api/set_functions.py | 5 + numpy/_array_api/sorting_functions.py | 9 ++ numpy/_array_api/statistical_functions.py | 29 ++++ numpy/_array_api/utility_functions.py | 9 ++ 11 files changed, 503 insertions(+) create mode 100644 numpy/_array_api/__init__.py create mode 100644 numpy/_array_api/constants.py create mode 100644 numpy/_array_api/creation_functions.py create mode 100644 numpy/_array_api/elementwise_functions.py create mode 100644 numpy/_array_api/linear_algebra_functions.py create mode 100644 numpy/_array_api/manipulation_functions.py create mode 100644 numpy/_array_api/searching_functions.py create mode 100644 numpy/_array_api/set_functions.py create mode 100644 numpy/_array_api/sorting_functions.py create mode 100644 numpy/_array_api/statistical_functions.py create mode 100644 numpy/_array_api/utility_functions.py (limited to 'numpy') diff --git a/numpy/_array_api/__init__.py b/numpy/_array_api/__init__.py new file mode 100644 index 000000000..878251e7c --- /dev/null +++ b/numpy/_array_api/__init__.py @@ -0,0 +1,45 @@ +__all__ = [] + +from .constants import e, inf, nan, pi + +__all__ += ['e', 'inf', 'nan', 'pi'] + +from .creation_functions import arange, empty, empty_like, eye, full, full_like, linspace, ones, ones_like, zeros, zeros_like + +__all__ += ['arange', 'empty', 'empty_like', 'eye', 'full', 'full_like', 'linspace', 'ones', 'ones_like', 'zeros', 'zeros_like'] + +from .elementwise_functions import abs, acos, acosh, add, asin, asinh, atan, atan2, atanh, bitwise_and, bitwise_left_shift, bitwise_invert, bitwise_or, bitwise_right_shift, bitwise_xor, ceil, cos, cosh, divide, equal, exp, expm1, floor, floor_divide, greater, greater_equal, isfinite, isinf, isnan, less, less_equal, log, log1p, log2, log10, logical_and, logical_not, logical_or, logical_xor, multiply, negative, not_equal, positive, pow, remainder, round, sign, sin, sinh, square, sqrt, subtract, tan, tanh, trunc + +__all__ += ['abs', 'acos', 'acosh', 'add', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'bitwise_and', 'bitwise_left_shift', 'bitwise_invert', 'bitwise_or', 'bitwise_right_shift', 'bitwise_xor', 'ceil', 'cos', 'cosh', 'divide', 'equal', 'exp', 'expm1', 'floor', 'floor_divide', 'greater', 'greater_equal', 'isfinite', 'isinf', 'isnan', 'less', 'less_equal', 'log', 'log1p', 'log2', 'log10', 'logical_and', 'logical_not', 'logical_or', 'logical_xor', 'multiply', 'negative', 'not_equal', 'positive', 'pow', 'remainder', 'round', 'sign', 'sin', 'sinh', 'square', 'sqrt', 'subtract', 'tan', 'tanh', 'trunc'] + +from .linear_algebra_functions import cross, det, diagonal, inv, norm, outer, trace, transpose + +__all__ += ['cross', 'det', 'diagonal', 'inv', 'norm', 'outer', 'trace', 'transpose'] + +# from .linear_algebra_functions import cholesky, cross, det, diagonal, dot, eig, eigvalsh, einsum, inv, lstsq, matmul, matrix_power, matrix_rank, norm, outer, pinv, qr, slogdet, solve, svd, trace, transpose +# +# __all__ += ['cholesky', 'cross', 'det', 'diagonal', 'dot', 'eig', 'eigvalsh', 'einsum', 'inv', 'lstsq', 'matmul', 'matrix_power', 'matrix_rank', 'norm', 'outer', 'pinv', 'qr', 'slogdet', 'solve', 'svd', 'trace', 'transpose'] + +from .manipulation_functions import concat, expand_dims, flip, reshape, roll, squeeze, stack + +__all__ += ['concat', 'expand_dims', 'flip', 'reshape', 'roll', 'squeeze', 'stack'] + +from .searching_functions import argmax, argmin, nonzero, where + +__all__ += ['argmax', 'argmin', 'nonzero', 'where'] + +from .set_functions import unique + +__all__ += ['unique'] + +from .sorting_functions import argsort, sort + +__all__ += ['argsort', 'sort'] + +from .statistical_functions import max, mean, min, prod, std, sum, var + +__all__ += ['max', 'mean', 'min', 'prod', 'std', 'sum', 'var'] + +from .utility_functions import all, any + +__all__ += ['all', 'any'] diff --git a/numpy/_array_api/constants.py b/numpy/_array_api/constants.py new file mode 100644 index 000000000..000777029 --- /dev/null +++ b/numpy/_array_api/constants.py @@ -0,0 +1,3 @@ +from .. import e, inf, nan, pi + +__all__ = ['e', 'inf', 'nan', 'pi'] diff --git a/numpy/_array_api/creation_functions.py b/numpy/_array_api/creation_functions.py new file mode 100644 index 000000000..50b0bd252 --- /dev/null +++ b/numpy/_array_api/creation_functions.py @@ -0,0 +1,45 @@ +def arange(start, /, *, stop=None, step=1, dtype=None): + from .. import arange + return arange(start, stop=stop, step=step, dtype=dtype) + +def empty(shape, /, *, dtype=None): + from .. import empty + return empty(shape, dtype=dtype) + +def empty_like(x, /, *, dtype=None): + from .. import empty_like + return empty_like(x, dtype=dtype) + +def eye(N, /, *, M=None, k=0, dtype=None): + from .. import eye + return eye(N, M=M, k=k, dtype=dtype) + +def full(shape, fill_value, /, *, dtype=None): + from .. import full + return full(shape, fill_value, dtype=dtype) + +def full_like(x, fill_value, /, *, dtype=None): + from .. import full_like + return full_like(x, fill_value, dtype=dtype) + +def linspace(start, stop, num, /, *, dtype=None, endpoint=True): + from .. import linspace + return linspace(start, stop, num, dtype=dtype, endpoint=endpoint) + +def ones(shape, /, *, dtype=None): + from .. import ones + return ones(shape, dtype=dtype) + +def ones_like(x, /, *, dtype=None): + from .. import ones_like + return ones_like(x, dtype=dtype) + +def zeros(shape, /, *, dtype=None): + from .. import zeros + return zeros(shape, dtype=dtype) + +def zeros_like(x, /, *, dtype=None): + from .. import zeros_like + return zeros_like(x, dtype=dtype) + +__all__ = ['arange', 'empty', 'empty_like', 'eye', 'full', 'full_like', 'linspace', 'ones', 'ones_like', 'zeros', 'zeros_like'] diff --git a/numpy/_array_api/elementwise_functions.py b/numpy/_array_api/elementwise_functions.py new file mode 100644 index 000000000..3e8349d29 --- /dev/null +++ b/numpy/_array_api/elementwise_functions.py @@ -0,0 +1,221 @@ +def abs(x, /): + from .. import abs + return abs(x) + +def acos(x, /): + from .. import acos + return acos(x) + +def acosh(x, /): + from .. import acosh + return acosh(x) + +def add(x1, x2, /): + from .. import add + return add(x1, x2) + +def asin(x, /): + from .. import asin + return asin(x) + +def asinh(x, /): + from .. import asinh + return asinh(x) + +def atan(x, /): + from .. import atan + return atan(x) + +def atan2(x1, x2, /): + from .. import atan2 + return atan2(x1, x2) + +def atanh(x, /): + from .. import atanh + return atanh(x) + +def bitwise_and(x1, x2, /): + from .. import bitwise_and + return bitwise_and(x1, x2) + +def bitwise_left_shift(x1, x2, /): + from .. import bitwise_left_shift + return bitwise_left_shift(x1, x2) + +def bitwise_invert(x, /): + from .. import bitwise_invert + return bitwise_invert(x) + +def bitwise_or(x1, x2, /): + from .. import bitwise_or + return bitwise_or(x1, x2) + +def bitwise_right_shift(x1, x2, /): + from .. import bitwise_right_shift + return bitwise_right_shift(x1, x2) + +def bitwise_xor(x1, x2, /): + from .. import bitwise_xor + return bitwise_xor(x1, x2) + +def ceil(x, /): + from .. import ceil + return ceil(x) + +def cos(x, /): + from .. import cos + return cos(x) + +def cosh(x, /): + from .. import cosh + return cosh(x) + +def divide(x1, x2, /): + from .. import divide + return divide(x1, x2) + +def equal(x1, x2, /): + from .. import equal + return equal(x1, x2) + +def exp(x, /): + from .. import exp + return exp(x) + +def expm1(x, /): + from .. import expm1 + return expm1(x) + +def floor(x, /): + from .. import floor + return floor(x) + +def floor_divide(x1, x2, /): + from .. import floor_divide + return floor_divide(x1, x2) + +def greater(x1, x2, /): + from .. import greater + return greater(x1, x2) + +def greater_equal(x1, x2, /): + from .. import greater_equal + return greater_equal(x1, x2) + +def isfinite(x, /): + from .. import isfinite + return isfinite(x) + +def isinf(x, /): + from .. import isinf + return isinf(x) + +def isnan(x, /): + from .. import isnan + return isnan(x) + +def less(x1, x2, /): + from .. import less + return less(x1, x2) + +def less_equal(x1, x2, /): + from .. import less_equal + return less_equal(x1, x2) + +def log(x, /): + from .. import log + return log(x) + +def log1p(x, /): + from .. import log1p + return log1p(x) + +def log2(x, /): + from .. import log2 + return log2(x) + +def log10(x, /): + from .. import log10 + return log10(x) + +def logical_and(x1, x2, /): + from .. import logical_and + return logical_and(x1, x2) + +def logical_not(x, /): + from .. import logical_not + return logical_not(x) + +def logical_or(x1, x2, /): + from .. import logical_or + return logical_or(x1, x2) + +def logical_xor(x1, x2, /): + from .. import logical_xor + return logical_xor(x1, x2) + +def multiply(x1, x2, /): + from .. import multiply + return multiply(x1, x2) + +def negative(x, /): + from .. import negative + return negative(x) + +def not_equal(x1, x2, /): + from .. import not_equal + return not_equal(x1, x2) + +def positive(x, /): + from .. import positive + return positive(x) + +def pow(x1, x2, /): + from .. import pow + return pow(x1, x2) + +def remainder(x1, x2, /): + from .. import remainder + return remainder(x1, x2) + +def round(x, /): + from .. import round + return round(x) + +def sign(x, /): + from .. import sign + return sign(x) + +def sin(x, /): + from .. import sin + return sin(x) + +def sinh(x, /): + from .. import sinh + return sinh(x) + +def square(x, /): + from .. import square + return square(x) + +def sqrt(x, /): + from .. import sqrt + return sqrt(x) + +def subtract(x1, x2, /): + from .. import subtract + return subtract(x1, x2) + +def tan(x, /): + from .. import tan + return tan(x) + +def tanh(x, /): + from .. import tanh + return tanh(x) + +def trunc(x, /): + from .. import trunc + return trunc(x) + +__all__ = ['abs', 'acos', 'acosh', 'add', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'bitwise_and', 'bitwise_left_shift', 'bitwise_invert', 'bitwise_or', 'bitwise_right_shift', 'bitwise_xor', 'ceil', 'cos', 'cosh', 'divide', 'equal', 'exp', 'expm1', 'floor', 'floor_divide', 'greater', 'greater_equal', 'isfinite', 'isinf', 'isnan', 'less', 'less_equal', 'log', 'log1p', 'log2', 'log10', 'logical_and', 'logical_not', 'logical_or', 'logical_xor', 'multiply', 'negative', 'not_equal', 'positive', 'pow', 'remainder', 'round', 'sign', 'sin', 'sinh', 'square', 'sqrt', 'subtract', 'tan', 'tanh', 'trunc'] diff --git a/numpy/_array_api/linear_algebra_functions.py b/numpy/_array_api/linear_algebra_functions.py new file mode 100644 index 000000000..5da7ac17b --- /dev/null +++ b/numpy/_array_api/linear_algebra_functions.py @@ -0,0 +1,91 @@ +# def cholesky(): +# from .. import cholesky +# return cholesky() + +def cross(x1, x2, /, *, axis=-1): + from .. import cross + return cross(x1, x2, axis=axis) + +def det(x, /): + from .. import det + return det(x) + +def diagonal(x, /, *, axis1=0, axis2=1, offset=0): + from .. import diagonal + return diagonal(x, axis1=axis1, axis2=axis2, offset=offset) + +# def dot(): +# from .. import dot +# return dot() +# +# def eig(): +# from .. import eig +# return eig() +# +# def eigvalsh(): +# from .. import eigvalsh +# return eigvalsh() +# +# def einsum(): +# from .. import einsum +# return einsum() + +def inv(x): + from .. import inv + return inv(x) + +# def lstsq(): +# from .. import lstsq +# return lstsq() +# +# def matmul(): +# from .. import matmul +# return matmul() +# +# def matrix_power(): +# from .. import matrix_power +# return matrix_power() +# +# def matrix_rank(): +# from .. import matrix_rank +# return matrix_rank() + +def norm(x, /, *, axis=None, keepdims=False, ord=None): + from .. import norm + return norm(x, axis=axis, keepdims=keepdims, ord=ord) + +def outer(x1, x2, /): + from .. import outer + return outer(x1, x2) + +# def pinv(): +# from .. import pinv +# return pinv() +# +# def qr(): +# from .. import qr +# return qr() +# +# def slogdet(): +# from .. import slogdet +# return slogdet() +# +# def solve(): +# from .. import solve +# return solve() +# +# def svd(): +# from .. import svd +# return svd() + +def trace(x, /, *, axis1=0, axis2=1, offset=0): + from .. import trace + return trace(x, axis1=axis1, axis2=axis2, offset=offset) + +def transpose(x, /, *, axes=None): + from .. import transpose + return transpose(x, axes=axes) + +# __all__ = ['cholesky', 'cross', 'det', 'diagonal', 'dot', 'eig', 'eigvalsh', 'einsum', 'inv', 'lstsq', 'matmul', 'matrix_power', 'matrix_rank', 'norm', 'outer', 'pinv', 'qr', 'slogdet', 'solve', 'svd', 'trace', 'transpose'] + +__all__ = ['cross', 'det', 'diagonal', 'inv', 'norm', 'outer', 'trace', 'transpose'] diff --git a/numpy/_array_api/manipulation_functions.py b/numpy/_array_api/manipulation_functions.py new file mode 100644 index 000000000..1934e8e4e --- /dev/null +++ b/numpy/_array_api/manipulation_functions.py @@ -0,0 +1,29 @@ +def concat(arrays, /, *, axis=0): + from .. import concat + return concat(arrays, axis=axis) + +def expand_dims(x, axis, /): + from .. import expand_dims + return expand_dims(x, axis) + +def flip(x, /, *, axis=None): + from .. import flip + return flip(x, axis=axis) + +def reshape(x, shape, /): + from .. import reshape + return reshape(x, shape) + +def roll(x, shift, /, *, axis=None): + from .. import roll + return roll(x, shift, axis=axis) + +def squeeze(x, /, *, axis=None): + from .. import squeeze + return squeeze(x, axis=axis) + +def stack(arrays, /, *, axis=0): + from .. import stack + return stack(arrays, axis=axis) + +__all__ = ['concat', 'expand_dims', 'flip', 'reshape', 'roll', 'squeeze', 'stack'] diff --git a/numpy/_array_api/searching_functions.py b/numpy/_array_api/searching_functions.py new file mode 100644 index 000000000..c4b6c58b5 --- /dev/null +++ b/numpy/_array_api/searching_functions.py @@ -0,0 +1,17 @@ +def argmax(x, /, *, axis=None, keepdims=False): + from .. import argmax + return argmax(x, axis=axis, keepdims=keepdims) + +def argmin(x, /, *, axis=None, keepdims=False): + from .. import argmin + return argmin(x, axis=axis, keepdims=keepdims) + +def nonzero(x, /): + from .. import nonzero + return nonzero(x) + +def where(condition, x1, x2, /): + from .. import where + return where(condition, x1, x2) + +__all__ = ['argmax', 'argmin', 'nonzero', 'where'] diff --git a/numpy/_array_api/set_functions.py b/numpy/_array_api/set_functions.py new file mode 100644 index 000000000..f218f1187 --- /dev/null +++ b/numpy/_array_api/set_functions.py @@ -0,0 +1,5 @@ +def unique(x, /, *, return_counts=False, return_index=False, return_inverse=False, sorted=True): + from .. import unique + return unique(x, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse, sorted=sorted) + +__all__ = ['unique'] diff --git a/numpy/_array_api/sorting_functions.py b/numpy/_array_api/sorting_functions.py new file mode 100644 index 000000000..384ec08f9 --- /dev/null +++ b/numpy/_array_api/sorting_functions.py @@ -0,0 +1,9 @@ +def argsort(x, /, *, axis=-1, descending=False, stable=True): + from .. import argsort + return argsort(x, axis=axis, descending=descending, stable=stable) + +def sort(x, /, *, axis=-1, descending=False, stable=True): + from .. import sort + return sort(x, axis=axis, descending=descending, stable=stable) + +__all__ = ['argsort', 'sort'] diff --git a/numpy/_array_api/statistical_functions.py b/numpy/_array_api/statistical_functions.py new file mode 100644 index 000000000..2cc712aea --- /dev/null +++ b/numpy/_array_api/statistical_functions.py @@ -0,0 +1,29 @@ +def max(x, /, *, axis=None, keepdims=False): + from .. import max + return max(x, axis=axis, keepdims=keepdims) + +def mean(x, /, *, axis=None, keepdims=False): + from .. import mean + return mean(x, axis=axis, keepdims=keepdims) + +def min(x, /, *, axis=None, keepdims=False): + from .. import min + return min(x, axis=axis, keepdims=keepdims) + +def prod(x, /, *, axis=None, keepdims=False): + from .. import prod + return prod(x, axis=axis, keepdims=keepdims) + +def std(x, /, *, axis=None, correction=0.0, keepdims=False): + from .. import std + return std(x, axis=axis, correction=correction, keepdims=keepdims) + +def sum(x, /, *, axis=None, keepdims=False): + from .. import sum + return sum(x, axis=axis, keepdims=keepdims) + +def var(x, /, *, axis=None, correction=0.0, keepdims=False): + from .. import var + return var(x, axis=axis, correction=correction, keepdims=keepdims) + +__all__ = ['max', 'mean', 'min', 'prod', 'std', 'sum', 'var'] diff --git a/numpy/_array_api/utility_functions.py b/numpy/_array_api/utility_functions.py new file mode 100644 index 000000000..eac0d4eaa --- /dev/null +++ b/numpy/_array_api/utility_functions.py @@ -0,0 +1,9 @@ +def all(x, /, *, axis=None, keepdims=False): + from .. import all + return all(x, axis=axis, keepdims=keepdims) + +def any(x, /, *, axis=None, keepdims=False): + from .. import any + return any(x, axis=axis, keepdims=keepdims) + +__all__ = ['all', 'any'] -- cgit v1.2.1 From 9934cf3abcd6ba9438c340042e94f8343e3f3d13 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 11 Jan 2021 16:36:11 -0700 Subject: Add dtypes to the _array_api namespace --- numpy/_array_api/__init__.py | 4 ++++ numpy/_array_api/dtypes.py | 3 +++ 2 files changed, 7 insertions(+) create mode 100644 numpy/_array_api/dtypes.py (limited to 'numpy') diff --git a/numpy/_array_api/__init__.py b/numpy/_array_api/__init__.py index 878251e7c..1677224c5 100644 --- a/numpy/_array_api/__init__.py +++ b/numpy/_array_api/__init__.py @@ -8,6 +8,10 @@ from .creation_functions import arange, empty, empty_like, eye, full, full_like, __all__ += ['arange', 'empty', 'empty_like', 'eye', 'full', 'full_like', 'linspace', 'ones', 'ones_like', 'zeros', 'zeros_like'] +from .dtypes import int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, bool + +__all__ += ['int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64', 'float32', 'float64', 'bool'] + from .elementwise_functions import abs, acos, acosh, add, asin, asinh, atan, atan2, atanh, bitwise_and, bitwise_left_shift, bitwise_invert, bitwise_or, bitwise_right_shift, bitwise_xor, ceil, cos, cosh, divide, equal, exp, expm1, floor, floor_divide, greater, greater_equal, isfinite, isinf, isnan, less, less_equal, log, log1p, log2, log10, logical_and, logical_not, logical_or, logical_xor, multiply, negative, not_equal, positive, pow, remainder, round, sign, sin, sinh, square, sqrt, subtract, tan, tanh, trunc __all__ += ['abs', 'acos', 'acosh', 'add', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'bitwise_and', 'bitwise_left_shift', 'bitwise_invert', 'bitwise_or', 'bitwise_right_shift', 'bitwise_xor', 'ceil', 'cos', 'cosh', 'divide', 'equal', 'exp', 'expm1', 'floor', 'floor_divide', 'greater', 'greater_equal', 'isfinite', 'isinf', 'isnan', 'less', 'less_equal', 'log', 'log1p', 'log2', 'log10', 'logical_and', 'logical_not', 'logical_or', 'logical_xor', 'multiply', 'negative', 'not_equal', 'positive', 'pow', 'remainder', 'round', 'sign', 'sin', 'sinh', 'square', 'sqrt', 'subtract', 'tan', 'tanh', 'trunc'] diff --git a/numpy/_array_api/dtypes.py b/numpy/_array_api/dtypes.py new file mode 100644 index 000000000..62fb3d321 --- /dev/null +++ b/numpy/_array_api/dtypes.py @@ -0,0 +1,3 @@ +from .. import int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, bool + +__all__ = ['int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64', 'float32', 'float64', 'bool'] -- cgit v1.2.1 From e00760ccbfdbefea1625f5407e94397f2c85e848 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 11 Jan 2021 16:43:51 -0700 Subject: Fix array API functions that are named differently or not in the default numpy namespace --- numpy/_array_api/elementwise_functions.py | 40 +++++++++++++++++----------- numpy/_array_api/linear_algebra_functions.py | 9 ++++--- numpy/_array_api/manipulation_functions.py | 5 ++-- 3 files changed, 33 insertions(+), 21 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/elementwise_functions.py b/numpy/_array_api/elementwise_functions.py index 3e8349d29..db7ce0a0e 100644 --- a/numpy/_array_api/elementwise_functions.py +++ b/numpy/_array_api/elementwise_functions.py @@ -3,36 +3,43 @@ def abs(x, /): return abs(x) def acos(x, /): - from .. import acos - return acos(x) + # Note: the function name is different here + from .. import arccos + return arccos(x) def acosh(x, /): - from .. import acosh - return acosh(x) + # Note: the function name is different here + from .. import arccosh + return arccosh(x) def add(x1, x2, /): from .. import add return add(x1, x2) def asin(x, /): - from .. import asin - return asin(x) + # Note: the function name is different here + from .. import arcsin + return arcsin(x) def asinh(x, /): - from .. import asinh - return asinh(x) + # Note: the function name is different here + from .. import arcsinh + return arcsinh(x) def atan(x, /): - from .. import atan - return atan(x) + # Note: the function name is different here + from .. import arctan + return arctan(x) def atan2(x1, x2, /): - from .. import atan2 - return atan2(x1, x2) + # Note: the function name is different here + from .. import arctan2 + return arctan2(x1, x2) def atanh(x, /): - from .. import atanh - return atanh(x) + # Note: the function name is different here + from .. import arctanh + return arctanh(x) def bitwise_and(x1, x2, /): from .. import bitwise_and @@ -171,8 +178,9 @@ def positive(x, /): return positive(x) def pow(x1, x2, /): - from .. import pow - return pow(x1, x2) + # Note: the function name is different here + from .. import power + return power(x1, x2) def remainder(x1, x2, /): from .. import remainder diff --git a/numpy/_array_api/linear_algebra_functions.py b/numpy/_array_api/linear_algebra_functions.py index 5da7ac17b..9995e6b98 100644 --- a/numpy/_array_api/linear_algebra_functions.py +++ b/numpy/_array_api/linear_algebra_functions.py @@ -7,7 +7,8 @@ def cross(x1, x2, /, *, axis=-1): return cross(x1, x2, axis=axis) def det(x, /): - from .. import det + # Note: this function is being imported from a nondefault namespace + from ..linalg import det return det(x) def diagonal(x, /, *, axis1=0, axis2=1, offset=0): @@ -31,7 +32,8 @@ def diagonal(x, /, *, axis1=0, axis2=1, offset=0): # return einsum() def inv(x): - from .. import inv + # Note: this function is being imported from a nondefault namespace + from ..linalg import inv return inv(x) # def lstsq(): @@ -51,7 +53,8 @@ def inv(x): # return matrix_rank() def norm(x, /, *, axis=None, keepdims=False, ord=None): - from .. import norm + # Note: this function is being imported from a nondefault namespace + from ..linalg import norm return norm(x, axis=axis, keepdims=keepdims, ord=ord) def outer(x1, x2, /): diff --git a/numpy/_array_api/manipulation_functions.py b/numpy/_array_api/manipulation_functions.py index 1934e8e4e..80ca3381e 100644 --- a/numpy/_array_api/manipulation_functions.py +++ b/numpy/_array_api/manipulation_functions.py @@ -1,6 +1,7 @@ def concat(arrays, /, *, axis=0): - from .. import concat - return concat(arrays, axis=axis) + # Note: the function name is different here + from .. import concatenate + return concatenate(arrays, axis=axis) def expand_dims(x, axis, /): from .. import expand_dims -- cgit v1.2.1 From fcff4e1d25abb173870fffdd0a0d1f63aca7fccf Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 11 Jan 2021 17:15:39 -0700 Subject: Fix the bool name in the array API namespace --- numpy/_array_api/dtypes.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/dtypes.py b/numpy/_array_api/dtypes.py index 62fb3d321..e94e70e9b 100644 --- a/numpy/_array_api/dtypes.py +++ b/numpy/_array_api/dtypes.py @@ -1,3 +1,5 @@ -from .. import int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, bool +from .. import int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64 +# Note: This name is changed +from .. import bool_ as bool __all__ = ['int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64', 'float32', 'float64', 'bool'] -- cgit v1.2.1 From f36b64848a4577188640cc146840d5652deb6bc0 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 11 Jan 2021 17:25:47 -0700 Subject: Fix different names for some bitwise functions in the array apis --- numpy/_array_api/elementwise_functions.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/elementwise_functions.py b/numpy/_array_api/elementwise_functions.py index db7ce0a0e..bacb733d9 100644 --- a/numpy/_array_api/elementwise_functions.py +++ b/numpy/_array_api/elementwise_functions.py @@ -46,20 +46,23 @@ def bitwise_and(x1, x2, /): return bitwise_and(x1, x2) def bitwise_left_shift(x1, x2, /): - from .. import bitwise_left_shift - return bitwise_left_shift(x1, x2) + # Note: the function name is different here + from .. import left_shift + return left_shift(x1, x2) def bitwise_invert(x, /): - from .. import bitwise_invert - return bitwise_invert(x) + # Note: the function name is different here + from .. import invert + return invert(x) def bitwise_or(x1, x2, /): from .. import bitwise_or return bitwise_or(x1, x2) def bitwise_right_shift(x1, x2, /): - from .. import bitwise_right_shift - return bitwise_right_shift(x1, x2) + # Note: the function name is different here + from .. import right_shift + return right_shift(x1, x2) def bitwise_xor(x1, x2, /): from .. import bitwise_xor -- cgit v1.2.1 From c8efdbb72eab78ed2fc735d3078ef8534dcc6ef7 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 11 Jan 2021 17:26:04 -0700 Subject: Fix different behavior of norm() with axis=None in the array API namespace --- numpy/_array_api/linear_algebra_functions.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/linear_algebra_functions.py b/numpy/_array_api/linear_algebra_functions.py index 9995e6b98..820dfffba 100644 --- a/numpy/_array_api/linear_algebra_functions.py +++ b/numpy/_array_api/linear_algebra_functions.py @@ -55,6 +55,9 @@ def inv(x): def norm(x, /, *, axis=None, keepdims=False, ord=None): # Note: this function is being imported from a nondefault namespace from ..linalg import norm + # Note: this is different from the default behavior + if axis == None and x.ndim > 2: + x = x.flatten() return norm(x, axis=axis, keepdims=keepdims, ord=ord) def outer(x1, x2, /): -- cgit v1.2.1 From 10427b0cb9895d9d1d55c95815d9f27732cfbeaa Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 11 Jan 2021 17:45:05 -0700 Subject: Correct some differing keyword arguments in the array API namespace --- numpy/_array_api/sorting_functions.py | 14 ++++++++++++-- numpy/_array_api/statistical_functions.py | 6 ++++-- 2 files changed, 16 insertions(+), 4 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/sorting_functions.py b/numpy/_array_api/sorting_functions.py index 384ec08f9..b75387737 100644 --- a/numpy/_array_api/sorting_functions.py +++ b/numpy/_array_api/sorting_functions.py @@ -1,9 +1,19 @@ def argsort(x, /, *, axis=-1, descending=False, stable=True): from .. import argsort - return argsort(x, axis=axis, descending=descending, stable=stable) + from .. import flip + # Note: this keyword argument is different, and the default is different. + kind = 'stable' if stable else 'quicksort' + res = argsort(x, axis=axis, kind=kind) + if descending: + res = flip(res, axis=axis) def sort(x, /, *, axis=-1, descending=False, stable=True): from .. import sort - return sort(x, axis=axis, descending=descending, stable=stable) + from .. import flip + # Note: this keyword argument is different, and the default is different. + kind = 'stable' if stable else 'quicksort' + res = sort(x, axis=axis, kind=kind) + if descending: + res = flip(res, axis=axis) __all__ = ['argsort', 'sort'] diff --git a/numpy/_array_api/statistical_functions.py b/numpy/_array_api/statistical_functions.py index 2cc712aea..b9180a863 100644 --- a/numpy/_array_api/statistical_functions.py +++ b/numpy/_array_api/statistical_functions.py @@ -16,7 +16,8 @@ def prod(x, /, *, axis=None, keepdims=False): def std(x, /, *, axis=None, correction=0.0, keepdims=False): from .. import std - return std(x, axis=axis, correction=correction, keepdims=keepdims) + # Note: the keyword argument correction is different here + return std(x, axis=axis, ddof=correction, keepdims=keepdims) def sum(x, /, *, axis=None, keepdims=False): from .. import sum @@ -24,6 +25,7 @@ def sum(x, /, *, axis=None, keepdims=False): def var(x, /, *, axis=None, correction=0.0, keepdims=False): from .. import var - return var(x, axis=axis, correction=correction, keepdims=keepdims) + # Note: the keyword argument correction is different here + return var(x, axis=axis, ddof=correction, keepdims=keepdims) __all__ = ['max', 'mean', 'min', 'prod', 'std', 'sum', 'var'] -- cgit v1.2.1 From d9651020aa9e1f6211b920954a357ac45712938d Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 12 Jan 2021 12:32:24 -0700 Subject: Add the device keyword to the array creation functions --- numpy/_array_api/creation_functions.py | 55 +++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 11 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/creation_functions.py b/numpy/_array_api/creation_functions.py index 50b0bd252..ee3466d2f 100644 --- a/numpy/_array_api/creation_functions.py +++ b/numpy/_array_api/creation_functions.py @@ -1,45 +1,78 @@ -def arange(start, /, *, stop=None, step=1, dtype=None): +def arange(start, /, *, stop=None, step=1, dtype=None, device=None): from .. import arange + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") return arange(start, stop=stop, step=step, dtype=dtype) -def empty(shape, /, *, dtype=None): +def empty(shape, /, *, dtype=None, device=None): from .. import empty + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") return empty(shape, dtype=dtype) -def empty_like(x, /, *, dtype=None): +def empty_like(x, /, *, dtype=None, device=None): from .. import empty_like + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") return empty_like(x, dtype=dtype) -def eye(N, /, *, M=None, k=0, dtype=None): +def eye(N, /, *, M=None, k=0, dtype=None, device=None): from .. import eye + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") return eye(N, M=M, k=k, dtype=dtype) -def full(shape, fill_value, /, *, dtype=None): +def full(shape, fill_value, /, *, dtype=None, device=None): from .. import full + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") return full(shape, fill_value, dtype=dtype) -def full_like(x, fill_value, /, *, dtype=None): +def full_like(x, fill_value, /, *, dtype=None, device=None): from .. import full_like + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") return full_like(x, fill_value, dtype=dtype) -def linspace(start, stop, num, /, *, dtype=None, endpoint=True): +def linspace(start, stop, num, /, *, dtype=None, device=None, endpoint=True): from .. import linspace + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") return linspace(start, stop, num, dtype=dtype, endpoint=endpoint) -def ones(shape, /, *, dtype=None): +def ones(shape, /, *, dtype=None, device=None): from .. import ones + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") return ones(shape, dtype=dtype) -def ones_like(x, /, *, dtype=None): +def ones_like(x, /, *, dtype=None, device=None): from .. import ones_like + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") return ones_like(x, dtype=dtype) -def zeros(shape, /, *, dtype=None): +def zeros(shape, /, *, dtype=None, device=None): from .. import zeros + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") return zeros(shape, dtype=dtype) -def zeros_like(x, /, *, dtype=None): +def zeros_like(x, /, *, dtype=None, device=None): from .. import zeros_like + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") return zeros_like(x, dtype=dtype) __all__ = ['arange', 'empty', 'empty_like', 'eye', 'full', 'full_like', 'linspace', 'ones', 'ones_like', 'zeros', 'zeros_like'] -- cgit v1.2.1 From 9578636259f86267c2253f4af2510ce1eeaf084c Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 12 Jan 2021 12:34:59 -0700 Subject: Make the array_api submodules private, and remove __all__ from individual files The specific submodule organization is an implementation detail and should not be used. Only the top-level numpy._array_api namespace should be used. --- numpy/_array_api/_constants.py | 1 + numpy/_array_api/_creation_functions.py | 76 +++++++++ numpy/_array_api/_dtypes.py | 3 + numpy/_array_api/_elementwise_functions.py | 230 +++++++++++++++++++++++++ numpy/_array_api/_linear_algebra_functions.py | 93 +++++++++++ numpy/_array_api/_manipulation_functions.py | 28 ++++ numpy/_array_api/_searching_functions.py | 15 ++ numpy/_array_api/_set_functions.py | 3 + numpy/_array_api/_sorting_functions.py | 17 ++ numpy/_array_api/_statistical_functions.py | 29 ++++ numpy/_array_api/_utility_functions.py | 7 + numpy/_array_api/constants.py | 3 - numpy/_array_api/creation_functions.py | 78 --------- numpy/_array_api/dtypes.py | 5 - numpy/_array_api/elementwise_functions.py | 232 -------------------------- numpy/_array_api/linear_algebra_functions.py | 97 ----------- numpy/_array_api/manipulation_functions.py | 30 ---- numpy/_array_api/searching_functions.py | 17 -- numpy/_array_api/set_functions.py | 5 - numpy/_array_api/sorting_functions.py | 19 --- numpy/_array_api/statistical_functions.py | 31 ---- numpy/_array_api/utility_functions.py | 9 - 22 files changed, 502 insertions(+), 526 deletions(-) create mode 100644 numpy/_array_api/_constants.py create mode 100644 numpy/_array_api/_creation_functions.py create mode 100644 numpy/_array_api/_dtypes.py create mode 100644 numpy/_array_api/_elementwise_functions.py create mode 100644 numpy/_array_api/_linear_algebra_functions.py create mode 100644 numpy/_array_api/_manipulation_functions.py create mode 100644 numpy/_array_api/_searching_functions.py create mode 100644 numpy/_array_api/_set_functions.py create mode 100644 numpy/_array_api/_sorting_functions.py create mode 100644 numpy/_array_api/_statistical_functions.py create mode 100644 numpy/_array_api/_utility_functions.py delete mode 100644 numpy/_array_api/constants.py delete mode 100644 numpy/_array_api/creation_functions.py delete mode 100644 numpy/_array_api/dtypes.py delete mode 100644 numpy/_array_api/elementwise_functions.py delete mode 100644 numpy/_array_api/linear_algebra_functions.py delete mode 100644 numpy/_array_api/manipulation_functions.py delete mode 100644 numpy/_array_api/searching_functions.py delete mode 100644 numpy/_array_api/set_functions.py delete mode 100644 numpy/_array_api/sorting_functions.py delete mode 100644 numpy/_array_api/statistical_functions.py delete mode 100644 numpy/_array_api/utility_functions.py (limited to 'numpy') diff --git a/numpy/_array_api/_constants.py b/numpy/_array_api/_constants.py new file mode 100644 index 000000000..075b8c3b9 --- /dev/null +++ b/numpy/_array_api/_constants.py @@ -0,0 +1 @@ +from .. import e, inf, nan, pi diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py new file mode 100644 index 000000000..4e91aa443 --- /dev/null +++ b/numpy/_array_api/_creation_functions.py @@ -0,0 +1,76 @@ +def arange(start, /, *, stop=None, step=1, dtype=None, device=None): + from .. import arange + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") + return arange(start, stop=stop, step=step, dtype=dtype) + +def empty(shape, /, *, dtype=None, device=None): + from .. import empty + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") + return empty(shape, dtype=dtype) + +def empty_like(x, /, *, dtype=None, device=None): + from .. import empty_like + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") + return empty_like(x, dtype=dtype) + +def eye(N, /, *, M=None, k=0, dtype=None, device=None): + from .. import eye + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") + return eye(N, M=M, k=k, dtype=dtype) + +def full(shape, fill_value, /, *, dtype=None, device=None): + from .. import full + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") + return full(shape, fill_value, dtype=dtype) + +def full_like(x, fill_value, /, *, dtype=None, device=None): + from .. import full_like + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") + return full_like(x, fill_value, dtype=dtype) + +def linspace(start, stop, num, /, *, dtype=None, device=None, endpoint=True): + from .. import linspace + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") + return linspace(start, stop, num, dtype=dtype, endpoint=endpoint) + +def ones(shape, /, *, dtype=None, device=None): + from .. import ones + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") + return ones(shape, dtype=dtype) + +def ones_like(x, /, *, dtype=None, device=None): + from .. import ones_like + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") + return ones_like(x, dtype=dtype) + +def zeros(shape, /, *, dtype=None, device=None): + from .. import zeros + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") + return zeros(shape, dtype=dtype) + +def zeros_like(x, /, *, dtype=None, device=None): + from .. import zeros_like + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") + return zeros_like(x, dtype=dtype) diff --git a/numpy/_array_api/_dtypes.py b/numpy/_array_api/_dtypes.py new file mode 100644 index 000000000..acf87fd82 --- /dev/null +++ b/numpy/_array_api/_dtypes.py @@ -0,0 +1,3 @@ +from .. import int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64 +# Note: This name is changed +from .. import bool_ as bool diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py new file mode 100644 index 000000000..f9052020e --- /dev/null +++ b/numpy/_array_api/_elementwise_functions.py @@ -0,0 +1,230 @@ +def abs(x, /): + from .. import abs + return abs(x) + +def acos(x, /): + # Note: the function name is different here + from .. import arccos + return arccos(x) + +def acosh(x, /): + # Note: the function name is different here + from .. import arccosh + return arccosh(x) + +def add(x1, x2, /): + from .. import add + return add(x1, x2) + +def asin(x, /): + # Note: the function name is different here + from .. import arcsin + return arcsin(x) + +def asinh(x, /): + # Note: the function name is different here + from .. import arcsinh + return arcsinh(x) + +def atan(x, /): + # Note: the function name is different here + from .. import arctan + return arctan(x) + +def atan2(x1, x2, /): + # Note: the function name is different here + from .. import arctan2 + return arctan2(x1, x2) + +def atanh(x, /): + # Note: the function name is different here + from .. import arctanh + return arctanh(x) + +def bitwise_and(x1, x2, /): + from .. import bitwise_and + return bitwise_and(x1, x2) + +def bitwise_left_shift(x1, x2, /): + # Note: the function name is different here + from .. import left_shift + return left_shift(x1, x2) + +def bitwise_invert(x, /): + # Note: the function name is different here + from .. import invert + return invert(x) + +def bitwise_or(x1, x2, /): + from .. import bitwise_or + return bitwise_or(x1, x2) + +def bitwise_right_shift(x1, x2, /): + # Note: the function name is different here + from .. import right_shift + return right_shift(x1, x2) + +def bitwise_xor(x1, x2, /): + from .. import bitwise_xor + return bitwise_xor(x1, x2) + +def ceil(x, /): + from .. import ceil + return ceil(x) + +def cos(x, /): + from .. import cos + return cos(x) + +def cosh(x, /): + from .. import cosh + return cosh(x) + +def divide(x1, x2, /): + from .. import divide + return divide(x1, x2) + +def equal(x1, x2, /): + from .. import equal + return equal(x1, x2) + +def exp(x, /): + from .. import exp + return exp(x) + +def expm1(x, /): + from .. import expm1 + return expm1(x) + +def floor(x, /): + from .. import floor + return floor(x) + +def floor_divide(x1, x2, /): + from .. import floor_divide + return floor_divide(x1, x2) + +def greater(x1, x2, /): + from .. import greater + return greater(x1, x2) + +def greater_equal(x1, x2, /): + from .. import greater_equal + return greater_equal(x1, x2) + +def isfinite(x, /): + from .. import isfinite + return isfinite(x) + +def isinf(x, /): + from .. import isinf + return isinf(x) + +def isnan(x, /): + from .. import isnan + return isnan(x) + +def less(x1, x2, /): + from .. import less + return less(x1, x2) + +def less_equal(x1, x2, /): + from .. import less_equal + return less_equal(x1, x2) + +def log(x, /): + from .. import log + return log(x) + +def log1p(x, /): + from .. import log1p + return log1p(x) + +def log2(x, /): + from .. import log2 + return log2(x) + +def log10(x, /): + from .. import log10 + return log10(x) + +def logical_and(x1, x2, /): + from .. import logical_and + return logical_and(x1, x2) + +def logical_not(x, /): + from .. import logical_not + return logical_not(x) + +def logical_or(x1, x2, /): + from .. import logical_or + return logical_or(x1, x2) + +def logical_xor(x1, x2, /): + from .. import logical_xor + return logical_xor(x1, x2) + +def multiply(x1, x2, /): + from .. import multiply + return multiply(x1, x2) + +def negative(x, /): + from .. import negative + return negative(x) + +def not_equal(x1, x2, /): + from .. import not_equal + return not_equal(x1, x2) + +def positive(x, /): + from .. import positive + return positive(x) + +def pow(x1, x2, /): + # Note: the function name is different here + from .. import power + return power(x1, x2) + +def remainder(x1, x2, /): + from .. import remainder + return remainder(x1, x2) + +def round(x, /): + from .. import round + return round(x) + +def sign(x, /): + from .. import sign + return sign(x) + +def sin(x, /): + from .. import sin + return sin(x) + +def sinh(x, /): + from .. import sinh + return sinh(x) + +def square(x, /): + from .. import square + return square(x) + +def sqrt(x, /): + from .. import sqrt + return sqrt(x) + +def subtract(x1, x2, /): + from .. import subtract + return subtract(x1, x2) + +def tan(x, /): + from .. import tan + return tan(x) + +def tanh(x, /): + from .. import tanh + return tanh(x) + +def trunc(x, /): + from .. import trunc + return trunc(x) diff --git a/numpy/_array_api/_linear_algebra_functions.py b/numpy/_array_api/_linear_algebra_functions.py new file mode 100644 index 000000000..10c81d12c --- /dev/null +++ b/numpy/_array_api/_linear_algebra_functions.py @@ -0,0 +1,93 @@ +# def cholesky(): +# from .. import cholesky +# return cholesky() + +def cross(x1, x2, /, *, axis=-1): + from .. import cross + return cross(x1, x2, axis=axis) + +def det(x, /): + # Note: this function is being imported from a nondefault namespace + from ..linalg import det + return det(x) + +def diagonal(x, /, *, axis1=0, axis2=1, offset=0): + from .. import diagonal + return diagonal(x, axis1=axis1, axis2=axis2, offset=offset) + +# def dot(): +# from .. import dot +# return dot() +# +# def eig(): +# from .. import eig +# return eig() +# +# def eigvalsh(): +# from .. import eigvalsh +# return eigvalsh() +# +# def einsum(): +# from .. import einsum +# return einsum() + +def inv(x): + # Note: this function is being imported from a nondefault namespace + from ..linalg import inv + return inv(x) + +# def lstsq(): +# from .. import lstsq +# return lstsq() +# +# def matmul(): +# from .. import matmul +# return matmul() +# +# def matrix_power(): +# from .. import matrix_power +# return matrix_power() +# +# def matrix_rank(): +# from .. import matrix_rank +# return matrix_rank() + +def norm(x, /, *, axis=None, keepdims=False, ord=None): + # Note: this function is being imported from a nondefault namespace + from ..linalg import norm + # Note: this is different from the default behavior + if axis == None and x.ndim > 2: + x = x.flatten() + return norm(x, axis=axis, keepdims=keepdims, ord=ord) + +def outer(x1, x2, /): + from .. import outer + return outer(x1, x2) + +# def pinv(): +# from .. import pinv +# return pinv() +# +# def qr(): +# from .. import qr +# return qr() +# +# def slogdet(): +# from .. import slogdet +# return slogdet() +# +# def solve(): +# from .. import solve +# return solve() +# +# def svd(): +# from .. import svd +# return svd() + +def trace(x, /, *, axis1=0, axis2=1, offset=0): + from .. import trace + return trace(x, axis1=axis1, axis2=axis2, offset=offset) + +def transpose(x, /, *, axes=None): + from .. import transpose + return transpose(x, axes=axes) diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py new file mode 100644 index 000000000..19e9c1cab --- /dev/null +++ b/numpy/_array_api/_manipulation_functions.py @@ -0,0 +1,28 @@ +def concat(arrays, /, *, axis=0): + # Note: the function name is different here + from .. import concatenate + return concatenate(arrays, axis=axis) + +def expand_dims(x, axis, /): + from .. import expand_dims + return expand_dims(x, axis) + +def flip(x, /, *, axis=None): + from .. import flip + return flip(x, axis=axis) + +def reshape(x, shape, /): + from .. import reshape + return reshape(x, shape) + +def roll(x, shift, /, *, axis=None): + from .. import roll + return roll(x, shift, axis=axis) + +def squeeze(x, /, *, axis=None): + from .. import squeeze + return squeeze(x, axis=axis) + +def stack(arrays, /, *, axis=0): + from .. import stack + return stack(arrays, axis=axis) diff --git a/numpy/_array_api/_searching_functions.py b/numpy/_array_api/_searching_functions.py new file mode 100644 index 000000000..c6035ca77 --- /dev/null +++ b/numpy/_array_api/_searching_functions.py @@ -0,0 +1,15 @@ +def argmax(x, /, *, axis=None, keepdims=False): + from .. import argmax + return argmax(x, axis=axis, keepdims=keepdims) + +def argmin(x, /, *, axis=None, keepdims=False): + from .. import argmin + return argmin(x, axis=axis, keepdims=keepdims) + +def nonzero(x, /): + from .. import nonzero + return nonzero(x) + +def where(condition, x1, x2, /): + from .. import where + return where(condition, x1, x2) diff --git a/numpy/_array_api/_set_functions.py b/numpy/_array_api/_set_functions.py new file mode 100644 index 000000000..b6198765a --- /dev/null +++ b/numpy/_array_api/_set_functions.py @@ -0,0 +1,3 @@ +def unique(x, /, *, return_counts=False, return_index=False, return_inverse=False, sorted=True): + from .. import unique + return unique(x, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse, sorted=sorted) diff --git a/numpy/_array_api/_sorting_functions.py b/numpy/_array_api/_sorting_functions.py new file mode 100644 index 000000000..98db3c7a2 --- /dev/null +++ b/numpy/_array_api/_sorting_functions.py @@ -0,0 +1,17 @@ +def argsort(x, /, *, axis=-1, descending=False, stable=True): + from .. import argsort + from .. import flip + # Note: this keyword argument is different, and the default is different. + kind = 'stable' if stable else 'quicksort' + res = argsort(x, axis=axis, kind=kind) + if descending: + res = flip(res, axis=axis) + +def sort(x, /, *, axis=-1, descending=False, stable=True): + from .. import sort + from .. import flip + # Note: this keyword argument is different, and the default is different. + kind = 'stable' if stable else 'quicksort' + res = sort(x, axis=axis, kind=kind) + if descending: + res = flip(res, axis=axis) diff --git a/numpy/_array_api/_statistical_functions.py b/numpy/_array_api/_statistical_functions.py new file mode 100644 index 000000000..339835095 --- /dev/null +++ b/numpy/_array_api/_statistical_functions.py @@ -0,0 +1,29 @@ +def max(x, /, *, axis=None, keepdims=False): + from .. import max + return max(x, axis=axis, keepdims=keepdims) + +def mean(x, /, *, axis=None, keepdims=False): + from .. import mean + return mean(x, axis=axis, keepdims=keepdims) + +def min(x, /, *, axis=None, keepdims=False): + from .. import min + return min(x, axis=axis, keepdims=keepdims) + +def prod(x, /, *, axis=None, keepdims=False): + from .. import prod + return prod(x, axis=axis, keepdims=keepdims) + +def std(x, /, *, axis=None, correction=0.0, keepdims=False): + from .. import std + # Note: the keyword argument correction is different here + return std(x, axis=axis, ddof=correction, keepdims=keepdims) + +def sum(x, /, *, axis=None, keepdims=False): + from .. import sum + return sum(x, axis=axis, keepdims=keepdims) + +def var(x, /, *, axis=None, correction=0.0, keepdims=False): + from .. import var + # Note: the keyword argument correction is different here + return var(x, axis=axis, ddof=correction, keepdims=keepdims) diff --git a/numpy/_array_api/_utility_functions.py b/numpy/_array_api/_utility_functions.py new file mode 100644 index 000000000..accc43e1e --- /dev/null +++ b/numpy/_array_api/_utility_functions.py @@ -0,0 +1,7 @@ +def all(x, /, *, axis=None, keepdims=False): + from .. import all + return all(x, axis=axis, keepdims=keepdims) + +def any(x, /, *, axis=None, keepdims=False): + from .. import any + return any(x, axis=axis, keepdims=keepdims) diff --git a/numpy/_array_api/constants.py b/numpy/_array_api/constants.py deleted file mode 100644 index 000777029..000000000 --- a/numpy/_array_api/constants.py +++ /dev/null @@ -1,3 +0,0 @@ -from .. import e, inf, nan, pi - -__all__ = ['e', 'inf', 'nan', 'pi'] diff --git a/numpy/_array_api/creation_functions.py b/numpy/_array_api/creation_functions.py deleted file mode 100644 index ee3466d2f..000000000 --- a/numpy/_array_api/creation_functions.py +++ /dev/null @@ -1,78 +0,0 @@ -def arange(start, /, *, stop=None, step=1, dtype=None, device=None): - from .. import arange - if device is not None: - # Note: Device support is not yet implemented on ndarray - raise NotImplementedError("Device support is not yet implemented") - return arange(start, stop=stop, step=step, dtype=dtype) - -def empty(shape, /, *, dtype=None, device=None): - from .. import empty - if device is not None: - # Note: Device support is not yet implemented on ndarray - raise NotImplementedError("Device support is not yet implemented") - return empty(shape, dtype=dtype) - -def empty_like(x, /, *, dtype=None, device=None): - from .. import empty_like - if device is not None: - # Note: Device support is not yet implemented on ndarray - raise NotImplementedError("Device support is not yet implemented") - return empty_like(x, dtype=dtype) - -def eye(N, /, *, M=None, k=0, dtype=None, device=None): - from .. import eye - if device is not None: - # Note: Device support is not yet implemented on ndarray - raise NotImplementedError("Device support is not yet implemented") - return eye(N, M=M, k=k, dtype=dtype) - -def full(shape, fill_value, /, *, dtype=None, device=None): - from .. import full - if device is not None: - # Note: Device support is not yet implemented on ndarray - raise NotImplementedError("Device support is not yet implemented") - return full(shape, fill_value, dtype=dtype) - -def full_like(x, fill_value, /, *, dtype=None, device=None): - from .. import full_like - if device is not None: - # Note: Device support is not yet implemented on ndarray - raise NotImplementedError("Device support is not yet implemented") - return full_like(x, fill_value, dtype=dtype) - -def linspace(start, stop, num, /, *, dtype=None, device=None, endpoint=True): - from .. import linspace - if device is not None: - # Note: Device support is not yet implemented on ndarray - raise NotImplementedError("Device support is not yet implemented") - return linspace(start, stop, num, dtype=dtype, endpoint=endpoint) - -def ones(shape, /, *, dtype=None, device=None): - from .. import ones - if device is not None: - # Note: Device support is not yet implemented on ndarray - raise NotImplementedError("Device support is not yet implemented") - return ones(shape, dtype=dtype) - -def ones_like(x, /, *, dtype=None, device=None): - from .. import ones_like - if device is not None: - # Note: Device support is not yet implemented on ndarray - raise NotImplementedError("Device support is not yet implemented") - return ones_like(x, dtype=dtype) - -def zeros(shape, /, *, dtype=None, device=None): - from .. import zeros - if device is not None: - # Note: Device support is not yet implemented on ndarray - raise NotImplementedError("Device support is not yet implemented") - return zeros(shape, dtype=dtype) - -def zeros_like(x, /, *, dtype=None, device=None): - from .. import zeros_like - if device is not None: - # Note: Device support is not yet implemented on ndarray - raise NotImplementedError("Device support is not yet implemented") - return zeros_like(x, dtype=dtype) - -__all__ = ['arange', 'empty', 'empty_like', 'eye', 'full', 'full_like', 'linspace', 'ones', 'ones_like', 'zeros', 'zeros_like'] diff --git a/numpy/_array_api/dtypes.py b/numpy/_array_api/dtypes.py deleted file mode 100644 index e94e70e9b..000000000 --- a/numpy/_array_api/dtypes.py +++ /dev/null @@ -1,5 +0,0 @@ -from .. import int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64 -# Note: This name is changed -from .. import bool_ as bool - -__all__ = ['int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64', 'float32', 'float64', 'bool'] diff --git a/numpy/_array_api/elementwise_functions.py b/numpy/_array_api/elementwise_functions.py deleted file mode 100644 index bacb733d9..000000000 --- a/numpy/_array_api/elementwise_functions.py +++ /dev/null @@ -1,232 +0,0 @@ -def abs(x, /): - from .. import abs - return abs(x) - -def acos(x, /): - # Note: the function name is different here - from .. import arccos - return arccos(x) - -def acosh(x, /): - # Note: the function name is different here - from .. import arccosh - return arccosh(x) - -def add(x1, x2, /): - from .. import add - return add(x1, x2) - -def asin(x, /): - # Note: the function name is different here - from .. import arcsin - return arcsin(x) - -def asinh(x, /): - # Note: the function name is different here - from .. import arcsinh - return arcsinh(x) - -def atan(x, /): - # Note: the function name is different here - from .. import arctan - return arctan(x) - -def atan2(x1, x2, /): - # Note: the function name is different here - from .. import arctan2 - return arctan2(x1, x2) - -def atanh(x, /): - # Note: the function name is different here - from .. import arctanh - return arctanh(x) - -def bitwise_and(x1, x2, /): - from .. import bitwise_and - return bitwise_and(x1, x2) - -def bitwise_left_shift(x1, x2, /): - # Note: the function name is different here - from .. import left_shift - return left_shift(x1, x2) - -def bitwise_invert(x, /): - # Note: the function name is different here - from .. import invert - return invert(x) - -def bitwise_or(x1, x2, /): - from .. import bitwise_or - return bitwise_or(x1, x2) - -def bitwise_right_shift(x1, x2, /): - # Note: the function name is different here - from .. import right_shift - return right_shift(x1, x2) - -def bitwise_xor(x1, x2, /): - from .. import bitwise_xor - return bitwise_xor(x1, x2) - -def ceil(x, /): - from .. import ceil - return ceil(x) - -def cos(x, /): - from .. import cos - return cos(x) - -def cosh(x, /): - from .. import cosh - return cosh(x) - -def divide(x1, x2, /): - from .. import divide - return divide(x1, x2) - -def equal(x1, x2, /): - from .. import equal - return equal(x1, x2) - -def exp(x, /): - from .. import exp - return exp(x) - -def expm1(x, /): - from .. import expm1 - return expm1(x) - -def floor(x, /): - from .. import floor - return floor(x) - -def floor_divide(x1, x2, /): - from .. import floor_divide - return floor_divide(x1, x2) - -def greater(x1, x2, /): - from .. import greater - return greater(x1, x2) - -def greater_equal(x1, x2, /): - from .. import greater_equal - return greater_equal(x1, x2) - -def isfinite(x, /): - from .. import isfinite - return isfinite(x) - -def isinf(x, /): - from .. import isinf - return isinf(x) - -def isnan(x, /): - from .. import isnan - return isnan(x) - -def less(x1, x2, /): - from .. import less - return less(x1, x2) - -def less_equal(x1, x2, /): - from .. import less_equal - return less_equal(x1, x2) - -def log(x, /): - from .. import log - return log(x) - -def log1p(x, /): - from .. import log1p - return log1p(x) - -def log2(x, /): - from .. import log2 - return log2(x) - -def log10(x, /): - from .. import log10 - return log10(x) - -def logical_and(x1, x2, /): - from .. import logical_and - return logical_and(x1, x2) - -def logical_not(x, /): - from .. import logical_not - return logical_not(x) - -def logical_or(x1, x2, /): - from .. import logical_or - return logical_or(x1, x2) - -def logical_xor(x1, x2, /): - from .. import logical_xor - return logical_xor(x1, x2) - -def multiply(x1, x2, /): - from .. import multiply - return multiply(x1, x2) - -def negative(x, /): - from .. import negative - return negative(x) - -def not_equal(x1, x2, /): - from .. import not_equal - return not_equal(x1, x2) - -def positive(x, /): - from .. import positive - return positive(x) - -def pow(x1, x2, /): - # Note: the function name is different here - from .. import power - return power(x1, x2) - -def remainder(x1, x2, /): - from .. import remainder - return remainder(x1, x2) - -def round(x, /): - from .. import round - return round(x) - -def sign(x, /): - from .. import sign - return sign(x) - -def sin(x, /): - from .. import sin - return sin(x) - -def sinh(x, /): - from .. import sinh - return sinh(x) - -def square(x, /): - from .. import square - return square(x) - -def sqrt(x, /): - from .. import sqrt - return sqrt(x) - -def subtract(x1, x2, /): - from .. import subtract - return subtract(x1, x2) - -def tan(x, /): - from .. import tan - return tan(x) - -def tanh(x, /): - from .. import tanh - return tanh(x) - -def trunc(x, /): - from .. import trunc - return trunc(x) - -__all__ = ['abs', 'acos', 'acosh', 'add', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'bitwise_and', 'bitwise_left_shift', 'bitwise_invert', 'bitwise_or', 'bitwise_right_shift', 'bitwise_xor', 'ceil', 'cos', 'cosh', 'divide', 'equal', 'exp', 'expm1', 'floor', 'floor_divide', 'greater', 'greater_equal', 'isfinite', 'isinf', 'isnan', 'less', 'less_equal', 'log', 'log1p', 'log2', 'log10', 'logical_and', 'logical_not', 'logical_or', 'logical_xor', 'multiply', 'negative', 'not_equal', 'positive', 'pow', 'remainder', 'round', 'sign', 'sin', 'sinh', 'square', 'sqrt', 'subtract', 'tan', 'tanh', 'trunc'] diff --git a/numpy/_array_api/linear_algebra_functions.py b/numpy/_array_api/linear_algebra_functions.py deleted file mode 100644 index 820dfffba..000000000 --- a/numpy/_array_api/linear_algebra_functions.py +++ /dev/null @@ -1,97 +0,0 @@ -# def cholesky(): -# from .. import cholesky -# return cholesky() - -def cross(x1, x2, /, *, axis=-1): - from .. import cross - return cross(x1, x2, axis=axis) - -def det(x, /): - # Note: this function is being imported from a nondefault namespace - from ..linalg import det - return det(x) - -def diagonal(x, /, *, axis1=0, axis2=1, offset=0): - from .. import diagonal - return diagonal(x, axis1=axis1, axis2=axis2, offset=offset) - -# def dot(): -# from .. import dot -# return dot() -# -# def eig(): -# from .. import eig -# return eig() -# -# def eigvalsh(): -# from .. import eigvalsh -# return eigvalsh() -# -# def einsum(): -# from .. import einsum -# return einsum() - -def inv(x): - # Note: this function is being imported from a nondefault namespace - from ..linalg import inv - return inv(x) - -# def lstsq(): -# from .. import lstsq -# return lstsq() -# -# def matmul(): -# from .. import matmul -# return matmul() -# -# def matrix_power(): -# from .. import matrix_power -# return matrix_power() -# -# def matrix_rank(): -# from .. import matrix_rank -# return matrix_rank() - -def norm(x, /, *, axis=None, keepdims=False, ord=None): - # Note: this function is being imported from a nondefault namespace - from ..linalg import norm - # Note: this is different from the default behavior - if axis == None and x.ndim > 2: - x = x.flatten() - return norm(x, axis=axis, keepdims=keepdims, ord=ord) - -def outer(x1, x2, /): - from .. import outer - return outer(x1, x2) - -# def pinv(): -# from .. import pinv -# return pinv() -# -# def qr(): -# from .. import qr -# return qr() -# -# def slogdet(): -# from .. import slogdet -# return slogdet() -# -# def solve(): -# from .. import solve -# return solve() -# -# def svd(): -# from .. import svd -# return svd() - -def trace(x, /, *, axis1=0, axis2=1, offset=0): - from .. import trace - return trace(x, axis1=axis1, axis2=axis2, offset=offset) - -def transpose(x, /, *, axes=None): - from .. import transpose - return transpose(x, axes=axes) - -# __all__ = ['cholesky', 'cross', 'det', 'diagonal', 'dot', 'eig', 'eigvalsh', 'einsum', 'inv', 'lstsq', 'matmul', 'matrix_power', 'matrix_rank', 'norm', 'outer', 'pinv', 'qr', 'slogdet', 'solve', 'svd', 'trace', 'transpose'] - -__all__ = ['cross', 'det', 'diagonal', 'inv', 'norm', 'outer', 'trace', 'transpose'] diff --git a/numpy/_array_api/manipulation_functions.py b/numpy/_array_api/manipulation_functions.py deleted file mode 100644 index 80ca3381e..000000000 --- a/numpy/_array_api/manipulation_functions.py +++ /dev/null @@ -1,30 +0,0 @@ -def concat(arrays, /, *, axis=0): - # Note: the function name is different here - from .. import concatenate - return concatenate(arrays, axis=axis) - -def expand_dims(x, axis, /): - from .. import expand_dims - return expand_dims(x, axis) - -def flip(x, /, *, axis=None): - from .. import flip - return flip(x, axis=axis) - -def reshape(x, shape, /): - from .. import reshape - return reshape(x, shape) - -def roll(x, shift, /, *, axis=None): - from .. import roll - return roll(x, shift, axis=axis) - -def squeeze(x, /, *, axis=None): - from .. import squeeze - return squeeze(x, axis=axis) - -def stack(arrays, /, *, axis=0): - from .. import stack - return stack(arrays, axis=axis) - -__all__ = ['concat', 'expand_dims', 'flip', 'reshape', 'roll', 'squeeze', 'stack'] diff --git a/numpy/_array_api/searching_functions.py b/numpy/_array_api/searching_functions.py deleted file mode 100644 index c4b6c58b5..000000000 --- a/numpy/_array_api/searching_functions.py +++ /dev/null @@ -1,17 +0,0 @@ -def argmax(x, /, *, axis=None, keepdims=False): - from .. import argmax - return argmax(x, axis=axis, keepdims=keepdims) - -def argmin(x, /, *, axis=None, keepdims=False): - from .. import argmin - return argmin(x, axis=axis, keepdims=keepdims) - -def nonzero(x, /): - from .. import nonzero - return nonzero(x) - -def where(condition, x1, x2, /): - from .. import where - return where(condition, x1, x2) - -__all__ = ['argmax', 'argmin', 'nonzero', 'where'] diff --git a/numpy/_array_api/set_functions.py b/numpy/_array_api/set_functions.py deleted file mode 100644 index f218f1187..000000000 --- a/numpy/_array_api/set_functions.py +++ /dev/null @@ -1,5 +0,0 @@ -def unique(x, /, *, return_counts=False, return_index=False, return_inverse=False, sorted=True): - from .. import unique - return unique(x, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse, sorted=sorted) - -__all__ = ['unique'] diff --git a/numpy/_array_api/sorting_functions.py b/numpy/_array_api/sorting_functions.py deleted file mode 100644 index b75387737..000000000 --- a/numpy/_array_api/sorting_functions.py +++ /dev/null @@ -1,19 +0,0 @@ -def argsort(x, /, *, axis=-1, descending=False, stable=True): - from .. import argsort - from .. import flip - # Note: this keyword argument is different, and the default is different. - kind = 'stable' if stable else 'quicksort' - res = argsort(x, axis=axis, kind=kind) - if descending: - res = flip(res, axis=axis) - -def sort(x, /, *, axis=-1, descending=False, stable=True): - from .. import sort - from .. import flip - # Note: this keyword argument is different, and the default is different. - kind = 'stable' if stable else 'quicksort' - res = sort(x, axis=axis, kind=kind) - if descending: - res = flip(res, axis=axis) - -__all__ = ['argsort', 'sort'] diff --git a/numpy/_array_api/statistical_functions.py b/numpy/_array_api/statistical_functions.py deleted file mode 100644 index b9180a863..000000000 --- a/numpy/_array_api/statistical_functions.py +++ /dev/null @@ -1,31 +0,0 @@ -def max(x, /, *, axis=None, keepdims=False): - from .. import max - return max(x, axis=axis, keepdims=keepdims) - -def mean(x, /, *, axis=None, keepdims=False): - from .. import mean - return mean(x, axis=axis, keepdims=keepdims) - -def min(x, /, *, axis=None, keepdims=False): - from .. import min - return min(x, axis=axis, keepdims=keepdims) - -def prod(x, /, *, axis=None, keepdims=False): - from .. import prod - return prod(x, axis=axis, keepdims=keepdims) - -def std(x, /, *, axis=None, correction=0.0, keepdims=False): - from .. import std - # Note: the keyword argument correction is different here - return std(x, axis=axis, ddof=correction, keepdims=keepdims) - -def sum(x, /, *, axis=None, keepdims=False): - from .. import sum - return sum(x, axis=axis, keepdims=keepdims) - -def var(x, /, *, axis=None, correction=0.0, keepdims=False): - from .. import var - # Note: the keyword argument correction is different here - return var(x, axis=axis, ddof=correction, keepdims=keepdims) - -__all__ = ['max', 'mean', 'min', 'prod', 'std', 'sum', 'var'] diff --git a/numpy/_array_api/utility_functions.py b/numpy/_array_api/utility_functions.py deleted file mode 100644 index eac0d4eaa..000000000 --- a/numpy/_array_api/utility_functions.py +++ /dev/null @@ -1,9 +0,0 @@ -def all(x, /, *, axis=None, keepdims=False): - from .. import all - return all(x, axis=axis, keepdims=keepdims) - -def any(x, /, *, axis=None, keepdims=False): - from .. import any - return any(x, axis=axis, keepdims=keepdims) - -__all__ = ['all', 'any'] -- cgit v1.2.1 From e521b16844efc2853c0db9014098cb3e37f6eb04 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 12 Jan 2021 12:57:45 -0700 Subject: Add missing returns to the array API sorting functions --- numpy/_array_api/_sorting_functions.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_sorting_functions.py b/numpy/_array_api/_sorting_functions.py index 98db3c7a2..fb2f819a2 100644 --- a/numpy/_array_api/_sorting_functions.py +++ b/numpy/_array_api/_sorting_functions.py @@ -6,6 +6,7 @@ def argsort(x, /, *, axis=-1, descending=False, stable=True): res = argsort(x, axis=axis, kind=kind) if descending: res = flip(res, axis=axis) + return res def sort(x, /, *, axis=-1, descending=False, stable=True): from .. import sort @@ -15,3 +16,4 @@ def sort(x, /, *, axis=-1, descending=False, stable=True): res = sort(x, axis=axis, kind=kind) if descending: res = flip(res, axis=axis) + return res -- cgit v1.2.1 From ba4e21ca150a2d8b3cc08a3e8c981f7042aacf6f Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 12 Jan 2021 16:21:25 -0700 Subject: Fix the array_api submodule __init__.py imports --- numpy/_array_api/__init__.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/__init__.py b/numpy/_array_api/__init__.py index 1677224c5..c5f8154d9 100644 --- a/numpy/_array_api/__init__.py +++ b/numpy/_array_api/__init__.py @@ -1,49 +1,49 @@ __all__ = [] -from .constants import e, inf, nan, pi +from ._constants import e, inf, nan, pi __all__ += ['e', 'inf', 'nan', 'pi'] -from .creation_functions import arange, empty, empty_like, eye, full, full_like, linspace, ones, ones_like, zeros, zeros_like +from ._creation_functions import arange, empty, empty_like, eye, full, full_like, linspace, ones, ones_like, zeros, zeros_like __all__ += ['arange', 'empty', 'empty_like', 'eye', 'full', 'full_like', 'linspace', 'ones', 'ones_like', 'zeros', 'zeros_like'] -from .dtypes import int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, bool +from ._dtypes import int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, bool __all__ += ['int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64', 'float32', 'float64', 'bool'] -from .elementwise_functions import abs, acos, acosh, add, asin, asinh, atan, atan2, atanh, bitwise_and, bitwise_left_shift, bitwise_invert, bitwise_or, bitwise_right_shift, bitwise_xor, ceil, cos, cosh, divide, equal, exp, expm1, floor, floor_divide, greater, greater_equal, isfinite, isinf, isnan, less, less_equal, log, log1p, log2, log10, logical_and, logical_not, logical_or, logical_xor, multiply, negative, not_equal, positive, pow, remainder, round, sign, sin, sinh, square, sqrt, subtract, tan, tanh, trunc +from ._elementwise_functions import abs, acos, acosh, add, asin, asinh, atan, atan2, atanh, bitwise_and, bitwise_left_shift, bitwise_invert, bitwise_or, bitwise_right_shift, bitwise_xor, ceil, cos, cosh, divide, equal, exp, expm1, floor, floor_divide, greater, greater_equal, isfinite, isinf, isnan, less, less_equal, log, log1p, log2, log10, logical_and, logical_not, logical_or, logical_xor, multiply, negative, not_equal, positive, pow, remainder, round, sign, sin, sinh, square, sqrt, subtract, tan, tanh, trunc __all__ += ['abs', 'acos', 'acosh', 'add', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'bitwise_and', 'bitwise_left_shift', 'bitwise_invert', 'bitwise_or', 'bitwise_right_shift', 'bitwise_xor', 'ceil', 'cos', 'cosh', 'divide', 'equal', 'exp', 'expm1', 'floor', 'floor_divide', 'greater', 'greater_equal', 'isfinite', 'isinf', 'isnan', 'less', 'less_equal', 'log', 'log1p', 'log2', 'log10', 'logical_and', 'logical_not', 'logical_or', 'logical_xor', 'multiply', 'negative', 'not_equal', 'positive', 'pow', 'remainder', 'round', 'sign', 'sin', 'sinh', 'square', 'sqrt', 'subtract', 'tan', 'tanh', 'trunc'] -from .linear_algebra_functions import cross, det, diagonal, inv, norm, outer, trace, transpose +from ._linear_algebra_functions import cross, det, diagonal, inv, norm, outer, trace, transpose __all__ += ['cross', 'det', 'diagonal', 'inv', 'norm', 'outer', 'trace', 'transpose'] -# from .linear_algebra_functions import cholesky, cross, det, diagonal, dot, eig, eigvalsh, einsum, inv, lstsq, matmul, matrix_power, matrix_rank, norm, outer, pinv, qr, slogdet, solve, svd, trace, transpose +# from ._linear_algebra_functions import cholesky, cross, det, diagonal, dot, eig, eigvalsh, einsum, inv, lstsq, matmul, matrix_power, matrix_rank, norm, outer, pinv, qr, slogdet, solve, svd, trace, transpose # # __all__ += ['cholesky', 'cross', 'det', 'diagonal', 'dot', 'eig', 'eigvalsh', 'einsum', 'inv', 'lstsq', 'matmul', 'matrix_power', 'matrix_rank', 'norm', 'outer', 'pinv', 'qr', 'slogdet', 'solve', 'svd', 'trace', 'transpose'] -from .manipulation_functions import concat, expand_dims, flip, reshape, roll, squeeze, stack +from ._manipulation_functions import concat, expand_dims, flip, reshape, roll, squeeze, stack __all__ += ['concat', 'expand_dims', 'flip', 'reshape', 'roll', 'squeeze', 'stack'] -from .searching_functions import argmax, argmin, nonzero, where +from ._searching_functions import argmax, argmin, nonzero, where __all__ += ['argmax', 'argmin', 'nonzero', 'where'] -from .set_functions import unique +from ._set_functions import unique __all__ += ['unique'] -from .sorting_functions import argsort, sort +from ._sorting_functions import argsort, sort __all__ += ['argsort', 'sort'] -from .statistical_functions import max, mean, min, prod, std, sum, var +from ._statistical_functions import max, mean, min, prod, std, sum, var __all__ += ['max', 'mean', 'min', 'prod', 'std', 'sum', 'var'] -from .utility_functions import all, any +from ._utility_functions import all, any __all__ += ['all', 'any'] -- cgit v1.2.1 From 4bd5d158e66e6b1ca5f1a767738f0674f0dc8095 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 12 Jan 2021 16:21:46 -0700 Subject: Use "import numpy as np" in the array_api submodule This avoids importing everything inside the individual functions, but still is preferred over importing the functions used explicitly, as most of them clash with the wrapper function names. --- numpy/_array_api/_creation_functions.py | 35 ++---- numpy/_array_api/_elementwise_functions.py | 167 +++++++++----------------- numpy/_array_api/_linear_algebra_functions.py | 68 ++++------- numpy/_array_api/_manipulation_functions.py | 23 ++-- numpy/_array_api/_searching_functions.py | 14 +-- numpy/_array_api/_set_functions.py | 5 +- numpy/_array_api/_sorting_functions.py | 14 +-- numpy/_array_api/_statistical_functions.py | 23 ++-- numpy/_array_api/_utility_functions.py | 8 +- 9 files changed, 131 insertions(+), 226 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 4e91aa443..b74eca060 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -1,76 +1,67 @@ +import numpy as np + def arange(start, /, *, stop=None, step=1, dtype=None, device=None): - from .. import arange if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return arange(start, stop=stop, step=step, dtype=dtype) + return np.arange(start, stop=stop, step=step, dtype=dtype) def empty(shape, /, *, dtype=None, device=None): - from .. import empty if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return empty(shape, dtype=dtype) + return np.empty(shape, dtype=dtype) def empty_like(x, /, *, dtype=None, device=None): - from .. import empty_like if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return empty_like(x, dtype=dtype) + return np.empty_like(x, dtype=dtype) def eye(N, /, *, M=None, k=0, dtype=None, device=None): - from .. import eye if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return eye(N, M=M, k=k, dtype=dtype) + return np.eye(N, M=M, k=k, dtype=dtype) def full(shape, fill_value, /, *, dtype=None, device=None): - from .. import full if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return full(shape, fill_value, dtype=dtype) + return np.full(shape, fill_value, dtype=dtype) def full_like(x, fill_value, /, *, dtype=None, device=None): - from .. import full_like if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return full_like(x, fill_value, dtype=dtype) + return np.full_like(x, fill_value, dtype=dtype) def linspace(start, stop, num, /, *, dtype=None, device=None, endpoint=True): - from .. import linspace if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return linspace(start, stop, num, dtype=dtype, endpoint=endpoint) + return np.linspace(start, stop, num, dtype=dtype, endpoint=endpoint) def ones(shape, /, *, dtype=None, device=None): - from .. import ones if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return ones(shape, dtype=dtype) + return np.ones(shape, dtype=dtype) def ones_like(x, /, *, dtype=None, device=None): - from .. import ones_like if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return ones_like(x, dtype=dtype) + return np.ones_like(x, dtype=dtype) def zeros(shape, /, *, dtype=None, device=None): - from .. import zeros if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return zeros(shape, dtype=dtype) + return np.zeros(shape, dtype=dtype) def zeros_like(x, /, *, dtype=None, device=None): - from .. import zeros_like if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return zeros_like(x, dtype=dtype) + return np.zeros_like(x, dtype=dtype) diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index f9052020e..ef820dd5b 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -1,230 +1,177 @@ +import numpy as np + def abs(x, /): - from .. import abs - return abs(x) + return np.abs(x) def acos(x, /): # Note: the function name is different here - from .. import arccos - return arccos(x) + return np.arccos(x) def acosh(x, /): # Note: the function name is different here - from .. import arccosh - return arccosh(x) + return np.arccosh(x) def add(x1, x2, /): - from .. import add - return add(x1, x2) + return np.add(x1, x2) def asin(x, /): # Note: the function name is different here - from .. import arcsin - return arcsin(x) + return np.arcsin(x) def asinh(x, /): # Note: the function name is different here - from .. import arcsinh - return arcsinh(x) + return np.arcsinh(x) def atan(x, /): # Note: the function name is different here - from .. import arctan - return arctan(x) + return np.arctan(x) def atan2(x1, x2, /): # Note: the function name is different here - from .. import arctan2 - return arctan2(x1, x2) + return np.arctan2(x1, x2) def atanh(x, /): # Note: the function name is different here - from .. import arctanh - return arctanh(x) + return np.arctanh(x) def bitwise_and(x1, x2, /): - from .. import bitwise_and - return bitwise_and(x1, x2) + return np.bitwise_and(x1, x2) def bitwise_left_shift(x1, x2, /): # Note: the function name is different here - from .. import left_shift - return left_shift(x1, x2) + return np.left_shift(x1, x2) def bitwise_invert(x, /): # Note: the function name is different here - from .. import invert - return invert(x) + return np.invert(x) def bitwise_or(x1, x2, /): - from .. import bitwise_or - return bitwise_or(x1, x2) + return np.bitwise_or(x1, x2) def bitwise_right_shift(x1, x2, /): # Note: the function name is different here - from .. import right_shift - return right_shift(x1, x2) + return np.right_shift(x1, x2) def bitwise_xor(x1, x2, /): - from .. import bitwise_xor - return bitwise_xor(x1, x2) + return np.bitwise_xor(x1, x2) def ceil(x, /): - from .. import ceil - return ceil(x) + return np.ceil(x) def cos(x, /): - from .. import cos - return cos(x) + return np.cos(x) def cosh(x, /): - from .. import cosh - return cosh(x) + return np.cosh(x) def divide(x1, x2, /): - from .. import divide - return divide(x1, x2) + return np.divide(x1, x2) def equal(x1, x2, /): - from .. import equal - return equal(x1, x2) + return np.equal(x1, x2) def exp(x, /): - from .. import exp - return exp(x) + return np.exp(x) def expm1(x, /): - from .. import expm1 - return expm1(x) + return np.expm1(x) def floor(x, /): - from .. import floor - return floor(x) + return np.floor(x) def floor_divide(x1, x2, /): - from .. import floor_divide - return floor_divide(x1, x2) + return np.floor_divide(x1, x2) def greater(x1, x2, /): - from .. import greater - return greater(x1, x2) + return np.greater(x1, x2) def greater_equal(x1, x2, /): - from .. import greater_equal - return greater_equal(x1, x2) + return np.greater_equal(x1, x2) def isfinite(x, /): - from .. import isfinite - return isfinite(x) + return np.isfinite(x) def isinf(x, /): - from .. import isinf - return isinf(x) + return np.isinf(x) def isnan(x, /): - from .. import isnan - return isnan(x) + return np.isnan(x) def less(x1, x2, /): - from .. import less - return less(x1, x2) + return np.less(x1, x2) def less_equal(x1, x2, /): - from .. import less_equal - return less_equal(x1, x2) + return np.less_equal(x1, x2) def log(x, /): - from .. import log - return log(x) + return np.log(x) def log1p(x, /): - from .. import log1p - return log1p(x) + return np.log1p(x) def log2(x, /): - from .. import log2 - return log2(x) + return np.log2(x) def log10(x, /): - from .. import log10 - return log10(x) + return np.log10(x) def logical_and(x1, x2, /): - from .. import logical_and - return logical_and(x1, x2) + return np.logical_and(x1, x2) def logical_not(x, /): - from .. import logical_not - return logical_not(x) + return np.logical_not(x) def logical_or(x1, x2, /): - from .. import logical_or - return logical_or(x1, x2) + return np.logical_or(x1, x2) def logical_xor(x1, x2, /): - from .. import logical_xor - return logical_xor(x1, x2) + return np.logical_xor(x1, x2) def multiply(x1, x2, /): - from .. import multiply - return multiply(x1, x2) + return np.multiply(x1, x2) def negative(x, /): - from .. import negative - return negative(x) + return np.negative(x) def not_equal(x1, x2, /): - from .. import not_equal - return not_equal(x1, x2) + return np.not_equal(x1, x2) def positive(x, /): - from .. import positive - return positive(x) + return np.positive(x) def pow(x1, x2, /): # Note: the function name is different here - from .. import power - return power(x1, x2) + return np.power(x1, x2) def remainder(x1, x2, /): - from .. import remainder - return remainder(x1, x2) + return np.remainder(x1, x2) def round(x, /): - from .. import round - return round(x) + return np.round(x) def sign(x, /): - from .. import sign - return sign(x) + return np.sign(x) def sin(x, /): - from .. import sin - return sin(x) + return np.sin(x) def sinh(x, /): - from .. import sinh - return sinh(x) + return np.sinh(x) def square(x, /): - from .. import square - return square(x) + return np.square(x) def sqrt(x, /): - from .. import sqrt - return sqrt(x) + return np.sqrt(x) def subtract(x1, x2, /): - from .. import subtract - return subtract(x1, x2) + return np.subtract(x1, x2) def tan(x, /): - from .. import tan - return tan(x) + return np.tan(x) def tanh(x, /): - from .. import tanh - return tanh(x) + return np.tanh(x) def trunc(x, /): - from .. import trunc - return trunc(x) + return np.trunc(x) diff --git a/numpy/_array_api/_linear_algebra_functions.py b/numpy/_array_api/_linear_algebra_functions.py index 10c81d12c..ffb589c99 100644 --- a/numpy/_array_api/_linear_algebra_functions.py +++ b/numpy/_array_api/_linear_algebra_functions.py @@ -1,93 +1,73 @@ +import numpy as np + # def cholesky(): -# from .. import cholesky -# return cholesky() +# return np.cholesky() def cross(x1, x2, /, *, axis=-1): - from .. import cross - return cross(x1, x2, axis=axis) + return np.cross(x1, x2, axis=axis) def det(x, /): # Note: this function is being imported from a nondefault namespace - from ..linalg import det - return det(x) + return np.det(x) def diagonal(x, /, *, axis1=0, axis2=1, offset=0): - from .. import diagonal - return diagonal(x, axis1=axis1, axis2=axis2, offset=offset) + return np.diagonal(x, axis1=axis1, axis2=axis2, offset=offset) # def dot(): -# from .. import dot -# return dot() +# return np.dot() # # def eig(): -# from .. import eig -# return eig() +# return np.eig() # # def eigvalsh(): -# from .. import eigvalsh -# return eigvalsh() +# return np.eigvalsh() # # def einsum(): -# from .. import einsum -# return einsum() +# return np.einsum() def inv(x): # Note: this function is being imported from a nondefault namespace - from ..linalg import inv - return inv(x) + return np.inv(x) # def lstsq(): -# from .. import lstsq -# return lstsq() +# return np.lstsq() # # def matmul(): -# from .. import matmul -# return matmul() +# return np.matmul() # # def matrix_power(): -# from .. import matrix_power -# return matrix_power() +# return np.matrix_power() # # def matrix_rank(): -# from .. import matrix_rank -# return matrix_rank() +# return np.matrix_rank() def norm(x, /, *, axis=None, keepdims=False, ord=None): # Note: this function is being imported from a nondefault namespace - from ..linalg import norm # Note: this is different from the default behavior if axis == None and x.ndim > 2: x = x.flatten() - return norm(x, axis=axis, keepdims=keepdims, ord=ord) + return np.norm(x, axis=axis, keepdims=keepdims, ord=ord) def outer(x1, x2, /): - from .. import outer - return outer(x1, x2) + return np.outer(x1, x2) # def pinv(): -# from .. import pinv -# return pinv() +# return np.pinv() # # def qr(): -# from .. import qr -# return qr() +# return np.qr() # # def slogdet(): -# from .. import slogdet -# return slogdet() +# return np.slogdet() # # def solve(): -# from .. import solve -# return solve() +# return np.solve() # # def svd(): -# from .. import svd -# return svd() +# return np.svd() def trace(x, /, *, axis1=0, axis2=1, offset=0): - from .. import trace - return trace(x, axis1=axis1, axis2=axis2, offset=offset) + return np.trace(x, axis1=axis1, axis2=axis2, offset=offset) def transpose(x, /, *, axes=None): - from .. import transpose - return transpose(x, axes=axes) + return np.transpose(x, axes=axes) diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index 19e9c1cab..262c712f8 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -1,28 +1,23 @@ +import numpy as np + def concat(arrays, /, *, axis=0): # Note: the function name is different here - from .. import concatenate - return concatenate(arrays, axis=axis) + return np.concatenate(arrays, axis=axis) def expand_dims(x, axis, /): - from .. import expand_dims - return expand_dims(x, axis) + return np.expand_dims(x, axis) def flip(x, /, *, axis=None): - from .. import flip - return flip(x, axis=axis) + return np.flip(x, axis=axis) def reshape(x, shape, /): - from .. import reshape - return reshape(x, shape) + return np.reshape(x, shape) def roll(x, shift, /, *, axis=None): - from .. import roll - return roll(x, shift, axis=axis) + return np.roll(x, shift, axis=axis) def squeeze(x, /, *, axis=None): - from .. import squeeze - return squeeze(x, axis=axis) + return np.squeeze(x, axis=axis) def stack(arrays, /, *, axis=0): - from .. import stack - return stack(arrays, axis=axis) + return np.stack(arrays, axis=axis) diff --git a/numpy/_array_api/_searching_functions.py b/numpy/_array_api/_searching_functions.py index c6035ca77..62763eaca 100644 --- a/numpy/_array_api/_searching_functions.py +++ b/numpy/_array_api/_searching_functions.py @@ -1,15 +1,13 @@ +import numpy as np + def argmax(x, /, *, axis=None, keepdims=False): - from .. import argmax - return argmax(x, axis=axis, keepdims=keepdims) + return np.argmax(x, axis=axis, keepdims=keepdims) def argmin(x, /, *, axis=None, keepdims=False): - from .. import argmin - return argmin(x, axis=axis, keepdims=keepdims) + return np.argmin(x, axis=axis, keepdims=keepdims) def nonzero(x, /): - from .. import nonzero - return nonzero(x) + return np.nonzero(x) def where(condition, x1, x2, /): - from .. import where - return where(condition, x1, x2) + return np.where(condition, x1, x2) diff --git a/numpy/_array_api/_set_functions.py b/numpy/_array_api/_set_functions.py index b6198765a..7603b6b30 100644 --- a/numpy/_array_api/_set_functions.py +++ b/numpy/_array_api/_set_functions.py @@ -1,3 +1,4 @@ +import numpy as np + def unique(x, /, *, return_counts=False, return_index=False, return_inverse=False, sorted=True): - from .. import unique - return unique(x, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse, sorted=sorted) + return np.unique(x, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse, sorted=sorted) diff --git a/numpy/_array_api/_sorting_functions.py b/numpy/_array_api/_sorting_functions.py index fb2f819a2..6477029b9 100644 --- a/numpy/_array_api/_sorting_functions.py +++ b/numpy/_array_api/_sorting_functions.py @@ -1,19 +1,17 @@ +import numpy as np + def argsort(x, /, *, axis=-1, descending=False, stable=True): - from .. import argsort - from .. import flip # Note: this keyword argument is different, and the default is different. kind = 'stable' if stable else 'quicksort' - res = argsort(x, axis=axis, kind=kind) + res = np.argsort(x, axis=axis, kind=kind) if descending: - res = flip(res, axis=axis) + res = np.flip(res, axis=axis) return res def sort(x, /, *, axis=-1, descending=False, stable=True): - from .. import sort - from .. import flip # Note: this keyword argument is different, and the default is different. kind = 'stable' if stable else 'quicksort' - res = sort(x, axis=axis, kind=kind) + res = np.sort(x, axis=axis, kind=kind) if descending: - res = flip(res, axis=axis) + res = np.flip(res, axis=axis) return res diff --git a/numpy/_array_api/_statistical_functions.py b/numpy/_array_api/_statistical_functions.py index 339835095..833c47f66 100644 --- a/numpy/_array_api/_statistical_functions.py +++ b/numpy/_array_api/_statistical_functions.py @@ -1,29 +1,24 @@ +import numpy as np + def max(x, /, *, axis=None, keepdims=False): - from .. import max - return max(x, axis=axis, keepdims=keepdims) + return np.max(x, axis=axis, keepdims=keepdims) def mean(x, /, *, axis=None, keepdims=False): - from .. import mean - return mean(x, axis=axis, keepdims=keepdims) + return np.mean(x, axis=axis, keepdims=keepdims) def min(x, /, *, axis=None, keepdims=False): - from .. import min - return min(x, axis=axis, keepdims=keepdims) + return np.min(x, axis=axis, keepdims=keepdims) def prod(x, /, *, axis=None, keepdims=False): - from .. import prod - return prod(x, axis=axis, keepdims=keepdims) + return np.prod(x, axis=axis, keepdims=keepdims) def std(x, /, *, axis=None, correction=0.0, keepdims=False): - from .. import std # Note: the keyword argument correction is different here - return std(x, axis=axis, ddof=correction, keepdims=keepdims) + return np.std(x, axis=axis, ddof=correction, keepdims=keepdims) def sum(x, /, *, axis=None, keepdims=False): - from .. import sum - return sum(x, axis=axis, keepdims=keepdims) + return np.sum(x, axis=axis, keepdims=keepdims) def var(x, /, *, axis=None, correction=0.0, keepdims=False): - from .. import var # Note: the keyword argument correction is different here - return var(x, axis=axis, ddof=correction, keepdims=keepdims) + return np.var(x, axis=axis, ddof=correction, keepdims=keepdims) diff --git a/numpy/_array_api/_utility_functions.py b/numpy/_array_api/_utility_functions.py index accc43e1e..0bbdef412 100644 --- a/numpy/_array_api/_utility_functions.py +++ b/numpy/_array_api/_utility_functions.py @@ -1,7 +1,7 @@ +import numpy as np + def all(x, /, *, axis=None, keepdims=False): - from .. import all - return all(x, axis=axis, keepdims=keepdims) + return np.all(x, axis=axis, keepdims=keepdims) def any(x, /, *, axis=None, keepdims=False): - from .. import any - return any(x, axis=axis, keepdims=keepdims) + return np.any(x, axis=axis, keepdims=keepdims) -- cgit v1.2.1 From a78d20a279b3f081367109338c78ab20e08c642c Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 12 Jan 2021 16:30:31 -0700 Subject: Fix array API functions that need to use np.linalg --- numpy/_array_api/_linear_algebra_functions.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_linear_algebra_functions.py b/numpy/_array_api/_linear_algebra_functions.py index ffb589c99..920a86d9b 100644 --- a/numpy/_array_api/_linear_algebra_functions.py +++ b/numpy/_array_api/_linear_algebra_functions.py @@ -8,7 +8,7 @@ def cross(x1, x2, /, *, axis=-1): def det(x, /): # Note: this function is being imported from a nondefault namespace - return np.det(x) + return np.linalg.det(x) def diagonal(x, /, *, axis1=0, axis2=1, offset=0): return np.diagonal(x, axis1=axis1, axis2=axis2, offset=offset) @@ -27,7 +27,7 @@ def diagonal(x, /, *, axis1=0, axis2=1, offset=0): def inv(x): # Note: this function is being imported from a nondefault namespace - return np.inv(x) + return np.linalg.inv(x) # def lstsq(): # return np.lstsq() @@ -46,7 +46,7 @@ def norm(x, /, *, axis=None, keepdims=False, ord=None): # Note: this is different from the default behavior if axis == None and x.ndim > 2: x = x.flatten() - return np.norm(x, axis=axis, keepdims=keepdims, ord=ord) + return np.linalg.norm(x, axis=axis, keepdims=keepdims, ord=ord) def outer(x1, x2, /): return np.outer(x1, x2) -- cgit v1.2.1 From 00dda8df893d2df8730e0977178f1a116ec9cf91 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 12 Jan 2021 16:36:41 -0700 Subject: Add basic docstrings to the array API wrapper functions The docstrings just point back to the functions they wrap for now. More thought may need to be put into this for the future. Most functions can actually perhaps inherit the docstring of the function they wrap directly, but there are some functions that have differences (e.g., different names, different keyword arguments, fewer keyword arguments, etc.). There's also the question of how to handle cross-references/see alsos that point to functions not in the API spec and behavior shown in docstring examples that isn't required in the spec. --- numpy/_array_api/_creation_functions.py | 55 ++++++ numpy/_array_api/_elementwise_functions.py | 275 ++++++++++++++++++++++++++ numpy/_array_api/_linear_algebra_functions.py | 112 ++++++++++- numpy/_array_api/_manipulation_functions.py | 35 ++++ numpy/_array_api/_searching_functions.py | 20 ++ numpy/_array_api/_set_functions.py | 5 + numpy/_array_api/_sorting_functions.py | 10 + numpy/_array_api/_utility_functions.py | 10 + 8 files changed, 521 insertions(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index b74eca060..b6c0c22cc 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -1,66 +1,121 @@ import numpy as np def arange(start, /, *, stop=None, step=1, dtype=None, device=None): + """ + Array API compatible wrapper for :py:func:`np.arange `. + + See its docstring for more information. + """ if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") return np.arange(start, stop=stop, step=step, dtype=dtype) def empty(shape, /, *, dtype=None, device=None): + """ + Array API compatible wrapper for :py:func:`np.empty `. + + See its docstring for more information. + """ if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") return np.empty(shape, dtype=dtype) def empty_like(x, /, *, dtype=None, device=None): + """ + Array API compatible wrapper for :py:func:`np.empty_like `. + + See its docstring for more information. + """ if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") return np.empty_like(x, dtype=dtype) def eye(N, /, *, M=None, k=0, dtype=None, device=None): + """ + Array API compatible wrapper for :py:func:`np.eye `. + + See its docstring for more information. + """ if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") return np.eye(N, M=M, k=k, dtype=dtype) def full(shape, fill_value, /, *, dtype=None, device=None): + """ + Array API compatible wrapper for :py:func:`np.full `. + + See its docstring for more information. + """ if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") return np.full(shape, fill_value, dtype=dtype) def full_like(x, fill_value, /, *, dtype=None, device=None): + """ + Array API compatible wrapper for :py:func:`np.full_like `. + + See its docstring for more information. + """ if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") return np.full_like(x, fill_value, dtype=dtype) def linspace(start, stop, num, /, *, dtype=None, device=None, endpoint=True): + """ + Array API compatible wrapper for :py:func:`np.linspace `. + + See its docstring for more information. + """ if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") return np.linspace(start, stop, num, dtype=dtype, endpoint=endpoint) def ones(shape, /, *, dtype=None, device=None): + """ + Array API compatible wrapper for :py:func:`np.ones `. + + See its docstring for more information. + """ if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") return np.ones(shape, dtype=dtype) def ones_like(x, /, *, dtype=None, device=None): + """ + Array API compatible wrapper for :py:func:`np.ones_like `. + + See its docstring for more information. + """ if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") return np.ones_like(x, dtype=dtype) def zeros(shape, /, *, dtype=None, device=None): + """ + Array API compatible wrapper for :py:func:`np.zeros `. + + See its docstring for more information. + """ if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") return np.zeros(shape, dtype=dtype) def zeros_like(x, /, *, dtype=None, device=None): + """ + Array API compatible wrapper for :py:func:`np.zeros_like `. + + See its docstring for more information. + """ if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index ef820dd5b..7ec01b2e1 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -1,177 +1,452 @@ import numpy as np def abs(x, /): + """ + Array API compatible wrapper for :py:func:`np.abs `. + + See its docstring for more information. + """ return np.abs(x) def acos(x, /): + """ + Array API compatible wrapper for :py:func:`np.arccos `. + + See its docstring for more information. + """ # Note: the function name is different here return np.arccos(x) def acosh(x, /): + """ + Array API compatible wrapper for :py:func:`np.arccosh `. + + See its docstring for more information. + """ # Note: the function name is different here return np.arccosh(x) def add(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.add `. + + See its docstring for more information. + """ return np.add(x1, x2) def asin(x, /): + """ + Array API compatible wrapper for :py:func:`np.arcsin `. + + See its docstring for more information. + """ # Note: the function name is different here return np.arcsin(x) def asinh(x, /): + """ + Array API compatible wrapper for :py:func:`np.arcsinh `. + + See its docstring for more information. + """ # Note: the function name is different here return np.arcsinh(x) def atan(x, /): + """ + Array API compatible wrapper for :py:func:`np.arctan `. + + See its docstring for more information. + """ # Note: the function name is different here return np.arctan(x) def atan2(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.arctan2 `. + + See its docstring for more information. + """ # Note: the function name is different here return np.arctan2(x1, x2) def atanh(x, /): + """ + Array API compatible wrapper for :py:func:`np.arctanh `. + + See its docstring for more information. + """ # Note: the function name is different here return np.arctanh(x) def bitwise_and(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.bitwise_and `. + + See its docstring for more information. + """ return np.bitwise_and(x1, x2) def bitwise_left_shift(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.left_shift `. + + See its docstring for more information. + """ # Note: the function name is different here return np.left_shift(x1, x2) def bitwise_invert(x, /): + """ + Array API compatible wrapper for :py:func:`np.invert `. + + See its docstring for more information. + """ # Note: the function name is different here return np.invert(x) def bitwise_or(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.bitwise_or `. + + See its docstring for more information. + """ return np.bitwise_or(x1, x2) def bitwise_right_shift(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.right_shift `. + + See its docstring for more information. + """ # Note: the function name is different here return np.right_shift(x1, x2) def bitwise_xor(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.bitwise_xor `. + + See its docstring for more information. + """ return np.bitwise_xor(x1, x2) def ceil(x, /): + """ + Array API compatible wrapper for :py:func:`np.ceil `. + + See its docstring for more information. + """ return np.ceil(x) def cos(x, /): + """ + Array API compatible wrapper for :py:func:`np.cos `. + + See its docstring for more information. + """ return np.cos(x) def cosh(x, /): + """ + Array API compatible wrapper for :py:func:`np.cosh `. + + See its docstring for more information. + """ return np.cosh(x) def divide(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.divide `. + + See its docstring for more information. + """ return np.divide(x1, x2) def equal(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.equal `. + + See its docstring for more information. + """ return np.equal(x1, x2) def exp(x, /): + """ + Array API compatible wrapper for :py:func:`np.exp `. + + See its docstring for more information. + """ return np.exp(x) def expm1(x, /): + """ + Array API compatible wrapper for :py:func:`np.expm1 `. + + See its docstring for more information. + """ return np.expm1(x) def floor(x, /): + """ + Array API compatible wrapper for :py:func:`np.floor `. + + See its docstring for more information. + """ return np.floor(x) def floor_divide(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.floor_divide `. + + See its docstring for more information. + """ return np.floor_divide(x1, x2) def greater(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.greater `. + + See its docstring for more information. + """ return np.greater(x1, x2) def greater_equal(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.greater_equal `. + + See its docstring for more information. + """ return np.greater_equal(x1, x2) def isfinite(x, /): + """ + Array API compatible wrapper for :py:func:`np.isfinite `. + + See its docstring for more information. + """ return np.isfinite(x) def isinf(x, /): + """ + Array API compatible wrapper for :py:func:`np.isinf `. + + See its docstring for more information. + """ return np.isinf(x) def isnan(x, /): + """ + Array API compatible wrapper for :py:func:`np.isnan `. + + See its docstring for more information. + """ return np.isnan(x) def less(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.less `. + + See its docstring for more information. + """ return np.less(x1, x2) def less_equal(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.less_equal `. + + See its docstring for more information. + """ return np.less_equal(x1, x2) def log(x, /): + """ + Array API compatible wrapper for :py:func:`np.log `. + + See its docstring for more information. + """ return np.log(x) def log1p(x, /): + """ + Array API compatible wrapper for :py:func:`np.log1p `. + + See its docstring for more information. + """ return np.log1p(x) def log2(x, /): + """ + Array API compatible wrapper for :py:func:`np.log2 `. + + See its docstring for more information. + """ return np.log2(x) def log10(x, /): + """ + Array API compatible wrapper for :py:func:`np.log10 `. + + See its docstring for more information. + """ return np.log10(x) def logical_and(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.logical_and `. + + See its docstring for more information. + """ return np.logical_and(x1, x2) def logical_not(x, /): + """ + Array API compatible wrapper for :py:func:`np.logical_not `. + + See its docstring for more information. + """ return np.logical_not(x) def logical_or(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.logical_or `. + + See its docstring for more information. + """ return np.logical_or(x1, x2) def logical_xor(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.logical_xor `. + + See its docstring for more information. + """ return np.logical_xor(x1, x2) def multiply(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.multiply `. + + See its docstring for more information. + """ return np.multiply(x1, x2) def negative(x, /): + """ + Array API compatible wrapper for :py:func:`np.negative `. + + See its docstring for more information. + """ return np.negative(x) def not_equal(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.not_equal `. + + See its docstring for more information. + """ return np.not_equal(x1, x2) def positive(x, /): + """ + Array API compatible wrapper for :py:func:`np.positive `. + + See its docstring for more information. + """ return np.positive(x) def pow(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.power `. + + See its docstring for more information. + """ # Note: the function name is different here return np.power(x1, x2) def remainder(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.remainder `. + + See its docstring for more information. + """ return np.remainder(x1, x2) def round(x, /): + """ + Array API compatible wrapper for :py:func:`np.round `. + + See its docstring for more information. + """ return np.round(x) def sign(x, /): + """ + Array API compatible wrapper for :py:func:`np.sign `. + + See its docstring for more information. + """ return np.sign(x) def sin(x, /): + """ + Array API compatible wrapper for :py:func:`np.sin `. + + See its docstring for more information. + """ return np.sin(x) def sinh(x, /): + """ + Array API compatible wrapper for :py:func:`np.sinh `. + + See its docstring for more information. + """ return np.sinh(x) def square(x, /): + """ + Array API compatible wrapper for :py:func:`np.square `. + + See its docstring for more information. + """ return np.square(x) def sqrt(x, /): + """ + Array API compatible wrapper for :py:func:`np.sqrt `. + + See its docstring for more information. + """ return np.sqrt(x) def subtract(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.subtract `. + + See its docstring for more information. + """ return np.subtract(x1, x2) def tan(x, /): + """ + Array API compatible wrapper for :py:func:`np.tan `. + + See its docstring for more information. + """ return np.tan(x) def tanh(x, /): + """ + Array API compatible wrapper for :py:func:`np.tanh `. + + See its docstring for more information. + """ return np.tanh(x) def trunc(x, /): + """ + Array API compatible wrapper for :py:func:`np.trunc `. + + See its docstring for more information. + """ return np.trunc(x) diff --git a/numpy/_array_api/_linear_algebra_functions.py b/numpy/_array_api/_linear_algebra_functions.py index 920a86d9b..cfb184e8d 100644 --- a/numpy/_array_api/_linear_algebra_functions.py +++ b/numpy/_array_api/_linear_algebra_functions.py @@ -1,73 +1,183 @@ import numpy as np # def cholesky(): +# """ +# Array API compatible wrapper for :py:func:`np.cholesky `. +# +# See its docstring for more information. +# """ # return np.cholesky() def cross(x1, x2, /, *, axis=-1): + """ + Array API compatible wrapper for :py:func:`np.cross `. + + See its docstring for more information. + """ return np.cross(x1, x2, axis=axis) def det(x, /): + """ + Array API compatible wrapper for :py:func:`np.linalg.det `. + + See its docstring for more information. + """ # Note: this function is being imported from a nondefault namespace return np.linalg.det(x) def diagonal(x, /, *, axis1=0, axis2=1, offset=0): + """ + Array API compatible wrapper for :py:func:`np.diagonal `. + + See its docstring for more information. + """ return np.diagonal(x, axis1=axis1, axis2=axis2, offset=offset) # def dot(): +# """ +# Array API compatible wrapper for :py:func:`np.dot `. +# +# See its docstring for more information. +# """ # return np.dot() # # def eig(): +# """ +# Array API compatible wrapper for :py:func:`np.eig `. +# +# See its docstring for more information. +# """ # return np.eig() # # def eigvalsh(): +# """ +# Array API compatible wrapper for :py:func:`np.eigvalsh `. +# +# See its docstring for more information. +# """ # return np.eigvalsh() # # def einsum(): +# """ +# Array API compatible wrapper for :py:func:`np.einsum `. +# +# See its docstring for more information. +# """ # return np.einsum() def inv(x): + """ + Array API compatible wrapper for :py:func:`np.linalg.inv `. + + See its docstring for more information. + """ # Note: this function is being imported from a nondefault namespace return np.linalg.inv(x) # def lstsq(): +# """ +# Array API compatible wrapper for :py:func:`np.lstsq `. +# +# See its docstring for more information. +# """ # return np.lstsq() # # def matmul(): +# """ +# Array API compatible wrapper for :py:func:`np.matmul `. +# +# See its docstring for more information. +# """ # return np.matmul() # # def matrix_power(): +# """ +# Array API compatible wrapper for :py:func:`np.matrix_power `. +# +# See its docstring for more information. +# """ # return np.matrix_power() # # def matrix_rank(): +# """ +# Array API compatible wrapper for :py:func:`np.matrix_rank `. +# +# See its docstring for more information. +# """ # return np.matrix_rank() def norm(x, /, *, axis=None, keepdims=False, ord=None): - # Note: this function is being imported from a nondefault namespace + """ + Array API compatible wrapper for :py:func:`np.linalg.norm `. + + See its docstring for more information. + """ # Note: this is different from the default behavior if axis == None and x.ndim > 2: x = x.flatten() + # Note: this function is being imported from a nondefault namespace return np.linalg.norm(x, axis=axis, keepdims=keepdims, ord=ord) def outer(x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.outer `. + + See its docstring for more information. + """ return np.outer(x1, x2) # def pinv(): +# """ +# Array API compatible wrapper for :py:func:`np.pinv `. +# +# See its docstring for more information. +# """ # return np.pinv() # # def qr(): +# """ +# Array API compatible wrapper for :py:func:`np.qr `. +# +# See its docstring for more information. +# """ # return np.qr() # # def slogdet(): +# """ +# Array API compatible wrapper for :py:func:`np.slogdet `. +# +# See its docstring for more information. +# """ # return np.slogdet() # # def solve(): +# """ +# Array API compatible wrapper for :py:func:`np.solve `. +# +# See its docstring for more information. +# """ # return np.solve() # # def svd(): +# """ +# Array API compatible wrapper for :py:func:`np.svd `. +# +# See its docstring for more information. +# """ # return np.svd() def trace(x, /, *, axis1=0, axis2=1, offset=0): + """ + Array API compatible wrapper for :py:func:`np.trace `. + + See its docstring for more information. + """ return np.trace(x, axis1=axis1, axis2=axis2, offset=offset) def transpose(x, /, *, axes=None): + """ + Array API compatible wrapper for :py:func:`np.transpose `. + + See its docstring for more information. + """ return np.transpose(x, axes=axes) diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index 262c712f8..834aa2f8f 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -1,23 +1,58 @@ import numpy as np def concat(arrays, /, *, axis=0): + """ + Array API compatible wrapper for :py:func:`np.concatenate `. + + See its docstring for more information. + """ # Note: the function name is different here return np.concatenate(arrays, axis=axis) def expand_dims(x, axis, /): + """ + Array API compatible wrapper for :py:func:`np.expand_dims `. + + See its docstring for more information. + """ return np.expand_dims(x, axis) def flip(x, /, *, axis=None): + """ + Array API compatible wrapper for :py:func:`np.flip `. + + See its docstring for more information. + """ return np.flip(x, axis=axis) def reshape(x, shape, /): + """ + Array API compatible wrapper for :py:func:`np.reshape `. + + See its docstring for more information. + """ return np.reshape(x, shape) def roll(x, shift, /, *, axis=None): + """ + Array API compatible wrapper for :py:func:`np.roll `. + + See its docstring for more information. + """ return np.roll(x, shift, axis=axis) def squeeze(x, /, *, axis=None): + """ + Array API compatible wrapper for :py:func:`np.squeeze `. + + See its docstring for more information. + """ return np.squeeze(x, axis=axis) def stack(arrays, /, *, axis=0): + """ + Array API compatible wrapper for :py:func:`np.stack `. + + See its docstring for more information. + """ return np.stack(arrays, axis=axis) diff --git a/numpy/_array_api/_searching_functions.py b/numpy/_array_api/_searching_functions.py index 62763eaca..4eed66c48 100644 --- a/numpy/_array_api/_searching_functions.py +++ b/numpy/_array_api/_searching_functions.py @@ -1,13 +1,33 @@ import numpy as np def argmax(x, /, *, axis=None, keepdims=False): + """ + Array API compatible wrapper for :py:func:`np.argmax `. + + See its docstring for more information. + """ return np.argmax(x, axis=axis, keepdims=keepdims) def argmin(x, /, *, axis=None, keepdims=False): + """ + Array API compatible wrapper for :py:func:`np.argmin `. + + See its docstring for more information. + """ return np.argmin(x, axis=axis, keepdims=keepdims) def nonzero(x, /): + """ + Array API compatible wrapper for :py:func:`np.nonzero `. + + See its docstring for more information. + """ return np.nonzero(x) def where(condition, x1, x2, /): + """ + Array API compatible wrapper for :py:func:`np.where `. + + See its docstring for more information. + """ return np.where(condition, x1, x2) diff --git a/numpy/_array_api/_set_functions.py b/numpy/_array_api/_set_functions.py index 7603b6b30..fd1438be5 100644 --- a/numpy/_array_api/_set_functions.py +++ b/numpy/_array_api/_set_functions.py @@ -1,4 +1,9 @@ import numpy as np def unique(x, /, *, return_counts=False, return_index=False, return_inverse=False, sorted=True): + """ + Array API compatible wrapper for :py:func:`np.unique `. + + See its docstring for more information. + """ return np.unique(x, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse, sorted=sorted) diff --git a/numpy/_array_api/_sorting_functions.py b/numpy/_array_api/_sorting_functions.py index 6477029b9..5ffe6c8f9 100644 --- a/numpy/_array_api/_sorting_functions.py +++ b/numpy/_array_api/_sorting_functions.py @@ -1,6 +1,11 @@ import numpy as np def argsort(x, /, *, axis=-1, descending=False, stable=True): + """ + Array API compatible wrapper for :py:func:`np.argsort `. + + See its docstring for more information. + """ # Note: this keyword argument is different, and the default is different. kind = 'stable' if stable else 'quicksort' res = np.argsort(x, axis=axis, kind=kind) @@ -9,6 +14,11 @@ def argsort(x, /, *, axis=-1, descending=False, stable=True): return res def sort(x, /, *, axis=-1, descending=False, stable=True): + """ + Array API compatible wrapper for :py:func:`np.sort `. + + See its docstring for more information. + """ # Note: this keyword argument is different, and the default is different. kind = 'stable' if stable else 'quicksort' res = np.sort(x, axis=axis, kind=kind) diff --git a/numpy/_array_api/_utility_functions.py b/numpy/_array_api/_utility_functions.py index 0bbdef412..19743d15c 100644 --- a/numpy/_array_api/_utility_functions.py +++ b/numpy/_array_api/_utility_functions.py @@ -1,7 +1,17 @@ import numpy as np def all(x, /, *, axis=None, keepdims=False): + """ + Array API compatible wrapper for :py:func:`np.all `. + + See its docstring for more information. + """ return np.all(x, axis=axis, keepdims=keepdims) def any(x, /, *, axis=None, keepdims=False): + """ + Array API compatible wrapper for :py:func:`np.any `. + + See its docstring for more information. + """ return np.any(x, axis=axis, keepdims=keepdims) -- cgit v1.2.1 From df698f80732508af50b24ecc1b4bd34c470aaba8 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 13 Jan 2021 14:00:38 -0700 Subject: Add an explanatory docstring to _array_api/__init__.py This is mostly aimed at any potential reviewers of the module for now. --- numpy/_array_api/__init__.py | 66 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/__init__.py b/numpy/_array_api/__init__.py index c5f8154d9..ad66bc565 100644 --- a/numpy/_array_api/__init__.py +++ b/numpy/_array_api/__init__.py @@ -1,3 +1,69 @@ +""" +A NumPy sub-namespace that conforms to the Python array API standard. + +This is a proof-of-concept namespace that wraps the corresponding NumPy +functions to give a conforming implementation of the Python array API standard +(https://data-apis.github.io/array-api/latest/). The standard is currently in +an RFC phase and comments on it are both welcome and encouraged. Comments +should be made either at https://github.com/data-apis/array-api or at +https://github.com/data-apis/consortium-feedback/discussions. + +This submodule will be accompanied with a NEP (not yet written) proposing its +inclusion in NumPy. + +NumPy already follows the proposed spec for the most part, so this module +serves mostly as a thin wrapper around it. However, NumPy also implements a +lot of behavior that is not included in the spec, so this serves as a +restricted subset of the API. Only those functions that are part of the spec +are included in this namespace, and all functions are given with the exact +signature given in the spec, including the use of position-only arguments, and +omitting any extra keyword arguments implemented by NumPy but not part of the +spec. Note that the array object itself is unchanged, as implementing a +restricted subclass of ndarray seems unnecessarily complex for the purposes of +this namespace, so the API of array methods and other behaviors of the array +object will include things that are not part of the spec. + +The spec is designed as a "minimal API subset" and explicitly allows libraries +to include behaviors not specified by it. But users of this module that intend +to write portable code should be aware that only those behaviors that are +listed in the spec are guaranteed to be implemented across libraries. + +A few notes about the current state of this submodule: + +- There is a test suite that tests modules against the array API standard at + https://github.com/data-apis/array-api-tests. The test suite is still a work + in progress, but the existing tests pass on this module, with a few + exceptions: + + - Device support is not yet implemented in NumPy + (https://data-apis.github.io/array-api/latest/design_topics/device_support.html). + As a result, the `device` attribute of the array object is missing, and + array creation functions that take the `device` keyword argument will fail + with NotImplementedError. + + - DLPack support (see https://github.com/data-apis/array-api/pull/106) is + not included here, as it requires a full implementation in NumPy proper + first. + + - np.argmin and np.argmax do not implement the keepdims keyword argument. + + - Some linear algebra functions in the spec are still a work in progress (to + be added soon). These will be updated once the spec is. + + - Some tests in the test suite are still not fully correct in that they test + all datatypes whereas certain functions are only defined for a subset of + datatypes. + + The test suite is yet complete, and even the tests that exist are not + guaranteed to give a comprehensive coverage of the spec. Therefore, those + reviewing this submodule should refer to the standard documents themselves. + +- All places where the implementations in this submodule are known to deviate + from their corresponding functions in NumPy are marked with "# Note" + comments. Reviewers should make note of these comments. + +""" + __all__ = [] from ._constants import e, inf, nan, pi -- cgit v1.2.1 From be1b1932f73fb5946b4867337ba2fd2d31964d11 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 20 Jan 2021 16:11:17 -0700 Subject: Add type annotations to the array api submodule function definitions Some stubs still need to be modified to properly pass mypy type checking. Also, 'device' is just left as a TypeVar() for now. --- numpy/_array_api/_creation_functions.py | 26 +++--- numpy/_array_api/_elementwise_functions.py | 114 +++++++++++++------------- numpy/_array_api/_linear_algebra_functions.py | 20 +++-- numpy/_array_api/_manipulation_functions.py | 18 ++-- numpy/_array_api/_searching_functions.py | 12 ++- numpy/_array_api/_set_functions.py | 6 +- numpy/_array_api/_sorting_functions.py | 8 +- numpy/_array_api/_statistical_functions.py | 18 ++-- numpy/_array_api/_types.py | 18 ++++ numpy/_array_api/_utility_functions.py | 8 +- 10 files changed, 151 insertions(+), 97 deletions(-) create mode 100644 numpy/_array_api/_types.py (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index b6c0c22cc..1aeaffb71 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -1,6 +1,10 @@ +from __future__ import annotations + +from ._types import Optional, Tuple, Union, array, device, dtype + import numpy as np -def arange(start, /, *, stop=None, step=1, dtype=None, device=None): +def arange(start: Union[int, float], /, *, stop: Optional[Union[int, float]] = None, step: Union[int, float] = 1, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.arange `. @@ -11,7 +15,7 @@ def arange(start, /, *, stop=None, step=1, dtype=None, device=None): raise NotImplementedError("Device support is not yet implemented") return np.arange(start, stop=stop, step=step, dtype=dtype) -def empty(shape, /, *, dtype=None, device=None): +def empty(shape: Union[int, Tuple[int, ...]], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.empty `. @@ -22,7 +26,7 @@ def empty(shape, /, *, dtype=None, device=None): raise NotImplementedError("Device support is not yet implemented") return np.empty(shape, dtype=dtype) -def empty_like(x, /, *, dtype=None, device=None): +def empty_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.empty_like `. @@ -33,7 +37,7 @@ def empty_like(x, /, *, dtype=None, device=None): raise NotImplementedError("Device support is not yet implemented") return np.empty_like(x, dtype=dtype) -def eye(N, /, *, M=None, k=0, dtype=None, device=None): +def eye(N: int, /, *, M: Optional[int] = None, k: Optional[int] = 0, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.eye `. @@ -44,7 +48,7 @@ def eye(N, /, *, M=None, k=0, dtype=None, device=None): raise NotImplementedError("Device support is not yet implemented") return np.eye(N, M=M, k=k, dtype=dtype) -def full(shape, fill_value, /, *, dtype=None, device=None): +def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.full `. @@ -55,7 +59,7 @@ def full(shape, fill_value, /, *, dtype=None, device=None): raise NotImplementedError("Device support is not yet implemented") return np.full(shape, fill_value, dtype=dtype) -def full_like(x, fill_value, /, *, dtype=None, device=None): +def full_like(x: array, fill_value: Union[int, float], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.full_like `. @@ -66,7 +70,7 @@ def full_like(x, fill_value, /, *, dtype=None, device=None): raise NotImplementedError("Device support is not yet implemented") return np.full_like(x, fill_value, dtype=dtype) -def linspace(start, stop, num, /, *, dtype=None, device=None, endpoint=True): +def linspace(start: Union[int, float], stop: Union[int, float], num: int, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None, endpoint: Optional[bool] = True) -> array: """ Array API compatible wrapper for :py:func:`np.linspace `. @@ -77,7 +81,7 @@ def linspace(start, stop, num, /, *, dtype=None, device=None, endpoint=True): raise NotImplementedError("Device support is not yet implemented") return np.linspace(start, stop, num, dtype=dtype, endpoint=endpoint) -def ones(shape, /, *, dtype=None, device=None): +def ones(shape: Union[int, Tuple[int, ...]], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.ones `. @@ -88,7 +92,7 @@ def ones(shape, /, *, dtype=None, device=None): raise NotImplementedError("Device support is not yet implemented") return np.ones(shape, dtype=dtype) -def ones_like(x, /, *, dtype=None, device=None): +def ones_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.ones_like `. @@ -99,7 +103,7 @@ def ones_like(x, /, *, dtype=None, device=None): raise NotImplementedError("Device support is not yet implemented") return np.ones_like(x, dtype=dtype) -def zeros(shape, /, *, dtype=None, device=None): +def zeros(shape: Union[int, Tuple[int, ...]], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.zeros `. @@ -110,7 +114,7 @@ def zeros(shape, /, *, dtype=None, device=None): raise NotImplementedError("Device support is not yet implemented") return np.zeros(shape, dtype=dtype) -def zeros_like(x, /, *, dtype=None, device=None): +def zeros_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.zeros_like `. diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index 7ec01b2e1..9c013d8b4 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -1,6 +1,10 @@ +from __future__ import annotations + +from ._types import array + import numpy as np -def abs(x, /): +def abs(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.abs `. @@ -8,7 +12,7 @@ def abs(x, /): """ return np.abs(x) -def acos(x, /): +def acos(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arccos `. @@ -17,7 +21,7 @@ def acos(x, /): # Note: the function name is different here return np.arccos(x) -def acosh(x, /): +def acosh(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arccosh `. @@ -26,7 +30,7 @@ def acosh(x, /): # Note: the function name is different here return np.arccosh(x) -def add(x1, x2, /): +def add(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.add `. @@ -34,7 +38,7 @@ def add(x1, x2, /): """ return np.add(x1, x2) -def asin(x, /): +def asin(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arcsin `. @@ -43,7 +47,7 @@ def asin(x, /): # Note: the function name is different here return np.arcsin(x) -def asinh(x, /): +def asinh(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arcsinh `. @@ -52,7 +56,7 @@ def asinh(x, /): # Note: the function name is different here return np.arcsinh(x) -def atan(x, /): +def atan(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arctan `. @@ -61,7 +65,7 @@ def atan(x, /): # Note: the function name is different here return np.arctan(x) -def atan2(x1, x2, /): +def atan2(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arctan2 `. @@ -70,7 +74,7 @@ def atan2(x1, x2, /): # Note: the function name is different here return np.arctan2(x1, x2) -def atanh(x, /): +def atanh(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arctanh `. @@ -79,7 +83,7 @@ def atanh(x, /): # Note: the function name is different here return np.arctanh(x) -def bitwise_and(x1, x2, /): +def bitwise_and(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.bitwise_and `. @@ -87,7 +91,7 @@ def bitwise_and(x1, x2, /): """ return np.bitwise_and(x1, x2) -def bitwise_left_shift(x1, x2, /): +def bitwise_left_shift(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.left_shift `. @@ -96,7 +100,7 @@ def bitwise_left_shift(x1, x2, /): # Note: the function name is different here return np.left_shift(x1, x2) -def bitwise_invert(x, /): +def bitwise_invert(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.invert `. @@ -105,7 +109,7 @@ def bitwise_invert(x, /): # Note: the function name is different here return np.invert(x) -def bitwise_or(x1, x2, /): +def bitwise_or(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.bitwise_or `. @@ -113,7 +117,7 @@ def bitwise_or(x1, x2, /): """ return np.bitwise_or(x1, x2) -def bitwise_right_shift(x1, x2, /): +def bitwise_right_shift(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.right_shift `. @@ -122,7 +126,7 @@ def bitwise_right_shift(x1, x2, /): # Note: the function name is different here return np.right_shift(x1, x2) -def bitwise_xor(x1, x2, /): +def bitwise_xor(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.bitwise_xor `. @@ -130,7 +134,7 @@ def bitwise_xor(x1, x2, /): """ return np.bitwise_xor(x1, x2) -def ceil(x, /): +def ceil(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.ceil `. @@ -138,7 +142,7 @@ def ceil(x, /): """ return np.ceil(x) -def cos(x, /): +def cos(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.cos `. @@ -146,7 +150,7 @@ def cos(x, /): """ return np.cos(x) -def cosh(x, /): +def cosh(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.cosh `. @@ -154,7 +158,7 @@ def cosh(x, /): """ return np.cosh(x) -def divide(x1, x2, /): +def divide(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.divide `. @@ -162,7 +166,7 @@ def divide(x1, x2, /): """ return np.divide(x1, x2) -def equal(x1, x2, /): +def equal(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.equal `. @@ -170,7 +174,7 @@ def equal(x1, x2, /): """ return np.equal(x1, x2) -def exp(x, /): +def exp(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.exp `. @@ -178,7 +182,7 @@ def exp(x, /): """ return np.exp(x) -def expm1(x, /): +def expm1(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.expm1 `. @@ -186,7 +190,7 @@ def expm1(x, /): """ return np.expm1(x) -def floor(x, /): +def floor(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.floor `. @@ -194,7 +198,7 @@ def floor(x, /): """ return np.floor(x) -def floor_divide(x1, x2, /): +def floor_divide(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.floor_divide `. @@ -202,7 +206,7 @@ def floor_divide(x1, x2, /): """ return np.floor_divide(x1, x2) -def greater(x1, x2, /): +def greater(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.greater `. @@ -210,7 +214,7 @@ def greater(x1, x2, /): """ return np.greater(x1, x2) -def greater_equal(x1, x2, /): +def greater_equal(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.greater_equal `. @@ -218,7 +222,7 @@ def greater_equal(x1, x2, /): """ return np.greater_equal(x1, x2) -def isfinite(x, /): +def isfinite(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.isfinite `. @@ -226,7 +230,7 @@ def isfinite(x, /): """ return np.isfinite(x) -def isinf(x, /): +def isinf(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.isinf `. @@ -234,7 +238,7 @@ def isinf(x, /): """ return np.isinf(x) -def isnan(x, /): +def isnan(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.isnan `. @@ -242,7 +246,7 @@ def isnan(x, /): """ return np.isnan(x) -def less(x1, x2, /): +def less(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.less `. @@ -250,7 +254,7 @@ def less(x1, x2, /): """ return np.less(x1, x2) -def less_equal(x1, x2, /): +def less_equal(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.less_equal `. @@ -258,7 +262,7 @@ def less_equal(x1, x2, /): """ return np.less_equal(x1, x2) -def log(x, /): +def log(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.log `. @@ -266,7 +270,7 @@ def log(x, /): """ return np.log(x) -def log1p(x, /): +def log1p(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.log1p `. @@ -274,7 +278,7 @@ def log1p(x, /): """ return np.log1p(x) -def log2(x, /): +def log2(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.log2 `. @@ -282,7 +286,7 @@ def log2(x, /): """ return np.log2(x) -def log10(x, /): +def log10(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.log10 `. @@ -290,7 +294,7 @@ def log10(x, /): """ return np.log10(x) -def logical_and(x1, x2, /): +def logical_and(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.logical_and `. @@ -298,7 +302,7 @@ def logical_and(x1, x2, /): """ return np.logical_and(x1, x2) -def logical_not(x, /): +def logical_not(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.logical_not `. @@ -306,7 +310,7 @@ def logical_not(x, /): """ return np.logical_not(x) -def logical_or(x1, x2, /): +def logical_or(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.logical_or `. @@ -314,7 +318,7 @@ def logical_or(x1, x2, /): """ return np.logical_or(x1, x2) -def logical_xor(x1, x2, /): +def logical_xor(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.logical_xor `. @@ -322,7 +326,7 @@ def logical_xor(x1, x2, /): """ return np.logical_xor(x1, x2) -def multiply(x1, x2, /): +def multiply(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.multiply `. @@ -330,7 +334,7 @@ def multiply(x1, x2, /): """ return np.multiply(x1, x2) -def negative(x, /): +def negative(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.negative `. @@ -338,7 +342,7 @@ def negative(x, /): """ return np.negative(x) -def not_equal(x1, x2, /): +def not_equal(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.not_equal `. @@ -346,7 +350,7 @@ def not_equal(x1, x2, /): """ return np.not_equal(x1, x2) -def positive(x, /): +def positive(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.positive `. @@ -354,7 +358,7 @@ def positive(x, /): """ return np.positive(x) -def pow(x1, x2, /): +def pow(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.power `. @@ -363,7 +367,7 @@ def pow(x1, x2, /): # Note: the function name is different here return np.power(x1, x2) -def remainder(x1, x2, /): +def remainder(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.remainder `. @@ -371,7 +375,7 @@ def remainder(x1, x2, /): """ return np.remainder(x1, x2) -def round(x, /): +def round(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.round `. @@ -379,7 +383,7 @@ def round(x, /): """ return np.round(x) -def sign(x, /): +def sign(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.sign `. @@ -387,7 +391,7 @@ def sign(x, /): """ return np.sign(x) -def sin(x, /): +def sin(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.sin `. @@ -395,7 +399,7 @@ def sin(x, /): """ return np.sin(x) -def sinh(x, /): +def sinh(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.sinh `. @@ -403,7 +407,7 @@ def sinh(x, /): """ return np.sinh(x) -def square(x, /): +def square(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.square `. @@ -411,7 +415,7 @@ def square(x, /): """ return np.square(x) -def sqrt(x, /): +def sqrt(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.sqrt `. @@ -419,7 +423,7 @@ def sqrt(x, /): """ return np.sqrt(x) -def subtract(x1, x2, /): +def subtract(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.subtract `. @@ -427,7 +431,7 @@ def subtract(x1, x2, /): """ return np.subtract(x1, x2) -def tan(x, /): +def tan(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.tan `. @@ -435,7 +439,7 @@ def tan(x, /): """ return np.tan(x) -def tanh(x, /): +def tanh(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.tanh `. @@ -443,7 +447,7 @@ def tanh(x, /): """ return np.tanh(x) -def trunc(x, /): +def trunc(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.trunc `. diff --git a/numpy/_array_api/_linear_algebra_functions.py b/numpy/_array_api/_linear_algebra_functions.py index cfb184e8d..addbaeccb 100644 --- a/numpy/_array_api/_linear_algebra_functions.py +++ b/numpy/_array_api/_linear_algebra_functions.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +from ._types import Literal, Optional, Tuple, Union, array + import numpy as np # def cholesky(): @@ -8,7 +12,7 @@ import numpy as np # """ # return np.cholesky() -def cross(x1, x2, /, *, axis=-1): +def cross(x1: array, x2: array, /, *, axis: int = -1) -> array: """ Array API compatible wrapper for :py:func:`np.cross `. @@ -16,7 +20,7 @@ def cross(x1, x2, /, *, axis=-1): """ return np.cross(x1, x2, axis=axis) -def det(x, /): +def det(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.linalg.det `. @@ -25,7 +29,7 @@ def det(x, /): # Note: this function is being imported from a nondefault namespace return np.linalg.det(x) -def diagonal(x, /, *, axis1=0, axis2=1, offset=0): +def diagonal(x: array, /, *, axis1: int = 0, axis2: int = 1, offset: int = 0) -> array: """ Array API compatible wrapper for :py:func:`np.diagonal `. @@ -65,7 +69,7 @@ def diagonal(x, /, *, axis1=0, axis2=1, offset=0): # """ # return np.einsum() -def inv(x): +def inv(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.linalg.inv `. @@ -106,7 +110,7 @@ def inv(x): # """ # return np.matrix_rank() -def norm(x, /, *, axis=None, keepdims=False, ord=None): +def norm(x: array, /, *, axis: Optional[Union[int, Tuple[int, int]]] = None, keepdims: bool = False, ord: Optional[Union[int, float, Literal[np.inf, -np.inf, 'fro', 'nuc']]] = None) -> array: """ Array API compatible wrapper for :py:func:`np.linalg.norm `. @@ -118,7 +122,7 @@ def norm(x, /, *, axis=None, keepdims=False, ord=None): # Note: this function is being imported from a nondefault namespace return np.linalg.norm(x, axis=axis, keepdims=keepdims, ord=ord) -def outer(x1, x2, /): +def outer(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.outer `. @@ -166,7 +170,7 @@ def outer(x1, x2, /): # """ # return np.svd() -def trace(x, /, *, axis1=0, axis2=1, offset=0): +def trace(x: array, /, *, axis1: int = 0, axis2: int = 1, offset: int = 0) -> array: """ Array API compatible wrapper for :py:func:`np.trace `. @@ -174,7 +178,7 @@ def trace(x, /, *, axis1=0, axis2=1, offset=0): """ return np.trace(x, axis1=axis1, axis2=axis2, offset=offset) -def transpose(x, /, *, axes=None): +def transpose(x: array, /, *, axes: Optional[Tuple[int, ...]] = None) -> array: """ Array API compatible wrapper for :py:func:`np.transpose `. diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index 834aa2f8f..f79ef1f9c 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -1,6 +1,10 @@ +from __future__ import annotations + +from ._types import Optional, Tuple, Union, array + import numpy as np -def concat(arrays, /, *, axis=0): +def concat(arrays: Tuple[array], /, *, axis: Optional[int] = 0) -> array: """ Array API compatible wrapper for :py:func:`np.concatenate `. @@ -9,7 +13,7 @@ def concat(arrays, /, *, axis=0): # Note: the function name is different here return np.concatenate(arrays, axis=axis) -def expand_dims(x, axis, /): +def expand_dims(x: array, axis: int, /) -> array: """ Array API compatible wrapper for :py:func:`np.expand_dims `. @@ -17,7 +21,7 @@ def expand_dims(x, axis, /): """ return np.expand_dims(x, axis) -def flip(x, /, *, axis=None): +def flip(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: """ Array API compatible wrapper for :py:func:`np.flip `. @@ -25,7 +29,7 @@ def flip(x, /, *, axis=None): """ return np.flip(x, axis=axis) -def reshape(x, shape, /): +def reshape(x: array, shape: Tuple[int, ...], /) -> array: """ Array API compatible wrapper for :py:func:`np.reshape `. @@ -33,7 +37,7 @@ def reshape(x, shape, /): """ return np.reshape(x, shape) -def roll(x, shift, /, *, axis=None): +def roll(x: array, shift: Union[int, Tuple[int, ...]], /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: """ Array API compatible wrapper for :py:func:`np.roll `. @@ -41,7 +45,7 @@ def roll(x, shift, /, *, axis=None): """ return np.roll(x, shift, axis=axis) -def squeeze(x, /, *, axis=None): +def squeeze(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: """ Array API compatible wrapper for :py:func:`np.squeeze `. @@ -49,7 +53,7 @@ def squeeze(x, /, *, axis=None): """ return np.squeeze(x, axis=axis) -def stack(arrays, /, *, axis=0): +def stack(arrays: Tuple[array], /, *, axis: Optional[int] = 0) -> array: """ Array API compatible wrapper for :py:func:`np.stack `. diff --git a/numpy/_array_api/_searching_functions.py b/numpy/_array_api/_searching_functions.py index 4eed66c48..3b37167af 100644 --- a/numpy/_array_api/_searching_functions.py +++ b/numpy/_array_api/_searching_functions.py @@ -1,6 +1,10 @@ +from __future__ import annotations + +from ._types import Tuple, array + import numpy as np -def argmax(x, /, *, axis=None, keepdims=False): +def argmax(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: """ Array API compatible wrapper for :py:func:`np.argmax `. @@ -8,7 +12,7 @@ def argmax(x, /, *, axis=None, keepdims=False): """ return np.argmax(x, axis=axis, keepdims=keepdims) -def argmin(x, /, *, axis=None, keepdims=False): +def argmin(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: """ Array API compatible wrapper for :py:func:`np.argmin `. @@ -16,7 +20,7 @@ def argmin(x, /, *, axis=None, keepdims=False): """ return np.argmin(x, axis=axis, keepdims=keepdims) -def nonzero(x, /): +def nonzero(x: array, /) -> Tuple[array, ...]: """ Array API compatible wrapper for :py:func:`np.nonzero `. @@ -24,7 +28,7 @@ def nonzero(x, /): """ return np.nonzero(x) -def where(condition, x1, x2, /): +def where(condition: array, x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.where `. diff --git a/numpy/_array_api/_set_functions.py b/numpy/_array_api/_set_functions.py index fd1438be5..80288c57d 100644 --- a/numpy/_array_api/_set_functions.py +++ b/numpy/_array_api/_set_functions.py @@ -1,6 +1,10 @@ +from __future__ import annotations + +from ._types import Tuple, Union, array + import numpy as np -def unique(x, /, *, return_counts=False, return_index=False, return_inverse=False, sorted=True): +def unique(x: array, /, *, return_counts: bool = False, return_index: bool = False, return_inverse: bool = False, sorted: bool = True) -> Union[array, Tuple[array, ...]]: """ Array API compatible wrapper for :py:func:`np.unique `. diff --git a/numpy/_array_api/_sorting_functions.py b/numpy/_array_api/_sorting_functions.py index 5ffe6c8f9..cddfd1598 100644 --- a/numpy/_array_api/_sorting_functions.py +++ b/numpy/_array_api/_sorting_functions.py @@ -1,6 +1,10 @@ +from __future__ import annotations + +from ._types import array + import numpy as np -def argsort(x, /, *, axis=-1, descending=False, stable=True): +def argsort(x: array, /, *, axis: int = -1, descending: bool = False, stable: bool = True) -> array: """ Array API compatible wrapper for :py:func:`np.argsort `. @@ -13,7 +17,7 @@ def argsort(x, /, *, axis=-1, descending=False, stable=True): res = np.flip(res, axis=axis) return res -def sort(x, /, *, axis=-1, descending=False, stable=True): +def sort(x: array, /, *, axis: int = -1, descending: bool = False, stable: bool = True) -> array: """ Array API compatible wrapper for :py:func:`np.sort `. diff --git a/numpy/_array_api/_statistical_functions.py b/numpy/_array_api/_statistical_functions.py index 833c47f66..020053896 100644 --- a/numpy/_array_api/_statistical_functions.py +++ b/numpy/_array_api/_statistical_functions.py @@ -1,24 +1,28 @@ +from __future__ import annotations + +from ._types import Optional, Tuple, Union, array + import numpy as np -def max(x, /, *, axis=None, keepdims=False): +def max(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: return np.max(x, axis=axis, keepdims=keepdims) -def mean(x, /, *, axis=None, keepdims=False): +def mean(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: return np.mean(x, axis=axis, keepdims=keepdims) -def min(x, /, *, axis=None, keepdims=False): +def min(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: return np.min(x, axis=axis, keepdims=keepdims) -def prod(x, /, *, axis=None, keepdims=False): +def prod(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: return np.prod(x, axis=axis, keepdims=keepdims) -def std(x, /, *, axis=None, correction=0.0, keepdims=False): +def std(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> array: # Note: the keyword argument correction is different here return np.std(x, axis=axis, ddof=correction, keepdims=keepdims) -def sum(x, /, *, axis=None, keepdims=False): +def sum(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: return np.sum(x, axis=axis, keepdims=keepdims) -def var(x, /, *, axis=None, correction=0.0, keepdims=False): +def var(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> array: # Note: the keyword argument correction is different here return np.var(x, axis=axis, ddof=correction, keepdims=keepdims) diff --git a/numpy/_array_api/_types.py b/numpy/_array_api/_types.py new file mode 100644 index 000000000..e8867a29b --- /dev/null +++ b/numpy/_array_api/_types.py @@ -0,0 +1,18 @@ +""" +This file defines the types for type annotations. + +These names aren't part of the module namespace, but they are used in the +annotations in the function signatures. The functions in the module are only +valid for inputs that match the given type annotations. +""" + +__all__ = ['Literal', 'Optional', 'Tuple', 'Union', 'array', 'device', 'dtype'] + +from typing import Literal, Optional, Tuple, Union, TypeVar + +import numpy as np + +array = np.ndarray +device = TypeVar('device') +dtype = Literal[np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, + np.uint32, np.uint64, np.float32, np.float64] diff --git a/numpy/_array_api/_utility_functions.py b/numpy/_array_api/_utility_functions.py index 19743d15c..69e17e0e5 100644 --- a/numpy/_array_api/_utility_functions.py +++ b/numpy/_array_api/_utility_functions.py @@ -1,6 +1,10 @@ +from __future__ import annotations + +from ._types import Optional, Tuple, Union, array + import numpy as np -def all(x, /, *, axis=None, keepdims=False): +def all(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: """ Array API compatible wrapper for :py:func:`np.all `. @@ -8,7 +12,7 @@ def all(x, /, *, axis=None, keepdims=False): """ return np.all(x, axis=axis, keepdims=keepdims) -def any(x, /, *, axis=None, keepdims=False): +def any(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: """ Array API compatible wrapper for :py:func:`np.any `. -- cgit v1.2.1 From 5df8ec9673a73e71554c8f53cc6edb60533c5d17 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 20 Jan 2021 17:56:37 -0700 Subject: Fix some incorrect type annotations in the array API submodule (see https://github.com/data-apis/array-api/pull/116) --- numpy/_array_api/_creation_functions.py | 2 +- numpy/_array_api/_manipulation_functions.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 1aeaffb71..df64ed1d6 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -70,7 +70,7 @@ def full_like(x: array, fill_value: Union[int, float], /, *, dtype: Optional[dty raise NotImplementedError("Device support is not yet implemented") return np.full_like(x, fill_value, dtype=dtype) -def linspace(start: Union[int, float], stop: Union[int, float], num: int, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None, endpoint: Optional[bool] = True) -> array: +def linspace(start: Union[int, float], stop: Union[int, float], num: int, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None, endpoint: bool = True) -> array: """ Array API compatible wrapper for :py:func:`np.linspace `. diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index f79ef1f9c..a4247f2b5 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -53,7 +53,7 @@ def squeeze(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) """ return np.squeeze(x, axis=axis) -def stack(arrays: Tuple[array], /, *, axis: Optional[int] = 0) -> array: +def stack(arrays: Tuple[array], /, *, axis: int = 0) -> array: """ Array API compatible wrapper for :py:func:`np.stack `. -- cgit v1.2.1 From ad19f7f7dfcfe33fd4591f1be3b4d9d30887899a Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 20 Jan 2021 17:57:10 -0700 Subject: Use np.asarray in the array API submodule for any function that can return a scalar This is needed to pass mypy type checks for the given type annotations. --- numpy/_array_api/_linear_algebra_functions.py | 2 +- numpy/_array_api/_searching_functions.py | 6 ++++-- numpy/_array_api/_statistical_functions.py | 10 +++++----- numpy/_array_api/_utility_functions.py | 4 ++-- 4 files changed, 12 insertions(+), 10 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_linear_algebra_functions.py b/numpy/_array_api/_linear_algebra_functions.py index addbaeccb..ec67f9c0b 100644 --- a/numpy/_array_api/_linear_algebra_functions.py +++ b/numpy/_array_api/_linear_algebra_functions.py @@ -176,7 +176,7 @@ def trace(x: array, /, *, axis1: int = 0, axis2: int = 1, offset: int = 0) -> ar See its docstring for more information. """ - return np.trace(x, axis1=axis1, axis2=axis2, offset=offset) + return np.asarray(np.trace(x, axis1=axis1, axis2=axis2, offset=offset)) def transpose(x: array, /, *, axes: Optional[Tuple[int, ...]] = None) -> array: """ diff --git a/numpy/_array_api/_searching_functions.py b/numpy/_array_api/_searching_functions.py index 3b37167af..d5128cca9 100644 --- a/numpy/_array_api/_searching_functions.py +++ b/numpy/_array_api/_searching_functions.py @@ -10,7 +10,8 @@ def argmax(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: See its docstring for more information. """ - return np.argmax(x, axis=axis, keepdims=keepdims) + # Note: this currently fails as np.argmax does not implement keepdims + return np.asarray(np.argmax(x, axis=axis, keepdims=keepdims)) def argmin(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: """ @@ -18,7 +19,8 @@ def argmin(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: See its docstring for more information. """ - return np.argmin(x, axis=axis, keepdims=keepdims) + # Note: this currently fails as np.argmin does not implement keepdims + return np.asarray(np.argmin(x, axis=axis, keepdims=keepdims)) def nonzero(x: array, /) -> Tuple[array, ...]: """ diff --git a/numpy/_array_api/_statistical_functions.py b/numpy/_array_api/_statistical_functions.py index 020053896..e62410d01 100644 --- a/numpy/_array_api/_statistical_functions.py +++ b/numpy/_array_api/_statistical_functions.py @@ -8,21 +8,21 @@ def max(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keep return np.max(x, axis=axis, keepdims=keepdims) def mean(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.mean(x, axis=axis, keepdims=keepdims) + return np.asarray(np.mean(x, axis=axis, keepdims=keepdims)) def min(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: return np.min(x, axis=axis, keepdims=keepdims) def prod(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.prod(x, axis=axis, keepdims=keepdims) + return np.asarray(np.prod(x, axis=axis, keepdims=keepdims)) def std(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> array: # Note: the keyword argument correction is different here - return np.std(x, axis=axis, ddof=correction, keepdims=keepdims) + return np.asarray(np.std(x, axis=axis, ddof=correction, keepdims=keepdims)) def sum(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.sum(x, axis=axis, keepdims=keepdims) + return np.asarray(np.sum(x, axis=axis, keepdims=keepdims)) def var(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> array: # Note: the keyword argument correction is different here - return np.var(x, axis=axis, ddof=correction, keepdims=keepdims) + return np.asarray(np.var(x, axis=axis, ddof=correction, keepdims=keepdims)) diff --git a/numpy/_array_api/_utility_functions.py b/numpy/_array_api/_utility_functions.py index 69e17e0e5..51a04dc8b 100644 --- a/numpy/_array_api/_utility_functions.py +++ b/numpy/_array_api/_utility_functions.py @@ -10,7 +10,7 @@ def all(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keep See its docstring for more information. """ - return np.all(x, axis=axis, keepdims=keepdims) + return np.asarray(np.all(x, axis=axis, keepdims=keepdims)) def any(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: """ @@ -18,4 +18,4 @@ def any(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keep See its docstring for more information. """ - return np.any(x, axis=axis, keepdims=keepdims) + return np.asarray(np.any(x, axis=axis, keepdims=keepdims)) -- cgit v1.2.1 From 1efd55efa8cac9afd12d299dcf8912a7a7ac8a68 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 20 Jan 2021 18:25:20 -0700 Subject: Use _implementation on all functions that have it in the array API submodule That way they only work on actual ndarray inputs, not array-like, which is more inline with the spec. --- numpy/_array_api/_creation_functions.py | 8 ++++---- numpy/_array_api/_elementwise_functions.py | 2 +- numpy/_array_api/_linear_algebra_functions.py | 10 +++++----- numpy/_array_api/_manipulation_functions.py | 12 ++++++------ numpy/_array_api/_searching_functions.py | 8 ++++---- numpy/_array_api/_set_functions.py | 2 +- numpy/_array_api/_sorting_functions.py | 4 ++-- numpy/_array_api/_statistical_functions.py | 14 +++++++------- numpy/_array_api/_utility_functions.py | 4 ++-- 9 files changed, 32 insertions(+), 32 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index df64ed1d6..68326f291 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -35,7 +35,7 @@ def empty_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[d if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return np.empty_like(x, dtype=dtype) + return np.empty_like._implementation(x, dtype=dtype) def eye(N: int, /, *, M: Optional[int] = None, k: Optional[int] = 0, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ @@ -68,7 +68,7 @@ def full_like(x: array, fill_value: Union[int, float], /, *, dtype: Optional[dty if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return np.full_like(x, fill_value, dtype=dtype) + return np.full_like._implementation(x, fill_value, dtype=dtype) def linspace(start: Union[int, float], stop: Union[int, float], num: int, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None, endpoint: bool = True) -> array: """ @@ -101,7 +101,7 @@ def ones_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[de if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return np.ones_like(x, dtype=dtype) + return np.ones_like._implementation(x, dtype=dtype) def zeros(shape: Union[int, Tuple[int, ...]], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ @@ -123,4 +123,4 @@ def zeros_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[d if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return np.zeros_like(x, dtype=dtype) + return np.zeros_like._implementation(x, dtype=dtype) diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index 9c013d8b4..9de4261ac 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -381,7 +381,7 @@ def round(x: array, /) -> array: See its docstring for more information. """ - return np.round(x) + return np.round._implementation(x) def sign(x: array, /) -> array: """ diff --git a/numpy/_array_api/_linear_algebra_functions.py b/numpy/_array_api/_linear_algebra_functions.py index ec67f9c0b..e23800e0f 100644 --- a/numpy/_array_api/_linear_algebra_functions.py +++ b/numpy/_array_api/_linear_algebra_functions.py @@ -18,7 +18,7 @@ def cross(x1: array, x2: array, /, *, axis: int = -1) -> array: See its docstring for more information. """ - return np.cross(x1, x2, axis=axis) + return np.cross._implementation(x1, x2, axis=axis) def det(x: array, /) -> array: """ @@ -35,7 +35,7 @@ def diagonal(x: array, /, *, axis1: int = 0, axis2: int = 1, offset: int = 0) -> See its docstring for more information. """ - return np.diagonal(x, axis1=axis1, axis2=axis2, offset=offset) + return np.diagonal._implementation(x, axis1=axis1, axis2=axis2, offset=offset) # def dot(): # """ @@ -128,7 +128,7 @@ def outer(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.outer(x1, x2) + return np.outer._implementation(x1, x2) # def pinv(): # """ @@ -176,7 +176,7 @@ def trace(x: array, /, *, axis1: int = 0, axis2: int = 1, offset: int = 0) -> ar See its docstring for more information. """ - return np.asarray(np.trace(x, axis1=axis1, axis2=axis2, offset=offset)) + return np.asarray(np.trace._implementation(x, axis1=axis1, axis2=axis2, offset=offset)) def transpose(x: array, /, *, axes: Optional[Tuple[int, ...]] = None) -> array: """ @@ -184,4 +184,4 @@ def transpose(x: array, /, *, axes: Optional[Tuple[int, ...]] = None) -> array: See its docstring for more information. """ - return np.transpose(x, axes=axes) + return np.transpose._implementation(x, axes=axes) diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index a4247f2b5..e312b18c5 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -19,7 +19,7 @@ def expand_dims(x: array, axis: int, /) -> array: See its docstring for more information. """ - return np.expand_dims(x, axis) + return np.expand_dims._implementation(x, axis) def flip(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: """ @@ -27,7 +27,7 @@ def flip(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> See its docstring for more information. """ - return np.flip(x, axis=axis) + return np.flip._implementation(x, axis=axis) def reshape(x: array, shape: Tuple[int, ...], /) -> array: """ @@ -35,7 +35,7 @@ def reshape(x: array, shape: Tuple[int, ...], /) -> array: See its docstring for more information. """ - return np.reshape(x, shape) + return np.reshape._implementation(x, shape) def roll(x: array, shift: Union[int, Tuple[int, ...]], /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: """ @@ -43,7 +43,7 @@ def roll(x: array, shift: Union[int, Tuple[int, ...]], /, *, axis: Optional[Unio See its docstring for more information. """ - return np.roll(x, shift, axis=axis) + return np.roll._implementation(x, shift, axis=axis) def squeeze(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: """ @@ -51,7 +51,7 @@ def squeeze(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) See its docstring for more information. """ - return np.squeeze(x, axis=axis) + return np.squeeze._implementation(x, axis=axis) def stack(arrays: Tuple[array], /, *, axis: int = 0) -> array: """ @@ -59,4 +59,4 @@ def stack(arrays: Tuple[array], /, *, axis: int = 0) -> array: See its docstring for more information. """ - return np.stack(arrays, axis=axis) + return np.stack._implementation(arrays, axis=axis) diff --git a/numpy/_array_api/_searching_functions.py b/numpy/_array_api/_searching_functions.py index d5128cca9..77e4710e5 100644 --- a/numpy/_array_api/_searching_functions.py +++ b/numpy/_array_api/_searching_functions.py @@ -11,7 +11,7 @@ def argmax(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: See its docstring for more information. """ # Note: this currently fails as np.argmax does not implement keepdims - return np.asarray(np.argmax(x, axis=axis, keepdims=keepdims)) + return np.asarray(np.argmax._implementation(x, axis=axis, keepdims=keepdims)) def argmin(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: """ @@ -20,7 +20,7 @@ def argmin(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: See its docstring for more information. """ # Note: this currently fails as np.argmin does not implement keepdims - return np.asarray(np.argmin(x, axis=axis, keepdims=keepdims)) + return np.asarray(np.argmin._implementation(x, axis=axis, keepdims=keepdims)) def nonzero(x: array, /) -> Tuple[array, ...]: """ @@ -28,7 +28,7 @@ def nonzero(x: array, /) -> Tuple[array, ...]: See its docstring for more information. """ - return np.nonzero(x) + return np.nonzero._implementation(x) def where(condition: array, x1: array, x2: array, /) -> array: """ @@ -36,4 +36,4 @@ def where(condition: array, x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.where(condition, x1, x2) + return np.where._implementation(condition, x1, x2) diff --git a/numpy/_array_api/_set_functions.py b/numpy/_array_api/_set_functions.py index 80288c57d..0a75d727e 100644 --- a/numpy/_array_api/_set_functions.py +++ b/numpy/_array_api/_set_functions.py @@ -10,4 +10,4 @@ def unique(x: array, /, *, return_counts: bool = False, return_index: bool = Fal See its docstring for more information. """ - return np.unique(x, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse, sorted=sorted) + return np.unique._implementation(x, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse, sorted=sorted) diff --git a/numpy/_array_api/_sorting_functions.py b/numpy/_array_api/_sorting_functions.py index cddfd1598..17316b552 100644 --- a/numpy/_array_api/_sorting_functions.py +++ b/numpy/_array_api/_sorting_functions.py @@ -12,7 +12,7 @@ def argsort(x: array, /, *, axis: int = -1, descending: bool = False, stable: bo """ # Note: this keyword argument is different, and the default is different. kind = 'stable' if stable else 'quicksort' - res = np.argsort(x, axis=axis, kind=kind) + res = np.argsort._implementation(x, axis=axis, kind=kind) if descending: res = np.flip(res, axis=axis) return res @@ -25,7 +25,7 @@ def sort(x: array, /, *, axis: int = -1, descending: bool = False, stable: bool """ # Note: this keyword argument is different, and the default is different. kind = 'stable' if stable else 'quicksort' - res = np.sort(x, axis=axis, kind=kind) + res = np.sort._implementation(x, axis=axis, kind=kind) if descending: res = np.flip(res, axis=axis) return res diff --git a/numpy/_array_api/_statistical_functions.py b/numpy/_array_api/_statistical_functions.py index e62410d01..79bc125dc 100644 --- a/numpy/_array_api/_statistical_functions.py +++ b/numpy/_array_api/_statistical_functions.py @@ -5,24 +5,24 @@ from ._types import Optional, Tuple, Union, array import numpy as np def max(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.max(x, axis=axis, keepdims=keepdims) + return np.max._implementation(x, axis=axis, keepdims=keepdims) def mean(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.asarray(np.mean(x, axis=axis, keepdims=keepdims)) + return np.asarray(np.mean._implementation(x, axis=axis, keepdims=keepdims)) def min(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.min(x, axis=axis, keepdims=keepdims) + return np.min._implementation(x, axis=axis, keepdims=keepdims) def prod(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.asarray(np.prod(x, axis=axis, keepdims=keepdims)) + return np.asarray(np.prod._implementation(x, axis=axis, keepdims=keepdims)) def std(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> array: # Note: the keyword argument correction is different here - return np.asarray(np.std(x, axis=axis, ddof=correction, keepdims=keepdims)) + return np.asarray(np.std._implementation(x, axis=axis, ddof=correction, keepdims=keepdims)) def sum(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.asarray(np.sum(x, axis=axis, keepdims=keepdims)) + return np.asarray(np.sum._implementation(x, axis=axis, keepdims=keepdims)) def var(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> array: # Note: the keyword argument correction is different here - return np.asarray(np.var(x, axis=axis, ddof=correction, keepdims=keepdims)) + return np.asarray(np.var._implementation(x, axis=axis, ddof=correction, keepdims=keepdims)) diff --git a/numpy/_array_api/_utility_functions.py b/numpy/_array_api/_utility_functions.py index 51a04dc8b..7e1d6ec6e 100644 --- a/numpy/_array_api/_utility_functions.py +++ b/numpy/_array_api/_utility_functions.py @@ -10,7 +10,7 @@ def all(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keep See its docstring for more information. """ - return np.asarray(np.all(x, axis=axis, keepdims=keepdims)) + return np.asarray(np.all._implementation(x, axis=axis, keepdims=keepdims)) def any(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: """ @@ -18,4 +18,4 @@ def any(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keep See its docstring for more information. """ - return np.asarray(np.any(x, axis=axis, keepdims=keepdims)) + return np.asarray(np.any._implementation(x, axis=axis, keepdims=keepdims)) -- cgit v1.2.1 From affc5f0c2581a8d17825bcb7d9610e4f58560b5d Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 20 Jan 2021 18:30:45 -0700 Subject: Add some more notes to the array API module docstring --- numpy/_array_api/__init__.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/__init__.py b/numpy/_array_api/__init__.py index ad66bc565..6afddb26a 100644 --- a/numpy/_array_api/__init__.py +++ b/numpy/_array_api/__init__.py @@ -58,6 +58,20 @@ A few notes about the current state of this submodule: guaranteed to give a comprehensive coverage of the spec. Therefore, those reviewing this submodule should refer to the standard documents themselves. +- All functions include type annotations, corresponding to those given in the + spec (see _types.py for definitions of the types 'array', 'device', and + 'dtype'). These do not currently fully pass mypy due to some limitations in + mypy. + +- The array object is not modified at all. That means that functions return + np.ndarray, which has methods and attributes that aren't part of the spec. + Modifying/subclassing ndarray for the purposes of the array API namespace + was considered too complex for this initial implementation. + +- All functions that would otherwise accept array-like input have been wrapped + to only accept ndarray (with the exception of methods on the array object, + which are not modified). + - All places where the implementations in this submodule are known to deviate from their corresponding functions in NumPy are marked with "# Note" comments. Reviewers should make note of these comments. -- cgit v1.2.1 From f2ac67e236c1dbc60f66c4a4b041403a197e52f2 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 23 Feb 2021 16:17:07 -0700 Subject: Update array_api namespace with latest changes from the spec --- numpy/_array_api/_creation_functions.py | 18 +++++++++++++++++- numpy/_array_api/_data_type_functions.py | 30 ++++++++++++++++++++++++++++++ numpy/_array_api/_elementwise_functions.py | 8 ++++++++ numpy/_array_api/_set_functions.py | 4 ++-- numpy/_array_api/_types.py | 6 +++++- 5 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 numpy/_array_api/_data_type_functions.py (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 68326f291..d015734ff 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -1,9 +1,21 @@ from __future__ import annotations -from ._types import Optional, Tuple, Union, array, device, dtype +from ._types import (Optional, SupportsDLPack, SupportsBufferProtocol, Tuple, + Union, array, device, dtype) import numpy as np +def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, SupportsBufferProtocol], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None, copy: Optional[bool] = None) -> array: + """ + Array API compatible wrapper for :py:func:`np.asarray `. + + See its docstring for more information. + """ + if device is not None: + # Note: Device support is not yet implemented on ndarray + raise NotImplementedError("Device support is not yet implemented") + return np.asarray(obj, dtype=dtype, copy=copy) + def arange(start: Union[int, float], /, *, stop: Optional[Union[int, float]] = None, step: Union[int, float] = 1, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.arange `. @@ -48,6 +60,10 @@ def eye(N: int, /, *, M: Optional[int] = None, k: Optional[int] = 0, dtype: Opti raise NotImplementedError("Device support is not yet implemented") return np.eye(N, M=M, k=k, dtype=dtype) +def from_dlpack(x: object, /) -> array: + # Note: dlpack support is not yet implemented on ndarray + raise NotImplementedError("DLPack support is not yet implemented") + def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.full `. diff --git a/numpy/_array_api/_data_type_functions.py b/numpy/_array_api/_data_type_functions.py new file mode 100644 index 000000000..18f741ebd --- /dev/null +++ b/numpy/_array_api/_data_type_functions.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from ._types import Union, array, dtype +from collections.abc import Sequence + +import numpy as np + +def finfo(type: Union[dtype, array], /) -> finfo: + """ + Array API compatible wrapper for :py:func:`np.finfo `. + + See its docstring for more information. + """ + return np.finfo(type) + +def iinfo(type: Union[dtype, array], /) -> iinfo: + """ + Array API compatible wrapper for :py:func:`np.iinfo `. + + See its docstring for more information. + """ + return np.iinfo(type) + +def result_type(*arrays_and_dtypes: Sequence[Union[array, dtype]]) -> dtype: + """ + Array API compatible wrapper for :py:func:`np.result_type `. + + See its docstring for more information. + """ + return np.result_type(*arrays_and_dtypes) diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index 9de4261ac..a117c3370 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -294,6 +294,14 @@ def log10(x: array, /) -> array: """ return np.log10(x) +def logaddexp(x1: array, x2: array) -> array: + """ + Array API compatible wrapper for :py:func:`np.logaddexp `. + + See its docstring for more information. + """ + return np.logaddexp(x1, x2) + def logical_and(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.logical_and `. diff --git a/numpy/_array_api/_set_functions.py b/numpy/_array_api/_set_functions.py index 0a75d727e..4dfc215a7 100644 --- a/numpy/_array_api/_set_functions.py +++ b/numpy/_array_api/_set_functions.py @@ -4,10 +4,10 @@ from ._types import Tuple, Union, array import numpy as np -def unique(x: array, /, *, return_counts: bool = False, return_index: bool = False, return_inverse: bool = False, sorted: bool = True) -> Union[array, Tuple[array, ...]]: +def unique(x: array, /, *, return_counts: bool = False, return_index: bool = False, return_inverse: bool = False) -> Union[array, Tuple[array, ...]]: """ Array API compatible wrapper for :py:func:`np.unique `. See its docstring for more information. """ - return np.unique._implementation(x, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse, sorted=sorted) + return np.unique._implementation(x, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse) diff --git a/numpy/_array_api/_types.py b/numpy/_array_api/_types.py index e8867a29b..3800b7156 100644 --- a/numpy/_array_api/_types.py +++ b/numpy/_array_api/_types.py @@ -6,7 +6,8 @@ annotations in the function signatures. The functions in the module are only valid for inputs that match the given type annotations. """ -__all__ = ['Literal', 'Optional', 'Tuple', 'Union', 'array', 'device', 'dtype'] +__all__ = ['Literal', 'Optional', 'Tuple', 'Union', 'array', 'device', + 'dtype', 'SupportsDLPack', 'SupportsBufferProtocol', 'PyCapsule'] from typing import Literal, Optional, Tuple, Union, TypeVar @@ -16,3 +17,6 @@ array = np.ndarray device = TypeVar('device') dtype = Literal[np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64, np.float32, np.float64] +SupportsDLPack = TypeVar('SupportsDLPack') +SupportsBufferProtocol = TypeVar('SupportsBufferProtocol') +PyCapsule = TypeVar('PyCapsule') -- cgit v1.2.1 From d9438ad1a8f4f44809fce6c19096436159f5fb03 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 23 Feb 2021 18:03:29 -0700 Subject: Start implementing wrapper object for the array API So far, it just is a wrapper with all the methods defined in the spec, which all pass through. The next step is to make it so that the methods that behave differently actually work as the spec describes. We also still need to modify all the array_api functions to return this wrapper object instead of np.ndarray. --- numpy/_array_api/_array_object.py | 479 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 479 insertions(+) create mode 100644 numpy/_array_api/_array_object.py (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py new file mode 100644 index 000000000..5ce650ae9 --- /dev/null +++ b/numpy/_array_api/_array_object.py @@ -0,0 +1,479 @@ +""" +Wrapper class around the ndarray object for the array API standard. + +The array API standard defines some behaviors differently than ndarray, in +particular, type promotion rules are different (the standard has no +value-based casting). The standard also specifies a more limited subset of +array methods and functionalities than are implemented on ndarray. Since the +goal of the array_api namespace is to be a minimal implementation of the array +API standard, we need to define a separate wrapper class for the array_api +namespace. + +The standard compliant class is only a wrapper class. It is *not* a subclass +of ndarray. +""" + +from __future__ import annotations + +from enum import IntEnum +from ._types import Optional, PyCapsule, Tuple, Union, array + +class ndarray: + # Use a custom constructor instead of __init__, as manually initializing + # this class is not supported API. + @classmethod + def _new(cls, x, /): + """ + This is a private method for initializing the array API ndarray + object. + + Functions outside of the array_api submodule should not use this + method. Use one of the creation functions instead, such as + ``asarray``. + + """ + obj = super().__new__(cls) + obj._array = x + return obj + + # Prevent ndarray() from working + def __new__(cls, *args, **kwargs): + raise TypeError("The array_api ndarray object should not be instantiated directly. Use an array creation function, such as asarray(), instead.") + + def __abs__(x: array, /) -> array: + """ + Performs the operation __abs__. + """ + res = x._array.__abs__(x) + return x.__class__._new(res) + + def __add__(x1: array, x2: array, /) -> array: + """ + Performs the operation __add__. + """ + res = x1._array.__add__(x1, x2) + return x1.__class__._new(res) + + def __and__(x1: array, x2: array, /) -> array: + """ + Performs the operation __and__. + """ + res = x1._array.__and__(x1, x2) + return x1.__class__._new(res) + + def __bool__(x: array, /) -> bool: + """ + Performs the operation __bool__. + """ + res = x._array.__bool__(x) + return x.__class__._new(res) + + def __dlpack__(x: array, /, *, stream: Optional[int] = None) -> PyCapsule: + """ + Performs the operation __dlpack__. + """ + res = x._array.__dlpack__(x, stream=None) + return x.__class__._new(res) + + def __dlpack_device__(x: array, /) -> Tuple[IntEnum, int]: + """ + Performs the operation __dlpack_device__. + """ + res = x._array.__dlpack_device__(x) + return x.__class__._new(res) + + def __eq__(x1: array, x2: array, /) -> array: + """ + Performs the operation __eq__. + """ + res = x1._array.__eq__(x1, x2) + return x1.__class__._new(res) + + def __float__(x: array, /) -> float: + """ + Performs the operation __float__. + """ + res = x._array.__float__(x) + return x.__class__._new(res) + + def __floordiv__(x1: array, x2: array, /) -> array: + """ + Performs the operation __floordiv__. + """ + res = x1._array.__floordiv__(x1, x2) + return x1.__class__._new(res) + + def __ge__(x1: array, x2: array, /) -> array: + """ + Performs the operation __ge__. + """ + res = x1._array.__ge__(x1, x2) + return x1.__class__._new(res) + + def __getitem__(x: array, key: Union[int, slice, Tuple[Union[int, slice], ...], array], /) -> array: + """ + Performs the operation __getitem__. + """ + res = x._array.__getitem__(x, key) + return x.__class__._new(res) + + def __gt__(x1: array, x2: array, /) -> array: + """ + Performs the operation __gt__. + """ + res = x1._array.__gt__(x1, x2) + return x1.__class__._new(res) + + def __int__(x: array, /) -> int: + """ + Performs the in-place operation __int__. + """ + x._array.__int__(x) + + def __invert__(x: array, /) -> array: + """ + Performs the in-place operation __invert__. + """ + x._array.__invert__(x) + + def __le__(x1: array, x2: array, /) -> array: + """ + Performs the operation __le__. + """ + res = x1._array.__le__(x1, x2) + return x1.__class__._new(res) + + def __len__(x, /): + """ + Performs the operation __len__. + """ + res = x._array.__len__(x) + return x.__class__._new(res) + + def __lshift__(x1: array, x2: array, /) -> array: + """ + Performs the operation __lshift__. + """ + res = x1._array.__lshift__(x1, x2) + return x1.__class__._new(res) + + def __lt__(x1: array, x2: array, /) -> array: + """ + Performs the operation __lt__. + """ + res = x1._array.__lt__(x1, x2) + return x1.__class__._new(res) + + def __matmul__(x1: array, x2: array, /) -> array: + """ + Performs the operation __matmul__. + """ + res = x1._array.__matmul__(x1, x2) + return x1.__class__._new(res) + + def __mod__(x1: array, x2: array, /) -> array: + """ + Performs the operation __mod__. + """ + res = x1._array.__mod__(x1, x2) + return x1.__class__._new(res) + + def __mul__(x1: array, x2: array, /) -> array: + """ + Performs the operation __mul__. + """ + res = x1._array.__mul__(x1, x2) + return x1.__class__._new(res) + + def __ne__(x1: array, x2: array, /) -> array: + """ + Performs the operation __ne__. + """ + res = x1._array.__ne__(x1, x2) + return x1.__class__._new(res) + + def __neg__(x: array, /) -> array: + """ + Performs the operation __neg__. + """ + res = x._array.__neg__(x) + return x.__class__._new(res) + + def __or__(x1: array, x2: array, /) -> array: + """ + Performs the operation __or__. + """ + res = x1._array.__or__(x1, x2) + return x1.__class__._new(res) + + def __pos__(x: array, /) -> array: + """ + Performs the operation __pos__. + """ + res = x._array.__pos__(x) + return x.__class__._new(res) + + def __pow__(x1: array, x2: array, /) -> array: + """ + Performs the operation __pow__. + """ + res = x1._array.__pow__(x1, x2) + return x1.__class__._new(res) + + def __rshift__(x1: array, x2: array, /) -> array: + """ + Performs the operation __rshift__. + """ + res = x1._array.__rshift__(x1, x2) + return x1.__class__._new(res) + + def __setitem__(x, key, value, /): + """ + Performs the operation __setitem__. + """ + res = x._array.__setitem__(x, key, value) + return x.__class__._new(res) + + def __sub__(x1: array, x2: array, /) -> array: + """ + Performs the operation __sub__. + """ + res = x1._array.__sub__(x1, x2) + return x1.__class__._new(res) + + def __truediv__(x1: array, x2: array, /) -> array: + """ + Performs the operation __truediv__. + """ + res = x1._array.__truediv__(x1, x2) + return x1.__class__._new(res) + + def __xor__(x1: array, x2: array, /) -> array: + """ + Performs the operation __xor__. + """ + res = x1._array.__xor__(x1, x2) + return x1.__class__._new(res) + + def __iadd__(x1: array, x2: array, /) -> array: + """ + Performs the in-place operation __iadd__. + """ + x1._array.__iadd__(x1, x2) + + def __radd__(x1: array, x2: array, /) -> array: + """ + Performs the operation __radd__. + """ + res = x1._array.__radd__(x1, x2) + return x1.__class__._new(res) + + def __iand__(x1: array, x2: array, /) -> array: + """ + Performs the in-place operation __iand__. + """ + x1._array.__iand__(x1, x2) + + def __rand__(x1: array, x2: array, /) -> array: + """ + Performs the operation __rand__. + """ + res = x1._array.__rand__(x1, x2) + return x1.__class__._new(res) + + def __ifloordiv__(x1: array, x2: array, /) -> array: + """ + Performs the in-place operation __ifloordiv__. + """ + x1._array.__ifloordiv__(x1, x2) + + def __rfloordiv__(x1: array, x2: array, /) -> array: + """ + Performs the operation __rfloordiv__. + """ + res = x1._array.__rfloordiv__(x1, x2) + return x1.__class__._new(res) + + def __ilshift__(x1: array, x2: array, /) -> array: + """ + Performs the in-place operation __ilshift__. + """ + x1._array.__ilshift__(x1, x2) + + def __rlshift__(x1: array, x2: array, /) -> array: + """ + Performs the operation __rlshift__. + """ + res = x1._array.__rlshift__(x1, x2) + return x1.__class__._new(res) + + def __imatmul__(x1: array, x2: array, /) -> array: + """ + Performs the in-place operation __imatmul__. + """ + x1._array.__imatmul__(x1, x2) + + def __rmatmul__(x1: array, x2: array, /) -> array: + """ + Performs the operation __rmatmul__. + """ + res = x1._array.__rmatmul__(x1, x2) + return x1.__class__._new(res) + + def __imod__(x1: array, x2: array, /) -> array: + """ + Performs the in-place operation __imod__. + """ + x1._array.__imod__(x1, x2) + + def __rmod__(x1: array, x2: array, /) -> array: + """ + Performs the operation __rmod__. + """ + res = x1._array.__rmod__(x1, x2) + return x1.__class__._new(res) + + def __imul__(x1: array, x2: array, /) -> array: + """ + Performs the in-place operation __imul__. + """ + x1._array.__imul__(x1, x2) + + def __rmul__(x1: array, x2: array, /) -> array: + """ + Performs the operation __rmul__. + """ + res = x1._array.__rmul__(x1, x2) + return x1.__class__._new(res) + + def __ior__(x1: array, x2: array, /) -> array: + """ + Performs the in-place operation __ior__. + """ + x1._array.__ior__(x1, x2) + + def __ror__(x1: array, x2: array, /) -> array: + """ + Performs the operation __ror__. + """ + res = x1._array.__ror__(x1, x2) + return x1.__class__._new(res) + + def __ipow__(x1: array, x2: array, /) -> array: + """ + Performs the in-place operation __ipow__. + """ + x1._array.__ipow__(x1, x2) + + def __rpow__(x1: array, x2: array, /) -> array: + """ + Performs the operation __rpow__. + """ + res = x1._array.__rpow__(x1, x2) + return x1.__class__._new(res) + + def __irshift__(x1: array, x2: array, /) -> array: + """ + Performs the in-place operation __irshift__. + """ + x1._array.__irshift__(x1, x2) + + def __rrshift__(x1: array, x2: array, /) -> array: + """ + Performs the operation __rrshift__. + """ + res = x1._array.__rrshift__(x1, x2) + return x1.__class__._new(res) + + def __isub__(x1: array, x2: array, /) -> array: + """ + Performs the in-place operation __isub__. + """ + x1._array.__isub__(x1, x2) + + def __rsub__(x1: array, x2: array, /) -> array: + """ + Performs the operation __rsub__. + """ + res = x1._array.__rsub__(x1, x2) + return x1.__class__._new(res) + + def __itruediv__(x1: array, x2: array, /) -> array: + """ + Performs the in-place operation __itruediv__. + """ + x1._array.__itruediv__(x1, x2) + + def __rtruediv__(x1: array, x2: array, /) -> array: + """ + Performs the operation __rtruediv__. + """ + res = x1._array.__rtruediv__(x1, x2) + return x1.__class__._new(res) + + def __ixor__(x1: array, x2: array, /) -> array: + """ + Performs the in-place operation __ixor__. + """ + x1._array.__ixor__(x1, x2) + + def __rxor__(x1: array, x2: array, /) -> array: + """ + Performs the operation __rxor__. + """ + res = x1._array.__rxor__(x1, x2) + return x1.__class__._new(res) + + @property + def dtype(self): + """ + Array API compatible wrapper for :py:meth:`np.ndaray.dtype `. + + See its docstring for more information. + """ + return self._array.dtype + + @property + def device(self): + """ + Array API compatible wrapper for :py:meth:`np.ndaray.device `. + + See its docstring for more information. + """ + return self._array.device + + @property + def ndim(self): + """ + Array API compatible wrapper for :py:meth:`np.ndaray.ndim `. + + See its docstring for more information. + """ + return self._array.ndim + + @property + def shape(self): + """ + Array API compatible wrapper for :py:meth:`np.ndaray.shape `. + + See its docstring for more information. + """ + return self._array.shape + + @property + def size(self): + """ + Array API compatible wrapper for :py:meth:`np.ndaray.size `. + + See its docstring for more information. + """ + return self._array.size + + @property + def T(self): + """ + Array API compatible wrapper for :py:meth:`np.ndaray.T `. + + See its docstring for more information. + """ + return self._array.T -- cgit v1.2.1 From e233a0ac8ac3f740de3537c2cc874446babb21ce Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 23 Feb 2021 18:11:54 -0700 Subject: Add some missing names in the array_api namespace __all__ --- numpy/_array_api/__init__.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/__init__.py b/numpy/_array_api/__init__.py index 6afddb26a..43b2d4ce3 100644 --- a/numpy/_array_api/__init__.py +++ b/numpy/_array_api/__init__.py @@ -84,17 +84,21 @@ from ._constants import e, inf, nan, pi __all__ += ['e', 'inf', 'nan', 'pi'] -from ._creation_functions import arange, empty, empty_like, eye, full, full_like, linspace, ones, ones_like, zeros, zeros_like +from ._creation_functions import asarray, arange, empty, empty_like, eye, from_dlpack, full, full_like, linspace, ones, ones_like, zeros, zeros_like -__all__ += ['arange', 'empty', 'empty_like', 'eye', 'full', 'full_like', 'linspace', 'ones', 'ones_like', 'zeros', 'zeros_like'] +__all__ += ['asarray', 'arange', 'empty', 'empty_like', 'eye', 'from_dlpack', 'full', 'full_like', 'linspace', 'ones', 'ones_like', 'zeros', 'zeros_like'] + +from ._data_type_functions import finfo, iinfo, result_type + +__all__ += ['finfo', 'iinfo', 'result_type'] from ._dtypes import int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, bool __all__ += ['int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64', 'float32', 'float64', 'bool'] -from ._elementwise_functions import abs, acos, acosh, add, asin, asinh, atan, atan2, atanh, bitwise_and, bitwise_left_shift, bitwise_invert, bitwise_or, bitwise_right_shift, bitwise_xor, ceil, cos, cosh, divide, equal, exp, expm1, floor, floor_divide, greater, greater_equal, isfinite, isinf, isnan, less, less_equal, log, log1p, log2, log10, logical_and, logical_not, logical_or, logical_xor, multiply, negative, not_equal, positive, pow, remainder, round, sign, sin, sinh, square, sqrt, subtract, tan, tanh, trunc +from ._elementwise_functions import abs, acos, acosh, add, asin, asinh, atan, atan2, atanh, bitwise_and, bitwise_left_shift, bitwise_invert, bitwise_or, bitwise_right_shift, bitwise_xor, ceil, cos, cosh, divide, equal, exp, expm1, floor, floor_divide, greater, greater_equal, isfinite, isinf, isnan, less, less_equal, log, log1p, log2, log10, logaddexp, logical_and, logical_not, logical_or, logical_xor, multiply, negative, not_equal, positive, pow, remainder, round, sign, sin, sinh, square, sqrt, subtract, tan, tanh, trunc -__all__ += ['abs', 'acos', 'acosh', 'add', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'bitwise_and', 'bitwise_left_shift', 'bitwise_invert', 'bitwise_or', 'bitwise_right_shift', 'bitwise_xor', 'ceil', 'cos', 'cosh', 'divide', 'equal', 'exp', 'expm1', 'floor', 'floor_divide', 'greater', 'greater_equal', 'isfinite', 'isinf', 'isnan', 'less', 'less_equal', 'log', 'log1p', 'log2', 'log10', 'logical_and', 'logical_not', 'logical_or', 'logical_xor', 'multiply', 'negative', 'not_equal', 'positive', 'pow', 'remainder', 'round', 'sign', 'sin', 'sinh', 'square', 'sqrt', 'subtract', 'tan', 'tanh', 'trunc'] +__all__ += ['abs', 'acos', 'acosh', 'add', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'bitwise_and', 'bitwise_left_shift', 'bitwise_invert', 'bitwise_or', 'bitwise_right_shift', 'bitwise_xor', 'ceil', 'cos', 'cosh', 'divide', 'equal', 'exp', 'expm1', 'floor', 'floor_divide', 'greater', 'greater_equal', 'isfinite', 'isinf', 'isnan', 'less', 'less_equal', 'log', 'log1p', 'log2', 'log10', 'logaddexp', 'logical_and', 'logical_not', 'logical_or', 'logical_xor', 'multiply', 'negative', 'not_equal', 'positive', 'pow', 'remainder', 'round', 'sign', 'sin', 'sinh', 'square', 'sqrt', 'subtract', 'tan', 'tanh', 'trunc'] from ._linear_algebra_functions import cross, det, diagonal, inv, norm, outer, trace, transpose -- cgit v1.2.1 From c791956135e3a1e63ca1e51ec5a1a79e65015ce8 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 23 Feb 2021 18:14:49 -0700 Subject: Fix the copy keyword argument in the array_api namespace asarray() --- numpy/_array_api/_creation_functions.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index d015734ff..bd607234d 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -14,7 +14,10 @@ def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, Su if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return np.asarray(obj, dtype=dtype, copy=copy) + if copy is not None: + # Note: copy is not yet implemented in np.asarray + raise NotImplementedError("The copy keyword argument to asarray is not yet implemented") + return np.asarray(obj, dtype=dtype) def arange(start: Union[int, float], /, *, stop: Optional[Union[int, float]] = None, step: Union[int, float] = 1, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ -- cgit v1.2.1 From 853a18de30219a0d25709caac0410de6dfeb4e95 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 23 Feb 2021 18:21:35 -0700 Subject: Implement a simple passthrough __str__ and __repr__ on the array_api ndarray class These methods aren't required by the spec, but without them, the array object is harder to use interactively. --- numpy/_array_api/_array_object.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 5ce650ae9..09f5e5710 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -40,6 +40,23 @@ class ndarray: def __new__(cls, *args, **kwargs): raise TypeError("The array_api ndarray object should not be instantiated directly. Use an array creation function, such as asarray(), instead.") + # These functions are not required by the spec, but are implemented for + # the sake of usability. + + def __str__(x: array, /) -> str: + """ + Performs the operation __str__. + """ + return x._array.__str__() + + def __repr__(x: array, /) -> str: + """ + Performs the operation __repr__. + """ + return x._array.__repr__() + + # Everything below this is required by the spec. + def __abs__(x: array, /) -> array: """ Performs the operation __abs__. -- cgit v1.2.1 From a42f71ac8ac559d0ef770bdfd2f62bd1be4848d5 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 23 Feb 2021 18:54:02 -0700 Subject: Only allow supported dtypes in the array_api namespace asarray() --- numpy/_array_api/_creation_functions.py | 7 ++++++- numpy/_array_api/_dtypes.py | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index bd607234d..06d9e6dad 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -11,13 +11,18 @@ def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, Su See its docstring for more information. """ + from ._array_object import ndarray + from . import _dtypes if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") if copy is not None: # Note: copy is not yet implemented in np.asarray raise NotImplementedError("The copy keyword argument to asarray is not yet implemented") - return np.asarray(obj, dtype=dtype) + res = np.asarray(obj, dtype=dtype) + if res.dtype not in _dtypes._all_dtypes: + raise TypeError(f"The array_api namespace does not support the dtype {res.dtype}") + return ndarray._new(res) def arange(start: Union[int, float], /, *, stop: Optional[Union[int, float]] = None, step: Union[int, float] = 1, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ diff --git a/numpy/_array_api/_dtypes.py b/numpy/_array_api/_dtypes.py index acf87fd82..f5e25355f 100644 --- a/numpy/_array_api/_dtypes.py +++ b/numpy/_array_api/_dtypes.py @@ -1,3 +1,6 @@ from .. import int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64 # Note: This name is changed from .. import bool_ as bool + +_all_dtypes = [int8, int16, int32, int64, uint8, uint16, uint32, uint64, + float32, float64, bool] -- cgit v1.2.1 From cd7092078b8f74ac5f873c9547676688ffd22276 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 23 Feb 2021 18:54:43 -0700 Subject: Support array_api.ndarray in array_api.asarray() --- numpy/_array_api/_creation_functions.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 06d9e6dad..6d9a767b4 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -19,6 +19,8 @@ def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, Su if copy is not None: # Note: copy is not yet implemented in np.asarray raise NotImplementedError("The copy keyword argument to asarray is not yet implemented") + if isinstance(obj, ndarray): + return obj res = np.asarray(obj, dtype=dtype) if res.dtype not in _dtypes._all_dtypes: raise TypeError(f"The array_api namespace does not support the dtype {res.dtype}") -- cgit v1.2.1 From 3b9c910ab1687f5af6c2d20fd737704fe39706e2 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 26 Feb 2021 17:35:53 -0700 Subject: Return ndarray in the array_api namespace elementwise functions --- numpy/_array_api/_elementwise_functions.py | 113 +++++++++++++++-------------- 1 file changed, 57 insertions(+), 56 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index a117c3370..abb7ef4dd 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -1,6 +1,7 @@ from __future__ import annotations from ._types import array +from ._array_object import ndarray import numpy as np @@ -10,7 +11,7 @@ def abs(x: array, /) -> array: See its docstring for more information. """ - return np.abs(x) + return ndarray._new(np.abs(x._array)) def acos(x: array, /) -> array: """ @@ -19,7 +20,7 @@ def acos(x: array, /) -> array: See its docstring for more information. """ # Note: the function name is different here - return np.arccos(x) + return ndarray._new(np.arccos(x._array)) def acosh(x: array, /) -> array: """ @@ -28,7 +29,7 @@ def acosh(x: array, /) -> array: See its docstring for more information. """ # Note: the function name is different here - return np.arccosh(x) + return ndarray._new(np.arccosh(x._array)) def add(x1: array, x2: array, /) -> array: """ @@ -36,7 +37,7 @@ def add(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.add(x1, x2) + return ndarray._new(np.add(x1._array, x2._array)) def asin(x: array, /) -> array: """ @@ -45,7 +46,7 @@ def asin(x: array, /) -> array: See its docstring for more information. """ # Note: the function name is different here - return np.arcsin(x) + return ndarray._new(np.arcsin(x._array)) def asinh(x: array, /) -> array: """ @@ -54,7 +55,7 @@ def asinh(x: array, /) -> array: See its docstring for more information. """ # Note: the function name is different here - return np.arcsinh(x) + return ndarray._new(np.arcsinh(x._array)) def atan(x: array, /) -> array: """ @@ -63,7 +64,7 @@ def atan(x: array, /) -> array: See its docstring for more information. """ # Note: the function name is different here - return np.arctan(x) + return ndarray._new(np.arctan(x._array)) def atan2(x1: array, x2: array, /) -> array: """ @@ -72,7 +73,7 @@ def atan2(x1: array, x2: array, /) -> array: See its docstring for more information. """ # Note: the function name is different here - return np.arctan2(x1, x2) + return ndarray._new(np.arctan2(x1._array, x2._array)) def atanh(x: array, /) -> array: """ @@ -81,7 +82,7 @@ def atanh(x: array, /) -> array: See its docstring for more information. """ # Note: the function name is different here - return np.arctanh(x) + return ndarray._new(np.arctanh(x._array)) def bitwise_and(x1: array, x2: array, /) -> array: """ @@ -89,7 +90,7 @@ def bitwise_and(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.bitwise_and(x1, x2) + return ndarray._new(np.bitwise_and(x1._array, x2._array)) def bitwise_left_shift(x1: array, x2: array, /) -> array: """ @@ -98,7 +99,7 @@ def bitwise_left_shift(x1: array, x2: array, /) -> array: See its docstring for more information. """ # Note: the function name is different here - return np.left_shift(x1, x2) + return ndarray._new(np.left_shift(x1._array, x2._array)) def bitwise_invert(x: array, /) -> array: """ @@ -107,7 +108,7 @@ def bitwise_invert(x: array, /) -> array: See its docstring for more information. """ # Note: the function name is different here - return np.invert(x) + return ndarray._new(np.invert(x._array)) def bitwise_or(x1: array, x2: array, /) -> array: """ @@ -115,7 +116,7 @@ def bitwise_or(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.bitwise_or(x1, x2) + return ndarray._new(np.bitwise_or(x1._array, x2._array)) def bitwise_right_shift(x1: array, x2: array, /) -> array: """ @@ -124,7 +125,7 @@ def bitwise_right_shift(x1: array, x2: array, /) -> array: See its docstring for more information. """ # Note: the function name is different here - return np.right_shift(x1, x2) + return ndarray._new(np.right_shift(x1._array, x2._array)) def bitwise_xor(x1: array, x2: array, /) -> array: """ @@ -132,7 +133,7 @@ def bitwise_xor(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.bitwise_xor(x1, x2) + return ndarray._new(np.bitwise_xor(x1._array, x2._array)) def ceil(x: array, /) -> array: """ @@ -140,7 +141,7 @@ def ceil(x: array, /) -> array: See its docstring for more information. """ - return np.ceil(x) + return ndarray._new(np.ceil(x._array)) def cos(x: array, /) -> array: """ @@ -148,7 +149,7 @@ def cos(x: array, /) -> array: See its docstring for more information. """ - return np.cos(x) + return ndarray._new(np.cos(x._array)) def cosh(x: array, /) -> array: """ @@ -156,7 +157,7 @@ def cosh(x: array, /) -> array: See its docstring for more information. """ - return np.cosh(x) + return ndarray._new(np.cosh(x._array)) def divide(x1: array, x2: array, /) -> array: """ @@ -164,7 +165,7 @@ def divide(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.divide(x1, x2) + return ndarray._new(np.divide(x1._array, x2._array)) def equal(x1: array, x2: array, /) -> array: """ @@ -172,7 +173,7 @@ def equal(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.equal(x1, x2) + return ndarray._new(np.equal(x1._array, x2._array)) def exp(x: array, /) -> array: """ @@ -180,7 +181,7 @@ def exp(x: array, /) -> array: See its docstring for more information. """ - return np.exp(x) + return ndarray._new(np.exp(x._array)) def expm1(x: array, /) -> array: """ @@ -188,7 +189,7 @@ def expm1(x: array, /) -> array: See its docstring for more information. """ - return np.expm1(x) + return ndarray._new(np.expm1(x._array)) def floor(x: array, /) -> array: """ @@ -196,7 +197,7 @@ def floor(x: array, /) -> array: See its docstring for more information. """ - return np.floor(x) + return ndarray._new(np.floor(x._array)) def floor_divide(x1: array, x2: array, /) -> array: """ @@ -204,7 +205,7 @@ def floor_divide(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.floor_divide(x1, x2) + return ndarray._new(np.floor_divide(x1._array, x2._array)) def greater(x1: array, x2: array, /) -> array: """ @@ -212,7 +213,7 @@ def greater(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.greater(x1, x2) + return ndarray._new(np.greater(x1._array, x2._array)) def greater_equal(x1: array, x2: array, /) -> array: """ @@ -220,7 +221,7 @@ def greater_equal(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.greater_equal(x1, x2) + return ndarray._new(np.greater_equal(x1._array, x2._array)) def isfinite(x: array, /) -> array: """ @@ -228,7 +229,7 @@ def isfinite(x: array, /) -> array: See its docstring for more information. """ - return np.isfinite(x) + return ndarray._new(np.isfinite(x._array)) def isinf(x: array, /) -> array: """ @@ -236,7 +237,7 @@ def isinf(x: array, /) -> array: See its docstring for more information. """ - return np.isinf(x) + return ndarray._new(np.isinf(x._array)) def isnan(x: array, /) -> array: """ @@ -244,7 +245,7 @@ def isnan(x: array, /) -> array: See its docstring for more information. """ - return np.isnan(x) + return ndarray._new(np.isnan(x._array)) def less(x1: array, x2: array, /) -> array: """ @@ -252,7 +253,7 @@ def less(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.less(x1, x2) + return ndarray._new(np.less(x1._array, x2._array)) def less_equal(x1: array, x2: array, /) -> array: """ @@ -260,7 +261,7 @@ def less_equal(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.less_equal(x1, x2) + return ndarray._new(np.less_equal(x1._array, x2._array)) def log(x: array, /) -> array: """ @@ -268,7 +269,7 @@ def log(x: array, /) -> array: See its docstring for more information. """ - return np.log(x) + return ndarray._new(np.log(x._array)) def log1p(x: array, /) -> array: """ @@ -276,7 +277,7 @@ def log1p(x: array, /) -> array: See its docstring for more information. """ - return np.log1p(x) + return ndarray._new(np.log1p(x._array)) def log2(x: array, /) -> array: """ @@ -284,7 +285,7 @@ def log2(x: array, /) -> array: See its docstring for more information. """ - return np.log2(x) + return ndarray._new(np.log2(x._array)) def log10(x: array, /) -> array: """ @@ -292,7 +293,7 @@ def log10(x: array, /) -> array: See its docstring for more information. """ - return np.log10(x) + return ndarray._new(np.log10(x._array)) def logaddexp(x1: array, x2: array) -> array: """ @@ -300,7 +301,7 @@ def logaddexp(x1: array, x2: array) -> array: See its docstring for more information. """ - return np.logaddexp(x1, x2) + return ndarray._new(np.logaddexp(x1._array, x2._array)) def logical_and(x1: array, x2: array, /) -> array: """ @@ -308,7 +309,7 @@ def logical_and(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.logical_and(x1, x2) + return ndarray._new(np.logical_and(x1._array, x2._array)) def logical_not(x: array, /) -> array: """ @@ -316,7 +317,7 @@ def logical_not(x: array, /) -> array: See its docstring for more information. """ - return np.logical_not(x) + return ndarray._new(np.logical_not(x._array)) def logical_or(x1: array, x2: array, /) -> array: """ @@ -324,7 +325,7 @@ def logical_or(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.logical_or(x1, x2) + return ndarray._new(np.logical_or(x1._array, x2._array)) def logical_xor(x1: array, x2: array, /) -> array: """ @@ -332,7 +333,7 @@ def logical_xor(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.logical_xor(x1, x2) + return ndarray._new(np.logical_xor(x1._array, x2._array)) def multiply(x1: array, x2: array, /) -> array: """ @@ -340,7 +341,7 @@ def multiply(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.multiply(x1, x2) + return ndarray._new(np.multiply(x1._array, x2._array)) def negative(x: array, /) -> array: """ @@ -348,7 +349,7 @@ def negative(x: array, /) -> array: See its docstring for more information. """ - return np.negative(x) + return ndarray._new(np.negative(x._array)) def not_equal(x1: array, x2: array, /) -> array: """ @@ -356,7 +357,7 @@ def not_equal(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.not_equal(x1, x2) + return ndarray._new(np.not_equal(x1._array, x2._array)) def positive(x: array, /) -> array: """ @@ -364,7 +365,7 @@ def positive(x: array, /) -> array: See its docstring for more information. """ - return np.positive(x) + return ndarray._new(np.positive(x._array)) def pow(x1: array, x2: array, /) -> array: """ @@ -373,7 +374,7 @@ def pow(x1: array, x2: array, /) -> array: See its docstring for more information. """ # Note: the function name is different here - return np.power(x1, x2) + return ndarray._new(np.power(x1._array, x2._array)) def remainder(x1: array, x2: array, /) -> array: """ @@ -381,7 +382,7 @@ def remainder(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.remainder(x1, x2) + return ndarray._new(np.remainder(x1._array, x2._array)) def round(x: array, /) -> array: """ @@ -389,7 +390,7 @@ def round(x: array, /) -> array: See its docstring for more information. """ - return np.round._implementation(x) + return ndarray._new(np.round._implementation(x._array)) def sign(x: array, /) -> array: """ @@ -397,7 +398,7 @@ def sign(x: array, /) -> array: See its docstring for more information. """ - return np.sign(x) + return ndarray._new(np.sign(x._array)) def sin(x: array, /) -> array: """ @@ -405,7 +406,7 @@ def sin(x: array, /) -> array: See its docstring for more information. """ - return np.sin(x) + return ndarray._new(np.sin(x._array)) def sinh(x: array, /) -> array: """ @@ -413,7 +414,7 @@ def sinh(x: array, /) -> array: See its docstring for more information. """ - return np.sinh(x) + return ndarray._new(np.sinh(x._array)) def square(x: array, /) -> array: """ @@ -421,7 +422,7 @@ def square(x: array, /) -> array: See its docstring for more information. """ - return np.square(x) + return ndarray._new(np.square(x._array)) def sqrt(x: array, /) -> array: """ @@ -429,7 +430,7 @@ def sqrt(x: array, /) -> array: See its docstring for more information. """ - return np.sqrt(x) + return ndarray._new(np.sqrt(x._array)) def subtract(x1: array, x2: array, /) -> array: """ @@ -437,7 +438,7 @@ def subtract(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.subtract(x1, x2) + return ndarray._new(np.subtract(x1._array, x2._array)) def tan(x: array, /) -> array: """ @@ -445,7 +446,7 @@ def tan(x: array, /) -> array: See its docstring for more information. """ - return np.tan(x) + return ndarray._new(np.tan(x._array)) def tanh(x: array, /) -> array: """ @@ -453,7 +454,7 @@ def tanh(x: array, /) -> array: See its docstring for more information. """ - return np.tanh(x) + return ndarray._new(np.tanh(x._array)) def trunc(x: array, /) -> array: """ @@ -461,4 +462,4 @@ def trunc(x: array, /) -> array: See its docstring for more information. """ - return np.trunc(x) + return ndarray._new(np.trunc(x._array)) -- cgit v1.2.1 From 6e36bfce6fae5ce1a0aa2a71eee3d953366ba439 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 26 Feb 2021 17:37:27 -0700 Subject: Use a different repr form for array_api.ndarray than array --- numpy/_array_api/_array_object.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 09f5e5710..99e6147f5 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -47,13 +47,13 @@ class ndarray: """ Performs the operation __str__. """ - return x._array.__str__() + return x._array.__str__().replace('array', 'ndarray') def __repr__(x: array, /) -> str: """ Performs the operation __repr__. """ - return x._array.__repr__() + return x._array.__repr__().replace('array', 'ndarray') # Everything below this is required by the spec. -- cgit v1.2.1 From 061fecb0c68d35ab8761ad5f9f1b05f3c3bd293b Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 26 Feb 2021 17:38:07 -0700 Subject: Fix the dunder methods on array_api.ndarray --- numpy/_array_api/_array_object.py | 160 +++++++++++++++++++++----------------- 1 file changed, 88 insertions(+), 72 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 99e6147f5..4c4abeb4a 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -17,6 +17,7 @@ from __future__ import annotations from enum import IntEnum from ._types import Optional, PyCapsule, Tuple, Union, array +from ._creation_functions import asarray class ndarray: # Use a custom constructor instead of __init__, as manually initializing @@ -61,384 +62,399 @@ class ndarray: """ Performs the operation __abs__. """ - res = x._array.__abs__(x) + res = x._array.__abs__() return x.__class__._new(res) def __add__(x1: array, x2: array, /) -> array: """ Performs the operation __add__. """ - res = x1._array.__add__(x1, x2) + res = x1._array.__add__(asarray(x2)._array) return x1.__class__._new(res) def __and__(x1: array, x2: array, /) -> array: """ Performs the operation __and__. """ - res = x1._array.__and__(x1, x2) + res = x1._array.__and__(asarray(x2)._array) return x1.__class__._new(res) def __bool__(x: array, /) -> bool: """ Performs the operation __bool__. """ - res = x._array.__bool__(x) + res = x._array.__bool__() return x.__class__._new(res) def __dlpack__(x: array, /, *, stream: Optional[int] = None) -> PyCapsule: """ Performs the operation __dlpack__. """ - res = x._array.__dlpack__(x, stream=None) + res = x._array.__dlpack__(stream=None) return x.__class__._new(res) def __dlpack_device__(x: array, /) -> Tuple[IntEnum, int]: """ Performs the operation __dlpack_device__. """ - res = x._array.__dlpack_device__(x) + res = x._array.__dlpack_device__() return x.__class__._new(res) def __eq__(x1: array, x2: array, /) -> array: """ Performs the operation __eq__. """ - res = x1._array.__eq__(x1, x2) + res = x1._array.__eq__(asarray(x2)._array) return x1.__class__._new(res) def __float__(x: array, /) -> float: """ Performs the operation __float__. """ - res = x._array.__float__(x) + res = x._array.__float__() return x.__class__._new(res) def __floordiv__(x1: array, x2: array, /) -> array: """ Performs the operation __floordiv__. """ - res = x1._array.__floordiv__(x1, x2) + res = x1._array.__floordiv__(asarray(x2)._array) return x1.__class__._new(res) def __ge__(x1: array, x2: array, /) -> array: """ Performs the operation __ge__. """ - res = x1._array.__ge__(x1, x2) + res = x1._array.__ge__(asarray(x2)._array) return x1.__class__._new(res) def __getitem__(x: array, key: Union[int, slice, Tuple[Union[int, slice], ...], array], /) -> array: """ Performs the operation __getitem__. """ - res = x._array.__getitem__(x, key) + res = x._array.__getitem__(asarray(key)._array) return x.__class__._new(res) def __gt__(x1: array, x2: array, /) -> array: """ Performs the operation __gt__. """ - res = x1._array.__gt__(x1, x2) + res = x1._array.__gt__(asarray(x2)._array) return x1.__class__._new(res) def __int__(x: array, /) -> int: """ - Performs the in-place operation __int__. + Performs the operation __int__. """ - x._array.__int__(x) + res = x._array.__int__() + return x.__class__._new(res) def __invert__(x: array, /) -> array: """ - Performs the in-place operation __invert__. + Performs the operation __invert__. """ - x._array.__invert__(x) + res = x._array.__invert__() + return x.__class__._new(res) def __le__(x1: array, x2: array, /) -> array: """ Performs the operation __le__. """ - res = x1._array.__le__(x1, x2) + res = x1._array.__le__(asarray(x2)._array) return x1.__class__._new(res) def __len__(x, /): """ Performs the operation __len__. """ - res = x._array.__len__(x) + res = x._array.__len__() return x.__class__._new(res) def __lshift__(x1: array, x2: array, /) -> array: """ Performs the operation __lshift__. """ - res = x1._array.__lshift__(x1, x2) + res = x1._array.__lshift__(asarray(x2)._array) return x1.__class__._new(res) def __lt__(x1: array, x2: array, /) -> array: """ Performs the operation __lt__. """ - res = x1._array.__lt__(x1, x2) + res = x1._array.__lt__(asarray(x2)._array) return x1.__class__._new(res) def __matmul__(x1: array, x2: array, /) -> array: """ Performs the operation __matmul__. """ - res = x1._array.__matmul__(x1, x2) + res = x1._array.__matmul__(asarray(x2)._array) return x1.__class__._new(res) def __mod__(x1: array, x2: array, /) -> array: """ Performs the operation __mod__. """ - res = x1._array.__mod__(x1, x2) + res = x1._array.__mod__(asarray(x2)._array) return x1.__class__._new(res) def __mul__(x1: array, x2: array, /) -> array: """ Performs the operation __mul__. """ - res = x1._array.__mul__(x1, x2) + res = x1._array.__mul__(asarray(x2)._array) return x1.__class__._new(res) def __ne__(x1: array, x2: array, /) -> array: """ Performs the operation __ne__. """ - res = x1._array.__ne__(x1, x2) + res = x1._array.__ne__(asarray(x2)._array) return x1.__class__._new(res) def __neg__(x: array, /) -> array: """ Performs the operation __neg__. """ - res = x._array.__neg__(x) + res = x._array.__neg__() return x.__class__._new(res) def __or__(x1: array, x2: array, /) -> array: """ Performs the operation __or__. """ - res = x1._array.__or__(x1, x2) + res = x1._array.__or__(asarray(x2)._array) return x1.__class__._new(res) def __pos__(x: array, /) -> array: """ Performs the operation __pos__. """ - res = x._array.__pos__(x) + res = x._array.__pos__() return x.__class__._new(res) def __pow__(x1: array, x2: array, /) -> array: """ Performs the operation __pow__. """ - res = x1._array.__pow__(x1, x2) + res = x1._array.__pow__(asarray(x2)._array) return x1.__class__._new(res) def __rshift__(x1: array, x2: array, /) -> array: """ Performs the operation __rshift__. """ - res = x1._array.__rshift__(x1, x2) + res = x1._array.__rshift__(asarray(x2)._array) return x1.__class__._new(res) def __setitem__(x, key, value, /): """ Performs the operation __setitem__. """ - res = x._array.__setitem__(x, key, value) + res = x._array.__setitem__(asarray(key)._array, asarray(value)._array) return x.__class__._new(res) def __sub__(x1: array, x2: array, /) -> array: """ Performs the operation __sub__. """ - res = x1._array.__sub__(x1, x2) + res = x1._array.__sub__(asarray(x2)._array) return x1.__class__._new(res) def __truediv__(x1: array, x2: array, /) -> array: """ Performs the operation __truediv__. """ - res = x1._array.__truediv__(x1, x2) + res = x1._array.__truediv__(asarray(x2)._array) return x1.__class__._new(res) def __xor__(x1: array, x2: array, /) -> array: """ Performs the operation __xor__. """ - res = x1._array.__xor__(x1, x2) + res = x1._array.__xor__(asarray(x2)._array) return x1.__class__._new(res) def __iadd__(x1: array, x2: array, /) -> array: """ - Performs the in-place operation __iadd__. + Performs the operation __iadd__. """ - x1._array.__iadd__(x1, x2) + res = x1._array.__iadd__(asarray(x2)._array) + return x1.__class__._new(res) def __radd__(x1: array, x2: array, /) -> array: """ Performs the operation __radd__. """ - res = x1._array.__radd__(x1, x2) + res = x1._array.__radd__(asarray(x2)._array) return x1.__class__._new(res) def __iand__(x1: array, x2: array, /) -> array: """ - Performs the in-place operation __iand__. + Performs the operation __iand__. """ - x1._array.__iand__(x1, x2) + res = x1._array.__iand__(asarray(x2)._array) + return x1.__class__._new(res) def __rand__(x1: array, x2: array, /) -> array: """ Performs the operation __rand__. """ - res = x1._array.__rand__(x1, x2) + res = x1._array.__rand__(asarray(x2)._array) return x1.__class__._new(res) def __ifloordiv__(x1: array, x2: array, /) -> array: """ - Performs the in-place operation __ifloordiv__. + Performs the operation __ifloordiv__. """ - x1._array.__ifloordiv__(x1, x2) + res = x1._array.__ifloordiv__(asarray(x2)._array) + return x1.__class__._new(res) def __rfloordiv__(x1: array, x2: array, /) -> array: """ Performs the operation __rfloordiv__. """ - res = x1._array.__rfloordiv__(x1, x2) + res = x1._array.__rfloordiv__(asarray(x2)._array) return x1.__class__._new(res) def __ilshift__(x1: array, x2: array, /) -> array: """ - Performs the in-place operation __ilshift__. + Performs the operation __ilshift__. """ - x1._array.__ilshift__(x1, x2) + res = x1._array.__ilshift__(asarray(x2)._array) + return x1.__class__._new(res) def __rlshift__(x1: array, x2: array, /) -> array: """ Performs the operation __rlshift__. """ - res = x1._array.__rlshift__(x1, x2) + res = x1._array.__rlshift__(asarray(x2)._array) return x1.__class__._new(res) def __imatmul__(x1: array, x2: array, /) -> array: """ - Performs the in-place operation __imatmul__. + Performs the operation __imatmul__. """ - x1._array.__imatmul__(x1, x2) + res = x1._array.__imatmul__(asarray(x2)._array) + return x1.__class__._new(res) def __rmatmul__(x1: array, x2: array, /) -> array: """ Performs the operation __rmatmul__. """ - res = x1._array.__rmatmul__(x1, x2) + res = x1._array.__rmatmul__(asarray(x2)._array) return x1.__class__._new(res) def __imod__(x1: array, x2: array, /) -> array: """ - Performs the in-place operation __imod__. + Performs the operation __imod__. """ - x1._array.__imod__(x1, x2) + res = x1._array.__imod__(asarray(x2)._array) + return x1.__class__._new(res) def __rmod__(x1: array, x2: array, /) -> array: """ Performs the operation __rmod__. """ - res = x1._array.__rmod__(x1, x2) + res = x1._array.__rmod__(asarray(x2)._array) return x1.__class__._new(res) def __imul__(x1: array, x2: array, /) -> array: """ - Performs the in-place operation __imul__. + Performs the operation __imul__. """ - x1._array.__imul__(x1, x2) + res = x1._array.__imul__(asarray(x2)._array) + return x1.__class__._new(res) def __rmul__(x1: array, x2: array, /) -> array: """ Performs the operation __rmul__. """ - res = x1._array.__rmul__(x1, x2) + res = x1._array.__rmul__(asarray(x2)._array) return x1.__class__._new(res) def __ior__(x1: array, x2: array, /) -> array: """ - Performs the in-place operation __ior__. + Performs the operation __ior__. """ - x1._array.__ior__(x1, x2) + res = x1._array.__ior__(asarray(x2)._array) + return x1.__class__._new(res) def __ror__(x1: array, x2: array, /) -> array: """ Performs the operation __ror__. """ - res = x1._array.__ror__(x1, x2) + res = x1._array.__ror__(asarray(x2)._array) return x1.__class__._new(res) def __ipow__(x1: array, x2: array, /) -> array: """ - Performs the in-place operation __ipow__. + Performs the operation __ipow__. """ - x1._array.__ipow__(x1, x2) + res = x1._array.__ipow__(asarray(x2)._array) + return x1.__class__._new(res) def __rpow__(x1: array, x2: array, /) -> array: """ Performs the operation __rpow__. """ - res = x1._array.__rpow__(x1, x2) + res = x1._array.__rpow__(asarray(x2)._array) return x1.__class__._new(res) def __irshift__(x1: array, x2: array, /) -> array: """ - Performs the in-place operation __irshift__. + Performs the operation __irshift__. """ - x1._array.__irshift__(x1, x2) + res = x1._array.__irshift__(asarray(x2)._array) + return x1.__class__._new(res) def __rrshift__(x1: array, x2: array, /) -> array: """ Performs the operation __rrshift__. """ - res = x1._array.__rrshift__(x1, x2) + res = x1._array.__rrshift__(asarray(x2)._array) return x1.__class__._new(res) def __isub__(x1: array, x2: array, /) -> array: """ - Performs the in-place operation __isub__. + Performs the operation __isub__. """ - x1._array.__isub__(x1, x2) + res = x1._array.__isub__(asarray(x2)._array) + return x1.__class__._new(res) def __rsub__(x1: array, x2: array, /) -> array: """ Performs the operation __rsub__. """ - res = x1._array.__rsub__(x1, x2) + res = x1._array.__rsub__(asarray(x2)._array) return x1.__class__._new(res) def __itruediv__(x1: array, x2: array, /) -> array: """ - Performs the in-place operation __itruediv__. + Performs the operation __itruediv__. """ - x1._array.__itruediv__(x1, x2) + res = x1._array.__itruediv__(asarray(x2)._array) + return x1.__class__._new(res) def __rtruediv__(x1: array, x2: array, /) -> array: """ Performs the operation __rtruediv__. """ - res = x1._array.__rtruediv__(x1, x2) + res = x1._array.__rtruediv__(asarray(x2)._array) return x1.__class__._new(res) def __ixor__(x1: array, x2: array, /) -> array: """ - Performs the in-place operation __ixor__. + Performs the operation __ixor__. """ - x1._array.__ixor__(x1, x2) + res = x1._array.__ixor__(asarray(x2)._array) + return x1.__class__._new(res) def __rxor__(x1: array, x2: array, /) -> array: """ Performs the operation __rxor__. """ - res = x1._array.__rxor__(x1, x2) + res = x1._array.__rxor__(asarray(x2)._array) return x1.__class__._new(res) @property -- cgit v1.2.1 From 587613f056299766be2da00a64b5fa0ac31c84aa Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 26 Feb 2021 17:38:41 -0700 Subject: Use ndarray in the array API creation functions --- numpy/_array_api/_creation_functions.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 6d9a767b4..ba5b4c87a 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -32,10 +32,11 @@ def arange(start: Union[int, float], /, *, stop: Optional[Union[int, float]] = N See its docstring for more information. """ + from ._array_object import ndarray if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return np.arange(start, stop=stop, step=step, dtype=dtype) + return ndarray._new(np.arange(start, stop=stop, step=step, dtype=dtype)) def empty(shape: Union[int, Tuple[int, ...]], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ @@ -43,10 +44,11 @@ def empty(shape: Union[int, Tuple[int, ...]], /, *, dtype: Optional[dtype] = Non See its docstring for more information. """ + from ._array_object import ndarray if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return np.empty(shape, dtype=dtype) + return ndarray._new(np.empty(shape, dtype=dtype)) def empty_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ @@ -54,10 +56,11 @@ def empty_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[d See its docstring for more information. """ + from ._array_object import ndarray if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return np.empty_like._implementation(x, dtype=dtype) + return ndarray._new(np.empty_like._implementation(x._array, dtype=dtype)) def eye(N: int, /, *, M: Optional[int] = None, k: Optional[int] = 0, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ @@ -65,10 +68,11 @@ def eye(N: int, /, *, M: Optional[int] = None, k: Optional[int] = 0, dtype: Opti See its docstring for more information. """ + from ._array_object import ndarray if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return np.eye(N, M=M, k=k, dtype=dtype) + return ndarray._new(np.eye(N, M=M, k=k, dtype=dtype)) def from_dlpack(x: object, /) -> array: # Note: dlpack support is not yet implemented on ndarray @@ -80,10 +84,11 @@ def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], /, * See its docstring for more information. """ + from ._array_object import ndarray if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return np.full(shape, fill_value, dtype=dtype) + return ndarray._new(np.full(shape, fill_value, dtype=dtype)) def full_like(x: array, fill_value: Union[int, float], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ @@ -91,10 +96,11 @@ def full_like(x: array, fill_value: Union[int, float], /, *, dtype: Optional[dty See its docstring for more information. """ + from ._array_object import ndarray if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return np.full_like._implementation(x, fill_value, dtype=dtype) + return ndarray._new(np.full_like._implementation(x._array, fill_value, dtype=dtype)) def linspace(start: Union[int, float], stop: Union[int, float], num: int, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None, endpoint: bool = True) -> array: """ @@ -102,10 +108,11 @@ def linspace(start: Union[int, float], stop: Union[int, float], num: int, /, *, See its docstring for more information. """ + from ._array_object import ndarray if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return np.linspace(start, stop, num, dtype=dtype, endpoint=endpoint) + return ndarray._new(np.linspace(start, stop, num, dtype=dtype, endpoint=endpoint)) def ones(shape: Union[int, Tuple[int, ...]], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ @@ -113,10 +120,11 @@ def ones(shape: Union[int, Tuple[int, ...]], /, *, dtype: Optional[dtype] = None See its docstring for more information. """ + from ._array_object import ndarray if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return np.ones(shape, dtype=dtype) + return ndarray._new(np.ones(shape, dtype=dtype)) def ones_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ @@ -124,10 +132,11 @@ def ones_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[de See its docstring for more information. """ + from ._array_object import ndarray if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return np.ones_like._implementation(x, dtype=dtype) + return ndarray._new(np.ones_like._implementation(x._array, dtype=dtype)) def zeros(shape: Union[int, Tuple[int, ...]], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ @@ -135,10 +144,11 @@ def zeros(shape: Union[int, Tuple[int, ...]], /, *, dtype: Optional[dtype] = Non See its docstring for more information. """ + from ._array_object import ndarray if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return np.zeros(shape, dtype=dtype) + return ndarray._new(np.zeros(shape, dtype=dtype)) def zeros_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ @@ -146,7 +156,8 @@ def zeros_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[d See its docstring for more information. """ + from ._array_object import ndarray if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return np.zeros_like._implementation(x, dtype=dtype) + return ndarray._new(np.zeros_like._implementation(x._array, dtype=dtype)) -- cgit v1.2.1 From 892b536a36b89f362a845fd50959d6474ec2c5f4 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 26 Feb 2021 17:41:18 -0700 Subject: Only allow the spec guaranteed dtypes in the array API elementwise functions The array API namespace is designed to be only those parts of specification that are required. So many things that work in NumPy but are not required by the array API specification will not work in the array_api namespace functions. For example, transcendental functions will only work with floating-point dtypes, because those are the only dtypes required to work by the array API specification. --- numpy/_array_api/_dtypes.py | 5 ++ numpy/_array_api/_elementwise_functions.py | 114 +++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_dtypes.py b/numpy/_array_api/_dtypes.py index f5e25355f..d33ae1fce 100644 --- a/numpy/_array_api/_dtypes.py +++ b/numpy/_array_api/_dtypes.py @@ -4,3 +4,8 @@ from .. import bool_ as bool _all_dtypes = [int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, bool] +_boolean_dtypes = [bool] +_floating_dtypes = [float32, float64] +_integer_dtypes = [int8, int16, int32, int64, uint8, uint16, uint32, uint64] +_integer_or_boolean_dtypes = [bool, int8, int16, int32, int64, uint8, uint16, uint32, uint64] +_numeric_dtypes = [float32, float64, int8, int16, int32, int64, uint8, uint16, uint32, uint64] diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index abb7ef4dd..2357b337c 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -1,5 +1,7 @@ from __future__ import annotations +from ._dtypes import (_all_dtypes, _boolean_dtypes, _floating_dtypes, + _integer_dtypes, _integer_or_boolean_dtypes, _numeric_dtypes) from ._types import array from ._array_object import ndarray @@ -11,6 +13,8 @@ def abs(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in abs') return ndarray._new(np.abs(x._array)) def acos(x: array, /) -> array: @@ -19,6 +23,8 @@ def acos(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in acos') # Note: the function name is different here return ndarray._new(np.arccos(x._array)) @@ -28,6 +34,8 @@ def acosh(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in acosh') # Note: the function name is different here return ndarray._new(np.arccosh(x._array)) @@ -37,6 +45,8 @@ def add(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in add') return ndarray._new(np.add(x1._array, x2._array)) def asin(x: array, /) -> array: @@ -45,6 +55,8 @@ def asin(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in asin') # Note: the function name is different here return ndarray._new(np.arcsin(x._array)) @@ -54,6 +66,8 @@ def asinh(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in asinh') # Note: the function name is different here return ndarray._new(np.arcsinh(x._array)) @@ -63,6 +77,8 @@ def atan(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in atan') # Note: the function name is different here return ndarray._new(np.arctan(x._array)) @@ -72,6 +88,8 @@ def atan2(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in atan2') # Note: the function name is different here return ndarray._new(np.arctan2(x1._array, x2._array)) @@ -81,6 +99,8 @@ def atanh(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in atanh') # Note: the function name is different here return ndarray._new(np.arctanh(x._array)) @@ -90,6 +110,8 @@ def bitwise_and(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: + raise TypeError('Only integer_or_boolean dtypes are allowed in bitwise_and') return ndarray._new(np.bitwise_and(x1._array, x2._array)) def bitwise_left_shift(x1: array, x2: array, /) -> array: @@ -98,6 +120,8 @@ def bitwise_left_shift(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: + raise TypeError('Only integer dtypes are allowed in bitwise_left_shift') # Note: the function name is different here return ndarray._new(np.left_shift(x1._array, x2._array)) @@ -107,6 +131,8 @@ def bitwise_invert(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _integer_or_boolean_dtypes: + raise TypeError('Only integer or boolean dtypes are allowed in bitwise_invert') # Note: the function name is different here return ndarray._new(np.invert(x._array)) @@ -116,6 +142,8 @@ def bitwise_or(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: + raise TypeError('Only integer or boolean dtypes are allowed in bitwise_or') return ndarray._new(np.bitwise_or(x1._array, x2._array)) def bitwise_right_shift(x1: array, x2: array, /) -> array: @@ -124,6 +152,8 @@ def bitwise_right_shift(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: + raise TypeError('Only integer dtypes are allowed in bitwise_right_shift') # Note: the function name is different here return ndarray._new(np.right_shift(x1._array, x2._array)) @@ -133,6 +163,8 @@ def bitwise_xor(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: + raise TypeError('Only integer or boolean dtypes are allowed in bitwise_xor') return ndarray._new(np.bitwise_xor(x1._array, x2._array)) def ceil(x: array, /) -> array: @@ -141,6 +173,8 @@ def ceil(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in ceil') return ndarray._new(np.ceil(x._array)) def cos(x: array, /) -> array: @@ -149,6 +183,8 @@ def cos(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in cos') return ndarray._new(np.cos(x._array)) def cosh(x: array, /) -> array: @@ -157,6 +193,8 @@ def cosh(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in cosh') return ndarray._new(np.cosh(x._array)) def divide(x1: array, x2: array, /) -> array: @@ -165,6 +203,8 @@ def divide(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in divide') return ndarray._new(np.divide(x1._array, x2._array)) def equal(x1: array, x2: array, /) -> array: @@ -173,6 +213,8 @@ def equal(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _all_dtypes or x2.dtype not in _all_dtypes: + raise TypeError('Only array API spec dtypes are allowed in equal') return ndarray._new(np.equal(x1._array, x2._array)) def exp(x: array, /) -> array: @@ -181,6 +223,8 @@ def exp(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in exp') return ndarray._new(np.exp(x._array)) def expm1(x: array, /) -> array: @@ -189,6 +233,8 @@ def expm1(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in expm1') return ndarray._new(np.expm1(x._array)) def floor(x: array, /) -> array: @@ -197,6 +243,8 @@ def floor(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in floor') return ndarray._new(np.floor(x._array)) def floor_divide(x1: array, x2: array, /) -> array: @@ -205,6 +253,8 @@ def floor_divide(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in floor_divide') return ndarray._new(np.floor_divide(x1._array, x2._array)) def greater(x1: array, x2: array, /) -> array: @@ -213,6 +263,8 @@ def greater(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in greater') return ndarray._new(np.greater(x1._array, x2._array)) def greater_equal(x1: array, x2: array, /) -> array: @@ -221,6 +273,8 @@ def greater_equal(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in greater_equal') return ndarray._new(np.greater_equal(x1._array, x2._array)) def isfinite(x: array, /) -> array: @@ -229,6 +283,8 @@ def isfinite(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in isfinite') return ndarray._new(np.isfinite(x._array)) def isinf(x: array, /) -> array: @@ -237,6 +293,8 @@ def isinf(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in isinf') return ndarray._new(np.isinf(x._array)) def isnan(x: array, /) -> array: @@ -245,6 +303,8 @@ def isnan(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in isnan') return ndarray._new(np.isnan(x._array)) def less(x1: array, x2: array, /) -> array: @@ -253,6 +313,8 @@ def less(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in less') return ndarray._new(np.less(x1._array, x2._array)) def less_equal(x1: array, x2: array, /) -> array: @@ -261,6 +323,8 @@ def less_equal(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in less_equal') return ndarray._new(np.less_equal(x1._array, x2._array)) def log(x: array, /) -> array: @@ -269,6 +333,8 @@ def log(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in log') return ndarray._new(np.log(x._array)) def log1p(x: array, /) -> array: @@ -277,6 +343,8 @@ def log1p(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in log1p') return ndarray._new(np.log1p(x._array)) def log2(x: array, /) -> array: @@ -285,6 +353,8 @@ def log2(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in log2') return ndarray._new(np.log2(x._array)) def log10(x: array, /) -> array: @@ -293,6 +363,8 @@ def log10(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in log10') return ndarray._new(np.log10(x._array)) def logaddexp(x1: array, x2: array) -> array: @@ -301,6 +373,8 @@ def logaddexp(x1: array, x2: array) -> array: See its docstring for more information. """ + if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in logaddexp') return ndarray._new(np.logaddexp(x1._array, x2._array)) def logical_and(x1: array, x2: array, /) -> array: @@ -309,6 +383,8 @@ def logical_and(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: + raise TypeError('Only boolean dtypes are allowed in logical_and') return ndarray._new(np.logical_and(x1._array, x2._array)) def logical_not(x: array, /) -> array: @@ -317,6 +393,8 @@ def logical_not(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _boolean_dtypes: + raise TypeError('Only boolean dtypes are allowed in logical_not') return ndarray._new(np.logical_not(x._array)) def logical_or(x1: array, x2: array, /) -> array: @@ -325,6 +403,8 @@ def logical_or(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: + raise TypeError('Only boolean dtypes are allowed in logical_or') return ndarray._new(np.logical_or(x1._array, x2._array)) def logical_xor(x1: array, x2: array, /) -> array: @@ -333,6 +413,8 @@ def logical_xor(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: + raise TypeError('Only boolean dtypes are allowed in logical_xor') return ndarray._new(np.logical_xor(x1._array, x2._array)) def multiply(x1: array, x2: array, /) -> array: @@ -341,6 +423,8 @@ def multiply(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in multiply') return ndarray._new(np.multiply(x1._array, x2._array)) def negative(x: array, /) -> array: @@ -349,6 +433,8 @@ def negative(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in negative') return ndarray._new(np.negative(x._array)) def not_equal(x1: array, x2: array, /) -> array: @@ -357,6 +443,8 @@ def not_equal(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _all_dtypes or x2.dtype not in _all_dtypes: + raise TypeError('Only array API spec dtypes are allowed in not_equal') return ndarray._new(np.not_equal(x1._array, x2._array)) def positive(x: array, /) -> array: @@ -365,6 +453,8 @@ def positive(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in positive') return ndarray._new(np.positive(x._array)) def pow(x1: array, x2: array, /) -> array: @@ -373,6 +463,8 @@ def pow(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in pow') # Note: the function name is different here return ndarray._new(np.power(x1._array, x2._array)) @@ -382,6 +474,8 @@ def remainder(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in remainder') return ndarray._new(np.remainder(x1._array, x2._array)) def round(x: array, /) -> array: @@ -390,6 +484,8 @@ def round(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in round') return ndarray._new(np.round._implementation(x._array)) def sign(x: array, /) -> array: @@ -398,6 +494,8 @@ def sign(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in sign') return ndarray._new(np.sign(x._array)) def sin(x: array, /) -> array: @@ -406,6 +504,8 @@ def sin(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in sin') return ndarray._new(np.sin(x._array)) def sinh(x: array, /) -> array: @@ -414,6 +514,8 @@ def sinh(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in sinh') return ndarray._new(np.sinh(x._array)) def square(x: array, /) -> array: @@ -422,6 +524,8 @@ def square(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in square') return ndarray._new(np.square(x._array)) def sqrt(x: array, /) -> array: @@ -430,6 +534,8 @@ def sqrt(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in sqrt') return ndarray._new(np.sqrt(x._array)) def subtract(x1: array, x2: array, /) -> array: @@ -438,6 +544,8 @@ def subtract(x1: array, x2: array, /) -> array: See its docstring for more information. """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in subtract') return ndarray._new(np.subtract(x1._array, x2._array)) def tan(x: array, /) -> array: @@ -446,6 +554,8 @@ def tan(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in tan') return ndarray._new(np.tan(x._array)) def tanh(x: array, /) -> array: @@ -454,6 +564,8 @@ def tanh(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in tanh') return ndarray._new(np.tanh(x._array)) def trunc(x: array, /) -> array: @@ -462,4 +574,6 @@ def trunc(x: array, /) -> array: See its docstring for more information. """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in trunc') return ndarray._new(np.trunc(x._array)) -- cgit v1.2.1 From b7856e348d731551405bdf0dd41ff1b0416da129 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 26 Feb 2021 17:46:25 -0700 Subject: Make an error message easier to read --- numpy/_array_api/_creation_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index ba5b4c87a..4be482199 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -23,7 +23,7 @@ def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, Su return obj res = np.asarray(obj, dtype=dtype) if res.dtype not in _dtypes._all_dtypes: - raise TypeError(f"The array_api namespace does not support the dtype {res.dtype}") + raise TypeError(f"The array_api namespace does not support the dtype '{res.dtype}'") return ndarray._new(res) def arange(start: Union[int, float], /, *, stop: Optional[Union[int, float]] = None, step: Union[int, float] = 1, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: -- cgit v1.2.1 From 2ff635c7cbc8804a3956ddbf8165f536dffc2df5 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 26 Feb 2021 18:22:06 -0700 Subject: Don't check if a dtype is in all_dtypes The array API namespace is not going to do type checking against arbitrary objects. An object that takes an array as input should assume that it will get an array API namespace array object. Passing a NumPy array or other type of object to any of the functions is undefined behavior, unless the type signature allows for it. --- numpy/_array_api/_elementwise_functions.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index 2357b337c..b48a38c3d 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -1,6 +1,6 @@ from __future__ import annotations -from ._dtypes import (_all_dtypes, _boolean_dtypes, _floating_dtypes, +from ._dtypes import (_boolean_dtypes, _floating_dtypes, _integer_dtypes, _integer_or_boolean_dtypes, _numeric_dtypes) from ._types import array from ._array_object import ndarray @@ -213,8 +213,6 @@ def equal(x1: array, x2: array, /) -> array: See its docstring for more information. """ - if x1.dtype not in _all_dtypes or x2.dtype not in _all_dtypes: - raise TypeError('Only array API spec dtypes are allowed in equal') return ndarray._new(np.equal(x1._array, x2._array)) def exp(x: array, /) -> array: @@ -443,8 +441,6 @@ def not_equal(x1: array, x2: array, /) -> array: See its docstring for more information. """ - if x1.dtype not in _all_dtypes or x2.dtype not in _all_dtypes: - raise TypeError('Only array API spec dtypes are allowed in not_equal') return ndarray._new(np.not_equal(x1._array, x2._array)) def positive(x: array, /) -> array: -- cgit v1.2.1 From b933ebbe1aee58af38f05a341dc3952fc761d777 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 26 Feb 2021 18:24:36 -0700 Subject: Allow dimension 0 arrays in the array API namespace full() and full_like() --- numpy/_array_api/_creation_functions.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 4be482199..197960211 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -2,6 +2,7 @@ from __future__ import annotations from ._types import (Optional, SupportsDLPack, SupportsBufferProtocol, Tuple, Union, array, device, dtype) +from ._dtypes import _all_dtypes import numpy as np @@ -88,7 +89,14 @@ def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], /, * if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return ndarray._new(np.full(shape, fill_value, dtype=dtype)) + if isinstance(fill_value, ndarray) and fill_value.ndim == 0: + fill_value = fill_value._array[...] + res = np.full(shape, fill_value, dtype=dtype) + if res.dtype not in _all_dtypes: + # This will happen if the fill value is not something that NumPy + # coerces to one of the acceptable dtypes. + raise TypeError("Invalid input to full") + return ndarray._new(res) def full_like(x: array, fill_value: Union[int, float], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ @@ -100,7 +108,12 @@ def full_like(x: array, fill_value: Union[int, float], /, *, dtype: Optional[dty if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return ndarray._new(np.full_like._implementation(x._array, fill_value, dtype=dtype)) + res = np.full_like._implementation(x._array, fill_value, dtype=dtype) + if res.dtype not in _all_dtypes: + # This will happen if the fill value is not something that NumPy + # coerces to one of the acceptable dtypes. + raise TypeError("Invalid input to full_like") + return ndarray._new(res) def linspace(start: Union[int, float], stop: Union[int, float], num: int, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None, endpoint: bool = True) -> array: """ -- cgit v1.2.1 From 73d2c1e1675ed9e7fe2bc389ec0079c5c6ce73ee Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 26 Feb 2021 18:24:55 -0700 Subject: Make the array API constants into dimension 0 arrays The spec does not actually specify whether these should be dimension 0 arrays or Python floats (which they are in NumPy). However, making them dimension 0 arrays is cleaner, and ensures they also have all the methods and attributes that are implemented on the ndarray object. --- numpy/_array_api/_constants.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_constants.py b/numpy/_array_api/_constants.py index 075b8c3b9..5fde34625 100644 --- a/numpy/_array_api/_constants.py +++ b/numpy/_array_api/_constants.py @@ -1 +1,9 @@ -from .. import e, inf, nan, pi +from ._array_object import ndarray +from ._dtypes import float64 + +import numpy as np + +e = ndarray._new(np.array(np.e, dtype=float64)) +inf = ndarray._new(np.array(np.inf, dtype=float64)) +nan = ndarray._new(np.array(np.nan, dtype=float64)) +pi = ndarray._new(np.array(np.pi, dtype=float64)) -- cgit v1.2.1 From 16030e4e6931b81997b6c2d8d0ef4f15e39b6057 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 26 Feb 2021 18:28:48 -0700 Subject: Clean up some imports --- numpy/_array_api/_creation_functions.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 197960211..888f24558 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -12,8 +12,9 @@ def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, Su See its docstring for more information. """ + # _array_object imports in this file are inside the functions to avoid + # circular imports from ._array_object import ndarray - from . import _dtypes if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") @@ -23,7 +24,7 @@ def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, Su if isinstance(obj, ndarray): return obj res = np.asarray(obj, dtype=dtype) - if res.dtype not in _dtypes._all_dtypes: + if res.dtype not in _all_dtypes: raise TypeError(f"The array_api namespace does not support the dtype '{res.dtype}'") return ndarray._new(res) -- cgit v1.2.1 From d40985cbb50aeadf276a1a9332e455ddc096e113 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 2 Mar 2021 16:03:56 -0700 Subject: Fix some dunder methods on that should not be converting things to arrays --- numpy/_array_api/_array_object.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 4c4abeb4a..23f8ab333 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -84,7 +84,7 @@ class ndarray: Performs the operation __bool__. """ res = x._array.__bool__() - return x.__class__._new(res) + return res def __dlpack__(x: array, /, *, stream: Optional[int] = None) -> PyCapsule: """ @@ -112,7 +112,7 @@ class ndarray: Performs the operation __float__. """ res = x._array.__float__() - return x.__class__._new(res) + return res def __floordiv__(x1: array, x2: array, /) -> array: """ @@ -132,7 +132,7 @@ class ndarray: """ Performs the operation __getitem__. """ - res = x._array.__getitem__(asarray(key)._array) + res = x._array.__getitem__(key) return x.__class__._new(res) def __gt__(x1: array, x2: array, /) -> array: @@ -147,7 +147,7 @@ class ndarray: Performs the operation __int__. """ res = x._array.__int__() - return x.__class__._new(res) + return res def __invert__(x: array, /) -> array: """ @@ -251,7 +251,7 @@ class ndarray: """ Performs the operation __setitem__. """ - res = x._array.__setitem__(asarray(key)._array, asarray(value)._array) + res = x._array.__setitem__(key, asarray(value)._array) return x.__class__._new(res) def __sub__(x1: array, x2: array, /) -> array: -- cgit v1.2.1 From f1f9dca83213aa4b0e6495900482f1a180a014ae Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 2 Mar 2021 16:08:41 -0700 Subject: Make the array API manipulation functions use the array API ndarray object --- numpy/_array_api/_manipulation_functions.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index e312b18c5..413dbb1b1 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -1,6 +1,7 @@ from __future__ import annotations from ._types import Optional, Tuple, Union, array +from ._array_object import ndarray import numpy as np @@ -10,8 +11,9 @@ def concat(arrays: Tuple[array], /, *, axis: Optional[int] = 0) -> array: See its docstring for more information. """ + arrays = tuple(a._array for a in arrays) # Note: the function name is different here - return np.concatenate(arrays, axis=axis) + return ndarray._new(np.concatenate(arrays, axis=axis)) def expand_dims(x: array, axis: int, /) -> array: """ @@ -19,7 +21,7 @@ def expand_dims(x: array, axis: int, /) -> array: See its docstring for more information. """ - return np.expand_dims._implementation(x, axis) + return ndarray._new(np.expand_dims._implementation(x._array, axis)) def flip(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: """ @@ -27,7 +29,7 @@ def flip(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> See its docstring for more information. """ - return np.flip._implementation(x, axis=axis) + return ndarray._new(np.flip._implementation(x._array, axis=axis)) def reshape(x: array, shape: Tuple[int, ...], /) -> array: """ @@ -35,7 +37,7 @@ def reshape(x: array, shape: Tuple[int, ...], /) -> array: See its docstring for more information. """ - return np.reshape._implementation(x, shape) + return ndarray._new(np.reshape._implementation(x._array, shape)) def roll(x: array, shift: Union[int, Tuple[int, ...]], /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: """ @@ -43,7 +45,7 @@ def roll(x: array, shift: Union[int, Tuple[int, ...]], /, *, axis: Optional[Unio See its docstring for more information. """ - return np.roll._implementation(x, shift, axis=axis) + return ndarray._new(np.roll._implementation(x._array, shift, axis=axis)) def squeeze(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: """ @@ -51,7 +53,7 @@ def squeeze(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) See its docstring for more information. """ - return np.squeeze._implementation(x, axis=axis) + return ndarray._array(np.squeeze._implementation(x._array, axis=axis)) def stack(arrays: Tuple[array], /, *, axis: int = 0) -> array: """ @@ -59,4 +61,5 @@ def stack(arrays: Tuple[array], /, *, axis: int = 0) -> array: See its docstring for more information. """ - return np.stack._implementation(arrays, axis=axis) + arrays = tuple(a._array for a in arrays) + return ndarray._array(np.stack._implementation(arrays, axis=axis)) -- cgit v1.2.1 From 63be085194ddf9d2d8fc32a0ccbe30936c78d870 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 2 Mar 2021 16:10:01 -0700 Subject: Only allow __bool__, __int__, and __float__ on arrays with shape () --- numpy/_array_api/_array_object.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 23f8ab333..32a7bc9a8 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -83,6 +83,9 @@ class ndarray: """ Performs the operation __bool__. """ + # Note: This is an error here. + if x._array.shape != (): + raise TypeError("bool is only allowed on arrays with shape ()") res = x._array.__bool__() return res @@ -111,6 +114,9 @@ class ndarray: """ Performs the operation __float__. """ + # Note: This is an error here. + if x._array.shape != (): + raise TypeError("bool is only allowed on arrays with shape ()") res = x._array.__float__() return res @@ -146,6 +152,9 @@ class ndarray: """ Performs the operation __int__. """ + # Note: This is an error here. + if x._array.shape != (): + raise TypeError("bool is only allowed on arrays with shape ()") res = x._array.__int__() return res -- cgit v1.2.1 From 7132764661b01e2f15a66d7c39d74ad4b2d434a9 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 2 Mar 2021 16:29:07 -0700 Subject: Remove _implementation from the array API functions As discussed at https://mail.python.org/pipermail/numpy-discussion/2021-February/081541.html, _implementation is not as useful for the array API module as previously thought. --- numpy/_array_api/_creation_functions.py | 8 ++++---- numpy/_array_api/_elementwise_functions.py | 2 +- numpy/_array_api/_linear_algebra_functions.py | 10 +++++----- numpy/_array_api/_manipulation_functions.py | 12 ++++++------ numpy/_array_api/_searching_functions.py | 8 ++++---- numpy/_array_api/_set_functions.py | 2 +- numpy/_array_api/_sorting_functions.py | 4 ++-- numpy/_array_api/_statistical_functions.py | 14 +++++++------- numpy/_array_api/_utility_functions.py | 4 ++-- 9 files changed, 32 insertions(+), 32 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 888f24558..5b73c8f5c 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -62,7 +62,7 @@ def empty_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[d if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return ndarray._new(np.empty_like._implementation(x._array, dtype=dtype)) + return ndarray._new(np.empty_like(x._array, dtype=dtype)) def eye(N: int, /, *, M: Optional[int] = None, k: Optional[int] = 0, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ @@ -109,7 +109,7 @@ def full_like(x: array, fill_value: Union[int, float], /, *, dtype: Optional[dty if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - res = np.full_like._implementation(x._array, fill_value, dtype=dtype) + res = np.full_like(x._array, fill_value, dtype=dtype) if res.dtype not in _all_dtypes: # This will happen if the fill value is not something that NumPy # coerces to one of the acceptable dtypes. @@ -150,7 +150,7 @@ def ones_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[de if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return ndarray._new(np.ones_like._implementation(x._array, dtype=dtype)) + return ndarray._new(np.ones_like(x._array, dtype=dtype)) def zeros(shape: Union[int, Tuple[int, ...]], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ @@ -174,4 +174,4 @@ def zeros_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[d if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return ndarray._new(np.zeros_like._implementation(x._array, dtype=dtype)) + return ndarray._new(np.zeros_like(x._array, dtype=dtype)) diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index b48a38c3d..9efe17e83 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -482,7 +482,7 @@ def round(x: array, /) -> array: """ if x.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in round') - return ndarray._new(np.round._implementation(x._array)) + return ndarray._new(np.round(x._array)) def sign(x: array, /) -> array: """ diff --git a/numpy/_array_api/_linear_algebra_functions.py b/numpy/_array_api/_linear_algebra_functions.py index e23800e0f..ec67f9c0b 100644 --- a/numpy/_array_api/_linear_algebra_functions.py +++ b/numpy/_array_api/_linear_algebra_functions.py @@ -18,7 +18,7 @@ def cross(x1: array, x2: array, /, *, axis: int = -1) -> array: See its docstring for more information. """ - return np.cross._implementation(x1, x2, axis=axis) + return np.cross(x1, x2, axis=axis) def det(x: array, /) -> array: """ @@ -35,7 +35,7 @@ def diagonal(x: array, /, *, axis1: int = 0, axis2: int = 1, offset: int = 0) -> See its docstring for more information. """ - return np.diagonal._implementation(x, axis1=axis1, axis2=axis2, offset=offset) + return np.diagonal(x, axis1=axis1, axis2=axis2, offset=offset) # def dot(): # """ @@ -128,7 +128,7 @@ def outer(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.outer._implementation(x1, x2) + return np.outer(x1, x2) # def pinv(): # """ @@ -176,7 +176,7 @@ def trace(x: array, /, *, axis1: int = 0, axis2: int = 1, offset: int = 0) -> ar See its docstring for more information. """ - return np.asarray(np.trace._implementation(x, axis1=axis1, axis2=axis2, offset=offset)) + return np.asarray(np.trace(x, axis1=axis1, axis2=axis2, offset=offset)) def transpose(x: array, /, *, axes: Optional[Tuple[int, ...]] = None) -> array: """ @@ -184,4 +184,4 @@ def transpose(x: array, /, *, axes: Optional[Tuple[int, ...]] = None) -> array: See its docstring for more information. """ - return np.transpose._implementation(x, axes=axes) + return np.transpose(x, axes=axes) diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index 413dbb1b1..1631a924f 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -21,7 +21,7 @@ def expand_dims(x: array, axis: int, /) -> array: See its docstring for more information. """ - return ndarray._new(np.expand_dims._implementation(x._array, axis)) + return ndarray._new(np.expand_dims(x._array, axis)) def flip(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: """ @@ -29,7 +29,7 @@ def flip(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> See its docstring for more information. """ - return ndarray._new(np.flip._implementation(x._array, axis=axis)) + return ndarray._new(np.flip(x._array, axis=axis)) def reshape(x: array, shape: Tuple[int, ...], /) -> array: """ @@ -37,7 +37,7 @@ def reshape(x: array, shape: Tuple[int, ...], /) -> array: See its docstring for more information. """ - return ndarray._new(np.reshape._implementation(x._array, shape)) + return ndarray._new(np.reshape(x._array, shape)) def roll(x: array, shift: Union[int, Tuple[int, ...]], /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: """ @@ -45,7 +45,7 @@ def roll(x: array, shift: Union[int, Tuple[int, ...]], /, *, axis: Optional[Unio See its docstring for more information. """ - return ndarray._new(np.roll._implementation(x._array, shift, axis=axis)) + return ndarray._new(np.roll(x._array, shift, axis=axis)) def squeeze(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: """ @@ -53,7 +53,7 @@ def squeeze(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) See its docstring for more information. """ - return ndarray._array(np.squeeze._implementation(x._array, axis=axis)) + return ndarray._array(np.squeeze(x._array, axis=axis)) def stack(arrays: Tuple[array], /, *, axis: int = 0) -> array: """ @@ -62,4 +62,4 @@ def stack(arrays: Tuple[array], /, *, axis: int = 0) -> array: See its docstring for more information. """ arrays = tuple(a._array for a in arrays) - return ndarray._array(np.stack._implementation(arrays, axis=axis)) + return ndarray._array(np.stack(arrays, axis=axis)) diff --git a/numpy/_array_api/_searching_functions.py b/numpy/_array_api/_searching_functions.py index 77e4710e5..d5128cca9 100644 --- a/numpy/_array_api/_searching_functions.py +++ b/numpy/_array_api/_searching_functions.py @@ -11,7 +11,7 @@ def argmax(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: See its docstring for more information. """ # Note: this currently fails as np.argmax does not implement keepdims - return np.asarray(np.argmax._implementation(x, axis=axis, keepdims=keepdims)) + return np.asarray(np.argmax(x, axis=axis, keepdims=keepdims)) def argmin(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: """ @@ -20,7 +20,7 @@ def argmin(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: See its docstring for more information. """ # Note: this currently fails as np.argmin does not implement keepdims - return np.asarray(np.argmin._implementation(x, axis=axis, keepdims=keepdims)) + return np.asarray(np.argmin(x, axis=axis, keepdims=keepdims)) def nonzero(x: array, /) -> Tuple[array, ...]: """ @@ -28,7 +28,7 @@ def nonzero(x: array, /) -> Tuple[array, ...]: See its docstring for more information. """ - return np.nonzero._implementation(x) + return np.nonzero(x) def where(condition: array, x1: array, x2: array, /) -> array: """ @@ -36,4 +36,4 @@ def where(condition: array, x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.where._implementation(condition, x1, x2) + return np.where(condition, x1, x2) diff --git a/numpy/_array_api/_set_functions.py b/numpy/_array_api/_set_functions.py index 4dfc215a7..91927a3a0 100644 --- a/numpy/_array_api/_set_functions.py +++ b/numpy/_array_api/_set_functions.py @@ -10,4 +10,4 @@ def unique(x: array, /, *, return_counts: bool = False, return_index: bool = Fal See its docstring for more information. """ - return np.unique._implementation(x, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse) + return np.unique(x, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse) diff --git a/numpy/_array_api/_sorting_functions.py b/numpy/_array_api/_sorting_functions.py index 17316b552..cddfd1598 100644 --- a/numpy/_array_api/_sorting_functions.py +++ b/numpy/_array_api/_sorting_functions.py @@ -12,7 +12,7 @@ def argsort(x: array, /, *, axis: int = -1, descending: bool = False, stable: bo """ # Note: this keyword argument is different, and the default is different. kind = 'stable' if stable else 'quicksort' - res = np.argsort._implementation(x, axis=axis, kind=kind) + res = np.argsort(x, axis=axis, kind=kind) if descending: res = np.flip(res, axis=axis) return res @@ -25,7 +25,7 @@ def sort(x: array, /, *, axis: int = -1, descending: bool = False, stable: bool """ # Note: this keyword argument is different, and the default is different. kind = 'stable' if stable else 'quicksort' - res = np.sort._implementation(x, axis=axis, kind=kind) + res = np.sort(x, axis=axis, kind=kind) if descending: res = np.flip(res, axis=axis) return res diff --git a/numpy/_array_api/_statistical_functions.py b/numpy/_array_api/_statistical_functions.py index 79bc125dc..e62410d01 100644 --- a/numpy/_array_api/_statistical_functions.py +++ b/numpy/_array_api/_statistical_functions.py @@ -5,24 +5,24 @@ from ._types import Optional, Tuple, Union, array import numpy as np def max(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.max._implementation(x, axis=axis, keepdims=keepdims) + return np.max(x, axis=axis, keepdims=keepdims) def mean(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.asarray(np.mean._implementation(x, axis=axis, keepdims=keepdims)) + return np.asarray(np.mean(x, axis=axis, keepdims=keepdims)) def min(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.min._implementation(x, axis=axis, keepdims=keepdims) + return np.min(x, axis=axis, keepdims=keepdims) def prod(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.asarray(np.prod._implementation(x, axis=axis, keepdims=keepdims)) + return np.asarray(np.prod(x, axis=axis, keepdims=keepdims)) def std(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> array: # Note: the keyword argument correction is different here - return np.asarray(np.std._implementation(x, axis=axis, ddof=correction, keepdims=keepdims)) + return np.asarray(np.std(x, axis=axis, ddof=correction, keepdims=keepdims)) def sum(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.asarray(np.sum._implementation(x, axis=axis, keepdims=keepdims)) + return np.asarray(np.sum(x, axis=axis, keepdims=keepdims)) def var(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> array: # Note: the keyword argument correction is different here - return np.asarray(np.var._implementation(x, axis=axis, ddof=correction, keepdims=keepdims)) + return np.asarray(np.var(x, axis=axis, ddof=correction, keepdims=keepdims)) diff --git a/numpy/_array_api/_utility_functions.py b/numpy/_array_api/_utility_functions.py index 7e1d6ec6e..51a04dc8b 100644 --- a/numpy/_array_api/_utility_functions.py +++ b/numpy/_array_api/_utility_functions.py @@ -10,7 +10,7 @@ def all(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keep See its docstring for more information. """ - return np.asarray(np.all._implementation(x, axis=axis, keepdims=keepdims)) + return np.asarray(np.all(x, axis=axis, keepdims=keepdims)) def any(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: """ @@ -18,4 +18,4 @@ def any(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keep See its docstring for more information. """ - return np.asarray(np.any._implementation(x, axis=axis, keepdims=keepdims)) + return np.asarray(np.any(x, axis=axis, keepdims=keepdims)) -- cgit v1.2.1 From 58c2a996afd13f729ec5d2aed77151c8e799548b Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 2 Mar 2021 16:57:03 -0700 Subject: Make sure the array API ndarray object cannot wrap an array scalar --- numpy/_array_api/_array_object.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 32a7bc9a8..b78405860 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -19,6 +19,8 @@ from enum import IntEnum from ._types import Optional, PyCapsule, Tuple, Union, array from ._creation_functions import asarray +import numpy as np + class ndarray: # Use a custom constructor instead of __init__, as manually initializing # this class is not supported API. @@ -34,6 +36,10 @@ class ndarray: """ obj = super().__new__(cls) + # Note: The spec does not have array scalars, only shape () arrays. + if isinstance(x, np.generic): + # x[...] converts an array scalar to a shape () array. + x = x[...] obj._array = x return obj -- cgit v1.2.1 From cdd6bbcdf260a4d6947901604dc8dd64c864c8d4 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 5 Mar 2021 14:35:35 -0700 Subject: Support the ndarray object in the remaining array API functions --- numpy/_array_api/_data_type_functions.py | 4 +++- numpy/_array_api/_linear_algebra_functions.py | 17 +++++++++-------- numpy/_array_api/_searching_functions.py | 9 +++++---- numpy/_array_api/_set_functions.py | 3 ++- numpy/_array_api/_sorting_functions.py | 9 +++++---- numpy/_array_api/_statistical_functions.py | 15 ++++++++------- numpy/_array_api/_utility_functions.py | 5 +++-- 7 files changed, 35 insertions(+), 27 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_data_type_functions.py b/numpy/_array_api/_data_type_functions.py index 18f741ebd..9e4dcba95 100644 --- a/numpy/_array_api/_data_type_functions.py +++ b/numpy/_array_api/_data_type_functions.py @@ -1,6 +1,8 @@ from __future__ import annotations from ._types import Union, array, dtype +from ._array_object import ndarray + from collections.abc import Sequence import numpy as np @@ -27,4 +29,4 @@ def result_type(*arrays_and_dtypes: Sequence[Union[array, dtype]]) -> dtype: See its docstring for more information. """ - return np.result_type(*arrays_and_dtypes) + return np.result_type(*(a._array if isinstance(a, ndarray) else a for a in arrays_and_dtypes)) diff --git a/numpy/_array_api/_linear_algebra_functions.py b/numpy/_array_api/_linear_algebra_functions.py index ec67f9c0b..95ed00fd5 100644 --- a/numpy/_array_api/_linear_algebra_functions.py +++ b/numpy/_array_api/_linear_algebra_functions.py @@ -1,6 +1,7 @@ from __future__ import annotations from ._types import Literal, Optional, Tuple, Union, array +from ._array_object import ndarray import numpy as np @@ -18,7 +19,7 @@ def cross(x1: array, x2: array, /, *, axis: int = -1) -> array: See its docstring for more information. """ - return np.cross(x1, x2, axis=axis) + return ndarray._new(np.cross(x1._array, x2._array, axis=axis)) def det(x: array, /) -> array: """ @@ -27,7 +28,7 @@ def det(x: array, /) -> array: See its docstring for more information. """ # Note: this function is being imported from a nondefault namespace - return np.linalg.det(x) + return ndarray._new(np.linalg.det(x._array)) def diagonal(x: array, /, *, axis1: int = 0, axis2: int = 1, offset: int = 0) -> array: """ @@ -35,7 +36,7 @@ def diagonal(x: array, /, *, axis1: int = 0, axis2: int = 1, offset: int = 0) -> See its docstring for more information. """ - return np.diagonal(x, axis1=axis1, axis2=axis2, offset=offset) + return ndarray._new(np.diagonal(x._array, axis1=axis1, axis2=axis2, offset=offset)) # def dot(): # """ @@ -76,7 +77,7 @@ def inv(x: array, /) -> array: See its docstring for more information. """ # Note: this function is being imported from a nondefault namespace - return np.linalg.inv(x) + return ndarray._new(np.linalg.inv(x._array)) # def lstsq(): # """ @@ -120,7 +121,7 @@ def norm(x: array, /, *, axis: Optional[Union[int, Tuple[int, int]]] = None, kee if axis == None and x.ndim > 2: x = x.flatten() # Note: this function is being imported from a nondefault namespace - return np.linalg.norm(x, axis=axis, keepdims=keepdims, ord=ord) + return ndarray._new(np.linalg.norm(x._array, axis=axis, keepdims=keepdims, ord=ord)) def outer(x1: array, x2: array, /) -> array: """ @@ -128,7 +129,7 @@ def outer(x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.outer(x1, x2) + return ndarray._new(np.outer(x1._array, x2._array)) # def pinv(): # """ @@ -176,7 +177,7 @@ def trace(x: array, /, *, axis1: int = 0, axis2: int = 1, offset: int = 0) -> ar See its docstring for more information. """ - return np.asarray(np.trace(x, axis1=axis1, axis2=axis2, offset=offset)) + return ndarray._new(np.asarray(np.trace(x._array, axis1=axis1, axis2=axis2, offset=offset))) def transpose(x: array, /, *, axes: Optional[Tuple[int, ...]] = None) -> array: """ @@ -184,4 +185,4 @@ def transpose(x: array, /, *, axes: Optional[Tuple[int, ...]] = None) -> array: See its docstring for more information. """ - return np.transpose(x, axes=axes) + return ndarray._new(np.transpose(x._array, axes=axes)) diff --git a/numpy/_array_api/_searching_functions.py b/numpy/_array_api/_searching_functions.py index d5128cca9..44e5b2775 100644 --- a/numpy/_array_api/_searching_functions.py +++ b/numpy/_array_api/_searching_functions.py @@ -1,6 +1,7 @@ from __future__ import annotations from ._types import Tuple, array +from ._array_object import ndarray import numpy as np @@ -11,7 +12,7 @@ def argmax(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: See its docstring for more information. """ # Note: this currently fails as np.argmax does not implement keepdims - return np.asarray(np.argmax(x, axis=axis, keepdims=keepdims)) + return ndarray._new(np.asarray(np.argmax(x._array, axis=axis, keepdims=keepdims))) def argmin(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: """ @@ -20,7 +21,7 @@ def argmin(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: See its docstring for more information. """ # Note: this currently fails as np.argmin does not implement keepdims - return np.asarray(np.argmin(x, axis=axis, keepdims=keepdims)) + return ndarray._new(np.asarray(np.argmin(x._array, axis=axis, keepdims=keepdims))) def nonzero(x: array, /) -> Tuple[array, ...]: """ @@ -28,7 +29,7 @@ def nonzero(x: array, /) -> Tuple[array, ...]: See its docstring for more information. """ - return np.nonzero(x) + return ndarray._new(np.nonzero(x._array)) def where(condition: array, x1: array, x2: array, /) -> array: """ @@ -36,4 +37,4 @@ def where(condition: array, x1: array, x2: array, /) -> array: See its docstring for more information. """ - return np.where(condition, x1, x2) + return ndarray._new(np.where(condition._array, x1._array, x2._array)) diff --git a/numpy/_array_api/_set_functions.py b/numpy/_array_api/_set_functions.py index 91927a3a0..f5cd6d324 100644 --- a/numpy/_array_api/_set_functions.py +++ b/numpy/_array_api/_set_functions.py @@ -1,6 +1,7 @@ from __future__ import annotations from ._types import Tuple, Union, array +from ._array_object import ndarray import numpy as np @@ -10,4 +11,4 @@ def unique(x: array, /, *, return_counts: bool = False, return_index: bool = Fal See its docstring for more information. """ - return np.unique(x, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse) + return ndarray._new(np.unique(x._array, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse)) diff --git a/numpy/_array_api/_sorting_functions.py b/numpy/_array_api/_sorting_functions.py index cddfd1598..2e054b03a 100644 --- a/numpy/_array_api/_sorting_functions.py +++ b/numpy/_array_api/_sorting_functions.py @@ -1,6 +1,7 @@ from __future__ import annotations from ._types import array +from ._array_object import ndarray import numpy as np @@ -12,10 +13,10 @@ def argsort(x: array, /, *, axis: int = -1, descending: bool = False, stable: bo """ # Note: this keyword argument is different, and the default is different. kind = 'stable' if stable else 'quicksort' - res = np.argsort(x, axis=axis, kind=kind) + res = np.argsort(x._array, axis=axis, kind=kind) if descending: res = np.flip(res, axis=axis) - return res + return ndarray._new(res) def sort(x: array, /, *, axis: int = -1, descending: bool = False, stable: bool = True) -> array: """ @@ -25,7 +26,7 @@ def sort(x: array, /, *, axis: int = -1, descending: bool = False, stable: bool """ # Note: this keyword argument is different, and the default is different. kind = 'stable' if stable else 'quicksort' - res = np.sort(x, axis=axis, kind=kind) + res = np.sort(x._array, axis=axis, kind=kind) if descending: res = np.flip(res, axis=axis) - return res + return ndarray._new(res) diff --git a/numpy/_array_api/_statistical_functions.py b/numpy/_array_api/_statistical_functions.py index e62410d01..fa3551248 100644 --- a/numpy/_array_api/_statistical_functions.py +++ b/numpy/_array_api/_statistical_functions.py @@ -1,28 +1,29 @@ from __future__ import annotations from ._types import Optional, Tuple, Union, array +from ._array_object import ndarray import numpy as np def max(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.max(x, axis=axis, keepdims=keepdims) + return ndarray._new(np.max(x._array, axis=axis, keepdims=keepdims)) def mean(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.asarray(np.mean(x, axis=axis, keepdims=keepdims)) + return ndarray._new(np.asarray(np.mean(x._array, axis=axis, keepdims=keepdims))) def min(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.min(x, axis=axis, keepdims=keepdims) + return ndarray._new(np.min(x._array, axis=axis, keepdims=keepdims)) def prod(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.asarray(np.prod(x, axis=axis, keepdims=keepdims)) + return ndarray._new(np.asarray(np.prod(x._array, axis=axis, keepdims=keepdims))) def std(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> array: # Note: the keyword argument correction is different here - return np.asarray(np.std(x, axis=axis, ddof=correction, keepdims=keepdims)) + return ndarray._new(np.asarray(np.std(x._array, axis=axis, ddof=correction, keepdims=keepdims))) def sum(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: - return np.asarray(np.sum(x, axis=axis, keepdims=keepdims)) + return ndarray._new(np.asarray(np.sum(x._array, axis=axis, keepdims=keepdims))) def var(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> array: # Note: the keyword argument correction is different here - return np.asarray(np.var(x, axis=axis, ddof=correction, keepdims=keepdims)) + return ndarray._new(np.asarray(np.var(x._array, axis=axis, ddof=correction, keepdims=keepdims))) diff --git a/numpy/_array_api/_utility_functions.py b/numpy/_array_api/_utility_functions.py index 51a04dc8b..c4721ad7e 100644 --- a/numpy/_array_api/_utility_functions.py +++ b/numpy/_array_api/_utility_functions.py @@ -1,6 +1,7 @@ from __future__ import annotations from ._types import Optional, Tuple, Union, array +from ._array_object import ndarray import numpy as np @@ -10,7 +11,7 @@ def all(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keep See its docstring for more information. """ - return np.asarray(np.all(x, axis=axis, keepdims=keepdims)) + return ndarray._new(np.asarray(np.all(x._array, axis=axis, keepdims=keepdims))) def any(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: """ @@ -18,4 +19,4 @@ def any(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keep See its docstring for more information. """ - return np.asarray(np.any(x, axis=axis, keepdims=keepdims)) + return ndarray._new(np.asarray(np.any(x._array, axis=axis, keepdims=keepdims))) -- cgit v1.2.1 From 1ccbe680e24f2d2254ee4cd5053bb10859b22d1d Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 9 Mar 2021 15:47:07 -0700 Subject: Only allow indices that are required by the spec in the array API namespace The private function _validate_indices describes the cases that are disallowed. This functionality should be tested (it isn't yet), as the array API test suite will only test the cases that are allowed, not that non-required cases are rejected. --- numpy/_array_api/_array_object.py | 115 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index b78405860..64ce740f0 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -15,9 +15,11 @@ of ndarray. from __future__ import annotations +import operator from enum import IntEnum from ._types import Optional, PyCapsule, Tuple, Union, array from ._creation_functions import asarray +from ._dtypes import _boolean_dtypes, _integer_dtypes import numpy as np @@ -140,10 +142,120 @@ class ndarray: res = x1._array.__ge__(asarray(x2)._array) return x1.__class__._new(res) + # Note: A large fraction of allowed indices are disallowed here (see the + # docstring below) + @staticmethod + def _validate_index(key, shape): + """ + Validate an index according to the array API. + + The array API specification only requires a subset of indices that are + supported by NumPy. This function will reject any index that is + allowed by NumPy but not required by the array API specification. We + always raise ``IndexError`` on such indices (the spec does not require + any specific behavior on them, but this makes the NumPy array API + namespace a minimal implementation of the spec). + + This function either raises IndexError if the index ``key`` is + invalid, or a new key to be used in place of ``key`` in indexing. It + only raises ``IndexError`` on indices that are not already rejected by + NumPy, as NumPy will already raise the appropriate error on such + indices. ``shape`` may be None, in which case, only cases that are + independent of the array shape are checked. + + The following cases are allowed by NumPy, but not specified by the array + API specification: + + - The start and stop of a slice may not be out of bounds. In + particular, for a slice ``i:j:k`` on an axis of size ``n``, only the + following are allowed: + + - ``i`` or ``j`` omitted (``None``). + - ``-n <= i <= max(0, n - 1)``. + - For ``k > 0`` or ``k`` omitted (``None``), ``-n <= j <= n``. + - For ``k < 0``, ``-n - 1 <= j <= max(0, n - 1)``. + + - Boolean array indices are not allowed as part of a larger tuple + index. + + - Integer array indices are not allowed (with the exception of shape + () arrays, which are treated the same as scalars). + + Additionally, it should be noted that indices that would return a + scalar in NumPy will return a shape () array. Array scalars are not allowed + in the specification, only shape () arrays. This is done in the + ``ndarray._new`` constructor, not this function. + + """ + if isinstance(key, slice): + if shape is None: + return key + if shape == (): + return key + size = shape[0] + # Ensure invalid slice entries are passed through. + if key.start is not None: + try: + operator.index(key.start) + except TypeError: + return key + if not (-size <= key.start <= max(0, size - 1)): + raise IndexError("Slices with out-of-bounds start are not allowed in the array API namespace") + if key.stop is not None: + try: + operator.index(key.stop) + except TypeError: + return key + step = 1 if key.step is None else key.step + if (step > 0 and not (-size <= key.stop <= size) + or step < 0 and not (-size - 1 <= key.stop <= max(0, size - 1))): + raise IndexError("Slices with out-of-bounds stop are not allowed in the array API namespace") + return key + + elif isinstance(key, tuple): + key = tuple(ndarray._validate_index(idx, None) for idx in key) + + for idx in key: + if isinstance(idx, np.ndarray) and idx.dtype in _boolean_dtypes or isinstance(idx, (bool, np.bool_)): + if len(key) == 1: + return key + raise IndexError("Boolean array indices combined with other indices are not allowed in the array API namespace") + + if shape is None: + return key + n_ellipsis = key.count(...) + if n_ellipsis > 1: + return key + ellipsis_i = key.index(...) if n_ellipsis else len(key) + + for idx, size in list(zip(key[:ellipsis_i], shape)) + list(zip(key[:ellipsis_i:-1], shape[:ellipsis_i:-1])): + ndarray._validate_index(idx, (size,)) + return key + elif isinstance(key, bool): + return key + elif isinstance(key, ndarray): + if key.dtype in _integer_dtypes: + if key.shape != (): + raise IndexError("Integer array indices with shape != () are not allowed in the array API namespace") + return key._array + elif key is Ellipsis: + return key + elif key is None: + raise IndexError("newaxis indices are not allowed in the array API namespace") + try: + return operator.index(key) + except TypeError: + # Note: This also omits boolean arrays that are not already in + # ndarray() form, like a list of booleans. + raise IndexError("Only integers, slices (`:`), ellipsis (`...`), and boolean arrays are valid indices in the array API namespace") + def __getitem__(x: array, key: Union[int, slice, Tuple[Union[int, slice], ...], array], /) -> array: """ Performs the operation __getitem__. """ + # Note: Only indices required by the spec are allowed. See the + # docstring of _validate_index + key = x._validate_index(key, x.shape) res = x._array.__getitem__(key) return x.__class__._new(res) @@ -266,6 +378,9 @@ class ndarray: """ Performs the operation __setitem__. """ + # Note: Only indices required by the spec are allowed. See the + # docstring of _validate_index + key = x._validate_index(key, x.shape) res = x._array.__setitem__(key, asarray(value)._array) return x.__class__._new(res) -- cgit v1.2.1 From be45fa10e993c858e559b9fb0556e18a5e355595 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 9 Mar 2021 15:54:53 -0700 Subject: Update the state of the array API in the __init__.py docstring --- numpy/_array_api/__init__.py | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/__init__.py b/numpy/_array_api/__init__.py index 43b2d4ce3..880deb613 100644 --- a/numpy/_array_api/__init__.py +++ b/numpy/_array_api/__init__.py @@ -58,20 +58,32 @@ A few notes about the current state of this submodule: guaranteed to give a comprehensive coverage of the spec. Therefore, those reviewing this submodule should refer to the standard documents themselves. +- There is a custom array object, numpy._array_api.ndarray, which is returned + by all functions in this module. All functions in the array API namespace + implicitly assume that they will only receive this object as input. The only + way to create instances of this object is to use one of the array creation + functions. It does not have a public constructor on the object itself. The + object is a small wrapper Python class around numpy.ndarray. The main + purpose of it is to restrict the namespace of the array object to only those + methods that are required by the spec, as well as to limit/change certain + behavior that differs in the spec. In particular: + + - Indexing: Only a subset of indices supported by NumPy are required by the + spec. The ndarray object restricts indexing to only allow those types of + indices that are required by the spec. See the docstring of the + numpy._array_api.ndarray._validate_indices helper function for more + information. + + - Type promotion: Some type promotion rules are different in the spec. In + particular, the spec does not have any value-based casing. Note that the + code to correct the type promotion rules on numpy._array_api.ndarray is + not yet implemented. + - All functions include type annotations, corresponding to those given in the spec (see _types.py for definitions of the types 'array', 'device', and 'dtype'). These do not currently fully pass mypy due to some limitations in mypy. -- The array object is not modified at all. That means that functions return - np.ndarray, which has methods and attributes that aren't part of the spec. - Modifying/subclassing ndarray for the purposes of the array API namespace - was considered too complex for this initial implementation. - -- All functions that would otherwise accept array-like input have been wrapped - to only accept ndarray (with the exception of methods on the array object, - which are not modified). - - All places where the implementations in this submodule are known to deviate from their corresponding functions in NumPy are marked with "# Note" comments. Reviewers should make note of these comments. -- cgit v1.2.1 From 0ac7de9a670517a46ee82670d3a790dafbb6071c Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 9 Mar 2021 17:01:41 -0700 Subject: Implement __array_namespace__ on the array API ndarray object --- numpy/_array_api/_array_object.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 64ce740f0..84cbf3527 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -87,6 +87,12 @@ class ndarray: res = x1._array.__and__(asarray(x2)._array) return x1.__class__._new(res) + def __array_namespace__(self, /, *, api_version=None): + if api_version is not None: + raise ValueError("Unrecognized array API version") + from numpy import _array_api + return _array_api + def __bool__(x: array, /) -> bool: """ Performs the operation __bool__. -- cgit v1.2.1 From 9e50716df9c7b4b59a11fda8fc99835f070cc152 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 9 Mar 2021 17:18:46 -0700 Subject: Use 'self' and 'other' for the array API ndarray method parameter names --- numpy/_array_api/_array_object.py | 354 +++++++++++++++++++------------------- 1 file changed, 177 insertions(+), 177 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 84cbf3527..247194017 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -52,40 +52,40 @@ class ndarray: # These functions are not required by the spec, but are implemented for # the sake of usability. - def __str__(x: array, /) -> str: + def __str__(self: array, /) -> str: """ Performs the operation __str__. """ - return x._array.__str__().replace('array', 'ndarray') + return self._array.__str__().replace('array', 'ndarray') - def __repr__(x: array, /) -> str: + def __repr__(self: array, /) -> str: """ Performs the operation __repr__. """ - return x._array.__repr__().replace('array', 'ndarray') + return self._array.__repr__().replace('array', 'ndarray') # Everything below this is required by the spec. - def __abs__(x: array, /) -> array: + def __abs__(self: array, /) -> array: """ Performs the operation __abs__. """ - res = x._array.__abs__() - return x.__class__._new(res) + res = self._array.__abs__() + return self.__class__._new(res) - def __add__(x1: array, x2: array, /) -> array: + def __add__(self: array, other: array, /) -> array: """ Performs the operation __add__. """ - res = x1._array.__add__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__add__(asarray(other)._array) + return self.__class__._new(res) - def __and__(x1: array, x2: array, /) -> array: + def __and__(self: array, other: array, /) -> array: """ Performs the operation __and__. """ - res = x1._array.__and__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__and__(asarray(other)._array) + return self.__class__._new(res) def __array_namespace__(self, /, *, api_version=None): if api_version is not None: @@ -93,60 +93,60 @@ class ndarray: from numpy import _array_api return _array_api - def __bool__(x: array, /) -> bool: + def __bool__(self: array, /) -> bool: """ Performs the operation __bool__. """ # Note: This is an error here. - if x._array.shape != (): + if self._array.shape != (): raise TypeError("bool is only allowed on arrays with shape ()") - res = x._array.__bool__() + res = self._array.__bool__() return res - def __dlpack__(x: array, /, *, stream: Optional[int] = None) -> PyCapsule: + def __dlpack__(self: array, /, *, stream: Optional[int] = None) -> PyCapsule: """ Performs the operation __dlpack__. """ - res = x._array.__dlpack__(stream=None) - return x.__class__._new(res) + res = self._array.__dlpack__(stream=None) + return self.__class__._new(res) - def __dlpack_device__(x: array, /) -> Tuple[IntEnum, int]: + def __dlpack_device__(self: array, /) -> Tuple[IntEnum, int]: """ Performs the operation __dlpack_device__. """ - res = x._array.__dlpack_device__() - return x.__class__._new(res) + res = self._array.__dlpack_device__() + return self.__class__._new(res) - def __eq__(x1: array, x2: array, /) -> array: + def __eq__(self: array, other: array, /) -> array: """ Performs the operation __eq__. """ - res = x1._array.__eq__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__eq__(asarray(other)._array) + return self.__class__._new(res) - def __float__(x: array, /) -> float: + def __float__(self: array, /) -> float: """ Performs the operation __float__. """ # Note: This is an error here. - if x._array.shape != (): + if self._array.shape != (): raise TypeError("bool is only allowed on arrays with shape ()") - res = x._array.__float__() + res = self._array.__float__() return res - def __floordiv__(x1: array, x2: array, /) -> array: + def __floordiv__(self: array, other: array, /) -> array: """ Performs the operation __floordiv__. """ - res = x1._array.__floordiv__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__floordiv__(asarray(other)._array) + return self.__class__._new(res) - def __ge__(x1: array, x2: array, /) -> array: + def __ge__(self: array, other: array, /) -> array: """ Performs the operation __ge__. """ - res = x1._array.__ge__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__ge__(asarray(other)._array) + return self.__class__._new(res) # Note: A large fraction of allowed indices are disallowed here (see the # docstring below) @@ -255,343 +255,343 @@ class ndarray: # ndarray() form, like a list of booleans. raise IndexError("Only integers, slices (`:`), ellipsis (`...`), and boolean arrays are valid indices in the array API namespace") - def __getitem__(x: array, key: Union[int, slice, Tuple[Union[int, slice], ...], array], /) -> array: + def __getitem__(self: array, key: Union[int, slice, Tuple[Union[int, slice], ...], array], /) -> array: """ Performs the operation __getitem__. """ # Note: Only indices required by the spec are allowed. See the # docstring of _validate_index - key = x._validate_index(key, x.shape) - res = x._array.__getitem__(key) - return x.__class__._new(res) + key = self._validate_index(key, self.shape) + res = self._array.__getitem__(key) + return self.__class__._new(res) - def __gt__(x1: array, x2: array, /) -> array: + def __gt__(self: array, other: array, /) -> array: """ Performs the operation __gt__. """ - res = x1._array.__gt__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__gt__(asarray(other)._array) + return self.__class__._new(res) - def __int__(x: array, /) -> int: + def __int__(self: array, /) -> int: """ Performs the operation __int__. """ # Note: This is an error here. - if x._array.shape != (): + if self._array.shape != (): raise TypeError("bool is only allowed on arrays with shape ()") - res = x._array.__int__() + res = self._array.__int__() return res - def __invert__(x: array, /) -> array: + def __invert__(self: array, /) -> array: """ Performs the operation __invert__. """ - res = x._array.__invert__() - return x.__class__._new(res) + res = self._array.__invert__() + return self.__class__._new(res) - def __le__(x1: array, x2: array, /) -> array: + def __le__(self: array, other: array, /) -> array: """ Performs the operation __le__. """ - res = x1._array.__le__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__le__(asarray(other)._array) + return self.__class__._new(res) - def __len__(x, /): + def __len__(self, /): """ Performs the operation __len__. """ - res = x._array.__len__() - return x.__class__._new(res) + res = self._array.__len__() + return self.__class__._new(res) - def __lshift__(x1: array, x2: array, /) -> array: + def __lshift__(self: array, other: array, /) -> array: """ Performs the operation __lshift__. """ - res = x1._array.__lshift__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__lshift__(asarray(other)._array) + return self.__class__._new(res) - def __lt__(x1: array, x2: array, /) -> array: + def __lt__(self: array, other: array, /) -> array: """ Performs the operation __lt__. """ - res = x1._array.__lt__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__lt__(asarray(other)._array) + return self.__class__._new(res) - def __matmul__(x1: array, x2: array, /) -> array: + def __matmul__(self: array, other: array, /) -> array: """ Performs the operation __matmul__. """ - res = x1._array.__matmul__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__matmul__(asarray(other)._array) + return self.__class__._new(res) - def __mod__(x1: array, x2: array, /) -> array: + def __mod__(self: array, other: array, /) -> array: """ Performs the operation __mod__. """ - res = x1._array.__mod__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__mod__(asarray(other)._array) + return self.__class__._new(res) - def __mul__(x1: array, x2: array, /) -> array: + def __mul__(self: array, other: array, /) -> array: """ Performs the operation __mul__. """ - res = x1._array.__mul__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__mul__(asarray(other)._array) + return self.__class__._new(res) - def __ne__(x1: array, x2: array, /) -> array: + def __ne__(self: array, other: array, /) -> array: """ Performs the operation __ne__. """ - res = x1._array.__ne__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__ne__(asarray(other)._array) + return self.__class__._new(res) - def __neg__(x: array, /) -> array: + def __neg__(self: array, /) -> array: """ Performs the operation __neg__. """ - res = x._array.__neg__() - return x.__class__._new(res) + res = self._array.__neg__() + return self.__class__._new(res) - def __or__(x1: array, x2: array, /) -> array: + def __or__(self: array, other: array, /) -> array: """ Performs the operation __or__. """ - res = x1._array.__or__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__or__(asarray(other)._array) + return self.__class__._new(res) - def __pos__(x: array, /) -> array: + def __pos__(self: array, /) -> array: """ Performs the operation __pos__. """ - res = x._array.__pos__() - return x.__class__._new(res) + res = self._array.__pos__() + return self.__class__._new(res) - def __pow__(x1: array, x2: array, /) -> array: + def __pow__(self: array, other: array, /) -> array: """ Performs the operation __pow__. """ - res = x1._array.__pow__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__pow__(asarray(other)._array) + return self.__class__._new(res) - def __rshift__(x1: array, x2: array, /) -> array: + def __rshift__(self: array, other: array, /) -> array: """ Performs the operation __rshift__. """ - res = x1._array.__rshift__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__rshift__(asarray(other)._array) + return self.__class__._new(res) - def __setitem__(x, key, value, /): + def __setitem__(self, key, value, /): """ Performs the operation __setitem__. """ # Note: Only indices required by the spec are allowed. See the # docstring of _validate_index - key = x._validate_index(key, x.shape) - res = x._array.__setitem__(key, asarray(value)._array) - return x.__class__._new(res) + key = self._validate_index(key, self.shape) + res = self._array.__setitem__(key, asarray(value)._array) + return self.__class__._new(res) - def __sub__(x1: array, x2: array, /) -> array: + def __sub__(self: array, other: array, /) -> array: """ Performs the operation __sub__. """ - res = x1._array.__sub__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__sub__(asarray(other)._array) + return self.__class__._new(res) - def __truediv__(x1: array, x2: array, /) -> array: + def __truediv__(self: array, other: array, /) -> array: """ Performs the operation __truediv__. """ - res = x1._array.__truediv__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__truediv__(asarray(other)._array) + return self.__class__._new(res) - def __xor__(x1: array, x2: array, /) -> array: + def __xor__(self: array, other: array, /) -> array: """ Performs the operation __xor__. """ - res = x1._array.__xor__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__xor__(asarray(other)._array) + return self.__class__._new(res) - def __iadd__(x1: array, x2: array, /) -> array: + def __iadd__(self: array, other: array, /) -> array: """ Performs the operation __iadd__. """ - res = x1._array.__iadd__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__iadd__(asarray(other)._array) + return self.__class__._new(res) - def __radd__(x1: array, x2: array, /) -> array: + def __radd__(self: array, other: array, /) -> array: """ Performs the operation __radd__. """ - res = x1._array.__radd__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__radd__(asarray(other)._array) + return self.__class__._new(res) - def __iand__(x1: array, x2: array, /) -> array: + def __iand__(self: array, other: array, /) -> array: """ Performs the operation __iand__. """ - res = x1._array.__iand__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__iand__(asarray(other)._array) + return self.__class__._new(res) - def __rand__(x1: array, x2: array, /) -> array: + def __rand__(self: array, other: array, /) -> array: """ Performs the operation __rand__. """ - res = x1._array.__rand__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__rand__(asarray(other)._array) + return self.__class__._new(res) - def __ifloordiv__(x1: array, x2: array, /) -> array: + def __ifloordiv__(self: array, other: array, /) -> array: """ Performs the operation __ifloordiv__. """ - res = x1._array.__ifloordiv__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__ifloordiv__(asarray(other)._array) + return self.__class__._new(res) - def __rfloordiv__(x1: array, x2: array, /) -> array: + def __rfloordiv__(self: array, other: array, /) -> array: """ Performs the operation __rfloordiv__. """ - res = x1._array.__rfloordiv__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__rfloordiv__(asarray(other)._array) + return self.__class__._new(res) - def __ilshift__(x1: array, x2: array, /) -> array: + def __ilshift__(self: array, other: array, /) -> array: """ Performs the operation __ilshift__. """ - res = x1._array.__ilshift__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__ilshift__(asarray(other)._array) + return self.__class__._new(res) - def __rlshift__(x1: array, x2: array, /) -> array: + def __rlshift__(self: array, other: array, /) -> array: """ Performs the operation __rlshift__. """ - res = x1._array.__rlshift__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__rlshift__(asarray(other)._array) + return self.__class__._new(res) - def __imatmul__(x1: array, x2: array, /) -> array: + def __imatmul__(self: array, other: array, /) -> array: """ Performs the operation __imatmul__. """ - res = x1._array.__imatmul__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__imatmul__(asarray(other)._array) + return self.__class__._new(res) - def __rmatmul__(x1: array, x2: array, /) -> array: + def __rmatmul__(self: array, other: array, /) -> array: """ Performs the operation __rmatmul__. """ - res = x1._array.__rmatmul__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__rmatmul__(asarray(other)._array) + return self.__class__._new(res) - def __imod__(x1: array, x2: array, /) -> array: + def __imod__(self: array, other: array, /) -> array: """ Performs the operation __imod__. """ - res = x1._array.__imod__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__imod__(asarray(other)._array) + return self.__class__._new(res) - def __rmod__(x1: array, x2: array, /) -> array: + def __rmod__(self: array, other: array, /) -> array: """ Performs the operation __rmod__. """ - res = x1._array.__rmod__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__rmod__(asarray(other)._array) + return self.__class__._new(res) - def __imul__(x1: array, x2: array, /) -> array: + def __imul__(self: array, other: array, /) -> array: """ Performs the operation __imul__. """ - res = x1._array.__imul__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__imul__(asarray(other)._array) + return self.__class__._new(res) - def __rmul__(x1: array, x2: array, /) -> array: + def __rmul__(self: array, other: array, /) -> array: """ Performs the operation __rmul__. """ - res = x1._array.__rmul__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__rmul__(asarray(other)._array) + return self.__class__._new(res) - def __ior__(x1: array, x2: array, /) -> array: + def __ior__(self: array, other: array, /) -> array: """ Performs the operation __ior__. """ - res = x1._array.__ior__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__ior__(asarray(other)._array) + return self.__class__._new(res) - def __ror__(x1: array, x2: array, /) -> array: + def __ror__(self: array, other: array, /) -> array: """ Performs the operation __ror__. """ - res = x1._array.__ror__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__ror__(asarray(other)._array) + return self.__class__._new(res) - def __ipow__(x1: array, x2: array, /) -> array: + def __ipow__(self: array, other: array, /) -> array: """ Performs the operation __ipow__. """ - res = x1._array.__ipow__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__ipow__(asarray(other)._array) + return self.__class__._new(res) - def __rpow__(x1: array, x2: array, /) -> array: + def __rpow__(self: array, other: array, /) -> array: """ Performs the operation __rpow__. """ - res = x1._array.__rpow__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__rpow__(asarray(other)._array) + return self.__class__._new(res) - def __irshift__(x1: array, x2: array, /) -> array: + def __irshift__(self: array, other: array, /) -> array: """ Performs the operation __irshift__. """ - res = x1._array.__irshift__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__irshift__(asarray(other)._array) + return self.__class__._new(res) - def __rrshift__(x1: array, x2: array, /) -> array: + def __rrshift__(self: array, other: array, /) -> array: """ Performs the operation __rrshift__. """ - res = x1._array.__rrshift__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__rrshift__(asarray(other)._array) + return self.__class__._new(res) - def __isub__(x1: array, x2: array, /) -> array: + def __isub__(self: array, other: array, /) -> array: """ Performs the operation __isub__. """ - res = x1._array.__isub__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__isub__(asarray(other)._array) + return self.__class__._new(res) - def __rsub__(x1: array, x2: array, /) -> array: + def __rsub__(self: array, other: array, /) -> array: """ Performs the operation __rsub__. """ - res = x1._array.__rsub__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__rsub__(asarray(other)._array) + return self.__class__._new(res) - def __itruediv__(x1: array, x2: array, /) -> array: + def __itruediv__(self: array, other: array, /) -> array: """ Performs the operation __itruediv__. """ - res = x1._array.__itruediv__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__itruediv__(asarray(other)._array) + return self.__class__._new(res) - def __rtruediv__(x1: array, x2: array, /) -> array: + def __rtruediv__(self: array, other: array, /) -> array: """ Performs the operation __rtruediv__. """ - res = x1._array.__rtruediv__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__rtruediv__(asarray(other)._array) + return self.__class__._new(res) - def __ixor__(x1: array, x2: array, /) -> array: + def __ixor__(self: array, other: array, /) -> array: """ Performs the operation __ixor__. """ - res = x1._array.__ixor__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__ixor__(asarray(other)._array) + return self.__class__._new(res) - def __rxor__(x1: array, x2: array, /) -> array: + def __rxor__(self: array, other: array, /) -> array: """ Performs the operation __rxor__. """ - res = x1._array.__rxor__(asarray(x2)._array) - return x1.__class__._new(res) + res = self._array.__rxor__(asarray(other)._array) + return self.__class__._new(res) @property def dtype(self): -- cgit v1.2.1 From 6c17d4bfd080c00082efa60891212f95b2500c18 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 9 Mar 2021 17:19:42 -0700 Subject: Update the array API namespace __init__.py docstring with todos --- numpy/_array_api/__init__.py | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/__init__.py b/numpy/_array_api/__init__.py index 880deb613..ebbe0bb91 100644 --- a/numpy/_array_api/__init__.py +++ b/numpy/_array_api/__init__.py @@ -65,8 +65,8 @@ A few notes about the current state of this submodule: functions. It does not have a public constructor on the object itself. The object is a small wrapper Python class around numpy.ndarray. The main purpose of it is to restrict the namespace of the array object to only those - methods that are required by the spec, as well as to limit/change certain - behavior that differs in the spec. In particular: + dtypes and only those methods that are required by the spec, as well as to + limit/change certain behavior that differs in the spec. In particular: - Indexing: Only a subset of indices supported by NumPy are required by the spec. The ndarray object restricts indexing to only allow those types of @@ -75,7 +75,7 @@ A few notes about the current state of this submodule: information. - Type promotion: Some type promotion rules are different in the spec. In - particular, the spec does not have any value-based casing. Note that the + particular, the spec does not have any value-based casting. Note that the code to correct the type promotion rules on numpy._array_api.ndarray is not yet implemented. @@ -84,10 +84,30 @@ A few notes about the current state of this submodule: 'dtype'). These do not currently fully pass mypy due to some limitations in mypy. +- The wrapper functions in this module do not do any type checking for things + that would be impossible without leaving the _array_api namespace. + - All places where the implementations in this submodule are known to deviate from their corresponding functions in NumPy are marked with "# Note" comments. Reviewers should make note of these comments. +Still TODO in this module are: + +- Implement the spec type promotion rules on the ndarray object. + +- Disable NumPy warnings in the API functions. + +- Implement keepdims on argmin and argmax. + +- Device support and DLPack support are not yet implemented. These require + support in NumPy itself first. + +- The a non-default value for the `copy` keyword argument is not yet + implemented on asarray. This requires support in numpy.asarray() first. + +- Some functions are not yet fully tested in the array API test suite, and may + require updates that are not yet known until the tests are written. + """ __all__ = [] -- cgit v1.2.1 From 48a2d8c39bb58611307122f55da6f0e99cf86086 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 9 Mar 2021 17:19:50 -0700 Subject: Add a small docstring to the array API ndarray object --- numpy/_array_api/_array_object.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 247194017..e9ea6ef45 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -24,6 +24,20 @@ from ._dtypes import _boolean_dtypes, _integer_dtypes import numpy as np class ndarray: + """ + ndarray object for the array API namespace. + + See the docstring of :py:obj:`np.ndarray ` for more + information. + + This is a wrapper around numpy.ndarray that restricts the usage to only + those things that are required by the array API namespace. Note, + attributes on this object that start with a single underscore are not part + of the API specification and should only be used internally. This object + should not be constructed directly. Rather, use one of the creation + functions, such as asarray(). + + """ # Use a custom constructor instead of __init__, as manually initializing # this class is not supported API. @classmethod -- cgit v1.2.1 From 8671a368cff459d5a00c5b43d330d084e2f6ed3d Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 9 Mar 2021 17:20:56 -0700 Subject: Use the array API types for the array API type annotations --- numpy/_array_api/_types.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_types.py b/numpy/_array_api/_types.py index 3800b7156..32d03e2a7 100644 --- a/numpy/_array_api/_types.py +++ b/numpy/_array_api/_types.py @@ -11,12 +11,13 @@ __all__ = ['Literal', 'Optional', 'Tuple', 'Union', 'array', 'device', from typing import Literal, Optional, Tuple, Union, TypeVar -import numpy as np +from . import (ndarray, int8, int16, int32, int64, uint8, uint16, uint32, + uint64, float32, float64) -array = np.ndarray +array = ndarray device = TypeVar('device') -dtype = Literal[np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, - np.uint32, np.uint64, np.float32, np.float64] +dtype = Literal[int8, int16, int32, int64, uint8, uint16, + uint32, uint64, float32, float64] SupportsDLPack = TypeVar('SupportsDLPack') SupportsBufferProtocol = TypeVar('SupportsBufferProtocol') PyCapsule = TypeVar('PyCapsule') -- cgit v1.2.1 From da5dc954ea5e5a1c4fc43240f4ddfec051d71845 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 11 Mar 2021 16:44:39 -0700 Subject: Only return the same array in asarray if the dtype is the same --- numpy/_array_api/_creation_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 5b73c8f5c..cee379f59 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -21,7 +21,7 @@ def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, Su if copy is not None: # Note: copy is not yet implemented in np.asarray raise NotImplementedError("The copy keyword argument to asarray is not yet implemented") - if isinstance(obj, ndarray): + if isinstance(obj, ndarray) and (dtype is None or obj.dtype == dtype): return obj res = np.asarray(obj, dtype=dtype) if res.dtype not in _all_dtypes: -- cgit v1.2.1 From e42ae01029caf63e28b7533d4f7fd03ac0244066 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 11 Mar 2021 16:44:53 -0700 Subject: Use more robust code for converting an array scalar to a shape () array --- numpy/_array_api/_array_object.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index e9ea6ef45..371361de9 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -54,8 +54,10 @@ class ndarray: obj = super().__new__(cls) # Note: The spec does not have array scalars, only shape () arrays. if isinstance(x, np.generic): - # x[...] converts an array scalar to a shape () array. - x = x[...] + # Convert the array scalar to a shape () array + xa = np.empty((), x.dtype) + xa[()] = x + x = xa obj._array = x return obj -- cgit v1.2.1 From ed05662905f83864a937fa67b7f762cda1277df2 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 17 Mar 2021 19:28:18 -0600 Subject: Fix circular imports from types --- numpy/_array_api/_array_object.py | 5 ++++- numpy/_array_api/_creation_functions.py | 7 +++++-- numpy/_array_api/_data_type_functions.py | 5 ++++- numpy/_array_api/_elementwise_functions.py | 5 ++++- numpy/_array_api/_linear_algebra_functions.py | 5 ++++- numpy/_array_api/_manipulation_functions.py | 5 ++++- numpy/_array_api/_searching_functions.py | 5 ++++- numpy/_array_api/_set_functions.py | 5 ++++- numpy/_array_api/_sorting_functions.py | 5 ++++- numpy/_array_api/_statistical_functions.py | 5 ++++- numpy/_array_api/_utility_functions.py | 5 ++++- 11 files changed, 45 insertions(+), 12 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 371361de9..4b11a0ca0 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -17,10 +17,13 @@ from __future__ import annotations import operator from enum import IntEnum -from ._types import Optional, PyCapsule, Tuple, Union, array from ._creation_functions import asarray from ._dtypes import _boolean_dtypes, _integer_dtypes +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from ._types import Optional, PyCapsule, Tuple, Union, array + import numpy as np class ndarray: diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index cee379f59..e9ef983fd 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -1,7 +1,10 @@ from __future__ import annotations -from ._types import (Optional, SupportsDLPack, SupportsBufferProtocol, Tuple, - Union, array, device, dtype) + +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from ._types import (Optional, SupportsDLPack, SupportsBufferProtocol, Tuple, + Union, array, device, dtype) from ._dtypes import _all_dtypes import numpy as np diff --git a/numpy/_array_api/_data_type_functions.py b/numpy/_array_api/_data_type_functions.py index 9e4dcba95..0488ba915 100644 --- a/numpy/_array_api/_data_type_functions.py +++ b/numpy/_array_api/_data_type_functions.py @@ -1,8 +1,11 @@ from __future__ import annotations -from ._types import Union, array, dtype from ._array_object import ndarray +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from ._types import Union, array, dtype + from collections.abc import Sequence import numpy as np diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index 9efe17e83..cd2e8661f 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -2,9 +2,12 @@ from __future__ import annotations from ._dtypes import (_boolean_dtypes, _floating_dtypes, _integer_dtypes, _integer_or_boolean_dtypes, _numeric_dtypes) -from ._types import array from ._array_object import ndarray +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from ._types import array + import numpy as np def abs(x: array, /) -> array: diff --git a/numpy/_array_api/_linear_algebra_functions.py b/numpy/_array_api/_linear_algebra_functions.py index 95ed00fd5..f57fe292a 100644 --- a/numpy/_array_api/_linear_algebra_functions.py +++ b/numpy/_array_api/_linear_algebra_functions.py @@ -1,8 +1,11 @@ from __future__ import annotations -from ._types import Literal, Optional, Tuple, Union, array from ._array_object import ndarray +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from ._types import Literal, Optional, Tuple, Union, array + import numpy as np # def cholesky(): diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index 1631a924f..4e5ca0728 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -1,8 +1,11 @@ from __future__ import annotations -from ._types import Optional, Tuple, Union, array from ._array_object import ndarray +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from ._types import Optional, Tuple, Union, array + import numpy as np def concat(arrays: Tuple[array], /, *, axis: Optional[int] = 0) -> array: diff --git a/numpy/_array_api/_searching_functions.py b/numpy/_array_api/_searching_functions.py index 44e5b2775..9a5d583bc 100644 --- a/numpy/_array_api/_searching_functions.py +++ b/numpy/_array_api/_searching_functions.py @@ -1,8 +1,11 @@ from __future__ import annotations -from ._types import Tuple, array from ._array_object import ndarray +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from ._types import Tuple, array + import numpy as np def argmax(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: diff --git a/numpy/_array_api/_set_functions.py b/numpy/_array_api/_set_functions.py index f5cd6d324..025a27d80 100644 --- a/numpy/_array_api/_set_functions.py +++ b/numpy/_array_api/_set_functions.py @@ -1,8 +1,11 @@ from __future__ import annotations -from ._types import Tuple, Union, array from ._array_object import ndarray +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from ._types import Tuple, Union, array + import numpy as np def unique(x: array, /, *, return_counts: bool = False, return_index: bool = False, return_inverse: bool = False) -> Union[array, Tuple[array, ...]]: diff --git a/numpy/_array_api/_sorting_functions.py b/numpy/_array_api/_sorting_functions.py index 2e054b03a..6e87bd90e 100644 --- a/numpy/_array_api/_sorting_functions.py +++ b/numpy/_array_api/_sorting_functions.py @@ -1,8 +1,11 @@ from __future__ import annotations -from ._types import array from ._array_object import ndarray +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from ._types import array + import numpy as np def argsort(x: array, /, *, axis: int = -1, descending: bool = False, stable: bool = True) -> array: diff --git a/numpy/_array_api/_statistical_functions.py b/numpy/_array_api/_statistical_functions.py index fa3551248..26afd7354 100644 --- a/numpy/_array_api/_statistical_functions.py +++ b/numpy/_array_api/_statistical_functions.py @@ -1,8 +1,11 @@ from __future__ import annotations -from ._types import Optional, Tuple, Union, array from ._array_object import ndarray +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from ._types import Optional, Tuple, Union, array + import numpy as np def max(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: diff --git a/numpy/_array_api/_utility_functions.py b/numpy/_array_api/_utility_functions.py index c4721ad7e..e280b5785 100644 --- a/numpy/_array_api/_utility_functions.py +++ b/numpy/_array_api/_utility_functions.py @@ -1,8 +1,11 @@ from __future__ import annotations -from ._types import Optional, Tuple, Union, array from ._array_object import ndarray +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from ._types import Optional, Tuple, Union, array + import numpy as np def all(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: -- cgit v1.2.1 From 8968bc31944eb2af214b591d38eca3b0cea56f4b Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 18 Mar 2021 15:20:24 -0600 Subject: bitwise_left_shift and bitwise_right_shift should return the dtype of the first argument --- numpy/_array_api/_elementwise_functions.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index cd2e8661f..a8af04c62 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -123,10 +123,13 @@ def bitwise_left_shift(x1: array, x2: array, /) -> array: See its docstring for more information. """ + # Note: the function name is different here if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: raise TypeError('Only integer dtypes are allowed in bitwise_left_shift') - # Note: the function name is different here - return ndarray._new(np.left_shift(x1._array, x2._array)) + # Note: The spec requires the return dtype of bitwise_left_shift to be the + # same as the first argument. np.left_shift() returns a type that is the + # type promotion of the two input types. + return ndarray._new(np.left_shift(x1._array, x2._array).astype(x1.dtype)) def bitwise_invert(x: array, /) -> array: """ @@ -155,10 +158,13 @@ def bitwise_right_shift(x1: array, x2: array, /) -> array: See its docstring for more information. """ + # Note: the function name is different here if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: raise TypeError('Only integer dtypes are allowed in bitwise_right_shift') - # Note: the function name is different here - return ndarray._new(np.right_shift(x1._array, x2._array)) + # Note: The spec requires the return dtype of bitwise_left_shift to be the + # same as the first argument. np.left_shift() returns a type that is the + # type promotion of the two input types. + return ndarray._new(np.right_shift(x1._array, x2._array).astype(x1.dtype)) def bitwise_xor(x1: array, x2: array, /) -> array: """ -- cgit v1.2.1 From 479c8a24121465bbb9e0e193dc2da39cd08bdfe4 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 18 Mar 2021 15:26:50 -0600 Subject: bitwise_left_shift and bitwise_right_shift are only defined for x2 >= 0 --- numpy/_array_api/_elementwise_functions.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index a8af04c62..aa48f440c 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -126,6 +126,9 @@ def bitwise_left_shift(x1: array, x2: array, /) -> array: # Note: the function name is different here if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: raise TypeError('Only integer dtypes are allowed in bitwise_left_shift') + # Note: bitwise_left_shift is only defined for x2 nonnegative. + if np.any(x2._array < 0): + raise ValueError('bitwise_left_shift(x1, x2) is only defined for x2 >= 0') # Note: The spec requires the return dtype of bitwise_left_shift to be the # same as the first argument. np.left_shift() returns a type that is the # type promotion of the two input types. @@ -161,6 +164,9 @@ def bitwise_right_shift(x1: array, x2: array, /) -> array: # Note: the function name is different here if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: raise TypeError('Only integer dtypes are allowed in bitwise_right_shift') + # Note: bitwise_right_shift is only defined for x2 nonnegative. + if np.any(x2._array < 0): + raise ValueError('bitwise_right_shift(x1, x2) is only defined for x2 >= 0') # Note: The spec requires the return dtype of bitwise_left_shift to be the # same as the first argument. np.left_shift() returns a type that is the # type promotion of the two input types. -- cgit v1.2.1 From 7ce435c610fcd7fee01da9d9e7ff5c1ab4ae6ef6 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 30 Mar 2021 13:53:11 -0600 Subject: Update some annotations updated from the spec --- numpy/_array_api/_array_object.py | 4 ++-- numpy/_array_api/_data_type_functions.py | 4 ++-- numpy/_array_api/_manipulation_functions.py | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 4b11a0ca0..ad0cbc71e 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -106,7 +106,7 @@ class ndarray: res = self._array.__and__(asarray(other)._array) return self.__class__._new(res) - def __array_namespace__(self, /, *, api_version=None): + def __array_namespace__(self: array, /, *, api_version: Optional[str] = None) -> object: if api_version is not None: raise ValueError("Unrecognized array API version") from numpy import _array_api @@ -274,7 +274,7 @@ class ndarray: # ndarray() form, like a list of booleans. raise IndexError("Only integers, slices (`:`), ellipsis (`...`), and boolean arrays are valid indices in the array API namespace") - def __getitem__(self: array, key: Union[int, slice, Tuple[Union[int, slice], ...], array], /) -> array: + def __getitem__(self: array, key: Union[int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], array], /) -> array: """ Performs the operation __getitem__. """ diff --git a/numpy/_array_api/_data_type_functions.py b/numpy/_array_api/_data_type_functions.py index 0488ba915..d4816a41f 100644 --- a/numpy/_array_api/_data_type_functions.py +++ b/numpy/_array_api/_data_type_functions.py @@ -10,7 +10,7 @@ from collections.abc import Sequence import numpy as np -def finfo(type: Union[dtype, array], /) -> finfo: +def finfo(type: Union[dtype, array], /) -> finfo_object: """ Array API compatible wrapper for :py:func:`np.finfo `. @@ -18,7 +18,7 @@ def finfo(type: Union[dtype, array], /) -> finfo: """ return np.finfo(type) -def iinfo(type: Union[dtype, array], /) -> iinfo: +def iinfo(type: Union[dtype, array], /) -> iinfo_object: """ Array API compatible wrapper for :py:func:`np.iinfo `. diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index 4e5ca0728..033ed23a0 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -8,7 +8,7 @@ if TYPE_CHECKING: import numpy as np -def concat(arrays: Tuple[array], /, *, axis: Optional[int] = 0) -> array: +def concat(arrays: Tuple[array, ...], /, *, axis: Optional[int] = 0) -> array: """ Array API compatible wrapper for :py:func:`np.concatenate `. @@ -58,7 +58,7 @@ def squeeze(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) """ return ndarray._array(np.squeeze(x._array, axis=axis)) -def stack(arrays: Tuple[array], /, *, axis: int = 0) -> array: +def stack(arrays: Tuple[array, ...], /, *, axis: int = 0) -> array: """ Array API compatible wrapper for :py:func:`np.stack `. -- cgit v1.2.1 From 9fe4fc7ff7f477fc4aaad850f8d1841beb2924bc Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 31 Mar 2021 16:34:23 -0600 Subject: Make the array API follow the spec Python scalar promotion rules --- numpy/_array_api/_array_object.py | 129 +++++++++++++++++++++++++++++++++++++- 1 file changed, 128 insertions(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index ad0cbc71e..c5acb5d1d 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -18,7 +18,7 @@ from __future__ import annotations import operator from enum import IntEnum from ._creation_functions import asarray -from ._dtypes import _boolean_dtypes, _integer_dtypes +from ._dtypes import _boolean_dtypes, _integer_dtypes, _floating_dtypes from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -83,6 +83,37 @@ class ndarray: """ return self._array.__repr__().replace('array', 'ndarray') + # Helper function to match the type promotion rules in the spec + def _promote_scalar(self, scalar): + """ + Returns a promoted version of a Python scalar appropiate for use with + operations on self. + + This may raise an OverflowError in cases where the scalar is an + integer that is too large to fit in a NumPy integer dtype, or + TypeError when the scalar type is incompatible with the dtype of self. + + Note: this helper function returns a NumPy array (NOT a NumPy array + API ndarray). + """ + if isinstance(scalar, bool): + if self.dtype not in _boolean_dtypes: + raise TypeError("Python bool scalars can only be promoted with bool arrays") + elif isinstance(scalar, int): + if self.dtype in _boolean_dtypes: + raise TypeError("Python int scalars cannot be promoted with bool arrays") + elif isinstance(scalar, float): + if self.dtype not in _floating_dtypes: + raise TypeError("Python float scalars can only be promoted with floating-point arrays.") + else: + raise TypeError("'scalar' must be a Python scalar") + + # Note: the spec only specifies integer-dtype/int promotion + # behavior for integers within the bounds of the integer dtype. + # Outside of those bounds we use the default NumPy behavior (either + # cast or raise OverflowError). + return np.array(scalar, self.dtype) + # Everything below this is required by the spec. def __abs__(self: array, /) -> array: @@ -96,6 +127,8 @@ class ndarray: """ Performs the operation __add__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__add__(asarray(other)._array) return self.__class__._new(res) @@ -103,6 +136,8 @@ class ndarray: """ Performs the operation __and__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__and__(asarray(other)._array) return self.__class__._new(res) @@ -140,6 +175,8 @@ class ndarray: """ Performs the operation __eq__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__eq__(asarray(other)._array) return self.__class__._new(res) @@ -157,6 +194,8 @@ class ndarray: """ Performs the operation __floordiv__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__floordiv__(asarray(other)._array) return self.__class__._new(res) @@ -164,6 +203,8 @@ class ndarray: """ Performs the operation __ge__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__ge__(asarray(other)._array) return self.__class__._new(res) @@ -288,6 +329,8 @@ class ndarray: """ Performs the operation __gt__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__gt__(asarray(other)._array) return self.__class__._new(res) @@ -312,6 +355,8 @@ class ndarray: """ Performs the operation __le__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__le__(asarray(other)._array) return self.__class__._new(res) @@ -326,6 +371,8 @@ class ndarray: """ Performs the operation __lshift__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__lshift__(asarray(other)._array) return self.__class__._new(res) @@ -333,6 +380,8 @@ class ndarray: """ Performs the operation __lt__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__lt__(asarray(other)._array) return self.__class__._new(res) @@ -340,6 +389,10 @@ class ndarray: """ Performs the operation __matmul__. """ + if isinstance(other, (int, float, bool)): + # matmul is not defined for scalars, but without this, we may get + # the wrong error message from asarray. + other = self._promote_scalar(other) res = self._array.__matmul__(asarray(other)._array) return self.__class__._new(res) @@ -347,6 +400,8 @@ class ndarray: """ Performs the operation __mod__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__mod__(asarray(other)._array) return self.__class__._new(res) @@ -354,6 +409,8 @@ class ndarray: """ Performs the operation __mul__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__mul__(asarray(other)._array) return self.__class__._new(res) @@ -361,6 +418,8 @@ class ndarray: """ Performs the operation __ne__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__ne__(asarray(other)._array) return self.__class__._new(res) @@ -375,6 +434,8 @@ class ndarray: """ Performs the operation __or__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__or__(asarray(other)._array) return self.__class__._new(res) @@ -389,6 +450,8 @@ class ndarray: """ Performs the operation __pow__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__pow__(asarray(other)._array) return self.__class__._new(res) @@ -396,6 +459,8 @@ class ndarray: """ Performs the operation __rshift__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__rshift__(asarray(other)._array) return self.__class__._new(res) @@ -413,6 +478,8 @@ class ndarray: """ Performs the operation __sub__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__sub__(asarray(other)._array) return self.__class__._new(res) @@ -420,6 +487,8 @@ class ndarray: """ Performs the operation __truediv__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__truediv__(asarray(other)._array) return self.__class__._new(res) @@ -427,6 +496,8 @@ class ndarray: """ Performs the operation __xor__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__xor__(asarray(other)._array) return self.__class__._new(res) @@ -434,6 +505,8 @@ class ndarray: """ Performs the operation __iadd__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__iadd__(asarray(other)._array) return self.__class__._new(res) @@ -441,6 +514,8 @@ class ndarray: """ Performs the operation __radd__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__radd__(asarray(other)._array) return self.__class__._new(res) @@ -448,6 +523,8 @@ class ndarray: """ Performs the operation __iand__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__iand__(asarray(other)._array) return self.__class__._new(res) @@ -455,6 +532,8 @@ class ndarray: """ Performs the operation __rand__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__rand__(asarray(other)._array) return self.__class__._new(res) @@ -462,6 +541,8 @@ class ndarray: """ Performs the operation __ifloordiv__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__ifloordiv__(asarray(other)._array) return self.__class__._new(res) @@ -469,6 +550,8 @@ class ndarray: """ Performs the operation __rfloordiv__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__rfloordiv__(asarray(other)._array) return self.__class__._new(res) @@ -476,6 +559,8 @@ class ndarray: """ Performs the operation __ilshift__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__ilshift__(asarray(other)._array) return self.__class__._new(res) @@ -483,6 +568,8 @@ class ndarray: """ Performs the operation __rlshift__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__rlshift__(asarray(other)._array) return self.__class__._new(res) @@ -490,6 +577,10 @@ class ndarray: """ Performs the operation __imatmul__. """ + if isinstance(other, (int, float, bool)): + # matmul is not defined for scalars, but without this, we may get + # the wrong error message from asarray. + other = self._promote_scalar(other) res = self._array.__imatmul__(asarray(other)._array) return self.__class__._new(res) @@ -497,6 +588,10 @@ class ndarray: """ Performs the operation __rmatmul__. """ + if isinstance(other, (int, float, bool)): + # matmul is not defined for scalars, but without this, we may get + # the wrong error message from asarray. + other = self._promote_scalar(other) res = self._array.__rmatmul__(asarray(other)._array) return self.__class__._new(res) @@ -504,6 +599,8 @@ class ndarray: """ Performs the operation __imod__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__imod__(asarray(other)._array) return self.__class__._new(res) @@ -511,6 +608,8 @@ class ndarray: """ Performs the operation __rmod__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__rmod__(asarray(other)._array) return self.__class__._new(res) @@ -518,6 +617,8 @@ class ndarray: """ Performs the operation __imul__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__imul__(asarray(other)._array) return self.__class__._new(res) @@ -525,6 +626,8 @@ class ndarray: """ Performs the operation __rmul__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__rmul__(asarray(other)._array) return self.__class__._new(res) @@ -532,6 +635,8 @@ class ndarray: """ Performs the operation __ior__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__ior__(asarray(other)._array) return self.__class__._new(res) @@ -539,6 +644,8 @@ class ndarray: """ Performs the operation __ror__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__ror__(asarray(other)._array) return self.__class__._new(res) @@ -546,6 +653,8 @@ class ndarray: """ Performs the operation __ipow__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__ipow__(asarray(other)._array) return self.__class__._new(res) @@ -553,6 +662,8 @@ class ndarray: """ Performs the operation __rpow__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__rpow__(asarray(other)._array) return self.__class__._new(res) @@ -560,6 +671,8 @@ class ndarray: """ Performs the operation __irshift__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__irshift__(asarray(other)._array) return self.__class__._new(res) @@ -567,6 +680,8 @@ class ndarray: """ Performs the operation __rrshift__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__rrshift__(asarray(other)._array) return self.__class__._new(res) @@ -574,6 +689,8 @@ class ndarray: """ Performs the operation __isub__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__isub__(asarray(other)._array) return self.__class__._new(res) @@ -581,6 +698,8 @@ class ndarray: """ Performs the operation __rsub__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__rsub__(asarray(other)._array) return self.__class__._new(res) @@ -588,6 +707,8 @@ class ndarray: """ Performs the operation __itruediv__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__itruediv__(asarray(other)._array) return self.__class__._new(res) @@ -595,6 +716,8 @@ class ndarray: """ Performs the operation __rtruediv__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__rtruediv__(asarray(other)._array) return self.__class__._new(res) @@ -602,6 +725,8 @@ class ndarray: """ Performs the operation __ixor__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__ixor__(asarray(other)._array) return self.__class__._new(res) @@ -609,6 +734,8 @@ class ndarray: """ Performs the operation __rxor__. """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) res = self._array.__rxor__(asarray(other)._array) return self.__class__._new(res) -- cgit v1.2.1 From fb5c69775f516f06fcf6e79e0762f09e6e8cb907 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 31 Mar 2021 17:38:46 -0600 Subject: Give a better error message in the array API asarray for out of bounds integers Without this the error message would be a confusing message about object arrays not being supported. --- numpy/_array_api/_creation_functions.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index e9ef983fd..107f3c313 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -26,6 +26,10 @@ def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, Su raise NotImplementedError("The copy keyword argument to asarray is not yet implemented") if isinstance(obj, ndarray) and (dtype is None or obj.dtype == dtype): return obj + if isinstance(obj, int) and (obj > 2**64 or obj < -2**63): + # Give a better error message in this case. NumPy would convert this + # to an object array. + raise OverflowError("Integer out of bounds for array dtypes") res = np.asarray(obj, dtype=dtype) if res.dtype not in _all_dtypes: raise TypeError(f"The array_api namespace does not support the dtype '{res.dtype}'") -- cgit v1.2.1 From b75a135751e4b38f144027678d1ddc74ee4d50fc Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 13 Apr 2021 17:05:44 -0600 Subject: Fix int bounds checking in asarray() to only happen when dtype=None --- numpy/_array_api/_creation_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 107f3c313..003b10afb 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -26,7 +26,7 @@ def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, Su raise NotImplementedError("The copy keyword argument to asarray is not yet implemented") if isinstance(obj, ndarray) and (dtype is None or obj.dtype == dtype): return obj - if isinstance(obj, int) and (obj > 2**64 or obj < -2**63): + if dtype is None and isinstance(obj, int) and (obj > 2**64 or obj < -2**63): # Give a better error message in this case. NumPy would convert this # to an object array. raise OverflowError("Integer out of bounds for array dtypes") -- cgit v1.2.1 From d40d2bcfbe01678479fe741ab8b0ff5e431e0329 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 13 Apr 2021 18:11:09 -0600 Subject: Fix ceil() and floor() in the array API to always return the same dtype --- numpy/_array_api/_elementwise_functions.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index aa48f440c..3ca71b53e 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -190,6 +190,9 @@ def ceil(x: array, /) -> array: """ if x.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in ceil') + if x.dtype in _integer_dtypes: + # Note: The return dtype of ceil is the same as the input + return x return ndarray._new(np.ceil(x._array)) def cos(x: array, /) -> array: @@ -258,6 +261,9 @@ def floor(x: array, /) -> array: """ if x.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in floor') + if x.dtype in _integer_dtypes: + # Note: The return dtype of floor is the same as the input + return x return ndarray._new(np.floor(x._array)) def floor_divide(x1: array, x2: array, /) -> array: -- cgit v1.2.1 From 844fcd39692da676a7204c6cc7feea428ba49609 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 14 Apr 2021 17:07:00 -0600 Subject: Move function name change notes to before the def line --- numpy/_array_api/_elementwise_functions.py | 22 +++++++++++----------- numpy/_array_api/_manipulation_functions.py | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index 3ca71b53e..5ee33f60f 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -20,6 +20,7 @@ def abs(x: array, /) -> array: raise TypeError('Only numeric dtypes are allowed in abs') return ndarray._new(np.abs(x._array)) +# Note: the function name is different here def acos(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arccos `. @@ -28,9 +29,9 @@ def acos(x: array, /) -> array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in acos') - # Note: the function name is different here return ndarray._new(np.arccos(x._array)) +# Note: the function name is different here def acosh(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arccosh `. @@ -39,7 +40,6 @@ def acosh(x: array, /) -> array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in acosh') - # Note: the function name is different here return ndarray._new(np.arccosh(x._array)) def add(x1: array, x2: array, /) -> array: @@ -52,6 +52,7 @@ def add(x1: array, x2: array, /) -> array: raise TypeError('Only numeric dtypes are allowed in add') return ndarray._new(np.add(x1._array, x2._array)) +# Note: the function name is different here def asin(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arcsin `. @@ -60,9 +61,9 @@ def asin(x: array, /) -> array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in asin') - # Note: the function name is different here return ndarray._new(np.arcsin(x._array)) +# Note: the function name is different here def asinh(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arcsinh `. @@ -71,9 +72,9 @@ def asinh(x: array, /) -> array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in asinh') - # Note: the function name is different here return ndarray._new(np.arcsinh(x._array)) +# Note: the function name is different here def atan(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arctan `. @@ -82,9 +83,9 @@ def atan(x: array, /) -> array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in atan') - # Note: the function name is different here return ndarray._new(np.arctan(x._array)) +# Note: the function name is different here def atan2(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arctan2 `. @@ -93,9 +94,9 @@ def atan2(x1: array, x2: array, /) -> array: """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in atan2') - # Note: the function name is different here return ndarray._new(np.arctan2(x1._array, x2._array)) +# Note: the function name is different here def atanh(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arctanh `. @@ -104,7 +105,6 @@ def atanh(x: array, /) -> array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in atanh') - # Note: the function name is different here return ndarray._new(np.arctanh(x._array)) def bitwise_and(x1: array, x2: array, /) -> array: @@ -117,13 +117,13 @@ def bitwise_and(x1: array, x2: array, /) -> array: raise TypeError('Only integer_or_boolean dtypes are allowed in bitwise_and') return ndarray._new(np.bitwise_and(x1._array, x2._array)) +# Note: the function name is different here def bitwise_left_shift(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.left_shift `. See its docstring for more information. """ - # Note: the function name is different here if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: raise TypeError('Only integer dtypes are allowed in bitwise_left_shift') # Note: bitwise_left_shift is only defined for x2 nonnegative. @@ -134,6 +134,7 @@ def bitwise_left_shift(x1: array, x2: array, /) -> array: # type promotion of the two input types. return ndarray._new(np.left_shift(x1._array, x2._array).astype(x1.dtype)) +# Note: the function name is different here def bitwise_invert(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.invert `. @@ -142,7 +143,6 @@ def bitwise_invert(x: array, /) -> array: """ if x.dtype not in _integer_or_boolean_dtypes: raise TypeError('Only integer or boolean dtypes are allowed in bitwise_invert') - # Note: the function name is different here return ndarray._new(np.invert(x._array)) def bitwise_or(x1: array, x2: array, /) -> array: @@ -155,13 +155,13 @@ def bitwise_or(x1: array, x2: array, /) -> array: raise TypeError('Only integer or boolean dtypes are allowed in bitwise_or') return ndarray._new(np.bitwise_or(x1._array, x2._array)) +# Note: the function name is different here def bitwise_right_shift(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.right_shift `. See its docstring for more information. """ - # Note: the function name is different here if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: raise TypeError('Only integer dtypes are allowed in bitwise_right_shift') # Note: bitwise_right_shift is only defined for x2 nonnegative. @@ -474,6 +474,7 @@ def positive(x: array, /) -> array: raise TypeError('Only numeric dtypes are allowed in positive') return ndarray._new(np.positive(x._array)) +# Note: the function name is different here def pow(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.power `. @@ -482,7 +483,6 @@ def pow(x1: array, x2: array, /) -> array: """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in pow') - # Note: the function name is different here return ndarray._new(np.power(x1._array, x2._array)) def remainder(x1: array, x2: array, /) -> array: diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index 033ed23a0..6ac7be02f 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -8,6 +8,7 @@ if TYPE_CHECKING: import numpy as np +# Note: the function name is different here def concat(arrays: Tuple[array, ...], /, *, axis: Optional[int] = 0) -> array: """ Array API compatible wrapper for :py:func:`np.concatenate `. @@ -15,7 +16,6 @@ def concat(arrays: Tuple[array, ...], /, *, axis: Optional[int] = 0) -> array: See its docstring for more information. """ arrays = tuple(a._array for a in arrays) - # Note: the function name is different here return ndarray._new(np.concatenate(arrays, axis=axis)) def expand_dims(x: array, axis: int, /) -> array: -- cgit v1.2.1 From 9af1cc60edd4fdbb7e9c18d124e639a44ce420c7 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 15 Apr 2021 15:31:33 -0600 Subject: Use dtype objects instead of classes in the array API The array API does not require any methods or behaviors on dtype objects, other than that they be literals that can be compared for equality and passed to dtype keywords in functions. Since dtype objects are already used by the dtype attribute of ndarray, this makes it consistent, so that func(dtype=).dtype will give exactly back, which will be the same thing as numpy._array_api.. This also fixes an issue in the array API test suite due to the fact that dtype classes and objects are not equal as dictionary keys. --- numpy/_array_api/_dtypes.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_dtypes.py b/numpy/_array_api/_dtypes.py index d33ae1fce..c874763dd 100644 --- a/numpy/_array_api/_dtypes.py +++ b/numpy/_array_api/_dtypes.py @@ -1,6 +1,19 @@ -from .. import int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64 +import numpy as np + +# Note: we use dtype objects instead of dtype classes. The spec does not +# require any behavior on dtypes other than equality. +int8 = np.dtype('int8') +int16 = np.dtype('int16') +int32 = np.dtype('int32') +int64 = np.dtype('int64') +uint8 = np.dtype('uint8') +uint16 = np.dtype('uint16') +uint32 = np.dtype('uint32') +uint64 = np.dtype('uint64') +float32 = np.dtype('float32') +float64 = np.dtype('float64') # Note: This name is changed -from .. import bool_ as bool +bool = np.dtype('bool') _all_dtypes = [int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, bool] -- cgit v1.2.1 From 6c196f540429aa0869e8fb66917a5e76447d2c02 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 15 Apr 2021 15:39:14 -0600 Subject: Fix type promotion consistency for the array API elementwise functions and operators NumPy's type promotion behavior deviates from the spec, which says that type promotion should work independently of shapes or values, in cases where one array is 0-d and the other is not. A helper function is added that works around this issue by adding a dimension to the 0-d array before passing it to the NumPy function. This function is used in elementwise functions and operators. It may still need to be applied to other functions in the namespace. Additionally, this fixes: - The shift operators (<< and >>) should always return the same dtype as the first argument. - NumPy's __pow__ does not type promote the two arguments, so we use the array API pow() in ndarray.__pow__, which does. - The internal _promote_scalar helper function was changed to return an array API ndarray object, as this is simpler with the inclusion of the new _normalize_two_args helper in the operators. --- numpy/_array_api/_array_object.py | 180 ++++++++++++++++++++--------- numpy/_array_api/_elementwise_functions.py | 26 ++++- 2 files changed, 152 insertions(+), 54 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index c5acb5d1d..d1aa8d3fb 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -86,15 +86,12 @@ class ndarray: # Helper function to match the type promotion rules in the spec def _promote_scalar(self, scalar): """ - Returns a promoted version of a Python scalar appropiate for use with + Returns a promoted version of a Python scalar appropriate for use with operations on self. This may raise an OverflowError in cases where the scalar is an integer that is too large to fit in a NumPy integer dtype, or TypeError when the scalar type is incompatible with the dtype of self. - - Note: this helper function returns a NumPy array (NOT a NumPy array - API ndarray). """ if isinstance(scalar, bool): if self.dtype not in _boolean_dtypes: @@ -112,9 +109,38 @@ class ndarray: # behavior for integers within the bounds of the integer dtype. # Outside of those bounds we use the default NumPy behavior (either # cast or raise OverflowError). - return np.array(scalar, self.dtype) + return ndarray._new(np.array(scalar, self.dtype)) + + @staticmethod + def _normalize_two_args(x1, x2): + """ + Normalize inputs to two arg functions to fix type promotion rules + + NumPy deviates from the spec type promotion rules in cases where one + argument is 0-dimensional and the other is not. For example: + + >>> import numpy as np + >>> a = np.array([1.0], dtype=np.float32) + >>> b = np.array(1.0, dtype=np.float64) + >>> np.add(a, b) # The spec says this should be float64 + array([2.], dtype=float32) - # Everything below this is required by the spec. + To fix this, we add a dimension to the 0-dimension array before passing it + through. This works because a dimension would be added anyway from + broadcasting, so the resulting shape is the same, but this prevents NumPy + from not promoting the dtype. + """ + if x1.shape == () and x2.shape != (): + # The _array[None] workaround was chosen because it is relatively + # performant. broadcast_to(x1._array, x2.shape) is much slower. We + # could also manually type promote x2, but that is more complicated + # and about the same performance as this. + x1 = ndarray._new(x1._array[None]) + elif x2.shape == () and x1.shape != (): + x2 = ndarray._new(x2._array[None]) + return (x1, x2) + + # Everything below this line is required by the spec. def __abs__(self: array, /) -> array: """ @@ -129,7 +155,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__add__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__add__(other._array) return self.__class__._new(res) def __and__(self: array, other: array, /) -> array: @@ -138,7 +165,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__and__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__and__(other._array) return self.__class__._new(res) def __array_namespace__(self: array, /, *, api_version: Optional[str] = None) -> object: @@ -177,7 +205,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__eq__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__eq__(other._array) return self.__class__._new(res) def __float__(self: array, /) -> float: @@ -196,7 +225,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__floordiv__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__floordiv__(other._array) return self.__class__._new(res) def __ge__(self: array, other: array, /) -> array: @@ -205,7 +235,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__ge__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__ge__(other._array) return self.__class__._new(res) # Note: A large fraction of allowed indices are disallowed here (see the @@ -331,7 +362,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__gt__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__gt__(other._array) return self.__class__._new(res) def __int__(self: array, /) -> int: @@ -357,7 +389,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__le__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__le__(other._array) return self.__class__._new(res) def __len__(self, /): @@ -373,7 +406,11 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__lshift__(asarray(other)._array) + # Note: The spec requires the return dtype of bitwise_left_shift, and + # hence also __lshift__, to be the same as the first argument. + # np.ndarray.__lshift__ returns a type that is the type promotion of + # the two input types. + res = self._array.__lshift__(other._array).astype(self.dtype) return self.__class__._new(res) def __lt__(self: array, other: array, /) -> array: @@ -382,7 +419,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__lt__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__lt__(other._array) return self.__class__._new(res) def __matmul__(self: array, other: array, /) -> array: @@ -393,7 +431,7 @@ class ndarray: # matmul is not defined for scalars, but without this, we may get # the wrong error message from asarray. other = self._promote_scalar(other) - res = self._array.__matmul__(asarray(other)._array) + res = self._array.__matmul__(other._array) return self.__class__._new(res) def __mod__(self: array, other: array, /) -> array: @@ -402,7 +440,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__mod__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__mod__(other._array) return self.__class__._new(res) def __mul__(self: array, other: array, /) -> array: @@ -411,7 +450,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__mul__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__mul__(other._array) return self.__class__._new(res) def __ne__(self: array, other: array, /) -> array: @@ -420,7 +460,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__ne__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__ne__(other._array) return self.__class__._new(res) def __neg__(self: array, /) -> array: @@ -436,7 +477,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__or__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__or__(other._array) return self.__class__._new(res) def __pos__(self: array, /) -> array: @@ -450,10 +492,13 @@ class ndarray: """ Performs the operation __pow__. """ + from ._elementwise_functions import pow + if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__pow__(asarray(other)._array) - return self.__class__._new(res) + # Note: NumPy's __pow__ does not follow type promotion rules for 0-d + # arrays, so we use pow() here instead. + return pow(self, other) def __rshift__(self: array, other: array, /) -> array: """ @@ -461,7 +506,11 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__rshift__(asarray(other)._array) + # Note: The spec requires the return dtype of bitwise_right_shift, and + # hence also __rshift__, to be the same as the first argument. + # np.ndarray.__rshift__ returns a type that is the type promotion of + # the two input types. + res = self._array.__rshift__(other._array).astype(self.dtype) return self.__class__._new(res) def __setitem__(self, key, value, /): @@ -480,7 +529,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__sub__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__sub__(other._array) return self.__class__._new(res) def __truediv__(self: array, other: array, /) -> array: @@ -489,7 +539,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__truediv__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__truediv__(other._array) return self.__class__._new(res) def __xor__(self: array, other: array, /) -> array: @@ -498,7 +549,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__xor__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__xor__(other._array) return self.__class__._new(res) def __iadd__(self: array, other: array, /) -> array: @@ -507,7 +559,9 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__iadd__(asarray(other)._array) + res = self._array.__iadd__(other._array) + if res.dtype != self.dtype: + raise RuntimeError return self.__class__._new(res) def __radd__(self: array, other: array, /) -> array: @@ -516,7 +570,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__radd__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__radd__(other._array) return self.__class__._new(res) def __iand__(self: array, other: array, /) -> array: @@ -525,7 +580,7 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__iand__(asarray(other)._array) + res = self._array.__iand__(other._array) return self.__class__._new(res) def __rand__(self: array, other: array, /) -> array: @@ -534,7 +589,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__rand__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__rand__(other._array) return self.__class__._new(res) def __ifloordiv__(self: array, other: array, /) -> array: @@ -543,7 +599,7 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__ifloordiv__(asarray(other)._array) + res = self._array.__ifloordiv__(other._array) return self.__class__._new(res) def __rfloordiv__(self: array, other: array, /) -> array: @@ -552,7 +608,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__rfloordiv__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__rfloordiv__(other._array) return self.__class__._new(res) def __ilshift__(self: array, other: array, /) -> array: @@ -561,7 +618,7 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__ilshift__(asarray(other)._array) + res = self._array.__ilshift__(other._array) return self.__class__._new(res) def __rlshift__(self: array, other: array, /) -> array: @@ -570,7 +627,11 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__rlshift__(asarray(other)._array) + # Note: The spec requires the return dtype of bitwise_left_shift, and + # hence also __lshift__, to be the same as the first argument. + # np.ndarray.__lshift__ returns a type that is the type promotion of + # the two input types. + res = self._array.__rlshift__(other._array).astype(other.dtype) return self.__class__._new(res) def __imatmul__(self: array, other: array, /) -> array: @@ -581,7 +642,7 @@ class ndarray: # matmul is not defined for scalars, but without this, we may get # the wrong error message from asarray. other = self._promote_scalar(other) - res = self._array.__imatmul__(asarray(other)._array) + res = self._array.__imatmul__(other._array) return self.__class__._new(res) def __rmatmul__(self: array, other: array, /) -> array: @@ -592,7 +653,7 @@ class ndarray: # matmul is not defined for scalars, but without this, we may get # the wrong error message from asarray. other = self._promote_scalar(other) - res = self._array.__rmatmul__(asarray(other)._array) + res = self._array.__rmatmul__(other._array) return self.__class__._new(res) def __imod__(self: array, other: array, /) -> array: @@ -601,7 +662,7 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__imod__(asarray(other)._array) + res = self._array.__imod__(other._array) return self.__class__._new(res) def __rmod__(self: array, other: array, /) -> array: @@ -610,7 +671,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__rmod__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__rmod__(other._array) return self.__class__._new(res) def __imul__(self: array, other: array, /) -> array: @@ -619,7 +681,7 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__imul__(asarray(other)._array) + res = self._array.__imul__(other._array) return self.__class__._new(res) def __rmul__(self: array, other: array, /) -> array: @@ -628,7 +690,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__rmul__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__rmul__(other._array) return self.__class__._new(res) def __ior__(self: array, other: array, /) -> array: @@ -637,7 +700,7 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__ior__(asarray(other)._array) + res = self._array.__ior__(other._array) return self.__class__._new(res) def __ror__(self: array, other: array, /) -> array: @@ -646,7 +709,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__ror__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__ror__(other._array) return self.__class__._new(res) def __ipow__(self: array, other: array, /) -> array: @@ -655,17 +719,20 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__ipow__(asarray(other)._array) + res = self._array.__ipow__(other._array) return self.__class__._new(res) def __rpow__(self: array, other: array, /) -> array: """ Performs the operation __rpow__. """ + from ._elementwise_functions import pow + if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__rpow__(asarray(other)._array) - return self.__class__._new(res) + # Note: NumPy's __pow__ does not follow the spec type promotion rules + # for 0-d arrays, so we use pow() here instead. + return pow(other, self) def __irshift__(self: array, other: array, /) -> array: """ @@ -673,7 +740,7 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__irshift__(asarray(other)._array) + res = self._array.__irshift__(other._array) return self.__class__._new(res) def __rrshift__(self: array, other: array, /) -> array: @@ -682,7 +749,11 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__rrshift__(asarray(other)._array) + # Note: The spec requires the return dtype of bitwise_right_shift, and + # hence also __rshift__, to be the same as the first argument. + # np.ndarray.__rshift__ returns a type that is the type promotion of + # the two input types. + res = self._array.__rrshift__(other._array).astype(other.dtype) return self.__class__._new(res) def __isub__(self: array, other: array, /) -> array: @@ -691,7 +762,7 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__isub__(asarray(other)._array) + res = self._array.__isub__(other._array) return self.__class__._new(res) def __rsub__(self: array, other: array, /) -> array: @@ -700,7 +771,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__rsub__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__rsub__(other._array) return self.__class__._new(res) def __itruediv__(self: array, other: array, /) -> array: @@ -709,7 +781,7 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__itruediv__(asarray(other)._array) + res = self._array.__itruediv__(other._array) return self.__class__._new(res) def __rtruediv__(self: array, other: array, /) -> array: @@ -718,7 +790,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__rtruediv__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__rtruediv__(other._array) return self.__class__._new(res) def __ixor__(self: array, other: array, /) -> array: @@ -727,7 +800,7 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__ixor__(asarray(other)._array) + res = self._array.__ixor__(other._array) return self.__class__._new(res) def __rxor__(self: array, other: array, /) -> array: @@ -736,7 +809,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__rxor__(asarray(other)._array) + self, other = self._normalize_two_args(self, other) + res = self._array.__rxor__(other._array) return self.__class__._new(res) @property diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index 5ee33f60f..cb855da12 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -1,7 +1,8 @@ from __future__ import annotations from ._dtypes import (_boolean_dtypes, _floating_dtypes, - _integer_dtypes, _integer_or_boolean_dtypes, _numeric_dtypes) + _integer_dtypes, _integer_or_boolean_dtypes, + _numeric_dtypes) from ._array_object import ndarray from typing import TYPE_CHECKING @@ -50,6 +51,7 @@ def add(x1: array, x2: array, /) -> array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in add') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.add(x1._array, x2._array)) # Note: the function name is different here @@ -94,6 +96,7 @@ def atan2(x1: array, x2: array, /) -> array: """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in atan2') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.arctan2(x1._array, x2._array)) # Note: the function name is different here @@ -115,6 +118,7 @@ def bitwise_and(x1: array, x2: array, /) -> array: """ if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: raise TypeError('Only integer_or_boolean dtypes are allowed in bitwise_and') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.bitwise_and(x1._array, x2._array)) # Note: the function name is different here @@ -126,6 +130,7 @@ def bitwise_left_shift(x1: array, x2: array, /) -> array: """ if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: raise TypeError('Only integer dtypes are allowed in bitwise_left_shift') + x1, x2 = ndarray._normalize_two_args(x1, x2) # Note: bitwise_left_shift is only defined for x2 nonnegative. if np.any(x2._array < 0): raise ValueError('bitwise_left_shift(x1, x2) is only defined for x2 >= 0') @@ -153,6 +158,7 @@ def bitwise_or(x1: array, x2: array, /) -> array: """ if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: raise TypeError('Only integer or boolean dtypes are allowed in bitwise_or') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.bitwise_or(x1._array, x2._array)) # Note: the function name is different here @@ -164,6 +170,7 @@ def bitwise_right_shift(x1: array, x2: array, /) -> array: """ if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: raise TypeError('Only integer dtypes are allowed in bitwise_right_shift') + x1, x2 = ndarray._normalize_two_args(x1, x2) # Note: bitwise_right_shift is only defined for x2 nonnegative. if np.any(x2._array < 0): raise ValueError('bitwise_right_shift(x1, x2) is only defined for x2 >= 0') @@ -180,6 +187,7 @@ def bitwise_xor(x1: array, x2: array, /) -> array: """ if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: raise TypeError('Only integer or boolean dtypes are allowed in bitwise_xor') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.bitwise_xor(x1._array, x2._array)) def ceil(x: array, /) -> array: @@ -223,6 +231,7 @@ def divide(x1: array, x2: array, /) -> array: """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in divide') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.divide(x1._array, x2._array)) def equal(x1: array, x2: array, /) -> array: @@ -231,6 +240,7 @@ def equal(x1: array, x2: array, /) -> array: See its docstring for more information. """ + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.equal(x1._array, x2._array)) def exp(x: array, /) -> array: @@ -274,6 +284,7 @@ def floor_divide(x1: array, x2: array, /) -> array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in floor_divide') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.floor_divide(x1._array, x2._array)) def greater(x1: array, x2: array, /) -> array: @@ -284,6 +295,7 @@ def greater(x1: array, x2: array, /) -> array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in greater') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.greater(x1._array, x2._array)) def greater_equal(x1: array, x2: array, /) -> array: @@ -294,6 +306,7 @@ def greater_equal(x1: array, x2: array, /) -> array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in greater_equal') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.greater_equal(x1._array, x2._array)) def isfinite(x: array, /) -> array: @@ -334,6 +347,7 @@ def less(x1: array, x2: array, /) -> array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in less') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.less(x1._array, x2._array)) def less_equal(x1: array, x2: array, /) -> array: @@ -344,6 +358,7 @@ def less_equal(x1: array, x2: array, /) -> array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in less_equal') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.less_equal(x1._array, x2._array)) def log(x: array, /) -> array: @@ -394,6 +409,7 @@ def logaddexp(x1: array, x2: array) -> array: """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in logaddexp') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.logaddexp(x1._array, x2._array)) def logical_and(x1: array, x2: array, /) -> array: @@ -404,6 +420,7 @@ def logical_and(x1: array, x2: array, /) -> array: """ if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: raise TypeError('Only boolean dtypes are allowed in logical_and') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.logical_and(x1._array, x2._array)) def logical_not(x: array, /) -> array: @@ -424,6 +441,7 @@ def logical_or(x1: array, x2: array, /) -> array: """ if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: raise TypeError('Only boolean dtypes are allowed in logical_or') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.logical_or(x1._array, x2._array)) def logical_xor(x1: array, x2: array, /) -> array: @@ -434,6 +452,7 @@ def logical_xor(x1: array, x2: array, /) -> array: """ if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: raise TypeError('Only boolean dtypes are allowed in logical_xor') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.logical_xor(x1._array, x2._array)) def multiply(x1: array, x2: array, /) -> array: @@ -444,6 +463,7 @@ def multiply(x1: array, x2: array, /) -> array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in multiply') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.multiply(x1._array, x2._array)) def negative(x: array, /) -> array: @@ -462,6 +482,7 @@ def not_equal(x1: array, x2: array, /) -> array: See its docstring for more information. """ + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.not_equal(x1._array, x2._array)) def positive(x: array, /) -> array: @@ -483,6 +504,7 @@ def pow(x1: array, x2: array, /) -> array: """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in pow') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.power(x1._array, x2._array)) def remainder(x1: array, x2: array, /) -> array: @@ -493,6 +515,7 @@ def remainder(x1: array, x2: array, /) -> array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in remainder') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.remainder(x1._array, x2._array)) def round(x: array, /) -> array: @@ -563,6 +586,7 @@ def subtract(x1: array, x2: array, /) -> array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in subtract') + x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.subtract(x1._array, x2._array)) def tan(x: array, /) -> array: -- cgit v1.2.1 From b0b2539208a650ef5651fdfb9c16d57c8412d1c7 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 26 Apr 2021 16:55:00 -0600 Subject: Add meshgrid(), broadcast_arrays(), broadcast_to(), and can_cast() to the array API namespace --- numpy/_array_api/__init__.py | 8 ++++---- numpy/_array_api/_creation_functions.py | 15 ++++++++++++-- numpy/_array_api/_data_type_functions.py | 34 +++++++++++++++++++++++++++++--- numpy/_array_api/_types.py | 4 ++-- 4 files changed, 50 insertions(+), 11 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/__init__.py b/numpy/_array_api/__init__.py index ebbe0bb91..56699b09d 100644 --- a/numpy/_array_api/__init__.py +++ b/numpy/_array_api/__init__.py @@ -116,13 +116,13 @@ from ._constants import e, inf, nan, pi __all__ += ['e', 'inf', 'nan', 'pi'] -from ._creation_functions import asarray, arange, empty, empty_like, eye, from_dlpack, full, full_like, linspace, ones, ones_like, zeros, zeros_like +from ._creation_functions import asarray, arange, empty, empty_like, eye, from_dlpack, full, full_like, linspace, meshgrid, ones, ones_like, zeros, zeros_like -__all__ += ['asarray', 'arange', 'empty', 'empty_like', 'eye', 'from_dlpack', 'full', 'full_like', 'linspace', 'ones', 'ones_like', 'zeros', 'zeros_like'] +__all__ += ['asarray', 'arange', 'empty', 'empty_like', 'eye', 'from_dlpack', 'full', 'full_like', 'linspace', 'meshgrid', 'ones', 'ones_like', 'zeros', 'zeros_like'] -from ._data_type_functions import finfo, iinfo, result_type +from ._data_type_functions import broadcast_arrays, broadcast_to, can_cast, finfo, iinfo, result_type -__all__ += ['finfo', 'iinfo', 'result_type'] +__all__ += ['broadcast_arrays', 'broadcast_to', 'can_cast', 'finfo', 'iinfo', 'result_type'] from ._dtypes import int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, bool diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 003b10afb..c6db3cb7b 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -3,8 +3,10 @@ from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import (Optional, SupportsDLPack, SupportsBufferProtocol, Tuple, - Union, array, device, dtype) + from ._types import (List, Optional, SupportsDLPack, + SupportsBufferProtocol, Tuple, Union, array, device, + dtype) + from collections.abc import Sequence from ._dtypes import _all_dtypes import numpy as np @@ -135,6 +137,15 @@ def linspace(start: Union[int, float], stop: Union[int, float], num: int, /, *, raise NotImplementedError("Device support is not yet implemented") return ndarray._new(np.linspace(start, stop, num, dtype=dtype, endpoint=endpoint)) +def meshgrid(*arrays: Sequence[array], indexing: str = 'xy') -> List[array, ...]: + """ + Array API compatible wrapper for :py:func:`np.meshgrid `. + + See its docstring for more information. + """ + from ._array_object import ndarray + return [ndarray._new(array) for array in np.meshgrid(*[a._array for a in arrays], indexing=indexing)] + def ones(shape: Union[int, Tuple[int, ...]], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.ones `. diff --git a/numpy/_array_api/_data_type_functions.py b/numpy/_array_api/_data_type_functions.py index d4816a41f..81eacfe0f 100644 --- a/numpy/_array_api/_data_type_functions.py +++ b/numpy/_array_api/_data_type_functions.py @@ -4,12 +4,40 @@ from ._array_object import ndarray from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Union, array, dtype - -from collections.abc import Sequence + from ._types import List, Tuple, Union, array, dtype + from collections.abc import Sequence import numpy as np +def broadcast_arrays(*arrays: Sequence[array]) -> List[array]: + """ + Array API compatible wrapper for :py:func:`np.broadcast_arrays `. + + See its docstring for more information. + """ + from ._array_object import ndarray + return [ndarray._new(array) for array in np.broadcast_arrays(*[a._array for a in arrays])] + +def broadcast_to(x: array, shape: Tuple[int, ...], /) -> array: + """ + Array API compatible wrapper for :py:func:`np.broadcast_to `. + + See its docstring for more information. + """ + from ._array_object import ndarray + return ndarray._new(np.broadcast_to(x._array, shape)) + +def can_cast(from_: Union[dtype, array], to: dtype, /) -> bool: + """ + Array API compatible wrapper for :py:func:`np.can_cast `. + + See its docstring for more information. + """ + from ._array_object import ndarray + if isinstance(from_, ndarray): + from_ = from_._array + return np.can_cast(from_, to) + def finfo(type: Union[dtype, array], /) -> finfo_object: """ Array API compatible wrapper for :py:func:`np.finfo `. diff --git a/numpy/_array_api/_types.py b/numpy/_array_api/_types.py index 32d03e2a7..36c9aa610 100644 --- a/numpy/_array_api/_types.py +++ b/numpy/_array_api/_types.py @@ -6,10 +6,10 @@ annotations in the function signatures. The functions in the module are only valid for inputs that match the given type annotations. """ -__all__ = ['Literal', 'Optional', 'Tuple', 'Union', 'array', 'device', +__all__ = ['List', 'Literal', 'Optional', 'Tuple', 'Union', 'array', 'device', 'dtype', 'SupportsDLPack', 'SupportsBufferProtocol', 'PyCapsule'] -from typing import Literal, Optional, Tuple, Union, TypeVar +from typing import List, Literal, Optional, Tuple, Union, TypeVar from . import (ndarray, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64) -- cgit v1.2.1 From 6115cce356868d4b62ac25fc6777e3bdd0a7eb57 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 28 Apr 2021 16:36:11 -0600 Subject: Update signatures in the array API namespace from the latest version of the spec --- numpy/_array_api/_creation_functions.py | 18 +++++++++--------- numpy/_array_api/_data_type_functions.py | 2 +- numpy/_array_api/_manipulation_functions.py | 6 +++--- 3 files changed, 13 insertions(+), 13 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index c6db3cb7b..08dc772b5 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -37,7 +37,7 @@ def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, Su raise TypeError(f"The array_api namespace does not support the dtype '{res.dtype}'") return ndarray._new(res) -def arange(start: Union[int, float], /, *, stop: Optional[Union[int, float]] = None, step: Union[int, float] = 1, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def arange(start: Union[int, float], /, stop: Optional[Union[int, float]] = None, step: Union[int, float] = 1, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.arange `. @@ -49,7 +49,7 @@ def arange(start: Union[int, float], /, *, stop: Optional[Union[int, float]] = N raise NotImplementedError("Device support is not yet implemented") return ndarray._new(np.arange(start, stop=stop, step=step, dtype=dtype)) -def empty(shape: Union[int, Tuple[int, ...]], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def empty(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.empty `. @@ -73,7 +73,7 @@ def empty_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[d raise NotImplementedError("Device support is not yet implemented") return ndarray._new(np.empty_like(x._array, dtype=dtype)) -def eye(N: int, /, *, M: Optional[int] = None, k: Optional[int] = 0, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def eye(n_rows: int, n_cols: Optional[int] = None, /, *, k: Optional[int] = 0, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.eye `. @@ -83,13 +83,13 @@ def eye(N: int, /, *, M: Optional[int] = None, k: Optional[int] = 0, dtype: Opti if device is not None: # Note: Device support is not yet implemented on ndarray raise NotImplementedError("Device support is not yet implemented") - return ndarray._new(np.eye(N, M=M, k=k, dtype=dtype)) + return ndarray._new(np.eye(n_rows, M=n_cols, k=k, dtype=dtype)) def from_dlpack(x: object, /) -> array: # Note: dlpack support is not yet implemented on ndarray raise NotImplementedError("DLPack support is not yet implemented") -def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.full `. @@ -108,7 +108,7 @@ def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], /, * raise TypeError("Invalid input to full") return ndarray._new(res) -def full_like(x: array, fill_value: Union[int, float], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def full_like(x: array, /, fill_value: Union[int, float], *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.full_like `. @@ -125,7 +125,7 @@ def full_like(x: array, fill_value: Union[int, float], /, *, dtype: Optional[dty raise TypeError("Invalid input to full_like") return ndarray._new(res) -def linspace(start: Union[int, float], stop: Union[int, float], num: int, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None, endpoint: bool = True) -> array: +def linspace(start: Union[int, float], stop: Union[int, float], /, num: int, *, dtype: Optional[dtype] = None, device: Optional[device] = None, endpoint: bool = True) -> array: """ Array API compatible wrapper for :py:func:`np.linspace `. @@ -146,7 +146,7 @@ def meshgrid(*arrays: Sequence[array], indexing: str = 'xy') -> List[array, ...] from ._array_object import ndarray return [ndarray._new(array) for array in np.meshgrid(*[a._array for a in arrays], indexing=indexing)] -def ones(shape: Union[int, Tuple[int, ...]], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def ones(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.ones `. @@ -170,7 +170,7 @@ def ones_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[de raise NotImplementedError("Device support is not yet implemented") return ndarray._new(np.ones_like(x._array, dtype=dtype)) -def zeros(shape: Union[int, Tuple[int, ...]], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: """ Array API compatible wrapper for :py:func:`np.zeros `. diff --git a/numpy/_array_api/_data_type_functions.py b/numpy/_array_api/_data_type_functions.py index 81eacfe0f..03a857dfc 100644 --- a/numpy/_array_api/_data_type_functions.py +++ b/numpy/_array_api/_data_type_functions.py @@ -18,7 +18,7 @@ def broadcast_arrays(*arrays: Sequence[array]) -> List[array]: from ._array_object import ndarray return [ndarray._new(array) for array in np.broadcast_arrays(*[a._array for a in arrays])] -def broadcast_to(x: array, shape: Tuple[int, ...], /) -> array: +def broadcast_to(x: array, /, shape: Tuple[int, ...]) -> array: """ Array API compatible wrapper for :py:func:`np.broadcast_to `. diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index 6ac7be02f..fb9e25baa 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -18,7 +18,7 @@ def concat(arrays: Tuple[array, ...], /, *, axis: Optional[int] = 0) -> array: arrays = tuple(a._array for a in arrays) return ndarray._new(np.concatenate(arrays, axis=axis)) -def expand_dims(x: array, axis: int, /) -> array: +def expand_dims(x: array, /, *, axis: int) -> array: """ Array API compatible wrapper for :py:func:`np.expand_dims `. @@ -34,7 +34,7 @@ def flip(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> """ return ndarray._new(np.flip(x._array, axis=axis)) -def reshape(x: array, shape: Tuple[int, ...], /) -> array: +def reshape(x: array, /, shape: Tuple[int, ...]) -> array: """ Array API compatible wrapper for :py:func:`np.reshape `. @@ -42,7 +42,7 @@ def reshape(x: array, shape: Tuple[int, ...], /) -> array: """ return ndarray._new(np.reshape(x._array, shape)) -def roll(x: array, shift: Union[int, Tuple[int, ...]], /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: +def roll(x: array, /, shift: Union[int, Tuple[int, ...]], *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: """ Array API compatible wrapper for :py:func:`np.roll `. -- cgit v1.2.1 From edf68c5bcc0df076af25f65c561350d98c05402f Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 30 Apr 2021 17:18:01 -0600 Subject: Fix some error messages --- numpy/_array_api/_array_object.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index d1aa8d3fb..1410020e2 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -215,7 +215,7 @@ class ndarray: """ # Note: This is an error here. if self._array.shape != (): - raise TypeError("bool is only allowed on arrays with shape ()") + raise TypeError("float is only allowed on arrays with shape ()") res = self._array.__float__() return res @@ -372,7 +372,7 @@ class ndarray: """ # Note: This is an error here. if self._array.shape != (): - raise TypeError("bool is only allowed on arrays with shape ()") + raise TypeError("int is only allowed on arrays with shape ()") res = self._array.__int__() return res -- cgit v1.2.1 From 219968727b6a28e8564a22284cb630a808bc0c04 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 10 May 2021 15:07:36 -0600 Subject: Fix the array API norm() function --- numpy/_array_api/_linear_algebra_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_linear_algebra_functions.py b/numpy/_array_api/_linear_algebra_functions.py index f57fe292a..99a386866 100644 --- a/numpy/_array_api/_linear_algebra_functions.py +++ b/numpy/_array_api/_linear_algebra_functions.py @@ -122,7 +122,7 @@ def norm(x: array, /, *, axis: Optional[Union[int, Tuple[int, int]]] = None, kee """ # Note: this is different from the default behavior if axis == None and x.ndim > 2: - x = x.flatten() + x = ndarray._new(x._array.flatten()) # Note: this function is being imported from a nondefault namespace return ndarray._new(np.linalg.norm(x._array, axis=axis, keepdims=keepdims, ord=ord)) -- cgit v1.2.1 From 533d0468f12e89b1b4b299f0344a31378853b012 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 10 May 2021 15:18:42 -0600 Subject: Fix array API squeeze() and stack() --- numpy/_array_api/_manipulation_functions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index fb9e25baa..b461f6b6b 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -56,7 +56,7 @@ def squeeze(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) See its docstring for more information. """ - return ndarray._array(np.squeeze(x._array, axis=axis)) + return ndarray._new(np.squeeze(x._array, axis=axis)) def stack(arrays: Tuple[array, ...], /, *, axis: int = 0) -> array: """ @@ -65,4 +65,4 @@ def stack(arrays: Tuple[array, ...], /, *, axis: int = 0) -> array: See its docstring for more information. """ arrays = tuple(a._array for a in arrays) - return ndarray._array(np.stack(arrays, axis=axis)) + return ndarray._new(np.stack(arrays, axis=axis)) -- cgit v1.2.1 From 4817784c6e1050034faabb1b3d04382fe8997b41 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 10 May 2021 15:35:40 -0600 Subject: Make the array API constants Python floats --- numpy/_array_api/_constants.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_constants.py b/numpy/_array_api/_constants.py index 5fde34625..9541941e7 100644 --- a/numpy/_array_api/_constants.py +++ b/numpy/_array_api/_constants.py @@ -1,9 +1,6 @@ -from ._array_object import ndarray -from ._dtypes import float64 - import numpy as np -e = ndarray._new(np.array(np.e, dtype=float64)) -inf = ndarray._new(np.array(np.inf, dtype=float64)) -nan = ndarray._new(np.array(np.nan, dtype=float64)) -pi = ndarray._new(np.array(np.pi, dtype=float64)) +e = np.e +inf = np.inf +nan = np.nan +pi = np.pi -- cgit v1.2.1 From 96f40fed3f08043986adb3db860cf0e647b27085 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 17 May 2021 16:43:31 -0600 Subject: Ignore warnings in array API functions that can raise them --- numpy/_array_api/_array_object.py | 25 ++++++++++++++++++++++++- numpy/_array_api/_elementwise_functions.py | 25 +++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 1410020e2..119992bdc 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -149,6 +149,7 @@ class ndarray: res = self._array.__abs__() return self.__class__._new(res) + @np.errstate(all='ignore') def __add__(self: array, other: array, /) -> array: """ Performs the operation __add__. @@ -196,6 +197,7 @@ class ndarray: """ Performs the operation __dlpack_device__. """ + # Note: device support is required for this res = self._array.__dlpack_device__() return self.__class__._new(res) @@ -219,6 +221,7 @@ class ndarray: res = self._array.__float__() return res + @np.errstate(all='ignore') def __floordiv__(self: array, other: array, /) -> array: """ Performs the operation __floordiv__. @@ -434,6 +437,7 @@ class ndarray: res = self._array.__matmul__(other._array) return self.__class__._new(res) + @np.errstate(all='ignore') def __mod__(self: array, other: array, /) -> array: """ Performs the operation __mod__. @@ -444,6 +448,7 @@ class ndarray: res = self._array.__mod__(other._array) return self.__class__._new(res) + @np.errstate(all='ignore') def __mul__(self: array, other: array, /) -> array: """ Performs the operation __mul__. @@ -488,6 +493,7 @@ class ndarray: res = self._array.__pos__() return self.__class__._new(res) + @np.errstate(all='ignore') def __pow__(self: array, other: array, /) -> array: """ Performs the operation __pow__. @@ -523,6 +529,7 @@ class ndarray: res = self._array.__setitem__(key, asarray(value)._array) return self.__class__._new(res) + @np.errstate(all='ignore') def __sub__(self: array, other: array, /) -> array: """ Performs the operation __sub__. @@ -533,6 +540,7 @@ class ndarray: res = self._array.__sub__(other._array) return self.__class__._new(res) + @np.errstate(all='ignore') def __truediv__(self: array, other: array, /) -> array: """ Performs the operation __truediv__. @@ -553,6 +561,7 @@ class ndarray: res = self._array.__xor__(other._array) return self.__class__._new(res) + @np.errstate(all='ignore') def __iadd__(self: array, other: array, /) -> array: """ Performs the operation __iadd__. @@ -564,6 +573,7 @@ class ndarray: raise RuntimeError return self.__class__._new(res) + @np.errstate(all='ignore') def __radd__(self: array, other: array, /) -> array: """ Performs the operation __radd__. @@ -593,6 +603,7 @@ class ndarray: res = self._array.__rand__(other._array) return self.__class__._new(res) + @np.errstate(all='ignore') def __ifloordiv__(self: array, other: array, /) -> array: """ Performs the operation __ifloordiv__. @@ -602,6 +613,7 @@ class ndarray: res = self._array.__ifloordiv__(other._array) return self.__class__._new(res) + @np.errstate(all='ignore') def __rfloordiv__(self: array, other: array, /) -> array: """ Performs the operation __rfloordiv__. @@ -656,6 +668,7 @@ class ndarray: res = self._array.__rmatmul__(other._array) return self.__class__._new(res) + @np.errstate(all='ignore') def __imod__(self: array, other: array, /) -> array: """ Performs the operation __imod__. @@ -665,6 +678,7 @@ class ndarray: res = self._array.__imod__(other._array) return self.__class__._new(res) + @np.errstate(all='ignore') def __rmod__(self: array, other: array, /) -> array: """ Performs the operation __rmod__. @@ -675,6 +689,7 @@ class ndarray: res = self._array.__rmod__(other._array) return self.__class__._new(res) + @np.errstate(all='ignore') def __imul__(self: array, other: array, /) -> array: """ Performs the operation __imul__. @@ -684,6 +699,7 @@ class ndarray: res = self._array.__imul__(other._array) return self.__class__._new(res) + @np.errstate(all='ignore') def __rmul__(self: array, other: array, /) -> array: """ Performs the operation __rmul__. @@ -713,6 +729,7 @@ class ndarray: res = self._array.__ror__(other._array) return self.__class__._new(res) + @np.errstate(all='ignore') def __ipow__(self: array, other: array, /) -> array: """ Performs the operation __ipow__. @@ -722,6 +739,7 @@ class ndarray: res = self._array.__ipow__(other._array) return self.__class__._new(res) + @np.errstate(all='ignore') def __rpow__(self: array, other: array, /) -> array: """ Performs the operation __rpow__. @@ -756,6 +774,7 @@ class ndarray: res = self._array.__rrshift__(other._array).astype(other.dtype) return self.__class__._new(res) + @np.errstate(all='ignore') def __isub__(self: array, other: array, /) -> array: """ Performs the operation __isub__. @@ -765,6 +784,7 @@ class ndarray: res = self._array.__isub__(other._array) return self.__class__._new(res) + @np.errstate(all='ignore') def __rsub__(self: array, other: array, /) -> array: """ Performs the operation __rsub__. @@ -775,6 +795,7 @@ class ndarray: res = self._array.__rsub__(other._array) return self.__class__._new(res) + @np.errstate(all='ignore') def __itruediv__(self: array, other: array, /) -> array: """ Performs the operation __itruediv__. @@ -784,6 +805,7 @@ class ndarray: res = self._array.__itruediv__(other._array) return self.__class__._new(res) + @np.errstate(all='ignore') def __rtruediv__(self: array, other: array, /) -> array: """ Performs the operation __rtruediv__. @@ -829,7 +851,8 @@ class ndarray: See its docstring for more information. """ - return self._array.device + # Note: device support is required for this + raise NotImplementedError("The device attribute is not yet implemented") @property def ndim(self): diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index cb855da12..197e77324 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -22,6 +22,7 @@ def abs(x: array, /) -> array: return ndarray._new(np.abs(x._array)) # Note: the function name is different here +@np.errstate(all='ignore') def acos(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arccos `. @@ -33,6 +34,7 @@ def acos(x: array, /) -> array: return ndarray._new(np.arccos(x._array)) # Note: the function name is different here +@np.errstate(all='ignore') def acosh(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arccosh `. @@ -43,6 +45,7 @@ def acosh(x: array, /) -> array: raise TypeError('Only floating-point dtypes are allowed in acosh') return ndarray._new(np.arccosh(x._array)) +@np.errstate(all='ignore') def add(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.add `. @@ -55,6 +58,7 @@ def add(x1: array, x2: array, /) -> array: return ndarray._new(np.add(x1._array, x2._array)) # Note: the function name is different here +@np.errstate(all='ignore') def asin(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arcsin `. @@ -66,6 +70,7 @@ def asin(x: array, /) -> array: return ndarray._new(np.arcsin(x._array)) # Note: the function name is different here +@np.errstate(all='ignore') def asinh(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arcsinh `. @@ -100,6 +105,7 @@ def atan2(x1: array, x2: array, /) -> array: return ndarray._new(np.arctan2(x1._array, x2._array)) # Note: the function name is different here +@np.errstate(all='ignore') def atanh(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.arctanh `. @@ -203,6 +209,7 @@ def ceil(x: array, /) -> array: return x return ndarray._new(np.ceil(x._array)) +@np.errstate(all='ignore') def cos(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.cos `. @@ -213,6 +220,7 @@ def cos(x: array, /) -> array: raise TypeError('Only floating-point dtypes are allowed in cos') return ndarray._new(np.cos(x._array)) +@np.errstate(all='ignore') def cosh(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.cosh `. @@ -223,6 +231,7 @@ def cosh(x: array, /) -> array: raise TypeError('Only floating-point dtypes are allowed in cosh') return ndarray._new(np.cosh(x._array)) +@np.errstate(all='ignore') def divide(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.divide `. @@ -243,6 +252,7 @@ def equal(x1: array, x2: array, /) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.equal(x1._array, x2._array)) +@np.errstate(all='ignore') def exp(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.exp `. @@ -253,6 +263,7 @@ def exp(x: array, /) -> array: raise TypeError('Only floating-point dtypes are allowed in exp') return ndarray._new(np.exp(x._array)) +@np.errstate(all='ignore') def expm1(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.expm1 `. @@ -276,6 +287,7 @@ def floor(x: array, /) -> array: return x return ndarray._new(np.floor(x._array)) +@np.errstate(all='ignore') def floor_divide(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.floor_divide `. @@ -361,6 +373,7 @@ def less_equal(x1: array, x2: array, /) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.less_equal(x1._array, x2._array)) +@np.errstate(all='ignore') def log(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.log `. @@ -371,6 +384,7 @@ def log(x: array, /) -> array: raise TypeError('Only floating-point dtypes are allowed in log') return ndarray._new(np.log(x._array)) +@np.errstate(all='ignore') def log1p(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.log1p `. @@ -381,6 +395,7 @@ def log1p(x: array, /) -> array: raise TypeError('Only floating-point dtypes are allowed in log1p') return ndarray._new(np.log1p(x._array)) +@np.errstate(all='ignore') def log2(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.log2 `. @@ -391,6 +406,7 @@ def log2(x: array, /) -> array: raise TypeError('Only floating-point dtypes are allowed in log2') return ndarray._new(np.log2(x._array)) +@np.errstate(all='ignore') def log10(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.log10 `. @@ -455,6 +471,7 @@ def logical_xor(x1: array, x2: array, /) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.logical_xor(x1._array, x2._array)) +@np.errstate(all='ignore') def multiply(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.multiply `. @@ -496,6 +513,7 @@ def positive(x: array, /) -> array: return ndarray._new(np.positive(x._array)) # Note: the function name is different here +@np.errstate(all='ignore') def pow(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.power `. @@ -507,6 +525,7 @@ def pow(x1: array, x2: array, /) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.power(x1._array, x2._array)) +@np.errstate(all='ignore') def remainder(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.remainder `. @@ -538,6 +557,7 @@ def sign(x: array, /) -> array: raise TypeError('Only numeric dtypes are allowed in sign') return ndarray._new(np.sign(x._array)) +@np.errstate(all='ignore') def sin(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.sin `. @@ -548,6 +568,7 @@ def sin(x: array, /) -> array: raise TypeError('Only floating-point dtypes are allowed in sin') return ndarray._new(np.sin(x._array)) +@np.errstate(all='ignore') def sinh(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.sinh `. @@ -558,6 +579,7 @@ def sinh(x: array, /) -> array: raise TypeError('Only floating-point dtypes are allowed in sinh') return ndarray._new(np.sinh(x._array)) +@np.errstate(all='ignore') def square(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.square `. @@ -568,6 +590,7 @@ def square(x: array, /) -> array: raise TypeError('Only numeric dtypes are allowed in square') return ndarray._new(np.square(x._array)) +@np.errstate(all='ignore') def sqrt(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.sqrt `. @@ -578,6 +601,7 @@ def sqrt(x: array, /) -> array: raise TypeError('Only floating-point dtypes are allowed in sqrt') return ndarray._new(np.sqrt(x._array)) +@np.errstate(all='ignore') def subtract(x1: array, x2: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.subtract `. @@ -589,6 +613,7 @@ def subtract(x1: array, x2: array, /) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.subtract(x1._array, x2._array)) +@np.errstate(all='ignore') def tan(x: array, /) -> array: """ Array API compatible wrapper for :py:func:`np.tan `. -- cgit v1.2.1 From be1ee6c93e63da3a7766a504304755283fb1411a Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 20 May 2021 16:38:44 -0600 Subject: Update signatures from the latest version of the array API spec --- numpy/_array_api/_array_object.py | 84 ++++++++++++++--------------- numpy/_array_api/_manipulation_functions.py | 2 +- 2 files changed, 43 insertions(+), 43 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 119992bdc..30858b7c5 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -150,7 +150,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __add__(self: array, other: array, /) -> array: + def __add__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __add__. """ @@ -160,7 +160,7 @@ class ndarray: res = self._array.__add__(other._array) return self.__class__._new(res) - def __and__(self: array, other: array, /) -> array: + def __and__(self: array, other: Union[int, bool, array], /) -> array: """ Performs the operation __and__. """ @@ -201,7 +201,7 @@ class ndarray: res = self._array.__dlpack_device__() return self.__class__._new(res) - def __eq__(self: array, other: array, /) -> array: + def __eq__(self: array, other: Union[int, float, bool, array], /) -> array: """ Performs the operation __eq__. """ @@ -222,7 +222,7 @@ class ndarray: return res @np.errstate(all='ignore') - def __floordiv__(self: array, other: array, /) -> array: + def __floordiv__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __floordiv__. """ @@ -232,7 +232,7 @@ class ndarray: res = self._array.__floordiv__(other._array) return self.__class__._new(res) - def __ge__(self: array, other: array, /) -> array: + def __ge__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __ge__. """ @@ -359,7 +359,7 @@ class ndarray: res = self._array.__getitem__(key) return self.__class__._new(res) - def __gt__(self: array, other: array, /) -> array: + def __gt__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __gt__. """ @@ -386,7 +386,7 @@ class ndarray: res = self._array.__invert__() return self.__class__._new(res) - def __le__(self: array, other: array, /) -> array: + def __le__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __le__. """ @@ -403,7 +403,7 @@ class ndarray: res = self._array.__len__() return self.__class__._new(res) - def __lshift__(self: array, other: array, /) -> array: + def __lshift__(self: array, other: Union[int, array], /) -> array: """ Performs the operation __lshift__. """ @@ -416,7 +416,7 @@ class ndarray: res = self._array.__lshift__(other._array).astype(self.dtype) return self.__class__._new(res) - def __lt__(self: array, other: array, /) -> array: + def __lt__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __lt__. """ @@ -438,7 +438,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __mod__(self: array, other: array, /) -> array: + def __mod__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __mod__. """ @@ -449,7 +449,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __mul__(self: array, other: array, /) -> array: + def __mul__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __mul__. """ @@ -459,7 +459,7 @@ class ndarray: res = self._array.__mul__(other._array) return self.__class__._new(res) - def __ne__(self: array, other: array, /) -> array: + def __ne__(self: array, other: Union[int, float, bool, array], /) -> array: """ Performs the operation __ne__. """ @@ -476,7 +476,7 @@ class ndarray: res = self._array.__neg__() return self.__class__._new(res) - def __or__(self: array, other: array, /) -> array: + def __or__(self: array, other: Union[int, bool, array], /) -> array: """ Performs the operation __or__. """ @@ -494,7 +494,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __pow__(self: array, other: array, /) -> array: + def __pow__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __pow__. """ @@ -506,7 +506,7 @@ class ndarray: # arrays, so we use pow() here instead. return pow(self, other) - def __rshift__(self: array, other: array, /) -> array: + def __rshift__(self: array, other: Union[int, array], /) -> array: """ Performs the operation __rshift__. """ @@ -530,7 +530,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __sub__(self: array, other: array, /) -> array: + def __sub__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __sub__. """ @@ -541,7 +541,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __truediv__(self: array, other: array, /) -> array: + def __truediv__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __truediv__. """ @@ -551,7 +551,7 @@ class ndarray: res = self._array.__truediv__(other._array) return self.__class__._new(res) - def __xor__(self: array, other: array, /) -> array: + def __xor__(self: array, other: Union[int, bool, array], /) -> array: """ Performs the operation __xor__. """ @@ -562,7 +562,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __iadd__(self: array, other: array, /) -> array: + def __iadd__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __iadd__. """ @@ -574,7 +574,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __radd__(self: array, other: array, /) -> array: + def __radd__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __radd__. """ @@ -584,7 +584,7 @@ class ndarray: res = self._array.__radd__(other._array) return self.__class__._new(res) - def __iand__(self: array, other: array, /) -> array: + def __iand__(self: array, other: Union[int, bool, array], /) -> array: """ Performs the operation __iand__. """ @@ -593,7 +593,7 @@ class ndarray: res = self._array.__iand__(other._array) return self.__class__._new(res) - def __rand__(self: array, other: array, /) -> array: + def __rand__(self: array, other: Union[int, bool, array], /) -> array: """ Performs the operation __rand__. """ @@ -604,7 +604,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __ifloordiv__(self: array, other: array, /) -> array: + def __ifloordiv__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __ifloordiv__. """ @@ -614,7 +614,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __rfloordiv__(self: array, other: array, /) -> array: + def __rfloordiv__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __rfloordiv__. """ @@ -624,7 +624,7 @@ class ndarray: res = self._array.__rfloordiv__(other._array) return self.__class__._new(res) - def __ilshift__(self: array, other: array, /) -> array: + def __ilshift__(self: array, other: Union[int, array], /) -> array: """ Performs the operation __ilshift__. """ @@ -633,7 +633,7 @@ class ndarray: res = self._array.__ilshift__(other._array) return self.__class__._new(res) - def __rlshift__(self: array, other: array, /) -> array: + def __rlshift__(self: array, other: Union[int, array], /) -> array: """ Performs the operation __rlshift__. """ @@ -669,7 +669,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __imod__(self: array, other: array, /) -> array: + def __imod__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __imod__. """ @@ -679,7 +679,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __rmod__(self: array, other: array, /) -> array: + def __rmod__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __rmod__. """ @@ -690,7 +690,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __imul__(self: array, other: array, /) -> array: + def __imul__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __imul__. """ @@ -700,7 +700,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __rmul__(self: array, other: array, /) -> array: + def __rmul__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __rmul__. """ @@ -710,7 +710,7 @@ class ndarray: res = self._array.__rmul__(other._array) return self.__class__._new(res) - def __ior__(self: array, other: array, /) -> array: + def __ior__(self: array, other: Union[int, bool, array], /) -> array: """ Performs the operation __ior__. """ @@ -719,7 +719,7 @@ class ndarray: res = self._array.__ior__(other._array) return self.__class__._new(res) - def __ror__(self: array, other: array, /) -> array: + def __ror__(self: array, other: Union[int, bool, array], /) -> array: """ Performs the operation __ror__. """ @@ -730,7 +730,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __ipow__(self: array, other: array, /) -> array: + def __ipow__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __ipow__. """ @@ -740,7 +740,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __rpow__(self: array, other: array, /) -> array: + def __rpow__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __rpow__. """ @@ -752,7 +752,7 @@ class ndarray: # for 0-d arrays, so we use pow() here instead. return pow(other, self) - def __irshift__(self: array, other: array, /) -> array: + def __irshift__(self: array, other: Union[int, array], /) -> array: """ Performs the operation __irshift__. """ @@ -761,7 +761,7 @@ class ndarray: res = self._array.__irshift__(other._array) return self.__class__._new(res) - def __rrshift__(self: array, other: array, /) -> array: + def __rrshift__(self: array, other: Union[int, array], /) -> array: """ Performs the operation __rrshift__. """ @@ -775,7 +775,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __isub__(self: array, other: array, /) -> array: + def __isub__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __isub__. """ @@ -785,7 +785,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __rsub__(self: array, other: array, /) -> array: + def __rsub__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __rsub__. """ @@ -796,7 +796,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __itruediv__(self: array, other: array, /) -> array: + def __itruediv__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __itruediv__. """ @@ -806,7 +806,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __rtruediv__(self: array, other: array, /) -> array: + def __rtruediv__(self: array, other: Union[int, float, array], /) -> array: """ Performs the operation __rtruediv__. """ @@ -816,7 +816,7 @@ class ndarray: res = self._array.__rtruediv__(other._array) return self.__class__._new(res) - def __ixor__(self: array, other: array, /) -> array: + def __ixor__(self: array, other: Union[int, bool, array], /) -> array: """ Performs the operation __ixor__. """ @@ -825,7 +825,7 @@ class ndarray: res = self._array.__ixor__(other._array) return self.__class__._new(res) - def __rxor__(self: array, other: array, /) -> array: + def __rxor__(self: array, other: Union[int, bool, array], /) -> array: """ Performs the operation __rxor__. """ diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index b461f6b6b..5f7b0a451 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -50,7 +50,7 @@ def roll(x: array, /, shift: Union[int, Tuple[int, ...]], *, axis: Optional[Unio """ return ndarray._new(np.roll(x._array, shift, axis=axis)) -def squeeze(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: +def squeeze(x: array, /, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: """ Array API compatible wrapper for :py:func:`np.squeeze `. -- cgit v1.2.1 From 25042301ae655ace524978d2aa7cd08997fd4111 Mon Sep 17 00:00:00 2001 From: iameskild Date: Fri, 11 Jun 2021 07:03:40 -0700 Subject: Add check if __setitem__ indx is ma --- numpy/ma/core.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 82e5e7155..cd3753745 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -3381,8 +3381,11 @@ class MaskedArray(ndarray): _mask[indx] = mval elif not self._hardmask: # Set the data, then the mask - _data[indx] = dval - _mask[indx] = mval + if isinstance(indx, masked_array): + _data[indx.data] = dval + else: + _data[indx] = dval + _mask[indx] = mval elif hasattr(indx, 'dtype') and (indx.dtype == MaskType): indx = indx * umath.logical_not(_mask) _data[indx] = dval -- cgit v1.2.1 From 084619c079f5dd1f555ee2036b84136ec562b9ad Mon Sep 17 00:00:00 2001 From: ImenRajhi Date: Sat, 12 Jun 2021 11:36:36 +0000 Subject: added a test for condition assignment for masked arrays --- numpy/ma/tests/test_old_ma.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'numpy') diff --git a/numpy/ma/tests/test_old_ma.py b/numpy/ma/tests/test_old_ma.py index ab003b94e..7ac4c328f 100644 --- a/numpy/ma/tests/test_old_ma.py +++ b/numpy/ma/tests/test_old_ma.py @@ -697,6 +697,13 @@ class TestMa: assert_equal(b[0].shape, ()) assert_equal(b[1].shape, ()) + def test_assignment_by_condition(self): + # Test for gh-18951 + a = array([1, 2, 3, 4], mask=[1, 0, 1, 0]) + c = a>=3 + a[c] = 5 + assert_(a[2] is masked) + class TestUfuncs: def setup(self): -- cgit v1.2.1 From 2ecb55d48e8d54b059304d4898b403d0f9031afa Mon Sep 17 00:00:00 2001 From: ImenRajhi Date: Mon, 14 Jun 2021 14:15:00 +0000 Subject: added tests for 18951 fix --- numpy/ma/tests/test_old_ma.py | 7 ------- 1 file changed, 7 deletions(-) (limited to 'numpy') diff --git a/numpy/ma/tests/test_old_ma.py b/numpy/ma/tests/test_old_ma.py index 7ac4c328f..ab003b94e 100644 --- a/numpy/ma/tests/test_old_ma.py +++ b/numpy/ma/tests/test_old_ma.py @@ -697,13 +697,6 @@ class TestMa: assert_equal(b[0].shape, ()) assert_equal(b[1].shape, ()) - def test_assignment_by_condition(self): - # Test for gh-18951 - a = array([1, 2, 3, 4], mask=[1, 0, 1, 0]) - c = a>=3 - a[c] = 5 - assert_(a[2] is masked) - class TestUfuncs: def setup(self): -- cgit v1.2.1 From 8ca435cff334671152bed11c817ec04ca5a702de Mon Sep 17 00:00:00 2001 From: ImenRajhi Date: Mon, 14 Jun 2021 14:19:14 +0000 Subject: added tests for 18951 fix --- numpy/ma/tests/test_old_ma.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'numpy') diff --git a/numpy/ma/tests/test_old_ma.py b/numpy/ma/tests/test_old_ma.py index ab003b94e..7ac4c328f 100644 --- a/numpy/ma/tests/test_old_ma.py +++ b/numpy/ma/tests/test_old_ma.py @@ -697,6 +697,13 @@ class TestMa: assert_equal(b[0].shape, ()) assert_equal(b[1].shape, ()) + def test_assignment_by_condition(self): + # Test for gh-18951 + a = array([1, 2, 3, 4], mask=[1, 0, 1, 0]) + c = a>=3 + a[c] = 5 + assert_(a[2] is masked) + class TestUfuncs: def setup(self): -- cgit v1.2.1 From b92df509ca6767f65891b1b0eac6e64ee7621ee4 Mon Sep 17 00:00:00 2001 From: iameskild Date: Mon, 14 Jun 2021 08:54:40 -0700 Subject: Fix lint E225 complaint --- numpy/ma/tests/test_old_ma.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/ma/tests/test_old_ma.py b/numpy/ma/tests/test_old_ma.py index 7ac4c328f..2e0097dc8 100644 --- a/numpy/ma/tests/test_old_ma.py +++ b/numpy/ma/tests/test_old_ma.py @@ -700,7 +700,7 @@ class TestMa: def test_assignment_by_condition(self): # Test for gh-18951 a = array([1, 2, 3, 4], mask=[1, 0, 1, 0]) - c = a>=3 + c = a >= 3 a[c] = 5 assert_(a[2] is masked) -- cgit v1.2.1 From f6015d2754dde04342ca2a0d719ca7f01d6e0dcb Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 14 Jun 2021 15:30:28 -0600 Subject: Update a function signature from the array API spec --- numpy/_array_api/_array_object.py | 4 ++-- numpy/_array_api/_types.py | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 30858b7c5..5f169ab66 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -22,7 +22,7 @@ from ._dtypes import _boolean_dtypes, _integer_dtypes, _floating_dtypes from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Optional, PyCapsule, Tuple, Union, array + from ._types import Any, Optional, PyCapsule, Tuple, Union, array import numpy as np @@ -186,7 +186,7 @@ class ndarray: res = self._array.__bool__() return res - def __dlpack__(self: array, /, *, stream: Optional[int] = None) -> PyCapsule: + def __dlpack__(self: array, /, *, stream: Optional[Union[int, Any]] = None) -> PyCapsule: """ Performs the operation __dlpack__. """ diff --git a/numpy/_array_api/_types.py b/numpy/_array_api/_types.py index 36c9aa610..1086699fc 100644 --- a/numpy/_array_api/_types.py +++ b/numpy/_array_api/_types.py @@ -6,10 +6,11 @@ annotations in the function signatures. The functions in the module are only valid for inputs that match the given type annotations. """ -__all__ = ['List', 'Literal', 'Optional', 'Tuple', 'Union', 'array', 'device', - 'dtype', 'SupportsDLPack', 'SupportsBufferProtocol', 'PyCapsule'] +__all__ = ['Any', 'List', 'Literal', 'Optional', 'Tuple', 'Union', 'array', + 'device', 'dtype', 'SupportsDLPack', 'SupportsBufferProtocol', + 'PyCapsule'] -from typing import List, Literal, Optional, Tuple, Union, TypeVar +from typing import Any, List, Literal, Optional, Tuple, Union, TypeVar from . import (ndarray, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64) -- cgit v1.2.1 From cad21e94b58b125a4264f154e91a1730dcf550da Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 1 Jul 2021 15:48:19 -0600 Subject: Update the linear algebra functions in the array API namespace For now, only the functions in from the main spec namespace are implemented. The remaining linear algebra functions are part of an extension in the spec, and will be implemented in a future pull request. This is because the linear algebra functions are relatively complicated, so they will be easier to review separately. This also updates those functions that do remain for now to be more compliant with the spec. --- numpy/_array_api/__init__.py | 15 ++- numpy/_array_api/_linear_algebra_functions.py | 181 ++++---------------------- 2 files changed, 31 insertions(+), 165 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/__init__.py b/numpy/_array_api/__init__.py index 56699b09d..e39a2c7d0 100644 --- a/numpy/_array_api/__init__.py +++ b/numpy/_array_api/__init__.py @@ -47,8 +47,8 @@ A few notes about the current state of this submodule: - np.argmin and np.argmax do not implement the keepdims keyword argument. - - Some linear algebra functions in the spec are still a work in progress (to - be added soon). These will be updated once the spec is. + - The linear algebra extension in the spec will be added in a future pull +request. - Some tests in the test suite are still not fully correct in that they test all datatypes whereas certain functions are only defined for a subset of @@ -132,13 +132,14 @@ from ._elementwise_functions import abs, acos, acosh, add, asin, asinh, atan, at __all__ += ['abs', 'acos', 'acosh', 'add', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'bitwise_and', 'bitwise_left_shift', 'bitwise_invert', 'bitwise_or', 'bitwise_right_shift', 'bitwise_xor', 'ceil', 'cos', 'cosh', 'divide', 'equal', 'exp', 'expm1', 'floor', 'floor_divide', 'greater', 'greater_equal', 'isfinite', 'isinf', 'isnan', 'less', 'less_equal', 'log', 'log1p', 'log2', 'log10', 'logaddexp', 'logical_and', 'logical_not', 'logical_or', 'logical_xor', 'multiply', 'negative', 'not_equal', 'positive', 'pow', 'remainder', 'round', 'sign', 'sin', 'sinh', 'square', 'sqrt', 'subtract', 'tan', 'tanh', 'trunc'] -from ._linear_algebra_functions import cross, det, diagonal, inv, norm, outer, trace, transpose +# einsum is not yet implemented in the array API spec. -__all__ += ['cross', 'det', 'diagonal', 'inv', 'norm', 'outer', 'trace', 'transpose'] +# from ._linear_algebra_functions import einsum +# __all__ += ['einsum'] -# from ._linear_algebra_functions import cholesky, cross, det, diagonal, dot, eig, eigvalsh, einsum, inv, lstsq, matmul, matrix_power, matrix_rank, norm, outer, pinv, qr, slogdet, solve, svd, trace, transpose -# -# __all__ += ['cholesky', 'cross', 'det', 'diagonal', 'dot', 'eig', 'eigvalsh', 'einsum', 'inv', 'lstsq', 'matmul', 'matrix_power', 'matrix_rank', 'norm', 'outer', 'pinv', 'qr', 'slogdet', 'solve', 'svd', 'trace', 'transpose'] +from ._linear_algebra_functions import matmul, tensordot, transpose, vecdot + +__all__ += ['matmul', 'tensordot', 'transpose', 'vecdot'] from ._manipulation_functions import concat, expand_dims, flip, reshape, roll, squeeze, stack diff --git a/numpy/_array_api/_linear_algebra_functions.py b/numpy/_array_api/_linear_algebra_functions.py index 99a386866..461770641 100644 --- a/numpy/_array_api/_linear_algebra_functions.py +++ b/numpy/_array_api/_linear_algebra_functions.py @@ -1,70 +1,16 @@ from __future__ import annotations from ._array_object import ndarray +from ._dtypes import _numeric_dtypes from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Literal, Optional, Tuple, Union, array + from ._types import Optional, Sequence, Tuple, Union, array import numpy as np -# def cholesky(): -# """ -# Array API compatible wrapper for :py:func:`np.cholesky `. -# -# See its docstring for more information. -# """ -# return np.cholesky() - -def cross(x1: array, x2: array, /, *, axis: int = -1) -> array: - """ - Array API compatible wrapper for :py:func:`np.cross `. +# einsum is not yet implemented in the array API spec. - See its docstring for more information. - """ - return ndarray._new(np.cross(x1._array, x2._array, axis=axis)) - -def det(x: array, /) -> array: - """ - Array API compatible wrapper for :py:func:`np.linalg.det `. - - See its docstring for more information. - """ - # Note: this function is being imported from a nondefault namespace - return ndarray._new(np.linalg.det(x._array)) - -def diagonal(x: array, /, *, axis1: int = 0, axis2: int = 1, offset: int = 0) -> array: - """ - Array API compatible wrapper for :py:func:`np.diagonal `. - - See its docstring for more information. - """ - return ndarray._new(np.diagonal(x._array, axis1=axis1, axis2=axis2, offset=offset)) - -# def dot(): -# """ -# Array API compatible wrapper for :py:func:`np.dot `. -# -# See its docstring for more information. -# """ -# return np.dot() -# -# def eig(): -# """ -# Array API compatible wrapper for :py:func:`np.eig `. -# -# See its docstring for more information. -# """ -# return np.eig() -# -# def eigvalsh(): -# """ -# Array API compatible wrapper for :py:func:`np.eigvalsh `. -# -# See its docstring for more information. -# """ -# return np.eigvalsh() -# # def einsum(): # """ # Array API compatible wrapper for :py:func:`np.einsum `. @@ -73,114 +19,27 @@ def diagonal(x: array, /, *, axis1: int = 0, axis2: int = 1, offset: int = 0) -> # """ # return np.einsum() -def inv(x: array, /) -> array: - """ - Array API compatible wrapper for :py:func:`np.linalg.inv `. - - See its docstring for more information. - """ - # Note: this function is being imported from a nondefault namespace - return ndarray._new(np.linalg.inv(x._array)) - -# def lstsq(): -# """ -# Array API compatible wrapper for :py:func:`np.lstsq `. -# -# See its docstring for more information. -# """ -# return np.lstsq() -# -# def matmul(): -# """ -# Array API compatible wrapper for :py:func:`np.matmul `. -# -# See its docstring for more information. -# """ -# return np.matmul() -# -# def matrix_power(): -# """ -# Array API compatible wrapper for :py:func:`np.matrix_power `. -# -# See its docstring for more information. -# """ -# return np.matrix_power() -# -# def matrix_rank(): -# """ -# Array API compatible wrapper for :py:func:`np.matrix_rank `. -# -# See its docstring for more information. -# """ -# return np.matrix_rank() - -def norm(x: array, /, *, axis: Optional[Union[int, Tuple[int, int]]] = None, keepdims: bool = False, ord: Optional[Union[int, float, Literal[np.inf, -np.inf, 'fro', 'nuc']]] = None) -> array: - """ - Array API compatible wrapper for :py:func:`np.linalg.norm `. - - See its docstring for more information. - """ - # Note: this is different from the default behavior - if axis == None and x.ndim > 2: - x = ndarray._new(x._array.flatten()) - # Note: this function is being imported from a nondefault namespace - return ndarray._new(np.linalg.norm(x._array, axis=axis, keepdims=keepdims, ord=ord)) - -def outer(x1: array, x2: array, /) -> array: +def matmul(x1: array, x2: array, /) -> array: """ - Array API compatible wrapper for :py:func:`np.outer `. + Array API compatible wrapper for :py:func:`np.matmul `. See its docstring for more information. """ - return ndarray._new(np.outer(x1._array, x2._array)) + # Note: the restriction to numeric dtypes only is different from + # np.matmul. + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in matmul') -# def pinv(): -# """ -# Array API compatible wrapper for :py:func:`np.pinv `. -# -# See its docstring for more information. -# """ -# return np.pinv() -# -# def qr(): -# """ -# Array API compatible wrapper for :py:func:`np.qr `. -# -# See its docstring for more information. -# """ -# return np.qr() -# -# def slogdet(): -# """ -# Array API compatible wrapper for :py:func:`np.slogdet `. -# -# See its docstring for more information. -# """ -# return np.slogdet() -# -# def solve(): -# """ -# Array API compatible wrapper for :py:func:`np.solve `. -# -# See its docstring for more information. -# """ -# return np.solve() -# -# def svd(): -# """ -# Array API compatible wrapper for :py:func:`np.svd `. -# -# See its docstring for more information. -# """ -# return np.svd() + return ndarray._new(np.matmul(x1._array, x2._array)) -def trace(x: array, /, *, axis1: int = 0, axis2: int = 1, offset: int = 0) -> array: - """ - Array API compatible wrapper for :py:func:`np.trace `. +# Note: axes must be a tuple, unlike np.tensordot where it can be an array or array-like. +def tensordot(x1: array, x2: array, /, *, axes: Union[int, Tuple[Sequence[int], Sequence[int]]] = 2) -> array: + # Note: the restriction to numeric dtypes only is different from + # np.tensordot. + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in tensordot') - See its docstring for more information. - """ - return ndarray._new(np.asarray(np.trace(x._array, axis1=axis1, axis2=axis2, offset=offset))) + return ndarray._new(np.tensordot(x1._array, x2._array, axes=axes)) def transpose(x: array, /, *, axes: Optional[Tuple[int, ...]] = None) -> array: """ @@ -189,3 +48,9 @@ def transpose(x: array, /, *, axes: Optional[Tuple[int, ...]] = None) -> array: See its docstring for more information. """ return ndarray._new(np.transpose(x._array, axes=axes)) + +# Note: vecdot is not in NumPy +def vecdot(x1: array, x2: array, /, *, axis: Optional[int] = None) -> array: + if axis is None: + axis = -1 + return tensordot(x1, x2, axes=((axis,), (axis,))) -- cgit v1.2.1 From 01780805fabd160514a25d44972d527c3c99f8c8 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 8 Jul 2021 16:09:50 -0600 Subject: Fix in-place operators to not recreate the wrapper class --- numpy/_array_api/_array_object.py | 50 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 26 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 5f169ab66..a3de25478 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -568,10 +568,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__iadd__(other._array) - if res.dtype != self.dtype: - raise RuntimeError - return self.__class__._new(res) + self._array.__iadd__(other._array) + return self @np.errstate(all='ignore') def __radd__(self: array, other: Union[int, float, array], /) -> array: @@ -590,8 +588,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__iand__(other._array) - return self.__class__._new(res) + self._array.__iand__(other._array) + return self def __rand__(self: array, other: Union[int, bool, array], /) -> array: """ @@ -610,8 +608,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__ifloordiv__(other._array) - return self.__class__._new(res) + self._array.__ifloordiv__(other._array) + return self @np.errstate(all='ignore') def __rfloordiv__(self: array, other: Union[int, float, array], /) -> array: @@ -630,8 +628,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__ilshift__(other._array) - return self.__class__._new(res) + self._array.__ilshift__(other._array) + return self def __rlshift__(self: array, other: Union[int, array], /) -> array: """ @@ -675,8 +673,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__imod__(other._array) - return self.__class__._new(res) + self._array.__imod__(other._array) + return self @np.errstate(all='ignore') def __rmod__(self: array, other: Union[int, float, array], /) -> array: @@ -696,8 +694,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__imul__(other._array) - return self.__class__._new(res) + self._array.__imul__(other._array) + return self @np.errstate(all='ignore') def __rmul__(self: array, other: Union[int, float, array], /) -> array: @@ -716,8 +714,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__ior__(other._array) - return self.__class__._new(res) + self._array.__ior__(other._array) + return self def __ror__(self: array, other: Union[int, bool, array], /) -> array: """ @@ -736,8 +734,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__ipow__(other._array) - return self.__class__._new(res) + self._array.__ipow__(other._array) + return self @np.errstate(all='ignore') def __rpow__(self: array, other: Union[int, float, array], /) -> array: @@ -758,8 +756,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__irshift__(other._array) - return self.__class__._new(res) + self._array.__irshift__(other._array) + return self def __rrshift__(self: array, other: Union[int, array], /) -> array: """ @@ -781,8 +779,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__isub__(other._array) - return self.__class__._new(res) + self._array.__isub__(other._array) + return self @np.errstate(all='ignore') def __rsub__(self: array, other: Union[int, float, array], /) -> array: @@ -802,8 +800,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__itruediv__(other._array) - return self.__class__._new(res) + self._array.__itruediv__(other._array) + return self @np.errstate(all='ignore') def __rtruediv__(self: array, other: Union[int, float, array], /) -> array: @@ -822,8 +820,8 @@ class ndarray: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - res = self._array.__ixor__(other._array) - return self.__class__._new(res) + self._array.__ixor__(other._array) + return self def __rxor__(self: array, other: Union[int, bool, array], /) -> array: """ -- cgit v1.2.1 From 13796236295b344ee83e79c8a33ad6205c0095db Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 8 Jul 2021 16:10:27 -0600 Subject: Fix the __imatmul__ method in the array API namespace --- numpy/_array_api/_array_object.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index a3de25478..8f7252160 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -648,12 +648,21 @@ class ndarray: """ Performs the operation __imatmul__. """ + # Note: NumPy does not implement __imatmul__. + if isinstance(other, (int, float, bool)): # matmul is not defined for scalars, but without this, we may get # the wrong error message from asarray. other = self._promote_scalar(other) - res = self._array.__imatmul__(other._array) - return self.__class__._new(res) + # __imatmul__ can only be allowed when it would not change the shape + # of self. + other_shape = other.shape + if self.shape == () or other_shape == (): + raise ValueError("@= requires at least one dimension") + if len(other_shape) == 1 or other_shape[-1] != other_shape[-2]: + raise ValueError("@= cannot change the shape of the input array") + self._array[:] = self._array.__matmul__(other._array) + return self def __rmatmul__(self: array, other: array, /) -> array: """ -- cgit v1.2.1 From fc1ff6fc3045482a72c359689ee7bfa7e3299985 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 8 Jul 2021 16:56:27 -0600 Subject: Capitalize the names of the type hint types in the array API That way they aren't ambiguous with the attributes with the same names. --- numpy/_array_api/_array_object.py | 118 +++++++++++++------------- numpy/_array_api/_creation_functions.py | 32 +++---- numpy/_array_api/_data_type_functions.py | 14 +-- numpy/_array_api/_elementwise_functions.py | 114 ++++++++++++------------- numpy/_array_api/_linear_algebra_functions.py | 10 +-- numpy/_array_api/_manipulation_functions.py | 16 ++-- numpy/_array_api/_searching_functions.py | 10 +-- numpy/_array_api/_set_functions.py | 4 +- numpy/_array_api/_sorting_functions.py | 6 +- numpy/_array_api/_statistical_functions.py | 16 ++-- numpy/_array_api/_types.py | 10 +-- numpy/_array_api/_utility_functions.py | 6 +- 12 files changed, 178 insertions(+), 178 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 8f7252160..89ec3ba1a 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -22,7 +22,7 @@ from ._dtypes import _boolean_dtypes, _integer_dtypes, _floating_dtypes from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Any, Optional, PyCapsule, Tuple, Union, array + from ._types import Any, Optional, PyCapsule, Tuple, Union, Array import numpy as np @@ -71,13 +71,13 @@ class ndarray: # These functions are not required by the spec, but are implemented for # the sake of usability. - def __str__(self: array, /) -> str: + def __str__(self: Array, /) -> str: """ Performs the operation __str__. """ return self._array.__str__().replace('array', 'ndarray') - def __repr__(self: array, /) -> str: + def __repr__(self: Array, /) -> str: """ Performs the operation __repr__. """ @@ -142,7 +142,7 @@ class ndarray: # Everything below this line is required by the spec. - def __abs__(self: array, /) -> array: + def __abs__(self: Array, /) -> Array: """ Performs the operation __abs__. """ @@ -150,7 +150,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __add__(self: array, other: Union[int, float, array], /) -> array: + def __add__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __add__. """ @@ -160,7 +160,7 @@ class ndarray: res = self._array.__add__(other._array) return self.__class__._new(res) - def __and__(self: array, other: Union[int, bool, array], /) -> array: + def __and__(self: Array, other: Union[int, bool, Array], /) -> Array: """ Performs the operation __and__. """ @@ -170,13 +170,13 @@ class ndarray: res = self._array.__and__(other._array) return self.__class__._new(res) - def __array_namespace__(self: array, /, *, api_version: Optional[str] = None) -> object: + def __array_namespace__(self: Array, /, *, api_version: Optional[str] = None) -> object: if api_version is not None: raise ValueError("Unrecognized array API version") from numpy import _array_api return _array_api - def __bool__(self: array, /) -> bool: + def __bool__(self: Array, /) -> bool: """ Performs the operation __bool__. """ @@ -186,14 +186,14 @@ class ndarray: res = self._array.__bool__() return res - def __dlpack__(self: array, /, *, stream: Optional[Union[int, Any]] = None) -> PyCapsule: + def __dlpack__(self: Array, /, *, stream: Optional[Union[int, Any]] = None) -> PyCapsule: """ Performs the operation __dlpack__. """ res = self._array.__dlpack__(stream=None) return self.__class__._new(res) - def __dlpack_device__(self: array, /) -> Tuple[IntEnum, int]: + def __dlpack_device__(self: Array, /) -> Tuple[IntEnum, int]: """ Performs the operation __dlpack_device__. """ @@ -201,7 +201,7 @@ class ndarray: res = self._array.__dlpack_device__() return self.__class__._new(res) - def __eq__(self: array, other: Union[int, float, bool, array], /) -> array: + def __eq__(self: Array, other: Union[int, float, bool, Array], /) -> Array: """ Performs the operation __eq__. """ @@ -211,7 +211,7 @@ class ndarray: res = self._array.__eq__(other._array) return self.__class__._new(res) - def __float__(self: array, /) -> float: + def __float__(self: Array, /) -> float: """ Performs the operation __float__. """ @@ -222,7 +222,7 @@ class ndarray: return res @np.errstate(all='ignore') - def __floordiv__(self: array, other: Union[int, float, array], /) -> array: + def __floordiv__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __floordiv__. """ @@ -232,7 +232,7 @@ class ndarray: res = self._array.__floordiv__(other._array) return self.__class__._new(res) - def __ge__(self: array, other: Union[int, float, array], /) -> array: + def __ge__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __ge__. """ @@ -349,7 +349,7 @@ class ndarray: # ndarray() form, like a list of booleans. raise IndexError("Only integers, slices (`:`), ellipsis (`...`), and boolean arrays are valid indices in the array API namespace") - def __getitem__(self: array, key: Union[int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], array], /) -> array: + def __getitem__(self: Array, key: Union[int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], Array], /) -> Array: """ Performs the operation __getitem__. """ @@ -359,7 +359,7 @@ class ndarray: res = self._array.__getitem__(key) return self.__class__._new(res) - def __gt__(self: array, other: Union[int, float, array], /) -> array: + def __gt__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __gt__. """ @@ -369,7 +369,7 @@ class ndarray: res = self._array.__gt__(other._array) return self.__class__._new(res) - def __int__(self: array, /) -> int: + def __int__(self: Array, /) -> int: """ Performs the operation __int__. """ @@ -379,14 +379,14 @@ class ndarray: res = self._array.__int__() return res - def __invert__(self: array, /) -> array: + def __invert__(self: Array, /) -> Array: """ Performs the operation __invert__. """ res = self._array.__invert__() return self.__class__._new(res) - def __le__(self: array, other: Union[int, float, array], /) -> array: + def __le__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __le__. """ @@ -403,7 +403,7 @@ class ndarray: res = self._array.__len__() return self.__class__._new(res) - def __lshift__(self: array, other: Union[int, array], /) -> array: + def __lshift__(self: Array, other: Union[int, Array], /) -> Array: """ Performs the operation __lshift__. """ @@ -416,7 +416,7 @@ class ndarray: res = self._array.__lshift__(other._array).astype(self.dtype) return self.__class__._new(res) - def __lt__(self: array, other: Union[int, float, array], /) -> array: + def __lt__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __lt__. """ @@ -426,7 +426,7 @@ class ndarray: res = self._array.__lt__(other._array) return self.__class__._new(res) - def __matmul__(self: array, other: array, /) -> array: + def __matmul__(self: Array, other: Array, /) -> Array: """ Performs the operation __matmul__. """ @@ -438,7 +438,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __mod__(self: array, other: Union[int, float, array], /) -> array: + def __mod__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __mod__. """ @@ -449,7 +449,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __mul__(self: array, other: Union[int, float, array], /) -> array: + def __mul__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __mul__. """ @@ -459,7 +459,7 @@ class ndarray: res = self._array.__mul__(other._array) return self.__class__._new(res) - def __ne__(self: array, other: Union[int, float, bool, array], /) -> array: + def __ne__(self: Array, other: Union[int, float, bool, Array], /) -> Array: """ Performs the operation __ne__. """ @@ -469,14 +469,14 @@ class ndarray: res = self._array.__ne__(other._array) return self.__class__._new(res) - def __neg__(self: array, /) -> array: + def __neg__(self: Array, /) -> Array: """ Performs the operation __neg__. """ res = self._array.__neg__() return self.__class__._new(res) - def __or__(self: array, other: Union[int, bool, array], /) -> array: + def __or__(self: Array, other: Union[int, bool, Array], /) -> Array: """ Performs the operation __or__. """ @@ -486,7 +486,7 @@ class ndarray: res = self._array.__or__(other._array) return self.__class__._new(res) - def __pos__(self: array, /) -> array: + def __pos__(self: Array, /) -> Array: """ Performs the operation __pos__. """ @@ -494,7 +494,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __pow__(self: array, other: Union[int, float, array], /) -> array: + def __pow__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __pow__. """ @@ -506,7 +506,7 @@ class ndarray: # arrays, so we use pow() here instead. return pow(self, other) - def __rshift__(self: array, other: Union[int, array], /) -> array: + def __rshift__(self: Array, other: Union[int, Array], /) -> Array: """ Performs the operation __rshift__. """ @@ -530,7 +530,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __sub__(self: array, other: Union[int, float, array], /) -> array: + def __sub__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __sub__. """ @@ -541,7 +541,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __truediv__(self: array, other: Union[int, float, array], /) -> array: + def __truediv__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __truediv__. """ @@ -551,7 +551,7 @@ class ndarray: res = self._array.__truediv__(other._array) return self.__class__._new(res) - def __xor__(self: array, other: Union[int, bool, array], /) -> array: + def __xor__(self: Array, other: Union[int, bool, Array], /) -> Array: """ Performs the operation __xor__. """ @@ -562,7 +562,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __iadd__(self: array, other: Union[int, float, array], /) -> array: + def __iadd__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __iadd__. """ @@ -572,7 +572,7 @@ class ndarray: return self @np.errstate(all='ignore') - def __radd__(self: array, other: Union[int, float, array], /) -> array: + def __radd__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __radd__. """ @@ -582,7 +582,7 @@ class ndarray: res = self._array.__radd__(other._array) return self.__class__._new(res) - def __iand__(self: array, other: Union[int, bool, array], /) -> array: + def __iand__(self: Array, other: Union[int, bool, Array], /) -> Array: """ Performs the operation __iand__. """ @@ -591,7 +591,7 @@ class ndarray: self._array.__iand__(other._array) return self - def __rand__(self: array, other: Union[int, bool, array], /) -> array: + def __rand__(self: Array, other: Union[int, bool, Array], /) -> Array: """ Performs the operation __rand__. """ @@ -602,7 +602,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __ifloordiv__(self: array, other: Union[int, float, array], /) -> array: + def __ifloordiv__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __ifloordiv__. """ @@ -612,7 +612,7 @@ class ndarray: return self @np.errstate(all='ignore') - def __rfloordiv__(self: array, other: Union[int, float, array], /) -> array: + def __rfloordiv__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __rfloordiv__. """ @@ -622,7 +622,7 @@ class ndarray: res = self._array.__rfloordiv__(other._array) return self.__class__._new(res) - def __ilshift__(self: array, other: Union[int, array], /) -> array: + def __ilshift__(self: Array, other: Union[int, Array], /) -> Array: """ Performs the operation __ilshift__. """ @@ -631,7 +631,7 @@ class ndarray: self._array.__ilshift__(other._array) return self - def __rlshift__(self: array, other: Union[int, array], /) -> array: + def __rlshift__(self: Array, other: Union[int, Array], /) -> Array: """ Performs the operation __rlshift__. """ @@ -644,7 +644,7 @@ class ndarray: res = self._array.__rlshift__(other._array).astype(other.dtype) return self.__class__._new(res) - def __imatmul__(self: array, other: array, /) -> array: + def __imatmul__(self: Array, other: Array, /) -> Array: """ Performs the operation __imatmul__. """ @@ -664,7 +664,7 @@ class ndarray: self._array[:] = self._array.__matmul__(other._array) return self - def __rmatmul__(self: array, other: array, /) -> array: + def __rmatmul__(self: Array, other: Array, /) -> Array: """ Performs the operation __rmatmul__. """ @@ -676,7 +676,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __imod__(self: array, other: Union[int, float, array], /) -> array: + def __imod__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __imod__. """ @@ -686,7 +686,7 @@ class ndarray: return self @np.errstate(all='ignore') - def __rmod__(self: array, other: Union[int, float, array], /) -> array: + def __rmod__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __rmod__. """ @@ -697,7 +697,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __imul__(self: array, other: Union[int, float, array], /) -> array: + def __imul__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __imul__. """ @@ -707,7 +707,7 @@ class ndarray: return self @np.errstate(all='ignore') - def __rmul__(self: array, other: Union[int, float, array], /) -> array: + def __rmul__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __rmul__. """ @@ -717,7 +717,7 @@ class ndarray: res = self._array.__rmul__(other._array) return self.__class__._new(res) - def __ior__(self: array, other: Union[int, bool, array], /) -> array: + def __ior__(self: Array, other: Union[int, bool, Array], /) -> Array: """ Performs the operation __ior__. """ @@ -726,7 +726,7 @@ class ndarray: self._array.__ior__(other._array) return self - def __ror__(self: array, other: Union[int, bool, array], /) -> array: + def __ror__(self: Array, other: Union[int, bool, Array], /) -> Array: """ Performs the operation __ror__. """ @@ -737,7 +737,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __ipow__(self: array, other: Union[int, float, array], /) -> array: + def __ipow__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __ipow__. """ @@ -747,7 +747,7 @@ class ndarray: return self @np.errstate(all='ignore') - def __rpow__(self: array, other: Union[int, float, array], /) -> array: + def __rpow__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __rpow__. """ @@ -759,7 +759,7 @@ class ndarray: # for 0-d arrays, so we use pow() here instead. return pow(other, self) - def __irshift__(self: array, other: Union[int, array], /) -> array: + def __irshift__(self: Array, other: Union[int, Array], /) -> Array: """ Performs the operation __irshift__. """ @@ -768,7 +768,7 @@ class ndarray: self._array.__irshift__(other._array) return self - def __rrshift__(self: array, other: Union[int, array], /) -> array: + def __rrshift__(self: Array, other: Union[int, Array], /) -> Array: """ Performs the operation __rrshift__. """ @@ -782,7 +782,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __isub__(self: array, other: Union[int, float, array], /) -> array: + def __isub__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __isub__. """ @@ -792,7 +792,7 @@ class ndarray: return self @np.errstate(all='ignore') - def __rsub__(self: array, other: Union[int, float, array], /) -> array: + def __rsub__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __rsub__. """ @@ -803,7 +803,7 @@ class ndarray: return self.__class__._new(res) @np.errstate(all='ignore') - def __itruediv__(self: array, other: Union[int, float, array], /) -> array: + def __itruediv__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __itruediv__. """ @@ -813,7 +813,7 @@ class ndarray: return self @np.errstate(all='ignore') - def __rtruediv__(self: array, other: Union[int, float, array], /) -> array: + def __rtruediv__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __rtruediv__. """ @@ -823,7 +823,7 @@ class ndarray: res = self._array.__rtruediv__(other._array) return self.__class__._new(res) - def __ixor__(self: array, other: Union[int, bool, array], /) -> array: + def __ixor__(self: Array, other: Union[int, bool, Array], /) -> Array: """ Performs the operation __ixor__. """ @@ -832,7 +832,7 @@ class ndarray: self._array.__ixor__(other._array) return self - def __rxor__(self: array, other: Union[int, bool, array], /) -> array: + def __rxor__(self: Array, other: Union[int, bool, Array], /) -> Array: """ Performs the operation __rxor__. """ diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 08dc772b5..9845dd70f 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -4,14 +4,14 @@ from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: from ._types import (List, Optional, SupportsDLPack, - SupportsBufferProtocol, Tuple, Union, array, device, - dtype) + SupportsBufferProtocol, Tuple, Union, Array, Device, + Dtype) from collections.abc import Sequence from ._dtypes import _all_dtypes import numpy as np -def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, SupportsBufferProtocol], /, *, dtype: Optional[dtype] = None, device: Optional[device] = None, copy: Optional[bool] = None) -> array: +def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, SupportsBufferProtocol], /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, copy: Optional[bool] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.asarray `. @@ -37,7 +37,7 @@ def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, Su raise TypeError(f"The array_api namespace does not support the dtype '{res.dtype}'") return ndarray._new(res) -def arange(start: Union[int, float], /, stop: Optional[Union[int, float]] = None, step: Union[int, float] = 1, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def arange(start: Union[int, float], /, stop: Optional[Union[int, float]] = None, step: Union[int, float] = 1, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.arange `. @@ -49,7 +49,7 @@ def arange(start: Union[int, float], /, stop: Optional[Union[int, float]] = None raise NotImplementedError("Device support is not yet implemented") return ndarray._new(np.arange(start, stop=stop, step=step, dtype=dtype)) -def empty(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def empty(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.empty `. @@ -61,7 +61,7 @@ def empty(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[dtype] = None, raise NotImplementedError("Device support is not yet implemented") return ndarray._new(np.empty(shape, dtype=dtype)) -def empty_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def empty_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.empty_like `. @@ -73,7 +73,7 @@ def empty_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[d raise NotImplementedError("Device support is not yet implemented") return ndarray._new(np.empty_like(x._array, dtype=dtype)) -def eye(n_rows: int, n_cols: Optional[int] = None, /, *, k: Optional[int] = 0, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def eye(n_rows: int, n_cols: Optional[int] = None, /, *, k: Optional[int] = 0, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.eye `. @@ -85,11 +85,11 @@ def eye(n_rows: int, n_cols: Optional[int] = None, /, *, k: Optional[int] = 0, d raise NotImplementedError("Device support is not yet implemented") return ndarray._new(np.eye(n_rows, M=n_cols, k=k, dtype=dtype)) -def from_dlpack(x: object, /) -> array: +def from_dlpack(x: object, /) -> Array: # Note: dlpack support is not yet implemented on ndarray raise NotImplementedError("DLPack support is not yet implemented") -def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.full `. @@ -108,7 +108,7 @@ def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], *, d raise TypeError("Invalid input to full") return ndarray._new(res) -def full_like(x: array, /, fill_value: Union[int, float], *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def full_like(x: Array, /, fill_value: Union[int, float], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.full_like `. @@ -125,7 +125,7 @@ def full_like(x: array, /, fill_value: Union[int, float], *, dtype: Optional[dty raise TypeError("Invalid input to full_like") return ndarray._new(res) -def linspace(start: Union[int, float], stop: Union[int, float], /, num: int, *, dtype: Optional[dtype] = None, device: Optional[device] = None, endpoint: bool = True) -> array: +def linspace(start: Union[int, float], stop: Union[int, float], /, num: int, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, endpoint: bool = True) -> Array: """ Array API compatible wrapper for :py:func:`np.linspace `. @@ -137,7 +137,7 @@ def linspace(start: Union[int, float], stop: Union[int, float], /, num: int, *, raise NotImplementedError("Device support is not yet implemented") return ndarray._new(np.linspace(start, stop, num, dtype=dtype, endpoint=endpoint)) -def meshgrid(*arrays: Sequence[array], indexing: str = 'xy') -> List[array, ...]: +def meshgrid(*arrays: Sequence[Array], indexing: str = 'xy') -> List[Array, ...]: """ Array API compatible wrapper for :py:func:`np.meshgrid `. @@ -146,7 +146,7 @@ def meshgrid(*arrays: Sequence[array], indexing: str = 'xy') -> List[array, ...] from ._array_object import ndarray return [ndarray._new(array) for array in np.meshgrid(*[a._array for a in arrays], indexing=indexing)] -def ones(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def ones(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.ones `. @@ -158,7 +158,7 @@ def ones(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[dtype] = None, d raise NotImplementedError("Device support is not yet implemented") return ndarray._new(np.ones(shape, dtype=dtype)) -def ones_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def ones_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.ones_like `. @@ -170,7 +170,7 @@ def ones_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[de raise NotImplementedError("Device support is not yet implemented") return ndarray._new(np.ones_like(x._array, dtype=dtype)) -def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.zeros `. @@ -182,7 +182,7 @@ def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[dtype] = None, raise NotImplementedError("Device support is not yet implemented") return ndarray._new(np.zeros(shape, dtype=dtype)) -def zeros_like(x: array, /, *, dtype: Optional[dtype] = None, device: Optional[device] = None) -> array: +def zeros_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.zeros_like `. diff --git a/numpy/_array_api/_data_type_functions.py b/numpy/_array_api/_data_type_functions.py index 03a857dfc..5ab611fd3 100644 --- a/numpy/_array_api/_data_type_functions.py +++ b/numpy/_array_api/_data_type_functions.py @@ -4,12 +4,12 @@ from ._array_object import ndarray from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import List, Tuple, Union, array, dtype + from ._types import List, Tuple, Union, Array, Dtype from collections.abc import Sequence import numpy as np -def broadcast_arrays(*arrays: Sequence[array]) -> List[array]: +def broadcast_arrays(*arrays: Sequence[Array]) -> List[Array]: """ Array API compatible wrapper for :py:func:`np.broadcast_arrays `. @@ -18,7 +18,7 @@ def broadcast_arrays(*arrays: Sequence[array]) -> List[array]: from ._array_object import ndarray return [ndarray._new(array) for array in np.broadcast_arrays(*[a._array for a in arrays])] -def broadcast_to(x: array, /, shape: Tuple[int, ...]) -> array: +def broadcast_to(x: Array, /, shape: Tuple[int, ...]) -> Array: """ Array API compatible wrapper for :py:func:`np.broadcast_to `. @@ -27,7 +27,7 @@ def broadcast_to(x: array, /, shape: Tuple[int, ...]) -> array: from ._array_object import ndarray return ndarray._new(np.broadcast_to(x._array, shape)) -def can_cast(from_: Union[dtype, array], to: dtype, /) -> bool: +def can_cast(from_: Union[Dtype, Array], to: Dtype, /) -> bool: """ Array API compatible wrapper for :py:func:`np.can_cast `. @@ -38,7 +38,7 @@ def can_cast(from_: Union[dtype, array], to: dtype, /) -> bool: from_ = from_._array return np.can_cast(from_, to) -def finfo(type: Union[dtype, array], /) -> finfo_object: +def finfo(type: Union[Dtype, Array], /) -> finfo_object: """ Array API compatible wrapper for :py:func:`np.finfo `. @@ -46,7 +46,7 @@ def finfo(type: Union[dtype, array], /) -> finfo_object: """ return np.finfo(type) -def iinfo(type: Union[dtype, array], /) -> iinfo_object: +def iinfo(type: Union[Dtype, Array], /) -> iinfo_object: """ Array API compatible wrapper for :py:func:`np.iinfo `. @@ -54,7 +54,7 @@ def iinfo(type: Union[dtype, array], /) -> iinfo_object: """ return np.iinfo(type) -def result_type(*arrays_and_dtypes: Sequence[Union[array, dtype]]) -> dtype: +def result_type(*arrays_and_dtypes: Sequence[Union[Array, Dtype]]) -> Dtype: """ Array API compatible wrapper for :py:func:`np.result_type `. diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index 197e77324..ae265181a 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -7,11 +7,11 @@ from ._array_object import ndarray from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import array + from ._types import Array import numpy as np -def abs(x: array, /) -> array: +def abs(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.abs `. @@ -23,7 +23,7 @@ def abs(x: array, /) -> array: # Note: the function name is different here @np.errstate(all='ignore') -def acos(x: array, /) -> array: +def acos(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.arccos `. @@ -35,7 +35,7 @@ def acos(x: array, /) -> array: # Note: the function name is different here @np.errstate(all='ignore') -def acosh(x: array, /) -> array: +def acosh(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.arccosh `. @@ -46,7 +46,7 @@ def acosh(x: array, /) -> array: return ndarray._new(np.arccosh(x._array)) @np.errstate(all='ignore') -def add(x1: array, x2: array, /) -> array: +def add(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.add `. @@ -59,7 +59,7 @@ def add(x1: array, x2: array, /) -> array: # Note: the function name is different here @np.errstate(all='ignore') -def asin(x: array, /) -> array: +def asin(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.arcsin `. @@ -71,7 +71,7 @@ def asin(x: array, /) -> array: # Note: the function name is different here @np.errstate(all='ignore') -def asinh(x: array, /) -> array: +def asinh(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.arcsinh `. @@ -82,7 +82,7 @@ def asinh(x: array, /) -> array: return ndarray._new(np.arcsinh(x._array)) # Note: the function name is different here -def atan(x: array, /) -> array: +def atan(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.arctan `. @@ -93,7 +93,7 @@ def atan(x: array, /) -> array: return ndarray._new(np.arctan(x._array)) # Note: the function name is different here -def atan2(x1: array, x2: array, /) -> array: +def atan2(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.arctan2 `. @@ -106,7 +106,7 @@ def atan2(x1: array, x2: array, /) -> array: # Note: the function name is different here @np.errstate(all='ignore') -def atanh(x: array, /) -> array: +def atanh(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.arctanh `. @@ -116,7 +116,7 @@ def atanh(x: array, /) -> array: raise TypeError('Only floating-point dtypes are allowed in atanh') return ndarray._new(np.arctanh(x._array)) -def bitwise_and(x1: array, x2: array, /) -> array: +def bitwise_and(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.bitwise_and `. @@ -128,7 +128,7 @@ def bitwise_and(x1: array, x2: array, /) -> array: return ndarray._new(np.bitwise_and(x1._array, x2._array)) # Note: the function name is different here -def bitwise_left_shift(x1: array, x2: array, /) -> array: +def bitwise_left_shift(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.left_shift `. @@ -146,7 +146,7 @@ def bitwise_left_shift(x1: array, x2: array, /) -> array: return ndarray._new(np.left_shift(x1._array, x2._array).astype(x1.dtype)) # Note: the function name is different here -def bitwise_invert(x: array, /) -> array: +def bitwise_invert(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.invert `. @@ -156,7 +156,7 @@ def bitwise_invert(x: array, /) -> array: raise TypeError('Only integer or boolean dtypes are allowed in bitwise_invert') return ndarray._new(np.invert(x._array)) -def bitwise_or(x1: array, x2: array, /) -> array: +def bitwise_or(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.bitwise_or `. @@ -168,7 +168,7 @@ def bitwise_or(x1: array, x2: array, /) -> array: return ndarray._new(np.bitwise_or(x1._array, x2._array)) # Note: the function name is different here -def bitwise_right_shift(x1: array, x2: array, /) -> array: +def bitwise_right_shift(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.right_shift `. @@ -185,7 +185,7 @@ def bitwise_right_shift(x1: array, x2: array, /) -> array: # type promotion of the two input types. return ndarray._new(np.right_shift(x1._array, x2._array).astype(x1.dtype)) -def bitwise_xor(x1: array, x2: array, /) -> array: +def bitwise_xor(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.bitwise_xor `. @@ -196,7 +196,7 @@ def bitwise_xor(x1: array, x2: array, /) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.bitwise_xor(x1._array, x2._array)) -def ceil(x: array, /) -> array: +def ceil(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.ceil `. @@ -210,7 +210,7 @@ def ceil(x: array, /) -> array: return ndarray._new(np.ceil(x._array)) @np.errstate(all='ignore') -def cos(x: array, /) -> array: +def cos(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.cos `. @@ -221,7 +221,7 @@ def cos(x: array, /) -> array: return ndarray._new(np.cos(x._array)) @np.errstate(all='ignore') -def cosh(x: array, /) -> array: +def cosh(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.cosh `. @@ -232,7 +232,7 @@ def cosh(x: array, /) -> array: return ndarray._new(np.cosh(x._array)) @np.errstate(all='ignore') -def divide(x1: array, x2: array, /) -> array: +def divide(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.divide `. @@ -243,7 +243,7 @@ def divide(x1: array, x2: array, /) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.divide(x1._array, x2._array)) -def equal(x1: array, x2: array, /) -> array: +def equal(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.equal `. @@ -253,7 +253,7 @@ def equal(x1: array, x2: array, /) -> array: return ndarray._new(np.equal(x1._array, x2._array)) @np.errstate(all='ignore') -def exp(x: array, /) -> array: +def exp(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.exp `. @@ -264,7 +264,7 @@ def exp(x: array, /) -> array: return ndarray._new(np.exp(x._array)) @np.errstate(all='ignore') -def expm1(x: array, /) -> array: +def expm1(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.expm1 `. @@ -274,7 +274,7 @@ def expm1(x: array, /) -> array: raise TypeError('Only floating-point dtypes are allowed in expm1') return ndarray._new(np.expm1(x._array)) -def floor(x: array, /) -> array: +def floor(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.floor `. @@ -288,7 +288,7 @@ def floor(x: array, /) -> array: return ndarray._new(np.floor(x._array)) @np.errstate(all='ignore') -def floor_divide(x1: array, x2: array, /) -> array: +def floor_divide(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.floor_divide `. @@ -299,7 +299,7 @@ def floor_divide(x1: array, x2: array, /) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.floor_divide(x1._array, x2._array)) -def greater(x1: array, x2: array, /) -> array: +def greater(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.greater `. @@ -310,7 +310,7 @@ def greater(x1: array, x2: array, /) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.greater(x1._array, x2._array)) -def greater_equal(x1: array, x2: array, /) -> array: +def greater_equal(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.greater_equal `. @@ -321,7 +321,7 @@ def greater_equal(x1: array, x2: array, /) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.greater_equal(x1._array, x2._array)) -def isfinite(x: array, /) -> array: +def isfinite(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.isfinite `. @@ -331,7 +331,7 @@ def isfinite(x: array, /) -> array: raise TypeError('Only numeric dtypes are allowed in isfinite') return ndarray._new(np.isfinite(x._array)) -def isinf(x: array, /) -> array: +def isinf(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.isinf `. @@ -341,7 +341,7 @@ def isinf(x: array, /) -> array: raise TypeError('Only numeric dtypes are allowed in isinf') return ndarray._new(np.isinf(x._array)) -def isnan(x: array, /) -> array: +def isnan(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.isnan `. @@ -351,7 +351,7 @@ def isnan(x: array, /) -> array: raise TypeError('Only numeric dtypes are allowed in isnan') return ndarray._new(np.isnan(x._array)) -def less(x1: array, x2: array, /) -> array: +def less(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.less `. @@ -362,7 +362,7 @@ def less(x1: array, x2: array, /) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.less(x1._array, x2._array)) -def less_equal(x1: array, x2: array, /) -> array: +def less_equal(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.less_equal `. @@ -374,7 +374,7 @@ def less_equal(x1: array, x2: array, /) -> array: return ndarray._new(np.less_equal(x1._array, x2._array)) @np.errstate(all='ignore') -def log(x: array, /) -> array: +def log(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.log `. @@ -385,7 +385,7 @@ def log(x: array, /) -> array: return ndarray._new(np.log(x._array)) @np.errstate(all='ignore') -def log1p(x: array, /) -> array: +def log1p(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.log1p `. @@ -396,7 +396,7 @@ def log1p(x: array, /) -> array: return ndarray._new(np.log1p(x._array)) @np.errstate(all='ignore') -def log2(x: array, /) -> array: +def log2(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.log2 `. @@ -407,7 +407,7 @@ def log2(x: array, /) -> array: return ndarray._new(np.log2(x._array)) @np.errstate(all='ignore') -def log10(x: array, /) -> array: +def log10(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.log10 `. @@ -417,7 +417,7 @@ def log10(x: array, /) -> array: raise TypeError('Only floating-point dtypes are allowed in log10') return ndarray._new(np.log10(x._array)) -def logaddexp(x1: array, x2: array) -> array: +def logaddexp(x1: Array, x2: Array) -> Array: """ Array API compatible wrapper for :py:func:`np.logaddexp `. @@ -428,7 +428,7 @@ def logaddexp(x1: array, x2: array) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.logaddexp(x1._array, x2._array)) -def logical_and(x1: array, x2: array, /) -> array: +def logical_and(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.logical_and `. @@ -439,7 +439,7 @@ def logical_and(x1: array, x2: array, /) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.logical_and(x1._array, x2._array)) -def logical_not(x: array, /) -> array: +def logical_not(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.logical_not `. @@ -449,7 +449,7 @@ def logical_not(x: array, /) -> array: raise TypeError('Only boolean dtypes are allowed in logical_not') return ndarray._new(np.logical_not(x._array)) -def logical_or(x1: array, x2: array, /) -> array: +def logical_or(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.logical_or `. @@ -460,7 +460,7 @@ def logical_or(x1: array, x2: array, /) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.logical_or(x1._array, x2._array)) -def logical_xor(x1: array, x2: array, /) -> array: +def logical_xor(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.logical_xor `. @@ -472,7 +472,7 @@ def logical_xor(x1: array, x2: array, /) -> array: return ndarray._new(np.logical_xor(x1._array, x2._array)) @np.errstate(all='ignore') -def multiply(x1: array, x2: array, /) -> array: +def multiply(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.multiply `. @@ -483,7 +483,7 @@ def multiply(x1: array, x2: array, /) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.multiply(x1._array, x2._array)) -def negative(x: array, /) -> array: +def negative(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.negative `. @@ -493,7 +493,7 @@ def negative(x: array, /) -> array: raise TypeError('Only numeric dtypes are allowed in negative') return ndarray._new(np.negative(x._array)) -def not_equal(x1: array, x2: array, /) -> array: +def not_equal(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.not_equal `. @@ -502,7 +502,7 @@ def not_equal(x1: array, x2: array, /) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.not_equal(x1._array, x2._array)) -def positive(x: array, /) -> array: +def positive(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.positive `. @@ -514,7 +514,7 @@ def positive(x: array, /) -> array: # Note: the function name is different here @np.errstate(all='ignore') -def pow(x1: array, x2: array, /) -> array: +def pow(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.power `. @@ -526,7 +526,7 @@ def pow(x1: array, x2: array, /) -> array: return ndarray._new(np.power(x1._array, x2._array)) @np.errstate(all='ignore') -def remainder(x1: array, x2: array, /) -> array: +def remainder(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.remainder `. @@ -537,7 +537,7 @@ def remainder(x1: array, x2: array, /) -> array: x1, x2 = ndarray._normalize_two_args(x1, x2) return ndarray._new(np.remainder(x1._array, x2._array)) -def round(x: array, /) -> array: +def round(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.round `. @@ -547,7 +547,7 @@ def round(x: array, /) -> array: raise TypeError('Only numeric dtypes are allowed in round') return ndarray._new(np.round(x._array)) -def sign(x: array, /) -> array: +def sign(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.sign `. @@ -558,7 +558,7 @@ def sign(x: array, /) -> array: return ndarray._new(np.sign(x._array)) @np.errstate(all='ignore') -def sin(x: array, /) -> array: +def sin(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.sin `. @@ -569,7 +569,7 @@ def sin(x: array, /) -> array: return ndarray._new(np.sin(x._array)) @np.errstate(all='ignore') -def sinh(x: array, /) -> array: +def sinh(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.sinh `. @@ -580,7 +580,7 @@ def sinh(x: array, /) -> array: return ndarray._new(np.sinh(x._array)) @np.errstate(all='ignore') -def square(x: array, /) -> array: +def square(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.square `. @@ -591,7 +591,7 @@ def square(x: array, /) -> array: return ndarray._new(np.square(x._array)) @np.errstate(all='ignore') -def sqrt(x: array, /) -> array: +def sqrt(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.sqrt `. @@ -602,7 +602,7 @@ def sqrt(x: array, /) -> array: return ndarray._new(np.sqrt(x._array)) @np.errstate(all='ignore') -def subtract(x1: array, x2: array, /) -> array: +def subtract(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.subtract `. @@ -614,7 +614,7 @@ def subtract(x1: array, x2: array, /) -> array: return ndarray._new(np.subtract(x1._array, x2._array)) @np.errstate(all='ignore') -def tan(x: array, /) -> array: +def tan(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.tan `. @@ -624,7 +624,7 @@ def tan(x: array, /) -> array: raise TypeError('Only floating-point dtypes are allowed in tan') return ndarray._new(np.tan(x._array)) -def tanh(x: array, /) -> array: +def tanh(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.tanh `. @@ -634,7 +634,7 @@ def tanh(x: array, /) -> array: raise TypeError('Only floating-point dtypes are allowed in tanh') return ndarray._new(np.tanh(x._array)) -def trunc(x: array, /) -> array: +def trunc(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.trunc `. diff --git a/numpy/_array_api/_linear_algebra_functions.py b/numpy/_array_api/_linear_algebra_functions.py index 461770641..b6b0c6f6e 100644 --- a/numpy/_array_api/_linear_algebra_functions.py +++ b/numpy/_array_api/_linear_algebra_functions.py @@ -5,7 +5,7 @@ from ._dtypes import _numeric_dtypes from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Optional, Sequence, Tuple, Union, array + from ._types import Optional, Sequence, Tuple, Union, Array import numpy as np @@ -19,7 +19,7 @@ import numpy as np # """ # return np.einsum() -def matmul(x1: array, x2: array, /) -> array: +def matmul(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.matmul `. @@ -33,7 +33,7 @@ def matmul(x1: array, x2: array, /) -> array: return ndarray._new(np.matmul(x1._array, x2._array)) # Note: axes must be a tuple, unlike np.tensordot where it can be an array or array-like. -def tensordot(x1: array, x2: array, /, *, axes: Union[int, Tuple[Sequence[int], Sequence[int]]] = 2) -> array: +def tensordot(x1: Array, x2: Array, /, *, axes: Union[int, Tuple[Sequence[int], Sequence[int]]] = 2) -> Array: # Note: the restriction to numeric dtypes only is different from # np.tensordot. if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: @@ -41,7 +41,7 @@ def tensordot(x1: array, x2: array, /, *, axes: Union[int, Tuple[Sequence[int], return ndarray._new(np.tensordot(x1._array, x2._array, axes=axes)) -def transpose(x: array, /, *, axes: Optional[Tuple[int, ...]] = None) -> array: +def transpose(x: Array, /, *, axes: Optional[Tuple[int, ...]] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.transpose `. @@ -50,7 +50,7 @@ def transpose(x: array, /, *, axes: Optional[Tuple[int, ...]] = None) -> array: return ndarray._new(np.transpose(x._array, axes=axes)) # Note: vecdot is not in NumPy -def vecdot(x1: array, x2: array, /, *, axis: Optional[int] = None) -> array: +def vecdot(x1: Array, x2: Array, /, *, axis: Optional[int] = None) -> Array: if axis is None: axis = -1 return tensordot(x1, x2, axes=((axis,), (axis,))) diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index 5f7b0a451..da02155f9 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -4,12 +4,12 @@ from ._array_object import ndarray from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Optional, Tuple, Union, array + from ._types import Optional, Tuple, Union, Array import numpy as np # Note: the function name is different here -def concat(arrays: Tuple[array, ...], /, *, axis: Optional[int] = 0) -> array: +def concat(arrays: Tuple[Array, ...], /, *, axis: Optional[int] = 0) -> Array: """ Array API compatible wrapper for :py:func:`np.concatenate `. @@ -18,7 +18,7 @@ def concat(arrays: Tuple[array, ...], /, *, axis: Optional[int] = 0) -> array: arrays = tuple(a._array for a in arrays) return ndarray._new(np.concatenate(arrays, axis=axis)) -def expand_dims(x: array, /, *, axis: int) -> array: +def expand_dims(x: Array, /, *, axis: int) -> Array: """ Array API compatible wrapper for :py:func:`np.expand_dims `. @@ -26,7 +26,7 @@ def expand_dims(x: array, /, *, axis: int) -> array: """ return ndarray._new(np.expand_dims(x._array, axis)) -def flip(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: +def flip(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.flip `. @@ -34,7 +34,7 @@ def flip(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> """ return ndarray._new(np.flip(x._array, axis=axis)) -def reshape(x: array, /, shape: Tuple[int, ...]) -> array: +def reshape(x: Array, /, shape: Tuple[int, ...]) -> Array: """ Array API compatible wrapper for :py:func:`np.reshape `. @@ -42,7 +42,7 @@ def reshape(x: array, /, shape: Tuple[int, ...]) -> array: """ return ndarray._new(np.reshape(x._array, shape)) -def roll(x: array, /, shift: Union[int, Tuple[int, ...]], *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: +def roll(x: Array, /, shift: Union[int, Tuple[int, ...]], *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.roll `. @@ -50,7 +50,7 @@ def roll(x: array, /, shift: Union[int, Tuple[int, ...]], *, axis: Optional[Unio """ return ndarray._new(np.roll(x._array, shift, axis=axis)) -def squeeze(x: array, /, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> array: +def squeeze(x: Array, /, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.squeeze `. @@ -58,7 +58,7 @@ def squeeze(x: array, /, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> """ return ndarray._new(np.squeeze(x._array, axis=axis)) -def stack(arrays: Tuple[array, ...], /, *, axis: int = 0) -> array: +def stack(arrays: Tuple[Array, ...], /, *, axis: int = 0) -> Array: """ Array API compatible wrapper for :py:func:`np.stack `. diff --git a/numpy/_array_api/_searching_functions.py b/numpy/_array_api/_searching_functions.py index 9a5d583bc..690256430 100644 --- a/numpy/_array_api/_searching_functions.py +++ b/numpy/_array_api/_searching_functions.py @@ -4,11 +4,11 @@ from ._array_object import ndarray from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Tuple, array + from ._types import Tuple, Array import numpy as np -def argmax(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: +def argmax(x: Array, /, *, axis: int = None, keepdims: bool = False) -> Array: """ Array API compatible wrapper for :py:func:`np.argmax `. @@ -17,7 +17,7 @@ def argmax(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: # Note: this currently fails as np.argmax does not implement keepdims return ndarray._new(np.asarray(np.argmax(x._array, axis=axis, keepdims=keepdims))) -def argmin(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: +def argmin(x: Array, /, *, axis: int = None, keepdims: bool = False) -> Array: """ Array API compatible wrapper for :py:func:`np.argmin `. @@ -26,7 +26,7 @@ def argmin(x: array, /, *, axis: int = None, keepdims: bool = False) -> array: # Note: this currently fails as np.argmin does not implement keepdims return ndarray._new(np.asarray(np.argmin(x._array, axis=axis, keepdims=keepdims))) -def nonzero(x: array, /) -> Tuple[array, ...]: +def nonzero(x: Array, /) -> Tuple[Array, ...]: """ Array API compatible wrapper for :py:func:`np.nonzero `. @@ -34,7 +34,7 @@ def nonzero(x: array, /) -> Tuple[array, ...]: """ return ndarray._new(np.nonzero(x._array)) -def where(condition: array, x1: array, x2: array, /) -> array: +def where(condition: Array, x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.where `. diff --git a/numpy/_array_api/_set_functions.py b/numpy/_array_api/_set_functions.py index 025a27d80..719d54e5f 100644 --- a/numpy/_array_api/_set_functions.py +++ b/numpy/_array_api/_set_functions.py @@ -4,11 +4,11 @@ from ._array_object import ndarray from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Tuple, Union, array + from ._types import Tuple, Union, Array import numpy as np -def unique(x: array, /, *, return_counts: bool = False, return_index: bool = False, return_inverse: bool = False) -> Union[array, Tuple[array, ...]]: +def unique(x: Array, /, *, return_counts: bool = False, return_index: bool = False, return_inverse: bool = False) -> Union[Array, Tuple[Array, ...]]: """ Array API compatible wrapper for :py:func:`np.unique `. diff --git a/numpy/_array_api/_sorting_functions.py b/numpy/_array_api/_sorting_functions.py index 6e87bd90e..3dc0ec444 100644 --- a/numpy/_array_api/_sorting_functions.py +++ b/numpy/_array_api/_sorting_functions.py @@ -4,11 +4,11 @@ from ._array_object import ndarray from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import array + from ._types import Array import numpy as np -def argsort(x: array, /, *, axis: int = -1, descending: bool = False, stable: bool = True) -> array: +def argsort(x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True) -> Array: """ Array API compatible wrapper for :py:func:`np.argsort `. @@ -21,7 +21,7 @@ def argsort(x: array, /, *, axis: int = -1, descending: bool = False, stable: bo res = np.flip(res, axis=axis) return ndarray._new(res) -def sort(x: array, /, *, axis: int = -1, descending: bool = False, stable: bool = True) -> array: +def sort(x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True) -> Array: """ Array API compatible wrapper for :py:func:`np.sort `. diff --git a/numpy/_array_api/_statistical_functions.py b/numpy/_array_api/_statistical_functions.py index 26afd7354..e6a791fe6 100644 --- a/numpy/_array_api/_statistical_functions.py +++ b/numpy/_array_api/_statistical_functions.py @@ -4,29 +4,29 @@ from ._array_object import ndarray from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Optional, Tuple, Union, array + from ._types import Optional, Tuple, Union, Array import numpy as np -def max(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: +def max(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: return ndarray._new(np.max(x._array, axis=axis, keepdims=keepdims)) -def mean(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: +def mean(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: return ndarray._new(np.asarray(np.mean(x._array, axis=axis, keepdims=keepdims))) -def min(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: +def min(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: return ndarray._new(np.min(x._array, axis=axis, keepdims=keepdims)) -def prod(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: +def prod(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: return ndarray._new(np.asarray(np.prod(x._array, axis=axis, keepdims=keepdims))) -def std(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> array: +def std(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> Array: # Note: the keyword argument correction is different here return ndarray._new(np.asarray(np.std(x._array, axis=axis, ddof=correction, keepdims=keepdims))) -def sum(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: +def sum(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: return ndarray._new(np.asarray(np.sum(x._array, axis=axis, keepdims=keepdims))) -def var(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> array: +def var(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> Array: # Note: the keyword argument correction is different here return ndarray._new(np.asarray(np.var(x._array, axis=axis, ddof=correction, keepdims=keepdims))) diff --git a/numpy/_array_api/_types.py b/numpy/_array_api/_types.py index 1086699fc..602c1df3e 100644 --- a/numpy/_array_api/_types.py +++ b/numpy/_array_api/_types.py @@ -6,8 +6,8 @@ annotations in the function signatures. The functions in the module are only valid for inputs that match the given type annotations. """ -__all__ = ['Any', 'List', 'Literal', 'Optional', 'Tuple', 'Union', 'array', - 'device', 'dtype', 'SupportsDLPack', 'SupportsBufferProtocol', +__all__ = ['Any', 'List', 'Literal', 'Optional', 'Tuple', 'Union', 'Array', + 'Device', 'Dtype', 'SupportsDLPack', 'SupportsBufferProtocol', 'PyCapsule'] from typing import Any, List, Literal, Optional, Tuple, Union, TypeVar @@ -15,9 +15,9 @@ from typing import Any, List, Literal, Optional, Tuple, Union, TypeVar from . import (ndarray, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64) -array = ndarray -device = TypeVar('device') -dtype = Literal[int8, int16, int32, int64, uint8, uint16, +Array = ndarray +Device = TypeVar('device') +Dtype = Literal[int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64] SupportsDLPack = TypeVar('SupportsDLPack') SupportsBufferProtocol = TypeVar('SupportsBufferProtocol') diff --git a/numpy/_array_api/_utility_functions.py b/numpy/_array_api/_utility_functions.py index e280b5785..a6a7721dd 100644 --- a/numpy/_array_api/_utility_functions.py +++ b/numpy/_array_api/_utility_functions.py @@ -4,11 +4,11 @@ from ._array_object import ndarray from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Optional, Tuple, Union, array + from ._types import Optional, Tuple, Union, Array import numpy as np -def all(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: +def all(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: """ Array API compatible wrapper for :py:func:`np.all `. @@ -16,7 +16,7 @@ def all(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keep """ return ndarray._new(np.asarray(np.all(x._array, axis=axis, keepdims=keepdims))) -def any(x: array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> array: +def any(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: """ Array API compatible wrapper for :py:func:`np.any `. -- cgit v1.2.1 From aee3a56d4e150a55c590966c9cc2ae0e201fa936 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 8 Jul 2021 17:22:47 -0600 Subject: Rename the array class in the array API namespace from ndarray to Array The actual class name doesn't matter because it isn't part of the namespace API (arrays should be constructed with the array creation functions like asarray()). However, it is better to use a name that is different from the existing NumPy array object to avoid ambiguity. --- numpy/_array_api/__init__.py | 10 +- numpy/_array_api/_array_object.py | 32 ++--- numpy/_array_api/_creation_functions.py | 84 ++++++------- numpy/_array_api/_data_type_functions.py | 18 +-- numpy/_array_api/_elementwise_functions.py | 164 +++++++++++++------------- numpy/_array_api/_linear_algebra_functions.py | 12 +- numpy/_array_api/_manipulation_functions.py | 18 +-- numpy/_array_api/_searching_functions.py | 12 +- numpy/_array_api/_set_functions.py | 6 +- numpy/_array_api/_sorting_functions.py | 10 +- numpy/_array_api/_statistical_functions.py | 18 +-- numpy/_array_api/_types.py | 2 +- numpy/_array_api/_utility_functions.py | 8 +- 13 files changed, 192 insertions(+), 202 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/__init__.py b/numpy/_array_api/__init__.py index e39a2c7d0..320c8df19 100644 --- a/numpy/_array_api/__init__.py +++ b/numpy/_array_api/__init__.py @@ -58,7 +58,7 @@ request. guaranteed to give a comprehensive coverage of the spec. Therefore, those reviewing this submodule should refer to the standard documents themselves. -- There is a custom array object, numpy._array_api.ndarray, which is returned +- There is a custom array object, numpy._array_api.Array, which is returned by all functions in this module. All functions in the array API namespace implicitly assume that they will only receive this object as input. The only way to create instances of this object is to use one of the array creation @@ -69,14 +69,14 @@ request. limit/change certain behavior that differs in the spec. In particular: - Indexing: Only a subset of indices supported by NumPy are required by the - spec. The ndarray object restricts indexing to only allow those types of + spec. The Array object restricts indexing to only allow those types of indices that are required by the spec. See the docstring of the - numpy._array_api.ndarray._validate_indices helper function for more + numpy._array_api.Array._validate_indices helper function for more information. - Type promotion: Some type promotion rules are different in the spec. In particular, the spec does not have any value-based casting. Note that the - code to correct the type promotion rules on numpy._array_api.ndarray is + code to correct the type promotion rules on numpy._array_api.Array is not yet implemented. - All functions include type annotations, corresponding to those given in the @@ -93,7 +93,7 @@ request. Still TODO in this module are: -- Implement the spec type promotion rules on the ndarray object. +- Implement the spec type promotion rules on the Array object. - Disable NumPy warnings in the API functions. diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 89ec3ba1a..9ea0eef18 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -22,13 +22,13 @@ from ._dtypes import _boolean_dtypes, _integer_dtypes, _floating_dtypes from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Any, Optional, PyCapsule, Tuple, Union, Array + from ._types import Any, Optional, PyCapsule, Tuple, Union, Device, Dtype import numpy as np -class ndarray: +class Array: """ - ndarray object for the array API namespace. + n-d array object for the array API namespace. See the docstring of :py:obj:`np.ndarray ` for more information. @@ -46,7 +46,7 @@ class ndarray: @classmethod def _new(cls, x, /): """ - This is a private method for initializing the array API ndarray + This is a private method for initializing the array API Array object. Functions outside of the array_api submodule should not use this @@ -64,9 +64,9 @@ class ndarray: obj._array = x return obj - # Prevent ndarray() from working + # Prevent Array() from working def __new__(cls, *args, **kwargs): - raise TypeError("The array_api ndarray object should not be instantiated directly. Use an array creation function, such as asarray(), instead.") + raise TypeError("The array_api Array object should not be instantiated directly. Use an array creation function, such as asarray(), instead.") # These functions are not required by the spec, but are implemented for # the sake of usability. @@ -75,13 +75,13 @@ class ndarray: """ Performs the operation __str__. """ - return self._array.__str__().replace('array', 'ndarray') + return self._array.__str__().replace('array', 'Array') def __repr__(self: Array, /) -> str: """ Performs the operation __repr__. """ - return self._array.__repr__().replace('array', 'ndarray') + return self._array.__repr__().replace('array', 'Array') # Helper function to match the type promotion rules in the spec def _promote_scalar(self, scalar): @@ -109,7 +109,7 @@ class ndarray: # behavior for integers within the bounds of the integer dtype. # Outside of those bounds we use the default NumPy behavior (either # cast or raise OverflowError). - return ndarray._new(np.array(scalar, self.dtype)) + return Array._new(np.array(scalar, self.dtype)) @staticmethod def _normalize_two_args(x1, x2): @@ -135,9 +135,9 @@ class ndarray: # performant. broadcast_to(x1._array, x2.shape) is much slower. We # could also manually type promote x2, but that is more complicated # and about the same performance as this. - x1 = ndarray._new(x1._array[None]) + x1 = Array._new(x1._array[None]) elif x2.shape == () and x1.shape != (): - x2 = ndarray._new(x2._array[None]) + x2 = Array._new(x2._array[None]) return (x1, x2) # Everything below this line is required by the spec. @@ -284,7 +284,7 @@ class ndarray: Additionally, it should be noted that indices that would return a scalar in NumPy will return a shape () array. Array scalars are not allowed in the specification, only shape () arrays. This is done in the - ``ndarray._new`` constructor, not this function. + ``Array._new`` constructor, not this function. """ if isinstance(key, slice): @@ -313,7 +313,7 @@ class ndarray: return key elif isinstance(key, tuple): - key = tuple(ndarray._validate_index(idx, None) for idx in key) + key = tuple(Array._validate_index(idx, None) for idx in key) for idx in key: if isinstance(idx, np.ndarray) and idx.dtype in _boolean_dtypes or isinstance(idx, (bool, np.bool_)): @@ -329,11 +329,11 @@ class ndarray: ellipsis_i = key.index(...) if n_ellipsis else len(key) for idx, size in list(zip(key[:ellipsis_i], shape)) + list(zip(key[:ellipsis_i:-1], shape[:ellipsis_i:-1])): - ndarray._validate_index(idx, (size,)) + Array._validate_index(idx, (size,)) return key elif isinstance(key, bool): return key - elif isinstance(key, ndarray): + elif isinstance(key, Array): if key.dtype in _integer_dtypes: if key.shape != (): raise IndexError("Integer array indices with shape != () are not allowed in the array API namespace") @@ -346,7 +346,7 @@ class ndarray: return operator.index(key) except TypeError: # Note: This also omits boolean arrays that are not already in - # ndarray() form, like a list of booleans. + # Array() form, like a list of booleans. raise IndexError("Only integers, slices (`:`), ellipsis (`...`), and boolean arrays are valid indices in the array API namespace") def __getitem__(self: Array, key: Union[int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], Array], /) -> Array: diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 9845dd70f..8fb2a8b12 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -19,14 +19,14 @@ def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, Su """ # _array_object imports in this file are inside the functions to avoid # circular imports - from ._array_object import ndarray + from ._array_object import Array if device is not None: - # Note: Device support is not yet implemented on ndarray + # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") if copy is not None: # Note: copy is not yet implemented in np.asarray raise NotImplementedError("The copy keyword argument to asarray is not yet implemented") - if isinstance(obj, ndarray) and (dtype is None or obj.dtype == dtype): + if isinstance(obj, Array) and (dtype is None or obj.dtype == dtype): return obj if dtype is None and isinstance(obj, int) and (obj > 2**64 or obj < -2**63): # Give a better error message in this case. NumPy would convert this @@ -35,7 +35,7 @@ def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, Su res = np.asarray(obj, dtype=dtype) if res.dtype not in _all_dtypes: raise TypeError(f"The array_api namespace does not support the dtype '{res.dtype}'") - return ndarray._new(res) + return Array._new(res) def arange(start: Union[int, float], /, stop: Optional[Union[int, float]] = None, step: Union[int, float] = 1, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ @@ -43,11 +43,11 @@ def arange(start: Union[int, float], /, stop: Optional[Union[int, float]] = None See its docstring for more information. """ - from ._array_object import ndarray + from ._array_object import Array if device is not None: - # Note: Device support is not yet implemented on ndarray + # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") - return ndarray._new(np.arange(start, stop=stop, step=step, dtype=dtype)) + return Array._new(np.arange(start, stop=stop, step=step, dtype=dtype)) def empty(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ @@ -55,11 +55,11 @@ def empty(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, See its docstring for more information. """ - from ._array_object import ndarray + from ._array_object import Array if device is not None: - # Note: Device support is not yet implemented on ndarray + # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") - return ndarray._new(np.empty(shape, dtype=dtype)) + return Array._new(np.empty(shape, dtype=dtype)) def empty_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ @@ -67,11 +67,11 @@ def empty_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[D See its docstring for more information. """ - from ._array_object import ndarray + from ._array_object import Array if device is not None: - # Note: Device support is not yet implemented on ndarray + # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") - return ndarray._new(np.empty_like(x._array, dtype=dtype)) + return Array._new(np.empty_like(x._array, dtype=dtype)) def eye(n_rows: int, n_cols: Optional[int] = None, /, *, k: Optional[int] = 0, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ @@ -79,14 +79,14 @@ def eye(n_rows: int, n_cols: Optional[int] = None, /, *, k: Optional[int] = 0, d See its docstring for more information. """ - from ._array_object import ndarray + from ._array_object import Array if device is not None: - # Note: Device support is not yet implemented on ndarray + # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") - return ndarray._new(np.eye(n_rows, M=n_cols, k=k, dtype=dtype)) + return Array._new(np.eye(n_rows, M=n_cols, k=k, dtype=dtype)) def from_dlpack(x: object, /) -> Array: - # Note: dlpack support is not yet implemented on ndarray + # Note: dlpack support is not yet implemented on Array raise NotImplementedError("DLPack support is not yet implemented") def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: @@ -95,18 +95,18 @@ def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], *, d See its docstring for more information. """ - from ._array_object import ndarray + from ._array_object import Array if device is not None: - # Note: Device support is not yet implemented on ndarray + # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") - if isinstance(fill_value, ndarray) and fill_value.ndim == 0: - fill_value = fill_value._array[...] + if isinstance(fill_value, Array) and fill_value.ndim == 0: + fill_value = fill_value._array[...] res = np.full(shape, fill_value, dtype=dtype) if res.dtype not in _all_dtypes: # This will happen if the fill value is not something that NumPy # coerces to one of the acceptable dtypes. raise TypeError("Invalid input to full") - return ndarray._new(res) + return Array._new(res) def full_like(x: Array, /, fill_value: Union[int, float], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ @@ -114,16 +114,16 @@ def full_like(x: Array, /, fill_value: Union[int, float], *, dtype: Optional[Dty See its docstring for more information. """ - from ._array_object import ndarray + from ._array_object import Array if device is not None: - # Note: Device support is not yet implemented on ndarray + # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") res = np.full_like(x._array, fill_value, dtype=dtype) if res.dtype not in _all_dtypes: # This will happen if the fill value is not something that NumPy # coerces to one of the acceptable dtypes. raise TypeError("Invalid input to full_like") - return ndarray._new(res) + return Array._new(res) def linspace(start: Union[int, float], stop: Union[int, float], /, num: int, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, endpoint: bool = True) -> Array: """ @@ -131,11 +131,11 @@ def linspace(start: Union[int, float], stop: Union[int, float], /, num: int, *, See its docstring for more information. """ - from ._array_object import ndarray + from ._array_object import Array if device is not None: - # Note: Device support is not yet implemented on ndarray + # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") - return ndarray._new(np.linspace(start, stop, num, dtype=dtype, endpoint=endpoint)) + return Array._new(np.linspace(start, stop, num, dtype=dtype, endpoint=endpoint)) def meshgrid(*arrays: Sequence[Array], indexing: str = 'xy') -> List[Array, ...]: """ @@ -143,8 +143,8 @@ def meshgrid(*arrays: Sequence[Array], indexing: str = 'xy') -> List[Array, ...] See its docstring for more information. """ - from ._array_object import ndarray - return [ndarray._new(array) for array in np.meshgrid(*[a._array for a in arrays], indexing=indexing)] + from ._array_object import Array + return [Array._new(array) for array in np.meshgrid(*[a._array for a in arrays], indexing=indexing)] def ones(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ @@ -152,11 +152,11 @@ def ones(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, d See its docstring for more information. """ - from ._array_object import ndarray + from ._array_object import Array if device is not None: - # Note: Device support is not yet implemented on ndarray + # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") - return ndarray._new(np.ones(shape, dtype=dtype)) + return Array._new(np.ones(shape, dtype=dtype)) def ones_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ @@ -164,11 +164,11 @@ def ones_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[De See its docstring for more information. """ - from ._array_object import ndarray + from ._array_object import Array if device is not None: - # Note: Device support is not yet implemented on ndarray + # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") - return ndarray._new(np.ones_like(x._array, dtype=dtype)) + return Array._new(np.ones_like(x._array, dtype=dtype)) def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ @@ -176,11 +176,11 @@ def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, See its docstring for more information. """ - from ._array_object import ndarray + from ._array_object import Array if device is not None: - # Note: Device support is not yet implemented on ndarray + # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") - return ndarray._new(np.zeros(shape, dtype=dtype)) + return Array._new(np.zeros(shape, dtype=dtype)) def zeros_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: """ @@ -188,8 +188,8 @@ def zeros_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[D See its docstring for more information. """ - from ._array_object import ndarray + from ._array_object import Array if device is not None: - # Note: Device support is not yet implemented on ndarray + # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") - return ndarray._new(np.zeros_like(x._array, dtype=dtype)) + return Array._new(np.zeros_like(x._array, dtype=dtype)) diff --git a/numpy/_array_api/_data_type_functions.py b/numpy/_array_api/_data_type_functions.py index 5ab611fd3..2f304bf49 100644 --- a/numpy/_array_api/_data_type_functions.py +++ b/numpy/_array_api/_data_type_functions.py @@ -1,10 +1,10 @@ from __future__ import annotations -from ._array_object import ndarray +from ._array_object import Array from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import List, Tuple, Union, Array, Dtype + from ._types import List, Tuple, Union, Dtype from collections.abc import Sequence import numpy as np @@ -15,8 +15,8 @@ def broadcast_arrays(*arrays: Sequence[Array]) -> List[Array]: See its docstring for more information. """ - from ._array_object import ndarray - return [ndarray._new(array) for array in np.broadcast_arrays(*[a._array for a in arrays])] + from ._array_object import Array + return [Array._new(array) for array in np.broadcast_arrays(*[a._array for a in arrays])] def broadcast_to(x: Array, /, shape: Tuple[int, ...]) -> Array: """ @@ -24,8 +24,8 @@ def broadcast_to(x: Array, /, shape: Tuple[int, ...]) -> Array: See its docstring for more information. """ - from ._array_object import ndarray - return ndarray._new(np.broadcast_to(x._array, shape)) + from ._array_object import Array + return Array._new(np.broadcast_to(x._array, shape)) def can_cast(from_: Union[Dtype, Array], to: Dtype, /) -> bool: """ @@ -33,8 +33,8 @@ def can_cast(from_: Union[Dtype, Array], to: Dtype, /) -> bool: See its docstring for more information. """ - from ._array_object import ndarray - if isinstance(from_, ndarray): + from ._array_object import Array + if isinstance(from_, Array): from_ = from_._array return np.can_cast(from_, to) @@ -60,4 +60,4 @@ def result_type(*arrays_and_dtypes: Sequence[Union[Array, Dtype]]) -> Dtype: See its docstring for more information. """ - return np.result_type(*(a._array if isinstance(a, ndarray) else a for a in arrays_and_dtypes)) + return np.result_type(*(a._array if isinstance(a, Array) else a for a in arrays_and_dtypes)) diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index ae265181a..8dedc77fb 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -3,11 +3,7 @@ from __future__ import annotations from ._dtypes import (_boolean_dtypes, _floating_dtypes, _integer_dtypes, _integer_or_boolean_dtypes, _numeric_dtypes) -from ._array_object import ndarray - -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from ._types import Array +from ._array_object import Array import numpy as np @@ -19,7 +15,7 @@ def abs(x: Array, /) -> Array: """ if x.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in abs') - return ndarray._new(np.abs(x._array)) + return Array._new(np.abs(x._array)) # Note: the function name is different here @np.errstate(all='ignore') @@ -31,7 +27,7 @@ def acos(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in acos') - return ndarray._new(np.arccos(x._array)) + return Array._new(np.arccos(x._array)) # Note: the function name is different here @np.errstate(all='ignore') @@ -43,7 +39,7 @@ def acosh(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in acosh') - return ndarray._new(np.arccosh(x._array)) + return Array._new(np.arccosh(x._array)) @np.errstate(all='ignore') def add(x1: Array, x2: Array, /) -> Array: @@ -54,8 +50,8 @@ def add(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in add') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.add(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.add(x1._array, x2._array)) # Note: the function name is different here @np.errstate(all='ignore') @@ -67,7 +63,7 @@ def asin(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in asin') - return ndarray._new(np.arcsin(x._array)) + return Array._new(np.arcsin(x._array)) # Note: the function name is different here @np.errstate(all='ignore') @@ -79,7 +75,7 @@ def asinh(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in asinh') - return ndarray._new(np.arcsinh(x._array)) + return Array._new(np.arcsinh(x._array)) # Note: the function name is different here def atan(x: Array, /) -> Array: @@ -90,7 +86,7 @@ def atan(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in atan') - return ndarray._new(np.arctan(x._array)) + return Array._new(np.arctan(x._array)) # Note: the function name is different here def atan2(x1: Array, x2: Array, /) -> Array: @@ -101,8 +97,8 @@ def atan2(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in atan2') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.arctan2(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.arctan2(x1._array, x2._array)) # Note: the function name is different here @np.errstate(all='ignore') @@ -114,7 +110,7 @@ def atanh(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in atanh') - return ndarray._new(np.arctanh(x._array)) + return Array._new(np.arctanh(x._array)) def bitwise_and(x1: Array, x2: Array, /) -> Array: """ @@ -124,8 +120,8 @@ def bitwise_and(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: raise TypeError('Only integer_or_boolean dtypes are allowed in bitwise_and') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.bitwise_and(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.bitwise_and(x1._array, x2._array)) # Note: the function name is different here def bitwise_left_shift(x1: Array, x2: Array, /) -> Array: @@ -136,14 +132,14 @@ def bitwise_left_shift(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: raise TypeError('Only integer dtypes are allowed in bitwise_left_shift') - x1, x2 = ndarray._normalize_two_args(x1, x2) + x1, x2 = Array._normalize_two_args(x1, x2) # Note: bitwise_left_shift is only defined for x2 nonnegative. if np.any(x2._array < 0): raise ValueError('bitwise_left_shift(x1, x2) is only defined for x2 >= 0') # Note: The spec requires the return dtype of bitwise_left_shift to be the # same as the first argument. np.left_shift() returns a type that is the # type promotion of the two input types. - return ndarray._new(np.left_shift(x1._array, x2._array).astype(x1.dtype)) + return Array._new(np.left_shift(x1._array, x2._array).astype(x1.dtype)) # Note: the function name is different here def bitwise_invert(x: Array, /) -> Array: @@ -154,7 +150,7 @@ def bitwise_invert(x: Array, /) -> Array: """ if x.dtype not in _integer_or_boolean_dtypes: raise TypeError('Only integer or boolean dtypes are allowed in bitwise_invert') - return ndarray._new(np.invert(x._array)) + return Array._new(np.invert(x._array)) def bitwise_or(x1: Array, x2: Array, /) -> Array: """ @@ -164,8 +160,8 @@ def bitwise_or(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: raise TypeError('Only integer or boolean dtypes are allowed in bitwise_or') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.bitwise_or(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.bitwise_or(x1._array, x2._array)) # Note: the function name is different here def bitwise_right_shift(x1: Array, x2: Array, /) -> Array: @@ -176,14 +172,14 @@ def bitwise_right_shift(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: raise TypeError('Only integer dtypes are allowed in bitwise_right_shift') - x1, x2 = ndarray._normalize_two_args(x1, x2) + x1, x2 = Array._normalize_two_args(x1, x2) # Note: bitwise_right_shift is only defined for x2 nonnegative. if np.any(x2._array < 0): raise ValueError('bitwise_right_shift(x1, x2) is only defined for x2 >= 0') # Note: The spec requires the return dtype of bitwise_left_shift to be the # same as the first argument. np.left_shift() returns a type that is the # type promotion of the two input types. - return ndarray._new(np.right_shift(x1._array, x2._array).astype(x1.dtype)) + return Array._new(np.right_shift(x1._array, x2._array).astype(x1.dtype)) def bitwise_xor(x1: Array, x2: Array, /) -> Array: """ @@ -193,8 +189,8 @@ def bitwise_xor(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: raise TypeError('Only integer or boolean dtypes are allowed in bitwise_xor') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.bitwise_xor(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.bitwise_xor(x1._array, x2._array)) def ceil(x: Array, /) -> Array: """ @@ -207,7 +203,7 @@ def ceil(x: Array, /) -> Array: if x.dtype in _integer_dtypes: # Note: The return dtype of ceil is the same as the input return x - return ndarray._new(np.ceil(x._array)) + return Array._new(np.ceil(x._array)) @np.errstate(all='ignore') def cos(x: Array, /) -> Array: @@ -218,7 +214,7 @@ def cos(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in cos') - return ndarray._new(np.cos(x._array)) + return Array._new(np.cos(x._array)) @np.errstate(all='ignore') def cosh(x: Array, /) -> Array: @@ -229,7 +225,7 @@ def cosh(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in cosh') - return ndarray._new(np.cosh(x._array)) + return Array._new(np.cosh(x._array)) @np.errstate(all='ignore') def divide(x1: Array, x2: Array, /) -> Array: @@ -240,8 +236,8 @@ def divide(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in divide') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.divide(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.divide(x1._array, x2._array)) def equal(x1: Array, x2: Array, /) -> Array: """ @@ -249,8 +245,8 @@ def equal(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.equal(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.equal(x1._array, x2._array)) @np.errstate(all='ignore') def exp(x: Array, /) -> Array: @@ -261,7 +257,7 @@ def exp(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in exp') - return ndarray._new(np.exp(x._array)) + return Array._new(np.exp(x._array)) @np.errstate(all='ignore') def expm1(x: Array, /) -> Array: @@ -272,7 +268,7 @@ def expm1(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in expm1') - return ndarray._new(np.expm1(x._array)) + return Array._new(np.expm1(x._array)) def floor(x: Array, /) -> Array: """ @@ -285,7 +281,7 @@ def floor(x: Array, /) -> Array: if x.dtype in _integer_dtypes: # Note: The return dtype of floor is the same as the input return x - return ndarray._new(np.floor(x._array)) + return Array._new(np.floor(x._array)) @np.errstate(all='ignore') def floor_divide(x1: Array, x2: Array, /) -> Array: @@ -296,8 +292,8 @@ def floor_divide(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in floor_divide') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.floor_divide(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.floor_divide(x1._array, x2._array)) def greater(x1: Array, x2: Array, /) -> Array: """ @@ -307,8 +303,8 @@ def greater(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in greater') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.greater(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.greater(x1._array, x2._array)) def greater_equal(x1: Array, x2: Array, /) -> Array: """ @@ -318,8 +314,8 @@ def greater_equal(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in greater_equal') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.greater_equal(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.greater_equal(x1._array, x2._array)) def isfinite(x: Array, /) -> Array: """ @@ -329,7 +325,7 @@ def isfinite(x: Array, /) -> Array: """ if x.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in isfinite') - return ndarray._new(np.isfinite(x._array)) + return Array._new(np.isfinite(x._array)) def isinf(x: Array, /) -> Array: """ @@ -339,7 +335,7 @@ def isinf(x: Array, /) -> Array: """ if x.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in isinf') - return ndarray._new(np.isinf(x._array)) + return Array._new(np.isinf(x._array)) def isnan(x: Array, /) -> Array: """ @@ -349,7 +345,7 @@ def isnan(x: Array, /) -> Array: """ if x.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in isnan') - return ndarray._new(np.isnan(x._array)) + return Array._new(np.isnan(x._array)) def less(x1: Array, x2: Array, /) -> Array: """ @@ -359,8 +355,8 @@ def less(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in less') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.less(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.less(x1._array, x2._array)) def less_equal(x1: Array, x2: Array, /) -> Array: """ @@ -370,8 +366,8 @@ def less_equal(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in less_equal') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.less_equal(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.less_equal(x1._array, x2._array)) @np.errstate(all='ignore') def log(x: Array, /) -> Array: @@ -382,7 +378,7 @@ def log(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in log') - return ndarray._new(np.log(x._array)) + return Array._new(np.log(x._array)) @np.errstate(all='ignore') def log1p(x: Array, /) -> Array: @@ -393,7 +389,7 @@ def log1p(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in log1p') - return ndarray._new(np.log1p(x._array)) + return Array._new(np.log1p(x._array)) @np.errstate(all='ignore') def log2(x: Array, /) -> Array: @@ -404,7 +400,7 @@ def log2(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in log2') - return ndarray._new(np.log2(x._array)) + return Array._new(np.log2(x._array)) @np.errstate(all='ignore') def log10(x: Array, /) -> Array: @@ -415,7 +411,7 @@ def log10(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in log10') - return ndarray._new(np.log10(x._array)) + return Array._new(np.log10(x._array)) def logaddexp(x1: Array, x2: Array) -> Array: """ @@ -425,8 +421,8 @@ def logaddexp(x1: Array, x2: Array) -> Array: """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in logaddexp') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.logaddexp(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.logaddexp(x1._array, x2._array)) def logical_and(x1: Array, x2: Array, /) -> Array: """ @@ -436,8 +432,8 @@ def logical_and(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: raise TypeError('Only boolean dtypes are allowed in logical_and') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.logical_and(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.logical_and(x1._array, x2._array)) def logical_not(x: Array, /) -> Array: """ @@ -447,7 +443,7 @@ def logical_not(x: Array, /) -> Array: """ if x.dtype not in _boolean_dtypes: raise TypeError('Only boolean dtypes are allowed in logical_not') - return ndarray._new(np.logical_not(x._array)) + return Array._new(np.logical_not(x._array)) def logical_or(x1: Array, x2: Array, /) -> Array: """ @@ -457,8 +453,8 @@ def logical_or(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: raise TypeError('Only boolean dtypes are allowed in logical_or') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.logical_or(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.logical_or(x1._array, x2._array)) def logical_xor(x1: Array, x2: Array, /) -> Array: """ @@ -468,8 +464,8 @@ def logical_xor(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: raise TypeError('Only boolean dtypes are allowed in logical_xor') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.logical_xor(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.logical_xor(x1._array, x2._array)) @np.errstate(all='ignore') def multiply(x1: Array, x2: Array, /) -> Array: @@ -480,8 +476,8 @@ def multiply(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in multiply') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.multiply(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.multiply(x1._array, x2._array)) def negative(x: Array, /) -> Array: """ @@ -491,7 +487,7 @@ def negative(x: Array, /) -> Array: """ if x.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in negative') - return ndarray._new(np.negative(x._array)) + return Array._new(np.negative(x._array)) def not_equal(x1: Array, x2: Array, /) -> Array: """ @@ -499,8 +495,8 @@ def not_equal(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.not_equal(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.not_equal(x1._array, x2._array)) def positive(x: Array, /) -> Array: """ @@ -510,7 +506,7 @@ def positive(x: Array, /) -> Array: """ if x.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in positive') - return ndarray._new(np.positive(x._array)) + return Array._new(np.positive(x._array)) # Note: the function name is different here @np.errstate(all='ignore') @@ -522,8 +518,8 @@ def pow(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in pow') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.power(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.power(x1._array, x2._array)) @np.errstate(all='ignore') def remainder(x1: Array, x2: Array, /) -> Array: @@ -534,8 +530,8 @@ def remainder(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in remainder') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.remainder(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.remainder(x1._array, x2._array)) def round(x: Array, /) -> Array: """ @@ -545,7 +541,7 @@ def round(x: Array, /) -> Array: """ if x.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in round') - return ndarray._new(np.round(x._array)) + return Array._new(np.round(x._array)) def sign(x: Array, /) -> Array: """ @@ -555,7 +551,7 @@ def sign(x: Array, /) -> Array: """ if x.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in sign') - return ndarray._new(np.sign(x._array)) + return Array._new(np.sign(x._array)) @np.errstate(all='ignore') def sin(x: Array, /) -> Array: @@ -566,7 +562,7 @@ def sin(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in sin') - return ndarray._new(np.sin(x._array)) + return Array._new(np.sin(x._array)) @np.errstate(all='ignore') def sinh(x: Array, /) -> Array: @@ -577,7 +573,7 @@ def sinh(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in sinh') - return ndarray._new(np.sinh(x._array)) + return Array._new(np.sinh(x._array)) @np.errstate(all='ignore') def square(x: Array, /) -> Array: @@ -588,7 +584,7 @@ def square(x: Array, /) -> Array: """ if x.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in square') - return ndarray._new(np.square(x._array)) + return Array._new(np.square(x._array)) @np.errstate(all='ignore') def sqrt(x: Array, /) -> Array: @@ -599,7 +595,7 @@ def sqrt(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in sqrt') - return ndarray._new(np.sqrt(x._array)) + return Array._new(np.sqrt(x._array)) @np.errstate(all='ignore') def subtract(x1: Array, x2: Array, /) -> Array: @@ -610,8 +606,8 @@ def subtract(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in subtract') - x1, x2 = ndarray._normalize_two_args(x1, x2) - return ndarray._new(np.subtract(x1._array, x2._array)) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.subtract(x1._array, x2._array)) @np.errstate(all='ignore') def tan(x: Array, /) -> Array: @@ -622,7 +618,7 @@ def tan(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in tan') - return ndarray._new(np.tan(x._array)) + return Array._new(np.tan(x._array)) def tanh(x: Array, /) -> Array: """ @@ -632,7 +628,7 @@ def tanh(x: Array, /) -> Array: """ if x.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in tanh') - return ndarray._new(np.tanh(x._array)) + return Array._new(np.tanh(x._array)) def trunc(x: Array, /) -> Array: """ @@ -642,4 +638,4 @@ def trunc(x: Array, /) -> Array: """ if x.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in trunc') - return ndarray._new(np.trunc(x._array)) + return Array._new(np.trunc(x._array)) diff --git a/numpy/_array_api/_linear_algebra_functions.py b/numpy/_array_api/_linear_algebra_functions.py index b6b0c6f6e..b4b2af134 100644 --- a/numpy/_array_api/_linear_algebra_functions.py +++ b/numpy/_array_api/_linear_algebra_functions.py @@ -1,11 +1,9 @@ from __future__ import annotations -from ._array_object import ndarray +from ._array_object import Array from ._dtypes import _numeric_dtypes -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from ._types import Optional, Sequence, Tuple, Union, Array +from typing import Optional, Sequence, Tuple, Union import numpy as np @@ -30,7 +28,7 @@ def matmul(x1: Array, x2: Array, /) -> Array: if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in matmul') - return ndarray._new(np.matmul(x1._array, x2._array)) + return Array._new(np.matmul(x1._array, x2._array)) # Note: axes must be a tuple, unlike np.tensordot where it can be an array or array-like. def tensordot(x1: Array, x2: Array, /, *, axes: Union[int, Tuple[Sequence[int], Sequence[int]]] = 2) -> Array: @@ -39,7 +37,7 @@ def tensordot(x1: Array, x2: Array, /, *, axes: Union[int, Tuple[Sequence[int], if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in tensordot') - return ndarray._new(np.tensordot(x1._array, x2._array, axes=axes)) + return Array._new(np.tensordot(x1._array, x2._array, axes=axes)) def transpose(x: Array, /, *, axes: Optional[Tuple[int, ...]] = None) -> Array: """ @@ -47,7 +45,7 @@ def transpose(x: Array, /, *, axes: Optional[Tuple[int, ...]] = None) -> Array: See its docstring for more information. """ - return ndarray._new(np.transpose(x._array, axes=axes)) + return Array._new(np.transpose(x._array, axes=axes)) # Note: vecdot is not in NumPy def vecdot(x1: Array, x2: Array, /, *, axis: Optional[int] = None) -> Array: diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index da02155f9..c569d2834 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -1,10 +1,10 @@ from __future__ import annotations -from ._array_object import ndarray +from ._array_object import Array from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Optional, Tuple, Union, Array + from ._types import Optional, Tuple, Union import numpy as np @@ -16,7 +16,7 @@ def concat(arrays: Tuple[Array, ...], /, *, axis: Optional[int] = 0) -> Array: See its docstring for more information. """ arrays = tuple(a._array for a in arrays) - return ndarray._new(np.concatenate(arrays, axis=axis)) + return Array._new(np.concatenate(arrays, axis=axis)) def expand_dims(x: Array, /, *, axis: int) -> Array: """ @@ -24,7 +24,7 @@ def expand_dims(x: Array, /, *, axis: int) -> Array: See its docstring for more information. """ - return ndarray._new(np.expand_dims(x._array, axis)) + return Array._new(np.expand_dims(x._array, axis)) def flip(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> Array: """ @@ -32,7 +32,7 @@ def flip(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> See its docstring for more information. """ - return ndarray._new(np.flip(x._array, axis=axis)) + return Array._new(np.flip(x._array, axis=axis)) def reshape(x: Array, /, shape: Tuple[int, ...]) -> Array: """ @@ -40,7 +40,7 @@ def reshape(x: Array, /, shape: Tuple[int, ...]) -> Array: See its docstring for more information. """ - return ndarray._new(np.reshape(x._array, shape)) + return Array._new(np.reshape(x._array, shape)) def roll(x: Array, /, shift: Union[int, Tuple[int, ...]], *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> Array: """ @@ -48,7 +48,7 @@ def roll(x: Array, /, shift: Union[int, Tuple[int, ...]], *, axis: Optional[Unio See its docstring for more information. """ - return ndarray._new(np.roll(x._array, shift, axis=axis)) + return Array._new(np.roll(x._array, shift, axis=axis)) def squeeze(x: Array, /, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> Array: """ @@ -56,7 +56,7 @@ def squeeze(x: Array, /, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> See its docstring for more information. """ - return ndarray._new(np.squeeze(x._array, axis=axis)) + return Array._new(np.squeeze(x._array, axis=axis)) def stack(arrays: Tuple[Array, ...], /, *, axis: int = 0) -> Array: """ @@ -65,4 +65,4 @@ def stack(arrays: Tuple[Array, ...], /, *, axis: int = 0) -> Array: See its docstring for more information. """ arrays = tuple(a._array for a in arrays) - return ndarray._new(np.stack(arrays, axis=axis)) + return Array._new(np.stack(arrays, axis=axis)) diff --git a/numpy/_array_api/_searching_functions.py b/numpy/_array_api/_searching_functions.py index 690256430..727f3013f 100644 --- a/numpy/_array_api/_searching_functions.py +++ b/numpy/_array_api/_searching_functions.py @@ -1,10 +1,10 @@ from __future__ import annotations -from ._array_object import ndarray +from ._array_object import Array from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Tuple, Array + from ._types import Tuple import numpy as np @@ -15,7 +15,7 @@ def argmax(x: Array, /, *, axis: int = None, keepdims: bool = False) -> Array: See its docstring for more information. """ # Note: this currently fails as np.argmax does not implement keepdims - return ndarray._new(np.asarray(np.argmax(x._array, axis=axis, keepdims=keepdims))) + return Array._new(np.asarray(np.argmax(x._array, axis=axis, keepdims=keepdims))) def argmin(x: Array, /, *, axis: int = None, keepdims: bool = False) -> Array: """ @@ -24,7 +24,7 @@ def argmin(x: Array, /, *, axis: int = None, keepdims: bool = False) -> Array: See its docstring for more information. """ # Note: this currently fails as np.argmin does not implement keepdims - return ndarray._new(np.asarray(np.argmin(x._array, axis=axis, keepdims=keepdims))) + return Array._new(np.asarray(np.argmin(x._array, axis=axis, keepdims=keepdims))) def nonzero(x: Array, /) -> Tuple[Array, ...]: """ @@ -32,7 +32,7 @@ def nonzero(x: Array, /) -> Tuple[Array, ...]: See its docstring for more information. """ - return ndarray._new(np.nonzero(x._array)) + return Array._new(np.nonzero(x._array)) def where(condition: Array, x1: Array, x2: Array, /) -> Array: """ @@ -40,4 +40,4 @@ def where(condition: Array, x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ - return ndarray._new(np.where(condition._array, x1._array, x2._array)) + return Array._new(np.where(condition._array, x1._array, x2._array)) diff --git a/numpy/_array_api/_set_functions.py b/numpy/_array_api/_set_functions.py index 719d54e5f..098145866 100644 --- a/numpy/_array_api/_set_functions.py +++ b/numpy/_array_api/_set_functions.py @@ -1,10 +1,10 @@ from __future__ import annotations -from ._array_object import ndarray +from ._array_object import Array from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Tuple, Union, Array + from ._types import Tuple, Union import numpy as np @@ -14,4 +14,4 @@ def unique(x: Array, /, *, return_counts: bool = False, return_index: bool = Fal See its docstring for more information. """ - return ndarray._new(np.unique(x._array, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse)) + return Array._new(np.unique(x._array, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse)) diff --git a/numpy/_array_api/_sorting_functions.py b/numpy/_array_api/_sorting_functions.py index 3dc0ec444..a125e0718 100644 --- a/numpy/_array_api/_sorting_functions.py +++ b/numpy/_array_api/_sorting_functions.py @@ -1,10 +1,6 @@ from __future__ import annotations -from ._array_object import ndarray - -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from ._types import Array +from ._array_object import Array import numpy as np @@ -19,7 +15,7 @@ def argsort(x: Array, /, *, axis: int = -1, descending: bool = False, stable: bo res = np.argsort(x._array, axis=axis, kind=kind) if descending: res = np.flip(res, axis=axis) - return ndarray._new(res) + return Array._new(res) def sort(x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True) -> Array: """ @@ -32,4 +28,4 @@ def sort(x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool res = np.sort(x._array, axis=axis, kind=kind) if descending: res = np.flip(res, axis=axis) - return ndarray._new(res) + return Array._new(res) diff --git a/numpy/_array_api/_statistical_functions.py b/numpy/_array_api/_statistical_functions.py index e6a791fe6..4f6b1c034 100644 --- a/numpy/_array_api/_statistical_functions.py +++ b/numpy/_array_api/_statistical_functions.py @@ -1,32 +1,32 @@ from __future__ import annotations -from ._array_object import ndarray +from ._array_object import Array from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Optional, Tuple, Union, Array + from ._types import Optional, Tuple, Union import numpy as np def max(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: - return ndarray._new(np.max(x._array, axis=axis, keepdims=keepdims)) + return Array._new(np.max(x._array, axis=axis, keepdims=keepdims)) def mean(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: - return ndarray._new(np.asarray(np.mean(x._array, axis=axis, keepdims=keepdims))) + return Array._new(np.asarray(np.mean(x._array, axis=axis, keepdims=keepdims))) def min(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: - return ndarray._new(np.min(x._array, axis=axis, keepdims=keepdims)) + return Array._new(np.min(x._array, axis=axis, keepdims=keepdims)) def prod(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: - return ndarray._new(np.asarray(np.prod(x._array, axis=axis, keepdims=keepdims))) + return Array._new(np.asarray(np.prod(x._array, axis=axis, keepdims=keepdims))) def std(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> Array: # Note: the keyword argument correction is different here - return ndarray._new(np.asarray(np.std(x._array, axis=axis, ddof=correction, keepdims=keepdims))) + return Array._new(np.asarray(np.std(x._array, axis=axis, ddof=correction, keepdims=keepdims))) def sum(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: - return ndarray._new(np.asarray(np.sum(x._array, axis=axis, keepdims=keepdims))) + return Array._new(np.asarray(np.sum(x._array, axis=axis, keepdims=keepdims))) def var(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> Array: # Note: the keyword argument correction is different here - return ndarray._new(np.asarray(np.var(x._array, axis=axis, ddof=correction, keepdims=keepdims))) + return Array._new(np.asarray(np.var(x._array, axis=axis, ddof=correction, keepdims=keepdims))) diff --git a/numpy/_array_api/_types.py b/numpy/_array_api/_types.py index 602c1df3e..d365e5e8c 100644 --- a/numpy/_array_api/_types.py +++ b/numpy/_array_api/_types.py @@ -12,7 +12,7 @@ __all__ = ['Any', 'List', 'Literal', 'Optional', 'Tuple', 'Union', 'Array', from typing import Any, List, Literal, Optional, Tuple, Union, TypeVar -from . import (ndarray, int8, int16, int32, int64, uint8, uint16, uint32, +from . import (Array, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64) Array = ndarray diff --git a/numpy/_array_api/_utility_functions.py b/numpy/_array_api/_utility_functions.py index a6a7721dd..ba77d3668 100644 --- a/numpy/_array_api/_utility_functions.py +++ b/numpy/_array_api/_utility_functions.py @@ -1,10 +1,10 @@ from __future__ import annotations -from ._array_object import ndarray +from ._array_object import Array from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Optional, Tuple, Union, Array + from ._types import Optional, Tuple, Union import numpy as np @@ -14,7 +14,7 @@ def all(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keep See its docstring for more information. """ - return ndarray._new(np.asarray(np.all(x._array, axis=axis, keepdims=keepdims))) + return Array._new(np.asarray(np.all(x._array, axis=axis, keepdims=keepdims))) def any(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: """ @@ -22,4 +22,4 @@ def any(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keep See its docstring for more information. """ - return ndarray._new(np.asarray(np.any(x._array, axis=axis, keepdims=keepdims))) + return Array._new(np.asarray(np.any(x._array, axis=axis, keepdims=keepdims))) -- cgit v1.2.1 From 4240314ed1e77e0b9d4546e654871274faca3e64 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 8 Jul 2021 17:42:35 -0600 Subject: Update the docstring of numpy/_array_api/__init__.py --- numpy/_array_api/__init__.py | 69 ++++++++++++++++++++++++-------------------- 1 file changed, 37 insertions(+), 32 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/__init__.py b/numpy/_array_api/__init__.py index 320c8df19..be8345759 100644 --- a/numpy/_array_api/__init__.py +++ b/numpy/_array_api/__init__.py @@ -1,6 +1,8 @@ """ A NumPy sub-namespace that conforms to the Python array API standard. +This submodule accompanies NEP 47, which proposes its inclusion in NumPy. + This is a proof-of-concept namespace that wraps the corresponding NumPy functions to give a conforming implementation of the Python array API standard (https://data-apis.github.io/array-api/latest/). The standard is currently in @@ -8,9 +10,6 @@ an RFC phase and comments on it are both welcome and encouraged. Comments should be made either at https://github.com/data-apis/array-api or at https://github.com/data-apis/consortium-feedback/discussions. -This submodule will be accompanied with a NEP (not yet written) proposing its -inclusion in NumPy. - NumPy already follows the proposed spec for the most part, so this module serves mostly as a thin wrapper around it. However, NumPy also implements a lot of behavior that is not included in the spec, so this serves as a @@ -18,15 +17,19 @@ restricted subset of the API. Only those functions that are part of the spec are included in this namespace, and all functions are given with the exact signature given in the spec, including the use of position-only arguments, and omitting any extra keyword arguments implemented by NumPy but not part of the -spec. Note that the array object itself is unchanged, as implementing a -restricted subclass of ndarray seems unnecessarily complex for the purposes of -this namespace, so the API of array methods and other behaviors of the array -object will include things that are not part of the spec. - -The spec is designed as a "minimal API subset" and explicitly allows libraries -to include behaviors not specified by it. But users of this module that intend -to write portable code should be aware that only those behaviors that are -listed in the spec are guaranteed to be implemented across libraries. +spec. The behavior of some functions is also modified from the NumPy behavior +to conform to the standard. Note that the underlying array object itself is +wrapped in a wrapper Array() class, but is otherwise unchanged. This submodule +is implemented in pure Python with no C extensions. + +The array API spec is designed as a "minimal API subset" and explicitly allows +libraries to include behaviors not specified by it. But users of this module +that intend to write portable code should be aware that only those behaviors +that are listed in the spec are guaranteed to be implemented across libraries. +Consequently, the NumPy implementation was chosen to be both conforming and +minimal, so that users can use this implementation of the array API namespace +and be sure that behaviors that it defines will be available in conforming +namespaces from other libraries. A few notes about the current state of this submodule: @@ -45,16 +48,10 @@ A few notes about the current state of this submodule: not included here, as it requires a full implementation in NumPy proper first. - - np.argmin and np.argmax do not implement the keepdims keyword argument. - - The linear algebra extension in the spec will be added in a future pull request. - - Some tests in the test suite are still not fully correct in that they test - all datatypes whereas certain functions are only defined for a subset of - datatypes. - - The test suite is yet complete, and even the tests that exist are not + The test suite is not yet complete, and even the tests that exist are not guaranteed to give a comprehensive coverage of the spec. Therefore, those reviewing this submodule should refer to the standard documents themselves. @@ -68,6 +65,10 @@ request. dtypes and only those methods that are required by the spec, as well as to limit/change certain behavior that differs in the spec. In particular: + - The array API namespace does not have scalar objects, only 0-d arrays. + Operations in on Array that would create a scalar in NumPy create a 0-d + array. + - Indexing: Only a subset of indices supported by NumPy are required by the spec. The Array object restricts indexing to only allow those types of indices that are required by the spec. See the docstring of the @@ -75,17 +76,27 @@ request. information. - Type promotion: Some type promotion rules are different in the spec. In - particular, the spec does not have any value-based casting. Note that the - code to correct the type promotion rules on numpy._array_api.Array is - not yet implemented. + particular, the spec does not have any value-based casting. The + Array._promote_scalar method promotes Python scalars to arrays, + disallowing cross-type promotions like int -> float64 that are not allowed + in the spec. Array._normalize_two_args works around some type promotion + quirks in NumPy, particularly, value-based casting that occurs when one + argument of an operation is a 0-d array. - All functions include type annotations, corresponding to those given in the - spec (see _types.py for definitions of the types 'array', 'device', and - 'dtype'). These do not currently fully pass mypy due to some limitations in - mypy. + spec (see _types.py for definitions of some custom types). These do not + currently fully pass mypy due to some limitations in mypy. + +- Dtype objects are just the NumPy dtype objects, e.g., float64 = + np.dtype('float64'). The spec does not require any behavior on these dtype + objects other than that they be accessible by name and be comparable by + equality, but it was considered too much extra complexity to create custom + objects to represent dtypes. - The wrapper functions in this module do not do any type checking for things - that would be impossible without leaving the _array_api namespace. + that would be impossible without leaving the _array_api namespace. For + example, since the array API dtype objects are just the NumPy dtype objects, + one could pass in a non-spec NumPy dtype into a function. - All places where the implementations in this submodule are known to deviate from their corresponding functions in NumPy are marked with "# Note" @@ -93,12 +104,6 @@ request. Still TODO in this module are: -- Implement the spec type promotion rules on the Array object. - -- Disable NumPy warnings in the API functions. - -- Implement keepdims on argmin and argmax. - - Device support and DLPack support are not yet implemented. These require support in NumPy itself first. -- cgit v1.2.1 From 5780a9bb5f662891da39eae80961455aaba0f6ac Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 9 Jul 2021 13:56:34 -0600 Subject: Remove typing exports from numpy/_array_api/_types.py --- numpy/_array_api/_creation_functions.py | 7 +++---- numpy/_array_api/_manipulation_functions.py | 4 +--- numpy/_array_api/_types.py | 7 +++---- 3 files changed, 7 insertions(+), 11 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 8fb2a8b12..9e9722a55 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -1,11 +1,10 @@ from __future__ import annotations -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, List, Optional, Tuple, Union if TYPE_CHECKING: - from ._types import (List, Optional, SupportsDLPack, - SupportsBufferProtocol, Tuple, Union, Array, Device, - Dtype) + from ._types import (NestedSequence, SupportsDLPack, + SupportsBufferProtocol, Array, Device, Dtype) from collections.abc import Sequence from ._dtypes import _all_dtypes diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index c569d2834..fa0c08d7b 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -2,9 +2,7 @@ from __future__ import annotations from ._array_object import Array -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from ._types import Optional, Tuple, Union +from typing import List, Optional, Tuple, Union import numpy as np diff --git a/numpy/_array_api/_types.py b/numpy/_array_api/_types.py index d365e5e8c..050ad4031 100644 --- a/numpy/_array_api/_types.py +++ b/numpy/_array_api/_types.py @@ -6,11 +6,10 @@ annotations in the function signatures. The functions in the module are only valid for inputs that match the given type annotations. """ -__all__ = ['Any', 'List', 'Literal', 'Optional', 'Tuple', 'Union', 'Array', - 'Device', 'Dtype', 'SupportsDLPack', 'SupportsBufferProtocol', - 'PyCapsule'] +__all__ = ['Array', 'Device', 'Dtype', 'SupportsDLPack', + 'SupportsBufferProtocol', 'PyCapsule'] -from typing import Any, List, Literal, Optional, Tuple, Union, TypeVar +from typing import Any, Sequence, Type, Union from . import (Array, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64) -- cgit v1.2.1 From 29b7a69a39ac66ebd8f61c6c9c65e7e60b40b4a0 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 9 Jul 2021 13:57:01 -0600 Subject: Use better type definitions for the array API custom types --- numpy/_array_api/_types.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_types.py b/numpy/_array_api/_types.py index 050ad4031..4ff718205 100644 --- a/numpy/_array_api/_types.py +++ b/numpy/_array_api/_types.py @@ -14,10 +14,13 @@ from typing import Any, Sequence, Type, Union from . import (Array, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64) -Array = ndarray -Device = TypeVar('device') -Dtype = Literal[int8, int16, int32, int64, uint8, uint16, - uint32, uint64, float32, float64] -SupportsDLPack = TypeVar('SupportsDLPack') -SupportsBufferProtocol = TypeVar('SupportsBufferProtocol') -PyCapsule = TypeVar('PyCapsule') +# This should really be recursive, but that isn't supported yet. See the +# similar comment in numpy/typing/_array_like.py +NestedSequence = Sequence[Sequence[Any]] + +Device = Any +Dtype = Type[Union[[int8, int16, int32, int64, uint8, uint16, + uint32, uint64, float32, float64]]] +SupportsDLPack = Any +SupportsBufferProtocol = Any +PyCapsule = Any -- cgit v1.2.1 From 74478e2d943f4d61917d4d9122a042214eed94fd Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 9 Jul 2021 13:57:44 -0600 Subject: Use better type signatures in the array API module This includes returning custom dataclasses for finfo and iinfo that only contain the properties required by the array API specification. --- numpy/_array_api/_array_object.py | 15 ++++++------ numpy/_array_api/_creation_functions.py | 2 +- numpy/_array_api/_data_type_functions.py | 37 +++++++++++++++++++++++++++-- numpy/_array_api/_manipulation_functions.py | 4 ++-- 4 files changed, 46 insertions(+), 12 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 9ea0eef18..43d8a8961 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -396,7 +396,8 @@ class Array: res = self._array.__le__(other._array) return self.__class__._new(res) - def __len__(self, /): + # Note: __len__ may end up being removed from the array API spec. + def __len__(self, /) -> int: """ Performs the operation __len__. """ @@ -843,7 +844,7 @@ class Array: return self.__class__._new(res) @property - def dtype(self): + def dtype(self) -> Dtype: """ Array API compatible wrapper for :py:meth:`np.ndaray.dtype `. @@ -852,7 +853,7 @@ class Array: return self._array.dtype @property - def device(self): + def device(self) -> Device: """ Array API compatible wrapper for :py:meth:`np.ndaray.device `. @@ -862,7 +863,7 @@ class Array: raise NotImplementedError("The device attribute is not yet implemented") @property - def ndim(self): + def ndim(self) -> int: """ Array API compatible wrapper for :py:meth:`np.ndaray.ndim `. @@ -871,7 +872,7 @@ class Array: return self._array.ndim @property - def shape(self): + def shape(self) -> Tuple[int, ...]: """ Array API compatible wrapper for :py:meth:`np.ndaray.shape `. @@ -880,7 +881,7 @@ class Array: return self._array.shape @property - def size(self): + def size(self) -> int: """ Array API compatible wrapper for :py:meth:`np.ndaray.size `. @@ -889,7 +890,7 @@ class Array: return self._array.size @property - def T(self): + def T(self) -> Array: """ Array API compatible wrapper for :py:meth:`np.ndaray.T `. diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 9e9722a55..517c2bfdd 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -10,7 +10,7 @@ from ._dtypes import _all_dtypes import numpy as np -def asarray(obj: Union[float, NestedSequence[bool|int|float], SupportsDLPack, SupportsBufferProtocol], /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, copy: Optional[bool] = None) -> Array: +def asarray(obj: Union[Array, float, NestedSequence[bool|int|float], SupportsDLPack, SupportsBufferProtocol], /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, copy: Optional[bool] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.asarray `. diff --git a/numpy/_array_api/_data_type_functions.py b/numpy/_array_api/_data_type_functions.py index 2f304bf49..693ceae84 100644 --- a/numpy/_array_api/_data_type_functions.py +++ b/numpy/_array_api/_data_type_functions.py @@ -2,6 +2,7 @@ from __future__ import annotations from ._array_object import Array +from dataclasses import dataclass from typing import TYPE_CHECKING if TYPE_CHECKING: from ._types import List, Tuple, Union, Dtype @@ -38,13 +39,44 @@ def can_cast(from_: Union[Dtype, Array], to: Dtype, /) -> bool: from_ = from_._array return np.can_cast(from_, to) +# These are internal objects for the return types of finfo and iinfo, since +# the NumPy versions contain extra data that isn't part of the spec. +@dataclass +class finfo_object: + bits: int + # Note: The types of the float data here are float, whereas in NumPy they + # are scalars of the corresponding float dtype. + eps: float + max: float + min: float + # Note: smallest_normal is part of the array API spec, but cannot be used + # until https://github.com/numpy/numpy/pull/18536 is merged. + + # smallest_normal: float + +@dataclass +class iinfo_object: + bits: int + max: int + min: int + def finfo(type: Union[Dtype, Array], /) -> finfo_object: """ Array API compatible wrapper for :py:func:`np.finfo `. See its docstring for more information. """ - return np.finfo(type) + fi = np.finfo(type) + # Note: The types of the float data here are float, whereas in NumPy they + # are scalars of the corresponding float dtype. + return finfo_object( + fi.bits, + float(fi.eps), + float(fi.max), + float(fi.min), + # TODO: Uncomment this when #18536 is merged. + # float(fi.smallest_normal), + ) def iinfo(type: Union[Dtype, Array], /) -> iinfo_object: """ @@ -52,7 +84,8 @@ def iinfo(type: Union[Dtype, Array], /) -> iinfo_object: See its docstring for more information. """ - return np.iinfo(type) + ii = np.iinfo(type) + return iinfo_object(ii.bits, ii.max, ii.min) def result_type(*arrays_and_dtypes: Sequence[Union[Array, Dtype]]) -> Dtype: """ diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index fa0c08d7b..6308bfc26 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -7,7 +7,7 @@ from typing import List, Optional, Tuple, Union import numpy as np # Note: the function name is different here -def concat(arrays: Tuple[Array, ...], /, *, axis: Optional[int] = 0) -> Array: +def concat(arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: Optional[int] = 0) -> Array: """ Array API compatible wrapper for :py:func:`np.concatenate `. @@ -56,7 +56,7 @@ def squeeze(x: Array, /, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> """ return Array._new(np.squeeze(x._array, axis=axis)) -def stack(arrays: Tuple[Array, ...], /, *, axis: int = 0) -> Array: +def stack(arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: int = 0) -> Array: """ Array API compatible wrapper for :py:func:`np.stack `. -- cgit v1.2.1 From 60add4a3ebabcc1e8dae07c4c11a65f56607f708 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 9 Jul 2021 13:58:30 -0600 Subject: Small code cleanup --- numpy/_array_api/_array_object.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 43d8a8961..404a09654 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -357,7 +357,7 @@ class Array: # docstring of _validate_index key = self._validate_index(key, self.shape) res = self._array.__getitem__(key) - return self.__class__._new(res) + return self._new(res) def __gt__(self: Array, other: Union[int, float, Array], /) -> Array: """ -- cgit v1.2.1 From 6379138a6da6ebf73bfc4bc4e019a21d8a99be0a Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 9 Jul 2021 14:00:34 -0600 Subject: Rename numpy/_array_api/_types.py to numpy/_array_api/_typing.py --- numpy/_array_api/__init__.py | 2 +- numpy/_array_api/_array_object.py | 2 +- numpy/_array_api/_creation_functions.py | 2 +- numpy/_array_api/_data_type_functions.py | 2 +- numpy/_array_api/_searching_functions.py | 2 +- numpy/_array_api/_set_functions.py | 2 +- numpy/_array_api/_statistical_functions.py | 2 +- numpy/_array_api/_types.py | 26 -------------------------- numpy/_array_api/_typing.py | 26 ++++++++++++++++++++++++++ numpy/_array_api/_utility_functions.py | 2 +- 10 files changed, 34 insertions(+), 34 deletions(-) delete mode 100644 numpy/_array_api/_types.py create mode 100644 numpy/_array_api/_typing.py (limited to 'numpy') diff --git a/numpy/_array_api/__init__.py b/numpy/_array_api/__init__.py index be8345759..57a4ff4e1 100644 --- a/numpy/_array_api/__init__.py +++ b/numpy/_array_api/__init__.py @@ -84,7 +84,7 @@ request. argument of an operation is a 0-d array. - All functions include type annotations, corresponding to those given in the - spec (see _types.py for definitions of some custom types). These do not + spec (see _typing.py for definitions of some custom types). These do not currently fully pass mypy due to some limitations in mypy. - Dtype objects are just the NumPy dtype objects, e.g., float64 = diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 404a09654..2377bffe3 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -22,7 +22,7 @@ from ._dtypes import _boolean_dtypes, _integer_dtypes, _floating_dtypes from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Any, Optional, PyCapsule, Tuple, Union, Device, Dtype + from ._typing import Any, Optional, PyCapsule, Tuple, Union, Device, Dtype import numpy as np diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 517c2bfdd..88b3808b4 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -3,7 +3,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, List, Optional, Tuple, Union if TYPE_CHECKING: - from ._types import (NestedSequence, SupportsDLPack, + from ._typing import (NestedSequence, SupportsDLPack, SupportsBufferProtocol, Array, Device, Dtype) from collections.abc import Sequence from ._dtypes import _all_dtypes diff --git a/numpy/_array_api/_data_type_functions.py b/numpy/_array_api/_data_type_functions.py index 693ceae84..0c42386b5 100644 --- a/numpy/_array_api/_data_type_functions.py +++ b/numpy/_array_api/_data_type_functions.py @@ -5,7 +5,7 @@ from ._array_object import Array from dataclasses import dataclass from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import List, Tuple, Union, Dtype + from ._typing import List, Tuple, Union, Dtype from collections.abc import Sequence import numpy as np diff --git a/numpy/_array_api/_searching_functions.py b/numpy/_array_api/_searching_functions.py index 727f3013f..c96f258bb 100644 --- a/numpy/_array_api/_searching_functions.py +++ b/numpy/_array_api/_searching_functions.py @@ -4,7 +4,7 @@ from ._array_object import Array from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Tuple + from ._typing import Tuple import numpy as np diff --git a/numpy/_array_api/_set_functions.py b/numpy/_array_api/_set_functions.py index 098145866..40d4895bf 100644 --- a/numpy/_array_api/_set_functions.py +++ b/numpy/_array_api/_set_functions.py @@ -4,7 +4,7 @@ from ._array_object import Array from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Tuple, Union + from ._typing import Tuple, Union import numpy as np diff --git a/numpy/_array_api/_statistical_functions.py b/numpy/_array_api/_statistical_functions.py index 4f6b1c034..9e032adf0 100644 --- a/numpy/_array_api/_statistical_functions.py +++ b/numpy/_array_api/_statistical_functions.py @@ -4,7 +4,7 @@ from ._array_object import Array from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Optional, Tuple, Union + from ._typing import Optional, Tuple, Union import numpy as np diff --git a/numpy/_array_api/_types.py b/numpy/_array_api/_types.py deleted file mode 100644 index 4ff718205..000000000 --- a/numpy/_array_api/_types.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -This file defines the types for type annotations. - -These names aren't part of the module namespace, but they are used in the -annotations in the function signatures. The functions in the module are only -valid for inputs that match the given type annotations. -""" - -__all__ = ['Array', 'Device', 'Dtype', 'SupportsDLPack', - 'SupportsBufferProtocol', 'PyCapsule'] - -from typing import Any, Sequence, Type, Union - -from . import (Array, int8, int16, int32, int64, uint8, uint16, uint32, - uint64, float32, float64) - -# This should really be recursive, but that isn't supported yet. See the -# similar comment in numpy/typing/_array_like.py -NestedSequence = Sequence[Sequence[Any]] - -Device = Any -Dtype = Type[Union[[int8, int16, int32, int64, uint8, uint16, - uint32, uint64, float32, float64]]] -SupportsDLPack = Any -SupportsBufferProtocol = Any -PyCapsule = Any diff --git a/numpy/_array_api/_typing.py b/numpy/_array_api/_typing.py new file mode 100644 index 000000000..4ff718205 --- /dev/null +++ b/numpy/_array_api/_typing.py @@ -0,0 +1,26 @@ +""" +This file defines the types for type annotations. + +These names aren't part of the module namespace, but they are used in the +annotations in the function signatures. The functions in the module are only +valid for inputs that match the given type annotations. +""" + +__all__ = ['Array', 'Device', 'Dtype', 'SupportsDLPack', + 'SupportsBufferProtocol', 'PyCapsule'] + +from typing import Any, Sequence, Type, Union + +from . import (Array, int8, int16, int32, int64, uint8, uint16, uint32, + uint64, float32, float64) + +# This should really be recursive, but that isn't supported yet. See the +# similar comment in numpy/typing/_array_like.py +NestedSequence = Sequence[Sequence[Any]] + +Device = Any +Dtype = Type[Union[[int8, int16, int32, int64, uint8, uint16, + uint32, uint64, float32, float64]]] +SupportsDLPack = Any +SupportsBufferProtocol = Any +PyCapsule = Any diff --git a/numpy/_array_api/_utility_functions.py b/numpy/_array_api/_utility_functions.py index ba77d3668..3a387877e 100644 --- a/numpy/_array_api/_utility_functions.py +++ b/numpy/_array_api/_utility_functions.py @@ -4,7 +4,7 @@ from ._array_object import Array from typing import TYPE_CHECKING if TYPE_CHECKING: - from ._types import Optional, Tuple, Union + from ._typing import Optional, Tuple, Union import numpy as np -- cgit v1.2.1 From 5febef530e055572fd5eac18807675ee451c81b0 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 9 Jul 2021 16:24:45 -0600 Subject: Only allow floating-point dtypes in the array API __pow__ and __truediv__ See https://github.com/data-apis/array-api/pull/221. --- numpy/_array_api/_array_object.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 2377bffe3..797f9ea4f 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -503,6 +503,8 @@ class Array: if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) + if self.dtype not in _floating_dtypes or other.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in __pow__') # Note: NumPy's __pow__ does not follow type promotion rules for 0-d # arrays, so we use pow() here instead. return pow(self, other) @@ -548,6 +550,8 @@ class Array: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) + if self.dtype not in _floating_dtypes or other.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in __truediv__') self, other = self._normalize_two_args(self, other) res = self._array.__truediv__(other._array) return self.__class__._new(res) @@ -744,6 +748,8 @@ class Array: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) + if self.dtype not in _floating_dtypes or other.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in __pow__') self._array.__ipow__(other._array) return self @@ -756,6 +762,8 @@ class Array: if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) + if self.dtype not in _floating_dtypes or other.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in __pow__') # Note: NumPy's __pow__ does not follow the spec type promotion rules # for 0-d arrays, so we use pow() here instead. return pow(other, self) @@ -810,6 +818,8 @@ class Array: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) + if self.dtype not in _floating_dtypes or other.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in __truediv__') self._array.__itruediv__(other._array) return self @@ -820,6 +830,8 @@ class Array: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) + if self.dtype not in _floating_dtypes or other.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in __truediv__') self, other = self._normalize_two_args(self, other) res = self._array.__rtruediv__(other._array) return self.__class__._new(res) -- cgit v1.2.1 From c5999e2163f06bb9316dab03d0e1b2173e78bb65 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 9 Jul 2021 16:28:13 -0600 Subject: Update the type hints for the array API __pow__ and __truediv__ They should not accept int. PEP 484 actually makes int a subtype of float, so this won't actually affect type checkers the way we would want. --- numpy/_array_api/_array_object.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 797f9ea4f..a58063698 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -494,8 +494,10 @@ class Array: res = self._array.__pos__() return self.__class__._new(res) + # PEP 484 requires int to be a subtype of float, but __pow__ should not + # accept int. @np.errstate(all='ignore') - def __pow__(self: Array, other: Union[int, float, Array], /) -> Array: + def __pow__(self: Array, other: Union[float, Array], /) -> Array: """ Performs the operation __pow__. """ @@ -543,8 +545,10 @@ class Array: res = self._array.__sub__(other._array) return self.__class__._new(res) + # PEP 484 requires int to be a subtype of float, but __truediv__ should + # not accept int. @np.errstate(all='ignore') - def __truediv__(self: Array, other: Union[int, float, Array], /) -> Array: + def __truediv__(self: Array, other: Union[float, Array], /) -> Array: """ Performs the operation __truediv__. """ @@ -742,7 +746,7 @@ class Array: return self.__class__._new(res) @np.errstate(all='ignore') - def __ipow__(self: Array, other: Union[int, float, Array], /) -> Array: + def __ipow__(self: Array, other: Union[float, Array], /) -> Array: """ Performs the operation __ipow__. """ @@ -754,7 +758,7 @@ class Array: return self @np.errstate(all='ignore') - def __rpow__(self: Array, other: Union[int, float, Array], /) -> Array: + def __rpow__(self: Array, other: Union[float, Array], /) -> Array: """ Performs the operation __rpow__. """ @@ -812,7 +816,7 @@ class Array: return self.__class__._new(res) @np.errstate(all='ignore') - def __itruediv__(self: Array, other: Union[int, float, Array], /) -> Array: + def __itruediv__(self: Array, other: Union[float, Array], /) -> Array: """ Performs the operation __itruediv__. """ @@ -824,7 +828,7 @@ class Array: return self @np.errstate(all='ignore') - def __rtruediv__(self: Array, other: Union[int, float, Array], /) -> Array: + def __rtruediv__(self: Array, other: Union[float, Array], /) -> Array: """ Performs the operation __rtruediv__. """ -- cgit v1.2.1 From 29974fba6810e1be7e8a2ba8322bd8c78a9012d0 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 9 Jul 2021 16:32:42 -0600 Subject: Use tuples for internal type lists in the array API These are easier for type checkers to handle. --- numpy/_array_api/_dtypes.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_dtypes.py b/numpy/_array_api/_dtypes.py index c874763dd..1bec88c36 100644 --- a/numpy/_array_api/_dtypes.py +++ b/numpy/_array_api/_dtypes.py @@ -15,10 +15,10 @@ float64 = np.dtype('float64') # Note: This name is changed bool = np.dtype('bool') -_all_dtypes = [int8, int16, int32, int64, uint8, uint16, uint32, uint64, - float32, float64, bool] -_boolean_dtypes = [bool] -_floating_dtypes = [float32, float64] -_integer_dtypes = [int8, int16, int32, int64, uint8, uint16, uint32, uint64] -_integer_or_boolean_dtypes = [bool, int8, int16, int32, int64, uint8, uint16, uint32, uint64] -_numeric_dtypes = [float32, float64, int8, int16, int32, int64, uint8, uint16, uint32, uint64] +_all_dtypes = (int8, int16, int32, int64, uint8, uint16, uint32, uint64, + float32, float64, bool) +_boolean_dtypes = (bool) +_floating_dtypes = (float32, float64) +_integer_dtypes = (int8, int16, int32, int64, uint8, uint16, uint32, uint64) +_integer_or_boolean_dtypes = (bool, int8, int16, int32, int64, uint8, uint16, uint32, uint64) +_numeric_dtypes = (float32, float64, int8, int16, int32, int64, uint8, uint16, uint32, uint64) -- cgit v1.2.1 From 8ca96b2c949d23dd9fbfc7896845aa79dd2f0181 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 9 Jul 2021 16:38:08 -0600 Subject: Fix some typing imports --- numpy/_array_api/_array_object.py | 4 ++-- numpy/_array_api/_creation_functions.py | 4 ++-- numpy/_array_api/_data_type_functions.py | 4 ++-- numpy/_array_api/_searching_functions.py | 4 +--- numpy/_array_api/_set_functions.py | 4 +--- numpy/_array_api/_statistical_functions.py | 4 +--- numpy/_array_api/_utility_functions.py | 4 +--- 7 files changed, 10 insertions(+), 18 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index a58063698..0e0544afe 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -20,9 +20,9 @@ from enum import IntEnum from ._creation_functions import asarray from ._dtypes import _boolean_dtypes, _integer_dtypes, _floating_dtypes -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Any, Optional, Tuple, Union if TYPE_CHECKING: - from ._typing import Any, Optional, PyCapsule, Tuple, Union, Device, Dtype + from ._typing import PyCapsule, Device, Dtype import numpy as np diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 88b3808b4..2be0aea09 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -3,8 +3,8 @@ from __future__ import annotations from typing import TYPE_CHECKING, List, Optional, Tuple, Union if TYPE_CHECKING: - from ._typing import (NestedSequence, SupportsDLPack, - SupportsBufferProtocol, Array, Device, Dtype) + from ._typing import (Array, Device, Dtype, NestedSequence, + SupportsDLPack, SupportsBufferProtocol) from collections.abc import Sequence from ._dtypes import _all_dtypes diff --git a/numpy/_array_api/_data_type_functions.py b/numpy/_array_api/_data_type_functions.py index 0c42386b5..fe5c7557f 100644 --- a/numpy/_array_api/_data_type_functions.py +++ b/numpy/_array_api/_data_type_functions.py @@ -3,9 +3,9 @@ from __future__ import annotations from ._array_object import Array from dataclasses import dataclass -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, List, Tuple, Union if TYPE_CHECKING: - from ._typing import List, Tuple, Union, Dtype + from ._typing import Dtype from collections.abc import Sequence import numpy as np diff --git a/numpy/_array_api/_searching_functions.py b/numpy/_array_api/_searching_functions.py index c96f258bb..0c2bbd737 100644 --- a/numpy/_array_api/_searching_functions.py +++ b/numpy/_array_api/_searching_functions.py @@ -2,9 +2,7 @@ from __future__ import annotations from ._array_object import Array -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from ._typing import Tuple +from typing import Optional, Tuple import numpy as np diff --git a/numpy/_array_api/_set_functions.py b/numpy/_array_api/_set_functions.py index 40d4895bf..f28c2ee72 100644 --- a/numpy/_array_api/_set_functions.py +++ b/numpy/_array_api/_set_functions.py @@ -2,9 +2,7 @@ from __future__ import annotations from ._array_object import Array -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from ._typing import Tuple, Union +from typing import Tuple, Union import numpy as np diff --git a/numpy/_array_api/_statistical_functions.py b/numpy/_array_api/_statistical_functions.py index 9e032adf0..61fc60c46 100644 --- a/numpy/_array_api/_statistical_functions.py +++ b/numpy/_array_api/_statistical_functions.py @@ -2,9 +2,7 @@ from __future__ import annotations from ._array_object import Array -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from ._typing import Optional, Tuple, Union +from typing import Optional, Tuple, Union import numpy as np diff --git a/numpy/_array_api/_utility_functions.py b/numpy/_array_api/_utility_functions.py index 3a387877e..f243bfe68 100644 --- a/numpy/_array_api/_utility_functions.py +++ b/numpy/_array_api/_utility_functions.py @@ -2,9 +2,7 @@ from __future__ import annotations from ._array_object import Array -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from ._typing import Optional, Tuple, Union +from typing import Optional, Tuple, Union import numpy as np -- cgit v1.2.1 From 639aa7cd7ce02b741376c6c10226a6014310fc0b Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 9 Jul 2021 16:39:56 -0600 Subject: Fix the type hints for argmin/argmax in the array API --- numpy/_array_api/_searching_functions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_searching_functions.py b/numpy/_array_api/_searching_functions.py index 0c2bbd737..4764992a1 100644 --- a/numpy/_array_api/_searching_functions.py +++ b/numpy/_array_api/_searching_functions.py @@ -6,7 +6,7 @@ from typing import Optional, Tuple import numpy as np -def argmax(x: Array, /, *, axis: int = None, keepdims: bool = False) -> Array: +def argmax(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) -> Array: """ Array API compatible wrapper for :py:func:`np.argmax `. @@ -15,7 +15,7 @@ def argmax(x: Array, /, *, axis: int = None, keepdims: bool = False) -> Array: # Note: this currently fails as np.argmax does not implement keepdims return Array._new(np.asarray(np.argmax(x._array, axis=axis, keepdims=keepdims))) -def argmin(x: Array, /, *, axis: int = None, keepdims: bool = False) -> Array: +def argmin(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) -> Array: """ Array API compatible wrapper for :py:func:`np.argmin `. -- cgit v1.2.1 From 6765494edee2b90f239ae622abb5f3a7d218aa84 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 9 Jul 2021 17:58:39 -0600 Subject: Fix typo --- numpy/_array_api/_dtypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_dtypes.py b/numpy/_array_api/_dtypes.py index 1bec88c36..b183c800f 100644 --- a/numpy/_array_api/_dtypes.py +++ b/numpy/_array_api/_dtypes.py @@ -17,7 +17,7 @@ bool = np.dtype('bool') _all_dtypes = (int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, bool) -_boolean_dtypes = (bool) +_boolean_dtypes = (bool,) _floating_dtypes = (float32, float64) _integer_dtypes = (int8, int16, int32, int64, uint8, uint16, uint32, uint64) _integer_or_boolean_dtypes = (bool, int8, int16, int32, int64, uint8, uint16, uint32, uint64) -- cgit v1.2.1 From 5217236995f839250a148e533f4395007288cc24 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 9 Jul 2021 18:08:22 -0600 Subject: Make the array API left and right shift do type promotion The spec previously said it should return the type of the left argument, but this was changed to do type promotion to be consistent with all the other elementwise functions/operators. --- numpy/_array_api/_array_object.py | 28 ++++++++-------------------- numpy/_array_api/_elementwise_functions.py | 10 ++-------- 2 files changed, 10 insertions(+), 28 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 0e0544afe..6b9647626 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -410,11 +410,8 @@ class Array: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - # Note: The spec requires the return dtype of bitwise_left_shift, and - # hence also __lshift__, to be the same as the first argument. - # np.ndarray.__lshift__ returns a type that is the type promotion of - # the two input types. - res = self._array.__lshift__(other._array).astype(self.dtype) + self, other = self._normalize_two_args(self, other) + res = self._array.__lshift__(other._array) return self.__class__._new(res) def __lt__(self: Array, other: Union[int, float, Array], /) -> Array: @@ -517,11 +514,8 @@ class Array: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - # Note: The spec requires the return dtype of bitwise_right_shift, and - # hence also __rshift__, to be the same as the first argument. - # np.ndarray.__rshift__ returns a type that is the type promotion of - # the two input types. - res = self._array.__rshift__(other._array).astype(self.dtype) + self, other = self._normalize_two_args(self, other) + res = self._array.__rshift__(other._array) return self.__class__._new(res) def __setitem__(self, key, value, /): @@ -646,11 +640,8 @@ class Array: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - # Note: The spec requires the return dtype of bitwise_left_shift, and - # hence also __lshift__, to be the same as the first argument. - # np.ndarray.__lshift__ returns a type that is the type promotion of - # the two input types. - res = self._array.__rlshift__(other._array).astype(other.dtype) + self, other = self._normalize_two_args(self, other) + res = self._array.__rlshift__(other._array) return self.__class__._new(res) def __imatmul__(self: Array, other: Array, /) -> Array: @@ -787,11 +778,8 @@ class Array: """ if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) - # Note: The spec requires the return dtype of bitwise_right_shift, and - # hence also __rshift__, to be the same as the first argument. - # np.ndarray.__rshift__ returns a type that is the type promotion of - # the two input types. - res = self._array.__rrshift__(other._array).astype(other.dtype) + self, other = self._normalize_two_args(self, other) + res = self._array.__rrshift__(other._array) return self.__class__._new(res) @np.errstate(all='ignore') diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index 8dedc77fb..0d955b614 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -136,10 +136,7 @@ def bitwise_left_shift(x1: Array, x2: Array, /) -> Array: # Note: bitwise_left_shift is only defined for x2 nonnegative. if np.any(x2._array < 0): raise ValueError('bitwise_left_shift(x1, x2) is only defined for x2 >= 0') - # Note: The spec requires the return dtype of bitwise_left_shift to be the - # same as the first argument. np.left_shift() returns a type that is the - # type promotion of the two input types. - return Array._new(np.left_shift(x1._array, x2._array).astype(x1.dtype)) + return Array._new(np.left_shift(x1._array, x2._array)) # Note: the function name is different here def bitwise_invert(x: Array, /) -> Array: @@ -176,10 +173,7 @@ def bitwise_right_shift(x1: Array, x2: Array, /) -> Array: # Note: bitwise_right_shift is only defined for x2 nonnegative. if np.any(x2._array < 0): raise ValueError('bitwise_right_shift(x1, x2) is only defined for x2 >= 0') - # Note: The spec requires the return dtype of bitwise_left_shift to be the - # same as the first argument. np.left_shift() returns a type that is the - # type promotion of the two input types. - return Array._new(np.right_shift(x1._array, x2._array).astype(x1.dtype)) + return Array._new(np.right_shift(x1._array, x2._array)) def bitwise_xor(x1: Array, x2: Array, /) -> Array: """ -- cgit v1.2.1 From 2bdc5c33b1e9eb394eb62533f4ae4df081ea1452 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 15 Jul 2021 14:19:17 -0600 Subject: Make the _array_api submodule install correctly --- numpy/setup.py | 1 + 1 file changed, 1 insertion(+) (limited to 'numpy') diff --git a/numpy/setup.py b/numpy/setup.py index cbf633504..82c4c8d1b 100644 --- a/numpy/setup.py +++ b/numpy/setup.py @@ -4,6 +4,7 @@ def configuration(parent_package='',top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration('numpy', parent_package, top_path) + config.add_subpackage('_array_api') config.add_subpackage('compat') config.add_subpackage('core') config.add_subpackage('distutils') -- cgit v1.2.1 From 49bd66076439a1de85c9924d800546973c857f95 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 15 Jul 2021 16:27:59 -0600 Subject: Use ndim == 0 instead of shape == () in the array API code --- numpy/_array_api/_array_object.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 6b9647626..7b5531b9b 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -55,9 +55,9 @@ class Array: """ obj = super().__new__(cls) - # Note: The spec does not have array scalars, only shape () arrays. + # Note: The spec does not have array scalars, only 0-D arrays. if isinstance(x, np.generic): - # Convert the array scalar to a shape () array + # Convert the array scalar to a 0-D array xa = np.empty((), x.dtype) xa[()] = x x = xa @@ -130,13 +130,13 @@ class Array: broadcasting, so the resulting shape is the same, but this prevents NumPy from not promoting the dtype. """ - if x1.shape == () and x2.shape != (): + if x1.ndim == 0 and x2.ndim != 0: # The _array[None] workaround was chosen because it is relatively # performant. broadcast_to(x1._array, x2.shape) is much slower. We # could also manually type promote x2, but that is more complicated # and about the same performance as this. x1 = Array._new(x1._array[None]) - elif x2.shape == () and x1.shape != (): + elif x2.ndim == 0 and x1.ndim != 0: x2 = Array._new(x2._array[None]) return (x1, x2) @@ -181,8 +181,8 @@ class Array: Performs the operation __bool__. """ # Note: This is an error here. - if self._array.shape != (): - raise TypeError("bool is only allowed on arrays with shape ()") + if self._array.ndim != 0: + raise TypeError("bool is only allowed on arrays with 0 dimensions") res = self._array.__bool__() return res @@ -216,8 +216,8 @@ class Array: Performs the operation __float__. """ # Note: This is an error here. - if self._array.shape != (): - raise TypeError("float is only allowed on arrays with shape ()") + if self._array.ndim != 0: + raise TypeError("float is only allowed on arrays with 0 dimensions") res = self._array.__float__() return res @@ -278,12 +278,12 @@ class Array: - Boolean array indices are not allowed as part of a larger tuple index. - - Integer array indices are not allowed (with the exception of shape - () arrays, which are treated the same as scalars). + - Integer array indices are not allowed (with the exception of 0-D + arrays, which are treated the same as scalars). Additionally, it should be noted that indices that would return a - scalar in NumPy will return a shape () array. Array scalars are not allowed - in the specification, only shape () arrays. This is done in the + scalar in NumPy will return a 0-D array. Array scalars are not allowed + in the specification, only 0-D arrays. This is done in the ``Array._new`` constructor, not this function. """ @@ -335,8 +335,8 @@ class Array: return key elif isinstance(key, Array): if key.dtype in _integer_dtypes: - if key.shape != (): - raise IndexError("Integer array indices with shape != () are not allowed in the array API namespace") + if key.ndim != 0: + raise IndexError("Non-zero dimensional integer array indices are not allowed in the array API namespace") return key._array elif key is Ellipsis: return key @@ -374,8 +374,8 @@ class Array: Performs the operation __int__. """ # Note: This is an error here. - if self._array.shape != (): - raise TypeError("int is only allowed on arrays with shape ()") + if self._array.ndim != 0: + raise TypeError("int is only allowed on arrays with 0 dimensions") res = self._array.__int__() return res -- cgit v1.2.1 From b58fbd24783b29d377427e837d189fa039422c9a Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 15 Jul 2021 16:36:26 -0600 Subject: Correct disallow things like a[(0, 1), (0, 1)] in the array API namespace --- numpy/_array_api/_array_object.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 7b5531b9b..6e451ab7a 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -320,6 +320,8 @@ class Array: if len(key) == 1: return key raise IndexError("Boolean array indices combined with other indices are not allowed in the array API namespace") + if isinstance(idx, tuple): + raise IndexError("Nested tuple indices are not allowed in the array API namespace") if shape is None: return key -- cgit v1.2.1 From c5580134c71b68c0bd24104a6393e3cea9cb25ce Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 15 Jul 2021 16:40:07 -0600 Subject: Add a comment about the _normalize_two_args trick --- numpy/_array_api/_array_object.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 6e451ab7a..e523acd2d 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -130,6 +130,12 @@ class Array: broadcasting, so the resulting shape is the same, but this prevents NumPy from not promoting the dtype. """ + # Another option would be to use signature=(x1.dtype, x2.dtype, None), + # but that only works for ufuncs, so we would have to call the ufuncs + # directly in the operator methods. One should also note that this + # sort of trick wouldn't work for functions like searchsorted, which + # don't do normal broadcasting, but there aren't any functions like + # that in the array API namespace. if x1.ndim == 0 and x2.ndim != 0: # The _array[None] workaround was chosen because it is relatively # performant. broadcast_to(x1._array, x2.shape) is much slower. We -- cgit v1.2.1 From 6855a8ad71a5041d9a2804910f011ac09a859a48 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 15 Jul 2021 16:40:44 -0600 Subject: Move _validate_index above the methods that are actually part of the spec --- numpy/_array_api/_array_object.py | 204 +++++++++++++++++++------------------- 1 file changed, 102 insertions(+), 102 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index e523acd2d..99f0e5221 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -146,108 +146,6 @@ class Array: x2 = Array._new(x2._array[None]) return (x1, x2) - # Everything below this line is required by the spec. - - def __abs__(self: Array, /) -> Array: - """ - Performs the operation __abs__. - """ - res = self._array.__abs__() - return self.__class__._new(res) - - @np.errstate(all='ignore') - def __add__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __add__. - """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) - self, other = self._normalize_two_args(self, other) - res = self._array.__add__(other._array) - return self.__class__._new(res) - - def __and__(self: Array, other: Union[int, bool, Array], /) -> Array: - """ - Performs the operation __and__. - """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) - self, other = self._normalize_two_args(self, other) - res = self._array.__and__(other._array) - return self.__class__._new(res) - - def __array_namespace__(self: Array, /, *, api_version: Optional[str] = None) -> object: - if api_version is not None: - raise ValueError("Unrecognized array API version") - from numpy import _array_api - return _array_api - - def __bool__(self: Array, /) -> bool: - """ - Performs the operation __bool__. - """ - # Note: This is an error here. - if self._array.ndim != 0: - raise TypeError("bool is only allowed on arrays with 0 dimensions") - res = self._array.__bool__() - return res - - def __dlpack__(self: Array, /, *, stream: Optional[Union[int, Any]] = None) -> PyCapsule: - """ - Performs the operation __dlpack__. - """ - res = self._array.__dlpack__(stream=None) - return self.__class__._new(res) - - def __dlpack_device__(self: Array, /) -> Tuple[IntEnum, int]: - """ - Performs the operation __dlpack_device__. - """ - # Note: device support is required for this - res = self._array.__dlpack_device__() - return self.__class__._new(res) - - def __eq__(self: Array, other: Union[int, float, bool, Array], /) -> Array: - """ - Performs the operation __eq__. - """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) - self, other = self._normalize_two_args(self, other) - res = self._array.__eq__(other._array) - return self.__class__._new(res) - - def __float__(self: Array, /) -> float: - """ - Performs the operation __float__. - """ - # Note: This is an error here. - if self._array.ndim != 0: - raise TypeError("float is only allowed on arrays with 0 dimensions") - res = self._array.__float__() - return res - - @np.errstate(all='ignore') - def __floordiv__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __floordiv__. - """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) - self, other = self._normalize_two_args(self, other) - res = self._array.__floordiv__(other._array) - return self.__class__._new(res) - - def __ge__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __ge__. - """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) - self, other = self._normalize_two_args(self, other) - res = self._array.__ge__(other._array) - return self.__class__._new(res) - # Note: A large fraction of allowed indices are disallowed here (see the # docstring below) @staticmethod @@ -357,6 +255,108 @@ class Array: # Array() form, like a list of booleans. raise IndexError("Only integers, slices (`:`), ellipsis (`...`), and boolean arrays are valid indices in the array API namespace") + # Everything below this line is required by the spec. + + def __abs__(self: Array, /) -> Array: + """ + Performs the operation __abs__. + """ + res = self._array.__abs__() + return self.__class__._new(res) + + @np.errstate(all='ignore') + def __add__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __add__. + """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) + self, other = self._normalize_two_args(self, other) + res = self._array.__add__(other._array) + return self.__class__._new(res) + + def __and__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __and__. + """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) + self, other = self._normalize_two_args(self, other) + res = self._array.__and__(other._array) + return self.__class__._new(res) + + def __array_namespace__(self: Array, /, *, api_version: Optional[str] = None) -> object: + if api_version is not None: + raise ValueError("Unrecognized array API version") + from numpy import _array_api + return _array_api + + def __bool__(self: Array, /) -> bool: + """ + Performs the operation __bool__. + """ + # Note: This is an error here. + if self._array.ndim != 0: + raise TypeError("bool is only allowed on arrays with 0 dimensions") + res = self._array.__bool__() + return res + + def __dlpack__(self: Array, /, *, stream: Optional[Union[int, Any]] = None) -> PyCapsule: + """ + Performs the operation __dlpack__. + """ + res = self._array.__dlpack__(stream=None) + return self.__class__._new(res) + + def __dlpack_device__(self: Array, /) -> Tuple[IntEnum, int]: + """ + Performs the operation __dlpack_device__. + """ + # Note: device support is required for this + res = self._array.__dlpack_device__() + return self.__class__._new(res) + + def __eq__(self: Array, other: Union[int, float, bool, Array], /) -> Array: + """ + Performs the operation __eq__. + """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) + self, other = self._normalize_two_args(self, other) + res = self._array.__eq__(other._array) + return self.__class__._new(res) + + def __float__(self: Array, /) -> float: + """ + Performs the operation __float__. + """ + # Note: This is an error here. + if self._array.ndim != 0: + raise TypeError("float is only allowed on arrays with 0 dimensions") + res = self._array.__float__() + return res + + @np.errstate(all='ignore') + def __floordiv__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __floordiv__. + """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) + self, other = self._normalize_two_args(self, other) + res = self._array.__floordiv__(other._array) + return self.__class__._new(res) + + def __ge__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __ge__. + """ + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) + self, other = self._normalize_two_args(self, other) + res = self._array.__ge__(other._array) + return self.__class__._new(res) + def __getitem__(self: Array, key: Union[int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], Array], /) -> Array: """ Performs the operation __getitem__. -- cgit v1.2.1 From 6f98f9e0b73d4ca9e5a7d85091593ca8f0718e97 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 15 Jul 2021 18:44:47 -0600 Subject: Remove error ignoring in the array API namespace The array API requires that elementwise functions always give a value, like nan. However, it doesn't specify that the functions need not give warnings. It is possible here to still break things by manually changing warnings into errors with np.seterr(), but this is considered unsupported behavior. --- numpy/_array_api/_array_object.py | 21 --------------------- numpy/_array_api/_elementwise_functions.py | 25 ------------------------- 2 files changed, 46 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 99f0e5221..0219c8532 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -264,7 +264,6 @@ class Array: res = self._array.__abs__() return self.__class__._new(res) - @np.errstate(all='ignore') def __add__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __add__. @@ -336,7 +335,6 @@ class Array: res = self._array.__float__() return res - @np.errstate(all='ignore') def __floordiv__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __floordiv__. @@ -443,7 +441,6 @@ class Array: res = self._array.__matmul__(other._array) return self.__class__._new(res) - @np.errstate(all='ignore') def __mod__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __mod__. @@ -454,7 +451,6 @@ class Array: res = self._array.__mod__(other._array) return self.__class__._new(res) - @np.errstate(all='ignore') def __mul__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __mul__. @@ -501,7 +497,6 @@ class Array: # PEP 484 requires int to be a subtype of float, but __pow__ should not # accept int. - @np.errstate(all='ignore') def __pow__(self: Array, other: Union[float, Array], /) -> Array: """ Performs the operation __pow__. @@ -536,7 +531,6 @@ class Array: res = self._array.__setitem__(key, asarray(value)._array) return self.__class__._new(res) - @np.errstate(all='ignore') def __sub__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __sub__. @@ -549,7 +543,6 @@ class Array: # PEP 484 requires int to be a subtype of float, but __truediv__ should # not accept int. - @np.errstate(all='ignore') def __truediv__(self: Array, other: Union[float, Array], /) -> Array: """ Performs the operation __truediv__. @@ -572,7 +565,6 @@ class Array: res = self._array.__xor__(other._array) return self.__class__._new(res) - @np.errstate(all='ignore') def __iadd__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __iadd__. @@ -582,7 +574,6 @@ class Array: self._array.__iadd__(other._array) return self - @np.errstate(all='ignore') def __radd__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __radd__. @@ -612,7 +603,6 @@ class Array: res = self._array.__rand__(other._array) return self.__class__._new(res) - @np.errstate(all='ignore') def __ifloordiv__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __ifloordiv__. @@ -622,7 +612,6 @@ class Array: self._array.__ifloordiv__(other._array) return self - @np.errstate(all='ignore') def __rfloordiv__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __rfloordiv__. @@ -683,7 +672,6 @@ class Array: res = self._array.__rmatmul__(other._array) return self.__class__._new(res) - @np.errstate(all='ignore') def __imod__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __imod__. @@ -693,7 +681,6 @@ class Array: self._array.__imod__(other._array) return self - @np.errstate(all='ignore') def __rmod__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __rmod__. @@ -704,7 +691,6 @@ class Array: res = self._array.__rmod__(other._array) return self.__class__._new(res) - @np.errstate(all='ignore') def __imul__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __imul__. @@ -714,7 +700,6 @@ class Array: self._array.__imul__(other._array) return self - @np.errstate(all='ignore') def __rmul__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __rmul__. @@ -744,7 +729,6 @@ class Array: res = self._array.__ror__(other._array) return self.__class__._new(res) - @np.errstate(all='ignore') def __ipow__(self: Array, other: Union[float, Array], /) -> Array: """ Performs the operation __ipow__. @@ -756,7 +740,6 @@ class Array: self._array.__ipow__(other._array) return self - @np.errstate(all='ignore') def __rpow__(self: Array, other: Union[float, Array], /) -> Array: """ Performs the operation __rpow__. @@ -790,7 +773,6 @@ class Array: res = self._array.__rrshift__(other._array) return self.__class__._new(res) - @np.errstate(all='ignore') def __isub__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __isub__. @@ -800,7 +782,6 @@ class Array: self._array.__isub__(other._array) return self - @np.errstate(all='ignore') def __rsub__(self: Array, other: Union[int, float, Array], /) -> Array: """ Performs the operation __rsub__. @@ -811,7 +792,6 @@ class Array: res = self._array.__rsub__(other._array) return self.__class__._new(res) - @np.errstate(all='ignore') def __itruediv__(self: Array, other: Union[float, Array], /) -> Array: """ Performs the operation __itruediv__. @@ -823,7 +803,6 @@ class Array: self._array.__itruediv__(other._array) return self - @np.errstate(all='ignore') def __rtruediv__(self: Array, other: Union[float, Array], /) -> Array: """ Performs the operation __rtruediv__. diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index 0d955b614..ade2aab0c 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -18,7 +18,6 @@ def abs(x: Array, /) -> Array: return Array._new(np.abs(x._array)) # Note: the function name is different here -@np.errstate(all='ignore') def acos(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.arccos `. @@ -30,7 +29,6 @@ def acos(x: Array, /) -> Array: return Array._new(np.arccos(x._array)) # Note: the function name is different here -@np.errstate(all='ignore') def acosh(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.arccosh `. @@ -41,7 +39,6 @@ def acosh(x: Array, /) -> Array: raise TypeError('Only floating-point dtypes are allowed in acosh') return Array._new(np.arccosh(x._array)) -@np.errstate(all='ignore') def add(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.add `. @@ -54,7 +51,6 @@ def add(x1: Array, x2: Array, /) -> Array: return Array._new(np.add(x1._array, x2._array)) # Note: the function name is different here -@np.errstate(all='ignore') def asin(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.arcsin `. @@ -66,7 +62,6 @@ def asin(x: Array, /) -> Array: return Array._new(np.arcsin(x._array)) # Note: the function name is different here -@np.errstate(all='ignore') def asinh(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.arcsinh `. @@ -101,7 +96,6 @@ def atan2(x1: Array, x2: Array, /) -> Array: return Array._new(np.arctan2(x1._array, x2._array)) # Note: the function name is different here -@np.errstate(all='ignore') def atanh(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.arctanh `. @@ -199,7 +193,6 @@ def ceil(x: Array, /) -> Array: return x return Array._new(np.ceil(x._array)) -@np.errstate(all='ignore') def cos(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.cos `. @@ -210,7 +203,6 @@ def cos(x: Array, /) -> Array: raise TypeError('Only floating-point dtypes are allowed in cos') return Array._new(np.cos(x._array)) -@np.errstate(all='ignore') def cosh(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.cosh `. @@ -221,7 +213,6 @@ def cosh(x: Array, /) -> Array: raise TypeError('Only floating-point dtypes are allowed in cosh') return Array._new(np.cosh(x._array)) -@np.errstate(all='ignore') def divide(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.divide `. @@ -242,7 +233,6 @@ def equal(x1: Array, x2: Array, /) -> Array: x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.equal(x1._array, x2._array)) -@np.errstate(all='ignore') def exp(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.exp `. @@ -253,7 +243,6 @@ def exp(x: Array, /) -> Array: raise TypeError('Only floating-point dtypes are allowed in exp') return Array._new(np.exp(x._array)) -@np.errstate(all='ignore') def expm1(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.expm1 `. @@ -277,7 +266,6 @@ def floor(x: Array, /) -> Array: return x return Array._new(np.floor(x._array)) -@np.errstate(all='ignore') def floor_divide(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.floor_divide `. @@ -363,7 +351,6 @@ def less_equal(x1: Array, x2: Array, /) -> Array: x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.less_equal(x1._array, x2._array)) -@np.errstate(all='ignore') def log(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.log `. @@ -374,7 +361,6 @@ def log(x: Array, /) -> Array: raise TypeError('Only floating-point dtypes are allowed in log') return Array._new(np.log(x._array)) -@np.errstate(all='ignore') def log1p(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.log1p `. @@ -385,7 +371,6 @@ def log1p(x: Array, /) -> Array: raise TypeError('Only floating-point dtypes are allowed in log1p') return Array._new(np.log1p(x._array)) -@np.errstate(all='ignore') def log2(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.log2 `. @@ -396,7 +381,6 @@ def log2(x: Array, /) -> Array: raise TypeError('Only floating-point dtypes are allowed in log2') return Array._new(np.log2(x._array)) -@np.errstate(all='ignore') def log10(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.log10 `. @@ -461,7 +445,6 @@ def logical_xor(x1: Array, x2: Array, /) -> Array: x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.logical_xor(x1._array, x2._array)) -@np.errstate(all='ignore') def multiply(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.multiply `. @@ -503,7 +486,6 @@ def positive(x: Array, /) -> Array: return Array._new(np.positive(x._array)) # Note: the function name is different here -@np.errstate(all='ignore') def pow(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.power `. @@ -515,7 +497,6 @@ def pow(x1: Array, x2: Array, /) -> Array: x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.power(x1._array, x2._array)) -@np.errstate(all='ignore') def remainder(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.remainder `. @@ -547,7 +528,6 @@ def sign(x: Array, /) -> Array: raise TypeError('Only numeric dtypes are allowed in sign') return Array._new(np.sign(x._array)) -@np.errstate(all='ignore') def sin(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.sin `. @@ -558,7 +538,6 @@ def sin(x: Array, /) -> Array: raise TypeError('Only floating-point dtypes are allowed in sin') return Array._new(np.sin(x._array)) -@np.errstate(all='ignore') def sinh(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.sinh `. @@ -569,7 +548,6 @@ def sinh(x: Array, /) -> Array: raise TypeError('Only floating-point dtypes are allowed in sinh') return Array._new(np.sinh(x._array)) -@np.errstate(all='ignore') def square(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.square `. @@ -580,7 +558,6 @@ def square(x: Array, /) -> Array: raise TypeError('Only numeric dtypes are allowed in square') return Array._new(np.square(x._array)) -@np.errstate(all='ignore') def sqrt(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.sqrt `. @@ -591,7 +568,6 @@ def sqrt(x: Array, /) -> Array: raise TypeError('Only floating-point dtypes are allowed in sqrt') return Array._new(np.sqrt(x._array)) -@np.errstate(all='ignore') def subtract(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.subtract `. @@ -603,7 +579,6 @@ def subtract(x1: Array, x2: Array, /) -> Array: x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.subtract(x1._array, x2._array)) -@np.errstate(all='ignore') def tan(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.tan `. -- cgit v1.2.1 From 48df7af2089d1f7be8aee646a89d5423b9a9a6b1 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 15 Jul 2021 18:51:05 -0600 Subject: Fix some spelling errors --- numpy/_array_api/_array_object.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 0219c8532..547143a4b 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -837,7 +837,7 @@ class Array: @property def dtype(self) -> Dtype: """ - Array API compatible wrapper for :py:meth:`np.ndaray.dtype `. + Array API compatible wrapper for :py:meth:`np.ndarray.dtype `. See its docstring for more information. """ @@ -856,7 +856,7 @@ class Array: @property def ndim(self) -> int: """ - Array API compatible wrapper for :py:meth:`np.ndaray.ndim `. + Array API compatible wrapper for :py:meth:`np.ndarray.ndim `. See its docstring for more information. """ @@ -865,7 +865,7 @@ class Array: @property def shape(self) -> Tuple[int, ...]: """ - Array API compatible wrapper for :py:meth:`np.ndaray.shape `. + Array API compatible wrapper for :py:meth:`np.ndarray.shape `. See its docstring for more information. """ @@ -874,7 +874,7 @@ class Array: @property def size(self) -> int: """ - Array API compatible wrapper for :py:meth:`np.ndaray.size `. + Array API compatible wrapper for :py:meth:`np.ndarray.size `. See its docstring for more information. """ @@ -883,7 +883,7 @@ class Array: @property def T(self) -> Array: """ - Array API compatible wrapper for :py:meth:`np.ndaray.T `. + Array API compatible wrapper for :py:meth:`np.ndarray.T `. See its docstring for more information. """ -- cgit v1.2.1 From 3cab20ee117d39c74abd2a28f142529e379844b1 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 15 Jul 2021 18:51:13 -0600 Subject: Make numpy._array_api.Array.device return "cpu" --- numpy/_array_api/_array_object.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 547143a4b..0659b7b05 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -845,13 +845,7 @@ class Array: @property def device(self) -> Device: - """ - Array API compatible wrapper for :py:meth:`np.ndaray.device `. - - See its docstring for more information. - """ - # Note: device support is required for this - raise NotImplementedError("The device attribute is not yet implemented") + return 'cpu' @property def ndim(self) -> int: -- cgit v1.2.1 From 185d06d45cbc21ad06d7719ff265b1132d1c41ff Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 16 Jul 2021 15:01:09 -0600 Subject: Guard against non-array API inputs in the array API result_type() --- numpy/_array_api/_data_type_functions.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_data_type_functions.py b/numpy/_array_api/_data_type_functions.py index fe5c7557f..7e0c35a65 100644 --- a/numpy/_array_api/_data_type_functions.py +++ b/numpy/_array_api/_data_type_functions.py @@ -1,6 +1,7 @@ from __future__ import annotations from ._array_object import Array +from ._dtypes import _all_dtypes from dataclasses import dataclass from typing import TYPE_CHECKING, List, Tuple, Union @@ -93,4 +94,12 @@ def result_type(*arrays_and_dtypes: Sequence[Union[Array, Dtype]]) -> Dtype: See its docstring for more information. """ - return np.result_type(*(a._array if isinstance(a, Array) else a for a in arrays_and_dtypes)) + A = [] + for a in arrays_and_dtypes: + if isinstance(a, Array): + a = a._array + elif isinstance(a, np.ndarray) or a not in _all_dtypes: + raise TypeError("result_type() inputs must be array_api arrays or dtypes") + A.append(a) + + return np.result_type(*A) -- cgit v1.2.1 From d9b958259bda8db50949922770101217b2d0b50a Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 16 Jul 2021 15:02:10 -0600 Subject: Move the dtype check to the array API Array._new constructor --- numpy/_array_api/_array_object.py | 4 +++- numpy/_array_api/_creation_functions.py | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 0659b7b05..267bd698f 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -18,7 +18,7 @@ from __future__ import annotations import operator from enum import IntEnum from ._creation_functions import asarray -from ._dtypes import _boolean_dtypes, _integer_dtypes, _floating_dtypes +from ._dtypes import _all_dtypes, _boolean_dtypes, _integer_dtypes, _floating_dtypes from typing import TYPE_CHECKING, Any, Optional, Tuple, Union if TYPE_CHECKING: @@ -61,6 +61,8 @@ class Array: xa = np.empty((), x.dtype) xa[()] = x x = xa + if x.dtype not in _all_dtypes: + raise TypeError(f"The array_api namespace does not support the dtype '{x.dtype}'") obj._array = x return obj diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 2be0aea09..24d28a3fa 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -32,8 +32,6 @@ def asarray(obj: Union[Array, float, NestedSequence[bool|int|float], SupportsDLP # to an object array. raise OverflowError("Integer out of bounds for array dtypes") res = np.asarray(obj, dtype=dtype) - if res.dtype not in _all_dtypes: - raise TypeError(f"The array_api namespace does not support the dtype '{res.dtype}'") return Array._new(res) def arange(start: Union[int, float], /, stop: Optional[Union[int, float]] = None, step: Union[int, float] = 1, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: -- cgit v1.2.1 From 56345ffb82af39149f7cdf8720089e0ba42e798c Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 16 Jul 2021 15:02:44 -0600 Subject: Use asarray to convert a scalar into an array in the array API Array constructor --- numpy/_array_api/_array_object.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 267bd698f..455e4fb63 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -58,9 +58,7 @@ class Array: # Note: The spec does not have array scalars, only 0-D arrays. if isinstance(x, np.generic): # Convert the array scalar to a 0-D array - xa = np.empty((), x.dtype) - xa[()] = x - x = xa + x = np.asarray(x) if x.dtype not in _all_dtypes: raise TypeError(f"The array_api namespace does not support the dtype '{x.dtype}'") obj._array = x -- cgit v1.2.1 From 7c5380d61e31db60f44bdc86047c71f2b1f63458 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 16 Jul 2021 15:03:05 -0600 Subject: Remove an unnecessary indexing --- numpy/_array_api/_creation_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 24d28a3fa..e8c88d9b5 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -97,7 +97,7 @@ def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], *, d # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") if isinstance(fill_value, Array) and fill_value.ndim == 0: - fill_value = fill_value._array[...] + fill_value = fill_value._array res = np.full(shape, fill_value, dtype=dtype) if res.dtype not in _all_dtypes: # This will happen if the fill value is not something that NumPy -- cgit v1.2.1 From 687e2a3b1ee167ae8dc359cf7e92b67e551dbbfe Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 16 Jul 2021 15:26:42 -0600 Subject: Add type hints to the array API __setitem__ --- numpy/_array_api/_array_object.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 455e4fb63..06cb1e925 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -521,7 +521,7 @@ class Array: res = self._array.__rshift__(other._array) return self.__class__._new(res) - def __setitem__(self, key, value, /): + def __setitem__(self, key: Union[int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], Array], value: Union[int, float, bool, Array], /) -> Array: """ Performs the operation __setitem__. """ -- cgit v1.2.1 From a566cd1c7110d36d0e7a1f2746ea61e45f49eb89 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 16 Jul 2021 16:35:13 -0600 Subject: Change the type hint for stream __dlpack__ to just None --- numpy/_array_api/_array_object.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 06cb1e925..4e3c7b344 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -300,11 +300,11 @@ class Array: res = self._array.__bool__() return res - def __dlpack__(self: Array, /, *, stream: Optional[Union[int, Any]] = None) -> PyCapsule: + def __dlpack__(self: Array, /, *, stream: None = None) -> PyCapsule: """ Performs the operation __dlpack__. """ - res = self._array.__dlpack__(stream=None) + res = self._array.__dlpack__(stream=stream) return self.__class__._new(res) def __dlpack_device__(self: Array, /) -> Tuple[IntEnum, int]: -- cgit v1.2.1 From f20be6ad3239a2e7a611ad42c9b36df7863e9883 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 19 Jul 2021 17:25:02 -0600 Subject: Start adding tests for the array API submodule The tests for the module will mostly focus on those things that aren't already tested by the official array API test suite (https://github.com/data-apis/array-api-tests). Currently, indexing tests are added, which test that the Array object correctly rejects otherwise valid indices that are not required by the array API spec. --- numpy/_array_api/_array_object.py | 4 +- numpy/_array_api/tests/__init__.py | 7 ++++ numpy/_array_api/tests/test_array_object.py | 59 +++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 1 deletion(-) create mode 100644 numpy/_array_api/tests/__init__.py create mode 100644 numpy/_array_api/tests/test_array_object.py (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 4e3c7b344..54280ef37 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -158,7 +158,9 @@ class Array: allowed by NumPy but not required by the array API specification. We always raise ``IndexError`` on such indices (the spec does not require any specific behavior on them, but this makes the NumPy array API - namespace a minimal implementation of the spec). + namespace a minimal implementation of the spec). See + https://data-apis.org/array-api/latest/API_specification/indexing.html + for the full list of required indexing behavior This function either raises IndexError if the index ``key`` is invalid, or a new key to be used in place of ``key`` in indexing. It diff --git a/numpy/_array_api/tests/__init__.py b/numpy/_array_api/tests/__init__.py new file mode 100644 index 000000000..536062e38 --- /dev/null +++ b/numpy/_array_api/tests/__init__.py @@ -0,0 +1,7 @@ +""" +Tests for the array API namespace. + +Note, full compliance with the array API can be tested with the official array API test +suite https://github.com/data-apis/array-api-tests. This test suite primarily +focuses on those things that are not tested by the official test suite. +""" diff --git a/numpy/_array_api/tests/test_array_object.py b/numpy/_array_api/tests/test_array_object.py new file mode 100644 index 000000000..49ec3b37b --- /dev/null +++ b/numpy/_array_api/tests/test_array_object.py @@ -0,0 +1,59 @@ +from numpy.testing import assert_raises +import numpy as np + +from .. import ones, asarray + +def test_validate_index(): + # The indexing tests in the official array API test suite test that the + # array object correctly handles the subset of indices that are required + # by the spec. But the NumPy array API implementation specifically + # disallows any index not required by the spec, via Array._validate_index. + # This test focuses on testing that non-valid indices are correctly + # rejected. See + # https://data-apis.org/array-api/latest/API_specification/indexing.html + # and the docstring of Array._validate_index for the exact indexing + # behavior that should be allowed. This does not test indices that are + # already invalid in NumPy itself because Array will generally just pass + # such indices directly to the underlying np.ndarray. + + a = ones((3, 4)) + + # Out of bounds slices are not allowed + assert_raises(IndexError, lambda: a[:4]) + assert_raises(IndexError, lambda: a[:-4]) + assert_raises(IndexError, lambda: a[:3:-1]) + assert_raises(IndexError, lambda: a[:-5:-1]) + assert_raises(IndexError, lambda: a[3:]) + assert_raises(IndexError, lambda: a[-4:]) + assert_raises(IndexError, lambda: a[3::-1]) + assert_raises(IndexError, lambda: a[-4::-1]) + + assert_raises(IndexError, lambda: a[...,:5]) + assert_raises(IndexError, lambda: a[...,:-5]) + assert_raises(IndexError, lambda: a[...,:4:-1]) + assert_raises(IndexError, lambda: a[...,:-6:-1]) + assert_raises(IndexError, lambda: a[...,4:]) + assert_raises(IndexError, lambda: a[...,-5:]) + assert_raises(IndexError, lambda: a[...,4::-1]) + assert_raises(IndexError, lambda: a[...,-5::-1]) + + # Boolean indices cannot be part of a larger tuple index + assert_raises(IndexError, lambda: a[a[:,0]==1,0]) + assert_raises(IndexError, lambda: a[a[:,0]==1,...]) + assert_raises(IndexError, lambda: a[..., a[0]==1]) + assert_raises(IndexError, lambda: a[[True, True, True]]) + assert_raises(IndexError, lambda: a[(True, True, True),]) + + # Integer array indices are not allowed (except for 0-D) + idx = asarray([[0, 1]]) + assert_raises(IndexError, lambda: a[idx]) + assert_raises(IndexError, lambda: a[idx,]) + assert_raises(IndexError, lambda: a[[0, 1]]) + assert_raises(IndexError, lambda: a[(0, 1), (0, 1)]) + assert_raises(IndexError, lambda: a[[0, 1]]) + assert_raises(IndexError, lambda: a[np.array([[0, 1]])]) + + # np.newaxis is not allowed + assert_raises(IndexError, lambda: a[None]) + assert_raises(IndexError, lambda: a[None, ...]) + assert_raises(IndexError, lambda: a[..., None]) -- cgit v1.2.1 From 640d68ac8593be4006ea1e63f0354d7b07767541 Mon Sep 17 00:00:00 2001 From: ZHANG NA Date: Tue, 20 Jul 2021 14:44:30 +0800 Subject: BLD: Add LoongArch support --- numpy/core/include/numpy/npy_cpu.h | 3 +++ numpy/core/include/numpy/npy_endian.h | 1 + 2 files changed, 4 insertions(+) (limited to 'numpy') diff --git a/numpy/core/include/numpy/npy_cpu.h b/numpy/core/include/numpy/npy_cpu.h index 065176ac5..c39aca400 100644 --- a/numpy/core/include/numpy/npy_cpu.h +++ b/numpy/core/include/numpy/npy_cpu.h @@ -18,6 +18,7 @@ * NPY_CPU_ARCEL * NPY_CPU_ARCEB * NPY_CPU_RISCV64 + * NPY_CPU_LOONGARCH * NPY_CPU_WASM */ #ifndef _NPY_CPUARCH_H_ @@ -102,6 +103,8 @@ #define NPY_CPU_ARCEB #elif defined(__riscv) && defined(__riscv_xlen) && __riscv_xlen == 64 #define NPY_CPU_RISCV64 +#elif defined(__loongarch__) + #define NPY_CPU_LOONGARCH #elif defined(__EMSCRIPTEN__) /* __EMSCRIPTEN__ is defined by emscripten: an LLVM-to-Web compiler */ #define NPY_CPU_WASM diff --git a/numpy/core/include/numpy/npy_endian.h b/numpy/core/include/numpy/npy_endian.h index aa367a002..620595bec 100644 --- a/numpy/core/include/numpy/npy_endian.h +++ b/numpy/core/include/numpy/npy_endian.h @@ -49,6 +49,7 @@ || defined(NPY_CPU_PPC64LE) \ || defined(NPY_CPU_ARCEL) \ || defined(NPY_CPU_RISCV64) \ + || defined(NPY_CPU_LOONGARCH) \ || defined(NPY_CPU_WASM) #define NPY_BYTE_ORDER NPY_LITTLE_ENDIAN #elif defined(NPY_CPU_PPC) \ -- cgit v1.2.1 From e34c097981ad573c9871f5f52d1e6e5769529dda Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Tue, 20 Jul 2021 19:32:36 -0600 Subject: Always include the dtype in the array API Array repr --- numpy/_array_api/_array_object.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 54280ef37..f8bad0b59 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -81,7 +81,7 @@ class Array: """ Performs the operation __repr__. """ - return self._array.__repr__().replace('array', 'Array') + return f"Array({np.array2string(self._array, separator=', ')}, dtype={self.dtype.name})" # Helper function to match the type promotion rules in the spec def _promote_scalar(self, scalar): -- cgit v1.2.1 From f74b35996a15637cec567e0dffdacf7f3c24c7f5 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 21 Jul 2021 15:44:10 -0600 Subject: Add bool and int explicitly to the array API asarray() input type hints --- numpy/_array_api/_creation_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index e8c88d9b5..3c591ffe1 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -10,7 +10,7 @@ from ._dtypes import _all_dtypes import numpy as np -def asarray(obj: Union[Array, float, NestedSequence[bool|int|float], SupportsDLPack, SupportsBufferProtocol], /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, copy: Optional[bool] = None) -> Array: +def asarray(obj: Union[Array, bool, int, float, NestedSequence[bool|int|float], SupportsDLPack, SupportsBufferProtocol], /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, copy: Optional[bool] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.asarray `. -- cgit v1.2.1 From 4fd028ddf76d37e869bbde024cc1dc1456c7fb6f Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 21 Jul 2021 15:45:36 -0600 Subject: Implement the array API result_type() manually np.result_type() has too many behaviors that we want to avoid in the array API namespace, like value-based casting and unwanted type promotions. Instead, we implement the exact type promotion table from the spec. --- numpy/_array_api/_data_type_functions.py | 18 +++++++-- numpy/_array_api/_dtypes.py | 69 ++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 3 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_data_type_functions.py b/numpy/_array_api/_data_type_functions.py index 7e0c35a65..17a00cc6d 100644 --- a/numpy/_array_api/_data_type_functions.py +++ b/numpy/_array_api/_data_type_functions.py @@ -1,7 +1,7 @@ from __future__ import annotations from ._array_object import Array -from ._dtypes import _all_dtypes +from ._dtypes import _all_dtypes, _result_type from dataclasses import dataclass from typing import TYPE_CHECKING, List, Tuple, Union @@ -94,12 +94,24 @@ def result_type(*arrays_and_dtypes: Sequence[Union[Array, Dtype]]) -> Dtype: See its docstring for more information. """ + # Note: we use a custom implementation that gives only the type promotions + # required by the spec rather than using np.result_type. NumPy implements + # too many extra type promotions like int64 + uint64 -> float64, and does + # value-based casting on scalar arrays. A = [] for a in arrays_and_dtypes: if isinstance(a, Array): - a = a._array + a = a.dtype elif isinstance(a, np.ndarray) or a not in _all_dtypes: raise TypeError("result_type() inputs must be array_api arrays or dtypes") A.append(a) - return np.result_type(*A) + if len(A) == 0: + raise ValueError("at least one array or dtype is required") + elif len(A) == 1: + return A[0] + else: + t = A[0] + for t2 in A[1:]: + t = _result_type(t, t2) + return t diff --git a/numpy/_array_api/_dtypes.py b/numpy/_array_api/_dtypes.py index b183c800f..9abe4cc83 100644 --- a/numpy/_array_api/_dtypes.py +++ b/numpy/_array_api/_dtypes.py @@ -22,3 +22,72 @@ _floating_dtypes = (float32, float64) _integer_dtypes = (int8, int16, int32, int64, uint8, uint16, uint32, uint64) _integer_or_boolean_dtypes = (bool, int8, int16, int32, int64, uint8, uint16, uint32, uint64) _numeric_dtypes = (float32, float64, int8, int16, int32, int64, uint8, uint16, uint32, uint64) + +_promotion_table = { + (int8, int8): int8, + (int8, int16): int16, + (int8, int32): int32, + (int8, int64): int64, + (int16, int8): int16, + (int16, int16): int16, + (int16, int32): int32, + (int16, int64): int64, + (int32, int8): int32, + (int32, int16): int32, + (int32, int32): int32, + (int32, int64): int64, + (int64, int8): int64, + (int64, int16): int64, + (int64, int32): int64, + (int64, int64): int64, + (uint8, uint8): uint8, + (uint8, uint16): uint16, + (uint8, uint32): uint32, + (uint8, uint64): uint64, + (uint16, uint8): uint16, + (uint16, uint16): uint16, + (uint16, uint32): uint32, + (uint16, uint64): uint64, + (uint32, uint8): uint32, + (uint32, uint16): uint32, + (uint32, uint32): uint32, + (uint32, uint64): uint64, + (uint64, uint8): uint64, + (uint64, uint16): uint64, + (uint64, uint32): uint64, + (uint64, uint64): uint64, + (int8, uint8): int16, + (int8, uint16): int32, + (int8, uint32): int64, + (int16, uint8): int16, + (int16, uint16): int32, + (int16, uint32): int64, + (int32, uint8): int32, + (int32, uint16): int32, + (int32, uint32): int64, + (int64, uint8): int64, + (int64, uint16): int64, + (int64, uint32): int64, + (uint8, int8): int16, + (uint16, int8): int32, + (uint32, int8): int64, + (uint8, int16): int16, + (uint16, int16): int32, + (uint32, int16): int64, + (uint8, int32): int32, + (uint16, int32): int32, + (uint32, int32): int64, + (uint8, int64): int64, + (uint16, int64): int64, + (uint32, int64): int64, + (float32, float32): float32, + (float32, float64): float64, + (float64, float32): float64, + (float64, float64): float64, + (bool, bool): bool, +} + +def _result_type(type1, type2): + if (type1, type2) in _promotion_table: + return _promotion_table[type1, type2] + raise TypeError(f"{type1} and {type2} cannot be type promoted together") -- cgit v1.2.1 From 9d5d0ec2264c86a19714cf185a5a183df14cbb94 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 21 Jul 2021 15:47:33 -0600 Subject: Fix a typo in an error message --- numpy/_array_api/_elementwise_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index ade2aab0c..c07c32de7 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -113,7 +113,7 @@ def bitwise_and(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: - raise TypeError('Only integer_or_boolean dtypes are allowed in bitwise_and') + raise TypeError('Only integer or boolean dtypes are allowed in bitwise_and') x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.bitwise_and(x1._array, x2._array)) -- cgit v1.2.1 From 63a9a87360ef492c46c37416b8270563e73a6349 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 21 Jul 2021 15:48:06 -0600 Subject: Restrict the array API namespace array operator type promotions Only those type promotions that are required by the spec are allowed. In particular, promotions across kinds, like integer + floating-point, are not allowed, except for the case of Python scalars. Tests are added for this. This commit additionally makes the operators return NotImplemented on unexpected input types rather than directly giving a TypeError. This is not strictly required by the array API spec, but it is generally considered a best practice for operator methods in Python. This same thing will be implemented for the various functions in the array API namespace in a later commit. --- numpy/_array_api/_array_object.py | 305 ++++++++++++++++++---------- numpy/_array_api/tests/test_array_object.py | 178 +++++++++++++++- 2 files changed, 376 insertions(+), 107 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index f8bad0b59..f6371fbf4 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -18,7 +18,8 @@ from __future__ import annotations import operator from enum import IntEnum from ._creation_functions import asarray -from ._dtypes import _all_dtypes, _boolean_dtypes, _integer_dtypes, _floating_dtypes +from ._dtypes import (_all_dtypes, _boolean_dtypes, _integer_dtypes, + _integer_or_boolean_dtypes, _floating_dtypes, _numeric_dtypes) from typing import TYPE_CHECKING, Any, Optional, Tuple, Union if TYPE_CHECKING: @@ -83,6 +84,52 @@ class Array: """ return f"Array({np.array2string(self._array, separator=', ')}, dtype={self.dtype.name})" + # These are various helper functions to make the array behavior match the + # spec in places where it either deviates from or is more strict than + # NumPy behavior + + def _check_allowed_dtypes(self, other, dtype_category, op): + """ + Helper function for operators to only allow specific input dtypes + + Use like + + other = self._check_allowed_dtypes(other, 'numeric', '__add__') + if other is NotImplemented: + return other + """ + from ._dtypes import _result_type + + _dtypes = { + 'all': _all_dtypes, + 'numeric': _numeric_dtypes, + 'integer': _integer_dtypes, + 'integer or boolean': _integer_or_boolean_dtypes, + 'boolean': _boolean_dtypes, + 'floating-point': _floating_dtypes, + } + + if self.dtype not in _dtypes[dtype_category]: + raise TypeError(f'Only {dtype_category} dtypes are allowed in {op}') + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) + elif isinstance(other, Array): + if other.dtype not in _dtypes[dtype_category]: + raise TypeError(f'Only {dtype_category} dtypes are allowed in {op}') + else: + return NotImplemented + + res_dtype = _result_type(self.dtype, other.dtype) + if op.startswith('__i'): + # Note: NumPy will allow in-place operators in some cases where the type promoted operator does not match the left-hand side operand. For example, + + # >>> a = np.array(1, dtype=np.int8) + # >>> a += np.array(1, dtype=np.int16) + if res_dtype != self.dtype: + raise TypeError(f"Cannot perform {op} with dtypes {self.dtype} and {other.dtype}") + + return other + # Helper function to match the type promotion rules in the spec def _promote_scalar(self, scalar): """ @@ -270,8 +317,9 @@ class Array: """ Performs the operation __add__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__add__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__add__(other._array) return self.__class__._new(res) @@ -280,8 +328,9 @@ class Array: """ Performs the operation __and__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'integer or boolean', '__and__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__and__(other._array) return self.__class__._new(res) @@ -321,6 +370,11 @@ class Array: """ Performs the operation __eq__. """ + # Even though "all" dtypes are allowed, we still require them to be + # promotable with each other. + other = self._check_allowed_dtypes(other, 'all', '__eq__') + if other is NotImplemented: + return other if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) self, other = self._normalize_two_args(self, other) @@ -341,8 +395,9 @@ class Array: """ Performs the operation __floordiv__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__floordiv__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__floordiv__(other._array) return self.__class__._new(res) @@ -351,8 +406,9 @@ class Array: """ Performs the operation __ge__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__ge__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__ge__(other._array) return self.__class__._new(res) @@ -371,8 +427,9 @@ class Array: """ Performs the operation __gt__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__gt__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__gt__(other._array) return self.__class__._new(res) @@ -391,6 +448,8 @@ class Array: """ Performs the operation __invert__. """ + if self.dtype not in _integer_or_boolean_dtypes: + raise TypeError('Only integer or boolean dtypes are allowed in __invert__') res = self._array.__invert__() return self.__class__._new(res) @@ -398,8 +457,9 @@ class Array: """ Performs the operation __le__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__le__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__le__(other._array) return self.__class__._new(res) @@ -416,8 +476,9 @@ class Array: """ Performs the operation __lshift__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'integer', '__lshift__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__lshift__(other._array) return self.__class__._new(res) @@ -426,8 +487,9 @@ class Array: """ Performs the operation __lt__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__lt__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__lt__(other._array) return self.__class__._new(res) @@ -436,10 +498,11 @@ class Array: """ Performs the operation __matmul__. """ - if isinstance(other, (int, float, bool)): - # matmul is not defined for scalars, but without this, we may get - # the wrong error message from asarray. - other = self._promote_scalar(other) + # matmul is not defined for scalars, but without this, we may get + # the wrong error message from asarray. + other = self._check_allowed_dtypes(other, 'numeric', '__matmul__') + if other is NotImplemented: + return other res = self._array.__matmul__(other._array) return self.__class__._new(res) @@ -447,8 +510,9 @@ class Array: """ Performs the operation __mod__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__mod__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__mod__(other._array) return self.__class__._new(res) @@ -457,8 +521,9 @@ class Array: """ Performs the operation __mul__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__mul__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__mul__(other._array) return self.__class__._new(res) @@ -467,6 +532,9 @@ class Array: """ Performs the operation __ne__. """ + other = self._check_allowed_dtypes(other, 'all', '__ne__') + if other is NotImplemented: + return other if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) self, other = self._normalize_two_args(self, other) @@ -477,6 +545,8 @@ class Array: """ Performs the operation __neg__. """ + if self.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in __neg__') res = self._array.__neg__() return self.__class__._new(res) @@ -484,8 +554,9 @@ class Array: """ Performs the operation __or__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'integer or boolean', '__or__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__or__(other._array) return self.__class__._new(res) @@ -494,6 +565,8 @@ class Array: """ Performs the operation __pos__. """ + if self.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in __pos__') res = self._array.__pos__() return self.__class__._new(res) @@ -505,10 +578,9 @@ class Array: """ from ._elementwise_functions import pow - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) - if self.dtype not in _floating_dtypes or other.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in __pow__') + other = self._check_allowed_dtypes(other, 'floating-point', '__pow__') + if other is NotImplemented: + return other # Note: NumPy's __pow__ does not follow type promotion rules for 0-d # arrays, so we use pow() here instead. return pow(self, other) @@ -517,8 +589,9 @@ class Array: """ Performs the operation __rshift__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'integer', '__rshift__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__rshift__(other._array) return self.__class__._new(res) @@ -537,8 +610,9 @@ class Array: """ Performs the operation __sub__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__sub__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__sub__(other._array) return self.__class__._new(res) @@ -549,10 +623,9 @@ class Array: """ Performs the operation __truediv__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) - if self.dtype not in _floating_dtypes or other.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in __truediv__') + other = self._check_allowed_dtypes(other, 'floating-point', '__truediv__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__truediv__(other._array) return self.__class__._new(res) @@ -561,8 +634,9 @@ class Array: """ Performs the operation __xor__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'integer or boolean', '__xor__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__xor__(other._array) return self.__class__._new(res) @@ -571,8 +645,9 @@ class Array: """ Performs the operation __iadd__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__iadd__') + if other is NotImplemented: + return other self._array.__iadd__(other._array) return self @@ -580,8 +655,9 @@ class Array: """ Performs the operation __radd__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__radd__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__radd__(other._array) return self.__class__._new(res) @@ -590,8 +666,9 @@ class Array: """ Performs the operation __iand__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'integer or boolean', '__iand__') + if other is NotImplemented: + return other self._array.__iand__(other._array) return self @@ -599,8 +676,9 @@ class Array: """ Performs the operation __rand__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'integer or boolean', '__rand__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__rand__(other._array) return self.__class__._new(res) @@ -609,8 +687,9 @@ class Array: """ Performs the operation __ifloordiv__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__ifloordiv__') + if other is NotImplemented: + return other self._array.__ifloordiv__(other._array) return self @@ -618,8 +697,9 @@ class Array: """ Performs the operation __rfloordiv__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__rfloordiv__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__rfloordiv__(other._array) return self.__class__._new(res) @@ -628,8 +708,9 @@ class Array: """ Performs the operation __ilshift__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'integer', '__ilshift__') + if other is NotImplemented: + return other self._array.__ilshift__(other._array) return self @@ -637,8 +718,9 @@ class Array: """ Performs the operation __rlshift__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'integer', '__rlshift__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__rlshift__(other._array) return self.__class__._new(res) @@ -649,15 +731,17 @@ class Array: """ # Note: NumPy does not implement __imatmul__. - if isinstance(other, (int, float, bool)): - # matmul is not defined for scalars, but without this, we may get - # the wrong error message from asarray. - other = self._promote_scalar(other) + # matmul is not defined for scalars, but without this, we may get + # the wrong error message from asarray. + other = self._check_allowed_dtypes(other, 'numeric', '__imatmul__') + if other is NotImplemented: + return other + # __imatmul__ can only be allowed when it would not change the shape # of self. other_shape = other.shape if self.shape == () or other_shape == (): - raise ValueError("@= requires at least one dimension") + raise TypeError("@= requires at least one dimension") if len(other_shape) == 1 or other_shape[-1] != other_shape[-2]: raise ValueError("@= cannot change the shape of the input array") self._array[:] = self._array.__matmul__(other._array) @@ -667,10 +751,11 @@ class Array: """ Performs the operation __rmatmul__. """ - if isinstance(other, (int, float, bool)): - # matmul is not defined for scalars, but without this, we may get - # the wrong error message from asarray. - other = self._promote_scalar(other) + # matmul is not defined for scalars, but without this, we may get + # the wrong error message from asarray. + other = self._check_allowed_dtypes(other, 'numeric', '__rmatmul__') + if other is NotImplemented: + return other res = self._array.__rmatmul__(other._array) return self.__class__._new(res) @@ -678,8 +763,9 @@ class Array: """ Performs the operation __imod__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__imod__') + if other is NotImplemented: + return other self._array.__imod__(other._array) return self @@ -687,8 +773,9 @@ class Array: """ Performs the operation __rmod__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__rmod__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__rmod__(other._array) return self.__class__._new(res) @@ -697,8 +784,9 @@ class Array: """ Performs the operation __imul__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__imul__') + if other is NotImplemented: + return other self._array.__imul__(other._array) return self @@ -706,8 +794,9 @@ class Array: """ Performs the operation __rmul__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__rmul__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__rmul__(other._array) return self.__class__._new(res) @@ -716,8 +805,9 @@ class Array: """ Performs the operation __ior__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'integer or boolean', '__ior__') + if other is NotImplemented: + return other self._array.__ior__(other._array) return self @@ -725,8 +815,9 @@ class Array: """ Performs the operation __ror__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'integer or boolean', '__ror__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__ror__(other._array) return self.__class__._new(res) @@ -735,10 +826,9 @@ class Array: """ Performs the operation __ipow__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) - if self.dtype not in _floating_dtypes or other.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in __pow__') + other = self._check_allowed_dtypes(other, 'floating-point', '__ipow__') + if other is NotImplemented: + return other self._array.__ipow__(other._array) return self @@ -748,10 +838,9 @@ class Array: """ from ._elementwise_functions import pow - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) - if self.dtype not in _floating_dtypes or other.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in __pow__') + other = self._check_allowed_dtypes(other, 'floating-point', '__rpow__') + if other is NotImplemented: + return other # Note: NumPy's __pow__ does not follow the spec type promotion rules # for 0-d arrays, so we use pow() here instead. return pow(other, self) @@ -760,8 +849,9 @@ class Array: """ Performs the operation __irshift__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'integer', '__irshift__') + if other is NotImplemented: + return other self._array.__irshift__(other._array) return self @@ -769,8 +859,9 @@ class Array: """ Performs the operation __rrshift__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'integer', '__rrshift__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__rrshift__(other._array) return self.__class__._new(res) @@ -779,8 +870,9 @@ class Array: """ Performs the operation __isub__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__isub__') + if other is NotImplemented: + return other self._array.__isub__(other._array) return self @@ -788,8 +880,9 @@ class Array: """ Performs the operation __rsub__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'numeric', '__rsub__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__rsub__(other._array) return self.__class__._new(res) @@ -798,10 +891,9 @@ class Array: """ Performs the operation __itruediv__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) - if self.dtype not in _floating_dtypes or other.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in __truediv__') + other = self._check_allowed_dtypes(other, 'floating-point', '__itruediv__') + if other is NotImplemented: + return other self._array.__itruediv__(other._array) return self @@ -809,10 +901,9 @@ class Array: """ Performs the operation __rtruediv__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) - if self.dtype not in _floating_dtypes or other.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in __truediv__') + other = self._check_allowed_dtypes(other, 'floating-point', '__rtruediv__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__rtruediv__(other._array) return self.__class__._new(res) @@ -821,8 +912,9 @@ class Array: """ Performs the operation __ixor__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'integer or boolean', '__ixor__') + if other is NotImplemented: + return other self._array.__ixor__(other._array) return self @@ -830,8 +922,9 @@ class Array: """ Performs the operation __rxor__. """ - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) + other = self._check_allowed_dtypes(other, 'integer or boolean', '__rxor__') + if other is NotImplemented: + return other self, other = self._normalize_two_args(self, other) res = self._array.__rxor__(other._array) return self.__class__._new(res) diff --git a/numpy/_array_api/tests/test_array_object.py b/numpy/_array_api/tests/test_array_object.py index 49ec3b37b..5aba2b23c 100644 --- a/numpy/_array_api/tests/test_array_object.py +++ b/numpy/_array_api/tests/test_array_object.py @@ -1,7 +1,10 @@ from numpy.testing import assert_raises import numpy as np -from .. import ones, asarray +from .. import ones, asarray, result_type +from .._dtypes import (_all_dtypes, _boolean_dtypes, _floating_dtypes, + _integer_dtypes, _integer_or_boolean_dtypes, + _numeric_dtypes, int8, int16, int32, int64, uint64) def test_validate_index(): # The indexing tests in the official array API test suite test that the @@ -57,3 +60,176 @@ def test_validate_index(): assert_raises(IndexError, lambda: a[None]) assert_raises(IndexError, lambda: a[None, ...]) assert_raises(IndexError, lambda: a[..., None]) + +def test_operators(): + # For every operator, we test that it works for the required type + # combinations and raises TypeError otherwise + binary_op_dtypes ={ + '__add__': 'numeric', + '__and__': 'integer_or_boolean', + '__eq__': 'all', + '__floordiv__': 'numeric', + '__ge__': 'numeric', + '__gt__': 'numeric', + '__le__': 'numeric', + '__lshift__': 'integer', + '__lt__': 'numeric', + '__mod__': 'numeric', + '__mul__': 'numeric', + '__ne__': 'all', + '__or__': 'integer_or_boolean', + '__pow__': 'floating', + '__rshift__': 'integer', + '__sub__': 'numeric', + '__truediv__': 'floating', + '__xor__': 'integer_or_boolean', + } + + # Recompute each time because of in-place ops + def _array_vals(): + for d in _integer_dtypes: + yield asarray(1, dtype=d) + for d in _boolean_dtypes: + yield asarray(False, dtype=d) + for d in _floating_dtypes: + yield asarray(1., dtype=d) + + for op, dtypes in binary_op_dtypes.items(): + ops = [op] + if op not in ['__eq__', '__ne__', '__le__', '__ge__', '__lt__', '__gt__']: + rop = '__r' + op[2:] + iop = '__i' + op[2:] + ops += [rop, iop] + for s in [1, 1., False]: + for _op in ops: + for a in _array_vals(): + # Test array op scalar. From the spec, the following combinations + # are supported: + + # - Python bool for a bool array dtype, + # - a Python int within the bounds of the given dtype for integer array dtypes, + # - a Python int or float for floating-point array dtypes + + # We do not do bounds checking for int scalars, but rather use the default + # NumPy behavior for casting in that case. + + if ((dtypes == "all" + or dtypes == "numeric" and a.dtype in _numeric_dtypes + or dtypes == "integer" and a.dtype in _integer_dtypes + or dtypes == "integer_or_boolean" and a.dtype in _integer_or_boolean_dtypes + or dtypes == "boolean" and a.dtype in _boolean_dtypes + or dtypes == "floating" and a.dtype in _floating_dtypes + ) + # bool is a subtype of int, which is why we avoid + # isinstance here. + and (a.dtype in _boolean_dtypes and type(s) == bool + or a.dtype in _integer_dtypes and type(s) == int + or a.dtype in _floating_dtypes and type(s) in [float, int] + )): + # Only test for no error + getattr(a, _op)(s) + else: + assert_raises(TypeError, lambda: getattr(a, _op)(s)) + + # Test array op array. + for _op in ops: + for x in _array_vals(): + for y in _array_vals(): + # See the promotion table in NEP 47 or the array + # API spec page on type promotion. Mixed kind + # promotion is not defined. + if (x.dtype == uint64 and y.dtype in [int8, int16, int32, int64] + or y.dtype == uint64 and x.dtype in [int8, int16, int32, int64] + or x.dtype in _integer_dtypes and y.dtype not in _integer_dtypes + or y.dtype in _integer_dtypes and x.dtype not in _integer_dtypes + or x.dtype in _boolean_dtypes and y.dtype not in _boolean_dtypes + or y.dtype in _boolean_dtypes and x.dtype not in _boolean_dtypes + or x.dtype in _floating_dtypes and y.dtype not in _floating_dtypes + or y.dtype in _floating_dtypes and x.dtype not in _floating_dtypes + ): + assert_raises(TypeError, lambda: getattr(x, _op)(y)) + # Ensure in-place operators only promote to the same dtype as the left operand. + elif _op.startswith('__i') and result_type(x.dtype, y.dtype) != x.dtype: + assert_raises(TypeError, lambda: getattr(x, _op)(y)) + # Ensure only those dtypes that are required for every operator are allowed. + elif (dtypes == "all" and (x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes + or x.dtype in _numeric_dtypes and y.dtype in _numeric_dtypes) + or (dtypes == "numeric" and x.dtype in _numeric_dtypes and y.dtype in _numeric_dtypes) + or dtypes == "integer" and x.dtype in _integer_dtypes and y.dtype in _numeric_dtypes + or dtypes == "integer_or_boolean" and (x.dtype in _integer_dtypes and y.dtype in _integer_dtypes + or x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes) + or dtypes == "boolean" and x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes + or dtypes == "floating" and x.dtype in _floating_dtypes and y.dtype in _floating_dtypes + ): + getattr(x, _op)(y) + else: + assert_raises(TypeError, lambda: getattr(x, _op)(y)) + + unary_op_dtypes ={ + '__invert__': 'integer_or_boolean', + '__neg__': 'numeric', + '__pos__': 'numeric', + } + for op, dtypes in unary_op_dtypes.items(): + for a in _array_vals(): + if (dtypes == "numeric" and a.dtype in _numeric_dtypes + or dtypes == "integer_or_boolean" and a.dtype in _integer_or_boolean_dtypes + ): + # Only test for no error + getattr(a, op)() + else: + assert_raises(TypeError, lambda: getattr(a, op)()) + + # Finally, matmul() must be tested separately, because it works a bit + # different from the other operations. + def _matmul_array_vals(): + for a in _array_vals(): + yield a + for d in _all_dtypes: + yield ones((3, 4), dtype=d) + yield ones((4, 2), dtype=d) + yield ones((4, 4), dtype=d) + + # Scalars always error + for _op in ['__matmul__', '__rmatmul__', '__imatmul__']: + for s in [1, 1., False]: + for a in _matmul_array_vals(): + if (type(s) in [float, int] and a.dtype in _floating_dtypes + or type(s) == int and a.dtype in _integer_dtypes): + # Type promotion is valid, but @ is not allowed on 0-D + # inputs, so the error is a ValueError + assert_raises(ValueError, lambda: getattr(a, _op)(s)) + else: + assert_raises(TypeError, lambda: getattr(a, _op)(s)) + + for x in _matmul_array_vals(): + for y in _matmul_array_vals(): + if (x.dtype == uint64 and y.dtype in [int8, int16, int32, int64] + or y.dtype == uint64 and x.dtype in [int8, int16, int32, int64] + or x.dtype in _integer_dtypes and y.dtype not in _integer_dtypes + or y.dtype in _integer_dtypes and x.dtype not in _integer_dtypes + or x.dtype in _floating_dtypes and y.dtype not in _floating_dtypes + or y.dtype in _floating_dtypes and x.dtype not in _floating_dtypes + or x.dtype in _boolean_dtypes + or y.dtype in _boolean_dtypes + ): + assert_raises(TypeError, lambda: x.__matmul__(y)) + assert_raises(TypeError, lambda: y.__rmatmul__(x)) + assert_raises(TypeError, lambda: x.__imatmul__(y)) + elif x.shape == () or y.shape == () or x.shape[1] != y.shape[0]: + assert_raises(ValueError, lambda: x.__matmul__(y)) + assert_raises(ValueError, lambda: y.__rmatmul__(x)) + if result_type(x.dtype, y.dtype) != x.dtype: + assert_raises(TypeError, lambda: x.__imatmul__(y)) + else: + assert_raises(ValueError, lambda: x.__imatmul__(y)) + else: + x.__matmul__(y) + y.__rmatmul__(x) + if result_type(x.dtype, y.dtype) != x.dtype: + assert_raises(TypeError, lambda: x.__imatmul__(y)) + elif y.shape[0] != y.shape[1]: + # This one fails because x @ y has a different shape from x + assert_raises(ValueError, lambda: x.__imatmul__(y)) + else: + x.__imatmul__(y) -- cgit v1.2.1 From a16d76388d57f34856803dfef19bacd3a9980b60 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 22 Jul 2021 16:38:25 -0600 Subject: Use ValueError instead of TypeError for array API @= This is consistent with @ and with NumPy. --- numpy/_array_api/_array_object.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index f6371fbf4..2d999e2f3 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -741,7 +741,7 @@ class Array: # of self. other_shape = other.shape if self.shape == () or other_shape == (): - raise TypeError("@= requires at least one dimension") + raise ValueError("@= requires at least one dimension") if len(other_shape) == 1 or other_shape[-1] != other_shape[-2]: raise ValueError("@= cannot change the shape of the input array") self._array[:] = self._array.__matmul__(other._array) -- cgit v1.2.1 From 776b1171aa76cc912abafb8434850bc9d37bd482 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 22 Jul 2021 16:39:29 -0600 Subject: Add some more comments about array API type promotion stuff --- numpy/_array_api/_array_object.py | 9 ++++++++- numpy/_array_api/_dtypes.py | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 2d999e2f3..505c27839 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -119,12 +119,19 @@ class Array: else: return NotImplemented + # This will raise TypeError for type combinations that are not allowed + # to promote in the spec (even if the NumPy array operator would + # promote them). res_dtype = _result_type(self.dtype, other.dtype) if op.startswith('__i'): - # Note: NumPy will allow in-place operators in some cases where the type promoted operator does not match the left-hand side operand. For example, + # Note: NumPy will allow in-place operators in some cases where + # the type promoted operator does not match the left-hand side + # operand. For example, # >>> a = np.array(1, dtype=np.int8) # >>> a += np.array(1, dtype=np.int16) + + # The spec explicitly disallows this. if res_dtype != self.dtype: raise TypeError(f"Cannot perform {op} with dtypes {self.dtype} and {other.dtype}") diff --git a/numpy/_array_api/_dtypes.py b/numpy/_array_api/_dtypes.py index 9abe4cc83..fcdb562da 100644 --- a/numpy/_array_api/_dtypes.py +++ b/numpy/_array_api/_dtypes.py @@ -23,6 +23,13 @@ _integer_dtypes = (int8, int16, int32, int64, uint8, uint16, uint32, uint64) _integer_or_boolean_dtypes = (bool, int8, int16, int32, int64, uint8, uint16, uint32, uint64) _numeric_dtypes = (float32, float64, int8, int16, int32, int64, uint8, uint16, uint32, uint64) +# Note: the spec defines a restricted type promotion table compared to NumPy. +# In particular, cross-kind promotions like integer + float or boolean + +# integer are not allowed, even for functions that accept both kinds. +# Additionally, NumPy promotes signed integer + uint64 to float64, but this +# promotion is not allowed here. To be clear, Python scalar int objects are +# allowed to promote to floating-point dtypes, but only in array operators +# (see Array._promote_scalar) method in _array_object.py. _promotion_table = { (int8, int8): int8, (int8, int16): int16, -- cgit v1.2.1 From 626567645b180179159fa1807e72b26d58ce20dd Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 22 Jul 2021 16:39:54 -0600 Subject: Prevent unwanted type promotions everywhere in the array API namespace --- numpy/_array_api/_elementwise_functions.py | 48 ++++++++++++++++++++++++++- numpy/_array_api/_linear_algebra_functions.py | 6 +++- numpy/_array_api/_manipulation_functions.py | 5 +++ numpy/_array_api/_searching_functions.py | 3 ++ 4 files changed, 60 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index c07c32de7..67fb7034d 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -2,7 +2,7 @@ from __future__ import annotations from ._dtypes import (_boolean_dtypes, _floating_dtypes, _integer_dtypes, _integer_or_boolean_dtypes, - _numeric_dtypes) + _numeric_dtypes, _result_type) from ._array_object import Array import numpy as np @@ -47,6 +47,8 @@ def add(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in add') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.add(x1._array, x2._array)) @@ -92,6 +94,8 @@ def atan2(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in atan2') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.arctan2(x1._array, x2._array)) @@ -114,6 +118,8 @@ def bitwise_and(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: raise TypeError('Only integer or boolean dtypes are allowed in bitwise_and') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.bitwise_and(x1._array, x2._array)) @@ -126,6 +132,8 @@ def bitwise_left_shift(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: raise TypeError('Only integer dtypes are allowed in bitwise_left_shift') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) # Note: bitwise_left_shift is only defined for x2 nonnegative. if np.any(x2._array < 0): @@ -151,6 +159,8 @@ def bitwise_or(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: raise TypeError('Only integer or boolean dtypes are allowed in bitwise_or') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.bitwise_or(x1._array, x2._array)) @@ -163,6 +173,8 @@ def bitwise_right_shift(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: raise TypeError('Only integer dtypes are allowed in bitwise_right_shift') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) # Note: bitwise_right_shift is only defined for x2 nonnegative. if np.any(x2._array < 0): @@ -177,6 +189,8 @@ def bitwise_xor(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: raise TypeError('Only integer or boolean dtypes are allowed in bitwise_xor') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.bitwise_xor(x1._array, x2._array)) @@ -221,6 +235,8 @@ def divide(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in divide') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.divide(x1._array, x2._array)) @@ -230,6 +246,8 @@ def equal(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.equal(x1._array, x2._array)) @@ -274,6 +292,8 @@ def floor_divide(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in floor_divide') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.floor_divide(x1._array, x2._array)) @@ -285,6 +305,8 @@ def greater(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in greater') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.greater(x1._array, x2._array)) @@ -296,6 +318,8 @@ def greater_equal(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in greater_equal') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.greater_equal(x1._array, x2._array)) @@ -337,6 +361,8 @@ def less(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in less') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.less(x1._array, x2._array)) @@ -348,6 +374,8 @@ def less_equal(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in less_equal') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.less_equal(x1._array, x2._array)) @@ -399,6 +427,8 @@ def logaddexp(x1: Array, x2: Array) -> Array: """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in logaddexp') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.logaddexp(x1._array, x2._array)) @@ -410,6 +440,8 @@ def logical_and(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: raise TypeError('Only boolean dtypes are allowed in logical_and') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.logical_and(x1._array, x2._array)) @@ -431,6 +463,8 @@ def logical_or(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: raise TypeError('Only boolean dtypes are allowed in logical_or') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.logical_or(x1._array, x2._array)) @@ -442,6 +476,8 @@ def logical_xor(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: raise TypeError('Only boolean dtypes are allowed in logical_xor') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.logical_xor(x1._array, x2._array)) @@ -453,6 +489,8 @@ def multiply(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in multiply') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.multiply(x1._array, x2._array)) @@ -472,6 +510,8 @@ def not_equal(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.not_equal(x1._array, x2._array)) @@ -494,6 +534,8 @@ def pow(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: raise TypeError('Only floating-point dtypes are allowed in pow') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.power(x1._array, x2._array)) @@ -505,6 +547,8 @@ def remainder(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in remainder') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.remainder(x1._array, x2._array)) @@ -576,6 +620,8 @@ def subtract(x1: Array, x2: Array, /) -> Array: """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in subtract') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.subtract(x1._array, x2._array)) diff --git a/numpy/_array_api/_linear_algebra_functions.py b/numpy/_array_api/_linear_algebra_functions.py index b4b2af134..f13f9c541 100644 --- a/numpy/_array_api/_linear_algebra_functions.py +++ b/numpy/_array_api/_linear_algebra_functions.py @@ -1,7 +1,7 @@ from __future__ import annotations from ._array_object import Array -from ._dtypes import _numeric_dtypes +from ._dtypes import _numeric_dtypes, _result_type from typing import Optional, Sequence, Tuple, Union @@ -27,6 +27,8 @@ def matmul(x1: Array, x2: Array, /) -> Array: # np.matmul. if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in matmul') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) return Array._new(np.matmul(x1._array, x2._array)) @@ -36,6 +38,8 @@ def tensordot(x1: Array, x2: Array, /, *, axes: Union[int, Tuple[Sequence[int], # np.tensordot. if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in tensordot') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) return Array._new(np.tensordot(x1._array, x2._array, axes=axes)) diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py index 6308bfc26..fa6344beb 100644 --- a/numpy/_array_api/_manipulation_functions.py +++ b/numpy/_array_api/_manipulation_functions.py @@ -1,6 +1,7 @@ from __future__ import annotations from ._array_object import Array +from ._data_type_functions import result_type from typing import List, Optional, Tuple, Union @@ -14,6 +15,8 @@ def concat(arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: Optional[i See its docstring for more information. """ arrays = tuple(a._array for a in arrays) + # Call result type here just to raise on disallowed type combinations + result_type(*arrays) return Array._new(np.concatenate(arrays, axis=axis)) def expand_dims(x: Array, /, *, axis: int) -> Array: @@ -63,4 +66,6 @@ def stack(arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: int = 0) -> See its docstring for more information. """ arrays = tuple(a._array for a in arrays) + # Call result type here just to raise on disallowed type combinations + result_type(*arrays) return Array._new(np.stack(arrays, axis=axis)) diff --git a/numpy/_array_api/_searching_functions.py b/numpy/_array_api/_searching_functions.py index 4764992a1..d80720850 100644 --- a/numpy/_array_api/_searching_functions.py +++ b/numpy/_array_api/_searching_functions.py @@ -1,6 +1,7 @@ from __future__ import annotations from ._array_object import Array +from ._dtypes import _result_type from typing import Optional, Tuple @@ -38,4 +39,6 @@ def where(condition: Array, x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) return Array._new(np.where(condition._array, x1._array, x2._array)) -- cgit v1.2.1 From 1e835f9f70a3cba6fc7a053edcbd1b1a01ee79b4 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 22 Jul 2021 17:04:12 -0600 Subject: Remove some dead code --- numpy/_array_api/_array_object.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 505c27839..98e2f78f9 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -382,8 +382,6 @@ class Array: other = self._check_allowed_dtypes(other, 'all', '__eq__') if other is NotImplemented: return other - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) self, other = self._normalize_two_args(self, other) res = self._array.__eq__(other._array) return self.__class__._new(res) @@ -542,8 +540,6 @@ class Array: other = self._check_allowed_dtypes(other, 'all', '__ne__') if other is NotImplemented: return other - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) self, other = self._normalize_two_args(self, other) res = self._array.__ne__(other._array) return self.__class__._new(res) -- cgit v1.2.1 From 64bb971096892c08416c5787d705a29bcd5b64b5 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 22 Jul 2021 17:04:24 -0600 Subject: Add tests for Python scalar constructors on array API arrays --- numpy/_array_api/tests/test_array_object.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/tests/test_array_object.py b/numpy/_array_api/tests/test_array_object.py index 5aba2b23c..25802f93c 100644 --- a/numpy/_array_api/tests/test_array_object.py +++ b/numpy/_array_api/tests/test_array_object.py @@ -233,3 +233,17 @@ def test_operators(): assert_raises(ValueError, lambda: x.__imatmul__(y)) else: x.__imatmul__(y) + +def test_python_scalar_construtors(): + a = asarray(False) + b = asarray(0) + c = asarray(0.) + + assert bool(a) == bool(b) == bool(c) == False + assert int(a) == int(b) == int(c) == 0 + assert float(a) == float(b) == float(c) == 0. + + # bool/int/float should only be allowed on 0-D arrays. + assert_raises(TypeError, lambda: bool(asarray([False]))) + assert_raises(TypeError, lambda: int(asarray([0]))) + assert_raises(TypeError, lambda: float(asarray([0.]))) -- cgit v1.2.1 From deaf0bf6fc819c9c7b4dcffe0d4aee43bdc33bae Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 22 Jul 2021 18:19:59 -0600 Subject: Fix the array API __abs__() to restrict to numeric dtypes --- numpy/_array_api/_array_object.py | 2 ++ numpy/_array_api/tests/test_array_object.py | 1 + 2 files changed, 3 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 98e2f78f9..cd16f49ee 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -317,6 +317,8 @@ class Array: """ Performs the operation __abs__. """ + if self.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in __abs__') res = self._array.__abs__() return self.__class__._new(res) diff --git a/numpy/_array_api/tests/test_array_object.py b/numpy/_array_api/tests/test_array_object.py index 25802f93c..22078bbee 100644 --- a/numpy/_array_api/tests/test_array_object.py +++ b/numpy/_array_api/tests/test_array_object.py @@ -166,6 +166,7 @@ def test_operators(): assert_raises(TypeError, lambda: getattr(x, _op)(y)) unary_op_dtypes ={ + '__abs__': 'numeric', '__invert__': 'integer_or_boolean', '__neg__': 'numeric', '__pos__': 'numeric', -- cgit v1.2.1 From e7f6dfecccc9dc84520af1a9f0000b3b0d0f4895 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 22 Jul 2021 18:20:44 -0600 Subject: Fix the array API trunc() to return the same dtype as the input It is similar to floor() and ceil() but I missed it previously. --- numpy/_array_api/_elementwise_functions.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py index 67fb7034d..7833ebe54 100644 --- a/numpy/_array_api/_elementwise_functions.py +++ b/numpy/_array_api/_elementwise_functions.py @@ -653,4 +653,7 @@ def trunc(x: Array, /) -> Array: """ if x.dtype not in _numeric_dtypes: raise TypeError('Only numeric dtypes are allowed in trunc') + if x.dtype in _integer_dtypes: + # Note: The return dtype of trunc is the same as the input + return x return Array._new(np.trunc(x._array)) -- cgit v1.2.1 From e4b7205fbaece2b604b0ac2b11a586a9f7c6b3dd Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 23 Jul 2021 14:17:09 -0600 Subject: Fix the array API Array.__setitem__ --- numpy/_array_api/_array_object.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index cd16f49ee..13b093f4f 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -608,8 +608,7 @@ class Array: # Note: Only indices required by the spec are allowed. See the # docstring of _validate_index key = self._validate_index(key, self.shape) - res = self._array.__setitem__(key, asarray(value)._array) - return self.__class__._new(res) + self._array.__setitem__(key, asarray(value)._array) def __sub__(self: Array, other: Union[int, float, Array], /) -> Array: """ -- cgit v1.2.1 From 65ed981e94b166f2fb87f1239308f4b01897e617 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 23 Jul 2021 15:43:41 -0600 Subject: Add tests for error cases for the array API elementwise functions --- .../_array_api/tests/test_elementwise_functions.py | 110 +++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 numpy/_array_api/tests/test_elementwise_functions.py (limited to 'numpy') diff --git a/numpy/_array_api/tests/test_elementwise_functions.py b/numpy/_array_api/tests/test_elementwise_functions.py new file mode 100644 index 000000000..994cb0bf0 --- /dev/null +++ b/numpy/_array_api/tests/test_elementwise_functions.py @@ -0,0 +1,110 @@ +from inspect import getfullargspec + +from numpy.testing import assert_raises + +from .. import asarray, _elementwise_functions +from .._elementwise_functions import bitwise_left_shift, bitwise_right_shift +from .._dtypes import (_all_dtypes, _boolean_dtypes, _floating_dtypes, + _integer_dtypes, _integer_or_boolean_dtypes, + _numeric_dtypes) + +def nargs(func): + return len(getfullargspec(func).args) + +def test_function_types(): + # Test that every function accepts only the required input types. We only + # test the negative cases here (error). The positive cases are tested in + # the array API test suite. + + elementwise_function_input_types = { + 'abs': 'numeric', + 'acos': 'floating', + 'acosh': 'floating', + 'add': 'numeric', + 'asin': 'floating', + 'asinh': 'floating', + 'atan': 'floating', + 'atan2': 'floating', + 'atanh': 'floating', + 'bitwise_and': 'integer_or_boolean', + 'bitwise_invert': 'integer_or_boolean', + 'bitwise_left_shift': 'integer', + 'bitwise_or': 'integer_or_boolean', + 'bitwise_right_shift': 'integer', + 'bitwise_xor': 'integer_or_boolean', + 'ceil': 'numeric', + 'cos': 'floating', + 'cosh': 'floating', + 'divide': 'floating', + 'equal': 'all', + 'exp': 'floating', + 'expm1': 'floating', + 'floor': 'numeric', + 'floor_divide': 'numeric', + 'greater': 'numeric', + 'greater_equal': 'numeric', + 'isfinite': 'numeric', + 'isinf': 'numeric', + 'isnan': 'numeric', + 'less': 'numeric', + 'less_equal': 'numeric', + 'log': 'floating', + 'logaddexp': 'floating', + 'log10': 'floating', + 'log1p': 'floating', + 'log2': 'floating', + 'logical_and': 'boolean', + 'logical_not': 'boolean', + 'logical_or': 'boolean', + 'logical_xor': 'boolean', + 'multiply': 'numeric', + 'negative': 'numeric', + 'not_equal': 'all', + 'positive': 'numeric', + 'pow': 'floating', + 'remainder': 'numeric', + 'round': 'numeric', + 'sign': 'numeric', + 'sin': 'floating', + 'sinh': 'floating', + 'sqrt': 'floating', + 'square': 'numeric', + 'subtract': 'numeric', + 'tan': 'floating', + 'tanh': 'floating', + 'trunc': 'numeric', + } + + _dtypes = { + 'all': _all_dtypes, + 'numeric': _numeric_dtypes, + 'integer': _integer_dtypes, + 'integer_or_boolean': _integer_or_boolean_dtypes, + 'boolean': _boolean_dtypes, + 'floating': _floating_dtypes, + } + + def _array_vals(): + for d in _integer_dtypes: + yield asarray(1, dtype=d) + for d in _boolean_dtypes: + yield asarray(False, dtype=d) + for d in _floating_dtypes: + yield asarray(1., dtype=d) + + for x in _array_vals(): + for func_name, types in elementwise_function_input_types.items(): + dtypes = _dtypes[types] + func = getattr(_elementwise_functions, func_name) + if nargs(func) == 2: + for y in _array_vals(): + if x.dtype not in dtypes or y.dtype not in dtypes: + assert_raises(TypeError, lambda: func(x, y)) + else: + if x.dtype not in dtypes: + assert_raises(TypeError, lambda: func(x)) + +def test_bitwise_shift_error(): + # bitwise shift functions should raise when the second argument is negative + assert_raises(ValueError, lambda: bitwise_left_shift(asarray([1, 1]), asarray([1, -1]))) + assert_raises(ValueError, lambda: bitwise_right_shift(asarray([1, 1]), asarray([1, -1]))) -- cgit v1.2.1 From 5882962a6b7bc684c86a37b010403ee1908d57bd Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 23 Jul 2021 15:45:45 -0600 Subject: Assume the current array API version is 2021. See https://github.com/numpy/numpy/pull/18585#discussion_r675849149. --- numpy/_array_api/_array_object.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py index 13b093f4f..3ff845dd7 100644 --- a/numpy/_array_api/_array_object.py +++ b/numpy/_array_api/_array_object.py @@ -345,8 +345,8 @@ class Array: return self.__class__._new(res) def __array_namespace__(self: Array, /, *, api_version: Optional[str] = None) -> object: - if api_version is not None: - raise ValueError("Unrecognized array API version") + if api_version is not None and not api_version.startswith('2021.'): + raise ValueError(f"Unrecognized array API version: {api_version!r}") from numpy import _array_api return _array_api -- cgit v1.2.1 From 8680a12bbcb18a4974cd4fd31068e16b67868026 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 23 Jul 2021 17:46:16 -0600 Subject: Only allow dtypes to be spelled with their names in the array API Other spellings like dtype=int or dtype='i' are not part of the spec. --- numpy/_array_api/_creation_functions.py | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 3c591ffe1..f92a93c5d 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -10,6 +10,16 @@ from ._dtypes import _all_dtypes import numpy as np +def _check_valid_dtype(dtype): + # Note: Only spelling dtypes as the dtype objects is supported. + + # We use this instead of "dtype in _all_dtypes" because the dtype objects + # define equality with the sorts of things we want to disallw. + for d in (None,) + _all_dtypes: + if dtype is d: + return + raise ValueError("dtype must be one of the supported dtypes") + def asarray(obj: Union[Array, bool, int, float, NestedSequence[bool|int|float], SupportsDLPack, SupportsBufferProtocol], /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, copy: Optional[bool] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.asarray `. @@ -19,6 +29,8 @@ def asarray(obj: Union[Array, bool, int, float, NestedSequence[bool|int|float], # _array_object imports in this file are inside the functions to avoid # circular imports from ._array_object import Array + + _check_valid_dtype(dtype) if device is not None: # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") @@ -41,6 +53,8 @@ def arange(start: Union[int, float], /, stop: Optional[Union[int, float]] = None See its docstring for more information. """ from ._array_object import Array + + _check_valid_dtype(dtype) if device is not None: # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") @@ -53,6 +67,8 @@ def empty(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, See its docstring for more information. """ from ._array_object import Array + + _check_valid_dtype(dtype) if device is not None: # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") @@ -65,6 +81,8 @@ def empty_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[D See its docstring for more information. """ from ._array_object import Array + + _check_valid_dtype(dtype) if device is not None: # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") @@ -77,6 +95,8 @@ def eye(n_rows: int, n_cols: Optional[int] = None, /, *, k: Optional[int] = 0, d See its docstring for more information. """ from ._array_object import Array + + _check_valid_dtype(dtype) if device is not None: # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") @@ -93,6 +113,8 @@ def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], *, d See its docstring for more information. """ from ._array_object import Array + + _check_valid_dtype(dtype) if device is not None: # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") @@ -112,6 +134,8 @@ def full_like(x: Array, /, fill_value: Union[int, float], *, dtype: Optional[Dty See its docstring for more information. """ from ._array_object import Array + + _check_valid_dtype(dtype) if device is not None: # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") @@ -129,6 +153,8 @@ def linspace(start: Union[int, float], stop: Union[int, float], /, num: int, *, See its docstring for more information. """ from ._array_object import Array + + _check_valid_dtype(dtype) if device is not None: # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") @@ -150,6 +176,8 @@ def ones(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, d See its docstring for more information. """ from ._array_object import Array + + _check_valid_dtype(dtype) if device is not None: # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") @@ -162,6 +190,8 @@ def ones_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[De See its docstring for more information. """ from ._array_object import Array + + _check_valid_dtype(dtype) if device is not None: # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") @@ -174,6 +204,8 @@ def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, See its docstring for more information. """ from ._array_object import Array + + _check_valid_dtype(dtype) if device is not None: # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") @@ -186,6 +218,8 @@ def zeros_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[D See its docstring for more information. """ from ._array_object import Array + + _check_valid_dtype(dtype) if device is not None: # Note: Device support is not yet implemented on Array raise NotImplementedError("Device support is not yet implemented") -- cgit v1.2.1 From 1823e7ec93222ad9022e50448c2e9310bd218c66 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 23 Jul 2021 18:01:12 -0600 Subject: Allow setting device='cpu' in the array API creation functions --- numpy/_array_api/_creation_functions.py | 60 +++++++++++++-------------------- 1 file changed, 24 insertions(+), 36 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index f92a93c5d..1d8c5499a 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -31,9 +31,8 @@ def asarray(obj: Union[Array, bool, int, float, NestedSequence[bool|int|float], from ._array_object import Array _check_valid_dtype(dtype) - if device is not None: - # Note: Device support is not yet implemented on Array - raise NotImplementedError("Device support is not yet implemented") + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") if copy is not None: # Note: copy is not yet implemented in np.asarray raise NotImplementedError("The copy keyword argument to asarray is not yet implemented") @@ -55,9 +54,8 @@ def arange(start: Union[int, float], /, stop: Optional[Union[int, float]] = None from ._array_object import Array _check_valid_dtype(dtype) - if device is not None: - # Note: Device support is not yet implemented on Array - raise NotImplementedError("Device support is not yet implemented") + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") return Array._new(np.arange(start, stop=stop, step=step, dtype=dtype)) def empty(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: @@ -69,9 +67,8 @@ def empty(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, from ._array_object import Array _check_valid_dtype(dtype) - if device is not None: - # Note: Device support is not yet implemented on Array - raise NotImplementedError("Device support is not yet implemented") + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") return Array._new(np.empty(shape, dtype=dtype)) def empty_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: @@ -83,9 +80,8 @@ def empty_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[D from ._array_object import Array _check_valid_dtype(dtype) - if device is not None: - # Note: Device support is not yet implemented on Array - raise NotImplementedError("Device support is not yet implemented") + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") return Array._new(np.empty_like(x._array, dtype=dtype)) def eye(n_rows: int, n_cols: Optional[int] = None, /, *, k: Optional[int] = 0, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: @@ -97,9 +93,8 @@ def eye(n_rows: int, n_cols: Optional[int] = None, /, *, k: Optional[int] = 0, d from ._array_object import Array _check_valid_dtype(dtype) - if device is not None: - # Note: Device support is not yet implemented on Array - raise NotImplementedError("Device support is not yet implemented") + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") return Array._new(np.eye(n_rows, M=n_cols, k=k, dtype=dtype)) def from_dlpack(x: object, /) -> Array: @@ -115,9 +110,8 @@ def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], *, d from ._array_object import Array _check_valid_dtype(dtype) - if device is not None: - # Note: Device support is not yet implemented on Array - raise NotImplementedError("Device support is not yet implemented") + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") if isinstance(fill_value, Array) and fill_value.ndim == 0: fill_value = fill_value._array res = np.full(shape, fill_value, dtype=dtype) @@ -136,9 +130,8 @@ def full_like(x: Array, /, fill_value: Union[int, float], *, dtype: Optional[Dty from ._array_object import Array _check_valid_dtype(dtype) - if device is not None: - # Note: Device support is not yet implemented on Array - raise NotImplementedError("Device support is not yet implemented") + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") res = np.full_like(x._array, fill_value, dtype=dtype) if res.dtype not in _all_dtypes: # This will happen if the fill value is not something that NumPy @@ -155,9 +148,8 @@ def linspace(start: Union[int, float], stop: Union[int, float], /, num: int, *, from ._array_object import Array _check_valid_dtype(dtype) - if device is not None: - # Note: Device support is not yet implemented on Array - raise NotImplementedError("Device support is not yet implemented") + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") return Array._new(np.linspace(start, stop, num, dtype=dtype, endpoint=endpoint)) def meshgrid(*arrays: Sequence[Array], indexing: str = 'xy') -> List[Array, ...]: @@ -178,9 +170,8 @@ def ones(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, d from ._array_object import Array _check_valid_dtype(dtype) - if device is not None: - # Note: Device support is not yet implemented on Array - raise NotImplementedError("Device support is not yet implemented") + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") return Array._new(np.ones(shape, dtype=dtype)) def ones_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: @@ -192,9 +183,8 @@ def ones_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[De from ._array_object import Array _check_valid_dtype(dtype) - if device is not None: - # Note: Device support is not yet implemented on Array - raise NotImplementedError("Device support is not yet implemented") + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") return Array._new(np.ones_like(x._array, dtype=dtype)) def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: @@ -206,9 +196,8 @@ def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, from ._array_object import Array _check_valid_dtype(dtype) - if device is not None: - # Note: Device support is not yet implemented on Array - raise NotImplementedError("Device support is not yet implemented") + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") return Array._new(np.zeros(shape, dtype=dtype)) def zeros_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: @@ -220,7 +209,6 @@ def zeros_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[D from ._array_object import Array _check_valid_dtype(dtype) - if device is not None: - # Note: Device support is not yet implemented on Array - raise NotImplementedError("Device support is not yet implemented") + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") return Array._new(np.zeros_like(x._array, dtype=dtype)) -- cgit v1.2.1 From d93aad2bde7965d1fdb506a5379d8007b399b3f0 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 23 Jul 2021 18:26:39 -0600 Subject: Enable asarray(copy=True) in the array API namespace --- numpy/_array_api/_creation_functions.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 1d8c5499a..0c39a5875 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -33,10 +33,12 @@ def asarray(obj: Union[Array, bool, int, float, NestedSequence[bool|int|float], _check_valid_dtype(dtype) if device not in ['cpu', None]: raise ValueError(f"Unsupported device {device!r}") - if copy is not None: - # Note: copy is not yet implemented in np.asarray - raise NotImplementedError("The copy keyword argument to asarray is not yet implemented") + if copy is False: + # Note: copy=False is not yet implemented in np.asarray + raise NotImplementedError("copy=False is not yet implemented") if isinstance(obj, Array) and (dtype is None or obj.dtype == dtype): + if copy is True: + return Array._new(np.array(obj._array, copy=True, dtype=dtype)) return obj if dtype is None and isinstance(obj, int) and (obj > 2**64 or obj < -2**63): # Give a better error message in this case. NumPy would convert this -- cgit v1.2.1 From 09a4f8c7fc961e9bf536060533a4fd26c35004d8 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 23 Jul 2021 18:27:24 -0600 Subject: Add a TODO comment --- numpy/_array_api/_creation_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py index 0c39a5875..acf78056a 100644 --- a/numpy/_array_api/_creation_functions.py +++ b/numpy/_array_api/_creation_functions.py @@ -42,7 +42,7 @@ def asarray(obj: Union[Array, bool, int, float, NestedSequence[bool|int|float], return obj if dtype is None and isinstance(obj, int) and (obj > 2**64 or obj < -2**63): # Give a better error message in this case. NumPy would convert this - # to an object array. + # to an object array. TODO: This won't handle large integers in lists. raise OverflowError("Integer out of bounds for array dtypes") res = np.asarray(obj, dtype=dtype) return Array._new(res) -- cgit v1.2.1 From 3b91f476fbbecbd111f10efd0aae1df8eed5d667 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 23 Jul 2021 18:28:06 -0600 Subject: Add tests for the array API creation functions As with the other array API tests, the tests primarily focus on things that should error. Working behavior is tested by the official array API test suite. --- numpy/_array_api/tests/test_creation_functions.py | 103 ++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 numpy/_array_api/tests/test_creation_functions.py (limited to 'numpy') diff --git a/numpy/_array_api/tests/test_creation_functions.py b/numpy/_array_api/tests/test_creation_functions.py new file mode 100644 index 000000000..654f1d9b3 --- /dev/null +++ b/numpy/_array_api/tests/test_creation_functions.py @@ -0,0 +1,103 @@ +from numpy.testing import assert_raises +import numpy as np + +from .. import all +from .._creation_functions import (asarray, arange, empty, empty_like, eye, from_dlpack, full, full_like, linspace, meshgrid, ones, ones_like, zeros, zeros_like) +from .._array_object import Array +from .._dtypes import (_all_dtypes, _boolean_dtypes, _floating_dtypes, + _integer_dtypes, _integer_or_boolean_dtypes, + _numeric_dtypes, int8, int16, int32, int64, uint64) + +def test_asarray_errors(): + # Test various protections against incorrect usage + assert_raises(TypeError, lambda: Array([1])) + assert_raises(TypeError, lambda: asarray(['a'])) + assert_raises(ValueError, lambda: asarray([1.], dtype=np.float16)) + assert_raises(OverflowError, lambda: asarray(2**100)) + # Preferably this would be OverflowError + # assert_raises(OverflowError, lambda: asarray([2**100])) + assert_raises(TypeError, lambda: asarray([2**100])) + asarray([1], device='cpu') # Doesn't error + assert_raises(ValueError, lambda: asarray([1], device='gpu')) + + assert_raises(ValueError, lambda: asarray([1], dtype=int)) + assert_raises(ValueError, lambda: asarray([1], dtype='i')) + +def test_asarray_copy(): + a = asarray([1]) + b = asarray(a, copy=True) + a[0] = 0 + assert all(b[0] == 1) + assert all(a[0] == 0) + # Once copy=False is implemented, replace this with + # a = asarray([1]) + # b = asarray(a, copy=False) + # a[0] = 0 + # assert all(b[0] == 0) + assert_raises(NotImplementedError, lambda: asarray(a, copy=False)) + +def test_arange_errors(): + arange(1, device='cpu') # Doesn't error + assert_raises(ValueError, lambda: arange(1, device='gpu')) + assert_raises(ValueError, lambda: arange(1, dtype=int)) + assert_raises(ValueError, lambda: arange(1, dtype='i')) + +def test_empty_errors(): + empty((1,), device='cpu') # Doesn't error + assert_raises(ValueError, lambda: empty((1,), device='gpu')) + assert_raises(ValueError, lambda: empty((1,), dtype=int)) + assert_raises(ValueError, lambda: empty((1,), dtype='i')) + +def test_empty_like_errors(): + empty_like(asarray(1), device='cpu') # Doesn't error + assert_raises(ValueError, lambda: empty_like(asarray(1), device='gpu')) + assert_raises(ValueError, lambda: empty_like(asarray(1), dtype=int)) + assert_raises(ValueError, lambda: empty_like(asarray(1), dtype='i')) + +def test_eye_errors(): + eye(1, device='cpu') # Doesn't error + assert_raises(ValueError, lambda: eye(1, device='gpu')) + assert_raises(ValueError, lambda: eye(1, dtype=int)) + assert_raises(ValueError, lambda: eye(1, dtype='i')) + +def test_full_errors(): + full((1,), 0, device='cpu') # Doesn't error + assert_raises(ValueError, lambda: full((1,), 0, device='gpu')) + assert_raises(ValueError, lambda: full((1,), 0, dtype=int)) + assert_raises(ValueError, lambda: full((1,), 0, dtype='i')) + +def test_full_like_errors(): + full_like(asarray(1), 0, device='cpu') # Doesn't error + assert_raises(ValueError, lambda: full_like(asarray(1), 0, device='gpu')) + assert_raises(ValueError, lambda: full_like(asarray(1), 0, dtype=int)) + assert_raises(ValueError, lambda: full_like(asarray(1), 0, dtype='i')) + +def test_linspace_errors(): + linspace(0, 1, 10, device='cpu') # Doesn't error + assert_raises(ValueError, lambda: linspace(0, 1, 10, device='gpu')) + assert_raises(ValueError, lambda: linspace(0, 1, 10, dtype=float)) + assert_raises(ValueError, lambda: linspace(0, 1, 10, dtype='f')) + +def test_ones_errors(): + ones((1,), device='cpu') # Doesn't error + assert_raises(ValueError, lambda: ones((1,), device='gpu')) + assert_raises(ValueError, lambda: ones((1,), dtype=int)) + assert_raises(ValueError, lambda: ones((1,), dtype='i')) + +def test_ones_like_errors(): + ones_like(asarray(1), device='cpu') # Doesn't error + assert_raises(ValueError, lambda: ones_like(asarray(1), device='gpu')) + assert_raises(ValueError, lambda: ones_like(asarray(1), dtype=int)) + assert_raises(ValueError, lambda: ones_like(asarray(1), dtype='i')) + +def test_zeros_errors(): + zeros((1,), device='cpu') # Doesn't error + assert_raises(ValueError, lambda: zeros((1,), device='gpu')) + assert_raises(ValueError, lambda: zeros((1,), dtype=int)) + assert_raises(ValueError, lambda: zeros((1,), dtype='i')) + +def test_zeros_like_errors(): + zeros_like(asarray(1), device='cpu') # Doesn't error + assert_raises(ValueError, lambda: zeros_like(asarray(1), device='gpu')) + assert_raises(ValueError, lambda: zeros_like(asarray(1), dtype=int)) + assert_raises(ValueError, lambda: zeros_like(asarray(1), dtype='i')) -- cgit v1.2.1 From fbd8f9a2c48fceddb10de225dc49e3d7c17c678d Mon Sep 17 00:00:00 2001 From: Carl Johnsen Date: Thu, 29 Jul 2021 09:18:12 +0200 Subject: BLD: loaded extra flags when checking for libflame --- numpy/distutils/system_info.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'numpy') diff --git a/numpy/distutils/system_info.py b/numpy/distutils/system_info.py index 2846d754e..8467e1c19 100644 --- a/numpy/distutils/system_info.py +++ b/numpy/distutils/system_info.py @@ -2424,6 +2424,10 @@ class flame_info(system_info): if info is None: return + # Add the extra flag args to info + extra_info = self.calc_extra_info() + dict_append(info, **extra_info) + if self.check_embedded_lapack(info): # check if the user has supplied all information required self.set_info(**info) -- cgit v1.2.1 From 3d46286ec1b93e4eeca6ed6ce7b957fb8ab1005c Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Tue, 27 Jul 2021 15:18:29 -0700 Subject: ENH: Add basic promoter capability to ufunc dispatching --- numpy/core/src/umath/dispatching.c | 74 ++++++++++++++++++++++++++++++++------ numpy/core/src/umath/dispatching.h | 4 +++ 2 files changed, 68 insertions(+), 10 deletions(-) (limited to 'numpy') diff --git a/numpy/core/src/umath/dispatching.c b/numpy/core/src/umath/dispatching.c index 1d3ad9dff..6bad5bd38 100644 --- a/numpy/core/src/umath/dispatching.c +++ b/numpy/core/src/umath/dispatching.c @@ -97,8 +97,9 @@ PyUFunc_AddLoop(PyUFuncObject *ufunc, PyObject *info, int ignore_duplicate) return -1; } } - if (!PyObject_TypeCheck(PyTuple_GET_ITEM(info, 1), &PyArrayMethod_Type)) { - /* Must also accept promoters in the future. */ + PyObject *meth_or_promoter = PyTuple_GET_ITEM(info, 1); + if (!PyObject_TypeCheck(meth_or_promoter, &PyArrayMethod_Type) + && !PyCapsule_IsValid(meth_or_promoter, "numpy._ufunc_promoter")) { PyErr_SetString(PyExc_TypeError, "Second argument to info must be an ArrayMethod or promoter"); return -1; @@ -353,15 +354,68 @@ resolve_implementation_info(PyUFuncObject *ufunc, * those defined by the `signature` unmodified). */ static PyObject * -call_promoter_and_recurse( - PyUFuncObject *NPY_UNUSED(ufunc), PyObject *NPY_UNUSED(promoter), - PyArray_DTypeMeta *NPY_UNUSED(op_dtypes[]), - PyArray_DTypeMeta *NPY_UNUSED(signature[]), - PyArrayObject *const NPY_UNUSED(operands[])) +call_promoter_and_recurse(PyUFuncObject *ufunc, PyObject *promoter, + PyArray_DTypeMeta *op_dtypes[], PyArray_DTypeMeta *signature[], + PyArrayObject *const operands[]) { - PyErr_SetString(PyExc_NotImplementedError, - "Internal NumPy error, promoters are not used/implemented yet."); - return NULL; + int nargs = ufunc->nargs; + PyObject *resolved_info = NULL; + + int promoter_result; + PyArray_DTypeMeta *new_op_dtypes[NPY_MAXARGS]; + + if (PyCapsule_CheckExact(promoter)) { + /* We could also go the other way and wrap up the python function... */ + promoter_function *promoter_function = PyCapsule_GetPointer(promoter, + "numpy._ufunc_promoter"); + if (promoter_function == NULL) { + return NULL; + } + promoter_result = promoter_function(ufunc, + op_dtypes, signature, new_op_dtypes); + } + else { + PyErr_SetString(PyExc_NotImplementedError, + "Calling python functions for promotion is not implemented."); + return NULL; + } + if (promoter_result < 0) { + return NULL; + } + /* + * If none of the dtypes changes, we would recurse infinitely, abort. + * (Of course it is nevertheless possible to recurse infinitely.) + */ + int dtypes_changed = 0; + for (int i = 0; i < nargs; i++) { + if (new_op_dtypes[i] != op_dtypes[i]) { + dtypes_changed = 1; + break; + } + } + if (!dtypes_changed) { + goto finish; + } + + /* + * Do a recursive call, the promotion function has to ensure that the + * new tuple is strictly more precise (thus guaranteeing eventual finishing) + */ + if (Py_EnterRecursiveCall(" during ufunc promotion.") != 0) { + goto finish; + } + /* TODO: The caching logic here may need revising: */ + resolved_info = promote_and_get_info_and_ufuncimpl(ufunc, + operands, signature, new_op_dtypes, + /* no legacy promotion */ NPY_FALSE, /* cache */ NPY_TRUE); + + Py_LeaveRecursiveCall(); + + finish: + for (int i = 0; i < nargs; i++) { + Py_XDECREF(new_op_dtypes[i]); + } + return resolved_info; } diff --git a/numpy/core/src/umath/dispatching.h b/numpy/core/src/umath/dispatching.h index b01bc79fa..8d116873c 100644 --- a/numpy/core/src/umath/dispatching.h +++ b/numpy/core/src/umath/dispatching.h @@ -7,6 +7,10 @@ #include "array_method.h" +typedef int promoter_function(PyUFuncObject *ufunc, + PyArray_DTypeMeta *op_dtypes[], PyArray_DTypeMeta *signature[], + PyArray_DTypeMeta *new_op_dtypes[]); + NPY_NO_EXPORT int PyUFunc_AddLoop(PyUFuncObject *ufunc, PyObject *info, int ignore_duplicate); -- cgit v1.2.1 From 693966c8e6677cc573f19f436aa2a310d986e4ed Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Tue, 27 Jul 2021 16:20:07 -0700 Subject: TST: Add basic promotion test to the scaled-float custom DType --- numpy/core/src/umath/_scaled_float_dtype.c | 67 ++++++++++++++++++++++++++---- numpy/core/tests/test_custom_dtypes.py | 12 ++++++ 2 files changed, 70 insertions(+), 9 deletions(-) (limited to 'numpy') diff --git a/numpy/core/src/umath/_scaled_float_dtype.c b/numpy/core/src/umath/_scaled_float_dtype.c index aa9c4549c..8ca31169f 100644 --- a/numpy/core/src/umath/_scaled_float_dtype.c +++ b/numpy/core/src/umath/_scaled_float_dtype.c @@ -464,9 +464,6 @@ init_casts(void) * 2. Addition, which needs to use the common instance, and runs into * cast safety subtleties since we will implement it without an additional * cast. - * - * NOTE: When first writing this, promotion did not exist for new-style loops, - * if it exists, we could use promotion to implement double * sfloat. */ static int multiply_sfloats(PyArrayMethod_Context *NPY_UNUSED(context), @@ -591,7 +588,8 @@ add_sfloats_resolve_descriptors( static int -add_loop(const char *ufunc_name, PyBoundArrayMethodObject *bmeth) +add_loop(const char *ufunc_name, + PyArray_DTypeMeta *dtypes[3], PyObject *meth_or_promoter) { PyObject *mod = PyImport_ImportModule("numpy"); if (mod == NULL) { @@ -605,13 +603,12 @@ add_loop(const char *ufunc_name, PyBoundArrayMethodObject *bmeth) "numpy.%s was not a ufunc!", ufunc_name); return -1; } - PyObject *dtype_tup = PyArray_TupleFromItems( - 3, (PyObject **)bmeth->dtypes, 0); + PyObject *dtype_tup = PyArray_TupleFromItems(3, (PyObject **)dtypes, 1); if (dtype_tup == NULL) { Py_DECREF(ufunc); return -1; } - PyObject *info = PyTuple_Pack(2, dtype_tup, bmeth->method); + PyObject *info = PyTuple_Pack(2, dtype_tup, meth_or_promoter); Py_DECREF(dtype_tup); if (info == NULL) { Py_DECREF(ufunc); @@ -624,6 +621,28 @@ add_loop(const char *ufunc_name, PyBoundArrayMethodObject *bmeth) } + +/* + * We add some very basic promoters to allow multiplying normal and scaled + */ +static int +promote_to_sfloat(PyUFuncObject *NPY_UNUSED(ufunc), + PyArray_DTypeMeta *const NPY_UNUSED(dtypes[3]), + PyArray_DTypeMeta *const signature[3], + PyArray_DTypeMeta *new_dtypes[3]) +{ + for (int i = 0; i < 3; i++) { + PyArray_DTypeMeta *new = &PyArray_SFloatDType; + if (signature[i] != NULL) { + new = signature[i]; + } + Py_INCREF(new); + new_dtypes[i] = new; + } + return 0; +} + + /* * Add new ufunc loops (this is somewhat clumsy as of writing it, but should * get less so with the introduction of public API). @@ -650,7 +669,8 @@ init_ufuncs(void) { if (bmeth == NULL) { return -1; } - int res = add_loop("multiply", bmeth); + int res = add_loop("multiply", + bmeth->dtypes, (PyObject *)bmeth->method); Py_DECREF(bmeth); if (res < 0) { return -1; @@ -667,11 +687,40 @@ init_ufuncs(void) { if (bmeth == NULL) { return -1; } - res = add_loop("add", bmeth); + res = add_loop("add", + bmeth->dtypes, (PyObject *)bmeth->method); Py_DECREF(bmeth); if (res < 0) { return -1; } + + /* + * Add a promoter for both directions of multiply with double. + */ + PyArray_DTypeMeta *double_DType = PyArray_DTypeFromTypeNum(NPY_DOUBLE); + Py_DECREF(double_DType); /* immortal anyway */ + + PyArray_DTypeMeta *promoter_dtypes[3] = { + &PyArray_SFloatDType, double_DType, NULL}; + + PyObject *promoter = PyCapsule_New( + &promote_to_sfloat, "numpy._ufunc_promoter", NULL); + if (promoter == NULL) { + return -1; + } + res = add_loop("multiply", promoter_dtypes, promoter); + if (res < 0) { + Py_DECREF(promoter); + return -1; + } + promoter_dtypes[0] = double_DType; + promoter_dtypes[1] = &PyArray_SFloatDType; + res = add_loop("multiply", promoter_dtypes, promoter); + Py_DECREF(promoter); + if (res < 0) { + return -1; + } + return 0; } diff --git a/numpy/core/tests/test_custom_dtypes.py b/numpy/core/tests/test_custom_dtypes.py index 3ec2363b9..5eb82bc93 100644 --- a/numpy/core/tests/test_custom_dtypes.py +++ b/numpy/core/tests/test_custom_dtypes.py @@ -101,6 +101,18 @@ class TestSFloat: expected_view = a.view(np.float64) * b.view(np.float64) assert_array_equal(res.view(np.float64), expected_view) + def test_basic_multiply_promotion(self): + float_a = np.array([1., 2., 3.]) + b = self._get_array(2.) + + res1 = float_a * b + res2 = b * float_a + # one factor is one, so we get the factor of b: + assert res1.dtype == res2.dtype == b.dtype + expected_view = float_a * b.view(np.float64) + assert_array_equal(res1.view(np.float64), expected_view) + assert_array_equal(res2.view(np.float64), expected_view) + def test_basic_addition(self): a = self._get_array(2.) b = self._get_array(4.) -- cgit v1.2.1 From 8571fc5d2fa4b7ab1f6730bce5d728328139c09f Mon Sep 17 00:00:00 2001 From: Bas van Beek <43369155+BvB93@users.noreply.github.com> Date: Wed, 4 Aug 2021 18:35:00 +0200 Subject: MAINT: Rename `types.Union` to `types.UnionType` The class got renamed in the 3.10rc1 release --- numpy/core/numerictypes.pyi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/core/numerictypes.pyi b/numpy/core/numerictypes.pyi index e99e1c500..d5e3ccffb 100644 --- a/numpy/core/numerictypes.pyi +++ b/numpy/core/numerictypes.pyi @@ -86,8 +86,8 @@ class _typedict(Dict[Type[generic], _T]): if sys.version_info >= (3, 10): _TypeTuple = Union[ Type[Any], - types.Union, - Tuple[Union[Type[Any], types.Union, Tuple[Any, ...]], ...], + types.UnionType, + Tuple[Union[Type[Any], types.UnionType, Tuple[Any, ...]], ...], ] else: _TypeTuple = Union[ -- cgit v1.2.1 From 6e57d829cb6628610e163524f203245b247a2839 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 4 Aug 2021 16:47:05 -0600 Subject: Rename numpy._array_api to numpy.array_api Instead of the leading underscore, the experimentalness of the module will be indicated by omitting a warning on import. That we, we do not have to change the API from underscore to no underscore when the module is no longer experimental. --- numpy/_array_api/__init__.py | 171 ---- numpy/_array_api/_array_object.py | 983 --------------------- numpy/_array_api/_constants.py | 6 - numpy/_array_api/_creation_functions.py | 216 ----- numpy/_array_api/_data_type_functions.py | 117 --- numpy/_array_api/_dtypes.py | 100 --- numpy/_array_api/_elementwise_functions.py | 659 -------------- numpy/_array_api/_linear_algebra_functions.py | 58 -- numpy/_array_api/_manipulation_functions.py | 71 -- numpy/_array_api/_searching_functions.py | 44 - numpy/_array_api/_set_functions.py | 15 - numpy/_array_api/_sorting_functions.py | 31 - numpy/_array_api/_statistical_functions.py | 30 - numpy/_array_api/_typing.py | 26 - numpy/_array_api/_utility_functions.py | 23 - numpy/_array_api/tests/__init__.py | 7 - numpy/_array_api/tests/test_array_object.py | 250 ------ numpy/_array_api/tests/test_creation_functions.py | 103 --- .../_array_api/tests/test_elementwise_functions.py | 110 --- numpy/array_api/__init__.py | 171 ++++ numpy/array_api/_array_object.py | 983 +++++++++++++++++++++ numpy/array_api/_constants.py | 6 + numpy/array_api/_creation_functions.py | 216 +++++ numpy/array_api/_data_type_functions.py | 117 +++ numpy/array_api/_dtypes.py | 100 +++ numpy/array_api/_elementwise_functions.py | 659 ++++++++++++++ numpy/array_api/_linear_algebra_functions.py | 58 ++ numpy/array_api/_manipulation_functions.py | 71 ++ numpy/array_api/_searching_functions.py | 44 + numpy/array_api/_set_functions.py | 15 + numpy/array_api/_sorting_functions.py | 31 + numpy/array_api/_statistical_functions.py | 30 + numpy/array_api/_typing.py | 26 + numpy/array_api/_utility_functions.py | 23 + numpy/array_api/tests/__init__.py | 7 + numpy/array_api/tests/test_array_object.py | 250 ++++++ numpy/array_api/tests/test_creation_functions.py | 103 +++ .../array_api/tests/test_elementwise_functions.py | 110 +++ numpy/setup.py | 2 +- 39 files changed, 3021 insertions(+), 3021 deletions(-) delete mode 100644 numpy/_array_api/__init__.py delete mode 100644 numpy/_array_api/_array_object.py delete mode 100644 numpy/_array_api/_constants.py delete mode 100644 numpy/_array_api/_creation_functions.py delete mode 100644 numpy/_array_api/_data_type_functions.py delete mode 100644 numpy/_array_api/_dtypes.py delete mode 100644 numpy/_array_api/_elementwise_functions.py delete mode 100644 numpy/_array_api/_linear_algebra_functions.py delete mode 100644 numpy/_array_api/_manipulation_functions.py delete mode 100644 numpy/_array_api/_searching_functions.py delete mode 100644 numpy/_array_api/_set_functions.py delete mode 100644 numpy/_array_api/_sorting_functions.py delete mode 100644 numpy/_array_api/_statistical_functions.py delete mode 100644 numpy/_array_api/_typing.py delete mode 100644 numpy/_array_api/_utility_functions.py delete mode 100644 numpy/_array_api/tests/__init__.py delete mode 100644 numpy/_array_api/tests/test_array_object.py delete mode 100644 numpy/_array_api/tests/test_creation_functions.py delete mode 100644 numpy/_array_api/tests/test_elementwise_functions.py create mode 100644 numpy/array_api/__init__.py create mode 100644 numpy/array_api/_array_object.py create mode 100644 numpy/array_api/_constants.py create mode 100644 numpy/array_api/_creation_functions.py create mode 100644 numpy/array_api/_data_type_functions.py create mode 100644 numpy/array_api/_dtypes.py create mode 100644 numpy/array_api/_elementwise_functions.py create mode 100644 numpy/array_api/_linear_algebra_functions.py create mode 100644 numpy/array_api/_manipulation_functions.py create mode 100644 numpy/array_api/_searching_functions.py create mode 100644 numpy/array_api/_set_functions.py create mode 100644 numpy/array_api/_sorting_functions.py create mode 100644 numpy/array_api/_statistical_functions.py create mode 100644 numpy/array_api/_typing.py create mode 100644 numpy/array_api/_utility_functions.py create mode 100644 numpy/array_api/tests/__init__.py create mode 100644 numpy/array_api/tests/test_array_object.py create mode 100644 numpy/array_api/tests/test_creation_functions.py create mode 100644 numpy/array_api/tests/test_elementwise_functions.py (limited to 'numpy') diff --git a/numpy/_array_api/__init__.py b/numpy/_array_api/__init__.py deleted file mode 100644 index 57a4ff4e1..000000000 --- a/numpy/_array_api/__init__.py +++ /dev/null @@ -1,171 +0,0 @@ -""" -A NumPy sub-namespace that conforms to the Python array API standard. - -This submodule accompanies NEP 47, which proposes its inclusion in NumPy. - -This is a proof-of-concept namespace that wraps the corresponding NumPy -functions to give a conforming implementation of the Python array API standard -(https://data-apis.github.io/array-api/latest/). The standard is currently in -an RFC phase and comments on it are both welcome and encouraged. Comments -should be made either at https://github.com/data-apis/array-api or at -https://github.com/data-apis/consortium-feedback/discussions. - -NumPy already follows the proposed spec for the most part, so this module -serves mostly as a thin wrapper around it. However, NumPy also implements a -lot of behavior that is not included in the spec, so this serves as a -restricted subset of the API. Only those functions that are part of the spec -are included in this namespace, and all functions are given with the exact -signature given in the spec, including the use of position-only arguments, and -omitting any extra keyword arguments implemented by NumPy but not part of the -spec. The behavior of some functions is also modified from the NumPy behavior -to conform to the standard. Note that the underlying array object itself is -wrapped in a wrapper Array() class, but is otherwise unchanged. This submodule -is implemented in pure Python with no C extensions. - -The array API spec is designed as a "minimal API subset" and explicitly allows -libraries to include behaviors not specified by it. But users of this module -that intend to write portable code should be aware that only those behaviors -that are listed in the spec are guaranteed to be implemented across libraries. -Consequently, the NumPy implementation was chosen to be both conforming and -minimal, so that users can use this implementation of the array API namespace -and be sure that behaviors that it defines will be available in conforming -namespaces from other libraries. - -A few notes about the current state of this submodule: - -- There is a test suite that tests modules against the array API standard at - https://github.com/data-apis/array-api-tests. The test suite is still a work - in progress, but the existing tests pass on this module, with a few - exceptions: - - - Device support is not yet implemented in NumPy - (https://data-apis.github.io/array-api/latest/design_topics/device_support.html). - As a result, the `device` attribute of the array object is missing, and - array creation functions that take the `device` keyword argument will fail - with NotImplementedError. - - - DLPack support (see https://github.com/data-apis/array-api/pull/106) is - not included here, as it requires a full implementation in NumPy proper - first. - - - The linear algebra extension in the spec will be added in a future pull -request. - - The test suite is not yet complete, and even the tests that exist are not - guaranteed to give a comprehensive coverage of the spec. Therefore, those - reviewing this submodule should refer to the standard documents themselves. - -- There is a custom array object, numpy._array_api.Array, which is returned - by all functions in this module. All functions in the array API namespace - implicitly assume that they will only receive this object as input. The only - way to create instances of this object is to use one of the array creation - functions. It does not have a public constructor on the object itself. The - object is a small wrapper Python class around numpy.ndarray. The main - purpose of it is to restrict the namespace of the array object to only those - dtypes and only those methods that are required by the spec, as well as to - limit/change certain behavior that differs in the spec. In particular: - - - The array API namespace does not have scalar objects, only 0-d arrays. - Operations in on Array that would create a scalar in NumPy create a 0-d - array. - - - Indexing: Only a subset of indices supported by NumPy are required by the - spec. The Array object restricts indexing to only allow those types of - indices that are required by the spec. See the docstring of the - numpy._array_api.Array._validate_indices helper function for more - information. - - - Type promotion: Some type promotion rules are different in the spec. In - particular, the spec does not have any value-based casting. The - Array._promote_scalar method promotes Python scalars to arrays, - disallowing cross-type promotions like int -> float64 that are not allowed - in the spec. Array._normalize_two_args works around some type promotion - quirks in NumPy, particularly, value-based casting that occurs when one - argument of an operation is a 0-d array. - -- All functions include type annotations, corresponding to those given in the - spec (see _typing.py for definitions of some custom types). These do not - currently fully pass mypy due to some limitations in mypy. - -- Dtype objects are just the NumPy dtype objects, e.g., float64 = - np.dtype('float64'). The spec does not require any behavior on these dtype - objects other than that they be accessible by name and be comparable by - equality, but it was considered too much extra complexity to create custom - objects to represent dtypes. - -- The wrapper functions in this module do not do any type checking for things - that would be impossible without leaving the _array_api namespace. For - example, since the array API dtype objects are just the NumPy dtype objects, - one could pass in a non-spec NumPy dtype into a function. - -- All places where the implementations in this submodule are known to deviate - from their corresponding functions in NumPy are marked with "# Note" - comments. Reviewers should make note of these comments. - -Still TODO in this module are: - -- Device support and DLPack support are not yet implemented. These require - support in NumPy itself first. - -- The a non-default value for the `copy` keyword argument is not yet - implemented on asarray. This requires support in numpy.asarray() first. - -- Some functions are not yet fully tested in the array API test suite, and may - require updates that are not yet known until the tests are written. - -""" - -__all__ = [] - -from ._constants import e, inf, nan, pi - -__all__ += ['e', 'inf', 'nan', 'pi'] - -from ._creation_functions import asarray, arange, empty, empty_like, eye, from_dlpack, full, full_like, linspace, meshgrid, ones, ones_like, zeros, zeros_like - -__all__ += ['asarray', 'arange', 'empty', 'empty_like', 'eye', 'from_dlpack', 'full', 'full_like', 'linspace', 'meshgrid', 'ones', 'ones_like', 'zeros', 'zeros_like'] - -from ._data_type_functions import broadcast_arrays, broadcast_to, can_cast, finfo, iinfo, result_type - -__all__ += ['broadcast_arrays', 'broadcast_to', 'can_cast', 'finfo', 'iinfo', 'result_type'] - -from ._dtypes import int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, bool - -__all__ += ['int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64', 'float32', 'float64', 'bool'] - -from ._elementwise_functions import abs, acos, acosh, add, asin, asinh, atan, atan2, atanh, bitwise_and, bitwise_left_shift, bitwise_invert, bitwise_or, bitwise_right_shift, bitwise_xor, ceil, cos, cosh, divide, equal, exp, expm1, floor, floor_divide, greater, greater_equal, isfinite, isinf, isnan, less, less_equal, log, log1p, log2, log10, logaddexp, logical_and, logical_not, logical_or, logical_xor, multiply, negative, not_equal, positive, pow, remainder, round, sign, sin, sinh, square, sqrt, subtract, tan, tanh, trunc - -__all__ += ['abs', 'acos', 'acosh', 'add', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'bitwise_and', 'bitwise_left_shift', 'bitwise_invert', 'bitwise_or', 'bitwise_right_shift', 'bitwise_xor', 'ceil', 'cos', 'cosh', 'divide', 'equal', 'exp', 'expm1', 'floor', 'floor_divide', 'greater', 'greater_equal', 'isfinite', 'isinf', 'isnan', 'less', 'less_equal', 'log', 'log1p', 'log2', 'log10', 'logaddexp', 'logical_and', 'logical_not', 'logical_or', 'logical_xor', 'multiply', 'negative', 'not_equal', 'positive', 'pow', 'remainder', 'round', 'sign', 'sin', 'sinh', 'square', 'sqrt', 'subtract', 'tan', 'tanh', 'trunc'] - -# einsum is not yet implemented in the array API spec. - -# from ._linear_algebra_functions import einsum -# __all__ += ['einsum'] - -from ._linear_algebra_functions import matmul, tensordot, transpose, vecdot - -__all__ += ['matmul', 'tensordot', 'transpose', 'vecdot'] - -from ._manipulation_functions import concat, expand_dims, flip, reshape, roll, squeeze, stack - -__all__ += ['concat', 'expand_dims', 'flip', 'reshape', 'roll', 'squeeze', 'stack'] - -from ._searching_functions import argmax, argmin, nonzero, where - -__all__ += ['argmax', 'argmin', 'nonzero', 'where'] - -from ._set_functions import unique - -__all__ += ['unique'] - -from ._sorting_functions import argsort, sort - -__all__ += ['argsort', 'sort'] - -from ._statistical_functions import max, mean, min, prod, std, sum, var - -__all__ += ['max', 'mean', 'min', 'prod', 'std', 'sum', 'var'] - -from ._utility_functions import all, any - -__all__ += ['all', 'any'] diff --git a/numpy/_array_api/_array_object.py b/numpy/_array_api/_array_object.py deleted file mode 100644 index 3ff845dd7..000000000 --- a/numpy/_array_api/_array_object.py +++ /dev/null @@ -1,983 +0,0 @@ -""" -Wrapper class around the ndarray object for the array API standard. - -The array API standard defines some behaviors differently than ndarray, in -particular, type promotion rules are different (the standard has no -value-based casting). The standard also specifies a more limited subset of -array methods and functionalities than are implemented on ndarray. Since the -goal of the array_api namespace is to be a minimal implementation of the array -API standard, we need to define a separate wrapper class for the array_api -namespace. - -The standard compliant class is only a wrapper class. It is *not* a subclass -of ndarray. -""" - -from __future__ import annotations - -import operator -from enum import IntEnum -from ._creation_functions import asarray -from ._dtypes import (_all_dtypes, _boolean_dtypes, _integer_dtypes, - _integer_or_boolean_dtypes, _floating_dtypes, _numeric_dtypes) - -from typing import TYPE_CHECKING, Any, Optional, Tuple, Union -if TYPE_CHECKING: - from ._typing import PyCapsule, Device, Dtype - -import numpy as np - -class Array: - """ - n-d array object for the array API namespace. - - See the docstring of :py:obj:`np.ndarray ` for more - information. - - This is a wrapper around numpy.ndarray that restricts the usage to only - those things that are required by the array API namespace. Note, - attributes on this object that start with a single underscore are not part - of the API specification and should only be used internally. This object - should not be constructed directly. Rather, use one of the creation - functions, such as asarray(). - - """ - # Use a custom constructor instead of __init__, as manually initializing - # this class is not supported API. - @classmethod - def _new(cls, x, /): - """ - This is a private method for initializing the array API Array - object. - - Functions outside of the array_api submodule should not use this - method. Use one of the creation functions instead, such as - ``asarray``. - - """ - obj = super().__new__(cls) - # Note: The spec does not have array scalars, only 0-D arrays. - if isinstance(x, np.generic): - # Convert the array scalar to a 0-D array - x = np.asarray(x) - if x.dtype not in _all_dtypes: - raise TypeError(f"The array_api namespace does not support the dtype '{x.dtype}'") - obj._array = x - return obj - - # Prevent Array() from working - def __new__(cls, *args, **kwargs): - raise TypeError("The array_api Array object should not be instantiated directly. Use an array creation function, such as asarray(), instead.") - - # These functions are not required by the spec, but are implemented for - # the sake of usability. - - def __str__(self: Array, /) -> str: - """ - Performs the operation __str__. - """ - return self._array.__str__().replace('array', 'Array') - - def __repr__(self: Array, /) -> str: - """ - Performs the operation __repr__. - """ - return f"Array({np.array2string(self._array, separator=', ')}, dtype={self.dtype.name})" - - # These are various helper functions to make the array behavior match the - # spec in places where it either deviates from or is more strict than - # NumPy behavior - - def _check_allowed_dtypes(self, other, dtype_category, op): - """ - Helper function for operators to only allow specific input dtypes - - Use like - - other = self._check_allowed_dtypes(other, 'numeric', '__add__') - if other is NotImplemented: - return other - """ - from ._dtypes import _result_type - - _dtypes = { - 'all': _all_dtypes, - 'numeric': _numeric_dtypes, - 'integer': _integer_dtypes, - 'integer or boolean': _integer_or_boolean_dtypes, - 'boolean': _boolean_dtypes, - 'floating-point': _floating_dtypes, - } - - if self.dtype not in _dtypes[dtype_category]: - raise TypeError(f'Only {dtype_category} dtypes are allowed in {op}') - if isinstance(other, (int, float, bool)): - other = self._promote_scalar(other) - elif isinstance(other, Array): - if other.dtype not in _dtypes[dtype_category]: - raise TypeError(f'Only {dtype_category} dtypes are allowed in {op}') - else: - return NotImplemented - - # This will raise TypeError for type combinations that are not allowed - # to promote in the spec (even if the NumPy array operator would - # promote them). - res_dtype = _result_type(self.dtype, other.dtype) - if op.startswith('__i'): - # Note: NumPy will allow in-place operators in some cases where - # the type promoted operator does not match the left-hand side - # operand. For example, - - # >>> a = np.array(1, dtype=np.int8) - # >>> a += np.array(1, dtype=np.int16) - - # The spec explicitly disallows this. - if res_dtype != self.dtype: - raise TypeError(f"Cannot perform {op} with dtypes {self.dtype} and {other.dtype}") - - return other - - # Helper function to match the type promotion rules in the spec - def _promote_scalar(self, scalar): - """ - Returns a promoted version of a Python scalar appropriate for use with - operations on self. - - This may raise an OverflowError in cases where the scalar is an - integer that is too large to fit in a NumPy integer dtype, or - TypeError when the scalar type is incompatible with the dtype of self. - """ - if isinstance(scalar, bool): - if self.dtype not in _boolean_dtypes: - raise TypeError("Python bool scalars can only be promoted with bool arrays") - elif isinstance(scalar, int): - if self.dtype in _boolean_dtypes: - raise TypeError("Python int scalars cannot be promoted with bool arrays") - elif isinstance(scalar, float): - if self.dtype not in _floating_dtypes: - raise TypeError("Python float scalars can only be promoted with floating-point arrays.") - else: - raise TypeError("'scalar' must be a Python scalar") - - # Note: the spec only specifies integer-dtype/int promotion - # behavior for integers within the bounds of the integer dtype. - # Outside of those bounds we use the default NumPy behavior (either - # cast or raise OverflowError). - return Array._new(np.array(scalar, self.dtype)) - - @staticmethod - def _normalize_two_args(x1, x2): - """ - Normalize inputs to two arg functions to fix type promotion rules - - NumPy deviates from the spec type promotion rules in cases where one - argument is 0-dimensional and the other is not. For example: - - >>> import numpy as np - >>> a = np.array([1.0], dtype=np.float32) - >>> b = np.array(1.0, dtype=np.float64) - >>> np.add(a, b) # The spec says this should be float64 - array([2.], dtype=float32) - - To fix this, we add a dimension to the 0-dimension array before passing it - through. This works because a dimension would be added anyway from - broadcasting, so the resulting shape is the same, but this prevents NumPy - from not promoting the dtype. - """ - # Another option would be to use signature=(x1.dtype, x2.dtype, None), - # but that only works for ufuncs, so we would have to call the ufuncs - # directly in the operator methods. One should also note that this - # sort of trick wouldn't work for functions like searchsorted, which - # don't do normal broadcasting, but there aren't any functions like - # that in the array API namespace. - if x1.ndim == 0 and x2.ndim != 0: - # The _array[None] workaround was chosen because it is relatively - # performant. broadcast_to(x1._array, x2.shape) is much slower. We - # could also manually type promote x2, but that is more complicated - # and about the same performance as this. - x1 = Array._new(x1._array[None]) - elif x2.ndim == 0 and x1.ndim != 0: - x2 = Array._new(x2._array[None]) - return (x1, x2) - - # Note: A large fraction of allowed indices are disallowed here (see the - # docstring below) - @staticmethod - def _validate_index(key, shape): - """ - Validate an index according to the array API. - - The array API specification only requires a subset of indices that are - supported by NumPy. This function will reject any index that is - allowed by NumPy but not required by the array API specification. We - always raise ``IndexError`` on such indices (the spec does not require - any specific behavior on them, but this makes the NumPy array API - namespace a minimal implementation of the spec). See - https://data-apis.org/array-api/latest/API_specification/indexing.html - for the full list of required indexing behavior - - This function either raises IndexError if the index ``key`` is - invalid, or a new key to be used in place of ``key`` in indexing. It - only raises ``IndexError`` on indices that are not already rejected by - NumPy, as NumPy will already raise the appropriate error on such - indices. ``shape`` may be None, in which case, only cases that are - independent of the array shape are checked. - - The following cases are allowed by NumPy, but not specified by the array - API specification: - - - The start and stop of a slice may not be out of bounds. In - particular, for a slice ``i:j:k`` on an axis of size ``n``, only the - following are allowed: - - - ``i`` or ``j`` omitted (``None``). - - ``-n <= i <= max(0, n - 1)``. - - For ``k > 0`` or ``k`` omitted (``None``), ``-n <= j <= n``. - - For ``k < 0``, ``-n - 1 <= j <= max(0, n - 1)``. - - - Boolean array indices are not allowed as part of a larger tuple - index. - - - Integer array indices are not allowed (with the exception of 0-D - arrays, which are treated the same as scalars). - - Additionally, it should be noted that indices that would return a - scalar in NumPy will return a 0-D array. Array scalars are not allowed - in the specification, only 0-D arrays. This is done in the - ``Array._new`` constructor, not this function. - - """ - if isinstance(key, slice): - if shape is None: - return key - if shape == (): - return key - size = shape[0] - # Ensure invalid slice entries are passed through. - if key.start is not None: - try: - operator.index(key.start) - except TypeError: - return key - if not (-size <= key.start <= max(0, size - 1)): - raise IndexError("Slices with out-of-bounds start are not allowed in the array API namespace") - if key.stop is not None: - try: - operator.index(key.stop) - except TypeError: - return key - step = 1 if key.step is None else key.step - if (step > 0 and not (-size <= key.stop <= size) - or step < 0 and not (-size - 1 <= key.stop <= max(0, size - 1))): - raise IndexError("Slices with out-of-bounds stop are not allowed in the array API namespace") - return key - - elif isinstance(key, tuple): - key = tuple(Array._validate_index(idx, None) for idx in key) - - for idx in key: - if isinstance(idx, np.ndarray) and idx.dtype in _boolean_dtypes or isinstance(idx, (bool, np.bool_)): - if len(key) == 1: - return key - raise IndexError("Boolean array indices combined with other indices are not allowed in the array API namespace") - if isinstance(idx, tuple): - raise IndexError("Nested tuple indices are not allowed in the array API namespace") - - if shape is None: - return key - n_ellipsis = key.count(...) - if n_ellipsis > 1: - return key - ellipsis_i = key.index(...) if n_ellipsis else len(key) - - for idx, size in list(zip(key[:ellipsis_i], shape)) + list(zip(key[:ellipsis_i:-1], shape[:ellipsis_i:-1])): - Array._validate_index(idx, (size,)) - return key - elif isinstance(key, bool): - return key - elif isinstance(key, Array): - if key.dtype in _integer_dtypes: - if key.ndim != 0: - raise IndexError("Non-zero dimensional integer array indices are not allowed in the array API namespace") - return key._array - elif key is Ellipsis: - return key - elif key is None: - raise IndexError("newaxis indices are not allowed in the array API namespace") - try: - return operator.index(key) - except TypeError: - # Note: This also omits boolean arrays that are not already in - # Array() form, like a list of booleans. - raise IndexError("Only integers, slices (`:`), ellipsis (`...`), and boolean arrays are valid indices in the array API namespace") - - # Everything below this line is required by the spec. - - def __abs__(self: Array, /) -> Array: - """ - Performs the operation __abs__. - """ - if self.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in __abs__') - res = self._array.__abs__() - return self.__class__._new(res) - - def __add__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __add__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__add__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__add__(other._array) - return self.__class__._new(res) - - def __and__(self: Array, other: Union[int, bool, Array], /) -> Array: - """ - Performs the operation __and__. - """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__and__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__and__(other._array) - return self.__class__._new(res) - - def __array_namespace__(self: Array, /, *, api_version: Optional[str] = None) -> object: - if api_version is not None and not api_version.startswith('2021.'): - raise ValueError(f"Unrecognized array API version: {api_version!r}") - from numpy import _array_api - return _array_api - - def __bool__(self: Array, /) -> bool: - """ - Performs the operation __bool__. - """ - # Note: This is an error here. - if self._array.ndim != 0: - raise TypeError("bool is only allowed on arrays with 0 dimensions") - res = self._array.__bool__() - return res - - def __dlpack__(self: Array, /, *, stream: None = None) -> PyCapsule: - """ - Performs the operation __dlpack__. - """ - res = self._array.__dlpack__(stream=stream) - return self.__class__._new(res) - - def __dlpack_device__(self: Array, /) -> Tuple[IntEnum, int]: - """ - Performs the operation __dlpack_device__. - """ - # Note: device support is required for this - res = self._array.__dlpack_device__() - return self.__class__._new(res) - - def __eq__(self: Array, other: Union[int, float, bool, Array], /) -> Array: - """ - Performs the operation __eq__. - """ - # Even though "all" dtypes are allowed, we still require them to be - # promotable with each other. - other = self._check_allowed_dtypes(other, 'all', '__eq__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__eq__(other._array) - return self.__class__._new(res) - - def __float__(self: Array, /) -> float: - """ - Performs the operation __float__. - """ - # Note: This is an error here. - if self._array.ndim != 0: - raise TypeError("float is only allowed on arrays with 0 dimensions") - res = self._array.__float__() - return res - - def __floordiv__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __floordiv__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__floordiv__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__floordiv__(other._array) - return self.__class__._new(res) - - def __ge__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __ge__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__ge__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__ge__(other._array) - return self.__class__._new(res) - - def __getitem__(self: Array, key: Union[int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], Array], /) -> Array: - """ - Performs the operation __getitem__. - """ - # Note: Only indices required by the spec are allowed. See the - # docstring of _validate_index - key = self._validate_index(key, self.shape) - res = self._array.__getitem__(key) - return self._new(res) - - def __gt__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __gt__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__gt__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__gt__(other._array) - return self.__class__._new(res) - - def __int__(self: Array, /) -> int: - """ - Performs the operation __int__. - """ - # Note: This is an error here. - if self._array.ndim != 0: - raise TypeError("int is only allowed on arrays with 0 dimensions") - res = self._array.__int__() - return res - - def __invert__(self: Array, /) -> Array: - """ - Performs the operation __invert__. - """ - if self.dtype not in _integer_or_boolean_dtypes: - raise TypeError('Only integer or boolean dtypes are allowed in __invert__') - res = self._array.__invert__() - return self.__class__._new(res) - - def __le__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __le__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__le__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__le__(other._array) - return self.__class__._new(res) - - # Note: __len__ may end up being removed from the array API spec. - def __len__(self, /) -> int: - """ - Performs the operation __len__. - """ - res = self._array.__len__() - return self.__class__._new(res) - - def __lshift__(self: Array, other: Union[int, Array], /) -> Array: - """ - Performs the operation __lshift__. - """ - other = self._check_allowed_dtypes(other, 'integer', '__lshift__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__lshift__(other._array) - return self.__class__._new(res) - - def __lt__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __lt__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__lt__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__lt__(other._array) - return self.__class__._new(res) - - def __matmul__(self: Array, other: Array, /) -> Array: - """ - Performs the operation __matmul__. - """ - # matmul is not defined for scalars, but without this, we may get - # the wrong error message from asarray. - other = self._check_allowed_dtypes(other, 'numeric', '__matmul__') - if other is NotImplemented: - return other - res = self._array.__matmul__(other._array) - return self.__class__._new(res) - - def __mod__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __mod__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__mod__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__mod__(other._array) - return self.__class__._new(res) - - def __mul__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __mul__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__mul__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__mul__(other._array) - return self.__class__._new(res) - - def __ne__(self: Array, other: Union[int, float, bool, Array], /) -> Array: - """ - Performs the operation __ne__. - """ - other = self._check_allowed_dtypes(other, 'all', '__ne__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__ne__(other._array) - return self.__class__._new(res) - - def __neg__(self: Array, /) -> Array: - """ - Performs the operation __neg__. - """ - if self.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in __neg__') - res = self._array.__neg__() - return self.__class__._new(res) - - def __or__(self: Array, other: Union[int, bool, Array], /) -> Array: - """ - Performs the operation __or__. - """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__or__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__or__(other._array) - return self.__class__._new(res) - - def __pos__(self: Array, /) -> Array: - """ - Performs the operation __pos__. - """ - if self.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in __pos__') - res = self._array.__pos__() - return self.__class__._new(res) - - # PEP 484 requires int to be a subtype of float, but __pow__ should not - # accept int. - def __pow__(self: Array, other: Union[float, Array], /) -> Array: - """ - Performs the operation __pow__. - """ - from ._elementwise_functions import pow - - other = self._check_allowed_dtypes(other, 'floating-point', '__pow__') - if other is NotImplemented: - return other - # Note: NumPy's __pow__ does not follow type promotion rules for 0-d - # arrays, so we use pow() here instead. - return pow(self, other) - - def __rshift__(self: Array, other: Union[int, Array], /) -> Array: - """ - Performs the operation __rshift__. - """ - other = self._check_allowed_dtypes(other, 'integer', '__rshift__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__rshift__(other._array) - return self.__class__._new(res) - - def __setitem__(self, key: Union[int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], Array], value: Union[int, float, bool, Array], /) -> Array: - """ - Performs the operation __setitem__. - """ - # Note: Only indices required by the spec are allowed. See the - # docstring of _validate_index - key = self._validate_index(key, self.shape) - self._array.__setitem__(key, asarray(value)._array) - - def __sub__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __sub__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__sub__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__sub__(other._array) - return self.__class__._new(res) - - # PEP 484 requires int to be a subtype of float, but __truediv__ should - # not accept int. - def __truediv__(self: Array, other: Union[float, Array], /) -> Array: - """ - Performs the operation __truediv__. - """ - other = self._check_allowed_dtypes(other, 'floating-point', '__truediv__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__truediv__(other._array) - return self.__class__._new(res) - - def __xor__(self: Array, other: Union[int, bool, Array], /) -> Array: - """ - Performs the operation __xor__. - """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__xor__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__xor__(other._array) - return self.__class__._new(res) - - def __iadd__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __iadd__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__iadd__') - if other is NotImplemented: - return other - self._array.__iadd__(other._array) - return self - - def __radd__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __radd__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__radd__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__radd__(other._array) - return self.__class__._new(res) - - def __iand__(self: Array, other: Union[int, bool, Array], /) -> Array: - """ - Performs the operation __iand__. - """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__iand__') - if other is NotImplemented: - return other - self._array.__iand__(other._array) - return self - - def __rand__(self: Array, other: Union[int, bool, Array], /) -> Array: - """ - Performs the operation __rand__. - """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__rand__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__rand__(other._array) - return self.__class__._new(res) - - def __ifloordiv__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __ifloordiv__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__ifloordiv__') - if other is NotImplemented: - return other - self._array.__ifloordiv__(other._array) - return self - - def __rfloordiv__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __rfloordiv__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__rfloordiv__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__rfloordiv__(other._array) - return self.__class__._new(res) - - def __ilshift__(self: Array, other: Union[int, Array], /) -> Array: - """ - Performs the operation __ilshift__. - """ - other = self._check_allowed_dtypes(other, 'integer', '__ilshift__') - if other is NotImplemented: - return other - self._array.__ilshift__(other._array) - return self - - def __rlshift__(self: Array, other: Union[int, Array], /) -> Array: - """ - Performs the operation __rlshift__. - """ - other = self._check_allowed_dtypes(other, 'integer', '__rlshift__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__rlshift__(other._array) - return self.__class__._new(res) - - def __imatmul__(self: Array, other: Array, /) -> Array: - """ - Performs the operation __imatmul__. - """ - # Note: NumPy does not implement __imatmul__. - - # matmul is not defined for scalars, but without this, we may get - # the wrong error message from asarray. - other = self._check_allowed_dtypes(other, 'numeric', '__imatmul__') - if other is NotImplemented: - return other - - # __imatmul__ can only be allowed when it would not change the shape - # of self. - other_shape = other.shape - if self.shape == () or other_shape == (): - raise ValueError("@= requires at least one dimension") - if len(other_shape) == 1 or other_shape[-1] != other_shape[-2]: - raise ValueError("@= cannot change the shape of the input array") - self._array[:] = self._array.__matmul__(other._array) - return self - - def __rmatmul__(self: Array, other: Array, /) -> Array: - """ - Performs the operation __rmatmul__. - """ - # matmul is not defined for scalars, but without this, we may get - # the wrong error message from asarray. - other = self._check_allowed_dtypes(other, 'numeric', '__rmatmul__') - if other is NotImplemented: - return other - res = self._array.__rmatmul__(other._array) - return self.__class__._new(res) - - def __imod__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __imod__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__imod__') - if other is NotImplemented: - return other - self._array.__imod__(other._array) - return self - - def __rmod__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __rmod__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__rmod__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__rmod__(other._array) - return self.__class__._new(res) - - def __imul__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __imul__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__imul__') - if other is NotImplemented: - return other - self._array.__imul__(other._array) - return self - - def __rmul__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __rmul__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__rmul__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__rmul__(other._array) - return self.__class__._new(res) - - def __ior__(self: Array, other: Union[int, bool, Array], /) -> Array: - """ - Performs the operation __ior__. - """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__ior__') - if other is NotImplemented: - return other - self._array.__ior__(other._array) - return self - - def __ror__(self: Array, other: Union[int, bool, Array], /) -> Array: - """ - Performs the operation __ror__. - """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__ror__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__ror__(other._array) - return self.__class__._new(res) - - def __ipow__(self: Array, other: Union[float, Array], /) -> Array: - """ - Performs the operation __ipow__. - """ - other = self._check_allowed_dtypes(other, 'floating-point', '__ipow__') - if other is NotImplemented: - return other - self._array.__ipow__(other._array) - return self - - def __rpow__(self: Array, other: Union[float, Array], /) -> Array: - """ - Performs the operation __rpow__. - """ - from ._elementwise_functions import pow - - other = self._check_allowed_dtypes(other, 'floating-point', '__rpow__') - if other is NotImplemented: - return other - # Note: NumPy's __pow__ does not follow the spec type promotion rules - # for 0-d arrays, so we use pow() here instead. - return pow(other, self) - - def __irshift__(self: Array, other: Union[int, Array], /) -> Array: - """ - Performs the operation __irshift__. - """ - other = self._check_allowed_dtypes(other, 'integer', '__irshift__') - if other is NotImplemented: - return other - self._array.__irshift__(other._array) - return self - - def __rrshift__(self: Array, other: Union[int, Array], /) -> Array: - """ - Performs the operation __rrshift__. - """ - other = self._check_allowed_dtypes(other, 'integer', '__rrshift__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__rrshift__(other._array) - return self.__class__._new(res) - - def __isub__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __isub__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__isub__') - if other is NotImplemented: - return other - self._array.__isub__(other._array) - return self - - def __rsub__(self: Array, other: Union[int, float, Array], /) -> Array: - """ - Performs the operation __rsub__. - """ - other = self._check_allowed_dtypes(other, 'numeric', '__rsub__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__rsub__(other._array) - return self.__class__._new(res) - - def __itruediv__(self: Array, other: Union[float, Array], /) -> Array: - """ - Performs the operation __itruediv__. - """ - other = self._check_allowed_dtypes(other, 'floating-point', '__itruediv__') - if other is NotImplemented: - return other - self._array.__itruediv__(other._array) - return self - - def __rtruediv__(self: Array, other: Union[float, Array], /) -> Array: - """ - Performs the operation __rtruediv__. - """ - other = self._check_allowed_dtypes(other, 'floating-point', '__rtruediv__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__rtruediv__(other._array) - return self.__class__._new(res) - - def __ixor__(self: Array, other: Union[int, bool, Array], /) -> Array: - """ - Performs the operation __ixor__. - """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__ixor__') - if other is NotImplemented: - return other - self._array.__ixor__(other._array) - return self - - def __rxor__(self: Array, other: Union[int, bool, Array], /) -> Array: - """ - Performs the operation __rxor__. - """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__rxor__') - if other is NotImplemented: - return other - self, other = self._normalize_two_args(self, other) - res = self._array.__rxor__(other._array) - return self.__class__._new(res) - - @property - def dtype(self) -> Dtype: - """ - Array API compatible wrapper for :py:meth:`np.ndarray.dtype `. - - See its docstring for more information. - """ - return self._array.dtype - - @property - def device(self) -> Device: - return 'cpu' - - @property - def ndim(self) -> int: - """ - Array API compatible wrapper for :py:meth:`np.ndarray.ndim `. - - See its docstring for more information. - """ - return self._array.ndim - - @property - def shape(self) -> Tuple[int, ...]: - """ - Array API compatible wrapper for :py:meth:`np.ndarray.shape `. - - See its docstring for more information. - """ - return self._array.shape - - @property - def size(self) -> int: - """ - Array API compatible wrapper for :py:meth:`np.ndarray.size `. - - See its docstring for more information. - """ - return self._array.size - - @property - def T(self) -> Array: - """ - Array API compatible wrapper for :py:meth:`np.ndarray.T `. - - See its docstring for more information. - """ - return self._array.T diff --git a/numpy/_array_api/_constants.py b/numpy/_array_api/_constants.py deleted file mode 100644 index 9541941e7..000000000 --- a/numpy/_array_api/_constants.py +++ /dev/null @@ -1,6 +0,0 @@ -import numpy as np - -e = np.e -inf = np.inf -nan = np.nan -pi = np.pi diff --git a/numpy/_array_api/_creation_functions.py b/numpy/_array_api/_creation_functions.py deleted file mode 100644 index acf78056a..000000000 --- a/numpy/_array_api/_creation_functions.py +++ /dev/null @@ -1,216 +0,0 @@ -from __future__ import annotations - - -from typing import TYPE_CHECKING, List, Optional, Tuple, Union -if TYPE_CHECKING: - from ._typing import (Array, Device, Dtype, NestedSequence, - SupportsDLPack, SupportsBufferProtocol) - from collections.abc import Sequence -from ._dtypes import _all_dtypes - -import numpy as np - -def _check_valid_dtype(dtype): - # Note: Only spelling dtypes as the dtype objects is supported. - - # We use this instead of "dtype in _all_dtypes" because the dtype objects - # define equality with the sorts of things we want to disallw. - for d in (None,) + _all_dtypes: - if dtype is d: - return - raise ValueError("dtype must be one of the supported dtypes") - -def asarray(obj: Union[Array, bool, int, float, NestedSequence[bool|int|float], SupportsDLPack, SupportsBufferProtocol], /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, copy: Optional[bool] = None) -> Array: - """ - Array API compatible wrapper for :py:func:`np.asarray `. - - See its docstring for more information. - """ - # _array_object imports in this file are inside the functions to avoid - # circular imports - from ._array_object import Array - - _check_valid_dtype(dtype) - if device not in ['cpu', None]: - raise ValueError(f"Unsupported device {device!r}") - if copy is False: - # Note: copy=False is not yet implemented in np.asarray - raise NotImplementedError("copy=False is not yet implemented") - if isinstance(obj, Array) and (dtype is None or obj.dtype == dtype): - if copy is True: - return Array._new(np.array(obj._array, copy=True, dtype=dtype)) - return obj - if dtype is None and isinstance(obj, int) and (obj > 2**64 or obj < -2**63): - # Give a better error message in this case. NumPy would convert this - # to an object array. TODO: This won't handle large integers in lists. - raise OverflowError("Integer out of bounds for array dtypes") - res = np.asarray(obj, dtype=dtype) - return Array._new(res) - -def arange(start: Union[int, float], /, stop: Optional[Union[int, float]] = None, step: Union[int, float] = 1, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: - """ - Array API compatible wrapper for :py:func:`np.arange `. - - See its docstring for more information. - """ - from ._array_object import Array - - _check_valid_dtype(dtype) - if device not in ['cpu', None]: - raise ValueError(f"Unsupported device {device!r}") - return Array._new(np.arange(start, stop=stop, step=step, dtype=dtype)) - -def empty(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: - """ - Array API compatible wrapper for :py:func:`np.empty `. - - See its docstring for more information. - """ - from ._array_object import Array - - _check_valid_dtype(dtype) - if device not in ['cpu', None]: - raise ValueError(f"Unsupported device {device!r}") - return Array._new(np.empty(shape, dtype=dtype)) - -def empty_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: - """ - Array API compatible wrapper for :py:func:`np.empty_like `. - - See its docstring for more information. - """ - from ._array_object import Array - - _check_valid_dtype(dtype) - if device not in ['cpu', None]: - raise ValueError(f"Unsupported device {device!r}") - return Array._new(np.empty_like(x._array, dtype=dtype)) - -def eye(n_rows: int, n_cols: Optional[int] = None, /, *, k: Optional[int] = 0, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: - """ - Array API compatible wrapper for :py:func:`np.eye `. - - See its docstring for more information. - """ - from ._array_object import Array - - _check_valid_dtype(dtype) - if device not in ['cpu', None]: - raise ValueError(f"Unsupported device {device!r}") - return Array._new(np.eye(n_rows, M=n_cols, k=k, dtype=dtype)) - -def from_dlpack(x: object, /) -> Array: - # Note: dlpack support is not yet implemented on Array - raise NotImplementedError("DLPack support is not yet implemented") - -def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: - """ - Array API compatible wrapper for :py:func:`np.full `. - - See its docstring for more information. - """ - from ._array_object import Array - - _check_valid_dtype(dtype) - if device not in ['cpu', None]: - raise ValueError(f"Unsupported device {device!r}") - if isinstance(fill_value, Array) and fill_value.ndim == 0: - fill_value = fill_value._array - res = np.full(shape, fill_value, dtype=dtype) - if res.dtype not in _all_dtypes: - # This will happen if the fill value is not something that NumPy - # coerces to one of the acceptable dtypes. - raise TypeError("Invalid input to full") - return Array._new(res) - -def full_like(x: Array, /, fill_value: Union[int, float], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: - """ - Array API compatible wrapper for :py:func:`np.full_like `. - - See its docstring for more information. - """ - from ._array_object import Array - - _check_valid_dtype(dtype) - if device not in ['cpu', None]: - raise ValueError(f"Unsupported device {device!r}") - res = np.full_like(x._array, fill_value, dtype=dtype) - if res.dtype not in _all_dtypes: - # This will happen if the fill value is not something that NumPy - # coerces to one of the acceptable dtypes. - raise TypeError("Invalid input to full_like") - return Array._new(res) - -def linspace(start: Union[int, float], stop: Union[int, float], /, num: int, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, endpoint: bool = True) -> Array: - """ - Array API compatible wrapper for :py:func:`np.linspace `. - - See its docstring for more information. - """ - from ._array_object import Array - - _check_valid_dtype(dtype) - if device not in ['cpu', None]: - raise ValueError(f"Unsupported device {device!r}") - return Array._new(np.linspace(start, stop, num, dtype=dtype, endpoint=endpoint)) - -def meshgrid(*arrays: Sequence[Array], indexing: str = 'xy') -> List[Array, ...]: - """ - Array API compatible wrapper for :py:func:`np.meshgrid `. - - See its docstring for more information. - """ - from ._array_object import Array - return [Array._new(array) for array in np.meshgrid(*[a._array for a in arrays], indexing=indexing)] - -def ones(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: - """ - Array API compatible wrapper for :py:func:`np.ones `. - - See its docstring for more information. - """ - from ._array_object import Array - - _check_valid_dtype(dtype) - if device not in ['cpu', None]: - raise ValueError(f"Unsupported device {device!r}") - return Array._new(np.ones(shape, dtype=dtype)) - -def ones_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: - """ - Array API compatible wrapper for :py:func:`np.ones_like `. - - See its docstring for more information. - """ - from ._array_object import Array - - _check_valid_dtype(dtype) - if device not in ['cpu', None]: - raise ValueError(f"Unsupported device {device!r}") - return Array._new(np.ones_like(x._array, dtype=dtype)) - -def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: - """ - Array API compatible wrapper for :py:func:`np.zeros `. - - See its docstring for more information. - """ - from ._array_object import Array - - _check_valid_dtype(dtype) - if device not in ['cpu', None]: - raise ValueError(f"Unsupported device {device!r}") - return Array._new(np.zeros(shape, dtype=dtype)) - -def zeros_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: - """ - Array API compatible wrapper for :py:func:`np.zeros_like `. - - See its docstring for more information. - """ - from ._array_object import Array - - _check_valid_dtype(dtype) - if device not in ['cpu', None]: - raise ValueError(f"Unsupported device {device!r}") - return Array._new(np.zeros_like(x._array, dtype=dtype)) diff --git a/numpy/_array_api/_data_type_functions.py b/numpy/_array_api/_data_type_functions.py deleted file mode 100644 index 17a00cc6d..000000000 --- a/numpy/_array_api/_data_type_functions.py +++ /dev/null @@ -1,117 +0,0 @@ -from __future__ import annotations - -from ._array_object import Array -from ._dtypes import _all_dtypes, _result_type - -from dataclasses import dataclass -from typing import TYPE_CHECKING, List, Tuple, Union -if TYPE_CHECKING: - from ._typing import Dtype - from collections.abc import Sequence - -import numpy as np - -def broadcast_arrays(*arrays: Sequence[Array]) -> List[Array]: - """ - Array API compatible wrapper for :py:func:`np.broadcast_arrays `. - - See its docstring for more information. - """ - from ._array_object import Array - return [Array._new(array) for array in np.broadcast_arrays(*[a._array for a in arrays])] - -def broadcast_to(x: Array, /, shape: Tuple[int, ...]) -> Array: - """ - Array API compatible wrapper for :py:func:`np.broadcast_to `. - - See its docstring for more information. - """ - from ._array_object import Array - return Array._new(np.broadcast_to(x._array, shape)) - -def can_cast(from_: Union[Dtype, Array], to: Dtype, /) -> bool: - """ - Array API compatible wrapper for :py:func:`np.can_cast `. - - See its docstring for more information. - """ - from ._array_object import Array - if isinstance(from_, Array): - from_ = from_._array - return np.can_cast(from_, to) - -# These are internal objects for the return types of finfo and iinfo, since -# the NumPy versions contain extra data that isn't part of the spec. -@dataclass -class finfo_object: - bits: int - # Note: The types of the float data here are float, whereas in NumPy they - # are scalars of the corresponding float dtype. - eps: float - max: float - min: float - # Note: smallest_normal is part of the array API spec, but cannot be used - # until https://github.com/numpy/numpy/pull/18536 is merged. - - # smallest_normal: float - -@dataclass -class iinfo_object: - bits: int - max: int - min: int - -def finfo(type: Union[Dtype, Array], /) -> finfo_object: - """ - Array API compatible wrapper for :py:func:`np.finfo `. - - See its docstring for more information. - """ - fi = np.finfo(type) - # Note: The types of the float data here are float, whereas in NumPy they - # are scalars of the corresponding float dtype. - return finfo_object( - fi.bits, - float(fi.eps), - float(fi.max), - float(fi.min), - # TODO: Uncomment this when #18536 is merged. - # float(fi.smallest_normal), - ) - -def iinfo(type: Union[Dtype, Array], /) -> iinfo_object: - """ - Array API compatible wrapper for :py:func:`np.iinfo `. - - See its docstring for more information. - """ - ii = np.iinfo(type) - return iinfo_object(ii.bits, ii.max, ii.min) - -def result_type(*arrays_and_dtypes: Sequence[Union[Array, Dtype]]) -> Dtype: - """ - Array API compatible wrapper for :py:func:`np.result_type `. - - See its docstring for more information. - """ - # Note: we use a custom implementation that gives only the type promotions - # required by the spec rather than using np.result_type. NumPy implements - # too many extra type promotions like int64 + uint64 -> float64, and does - # value-based casting on scalar arrays. - A = [] - for a in arrays_and_dtypes: - if isinstance(a, Array): - a = a.dtype - elif isinstance(a, np.ndarray) or a not in _all_dtypes: - raise TypeError("result_type() inputs must be array_api arrays or dtypes") - A.append(a) - - if len(A) == 0: - raise ValueError("at least one array or dtype is required") - elif len(A) == 1: - return A[0] - else: - t = A[0] - for t2 in A[1:]: - t = _result_type(t, t2) - return t diff --git a/numpy/_array_api/_dtypes.py b/numpy/_array_api/_dtypes.py deleted file mode 100644 index fcdb562da..000000000 --- a/numpy/_array_api/_dtypes.py +++ /dev/null @@ -1,100 +0,0 @@ -import numpy as np - -# Note: we use dtype objects instead of dtype classes. The spec does not -# require any behavior on dtypes other than equality. -int8 = np.dtype('int8') -int16 = np.dtype('int16') -int32 = np.dtype('int32') -int64 = np.dtype('int64') -uint8 = np.dtype('uint8') -uint16 = np.dtype('uint16') -uint32 = np.dtype('uint32') -uint64 = np.dtype('uint64') -float32 = np.dtype('float32') -float64 = np.dtype('float64') -# Note: This name is changed -bool = np.dtype('bool') - -_all_dtypes = (int8, int16, int32, int64, uint8, uint16, uint32, uint64, - float32, float64, bool) -_boolean_dtypes = (bool,) -_floating_dtypes = (float32, float64) -_integer_dtypes = (int8, int16, int32, int64, uint8, uint16, uint32, uint64) -_integer_or_boolean_dtypes = (bool, int8, int16, int32, int64, uint8, uint16, uint32, uint64) -_numeric_dtypes = (float32, float64, int8, int16, int32, int64, uint8, uint16, uint32, uint64) - -# Note: the spec defines a restricted type promotion table compared to NumPy. -# In particular, cross-kind promotions like integer + float or boolean + -# integer are not allowed, even for functions that accept both kinds. -# Additionally, NumPy promotes signed integer + uint64 to float64, but this -# promotion is not allowed here. To be clear, Python scalar int objects are -# allowed to promote to floating-point dtypes, but only in array operators -# (see Array._promote_scalar) method in _array_object.py. -_promotion_table = { - (int8, int8): int8, - (int8, int16): int16, - (int8, int32): int32, - (int8, int64): int64, - (int16, int8): int16, - (int16, int16): int16, - (int16, int32): int32, - (int16, int64): int64, - (int32, int8): int32, - (int32, int16): int32, - (int32, int32): int32, - (int32, int64): int64, - (int64, int8): int64, - (int64, int16): int64, - (int64, int32): int64, - (int64, int64): int64, - (uint8, uint8): uint8, - (uint8, uint16): uint16, - (uint8, uint32): uint32, - (uint8, uint64): uint64, - (uint16, uint8): uint16, - (uint16, uint16): uint16, - (uint16, uint32): uint32, - (uint16, uint64): uint64, - (uint32, uint8): uint32, - (uint32, uint16): uint32, - (uint32, uint32): uint32, - (uint32, uint64): uint64, - (uint64, uint8): uint64, - (uint64, uint16): uint64, - (uint64, uint32): uint64, - (uint64, uint64): uint64, - (int8, uint8): int16, - (int8, uint16): int32, - (int8, uint32): int64, - (int16, uint8): int16, - (int16, uint16): int32, - (int16, uint32): int64, - (int32, uint8): int32, - (int32, uint16): int32, - (int32, uint32): int64, - (int64, uint8): int64, - (int64, uint16): int64, - (int64, uint32): int64, - (uint8, int8): int16, - (uint16, int8): int32, - (uint32, int8): int64, - (uint8, int16): int16, - (uint16, int16): int32, - (uint32, int16): int64, - (uint8, int32): int32, - (uint16, int32): int32, - (uint32, int32): int64, - (uint8, int64): int64, - (uint16, int64): int64, - (uint32, int64): int64, - (float32, float32): float32, - (float32, float64): float64, - (float64, float32): float64, - (float64, float64): float64, - (bool, bool): bool, -} - -def _result_type(type1, type2): - if (type1, type2) in _promotion_table: - return _promotion_table[type1, type2] - raise TypeError(f"{type1} and {type2} cannot be type promoted together") diff --git a/numpy/_array_api/_elementwise_functions.py b/numpy/_array_api/_elementwise_functions.py deleted file mode 100644 index 7833ebe54..000000000 --- a/numpy/_array_api/_elementwise_functions.py +++ /dev/null @@ -1,659 +0,0 @@ -from __future__ import annotations - -from ._dtypes import (_boolean_dtypes, _floating_dtypes, - _integer_dtypes, _integer_or_boolean_dtypes, - _numeric_dtypes, _result_type) -from ._array_object import Array - -import numpy as np - -def abs(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.abs `. - - See its docstring for more information. - """ - if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in abs') - return Array._new(np.abs(x._array)) - -# Note: the function name is different here -def acos(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.arccos `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in acos') - return Array._new(np.arccos(x._array)) - -# Note: the function name is different here -def acosh(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.arccosh `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in acosh') - return Array._new(np.arccosh(x._array)) - -def add(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.add `. - - See its docstring for more information. - """ - if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in add') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.add(x1._array, x2._array)) - -# Note: the function name is different here -def asin(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.arcsin `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in asin') - return Array._new(np.arcsin(x._array)) - -# Note: the function name is different here -def asinh(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.arcsinh `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in asinh') - return Array._new(np.arcsinh(x._array)) - -# Note: the function name is different here -def atan(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.arctan `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in atan') - return Array._new(np.arctan(x._array)) - -# Note: the function name is different here -def atan2(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.arctan2 `. - - See its docstring for more information. - """ - if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in atan2') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.arctan2(x1._array, x2._array)) - -# Note: the function name is different here -def atanh(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.arctanh `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in atanh') - return Array._new(np.arctanh(x._array)) - -def bitwise_and(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.bitwise_and `. - - See its docstring for more information. - """ - if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: - raise TypeError('Only integer or boolean dtypes are allowed in bitwise_and') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.bitwise_and(x1._array, x2._array)) - -# Note: the function name is different here -def bitwise_left_shift(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.left_shift `. - - See its docstring for more information. - """ - if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: - raise TypeError('Only integer dtypes are allowed in bitwise_left_shift') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - # Note: bitwise_left_shift is only defined for x2 nonnegative. - if np.any(x2._array < 0): - raise ValueError('bitwise_left_shift(x1, x2) is only defined for x2 >= 0') - return Array._new(np.left_shift(x1._array, x2._array)) - -# Note: the function name is different here -def bitwise_invert(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.invert `. - - See its docstring for more information. - """ - if x.dtype not in _integer_or_boolean_dtypes: - raise TypeError('Only integer or boolean dtypes are allowed in bitwise_invert') - return Array._new(np.invert(x._array)) - -def bitwise_or(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.bitwise_or `. - - See its docstring for more information. - """ - if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: - raise TypeError('Only integer or boolean dtypes are allowed in bitwise_or') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.bitwise_or(x1._array, x2._array)) - -# Note: the function name is different here -def bitwise_right_shift(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.right_shift `. - - See its docstring for more information. - """ - if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: - raise TypeError('Only integer dtypes are allowed in bitwise_right_shift') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - # Note: bitwise_right_shift is only defined for x2 nonnegative. - if np.any(x2._array < 0): - raise ValueError('bitwise_right_shift(x1, x2) is only defined for x2 >= 0') - return Array._new(np.right_shift(x1._array, x2._array)) - -def bitwise_xor(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.bitwise_xor `. - - See its docstring for more information. - """ - if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: - raise TypeError('Only integer or boolean dtypes are allowed in bitwise_xor') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.bitwise_xor(x1._array, x2._array)) - -def ceil(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.ceil `. - - See its docstring for more information. - """ - if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in ceil') - if x.dtype in _integer_dtypes: - # Note: The return dtype of ceil is the same as the input - return x - return Array._new(np.ceil(x._array)) - -def cos(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.cos `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in cos') - return Array._new(np.cos(x._array)) - -def cosh(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.cosh `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in cosh') - return Array._new(np.cosh(x._array)) - -def divide(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.divide `. - - See its docstring for more information. - """ - if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in divide') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.divide(x1._array, x2._array)) - -def equal(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.equal `. - - See its docstring for more information. - """ - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.equal(x1._array, x2._array)) - -def exp(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.exp `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in exp') - return Array._new(np.exp(x._array)) - -def expm1(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.expm1 `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in expm1') - return Array._new(np.expm1(x._array)) - -def floor(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.floor `. - - See its docstring for more information. - """ - if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in floor') - if x.dtype in _integer_dtypes: - # Note: The return dtype of floor is the same as the input - return x - return Array._new(np.floor(x._array)) - -def floor_divide(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.floor_divide `. - - See its docstring for more information. - """ - if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in floor_divide') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.floor_divide(x1._array, x2._array)) - -def greater(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.greater `. - - See its docstring for more information. - """ - if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in greater') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.greater(x1._array, x2._array)) - -def greater_equal(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.greater_equal `. - - See its docstring for more information. - """ - if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in greater_equal') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.greater_equal(x1._array, x2._array)) - -def isfinite(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.isfinite `. - - See its docstring for more information. - """ - if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in isfinite') - return Array._new(np.isfinite(x._array)) - -def isinf(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.isinf `. - - See its docstring for more information. - """ - if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in isinf') - return Array._new(np.isinf(x._array)) - -def isnan(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.isnan `. - - See its docstring for more information. - """ - if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in isnan') - return Array._new(np.isnan(x._array)) - -def less(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.less `. - - See its docstring for more information. - """ - if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in less') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.less(x1._array, x2._array)) - -def less_equal(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.less_equal `. - - See its docstring for more information. - """ - if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in less_equal') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.less_equal(x1._array, x2._array)) - -def log(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.log `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in log') - return Array._new(np.log(x._array)) - -def log1p(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.log1p `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in log1p') - return Array._new(np.log1p(x._array)) - -def log2(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.log2 `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in log2') - return Array._new(np.log2(x._array)) - -def log10(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.log10 `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in log10') - return Array._new(np.log10(x._array)) - -def logaddexp(x1: Array, x2: Array) -> Array: - """ - Array API compatible wrapper for :py:func:`np.logaddexp `. - - See its docstring for more information. - """ - if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in logaddexp') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.logaddexp(x1._array, x2._array)) - -def logical_and(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.logical_and `. - - See its docstring for more information. - """ - if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: - raise TypeError('Only boolean dtypes are allowed in logical_and') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.logical_and(x1._array, x2._array)) - -def logical_not(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.logical_not `. - - See its docstring for more information. - """ - if x.dtype not in _boolean_dtypes: - raise TypeError('Only boolean dtypes are allowed in logical_not') - return Array._new(np.logical_not(x._array)) - -def logical_or(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.logical_or `. - - See its docstring for more information. - """ - if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: - raise TypeError('Only boolean dtypes are allowed in logical_or') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.logical_or(x1._array, x2._array)) - -def logical_xor(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.logical_xor `. - - See its docstring for more information. - """ - if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: - raise TypeError('Only boolean dtypes are allowed in logical_xor') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.logical_xor(x1._array, x2._array)) - -def multiply(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.multiply `. - - See its docstring for more information. - """ - if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in multiply') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.multiply(x1._array, x2._array)) - -def negative(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.negative `. - - See its docstring for more information. - """ - if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in negative') - return Array._new(np.negative(x._array)) - -def not_equal(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.not_equal `. - - See its docstring for more information. - """ - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.not_equal(x1._array, x2._array)) - -def positive(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.positive `. - - See its docstring for more information. - """ - if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in positive') - return Array._new(np.positive(x._array)) - -# Note: the function name is different here -def pow(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.power `. - - See its docstring for more information. - """ - if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in pow') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.power(x1._array, x2._array)) - -def remainder(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.remainder `. - - See its docstring for more information. - """ - if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in remainder') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.remainder(x1._array, x2._array)) - -def round(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.round `. - - See its docstring for more information. - """ - if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in round') - return Array._new(np.round(x._array)) - -def sign(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.sign `. - - See its docstring for more information. - """ - if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in sign') - return Array._new(np.sign(x._array)) - -def sin(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.sin `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in sin') - return Array._new(np.sin(x._array)) - -def sinh(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.sinh `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in sinh') - return Array._new(np.sinh(x._array)) - -def square(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.square `. - - See its docstring for more information. - """ - if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in square') - return Array._new(np.square(x._array)) - -def sqrt(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.sqrt `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in sqrt') - return Array._new(np.sqrt(x._array)) - -def subtract(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.subtract `. - - See its docstring for more information. - """ - if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in subtract') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - x1, x2 = Array._normalize_two_args(x1, x2) - return Array._new(np.subtract(x1._array, x2._array)) - -def tan(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.tan `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in tan') - return Array._new(np.tan(x._array)) - -def tanh(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.tanh `. - - See its docstring for more information. - """ - if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in tanh') - return Array._new(np.tanh(x._array)) - -def trunc(x: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.trunc `. - - See its docstring for more information. - """ - if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in trunc') - if x.dtype in _integer_dtypes: - # Note: The return dtype of trunc is the same as the input - return x - return Array._new(np.trunc(x._array)) diff --git a/numpy/_array_api/_linear_algebra_functions.py b/numpy/_array_api/_linear_algebra_functions.py deleted file mode 100644 index f13f9c541..000000000 --- a/numpy/_array_api/_linear_algebra_functions.py +++ /dev/null @@ -1,58 +0,0 @@ -from __future__ import annotations - -from ._array_object import Array -from ._dtypes import _numeric_dtypes, _result_type - -from typing import Optional, Sequence, Tuple, Union - -import numpy as np - -# einsum is not yet implemented in the array API spec. - -# def einsum(): -# """ -# Array API compatible wrapper for :py:func:`np.einsum `. -# -# See its docstring for more information. -# """ -# return np.einsum() - -def matmul(x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.matmul `. - - See its docstring for more information. - """ - # Note: the restriction to numeric dtypes only is different from - # np.matmul. - if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in matmul') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - - return Array._new(np.matmul(x1._array, x2._array)) - -# Note: axes must be a tuple, unlike np.tensordot where it can be an array or array-like. -def tensordot(x1: Array, x2: Array, /, *, axes: Union[int, Tuple[Sequence[int], Sequence[int]]] = 2) -> Array: - # Note: the restriction to numeric dtypes only is different from - # np.tensordot. - if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in tensordot') - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - - return Array._new(np.tensordot(x1._array, x2._array, axes=axes)) - -def transpose(x: Array, /, *, axes: Optional[Tuple[int, ...]] = None) -> Array: - """ - Array API compatible wrapper for :py:func:`np.transpose `. - - See its docstring for more information. - """ - return Array._new(np.transpose(x._array, axes=axes)) - -# Note: vecdot is not in NumPy -def vecdot(x1: Array, x2: Array, /, *, axis: Optional[int] = None) -> Array: - if axis is None: - axis = -1 - return tensordot(x1, x2, axes=((axis,), (axis,))) diff --git a/numpy/_array_api/_manipulation_functions.py b/numpy/_array_api/_manipulation_functions.py deleted file mode 100644 index fa6344beb..000000000 --- a/numpy/_array_api/_manipulation_functions.py +++ /dev/null @@ -1,71 +0,0 @@ -from __future__ import annotations - -from ._array_object import Array -from ._data_type_functions import result_type - -from typing import List, Optional, Tuple, Union - -import numpy as np - -# Note: the function name is different here -def concat(arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: Optional[int] = 0) -> Array: - """ - Array API compatible wrapper for :py:func:`np.concatenate `. - - See its docstring for more information. - """ - arrays = tuple(a._array for a in arrays) - # Call result type here just to raise on disallowed type combinations - result_type(*arrays) - return Array._new(np.concatenate(arrays, axis=axis)) - -def expand_dims(x: Array, /, *, axis: int) -> Array: - """ - Array API compatible wrapper for :py:func:`np.expand_dims `. - - See its docstring for more information. - """ - return Array._new(np.expand_dims(x._array, axis)) - -def flip(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> Array: - """ - Array API compatible wrapper for :py:func:`np.flip `. - - See its docstring for more information. - """ - return Array._new(np.flip(x._array, axis=axis)) - -def reshape(x: Array, /, shape: Tuple[int, ...]) -> Array: - """ - Array API compatible wrapper for :py:func:`np.reshape `. - - See its docstring for more information. - """ - return Array._new(np.reshape(x._array, shape)) - -def roll(x: Array, /, shift: Union[int, Tuple[int, ...]], *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> Array: - """ - Array API compatible wrapper for :py:func:`np.roll `. - - See its docstring for more information. - """ - return Array._new(np.roll(x._array, shift, axis=axis)) - -def squeeze(x: Array, /, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> Array: - """ - Array API compatible wrapper for :py:func:`np.squeeze `. - - See its docstring for more information. - """ - return Array._new(np.squeeze(x._array, axis=axis)) - -def stack(arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: int = 0) -> Array: - """ - Array API compatible wrapper for :py:func:`np.stack `. - - See its docstring for more information. - """ - arrays = tuple(a._array for a in arrays) - # Call result type here just to raise on disallowed type combinations - result_type(*arrays) - return Array._new(np.stack(arrays, axis=axis)) diff --git a/numpy/_array_api/_searching_functions.py b/numpy/_array_api/_searching_functions.py deleted file mode 100644 index d80720850..000000000 --- a/numpy/_array_api/_searching_functions.py +++ /dev/null @@ -1,44 +0,0 @@ -from __future__ import annotations - -from ._array_object import Array -from ._dtypes import _result_type - -from typing import Optional, Tuple - -import numpy as np - -def argmax(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) -> Array: - """ - Array API compatible wrapper for :py:func:`np.argmax `. - - See its docstring for more information. - """ - # Note: this currently fails as np.argmax does not implement keepdims - return Array._new(np.asarray(np.argmax(x._array, axis=axis, keepdims=keepdims))) - -def argmin(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) -> Array: - """ - Array API compatible wrapper for :py:func:`np.argmin `. - - See its docstring for more information. - """ - # Note: this currently fails as np.argmin does not implement keepdims - return Array._new(np.asarray(np.argmin(x._array, axis=axis, keepdims=keepdims))) - -def nonzero(x: Array, /) -> Tuple[Array, ...]: - """ - Array API compatible wrapper for :py:func:`np.nonzero `. - - See its docstring for more information. - """ - return Array._new(np.nonzero(x._array)) - -def where(condition: Array, x1: Array, x2: Array, /) -> Array: - """ - Array API compatible wrapper for :py:func:`np.where `. - - See its docstring for more information. - """ - # Call result type here just to raise on disallowed type combinations - _result_type(x1.dtype, x2.dtype) - return Array._new(np.where(condition._array, x1._array, x2._array)) diff --git a/numpy/_array_api/_set_functions.py b/numpy/_array_api/_set_functions.py deleted file mode 100644 index f28c2ee72..000000000 --- a/numpy/_array_api/_set_functions.py +++ /dev/null @@ -1,15 +0,0 @@ -from __future__ import annotations - -from ._array_object import Array - -from typing import Tuple, Union - -import numpy as np - -def unique(x: Array, /, *, return_counts: bool = False, return_index: bool = False, return_inverse: bool = False) -> Union[Array, Tuple[Array, ...]]: - """ - Array API compatible wrapper for :py:func:`np.unique `. - - See its docstring for more information. - """ - return Array._new(np.unique(x._array, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse)) diff --git a/numpy/_array_api/_sorting_functions.py b/numpy/_array_api/_sorting_functions.py deleted file mode 100644 index a125e0718..000000000 --- a/numpy/_array_api/_sorting_functions.py +++ /dev/null @@ -1,31 +0,0 @@ -from __future__ import annotations - -from ._array_object import Array - -import numpy as np - -def argsort(x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True) -> Array: - """ - Array API compatible wrapper for :py:func:`np.argsort `. - - See its docstring for more information. - """ - # Note: this keyword argument is different, and the default is different. - kind = 'stable' if stable else 'quicksort' - res = np.argsort(x._array, axis=axis, kind=kind) - if descending: - res = np.flip(res, axis=axis) - return Array._new(res) - -def sort(x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True) -> Array: - """ - Array API compatible wrapper for :py:func:`np.sort `. - - See its docstring for more information. - """ - # Note: this keyword argument is different, and the default is different. - kind = 'stable' if stable else 'quicksort' - res = np.sort(x._array, axis=axis, kind=kind) - if descending: - res = np.flip(res, axis=axis) - return Array._new(res) diff --git a/numpy/_array_api/_statistical_functions.py b/numpy/_array_api/_statistical_functions.py deleted file mode 100644 index 61fc60c46..000000000 --- a/numpy/_array_api/_statistical_functions.py +++ /dev/null @@ -1,30 +0,0 @@ -from __future__ import annotations - -from ._array_object import Array - -from typing import Optional, Tuple, Union - -import numpy as np - -def max(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: - return Array._new(np.max(x._array, axis=axis, keepdims=keepdims)) - -def mean(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: - return Array._new(np.asarray(np.mean(x._array, axis=axis, keepdims=keepdims))) - -def min(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: - return Array._new(np.min(x._array, axis=axis, keepdims=keepdims)) - -def prod(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: - return Array._new(np.asarray(np.prod(x._array, axis=axis, keepdims=keepdims))) - -def std(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> Array: - # Note: the keyword argument correction is different here - return Array._new(np.asarray(np.std(x._array, axis=axis, ddof=correction, keepdims=keepdims))) - -def sum(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: - return Array._new(np.asarray(np.sum(x._array, axis=axis, keepdims=keepdims))) - -def var(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> Array: - # Note: the keyword argument correction is different here - return Array._new(np.asarray(np.var(x._array, axis=axis, ddof=correction, keepdims=keepdims))) diff --git a/numpy/_array_api/_typing.py b/numpy/_array_api/_typing.py deleted file mode 100644 index 4ff718205..000000000 --- a/numpy/_array_api/_typing.py +++ /dev/null @@ -1,26 +0,0 @@ -""" -This file defines the types for type annotations. - -These names aren't part of the module namespace, but they are used in the -annotations in the function signatures. The functions in the module are only -valid for inputs that match the given type annotations. -""" - -__all__ = ['Array', 'Device', 'Dtype', 'SupportsDLPack', - 'SupportsBufferProtocol', 'PyCapsule'] - -from typing import Any, Sequence, Type, Union - -from . import (Array, int8, int16, int32, int64, uint8, uint16, uint32, - uint64, float32, float64) - -# This should really be recursive, but that isn't supported yet. See the -# similar comment in numpy/typing/_array_like.py -NestedSequence = Sequence[Sequence[Any]] - -Device = Any -Dtype = Type[Union[[int8, int16, int32, int64, uint8, uint16, - uint32, uint64, float32, float64]]] -SupportsDLPack = Any -SupportsBufferProtocol = Any -PyCapsule = Any diff --git a/numpy/_array_api/_utility_functions.py b/numpy/_array_api/_utility_functions.py deleted file mode 100644 index f243bfe68..000000000 --- a/numpy/_array_api/_utility_functions.py +++ /dev/null @@ -1,23 +0,0 @@ -from __future__ import annotations - -from ._array_object import Array - -from typing import Optional, Tuple, Union - -import numpy as np - -def all(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: - """ - Array API compatible wrapper for :py:func:`np.all `. - - See its docstring for more information. - """ - return Array._new(np.asarray(np.all(x._array, axis=axis, keepdims=keepdims))) - -def any(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: - """ - Array API compatible wrapper for :py:func:`np.any `. - - See its docstring for more information. - """ - return Array._new(np.asarray(np.any(x._array, axis=axis, keepdims=keepdims))) diff --git a/numpy/_array_api/tests/__init__.py b/numpy/_array_api/tests/__init__.py deleted file mode 100644 index 536062e38..000000000 --- a/numpy/_array_api/tests/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -""" -Tests for the array API namespace. - -Note, full compliance with the array API can be tested with the official array API test -suite https://github.com/data-apis/array-api-tests. This test suite primarily -focuses on those things that are not tested by the official test suite. -""" diff --git a/numpy/_array_api/tests/test_array_object.py b/numpy/_array_api/tests/test_array_object.py deleted file mode 100644 index 22078bbee..000000000 --- a/numpy/_array_api/tests/test_array_object.py +++ /dev/null @@ -1,250 +0,0 @@ -from numpy.testing import assert_raises -import numpy as np - -from .. import ones, asarray, result_type -from .._dtypes import (_all_dtypes, _boolean_dtypes, _floating_dtypes, - _integer_dtypes, _integer_or_boolean_dtypes, - _numeric_dtypes, int8, int16, int32, int64, uint64) - -def test_validate_index(): - # The indexing tests in the official array API test suite test that the - # array object correctly handles the subset of indices that are required - # by the spec. But the NumPy array API implementation specifically - # disallows any index not required by the spec, via Array._validate_index. - # This test focuses on testing that non-valid indices are correctly - # rejected. See - # https://data-apis.org/array-api/latest/API_specification/indexing.html - # and the docstring of Array._validate_index for the exact indexing - # behavior that should be allowed. This does not test indices that are - # already invalid in NumPy itself because Array will generally just pass - # such indices directly to the underlying np.ndarray. - - a = ones((3, 4)) - - # Out of bounds slices are not allowed - assert_raises(IndexError, lambda: a[:4]) - assert_raises(IndexError, lambda: a[:-4]) - assert_raises(IndexError, lambda: a[:3:-1]) - assert_raises(IndexError, lambda: a[:-5:-1]) - assert_raises(IndexError, lambda: a[3:]) - assert_raises(IndexError, lambda: a[-4:]) - assert_raises(IndexError, lambda: a[3::-1]) - assert_raises(IndexError, lambda: a[-4::-1]) - - assert_raises(IndexError, lambda: a[...,:5]) - assert_raises(IndexError, lambda: a[...,:-5]) - assert_raises(IndexError, lambda: a[...,:4:-1]) - assert_raises(IndexError, lambda: a[...,:-6:-1]) - assert_raises(IndexError, lambda: a[...,4:]) - assert_raises(IndexError, lambda: a[...,-5:]) - assert_raises(IndexError, lambda: a[...,4::-1]) - assert_raises(IndexError, lambda: a[...,-5::-1]) - - # Boolean indices cannot be part of a larger tuple index - assert_raises(IndexError, lambda: a[a[:,0]==1,0]) - assert_raises(IndexError, lambda: a[a[:,0]==1,...]) - assert_raises(IndexError, lambda: a[..., a[0]==1]) - assert_raises(IndexError, lambda: a[[True, True, True]]) - assert_raises(IndexError, lambda: a[(True, True, True),]) - - # Integer array indices are not allowed (except for 0-D) - idx = asarray([[0, 1]]) - assert_raises(IndexError, lambda: a[idx]) - assert_raises(IndexError, lambda: a[idx,]) - assert_raises(IndexError, lambda: a[[0, 1]]) - assert_raises(IndexError, lambda: a[(0, 1), (0, 1)]) - assert_raises(IndexError, lambda: a[[0, 1]]) - assert_raises(IndexError, lambda: a[np.array([[0, 1]])]) - - # np.newaxis is not allowed - assert_raises(IndexError, lambda: a[None]) - assert_raises(IndexError, lambda: a[None, ...]) - assert_raises(IndexError, lambda: a[..., None]) - -def test_operators(): - # For every operator, we test that it works for the required type - # combinations and raises TypeError otherwise - binary_op_dtypes ={ - '__add__': 'numeric', - '__and__': 'integer_or_boolean', - '__eq__': 'all', - '__floordiv__': 'numeric', - '__ge__': 'numeric', - '__gt__': 'numeric', - '__le__': 'numeric', - '__lshift__': 'integer', - '__lt__': 'numeric', - '__mod__': 'numeric', - '__mul__': 'numeric', - '__ne__': 'all', - '__or__': 'integer_or_boolean', - '__pow__': 'floating', - '__rshift__': 'integer', - '__sub__': 'numeric', - '__truediv__': 'floating', - '__xor__': 'integer_or_boolean', - } - - # Recompute each time because of in-place ops - def _array_vals(): - for d in _integer_dtypes: - yield asarray(1, dtype=d) - for d in _boolean_dtypes: - yield asarray(False, dtype=d) - for d in _floating_dtypes: - yield asarray(1., dtype=d) - - for op, dtypes in binary_op_dtypes.items(): - ops = [op] - if op not in ['__eq__', '__ne__', '__le__', '__ge__', '__lt__', '__gt__']: - rop = '__r' + op[2:] - iop = '__i' + op[2:] - ops += [rop, iop] - for s in [1, 1., False]: - for _op in ops: - for a in _array_vals(): - # Test array op scalar. From the spec, the following combinations - # are supported: - - # - Python bool for a bool array dtype, - # - a Python int within the bounds of the given dtype for integer array dtypes, - # - a Python int or float for floating-point array dtypes - - # We do not do bounds checking for int scalars, but rather use the default - # NumPy behavior for casting in that case. - - if ((dtypes == "all" - or dtypes == "numeric" and a.dtype in _numeric_dtypes - or dtypes == "integer" and a.dtype in _integer_dtypes - or dtypes == "integer_or_boolean" and a.dtype in _integer_or_boolean_dtypes - or dtypes == "boolean" and a.dtype in _boolean_dtypes - or dtypes == "floating" and a.dtype in _floating_dtypes - ) - # bool is a subtype of int, which is why we avoid - # isinstance here. - and (a.dtype in _boolean_dtypes and type(s) == bool - or a.dtype in _integer_dtypes and type(s) == int - or a.dtype in _floating_dtypes and type(s) in [float, int] - )): - # Only test for no error - getattr(a, _op)(s) - else: - assert_raises(TypeError, lambda: getattr(a, _op)(s)) - - # Test array op array. - for _op in ops: - for x in _array_vals(): - for y in _array_vals(): - # See the promotion table in NEP 47 or the array - # API spec page on type promotion. Mixed kind - # promotion is not defined. - if (x.dtype == uint64 and y.dtype in [int8, int16, int32, int64] - or y.dtype == uint64 and x.dtype in [int8, int16, int32, int64] - or x.dtype in _integer_dtypes and y.dtype not in _integer_dtypes - or y.dtype in _integer_dtypes and x.dtype not in _integer_dtypes - or x.dtype in _boolean_dtypes and y.dtype not in _boolean_dtypes - or y.dtype in _boolean_dtypes and x.dtype not in _boolean_dtypes - or x.dtype in _floating_dtypes and y.dtype not in _floating_dtypes - or y.dtype in _floating_dtypes and x.dtype not in _floating_dtypes - ): - assert_raises(TypeError, lambda: getattr(x, _op)(y)) - # Ensure in-place operators only promote to the same dtype as the left operand. - elif _op.startswith('__i') and result_type(x.dtype, y.dtype) != x.dtype: - assert_raises(TypeError, lambda: getattr(x, _op)(y)) - # Ensure only those dtypes that are required for every operator are allowed. - elif (dtypes == "all" and (x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes - or x.dtype in _numeric_dtypes and y.dtype in _numeric_dtypes) - or (dtypes == "numeric" and x.dtype in _numeric_dtypes and y.dtype in _numeric_dtypes) - or dtypes == "integer" and x.dtype in _integer_dtypes and y.dtype in _numeric_dtypes - or dtypes == "integer_or_boolean" and (x.dtype in _integer_dtypes and y.dtype in _integer_dtypes - or x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes) - or dtypes == "boolean" and x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes - or dtypes == "floating" and x.dtype in _floating_dtypes and y.dtype in _floating_dtypes - ): - getattr(x, _op)(y) - else: - assert_raises(TypeError, lambda: getattr(x, _op)(y)) - - unary_op_dtypes ={ - '__abs__': 'numeric', - '__invert__': 'integer_or_boolean', - '__neg__': 'numeric', - '__pos__': 'numeric', - } - for op, dtypes in unary_op_dtypes.items(): - for a in _array_vals(): - if (dtypes == "numeric" and a.dtype in _numeric_dtypes - or dtypes == "integer_or_boolean" and a.dtype in _integer_or_boolean_dtypes - ): - # Only test for no error - getattr(a, op)() - else: - assert_raises(TypeError, lambda: getattr(a, op)()) - - # Finally, matmul() must be tested separately, because it works a bit - # different from the other operations. - def _matmul_array_vals(): - for a in _array_vals(): - yield a - for d in _all_dtypes: - yield ones((3, 4), dtype=d) - yield ones((4, 2), dtype=d) - yield ones((4, 4), dtype=d) - - # Scalars always error - for _op in ['__matmul__', '__rmatmul__', '__imatmul__']: - for s in [1, 1., False]: - for a in _matmul_array_vals(): - if (type(s) in [float, int] and a.dtype in _floating_dtypes - or type(s) == int and a.dtype in _integer_dtypes): - # Type promotion is valid, but @ is not allowed on 0-D - # inputs, so the error is a ValueError - assert_raises(ValueError, lambda: getattr(a, _op)(s)) - else: - assert_raises(TypeError, lambda: getattr(a, _op)(s)) - - for x in _matmul_array_vals(): - for y in _matmul_array_vals(): - if (x.dtype == uint64 and y.dtype in [int8, int16, int32, int64] - or y.dtype == uint64 and x.dtype in [int8, int16, int32, int64] - or x.dtype in _integer_dtypes and y.dtype not in _integer_dtypes - or y.dtype in _integer_dtypes and x.dtype not in _integer_dtypes - or x.dtype in _floating_dtypes and y.dtype not in _floating_dtypes - or y.dtype in _floating_dtypes and x.dtype not in _floating_dtypes - or x.dtype in _boolean_dtypes - or y.dtype in _boolean_dtypes - ): - assert_raises(TypeError, lambda: x.__matmul__(y)) - assert_raises(TypeError, lambda: y.__rmatmul__(x)) - assert_raises(TypeError, lambda: x.__imatmul__(y)) - elif x.shape == () or y.shape == () or x.shape[1] != y.shape[0]: - assert_raises(ValueError, lambda: x.__matmul__(y)) - assert_raises(ValueError, lambda: y.__rmatmul__(x)) - if result_type(x.dtype, y.dtype) != x.dtype: - assert_raises(TypeError, lambda: x.__imatmul__(y)) - else: - assert_raises(ValueError, lambda: x.__imatmul__(y)) - else: - x.__matmul__(y) - y.__rmatmul__(x) - if result_type(x.dtype, y.dtype) != x.dtype: - assert_raises(TypeError, lambda: x.__imatmul__(y)) - elif y.shape[0] != y.shape[1]: - # This one fails because x @ y has a different shape from x - assert_raises(ValueError, lambda: x.__imatmul__(y)) - else: - x.__imatmul__(y) - -def test_python_scalar_construtors(): - a = asarray(False) - b = asarray(0) - c = asarray(0.) - - assert bool(a) == bool(b) == bool(c) == False - assert int(a) == int(b) == int(c) == 0 - assert float(a) == float(b) == float(c) == 0. - - # bool/int/float should only be allowed on 0-D arrays. - assert_raises(TypeError, lambda: bool(asarray([False]))) - assert_raises(TypeError, lambda: int(asarray([0]))) - assert_raises(TypeError, lambda: float(asarray([0.]))) diff --git a/numpy/_array_api/tests/test_creation_functions.py b/numpy/_array_api/tests/test_creation_functions.py deleted file mode 100644 index 654f1d9b3..000000000 --- a/numpy/_array_api/tests/test_creation_functions.py +++ /dev/null @@ -1,103 +0,0 @@ -from numpy.testing import assert_raises -import numpy as np - -from .. import all -from .._creation_functions import (asarray, arange, empty, empty_like, eye, from_dlpack, full, full_like, linspace, meshgrid, ones, ones_like, zeros, zeros_like) -from .._array_object import Array -from .._dtypes import (_all_dtypes, _boolean_dtypes, _floating_dtypes, - _integer_dtypes, _integer_or_boolean_dtypes, - _numeric_dtypes, int8, int16, int32, int64, uint64) - -def test_asarray_errors(): - # Test various protections against incorrect usage - assert_raises(TypeError, lambda: Array([1])) - assert_raises(TypeError, lambda: asarray(['a'])) - assert_raises(ValueError, lambda: asarray([1.], dtype=np.float16)) - assert_raises(OverflowError, lambda: asarray(2**100)) - # Preferably this would be OverflowError - # assert_raises(OverflowError, lambda: asarray([2**100])) - assert_raises(TypeError, lambda: asarray([2**100])) - asarray([1], device='cpu') # Doesn't error - assert_raises(ValueError, lambda: asarray([1], device='gpu')) - - assert_raises(ValueError, lambda: asarray([1], dtype=int)) - assert_raises(ValueError, lambda: asarray([1], dtype='i')) - -def test_asarray_copy(): - a = asarray([1]) - b = asarray(a, copy=True) - a[0] = 0 - assert all(b[0] == 1) - assert all(a[0] == 0) - # Once copy=False is implemented, replace this with - # a = asarray([1]) - # b = asarray(a, copy=False) - # a[0] = 0 - # assert all(b[0] == 0) - assert_raises(NotImplementedError, lambda: asarray(a, copy=False)) - -def test_arange_errors(): - arange(1, device='cpu') # Doesn't error - assert_raises(ValueError, lambda: arange(1, device='gpu')) - assert_raises(ValueError, lambda: arange(1, dtype=int)) - assert_raises(ValueError, lambda: arange(1, dtype='i')) - -def test_empty_errors(): - empty((1,), device='cpu') # Doesn't error - assert_raises(ValueError, lambda: empty((1,), device='gpu')) - assert_raises(ValueError, lambda: empty((1,), dtype=int)) - assert_raises(ValueError, lambda: empty((1,), dtype='i')) - -def test_empty_like_errors(): - empty_like(asarray(1), device='cpu') # Doesn't error - assert_raises(ValueError, lambda: empty_like(asarray(1), device='gpu')) - assert_raises(ValueError, lambda: empty_like(asarray(1), dtype=int)) - assert_raises(ValueError, lambda: empty_like(asarray(1), dtype='i')) - -def test_eye_errors(): - eye(1, device='cpu') # Doesn't error - assert_raises(ValueError, lambda: eye(1, device='gpu')) - assert_raises(ValueError, lambda: eye(1, dtype=int)) - assert_raises(ValueError, lambda: eye(1, dtype='i')) - -def test_full_errors(): - full((1,), 0, device='cpu') # Doesn't error - assert_raises(ValueError, lambda: full((1,), 0, device='gpu')) - assert_raises(ValueError, lambda: full((1,), 0, dtype=int)) - assert_raises(ValueError, lambda: full((1,), 0, dtype='i')) - -def test_full_like_errors(): - full_like(asarray(1), 0, device='cpu') # Doesn't error - assert_raises(ValueError, lambda: full_like(asarray(1), 0, device='gpu')) - assert_raises(ValueError, lambda: full_like(asarray(1), 0, dtype=int)) - assert_raises(ValueError, lambda: full_like(asarray(1), 0, dtype='i')) - -def test_linspace_errors(): - linspace(0, 1, 10, device='cpu') # Doesn't error - assert_raises(ValueError, lambda: linspace(0, 1, 10, device='gpu')) - assert_raises(ValueError, lambda: linspace(0, 1, 10, dtype=float)) - assert_raises(ValueError, lambda: linspace(0, 1, 10, dtype='f')) - -def test_ones_errors(): - ones((1,), device='cpu') # Doesn't error - assert_raises(ValueError, lambda: ones((1,), device='gpu')) - assert_raises(ValueError, lambda: ones((1,), dtype=int)) - assert_raises(ValueError, lambda: ones((1,), dtype='i')) - -def test_ones_like_errors(): - ones_like(asarray(1), device='cpu') # Doesn't error - assert_raises(ValueError, lambda: ones_like(asarray(1), device='gpu')) - assert_raises(ValueError, lambda: ones_like(asarray(1), dtype=int)) - assert_raises(ValueError, lambda: ones_like(asarray(1), dtype='i')) - -def test_zeros_errors(): - zeros((1,), device='cpu') # Doesn't error - assert_raises(ValueError, lambda: zeros((1,), device='gpu')) - assert_raises(ValueError, lambda: zeros((1,), dtype=int)) - assert_raises(ValueError, lambda: zeros((1,), dtype='i')) - -def test_zeros_like_errors(): - zeros_like(asarray(1), device='cpu') # Doesn't error - assert_raises(ValueError, lambda: zeros_like(asarray(1), device='gpu')) - assert_raises(ValueError, lambda: zeros_like(asarray(1), dtype=int)) - assert_raises(ValueError, lambda: zeros_like(asarray(1), dtype='i')) diff --git a/numpy/_array_api/tests/test_elementwise_functions.py b/numpy/_array_api/tests/test_elementwise_functions.py deleted file mode 100644 index 994cb0bf0..000000000 --- a/numpy/_array_api/tests/test_elementwise_functions.py +++ /dev/null @@ -1,110 +0,0 @@ -from inspect import getfullargspec - -from numpy.testing import assert_raises - -from .. import asarray, _elementwise_functions -from .._elementwise_functions import bitwise_left_shift, bitwise_right_shift -from .._dtypes import (_all_dtypes, _boolean_dtypes, _floating_dtypes, - _integer_dtypes, _integer_or_boolean_dtypes, - _numeric_dtypes) - -def nargs(func): - return len(getfullargspec(func).args) - -def test_function_types(): - # Test that every function accepts only the required input types. We only - # test the negative cases here (error). The positive cases are tested in - # the array API test suite. - - elementwise_function_input_types = { - 'abs': 'numeric', - 'acos': 'floating', - 'acosh': 'floating', - 'add': 'numeric', - 'asin': 'floating', - 'asinh': 'floating', - 'atan': 'floating', - 'atan2': 'floating', - 'atanh': 'floating', - 'bitwise_and': 'integer_or_boolean', - 'bitwise_invert': 'integer_or_boolean', - 'bitwise_left_shift': 'integer', - 'bitwise_or': 'integer_or_boolean', - 'bitwise_right_shift': 'integer', - 'bitwise_xor': 'integer_or_boolean', - 'ceil': 'numeric', - 'cos': 'floating', - 'cosh': 'floating', - 'divide': 'floating', - 'equal': 'all', - 'exp': 'floating', - 'expm1': 'floating', - 'floor': 'numeric', - 'floor_divide': 'numeric', - 'greater': 'numeric', - 'greater_equal': 'numeric', - 'isfinite': 'numeric', - 'isinf': 'numeric', - 'isnan': 'numeric', - 'less': 'numeric', - 'less_equal': 'numeric', - 'log': 'floating', - 'logaddexp': 'floating', - 'log10': 'floating', - 'log1p': 'floating', - 'log2': 'floating', - 'logical_and': 'boolean', - 'logical_not': 'boolean', - 'logical_or': 'boolean', - 'logical_xor': 'boolean', - 'multiply': 'numeric', - 'negative': 'numeric', - 'not_equal': 'all', - 'positive': 'numeric', - 'pow': 'floating', - 'remainder': 'numeric', - 'round': 'numeric', - 'sign': 'numeric', - 'sin': 'floating', - 'sinh': 'floating', - 'sqrt': 'floating', - 'square': 'numeric', - 'subtract': 'numeric', - 'tan': 'floating', - 'tanh': 'floating', - 'trunc': 'numeric', - } - - _dtypes = { - 'all': _all_dtypes, - 'numeric': _numeric_dtypes, - 'integer': _integer_dtypes, - 'integer_or_boolean': _integer_or_boolean_dtypes, - 'boolean': _boolean_dtypes, - 'floating': _floating_dtypes, - } - - def _array_vals(): - for d in _integer_dtypes: - yield asarray(1, dtype=d) - for d in _boolean_dtypes: - yield asarray(False, dtype=d) - for d in _floating_dtypes: - yield asarray(1., dtype=d) - - for x in _array_vals(): - for func_name, types in elementwise_function_input_types.items(): - dtypes = _dtypes[types] - func = getattr(_elementwise_functions, func_name) - if nargs(func) == 2: - for y in _array_vals(): - if x.dtype not in dtypes or y.dtype not in dtypes: - assert_raises(TypeError, lambda: func(x, y)) - else: - if x.dtype not in dtypes: - assert_raises(TypeError, lambda: func(x)) - -def test_bitwise_shift_error(): - # bitwise shift functions should raise when the second argument is negative - assert_raises(ValueError, lambda: bitwise_left_shift(asarray([1, 1]), asarray([1, -1]))) - assert_raises(ValueError, lambda: bitwise_right_shift(asarray([1, 1]), asarray([1, -1]))) diff --git a/numpy/array_api/__init__.py b/numpy/array_api/__init__.py new file mode 100644 index 000000000..4650e3db8 --- /dev/null +++ b/numpy/array_api/__init__.py @@ -0,0 +1,171 @@ +""" +A NumPy sub-namespace that conforms to the Python array API standard. + +This submodule accompanies NEP 47, which proposes its inclusion in NumPy. + +This is a proof-of-concept namespace that wraps the corresponding NumPy +functions to give a conforming implementation of the Python array API standard +(https://data-apis.github.io/array-api/latest/). The standard is currently in +an RFC phase and comments on it are both welcome and encouraged. Comments +should be made either at https://github.com/data-apis/array-api or at +https://github.com/data-apis/consortium-feedback/discussions. + +NumPy already follows the proposed spec for the most part, so this module +serves mostly as a thin wrapper around it. However, NumPy also implements a +lot of behavior that is not included in the spec, so this serves as a +restricted subset of the API. Only those functions that are part of the spec +are included in this namespace, and all functions are given with the exact +signature given in the spec, including the use of position-only arguments, and +omitting any extra keyword arguments implemented by NumPy but not part of the +spec. The behavior of some functions is also modified from the NumPy behavior +to conform to the standard. Note that the underlying array object itself is +wrapped in a wrapper Array() class, but is otherwise unchanged. This submodule +is implemented in pure Python with no C extensions. + +The array API spec is designed as a "minimal API subset" and explicitly allows +libraries to include behaviors not specified by it. But users of this module +that intend to write portable code should be aware that only those behaviors +that are listed in the spec are guaranteed to be implemented across libraries. +Consequently, the NumPy implementation was chosen to be both conforming and +minimal, so that users can use this implementation of the array API namespace +and be sure that behaviors that it defines will be available in conforming +namespaces from other libraries. + +A few notes about the current state of this submodule: + +- There is a test suite that tests modules against the array API standard at + https://github.com/data-apis/array-api-tests. The test suite is still a work + in progress, but the existing tests pass on this module, with a few + exceptions: + + - Device support is not yet implemented in NumPy + (https://data-apis.github.io/array-api/latest/design_topics/device_support.html). + As a result, the `device` attribute of the array object is missing, and + array creation functions that take the `device` keyword argument will fail + with NotImplementedError. + + - DLPack support (see https://github.com/data-apis/array-api/pull/106) is + not included here, as it requires a full implementation in NumPy proper + first. + + - The linear algebra extension in the spec will be added in a future pull +request. + + The test suite is not yet complete, and even the tests that exist are not + guaranteed to give a comprehensive coverage of the spec. Therefore, those + reviewing this submodule should refer to the standard documents themselves. + +- There is a custom array object, numpy.array_api.Array, which is returned + by all functions in this module. All functions in the array API namespace + implicitly assume that they will only receive this object as input. The only + way to create instances of this object is to use one of the array creation + functions. It does not have a public constructor on the object itself. The + object is a small wrapper Python class around numpy.ndarray. The main + purpose of it is to restrict the namespace of the array object to only those + dtypes and only those methods that are required by the spec, as well as to + limit/change certain behavior that differs in the spec. In particular: + + - The array API namespace does not have scalar objects, only 0-d arrays. + Operations in on Array that would create a scalar in NumPy create a 0-d + array. + + - Indexing: Only a subset of indices supported by NumPy are required by the + spec. The Array object restricts indexing to only allow those types of + indices that are required by the spec. See the docstring of the + numpy.array_api.Array._validate_indices helper function for more + information. + + - Type promotion: Some type promotion rules are different in the spec. In + particular, the spec does not have any value-based casting. The + Array._promote_scalar method promotes Python scalars to arrays, + disallowing cross-type promotions like int -> float64 that are not allowed + in the spec. Array._normalize_two_args works around some type promotion + quirks in NumPy, particularly, value-based casting that occurs when one + argument of an operation is a 0-d array. + +- All functions include type annotations, corresponding to those given in the + spec (see _typing.py for definitions of some custom types). These do not + currently fully pass mypy due to some limitations in mypy. + +- Dtype objects are just the NumPy dtype objects, e.g., float64 = + np.dtype('float64'). The spec does not require any behavior on these dtype + objects other than that they be accessible by name and be comparable by + equality, but it was considered too much extra complexity to create custom + objects to represent dtypes. + +- The wrapper functions in this module do not do any type checking for things + that would be impossible without leaving the array_api namespace. For + example, since the array API dtype objects are just the NumPy dtype objects, + one could pass in a non-spec NumPy dtype into a function. + +- All places where the implementations in this submodule are known to deviate + from their corresponding functions in NumPy are marked with "# Note" + comments. Reviewers should make note of these comments. + +Still TODO in this module are: + +- Device support and DLPack support are not yet implemented. These require + support in NumPy itself first. + +- The a non-default value for the `copy` keyword argument is not yet + implemented on asarray. This requires support in numpy.asarray() first. + +- Some functions are not yet fully tested in the array API test suite, and may + require updates that are not yet known until the tests are written. + +""" + +__all__ = [] + +from ._constants import e, inf, nan, pi + +__all__ += ['e', 'inf', 'nan', 'pi'] + +from ._creation_functions import asarray, arange, empty, empty_like, eye, from_dlpack, full, full_like, linspace, meshgrid, ones, ones_like, zeros, zeros_like + +__all__ += ['asarray', 'arange', 'empty', 'empty_like', 'eye', 'from_dlpack', 'full', 'full_like', 'linspace', 'meshgrid', 'ones', 'ones_like', 'zeros', 'zeros_like'] + +from ._data_type_functions import broadcast_arrays, broadcast_to, can_cast, finfo, iinfo, result_type + +__all__ += ['broadcast_arrays', 'broadcast_to', 'can_cast', 'finfo', 'iinfo', 'result_type'] + +from ._dtypes import int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, bool + +__all__ += ['int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64', 'float32', 'float64', 'bool'] + +from ._elementwise_functions import abs, acos, acosh, add, asin, asinh, atan, atan2, atanh, bitwise_and, bitwise_left_shift, bitwise_invert, bitwise_or, bitwise_right_shift, bitwise_xor, ceil, cos, cosh, divide, equal, exp, expm1, floor, floor_divide, greater, greater_equal, isfinite, isinf, isnan, less, less_equal, log, log1p, log2, log10, logaddexp, logical_and, logical_not, logical_or, logical_xor, multiply, negative, not_equal, positive, pow, remainder, round, sign, sin, sinh, square, sqrt, subtract, tan, tanh, trunc + +__all__ += ['abs', 'acos', 'acosh', 'add', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'bitwise_and', 'bitwise_left_shift', 'bitwise_invert', 'bitwise_or', 'bitwise_right_shift', 'bitwise_xor', 'ceil', 'cos', 'cosh', 'divide', 'equal', 'exp', 'expm1', 'floor', 'floor_divide', 'greater', 'greater_equal', 'isfinite', 'isinf', 'isnan', 'less', 'less_equal', 'log', 'log1p', 'log2', 'log10', 'logaddexp', 'logical_and', 'logical_not', 'logical_or', 'logical_xor', 'multiply', 'negative', 'not_equal', 'positive', 'pow', 'remainder', 'round', 'sign', 'sin', 'sinh', 'square', 'sqrt', 'subtract', 'tan', 'tanh', 'trunc'] + +# einsum is not yet implemented in the array API spec. + +# from ._linear_algebra_functions import einsum +# __all__ += ['einsum'] + +from ._linear_algebra_functions import matmul, tensordot, transpose, vecdot + +__all__ += ['matmul', 'tensordot', 'transpose', 'vecdot'] + +from ._manipulation_functions import concat, expand_dims, flip, reshape, roll, squeeze, stack + +__all__ += ['concat', 'expand_dims', 'flip', 'reshape', 'roll', 'squeeze', 'stack'] + +from ._searching_functions import argmax, argmin, nonzero, where + +__all__ += ['argmax', 'argmin', 'nonzero', 'where'] + +from ._set_functions import unique + +__all__ += ['unique'] + +from ._sorting_functions import argsort, sort + +__all__ += ['argsort', 'sort'] + +from ._statistical_functions import max, mean, min, prod, std, sum, var + +__all__ += ['max', 'mean', 'min', 'prod', 'std', 'sum', 'var'] + +from ._utility_functions import all, any + +__all__ += ['all', 'any'] diff --git a/numpy/array_api/_array_object.py b/numpy/array_api/_array_object.py new file mode 100644 index 000000000..24957fde6 --- /dev/null +++ b/numpy/array_api/_array_object.py @@ -0,0 +1,983 @@ +""" +Wrapper class around the ndarray object for the array API standard. + +The array API standard defines some behaviors differently than ndarray, in +particular, type promotion rules are different (the standard has no +value-based casting). The standard also specifies a more limited subset of +array methods and functionalities than are implemented on ndarray. Since the +goal of the array_api namespace is to be a minimal implementation of the array +API standard, we need to define a separate wrapper class for the array_api +namespace. + +The standard compliant class is only a wrapper class. It is *not* a subclass +of ndarray. +""" + +from __future__ import annotations + +import operator +from enum import IntEnum +from ._creation_functions import asarray +from ._dtypes import (_all_dtypes, _boolean_dtypes, _integer_dtypes, + _integer_or_boolean_dtypes, _floating_dtypes, _numeric_dtypes) + +from typing import TYPE_CHECKING, Any, Optional, Tuple, Union +if TYPE_CHECKING: + from ._typing import PyCapsule, Device, Dtype + +import numpy as np + +class Array: + """ + n-d array object for the array API namespace. + + See the docstring of :py:obj:`np.ndarray ` for more + information. + + This is a wrapper around numpy.ndarray that restricts the usage to only + those things that are required by the array API namespace. Note, + attributes on this object that start with a single underscore are not part + of the API specification and should only be used internally. This object + should not be constructed directly. Rather, use one of the creation + functions, such as asarray(). + + """ + # Use a custom constructor instead of __init__, as manually initializing + # this class is not supported API. + @classmethod + def _new(cls, x, /): + """ + This is a private method for initializing the array API Array + object. + + Functions outside of the array_api submodule should not use this + method. Use one of the creation functions instead, such as + ``asarray``. + + """ + obj = super().__new__(cls) + # Note: The spec does not have array scalars, only 0-D arrays. + if isinstance(x, np.generic): + # Convert the array scalar to a 0-D array + x = np.asarray(x) + if x.dtype not in _all_dtypes: + raise TypeError(f"The array_api namespace does not support the dtype '{x.dtype}'") + obj._array = x + return obj + + # Prevent Array() from working + def __new__(cls, *args, **kwargs): + raise TypeError("The array_api Array object should not be instantiated directly. Use an array creation function, such as asarray(), instead.") + + # These functions are not required by the spec, but are implemented for + # the sake of usability. + + def __str__(self: Array, /) -> str: + """ + Performs the operation __str__. + """ + return self._array.__str__().replace('array', 'Array') + + def __repr__(self: Array, /) -> str: + """ + Performs the operation __repr__. + """ + return f"Array({np.array2string(self._array, separator=', ')}, dtype={self.dtype.name})" + + # These are various helper functions to make the array behavior match the + # spec in places where it either deviates from or is more strict than + # NumPy behavior + + def _check_allowed_dtypes(self, other, dtype_category, op): + """ + Helper function for operators to only allow specific input dtypes + + Use like + + other = self._check_allowed_dtypes(other, 'numeric', '__add__') + if other is NotImplemented: + return other + """ + from ._dtypes import _result_type + + _dtypes = { + 'all': _all_dtypes, + 'numeric': _numeric_dtypes, + 'integer': _integer_dtypes, + 'integer or boolean': _integer_or_boolean_dtypes, + 'boolean': _boolean_dtypes, + 'floating-point': _floating_dtypes, + } + + if self.dtype not in _dtypes[dtype_category]: + raise TypeError(f'Only {dtype_category} dtypes are allowed in {op}') + if isinstance(other, (int, float, bool)): + other = self._promote_scalar(other) + elif isinstance(other, Array): + if other.dtype not in _dtypes[dtype_category]: + raise TypeError(f'Only {dtype_category} dtypes are allowed in {op}') + else: + return NotImplemented + + # This will raise TypeError for type combinations that are not allowed + # to promote in the spec (even if the NumPy array operator would + # promote them). + res_dtype = _result_type(self.dtype, other.dtype) + if op.startswith('__i'): + # Note: NumPy will allow in-place operators in some cases where + # the type promoted operator does not match the left-hand side + # operand. For example, + + # >>> a = np.array(1, dtype=np.int8) + # >>> a += np.array(1, dtype=np.int16) + + # The spec explicitly disallows this. + if res_dtype != self.dtype: + raise TypeError(f"Cannot perform {op} with dtypes {self.dtype} and {other.dtype}") + + return other + + # Helper function to match the type promotion rules in the spec + def _promote_scalar(self, scalar): + """ + Returns a promoted version of a Python scalar appropriate for use with + operations on self. + + This may raise an OverflowError in cases where the scalar is an + integer that is too large to fit in a NumPy integer dtype, or + TypeError when the scalar type is incompatible with the dtype of self. + """ + if isinstance(scalar, bool): + if self.dtype not in _boolean_dtypes: + raise TypeError("Python bool scalars can only be promoted with bool arrays") + elif isinstance(scalar, int): + if self.dtype in _boolean_dtypes: + raise TypeError("Python int scalars cannot be promoted with bool arrays") + elif isinstance(scalar, float): + if self.dtype not in _floating_dtypes: + raise TypeError("Python float scalars can only be promoted with floating-point arrays.") + else: + raise TypeError("'scalar' must be a Python scalar") + + # Note: the spec only specifies integer-dtype/int promotion + # behavior for integers within the bounds of the integer dtype. + # Outside of those bounds we use the default NumPy behavior (either + # cast or raise OverflowError). + return Array._new(np.array(scalar, self.dtype)) + + @staticmethod + def _normalize_two_args(x1, x2): + """ + Normalize inputs to two arg functions to fix type promotion rules + + NumPy deviates from the spec type promotion rules in cases where one + argument is 0-dimensional and the other is not. For example: + + >>> import numpy as np + >>> a = np.array([1.0], dtype=np.float32) + >>> b = np.array(1.0, dtype=np.float64) + >>> np.add(a, b) # The spec says this should be float64 + array([2.], dtype=float32) + + To fix this, we add a dimension to the 0-dimension array before passing it + through. This works because a dimension would be added anyway from + broadcasting, so the resulting shape is the same, but this prevents NumPy + from not promoting the dtype. + """ + # Another option would be to use signature=(x1.dtype, x2.dtype, None), + # but that only works for ufuncs, so we would have to call the ufuncs + # directly in the operator methods. One should also note that this + # sort of trick wouldn't work for functions like searchsorted, which + # don't do normal broadcasting, but there aren't any functions like + # that in the array API namespace. + if x1.ndim == 0 and x2.ndim != 0: + # The _array[None] workaround was chosen because it is relatively + # performant. broadcast_to(x1._array, x2.shape) is much slower. We + # could also manually type promote x2, but that is more complicated + # and about the same performance as this. + x1 = Array._new(x1._array[None]) + elif x2.ndim == 0 and x1.ndim != 0: + x2 = Array._new(x2._array[None]) + return (x1, x2) + + # Note: A large fraction of allowed indices are disallowed here (see the + # docstring below) + @staticmethod + def _validate_index(key, shape): + """ + Validate an index according to the array API. + + The array API specification only requires a subset of indices that are + supported by NumPy. This function will reject any index that is + allowed by NumPy but not required by the array API specification. We + always raise ``IndexError`` on such indices (the spec does not require + any specific behavior on them, but this makes the NumPy array API + namespace a minimal implementation of the spec). See + https://data-apis.org/array-api/latest/API_specification/indexing.html + for the full list of required indexing behavior + + This function either raises IndexError if the index ``key`` is + invalid, or a new key to be used in place of ``key`` in indexing. It + only raises ``IndexError`` on indices that are not already rejected by + NumPy, as NumPy will already raise the appropriate error on such + indices. ``shape`` may be None, in which case, only cases that are + independent of the array shape are checked. + + The following cases are allowed by NumPy, but not specified by the array + API specification: + + - The start and stop of a slice may not be out of bounds. In + particular, for a slice ``i:j:k`` on an axis of size ``n``, only the + following are allowed: + + - ``i`` or ``j`` omitted (``None``). + - ``-n <= i <= max(0, n - 1)``. + - For ``k > 0`` or ``k`` omitted (``None``), ``-n <= j <= n``. + - For ``k < 0``, ``-n - 1 <= j <= max(0, n - 1)``. + + - Boolean array indices are not allowed as part of a larger tuple + index. + + - Integer array indices are not allowed (with the exception of 0-D + arrays, which are treated the same as scalars). + + Additionally, it should be noted that indices that would return a + scalar in NumPy will return a 0-D array. Array scalars are not allowed + in the specification, only 0-D arrays. This is done in the + ``Array._new`` constructor, not this function. + + """ + if isinstance(key, slice): + if shape is None: + return key + if shape == (): + return key + size = shape[0] + # Ensure invalid slice entries are passed through. + if key.start is not None: + try: + operator.index(key.start) + except TypeError: + return key + if not (-size <= key.start <= max(0, size - 1)): + raise IndexError("Slices with out-of-bounds start are not allowed in the array API namespace") + if key.stop is not None: + try: + operator.index(key.stop) + except TypeError: + return key + step = 1 if key.step is None else key.step + if (step > 0 and not (-size <= key.stop <= size) + or step < 0 and not (-size - 1 <= key.stop <= max(0, size - 1))): + raise IndexError("Slices with out-of-bounds stop are not allowed in the array API namespace") + return key + + elif isinstance(key, tuple): + key = tuple(Array._validate_index(idx, None) for idx in key) + + for idx in key: + if isinstance(idx, np.ndarray) and idx.dtype in _boolean_dtypes or isinstance(idx, (bool, np.bool_)): + if len(key) == 1: + return key + raise IndexError("Boolean array indices combined with other indices are not allowed in the array API namespace") + if isinstance(idx, tuple): + raise IndexError("Nested tuple indices are not allowed in the array API namespace") + + if shape is None: + return key + n_ellipsis = key.count(...) + if n_ellipsis > 1: + return key + ellipsis_i = key.index(...) if n_ellipsis else len(key) + + for idx, size in list(zip(key[:ellipsis_i], shape)) + list(zip(key[:ellipsis_i:-1], shape[:ellipsis_i:-1])): + Array._validate_index(idx, (size,)) + return key + elif isinstance(key, bool): + return key + elif isinstance(key, Array): + if key.dtype in _integer_dtypes: + if key.ndim != 0: + raise IndexError("Non-zero dimensional integer array indices are not allowed in the array API namespace") + return key._array + elif key is Ellipsis: + return key + elif key is None: + raise IndexError("newaxis indices are not allowed in the array API namespace") + try: + return operator.index(key) + except TypeError: + # Note: This also omits boolean arrays that are not already in + # Array() form, like a list of booleans. + raise IndexError("Only integers, slices (`:`), ellipsis (`...`), and boolean arrays are valid indices in the array API namespace") + + # Everything below this line is required by the spec. + + def __abs__(self: Array, /) -> Array: + """ + Performs the operation __abs__. + """ + if self.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in __abs__') + res = self._array.__abs__() + return self.__class__._new(res) + + def __add__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __add__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__add__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__add__(other._array) + return self.__class__._new(res) + + def __and__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __and__. + """ + other = self._check_allowed_dtypes(other, 'integer or boolean', '__and__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__and__(other._array) + return self.__class__._new(res) + + def __array_namespace__(self: Array, /, *, api_version: Optional[str] = None) -> object: + if api_version is not None and not api_version.startswith('2021.'): + raise ValueError(f"Unrecognized array API version: {api_version!r}") + from numpy import array_api + return array_api + + def __bool__(self: Array, /) -> bool: + """ + Performs the operation __bool__. + """ + # Note: This is an error here. + if self._array.ndim != 0: + raise TypeError("bool is only allowed on arrays with 0 dimensions") + res = self._array.__bool__() + return res + + def __dlpack__(self: Array, /, *, stream: None = None) -> PyCapsule: + """ + Performs the operation __dlpack__. + """ + res = self._array.__dlpack__(stream=stream) + return self.__class__._new(res) + + def __dlpack_device__(self: Array, /) -> Tuple[IntEnum, int]: + """ + Performs the operation __dlpack_device__. + """ + # Note: device support is required for this + res = self._array.__dlpack_device__() + return self.__class__._new(res) + + def __eq__(self: Array, other: Union[int, float, bool, Array], /) -> Array: + """ + Performs the operation __eq__. + """ + # Even though "all" dtypes are allowed, we still require them to be + # promotable with each other. + other = self._check_allowed_dtypes(other, 'all', '__eq__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__eq__(other._array) + return self.__class__._new(res) + + def __float__(self: Array, /) -> float: + """ + Performs the operation __float__. + """ + # Note: This is an error here. + if self._array.ndim != 0: + raise TypeError("float is only allowed on arrays with 0 dimensions") + res = self._array.__float__() + return res + + def __floordiv__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __floordiv__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__floordiv__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__floordiv__(other._array) + return self.__class__._new(res) + + def __ge__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __ge__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__ge__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__ge__(other._array) + return self.__class__._new(res) + + def __getitem__(self: Array, key: Union[int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], Array], /) -> Array: + """ + Performs the operation __getitem__. + """ + # Note: Only indices required by the spec are allowed. See the + # docstring of _validate_index + key = self._validate_index(key, self.shape) + res = self._array.__getitem__(key) + return self._new(res) + + def __gt__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __gt__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__gt__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__gt__(other._array) + return self.__class__._new(res) + + def __int__(self: Array, /) -> int: + """ + Performs the operation __int__. + """ + # Note: This is an error here. + if self._array.ndim != 0: + raise TypeError("int is only allowed on arrays with 0 dimensions") + res = self._array.__int__() + return res + + def __invert__(self: Array, /) -> Array: + """ + Performs the operation __invert__. + """ + if self.dtype not in _integer_or_boolean_dtypes: + raise TypeError('Only integer or boolean dtypes are allowed in __invert__') + res = self._array.__invert__() + return self.__class__._new(res) + + def __le__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __le__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__le__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__le__(other._array) + return self.__class__._new(res) + + # Note: __len__ may end up being removed from the array API spec. + def __len__(self, /) -> int: + """ + Performs the operation __len__. + """ + res = self._array.__len__() + return self.__class__._new(res) + + def __lshift__(self: Array, other: Union[int, Array], /) -> Array: + """ + Performs the operation __lshift__. + """ + other = self._check_allowed_dtypes(other, 'integer', '__lshift__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__lshift__(other._array) + return self.__class__._new(res) + + def __lt__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __lt__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__lt__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__lt__(other._array) + return self.__class__._new(res) + + def __matmul__(self: Array, other: Array, /) -> Array: + """ + Performs the operation __matmul__. + """ + # matmul is not defined for scalars, but without this, we may get + # the wrong error message from asarray. + other = self._check_allowed_dtypes(other, 'numeric', '__matmul__') + if other is NotImplemented: + return other + res = self._array.__matmul__(other._array) + return self.__class__._new(res) + + def __mod__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __mod__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__mod__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__mod__(other._array) + return self.__class__._new(res) + + def __mul__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __mul__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__mul__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__mul__(other._array) + return self.__class__._new(res) + + def __ne__(self: Array, other: Union[int, float, bool, Array], /) -> Array: + """ + Performs the operation __ne__. + """ + other = self._check_allowed_dtypes(other, 'all', '__ne__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__ne__(other._array) + return self.__class__._new(res) + + def __neg__(self: Array, /) -> Array: + """ + Performs the operation __neg__. + """ + if self.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in __neg__') + res = self._array.__neg__() + return self.__class__._new(res) + + def __or__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __or__. + """ + other = self._check_allowed_dtypes(other, 'integer or boolean', '__or__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__or__(other._array) + return self.__class__._new(res) + + def __pos__(self: Array, /) -> Array: + """ + Performs the operation __pos__. + """ + if self.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in __pos__') + res = self._array.__pos__() + return self.__class__._new(res) + + # PEP 484 requires int to be a subtype of float, but __pow__ should not + # accept int. + def __pow__(self: Array, other: Union[float, Array], /) -> Array: + """ + Performs the operation __pow__. + """ + from ._elementwise_functions import pow + + other = self._check_allowed_dtypes(other, 'floating-point', '__pow__') + if other is NotImplemented: + return other + # Note: NumPy's __pow__ does not follow type promotion rules for 0-d + # arrays, so we use pow() here instead. + return pow(self, other) + + def __rshift__(self: Array, other: Union[int, Array], /) -> Array: + """ + Performs the operation __rshift__. + """ + other = self._check_allowed_dtypes(other, 'integer', '__rshift__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rshift__(other._array) + return self.__class__._new(res) + + def __setitem__(self, key: Union[int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], Array], value: Union[int, float, bool, Array], /) -> Array: + """ + Performs the operation __setitem__. + """ + # Note: Only indices required by the spec are allowed. See the + # docstring of _validate_index + key = self._validate_index(key, self.shape) + self._array.__setitem__(key, asarray(value)._array) + + def __sub__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __sub__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__sub__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__sub__(other._array) + return self.__class__._new(res) + + # PEP 484 requires int to be a subtype of float, but __truediv__ should + # not accept int. + def __truediv__(self: Array, other: Union[float, Array], /) -> Array: + """ + Performs the operation __truediv__. + """ + other = self._check_allowed_dtypes(other, 'floating-point', '__truediv__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__truediv__(other._array) + return self.__class__._new(res) + + def __xor__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __xor__. + """ + other = self._check_allowed_dtypes(other, 'integer or boolean', '__xor__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__xor__(other._array) + return self.__class__._new(res) + + def __iadd__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __iadd__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__iadd__') + if other is NotImplemented: + return other + self._array.__iadd__(other._array) + return self + + def __radd__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __radd__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__radd__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__radd__(other._array) + return self.__class__._new(res) + + def __iand__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __iand__. + """ + other = self._check_allowed_dtypes(other, 'integer or boolean', '__iand__') + if other is NotImplemented: + return other + self._array.__iand__(other._array) + return self + + def __rand__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __rand__. + """ + other = self._check_allowed_dtypes(other, 'integer or boolean', '__rand__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rand__(other._array) + return self.__class__._new(res) + + def __ifloordiv__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __ifloordiv__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__ifloordiv__') + if other is NotImplemented: + return other + self._array.__ifloordiv__(other._array) + return self + + def __rfloordiv__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __rfloordiv__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__rfloordiv__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rfloordiv__(other._array) + return self.__class__._new(res) + + def __ilshift__(self: Array, other: Union[int, Array], /) -> Array: + """ + Performs the operation __ilshift__. + """ + other = self._check_allowed_dtypes(other, 'integer', '__ilshift__') + if other is NotImplemented: + return other + self._array.__ilshift__(other._array) + return self + + def __rlshift__(self: Array, other: Union[int, Array], /) -> Array: + """ + Performs the operation __rlshift__. + """ + other = self._check_allowed_dtypes(other, 'integer', '__rlshift__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rlshift__(other._array) + return self.__class__._new(res) + + def __imatmul__(self: Array, other: Array, /) -> Array: + """ + Performs the operation __imatmul__. + """ + # Note: NumPy does not implement __imatmul__. + + # matmul is not defined for scalars, but without this, we may get + # the wrong error message from asarray. + other = self._check_allowed_dtypes(other, 'numeric', '__imatmul__') + if other is NotImplemented: + return other + + # __imatmul__ can only be allowed when it would not change the shape + # of self. + other_shape = other.shape + if self.shape == () or other_shape == (): + raise ValueError("@= requires at least one dimension") + if len(other_shape) == 1 or other_shape[-1] != other_shape[-2]: + raise ValueError("@= cannot change the shape of the input array") + self._array[:] = self._array.__matmul__(other._array) + return self + + def __rmatmul__(self: Array, other: Array, /) -> Array: + """ + Performs the operation __rmatmul__. + """ + # matmul is not defined for scalars, but without this, we may get + # the wrong error message from asarray. + other = self._check_allowed_dtypes(other, 'numeric', '__rmatmul__') + if other is NotImplemented: + return other + res = self._array.__rmatmul__(other._array) + return self.__class__._new(res) + + def __imod__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __imod__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__imod__') + if other is NotImplemented: + return other + self._array.__imod__(other._array) + return self + + def __rmod__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __rmod__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__rmod__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rmod__(other._array) + return self.__class__._new(res) + + def __imul__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __imul__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__imul__') + if other is NotImplemented: + return other + self._array.__imul__(other._array) + return self + + def __rmul__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __rmul__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__rmul__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rmul__(other._array) + return self.__class__._new(res) + + def __ior__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __ior__. + """ + other = self._check_allowed_dtypes(other, 'integer or boolean', '__ior__') + if other is NotImplemented: + return other + self._array.__ior__(other._array) + return self + + def __ror__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __ror__. + """ + other = self._check_allowed_dtypes(other, 'integer or boolean', '__ror__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__ror__(other._array) + return self.__class__._new(res) + + def __ipow__(self: Array, other: Union[float, Array], /) -> Array: + """ + Performs the operation __ipow__. + """ + other = self._check_allowed_dtypes(other, 'floating-point', '__ipow__') + if other is NotImplemented: + return other + self._array.__ipow__(other._array) + return self + + def __rpow__(self: Array, other: Union[float, Array], /) -> Array: + """ + Performs the operation __rpow__. + """ + from ._elementwise_functions import pow + + other = self._check_allowed_dtypes(other, 'floating-point', '__rpow__') + if other is NotImplemented: + return other + # Note: NumPy's __pow__ does not follow the spec type promotion rules + # for 0-d arrays, so we use pow() here instead. + return pow(other, self) + + def __irshift__(self: Array, other: Union[int, Array], /) -> Array: + """ + Performs the operation __irshift__. + """ + other = self._check_allowed_dtypes(other, 'integer', '__irshift__') + if other is NotImplemented: + return other + self._array.__irshift__(other._array) + return self + + def __rrshift__(self: Array, other: Union[int, Array], /) -> Array: + """ + Performs the operation __rrshift__. + """ + other = self._check_allowed_dtypes(other, 'integer', '__rrshift__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rrshift__(other._array) + return self.__class__._new(res) + + def __isub__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __isub__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__isub__') + if other is NotImplemented: + return other + self._array.__isub__(other._array) + return self + + def __rsub__(self: Array, other: Union[int, float, Array], /) -> Array: + """ + Performs the operation __rsub__. + """ + other = self._check_allowed_dtypes(other, 'numeric', '__rsub__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rsub__(other._array) + return self.__class__._new(res) + + def __itruediv__(self: Array, other: Union[float, Array], /) -> Array: + """ + Performs the operation __itruediv__. + """ + other = self._check_allowed_dtypes(other, 'floating-point', '__itruediv__') + if other is NotImplemented: + return other + self._array.__itruediv__(other._array) + return self + + def __rtruediv__(self: Array, other: Union[float, Array], /) -> Array: + """ + Performs the operation __rtruediv__. + """ + other = self._check_allowed_dtypes(other, 'floating-point', '__rtruediv__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rtruediv__(other._array) + return self.__class__._new(res) + + def __ixor__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __ixor__. + """ + other = self._check_allowed_dtypes(other, 'integer or boolean', '__ixor__') + if other is NotImplemented: + return other + self._array.__ixor__(other._array) + return self + + def __rxor__(self: Array, other: Union[int, bool, Array], /) -> Array: + """ + Performs the operation __rxor__. + """ + other = self._check_allowed_dtypes(other, 'integer or boolean', '__rxor__') + if other is NotImplemented: + return other + self, other = self._normalize_two_args(self, other) + res = self._array.__rxor__(other._array) + return self.__class__._new(res) + + @property + def dtype(self) -> Dtype: + """ + Array API compatible wrapper for :py:meth:`np.ndarray.dtype `. + + See its docstring for more information. + """ + return self._array.dtype + + @property + def device(self) -> Device: + return 'cpu' + + @property + def ndim(self) -> int: + """ + Array API compatible wrapper for :py:meth:`np.ndarray.ndim `. + + See its docstring for more information. + """ + return self._array.ndim + + @property + def shape(self) -> Tuple[int, ...]: + """ + Array API compatible wrapper for :py:meth:`np.ndarray.shape `. + + See its docstring for more information. + """ + return self._array.shape + + @property + def size(self) -> int: + """ + Array API compatible wrapper for :py:meth:`np.ndarray.size `. + + See its docstring for more information. + """ + return self._array.size + + @property + def T(self) -> Array: + """ + Array API compatible wrapper for :py:meth:`np.ndarray.T `. + + See its docstring for more information. + """ + return self._array.T diff --git a/numpy/array_api/_constants.py b/numpy/array_api/_constants.py new file mode 100644 index 000000000..9541941e7 --- /dev/null +++ b/numpy/array_api/_constants.py @@ -0,0 +1,6 @@ +import numpy as np + +e = np.e +inf = np.inf +nan = np.nan +pi = np.pi diff --git a/numpy/array_api/_creation_functions.py b/numpy/array_api/_creation_functions.py new file mode 100644 index 000000000..acf78056a --- /dev/null +++ b/numpy/array_api/_creation_functions.py @@ -0,0 +1,216 @@ +from __future__ import annotations + + +from typing import TYPE_CHECKING, List, Optional, Tuple, Union +if TYPE_CHECKING: + from ._typing import (Array, Device, Dtype, NestedSequence, + SupportsDLPack, SupportsBufferProtocol) + from collections.abc import Sequence +from ._dtypes import _all_dtypes + +import numpy as np + +def _check_valid_dtype(dtype): + # Note: Only spelling dtypes as the dtype objects is supported. + + # We use this instead of "dtype in _all_dtypes" because the dtype objects + # define equality with the sorts of things we want to disallw. + for d in (None,) + _all_dtypes: + if dtype is d: + return + raise ValueError("dtype must be one of the supported dtypes") + +def asarray(obj: Union[Array, bool, int, float, NestedSequence[bool|int|float], SupportsDLPack, SupportsBufferProtocol], /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, copy: Optional[bool] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.asarray `. + + See its docstring for more information. + """ + # _array_object imports in this file are inside the functions to avoid + # circular imports + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") + if copy is False: + # Note: copy=False is not yet implemented in np.asarray + raise NotImplementedError("copy=False is not yet implemented") + if isinstance(obj, Array) and (dtype is None or obj.dtype == dtype): + if copy is True: + return Array._new(np.array(obj._array, copy=True, dtype=dtype)) + return obj + if dtype is None and isinstance(obj, int) and (obj > 2**64 or obj < -2**63): + # Give a better error message in this case. NumPy would convert this + # to an object array. TODO: This won't handle large integers in lists. + raise OverflowError("Integer out of bounds for array dtypes") + res = np.asarray(obj, dtype=dtype) + return Array._new(res) + +def arange(start: Union[int, float], /, stop: Optional[Union[int, float]] = None, step: Union[int, float] = 1, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.arange `. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.arange(start, stop=stop, step=step, dtype=dtype)) + +def empty(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.empty `. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.empty(shape, dtype=dtype)) + +def empty_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.empty_like `. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.empty_like(x._array, dtype=dtype)) + +def eye(n_rows: int, n_cols: Optional[int] = None, /, *, k: Optional[int] = 0, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.eye `. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.eye(n_rows, M=n_cols, k=k, dtype=dtype)) + +def from_dlpack(x: object, /) -> Array: + # Note: dlpack support is not yet implemented on Array + raise NotImplementedError("DLPack support is not yet implemented") + +def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.full `. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") + if isinstance(fill_value, Array) and fill_value.ndim == 0: + fill_value = fill_value._array + res = np.full(shape, fill_value, dtype=dtype) + if res.dtype not in _all_dtypes: + # This will happen if the fill value is not something that NumPy + # coerces to one of the acceptable dtypes. + raise TypeError("Invalid input to full") + return Array._new(res) + +def full_like(x: Array, /, fill_value: Union[int, float], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.full_like `. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") + res = np.full_like(x._array, fill_value, dtype=dtype) + if res.dtype not in _all_dtypes: + # This will happen if the fill value is not something that NumPy + # coerces to one of the acceptable dtypes. + raise TypeError("Invalid input to full_like") + return Array._new(res) + +def linspace(start: Union[int, float], stop: Union[int, float], /, num: int, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, endpoint: bool = True) -> Array: + """ + Array API compatible wrapper for :py:func:`np.linspace `. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.linspace(start, stop, num, dtype=dtype, endpoint=endpoint)) + +def meshgrid(*arrays: Sequence[Array], indexing: str = 'xy') -> List[Array, ...]: + """ + Array API compatible wrapper for :py:func:`np.meshgrid `. + + See its docstring for more information. + """ + from ._array_object import Array + return [Array._new(array) for array in np.meshgrid(*[a._array for a in arrays], indexing=indexing)] + +def ones(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.ones `. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.ones(shape, dtype=dtype)) + +def ones_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.ones_like `. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.ones_like(x._array, dtype=dtype)) + +def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.zeros `. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.zeros(shape, dtype=dtype)) + +def zeros_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.zeros_like `. + + See its docstring for more information. + """ + from ._array_object import Array + + _check_valid_dtype(dtype) + if device not in ['cpu', None]: + raise ValueError(f"Unsupported device {device!r}") + return Array._new(np.zeros_like(x._array, dtype=dtype)) diff --git a/numpy/array_api/_data_type_functions.py b/numpy/array_api/_data_type_functions.py new file mode 100644 index 000000000..17a00cc6d --- /dev/null +++ b/numpy/array_api/_data_type_functions.py @@ -0,0 +1,117 @@ +from __future__ import annotations + +from ._array_object import Array +from ._dtypes import _all_dtypes, _result_type + +from dataclasses import dataclass +from typing import TYPE_CHECKING, List, Tuple, Union +if TYPE_CHECKING: + from ._typing import Dtype + from collections.abc import Sequence + +import numpy as np + +def broadcast_arrays(*arrays: Sequence[Array]) -> List[Array]: + """ + Array API compatible wrapper for :py:func:`np.broadcast_arrays `. + + See its docstring for more information. + """ + from ._array_object import Array + return [Array._new(array) for array in np.broadcast_arrays(*[a._array for a in arrays])] + +def broadcast_to(x: Array, /, shape: Tuple[int, ...]) -> Array: + """ + Array API compatible wrapper for :py:func:`np.broadcast_to `. + + See its docstring for more information. + """ + from ._array_object import Array + return Array._new(np.broadcast_to(x._array, shape)) + +def can_cast(from_: Union[Dtype, Array], to: Dtype, /) -> bool: + """ + Array API compatible wrapper for :py:func:`np.can_cast `. + + See its docstring for more information. + """ + from ._array_object import Array + if isinstance(from_, Array): + from_ = from_._array + return np.can_cast(from_, to) + +# These are internal objects for the return types of finfo and iinfo, since +# the NumPy versions contain extra data that isn't part of the spec. +@dataclass +class finfo_object: + bits: int + # Note: The types of the float data here are float, whereas in NumPy they + # are scalars of the corresponding float dtype. + eps: float + max: float + min: float + # Note: smallest_normal is part of the array API spec, but cannot be used + # until https://github.com/numpy/numpy/pull/18536 is merged. + + # smallest_normal: float + +@dataclass +class iinfo_object: + bits: int + max: int + min: int + +def finfo(type: Union[Dtype, Array], /) -> finfo_object: + """ + Array API compatible wrapper for :py:func:`np.finfo `. + + See its docstring for more information. + """ + fi = np.finfo(type) + # Note: The types of the float data here are float, whereas in NumPy they + # are scalars of the corresponding float dtype. + return finfo_object( + fi.bits, + float(fi.eps), + float(fi.max), + float(fi.min), + # TODO: Uncomment this when #18536 is merged. + # float(fi.smallest_normal), + ) + +def iinfo(type: Union[Dtype, Array], /) -> iinfo_object: + """ + Array API compatible wrapper for :py:func:`np.iinfo `. + + See its docstring for more information. + """ + ii = np.iinfo(type) + return iinfo_object(ii.bits, ii.max, ii.min) + +def result_type(*arrays_and_dtypes: Sequence[Union[Array, Dtype]]) -> Dtype: + """ + Array API compatible wrapper for :py:func:`np.result_type `. + + See its docstring for more information. + """ + # Note: we use a custom implementation that gives only the type promotions + # required by the spec rather than using np.result_type. NumPy implements + # too many extra type promotions like int64 + uint64 -> float64, and does + # value-based casting on scalar arrays. + A = [] + for a in arrays_and_dtypes: + if isinstance(a, Array): + a = a.dtype + elif isinstance(a, np.ndarray) or a not in _all_dtypes: + raise TypeError("result_type() inputs must be array_api arrays or dtypes") + A.append(a) + + if len(A) == 0: + raise ValueError("at least one array or dtype is required") + elif len(A) == 1: + return A[0] + else: + t = A[0] + for t2 in A[1:]: + t = _result_type(t, t2) + return t diff --git a/numpy/array_api/_dtypes.py b/numpy/array_api/_dtypes.py new file mode 100644 index 000000000..fcdb562da --- /dev/null +++ b/numpy/array_api/_dtypes.py @@ -0,0 +1,100 @@ +import numpy as np + +# Note: we use dtype objects instead of dtype classes. The spec does not +# require any behavior on dtypes other than equality. +int8 = np.dtype('int8') +int16 = np.dtype('int16') +int32 = np.dtype('int32') +int64 = np.dtype('int64') +uint8 = np.dtype('uint8') +uint16 = np.dtype('uint16') +uint32 = np.dtype('uint32') +uint64 = np.dtype('uint64') +float32 = np.dtype('float32') +float64 = np.dtype('float64') +# Note: This name is changed +bool = np.dtype('bool') + +_all_dtypes = (int8, int16, int32, int64, uint8, uint16, uint32, uint64, + float32, float64, bool) +_boolean_dtypes = (bool,) +_floating_dtypes = (float32, float64) +_integer_dtypes = (int8, int16, int32, int64, uint8, uint16, uint32, uint64) +_integer_or_boolean_dtypes = (bool, int8, int16, int32, int64, uint8, uint16, uint32, uint64) +_numeric_dtypes = (float32, float64, int8, int16, int32, int64, uint8, uint16, uint32, uint64) + +# Note: the spec defines a restricted type promotion table compared to NumPy. +# In particular, cross-kind promotions like integer + float or boolean + +# integer are not allowed, even for functions that accept both kinds. +# Additionally, NumPy promotes signed integer + uint64 to float64, but this +# promotion is not allowed here. To be clear, Python scalar int objects are +# allowed to promote to floating-point dtypes, but only in array operators +# (see Array._promote_scalar) method in _array_object.py. +_promotion_table = { + (int8, int8): int8, + (int8, int16): int16, + (int8, int32): int32, + (int8, int64): int64, + (int16, int8): int16, + (int16, int16): int16, + (int16, int32): int32, + (int16, int64): int64, + (int32, int8): int32, + (int32, int16): int32, + (int32, int32): int32, + (int32, int64): int64, + (int64, int8): int64, + (int64, int16): int64, + (int64, int32): int64, + (int64, int64): int64, + (uint8, uint8): uint8, + (uint8, uint16): uint16, + (uint8, uint32): uint32, + (uint8, uint64): uint64, + (uint16, uint8): uint16, + (uint16, uint16): uint16, + (uint16, uint32): uint32, + (uint16, uint64): uint64, + (uint32, uint8): uint32, + (uint32, uint16): uint32, + (uint32, uint32): uint32, + (uint32, uint64): uint64, + (uint64, uint8): uint64, + (uint64, uint16): uint64, + (uint64, uint32): uint64, + (uint64, uint64): uint64, + (int8, uint8): int16, + (int8, uint16): int32, + (int8, uint32): int64, + (int16, uint8): int16, + (int16, uint16): int32, + (int16, uint32): int64, + (int32, uint8): int32, + (int32, uint16): int32, + (int32, uint32): int64, + (int64, uint8): int64, + (int64, uint16): int64, + (int64, uint32): int64, + (uint8, int8): int16, + (uint16, int8): int32, + (uint32, int8): int64, + (uint8, int16): int16, + (uint16, int16): int32, + (uint32, int16): int64, + (uint8, int32): int32, + (uint16, int32): int32, + (uint32, int32): int64, + (uint8, int64): int64, + (uint16, int64): int64, + (uint32, int64): int64, + (float32, float32): float32, + (float32, float64): float64, + (float64, float32): float64, + (float64, float64): float64, + (bool, bool): bool, +} + +def _result_type(type1, type2): + if (type1, type2) in _promotion_table: + return _promotion_table[type1, type2] + raise TypeError(f"{type1} and {type2} cannot be type promoted together") diff --git a/numpy/array_api/_elementwise_functions.py b/numpy/array_api/_elementwise_functions.py new file mode 100644 index 000000000..7833ebe54 --- /dev/null +++ b/numpy/array_api/_elementwise_functions.py @@ -0,0 +1,659 @@ +from __future__ import annotations + +from ._dtypes import (_boolean_dtypes, _floating_dtypes, + _integer_dtypes, _integer_or_boolean_dtypes, + _numeric_dtypes, _result_type) +from ._array_object import Array + +import numpy as np + +def abs(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.abs `. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in abs') + return Array._new(np.abs(x._array)) + +# Note: the function name is different here +def acos(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.arccos `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in acos') + return Array._new(np.arccos(x._array)) + +# Note: the function name is different here +def acosh(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.arccosh `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in acosh') + return Array._new(np.arccosh(x._array)) + +def add(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.add `. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in add') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.add(x1._array, x2._array)) + +# Note: the function name is different here +def asin(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.arcsin `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in asin') + return Array._new(np.arcsin(x._array)) + +# Note: the function name is different here +def asinh(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.arcsinh `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in asinh') + return Array._new(np.arcsinh(x._array)) + +# Note: the function name is different here +def atan(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.arctan `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in atan') + return Array._new(np.arctan(x._array)) + +# Note: the function name is different here +def atan2(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.arctan2 `. + + See its docstring for more information. + """ + if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in atan2') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.arctan2(x1._array, x2._array)) + +# Note: the function name is different here +def atanh(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.arctanh `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in atanh') + return Array._new(np.arctanh(x._array)) + +def bitwise_and(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.bitwise_and `. + + See its docstring for more information. + """ + if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: + raise TypeError('Only integer or boolean dtypes are allowed in bitwise_and') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.bitwise_and(x1._array, x2._array)) + +# Note: the function name is different here +def bitwise_left_shift(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.left_shift `. + + See its docstring for more information. + """ + if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: + raise TypeError('Only integer dtypes are allowed in bitwise_left_shift') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + # Note: bitwise_left_shift is only defined for x2 nonnegative. + if np.any(x2._array < 0): + raise ValueError('bitwise_left_shift(x1, x2) is only defined for x2 >= 0') + return Array._new(np.left_shift(x1._array, x2._array)) + +# Note: the function name is different here +def bitwise_invert(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.invert `. + + See its docstring for more information. + """ + if x.dtype not in _integer_or_boolean_dtypes: + raise TypeError('Only integer or boolean dtypes are allowed in bitwise_invert') + return Array._new(np.invert(x._array)) + +def bitwise_or(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.bitwise_or `. + + See its docstring for more information. + """ + if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: + raise TypeError('Only integer or boolean dtypes are allowed in bitwise_or') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.bitwise_or(x1._array, x2._array)) + +# Note: the function name is different here +def bitwise_right_shift(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.right_shift `. + + See its docstring for more information. + """ + if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: + raise TypeError('Only integer dtypes are allowed in bitwise_right_shift') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + # Note: bitwise_right_shift is only defined for x2 nonnegative. + if np.any(x2._array < 0): + raise ValueError('bitwise_right_shift(x1, x2) is only defined for x2 >= 0') + return Array._new(np.right_shift(x1._array, x2._array)) + +def bitwise_xor(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.bitwise_xor `. + + See its docstring for more information. + """ + if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: + raise TypeError('Only integer or boolean dtypes are allowed in bitwise_xor') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.bitwise_xor(x1._array, x2._array)) + +def ceil(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.ceil `. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in ceil') + if x.dtype in _integer_dtypes: + # Note: The return dtype of ceil is the same as the input + return x + return Array._new(np.ceil(x._array)) + +def cos(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.cos `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in cos') + return Array._new(np.cos(x._array)) + +def cosh(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.cosh `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in cosh') + return Array._new(np.cosh(x._array)) + +def divide(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.divide `. + + See its docstring for more information. + """ + if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in divide') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.divide(x1._array, x2._array)) + +def equal(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.equal `. + + See its docstring for more information. + """ + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.equal(x1._array, x2._array)) + +def exp(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.exp `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in exp') + return Array._new(np.exp(x._array)) + +def expm1(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.expm1 `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in expm1') + return Array._new(np.expm1(x._array)) + +def floor(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.floor `. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in floor') + if x.dtype in _integer_dtypes: + # Note: The return dtype of floor is the same as the input + return x + return Array._new(np.floor(x._array)) + +def floor_divide(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.floor_divide `. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in floor_divide') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.floor_divide(x1._array, x2._array)) + +def greater(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.greater `. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in greater') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.greater(x1._array, x2._array)) + +def greater_equal(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.greater_equal `. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in greater_equal') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.greater_equal(x1._array, x2._array)) + +def isfinite(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.isfinite `. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in isfinite') + return Array._new(np.isfinite(x._array)) + +def isinf(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.isinf `. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in isinf') + return Array._new(np.isinf(x._array)) + +def isnan(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.isnan `. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in isnan') + return Array._new(np.isnan(x._array)) + +def less(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.less `. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in less') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.less(x1._array, x2._array)) + +def less_equal(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.less_equal `. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in less_equal') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.less_equal(x1._array, x2._array)) + +def log(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.log `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in log') + return Array._new(np.log(x._array)) + +def log1p(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.log1p `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in log1p') + return Array._new(np.log1p(x._array)) + +def log2(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.log2 `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in log2') + return Array._new(np.log2(x._array)) + +def log10(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.log10 `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in log10') + return Array._new(np.log10(x._array)) + +def logaddexp(x1: Array, x2: Array) -> Array: + """ + Array API compatible wrapper for :py:func:`np.logaddexp `. + + See its docstring for more information. + """ + if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in logaddexp') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.logaddexp(x1._array, x2._array)) + +def logical_and(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.logical_and `. + + See its docstring for more information. + """ + if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: + raise TypeError('Only boolean dtypes are allowed in logical_and') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.logical_and(x1._array, x2._array)) + +def logical_not(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.logical_not `. + + See its docstring for more information. + """ + if x.dtype not in _boolean_dtypes: + raise TypeError('Only boolean dtypes are allowed in logical_not') + return Array._new(np.logical_not(x._array)) + +def logical_or(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.logical_or `. + + See its docstring for more information. + """ + if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: + raise TypeError('Only boolean dtypes are allowed in logical_or') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.logical_or(x1._array, x2._array)) + +def logical_xor(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.logical_xor `. + + See its docstring for more information. + """ + if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: + raise TypeError('Only boolean dtypes are allowed in logical_xor') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.logical_xor(x1._array, x2._array)) + +def multiply(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.multiply `. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in multiply') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.multiply(x1._array, x2._array)) + +def negative(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.negative `. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in negative') + return Array._new(np.negative(x._array)) + +def not_equal(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.not_equal `. + + See its docstring for more information. + """ + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.not_equal(x1._array, x2._array)) + +def positive(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.positive `. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in positive') + return Array._new(np.positive(x._array)) + +# Note: the function name is different here +def pow(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.power `. + + See its docstring for more information. + """ + if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in pow') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.power(x1._array, x2._array)) + +def remainder(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.remainder `. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in remainder') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.remainder(x1._array, x2._array)) + +def round(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.round `. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in round') + return Array._new(np.round(x._array)) + +def sign(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.sign `. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in sign') + return Array._new(np.sign(x._array)) + +def sin(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.sin `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in sin') + return Array._new(np.sin(x._array)) + +def sinh(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.sinh `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in sinh') + return Array._new(np.sinh(x._array)) + +def square(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.square `. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in square') + return Array._new(np.square(x._array)) + +def sqrt(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.sqrt `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in sqrt') + return Array._new(np.sqrt(x._array)) + +def subtract(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.subtract `. + + See its docstring for more information. + """ + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in subtract') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + x1, x2 = Array._normalize_two_args(x1, x2) + return Array._new(np.subtract(x1._array, x2._array)) + +def tan(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.tan `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in tan') + return Array._new(np.tan(x._array)) + +def tanh(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.tanh `. + + See its docstring for more information. + """ + if x.dtype not in _floating_dtypes: + raise TypeError('Only floating-point dtypes are allowed in tanh') + return Array._new(np.tanh(x._array)) + +def trunc(x: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.trunc `. + + See its docstring for more information. + """ + if x.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in trunc') + if x.dtype in _integer_dtypes: + # Note: The return dtype of trunc is the same as the input + return x + return Array._new(np.trunc(x._array)) diff --git a/numpy/array_api/_linear_algebra_functions.py b/numpy/array_api/_linear_algebra_functions.py new file mode 100644 index 000000000..f13f9c541 --- /dev/null +++ b/numpy/array_api/_linear_algebra_functions.py @@ -0,0 +1,58 @@ +from __future__ import annotations + +from ._array_object import Array +from ._dtypes import _numeric_dtypes, _result_type + +from typing import Optional, Sequence, Tuple, Union + +import numpy as np + +# einsum is not yet implemented in the array API spec. + +# def einsum(): +# """ +# Array API compatible wrapper for :py:func:`np.einsum `. +# +# See its docstring for more information. +# """ +# return np.einsum() + +def matmul(x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.matmul `. + + See its docstring for more information. + """ + # Note: the restriction to numeric dtypes only is different from + # np.matmul. + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in matmul') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + + return Array._new(np.matmul(x1._array, x2._array)) + +# Note: axes must be a tuple, unlike np.tensordot where it can be an array or array-like. +def tensordot(x1: Array, x2: Array, /, *, axes: Union[int, Tuple[Sequence[int], Sequence[int]]] = 2) -> Array: + # Note: the restriction to numeric dtypes only is different from + # np.tensordot. + if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: + raise TypeError('Only numeric dtypes are allowed in tensordot') + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + + return Array._new(np.tensordot(x1._array, x2._array, axes=axes)) + +def transpose(x: Array, /, *, axes: Optional[Tuple[int, ...]] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.transpose `. + + See its docstring for more information. + """ + return Array._new(np.transpose(x._array, axes=axes)) + +# Note: vecdot is not in NumPy +def vecdot(x1: Array, x2: Array, /, *, axis: Optional[int] = None) -> Array: + if axis is None: + axis = -1 + return tensordot(x1, x2, axes=((axis,), (axis,))) diff --git a/numpy/array_api/_manipulation_functions.py b/numpy/array_api/_manipulation_functions.py new file mode 100644 index 000000000..fa6344beb --- /dev/null +++ b/numpy/array_api/_manipulation_functions.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +from ._array_object import Array +from ._data_type_functions import result_type + +from typing import List, Optional, Tuple, Union + +import numpy as np + +# Note: the function name is different here +def concat(arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: Optional[int] = 0) -> Array: + """ + Array API compatible wrapper for :py:func:`np.concatenate `. + + See its docstring for more information. + """ + arrays = tuple(a._array for a in arrays) + # Call result type here just to raise on disallowed type combinations + result_type(*arrays) + return Array._new(np.concatenate(arrays, axis=axis)) + +def expand_dims(x: Array, /, *, axis: int) -> Array: + """ + Array API compatible wrapper for :py:func:`np.expand_dims `. + + See its docstring for more information. + """ + return Array._new(np.expand_dims(x._array, axis)) + +def flip(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.flip `. + + See its docstring for more information. + """ + return Array._new(np.flip(x._array, axis=axis)) + +def reshape(x: Array, /, shape: Tuple[int, ...]) -> Array: + """ + Array API compatible wrapper for :py:func:`np.reshape `. + + See its docstring for more information. + """ + return Array._new(np.reshape(x._array, shape)) + +def roll(x: Array, /, shift: Union[int, Tuple[int, ...]], *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.roll `. + + See its docstring for more information. + """ + return Array._new(np.roll(x._array, shift, axis=axis)) + +def squeeze(x: Array, /, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> Array: + """ + Array API compatible wrapper for :py:func:`np.squeeze `. + + See its docstring for more information. + """ + return Array._new(np.squeeze(x._array, axis=axis)) + +def stack(arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: int = 0) -> Array: + """ + Array API compatible wrapper for :py:func:`np.stack `. + + See its docstring for more information. + """ + arrays = tuple(a._array for a in arrays) + # Call result type here just to raise on disallowed type combinations + result_type(*arrays) + return Array._new(np.stack(arrays, axis=axis)) diff --git a/numpy/array_api/_searching_functions.py b/numpy/array_api/_searching_functions.py new file mode 100644 index 000000000..d80720850 --- /dev/null +++ b/numpy/array_api/_searching_functions.py @@ -0,0 +1,44 @@ +from __future__ import annotations + +from ._array_object import Array +from ._dtypes import _result_type + +from typing import Optional, Tuple + +import numpy as np + +def argmax(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) -> Array: + """ + Array API compatible wrapper for :py:func:`np.argmax `. + + See its docstring for more information. + """ + # Note: this currently fails as np.argmax does not implement keepdims + return Array._new(np.asarray(np.argmax(x._array, axis=axis, keepdims=keepdims))) + +def argmin(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) -> Array: + """ + Array API compatible wrapper for :py:func:`np.argmin `. + + See its docstring for more information. + """ + # Note: this currently fails as np.argmin does not implement keepdims + return Array._new(np.asarray(np.argmin(x._array, axis=axis, keepdims=keepdims))) + +def nonzero(x: Array, /) -> Tuple[Array, ...]: + """ + Array API compatible wrapper for :py:func:`np.nonzero `. + + See its docstring for more information. + """ + return Array._new(np.nonzero(x._array)) + +def where(condition: Array, x1: Array, x2: Array, /) -> Array: + """ + Array API compatible wrapper for :py:func:`np.where `. + + See its docstring for more information. + """ + # Call result type here just to raise on disallowed type combinations + _result_type(x1.dtype, x2.dtype) + return Array._new(np.where(condition._array, x1._array, x2._array)) diff --git a/numpy/array_api/_set_functions.py b/numpy/array_api/_set_functions.py new file mode 100644 index 000000000..f28c2ee72 --- /dev/null +++ b/numpy/array_api/_set_functions.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +from ._array_object import Array + +from typing import Tuple, Union + +import numpy as np + +def unique(x: Array, /, *, return_counts: bool = False, return_index: bool = False, return_inverse: bool = False) -> Union[Array, Tuple[Array, ...]]: + """ + Array API compatible wrapper for :py:func:`np.unique `. + + See its docstring for more information. + """ + return Array._new(np.unique(x._array, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse)) diff --git a/numpy/array_api/_sorting_functions.py b/numpy/array_api/_sorting_functions.py new file mode 100644 index 000000000..a125e0718 --- /dev/null +++ b/numpy/array_api/_sorting_functions.py @@ -0,0 +1,31 @@ +from __future__ import annotations + +from ._array_object import Array + +import numpy as np + +def argsort(x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True) -> Array: + """ + Array API compatible wrapper for :py:func:`np.argsort `. + + See its docstring for more information. + """ + # Note: this keyword argument is different, and the default is different. + kind = 'stable' if stable else 'quicksort' + res = np.argsort(x._array, axis=axis, kind=kind) + if descending: + res = np.flip(res, axis=axis) + return Array._new(res) + +def sort(x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True) -> Array: + """ + Array API compatible wrapper for :py:func:`np.sort `. + + See its docstring for more information. + """ + # Note: this keyword argument is different, and the default is different. + kind = 'stable' if stable else 'quicksort' + res = np.sort(x._array, axis=axis, kind=kind) + if descending: + res = np.flip(res, axis=axis) + return Array._new(res) diff --git a/numpy/array_api/_statistical_functions.py b/numpy/array_api/_statistical_functions.py new file mode 100644 index 000000000..61fc60c46 --- /dev/null +++ b/numpy/array_api/_statistical_functions.py @@ -0,0 +1,30 @@ +from __future__ import annotations + +from ._array_object import Array + +from typing import Optional, Tuple, Union + +import numpy as np + +def max(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: + return Array._new(np.max(x._array, axis=axis, keepdims=keepdims)) + +def mean(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: + return Array._new(np.asarray(np.mean(x._array, axis=axis, keepdims=keepdims))) + +def min(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: + return Array._new(np.min(x._array, axis=axis, keepdims=keepdims)) + +def prod(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: + return Array._new(np.asarray(np.prod(x._array, axis=axis, keepdims=keepdims))) + +def std(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> Array: + # Note: the keyword argument correction is different here + return Array._new(np.asarray(np.std(x._array, axis=axis, ddof=correction, keepdims=keepdims))) + +def sum(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: + return Array._new(np.asarray(np.sum(x._array, axis=axis, keepdims=keepdims))) + +def var(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> Array: + # Note: the keyword argument correction is different here + return Array._new(np.asarray(np.var(x._array, axis=axis, ddof=correction, keepdims=keepdims))) diff --git a/numpy/array_api/_typing.py b/numpy/array_api/_typing.py new file mode 100644 index 000000000..4ff718205 --- /dev/null +++ b/numpy/array_api/_typing.py @@ -0,0 +1,26 @@ +""" +This file defines the types for type annotations. + +These names aren't part of the module namespace, but they are used in the +annotations in the function signatures. The functions in the module are only +valid for inputs that match the given type annotations. +""" + +__all__ = ['Array', 'Device', 'Dtype', 'SupportsDLPack', + 'SupportsBufferProtocol', 'PyCapsule'] + +from typing import Any, Sequence, Type, Union + +from . import (Array, int8, int16, int32, int64, uint8, uint16, uint32, + uint64, float32, float64) + +# This should really be recursive, but that isn't supported yet. See the +# similar comment in numpy/typing/_array_like.py +NestedSequence = Sequence[Sequence[Any]] + +Device = Any +Dtype = Type[Union[[int8, int16, int32, int64, uint8, uint16, + uint32, uint64, float32, float64]]] +SupportsDLPack = Any +SupportsBufferProtocol = Any +PyCapsule = Any diff --git a/numpy/array_api/_utility_functions.py b/numpy/array_api/_utility_functions.py new file mode 100644 index 000000000..f243bfe68 --- /dev/null +++ b/numpy/array_api/_utility_functions.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from ._array_object import Array + +from typing import Optional, Tuple, Union + +import numpy as np + +def all(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: + """ + Array API compatible wrapper for :py:func:`np.all `. + + See its docstring for more information. + """ + return Array._new(np.asarray(np.all(x._array, axis=axis, keepdims=keepdims))) + +def any(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: + """ + Array API compatible wrapper for :py:func:`np.any `. + + See its docstring for more information. + """ + return Array._new(np.asarray(np.any(x._array, axis=axis, keepdims=keepdims))) diff --git a/numpy/array_api/tests/__init__.py b/numpy/array_api/tests/__init__.py new file mode 100644 index 000000000..536062e38 --- /dev/null +++ b/numpy/array_api/tests/__init__.py @@ -0,0 +1,7 @@ +""" +Tests for the array API namespace. + +Note, full compliance with the array API can be tested with the official array API test +suite https://github.com/data-apis/array-api-tests. This test suite primarily +focuses on those things that are not tested by the official test suite. +""" diff --git a/numpy/array_api/tests/test_array_object.py b/numpy/array_api/tests/test_array_object.py new file mode 100644 index 000000000..22078bbee --- /dev/null +++ b/numpy/array_api/tests/test_array_object.py @@ -0,0 +1,250 @@ +from numpy.testing import assert_raises +import numpy as np + +from .. import ones, asarray, result_type +from .._dtypes import (_all_dtypes, _boolean_dtypes, _floating_dtypes, + _integer_dtypes, _integer_or_boolean_dtypes, + _numeric_dtypes, int8, int16, int32, int64, uint64) + +def test_validate_index(): + # The indexing tests in the official array API test suite test that the + # array object correctly handles the subset of indices that are required + # by the spec. But the NumPy array API implementation specifically + # disallows any index not required by the spec, via Array._validate_index. + # This test focuses on testing that non-valid indices are correctly + # rejected. See + # https://data-apis.org/array-api/latest/API_specification/indexing.html + # and the docstring of Array._validate_index for the exact indexing + # behavior that should be allowed. This does not test indices that are + # already invalid in NumPy itself because Array will generally just pass + # such indices directly to the underlying np.ndarray. + + a = ones((3, 4)) + + # Out of bounds slices are not allowed + assert_raises(IndexError, lambda: a[:4]) + assert_raises(IndexError, lambda: a[:-4]) + assert_raises(IndexError, lambda: a[:3:-1]) + assert_raises(IndexError, lambda: a[:-5:-1]) + assert_raises(IndexError, lambda: a[3:]) + assert_raises(IndexError, lambda: a[-4:]) + assert_raises(IndexError, lambda: a[3::-1]) + assert_raises(IndexError, lambda: a[-4::-1]) + + assert_raises(IndexError, lambda: a[...,:5]) + assert_raises(IndexError, lambda: a[...,:-5]) + assert_raises(IndexError, lambda: a[...,:4:-1]) + assert_raises(IndexError, lambda: a[...,:-6:-1]) + assert_raises(IndexError, lambda: a[...,4:]) + assert_raises(IndexError, lambda: a[...,-5:]) + assert_raises(IndexError, lambda: a[...,4::-1]) + assert_raises(IndexError, lambda: a[...,-5::-1]) + + # Boolean indices cannot be part of a larger tuple index + assert_raises(IndexError, lambda: a[a[:,0]==1,0]) + assert_raises(IndexError, lambda: a[a[:,0]==1,...]) + assert_raises(IndexError, lambda: a[..., a[0]==1]) + assert_raises(IndexError, lambda: a[[True, True, True]]) + assert_raises(IndexError, lambda: a[(True, True, True),]) + + # Integer array indices are not allowed (except for 0-D) + idx = asarray([[0, 1]]) + assert_raises(IndexError, lambda: a[idx]) + assert_raises(IndexError, lambda: a[idx,]) + assert_raises(IndexError, lambda: a[[0, 1]]) + assert_raises(IndexError, lambda: a[(0, 1), (0, 1)]) + assert_raises(IndexError, lambda: a[[0, 1]]) + assert_raises(IndexError, lambda: a[np.array([[0, 1]])]) + + # np.newaxis is not allowed + assert_raises(IndexError, lambda: a[None]) + assert_raises(IndexError, lambda: a[None, ...]) + assert_raises(IndexError, lambda: a[..., None]) + +def test_operators(): + # For every operator, we test that it works for the required type + # combinations and raises TypeError otherwise + binary_op_dtypes ={ + '__add__': 'numeric', + '__and__': 'integer_or_boolean', + '__eq__': 'all', + '__floordiv__': 'numeric', + '__ge__': 'numeric', + '__gt__': 'numeric', + '__le__': 'numeric', + '__lshift__': 'integer', + '__lt__': 'numeric', + '__mod__': 'numeric', + '__mul__': 'numeric', + '__ne__': 'all', + '__or__': 'integer_or_boolean', + '__pow__': 'floating', + '__rshift__': 'integer', + '__sub__': 'numeric', + '__truediv__': 'floating', + '__xor__': 'integer_or_boolean', + } + + # Recompute each time because of in-place ops + def _array_vals(): + for d in _integer_dtypes: + yield asarray(1, dtype=d) + for d in _boolean_dtypes: + yield asarray(False, dtype=d) + for d in _floating_dtypes: + yield asarray(1., dtype=d) + + for op, dtypes in binary_op_dtypes.items(): + ops = [op] + if op not in ['__eq__', '__ne__', '__le__', '__ge__', '__lt__', '__gt__']: + rop = '__r' + op[2:] + iop = '__i' + op[2:] + ops += [rop, iop] + for s in [1, 1., False]: + for _op in ops: + for a in _array_vals(): + # Test array op scalar. From the spec, the following combinations + # are supported: + + # - Python bool for a bool array dtype, + # - a Python int within the bounds of the given dtype for integer array dtypes, + # - a Python int or float for floating-point array dtypes + + # We do not do bounds checking for int scalars, but rather use the default + # NumPy behavior for casting in that case. + + if ((dtypes == "all" + or dtypes == "numeric" and a.dtype in _numeric_dtypes + or dtypes == "integer" and a.dtype in _integer_dtypes + or dtypes == "integer_or_boolean" and a.dtype in _integer_or_boolean_dtypes + or dtypes == "boolean" and a.dtype in _boolean_dtypes + or dtypes == "floating" and a.dtype in _floating_dtypes + ) + # bool is a subtype of int, which is why we avoid + # isinstance here. + and (a.dtype in _boolean_dtypes and type(s) == bool + or a.dtype in _integer_dtypes and type(s) == int + or a.dtype in _floating_dtypes and type(s) in [float, int] + )): + # Only test for no error + getattr(a, _op)(s) + else: + assert_raises(TypeError, lambda: getattr(a, _op)(s)) + + # Test array op array. + for _op in ops: + for x in _array_vals(): + for y in _array_vals(): + # See the promotion table in NEP 47 or the array + # API spec page on type promotion. Mixed kind + # promotion is not defined. + if (x.dtype == uint64 and y.dtype in [int8, int16, int32, int64] + or y.dtype == uint64 and x.dtype in [int8, int16, int32, int64] + or x.dtype in _integer_dtypes and y.dtype not in _integer_dtypes + or y.dtype in _integer_dtypes and x.dtype not in _integer_dtypes + or x.dtype in _boolean_dtypes and y.dtype not in _boolean_dtypes + or y.dtype in _boolean_dtypes and x.dtype not in _boolean_dtypes + or x.dtype in _floating_dtypes and y.dtype not in _floating_dtypes + or y.dtype in _floating_dtypes and x.dtype not in _floating_dtypes + ): + assert_raises(TypeError, lambda: getattr(x, _op)(y)) + # Ensure in-place operators only promote to the same dtype as the left operand. + elif _op.startswith('__i') and result_type(x.dtype, y.dtype) != x.dtype: + assert_raises(TypeError, lambda: getattr(x, _op)(y)) + # Ensure only those dtypes that are required for every operator are allowed. + elif (dtypes == "all" and (x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes + or x.dtype in _numeric_dtypes and y.dtype in _numeric_dtypes) + or (dtypes == "numeric" and x.dtype in _numeric_dtypes and y.dtype in _numeric_dtypes) + or dtypes == "integer" and x.dtype in _integer_dtypes and y.dtype in _numeric_dtypes + or dtypes == "integer_or_boolean" and (x.dtype in _integer_dtypes and y.dtype in _integer_dtypes + or x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes) + or dtypes == "boolean" and x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes + or dtypes == "floating" and x.dtype in _floating_dtypes and y.dtype in _floating_dtypes + ): + getattr(x, _op)(y) + else: + assert_raises(TypeError, lambda: getattr(x, _op)(y)) + + unary_op_dtypes ={ + '__abs__': 'numeric', + '__invert__': 'integer_or_boolean', + '__neg__': 'numeric', + '__pos__': 'numeric', + } + for op, dtypes in unary_op_dtypes.items(): + for a in _array_vals(): + if (dtypes == "numeric" and a.dtype in _numeric_dtypes + or dtypes == "integer_or_boolean" and a.dtype in _integer_or_boolean_dtypes + ): + # Only test for no error + getattr(a, op)() + else: + assert_raises(TypeError, lambda: getattr(a, op)()) + + # Finally, matmul() must be tested separately, because it works a bit + # different from the other operations. + def _matmul_array_vals(): + for a in _array_vals(): + yield a + for d in _all_dtypes: + yield ones((3, 4), dtype=d) + yield ones((4, 2), dtype=d) + yield ones((4, 4), dtype=d) + + # Scalars always error + for _op in ['__matmul__', '__rmatmul__', '__imatmul__']: + for s in [1, 1., False]: + for a in _matmul_array_vals(): + if (type(s) in [float, int] and a.dtype in _floating_dtypes + or type(s) == int and a.dtype in _integer_dtypes): + # Type promotion is valid, but @ is not allowed on 0-D + # inputs, so the error is a ValueError + assert_raises(ValueError, lambda: getattr(a, _op)(s)) + else: + assert_raises(TypeError, lambda: getattr(a, _op)(s)) + + for x in _matmul_array_vals(): + for y in _matmul_array_vals(): + if (x.dtype == uint64 and y.dtype in [int8, int16, int32, int64] + or y.dtype == uint64 and x.dtype in [int8, int16, int32, int64] + or x.dtype in _integer_dtypes and y.dtype not in _integer_dtypes + or y.dtype in _integer_dtypes and x.dtype not in _integer_dtypes + or x.dtype in _floating_dtypes and y.dtype not in _floating_dtypes + or y.dtype in _floating_dtypes and x.dtype not in _floating_dtypes + or x.dtype in _boolean_dtypes + or y.dtype in _boolean_dtypes + ): + assert_raises(TypeError, lambda: x.__matmul__(y)) + assert_raises(TypeError, lambda: y.__rmatmul__(x)) + assert_raises(TypeError, lambda: x.__imatmul__(y)) + elif x.shape == () or y.shape == () or x.shape[1] != y.shape[0]: + assert_raises(ValueError, lambda: x.__matmul__(y)) + assert_raises(ValueError, lambda: y.__rmatmul__(x)) + if result_type(x.dtype, y.dtype) != x.dtype: + assert_raises(TypeError, lambda: x.__imatmul__(y)) + else: + assert_raises(ValueError, lambda: x.__imatmul__(y)) + else: + x.__matmul__(y) + y.__rmatmul__(x) + if result_type(x.dtype, y.dtype) != x.dtype: + assert_raises(TypeError, lambda: x.__imatmul__(y)) + elif y.shape[0] != y.shape[1]: + # This one fails because x @ y has a different shape from x + assert_raises(ValueError, lambda: x.__imatmul__(y)) + else: + x.__imatmul__(y) + +def test_python_scalar_construtors(): + a = asarray(False) + b = asarray(0) + c = asarray(0.) + + assert bool(a) == bool(b) == bool(c) == False + assert int(a) == int(b) == int(c) == 0 + assert float(a) == float(b) == float(c) == 0. + + # bool/int/float should only be allowed on 0-D arrays. + assert_raises(TypeError, lambda: bool(asarray([False]))) + assert_raises(TypeError, lambda: int(asarray([0]))) + assert_raises(TypeError, lambda: float(asarray([0.]))) diff --git a/numpy/array_api/tests/test_creation_functions.py b/numpy/array_api/tests/test_creation_functions.py new file mode 100644 index 000000000..654f1d9b3 --- /dev/null +++ b/numpy/array_api/tests/test_creation_functions.py @@ -0,0 +1,103 @@ +from numpy.testing import assert_raises +import numpy as np + +from .. import all +from .._creation_functions import (asarray, arange, empty, empty_like, eye, from_dlpack, full, full_like, linspace, meshgrid, ones, ones_like, zeros, zeros_like) +from .._array_object import Array +from .._dtypes import (_all_dtypes, _boolean_dtypes, _floating_dtypes, + _integer_dtypes, _integer_or_boolean_dtypes, + _numeric_dtypes, int8, int16, int32, int64, uint64) + +def test_asarray_errors(): + # Test various protections against incorrect usage + assert_raises(TypeError, lambda: Array([1])) + assert_raises(TypeError, lambda: asarray(['a'])) + assert_raises(ValueError, lambda: asarray([1.], dtype=np.float16)) + assert_raises(OverflowError, lambda: asarray(2**100)) + # Preferably this would be OverflowError + # assert_raises(OverflowError, lambda: asarray([2**100])) + assert_raises(TypeError, lambda: asarray([2**100])) + asarray([1], device='cpu') # Doesn't error + assert_raises(ValueError, lambda: asarray([1], device='gpu')) + + assert_raises(ValueError, lambda: asarray([1], dtype=int)) + assert_raises(ValueError, lambda: asarray([1], dtype='i')) + +def test_asarray_copy(): + a = asarray([1]) + b = asarray(a, copy=True) + a[0] = 0 + assert all(b[0] == 1) + assert all(a[0] == 0) + # Once copy=False is implemented, replace this with + # a = asarray([1]) + # b = asarray(a, copy=False) + # a[0] = 0 + # assert all(b[0] == 0) + assert_raises(NotImplementedError, lambda: asarray(a, copy=False)) + +def test_arange_errors(): + arange(1, device='cpu') # Doesn't error + assert_raises(ValueError, lambda: arange(1, device='gpu')) + assert_raises(ValueError, lambda: arange(1, dtype=int)) + assert_raises(ValueError, lambda: arange(1, dtype='i')) + +def test_empty_errors(): + empty((1,), device='cpu') # Doesn't error + assert_raises(ValueError, lambda: empty((1,), device='gpu')) + assert_raises(ValueError, lambda: empty((1,), dtype=int)) + assert_raises(ValueError, lambda: empty((1,), dtype='i')) + +def test_empty_like_errors(): + empty_like(asarray(1), device='cpu') # Doesn't error + assert_raises(ValueError, lambda: empty_like(asarray(1), device='gpu')) + assert_raises(ValueError, lambda: empty_like(asarray(1), dtype=int)) + assert_raises(ValueError, lambda: empty_like(asarray(1), dtype='i')) + +def test_eye_errors(): + eye(1, device='cpu') # Doesn't error + assert_raises(ValueError, lambda: eye(1, device='gpu')) + assert_raises(ValueError, lambda: eye(1, dtype=int)) + assert_raises(ValueError, lambda: eye(1, dtype='i')) + +def test_full_errors(): + full((1,), 0, device='cpu') # Doesn't error + assert_raises(ValueError, lambda: full((1,), 0, device='gpu')) + assert_raises(ValueError, lambda: full((1,), 0, dtype=int)) + assert_raises(ValueError, lambda: full((1,), 0, dtype='i')) + +def test_full_like_errors(): + full_like(asarray(1), 0, device='cpu') # Doesn't error + assert_raises(ValueError, lambda: full_like(asarray(1), 0, device='gpu')) + assert_raises(ValueError, lambda: full_like(asarray(1), 0, dtype=int)) + assert_raises(ValueError, lambda: full_like(asarray(1), 0, dtype='i')) + +def test_linspace_errors(): + linspace(0, 1, 10, device='cpu') # Doesn't error + assert_raises(ValueError, lambda: linspace(0, 1, 10, device='gpu')) + assert_raises(ValueError, lambda: linspace(0, 1, 10, dtype=float)) + assert_raises(ValueError, lambda: linspace(0, 1, 10, dtype='f')) + +def test_ones_errors(): + ones((1,), device='cpu') # Doesn't error + assert_raises(ValueError, lambda: ones((1,), device='gpu')) + assert_raises(ValueError, lambda: ones((1,), dtype=int)) + assert_raises(ValueError, lambda: ones((1,), dtype='i')) + +def test_ones_like_errors(): + ones_like(asarray(1), device='cpu') # Doesn't error + assert_raises(ValueError, lambda: ones_like(asarray(1), device='gpu')) + assert_raises(ValueError, lambda: ones_like(asarray(1), dtype=int)) + assert_raises(ValueError, lambda: ones_like(asarray(1), dtype='i')) + +def test_zeros_errors(): + zeros((1,), device='cpu') # Doesn't error + assert_raises(ValueError, lambda: zeros((1,), device='gpu')) + assert_raises(ValueError, lambda: zeros((1,), dtype=int)) + assert_raises(ValueError, lambda: zeros((1,), dtype='i')) + +def test_zeros_like_errors(): + zeros_like(asarray(1), device='cpu') # Doesn't error + assert_raises(ValueError, lambda: zeros_like(asarray(1), device='gpu')) + assert_raises(ValueError, lambda: zeros_like(asarray(1), dtype=int)) + assert_raises(ValueError, lambda: zeros_like(asarray(1), dtype='i')) diff --git a/numpy/array_api/tests/test_elementwise_functions.py b/numpy/array_api/tests/test_elementwise_functions.py new file mode 100644 index 000000000..994cb0bf0 --- /dev/null +++ b/numpy/array_api/tests/test_elementwise_functions.py @@ -0,0 +1,110 @@ +from inspect import getfullargspec + +from numpy.testing import assert_raises + +from .. import asarray, _elementwise_functions +from .._elementwise_functions import bitwise_left_shift, bitwise_right_shift +from .._dtypes import (_all_dtypes, _boolean_dtypes, _floating_dtypes, + _integer_dtypes, _integer_or_boolean_dtypes, + _numeric_dtypes) + +def nargs(func): + return len(getfullargspec(func).args) + +def test_function_types(): + # Test that every function accepts only the required input types. We only + # test the negative cases here (error). The positive cases are tested in + # the array API test suite. + + elementwise_function_input_types = { + 'abs': 'numeric', + 'acos': 'floating', + 'acosh': 'floating', + 'add': 'numeric', + 'asin': 'floating', + 'asinh': 'floating', + 'atan': 'floating', + 'atan2': 'floating', + 'atanh': 'floating', + 'bitwise_and': 'integer_or_boolean', + 'bitwise_invert': 'integer_or_boolean', + 'bitwise_left_shift': 'integer', + 'bitwise_or': 'integer_or_boolean', + 'bitwise_right_shift': 'integer', + 'bitwise_xor': 'integer_or_boolean', + 'ceil': 'numeric', + 'cos': 'floating', + 'cosh': 'floating', + 'divide': 'floating', + 'equal': 'all', + 'exp': 'floating', + 'expm1': 'floating', + 'floor': 'numeric', + 'floor_divide': 'numeric', + 'greater': 'numeric', + 'greater_equal': 'numeric', + 'isfinite': 'numeric', + 'isinf': 'numeric', + 'isnan': 'numeric', + 'less': 'numeric', + 'less_equal': 'numeric', + 'log': 'floating', + 'logaddexp': 'floating', + 'log10': 'floating', + 'log1p': 'floating', + 'log2': 'floating', + 'logical_and': 'boolean', + 'logical_not': 'boolean', + 'logical_or': 'boolean', + 'logical_xor': 'boolean', + 'multiply': 'numeric', + 'negative': 'numeric', + 'not_equal': 'all', + 'positive': 'numeric', + 'pow': 'floating', + 'remainder': 'numeric', + 'round': 'numeric', + 'sign': 'numeric', + 'sin': 'floating', + 'sinh': 'floating', + 'sqrt': 'floating', + 'square': 'numeric', + 'subtract': 'numeric', + 'tan': 'floating', + 'tanh': 'floating', + 'trunc': 'numeric', + } + + _dtypes = { + 'all': _all_dtypes, + 'numeric': _numeric_dtypes, + 'integer': _integer_dtypes, + 'integer_or_boolean': _integer_or_boolean_dtypes, + 'boolean': _boolean_dtypes, + 'floating': _floating_dtypes, + } + + def _array_vals(): + for d in _integer_dtypes: + yield asarray(1, dtype=d) + for d in _boolean_dtypes: + yield asarray(False, dtype=d) + for d in _floating_dtypes: + yield asarray(1., dtype=d) + + for x in _array_vals(): + for func_name, types in elementwise_function_input_types.items(): + dtypes = _dtypes[types] + func = getattr(_elementwise_functions, func_name) + if nargs(func) == 2: + for y in _array_vals(): + if x.dtype not in dtypes or y.dtype not in dtypes: + assert_raises(TypeError, lambda: func(x, y)) + else: + if x.dtype not in dtypes: + assert_raises(TypeError, lambda: func(x)) + +def test_bitwise_shift_error(): + # bitwise shift functions should raise when the second argument is negative + assert_raises(ValueError, lambda: bitwise_left_shift(asarray([1, 1]), asarray([1, -1]))) + assert_raises(ValueError, lambda: bitwise_right_shift(asarray([1, 1]), asarray([1, -1]))) diff --git a/numpy/setup.py b/numpy/setup.py index 82c4c8d1b..a0ca99919 100644 --- a/numpy/setup.py +++ b/numpy/setup.py @@ -4,7 +4,7 @@ def configuration(parent_package='',top_path=None): from numpy.distutils.misc_util import Configuration config = Configuration('numpy', parent_package, top_path) - config.add_subpackage('_array_api') + config.add_subpackage('array_api') config.add_subpackage('compat') config.add_subpackage('core') config.add_subpackage('distutils') -- cgit v1.2.1 From ee852b432371e144456e012ec316c117170a7340 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 4 Aug 2021 16:55:51 -0600 Subject: Print a warning when importing the numpy.array_api submodule --- numpy/array_api/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'numpy') diff --git a/numpy/array_api/__init__.py b/numpy/array_api/__init__.py index 4650e3db8..1e9790a14 100644 --- a/numpy/array_api/__init__.py +++ b/numpy/array_api/__init__.py @@ -115,6 +115,10 @@ Still TODO in this module are: """ +import warnings +warnings.warn("The numpy.array_api submodule is still experimental. See NEP 47.", + stacklevel=2) + __all__ = [] from ._constants import e, inf, nan, pi -- cgit v1.2.1 From 7e6a026f4dff0ebe49913a119f2555562e4e93be Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 4 Aug 2021 19:46:21 -0600 Subject: Remove no longer comment about the keepdims argument to argmin --- numpy/array_api/_searching_functions.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'numpy') diff --git a/numpy/array_api/_searching_functions.py b/numpy/array_api/_searching_functions.py index d80720850..de5f43f3d 100644 --- a/numpy/array_api/_searching_functions.py +++ b/numpy/array_api/_searching_functions.py @@ -13,7 +13,6 @@ def argmax(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) - See its docstring for more information. """ - # Note: this currently fails as np.argmax does not implement keepdims return Array._new(np.asarray(np.argmax(x._array, axis=axis, keepdims=keepdims))) def argmin(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) -> Array: @@ -22,7 +21,6 @@ def argmin(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) - See its docstring for more information. """ - # Note: this currently fails as np.argmin does not implement keepdims return Array._new(np.asarray(np.argmin(x._array, axis=axis, keepdims=keepdims))) def nonzero(x: Array, /) -> Tuple[Array, ...]: -- cgit v1.2.1 From c23abdc57b2e6c0fa4f939085374c01c1c4452a9 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 4 Aug 2021 19:57:46 -0600 Subject: Remove asarray() calls from the array API statistical functions asarray() is already called in Array._new. --- numpy/array_api/_statistical_functions.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'numpy') diff --git a/numpy/array_api/_statistical_functions.py b/numpy/array_api/_statistical_functions.py index 61fc60c46..a606203bc 100644 --- a/numpy/array_api/_statistical_functions.py +++ b/numpy/array_api/_statistical_functions.py @@ -10,21 +10,21 @@ def max(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keep return Array._new(np.max(x._array, axis=axis, keepdims=keepdims)) def mean(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: - return Array._new(np.asarray(np.mean(x._array, axis=axis, keepdims=keepdims))) + return Array._new(np.mean(x._array, axis=axis, keepdims=keepdims)) def min(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: return Array._new(np.min(x._array, axis=axis, keepdims=keepdims)) def prod(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: - return Array._new(np.asarray(np.prod(x._array, axis=axis, keepdims=keepdims))) + return Array._new(np.prod(x._array, axis=axis, keepdims=keepdims)) def std(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> Array: # Note: the keyword argument correction is different here - return Array._new(np.asarray(np.std(x._array, axis=axis, ddof=correction, keepdims=keepdims))) + return Array._new(np.std(x._array, axis=axis, ddof=correction, keepdims=keepdims)) def sum(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: - return Array._new(np.asarray(np.sum(x._array, axis=axis, keepdims=keepdims))) + return Array._new(np.sum(x._array, axis=axis, keepdims=keepdims)) def var(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> Array: # Note: the keyword argument correction is different here - return Array._new(np.asarray(np.var(x._array, axis=axis, ddof=correction, keepdims=keepdims))) + return Array._new(np.var(x._array, axis=axis, ddof=correction, keepdims=keepdims)) -- cgit v1.2.1 From 5605d687019dc55e594d4e227747c72bebb71a3c Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 4 Aug 2021 19:59:47 -0600 Subject: Remove unused import --- numpy/array_api/_array_object.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/array_api/_array_object.py b/numpy/array_api/_array_object.py index 24957fde6..af70058e6 100644 --- a/numpy/array_api/_array_object.py +++ b/numpy/array_api/_array_object.py @@ -21,7 +21,7 @@ from ._creation_functions import asarray from ._dtypes import (_all_dtypes, _boolean_dtypes, _integer_dtypes, _integer_or_boolean_dtypes, _floating_dtypes, _numeric_dtypes) -from typing import TYPE_CHECKING, Any, Optional, Tuple, Union +from typing import TYPE_CHECKING, Optional, Tuple, Union if TYPE_CHECKING: from ._typing import PyCapsule, Device, Dtype -- cgit v1.2.1 From bc20d334b575f897157b1cf3eecda77f3e40e049 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 4 Aug 2021 20:01:11 -0600 Subject: Move the array API dtype categories into the top level They are not an official part of the spec but are useful for various parts of the implementation. --- numpy/array_api/_array_object.py | 17 ++++------------- numpy/array_api/_dtypes.py | 10 ++++++++++ numpy/array_api/tests/test_elementwise_functions.py | 16 +++------------- 3 files changed, 17 insertions(+), 26 deletions(-) (limited to 'numpy') diff --git a/numpy/array_api/_array_object.py b/numpy/array_api/_array_object.py index af70058e6..50906642d 100644 --- a/numpy/array_api/_array_object.py +++ b/numpy/array_api/_array_object.py @@ -98,23 +98,14 @@ class Array: if other is NotImplemented: return other """ - from ._dtypes import _result_type - - _dtypes = { - 'all': _all_dtypes, - 'numeric': _numeric_dtypes, - 'integer': _integer_dtypes, - 'integer or boolean': _integer_or_boolean_dtypes, - 'boolean': _boolean_dtypes, - 'floating-point': _floating_dtypes, - } - - if self.dtype not in _dtypes[dtype_category]: + from ._dtypes import _result_type, _dtype_categories + + if self.dtype not in _dtype_categories[dtype_category]: raise TypeError(f'Only {dtype_category} dtypes are allowed in {op}') if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) elif isinstance(other, Array): - if other.dtype not in _dtypes[dtype_category]: + if other.dtype not in _dtype_categories[dtype_category]: raise TypeError(f'Only {dtype_category} dtypes are allowed in {op}') else: return NotImplemented diff --git a/numpy/array_api/_dtypes.py b/numpy/array_api/_dtypes.py index fcdb562da..07be267da 100644 --- a/numpy/array_api/_dtypes.py +++ b/numpy/array_api/_dtypes.py @@ -23,6 +23,16 @@ _integer_dtypes = (int8, int16, int32, int64, uint8, uint16, uint32, uint64) _integer_or_boolean_dtypes = (bool, int8, int16, int32, int64, uint8, uint16, uint32, uint64) _numeric_dtypes = (float32, float64, int8, int16, int32, int64, uint8, uint16, uint32, uint64) +_dtype_categories = { + 'all': _all_dtypes, + 'numeric': _numeric_dtypes, + 'integer': _integer_dtypes, + 'integer or boolean': _integer_or_boolean_dtypes, + 'boolean': _boolean_dtypes, + 'floating-point': _floating_dtypes, +} + + # Note: the spec defines a restricted type promotion table compared to NumPy. # In particular, cross-kind promotions like integer + float or boolean + # integer are not allowed, even for functions that accept both kinds. diff --git a/numpy/array_api/tests/test_elementwise_functions.py b/numpy/array_api/tests/test_elementwise_functions.py index 994cb0bf0..2a5ddbc87 100644 --- a/numpy/array_api/tests/test_elementwise_functions.py +++ b/numpy/array_api/tests/test_elementwise_functions.py @@ -4,9 +4,8 @@ from numpy.testing import assert_raises from .. import asarray, _elementwise_functions from .._elementwise_functions import bitwise_left_shift, bitwise_right_shift -from .._dtypes import (_all_dtypes, _boolean_dtypes, _floating_dtypes, - _integer_dtypes, _integer_or_boolean_dtypes, - _numeric_dtypes) +from .._dtypes import (_dtype_categories, _boolean_dtypes, _floating_dtypes, + _integer_dtypes) def nargs(func): return len(getfullargspec(func).args) @@ -75,15 +74,6 @@ def test_function_types(): 'trunc': 'numeric', } - _dtypes = { - 'all': _all_dtypes, - 'numeric': _numeric_dtypes, - 'integer': _integer_dtypes, - 'integer_or_boolean': _integer_or_boolean_dtypes, - 'boolean': _boolean_dtypes, - 'floating': _floating_dtypes, - } - def _array_vals(): for d in _integer_dtypes: yield asarray(1, dtype=d) @@ -94,7 +84,7 @@ def test_function_types(): for x in _array_vals(): for func_name, types in elementwise_function_input_types.items(): - dtypes = _dtypes[types] + dtypes = _dtype_categories[types] func = getattr(_elementwise_functions, func_name) if nargs(func) == 2: for y in _array_vals(): -- cgit v1.2.1 From 6789a74312cda391b81ca803d38919555213a38f Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 4 Aug 2021 20:05:46 -0600 Subject: Move some imports out of functions to the top of the file Some of the imports in the array API module have to be inside functions to avoid circular imports, but these ones did not. --- numpy/array_api/_array_object.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'numpy') diff --git a/numpy/array_api/_array_object.py b/numpy/array_api/_array_object.py index 50906642d..364b88f89 100644 --- a/numpy/array_api/_array_object.py +++ b/numpy/array_api/_array_object.py @@ -19,7 +19,8 @@ import operator from enum import IntEnum from ._creation_functions import asarray from ._dtypes import (_all_dtypes, _boolean_dtypes, _integer_dtypes, - _integer_or_boolean_dtypes, _floating_dtypes, _numeric_dtypes) + _integer_or_boolean_dtypes, _floating_dtypes, + _numeric_dtypes, _result_type, _dtype_categories) from typing import TYPE_CHECKING, Optional, Tuple, Union if TYPE_CHECKING: @@ -27,6 +28,8 @@ if TYPE_CHECKING: import numpy as np +from numpy import array_api + class Array: """ n-d array object for the array API namespace. @@ -98,7 +101,6 @@ class Array: if other is NotImplemented: return other """ - from ._dtypes import _result_type, _dtype_categories if self.dtype not in _dtype_categories[dtype_category]: raise TypeError(f'Only {dtype_category} dtypes are allowed in {op}') @@ -338,7 +340,6 @@ class Array: def __array_namespace__(self: Array, /, *, api_version: Optional[str] = None) -> object: if api_version is not None and not api_version.startswith('2021.'): raise ValueError(f"Unrecognized array API version: {api_version!r}") - from numpy import array_api return array_api def __bool__(self: Array, /) -> bool: -- cgit v1.2.1 From 310929d12967cb0e8e6615466ff9b9f62fc899b6 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Wed, 4 Aug 2021 20:15:06 -0600 Subject: Fix casting for the array API concat() and stack() --- numpy/array_api/_manipulation_functions.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'numpy') diff --git a/numpy/array_api/_manipulation_functions.py b/numpy/array_api/_manipulation_functions.py index fa6344beb..e68dc6fcf 100644 --- a/numpy/array_api/_manipulation_functions.py +++ b/numpy/array_api/_manipulation_functions.py @@ -14,10 +14,11 @@ def concat(arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: Optional[i See its docstring for more information. """ + # Note: Casting rules here are different from the np.concatenate default + # (no for scalars with axis=None, no cross-kind casting) + dtype = result_type(*arrays) arrays = tuple(a._array for a in arrays) - # Call result type here just to raise on disallowed type combinations - result_type(*arrays) - return Array._new(np.concatenate(arrays, axis=axis)) + return Array._new(np.concatenate(arrays, axis=axis, dtype=dtype)) def expand_dims(x: Array, /, *, axis: int) -> Array: """ @@ -65,7 +66,7 @@ def stack(arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: int = 0) -> See its docstring for more information. """ - arrays = tuple(a._array for a in arrays) # Call result type here just to raise on disallowed type combinations result_type(*arrays) + arrays = tuple(a._array for a in arrays) return Array._new(np.stack(arrays, axis=axis)) -- cgit v1.2.1 From 3c9e27907d6f4b0b88cb7464cc19f9f08a63b83e Mon Sep 17 00:00:00 2001 From: Ross Barnowski Date: Thu, 5 Aug 2021 13:40:34 +0300 Subject: Rm numpy.lib.npyio.loads. --- numpy/__init__.pyi | 1 - numpy/core/_add_newdocs.py | 2 +- numpy/lib/__init__.pyi | 1 - numpy/lib/npyio.py | 11 +---------- numpy/lib/npyio.pyi | 2 -- 5 files changed, 2 insertions(+), 15 deletions(-) (limited to 'numpy') diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index 0c6ac34f9..fb68f4a56 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -516,7 +516,6 @@ from numpy.lib.npyio import ( recfromtxt as recfromtxt, recfromcsv as recfromcsv, load as load, - loads as loads, save as save, savez as savez, savez_compressed as savez_compressed, diff --git a/numpy/core/_add_newdocs.py b/numpy/core/_add_newdocs.py index 759a91d27..06f2a6376 100644 --- a/numpy/core/_add_newdocs.py +++ b/numpy/core/_add_newdocs.py @@ -3252,7 +3252,7 @@ add_newdoc('numpy.core.multiarray', 'ndarray', ('dumps', a.dumps() Returns the pickle of the array as a string. - pickle.loads or numpy.loads will convert the string back to an array. + pickle.loads will convert the string back to an array. Parameters ---------- diff --git a/numpy/lib/__init__.pyi b/numpy/lib/__init__.pyi index 25640ec07..ae23b2ec4 100644 --- a/numpy/lib/__init__.pyi +++ b/numpy/lib/__init__.pyi @@ -130,7 +130,6 @@ from numpy.lib.npyio import ( recfromtxt as recfromtxt, recfromcsv as recfromcsv, load as load, - loads as loads, save as save, savez as savez, savez_compressed as savez_compressed, diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 3b6a1c563..58133056f 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -26,18 +26,9 @@ from numpy.compat import ( ) -@set_module('numpy') -def loads(*args, **kwargs): - # NumPy 1.15.0, 2017-12-10 - warnings.warn( - "np.loads is deprecated, use pickle.loads instead", - DeprecationWarning, stacklevel=2) - return pickle.loads(*args, **kwargs) - - __all__ = [ 'savetxt', 'loadtxt', 'genfromtxt', 'ndfromtxt', 'mafromtxt', - 'recfromtxt', 'recfromcsv', 'load', 'loads', 'save', 'savez', + 'recfromtxt', 'recfromcsv', 'load', 'save', 'savez', 'savez_compressed', 'packbits', 'unpackbits', 'fromregex', 'DataSource' ] diff --git a/numpy/lib/npyio.pyi b/numpy/lib/npyio.pyi index 508357927..407175661 100644 --- a/numpy/lib/npyio.pyi +++ b/numpy/lib/npyio.pyi @@ -11,8 +11,6 @@ from numpy.core.multiarray import ( __all__: List[str] -def loads(*args, **kwargs): ... - class BagObj: def __init__(self, obj): ... def __getattribute__(self, key): ... -- cgit v1.2.1 From fe6e3afc2862a2734077c3d41d31470c3addc70b Mon Sep 17 00:00:00 2001 From: Ross Barnowski Date: Thu, 5 Aug 2021 13:48:43 +0300 Subject: Rm numpy.lib.npyio.ndfromtxt. --- numpy/lib/npyio.py | 30 +----------------------------- numpy/lib/npyio.pyi | 1 - numpy/lib/tests/test_io.py | 11 ----------- numpy/tests/test_public_api.py | 1 - 4 files changed, 1 insertion(+), 42 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 58133056f..08abac0ba 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -27,7 +27,7 @@ from numpy.compat import ( __all__ = [ - 'savetxt', 'loadtxt', 'genfromtxt', 'ndfromtxt', 'mafromtxt', + 'savetxt', 'loadtxt', 'genfromtxt', 'mafromtxt', 'recfromtxt', 'recfromcsv', 'load', 'save', 'savez', 'savez_compressed', 'packbits', 'unpackbits', 'fromregex', 'DataSource' ] @@ -2301,34 +2301,6 @@ _genfromtxt_with_like = array_function_dispatch( )(genfromtxt) -def ndfromtxt(fname, **kwargs): - """ - Load ASCII data stored in a file and return it as a single array. - - .. deprecated:: 1.17 - ndfromtxt` is a deprecated alias of `genfromtxt` which - overwrites the ``usemask`` argument with `False` even when - explicitly called as ``ndfromtxt(..., usemask=True)``. - Use `genfromtxt` instead. - - Parameters - ---------- - fname, kwargs : For a description of input parameters, see `genfromtxt`. - - See Also - -------- - numpy.genfromtxt : generic function. - - """ - kwargs['usemask'] = False - # Numpy 1.17 - warnings.warn( - "np.ndfromtxt is a deprecated alias of np.genfromtxt, " - "prefer the latter.", - DeprecationWarning, stacklevel=2) - return genfromtxt(fname, **kwargs) - - def mafromtxt(fname, **kwargs): """ Load ASCII data stored in a text file and return a masked array. diff --git a/numpy/lib/npyio.pyi b/numpy/lib/npyio.pyi index 407175661..22e689d79 100644 --- a/numpy/lib/npyio.pyi +++ b/numpy/lib/npyio.pyi @@ -98,5 +98,4 @@ def recfromtxt(fname, **kwargs): ... def recfromcsv(fname, **kwargs): ... # NOTE: Deprecated -# def ndfromtxt(fname, **kwargs): ... # def mafromtxt(fname, **kwargs): ... diff --git a/numpy/lib/tests/test_io.py b/numpy/lib/tests/test_io.py index d97ad76df..aa0037d32 100644 --- a/numpy/lib/tests/test_io.py +++ b/numpy/lib/tests/test_io.py @@ -2503,17 +2503,6 @@ class TestPathUsage: data = np.genfromtxt(path) assert_array_equal(a, data) - def test_ndfromtxt(self): - # Test outputting a standard ndarray - with temppath(suffix='.txt') as path: - path = Path(path) - with path.open('w') as f: - f.write(u'1 2\n3 4') - - control = np.array([[1, 2], [3, 4]], dtype=int) - test = np.genfromtxt(path, dtype=int) - assert_array_equal(test, control) - def test_mafromtxt(self): # From `test_fancy_dtype_alt` above with temppath(suffix='.txt') as path: diff --git a/numpy/tests/test_public_api.py b/numpy/tests/test_public_api.py index 6e4a8dee0..a40275eb7 100644 --- a/numpy/tests/test_public_api.py +++ b/numpy/tests/test_public_api.py @@ -46,7 +46,6 @@ def test_numpy_namespace(): 'get_array_wrap': 'numpy.lib.shape_base.get_array_wrap', 'get_include': 'numpy.lib.utils.get_include', 'mafromtxt': 'numpy.lib.npyio.mafromtxt', - 'ndfromtxt': 'numpy.lib.npyio.ndfromtxt', 'recfromcsv': 'numpy.lib.npyio.recfromcsv', 'recfromtxt': 'numpy.lib.npyio.recfromtxt', 'safe_eval': 'numpy.lib.utils.safe_eval', -- cgit v1.2.1 From 76639cc60a6a4ee0d7ca3dd1746c2c16367fb154 Mon Sep 17 00:00:00 2001 From: Ross Barnowski Date: Thu, 5 Aug 2021 13:52:34 +0300 Subject: Rm numpy.lib.npyio.mafromtxt. --- numpy/lib/npyio.py | 30 +----------------------------- numpy/lib/npyio.pyi | 3 --- numpy/lib/tests/test_io.py | 11 ----------- numpy/tests/test_public_api.py | 1 - 4 files changed, 1 insertion(+), 44 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 08abac0ba..d8adc3cdf 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -27,7 +27,7 @@ from numpy.compat import ( __all__ = [ - 'savetxt', 'loadtxt', 'genfromtxt', 'mafromtxt', + 'savetxt', 'loadtxt', 'genfromtxt', 'recfromtxt', 'recfromcsv', 'load', 'save', 'savez', 'savez_compressed', 'packbits', 'unpackbits', 'fromregex', 'DataSource' ] @@ -2301,34 +2301,6 @@ _genfromtxt_with_like = array_function_dispatch( )(genfromtxt) -def mafromtxt(fname, **kwargs): - """ - Load ASCII data stored in a text file and return a masked array. - - .. deprecated:: 1.17 - np.mafromtxt is a deprecated alias of `genfromtxt` which - overwrites the ``usemask`` argument with `True` even when - explicitly called as ``mafromtxt(..., usemask=False)``. - Use `genfromtxt` instead. - - Parameters - ---------- - fname, kwargs : For a description of input parameters, see `genfromtxt`. - - See Also - -------- - numpy.genfromtxt : generic function to load ASCII data. - - """ - kwargs['usemask'] = True - # Numpy 1.17 - warnings.warn( - "np.mafromtxt is a deprecated alias of np.genfromtxt, " - "prefer the latter.", - DeprecationWarning, stacklevel=2) - return genfromtxt(fname, **kwargs) - - def recfromtxt(fname, **kwargs): """ Load ASCII data from a file and return it in a record array. diff --git a/numpy/lib/npyio.pyi b/numpy/lib/npyio.pyi index 22e689d79..f69edd564 100644 --- a/numpy/lib/npyio.pyi +++ b/numpy/lib/npyio.pyi @@ -96,6 +96,3 @@ def genfromtxt( ): ... def recfromtxt(fname, **kwargs): ... def recfromcsv(fname, **kwargs): ... - -# NOTE: Deprecated -# def mafromtxt(fname, **kwargs): ... diff --git a/numpy/lib/tests/test_io.py b/numpy/lib/tests/test_io.py index aa0037d32..02a9789a7 100644 --- a/numpy/lib/tests/test_io.py +++ b/numpy/lib/tests/test_io.py @@ -2503,17 +2503,6 @@ class TestPathUsage: data = np.genfromtxt(path) assert_array_equal(a, data) - def test_mafromtxt(self): - # From `test_fancy_dtype_alt` above - with temppath(suffix='.txt') as path: - path = Path(path) - with path.open('w') as f: - f.write(u'1,2,3.0\n4,5,6.0\n') - - test = np.genfromtxt(path, delimiter=',', usemask=True) - control = ma.array([(1.0, 2.0, 3.0), (4.0, 5.0, 6.0)]) - assert_equal(test, control) - def test_recfromtxt(self): with temppath(suffix='.txt') as path: path = Path(path) diff --git a/numpy/tests/test_public_api.py b/numpy/tests/test_public_api.py index a40275eb7..3fa2edd8f 100644 --- a/numpy/tests/test_public_api.py +++ b/numpy/tests/test_public_api.py @@ -45,7 +45,6 @@ def test_numpy_namespace(): 'fastCopyAndTranspose': 'numpy.core._multiarray_umath._fastCopyAndTranspose', 'get_array_wrap': 'numpy.lib.shape_base.get_array_wrap', 'get_include': 'numpy.lib.utils.get_include', - 'mafromtxt': 'numpy.lib.npyio.mafromtxt', 'recfromcsv': 'numpy.lib.npyio.recfromcsv', 'recfromtxt': 'numpy.lib.npyio.recfromtxt', 'safe_eval': 'numpy.lib.utils.safe_eval', -- cgit v1.2.1 From 3730fc06cd821ec0d7794a6ae058141400921dba Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 5 Aug 2021 17:31:47 -0600 Subject: Give a better error when numpy.array_api is imported in Python 3.7 --- numpy/array_api/__init__.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'numpy') diff --git a/numpy/array_api/__init__.py b/numpy/array_api/__init__.py index 1e9790a14..08d19744f 100644 --- a/numpy/array_api/__init__.py +++ b/numpy/array_api/__init__.py @@ -115,6 +115,12 @@ Still TODO in this module are: """ +import sys +# numpy.array_api is 3.8+ because it makes extensive use of positional-only +# arguments. +if sys.version_info < (3, 8): + raise ImportError("The numpy.array_api submodule requires Python 3.8 or greater.") + import warnings warnings.warn("The numpy.array_api submodule is still experimental. See NEP 47.", stacklevel=2) -- cgit v1.2.1 From d74a7d0b3297e10194bd3899a0d17a63610e49a1 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 5 Aug 2021 20:01:51 -0600 Subject: Add a setup.py to the array_api submodule --- numpy/array_api/setup.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 numpy/array_api/setup.py (limited to 'numpy') diff --git a/numpy/array_api/setup.py b/numpy/array_api/setup.py new file mode 100644 index 000000000..da2350c8f --- /dev/null +++ b/numpy/array_api/setup.py @@ -0,0 +1,10 @@ +def configuration(parent_package='', top_path=None): + from numpy.distutils.misc_util import Configuration + config = Configuration('array_api', parent_package, top_path) + config.add_subpackage('tests') + return config + + +if __name__ == '__main__': + from numpy.distutils.core import setup + setup(configuration=configuration) -- cgit v1.2.1 From 67b0df4b38700acceb0197d704e0eb37b3fbd837 Mon Sep 17 00:00:00 2001 From: Bas van Beek <43369155+BvB93@users.noreply.github.com> Date: Fri, 6 Aug 2021 13:25:27 +0200 Subject: TST: Skip `test_lookfor` in 3.10rc1 Broken in rc1 as of bpo-44524 --- numpy/lib/tests/test_utils.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/lib/tests/test_utils.py b/numpy/lib/tests/test_utils.py index 8a877ae69..72c91836f 100644 --- a/numpy/lib/tests/test_utils.py +++ b/numpy/lib/tests/test_utils.py @@ -11,6 +11,10 @@ from io import StringIO @pytest.mark.skipif(sys.flags.optimize == 2, reason="Python running -OO") +@pytest.mark.skipif( + sys.version_info == (3, 10, 0, "candidate", 1), + reason="Broken as of bpo-44524", +) def test_lookfor(): out = StringIO() utils.lookfor('eigenvalue', module='numpy', output=out, @@ -160,7 +164,7 @@ def test_info_method_heading(): class WithPublicMethods: def first_method(): pass - + def _has_method_heading(cls): out = StringIO() utils.info(cls, output=out) -- cgit v1.2.1 From 4743e36f1336094733412c2246f0de22e1f93d8a Mon Sep 17 00:00:00 2001 From: Bas van Beek <43369155+BvB93@users.noreply.github.com> Date: Fri, 6 Aug 2021 13:32:33 +0200 Subject: TST: Hardcode the expected output of two `complex`-related tests Complex exponentiation is broken for `builtins.complex` as of bpo-44698 --- numpy/core/tests/test_umath_complex.py | 40 +++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 13 deletions(-) (limited to 'numpy') diff --git a/numpy/core/tests/test_umath_complex.py b/numpy/core/tests/test_umath_complex.py index c051cd61b..ad09830d4 100644 --- a/numpy/core/tests/test_umath_complex.py +++ b/numpy/core/tests/test_umath_complex.py @@ -372,11 +372,18 @@ class TestCpow: x = np.array([1, 1j, 2, 2.5+.37j, np.inf, np.nan]) y = np.array([1, 1j, -0.5+1.5j, -0.5+1.5j, 2, 3]) lx = list(range(len(x))) - # Compute the values for complex type in python - p_r = [complex(x[i]) ** complex(y[i]) for i in lx] - # Substitute a result allowed by C99 standard - p_r[4] = complex(np.inf, np.nan) - # Do the same with numpy complex scalars + + # Hardcode the expected `builtins.complex` values, + # as complex exponentiation is broken as of bpo-44698 + p_r = [ + 1+0j, + 0.20787957635076193+0j, + 0.35812203996480685+0.6097119028618724j, + 0.12659112128185032+0.48847676699581527j, + complex(np.inf, np.nan), + complex(np.nan, np.nan), + ] + n_r = [x[i] ** y[i] for i in lx] for i in lx: assert_almost_equal(n_r[i], p_r[i], err_msg='Loop %d\n' % i) @@ -385,11 +392,18 @@ class TestCpow: x = np.array([1, 1j, 2, 2.5+.37j, np.inf, np.nan]) y = np.array([1, 1j, -0.5+1.5j, -0.5+1.5j, 2, 3]) lx = list(range(len(x))) - # Compute the values for complex type in python - p_r = [complex(x[i]) ** complex(y[i]) for i in lx] - # Substitute a result allowed by C99 standard - p_r[4] = complex(np.inf, np.nan) - # Do the same with numpy arrays + + # Hardcode the expected `builtins.complex` values, + # as complex exponentiation is broken as of bpo-44698 + p_r = [ + 1+0j, + 0.20787957635076193+0j, + 0.35812203996480685+0.6097119028618724j, + 0.12659112128185032+0.48847676699581527j, + complex(np.inf, np.nan), + complex(np.nan, np.nan), + ] + n_r = x ** y for i in lx: assert_almost_equal(n_r[i], p_r[i], err_msg='Loop %d\n' % i) @@ -583,7 +597,7 @@ class TestComplexAbsoluteMixedDTypes: @pytest.mark.parametrize("stride", [-4,-3,-2,-1,1,2,3,4]) @pytest.mark.parametrize("astype", [np.complex64, np.complex128]) @pytest.mark.parametrize("func", ['abs', 'square', 'conjugate']) - + def test_array(self, stride, astype, func): dtype = [('template_id', ' Date: Tue, 3 Aug 2021 16:49:50 +0200 Subject: PERF: Special-case single-converter in loadtxt. ~5-13% speedup: `[*map(conv, items)]` (single converter, which is quite common) is much faster than `[conv(val) for conv, val in zip(converters, vals)]`. `_loadtxt_floatconv` and `fencode` were lifted out so that every "instance" of them is the same, allowing checking for whether there's different converters in use (actually, it looks like two `floatconv`s returned by two separate calls to `_getconv` have the same identity, but we don't need to rely on that. --- numpy/lib/npyio.py | 92 +++++++++++++++++++++++++++++------------------------- 1 file changed, 50 insertions(+), 42 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 7c73d9655..983e2615c 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -5,7 +5,7 @@ import itertools import warnings import weakref import contextlib -from operator import itemgetter, index as opindex +from operator import itemgetter, index as opindex, methodcaller from collections.abc import Mapping import numpy as np @@ -728,41 +728,42 @@ def _savez(file, args, kwds, compress, allow_pickle=True, pickle_kwargs=None): zipf.close() +def _floatconv(x): + try: + return float(x) # The fastest path. + except ValueError: + if '0x' in x: # Don't accidentally convert "a" ("0xa") to 10. + try: + return float.fromhex(x) + except ValueError: + pass + raise # Raise the original exception, which makes more sense. + + +_CONVERTERS = [ + (np.bool_, lambda x: bool(int(x))), + (np.uint64, np.uint64), + (np.int64, np.int64), + (np.integer, lambda x: int(float(x))), + (np.longdouble, np.longdouble), + (np.floating, _floatconv), + (complex, lambda x: complex(asstr(x).replace('+-', '-'))), + (np.bytes_, asbytes), + (np.unicode_, asunicode), +] + + def _getconv(dtype): - """ Find the correct dtype converter. Adapted from matplotlib """ + """ + Find the correct dtype converter. Adapted from matplotlib. - def floatconv(x): - try: - return float(x) # The fastest path. - except ValueError: - if '0x' in x: # Don't accidentally convert "a" ("0xa") to 10. - try: - return float.fromhex(x) - except ValueError: - pass - raise # Raise the original exception, which makes more sense. - - typ = dtype.type - if issubclass(typ, np.bool_): - return lambda x: bool(int(x)) - if issubclass(typ, np.uint64): - return np.uint64 - if issubclass(typ, np.int64): - return np.int64 - if issubclass(typ, np.integer): - return lambda x: int(float(x)) - elif issubclass(typ, np.longdouble): - return np.longdouble - elif issubclass(typ, np.floating): - return floatconv - elif issubclass(typ, complex): - return lambda x: complex(asstr(x).replace('+-', '-')) - elif issubclass(typ, np.bytes_): - return asbytes - elif issubclass(typ, np.unicode_): - return asunicode - else: - return asstr + Even when a lambda is returned, it is defined at the toplevel, to allow + testing for equality and enabling optimization for single-type data. + """ + for base, conv in _CONVERTERS: + if issubclass(dtype.type, base): + return conv + return asstr # _loadtxt_flatten_dtype_internal and _loadtxt_pack_items are loadtxt helpers @@ -1011,12 +1012,9 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, line_num = i + skiprows + 1 raise ValueError("Wrong number of columns at line %d" % line_num) - - # Convert each value according to its column and store - items = [conv(val) for (conv, val) in zip(converters, vals)] - - # Then pack it according to the dtype's nesting - items = packer(items) + # Convert each value according to its column, then pack it + # according to the dtype's nesting + items = packer(convert_row(vals)) X.append(items) if len(X) > chunk_size: yield X @@ -1154,8 +1152,18 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, else: converters[i] = conv - converters = [conv if conv is not bytes else - lambda x: x.encode(fencoding) for conv in converters] + fencode = methodcaller("encode", fencoding) + converters = [conv if conv is not bytes else fencode + for conv in converters] + if len(set(converters)) == 1: + # Optimize single-type data. Note that this is only reached if + # `_getconv` returns equal callables (i.e. not local lambdas) on + # equal dtypes. + def convert_row(vals, _conv=converters[0]): + return [*map(_conv, vals)] + else: + def convert_row(vals): + return [conv(val) for conv, val in zip(converters, vals)] # read data in chunks and fill it into an array via resize # over-allocating and shrinking the array later may be faster but is -- cgit v1.2.1 From a99489b4e8ced49a71b5884a265a7abbb4f98fe1 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Thu, 5 Aug 2021 23:52:15 +0200 Subject: PERF: Simplify some of loadtxt's standard converters. Standard converters only ever get called with str inputs (loadtxt performs the required decoding); saving a bunch of runtime typechecks (in `asstr`) results in a 15-20% speedup when loadtxt()ing the corresponding types. --- numpy/lib/npyio.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 983e2615c..650613c8b 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -740,16 +740,16 @@ def _floatconv(x): raise # Raise the original exception, which makes more sense. -_CONVERTERS = [ +_CONVERTERS = [ # These converters only ever get strs (not bytes) as input. (np.bool_, lambda x: bool(int(x))), (np.uint64, np.uint64), (np.int64, np.int64), (np.integer, lambda x: int(float(x))), (np.longdouble, np.longdouble), (np.floating, _floatconv), - (complex, lambda x: complex(asstr(x).replace('+-', '-'))), - (np.bytes_, asbytes), - (np.unicode_, asunicode), + (complex, lambda x: complex(x.replace('+-', '-'))), + (np.bytes_, methodcaller('encode', 'latin-1')), + (np.unicode_, str), ] @@ -763,7 +763,7 @@ def _getconv(dtype): for base, conv in _CONVERTERS: if issubclass(dtype.type, base): return conv - return asstr + return str # _loadtxt_flatten_dtype_internal and _loadtxt_pack_items are loadtxt helpers -- cgit v1.2.1 From 5e0ea3586184d4e088281cf727c350a7c26bd8ad Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Fri, 6 Aug 2021 19:09:26 +0200 Subject: MAINT: Skip a type check in loadtxt when using user converters. loadtxt only ever calls converters with strs now, so the type check is unneeded. Skipping the type check may have a tiny performance benefit, but the main point is just code clarity. --- numpy/lib/npyio.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 983e2615c..cbd59d5d4 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -1142,13 +1142,11 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, continue if byte_converters: # converters may use decode to workaround numpy's old - # behaviour, so encode the string again before passing to - # the user converter - def tobytes_first(x, conv): - if type(x) is bytes: - return conv(x) + # behaviour, so encode the string again (converters are only + # called with strings) before passing to the user converter. + def tobytes_first(conv, x): return conv(x.encode("latin1")) - converters[i] = functools.partial(tobytes_first, conv=conv) + converters[i] = functools.partial(tobytes_first, conv) else: converters[i] = conv -- cgit v1.2.1 From e6be8977bd741ee39fdc0b30fccae036c68db9f7 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 3 Aug 2021 16:03:26 +0200 Subject: PERF: In loadtxt, decide once and for all whether decoding is needed. ... and use a single decoder function instead of repeatedly checking the input type (in `_decode_line`). ~5-8% speedup. --- numpy/lib/npyio.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 983e2615c..159378992 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -979,9 +979,8 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, # Nested functions used by loadtxt. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - def split_line(line): - """Chop off comments, strip, and split at delimiter. """ - line = _decode_line(line, encoding=encoding) + def split_line(line: str): + """Chop off comments, strip, and split at delimiter.""" for comment in comments: # Much faster than using a single regex. line = line.split(comment, 1)[0] line = line.strip('\r\n') @@ -1002,8 +1001,7 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, X = [] line_iter = itertools.chain([first_line], fh) line_iter = itertools.islice(line_iter, max_rows) - for i, line in enumerate(line_iter): - vals = split_line(line) + for i, vals in enumerate(map(split_line, map(decode, line_iter))): if len(vals) == 0: continue if usecols: @@ -1108,7 +1106,8 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, # Read until we find a line with some values, and use it to determine # the need for decoding and estimate the number of columns. for first_line in fh: - ncols = len(usecols or split_line(first_line)) + ncols = len(usecols + or split_line(_decode_line(first_line, encoding))) if ncols: break else: # End of lines reached @@ -1117,6 +1116,13 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, warnings.warn('loadtxt: Empty input file: "%s"' % fname, stacklevel=2) + # Decide once and for all whether decoding is needed. + if isinstance(first_line, bytes): + decode = methodcaller( + "decode", encoding if encoding is not None else "latin1") + else: + def decode(line): return line + # Now that we know ncols, create the default converters list, and # set packing, if necessary. if len(dtype_types) > 1: -- cgit v1.2.1 From 6b681d938df505c9a669a1892186b378085b7ee8 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Wed, 4 Aug 2021 20:21:35 +0200 Subject: loadtxt: Preconstruct a (lineno, words) iterator to pass to read_data. Mostly to help later speedups, but also slightly optimizes `len(vals) == 0` into a bool check (in `filter`). --- numpy/lib/npyio.py | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 159378992..98b4eafe2 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -986,33 +986,28 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, line = line.strip('\r\n') return line.split(delimiter) if line else [] - def read_data(chunk_size): - """Parse each line, including the first. - - The file read, `fh`, is a global defined above. + def read_data(lineno_words_iter, chunk_size): + """ + Parse each line, including the first. Parameters ---------- + lineno_words_iter : Iterator[tuple[int, list[str]]] + Iterator returning line numbers and non-empty lines already split + into words. chunk_size : int At most `chunk_size` lines are read at a time, with iteration until all lines are read. - """ X = [] - line_iter = itertools.chain([first_line], fh) - line_iter = itertools.islice(line_iter, max_rows) - for i, vals in enumerate(map(split_line, map(decode, line_iter))): - if len(vals) == 0: - continue + for lineno, words in lineno_words_iter: if usecols: - vals = [vals[j] for j in usecols] - if len(vals) != ncols: - line_num = i + skiprows + 1 - raise ValueError("Wrong number of columns at line %d" - % line_num) + words = [words[j] for j in usecols] + if len(words) != ncols: + raise ValueError(f"Wrong number of columns at line {lineno}") # Convert each value according to its column, then pack it # according to the dtype's nesting - items = packer(convert_row(vals)) + items = packer(convert_row(words)) X.append(items) if len(X) > chunk_size: yield X @@ -1116,12 +1111,16 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, warnings.warn('loadtxt: Empty input file: "%s"' % fname, stacklevel=2) - # Decide once and for all whether decoding is needed. + line_iter = itertools.islice( + itertools.chain([first_line], fh), max_rows) if isinstance(first_line, bytes): - decode = methodcaller( + decoder = methodcaller( # latin1 matches _decode_line's behavior. "decode", encoding if encoding is not None else "latin1") - else: - def decode(line): return line + line_iter = map(decoder, line_iter) + + lineno_words_iter = filter( + itemgetter(1), # item[1] is words; filter skips empty lines. + enumerate(map(split_line, line_iter), 1 + skiprows)) # Now that we know ncols, create the default converters list, and # set packing, if necessary. @@ -1176,7 +1175,7 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, # probably not relevant compared to the cost of actually reading and # converting the data X = None - for x in read_data(_loadtxt_chunksize): + for x in read_data(lineno_words_iter, _loadtxt_chunksize): if X is None: X = np.array(x, dtype) else: -- cgit v1.2.1 From 3affc1738f656445493b8976a489f562a97cfb0c Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Fri, 6 Aug 2021 20:15:37 +0200 Subject: Move loadtxt bytes/str detection much earlier. --- numpy/lib/npyio.py | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 98b4eafe2..032e829b0 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -1073,11 +1073,24 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, if _is_string_like(fname): fh = np.lib._datasource.open(fname, 'rt', encoding=encoding) fencoding = getattr(fh, 'encoding', 'latin1') - fh = iter(fh) + line_iter = iter(fh) fown = True else: - fh = iter(fname) + line_iter = iter(fname) fencoding = getattr(fname, 'encoding', 'latin1') + try: + first_line = next(line_iter) + except StopIteration: + pass # Nothing matters if line_iter is empty. + else: + # Put first_line back. + line_iter = itertools.chain([first_line], line_iter) + if isinstance(first_line, bytes): + # Using latin1 matches _decode_line's behavior. + decoder = methodcaller( + "decode", + encoding if encoding is not None else "latin1") + line_iter = map(decoder, line_iter) except TypeError as e: raise ValueError( f"fname must be a string, filehandle, list of strings,\n" @@ -1096,28 +1109,22 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, try: # Skip the first `skiprows` lines for i in range(skiprows): - next(fh) + next(line_iter) # Read until we find a line with some values, and use it to determine # the need for decoding and estimate the number of columns. - for first_line in fh: - ncols = len(usecols - or split_line(_decode_line(first_line, encoding))) + for first_line in line_iter: + ncols = len(usecols or split_line(first_line)) if ncols: + # Put first_line back. + line_iter = itertools.chain([first_line], line_iter) break else: # End of lines reached - first_line = '' ncols = len(usecols or []) warnings.warn('loadtxt: Empty input file: "%s"' % fname, stacklevel=2) - line_iter = itertools.islice( - itertools.chain([first_line], fh), max_rows) - if isinstance(first_line, bytes): - decoder = methodcaller( # latin1 matches _decode_line's behavior. - "decode", encoding if encoding is not None else "latin1") - line_iter = map(decoder, line_iter) - + line_iter = itertools.islice(line_iter, max_rows) lineno_words_iter = filter( itemgetter(1), # item[1] is words; filter skips empty lines. enumerate(map(split_line, line_iter), 1 + skiprows)) -- cgit v1.2.1 From fcdadee7815cbb72a1036c0ef144d73e916eae6d Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 6 Aug 2021 16:09:23 -0600 Subject: Fix some dictionary key mismatches in the array API tests --- .../array_api/tests/test_elementwise_functions.py | 54 +++++++++++----------- 1 file changed, 27 insertions(+), 27 deletions(-) (limited to 'numpy') diff --git a/numpy/array_api/tests/test_elementwise_functions.py b/numpy/array_api/tests/test_elementwise_functions.py index 2a5ddbc87..ec76cb7a7 100644 --- a/numpy/array_api/tests/test_elementwise_functions.py +++ b/numpy/array_api/tests/test_elementwise_functions.py @@ -17,27 +17,27 @@ def test_function_types(): elementwise_function_input_types = { 'abs': 'numeric', - 'acos': 'floating', - 'acosh': 'floating', + 'acos': 'floating-point', + 'acosh': 'floating-point', 'add': 'numeric', - 'asin': 'floating', - 'asinh': 'floating', - 'atan': 'floating', - 'atan2': 'floating', - 'atanh': 'floating', - 'bitwise_and': 'integer_or_boolean', - 'bitwise_invert': 'integer_or_boolean', + 'asin': 'floating-point', + 'asinh': 'floating-point', + 'atan': 'floating-point', + 'atan2': 'floating-point', + 'atanh': 'floating-point', + 'bitwise_and': 'integer or boolean', + 'bitwise_invert': 'integer or boolean', 'bitwise_left_shift': 'integer', - 'bitwise_or': 'integer_or_boolean', + 'bitwise_or': 'integer or boolean', 'bitwise_right_shift': 'integer', - 'bitwise_xor': 'integer_or_boolean', + 'bitwise_xor': 'integer or boolean', 'ceil': 'numeric', - 'cos': 'floating', - 'cosh': 'floating', - 'divide': 'floating', + 'cos': 'floating-point', + 'cosh': 'floating-point', + 'divide': 'floating-point', 'equal': 'all', - 'exp': 'floating', - 'expm1': 'floating', + 'exp': 'floating-point', + 'expm1': 'floating-point', 'floor': 'numeric', 'floor_divide': 'numeric', 'greater': 'numeric', @@ -47,11 +47,11 @@ def test_function_types(): 'isnan': 'numeric', 'less': 'numeric', 'less_equal': 'numeric', - 'log': 'floating', - 'logaddexp': 'floating', - 'log10': 'floating', - 'log1p': 'floating', - 'log2': 'floating', + 'log': 'floating-point', + 'logaddexp': 'floating-point', + 'log10': 'floating-point', + 'log1p': 'floating-point', + 'log2': 'floating-point', 'logical_and': 'boolean', 'logical_not': 'boolean', 'logical_or': 'boolean', @@ -60,17 +60,17 @@ def test_function_types(): 'negative': 'numeric', 'not_equal': 'all', 'positive': 'numeric', - 'pow': 'floating', + 'pow': 'floating-point', 'remainder': 'numeric', 'round': 'numeric', 'sign': 'numeric', - 'sin': 'floating', - 'sinh': 'floating', - 'sqrt': 'floating', + 'sin': 'floating-point', + 'sinh': 'floating-point', + 'sqrt': 'floating-point', 'square': 'numeric', 'subtract': 'numeric', - 'tan': 'floating', - 'tanh': 'floating', + 'tan': 'floating-point', + 'tanh': 'floating-point', 'trunc': 'numeric', } -- cgit v1.2.1 From b6f71c8fc742e09d803c99fe41c06d6f2a81d4de Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 6 Aug 2021 16:10:09 -0600 Subject: Make the array API submodule not break the test suite The warning is issued on import, which otherwise breaks pytest collection. If we manually import early and ignore the warning, any further imports of the module won't issue the warning again, due to the way Python caches imports. --- numpy/_pytesttester.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'numpy') diff --git a/numpy/_pytesttester.py b/numpy/_pytesttester.py index acfaa1ca5..bfcbd4f1f 100644 --- a/numpy/_pytesttester.py +++ b/numpy/_pytesttester.py @@ -137,13 +137,20 @@ class PytestTester: # offset verbosity. The "-q" cancels a "-v". pytest_args += ["-q"] - # Filter out distutils cpu warnings (could be localized to - # distutils tests). ASV has problems with top level import, - # so fetch module for suppression here. with warnings.catch_warnings(): warnings.simplefilter("always") + # Filter out distutils cpu warnings (could be localized to + # distutils tests). ASV has problems with top level import, + # so fetch module for suppression here. from numpy.distutils import cpuinfo + # Ignore the warning from importing the array_api submodule. This + # warning is done on import, so it would break pytest collection, + # but importing it early here prevents the warning from being + # issued when it imported again. + warnings.simplefilter("ignore") + import numpy.array_api + # Filter out annoying import messages. Want these in both develop and # release mode. pytest_args += [ -- cgit v1.2.1 From 5c7074f90ee7093f231816cb356cb787e0f22802 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 6 Aug 2021 16:24:34 -0600 Subject: Fix the tests for Python 3.7 The array_api submodule needs to be skipped entirely, as it uses non-3.7 compatible syntax. --- numpy/_pytesttester.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'numpy') diff --git a/numpy/_pytesttester.py b/numpy/_pytesttester.py index bfcbd4f1f..1e24f75a7 100644 --- a/numpy/_pytesttester.py +++ b/numpy/_pytesttester.py @@ -144,12 +144,18 @@ class PytestTester: # so fetch module for suppression here. from numpy.distutils import cpuinfo - # Ignore the warning from importing the array_api submodule. This - # warning is done on import, so it would break pytest collection, - # but importing it early here prevents the warning from being - # issued when it imported again. - warnings.simplefilter("ignore") - import numpy.array_api + if sys.version_info >= (3, 8): + # Ignore the warning from importing the array_api submodule. This + # warning is done on import, so it would break pytest collection, + # but importing it early here prevents the warning from being + # issued when it imported again. + warnings.simplefilter("ignore") + import numpy.array_api + else: + # The array_api submodule is Python 3.8+ only due to the use + # of positional-only argument syntax. We have to ignore it + # completely or the tests will fail at the collection stage. + pytest_args += ['--ignore-glob=numpy/array_api/*'] # Filter out annoying import messages. Want these in both develop and # release mode. -- cgit v1.2.1 From 2fe8643cce651fa2ada5619f85e3cc16524d4076 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 6 Aug 2021 16:48:16 -0600 Subject: Fix the array API __len__ method --- numpy/array_api/_array_object.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/array_api/_array_object.py b/numpy/array_api/_array_object.py index 364b88f89..00f50eade 100644 --- a/numpy/array_api/_array_object.py +++ b/numpy/array_api/_array_object.py @@ -468,8 +468,7 @@ class Array: """ Performs the operation __len__. """ - res = self._array.__len__() - return self.__class__._new(res) + return self._array.__len__() def __lshift__(self: Array, other: Union[int, Array], /) -> Array: """ -- cgit v1.2.1 From 4063752757a97c444b8913947a0890f2c2387bca Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 6 Aug 2021 16:57:10 -0600 Subject: Fix the array API unique() function --- numpy/array_api/_set_functions.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/array_api/_set_functions.py b/numpy/array_api/_set_functions.py index f28c2ee72..acd59f597 100644 --- a/numpy/array_api/_set_functions.py +++ b/numpy/array_api/_set_functions.py @@ -12,4 +12,8 @@ def unique(x: Array, /, *, return_counts: bool = False, return_index: bool = Fal See its docstring for more information. """ - return Array._new(np.unique(x._array, return_counts=return_counts, return_index=return_index, return_inverse=return_inverse)) + res = np.unique(x._array, return_counts=return_counts, + return_index=return_index, return_inverse=return_inverse) + if isinstance(res, tuple): + return tuple(Array._new(i) for i in res) + return Array._new(res) -- cgit v1.2.1 From 1ae808401951bf8c4cbff97a30505f08741d811f Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 6 Aug 2021 17:12:54 -0600 Subject: Make the axis argument to squeeze() in the array_api module positional-only See data-apis/array-api#100. --- numpy/array_api/_manipulation_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/array_api/_manipulation_functions.py b/numpy/array_api/_manipulation_functions.py index e68dc6fcf..33f5d5a28 100644 --- a/numpy/array_api/_manipulation_functions.py +++ b/numpy/array_api/_manipulation_functions.py @@ -52,7 +52,7 @@ def roll(x: Array, /, shift: Union[int, Tuple[int, ...]], *, axis: Optional[Unio """ return Array._new(np.roll(x._array, shift, axis=axis)) -def squeeze(x: Array, /, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> Array: +def squeeze(x: Array, /, axis: Union[int, Tuple[int, ...]]) -> Array: """ Array API compatible wrapper for :py:func:`np.squeeze `. -- cgit v1.2.1 From f13f08f6c00ff5debf918dd50546b3215e39a5b8 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 6 Aug 2021 17:15:39 -0600 Subject: Fix the array API nonzero() function --- numpy/array_api/_searching_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/array_api/_searching_functions.py b/numpy/array_api/_searching_functions.py index de5f43f3d..9dcc76b2d 100644 --- a/numpy/array_api/_searching_functions.py +++ b/numpy/array_api/_searching_functions.py @@ -29,7 +29,7 @@ def nonzero(x: Array, /) -> Tuple[Array, ...]: See its docstring for more information. """ - return Array._new(np.nonzero(x._array)) + return tuple(Array._new(i) for i in np.nonzero(x._array)) def where(condition: Array, x1: Array, x2: Array, /) -> Array: """ -- cgit v1.2.1 From 21923a5fa71bfadf7dee0bb5b110cc2a5719eaac Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 6 Aug 2021 18:07:46 -0600 Subject: Update the docstring of numpy.array_api --- numpy/array_api/__init__.py | 79 +++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 38 deletions(-) (limited to 'numpy') diff --git a/numpy/array_api/__init__.py b/numpy/array_api/__init__.py index 08d19744f..4dc931732 100644 --- a/numpy/array_api/__init__.py +++ b/numpy/array_api/__init__.py @@ -1,7 +1,8 @@ """ A NumPy sub-namespace that conforms to the Python array API standard. -This submodule accompanies NEP 47, which proposes its inclusion in NumPy. +This submodule accompanies NEP 47, which proposes its inclusion in NumPy. It +is still considered experimental, and will issue a warning when imported. This is a proof-of-concept namespace that wraps the corresponding NumPy functions to give a conforming implementation of the Python array API standard @@ -38,35 +39,29 @@ A few notes about the current state of this submodule: in progress, but the existing tests pass on this module, with a few exceptions: - - Device support is not yet implemented in NumPy - (https://data-apis.github.io/array-api/latest/design_topics/device_support.html). - As a result, the `device` attribute of the array object is missing, and - array creation functions that take the `device` keyword argument will fail - with NotImplementedError. - - DLPack support (see https://github.com/data-apis/array-api/pull/106) is not included here, as it requires a full implementation in NumPy proper first. - - The linear algebra extension in the spec will be added in a future pull -request. - The test suite is not yet complete, and even the tests that exist are not - guaranteed to give a comprehensive coverage of the spec. Therefore, those - reviewing this submodule should refer to the standard documents themselves. - -- There is a custom array object, numpy.array_api.Array, which is returned - by all functions in this module. All functions in the array API namespace + guaranteed to give a comprehensive coverage of the spec. Therefore, when + reviewing and using this submodule, you should refer to the standard + documents themselves. There are some tests in numpy.array_api.tests, but + they primarily focus on things that are not tested by the official array API + test suite. + +- There is a custom array object, numpy.array_api.Array, which is returned by + all functions in this module. All functions in the array API namespace implicitly assume that they will only receive this object as input. The only way to create instances of this object is to use one of the array creation functions. It does not have a public constructor on the object itself. The - object is a small wrapper Python class around numpy.ndarray. The main - purpose of it is to restrict the namespace of the array object to only those - dtypes and only those methods that are required by the spec, as well as to - limit/change certain behavior that differs in the spec. In particular: + object is a small wrapper class around numpy.ndarray. The main purpose of it + is to restrict the namespace of the array object to only those dtypes and + only those methods that are required by the spec, as well as to limit/change + certain behavior that differs in the spec. In particular: - - The array API namespace does not have scalar objects, only 0-d arrays. - Operations in on Array that would create a scalar in NumPy create a 0-d + - The array API namespace does not have scalar objects, only 0-D arrays. + Operations on Array that would create a scalar in NumPy create a 0-D array. - Indexing: Only a subset of indices supported by NumPy are required by the @@ -76,12 +71,15 @@ request. information. - Type promotion: Some type promotion rules are different in the spec. In - particular, the spec does not have any value-based casting. The - Array._promote_scalar method promotes Python scalars to arrays, - disallowing cross-type promotions like int -> float64 that are not allowed - in the spec. Array._normalize_two_args works around some type promotion - quirks in NumPy, particularly, value-based casting that occurs when one - argument of an operation is a 0-d array. + particular, the spec does not have any value-based casting. The spec also + does not require cross-kind casting, like integer -> floating-point. Only + those promotions that are explicitly required by the array API + specification are allowed in this module. See NEP 47 for more info. + + - Functions do not automatically call asarray() on their input, and will not + work if the input type is not Array. The exception is array creation + functions, and Python operators on the Array object, which accept Python + scalars of the same type as the array dtype. - All functions include type annotations, corresponding to those given in the spec (see _typing.py for definitions of some custom types). These do not @@ -93,26 +91,31 @@ request. equality, but it was considered too much extra complexity to create custom objects to represent dtypes. -- The wrapper functions in this module do not do any type checking for things - that would be impossible without leaving the array_api namespace. For - example, since the array API dtype objects are just the NumPy dtype objects, - one could pass in a non-spec NumPy dtype into a function. - - All places where the implementations in this submodule are known to deviate - from their corresponding functions in NumPy are marked with "# Note" - comments. Reviewers should make note of these comments. + from their corresponding functions in NumPy are marked with "# Note:" + comments. Still TODO in this module are: -- Device support and DLPack support are not yet implemented. These require - support in NumPy itself first. +- DLPack support for numpy.ndarray is still in progress. See + https://github.com/numpy/numpy/pull/19083. -- The a non-default value for the `copy` keyword argument is not yet - implemented on asarray. This requires support in numpy.asarray() first. +- The copy=False keyword argument to asarray() is not yet implemented. This + requires support in numpy.asarray() first. - Some functions are not yet fully tested in the array API test suite, and may require updates that are not yet known until the tests are written. +- The spec is still in an RFC phase and may still have minor updates, which + will need to be reflected here. + +- The linear algebra extension in the spec will be added in a future pull + request. + +- Complex number support in array API spec is planned but not yet finalized, + as are the fft extension and certain linear algebra functions such as eig + that require complex dtypes. + """ import sys -- cgit v1.2.1 From 8f7d00ed447174d9398af3365709222b529c1cad Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Fri, 6 Aug 2021 18:22:00 -0600 Subject: Run (selective) black on the array_api submodule I've omitted a few changes from black that messed up the readability of some complicated if statements that were organized logically line-by-line, and some changes that use unnecessary operator spacing. --- numpy/array_api/__init__.py | 247 ++++++++++++++++++--- numpy/array_api/_array_object.py | 205 ++++++++++------- numpy/array_api/_creation_functions.py | 158 ++++++++++--- numpy/array_api/_data_type_functions.py | 16 +- numpy/array_api/_dtypes.py | 75 +++++-- numpy/array_api/_elementwise_functions.py | 194 ++++++++++------ numpy/array_api/_linear_algebra_functions.py | 16 +- numpy/array_api/_manipulation_functions.py | 18 +- numpy/array_api/_searching_functions.py | 4 + numpy/array_api/_set_functions.py | 18 +- numpy/array_api/_sorting_functions.py | 14 +- numpy/array_api/_statistical_functions.py | 65 +++++- numpy/array_api/_typing.py | 30 ++- numpy/array_api/_utility_functions.py | 18 +- numpy/array_api/setup.py | 10 +- numpy/array_api/tests/test_array_object.py | 101 +++++---- numpy/array_api/tests/test_creation_functions.py | 122 ++++++---- .../array_api/tests/test_elementwise_functions.py | 133 ++++++----- 18 files changed, 1054 insertions(+), 390 deletions(-) (limited to 'numpy') diff --git a/numpy/array_api/__init__.py b/numpy/array_api/__init__.py index 4dc931732..53c1f3850 100644 --- a/numpy/array_api/__init__.py +++ b/numpy/array_api/__init__.py @@ -119,36 +119,221 @@ Still TODO in this module are: """ import sys + # numpy.array_api is 3.8+ because it makes extensive use of positional-only # arguments. if sys.version_info < (3, 8): raise ImportError("The numpy.array_api submodule requires Python 3.8 or greater.") import warnings -warnings.warn("The numpy.array_api submodule is still experimental. See NEP 47.", - stacklevel=2) + +warnings.warn( + "The numpy.array_api submodule is still experimental. See NEP 47.", stacklevel=2 +) __all__ = [] from ._constants import e, inf, nan, pi -__all__ += ['e', 'inf', 'nan', 'pi'] - -from ._creation_functions import asarray, arange, empty, empty_like, eye, from_dlpack, full, full_like, linspace, meshgrid, ones, ones_like, zeros, zeros_like - -__all__ += ['asarray', 'arange', 'empty', 'empty_like', 'eye', 'from_dlpack', 'full', 'full_like', 'linspace', 'meshgrid', 'ones', 'ones_like', 'zeros', 'zeros_like'] - -from ._data_type_functions import broadcast_arrays, broadcast_to, can_cast, finfo, iinfo, result_type - -__all__ += ['broadcast_arrays', 'broadcast_to', 'can_cast', 'finfo', 'iinfo', 'result_type'] - -from ._dtypes import int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64, bool - -__all__ += ['int8', 'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64', 'float32', 'float64', 'bool'] - -from ._elementwise_functions import abs, acos, acosh, add, asin, asinh, atan, atan2, atanh, bitwise_and, bitwise_left_shift, bitwise_invert, bitwise_or, bitwise_right_shift, bitwise_xor, ceil, cos, cosh, divide, equal, exp, expm1, floor, floor_divide, greater, greater_equal, isfinite, isinf, isnan, less, less_equal, log, log1p, log2, log10, logaddexp, logical_and, logical_not, logical_or, logical_xor, multiply, negative, not_equal, positive, pow, remainder, round, sign, sin, sinh, square, sqrt, subtract, tan, tanh, trunc - -__all__ += ['abs', 'acos', 'acosh', 'add', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'bitwise_and', 'bitwise_left_shift', 'bitwise_invert', 'bitwise_or', 'bitwise_right_shift', 'bitwise_xor', 'ceil', 'cos', 'cosh', 'divide', 'equal', 'exp', 'expm1', 'floor', 'floor_divide', 'greater', 'greater_equal', 'isfinite', 'isinf', 'isnan', 'less', 'less_equal', 'log', 'log1p', 'log2', 'log10', 'logaddexp', 'logical_and', 'logical_not', 'logical_or', 'logical_xor', 'multiply', 'negative', 'not_equal', 'positive', 'pow', 'remainder', 'round', 'sign', 'sin', 'sinh', 'square', 'sqrt', 'subtract', 'tan', 'tanh', 'trunc'] +__all__ += ["e", "inf", "nan", "pi"] + +from ._creation_functions import ( + asarray, + arange, + empty, + empty_like, + eye, + from_dlpack, + full, + full_like, + linspace, + meshgrid, + ones, + ones_like, + zeros, + zeros_like, +) + +__all__ += [ + "asarray", + "arange", + "empty", + "empty_like", + "eye", + "from_dlpack", + "full", + "full_like", + "linspace", + "meshgrid", + "ones", + "ones_like", + "zeros", + "zeros_like", +] + +from ._data_type_functions import ( + broadcast_arrays, + broadcast_to, + can_cast, + finfo, + iinfo, + result_type, +) + +__all__ += [ + "broadcast_arrays", + "broadcast_to", + "can_cast", + "finfo", + "iinfo", + "result_type", +] + +from ._dtypes import ( + int8, + int16, + int32, + int64, + uint8, + uint16, + uint32, + uint64, + float32, + float64, + bool, +) + +__all__ += [ + "int8", + "int16", + "int32", + "int64", + "uint8", + "uint16", + "uint32", + "uint64", + "float32", + "float64", + "bool", +] + +from ._elementwise_functions import ( + abs, + acos, + acosh, + add, + asin, + asinh, + atan, + atan2, + atanh, + bitwise_and, + bitwise_left_shift, + bitwise_invert, + bitwise_or, + bitwise_right_shift, + bitwise_xor, + ceil, + cos, + cosh, + divide, + equal, + exp, + expm1, + floor, + floor_divide, + greater, + greater_equal, + isfinite, + isinf, + isnan, + less, + less_equal, + log, + log1p, + log2, + log10, + logaddexp, + logical_and, + logical_not, + logical_or, + logical_xor, + multiply, + negative, + not_equal, + positive, + pow, + remainder, + round, + sign, + sin, + sinh, + square, + sqrt, + subtract, + tan, + tanh, + trunc, +) + +__all__ += [ + "abs", + "acos", + "acosh", + "add", + "asin", + "asinh", + "atan", + "atan2", + "atanh", + "bitwise_and", + "bitwise_left_shift", + "bitwise_invert", + "bitwise_or", + "bitwise_right_shift", + "bitwise_xor", + "ceil", + "cos", + "cosh", + "divide", + "equal", + "exp", + "expm1", + "floor", + "floor_divide", + "greater", + "greater_equal", + "isfinite", + "isinf", + "isnan", + "less", + "less_equal", + "log", + "log1p", + "log2", + "log10", + "logaddexp", + "logical_and", + "logical_not", + "logical_or", + "logical_xor", + "multiply", + "negative", + "not_equal", + "positive", + "pow", + "remainder", + "round", + "sign", + "sin", + "sinh", + "square", + "sqrt", + "subtract", + "tan", + "tanh", + "trunc", +] # einsum is not yet implemented in the array API spec. @@ -157,28 +342,36 @@ __all__ += ['abs', 'acos', 'acosh', 'add', 'asin', 'asinh', 'atan', 'atan2', 'at from ._linear_algebra_functions import matmul, tensordot, transpose, vecdot -__all__ += ['matmul', 'tensordot', 'transpose', 'vecdot'] +__all__ += ["matmul", "tensordot", "transpose", "vecdot"] -from ._manipulation_functions import concat, expand_dims, flip, reshape, roll, squeeze, stack +from ._manipulation_functions import ( + concat, + expand_dims, + flip, + reshape, + roll, + squeeze, + stack, +) -__all__ += ['concat', 'expand_dims', 'flip', 'reshape', 'roll', 'squeeze', 'stack'] +__all__ += ["concat", "expand_dims", "flip", "reshape", "roll", "squeeze", "stack"] from ._searching_functions import argmax, argmin, nonzero, where -__all__ += ['argmax', 'argmin', 'nonzero', 'where'] +__all__ += ["argmax", "argmin", "nonzero", "where"] from ._set_functions import unique -__all__ += ['unique'] +__all__ += ["unique"] from ._sorting_functions import argsort, sort -__all__ += ['argsort', 'sort'] +__all__ += ["argsort", "sort"] from ._statistical_functions import max, mean, min, prod, std, sum, var -__all__ += ['max', 'mean', 'min', 'prod', 'std', 'sum', 'var'] +__all__ += ["max", "mean", "min", "prod", "std", "sum", "var"] from ._utility_functions import all, any -__all__ += ['all', 'any'] +__all__ += ["all", "any"] diff --git a/numpy/array_api/_array_object.py b/numpy/array_api/_array_object.py index 00f50eade..0f511a577 100644 --- a/numpy/array_api/_array_object.py +++ b/numpy/array_api/_array_object.py @@ -18,11 +18,19 @@ from __future__ import annotations import operator from enum import IntEnum from ._creation_functions import asarray -from ._dtypes import (_all_dtypes, _boolean_dtypes, _integer_dtypes, - _integer_or_boolean_dtypes, _floating_dtypes, - _numeric_dtypes, _result_type, _dtype_categories) +from ._dtypes import ( + _all_dtypes, + _boolean_dtypes, + _integer_dtypes, + _integer_or_boolean_dtypes, + _floating_dtypes, + _numeric_dtypes, + _result_type, + _dtype_categories, +) from typing import TYPE_CHECKING, Optional, Tuple, Union + if TYPE_CHECKING: from ._typing import PyCapsule, Device, Dtype @@ -30,6 +38,7 @@ import numpy as np from numpy import array_api + class Array: """ n-d array object for the array API namespace. @@ -45,6 +54,7 @@ class Array: functions, such as asarray(). """ + # Use a custom constructor instead of __init__, as manually initializing # this class is not supported API. @classmethod @@ -64,13 +74,17 @@ class Array: # Convert the array scalar to a 0-D array x = np.asarray(x) if x.dtype not in _all_dtypes: - raise TypeError(f"The array_api namespace does not support the dtype '{x.dtype}'") + raise TypeError( + f"The array_api namespace does not support the dtype '{x.dtype}'" + ) obj._array = x return obj # Prevent Array() from working def __new__(cls, *args, **kwargs): - raise TypeError("The array_api Array object should not be instantiated directly. Use an array creation function, such as asarray(), instead.") + raise TypeError( + "The array_api Array object should not be instantiated directly. Use an array creation function, such as asarray(), instead." + ) # These functions are not required by the spec, but are implemented for # the sake of usability. @@ -79,7 +93,7 @@ class Array: """ Performs the operation __str__. """ - return self._array.__str__().replace('array', 'Array') + return self._array.__str__().replace("array", "Array") def __repr__(self: Array, /) -> str: """ @@ -103,12 +117,12 @@ class Array: """ if self.dtype not in _dtype_categories[dtype_category]: - raise TypeError(f'Only {dtype_category} dtypes are allowed in {op}') + raise TypeError(f"Only {dtype_category} dtypes are allowed in {op}") if isinstance(other, (int, float, bool)): other = self._promote_scalar(other) elif isinstance(other, Array): if other.dtype not in _dtype_categories[dtype_category]: - raise TypeError(f'Only {dtype_category} dtypes are allowed in {op}') + raise TypeError(f"Only {dtype_category} dtypes are allowed in {op}") else: return NotImplemented @@ -116,7 +130,7 @@ class Array: # to promote in the spec (even if the NumPy array operator would # promote them). res_dtype = _result_type(self.dtype, other.dtype) - if op.startswith('__i'): + if op.startswith("__i"): # Note: NumPy will allow in-place operators in some cases where # the type promoted operator does not match the left-hand side # operand. For example, @@ -126,7 +140,9 @@ class Array: # The spec explicitly disallows this. if res_dtype != self.dtype: - raise TypeError(f"Cannot perform {op} with dtypes {self.dtype} and {other.dtype}") + raise TypeError( + f"Cannot perform {op} with dtypes {self.dtype} and {other.dtype}" + ) return other @@ -142,13 +158,19 @@ class Array: """ if isinstance(scalar, bool): if self.dtype not in _boolean_dtypes: - raise TypeError("Python bool scalars can only be promoted with bool arrays") + raise TypeError( + "Python bool scalars can only be promoted with bool arrays" + ) elif isinstance(scalar, int): if self.dtype in _boolean_dtypes: - raise TypeError("Python int scalars cannot be promoted with bool arrays") + raise TypeError( + "Python int scalars cannot be promoted with bool arrays" + ) elif isinstance(scalar, float): if self.dtype not in _floating_dtypes: - raise TypeError("Python float scalars can only be promoted with floating-point arrays.") + raise TypeError( + "Python float scalars can only be promoted with floating-point arrays." + ) else: raise TypeError("'scalar' must be a Python scalar") @@ -253,7 +275,9 @@ class Array: except TypeError: return key if not (-size <= key.start <= max(0, size - 1)): - raise IndexError("Slices with out-of-bounds start are not allowed in the array API namespace") + raise IndexError( + "Slices with out-of-bounds start are not allowed in the array API namespace" + ) if key.stop is not None: try: operator.index(key.stop) @@ -269,12 +293,20 @@ class Array: key = tuple(Array._validate_index(idx, None) for idx in key) for idx in key: - if isinstance(idx, np.ndarray) and idx.dtype in _boolean_dtypes or isinstance(idx, (bool, np.bool_)): + if ( + isinstance(idx, np.ndarray) + and idx.dtype in _boolean_dtypes + or isinstance(idx, (bool, np.bool_)) + ): if len(key) == 1: return key - raise IndexError("Boolean array indices combined with other indices are not allowed in the array API namespace") + raise IndexError( + "Boolean array indices combined with other indices are not allowed in the array API namespace" + ) if isinstance(idx, tuple): - raise IndexError("Nested tuple indices are not allowed in the array API namespace") + raise IndexError( + "Nested tuple indices are not allowed in the array API namespace" + ) if shape is None: return key @@ -283,7 +315,9 @@ class Array: return key ellipsis_i = key.index(...) if n_ellipsis else len(key) - for idx, size in list(zip(key[:ellipsis_i], shape)) + list(zip(key[:ellipsis_i:-1], shape[:ellipsis_i:-1])): + for idx, size in list(zip(key[:ellipsis_i], shape)) + list( + zip(key[:ellipsis_i:-1], shape[:ellipsis_i:-1]) + ): Array._validate_index(idx, (size,)) return key elif isinstance(key, bool): @@ -291,18 +325,24 @@ class Array: elif isinstance(key, Array): if key.dtype in _integer_dtypes: if key.ndim != 0: - raise IndexError("Non-zero dimensional integer array indices are not allowed in the array API namespace") + raise IndexError( + "Non-zero dimensional integer array indices are not allowed in the array API namespace" + ) return key._array elif key is Ellipsis: return key elif key is None: - raise IndexError("newaxis indices are not allowed in the array API namespace") + raise IndexError( + "newaxis indices are not allowed in the array API namespace" + ) try: return operator.index(key) except TypeError: # Note: This also omits boolean arrays that are not already in # Array() form, like a list of booleans. - raise IndexError("Only integers, slices (`:`), ellipsis (`...`), and boolean arrays are valid indices in the array API namespace") + raise IndexError( + "Only integers, slices (`:`), ellipsis (`...`), and boolean arrays are valid indices in the array API namespace" + ) # Everything below this line is required by the spec. @@ -311,7 +351,7 @@ class Array: Performs the operation __abs__. """ if self.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in __abs__') + raise TypeError("Only numeric dtypes are allowed in __abs__") res = self._array.__abs__() return self.__class__._new(res) @@ -319,7 +359,7 @@ class Array: """ Performs the operation __add__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__add__') + other = self._check_allowed_dtypes(other, "numeric", "__add__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -330,15 +370,17 @@ class Array: """ Performs the operation __and__. """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__and__') + other = self._check_allowed_dtypes(other, "integer or boolean", "__and__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) res = self._array.__and__(other._array) return self.__class__._new(res) - def __array_namespace__(self: Array, /, *, api_version: Optional[str] = None) -> object: - if api_version is not None and not api_version.startswith('2021.'): + def __array_namespace__( + self: Array, /, *, api_version: Optional[str] = None + ) -> object: + if api_version is not None and not api_version.startswith("2021."): raise ValueError(f"Unrecognized array API version: {api_version!r}") return array_api @@ -373,7 +415,7 @@ class Array: """ # Even though "all" dtypes are allowed, we still require them to be # promotable with each other. - other = self._check_allowed_dtypes(other, 'all', '__eq__') + other = self._check_allowed_dtypes(other, "all", "__eq__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -394,7 +436,7 @@ class Array: """ Performs the operation __floordiv__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__floordiv__') + other = self._check_allowed_dtypes(other, "numeric", "__floordiv__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -405,14 +447,20 @@ class Array: """ Performs the operation __ge__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__ge__') + other = self._check_allowed_dtypes(other, "numeric", "__ge__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) res = self._array.__ge__(other._array) return self.__class__._new(res) - def __getitem__(self: Array, key: Union[int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], Array], /) -> Array: + def __getitem__( + self: Array, + key: Union[ + int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], Array + ], + /, + ) -> Array: """ Performs the operation __getitem__. """ @@ -426,7 +474,7 @@ class Array: """ Performs the operation __gt__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__gt__') + other = self._check_allowed_dtypes(other, "numeric", "__gt__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -448,7 +496,7 @@ class Array: Performs the operation __invert__. """ if self.dtype not in _integer_or_boolean_dtypes: - raise TypeError('Only integer or boolean dtypes are allowed in __invert__') + raise TypeError("Only integer or boolean dtypes are allowed in __invert__") res = self._array.__invert__() return self.__class__._new(res) @@ -456,7 +504,7 @@ class Array: """ Performs the operation __le__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__le__') + other = self._check_allowed_dtypes(other, "numeric", "__le__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -474,7 +522,7 @@ class Array: """ Performs the operation __lshift__. """ - other = self._check_allowed_dtypes(other, 'integer', '__lshift__') + other = self._check_allowed_dtypes(other, "integer", "__lshift__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -485,7 +533,7 @@ class Array: """ Performs the operation __lt__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__lt__') + other = self._check_allowed_dtypes(other, "numeric", "__lt__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -498,7 +546,7 @@ class Array: """ # matmul is not defined for scalars, but without this, we may get # the wrong error message from asarray. - other = self._check_allowed_dtypes(other, 'numeric', '__matmul__') + other = self._check_allowed_dtypes(other, "numeric", "__matmul__") if other is NotImplemented: return other res = self._array.__matmul__(other._array) @@ -508,7 +556,7 @@ class Array: """ Performs the operation __mod__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__mod__') + other = self._check_allowed_dtypes(other, "numeric", "__mod__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -519,7 +567,7 @@ class Array: """ Performs the operation __mul__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__mul__') + other = self._check_allowed_dtypes(other, "numeric", "__mul__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -530,7 +578,7 @@ class Array: """ Performs the operation __ne__. """ - other = self._check_allowed_dtypes(other, 'all', '__ne__') + other = self._check_allowed_dtypes(other, "all", "__ne__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -542,7 +590,7 @@ class Array: Performs the operation __neg__. """ if self.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in __neg__') + raise TypeError("Only numeric dtypes are allowed in __neg__") res = self._array.__neg__() return self.__class__._new(res) @@ -550,7 +598,7 @@ class Array: """ Performs the operation __or__. """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__or__') + other = self._check_allowed_dtypes(other, "integer or boolean", "__or__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -562,7 +610,7 @@ class Array: Performs the operation __pos__. """ if self.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in __pos__') + raise TypeError("Only numeric dtypes are allowed in __pos__") res = self._array.__pos__() return self.__class__._new(res) @@ -574,7 +622,7 @@ class Array: """ from ._elementwise_functions import pow - other = self._check_allowed_dtypes(other, 'floating-point', '__pow__') + other = self._check_allowed_dtypes(other, "floating-point", "__pow__") if other is NotImplemented: return other # Note: NumPy's __pow__ does not follow type promotion rules for 0-d @@ -585,14 +633,21 @@ class Array: """ Performs the operation __rshift__. """ - other = self._check_allowed_dtypes(other, 'integer', '__rshift__') + other = self._check_allowed_dtypes(other, "integer", "__rshift__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) res = self._array.__rshift__(other._array) return self.__class__._new(res) - def __setitem__(self, key: Union[int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], Array], value: Union[int, float, bool, Array], /) -> Array: + def __setitem__( + self, + key: Union[ + int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], Array + ], + value: Union[int, float, bool, Array], + /, + ) -> Array: """ Performs the operation __setitem__. """ @@ -605,7 +660,7 @@ class Array: """ Performs the operation __sub__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__sub__') + other = self._check_allowed_dtypes(other, "numeric", "__sub__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -618,7 +673,7 @@ class Array: """ Performs the operation __truediv__. """ - other = self._check_allowed_dtypes(other, 'floating-point', '__truediv__') + other = self._check_allowed_dtypes(other, "floating-point", "__truediv__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -629,7 +684,7 @@ class Array: """ Performs the operation __xor__. """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__xor__') + other = self._check_allowed_dtypes(other, "integer or boolean", "__xor__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -640,7 +695,7 @@ class Array: """ Performs the operation __iadd__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__iadd__') + other = self._check_allowed_dtypes(other, "numeric", "__iadd__") if other is NotImplemented: return other self._array.__iadd__(other._array) @@ -650,7 +705,7 @@ class Array: """ Performs the operation __radd__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__radd__') + other = self._check_allowed_dtypes(other, "numeric", "__radd__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -661,7 +716,7 @@ class Array: """ Performs the operation __iand__. """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__iand__') + other = self._check_allowed_dtypes(other, "integer or boolean", "__iand__") if other is NotImplemented: return other self._array.__iand__(other._array) @@ -671,7 +726,7 @@ class Array: """ Performs the operation __rand__. """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__rand__') + other = self._check_allowed_dtypes(other, "integer or boolean", "__rand__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -682,7 +737,7 @@ class Array: """ Performs the operation __ifloordiv__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__ifloordiv__') + other = self._check_allowed_dtypes(other, "numeric", "__ifloordiv__") if other is NotImplemented: return other self._array.__ifloordiv__(other._array) @@ -692,7 +747,7 @@ class Array: """ Performs the operation __rfloordiv__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__rfloordiv__') + other = self._check_allowed_dtypes(other, "numeric", "__rfloordiv__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -703,7 +758,7 @@ class Array: """ Performs the operation __ilshift__. """ - other = self._check_allowed_dtypes(other, 'integer', '__ilshift__') + other = self._check_allowed_dtypes(other, "integer", "__ilshift__") if other is NotImplemented: return other self._array.__ilshift__(other._array) @@ -713,7 +768,7 @@ class Array: """ Performs the operation __rlshift__. """ - other = self._check_allowed_dtypes(other, 'integer', '__rlshift__') + other = self._check_allowed_dtypes(other, "integer", "__rlshift__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -728,7 +783,7 @@ class Array: # matmul is not defined for scalars, but without this, we may get # the wrong error message from asarray. - other = self._check_allowed_dtypes(other, 'numeric', '__imatmul__') + other = self._check_allowed_dtypes(other, "numeric", "__imatmul__") if other is NotImplemented: return other @@ -748,7 +803,7 @@ class Array: """ # matmul is not defined for scalars, but without this, we may get # the wrong error message from asarray. - other = self._check_allowed_dtypes(other, 'numeric', '__rmatmul__') + other = self._check_allowed_dtypes(other, "numeric", "__rmatmul__") if other is NotImplemented: return other res = self._array.__rmatmul__(other._array) @@ -758,7 +813,7 @@ class Array: """ Performs the operation __imod__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__imod__') + other = self._check_allowed_dtypes(other, "numeric", "__imod__") if other is NotImplemented: return other self._array.__imod__(other._array) @@ -768,7 +823,7 @@ class Array: """ Performs the operation __rmod__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__rmod__') + other = self._check_allowed_dtypes(other, "numeric", "__rmod__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -779,7 +834,7 @@ class Array: """ Performs the operation __imul__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__imul__') + other = self._check_allowed_dtypes(other, "numeric", "__imul__") if other is NotImplemented: return other self._array.__imul__(other._array) @@ -789,7 +844,7 @@ class Array: """ Performs the operation __rmul__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__rmul__') + other = self._check_allowed_dtypes(other, "numeric", "__rmul__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -800,7 +855,7 @@ class Array: """ Performs the operation __ior__. """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__ior__') + other = self._check_allowed_dtypes(other, "integer or boolean", "__ior__") if other is NotImplemented: return other self._array.__ior__(other._array) @@ -810,7 +865,7 @@ class Array: """ Performs the operation __ror__. """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__ror__') + other = self._check_allowed_dtypes(other, "integer or boolean", "__ror__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -821,7 +876,7 @@ class Array: """ Performs the operation __ipow__. """ - other = self._check_allowed_dtypes(other, 'floating-point', '__ipow__') + other = self._check_allowed_dtypes(other, "floating-point", "__ipow__") if other is NotImplemented: return other self._array.__ipow__(other._array) @@ -833,7 +888,7 @@ class Array: """ from ._elementwise_functions import pow - other = self._check_allowed_dtypes(other, 'floating-point', '__rpow__') + other = self._check_allowed_dtypes(other, "floating-point", "__rpow__") if other is NotImplemented: return other # Note: NumPy's __pow__ does not follow the spec type promotion rules @@ -844,7 +899,7 @@ class Array: """ Performs the operation __irshift__. """ - other = self._check_allowed_dtypes(other, 'integer', '__irshift__') + other = self._check_allowed_dtypes(other, "integer", "__irshift__") if other is NotImplemented: return other self._array.__irshift__(other._array) @@ -854,7 +909,7 @@ class Array: """ Performs the operation __rrshift__. """ - other = self._check_allowed_dtypes(other, 'integer', '__rrshift__') + other = self._check_allowed_dtypes(other, "integer", "__rrshift__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -865,7 +920,7 @@ class Array: """ Performs the operation __isub__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__isub__') + other = self._check_allowed_dtypes(other, "numeric", "__isub__") if other is NotImplemented: return other self._array.__isub__(other._array) @@ -875,7 +930,7 @@ class Array: """ Performs the operation __rsub__. """ - other = self._check_allowed_dtypes(other, 'numeric', '__rsub__') + other = self._check_allowed_dtypes(other, "numeric", "__rsub__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -886,7 +941,7 @@ class Array: """ Performs the operation __itruediv__. """ - other = self._check_allowed_dtypes(other, 'floating-point', '__itruediv__') + other = self._check_allowed_dtypes(other, "floating-point", "__itruediv__") if other is NotImplemented: return other self._array.__itruediv__(other._array) @@ -896,7 +951,7 @@ class Array: """ Performs the operation __rtruediv__. """ - other = self._check_allowed_dtypes(other, 'floating-point', '__rtruediv__') + other = self._check_allowed_dtypes(other, "floating-point", "__rtruediv__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -907,7 +962,7 @@ class Array: """ Performs the operation __ixor__. """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__ixor__') + other = self._check_allowed_dtypes(other, "integer or boolean", "__ixor__") if other is NotImplemented: return other self._array.__ixor__(other._array) @@ -917,7 +972,7 @@ class Array: """ Performs the operation __rxor__. """ - other = self._check_allowed_dtypes(other, 'integer or boolean', '__rxor__') + other = self._check_allowed_dtypes(other, "integer or boolean", "__rxor__") if other is NotImplemented: return other self, other = self._normalize_two_args(self, other) @@ -935,7 +990,7 @@ class Array: @property def device(self) -> Device: - return 'cpu' + return "cpu" @property def ndim(self) -> int: diff --git a/numpy/array_api/_creation_functions.py b/numpy/array_api/_creation_functions.py index acf78056a..e9c01e7e6 100644 --- a/numpy/array_api/_creation_functions.py +++ b/numpy/array_api/_creation_functions.py @@ -2,14 +2,22 @@ from __future__ import annotations from typing import TYPE_CHECKING, List, Optional, Tuple, Union + if TYPE_CHECKING: - from ._typing import (Array, Device, Dtype, NestedSequence, - SupportsDLPack, SupportsBufferProtocol) + from ._typing import ( + Array, + Device, + Dtype, + NestedSequence, + SupportsDLPack, + SupportsBufferProtocol, + ) from collections.abc import Sequence from ._dtypes import _all_dtypes import numpy as np + def _check_valid_dtype(dtype): # Note: Only spelling dtypes as the dtype objects is supported. @@ -20,7 +28,23 @@ def _check_valid_dtype(dtype): return raise ValueError("dtype must be one of the supported dtypes") -def asarray(obj: Union[Array, bool, int, float, NestedSequence[bool|int|float], SupportsDLPack, SupportsBufferProtocol], /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, copy: Optional[bool] = None) -> Array: + +def asarray( + obj: Union[ + Array, + bool, + int, + float, + NestedSequence[bool | int | float], + SupportsDLPack, + SupportsBufferProtocol, + ], + /, + *, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, + copy: Optional[bool] = None, +) -> Array: """ Array API compatible wrapper for :py:func:`np.asarray `. @@ -31,7 +55,7 @@ def asarray(obj: Union[Array, bool, int, float, NestedSequence[bool|int|float], from ._array_object import Array _check_valid_dtype(dtype) - if device not in ['cpu', None]: + if device not in ["cpu", None]: raise ValueError(f"Unsupported device {device!r}") if copy is False: # Note: copy=False is not yet implemented in np.asarray @@ -40,14 +64,23 @@ def asarray(obj: Union[Array, bool, int, float, NestedSequence[bool|int|float], if copy is True: return Array._new(np.array(obj._array, copy=True, dtype=dtype)) return obj - if dtype is None and isinstance(obj, int) and (obj > 2**64 or obj < -2**63): + if dtype is None and isinstance(obj, int) and (obj > 2 ** 64 or obj < -(2 ** 63)): # Give a better error message in this case. NumPy would convert this # to an object array. TODO: This won't handle large integers in lists. raise OverflowError("Integer out of bounds for array dtypes") res = np.asarray(obj, dtype=dtype) return Array._new(res) -def arange(start: Union[int, float], /, stop: Optional[Union[int, float]] = None, step: Union[int, float] = 1, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + +def arange( + start: Union[int, float], + /, + stop: Optional[Union[int, float]] = None, + step: Union[int, float] = 1, + *, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, +) -> Array: """ Array API compatible wrapper for :py:func:`np.arange `. @@ -56,11 +89,17 @@ def arange(start: Union[int, float], /, stop: Optional[Union[int, float]] = None from ._array_object import Array _check_valid_dtype(dtype) - if device not in ['cpu', None]: + if device not in ["cpu", None]: raise ValueError(f"Unsupported device {device!r}") return Array._new(np.arange(start, stop=stop, step=step, dtype=dtype)) -def empty(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + +def empty( + shape: Union[int, Tuple[int, ...]], + *, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, +) -> Array: """ Array API compatible wrapper for :py:func:`np.empty `. @@ -69,11 +108,14 @@ def empty(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, from ._array_object import Array _check_valid_dtype(dtype) - if device not in ['cpu', None]: + if device not in ["cpu", None]: raise ValueError(f"Unsupported device {device!r}") return Array._new(np.empty(shape, dtype=dtype)) -def empty_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + +def empty_like( + x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None +) -> Array: """ Array API compatible wrapper for :py:func:`np.empty_like `. @@ -82,11 +124,20 @@ def empty_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[D from ._array_object import Array _check_valid_dtype(dtype) - if device not in ['cpu', None]: + if device not in ["cpu", None]: raise ValueError(f"Unsupported device {device!r}") return Array._new(np.empty_like(x._array, dtype=dtype)) -def eye(n_rows: int, n_cols: Optional[int] = None, /, *, k: Optional[int] = 0, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + +def eye( + n_rows: int, + n_cols: Optional[int] = None, + /, + *, + k: Optional[int] = 0, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, +) -> Array: """ Array API compatible wrapper for :py:func:`np.eye `. @@ -95,15 +146,23 @@ def eye(n_rows: int, n_cols: Optional[int] = None, /, *, k: Optional[int] = 0, d from ._array_object import Array _check_valid_dtype(dtype) - if device not in ['cpu', None]: + if device not in ["cpu", None]: raise ValueError(f"Unsupported device {device!r}") return Array._new(np.eye(n_rows, M=n_cols, k=k, dtype=dtype)) + def from_dlpack(x: object, /) -> Array: # Note: dlpack support is not yet implemented on Array raise NotImplementedError("DLPack support is not yet implemented") -def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + +def full( + shape: Union[int, Tuple[int, ...]], + fill_value: Union[int, float], + *, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, +) -> Array: """ Array API compatible wrapper for :py:func:`np.full `. @@ -112,7 +171,7 @@ def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], *, d from ._array_object import Array _check_valid_dtype(dtype) - if device not in ['cpu', None]: + if device not in ["cpu", None]: raise ValueError(f"Unsupported device {device!r}") if isinstance(fill_value, Array) and fill_value.ndim == 0: fill_value = fill_value._array @@ -123,7 +182,15 @@ def full(shape: Union[int, Tuple[int, ...]], fill_value: Union[int, float], *, d raise TypeError("Invalid input to full") return Array._new(res) -def full_like(x: Array, /, fill_value: Union[int, float], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + +def full_like( + x: Array, + /, + fill_value: Union[int, float], + *, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, +) -> Array: """ Array API compatible wrapper for :py:func:`np.full_like `. @@ -132,7 +199,7 @@ def full_like(x: Array, /, fill_value: Union[int, float], *, dtype: Optional[Dty from ._array_object import Array _check_valid_dtype(dtype) - if device not in ['cpu', None]: + if device not in ["cpu", None]: raise ValueError(f"Unsupported device {device!r}") res = np.full_like(x._array, fill_value, dtype=dtype) if res.dtype not in _all_dtypes: @@ -141,7 +208,17 @@ def full_like(x: Array, /, fill_value: Union[int, float], *, dtype: Optional[Dty raise TypeError("Invalid input to full_like") return Array._new(res) -def linspace(start: Union[int, float], stop: Union[int, float], /, num: int, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None, endpoint: bool = True) -> Array: + +def linspace( + start: Union[int, float], + stop: Union[int, float], + /, + num: int, + *, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, + endpoint: bool = True, +) -> Array: """ Array API compatible wrapper for :py:func:`np.linspace `. @@ -150,20 +227,31 @@ def linspace(start: Union[int, float], stop: Union[int, float], /, num: int, *, from ._array_object import Array _check_valid_dtype(dtype) - if device not in ['cpu', None]: + if device not in ["cpu", None]: raise ValueError(f"Unsupported device {device!r}") return Array._new(np.linspace(start, stop, num, dtype=dtype, endpoint=endpoint)) -def meshgrid(*arrays: Sequence[Array], indexing: str = 'xy') -> List[Array, ...]: + +def meshgrid(*arrays: Sequence[Array], indexing: str = "xy") -> List[Array, ...]: """ Array API compatible wrapper for :py:func:`np.meshgrid `. See its docstring for more information. """ from ._array_object import Array - return [Array._new(array) for array in np.meshgrid(*[a._array for a in arrays], indexing=indexing)] -def ones(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + return [ + Array._new(array) + for array in np.meshgrid(*[a._array for a in arrays], indexing=indexing) + ] + + +def ones( + shape: Union[int, Tuple[int, ...]], + *, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, +) -> Array: """ Array API compatible wrapper for :py:func:`np.ones `. @@ -172,11 +260,14 @@ def ones(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, d from ._array_object import Array _check_valid_dtype(dtype) - if device not in ['cpu', None]: + if device not in ["cpu", None]: raise ValueError(f"Unsupported device {device!r}") return Array._new(np.ones(shape, dtype=dtype)) -def ones_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + +def ones_like( + x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None +) -> Array: """ Array API compatible wrapper for :py:func:`np.ones_like `. @@ -185,11 +276,17 @@ def ones_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[De from ._array_object import Array _check_valid_dtype(dtype) - if device not in ['cpu', None]: + if device not in ["cpu", None]: raise ValueError(f"Unsupported device {device!r}") return Array._new(np.ones_like(x._array, dtype=dtype)) -def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + +def zeros( + shape: Union[int, Tuple[int, ...]], + *, + dtype: Optional[Dtype] = None, + device: Optional[Device] = None, +) -> Array: """ Array API compatible wrapper for :py:func:`np.zeros `. @@ -198,11 +295,14 @@ def zeros(shape: Union[int, Tuple[int, ...]], *, dtype: Optional[Dtype] = None, from ._array_object import Array _check_valid_dtype(dtype) - if device not in ['cpu', None]: + if device not in ["cpu", None]: raise ValueError(f"Unsupported device {device!r}") return Array._new(np.zeros(shape, dtype=dtype)) -def zeros_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None) -> Array: + +def zeros_like( + x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[Device] = None +) -> Array: """ Array API compatible wrapper for :py:func:`np.zeros_like `. @@ -211,6 +311,6 @@ def zeros_like(x: Array, /, *, dtype: Optional[Dtype] = None, device: Optional[D from ._array_object import Array _check_valid_dtype(dtype) - if device not in ['cpu', None]: + if device not in ["cpu", None]: raise ValueError(f"Unsupported device {device!r}") return Array._new(np.zeros_like(x._array, dtype=dtype)) diff --git a/numpy/array_api/_data_type_functions.py b/numpy/array_api/_data_type_functions.py index 17a00cc6d..e6121a8a4 100644 --- a/numpy/array_api/_data_type_functions.py +++ b/numpy/array_api/_data_type_functions.py @@ -5,12 +5,14 @@ from ._dtypes import _all_dtypes, _result_type from dataclasses import dataclass from typing import TYPE_CHECKING, List, Tuple, Union + if TYPE_CHECKING: from ._typing import Dtype from collections.abc import Sequence import numpy as np + def broadcast_arrays(*arrays: Sequence[Array]) -> List[Array]: """ Array API compatible wrapper for :py:func:`np.broadcast_arrays `. @@ -18,7 +20,11 @@ def broadcast_arrays(*arrays: Sequence[Array]) -> List[Array]: See its docstring for more information. """ from ._array_object import Array - return [Array._new(array) for array in np.broadcast_arrays(*[a._array for a in arrays])] + + return [ + Array._new(array) for array in np.broadcast_arrays(*[a._array for a in arrays]) + ] + def broadcast_to(x: Array, /, shape: Tuple[int, ...]) -> Array: """ @@ -27,8 +33,10 @@ def broadcast_to(x: Array, /, shape: Tuple[int, ...]) -> Array: See its docstring for more information. """ from ._array_object import Array + return Array._new(np.broadcast_to(x._array, shape)) + def can_cast(from_: Union[Dtype, Array], to: Dtype, /) -> bool: """ Array API compatible wrapper for :py:func:`np.can_cast `. @@ -36,10 +44,12 @@ def can_cast(from_: Union[Dtype, Array], to: Dtype, /) -> bool: See its docstring for more information. """ from ._array_object import Array + if isinstance(from_, Array): from_ = from_._array return np.can_cast(from_, to) + # These are internal objects for the return types of finfo and iinfo, since # the NumPy versions contain extra data that isn't part of the spec. @dataclass @@ -55,12 +65,14 @@ class finfo_object: # smallest_normal: float + @dataclass class iinfo_object: bits: int max: int min: int + def finfo(type: Union[Dtype, Array], /) -> finfo_object: """ Array API compatible wrapper for :py:func:`np.finfo `. @@ -79,6 +91,7 @@ def finfo(type: Union[Dtype, Array], /) -> finfo_object: # float(fi.smallest_normal), ) + def iinfo(type: Union[Dtype, Array], /) -> iinfo_object: """ Array API compatible wrapper for :py:func:`np.iinfo `. @@ -88,6 +101,7 @@ def iinfo(type: Union[Dtype, Array], /) -> iinfo_object: ii = np.iinfo(type) return iinfo_object(ii.bits, ii.max, ii.min) + def result_type(*arrays_and_dtypes: Sequence[Union[Array, Dtype]]) -> Dtype: """ Array API compatible wrapper for :py:func:`np.result_type `. diff --git a/numpy/array_api/_dtypes.py b/numpy/array_api/_dtypes.py index 07be267da..476d619fe 100644 --- a/numpy/array_api/_dtypes.py +++ b/numpy/array_api/_dtypes.py @@ -2,34 +2,66 @@ import numpy as np # Note: we use dtype objects instead of dtype classes. The spec does not # require any behavior on dtypes other than equality. -int8 = np.dtype('int8') -int16 = np.dtype('int16') -int32 = np.dtype('int32') -int64 = np.dtype('int64') -uint8 = np.dtype('uint8') -uint16 = np.dtype('uint16') -uint32 = np.dtype('uint32') -uint64 = np.dtype('uint64') -float32 = np.dtype('float32') -float64 = np.dtype('float64') +int8 = np.dtype("int8") +int16 = np.dtype("int16") +int32 = np.dtype("int32") +int64 = np.dtype("int64") +uint8 = np.dtype("uint8") +uint16 = np.dtype("uint16") +uint32 = np.dtype("uint32") +uint64 = np.dtype("uint64") +float32 = np.dtype("float32") +float64 = np.dtype("float64") # Note: This name is changed -bool = np.dtype('bool') +bool = np.dtype("bool") -_all_dtypes = (int8, int16, int32, int64, uint8, uint16, uint32, uint64, - float32, float64, bool) +_all_dtypes = ( + int8, + int16, + int32, + int64, + uint8, + uint16, + uint32, + uint64, + float32, + float64, + bool, +) _boolean_dtypes = (bool,) _floating_dtypes = (float32, float64) _integer_dtypes = (int8, int16, int32, int64, uint8, uint16, uint32, uint64) -_integer_or_boolean_dtypes = (bool, int8, int16, int32, int64, uint8, uint16, uint32, uint64) -_numeric_dtypes = (float32, float64, int8, int16, int32, int64, uint8, uint16, uint32, uint64) +_integer_or_boolean_dtypes = ( + bool, + int8, + int16, + int32, + int64, + uint8, + uint16, + uint32, + uint64, +) +_numeric_dtypes = ( + float32, + float64, + int8, + int16, + int32, + int64, + uint8, + uint16, + uint32, + uint64, +) _dtype_categories = { - 'all': _all_dtypes, - 'numeric': _numeric_dtypes, - 'integer': _integer_dtypes, - 'integer or boolean': _integer_or_boolean_dtypes, - 'boolean': _boolean_dtypes, - 'floating-point': _floating_dtypes, + "all": _all_dtypes, + "numeric": _numeric_dtypes, + "integer": _integer_dtypes, + "integer or boolean": _integer_or_boolean_dtypes, + "boolean": _boolean_dtypes, + "floating-point": _floating_dtypes, } @@ -104,6 +136,7 @@ _promotion_table = { (bool, bool): bool, } + def _result_type(type1, type2): if (type1, type2) in _promotion_table: return _promotion_table[type1, type2] diff --git a/numpy/array_api/_elementwise_functions.py b/numpy/array_api/_elementwise_functions.py index 7833ebe54..4408fe833 100644 --- a/numpy/array_api/_elementwise_functions.py +++ b/numpy/array_api/_elementwise_functions.py @@ -1,12 +1,18 @@ from __future__ import annotations -from ._dtypes import (_boolean_dtypes, _floating_dtypes, - _integer_dtypes, _integer_or_boolean_dtypes, - _numeric_dtypes, _result_type) +from ._dtypes import ( + _boolean_dtypes, + _floating_dtypes, + _integer_dtypes, + _integer_or_boolean_dtypes, + _numeric_dtypes, + _result_type, +) from ._array_object import Array import numpy as np + def abs(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.abs `. @@ -14,9 +20,10 @@ def abs(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in abs') + raise TypeError("Only numeric dtypes are allowed in abs") return Array._new(np.abs(x._array)) + # Note: the function name is different here def acos(x: Array, /) -> Array: """ @@ -25,9 +32,10 @@ def acos(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in acos') + raise TypeError("Only floating-point dtypes are allowed in acos") return Array._new(np.arccos(x._array)) + # Note: the function name is different here def acosh(x: Array, /) -> Array: """ @@ -36,9 +44,10 @@ def acosh(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in acosh') + raise TypeError("Only floating-point dtypes are allowed in acosh") return Array._new(np.arccosh(x._array)) + def add(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.add `. @@ -46,12 +55,13 @@ def add(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in add') + raise TypeError("Only numeric dtypes are allowed in add") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.add(x1._array, x2._array)) + # Note: the function name is different here def asin(x: Array, /) -> Array: """ @@ -60,9 +70,10 @@ def asin(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in asin') + raise TypeError("Only floating-point dtypes are allowed in asin") return Array._new(np.arcsin(x._array)) + # Note: the function name is different here def asinh(x: Array, /) -> Array: """ @@ -71,9 +82,10 @@ def asinh(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in asinh') + raise TypeError("Only floating-point dtypes are allowed in asinh") return Array._new(np.arcsinh(x._array)) + # Note: the function name is different here def atan(x: Array, /) -> Array: """ @@ -82,9 +94,10 @@ def atan(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in atan') + raise TypeError("Only floating-point dtypes are allowed in atan") return Array._new(np.arctan(x._array)) + # Note: the function name is different here def atan2(x1: Array, x2: Array, /) -> Array: """ @@ -93,12 +106,13 @@ def atan2(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in atan2') + raise TypeError("Only floating-point dtypes are allowed in atan2") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.arctan2(x1._array, x2._array)) + # Note: the function name is different here def atanh(x: Array, /) -> Array: """ @@ -107,22 +121,27 @@ def atanh(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in atanh') + raise TypeError("Only floating-point dtypes are allowed in atanh") return Array._new(np.arctanh(x._array)) + def bitwise_and(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.bitwise_and `. See its docstring for more information. """ - if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: - raise TypeError('Only integer or boolean dtypes are allowed in bitwise_and') + if ( + x1.dtype not in _integer_or_boolean_dtypes + or x2.dtype not in _integer_or_boolean_dtypes + ): + raise TypeError("Only integer or boolean dtypes are allowed in bitwise_and") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.bitwise_and(x1._array, x2._array)) + # Note: the function name is different here def bitwise_left_shift(x1: Array, x2: Array, /) -> Array: """ @@ -131,15 +150,16 @@ def bitwise_left_shift(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: - raise TypeError('Only integer dtypes are allowed in bitwise_left_shift') + raise TypeError("Only integer dtypes are allowed in bitwise_left_shift") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) # Note: bitwise_left_shift is only defined for x2 nonnegative. if np.any(x2._array < 0): - raise ValueError('bitwise_left_shift(x1, x2) is only defined for x2 >= 0') + raise ValueError("bitwise_left_shift(x1, x2) is only defined for x2 >= 0") return Array._new(np.left_shift(x1._array, x2._array)) + # Note: the function name is different here def bitwise_invert(x: Array, /) -> Array: """ @@ -148,22 +168,27 @@ def bitwise_invert(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _integer_or_boolean_dtypes: - raise TypeError('Only integer or boolean dtypes are allowed in bitwise_invert') + raise TypeError("Only integer or boolean dtypes are allowed in bitwise_invert") return Array._new(np.invert(x._array)) + def bitwise_or(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.bitwise_or `. See its docstring for more information. """ - if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: - raise TypeError('Only integer or boolean dtypes are allowed in bitwise_or') + if ( + x1.dtype not in _integer_or_boolean_dtypes + or x2.dtype not in _integer_or_boolean_dtypes + ): + raise TypeError("Only integer or boolean dtypes are allowed in bitwise_or") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.bitwise_or(x1._array, x2._array)) + # Note: the function name is different here def bitwise_right_shift(x1: Array, x2: Array, /) -> Array: """ @@ -172,28 +197,33 @@ def bitwise_right_shift(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _integer_dtypes or x2.dtype not in _integer_dtypes: - raise TypeError('Only integer dtypes are allowed in bitwise_right_shift') + raise TypeError("Only integer dtypes are allowed in bitwise_right_shift") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) # Note: bitwise_right_shift is only defined for x2 nonnegative. if np.any(x2._array < 0): - raise ValueError('bitwise_right_shift(x1, x2) is only defined for x2 >= 0') + raise ValueError("bitwise_right_shift(x1, x2) is only defined for x2 >= 0") return Array._new(np.right_shift(x1._array, x2._array)) + def bitwise_xor(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.bitwise_xor `. See its docstring for more information. """ - if x1.dtype not in _integer_or_boolean_dtypes or x2.dtype not in _integer_or_boolean_dtypes: - raise TypeError('Only integer or boolean dtypes are allowed in bitwise_xor') + if ( + x1.dtype not in _integer_or_boolean_dtypes + or x2.dtype not in _integer_or_boolean_dtypes + ): + raise TypeError("Only integer or boolean dtypes are allowed in bitwise_xor") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.bitwise_xor(x1._array, x2._array)) + def ceil(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.ceil `. @@ -201,12 +231,13 @@ def ceil(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in ceil') + raise TypeError("Only numeric dtypes are allowed in ceil") if x.dtype in _integer_dtypes: # Note: The return dtype of ceil is the same as the input return x return Array._new(np.ceil(x._array)) + def cos(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.cos `. @@ -214,9 +245,10 @@ def cos(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in cos') + raise TypeError("Only floating-point dtypes are allowed in cos") return Array._new(np.cos(x._array)) + def cosh(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.cosh `. @@ -224,9 +256,10 @@ def cosh(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in cosh') + raise TypeError("Only floating-point dtypes are allowed in cosh") return Array._new(np.cosh(x._array)) + def divide(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.divide `. @@ -234,12 +267,13 @@ def divide(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in divide') + raise TypeError("Only floating-point dtypes are allowed in divide") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.divide(x1._array, x2._array)) + def equal(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.equal `. @@ -251,6 +285,7 @@ def equal(x1: Array, x2: Array, /) -> Array: x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.equal(x1._array, x2._array)) + def exp(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.exp `. @@ -258,9 +293,10 @@ def exp(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in exp') + raise TypeError("Only floating-point dtypes are allowed in exp") return Array._new(np.exp(x._array)) + def expm1(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.expm1 `. @@ -268,9 +304,10 @@ def expm1(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in expm1') + raise TypeError("Only floating-point dtypes are allowed in expm1") return Array._new(np.expm1(x._array)) + def floor(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.floor `. @@ -278,12 +315,13 @@ def floor(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in floor') + raise TypeError("Only numeric dtypes are allowed in floor") if x.dtype in _integer_dtypes: # Note: The return dtype of floor is the same as the input return x return Array._new(np.floor(x._array)) + def floor_divide(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.floor_divide `. @@ -291,12 +329,13 @@ def floor_divide(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in floor_divide') + raise TypeError("Only numeric dtypes are allowed in floor_divide") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.floor_divide(x1._array, x2._array)) + def greater(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.greater `. @@ -304,12 +343,13 @@ def greater(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in greater') + raise TypeError("Only numeric dtypes are allowed in greater") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.greater(x1._array, x2._array)) + def greater_equal(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.greater_equal `. @@ -317,12 +357,13 @@ def greater_equal(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in greater_equal') + raise TypeError("Only numeric dtypes are allowed in greater_equal") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.greater_equal(x1._array, x2._array)) + def isfinite(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.isfinite `. @@ -330,9 +371,10 @@ def isfinite(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in isfinite') + raise TypeError("Only numeric dtypes are allowed in isfinite") return Array._new(np.isfinite(x._array)) + def isinf(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.isinf `. @@ -340,9 +382,10 @@ def isinf(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in isinf') + raise TypeError("Only numeric dtypes are allowed in isinf") return Array._new(np.isinf(x._array)) + def isnan(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.isnan `. @@ -350,9 +393,10 @@ def isnan(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in isnan') + raise TypeError("Only numeric dtypes are allowed in isnan") return Array._new(np.isnan(x._array)) + def less(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.less `. @@ -360,12 +404,13 @@ def less(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in less') + raise TypeError("Only numeric dtypes are allowed in less") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.less(x1._array, x2._array)) + def less_equal(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.less_equal `. @@ -373,12 +418,13 @@ def less_equal(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in less_equal') + raise TypeError("Only numeric dtypes are allowed in less_equal") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.less_equal(x1._array, x2._array)) + def log(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.log `. @@ -386,9 +432,10 @@ def log(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in log') + raise TypeError("Only floating-point dtypes are allowed in log") return Array._new(np.log(x._array)) + def log1p(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.log1p `. @@ -396,9 +443,10 @@ def log1p(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in log1p') + raise TypeError("Only floating-point dtypes are allowed in log1p") return Array._new(np.log1p(x._array)) + def log2(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.log2 `. @@ -406,9 +454,10 @@ def log2(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in log2') + raise TypeError("Only floating-point dtypes are allowed in log2") return Array._new(np.log2(x._array)) + def log10(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.log10 `. @@ -416,9 +465,10 @@ def log10(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in log10') + raise TypeError("Only floating-point dtypes are allowed in log10") return Array._new(np.log10(x._array)) + def logaddexp(x1: Array, x2: Array) -> Array: """ Array API compatible wrapper for :py:func:`np.logaddexp `. @@ -426,12 +476,13 @@ def logaddexp(x1: Array, x2: Array) -> Array: See its docstring for more information. """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in logaddexp') + raise TypeError("Only floating-point dtypes are allowed in logaddexp") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.logaddexp(x1._array, x2._array)) + def logical_and(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.logical_and `. @@ -439,12 +490,13 @@ def logical_and(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: - raise TypeError('Only boolean dtypes are allowed in logical_and') + raise TypeError("Only boolean dtypes are allowed in logical_and") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.logical_and(x1._array, x2._array)) + def logical_not(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.logical_not `. @@ -452,9 +504,10 @@ def logical_not(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _boolean_dtypes: - raise TypeError('Only boolean dtypes are allowed in logical_not') + raise TypeError("Only boolean dtypes are allowed in logical_not") return Array._new(np.logical_not(x._array)) + def logical_or(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.logical_or `. @@ -462,12 +515,13 @@ def logical_or(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: - raise TypeError('Only boolean dtypes are allowed in logical_or') + raise TypeError("Only boolean dtypes are allowed in logical_or") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.logical_or(x1._array, x2._array)) + def logical_xor(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.logical_xor `. @@ -475,12 +529,13 @@ def logical_xor(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _boolean_dtypes or x2.dtype not in _boolean_dtypes: - raise TypeError('Only boolean dtypes are allowed in logical_xor') + raise TypeError("Only boolean dtypes are allowed in logical_xor") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.logical_xor(x1._array, x2._array)) + def multiply(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.multiply `. @@ -488,12 +543,13 @@ def multiply(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in multiply') + raise TypeError("Only numeric dtypes are allowed in multiply") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.multiply(x1._array, x2._array)) + def negative(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.negative `. @@ -501,9 +557,10 @@ def negative(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in negative') + raise TypeError("Only numeric dtypes are allowed in negative") return Array._new(np.negative(x._array)) + def not_equal(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.not_equal `. @@ -515,6 +572,7 @@ def not_equal(x1: Array, x2: Array, /) -> Array: x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.not_equal(x1._array, x2._array)) + def positive(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.positive `. @@ -522,9 +580,10 @@ def positive(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in positive') + raise TypeError("Only numeric dtypes are allowed in positive") return Array._new(np.positive(x._array)) + # Note: the function name is different here def pow(x1: Array, x2: Array, /) -> Array: """ @@ -533,12 +592,13 @@ def pow(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _floating_dtypes or x2.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in pow') + raise TypeError("Only floating-point dtypes are allowed in pow") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.power(x1._array, x2._array)) + def remainder(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.remainder `. @@ -546,12 +606,13 @@ def remainder(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in remainder') + raise TypeError("Only numeric dtypes are allowed in remainder") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.remainder(x1._array, x2._array)) + def round(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.round `. @@ -559,9 +620,10 @@ def round(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in round') + raise TypeError("Only numeric dtypes are allowed in round") return Array._new(np.round(x._array)) + def sign(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.sign `. @@ -569,9 +631,10 @@ def sign(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in sign') + raise TypeError("Only numeric dtypes are allowed in sign") return Array._new(np.sign(x._array)) + def sin(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.sin `. @@ -579,9 +642,10 @@ def sin(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in sin') + raise TypeError("Only floating-point dtypes are allowed in sin") return Array._new(np.sin(x._array)) + def sinh(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.sinh `. @@ -589,9 +653,10 @@ def sinh(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in sinh') + raise TypeError("Only floating-point dtypes are allowed in sinh") return Array._new(np.sinh(x._array)) + def square(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.square `. @@ -599,9 +664,10 @@ def square(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in square') + raise TypeError("Only numeric dtypes are allowed in square") return Array._new(np.square(x._array)) + def sqrt(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.sqrt `. @@ -609,9 +675,10 @@ def sqrt(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in sqrt') + raise TypeError("Only floating-point dtypes are allowed in sqrt") return Array._new(np.sqrt(x._array)) + def subtract(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.subtract `. @@ -619,12 +686,13 @@ def subtract(x1: Array, x2: Array, /) -> Array: See its docstring for more information. """ if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in subtract') + raise TypeError("Only numeric dtypes are allowed in subtract") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) x1, x2 = Array._normalize_two_args(x1, x2) return Array._new(np.subtract(x1._array, x2._array)) + def tan(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.tan `. @@ -632,9 +700,10 @@ def tan(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in tan') + raise TypeError("Only floating-point dtypes are allowed in tan") return Array._new(np.tan(x._array)) + def tanh(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.tanh `. @@ -642,9 +711,10 @@ def tanh(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _floating_dtypes: - raise TypeError('Only floating-point dtypes are allowed in tanh') + raise TypeError("Only floating-point dtypes are allowed in tanh") return Array._new(np.tanh(x._array)) + def trunc(x: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.trunc `. @@ -652,7 +722,7 @@ def trunc(x: Array, /) -> Array: See its docstring for more information. """ if x.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in trunc') + raise TypeError("Only numeric dtypes are allowed in trunc") if x.dtype in _integer_dtypes: # Note: The return dtype of trunc is the same as the input return x diff --git a/numpy/array_api/_linear_algebra_functions.py b/numpy/array_api/_linear_algebra_functions.py index f13f9c541..089081725 100644 --- a/numpy/array_api/_linear_algebra_functions.py +++ b/numpy/array_api/_linear_algebra_functions.py @@ -17,6 +17,7 @@ import numpy as np # """ # return np.einsum() + def matmul(x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.matmul `. @@ -26,23 +27,31 @@ def matmul(x1: Array, x2: Array, /) -> Array: # Note: the restriction to numeric dtypes only is different from # np.matmul. if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in matmul') + raise TypeError("Only numeric dtypes are allowed in matmul") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) return Array._new(np.matmul(x1._array, x2._array)) + # Note: axes must be a tuple, unlike np.tensordot where it can be an array or array-like. -def tensordot(x1: Array, x2: Array, /, *, axes: Union[int, Tuple[Sequence[int], Sequence[int]]] = 2) -> Array: +def tensordot( + x1: Array, + x2: Array, + /, + *, + axes: Union[int, Tuple[Sequence[int], Sequence[int]]] = 2, +) -> Array: # Note: the restriction to numeric dtypes only is different from # np.tensordot. if x1.dtype not in _numeric_dtypes or x2.dtype not in _numeric_dtypes: - raise TypeError('Only numeric dtypes are allowed in tensordot') + raise TypeError("Only numeric dtypes are allowed in tensordot") # Call result type here just to raise on disallowed type combinations _result_type(x1.dtype, x2.dtype) return Array._new(np.tensordot(x1._array, x2._array, axes=axes)) + def transpose(x: Array, /, *, axes: Optional[Tuple[int, ...]] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.transpose `. @@ -51,6 +60,7 @@ def transpose(x: Array, /, *, axes: Optional[Tuple[int, ...]] = None) -> Array: """ return Array._new(np.transpose(x._array, axes=axes)) + # Note: vecdot is not in NumPy def vecdot(x1: Array, x2: Array, /, *, axis: Optional[int] = None) -> Array: if axis is None: diff --git a/numpy/array_api/_manipulation_functions.py b/numpy/array_api/_manipulation_functions.py index 33f5d5a28..c11866261 100644 --- a/numpy/array_api/_manipulation_functions.py +++ b/numpy/array_api/_manipulation_functions.py @@ -8,7 +8,9 @@ from typing import List, Optional, Tuple, Union import numpy as np # Note: the function name is different here -def concat(arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: Optional[int] = 0) -> Array: +def concat( + arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: Optional[int] = 0 +) -> Array: """ Array API compatible wrapper for :py:func:`np.concatenate `. @@ -20,6 +22,7 @@ def concat(arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: Optional[i arrays = tuple(a._array for a in arrays) return Array._new(np.concatenate(arrays, axis=axis, dtype=dtype)) + def expand_dims(x: Array, /, *, axis: int) -> Array: """ Array API compatible wrapper for :py:func:`np.expand_dims `. @@ -28,6 +31,7 @@ def expand_dims(x: Array, /, *, axis: int) -> Array: """ return Array._new(np.expand_dims(x._array, axis)) + def flip(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> Array: """ Array API compatible wrapper for :py:func:`np.flip `. @@ -36,6 +40,7 @@ def flip(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> """ return Array._new(np.flip(x._array, axis=axis)) + def reshape(x: Array, /, shape: Tuple[int, ...]) -> Array: """ Array API compatible wrapper for :py:func:`np.reshape `. @@ -44,7 +49,14 @@ def reshape(x: Array, /, shape: Tuple[int, ...]) -> Array: """ return Array._new(np.reshape(x._array, shape)) -def roll(x: Array, /, shift: Union[int, Tuple[int, ...]], *, axis: Optional[Union[int, Tuple[int, ...]]] = None) -> Array: + +def roll( + x: Array, + /, + shift: Union[int, Tuple[int, ...]], + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, +) -> Array: """ Array API compatible wrapper for :py:func:`np.roll `. @@ -52,6 +64,7 @@ def roll(x: Array, /, shift: Union[int, Tuple[int, ...]], *, axis: Optional[Unio """ return Array._new(np.roll(x._array, shift, axis=axis)) + def squeeze(x: Array, /, axis: Union[int, Tuple[int, ...]]) -> Array: """ Array API compatible wrapper for :py:func:`np.squeeze `. @@ -60,6 +73,7 @@ def squeeze(x: Array, /, axis: Union[int, Tuple[int, ...]]) -> Array: """ return Array._new(np.squeeze(x._array, axis=axis)) + def stack(arrays: Union[Tuple[Array, ...], List[Array]], /, *, axis: int = 0) -> Array: """ Array API compatible wrapper for :py:func:`np.stack `. diff --git a/numpy/array_api/_searching_functions.py b/numpy/array_api/_searching_functions.py index 9dcc76b2d..3dcef61c3 100644 --- a/numpy/array_api/_searching_functions.py +++ b/numpy/array_api/_searching_functions.py @@ -7,6 +7,7 @@ from typing import Optional, Tuple import numpy as np + def argmax(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) -> Array: """ Array API compatible wrapper for :py:func:`np.argmax `. @@ -15,6 +16,7 @@ def argmax(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) - """ return Array._new(np.asarray(np.argmax(x._array, axis=axis, keepdims=keepdims))) + def argmin(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) -> Array: """ Array API compatible wrapper for :py:func:`np.argmin `. @@ -23,6 +25,7 @@ def argmin(x: Array, /, *, axis: Optional[int] = None, keepdims: bool = False) - """ return Array._new(np.asarray(np.argmin(x._array, axis=axis, keepdims=keepdims))) + def nonzero(x: Array, /) -> Tuple[Array, ...]: """ Array API compatible wrapper for :py:func:`np.nonzero `. @@ -31,6 +34,7 @@ def nonzero(x: Array, /) -> Tuple[Array, ...]: """ return tuple(Array._new(i) for i in np.nonzero(x._array)) + def where(condition: Array, x1: Array, x2: Array, /) -> Array: """ Array API compatible wrapper for :py:func:`np.where `. diff --git a/numpy/array_api/_set_functions.py b/numpy/array_api/_set_functions.py index acd59f597..357f238f5 100644 --- a/numpy/array_api/_set_functions.py +++ b/numpy/array_api/_set_functions.py @@ -6,14 +6,26 @@ from typing import Tuple, Union import numpy as np -def unique(x: Array, /, *, return_counts: bool = False, return_index: bool = False, return_inverse: bool = False) -> Union[Array, Tuple[Array, ...]]: + +def unique( + x: Array, + /, + *, + return_counts: bool = False, + return_index: bool = False, + return_inverse: bool = False, +) -> Union[Array, Tuple[Array, ...]]: """ Array API compatible wrapper for :py:func:`np.unique `. See its docstring for more information. """ - res = np.unique(x._array, return_counts=return_counts, - return_index=return_index, return_inverse=return_inverse) + res = np.unique( + x._array, + return_counts=return_counts, + return_index=return_index, + return_inverse=return_inverse, + ) if isinstance(res, tuple): return tuple(Array._new(i) for i in res) return Array._new(res) diff --git a/numpy/array_api/_sorting_functions.py b/numpy/array_api/_sorting_functions.py index a125e0718..9cd49786c 100644 --- a/numpy/array_api/_sorting_functions.py +++ b/numpy/array_api/_sorting_functions.py @@ -4,27 +4,33 @@ from ._array_object import Array import numpy as np -def argsort(x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True) -> Array: + +def argsort( + x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True +) -> Array: """ Array API compatible wrapper for :py:func:`np.argsort `. See its docstring for more information. """ # Note: this keyword argument is different, and the default is different. - kind = 'stable' if stable else 'quicksort' + kind = "stable" if stable else "quicksort" res = np.argsort(x._array, axis=axis, kind=kind) if descending: res = np.flip(res, axis=axis) return Array._new(res) -def sort(x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True) -> Array: + +def sort( + x: Array, /, *, axis: int = -1, descending: bool = False, stable: bool = True +) -> Array: """ Array API compatible wrapper for :py:func:`np.sort `. See its docstring for more information. """ # Note: this keyword argument is different, and the default is different. - kind = 'stable' if stable else 'quicksort' + kind = "stable" if stable else "quicksort" res = np.sort(x._array, axis=axis, kind=kind) if descending: res = np.flip(res, axis=axis) diff --git a/numpy/array_api/_statistical_functions.py b/numpy/array_api/_statistical_functions.py index a606203bc..63790b447 100644 --- a/numpy/array_api/_statistical_functions.py +++ b/numpy/array_api/_statistical_functions.py @@ -6,25 +6,76 @@ from typing import Optional, Tuple, Union import numpy as np -def max(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: + +def max( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> Array: return Array._new(np.max(x._array, axis=axis, keepdims=keepdims)) -def mean(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: + +def mean( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> Array: return Array._new(np.mean(x._array, axis=axis, keepdims=keepdims)) -def min(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: + +def min( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> Array: return Array._new(np.min(x._array, axis=axis, keepdims=keepdims)) -def prod(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: + +def prod( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> Array: return Array._new(np.prod(x._array, axis=axis, keepdims=keepdims)) -def std(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> Array: + +def std( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + correction: Union[int, float] = 0.0, + keepdims: bool = False, +) -> Array: # Note: the keyword argument correction is different here return Array._new(np.std(x._array, axis=axis, ddof=correction, keepdims=keepdims)) -def sum(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: + +def sum( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> Array: return Array._new(np.sum(x._array, axis=axis, keepdims=keepdims)) -def var(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, correction: Union[int, float] = 0.0, keepdims: bool = False) -> Array: + +def var( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + correction: Union[int, float] = 0.0, + keepdims: bool = False, +) -> Array: # Note: the keyword argument correction is different here return Array._new(np.var(x._array, axis=axis, ddof=correction, keepdims=keepdims)) diff --git a/numpy/array_api/_typing.py b/numpy/array_api/_typing.py index 4ff718205..d530a91ae 100644 --- a/numpy/array_api/_typing.py +++ b/numpy/array_api/_typing.py @@ -6,21 +6,39 @@ annotations in the function signatures. The functions in the module are only valid for inputs that match the given type annotations. """ -__all__ = ['Array', 'Device', 'Dtype', 'SupportsDLPack', - 'SupportsBufferProtocol', 'PyCapsule'] +__all__ = [ + "Array", + "Device", + "Dtype", + "SupportsDLPack", + "SupportsBufferProtocol", + "PyCapsule", +] from typing import Any, Sequence, Type, Union -from . import (Array, int8, int16, int32, int64, uint8, uint16, uint32, - uint64, float32, float64) +from . import ( + Array, + int8, + int16, + int32, + int64, + uint8, + uint16, + uint32, + uint64, + float32, + float64, +) # This should really be recursive, but that isn't supported yet. See the # similar comment in numpy/typing/_array_like.py NestedSequence = Sequence[Sequence[Any]] Device = Any -Dtype = Type[Union[[int8, int16, int32, int64, uint8, uint16, - uint32, uint64, float32, float64]]] +Dtype = Type[ + Union[[int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64]] +] SupportsDLPack = Any SupportsBufferProtocol = Any PyCapsule = Any diff --git a/numpy/array_api/_utility_functions.py b/numpy/array_api/_utility_functions.py index f243bfe68..5ecb4bd9f 100644 --- a/numpy/array_api/_utility_functions.py +++ b/numpy/array_api/_utility_functions.py @@ -6,7 +6,14 @@ from typing import Optional, Tuple, Union import numpy as np -def all(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: + +def all( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> Array: """ Array API compatible wrapper for :py:func:`np.all `. @@ -14,7 +21,14 @@ def all(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keep """ return Array._new(np.asarray(np.all(x._array, axis=axis, keepdims=keepdims))) -def any(x: Array, /, *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False) -> Array: + +def any( + x: Array, + /, + *, + axis: Optional[Union[int, Tuple[int, ...]]] = None, + keepdims: bool = False, +) -> Array: """ Array API compatible wrapper for :py:func:`np.any `. diff --git a/numpy/array_api/setup.py b/numpy/array_api/setup.py index da2350c8f..c8bc29102 100644 --- a/numpy/array_api/setup.py +++ b/numpy/array_api/setup.py @@ -1,10 +1,12 @@ -def configuration(parent_package='', top_path=None): +def configuration(parent_package="", top_path=None): from numpy.distutils.misc_util import Configuration - config = Configuration('array_api', parent_package, top_path) - config.add_subpackage('tests') + + config = Configuration("array_api", parent_package, top_path) + config.add_subpackage("tests") return config -if __name__ == '__main__': +if __name__ == "__main__": from numpy.distutils.core import setup + setup(configuration=configuration) diff --git a/numpy/array_api/tests/test_array_object.py b/numpy/array_api/tests/test_array_object.py index 22078bbee..088e09b9f 100644 --- a/numpy/array_api/tests/test_array_object.py +++ b/numpy/array_api/tests/test_array_object.py @@ -2,9 +2,20 @@ from numpy.testing import assert_raises import numpy as np from .. import ones, asarray, result_type -from .._dtypes import (_all_dtypes, _boolean_dtypes, _floating_dtypes, - _integer_dtypes, _integer_or_boolean_dtypes, - _numeric_dtypes, int8, int16, int32, int64, uint64) +from .._dtypes import ( + _all_dtypes, + _boolean_dtypes, + _floating_dtypes, + _integer_dtypes, + _integer_or_boolean_dtypes, + _numeric_dtypes, + int8, + int16, + int32, + int64, + uint64, +) + def test_validate_index(): # The indexing tests in the official array API test suite test that the @@ -61,28 +72,29 @@ def test_validate_index(): assert_raises(IndexError, lambda: a[None, ...]) assert_raises(IndexError, lambda: a[..., None]) + def test_operators(): # For every operator, we test that it works for the required type # combinations and raises TypeError otherwise - binary_op_dtypes ={ - '__add__': 'numeric', - '__and__': 'integer_or_boolean', - '__eq__': 'all', - '__floordiv__': 'numeric', - '__ge__': 'numeric', - '__gt__': 'numeric', - '__le__': 'numeric', - '__lshift__': 'integer', - '__lt__': 'numeric', - '__mod__': 'numeric', - '__mul__': 'numeric', - '__ne__': 'all', - '__or__': 'integer_or_boolean', - '__pow__': 'floating', - '__rshift__': 'integer', - '__sub__': 'numeric', - '__truediv__': 'floating', - '__xor__': 'integer_or_boolean', + binary_op_dtypes = { + "__add__": "numeric", + "__and__": "integer_or_boolean", + "__eq__": "all", + "__floordiv__": "numeric", + "__ge__": "numeric", + "__gt__": "numeric", + "__le__": "numeric", + "__lshift__": "integer", + "__lt__": "numeric", + "__mod__": "numeric", + "__mul__": "numeric", + "__ne__": "all", + "__or__": "integer_or_boolean", + "__pow__": "floating", + "__rshift__": "integer", + "__sub__": "numeric", + "__truediv__": "floating", + "__xor__": "integer_or_boolean", } # Recompute each time because of in-place ops @@ -92,15 +104,15 @@ def test_operators(): for d in _boolean_dtypes: yield asarray(False, dtype=d) for d in _floating_dtypes: - yield asarray(1., dtype=d) + yield asarray(1.0, dtype=d) for op, dtypes in binary_op_dtypes.items(): ops = [op] - if op not in ['__eq__', '__ne__', '__le__', '__ge__', '__lt__', '__gt__']: - rop = '__r' + op[2:] - iop = '__i' + op[2:] + if op not in ["__eq__", "__ne__", "__le__", "__ge__", "__lt__", "__gt__"]: + rop = "__r" + op[2:] + iop = "__i" + op[2:] ops += [rop, iop] - for s in [1, 1., False]: + for s in [1, 1.0, False]: for _op in ops: for a in _array_vals(): # Test array op scalar. From the spec, the following combinations @@ -149,7 +161,10 @@ def test_operators(): ): assert_raises(TypeError, lambda: getattr(x, _op)(y)) # Ensure in-place operators only promote to the same dtype as the left operand. - elif _op.startswith('__i') and result_type(x.dtype, y.dtype) != x.dtype: + elif ( + _op.startswith("__i") + and result_type(x.dtype, y.dtype) != x.dtype + ): assert_raises(TypeError, lambda: getattr(x, _op)(y)) # Ensure only those dtypes that are required for every operator are allowed. elif (dtypes == "all" and (x.dtype in _boolean_dtypes and y.dtype in _boolean_dtypes @@ -165,17 +180,20 @@ def test_operators(): else: assert_raises(TypeError, lambda: getattr(x, _op)(y)) - unary_op_dtypes ={ - '__abs__': 'numeric', - '__invert__': 'integer_or_boolean', - '__neg__': 'numeric', - '__pos__': 'numeric', + unary_op_dtypes = { + "__abs__": "numeric", + "__invert__": "integer_or_boolean", + "__neg__": "numeric", + "__pos__": "numeric", } for op, dtypes in unary_op_dtypes.items(): for a in _array_vals(): - if (dtypes == "numeric" and a.dtype in _numeric_dtypes - or dtypes == "integer_or_boolean" and a.dtype in _integer_or_boolean_dtypes - ): + if ( + dtypes == "numeric" + and a.dtype in _numeric_dtypes + or dtypes == "integer_or_boolean" + and a.dtype in _integer_or_boolean_dtypes + ): # Only test for no error getattr(a, op)() else: @@ -192,8 +210,8 @@ def test_operators(): yield ones((4, 4), dtype=d) # Scalars always error - for _op in ['__matmul__', '__rmatmul__', '__imatmul__']: - for s in [1, 1., False]: + for _op in ["__matmul__", "__rmatmul__", "__imatmul__"]: + for s in [1, 1.0, False]: for a in _matmul_array_vals(): if (type(s) in [float, int] and a.dtype in _floating_dtypes or type(s) == int and a.dtype in _integer_dtypes): @@ -235,16 +253,17 @@ def test_operators(): else: x.__imatmul__(y) + def test_python_scalar_construtors(): a = asarray(False) b = asarray(0) - c = asarray(0.) + c = asarray(0.0) assert bool(a) == bool(b) == bool(c) == False assert int(a) == int(b) == int(c) == 0 - assert float(a) == float(b) == float(c) == 0. + assert float(a) == float(b) == float(c) == 0.0 # bool/int/float should only be allowed on 0-D arrays. assert_raises(TypeError, lambda: bool(asarray([False]))) assert_raises(TypeError, lambda: int(asarray([0]))) - assert_raises(TypeError, lambda: float(asarray([0.]))) + assert_raises(TypeError, lambda: float(asarray([0.0]))) diff --git a/numpy/array_api/tests/test_creation_functions.py b/numpy/array_api/tests/test_creation_functions.py index 654f1d9b3..3cb8865cd 100644 --- a/numpy/array_api/tests/test_creation_functions.py +++ b/numpy/array_api/tests/test_creation_functions.py @@ -2,26 +2,53 @@ from numpy.testing import assert_raises import numpy as np from .. import all -from .._creation_functions import (asarray, arange, empty, empty_like, eye, from_dlpack, full, full_like, linspace, meshgrid, ones, ones_like, zeros, zeros_like) +from .._creation_functions import ( + asarray, + arange, + empty, + empty_like, + eye, + from_dlpack, + full, + full_like, + linspace, + meshgrid, + ones, + ones_like, + zeros, + zeros_like, +) from .._array_object import Array -from .._dtypes import (_all_dtypes, _boolean_dtypes, _floating_dtypes, - _integer_dtypes, _integer_or_boolean_dtypes, - _numeric_dtypes, int8, int16, int32, int64, uint64) +from .._dtypes import ( + _all_dtypes, + _boolean_dtypes, + _floating_dtypes, + _integer_dtypes, + _integer_or_boolean_dtypes, + _numeric_dtypes, + int8, + int16, + int32, + int64, + uint64, +) + def test_asarray_errors(): # Test various protections against incorrect usage assert_raises(TypeError, lambda: Array([1])) - assert_raises(TypeError, lambda: asarray(['a'])) - assert_raises(ValueError, lambda: asarray([1.], dtype=np.float16)) + assert_raises(TypeError, lambda: asarray(["a"])) + assert_raises(ValueError, lambda: asarray([1.0], dtype=np.float16)) assert_raises(OverflowError, lambda: asarray(2**100)) # Preferably this would be OverflowError # assert_raises(OverflowError, lambda: asarray([2**100])) assert_raises(TypeError, lambda: asarray([2**100])) - asarray([1], device='cpu') # Doesn't error - assert_raises(ValueError, lambda: asarray([1], device='gpu')) + asarray([1], device="cpu") # Doesn't error + assert_raises(ValueError, lambda: asarray([1], device="gpu")) assert_raises(ValueError, lambda: asarray([1], dtype=int)) - assert_raises(ValueError, lambda: asarray([1], dtype='i')) + assert_raises(ValueError, lambda: asarray([1], dtype="i")) + def test_asarray_copy(): a = asarray([1]) @@ -36,68 +63,79 @@ def test_asarray_copy(): # assert all(b[0] == 0) assert_raises(NotImplementedError, lambda: asarray(a, copy=False)) + def test_arange_errors(): - arange(1, device='cpu') # Doesn't error - assert_raises(ValueError, lambda: arange(1, device='gpu')) + arange(1, device="cpu") # Doesn't error + assert_raises(ValueError, lambda: arange(1, device="gpu")) assert_raises(ValueError, lambda: arange(1, dtype=int)) - assert_raises(ValueError, lambda: arange(1, dtype='i')) + assert_raises(ValueError, lambda: arange(1, dtype="i")) + def test_empty_errors(): - empty((1,), device='cpu') # Doesn't error - assert_raises(ValueError, lambda: empty((1,), device='gpu')) + empty((1,), device="cpu") # Doesn't error + assert_raises(ValueError, lambda: empty((1,), device="gpu")) assert_raises(ValueError, lambda: empty((1,), dtype=int)) - assert_raises(ValueError, lambda: empty((1,), dtype='i')) + assert_raises(ValueError, lambda: empty((1,), dtype="i")) + def test_empty_like_errors(): - empty_like(asarray(1), device='cpu') # Doesn't error - assert_raises(ValueError, lambda: empty_like(asarray(1), device='gpu')) + empty_like(asarray(1), device="cpu") # Doesn't error + assert_raises(ValueError, lambda: empty_like(asarray(1), device="gpu")) assert_raises(ValueError, lambda: empty_like(asarray(1), dtype=int)) - assert_raises(ValueError, lambda: empty_like(asarray(1), dtype='i')) + assert_raises(ValueError, lambda: empty_like(asarray(1), dtype="i")) + def test_eye_errors(): - eye(1, device='cpu') # Doesn't error - assert_raises(ValueError, lambda: eye(1, device='gpu')) + eye(1, device="cpu") # Doesn't error + assert_raises(ValueError, lambda: eye(1, device="gpu")) assert_raises(ValueError, lambda: eye(1, dtype=int)) - assert_raises(ValueError, lambda: eye(1, dtype='i')) + assert_raises(ValueError, lambda: eye(1, dtype="i")) + def test_full_errors(): - full((1,), 0, device='cpu') # Doesn't error - assert_raises(ValueError, lambda: full((1,), 0, device='gpu')) + full((1,), 0, device="cpu") # Doesn't error + assert_raises(ValueError, lambda: full((1,), 0, device="gpu")) assert_raises(ValueError, lambda: full((1,), 0, dtype=int)) - assert_raises(ValueError, lambda: full((1,), 0, dtype='i')) + assert_raises(ValueError, lambda: full((1,), 0, dtype="i")) + def test_full_like_errors(): - full_like(asarray(1), 0, device='cpu') # Doesn't error - assert_raises(ValueError, lambda: full_like(asarray(1), 0, device='gpu')) + full_like(asarray(1), 0, device="cpu") # Doesn't error + assert_raises(ValueError, lambda: full_like(asarray(1), 0, device="gpu")) assert_raises(ValueError, lambda: full_like(asarray(1), 0, dtype=int)) - assert_raises(ValueError, lambda: full_like(asarray(1), 0, dtype='i')) + assert_raises(ValueError, lambda: full_like(asarray(1), 0, dtype="i")) + def test_linspace_errors(): - linspace(0, 1, 10, device='cpu') # Doesn't error - assert_raises(ValueError, lambda: linspace(0, 1, 10, device='gpu')) + linspace(0, 1, 10, device="cpu") # Doesn't error + assert_raises(ValueError, lambda: linspace(0, 1, 10, device="gpu")) assert_raises(ValueError, lambda: linspace(0, 1, 10, dtype=float)) - assert_raises(ValueError, lambda: linspace(0, 1, 10, dtype='f')) + assert_raises(ValueError, lambda: linspace(0, 1, 10, dtype="f")) + def test_ones_errors(): - ones((1,), device='cpu') # Doesn't error - assert_raises(ValueError, lambda: ones((1,), device='gpu')) + ones((1,), device="cpu") # Doesn't error + assert_raises(ValueError, lambda: ones((1,), device="gpu")) assert_raises(ValueError, lambda: ones((1,), dtype=int)) - assert_raises(ValueError, lambda: ones((1,), dtype='i')) + assert_raises(ValueError, lambda: ones((1,), dtype="i")) + def test_ones_like_errors(): - ones_like(asarray(1), device='cpu') # Doesn't error - assert_raises(ValueError, lambda: ones_like(asarray(1), device='gpu')) + ones_like(asarray(1), device="cpu") # Doesn't error + assert_raises(ValueError, lambda: ones_like(asarray(1), device="gpu")) assert_raises(ValueError, lambda: ones_like(asarray(1), dtype=int)) - assert_raises(ValueError, lambda: ones_like(asarray(1), dtype='i')) + assert_raises(ValueError, lambda: ones_like(asarray(1), dtype="i")) + def test_zeros_errors(): - zeros((1,), device='cpu') # Doesn't error - assert_raises(ValueError, lambda: zeros((1,), device='gpu')) + zeros((1,), device="cpu") # Doesn't error + assert_raises(ValueError, lambda: zeros((1,), device="gpu")) assert_raises(ValueError, lambda: zeros((1,), dtype=int)) - assert_raises(ValueError, lambda: zeros((1,), dtype='i')) + assert_raises(ValueError, lambda: zeros((1,), dtype="i")) + def test_zeros_like_errors(): - zeros_like(asarray(1), device='cpu') # Doesn't error - assert_raises(ValueError, lambda: zeros_like(asarray(1), device='gpu')) + zeros_like(asarray(1), device="cpu") # Doesn't error + assert_raises(ValueError, lambda: zeros_like(asarray(1), device="gpu")) assert_raises(ValueError, lambda: zeros_like(asarray(1), dtype=int)) - assert_raises(ValueError, lambda: zeros_like(asarray(1), dtype='i')) + assert_raises(ValueError, lambda: zeros_like(asarray(1), dtype="i")) diff --git a/numpy/array_api/tests/test_elementwise_functions.py b/numpy/array_api/tests/test_elementwise_functions.py index ec76cb7a7..a9274aec9 100644 --- a/numpy/array_api/tests/test_elementwise_functions.py +++ b/numpy/array_api/tests/test_elementwise_functions.py @@ -4,74 +4,80 @@ from numpy.testing import assert_raises from .. import asarray, _elementwise_functions from .._elementwise_functions import bitwise_left_shift, bitwise_right_shift -from .._dtypes import (_dtype_categories, _boolean_dtypes, _floating_dtypes, - _integer_dtypes) +from .._dtypes import ( + _dtype_categories, + _boolean_dtypes, + _floating_dtypes, + _integer_dtypes, +) + def nargs(func): return len(getfullargspec(func).args) + def test_function_types(): # Test that every function accepts only the required input types. We only # test the negative cases here (error). The positive cases are tested in # the array API test suite. elementwise_function_input_types = { - 'abs': 'numeric', - 'acos': 'floating-point', - 'acosh': 'floating-point', - 'add': 'numeric', - 'asin': 'floating-point', - 'asinh': 'floating-point', - 'atan': 'floating-point', - 'atan2': 'floating-point', - 'atanh': 'floating-point', - 'bitwise_and': 'integer or boolean', - 'bitwise_invert': 'integer or boolean', - 'bitwise_left_shift': 'integer', - 'bitwise_or': 'integer or boolean', - 'bitwise_right_shift': 'integer', - 'bitwise_xor': 'integer or boolean', - 'ceil': 'numeric', - 'cos': 'floating-point', - 'cosh': 'floating-point', - 'divide': 'floating-point', - 'equal': 'all', - 'exp': 'floating-point', - 'expm1': 'floating-point', - 'floor': 'numeric', - 'floor_divide': 'numeric', - 'greater': 'numeric', - 'greater_equal': 'numeric', - 'isfinite': 'numeric', - 'isinf': 'numeric', - 'isnan': 'numeric', - 'less': 'numeric', - 'less_equal': 'numeric', - 'log': 'floating-point', - 'logaddexp': 'floating-point', - 'log10': 'floating-point', - 'log1p': 'floating-point', - 'log2': 'floating-point', - 'logical_and': 'boolean', - 'logical_not': 'boolean', - 'logical_or': 'boolean', - 'logical_xor': 'boolean', - 'multiply': 'numeric', - 'negative': 'numeric', - 'not_equal': 'all', - 'positive': 'numeric', - 'pow': 'floating-point', - 'remainder': 'numeric', - 'round': 'numeric', - 'sign': 'numeric', - 'sin': 'floating-point', - 'sinh': 'floating-point', - 'sqrt': 'floating-point', - 'square': 'numeric', - 'subtract': 'numeric', - 'tan': 'floating-point', - 'tanh': 'floating-point', - 'trunc': 'numeric', + "abs": "numeric", + "acos": "floating-point", + "acosh": "floating-point", + "add": "numeric", + "asin": "floating-point", + "asinh": "floating-point", + "atan": "floating-point", + "atan2": "floating-point", + "atanh": "floating-point", + "bitwise_and": "integer or boolean", + "bitwise_invert": "integer or boolean", + "bitwise_left_shift": "integer", + "bitwise_or": "integer or boolean", + "bitwise_right_shift": "integer", + "bitwise_xor": "integer or boolean", + "ceil": "numeric", + "cos": "floating-point", + "cosh": "floating-point", + "divide": "floating-point", + "equal": "all", + "exp": "floating-point", + "expm1": "floating-point", + "floor": "numeric", + "floor_divide": "numeric", + "greater": "numeric", + "greater_equal": "numeric", + "isfinite": "numeric", + "isinf": "numeric", + "isnan": "numeric", + "less": "numeric", + "less_equal": "numeric", + "log": "floating-point", + "logaddexp": "floating-point", + "log10": "floating-point", + "log1p": "floating-point", + "log2": "floating-point", + "logical_and": "boolean", + "logical_not": "boolean", + "logical_or": "boolean", + "logical_xor": "boolean", + "multiply": "numeric", + "negative": "numeric", + "not_equal": "all", + "positive": "numeric", + "pow": "floating-point", + "remainder": "numeric", + "round": "numeric", + "sign": "numeric", + "sin": "floating-point", + "sinh": "floating-point", + "sqrt": "floating-point", + "square": "numeric", + "subtract": "numeric", + "tan": "floating-point", + "tanh": "floating-point", + "trunc": "numeric", } def _array_vals(): @@ -80,7 +86,7 @@ def test_function_types(): for d in _boolean_dtypes: yield asarray(False, dtype=d) for d in _floating_dtypes: - yield asarray(1., dtype=d) + yield asarray(1.0, dtype=d) for x in _array_vals(): for func_name, types in elementwise_function_input_types.items(): @@ -94,7 +100,12 @@ def test_function_types(): if x.dtype not in dtypes: assert_raises(TypeError, lambda: func(x)) + def test_bitwise_shift_error(): # bitwise shift functions should raise when the second argument is negative - assert_raises(ValueError, lambda: bitwise_left_shift(asarray([1, 1]), asarray([1, -1]))) - assert_raises(ValueError, lambda: bitwise_right_shift(asarray([1, 1]), asarray([1, -1]))) + assert_raises( + ValueError, lambda: bitwise_left_shift(asarray([1, 1]), asarray([1, -1])) + ) + assert_raises( + ValueError, lambda: bitwise_right_shift(asarray([1, 1]), asarray([1, -1])) + ) -- cgit v1.2.1 From 82ce76c0e7011f4cd88fb4cc5be7b83f8a07f3c9 Mon Sep 17 00:00:00 2001 From: Ankit Dwivedi Date: Sat, 7 Aug 2021 05:34:06 +0000 Subject: add tests to check if whitespaces are ignored in gufunc signatures --- numpy/lib/tests/test_function_base.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'numpy') diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index e1b615223..dcfef94cf 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -1528,6 +1528,20 @@ class TestVectorize: ([('x',)], [('y',), ()])) assert_equal(nfb._parse_gufunc_signature('(),(a,b,c),(d)->(d,e)'), ([(), ('a', 'b', 'c'), ('d',)], [('d', 'e')])) + + # Test if whitespaces are ignored + assert_equal(nfb._parse_gufunc_signature('(x )->()'), ([('x',)], [()])) + assert_equal(nfb._parse_gufunc_signature('( x , y )->( )'), + ([('x', 'y')], [()])) + assert_equal(nfb._parse_gufunc_signature('(x),( y) ->()'), + ([('x',), ('y',)], [()])) + assert_equal(nfb._parse_gufunc_signature('( x)-> (y ) '), + ([('x',)], [('y',)])) + assert_equal(nfb._parse_gufunc_signature(' (x)->( y),( )'), + ([('x',)], [('y',), ()])) + assert_equal(nfb._parse_gufunc_signature('( ), ( a, b,c ) ,( d) -> (d , e)'), + ([(), ('a', 'b', 'c'), ('d',)], [('d', 'e')])) + with assert_raises(ValueError): nfb._parse_gufunc_signature('(x)(y)->()') with assert_raises(ValueError): -- cgit v1.2.1 From 943729592b380dc58368b6774ad7baa53469466c Mon Sep 17 00:00:00 2001 From: Ankit Dwivedi Date: Sat, 7 Aug 2021 06:09:47 +0000 Subject: ignore whitespaces while parsing gufunc signatures --- numpy/lib/function_base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index b43a1d666..93aea46c7 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -1844,9 +1844,9 @@ def disp(mesg, device=None, linefeed=True): # See https://docs.scipy.org/doc/numpy/reference/c-api.generalized-ufuncs.html -_DIMENSION_NAME = r'\w+' +_DIMENSION_NAME = r'\s*\w+\s*' _CORE_DIMENSION_LIST = '(?:{0:}(?:,{0:})*)?'.format(_DIMENSION_NAME) -_ARGUMENT = r'\({}\)'.format(_CORE_DIMENSION_LIST) +_ARGUMENT = r'\s*\({}\s*\)\s*'.format(_CORE_DIMENSION_LIST) _ARGUMENT_LIST = '{0:}(?:,{0:})*'.format(_ARGUMENT) _SIGNATURE = '^{0:}->{0:}$'.format(_ARGUMENT_LIST) @@ -1869,7 +1869,7 @@ def _parse_gufunc_signature(signature): if not re.match(_SIGNATURE, signature): raise ValueError( 'not a valid gufunc signature: {}'.format(signature)) - return tuple([tuple(re.findall(_DIMENSION_NAME, arg)) + return tuple([tuple([dim.strip() for dim in re.findall(_DIMENSION_NAME, arg)]) for arg in re.findall(_ARGUMENT, arg_list)] for arg_list in signature.split('->')) -- cgit v1.2.1 From 9d9cb200cb2d8c24d648f342ecc67b90efcf1f48 Mon Sep 17 00:00:00 2001 From: Ankit Dwivedi Date: Sat, 7 Aug 2021 16:05:07 +0000 Subject: fix lint errors --- numpy/lib/function_base.py | 3 ++- numpy/lib/tests/test_function_base.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index 93aea46c7..8d5264123 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -1869,7 +1869,8 @@ def _parse_gufunc_signature(signature): if not re.match(_SIGNATURE, signature): raise ValueError( 'not a valid gufunc signature: {}'.format(signature)) - return tuple([tuple([dim.strip() for dim in re.findall(_DIMENSION_NAME, arg)]) + return tuple([tuple([dim.strip() + for dim in re.findall(_DIMENSION_NAME, arg)]) for arg in re.findall(_ARGUMENT, arg_list)] for arg_list in signature.split('->')) diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index dcfef94cf..6e36e0969 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -1539,7 +1539,8 @@ class TestVectorize: ([('x',)], [('y',)])) assert_equal(nfb._parse_gufunc_signature(' (x)->( y),( )'), ([('x',)], [('y',), ()])) - assert_equal(nfb._parse_gufunc_signature('( ), ( a, b,c ) ,( d) -> (d , e)'), + assert_equal(nfb._parse_gufunc_signature( + '( ), ( a, b,c ) ,( d) -> (d , e)'), ([(), ('a', 'b', 'c'), ('d',)], [('d', 'e')])) with assert_raises(ValueError): -- cgit v1.2.1 From 2e96755a08cee5651baa5e5da4ce054f4621b237 Mon Sep 17 00:00:00 2001 From: Ankit Dwivedi Date: Sun, 8 Aug 2021 05:06:00 +0000 Subject: dummy change to kick off another build --- numpy/lib/tests/test_function_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index 6e36e0969..1d694e92f 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -1529,7 +1529,7 @@ class TestVectorize: assert_equal(nfb._parse_gufunc_signature('(),(a,b,c),(d)->(d,e)'), ([(), ('a', 'b', 'c'), ('d',)], [('d', 'e')])) - # Test if whitespaces are ignored + # Tests to check if whitespaces are ignored assert_equal(nfb._parse_gufunc_signature('(x )->()'), ([('x',)], [()])) assert_equal(nfb._parse_gufunc_signature('( x , y )->( )'), ([('x', 'y')], [()])) -- cgit v1.2.1 From f42da0df031a98bb17d22025a837466450a2e041 Mon Sep 17 00:00:00 2001 From: Ankit Dwivedi Date: Sun, 8 Aug 2021 20:11:35 +0000 Subject: add a couple of whitespace tests for signature parsing in C code --- numpy/core/tests/test_ufunc.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'numpy') diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index dab11d948..38aa06753 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -388,6 +388,24 @@ class TestUfunc: assert_equal(ixs, (0, 0, 0, 1, 2)) assert_equal(flags, (self.can_ignore, self.size_inferred, 0)) assert_equal(sizes, (3, -1, 9)) + + def test_signature9(self): + enabled, num_dims, ixs, flags, sizes = umt.test_signature( + 1, 1, "( 3) -> ( )") + assert_equal(enabled, 1) + assert_equal(num_dims, (1, 0)) + assert_equal(ixs, (0,)) + assert_equal(flags, (0,)) + assert_equal(sizes, (3,)) + + def test_signature10(self): + enabled, num_dims, ixs, flags, sizes = umt.test_signature( + 3, 1, "( 3? ) , (3? , 3?) ,(n )-> ( 9)") + assert_equal(enabled, 1) + assert_equal(num_dims, (1, 2, 1, 1)) + assert_equal(ixs, (0, 0, 0, 1, 2)) + assert_equal(flags, (self.can_ignore, self.size_inferred, 0)) + assert_equal(sizes, (3, -1, 9)) def test_signature_failure_extra_parenthesis(self): with assert_raises(ValueError): -- cgit v1.2.1 From a092406edf15e61e7f84990b1b79ca28ae01ece3 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Mon, 9 Aug 2021 10:50:41 -0500 Subject: BUG,DEP: Non-default UFunc signature/dtype usage should be deprecated Previously, this was an straight typeerror that went mostly unnoticed for 1.21.0 and 1.21.1. However, that was never the intention, it should have been a normal DeprecationWarning to allow transition where necessary. --- numpy/core/src/umath/ufunc_object.c | 11 +++++++---- numpy/core/tests/test_deprecations.py | 33 +++++++++++++++++++++++++++++++++ numpy/core/tests/test_ufunc.py | 21 --------------------- 3 files changed, 40 insertions(+), 25 deletions(-) (limited to 'numpy') diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index bed303a86..ebc6bf02a 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -4286,7 +4286,8 @@ _get_dtype(PyObject *dtype_obj) { else if (NPY_UNLIKELY(out->singleton != descr)) { /* This does not warn about `metadata`, but units is important. */ if (!PyArray_EquivTypes(out->singleton, descr)) { - PyErr_Format(PyExc_TypeError, + /* Deprecated NumPy 1.21.2 (was an accidental error in 1.21) */ + if (DEPRECATE( "The `dtype` and `signature` arguments to " "ufuncs only select the general DType and not details " "such as the byte order or time unit (with rare " @@ -4296,9 +4297,11 @@ _get_dtype(PyObject *dtype_obj) { "In rare cases where the time unit was preserved, " "either cast the inputs or provide an output array. " "In the future NumPy may transition to allow providing " - "`dtype=` to denote the outputs `dtype` as well"); - Py_DECREF(descr); - return NULL; + "`dtype=` to denote the outputs `dtype` as well. " + "(Deprecated NumPy 1.21)") < 0) { + Py_DECREF(descr); + return NULL; + } } } Py_INCREF(out); diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 42e632e4a..29603e3cc 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -1174,3 +1174,36 @@ class TestCtypesGetter(_DeprecationTestCase): ) def test_not_deprecated(self, name: str) -> None: self.assert_not_deprecated(lambda: getattr(self.ctypes, name)) + + +class TestUFuncForcedDTypeWarning(_DeprecationTestCase): + message = "The `dtype` and `signature` arguments to ufuncs only select the" + + def test_not_deprecated(self): + import pickle + # does not warn (test relies on bad pickling behaviour, simply remove + # it if the `assert int64 is not int64_2` should start failing. + int64 = np.dtype("int64") + int64_2 = pickle.loads(pickle.dumps(int64)) + assert int64 is not int64_2 + self.assert_not_deprecated(lambda: np.add(3, 4, dtype=int64_2)) + + def test_deprecation(self): + int64 = np.dtype("int64") + self.assert_deprecated(lambda: np.add(3, 5, dtype=int64.newbyteorder())) + self.assert_deprecated(lambda: np.add(3, 5, dtype="m8[ns]")) + + def test_behaviour(self): + int64 = np.dtype("int64") + arr = np.arange(10, dtype="m8[s]") + + with pytest.warns(DeprecationWarning, match=self.message): + np.add(3, 5, dtype=int64.newbyteorder()) + with pytest.warns(DeprecationWarning, match=self.message): + np.add(3, 5, dtype="m8[ns]") # previously used the "ns" + with pytest.warns(DeprecationWarning, match=self.message): + np.add(arr, arr, dtype="m8[ns]") # never preserved the "ns" + with pytest.warns(DeprecationWarning, match=self.message): + np.maximum(arr, arr, dtype="m8[ns]") # previously used the "ns" + with pytest.warns(DeprecationWarning, match=self.message): + np.maximum.reduce(arr, dtype="m8[ns]") # never preserved the "ns" diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index dab11d948..797bc6b4e 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -518,27 +518,6 @@ class TestUfunc: np.add(arr, arr, dtype="m") np.maximum(arr, arr, dtype="m") - def test_forced_dtype_warning(self): - # does not warn (test relies on bad pickling behaviour, simply remove - # it if the `assert int64 is not int64_2` should start failing. - int64 = np.dtype("int64") - int64_2 = pickle.loads(pickle.dumps(int64)) - assert int64 is not int64_2 - np.add(3, 4, dtype=int64_2) - - arr = np.arange(10, dtype="m8[s]") - msg = "The `dtype` and `signature` arguments to ufuncs only select the" - with pytest.raises(TypeError, match=msg): - np.add(3, 5, dtype=int64.newbyteorder()) - with pytest.raises(TypeError, match=msg): - np.add(3, 5, dtype="m8[ns]") # previously used the "ns" - with pytest.raises(TypeError, match=msg): - np.add(arr, arr, dtype="m8[ns]") # never preserved the "ns" - with pytest.raises(TypeError, match=msg): - np.maximum(arr, arr, dtype="m8[ns]") # previously used the "ns" - with pytest.raises(TypeError, match=msg): - np.maximum.reduce(arr, dtype="m8[ns]") # never preserved the "ns" - def test_true_divide(self): a = np.array(10) b = np.array(20) -- cgit v1.2.1 From 4a74cee8530e51fe60f77fa0131eda5e2c8a986d Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Tue, 10 Aug 2021 16:31:34 -0500 Subject: BUG: Remove logical object ufuncs with bool output While this makes sense, the implementation here does not actually work. It is very difficult to actually reach it, but if reached it just crashes the process, so remove it. (I am not actually sure why the input needs to be object already here, that seems to be an issue with the current dispatcher/promotion special casing object a bit oddly.) --- numpy/core/code_generators/generate_umath.py | 4 +--- numpy/core/tests/test_ufunc.py | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) (limited to 'numpy') diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py index 1b6917ebc..4891e8f23 100644 --- a/numpy/core/code_generators/generate_umath.py +++ b/numpy/core/code_generators/generate_umath.py @@ -489,7 +489,6 @@ defdict = { 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(nodatetime_or_obj, out='?', simd=[('avx2', ints)]), TD(O, f='npy_ObjectLogicalAnd'), - TD(O, f='npy_ObjectLogicalAnd', out='?'), ), 'logical_not': Ufunc(1, 1, None, @@ -497,7 +496,6 @@ defdict = { None, TD(nodatetime_or_obj, out='?', simd=[('avx2', ints)]), TD(O, f='npy_ObjectLogicalNot'), - TD(O, f='npy_ObjectLogicalNot', out='?'), ), 'logical_or': Ufunc(2, 1, False_, @@ -505,13 +503,13 @@ defdict = { 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(nodatetime_or_obj, out='?', simd=[('avx2', ints)]), TD(O, f='npy_ObjectLogicalOr'), - TD(O, f='npy_ObjectLogicalOr', out='?'), ), 'logical_xor': Ufunc(2, 1, False_, docstrings.get('numpy.core.umath.logical_xor'), 'PyUFunc_SimpleBinaryComparisonTypeResolver', TD(nodatetime_or_obj, out='?'), + # TODO: using obj.logical_xor() seems pretty much useless: TD(P, f='logical_xor'), ), 'maximum': diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index dab11d948..877319c0c 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -2049,6 +2049,27 @@ class TestUfunc: assert_raises(TypeError, f, a, b) assert_raises(TypeError, f, c, a) + @pytest.mark.parametrize("ufunc", + [np.logical_and, np.logical_or]) # logical_xor object loop is bad + @pytest.mark.parametrize("signature", + [(None, None, object), (object, None, None), + (None, object, None)]) + def test_logical_ufuncs_object_signatures(self, ufunc, signature): + a = np.array([True, None, False], dtype=object) + res = ufunc(a, a, signature=signature) + assert res.dtype == object + + @pytest.mark.parametrize("ufunc", + [np.logical_and, np.logical_or, np.logical_xor]) + @pytest.mark.parametrize("signature", + [(bool, None, object), (object, None, bool), + (None, object, bool)]) + def test_logical_ufuncs_mixed_object_signatures(self, ufunc, signature): + # Most mixed signatures fail (except those with bool out, e.g. `OO->?`) + a = np.array([True, None, False]) + with pytest.raises(TypeError): + ufunc(a, a, signature=signature) + def test_reduce_noncontig_output(self): # Check that reduction deals with non-contiguous output arrays # appropriately. -- cgit v1.2.1 From eeef9d4646103c3b1afd3085f1393f2b3f9575b2 Mon Sep 17 00:00:00 2001 From: NectDz <54990613+NectDz@users.noreply.github.com> Date: Tue, 10 Aug 2021 18:00:35 -0500 Subject: DEP: Remove deprecated numeric style dtype strings (#19539) Finishes the deprecation, and effectively closes gh-18993 * Insecure String Comparison * Finished Deprecations * Breaks numpy types * Removed elements in dep_tps * Delete Typecode Comment * Deleted for loop * Fixed 80 characters or more issue * Expired Release Note * Updated Release Note * Update numpy/core/numerictypes.py * Update numpy/core/tests/test_deprecations.py Co-authored-by: Sebastian Berg --- numpy/core/_type_aliases.py | 9 --------- numpy/core/src/multiarray/descriptor.c | 16 ---------------- numpy/core/tests/test_deprecations.py | 15 --------------- numpy/core/tests/test_dtype.py | 9 ++++++--- 4 files changed, 6 insertions(+), 43 deletions(-) (limited to 'numpy') diff --git a/numpy/core/_type_aliases.py b/numpy/core/_type_aliases.py index 67addef48..3765a0d34 100644 --- a/numpy/core/_type_aliases.py +++ b/numpy/core/_type_aliases.py @@ -115,15 +115,6 @@ def _add_aliases(): # add forward, reverse, and string mapping to numarray sctypeDict[char] = info.type - # Add deprecated numeric-style type aliases manually, at some point - # we may want to deprecate the lower case "bytes0" version as well. - for name in ["Bytes0", "Datetime64", "Str0", "Uint32", "Uint64"]: - if english_lower(name) not in allTypes: - # Only one of Uint32 or Uint64, aliases of `np.uintp`, was (and is) defined, note that this - # is not UInt32/UInt64 (capital i), which is removed. - continue - allTypes[name] = allTypes[english_lower(name)] - sctypeDict[name] = sctypeDict[english_lower(name)] _add_aliases() diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c index 50964dab8..90453e38f 100644 --- a/numpy/core/src/multiarray/descriptor.c +++ b/numpy/core/src/multiarray/descriptor.c @@ -1723,22 +1723,6 @@ _convert_from_str(PyObject *obj, int align) goto fail; } - /* Check for a deprecated Numeric-style typecode */ - /* `Uint` has deliberately weird uppercasing */ - char *dep_tps[] = {"Bytes", "Datetime64", "Str", "Uint"}; - int ndep_tps = sizeof(dep_tps) / sizeof(dep_tps[0]); - for (int i = 0; i < ndep_tps; ++i) { - char *dep_tp = dep_tps[i]; - - if (strncmp(type, dep_tp, strlen(dep_tp)) == 0) { - /* Deprecated 2020-06-09, NumPy 1.20 */ - if (DEPRECATE("Numeric-style type codes are " - "deprecated and will result in " - "an error in the future.") < 0) { - goto fail; - } - } - } /* * Probably only ever dispatches to `_convert_from_type`, but who * knows what users are injecting into `np.typeDict`. diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 42e632e4a..44a3ed74a 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -314,21 +314,6 @@ class TestBinaryReprInsufficientWidthParameterForRepresentation(_DeprecationTest self.assert_deprecated(np.binary_repr, args=args, kwargs=kwargs) -class TestNumericStyleTypecodes(_DeprecationTestCase): - """ - Most numeric style typecodes were previously deprecated (and removed) - in 1.20. This also deprecates the remaining ones. - """ - # 2020-06-09, NumPy 1.20 - def test_all_dtypes(self): - deprecated_types = ['Bytes0', 'Datetime64', 'Str0'] - # Depending on intp size, either Uint32 or Uint64 is defined: - deprecated_types.append(f"U{np.dtype(np.intp).name}") - for dt in deprecated_types: - self.assert_deprecated(np.dtype, exceptions=(TypeError,), - args=(dt,)) - - class TestDTypeAttributeIsDTypeDeprecation(_DeprecationTestCase): # Deprecated 2021-01-05, NumPy 1.21 message = r".*`.dtype` attribute" diff --git a/numpy/core/tests/test_dtype.py b/numpy/core/tests/test_dtype.py index 4f52268f5..23269f01b 100644 --- a/numpy/core/tests/test_dtype.py +++ b/numpy/core/tests/test_dtype.py @@ -109,9 +109,12 @@ class TestBuiltin: operation(np.dtype(np.int32), 7) @pytest.mark.parametrize("dtype", - ['Bool', 'Complex32', 'Complex64', 'Float16', 'Float32', 'Float64', - 'Int8', 'Int16', 'Int32', 'Int64', 'Object0', 'Timedelta64', - 'UInt8', 'UInt16', 'UInt32', 'UInt64', 'Void0', + ['Bool', 'Bytes0', 'Complex32', 'Complex64', + 'Datetime64', 'Float16', 'Float32', 'Float64', + 'Int8', 'Int16', 'Int32', 'Int64', + 'Object0', 'Str0', 'Timedelta64', + 'UInt8', 'UInt16', 'Uint32', 'UInt32', + 'Uint64', 'UInt64', 'Void0', "Float128", "Complex128"]) def test_numeric_style_types_are_invalid(self, dtype): with assert_raises(TypeError): -- cgit v1.2.1 From ae279066d6bd253e8675428fac8946938b8d48d9 Mon Sep 17 00:00:00 2001 From: Sayed Adel Date: Wed, 11 Aug 2021 04:40:50 +0200 Subject: BLD, SIMD: Fix testing extra checks when `-Werror` isn't applicable for Clang. In certain cases `-Werror` gets skipped during the availability test due to "unused arguments" warnings. To solve the issue, `-Werror-implicit-function-declaration` was added as a secondary flag, which is enough to guarantee the sanity of the testing process when it comes to testing the availability of certain intrinsics. --- numpy/distutils/ccompiler_opt.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/distutils/ccompiler_opt.py b/numpy/distutils/ccompiler_opt.py index 47d07ad4a..1942aa06e 100644 --- a/numpy/distutils/ccompiler_opt.py +++ b/numpy/distutils/ccompiler_opt.py @@ -193,7 +193,12 @@ class _Config: clang = dict( native = '-march=native', opt = "-O3", - werror = '-Werror' + # One of the following flags needs to be applicable for Clang to + # guarantee the sanity of the testing process, however in certain + # cases `-Werror` gets skipped during the availability test due to + # "unused arguments" warnings. + # see https://github.com/numpy/numpy/issues/19624 + werror = '-Werror-implicit-function-declaration -Werror' ), icc = dict( native = '-xHost', -- cgit v1.2.1 From ed7f30db21f4510e3e6106a68991e8ee21d781ff Mon Sep 17 00:00:00 2001 From: Ankit Dwivedi Date: Thu, 12 Aug 2021 00:37:47 +0530 Subject: replace whitespaces in the signature argument --- numpy/lib/function_base.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index 8d5264123..6f19619bb 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -1844,9 +1844,9 @@ def disp(mesg, device=None, linefeed=True): # See https://docs.scipy.org/doc/numpy/reference/c-api.generalized-ufuncs.html -_DIMENSION_NAME = r'\s*\w+\s*' +_DIMENSION_NAME = r'\w+' _CORE_DIMENSION_LIST = '(?:{0:}(?:,{0:})*)?'.format(_DIMENSION_NAME) -_ARGUMENT = r'\s*\({}\s*\)\s*'.format(_CORE_DIMENSION_LIST) +_ARGUMENT = r'\({}\)'.format(_CORE_DIMENSION_LIST) _ARGUMENT_LIST = '{0:}(?:,{0:})*'.format(_ARGUMENT) _SIGNATURE = '^{0:}->{0:}$'.format(_ARGUMENT_LIST) @@ -1866,11 +1866,12 @@ def _parse_gufunc_signature(signature): Tuple of input and output core dimensions parsed from the signature, each of the form List[Tuple[str, ...]]. """ + signature = re.sub(r'\s+', '', signature) + if not re.match(_SIGNATURE, signature): raise ValueError( 'not a valid gufunc signature: {}'.format(signature)) - return tuple([tuple([dim.strip() - for dim in re.findall(_DIMENSION_NAME, arg)]) + return tuple([tuple(re.findall(_DIMENSION_NAME, arg)) for arg in re.findall(_ARGUMENT, arg_list)] for arg_list in signature.split('->')) -- cgit v1.2.1 From d5956c170b07cf26b05c921d810dc387d7e819da Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 12 Aug 2021 15:22:58 -0600 Subject: Fix the return annotation for numpy.array_api.Array.__setitem__ --- numpy/array_api/_array_object.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/array_api/_array_object.py b/numpy/array_api/_array_object.py index 0f511a577..2d746e78b 100644 --- a/numpy/array_api/_array_object.py +++ b/numpy/array_api/_array_object.py @@ -647,7 +647,7 @@ class Array: ], value: Union[int, float, bool, Array], /, - ) -> Array: + ) -> None: """ Performs the operation __setitem__. """ -- cgit v1.2.1 From 90537b5dac1d0c569baa794967b919ae4f6fdcca Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 12 Aug 2021 15:34:45 -0600 Subject: Add smallest_normal to the array API finfo This was blocked on #18536, which has been merged. --- numpy/array_api/_data_type_functions.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'numpy') diff --git a/numpy/array_api/_data_type_functions.py b/numpy/array_api/_data_type_functions.py index e6121a8a4..fd92aa250 100644 --- a/numpy/array_api/_data_type_functions.py +++ b/numpy/array_api/_data_type_functions.py @@ -60,10 +60,7 @@ class finfo_object: eps: float max: float min: float - # Note: smallest_normal is part of the array API spec, but cannot be used - # until https://github.com/numpy/numpy/pull/18536 is merged. - - # smallest_normal: float + smallest_normal: float @dataclass @@ -87,8 +84,7 @@ def finfo(type: Union[Dtype, Array], /) -> finfo_object: float(fi.eps), float(fi.max), float(fi.min), - # TODO: Uncomment this when #18536 is merged. - # float(fi.smallest_normal), + float(fi.smallest_normal), ) -- cgit v1.2.1 From 22cb4f3156218abe3c20adcfaaff4fc609cc8a72 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Thu, 12 Aug 2021 16:56:10 -0600 Subject: Make sure array_api is included in the public API tests --- numpy/tests/test_public_api.py | 1 + 1 file changed, 1 insertion(+) (limited to 'numpy') diff --git a/numpy/tests/test_public_api.py b/numpy/tests/test_public_api.py index 6e4a8dee0..59e7b066c 100644 --- a/numpy/tests/test_public_api.py +++ b/numpy/tests/test_public_api.py @@ -137,6 +137,7 @@ def test_NPY_NO_EXPORT(): # current status is fine. For others it may make sense to work on making them # private, to clean up our public API and avoid confusion. PUBLIC_MODULES = ['numpy.' + s for s in [ + "array_api", "ctypeslib", "distutils", "distutils.cpuinfo", -- cgit v1.2.1 From 9965a99e10dc7ed85fa382076e67924b597c8e42 Mon Sep 17 00:00:00 2001 From: Bas van Beek Date: Thu, 4 Feb 2021 03:41:50 +0100 Subject: ENH: Add annotations for `np.lib.npyio` --- numpy/lib/npyio.pyi | 313 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 232 insertions(+), 81 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/npyio.pyi b/numpy/lib/npyio.pyi index f69edd564..264ceef14 100644 --- a/numpy/lib/npyio.pyi +++ b/numpy/lib/npyio.pyi @@ -1,98 +1,249 @@ -from typing import Mapping, List, Any +import os +import sys +import zipfile +import types +from typing import ( + Any, + Mapping, + TypeVar, + Generic, + List, + Type, + Iterator, + Union, + IO, + overload, + Sequence, + Callable, + Pattern, +) from numpy import ( DataSource as DataSource, + ndarray, + recarray, + dtype, + generic, + float64, + void, ) +from numpy.ma.mrecords import MaskedRecords +from numpy.typing import ArrayLike, DTypeLike, NDArray, _SupportsDType + from numpy.core.multiarray import ( packbits as packbits, unpackbits as unpackbits, ) +from typing_extensions import Protocol, Literal as L + +_T = TypeVar("_T") +_T_contra = TypeVar("_T_contra", contravariant=True) +_T_co = TypeVar("_T_co", covariant=True) +_SCT = TypeVar("_SCT", bound=generic) + +_DTypeLike = Union[ + Type[_SCT], + dtype[_SCT], + _SupportsDType[dtype[_SCT]], +] + +class _SupportsGetItem(Protocol[_T_contra, _T_co]): + def __getitem__(self, key: _T_contra) -> _T_co: ... + __all__: List[str] -class BagObj: - def __init__(self, obj): ... - def __getattribute__(self, key): ... - def __dir__(self): ... - -def zipfile_factory(file, *args, **kwargs): ... - -class NpzFile(Mapping[Any, Any]): - zip: Any - fid: Any - files: Any - allow_pickle: Any - pickle_kwargs: Any - f: Any - def __init__(self, fid, own_fid=..., allow_pickle=..., pickle_kwargs=...): ... - def __enter__(self): ... - def __exit__(self, exc_type, exc_value, traceback): ... - def close(self): ... - def __del__(self): ... - def __iter__(self): ... - def __len__(self): ... - def __getitem__(self, key): ... - def iteritems(self): ... - def iterkeys(self): ... - -def load(file, mmap_mode=..., allow_pickle=..., fix_imports=..., encoding=...): ... -def save(file, arr, allow_pickle=..., fix_imports=...): ... -def savez(file, *args, **kwds): ... -def savez_compressed(file, *args, **kwds): ... +class BagObj(Generic[_T_co]): + def __init__(self, obj: _SupportsGetItem[str, _T_co]) -> None: ... + def __getattribute__(self, key: str) -> _T_co: ... + def __dir__(self) -> List[str]: ... + +class NpzFile(Mapping[str, NDArray[Any]]): + zip: zipfile.ZipFile + fid: None | IO[str] + files: List[str] + allow_pickle: bool + pickle_kwargs: None | Mapping[str, Any] + # Represent `f` as a mutable property so we can access the type of `self` + @property + def f(self: _T) -> BagObj[_T]: ... + @f.setter + def f(self: _T, value: BagObj[_T]) -> None: ... + def __init__( + self, + fid: IO[str], + own_fid: bool = ..., + allow_pickle: bool = ..., + pickle_kwargs: None | Mapping[str, Any] = ..., + ) -> None: ... + def __enter__(self: _T) -> _T: ... + def __exit__( + self, + __exc_type: None | Type[BaseException], + __exc_value: None | BaseException, + __traceback: None | types.TracebackType, + ) -> None: ... + def close(self) -> None: ... + def __del__(self) -> None: ... + def __iter__(self) -> Iterator[str]: ... + def __len__(self) -> int: ... + def __getitem__(self, key: str) -> NDArray[Any]: ... + +# NOTE: Returns a `NpzFile` if file is a zip file; +# returns an `ndarray`/`memmap` otherwise +def load( + file: str | bytes | os.PathLike[Any] | IO[bytes], + mmap_mode: L[None, "r+", "r", "w+", "c"] = ..., + allow_pickle: bool = ..., + fix_imports: bool = ..., + encoding: L["ASCII", "latin1", "bytes"] = ..., +) -> Any: ... + +def save( + file: str | os.PathLike[str] | IO[bytes], + arr: ArrayLike, + allow_pickle: bool = ..., + fix_imports: bool = ..., +) -> None: ... + +def savez( + file: str | os.PathLike[str] | IO[bytes], + *args: ArrayLike, + **kwds: ArrayLike, +) -> None: ... + +def savez_compressed( + file: str | os.PathLike[str] | IO[bytes], + *args: ArrayLike, + **kwds: ArrayLike, +) -> None: ... + +@overload +def loadtxt( + fname: str | os.PathLike[str] | IO[Any], + dtype: None = ..., + comments: str | Sequence[str] = ..., + delimiter: None | str = ..., + converters: None | Mapping[int | str, Callable[[str], Any]] = ..., + skiprows: int = ..., + usecols: int | Sequence[int] = ..., + unpack: bool = ..., + ndmin: L[0, 1, 2] = ..., + encoding: None | str = ..., + max_rows: None | int = ..., + *, + like: None | ArrayLike = ... +) -> NDArray[float64]: ... +@overload +def loadtxt( + fname: str | os.PathLike[str] | IO[Any], + dtype: _DTypeLike[_SCT], + comments: str | Sequence[str] = ..., + delimiter: None | str = ..., + converters: None | Mapping[int | str, Callable[[str], Any]] = ..., + skiprows: int = ..., + usecols: int | Sequence[int] = ..., + unpack: bool = ..., + ndmin: L[0, 1, 2] = ..., + encoding: None | str = ..., + max_rows: None | int = ..., + *, + like: None | ArrayLike = ... +) -> NDArray[_SCT]: ... +@overload def loadtxt( - fname, - dtype=..., - comments=..., - delimiter=..., - converters=..., - skiprows=..., - usecols=..., - unpack=..., - ndmin=..., - encoding=..., - max_rows=..., + fname: str | os.PathLike[str] | IO[Any], + dtype: DTypeLike, + comments: str | Sequence[str] = ..., + delimiter: None | str = ..., + converters: None | Mapping[int | str, Callable[[str], Any]] = ..., + skiprows: int = ..., + usecols: int | Sequence[int] = ..., + unpack: bool = ..., + ndmin: L[0, 1, 2] = ..., + encoding: None | str = ..., + max_rows: None | int = ..., *, - like=..., -): ... + like: None | ArrayLike = ... +) -> NDArray[Any]: ... + def savetxt( - fname, - X, - fmt=..., - delimiter=..., - newline=..., - header=..., - footer=..., - comments=..., - encoding=..., -): ... -def fromregex(file, regexp, dtype, encoding=...): ... + fname: str | os.PathLike[str] | IO[Any], + X: ArrayLike, + fmt: str | Sequence[str] = ..., + delimiter: str = ..., + newline: str = ..., + header: str = ..., + footer: str = ..., + comments: str = ..., + encoding: None | str = ..., +) -> None: ... + +@overload +def fromregex( + file: str | IO[Any], + regexp: str | bytes | Pattern[Any], + dtype: _DTypeLike[_SCT], + encoding: None | str = ... +) -> NDArray[_SCT]: ... +@overload +def fromregex( + file: str | IO[Any], + regexp: str | bytes | Pattern[Any], + dtype: DTypeLike, + encoding: None | str = ... +) -> NDArray[Any]: ... + +# TODO: Sort out arguments +@overload +def genfromtxt( + fname: str | os.PathLike[str] | IO[Any], + dtype: None = ..., + *args: Any, + **kwargs: Any, +) -> NDArray[float64]: ... +@overload def genfromtxt( - fname, - dtype=..., - comments=..., - delimiter=..., - skip_header=..., - skip_footer=..., - converters=..., - missing_values=..., - filling_values=..., - usecols=..., - names=..., - excludelist=..., - deletechars=..., - replace_space=..., - autostrip=..., - case_sensitive=..., - defaultfmt=..., - unpack=..., - usemask=..., - loose=..., - invalid_raise=..., - max_rows=..., - encoding=..., + fname: str | os.PathLike[str] | IO[Any], + dtype: _DTypeLike[_SCT], + *args: Any, + **kwargs: Any, +) -> NDArray[_SCT]: ... +@overload +def genfromtxt( + fname: str | os.PathLike[str] | IO[Any], + dtype: DTypeLike, + *args: Any, + **kwargs: Any, +) -> NDArray[Any]: ... + +@overload +def recfromtxt( + fname: str | os.PathLike[str] | IO[Any], + *, + usemask: L[False] = ..., + **kwargs: Any, +) -> recarray[Any, dtype[void]]: ... +@overload +def recfromtxt( + fname: str | os.PathLike[str] | IO[Any], + *, + usemask: L[True], + **kwargs: Any, +) -> MaskedRecords[Any, dtype[void]]: ... + +@overload +def recfromcsv( + fname: str | os.PathLike[str] | IO[Any], + *, + usemask: L[False] = ..., + **kwargs: Any, +) -> recarray[Any, dtype[void]]: ... +@overload +def recfromcsv( + fname: str | os.PathLike[str] | IO[Any], *, - like=..., -): ... -def recfromtxt(fname, **kwargs): ... -def recfromcsv(fname, **kwargs): ... + usemask: L[True], + **kwargs: Any, +) -> MaskedRecords[Any, dtype[void]]: ... -- cgit v1.2.1 From e0b23266a2bb0b6d07b9d76df2223a1b339aeb14 Mon Sep 17 00:00:00 2001 From: Bas van Beek <43369155+BvB93@users.noreply.github.com> Date: Thu, 29 Jul 2021 13:42:38 +0200 Subject: TST: Add typing tests for `np.lib.npyio` --- numpy/typing/tests/data/fail/modules.py | 1 - numpy/typing/tests/data/fail/npyio.py | 31 ++++++++++++++ numpy/typing/tests/data/reveal/npyio.py | 71 +++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 numpy/typing/tests/data/fail/npyio.py create mode 100644 numpy/typing/tests/data/reveal/npyio.py (limited to 'numpy') diff --git a/numpy/typing/tests/data/fail/modules.py b/numpy/typing/tests/data/fail/modules.py index 7b9309329..59e724f22 100644 --- a/numpy/typing/tests/data/fail/modules.py +++ b/numpy/typing/tests/data/fail/modules.py @@ -12,7 +12,6 @@ np.math # E: Module has no attribute # Public sub-modules that are not imported to their parent module by default; # e.g. one must first execute `import numpy.lib.recfunctions` np.lib.recfunctions # E: Module has no attribute -np.ma.mrecords # E: Module has no attribute np.__NUMPY_SETUP__ # E: Module has no attribute np.__deprecated_attrs__ # E: Module has no attribute diff --git a/numpy/typing/tests/data/fail/npyio.py b/numpy/typing/tests/data/fail/npyio.py new file mode 100644 index 000000000..89c511c1c --- /dev/null +++ b/numpy/typing/tests/data/fail/npyio.py @@ -0,0 +1,31 @@ +import pathlib +from typing import IO + +import numpy.typing as npt +import numpy as np + +str_path: str +bytes_path: bytes +pathlib_path: pathlib.Path +str_file: IO[str] +AR_i8: npt.NDArray[np.int64] + +np.load(str_file) # E: incompatible type + +np.save(bytes_path, AR_i8) # E: incompatible type +np.save(str_file, AR_i8) # E: incompatible type + +np.savez(bytes_path, AR_i8) # E: incompatible type +np.savez(str_file, AR_i8) # E: incompatible type + +np.savez_compressed(bytes_path, AR_i8) # E: incompatible type +np.savez_compressed(str_file, AR_i8) # E: incompatible type + +np.loadtxt(bytes_path) # E: No overload variant + +np.fromregex(bytes_path, ".", np.int64) # E: No overload variant +np.fromregex(pathlib_path, ".", np.int64) # E: No overload variant + +np.recfromtxt(bytes_path) # E: No overload variant + +np.recfromcsv(bytes_path) # E: No overload variant diff --git a/numpy/typing/tests/data/reveal/npyio.py b/numpy/typing/tests/data/reveal/npyio.py new file mode 100644 index 000000000..36c0c540b --- /dev/null +++ b/numpy/typing/tests/data/reveal/npyio.py @@ -0,0 +1,71 @@ +import re +import pathlib +from typing import IO, List + +import numpy.typing as npt +import numpy as np + +str_path: str +pathlib_path: pathlib.Path +str_file: IO[str] +bytes_file: IO[bytes] + +bag_obj: np.lib.npyio.BagObj[int] +npz_file: np.lib.npyio.NpzFile + +AR_i8: npt.NDArray[np.int64] +AR_LIKE_f8: List[float] + +reveal_type(bag_obj.a) # E: int +reveal_type(bag_obj.b) # E: int + +reveal_type(npz_file.zip) # E: zipfile.ZipFile +reveal_type(npz_file.fid) # E: Union[None, typing.IO[builtins.str]] +reveal_type(npz_file.files) # E: list[builtins.str] +reveal_type(npz_file.allow_pickle) # E: bool +reveal_type(npz_file.pickle_kwargs) # E: Union[None, typing.Mapping[builtins.str, Any]] +reveal_type(npz_file.f) # E: numpy.lib.npyio.BagObj[numpy.lib.npyio.NpzFile] +reveal_type(npz_file["test"]) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(len(npz_file)) # E: int +with npz_file as f: + reveal_type(f) # E: numpy.lib.npyio.NpzFile + +reveal_type(np.load(bytes_file)) # E: Any +reveal_type(np.load(pathlib_path, allow_pickle=True)) # E: Any +reveal_type(np.load(str_path, encoding="bytes")) # E: Any + +reveal_type(np.save(bytes_file, AR_LIKE_f8)) # E: None +reveal_type(np.save(pathlib_path, AR_i8, allow_pickle=True)) # E: None +reveal_type(np.save(str_path, AR_LIKE_f8)) # E: None + +reveal_type(np.savez(bytes_file, AR_LIKE_f8)) # E: None +reveal_type(np.savez(pathlib_path, ar1=AR_i8, ar2=AR_i8)) # E: None +reveal_type(np.savez(str_path, AR_LIKE_f8, ar1=AR_i8)) # E: None + +reveal_type(np.savez_compressed(bytes_file, AR_LIKE_f8)) # E: None +reveal_type(np.savez_compressed(pathlib_path, ar1=AR_i8, ar2=AR_i8)) # E: None +reveal_type(np.savez_compressed(str_path, AR_LIKE_f8, ar1=AR_i8)) # E: None + +reveal_type(np.loadtxt(bytes_file)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.loadtxt(pathlib_path, dtype=np.str_)) # E: numpy.ndarray[Any, numpy.dtype[numpy.str_]] +reveal_type(np.loadtxt(str_path, dtype=str, skiprows=2)) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.loadtxt(str_file, comments="test")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.loadtxt(str_path, delimiter="\n")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.loadtxt(str_path, ndmin=2)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] + +reveal_type(np.fromregex(bytes_file, "test", np.float64)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.fromregex(str_file, b"test", dtype=float)) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.fromregex(str_path, re.compile("test"), dtype=np.str_, encoding="utf8")) # E: numpy.ndarray[Any, numpy.dtype[numpy.str_]] + +reveal_type(np.genfromtxt(bytes_file)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.genfromtxt(pathlib_path, dtype=np.str_)) # E: numpy.ndarray[Any, numpy.dtype[numpy.str_]] +reveal_type(np.genfromtxt(str_path, dtype=str, skiprows=2)) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.genfromtxt(str_file, comments="test")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.genfromtxt(str_path, delimiter="\n")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.genfromtxt(str_path, ndmin=2)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] + +reveal_type(np.recfromtxt(bytes_file)) # E: numpy.recarray[Any, numpy.dtype[numpy.void]] +reveal_type(np.recfromtxt(pathlib_path, usemask=True)) # E: numpy.ma.mrecords.MaskedRecords[Any, numpy.dtype[numpy.void]] + +reveal_type(np.recfromcsv(bytes_file)) # E: numpy.recarray[Any, numpy.dtype[numpy.void]] +reveal_type(np.recfromcsv(pathlib_path, usemask=True)) # E: numpy.ma.mrecords.MaskedRecords[Any, numpy.dtype[numpy.void]] -- cgit v1.2.1 From dfc25f5e7b3fc0c29a80c907e1588dbc6384a7b9 Mon Sep 17 00:00:00 2001 From: Charles Harris Date: Fri, 13 Aug 2021 09:54:33 -0600 Subject: MAINT: Cleanup code after dropping Python 3.7. --- numpy/__init__.py | 94 ++++++++++++++++----------------------- numpy/core/src/multiarray/alloc.c | 5 --- numpy/tests/test_public_api.py | 18 +------- 3 files changed, 41 insertions(+), 76 deletions(-) (limited to 'numpy') diff --git a/numpy/__init__.py b/numpy/__init__.py index 8546238ec..27bedb6c1 100644 --- a/numpy/__init__.py +++ b/numpy/__init__.py @@ -270,70 +270,54 @@ else: oldnumeric = 'removed' numarray = 'removed' - if sys.version_info[:2] >= (3, 7): - # module level getattr is only supported in 3.7 onwards - # https://www.python.org/dev/peps/pep-0562/ - def __getattr__(attr): - # Warn for expired attributes, and return a dummy function - # that always raises an exception. - try: - msg = __expired_functions__[attr] - except KeyError: - pass - else: - warnings.warn(msg, DeprecationWarning, stacklevel=2) - - def _expired(*args, **kwds): - raise RuntimeError(msg) - - return _expired - - # Emit warnings for deprecated attributes - try: - val, msg = __deprecated_attrs__[attr] - except KeyError: - pass - else: - warnings.warn(msg, DeprecationWarning, stacklevel=2) - return val - - # Importing Tester requires importing all of UnitTest which is not a - # cheap import Since it is mainly used in test suits, we lazy import it - # here to save on the order of 10 ms of import time for most users - # - # The previous way Tester was imported also had a side effect of adding - # the full `numpy.testing` namespace - if attr == 'testing': - import numpy.testing as testing - return testing - elif attr == 'Tester': - from .testing import Tester - return Tester - - raise AttributeError("module {!r} has no attribute " - "{!r}".format(__name__, attr)) - - def __dir__(): - return list(globals().keys() | {'Tester', 'testing'}) + def __getattr__(attr): + # Warn for expired attributes, and return a dummy function + # that always raises an exception. + try: + msg = __expired_functions__[attr] + except KeyError: + pass + else: + warnings.warn(msg, DeprecationWarning, stacklevel=2) - else: - # We don't actually use this ourselves anymore, but I'm not 100% sure that - # no-one else in the world is using it (though I hope not) - from .testing import Tester + def _expired(*args, **kwds): + raise RuntimeError(msg) - # We weren't able to emit a warning about these, so keep them around - globals().update({ - k: v - for k, (v, msg) in __deprecated_attrs__.items() - }) + return _expired + # Emit warnings for deprecated attributes + try: + val, msg = __deprecated_attrs__[attr] + except KeyError: + pass + else: + warnings.warn(msg, DeprecationWarning, stacklevel=2) + return val + + # Importing Tester requires importing all of UnitTest which is not a + # cheap import Since it is mainly used in test suits, we lazy import it + # here to save on the order of 10 ms of import time for most users + # + # The previous way Tester was imported also had a side effect of adding + # the full `numpy.testing` namespace + if attr == 'testing': + import numpy.testing as testing + return testing + elif attr == 'Tester': + from .testing import Tester + return Tester + + raise AttributeError("module {!r} has no attribute " + "{!r}".format(__name__, attr)) + + def __dir__(): + return list(globals().keys() | {'Tester', 'testing'}) # Pytest testing from numpy._pytesttester import PytestTester test = PytestTester(__name__) del PytestTester - def _sanity_check(): """ Quick sanity checks for common bugs caused by environment. diff --git a/numpy/core/src/multiarray/alloc.c b/numpy/core/src/multiarray/alloc.c index 887deff53..e74056736 100644 --- a/numpy/core/src/multiarray/alloc.c +++ b/numpy/core/src/multiarray/alloc.c @@ -3,11 +3,6 @@ #include "structmember.h" #include -/* public api in 3.7 */ -#if PY_VERSION_HEX < 0x03070000 -#define PyTraceMalloc_Track _PyTraceMalloc_Track -#define PyTraceMalloc_Untrack _PyTraceMalloc_Untrack -#endif #define NPY_NO_DEPRECATED_API NPY_API_VERSION #define _MULTIARRAYMODULE diff --git a/numpy/tests/test_public_api.py b/numpy/tests/test_public_api.py index 3fa2edd8f..ad04f5cec 100644 --- a/numpy/tests/test_public_api.py +++ b/numpy/tests/test_public_api.py @@ -52,22 +52,8 @@ def test_numpy_namespace(): 'show_config': 'numpy.__config__.show', 'who': 'numpy.lib.utils.who', } - if sys.version_info < (3, 7): - # These built-in types are re-exported by numpy. - builtins = { - 'bool': 'builtins.bool', - 'complex': 'builtins.complex', - 'float': 'builtins.float', - 'int': 'builtins.int', - 'long': 'builtins.int', - 'object': 'builtins.object', - 'str': 'builtins.str', - 'unicode': 'builtins.str', - } - allowlist = dict(undocumented, **builtins) - else: - # after 3.7, we override dir to not show these members - allowlist = undocumented + # We override dir to not show these members + allowlist = undocumented bad_results = check_dir(np) # pytest gives better error messages with the builtin assert than with # assert_equal -- cgit v1.2.1 From 9a649c3f0400861b5181e9d4087322662b01d280 Mon Sep 17 00:00:00 2001 From: Bas van Beek <43369155+BvB93@users.noreply.github.com> Date: Mon, 16 Aug 2021 18:58:48 +0200 Subject: ENH: Allow `np.fromregex` to accept `os.PathLike` implementations --- numpy/lib/npyio.py | 6 +++++- numpy/lib/npyio.pyi | 4 ++-- numpy/lib/tests/test_io.py | 6 ++++-- numpy/typing/tests/data/fail/npyio.py | 1 - numpy/typing/tests/data/reveal/npyio.py | 1 + 5 files changed, 12 insertions(+), 6 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index a593af65e..7a594f25b 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -1484,8 +1484,11 @@ def fromregex(file, regexp, dtype, encoding=None): Parameters ---------- - file : str or file + file : path or file Filename or file object to read. + + .. versionchanged:: 1.22.0 + Now accepts `os.PathLike` implementations. regexp : str or regexp Regular expression used to parse the file. Groups in the regular expression correspond to fields in the dtype. @@ -1535,6 +1538,7 @@ def fromregex(file, regexp, dtype, encoding=None): """ own_fh = False if not hasattr(file, "read"): + file = os.fspath(file) file = np.lib._datasource.open(file, 'rt', encoding=encoding) own_fh = True diff --git a/numpy/lib/npyio.pyi b/numpy/lib/npyio.pyi index 264ceef14..de6bc3ded 100644 --- a/numpy/lib/npyio.pyi +++ b/numpy/lib/npyio.pyi @@ -182,14 +182,14 @@ def savetxt( @overload def fromregex( - file: str | IO[Any], + file: str | os.PathLike[str] | IO[Any], regexp: str | bytes | Pattern[Any], dtype: _DTypeLike[_SCT], encoding: None | str = ... ) -> NDArray[_SCT]: ... @overload def fromregex( - file: str | IO[Any], + file: str | os.PathLike[str] | IO[Any], regexp: str | bytes | Pattern[Any], dtype: DTypeLike, encoding: None | str = ... diff --git a/numpy/lib/tests/test_io.py b/numpy/lib/tests/test_io.py index 02a9789a7..11f2b7d4d 100644 --- a/numpy/lib/tests/test_io.py +++ b/numpy/lib/tests/test_io.py @@ -1229,9 +1229,11 @@ class Testfromregex: a = np.array([(1312,), (1534,), (4444,)], dtype=dt) assert_array_equal(x, a) - def test_record_unicode(self): + @pytest.mark.parametrize("path_type", [str, Path]) + def test_record_unicode(self, path_type): utf8 = b'\xcf\x96' - with temppath() as path: + with temppath() as str_path: + path = path_type(str_path) with open(path, 'wb') as f: f.write(b'1.312 foo' + utf8 + b' \n1.534 bar\n4.444 qux') diff --git a/numpy/typing/tests/data/fail/npyio.py b/numpy/typing/tests/data/fail/npyio.py index 89c511c1c..8edabf2b3 100644 --- a/numpy/typing/tests/data/fail/npyio.py +++ b/numpy/typing/tests/data/fail/npyio.py @@ -24,7 +24,6 @@ np.savez_compressed(str_file, AR_i8) # E: incompatible type np.loadtxt(bytes_path) # E: No overload variant np.fromregex(bytes_path, ".", np.int64) # E: No overload variant -np.fromregex(pathlib_path, ".", np.int64) # E: No overload variant np.recfromtxt(bytes_path) # E: No overload variant diff --git a/numpy/typing/tests/data/reveal/npyio.py b/numpy/typing/tests/data/reveal/npyio.py index 36c0c540b..05005eb1c 100644 --- a/numpy/typing/tests/data/reveal/npyio.py +++ b/numpy/typing/tests/data/reveal/npyio.py @@ -56,6 +56,7 @@ reveal_type(np.loadtxt(str_path, ndmin=2)) # E: numpy.ndarray[Any, numpy.dtype[ reveal_type(np.fromregex(bytes_file, "test", np.float64)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(np.fromregex(str_file, b"test", dtype=float)) # E: numpy.ndarray[Any, numpy.dtype[Any]] reveal_type(np.fromregex(str_path, re.compile("test"), dtype=np.str_, encoding="utf8")) # E: numpy.ndarray[Any, numpy.dtype[numpy.str_]] +reveal_type(np.fromregex(pathlib_path, "test", np.float64)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(np.genfromtxt(bytes_file)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(np.genfromtxt(pathlib_path, dtype=np.str_)) # E: numpy.ndarray[Any, numpy.dtype[numpy.str_]] -- cgit v1.2.1 From 338e6638abb9b04f32ecc696f6dcc94cc59731a6 Mon Sep 17 00:00:00 2001 From: Bas van Beek Date: Mon, 16 Aug 2021 15:45:02 +0200 Subject: ENH: Add annotations for `np.lib.stride_tricks` --- numpy/lib/stride_tricks.pyi | 84 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 9 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/stride_tricks.pyi b/numpy/lib/stride_tricks.pyi index d2e744b5a..9e4e46b8b 100644 --- a/numpy/lib/stride_tricks.pyi +++ b/numpy/lib/stride_tricks.pyi @@ -1,16 +1,82 @@ -from typing import Any, List +from typing import Any, List, Dict, Iterable, TypeVar, overload +from typing_extensions import SupportsIndex -from numpy.typing import _ShapeLike, _Shape +from numpy import dtype, generic +from numpy.typing import ( + NDArray, + ArrayLike, + _ShapeLike, + _Shape, + _NestedSequence, + _SupportsArray, +) + +_SCT = TypeVar("_SCT", bound=generic) +_ArrayLike = _NestedSequence[_SupportsArray[dtype[_SCT]]] __all__: List[str] class DummyArray: - __array_interface__: Any - base: Any - def __init__(self, interface, base=...): ... + __array_interface__: Dict[str, Any] + base: None | NDArray[Any] + def __init__( + self, + interface: Dict[str, Any], + base: None | NDArray[Any] = ..., + ) -> None: ... + +@overload +def as_strided( + x: _ArrayLike[_SCT], + shape: None | Iterable[int] = ..., + strides: None | Iterable[int] = ..., + subok: bool = ..., + writeable: bool = ..., +) -> NDArray[_SCT]: ... +@overload +def as_strided( + x: ArrayLike, + shape: None | Iterable[int] = ..., + strides: None | Iterable[int] = ..., + subok: bool = ..., + writeable: bool = ..., +) -> NDArray[Any]: ... + +@overload +def sliding_window_view( + x: _ArrayLike[_SCT], + window_shape: int | Iterable[int], + axis: None | SupportsIndex = ..., + *, + subok: bool = ..., + writeable: bool = ..., +) -> NDArray[_SCT]: ... +@overload +def sliding_window_view( + x: ArrayLike, + window_shape: int | Iterable[int], + axis: None | SupportsIndex = ..., + *, + subok: bool = ..., + writeable: bool = ..., +) -> NDArray[Any]: ... + +@overload +def broadcast_to( + array: _ArrayLike[_SCT], + shape: int | Iterable[int], + subok: bool = ..., +) -> NDArray[_SCT]: ... +@overload +def broadcast_to( + array: ArrayLike, + shape: int | Iterable[int], + subok: bool = ..., +) -> NDArray[Any]: ... -def as_strided(x, shape=..., strides=..., subok=..., writeable=...): ... -def sliding_window_view(x, window_shape, axis=..., *, subok=..., writeable=...): ... -def broadcast_to(array, shape, subok=...): ... def broadcast_shapes(*args: _ShapeLike) -> _Shape: ... -def broadcast_arrays(*args, subok=...): ... + +def broadcast_arrays( + *args: ArrayLike, + subok: bool = ..., +) -> List[NDArray[Any]]: ... -- cgit v1.2.1 From 614057af8c2c50d2008936d9fa5b8e40f3b1a61b Mon Sep 17 00:00:00 2001 From: Bas van Beek <43369155+BvB93@users.noreply.github.com> Date: Mon, 16 Aug 2021 20:32:59 +0200 Subject: TST: Add typing tests for `np.lib.stride_tricks` --- numpy/typing/tests/data/fail/stride_tricks.py | 9 ++++++++ numpy/typing/tests/data/reveal/stride_tricks.py | 28 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 numpy/typing/tests/data/fail/stride_tricks.py create mode 100644 numpy/typing/tests/data/reveal/stride_tricks.py (limited to 'numpy') diff --git a/numpy/typing/tests/data/fail/stride_tricks.py b/numpy/typing/tests/data/fail/stride_tricks.py new file mode 100644 index 000000000..f2bfba743 --- /dev/null +++ b/numpy/typing/tests/data/fail/stride_tricks.py @@ -0,0 +1,9 @@ +import numpy as np +import numpy.typing as npt + +AR_f8: npt.NDArray[np.float64] + +np.lib.stride_tricks.as_strided(AR_f8, shape=8) # E: No overload variant +np.lib.stride_tricks.as_strided(AR_f8, strides=8) # E: No overload variant + +np.lib.stride_tricks.sliding_window_view(AR_f8, axis=(1,)) # E: No overload variant diff --git a/numpy/typing/tests/data/reveal/stride_tricks.py b/numpy/typing/tests/data/reveal/stride_tricks.py new file mode 100644 index 000000000..152d9cea6 --- /dev/null +++ b/numpy/typing/tests/data/reveal/stride_tricks.py @@ -0,0 +1,28 @@ +from typing import List, Dict, Any +import numpy as np +import numpy.typing as npt + +AR_f8: npt.NDArray[np.float64] +AR_LIKE_f: List[float] +interface_dict: Dict[str, Any] + +reveal_type(np.lib.stride_tricks.DummyArray(interface_dict)) # E: numpy.lib.stride_tricks.DummyArray + +reveal_type(np.lib.stride_tricks.as_strided(AR_f8)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.lib.stride_tricks.as_strided(AR_LIKE_f)) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.lib.stride_tricks.as_strided(AR_f8, strides=(1, 5))) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.lib.stride_tricks.as_strided(AR_f8, shape=[9, 20])) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] + +reveal_type(np.lib.stride_tricks.sliding_window_view(AR_f8, 5)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.lib.stride_tricks.sliding_window_view(AR_LIKE_f, (1, 5))) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.lib.stride_tricks.sliding_window_view(AR_f8, [9], axis=1)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] + +reveal_type(np.broadcast_to(AR_f8, 5)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.broadcast_to(AR_LIKE_f, (1, 5))) # E: numpy.ndarray[Any, numpy.dtype[Any]] +reveal_type(np.broadcast_to(AR_f8, [4, 6], subok=True)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] + +reveal_type(np.broadcast_shapes((1, 2), [3, 1], (3, 2))) # E: tuple[builtins.int] +reveal_type(np.broadcast_shapes((6, 7), (5, 6, 1), 7, (5, 1, 7))) # E: tuple[builtins.int] + +reveal_type(np.broadcast_arrays(AR_f8, AR_f8)) # E: list[numpy.ndarray[Any, numpy.dtype[Any]]] +reveal_type(np.broadcast_arrays(AR_f8, AR_LIKE_f)) # E: list[numpy.ndarray[Any, numpy.dtype[Any]]] -- cgit v1.2.1 From 4e5b2e1f0bdcce5c0d030f0e539c19dda518d765 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 17 Aug 2021 20:56:19 +0200 Subject: MAINT: In loadtxt, inline read_data. No speed difference; the point is to avoid an unnecessary inner generator (which was previously defined quite far away from its point of use). --- numpy/lib/npyio.py | 51 ++++++++++++++++++--------------------------------- 1 file changed, 18 insertions(+), 33 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 7a594f25b..00b5918f8 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -977,35 +977,6 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, line = line.strip('\r\n') return line.split(delimiter) if line else [] - def read_data(lineno_words_iter, chunk_size): - """ - Parse each line, including the first. - - Parameters - ---------- - lineno_words_iter : Iterator[tuple[int, list[str]]] - Iterator returning line numbers and non-empty lines already split - into words. - chunk_size : int - At most `chunk_size` lines are read at a time, with iteration - until all lines are read. - """ - X = [] - for lineno, words in lineno_words_iter: - if usecols: - words = [words[j] for j in usecols] - if len(words) != ncols: - raise ValueError(f"Wrong number of columns at line {lineno}") - # Convert each value according to its column, then pack it - # according to the dtype's nesting - items = packer(convert_row(words)) - X.append(items) - if len(X) > chunk_size: - yield X - X = [] - if X: - yield X - # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Main body of loadtxt. # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1171,15 +1142,29 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, # probably not relevant compared to the cost of actually reading and # converting the data X = None - for x in read_data(lineno_words_iter, _loadtxt_chunksize): + while True: + chunk = [] + for lineno, words in itertools.islice( + lineno_words_iter, _loadtxt_chunksize): + if usecols: + words = [words[j] for j in usecols] + if len(words) != ncols: + raise ValueError( + f"Wrong number of columns at line {lineno}") + # Convert each value according to its column, then pack it + # according to the dtype's nesting, and store it. + chunk.append(packer(convert_row(words))) + if not chunk: # The islice is empty, i.e. we're done. + break + if X is None: - X = np.array(x, dtype) + X = np.array(chunk, dtype) else: nshape = list(X.shape) pos = nshape[0] - nshape[0] += len(x) + nshape[0] += len(chunk) X.resize(nshape, refcheck=False) - X[pos:, ...] = x + X[pos:, ...] = chunk finally: if fown: fh.close() -- cgit v1.2.1 From a562cb2d44760d01a5481280d98248026fabeb40 Mon Sep 17 00:00:00 2001 From: Ghiles Meddour Date: Tue, 17 Aug 2021 23:40:56 +0200 Subject: DOC: Fix typo in `unwrap` docstring. --- numpy/lib/function_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py index 6f19619bb..d875a00ae 100644 --- a/numpy/lib/function_base.py +++ b/numpy/lib/function_base.py @@ -1512,7 +1512,7 @@ def unwrap(p, discont=None, axis=-1, *, period=2*pi): difference from their predecessor of more than ``max(discont, period/2)`` to their `period`-complementary values. - For the default case where `period` is :math:`2\pi` and is `discont` is + For the default case where `period` is :math:`2\pi` and `discont` is :math:`\pi`, this unwraps a radian phase `p` such that adjacent differences are never greater than :math:`\pi` by adding :math:`2k\pi` for some integer :math:`k`. -- cgit v1.2.1 From a5c9bb5e38edc43461593ce2166a2ad34451c99f Mon Sep 17 00:00:00 2001 From: shubham11941140 <63910248+shubham11941140@users.noreply.github.com> Date: Thu, 19 Aug 2021 18:35:45 +0530 Subject: index_tricks.py file not modified --- numpy/core/tests/test_datetime.py | 14 +++++++------- numpy/core/tests/test_numeric.py | 10 +++++----- numpy/fft/tests/test_pocketfft.py | 2 +- numpy/random/tests/test_generator_mt19937_regressions.py | 4 ++-- numpy/random/tests/test_randomstate_regression.py | 4 ++-- numpy/random/tests/test_regression.py | 4 ++-- 6 files changed, 19 insertions(+), 19 deletions(-) (limited to 'numpy') diff --git a/numpy/core/tests/test_datetime.py b/numpy/core/tests/test_datetime.py index b4146eadf..5a490646e 100644 --- a/numpy/core/tests/test_datetime.py +++ b/numpy/core/tests/test_datetime.py @@ -152,7 +152,7 @@ class TestDateTime: expected = np.arange(size) arr = np.tile(np.datetime64('NaT'), size) assert_equal(np.argsort(arr, kind='mergesort'), expected) - + @pytest.mark.parametrize("size", [ 3, 21, 217, 1000]) def test_timedelta_nat_argsort_stability(self, size): @@ -1373,13 +1373,13 @@ class TestDateTime: assert_equal(tda / 0.5, tdc) assert_equal((tda / 0.5).dtype, np.dtype('m8[h]')) # m8 / m8 - assert_equal(tda / tdb, 6.0 / 9.0) - assert_equal(np.divide(tda, tdb), 6.0 / 9.0) - assert_equal(np.true_divide(tda, tdb), 6.0 / 9.0) - assert_equal(tdb / tda, 9.0 / 6.0) + assert_equal(tda / tdb, 6 / 9) + assert_equal(np.divide(tda, tdb), 6 / 9) + assert_equal(np.true_divide(tda, tdb), 6 / 9) + assert_equal(tdb / tda, 9 / 6) assert_equal((tda / tdb).dtype, np.dtype('f8')) - assert_equal(tda / tdd, 60.0) - assert_equal(tdd / tda, 1.0 / 60.0) + assert_equal(tda / tdd, 60) + assert_equal(tdd / tda, 1 / 60) # int / m8 assert_raises(TypeError, np.divide, 2, tdb) diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index e2d648a3c..19de0a8aa 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -2381,7 +2381,7 @@ class TestClip: shape=in_shapes[1], elements={"allow_nan": False})) # Then calculate our result and expected result and check that they're - # equal! See gh-12519 and gh-19457 for discussion deciding on this + # equal! See gh-12519 and gh-19457 for discussion deciding on this # property and the result_type argument. result = np.clip(arr, amin, amax) t = np.result_type(arr, amin, amax) @@ -2637,15 +2637,15 @@ class TestStdVar: def test_ddof1(self): assert_almost_equal(np.var(self.A, ddof=1), - self.real_var*len(self.A)/float(len(self.A)-1)) + self.real_var * len(self.A) / (len(self.A) - 1)) assert_almost_equal(np.std(self.A, ddof=1)**2, - self.real_var*len(self.A)/float(len(self.A)-1)) + self.real_var*len(self.A) / (len(self.A) - 1)) def test_ddof2(self): assert_almost_equal(np.var(self.A, ddof=2), - self.real_var*len(self.A)/float(len(self.A)-2)) + self.real_var * len(self.A) / (len(self.A) - 2)) assert_almost_equal(np.std(self.A, ddof=2)**2, - self.real_var*len(self.A)/float(len(self.A)-2)) + self.real_var * len(self.A) / (len(self.A) - 2)) def test_out_scalar(self): d = np.arange(10) diff --git a/numpy/fft/tests/test_pocketfft.py b/numpy/fft/tests/test_pocketfft.py index 604ac8fde..392644237 100644 --- a/numpy/fft/tests/test_pocketfft.py +++ b/numpy/fft/tests/test_pocketfft.py @@ -10,7 +10,7 @@ import queue def fft1(x): L = len(x) - phase = -2j*np.pi*(np.arange(L)/float(L)) + phase = -2j * np.pi * (np.arange(L) / L) phase = np.arange(L).reshape(-1, 1) * phase return np.sum(x*np.exp(phase), axis=1) diff --git a/numpy/random/tests/test_generator_mt19937_regressions.py b/numpy/random/tests/test_generator_mt19937_regressions.py index 9f6dcdc6b..88d2792a6 100644 --- a/numpy/random/tests/test_generator_mt19937_regressions.py +++ b/numpy/random/tests/test_generator_mt19937_regressions.py @@ -32,11 +32,11 @@ class TestRegression: # these two frequency counts should be close to theoretical # numbers with this large sample # theoretical large N result is 0.49706795 - freq = np.sum(rvsn == 1) / float(N) + freq = np.sum(rvsn == 1) / N msg = f'Frequency was {freq:f}, should be > 0.45' assert_(freq > 0.45, msg) # theoretical large N result is 0.19882718 - freq = np.sum(rvsn == 2) / float(N) + freq = np.sum(rvsn == 2) / N msg = f'Frequency was {freq:f}, should be < 0.23' assert_(freq < 0.23, msg) diff --git a/numpy/random/tests/test_randomstate_regression.py b/numpy/random/tests/test_randomstate_regression.py index 0bf361e5e..595fb5fd3 100644 --- a/numpy/random/tests/test_randomstate_regression.py +++ b/numpy/random/tests/test_randomstate_regression.py @@ -43,11 +43,11 @@ class TestRegression: # these two frequency counts should be close to theoretical # numbers with this large sample # theoretical large N result is 0.49706795 - freq = np.sum(rvsn == 1) / float(N) + freq = np.sum(rvsn == 1) / N msg = f'Frequency was {freq:f}, should be > 0.45' assert_(freq > 0.45, msg) # theoretical large N result is 0.19882718 - freq = np.sum(rvsn == 2) / float(N) + freq = np.sum(rvsn == 2) / N msg = f'Frequency was {freq:f}, should be < 0.23' assert_(freq < 0.23, msg) diff --git a/numpy/random/tests/test_regression.py b/numpy/random/tests/test_regression.py index 54d5a3efb..8bf419875 100644 --- a/numpy/random/tests/test_regression.py +++ b/numpy/random/tests/test_regression.py @@ -39,11 +39,11 @@ class TestRegression: # these two frequency counts should be close to theoretical # numbers with this large sample # theoretical large N result is 0.49706795 - freq = np.sum(rvsn == 1) / float(N) + freq = np.sum(rvsn == 1) / N msg = f'Frequency was {freq:f}, should be > 0.45' assert_(freq > 0.45, msg) # theoretical large N result is 0.19882718 - freq = np.sum(rvsn == 2) / float(N) + freq = np.sum(rvsn == 2) / N msg = f'Frequency was {freq:f}, should be < 0.23' assert_(freq < 0.23, msg) -- cgit v1.2.1 From af0fe021058c045748e0117eee4e856c60c879c5 Mon Sep 17 00:00:00 2001 From: Yashasvi Misra Date: Thu, 19 Aug 2021 19:35:42 +0000 Subject: update --- numpy/core/src/multiarray/lowlevel_strided_loops.c.src | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'numpy') diff --git a/numpy/core/src/multiarray/lowlevel_strided_loops.c.src b/numpy/core/src/multiarray/lowlevel_strided_loops.c.src index e533e4932..e38873746 100644 --- a/numpy/core/src/multiarray/lowlevel_strided_loops.c.src +++ b/numpy/core/src/multiarray/lowlevel_strided_loops.c.src @@ -819,6 +819,10 @@ NPY_NO_EXPORT PyArrayMethod_StridedLoop * # define _CONVERT_FN(x) npy_floatbits_to_halfbits(x) # elif @is_double1@ # define _CONVERT_FN(x) npy_doublebits_to_halfbits(x) +# elif @is_half1@ +# define _CONVERT_FN(x) (x) +# elif @is_bool1@ +# define _CONVERT_FN(x) npy_float_to_half((float)(x!=0)) # else # define _CONVERT_FN(x) npy_float_to_half((float)x) # endif -- cgit v1.2.1 From 3f07ec8ff3d371defda55ed877af06b14b954a76 Mon Sep 17 00:00:00 2001 From: Derek Huang Date: Sun, 22 Aug 2021 16:16:57 -0400 Subject: BUG: address 19575 ref leak of capi_tmp in f2py/cb_rules.py --- numpy/f2py/cb_rules.py | 1 + 1 file changed, 1 insertion(+) (limited to 'numpy') diff --git a/numpy/f2py/cb_rules.py b/numpy/f2py/cb_rules.py index 62aa2fca9..5c9ddb00a 100644 --- a/numpy/f2py/cb_rules.py +++ b/numpy/f2py/cb_rules.py @@ -110,6 +110,7 @@ f2py_cb_start_clock(); capi_tmp = PyObject_GetAttrString(#modulename#_module,\"#argname#_extra_args\"); if (capi_tmp) { capi_arglist = (PyTupleObject *)PySequence_Tuple(capi_tmp); + Py_DECREF(capi_tmp); if (capi_arglist==NULL) { PyErr_SetString(#modulename#_error,\"Failed to convert #modulename#.#argname#_extra_args to tuple.\\n\"); goto capi_fail; -- cgit v1.2.1 From 14dc7d84adbfafd994d87da47170796f527af09d Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Sat, 21 Aug 2021 19:12:26 +0200 Subject: MAINT: Use a contextmanager to ensure loadtxt closes the input file. This seems easier to track that a giant try... finally. Also move the `fencoding` initialization to within the contextmanager, in the rather unlikely case an exception occurs during the call to `getpreferredencoding`. --- numpy/lib/npyio.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 00b5918f8..41970f720 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -1028,7 +1028,7 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, dtype_types, packer = _loadtxt_flatten_dtype_internal(dtype) - fown = False + fh_closing_ctx = contextlib.nullcontext() try: if isinstance(fname, os_PathLike): fname = os_fspath(fname) @@ -1036,7 +1036,7 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, fh = np.lib._datasource.open(fname, 'rt', encoding=encoding) fencoding = getattr(fh, 'encoding', 'latin1') line_iter = iter(fh) - fown = True + fh_closing_ctx = contextlib.closing(fh) else: line_iter = iter(fname) fencoding = getattr(fname, 'encoding', 'latin1') @@ -1059,16 +1059,17 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, f"or generator. Got {type(fname)} instead." ) from e - # input may be a python2 io stream - if encoding is not None: - fencoding = encoding - # we must assume local encoding - # TODO emit portability warning? - elif fencoding is None: - import locale - fencoding = locale.getpreferredencoding() + with fh_closing_ctx: + + # input may be a python2 io stream + if encoding is not None: + fencoding = encoding + # we must assume local encoding + # TODO emit portability warning? + elif fencoding is None: + import locale + fencoding = locale.getpreferredencoding() - try: # Skip the first `skiprows` lines for i in range(skiprows): next(line_iter) @@ -1165,9 +1166,6 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, nshape[0] += len(chunk) X.resize(nshape, refcheck=False) X[pos:, ...] = chunk - finally: - if fown: - fh.close() if X is None: X = np.array([], dtype) -- cgit v1.2.1 From 1767e601ce5fc353748d802444c4c534f31e14a0 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Wed, 4 Aug 2021 09:50:24 +0200 Subject: PERF: Optimize loadtxt usecols. 7-10% speedup in usecols benchmarks; it appears that even in the single-usecol case, avoiding the iteration over `usecols` more than compensates the cost of the extra function call to usecols_getter. --- numpy/lib/npyio.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 00b5918f8..815080d94 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -1004,14 +1004,14 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, byte_converters = True if usecols is not None: - # Allow usecols to be a single int or a sequence of ints + # Copy usecols, allowing it to be a single int or a sequence of ints. try: - usecols_as_list = list(usecols) + usecols = list(usecols) except TypeError: - usecols_as_list = [usecols] - for col_idx in usecols_as_list: + usecols = [usecols] + for i, col_idx in enumerate(usecols): try: - opindex(col_idx) + usecols[i] = opindex(col_idx) # Cast to builtin int now. except TypeError as e: e.args = ( "usecols must be an int or a sequence of ints but " @@ -1019,8 +1019,13 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, type(col_idx), ) raise - # Fall back to existing code - usecols = usecols_as_list + if len(usecols) > 1: + usecols_getter = itemgetter(*usecols) + else: + # Get an iterable back, even if using a single column. + def usecols_getter(words, _col=usecols[0]): return [words[_col]] + else: + usecols_getter = None # Make sure we're dealing with a proper dtype dtype = np.dtype(dtype) @@ -1146,9 +1151,9 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, chunk = [] for lineno, words in itertools.islice( lineno_words_iter, _loadtxt_chunksize): - if usecols: - words = [words[j] for j in usecols] - if len(words) != ncols: + if usecols_getter is not None: + words = usecols_getter(words) + elif len(words) != ncols: raise ValueError( f"Wrong number of columns at line {lineno}") # Convert each value according to its column, then pack it -- cgit v1.2.1 From 06be06301f948e22f0dd459643703834ae07179b Mon Sep 17 00:00:00 2001 From: slowy07 Date: Mon, 23 Aug 2021 07:19:19 +0700 Subject: fix: typo spelling grammar --- numpy/core/src/common/npy_cpu_dispatch.h | 8 ++++---- numpy/core/src/multiarray/convert_datatype.c | 2 +- numpy/core/tests/test_simd.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'numpy') diff --git a/numpy/core/src/common/npy_cpu_dispatch.h b/numpy/core/src/common/npy_cpu_dispatch.h index c8411104a..09e00badf 100644 --- a/numpy/core/src/common/npy_cpu_dispatch.h +++ b/numpy/core/src/common/npy_cpu_dispatch.h @@ -57,7 +57,7 @@ * avoid linking duplications due to the nature of the dispatch-able sources. * * Example: - * @targets baseline avx avx512_skx vsx3 asimdhp // configration statments + * @targets baseline avx avx512_skx vsx3 asimdhp // configuration statements * * void NPY_CPU_DISPATCH_CURFX(dispatch_me)(const int *src, int *dst) * { @@ -180,7 +180,7 @@ * Macro NPY_CPU_DISPATCH_DECLARE_XB(LEFT, ...) * * Same as `NPY_CPU_DISPATCH_DECLARE` but exclude the baseline declaration even - * if it was provided within the configration statments. + * if it was provided within the configuration statements. */ #define NPY_CPU_DISPATCH_DECLARE_XB(...) \ NPY__CPU_DISPATCH_CALL(NPY_CPU_DISPATCH_DECLARE_CHK_, NPY_CPU_DISPATCH_DECLARE_CB_, __VA_ARGS__) @@ -196,7 +196,7 @@ * Example: * Assume we have a dispatch-able source exporting the following function: * - * @targets baseline avx2 avx512_skx // configration statments + * @targets baseline avx2 avx512_skx // configration statements * * void NPY_CPU_DISPATCH_CURFX(dispatch_me)(const int *src, int *dst) * { @@ -238,7 +238,7 @@ * Macro NPY_CPU_DISPATCH_CALL_XB(LEFT, ...) * * Same as `NPY_CPU_DISPATCH_DECLARE` but exclude the baseline declaration even - * if it was provided within the configration statements. + * if it was provided within the configuration statements. * Returns void. */ #define NPY_CPU_DISPATCH_CALL_XB_CB_(TESTED_FEATURES, TARGET_NAME, LEFT, ...) \ diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c index e3b25d076..45b03a6f3 100644 --- a/numpy/core/src/multiarray/convert_datatype.c +++ b/numpy/core/src/multiarray/convert_datatype.c @@ -449,7 +449,7 @@ PyArray_GetCastSafety( /** * Check whether a cast is safe, see also `PyArray_GetCastSafety` for - * a similiar function. Unlike GetCastSafety, this function checks the + * a similar function. Unlike GetCastSafety, this function checks the * `castingimpl->casting` when available. This allows for two things: * * 1. It avoids calling `resolve_descriptors` in some cases. diff --git a/numpy/core/tests/test_simd.py b/numpy/core/tests/test_simd.py index ea5bbe103..f0c60953b 100644 --- a/numpy/core/tests/test_simd.py +++ b/numpy/core/tests/test_simd.py @@ -850,7 +850,7 @@ class _SIMD_ALL(_Test_Utility): return safe_neg = lambda x: -x-1 if -x > int_max else -x - # test round divison for signed integers + # test round division for signed integers for x, d in itertools.product(rdata, divisors): d_neg = safe_neg(d) data = self._data(x) -- cgit v1.2.1 From 46e8c59458b487d37103c1d48cb7314adeebd158 Mon Sep 17 00:00:00 2001 From: Charles Harris Date: Sun, 22 Aug 2021 19:58:34 -0600 Subject: STY: Slight style change --- numpy/lib/npyio.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 815080d94..91c25078b 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -1023,7 +1023,7 @@ def loadtxt(fname, dtype=float, comments='#', delimiter=None, usecols_getter = itemgetter(*usecols) else: # Get an iterable back, even if using a single column. - def usecols_getter(words, _col=usecols[0]): return [words[_col]] + usecols_getter = lambda obj, c=usecols[0]: [obj[c]] else: usecols_getter = None -- cgit v1.2.1 From 9978cc5a1861533abf7c6ed7b0f4e1d732171094 Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 23 Aug 2021 15:21:24 -0600 Subject: Remove Python 3.7 checks from the array API code NumPy has dropped Python 3.7, so these are no longer necessary (and they didn't completely work anyway). --- numpy/_pytesttester.py | 18 ++++++------------ numpy/array_api/__init__.py | 5 ----- 2 files changed, 6 insertions(+), 17 deletions(-) (limited to 'numpy') diff --git a/numpy/_pytesttester.py b/numpy/_pytesttester.py index 1e24f75a7..bfcbd4f1f 100644 --- a/numpy/_pytesttester.py +++ b/numpy/_pytesttester.py @@ -144,18 +144,12 @@ class PytestTester: # so fetch module for suppression here. from numpy.distutils import cpuinfo - if sys.version_info >= (3, 8): - # Ignore the warning from importing the array_api submodule. This - # warning is done on import, so it would break pytest collection, - # but importing it early here prevents the warning from being - # issued when it imported again. - warnings.simplefilter("ignore") - import numpy.array_api - else: - # The array_api submodule is Python 3.8+ only due to the use - # of positional-only argument syntax. We have to ignore it - # completely or the tests will fail at the collection stage. - pytest_args += ['--ignore-glob=numpy/array_api/*'] + # Ignore the warning from importing the array_api submodule. This + # warning is done on import, so it would break pytest collection, + # but importing it early here prevents the warning from being + # issued when it imported again. + warnings.simplefilter("ignore") + import numpy.array_api # Filter out annoying import messages. Want these in both develop and # release mode. diff --git a/numpy/array_api/__init__.py b/numpy/array_api/__init__.py index 53c1f3850..1e1ff242f 100644 --- a/numpy/array_api/__init__.py +++ b/numpy/array_api/__init__.py @@ -120,11 +120,6 @@ Still TODO in this module are: import sys -# numpy.array_api is 3.8+ because it makes extensive use of positional-only -# arguments. -if sys.version_info < (3, 8): - raise ImportError("The numpy.array_api submodule requires Python 3.8 or greater.") - import warnings warnings.warn( -- cgit v1.2.1 From 06ec0ec8dadf9e0e9f7518c9817b95f14df9d7be Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 23 Aug 2021 17:46:13 -0600 Subject: Remove an unused import --- numpy/array_api/__init__.py | 2 -- 1 file changed, 2 deletions(-) (limited to 'numpy') diff --git a/numpy/array_api/__init__.py b/numpy/array_api/__init__.py index 1e1ff242f..790157504 100644 --- a/numpy/array_api/__init__.py +++ b/numpy/array_api/__init__.py @@ -118,8 +118,6 @@ Still TODO in this module are: """ -import sys - import warnings warnings.warn( -- cgit v1.2.1 From 7091e4c48ce7af8a5263b6808a6d7976d4af4c6f Mon Sep 17 00:00:00 2001 From: Aaron Meurer Date: Mon, 23 Aug 2021 17:46:26 -0600 Subject: Use catch_warnings(record=True) instead of simplefilter('ignore') There is a test that fails in the presence of simplefilter('ignore') (test_warnings.py). catch_warnings(record=True) seems to be a way to get the same behavior without failing the test. --- numpy/_pytesttester.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/_pytesttester.py b/numpy/_pytesttester.py index bfcbd4f1f..8decb9dd7 100644 --- a/numpy/_pytesttester.py +++ b/numpy/_pytesttester.py @@ -144,11 +144,11 @@ class PytestTester: # so fetch module for suppression here. from numpy.distutils import cpuinfo + with warnings.catch_warnings(record=True): # Ignore the warning from importing the array_api submodule. This # warning is done on import, so it would break pytest collection, # but importing it early here prevents the warning from being # issued when it imported again. - warnings.simplefilter("ignore") import numpy.array_api # Filter out annoying import messages. Want these in both develop and -- cgit v1.2.1 From 60116f24a7121e1542fda0655bba56bd3647d26d Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 24 Aug 2021 10:37:06 +0200 Subject: MAINT: Avoid use of confusing compat aliases. As of Py3, np.compat.unicode == str, but that's not entirely obvious (it could correspond to some numpy dtype too), so just use plain str. Likewise for np.compat.int. tests are intentionally left unchanged, as they can be considered as implicitly testing the np.compat.py3k interface as well. --- numpy/lib/npyio.py | 4 ++-- numpy/random/_generator.pyx | 2 +- numpy/random/mtrand.pyx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'numpy') diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 91c25078b..dfcb07d4f 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -1537,9 +1537,9 @@ def fromregex(file, regexp, dtype, encoding=None): dtype = np.dtype(dtype) content = file.read() - if isinstance(content, bytes) and isinstance(regexp, np.compat.unicode): + if isinstance(content, bytes) and isinstance(regexp, str): regexp = asbytes(regexp) - elif isinstance(content, np.compat.unicode) and isinstance(regexp, bytes): + elif isinstance(content, str) and isinstance(regexp, bytes): regexp = asstr(regexp) if not hasattr(regexp, 'match'): diff --git a/numpy/random/_generator.pyx b/numpy/random/_generator.pyx index e2430d139..60b6bfc72 100644 --- a/numpy/random/_generator.pyx +++ b/numpy/random/_generator.pyx @@ -561,7 +561,7 @@ cdef class Generator: raise TypeError('Unsupported dtype %r for integers' % _dtype) - if size is None and dtype in (bool, int, np.compat.long): + if size is None and dtype in (bool, int): if np.array(ret).shape == (): return dtype(ret) return ret diff --git a/numpy/random/mtrand.pyx b/numpy/random/mtrand.pyx index 4f5862faa..c9d8ee8e3 100644 --- a/numpy/random/mtrand.pyx +++ b/numpy/random/mtrand.pyx @@ -763,7 +763,7 @@ cdef class RandomState: else: raise TypeError('Unsupported dtype %r for randint' % _dtype) - if size is None and dtype in (bool, int, np.compat.long): + if size is None and dtype in (bool, int): if np.array(ret).shape == (): return dtype(ret) return ret -- cgit v1.2.1 From 62f74111e9fff995b9fa32c3db8ca369d77c7ce6 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Thu, 5 Aug 2021 20:57:51 -0500 Subject: BUG: The normal cast-safety for ufunc loops is "no" casting This can now be set for a loop, which allows specialized loops that include the cast for example (or may make sense for units). However, the default here was just wrong, and apparently we missed any tests at all. (sklearn caught it luckily :)) --- numpy/core/src/umath/legacy_array_method.c | 4 ++-- numpy/core/tests/test_ufunc.py | 31 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/core/src/umath/legacy_array_method.c b/numpy/core/src/umath/legacy_array_method.c index a5e123baa..4351f1d25 100644 --- a/numpy/core/src/umath/legacy_array_method.c +++ b/numpy/core/src/umath/legacy_array_method.c @@ -142,7 +142,7 @@ simple_legacy_resolve_descriptors( } } - return NPY_SAFE_CASTING; + return NPY_NO_CASTING; fail: for (int i = 0; i < nin + nout; i++) { @@ -244,7 +244,7 @@ PyArray_NewLegacyWrappingArrayMethod(PyUFuncObject *ufunc, .dtypes = signature, .flags = flags, .slots = slots, - .casting = NPY_EQUIV_CASTING, + .casting = NPY_NO_CASTING, }; PyBoundArrayMethodObject *bound_res = PyArrayMethod_FromSpec_int(&spec, 1); diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index 657b6a79b..4fed7c5a8 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -536,6 +536,37 @@ class TestUfunc: np.add(arr, arr, dtype="m") np.maximum(arr, arr, dtype="m") + @pytest.mark.parametrize("ufunc", [np.add, np.sqrt]) + def test_cast_safety(self, ufunc): + """Basic test for the safest casts, because ufuncs inner loops can + inidicate a cast-safety as well (which is normally always "no"). + """ + def call_ufunc(arr, **kwargs): + return ufunc(*(arr,) * ufunc.nin, **kwargs) + + arr = np.array([1., 2., 3.], dtype=np.float32) + arr_bs = arr.astype(arr.dtype.newbyteorder()) + expected = call_ufunc(arr) + # Normally, a "no" cast: + res = call_ufunc(arr, casting="no") + assert_array_equal(expected, res) + # Byte-swapping is not allowed with "no" though: + with pytest.raises(TypeError): + call_ufunc(arr_bs, casting="no") + + # But is allowed with "equiv": + res = call_ufunc(arr_bs, casting="equiv") + assert_array_equal(expected, res) + + # Casting to float64 is safe, but not equiv: + with pytest.raises(TypeError): + call_ufunc(arr_bs, dtype=np.float64, casting="equiv") + + # but it is safe cast: + res = call_ufunc(arr_bs, dtype=np.float64, casting="safe") + expected = call_ufunc(arr.astype(np.float64)) # upcast + assert_array_equal(expected, res) + def test_true_divide(self): a = np.array(10) b = np.array(20) -- cgit v1.2.1 From 35070c3c2d517d996b56c5a94cb19675056c835c Mon Sep 17 00:00:00 2001 From: iameskild Date: Mon, 23 Aug 2021 18:11:11 -0700 Subject: Add check for when value is a np.ma --- numpy/ma/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/ma/core.py b/numpy/ma/core.py index ca9f4f474..b2ac383a2 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -3374,7 +3374,8 @@ class MaskedArray(ndarray): _mask[indx] = mval elif not self._hardmask: # Set the data, then the mask - if isinstance(indx, masked_array): + if (isinstance(indx, masked_array) and + not isinstance(value, masked_array)): _data[indx.data] = dval else: _data[indx] = dval -- cgit v1.2.1 From 4807a236befa1a4ba99d058dc7de8aa562fb2273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Robert?= Date: Mon, 23 Aug 2021 16:12:55 +0200 Subject: BUG: fix a regression where a masked_array's mask wouldn't update properly when indx was itself a masked_array instance. Closes #19721. See #19244 for context. --- numpy/ma/tests/test_old_ma.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'numpy') diff --git a/numpy/ma/tests/test_old_ma.py b/numpy/ma/tests/test_old_ma.py index 2e0097dc8..2b3034f9c 100644 --- a/numpy/ma/tests/test_old_ma.py +++ b/numpy/ma/tests/test_old_ma.py @@ -704,6 +704,15 @@ class TestMa: a[c] = 5 assert_(a[2] is masked) + def test_assignment_by_condition_2(self): + # gh-19721 + a = masked_array([0, 1], mask=[False, False]) + b = masked_array([0, 1], mask=[True, True]) + mask = a < 1 + b[mask] = a[mask] + expected_mask = [False, True] + assert_equal(b.mask, expected_mask) + class TestUfuncs: def setup(self): -- cgit v1.2.1 From a2a61e212fb1b8f27bc522b3769de93afdbb419d Mon Sep 17 00:00:00 2001 From: Charles Harris Date: Wed, 25 Aug 2021 08:55:59 -0600 Subject: MAINT: Fix spelling --- numpy/core/tests/test_ufunc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index 4fed7c5a8..c3ea10d93 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -539,7 +539,7 @@ class TestUfunc: @pytest.mark.parametrize("ufunc", [np.add, np.sqrt]) def test_cast_safety(self, ufunc): """Basic test for the safest casts, because ufuncs inner loops can - inidicate a cast-safety as well (which is normally always "no"). + indicate a cast-safety as well (which is normally always "no"). """ def call_ufunc(arr, **kwargs): return ufunc(*(arr,) * ufunc.nin, **kwargs) -- cgit v1.2.1 From 8b885046e1e1f11a76f6acdf9a5426280cf97bac Mon Sep 17 00:00:00 2001 From: Irit Katriel Date: Tue, 10 Aug 2021 16:01:27 +0100 Subject: remove import time compile --- numpy/core/overrides.py | 38 ++++++++------------------------------ 1 file changed, 8 insertions(+), 30 deletions(-) (limited to 'numpy') diff --git a/numpy/core/overrides.py b/numpy/core/overrides.py index 70085d896..d15980f7f 100644 --- a/numpy/core/overrides.py +++ b/numpy/core/overrides.py @@ -126,18 +126,6 @@ def set_module(module): return decorator - -# Call textwrap.dedent here instead of in the function so as to avoid -# calling dedent multiple times on the same text -_wrapped_func_source = textwrap.dedent(""" - @functools.wraps(implementation) - def {name}(*args, **kwargs): - relevant_args = dispatcher(*args, **kwargs) - return implement_array_function( - implementation, {name}, relevant_args, args, kwargs) - """) - - def array_function_dispatch(dispatcher, module=None, verify=True, docs_from_dispatcher=False): """Decorator for adding dispatch with the __array_function__ protocol. @@ -187,25 +175,15 @@ def array_function_dispatch(dispatcher, module=None, verify=True, if docs_from_dispatcher: add_docstring(implementation, dispatcher.__doc__) - # Equivalently, we could define this function directly instead of using - # exec. This version has the advantage of giving the helper function a - # more interpettable name. Otherwise, the original function does not - # show up at all in many cases, e.g., if it's written in C or if the - # dispatcher gets an invalid keyword argument. - source = _wrapped_func_source.format(name=implementation.__name__) - - source_object = compile( - source, filename='<__array_function__ internals>', mode='exec') - scope = { - 'implementation': implementation, - 'dispatcher': dispatcher, - 'functools': functools, - 'implement_array_function': implement_array_function, - } - exec(source_object, scope) - - public_api = scope[implementation.__name__] + @functools.wraps(implementation) + def public_api(*args, **kwargs): + relevant_args = dispatcher(*args, **kwargs) + return implement_array_function( + implementation, public_api, relevant_args, args, kwargs) + public_api.__code__ = public_api.__code__.replace( + co_name=implementation.__name__, + co_filename='') if module is not None: public_api.__module__ = module -- cgit v1.2.1 From d7d07ea88f06b1e37bfd5dc770e2b532378186b0 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Wed, 25 Aug 2021 14:27:50 -0700 Subject: Update numpy/core/overrides.py Co-authored-by: Eric Wieser --- numpy/core/overrides.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/core/overrides.py b/numpy/core/overrides.py index d15980f7f..e1fdd06f2 100644 --- a/numpy/core/overrides.py +++ b/numpy/core/overrides.py @@ -182,8 +182,8 @@ def array_function_dispatch(dispatcher, module=None, verify=True, implementation, public_api, relevant_args, args, kwargs) public_api.__code__ = public_api.__code__.replace( - co_name=implementation.__name__, - co_filename='') + co_name=implementation.__name__, + co_filename='<__array_function__ internals>') if module is not None: public_api.__module__ = module -- cgit v1.2.1 From aa0354e5b3bff381562aa7b83f544e7da442a7ab Mon Sep 17 00:00:00 2001 From: HowJMay Date: Fri, 27 Aug 2021 00:43:08 +0800 Subject: MAINT: Remove redundant semicolon --- numpy/core/include/numpy/npy_math.h | 2 +- numpy/core/src/common/simd/avx2/arithmetic.h | 2 +- numpy/core/src/umath/loops.c.src | 2 +- numpy/core/src/umath/loops_exponent_log.dispatch.c.src | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'numpy') diff --git a/numpy/core/include/numpy/npy_math.h b/numpy/core/include/numpy/npy_math.h index f32e298f0..e9a6a30d2 100644 --- a/numpy/core/include/numpy/npy_math.h +++ b/numpy/core/include/numpy/npy_math.h @@ -391,7 +391,7 @@ NPY_INPLACE npy_longdouble npy_heavisidel(npy_longdouble x, npy_longdouble h0); union { \ ctype z; \ type a[2]; \ - } z1;; \ + } z1; \ \ z1.a[0] = (x); \ z1.a[1] = (y); \ diff --git a/numpy/core/src/common/simd/avx2/arithmetic.h b/numpy/core/src/common/simd/avx2/arithmetic.h index e1b170863..ad9688338 100644 --- a/numpy/core/src/common/simd/avx2/arithmetic.h +++ b/numpy/core/src/common/simd/avx2/arithmetic.h @@ -284,7 +284,7 @@ NPY_FINLINE npy_uint32 npyv_sum_u32(npyv_u32 a) { __m256i s0 = _mm256_hadd_epi32(a, a); s0 = _mm256_hadd_epi32(s0, s0); - __m128i s1 = _mm256_extracti128_si256(s0, 1);; + __m128i s1 = _mm256_extracti128_si256(s0, 1); s1 = _mm_add_epi32(_mm256_castsi256_si128(s0), s1); return _mm_cvtsi128_si32(s1); } diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src index b1afa69a7..8df439aca 100644 --- a/numpy/core/src/umath/loops.c.src +++ b/numpy/core/src/umath/loops.c.src @@ -1340,7 +1340,7 @@ TIMEDELTA_mq_m_divide(char **args, npy_intp const *dimensions, npy_intp const *s *((npy_timedelta *)op1) = NPY_DATETIME_NAT; } else { - *((npy_timedelta *)op1) = libdivide_s64_do(in1, &fast_d);; + *((npy_timedelta *)op1) = libdivide_s64_do(in1, &fast_d); } } } 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 b17643d23..cc0fd19bb 100644 --- a/numpy/core/src/umath/loops_exponent_log.dispatch.c.src +++ b/numpy/core/src/umath/loops_exponent_log.dispatch.c.src @@ -800,7 +800,7 @@ AVX512F_exp_DOUBLE(npy_double * op, q = _mm512_fmadd_pd(q, r, mA2); q = _mm512_fmadd_pd(q, r, mA1); q = _mm512_mul_pd(q, r); - __m512d p = _mm512_fmadd_pd(r, q, r2);; + __m512d p = _mm512_fmadd_pd(r, q, r2); p = _mm512_add_pd(r1, p); /* Get 2^(j/32) from lookup table */ -- cgit v1.2.1 From 147f651ac05e08bfbc4e17ffc1b7673ab4c55796 Mon Sep 17 00:00:00 2001 From: Yashasvi Misra Date: Thu, 26 Aug 2021 18:40:55 +0000 Subject: add test --- numpy/core/tests/test_casting_unittests.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'numpy') diff --git a/numpy/core/tests/test_casting_unittests.py b/numpy/core/tests/test_casting_unittests.py index 3f67f1832..5874c53a3 100644 --- a/numpy/core/tests/test_casting_unittests.py +++ b/numpy/core/tests/test_casting_unittests.py @@ -190,6 +190,12 @@ class TestCasting: return arr1, arr2, values + res = np.array([0, 3, -7], dtype=np.int8).view(bool) + expected = [0, 1, 1] + + def conversion(self, res, expected): + assert_array_equal(res, expected) + def get_data_variation(self, arr1, arr2, aligned=True, contig=True): """ Returns a copy of arr1 that may be non-contiguous or unaligned, and a -- cgit v1.2.1 From f0c723e0ad31ec8628ab4d6147c2bf796c8d257b Mon Sep 17 00:00:00 2001 From: Yashasvi Misra Date: Fri, 27 Aug 2021 13:52:52 +0000 Subject: test: add test_float_to_bool() Signed-off-by: Yashasvi Misra --- numpy/core/tests/test_casting_unittests.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'numpy') diff --git a/numpy/core/tests/test_casting_unittests.py b/numpy/core/tests/test_casting_unittests.py index 5874c53a3..c59871166 100644 --- a/numpy/core/tests/test_casting_unittests.py +++ b/numpy/core/tests/test_casting_unittests.py @@ -190,12 +190,6 @@ class TestCasting: return arr1, arr2, values - res = np.array([0, 3, -7], dtype=np.int8).view(bool) - expected = [0, 1, 1] - - def conversion(self, res, expected): - assert_array_equal(res, expected) - def get_data_variation(self, arr1, arr2, aligned=True, contig=True): """ Returns a copy of arr1 that may be non-contiguous or unaligned, and a @@ -701,6 +695,14 @@ class TestCasting: expected = arr_normal.astype(dtype) except TypeError: with pytest.raises(TypeError): - arr_NULLs.astype(dtype) + arr_NULLs.astype(dtype), else: assert_array_equal(expected, arr_NULLs.astype(dtype)) + + + def test_float_to_bool(self): + # test case corresponding to gh-19514 + # simple test for casting bool_ to float16 + res = np.array([0, 3, -7], dtype=np.int8).view(bool) + expected = [0, 1, 1] + assert_array_equal(res, expected) \ No newline at end of file -- cgit v1.2.1 From 580d83ff127f330470867c009c7c6b17847f4287 Mon Sep 17 00:00:00 2001 From: Yashasvi Misra Date: Fri, 27 Aug 2021 13:59:05 +0000 Subject: lint: fix flake8 errors Signed-off-by: Yashasvi Misra --- numpy/core/tests/test_casting_unittests.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/core/tests/test_casting_unittests.py b/numpy/core/tests/test_casting_unittests.py index c59871166..a13e807e2 100644 --- a/numpy/core/tests/test_casting_unittests.py +++ b/numpy/core/tests/test_casting_unittests.py @@ -699,10 +699,9 @@ class TestCasting: else: assert_array_equal(expected, arr_NULLs.astype(dtype)) - def test_float_to_bool(self): # test case corresponding to gh-19514 # simple test for casting bool_ to float16 res = np.array([0, 3, -7], dtype=np.int8).view(bool) expected = [0, 1, 1] - assert_array_equal(res, expected) \ No newline at end of file + assert_array_equal(res, expected) -- cgit v1.2.1 From db76197e016481e1ce63a7588445c16cc4e1d0a0 Mon Sep 17 00:00:00 2001 From: Illviljan <14371165+Illviljan@users.noreply.github.com> Date: Fri, 27 Aug 2021 17:44:35 +0200 Subject: Fix so doctest passes --- numpy/core/fromnumeric.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'numpy') diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py index 764377bc9..ff082b644 100644 --- a/numpy/core/fromnumeric.py +++ b/numpy/core/fromnumeric.py @@ -3327,11 +3327,11 @@ def around(a, decimals=0, out=None): Examples -------- >>> np.around([0.37, 1.64]) - array([0., 2.]) + array([0., 2.]) >>> np.around([0.37, 1.64], decimals=1) - array([0.4, 1.6]) + array([0.4, 1.6]) >>> np.around([.5, 1.5, 2.5, 3.5, 4.5]) # rounds to nearest even value - array([0., 2., 2., 4., 4.]) + array([0., 2., 2., 4., 4.]) >>> np.around([1,2,3,11], decimals=1) # ndarray of ints is returned array([ 1, 2, 3, 11]) >>> np.around([1,2,3,11], decimals=-1) -- cgit v1.2.1 From 8243b25fc8b78f5fd358c14588e4511786276f8e Mon Sep 17 00:00:00 2001 From: Illviljan <14371165+Illviljan@users.noreply.github.com> Date: Fri, 27 Aug 2021 22:25:15 +0200 Subject: Remove reference since it's not used (#19766) --- numpy/core/fromnumeric.py | 3 --- 1 file changed, 3 deletions(-) (limited to 'numpy') diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py index ff082b644..5ecb1e666 100644 --- a/numpy/core/fromnumeric.py +++ b/numpy/core/fromnumeric.py @@ -3320,9 +3320,6 @@ def around(a, decimals=0, out=None): ---------- .. [1] "Lecture Notes on the Status of IEEE 754", William Kahan, https://people.eecs.berkeley.edu/~wkahan/ieee754status/IEEE754.PDF - .. [2] "How Futile are Mindless Assessments of - Roundoff in Floating-Point Computation?", William Kahan, - https://people.eecs.berkeley.edu/~wkahan/Mindless.pdf Examples -------- -- cgit v1.2.1 From d0b676b77bfa567eddb925bc31ead24ff0b576b1 Mon Sep 17 00:00:00 2001 From: Bas van Beek <43369155+BvB93@users.noreply.github.com> Date: Sat, 14 Aug 2021 21:39:50 +0200 Subject: MAINT: Drop .py code-paths specific to Python 3.7 --- numpy/typing/__init__.py | 19 +- numpy/typing/_array_like.py | 26 +- numpy/typing/_callable.py | 587 ++++++++++++++++++------------------- numpy/typing/_char_codes.py | 282 +++++++----------- numpy/typing/_dtype_like.py | 51 ++-- numpy/typing/_shape.py | 12 +- numpy/typing/tests/test_runtime.py | 10 +- 7 files changed, 417 insertions(+), 570 deletions(-) (limited to 'numpy') diff --git a/numpy/typing/__init__.py b/numpy/typing/__init__.py index d731f00ef..bfa7982c0 100644 --- a/numpy/typing/__init__.py +++ b/numpy/typing/__init__.py @@ -143,24 +143,7 @@ API # NOTE: The API section will be appended with additional entries # further down in this file -from typing import TYPE_CHECKING, List, Any - -if TYPE_CHECKING: - # typing_extensions is always available when type-checking - from typing_extensions import Literal as L - _HAS_TYPING_EXTENSIONS: L[True] -else: - try: - import typing_extensions - except ImportError: - _HAS_TYPING_EXTENSIONS = False - else: - _HAS_TYPING_EXTENSIONS = True - -if TYPE_CHECKING: - from typing_extensions import final -else: - def final(f): return f +from typing import TYPE_CHECKING, List, Any, final if not TYPE_CHECKING: __all__ = ["ArrayLike", "DTypeLike", "NBitBase", "NDArray"] diff --git a/numpy/typing/_array_like.py b/numpy/typing/_array_like.py index c562f3c1f..6ea0eb662 100644 --- a/numpy/typing/_array_like.py +++ b/numpy/typing/_array_like.py @@ -1,7 +1,6 @@ from __future__ import annotations -import sys -from typing import Any, Sequence, TYPE_CHECKING, Union, TypeVar, Generic +from typing import Any, Sequence, Protocol, Union, TypeVar from numpy import ( ndarray, dtype, @@ -19,28 +18,19 @@ from numpy import ( str_, bytes_, ) -from . import _HAS_TYPING_EXTENSIONS - -if sys.version_info >= (3, 8): - from typing import Protocol -elif _HAS_TYPING_EXTENSIONS: - from typing_extensions import Protocol _T = TypeVar("_T") _ScalarType = TypeVar("_ScalarType", bound=generic) _DType = TypeVar("_DType", bound="dtype[Any]") _DType_co = TypeVar("_DType_co", covariant=True, bound="dtype[Any]") -if TYPE_CHECKING or _HAS_TYPING_EXTENSIONS or sys.version_info >= (3, 8): - # The `_SupportsArray` protocol only cares about the default dtype - # (i.e. `dtype=None` or no `dtype` parameter at all) of the to-be returned - # array. - # Concrete implementations of the protocol are responsible for adding - # any and all remaining overloads - class _SupportsArray(Protocol[_DType_co]): - def __array__(self) -> ndarray[Any, _DType_co]: ... -else: - class _SupportsArray(Generic[_DType_co]): ... +# The `_SupportsArray` protocol only cares about the default dtype +# (i.e. `dtype=None` or no `dtype` parameter at all) of the to-be returned +# array. +# Concrete implementations of the protocol are responsible for adding +# any and all remaining overloads +class _SupportsArray(Protocol[_DType_co]): + def __array__(self) -> ndarray[Any, _DType_co]: ... # TODO: Wait for support for recursive types _NestedSequence = Union[ diff --git a/numpy/typing/_callable.py b/numpy/typing/_callable.py index 8f911da3b..63a8153af 100644 --- a/numpy/typing/_callable.py +++ b/numpy/typing/_callable.py @@ -10,7 +10,6 @@ See the `Mypy documentation`_ on protocols for more details. from __future__ import annotations -import sys from typing import ( Union, TypeVar, @@ -18,7 +17,7 @@ from typing import ( Any, Tuple, NoReturn, - TYPE_CHECKING, + Protocol, ) from numpy import ( @@ -45,312 +44,282 @@ from ._scalars import ( _FloatLike_co, _NumberLike_co, ) -from . import NBitBase, _HAS_TYPING_EXTENSIONS +from . import NBitBase from ._generic_alias import NDArray -if sys.version_info >= (3, 8): - from typing import Protocol -elif _HAS_TYPING_EXTENSIONS: - from typing_extensions import Protocol - -if TYPE_CHECKING or _HAS_TYPING_EXTENSIONS or sys.version_info >= (3, 8): - _T1 = TypeVar("_T1") - _T2 = TypeVar("_T2") - _2Tuple = Tuple[_T1, _T1] - - _NBit1 = TypeVar("_NBit1", bound=NBitBase) - _NBit2 = TypeVar("_NBit2", bound=NBitBase) - - _IntType = TypeVar("_IntType", bound=integer) - _FloatType = TypeVar("_FloatType", bound=floating) - _NumberType = TypeVar("_NumberType", bound=number) - _NumberType_co = TypeVar("_NumberType_co", covariant=True, bound=number) - _GenericType_co = TypeVar("_GenericType_co", covariant=True, bound=generic) - - class _BoolOp(Protocol[_GenericType_co]): - @overload - def __call__(self, __other: _BoolLike_co) -> _GenericType_co: ... - @overload # platform dependent - def __call__(self, __other: int) -> int_: ... - @overload - def __call__(self, __other: float) -> float64: ... - @overload - def __call__(self, __other: complex) -> complex128: ... - @overload - def __call__(self, __other: _NumberType) -> _NumberType: ... - - class _BoolBitOp(Protocol[_GenericType_co]): - @overload - def __call__(self, __other: _BoolLike_co) -> _GenericType_co: ... - @overload # platform dependent - def __call__(self, __other: int) -> int_: ... - @overload - def __call__(self, __other: _IntType) -> _IntType: ... - - class _BoolSub(Protocol): - # Note that `__other: bool_` is absent here - @overload - def __call__(self, __other: bool) -> NoReturn: ... - @overload # platform dependent - def __call__(self, __other: int) -> int_: ... - @overload - def __call__(self, __other: float) -> float64: ... - @overload - def __call__(self, __other: complex) -> complex128: ... - @overload - def __call__(self, __other: _NumberType) -> _NumberType: ... - - class _BoolTrueDiv(Protocol): - @overload - def __call__(self, __other: float | _IntLike_co) -> float64: ... - @overload - def __call__(self, __other: complex) -> complex128: ... - @overload - def __call__(self, __other: _NumberType) -> _NumberType: ... - - class _BoolMod(Protocol): - @overload - def __call__(self, __other: _BoolLike_co) -> int8: ... - @overload # platform dependent - def __call__(self, __other: int) -> int_: ... - @overload - def __call__(self, __other: float) -> float64: ... - @overload - def __call__(self, __other: _IntType) -> _IntType: ... - @overload - def __call__(self, __other: _FloatType) -> _FloatType: ... - - class _BoolDivMod(Protocol): - @overload - def __call__(self, __other: _BoolLike_co) -> _2Tuple[int8]: ... - @overload # platform dependent - def __call__(self, __other: int) -> _2Tuple[int_]: ... - @overload - def __call__(self, __other: float) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... - @overload - def __call__(self, __other: _IntType) -> _2Tuple[_IntType]: ... - @overload - def __call__(self, __other: _FloatType) -> _2Tuple[_FloatType]: ... - - class _TD64Div(Protocol[_NumberType_co]): - @overload - def __call__(self, __other: timedelta64) -> _NumberType_co: ... - @overload - def __call__(self, __other: _BoolLike_co) -> NoReturn: ... - @overload - def __call__(self, __other: _FloatLike_co) -> timedelta64: ... - - class _IntTrueDiv(Protocol[_NBit1]): - @overload - def __call__(self, __other: bool) -> floating[_NBit1]: ... - @overload - def __call__(self, __other: int) -> floating[_NBit1 | _NBitInt]: ... - @overload - def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... - @overload - def __call__( - self, __other: complex - ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... - @overload - def __call__(self, __other: integer[_NBit2]) -> floating[_NBit1 | _NBit2]: ... - - class _UnsignedIntOp(Protocol[_NBit1]): - # NOTE: `uint64 + signedinteger -> float64` - @overload - def __call__(self, __other: bool) -> unsignedinteger[_NBit1]: ... - @overload - def __call__( - self, __other: int | signedinteger[Any] - ) -> Any: ... - @overload - def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... - @overload - def __call__( - self, __other: complex - ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... - @overload - def __call__( - self, __other: unsignedinteger[_NBit2] - ) -> unsignedinteger[_NBit1 | _NBit2]: ... - - class _UnsignedIntBitOp(Protocol[_NBit1]): - @overload - def __call__(self, __other: bool) -> unsignedinteger[_NBit1]: ... - @overload - def __call__(self, __other: int) -> signedinteger[Any]: ... - @overload - def __call__(self, __other: signedinteger[Any]) -> signedinteger[Any]: ... - @overload - def __call__( - self, __other: unsignedinteger[_NBit2] - ) -> unsignedinteger[_NBit1 | _NBit2]: ... - - class _UnsignedIntMod(Protocol[_NBit1]): - @overload - def __call__(self, __other: bool) -> unsignedinteger[_NBit1]: ... - @overload - def __call__( - self, __other: int | signedinteger[Any] - ) -> Any: ... - @overload - def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... - @overload - def __call__( - self, __other: unsignedinteger[_NBit2] - ) -> unsignedinteger[_NBit1 | _NBit2]: ... - - class _UnsignedIntDivMod(Protocol[_NBit1]): - @overload - def __call__(self, __other: bool) -> _2Tuple[signedinteger[_NBit1]]: ... - @overload - def __call__( - self, __other: int | signedinteger[Any] - ) -> _2Tuple[Any]: ... - @overload - def __call__(self, __other: float) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... - @overload - def __call__( - self, __other: unsignedinteger[_NBit2] - ) -> _2Tuple[unsignedinteger[_NBit1 | _NBit2]]: ... - - class _SignedIntOp(Protocol[_NBit1]): - @overload - def __call__(self, __other: bool) -> signedinteger[_NBit1]: ... - @overload - def __call__(self, __other: int) -> signedinteger[_NBit1 | _NBitInt]: ... - @overload - def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... - @overload - def __call__( - self, __other: complex - ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... - @overload - def __call__( - self, __other: signedinteger[_NBit2] - ) -> signedinteger[_NBit1 | _NBit2]: ... - - class _SignedIntBitOp(Protocol[_NBit1]): - @overload - def __call__(self, __other: bool) -> signedinteger[_NBit1]: ... - @overload - def __call__(self, __other: int) -> signedinteger[_NBit1 | _NBitInt]: ... - @overload - def __call__( - self, __other: signedinteger[_NBit2] - ) -> signedinteger[_NBit1 | _NBit2]: ... - - class _SignedIntMod(Protocol[_NBit1]): - @overload - def __call__(self, __other: bool) -> signedinteger[_NBit1]: ... - @overload - def __call__(self, __other: int) -> signedinteger[_NBit1 | _NBitInt]: ... - @overload - def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... - @overload - def __call__( - self, __other: signedinteger[_NBit2] - ) -> signedinteger[_NBit1 | _NBit2]: ... - - class _SignedIntDivMod(Protocol[_NBit1]): - @overload - def __call__(self, __other: bool) -> _2Tuple[signedinteger[_NBit1]]: ... - @overload - def __call__(self, __other: int) -> _2Tuple[signedinteger[_NBit1 | _NBitInt]]: ... - @overload - def __call__(self, __other: float) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... - @overload - def __call__( - self, __other: signedinteger[_NBit2] - ) -> _2Tuple[signedinteger[_NBit1 | _NBit2]]: ... - - class _FloatOp(Protocol[_NBit1]): - @overload - def __call__(self, __other: bool) -> floating[_NBit1]: ... - @overload - def __call__(self, __other: int) -> floating[_NBit1 | _NBitInt]: ... - @overload - def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... - @overload - def __call__( - self, __other: complex - ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... - @overload - def __call__( - self, __other: integer[_NBit2] | floating[_NBit2] - ) -> floating[_NBit1 | _NBit2]: ... - - class _FloatMod(Protocol[_NBit1]): - @overload - def __call__(self, __other: bool) -> floating[_NBit1]: ... - @overload - def __call__(self, __other: int) -> floating[_NBit1 | _NBitInt]: ... - @overload - def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... - @overload - def __call__( - self, __other: integer[_NBit2] | floating[_NBit2] - ) -> floating[_NBit1 | _NBit2]: ... - - class _FloatDivMod(Protocol[_NBit1]): - @overload - def __call__(self, __other: bool) -> _2Tuple[floating[_NBit1]]: ... - @overload - def __call__(self, __other: int) -> _2Tuple[floating[_NBit1 | _NBitInt]]: ... - @overload - def __call__(self, __other: float) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... - @overload - def __call__( - self, __other: integer[_NBit2] | floating[_NBit2] - ) -> _2Tuple[floating[_NBit1 | _NBit2]]: ... - - class _ComplexOp(Protocol[_NBit1]): - @overload - def __call__(self, __other: bool) -> complexfloating[_NBit1, _NBit1]: ... - @overload - def __call__(self, __other: int) -> complexfloating[_NBit1 | _NBitInt, _NBit1 | _NBitInt]: ... - @overload - def __call__( - self, __other: complex - ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... - @overload - def __call__( - self, - __other: Union[ - integer[_NBit2], - floating[_NBit2], - complexfloating[_NBit2, _NBit2], - ] - ) -> complexfloating[_NBit1 | _NBit2, _NBit1 | _NBit2]: ... - - class _NumberOp(Protocol): - def __call__(self, __other: _NumberLike_co) -> Any: ... - - class _ComparisonOp(Protocol[_T1, _T2]): - @overload - def __call__(self, __other: _T1) -> bool_: ... - @overload - def __call__(self, __other: _T2) -> NDArray[bool_]: ... - -else: - _BoolOp = Any - _BoolBitOp = Any - _BoolSub = Any - _BoolTrueDiv = Any - _BoolMod = Any - _BoolDivMod = Any - _TD64Div = Any - _IntTrueDiv = Any - _UnsignedIntOp = Any - _UnsignedIntBitOp = Any - _UnsignedIntMod = Any - _UnsignedIntDivMod = Any - _SignedIntOp = Any - _SignedIntBitOp = Any - _SignedIntMod = Any - _SignedIntDivMod = Any - _FloatOp = Any - _FloatMod = Any - _FloatDivMod = Any - _ComplexOp = Any - _NumberOp = Any - _ComparisonOp = Any +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_2Tuple = Tuple[_T1, _T1] + +_NBit1 = TypeVar("_NBit1", bound=NBitBase) +_NBit2 = TypeVar("_NBit2", bound=NBitBase) + +_IntType = TypeVar("_IntType", bound=integer) +_FloatType = TypeVar("_FloatType", bound=floating) +_NumberType = TypeVar("_NumberType", bound=number) +_NumberType_co = TypeVar("_NumberType_co", covariant=True, bound=number) +_GenericType_co = TypeVar("_GenericType_co", covariant=True, bound=generic) + +class _BoolOp(Protocol[_GenericType_co]): + @overload + def __call__(self, __other: _BoolLike_co) -> _GenericType_co: ... + @overload # platform dependent + def __call__(self, __other: int) -> int_: ... + @overload + def __call__(self, __other: float) -> float64: ... + @overload + def __call__(self, __other: complex) -> complex128: ... + @overload + def __call__(self, __other: _NumberType) -> _NumberType: ... + +class _BoolBitOp(Protocol[_GenericType_co]): + @overload + def __call__(self, __other: _BoolLike_co) -> _GenericType_co: ... + @overload # platform dependent + def __call__(self, __other: int) -> int_: ... + @overload + def __call__(self, __other: _IntType) -> _IntType: ... + +class _BoolSub(Protocol): + # Note that `__other: bool_` is absent here + @overload + def __call__(self, __other: bool) -> NoReturn: ... + @overload # platform dependent + def __call__(self, __other: int) -> int_: ... + @overload + def __call__(self, __other: float) -> float64: ... + @overload + def __call__(self, __other: complex) -> complex128: ... + @overload + def __call__(self, __other: _NumberType) -> _NumberType: ... + +class _BoolTrueDiv(Protocol): + @overload + def __call__(self, __other: float | _IntLike_co) -> float64: ... + @overload + def __call__(self, __other: complex) -> complex128: ... + @overload + def __call__(self, __other: _NumberType) -> _NumberType: ... + +class _BoolMod(Protocol): + @overload + def __call__(self, __other: _BoolLike_co) -> int8: ... + @overload # platform dependent + def __call__(self, __other: int) -> int_: ... + @overload + def __call__(self, __other: float) -> float64: ... + @overload + def __call__(self, __other: _IntType) -> _IntType: ... + @overload + def __call__(self, __other: _FloatType) -> _FloatType: ... + +class _BoolDivMod(Protocol): + @overload + def __call__(self, __other: _BoolLike_co) -> _2Tuple[int8]: ... + @overload # platform dependent + def __call__(self, __other: int) -> _2Tuple[int_]: ... + @overload + def __call__(self, __other: float) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... + @overload + def __call__(self, __other: _IntType) -> _2Tuple[_IntType]: ... + @overload + def __call__(self, __other: _FloatType) -> _2Tuple[_FloatType]: ... + +class _TD64Div(Protocol[_NumberType_co]): + @overload + def __call__(self, __other: timedelta64) -> _NumberType_co: ... + @overload + def __call__(self, __other: _BoolLike_co) -> NoReturn: ... + @overload + def __call__(self, __other: _FloatLike_co) -> timedelta64: ... + +class _IntTrueDiv(Protocol[_NBit1]): + @overload + def __call__(self, __other: bool) -> floating[_NBit1]: ... + @overload + def __call__(self, __other: int) -> floating[_NBit1 | _NBitInt]: ... + @overload + def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, __other: complex + ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... + @overload + def __call__(self, __other: integer[_NBit2]) -> floating[_NBit1 | _NBit2]: ... + +class _UnsignedIntOp(Protocol[_NBit1]): + # NOTE: `uint64 + signedinteger -> float64` + @overload + def __call__(self, __other: bool) -> unsignedinteger[_NBit1]: ... + @overload + def __call__( + self, __other: int | signedinteger[Any] + ) -> Any: ... + @overload + def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, __other: complex + ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, __other: unsignedinteger[_NBit2] + ) -> unsignedinteger[_NBit1 | _NBit2]: ... + +class _UnsignedIntBitOp(Protocol[_NBit1]): + @overload + def __call__(self, __other: bool) -> unsignedinteger[_NBit1]: ... + @overload + def __call__(self, __other: int) -> signedinteger[Any]: ... + @overload + def __call__(self, __other: signedinteger[Any]) -> signedinteger[Any]: ... + @overload + def __call__( + self, __other: unsignedinteger[_NBit2] + ) -> unsignedinteger[_NBit1 | _NBit2]: ... + +class _UnsignedIntMod(Protocol[_NBit1]): + @overload + def __call__(self, __other: bool) -> unsignedinteger[_NBit1]: ... + @overload + def __call__( + self, __other: int | signedinteger[Any] + ) -> Any: ... + @overload + def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, __other: unsignedinteger[_NBit2] + ) -> unsignedinteger[_NBit1 | _NBit2]: ... + +class _UnsignedIntDivMod(Protocol[_NBit1]): + @overload + def __call__(self, __other: bool) -> _2Tuple[signedinteger[_NBit1]]: ... + @overload + def __call__( + self, __other: int | signedinteger[Any] + ) -> _2Tuple[Any]: ... + @overload + def __call__(self, __other: float) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... + @overload + def __call__( + self, __other: unsignedinteger[_NBit2] + ) -> _2Tuple[unsignedinteger[_NBit1 | _NBit2]]: ... + +class _SignedIntOp(Protocol[_NBit1]): + @overload + def __call__(self, __other: bool) -> signedinteger[_NBit1]: ... + @overload + def __call__(self, __other: int) -> signedinteger[_NBit1 | _NBitInt]: ... + @overload + def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, __other: complex + ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, __other: signedinteger[_NBit2] + ) -> signedinteger[_NBit1 | _NBit2]: ... + +class _SignedIntBitOp(Protocol[_NBit1]): + @overload + def __call__(self, __other: bool) -> signedinteger[_NBit1]: ... + @overload + def __call__(self, __other: int) -> signedinteger[_NBit1 | _NBitInt]: ... + @overload + def __call__( + self, __other: signedinteger[_NBit2] + ) -> signedinteger[_NBit1 | _NBit2]: ... + +class _SignedIntMod(Protocol[_NBit1]): + @overload + def __call__(self, __other: bool) -> signedinteger[_NBit1]: ... + @overload + def __call__(self, __other: int) -> signedinteger[_NBit1 | _NBitInt]: ... + @overload + def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, __other: signedinteger[_NBit2] + ) -> signedinteger[_NBit1 | _NBit2]: ... + +class _SignedIntDivMod(Protocol[_NBit1]): + @overload + def __call__(self, __other: bool) -> _2Tuple[signedinteger[_NBit1]]: ... + @overload + def __call__(self, __other: int) -> _2Tuple[signedinteger[_NBit1 | _NBitInt]]: ... + @overload + def __call__(self, __other: float) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... + @overload + def __call__( + self, __other: signedinteger[_NBit2] + ) -> _2Tuple[signedinteger[_NBit1 | _NBit2]]: ... + +class _FloatOp(Protocol[_NBit1]): + @overload + def __call__(self, __other: bool) -> floating[_NBit1]: ... + @overload + def __call__(self, __other: int) -> floating[_NBit1 | _NBitInt]: ... + @overload + def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, __other: complex + ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, __other: integer[_NBit2] | floating[_NBit2] + ) -> floating[_NBit1 | _NBit2]: ... + +class _FloatMod(Protocol[_NBit1]): + @overload + def __call__(self, __other: bool) -> floating[_NBit1]: ... + @overload + def __call__(self, __other: int) -> floating[_NBit1 | _NBitInt]: ... + @overload + def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, __other: integer[_NBit2] | floating[_NBit2] + ) -> floating[_NBit1 | _NBit2]: ... + +class _FloatDivMod(Protocol[_NBit1]): + @overload + def __call__(self, __other: bool) -> _2Tuple[floating[_NBit1]]: ... + @overload + def __call__(self, __other: int) -> _2Tuple[floating[_NBit1 | _NBitInt]]: ... + @overload + def __call__(self, __other: float) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... + @overload + def __call__( + self, __other: integer[_NBit2] | floating[_NBit2] + ) -> _2Tuple[floating[_NBit1 | _NBit2]]: ... + +class _ComplexOp(Protocol[_NBit1]): + @overload + def __call__(self, __other: bool) -> complexfloating[_NBit1, _NBit1]: ... + @overload + def __call__(self, __other: int) -> complexfloating[_NBit1 | _NBitInt, _NBit1 | _NBitInt]: ... + @overload + def __call__( + self, __other: complex + ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... + @overload + def __call__( + self, + __other: Union[ + integer[_NBit2], + floating[_NBit2], + complexfloating[_NBit2, _NBit2], + ] + ) -> complexfloating[_NBit1 | _NBit2, _NBit1 | _NBit2]: ... + +class _NumberOp(Protocol): + def __call__(self, __other: _NumberLike_co) -> Any: ... + +class _ComparisonOp(Protocol[_T1, _T2]): + @overload + def __call__(self, __other: _T1) -> bool_: ... + @overload + def __call__(self, __other: _T2) -> NDArray[bool_]: ... diff --git a/numpy/typing/_char_codes.py b/numpy/typing/_char_codes.py index 22ee168e9..139471084 100644 --- a/numpy/typing/_char_codes.py +++ b/numpy/typing/_char_codes.py @@ -1,171 +1,111 @@ -import sys -from typing import Any, TYPE_CHECKING - -from . import _HAS_TYPING_EXTENSIONS - -if sys.version_info >= (3, 8): - from typing import Literal -elif _HAS_TYPING_EXTENSIONS: - from typing_extensions import Literal - -if TYPE_CHECKING or _HAS_TYPING_EXTENSIONS or sys.version_info >= (3, 8): - _BoolCodes = Literal["?", "=?", "?", "bool", "bool_", "bool8"] - - _UInt8Codes = Literal["uint8", "u1", "=u1", "u1"] - _UInt16Codes = Literal["uint16", "u2", "=u2", "u2"] - _UInt32Codes = Literal["uint32", "u4", "=u4", "u4"] - _UInt64Codes = Literal["uint64", "u8", "=u8", "u8"] - - _Int8Codes = Literal["int8", "i1", "=i1", "i1"] - _Int16Codes = Literal["int16", "i2", "=i2", "i2"] - _Int32Codes = Literal["int32", "i4", "=i4", "i4"] - _Int64Codes = Literal["int64", "i8", "=i8", "i8"] - - _Float16Codes = Literal["float16", "f2", "=f2", "f2"] - _Float32Codes = Literal["float32", "f4", "=f4", "f4"] - _Float64Codes = Literal["float64", "f8", "=f8", "f8"] - - _Complex64Codes = Literal["complex64", "c8", "=c8", "c8"] - _Complex128Codes = Literal["complex128", "c16", "=c16", "c16"] - - _ByteCodes = Literal["byte", "b", "=b", "b"] - _ShortCodes = Literal["short", "h", "=h", "h"] - _IntCCodes = Literal["intc", "i", "=i", "i"] - _IntPCodes = Literal["intp", "int0", "p", "=p", "p"] - _IntCodes = Literal["long", "int", "int_", "l", "=l", "l"] - _LongLongCodes = Literal["longlong", "q", "=q", "q"] - - _UByteCodes = Literal["ubyte", "B", "=B", "B"] - _UShortCodes = Literal["ushort", "H", "=H", "H"] - _UIntCCodes = Literal["uintc", "I", "=I", "I"] - _UIntPCodes = Literal["uintp", "uint0", "P", "=P", "P"] - _UIntCodes = Literal["uint", "L", "=L", "L"] - _ULongLongCodes = Literal["ulonglong", "Q", "=Q", "Q"] - - _HalfCodes = Literal["half", "e", "=e", "e"] - _SingleCodes = Literal["single", "f", "=f", "f"] - _DoubleCodes = Literal["double", "float", "float_", "d", "=d", "d"] - _LongDoubleCodes = Literal["longdouble", "longfloat", "g", "=g", "g"] - - _CSingleCodes = Literal["csingle", "singlecomplex", "F", "=F", "F"] - _CDoubleCodes = Literal["cdouble", "complex", "complex_", "cfloat", "D", "=D", "D"] - _CLongDoubleCodes = Literal["clongdouble", "clongfloat", "longcomplex", "G", "=G", "G"] - - _StrCodes = Literal["str", "str_", "str0", "unicode", "unicode_", "U", "=U", "U"] - _BytesCodes = Literal["bytes", "bytes_", "bytes0", "S", "=S", "S"] - _VoidCodes = Literal["void", "void0", "V", "=V", "V"] - _ObjectCodes = Literal["object", "object_", "O", "=O", "O"] - - _DT64Codes = Literal[ - "datetime64", "=datetime64", "datetime64", - "datetime64[Y]", "=datetime64[Y]", "datetime64[Y]", - "datetime64[M]", "=datetime64[M]", "datetime64[M]", - "datetime64[W]", "=datetime64[W]", "datetime64[W]", - "datetime64[D]", "=datetime64[D]", "datetime64[D]", - "datetime64[h]", "=datetime64[h]", "datetime64[h]", - "datetime64[m]", "=datetime64[m]", "datetime64[m]", - "datetime64[s]", "=datetime64[s]", "datetime64[s]", - "datetime64[ms]", "=datetime64[ms]", "datetime64[ms]", - "datetime64[us]", "=datetime64[us]", "datetime64[us]", - "datetime64[ns]", "=datetime64[ns]", "datetime64[ns]", - "datetime64[ps]", "=datetime64[ps]", "datetime64[ps]", - "datetime64[fs]", "=datetime64[fs]", "datetime64[fs]", - "datetime64[as]", "=datetime64[as]", "datetime64[as]", - "M", "=M", "M", - "M8", "=M8", "M8", - "M8[Y]", "=M8[Y]", "M8[Y]", - "M8[M]", "=M8[M]", "M8[M]", - "M8[W]", "=M8[W]", "M8[W]", - "M8[D]", "=M8[D]", "M8[D]", - "M8[h]", "=M8[h]", "M8[h]", - "M8[m]", "=M8[m]", "M8[m]", - "M8[s]", "=M8[s]", "M8[s]", - "M8[ms]", "=M8[ms]", "M8[ms]", - "M8[us]", "=M8[us]", "M8[us]", - "M8[ns]", "=M8[ns]", "M8[ns]", - "M8[ps]", "=M8[ps]", "M8[ps]", - "M8[fs]", "=M8[fs]", "M8[fs]", - "M8[as]", "=M8[as]", "M8[as]", - ] - _TD64Codes = Literal[ - "timedelta64", "=timedelta64", "timedelta64", - "timedelta64[Y]", "=timedelta64[Y]", "timedelta64[Y]", - "timedelta64[M]", "=timedelta64[M]", "timedelta64[M]", - "timedelta64[W]", "=timedelta64[W]", "timedelta64[W]", - "timedelta64[D]", "=timedelta64[D]", "timedelta64[D]", - "timedelta64[h]", "=timedelta64[h]", "timedelta64[h]", - "timedelta64[m]", "=timedelta64[m]", "timedelta64[m]", - "timedelta64[s]", "=timedelta64[s]", "timedelta64[s]", - "timedelta64[ms]", "=timedelta64[ms]", "timedelta64[ms]", - "timedelta64[us]", "=timedelta64[us]", "timedelta64[us]", - "timedelta64[ns]", "=timedelta64[ns]", "timedelta64[ns]", - "timedelta64[ps]", "=timedelta64[ps]", "timedelta64[ps]", - "timedelta64[fs]", "=timedelta64[fs]", "timedelta64[fs]", - "timedelta64[as]", "=timedelta64[as]", "timedelta64[as]", - "m", "=m", "m", - "m8", "=m8", "m8", - "m8[Y]", "=m8[Y]", "m8[Y]", - "m8[M]", "=m8[M]", "m8[M]", - "m8[W]", "=m8[W]", "m8[W]", - "m8[D]", "=m8[D]", "m8[D]", - "m8[h]", "=m8[h]", "m8[h]", - "m8[m]", "=m8[m]", "m8[m]", - "m8[s]", "=m8[s]", "m8[s]", - "m8[ms]", "=m8[ms]", "m8[ms]", - "m8[us]", "=m8[us]", "m8[us]", - "m8[ns]", "=m8[ns]", "m8[ns]", - "m8[ps]", "=m8[ps]", "m8[ps]", - "m8[fs]", "=m8[fs]", "m8[fs]", - "m8[as]", "=m8[as]", "m8[as]", - ] - -else: - _BoolCodes = Any - - _UInt8Codes = Any - _UInt16Codes = Any - _UInt32Codes = Any - _UInt64Codes = Any - - _Int8Codes = Any - _Int16Codes = Any - _Int32Codes = Any - _Int64Codes = Any - - _Float16Codes = Any - _Float32Codes = Any - _Float64Codes = Any - - _Complex64Codes = Any - _Complex128Codes = Any - - _ByteCodes = Any - _ShortCodes = Any - _IntCCodes = Any - _IntPCodes = Any - _IntCodes = Any - _LongLongCodes = Any - - _UByteCodes = Any - _UShortCodes = Any - _UIntCCodes = Any - _UIntPCodes = Any - _UIntCodes = Any - _ULongLongCodes = Any - - _HalfCodes = Any - _SingleCodes = Any - _DoubleCodes = Any - _LongDoubleCodes = Any - - _CSingleCodes = Any - _CDoubleCodes = Any - _CLongDoubleCodes = Any - - _StrCodes = Any - _BytesCodes = Any - _VoidCodes = Any - _ObjectCodes = Any - - _DT64Codes = Any - _TD64Codes = Any +from typing import Literal + +_BoolCodes = Literal["?", "=?", "?", "bool", "bool_", "bool8"] + +_UInt8Codes = Literal["uint8", "u1", "=u1", "u1"] +_UInt16Codes = Literal["uint16", "u2", "=u2", "u2"] +_UInt32Codes = Literal["uint32", "u4", "=u4", "u4"] +_UInt64Codes = Literal["uint64", "u8", "=u8", "u8"] + +_Int8Codes = Literal["int8", "i1", "=i1", "i1"] +_Int16Codes = Literal["int16", "i2", "=i2", "i2"] +_Int32Codes = Literal["int32", "i4", "=i4", "i4"] +_Int64Codes = Literal["int64", "i8", "=i8", "i8"] + +_Float16Codes = Literal["float16", "f2", "=f2", "f2"] +_Float32Codes = Literal["float32", "f4", "=f4", "f4"] +_Float64Codes = Literal["float64", "f8", "=f8", "f8"] + +_Complex64Codes = Literal["complex64", "c8", "=c8", "c8"] +_Complex128Codes = Literal["complex128", "c16", "=c16", "c16"] + +_ByteCodes = Literal["byte", "b", "=b", "b"] +_ShortCodes = Literal["short", "h", "=h", "h"] +_IntCCodes = Literal["intc", "i", "=i", "i"] +_IntPCodes = Literal["intp", "int0", "p", "=p", "p"] +_IntCodes = Literal["long", "int", "int_", "l", "=l", "l"] +_LongLongCodes = Literal["longlong", "q", "=q", "q"] + +_UByteCodes = Literal["ubyte", "B", "=B", "B"] +_UShortCodes = Literal["ushort", "H", "=H", "H"] +_UIntCCodes = Literal["uintc", "I", "=I", "I"] +_UIntPCodes = Literal["uintp", "uint0", "P", "=P", "P"] +_UIntCodes = Literal["uint", "L", "=L", "L"] +_ULongLongCodes = Literal["ulonglong", "Q", "=Q", "Q"] + +_HalfCodes = Literal["half", "e", "=e", "e"] +_SingleCodes = Literal["single", "f", "=f", "f"] +_DoubleCodes = Literal["double", "float", "float_", "d", "=d", "d"] +_LongDoubleCodes = Literal["longdouble", "longfloat", "g", "=g", "g"] + +_CSingleCodes = Literal["csingle", "singlecomplex", "F", "=F", "F"] +_CDoubleCodes = Literal["cdouble", "complex", "complex_", "cfloat", "D", "=D", "D"] +_CLongDoubleCodes = Literal["clongdouble", "clongfloat", "longcomplex", "G", "=G", "G"] + +_StrCodes = Literal["str", "str_", "str0", "unicode", "unicode_", "U", "=U", "U"] +_BytesCodes = Literal["bytes", "bytes_", "bytes0", "S", "=S", "S"] +_VoidCodes = Literal["void", "void0", "V", "=V", "V"] +_ObjectCodes = Literal["object", "object_", "O", "=O", "O"] + +_DT64Codes = Literal[ + "datetime64", "=datetime64", "datetime64", + "datetime64[Y]", "=datetime64[Y]", "datetime64[Y]", + "datetime64[M]", "=datetime64[M]", "datetime64[M]", + "datetime64[W]", "=datetime64[W]", "datetime64[W]", + "datetime64[D]", "=datetime64[D]", "datetime64[D]", + "datetime64[h]", "=datetime64[h]", "datetime64[h]", + "datetime64[m]", "=datetime64[m]", "datetime64[m]", + "datetime64[s]", "=datetime64[s]", "datetime64[s]", + "datetime64[ms]", "=datetime64[ms]", "datetime64[ms]", + "datetime64[us]", "=datetime64[us]", "datetime64[us]", + "datetime64[ns]", "=datetime64[ns]", "datetime64[ns]", + "datetime64[ps]", "=datetime64[ps]", "datetime64[ps]", + "datetime64[fs]", "=datetime64[fs]", "datetime64[fs]", + "datetime64[as]", "=datetime64[as]", "datetime64[as]", + "M", "=M", "M", + "M8", "=M8", "M8", + "M8[Y]", "=M8[Y]", "M8[Y]", + "M8[M]", "=M8[M]", "M8[M]", + "M8[W]", "=M8[W]", "M8[W]", + "M8[D]", "=M8[D]", "M8[D]", + "M8[h]", "=M8[h]", "M8[h]", + "M8[m]", "=M8[m]", "M8[m]", + "M8[s]", "=M8[s]", "M8[s]", + "M8[ms]", "=M8[ms]", "M8[ms]", + "M8[us]", "=M8[us]", "M8[us]", + "M8[ns]", "=M8[ns]", "M8[ns]", + "M8[ps]", "=M8[ps]", "M8[ps]", + "M8[fs]", "=M8[fs]", "M8[fs]", + "M8[as]", "=M8[as]", "M8[as]", +] +_TD64Codes = Literal[ + "timedelta64", "=timedelta64", "timedelta64", + "timedelta64[Y]", "=timedelta64[Y]", "timedelta64[Y]", + "timedelta64[M]", "=timedelta64[M]", "timedelta64[M]", + "timedelta64[W]", "=timedelta64[W]", "timedelta64[W]", + "timedelta64[D]", "=timedelta64[D]", "timedelta64[D]", + "timedelta64[h]", "=timedelta64[h]", "timedelta64[h]", + "timedelta64[m]", "=timedelta64[m]", "timedelta64[m]", + "timedelta64[s]", "=timedelta64[s]", "timedelta64[s]", + "timedelta64[ms]", "=timedelta64[ms]", "timedelta64[ms]", + "timedelta64[us]", "=timedelta64[us]", "timedelta64[us]", + "timedelta64[ns]", "=timedelta64[ns]", "timedelta64[ns]", + "timedelta64[ps]", "=timedelta64[ps]", "timedelta64[ps]", + "timedelta64[fs]", "=timedelta64[fs]", "timedelta64[fs]", + "timedelta64[as]", "=timedelta64[as]", "timedelta64[as]", + "m", "=m", "m", + "m8", "=m8", "m8", + "m8[Y]", "=m8[Y]", "m8[Y]", + "m8[M]", "=m8[M]", "m8[M]", + "m8[W]", "=m8[W]", "m8[W]", + "m8[D]", "=m8[D]", "m8[D]", + "m8[h]", "=m8[h]", "m8[h]", + "m8[m]", "=m8[m]", "m8[m]", + "m8[s]", "=m8[s]", "m8[s]", + "m8[ms]", "=m8[ms]", "m8[ms]", + "m8[us]", "=m8[us]", "m8[us]", + "m8[ns]", "=m8[ns]", "m8[ns]", + "m8[ps]", "=m8[ps]", "m8[ps]", + "m8[fs]", "=m8[fs]", "m8[fs]", + "m8[as]", "=m8[as]", "m8[as]", +] diff --git a/numpy/typing/_dtype_like.py b/numpy/typing/_dtype_like.py index b2ce3adb4..0955f5b18 100644 --- a/numpy/typing/_dtype_like.py +++ b/numpy/typing/_dtype_like.py @@ -1,19 +1,10 @@ -import sys -from typing import Any, List, Sequence, Tuple, Union, Type, TypeVar, TYPE_CHECKING +from typing import Any, List, Sequence, Tuple, Union, Type, TypeVar, Protocol, TypedDict import numpy as np -from . import _HAS_TYPING_EXTENSIONS from ._shape import _ShapeLike from ._generic_alias import _DType as DType -if sys.version_info >= (3, 8): - from typing import Protocol, TypedDict -elif _HAS_TYPING_EXTENSIONS: - from typing_extensions import Protocol, TypedDict -else: - from ._generic_alias import _GenericAlias as GenericAlias - from ._char_codes import ( _BoolCodes, _UInt8Codes, @@ -59,30 +50,22 @@ from ._char_codes import ( _DTypeLikeNested = Any # TODO: wait for support for recursive types _DType_co = TypeVar("_DType_co", covariant=True, bound=DType[Any]) -if TYPE_CHECKING or _HAS_TYPING_EXTENSIONS or sys.version_info >= (3, 8): - # Mandatory keys - class _DTypeDictBase(TypedDict): - names: Sequence[str] - formats: Sequence[_DTypeLikeNested] - - # Mandatory + optional keys - class _DTypeDict(_DTypeDictBase, total=False): - offsets: Sequence[int] - titles: Sequence[Any] # Only `str` elements are usable as indexing aliases, but all objects are legal - itemsize: int - aligned: bool - - # A protocol for anything with the dtype attribute - class _SupportsDType(Protocol[_DType_co]): - @property - def dtype(self) -> _DType_co: ... - -else: - _DTypeDict = Any - - class _SupportsDType: ... - _SupportsDType = GenericAlias(_SupportsDType, _DType_co) - +# Mandatory keys +class _DTypeDictBase(TypedDict): + names: Sequence[str] + formats: Sequence[_DTypeLikeNested] + +# Mandatory + optional keys +class _DTypeDict(_DTypeDictBase, total=False): + offsets: Sequence[int] + titles: Sequence[Any] # Only `str` elements are usable as indexing aliases, but all objects are legal + itemsize: int + aligned: bool + +# A protocol for anything with the dtype attribute +class _SupportsDType(Protocol[_DType_co]): + @property + def dtype(self) -> _DType_co: ... # Would create a dtype[np.void] _VoidDTypeLike = Union[ diff --git a/numpy/typing/_shape.py b/numpy/typing/_shape.py index 75698f3d3..c28859b19 100644 --- a/numpy/typing/_shape.py +++ b/numpy/typing/_shape.py @@ -1,14 +1,4 @@ -import sys -from typing import Sequence, Tuple, Union, Any - -from . import _HAS_TYPING_EXTENSIONS - -if sys.version_info >= (3, 8): - from typing import SupportsIndex -elif _HAS_TYPING_EXTENSIONS: - from typing_extensions import SupportsIndex -else: - SupportsIndex = Any +from typing import Sequence, Tuple, Union, SupportsIndex _Shape = Tuple[int, ...] diff --git a/numpy/typing/tests/test_runtime.py b/numpy/typing/tests/test_runtime.py index e82b08ac2..151b06bed 100644 --- a/numpy/typing/tests/test_runtime.py +++ b/numpy/typing/tests/test_runtime.py @@ -3,18 +3,12 @@ from __future__ import annotations import sys -from typing import get_type_hints, Union, Tuple, NamedTuple +from typing import get_type_hints, Union, Tuple, NamedTuple, get_args, get_origin import pytest import numpy as np import numpy.typing as npt -try: - from typing_extensions import get_args, get_origin - SKIP = False -except ImportError: - SKIP = True - class TypeTup(NamedTuple): typ: type @@ -36,7 +30,6 @@ TYPES = { @pytest.mark.parametrize("name,tup", TYPES.items(), ids=TYPES.keys()) -@pytest.mark.skipif(SKIP, reason="requires typing-extensions") def test_get_args(name: type, tup: TypeTup) -> None: """Test `typing.get_args`.""" typ, ref = tup.typ, tup.args @@ -45,7 +38,6 @@ def test_get_args(name: type, tup: TypeTup) -> None: @pytest.mark.parametrize("name,tup", TYPES.items(), ids=TYPES.keys()) -@pytest.mark.skipif(SKIP, reason="requires typing-extensions") def test_get_origin(name: type, tup: TypeTup) -> None: """Test `typing.get_origin`.""" typ, ref = tup.typ, tup.origin -- cgit v1.2.1 From 355336119136f620c27f1adcc26074eb9528c159 Mon Sep 17 00:00:00 2001 From: Bas van Beek <43369155+BvB93@users.noreply.github.com> Date: Sat, 14 Aug 2021 22:02:29 +0200 Subject: MAINT: Drop .pyi code-paths specific to Python 3.7 --- numpy/__init__.pyi | 41 ++++++++++-------------------- numpy/_pytesttester.pyi | 3 +-- numpy/core/_asarray.pyi | 8 +----- numpy/core/_type_aliases.pyi | 8 +----- numpy/core/_ufunc_config.pyi | 8 +----- numpy/core/arrayprint.pyi | 8 +----- numpy/core/einsumfunc.pyi | 8 +----- numpy/core/fromnumeric.pyi | 8 +----- numpy/core/function_base.pyi | 8 +----- numpy/core/multiarray.pyi | 10 +++----- numpy/core/numeric.pyi | 7 +---- numpy/core/numerictypes.pyi | 8 +++--- numpy/core/shape_base.pyi | 8 +----- numpy/f2py/__init__.pyi | 3 +-- numpy/lib/arraypad.pyi | 8 ++---- numpy/lib/arrayterator.pyi | 1 - numpy/lib/format.pyi | 8 +----- numpy/lib/index_tricks.pyi | 8 ++---- numpy/lib/npyio.pyi | 4 +-- numpy/lib/shape_base.pyi | 3 +-- numpy/lib/stride_tricks.pyi | 3 +-- numpy/lib/type_check.pyi | 8 ++---- numpy/lib/utils.pyi | 7 +---- numpy/random/_generator.pyi | 8 +----- numpy/random/_mt19937.pyi | 8 +----- numpy/random/_pcg64.pyi | 8 +----- numpy/random/_philox.pyi | 8 +----- numpy/random/_sfc64.pyi | 8 +----- numpy/random/bit_generator.pyi | 7 +---- numpy/random/mtrand.pyi | 8 +----- numpy/testing/_private/utils.pyi | 8 +++--- numpy/typing/_ufunc.pyi | 4 +-- numpy/typing/tests/data/reveal/arraypad.py | 3 +-- 33 files changed, 57 insertions(+), 199 deletions(-) (limited to 'numpy') diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index fb68f4a56..f398f67b7 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -164,6 +164,7 @@ from numpy.typing._extended_precision import ( ) from typing import ( + Literal as L, Any, ByteString, Callable, @@ -189,13 +190,11 @@ from typing import ( Type, TypeVar, Union, + Protocol, + SupportsIndex, + Final, ) -if sys.version_info >= (3, 8): - from typing import Literal as L, Protocol, SupportsIndex, Final -else: - from typing_extensions import Literal as L, Protocol, SupportsIndex, Final - # Ensures that the stubs are picked up from numpy import ( char as char, @@ -3163,28 +3162,16 @@ class datetime64(generic): __gt__: _ComparisonOp[datetime64, _ArrayLikeDT64_co] __ge__: _ComparisonOp[datetime64, _ArrayLikeDT64_co] -# Support for `__index__` was added in python 3.8 (bpo-20092) -if sys.version_info >= (3, 8): - _IntValue = Union[SupportsInt, _CharLike_co, SupportsIndex] - _FloatValue = Union[None, _CharLike_co, SupportsFloat, SupportsIndex] - _ComplexValue = Union[ - None, - _CharLike_co, - SupportsFloat, - SupportsComplex, - SupportsIndex, - complex, # `complex` is not a subtype of `SupportsComplex` - ] -else: - _IntValue = Union[SupportsInt, _CharLike_co] - _FloatValue = Union[None, _CharLike_co, SupportsFloat] - _ComplexValue = Union[ - None, - _CharLike_co, - SupportsFloat, - SupportsComplex, - complex, - ] +_IntValue = Union[SupportsInt, _CharLike_co, SupportsIndex] +_FloatValue = Union[None, _CharLike_co, SupportsFloat, SupportsIndex] +_ComplexValue = Union[ + None, + _CharLike_co, + SupportsFloat, + SupportsComplex, + SupportsIndex, + complex, # `complex` is not a subtype of `SupportsComplex` +] class integer(number[_NBit1]): # type: ignore @property diff --git a/numpy/_pytesttester.pyi b/numpy/_pytesttester.pyi index 693f4128a..0be64b3f7 100644 --- a/numpy/_pytesttester.pyi +++ b/numpy/_pytesttester.pyi @@ -1,5 +1,4 @@ -from typing import List, Iterable -from typing_extensions import Literal as L +from typing import List, Iterable, Literal as L __all__: List[str] diff --git a/numpy/core/_asarray.pyi b/numpy/core/_asarray.pyi index 1928cfe12..fee9b7b6e 100644 --- a/numpy/core/_asarray.pyi +++ b/numpy/core/_asarray.pyi @@ -1,14 +1,8 @@ -import sys -from typing import TypeVar, Union, Iterable, overload +from typing import TypeVar, Union, Iterable, overload, Literal from numpy import ndarray from numpy.typing import ArrayLike, DTypeLike -if sys.version_info >= (3, 8): - from typing import Literal -else: - from typing_extensions import Literal - _ArrayType = TypeVar("_ArrayType", bound=ndarray) _Requirements = Literal[ diff --git a/numpy/core/_type_aliases.pyi b/numpy/core/_type_aliases.pyi index 6a1099cd3..c10d072f9 100644 --- a/numpy/core/_type_aliases.pyi +++ b/numpy/core/_type_aliases.pyi @@ -1,13 +1,7 @@ -import sys -from typing import Dict, Union, Type, List +from typing import Dict, Union, Type, List, TypedDict from numpy import generic, signedinteger, unsignedinteger, floating, complexfloating -if sys.version_info >= (3, 8): - from typing import TypedDict -else: - from typing_extensions import TypedDict - class _SCTypes(TypedDict): int: List[Type[signedinteger]] uint: List[Type[unsignedinteger]] diff --git a/numpy/core/_ufunc_config.pyi b/numpy/core/_ufunc_config.pyi index e90f1c510..aa48ddba7 100644 --- a/numpy/core/_ufunc_config.pyi +++ b/numpy/core/_ufunc_config.pyi @@ -1,10 +1,4 @@ -import sys -from typing import Optional, Union, Callable, Any - -if sys.version_info >= (3, 8): - from typing import Literal, Protocol, TypedDict -else: - from typing_extensions import Literal, Protocol, TypedDict +from typing import Optional, Union, Callable, Any, Literal, Protocol, TypedDict _ErrKind = Literal["ignore", "warn", "raise", "call", "print", "log"] _ErrFunc = Callable[[str, int], Any] diff --git a/numpy/core/arrayprint.pyi b/numpy/core/arrayprint.pyi index ac2b6f5a8..df22efed6 100644 --- a/numpy/core/arrayprint.pyi +++ b/numpy/core/arrayprint.pyi @@ -1,6 +1,5 @@ -import sys from types import TracebackType -from typing import Any, Optional, Callable, Union, Type +from typing import Any, Optional, Callable, Union, Type, Literal, TypedDict, SupportsIndex # Using a private class is by no means ideal, but it is simply a consquence # of a `contextlib.context` returning an instance of aformentioned class @@ -23,11 +22,6 @@ from numpy import ( ) from numpy.typing import ArrayLike, _CharLike_co, _FloatLike_co -if sys.version_info > (3, 8): - from typing import Literal, TypedDict, SupportsIndex -else: - from typing_extensions import Literal, TypedDict, SupportsIndex - _FloatMode = Literal["fixed", "unique", "maxprec", "maxprec_equal"] class _FormatDict(TypedDict, total=False): diff --git a/numpy/core/einsumfunc.pyi b/numpy/core/einsumfunc.pyi index 2457e8719..bb02590e6 100644 --- a/numpy/core/einsumfunc.pyi +++ b/numpy/core/einsumfunc.pyi @@ -1,5 +1,4 @@ -import sys -from typing import List, TypeVar, Optional, Any, overload, Union, Tuple, Sequence +from typing import List, TypeVar, Optional, Any, overload, Union, Tuple, Sequence, Literal from numpy import ( ndarray, @@ -26,11 +25,6 @@ from numpy.typing import ( _DTypeLikeComplex_co, ) -if sys.version_info >= (3, 8): - from typing import Literal -else: - from typing_extensions import Literal - _ArrayType = TypeVar( "_ArrayType", bound=ndarray[Any, dtype[Union[bool_, number[Any]]]], diff --git a/numpy/core/fromnumeric.pyi b/numpy/core/fromnumeric.pyi index 45057e4b1..3cbe1d5c5 100644 --- a/numpy/core/fromnumeric.pyi +++ b/numpy/core/fromnumeric.pyi @@ -1,6 +1,5 @@ -import sys import datetime as dt -from typing import Optional, Union, Sequence, Tuple, Any, overload, TypeVar +from typing import Optional, Union, Sequence, Tuple, Any, overload, TypeVar, Literal from numpy import ( ndarray, @@ -26,11 +25,6 @@ from numpy.typing import ( _NumberLike_co, ) -if sys.version_info >= (3, 8): - from typing import Literal -else: - from typing_extensions import Literal - # Various annotations for scalars # While dt.datetime and dt.timedelta are not technically part of NumPy, diff --git a/numpy/core/function_base.pyi b/numpy/core/function_base.pyi index b5d6ca6ab..c35629aa7 100644 --- a/numpy/core/function_base.pyi +++ b/numpy/core/function_base.pyi @@ -1,14 +1,8 @@ -import sys -from typing import overload, Tuple, Union, Sequence, Any +from typing import overload, Tuple, Union, Sequence, Any, SupportsIndex, Literal from numpy import ndarray from numpy.typing import ArrayLike, DTypeLike, _SupportsArray, _NumberLike_co -if sys.version_info >= (3, 8): - from typing import SupportsIndex, Literal -else: - from typing_extensions import SupportsIndex, Literal - # TODO: wait for support for recursive types _ArrayLikeNested = Sequence[Sequence[Any]] _ArrayLikeNumber = Union[ diff --git a/numpy/core/multiarray.pyi b/numpy/core/multiarray.pyi index a7d2e6bbf..b807ddff0 100644 --- a/numpy/core/multiarray.pyi +++ b/numpy/core/multiarray.pyi @@ -1,9 +1,9 @@ # TODO: Sort out any and all missing functions in this namespace import os -import sys import datetime as dt from typing import ( + Literal as L, Any, Callable, IO, @@ -16,6 +16,9 @@ from typing import ( Union, Sequence, Tuple, + SupportsIndex, + final, + Final, ) from numpy import ( @@ -78,11 +81,6 @@ from numpy.typing import ( _TD64Like_co, ) -if sys.version_info >= (3, 8): - from typing import SupportsIndex, final, Final, Literal as L -else: - from typing_extensions import SupportsIndex, final, Final, Literal as L - _SCT = TypeVar("_SCT", bound=generic) _ArrayType = TypeVar("_ArrayType", bound=NDArray[Any]) diff --git a/numpy/core/numeric.pyi b/numpy/core/numeric.pyi index 3c2b553ec..54ab4b7c8 100644 --- a/numpy/core/numeric.pyi +++ b/numpy/core/numeric.pyi @@ -1,4 +1,3 @@ -import sys from typing import ( Any, Optional, @@ -10,16 +9,12 @@ from typing import ( overload, TypeVar, Iterable, + Literal, ) from numpy import ndarray, generic, dtype, bool_, signedinteger, _OrderKACF, _OrderCF from numpy.typing import ArrayLike, DTypeLike, _ShapeLike -if sys.version_info >= (3, 8): - from typing import Literal -else: - from typing_extensions import Literal - _T = TypeVar("_T") _ArrayType = TypeVar("_ArrayType", bound=ndarray) diff --git a/numpy/core/numerictypes.pyi b/numpy/core/numerictypes.pyi index d5e3ccffb..1d3ff773b 100644 --- a/numpy/core/numerictypes.pyi +++ b/numpy/core/numerictypes.pyi @@ -1,6 +1,7 @@ import sys import types from typing import ( + Literal as L, Type, Union, Tuple, @@ -10,6 +11,8 @@ from typing import ( Dict, List, Iterable, + Protocol, + TypedDict, ) from numpy import ( @@ -49,11 +52,6 @@ from numpy.core._type_aliases import ( from numpy.typing import DTypeLike, ArrayLike, _SupportsDType -if sys.version_info >= (3, 8): - from typing import Literal as L, Protocol, TypedDict -else: - from typing_extensions import Literal as L, Protocol, TypedDict - _T = TypeVar("_T") _SCT = TypeVar("_SCT", bound=generic) diff --git a/numpy/core/shape_base.pyi b/numpy/core/shape_base.pyi index 9aaeceed7..a640991d3 100644 --- a/numpy/core/shape_base.pyi +++ b/numpy/core/shape_base.pyi @@ -1,14 +1,8 @@ -import sys -from typing import TypeVar, overload, List, Sequence, Any +from typing import TypeVar, overload, List, Sequence, Any, SupportsIndex from numpy import generic, dtype from numpy.typing import ArrayLike, NDArray, _NestedSequence, _SupportsArray -if sys.version_info >= (3, 8): - from typing import SupportsIndex -else: - from typing_extensions import SupportsIndex - _SCT = TypeVar("_SCT", bound=generic) _ArrayType = TypeVar("_ArrayType", bound=NDArray[Any]) diff --git a/numpy/f2py/__init__.pyi b/numpy/f2py/__init__.pyi index 7d8e092ea..e52e12bbd 100644 --- a/numpy/f2py/__init__.pyi +++ b/numpy/f2py/__init__.pyi @@ -1,7 +1,6 @@ import os import subprocess -from typing import Any, List, Iterable, Dict, overload -from typing_extensions import TypedDict, Literal as L +from typing import Literal as L, Any, List, Iterable, Dict, overload, TypedDict from numpy._pytesttester import PytestTester diff --git a/numpy/lib/arraypad.pyi b/numpy/lib/arraypad.pyi index df9538dd7..d6e07a6bd 100644 --- a/numpy/lib/arraypad.pyi +++ b/numpy/lib/arraypad.pyi @@ -1,11 +1,12 @@ -import sys from typing import ( + Literal as L, Any, Dict, List, overload, Tuple, TypeVar, + Protocol, ) from numpy import ndarray, dtype, generic @@ -18,11 +19,6 @@ from numpy.typing import ( _SupportsArray, ) -if sys.version_info >= (3, 8): - from typing import Literal as L, Protocol -else: - from typing_extensions import Literal as L, Protocol - _SCT = TypeVar("_SCT", bound=generic) class _ModeFunc(Protocol): diff --git a/numpy/lib/arrayterator.pyi b/numpy/lib/arrayterator.pyi index 39d6fd843..82c669206 100644 --- a/numpy/lib/arrayterator.pyi +++ b/numpy/lib/arrayterator.pyi @@ -1,4 +1,3 @@ -import sys from typing import ( List, Any, diff --git a/numpy/lib/format.pyi b/numpy/lib/format.pyi index 4c44d57bf..092245daf 100644 --- a/numpy/lib/format.pyi +++ b/numpy/lib/format.pyi @@ -1,10 +1,4 @@ -import sys -from typing import Any, List, Set - -if sys.version_info >= (3, 8): - from typing import Literal, Final -else: - from typing_extensions import Literal, Final +from typing import Any, List, Set, Literal, Final __all__: List[str] diff --git a/numpy/lib/index_tricks.pyi b/numpy/lib/index_tricks.pyi index 0f9ae94a9..530be3cae 100644 --- a/numpy/lib/index_tricks.pyi +++ b/numpy/lib/index_tricks.pyi @@ -1,4 +1,3 @@ -import sys from typing import ( Any, Tuple, @@ -8,6 +7,8 @@ from typing import ( List, Union, Sequence, + Literal, + SupportsIndex, ) from numpy import ( @@ -49,11 +50,6 @@ from numpy.core.multiarray import ( ravel_multi_index as ravel_multi_index, ) -if sys.version_info >= (3, 8): - from typing import Literal, SupportsIndex -else: - from typing_extensions import Literal, SupportsIndex - _T = TypeVar("_T") _DType = TypeVar("_DType", bound=dtype[Any]) _BoolType = TypeVar("_BoolType", Literal[True], Literal[False]) diff --git a/numpy/lib/npyio.pyi b/numpy/lib/npyio.pyi index de6bc3ded..1321afb55 100644 --- a/numpy/lib/npyio.pyi +++ b/numpy/lib/npyio.pyi @@ -3,6 +3,7 @@ import sys import zipfile import types from typing import ( + Literal as L, Any, Mapping, TypeVar, @@ -16,6 +17,7 @@ from typing import ( Sequence, Callable, Pattern, + Protocol, ) from numpy import ( @@ -36,8 +38,6 @@ from numpy.core.multiarray import ( unpackbits as unpackbits, ) -from typing_extensions import Protocol, Literal as L - _T = TypeVar("_T") _T_contra = TypeVar("_T_contra", contravariant=True) _T_co = TypeVar("_T_co", covariant=True) diff --git a/numpy/lib/shape_base.pyi b/numpy/lib/shape_base.pyi index cfb3040b7..4c275cc8c 100644 --- a/numpy/lib/shape_base.pyi +++ b/numpy/lib/shape_base.pyi @@ -1,5 +1,4 @@ -from typing import List, TypeVar, Callable, Sequence, Any, overload, Tuple -from typing_extensions import SupportsIndex, Protocol +from typing import List, TypeVar, Callable, Sequence, Any, overload, Tuple, SupportsIndex, Protocol from numpy import ( generic, diff --git a/numpy/lib/stride_tricks.pyi b/numpy/lib/stride_tricks.pyi index 9e4e46b8b..bafc46e9c 100644 --- a/numpy/lib/stride_tricks.pyi +++ b/numpy/lib/stride_tricks.pyi @@ -1,5 +1,4 @@ -from typing import Any, List, Dict, Iterable, TypeVar, overload -from typing_extensions import SupportsIndex +from typing import Any, List, Dict, Iterable, TypeVar, overload, SupportsIndex from numpy import dtype, generic from numpy.typing import ( diff --git a/numpy/lib/type_check.pyi b/numpy/lib/type_check.pyi index fbe325858..5eb0e62d2 100644 --- a/numpy/lib/type_check.pyi +++ b/numpy/lib/type_check.pyi @@ -1,5 +1,5 @@ -import sys from typing import ( + Literal as L, Any, Container, Iterable, @@ -7,6 +7,7 @@ from typing import ( overload, Type, TypeVar, + Protocol, ) from numpy import ( @@ -32,11 +33,6 @@ from numpy.typing import ( _DTypeLikeComplex, ) -if sys.version_info >= (3, 8): - from typing import Protocol, Literal as L -else: - from typing_extensions import Protocol, Literal as L - _T = TypeVar("_T") _T_co = TypeVar("_T_co", covariant=True) _SCT = TypeVar("_SCT", bound=generic) diff --git a/numpy/lib/utils.pyi b/numpy/lib/utils.pyi index 0518655c6..c13a219b5 100644 --- a/numpy/lib/utils.pyi +++ b/numpy/lib/utils.pyi @@ -1,4 +1,3 @@ -import sys from ast import AST from typing import ( Any, @@ -11,6 +10,7 @@ from typing import ( Tuple, TypeVar, Union, + Protocol, ) from numpy import ndarray, generic @@ -21,11 +21,6 @@ from numpy.core.numerictypes import ( issubsctype as issubsctype, ) -if sys.version_info >= (3, 8): - from typing import Protocol -else: - from typing_extensions import Protocol - _T_contra = TypeVar("_T_contra", contravariant=True) _FuncType = TypeVar("_FuncType", bound=Callable[..., Any]) diff --git a/numpy/random/_generator.pyi b/numpy/random/_generator.pyi index 14dc55131..64b683d7c 100644 --- a/numpy/random/_generator.pyi +++ b/numpy/random/_generator.pyi @@ -1,5 +1,4 @@ -import sys -from typing import Any, Callable, Dict, Optional, Tuple, Type, Union, overload, TypeVar +from typing import Any, Callable, Dict, Optional, Tuple, Type, Union, overload, TypeVar, Literal from numpy import ( bool_, @@ -44,11 +43,6 @@ from numpy.typing import ( _UIntCodes, ) -if sys.version_info >= (3, 8): - from typing import Literal -else: - from typing_extensions import Literal - _ArrayType = TypeVar("_ArrayType", bound=ndarray[Any, Any]) _DTypeLikeFloat32 = Union[ diff --git a/numpy/random/_mt19937.pyi b/numpy/random/_mt19937.pyi index 1b8bacdae..820f27392 100644 --- a/numpy/random/_mt19937.pyi +++ b/numpy/random/_mt19937.pyi @@ -1,15 +1,9 @@ -import sys -from typing import Any, Union +from typing import Any, Union, TypedDict from numpy import dtype, ndarray, uint32 from numpy.random.bit_generator import BitGenerator, SeedSequence from numpy.typing import _ArrayLikeInt_co -if sys.version_info >= (3, 8): - from typing import TypedDict -else: - from typing_extensions import TypedDict - class _MT19937Internal(TypedDict): key: ndarray[Any, dtype[uint32]] pos: int diff --git a/numpy/random/_pcg64.pyi b/numpy/random/_pcg64.pyi index 25e2fdde6..4881a987e 100644 --- a/numpy/random/_pcg64.pyi +++ b/numpy/random/_pcg64.pyi @@ -1,14 +1,8 @@ -import sys -from typing import Union +from typing import Union, TypedDict from numpy.random.bit_generator import BitGenerator, SeedSequence from numpy.typing import _ArrayLikeInt_co -if sys.version_info >= (3, 8): - from typing import TypedDict -else: - from typing_extensions import TypedDict - class _PCG64Internal(TypedDict): state: int inc: int diff --git a/numpy/random/_philox.pyi b/numpy/random/_philox.pyi index f6a5b9b9b..dd1c5e6e9 100644 --- a/numpy/random/_philox.pyi +++ b/numpy/random/_philox.pyi @@ -1,15 +1,9 @@ -import sys -from typing import Any, Union +from typing import Any, Union, TypedDict from numpy import dtype, ndarray, uint64 from numpy.random.bit_generator import BitGenerator, SeedSequence from numpy.typing import _ArrayLikeInt_co -if sys.version_info >= (3, 8): - from typing import TypedDict -else: - from typing_extensions import TypedDict - class _PhiloxInternal(TypedDict): counter: ndarray[Any, dtype[uint64]] key: ndarray[Any, dtype[uint64]] diff --git a/numpy/random/_sfc64.pyi b/numpy/random/_sfc64.pyi index 72a271c92..94d11a210 100644 --- a/numpy/random/_sfc64.pyi +++ b/numpy/random/_sfc64.pyi @@ -1,5 +1,4 @@ -import sys -from typing import Any, Union +from typing import Any, Union, TypedDict from numpy import dtype as dtype from numpy import ndarray as ndarray @@ -7,11 +6,6 @@ from numpy import uint64 from numpy.random.bit_generator import BitGenerator, SeedSequence from numpy.typing import _ArrayLikeInt_co -if sys.version_info >= (3, 8): - from typing import TypedDict -else: - from typing_extensions import TypedDict - class _SFC64Internal(TypedDict): state: ndarray[Any, dtype[uint64]] diff --git a/numpy/random/bit_generator.pyi b/numpy/random/bit_generator.pyi index 5b68dde6c..fa2f1ab12 100644 --- a/numpy/random/bit_generator.pyi +++ b/numpy/random/bit_generator.pyi @@ -1,5 +1,4 @@ import abc -import sys from threading import Lock from typing import ( Any, @@ -16,16 +15,12 @@ from typing import ( TypeVar, Union, overload, + Literal, ) from numpy import dtype, ndarray, uint32, uint64 from numpy.typing import _ArrayLikeInt_co, _ShapeLike, _SupportsDType, _UInt32Codes, _UInt64Codes -if sys.version_info >= (3, 8): - from typing import Literal -else: - from typing_extensions import Literal - _T = TypeVar("_T") _DTypeLikeUint32 = Union[ diff --git a/numpy/random/mtrand.pyi b/numpy/random/mtrand.pyi index 3137b0a95..cbe87a299 100644 --- a/numpy/random/mtrand.pyi +++ b/numpy/random/mtrand.pyi @@ -1,5 +1,4 @@ -import sys -from typing import Any, Callable, Dict, Optional, Tuple, Type, Union, overload +from typing import Any, Callable, Dict, Optional, Tuple, Type, Union, overload, Literal from numpy import ( bool_, @@ -44,11 +43,6 @@ from numpy.typing import ( _UIntCodes, ) -if sys.version_info >= (3, 8): - from typing import Literal -else: - from typing_extensions import Literal - _DTypeLikeFloat32 = Union[ dtype[float32], _SupportsDType[dtype[float32]], diff --git a/numpy/testing/_private/utils.pyi b/numpy/testing/_private/utils.pyi index 29915309f..ab5ca8784 100644 --- a/numpy/testing/_private/utils.pyi +++ b/numpy/testing/_private/utils.pyi @@ -6,6 +6,7 @@ import warnings import unittest import contextlib from typing import ( + Literal as L, Any, AnyStr, Callable, @@ -23,6 +24,8 @@ from typing import ( type_check_only, TypeVar, Union, + Final, + SupportsIndex, ) from numpy import generic, dtype, number, object_, bool_, _FloatValue @@ -40,11 +43,6 @@ from unittest.case import ( SkipTest as SkipTest, ) -if sys.version_info >= (3, 8): - from typing import Final, SupportsIndex, Literal as L -else: - from typing_extensions import Final, SupportsIndex, Literal as L - _T = TypeVar("_T") _ET = TypeVar("_ET", bound=BaseException) _FT = TypeVar("_FT", bound=Callable[..., Any]) diff --git a/numpy/typing/_ufunc.pyi b/numpy/typing/_ufunc.pyi index be1e654c2..37e6c008f 100644 --- a/numpy/typing/_ufunc.pyi +++ b/numpy/typing/_ufunc.pyi @@ -14,6 +14,8 @@ from typing import ( overload, Tuple, TypeVar, + Literal, + SupportsIndex, ) from numpy import ufunc, _CastingKind, _OrderKACF @@ -24,8 +26,6 @@ from ._scalars import _ScalarLike_co from ._array_like import ArrayLike, _ArrayLikeBool_co, _ArrayLikeInt_co from ._dtype_like import DTypeLike -from typing_extensions import Literal, SupportsIndex - _T = TypeVar("_T") _2Tuple = Tuple[_T, _T] _3Tuple = Tuple[_T, _T, _T] diff --git a/numpy/typing/tests/data/reveal/arraypad.py b/numpy/typing/tests/data/reveal/arraypad.py index ba5577ee0..03c03fb4e 100644 --- a/numpy/typing/tests/data/reveal/arraypad.py +++ b/numpy/typing/tests/data/reveal/arraypad.py @@ -1,5 +1,4 @@ -from typing import List, Any, Mapping, Tuple -from typing_extensions import SupportsIndex +from typing import List, Any, Mapping, Tuple, SupportsIndex import numpy as np import numpy.typing as npt -- cgit v1.2.1 From add26eb860dc1ee85c00d4c93a3ebf728b98448b Mon Sep 17 00:00:00 2001 From: Bas van Beek Date: Mon, 30 Aug 2021 15:07:45 +0200 Subject: BLD: Drop typing extension as an (optional) runtime dependency It might return in the future, but as of the moment we don't need it anymore --- numpy/typing/__init__.py | 7 ------ numpy/typing/tests/test_typing_extensions.py | 35 ---------------------------- 2 files changed, 42 deletions(-) delete mode 100644 numpy/typing/tests/test_typing_extensions.py (limited to 'numpy') diff --git a/numpy/typing/__init__.py b/numpy/typing/__init__.py index bfa7982c0..d60ddb5bb 100644 --- a/numpy/typing/__init__.py +++ b/numpy/typing/__init__.py @@ -5,13 +5,6 @@ Typing (:mod:`numpy.typing`) .. versionadded:: 1.20 -.. warning:: - - Some of the types in this module rely on features only present in - the standard library in Python 3.8 and greater. If you want to use - these types in earlier versions of Python, you should install the - typing-extensions_ package. - Large parts of the NumPy API have PEP-484-style type annotations. In addition a number of type aliases are available to users, most prominently the two below: diff --git a/numpy/typing/tests/test_typing_extensions.py b/numpy/typing/tests/test_typing_extensions.py deleted file mode 100644 index f59f222fb..000000000 --- a/numpy/typing/tests/test_typing_extensions.py +++ /dev/null @@ -1,35 +0,0 @@ -"""Tests for the optional typing-extensions dependency.""" - -import sys -import textwrap -import subprocess - -CODE = textwrap.dedent(r""" - import sys - import importlib - - assert "typing_extensions" not in sys.modules - assert "numpy.typing" not in sys.modules - - # Importing `typing_extensions` will now raise an `ImportError` - sys.modules["typing_extensions"] = None - assert importlib.import_module("numpy.typing") -""") - - -def test_no_typing_extensions() -> None: - """Import `numpy.typing` in the absence of typing-extensions. - - Notes - ----- - Ideally, we'd just run the normal typing tests in an environment where - typing-extensions is not installed, but unfortunatelly this is currently - impossible as it is an indirect hard dependency of pytest. - - """ - p = subprocess.run([sys.executable, '-c', CODE], capture_output=True) - if p.returncode: - raise AssertionError( - f"Non-zero return code: {p.returncode!r}\n\n{p.stderr.decode()}" - ) - -- cgit v1.2.1 From 45e43d72494a993188d809ef676fe3648a79e7bf Mon Sep 17 00:00:00 2001 From: Bas van Beek Date: Tue, 31 Aug 2021 12:03:27 +0200 Subject: STY: Use the PEP 457 positional-only syntax in the stub files --- numpy/__init__.pyi | 115 ++++++++++++++++++++------------------- numpy/core/_ufunc_config.pyi | 2 +- numpy/core/einsumfunc.pyi | 27 ++++++--- numpy/core/multiarray.pyi | 93 +++++++++++++++++-------------- numpy/core/shape_base.pyi | 12 ++-- numpy/lib/arraypad.pyi | 9 +-- numpy/lib/npyio.pyi | 7 ++- numpy/lib/shape_base.pyi | 10 ++-- numpy/lib/utils.pyi | 5 +- numpy/testing/_private/utils.pyi | 23 +++++--- 10 files changed, 168 insertions(+), 135 deletions(-) (limited to 'numpy') diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index f398f67b7..ca13cffb8 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -1184,9 +1184,9 @@ class flatiter(Generic[_NdArraySubClass]): self, key: Union[_ArrayLikeInt, slice, ellipsis], ) -> _NdArraySubClass: ... @overload - def __array__(self: flatiter[ndarray[Any, _DType]], __dtype: None = ...) -> ndarray[Any, _DType]: ... + def __array__(self: flatiter[ndarray[Any, _DType]], dtype: None = ..., /) -> ndarray[Any, _DType]: ... @overload - def __array__(self, __dtype: _DType) -> ndarray[Any, _DType]: ... + def __array__(self, dtype: _DType, /) -> ndarray[Any, _DType]: ... _OrderKACF = Optional[L["K", "A", "C", "F"]] _OrderACF = Optional[L["A", "C", "F"]] @@ -1215,7 +1215,7 @@ class _ArrayOrScalarCommon: def __str__(self) -> str: ... def __repr__(self) -> str: ... def __copy__(self: _ArraySelf) -> _ArraySelf: ... - def __deepcopy__(self: _ArraySelf, __memo: Optional[dict] = ...) -> _ArraySelf: ... + def __deepcopy__(self: _ArraySelf, memo: None | dict = ..., /) -> _ArraySelf: ... def __eq__(self, other): ... def __ne__(self, other): ... def copy(self: _ArraySelf, order: _OrderKACF = ...) -> _ArraySelf: ... @@ -1238,7 +1238,7 @@ class _ArrayOrScalarCommon: def __array_priority__(self) -> float: ... @property def __array_struct__(self): ... - def __setstate__(self, __state): ... + def __setstate__(self, state, /): ... # a `bool_` is returned when `keepdims=True` and `self` is a 0d array @overload @@ -1648,7 +1648,7 @@ _ArrayNumber_co = NDArray[Union[bool_, number[Any]]] _ArrayTD64_co = NDArray[Union[bool_, integer[Any], timedelta64]] class _SupportsItem(Protocol[_T_co]): - def item(self, __args: Any) -> _T_co: ... + def item(self, args: Any, /) -> _T_co: ... class _SupportsReal(Protocol[_T_co]): @property @@ -1687,20 +1687,22 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeType, _DType_co]): order: _OrderKACF = ..., ) -> _ArraySelf: ... @overload - def __array__(self, __dtype: None = ...) -> ndarray[Any, _DType_co]: ... + def __array__(self, dtype: None = ..., /) -> ndarray[Any, _DType_co]: ... @overload - def __array__(self, __dtype: _DType) -> ndarray[Any, _DType]: ... + def __array__(self, dtype: _DType, /) -> ndarray[Any, _DType]: ... def __array_wrap__( self, - __array: ndarray[_ShapeType2, _DType], - __context: None | Tuple[ufunc, Tuple[Any, ...], int] = ..., + array: ndarray[_ShapeType2, _DType], + context: None | Tuple[ufunc, Tuple[Any, ...], int] = ..., + /, ) -> ndarray[_ShapeType2, _DType]: ... def __array_prepare__( self, - __array: ndarray[_ShapeType2, _DType], - __context: None | Tuple[ufunc, Tuple[Any, ...], int] = ..., + array: ndarray[_ShapeType2, _DType], + context: None | Tuple[ufunc, Tuple[Any, ...], int] = ..., + /, ) -> ndarray[_ShapeType2, _DType]: ... @property @@ -1727,16 +1729,17 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeType, _DType_co]): @overload def item( self: ndarray[Any, dtype[_SupportsItem[_T]]], # type: ignore[type-var] - __args: Tuple[SupportsIndex, ...], + args: Tuple[SupportsIndex, ...], + /, ) -> _T: ... @overload - def itemset(self, __value: Any) -> None: ... + def itemset(self, value: Any, /) -> None: ... @overload - def itemset(self, __item: _ShapeLike, __value: Any) -> None: ... + def itemset(self, item: _ShapeLike, value: Any, /) -> None: ... @overload - def resize(self, __new_shape: _ShapeLike, *, refcheck: bool = ...) -> None: ... + def resize(self, new_shape: _ShapeLike, /, *, refcheck: bool = ...) -> None: ... @overload def resize(self, *new_shape: SupportsIndex, refcheck: bool = ...) -> None: ... @@ -1756,7 +1759,7 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeType, _DType_co]): ) -> ndarray[Any, _DType_co]: ... @overload - def transpose(self: _ArraySelf, __axes: _ShapeLike) -> _ArraySelf: ... + def transpose(self: _ArraySelf, axes: _ShapeLike, /) -> _ArraySelf: ... @overload def transpose(self: _ArraySelf, *axes: SupportsIndex) -> _ArraySelf: ... @@ -1895,7 +1898,7 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeType, _DType_co]): @overload def reshape( - self, __shape: _ShapeLike, *, order: _OrderACF = ... + self, shape: _ShapeLike, /, *, order: _OrderACF = ... ) -> ndarray[Any, _DType_co]: ... @overload def reshape( @@ -2901,9 +2904,9 @@ class generic(_ArrayOrScalarCommon): @abstractmethod def __init__(self, *args: Any, **kwargs: Any) -> None: ... @overload - def __array__(self: _ScalarType, __dtype: None = ...) -> ndarray[Any, dtype[_ScalarType]]: ... + def __array__(self: _ScalarType, dtype: None = ..., /) -> ndarray[Any, dtype[_ScalarType]]: ... @overload - def __array__(self, __dtype: _DType) -> ndarray[Any, _DType]: ... + def __array__(self, dtype: _DType, /) -> ndarray[Any, _DType]: ... @property def base(self) -> None: ... @property @@ -2971,8 +2974,7 @@ class generic(_ArrayOrScalarCommon): ) -> Any: ... def item( - self, - __args: Union[L[0], Tuple[()], Tuple[L[0]]] = ..., + self, args: L[0] | Tuple[()] | Tuple[L[0]] = ..., /, ) -> Any: ... @overload @@ -3018,7 +3020,7 @@ class generic(_ArrayOrScalarCommon): @overload def reshape( - self: _ScalarType, __shape: _ShapeLike, *, order: _OrderACF = ... + self: _ScalarType, shape: _ShapeLike, /, *, order: _OrderACF = ... ) -> ndarray[Any, dtype[_ScalarType]]: ... @overload def reshape( @@ -3028,7 +3030,7 @@ class generic(_ArrayOrScalarCommon): def squeeze( self: _ScalarType, axis: Union[L[0], Tuple[()]] = ... ) -> _ScalarType: ... - def transpose(self: _ScalarType, __axes: Tuple[()] = ...) -> _ScalarType: ... + def transpose(self: _ScalarType, axes: Tuple[()] = ..., /) -> _ScalarType: ... # Keep `dtype` at the bottom to avoid name conflicts with `np.dtype` @property def dtype(self: _ScalarType) -> dtype[_ScalarType]: ... @@ -3063,10 +3065,9 @@ class number(generic, Generic[_NBit1]): # type: ignore __ge__: _ComparisonOp[_NumberLike_co, _ArrayLikeNumber_co] class bool_(generic): - def __init__(self, __value: object = ...) -> None: ... + def __init__(self, value: object = ..., /) -> None: ... def item( - self, - __args: Union[L[0], Tuple[()], Tuple[L[0]]] = ..., + self, args: L[0] | Tuple[()] | Tuple[L[0]] = ..., /, ) -> bool: ... def tolist(self) -> bool: ... @property @@ -3112,7 +3113,7 @@ class bool_(generic): bool8 = bool_ class object_(generic): - def __init__(self, __value: object = ...) -> None: ... + def __init__(self, value: object = ..., /) -> None: ... @property def real(self: _ArraySelf) -> _ArraySelf: ... @property @@ -3141,14 +3142,16 @@ class datetime64(generic): @overload def __init__( self, - __value: Union[None, datetime64, _CharLike_co, _DatetimeScalar] = ..., - __format: Union[_CharLike_co, Tuple[_CharLike_co, _IntLike_co]] = ..., + value: None | datetime64 | _CharLike_co | _DatetimeScalar = ..., + format: _CharLike_co | Tuple[_CharLike_co, _IntLike_co] = ..., + /, ) -> None: ... @overload def __init__( self, - __value: int, - __format: Union[_CharLike_co, Tuple[_CharLike_co, _IntLike_co]] + value: int, + format: _CharLike_co | Tuple[_CharLike_co, _IntLike_co], + /, ) -> None: ... def __add__(self, other: _TD64Like_co) -> datetime64: ... def __radd__(self, other: _TD64Like_co) -> datetime64: ... @@ -3186,8 +3189,7 @@ class integer(number[_NBit1]): # type: ignore # NOTE: `__index__` is technically defined in the bottom-most # sub-classes (`int64`, `uint32`, etc) def item( - self, - __args: Union[L[0], Tuple[()], Tuple[L[0]]] = ..., + self, args: L[0] | Tuple[()] | Tuple[L[0]] = ..., /, ) -> int: ... def tolist(self) -> int: ... def __index__(self) -> int: ... @@ -3209,7 +3211,7 @@ class integer(number[_NBit1]): # type: ignore def __rxor__(self, other: _IntLike_co) -> integer: ... class signedinteger(integer[_NBit1]): - def __init__(self, __value: _IntValue = ...) -> None: ... + def __init__(self, value: _IntValue = ..., /) -> None: ... __add__: _SignedIntOp[_NBit1] __radd__: _SignedIntOp[_NBit1] __sub__: _SignedIntOp[_NBit1] @@ -3253,8 +3255,9 @@ longlong = signedinteger[_NBitLongLong] class timedelta64(generic): def __init__( self, - __value: Union[None, int, _CharLike_co, dt.timedelta, timedelta64] = ..., - __format: Union[_CharLike_co, Tuple[_CharLike_co, _IntLike_co]] = ..., + value: None | int | _CharLike_co | dt.timedelta | timedelta64 = ..., + format: _CharLike_co | Tuple[_CharLike_co, _IntLike_co] = ..., + /, ) -> None: ... @property def numerator(self: _ScalarType) -> _ScalarType: ... @@ -3290,7 +3293,7 @@ class timedelta64(generic): class unsignedinteger(integer[_NBit1]): # NOTE: `uint64 + signedinteger -> float64` - def __init__(self, __value: _IntValue = ...) -> None: ... + def __init__(self, value: _IntValue = ..., /) -> None: ... __add__: _UnsignedIntOp[_NBit1] __radd__: _UnsignedIntOp[_NBit1] __sub__: _UnsignedIntOp[_NBit1] @@ -3336,23 +3339,23 @@ _IntType = TypeVar("_IntType", bound=integer) _FloatType = TypeVar('_FloatType', bound=floating) class floating(inexact[_NBit1]): - def __init__(self, __value: _FloatValue = ...) -> None: ... + def __init__(self, value: _FloatValue = ..., /) -> None: ... def item( - self, - __args: Union[L[0], Tuple[()], Tuple[L[0]]] = ..., + self, args: L[0] | Tuple[()] | Tuple[L[0]] = ..., + /, ) -> float: ... def tolist(self) -> float: ... def is_integer(self: float64) -> bool: ... def hex(self: float64) -> str: ... @classmethod - def fromhex(cls: Type[float64], __string: str) -> float64: ... + def fromhex(cls: Type[float64], string: str, /) -> float64: ... def as_integer_ratio(self) -> Tuple[int, int]: ... if sys.version_info >= (3, 9): def __ceil__(self: float64) -> int: ... def __floor__(self: float64) -> int: ... def __trunc__(self: float64) -> int: ... def __getnewargs__(self: float64) -> Tuple[float]: ... - def __getformat__(self: float64, __typestr: L["double", "float"]) -> str: ... + def __getformat__(self: float64, typestr: L["double", "float"], /) -> str: ... @overload def __round__(self, ndigits: None = ...) -> int: ... @overload @@ -3390,10 +3393,9 @@ longfloat = floating[_NBitLongDouble] # describing the two 64 bit floats representing its real and imaginary component class complexfloating(inexact[_NBit1], Generic[_NBit1, _NBit2]): - def __init__(self, __value: _ComplexValue = ...) -> None: ... + def __init__(self, value: _ComplexValue = ..., /) -> None: ... def item( - self, - __args: Union[L[0], Tuple[()], Tuple[L[0]]] = ..., + self, args: L[0] | Tuple[()] | Tuple[L[0]] = ..., /, ) -> complex: ... def tolist(self) -> complex: ... @property @@ -3433,7 +3435,7 @@ class flexible(generic): ... # type: ignore # depending on whether or not it's used as an opaque bytes sequence # or a structure class void(flexible): - def __init__(self, __value: Union[_IntLike_co, bytes]) -> None: ... + def __init__(self, value: _IntLike_co | bytes, /) -> None: ... @property def real(self: _ArraySelf) -> _ArraySelf: ... @property @@ -3455,14 +3457,13 @@ class character(flexible): # type: ignore class bytes_(character, bytes): @overload - def __init__(self, __value: object = ...) -> None: ... + def __init__(self, value: object = ..., /) -> None: ... @overload def __init__( - self, __value: str, encoding: str = ..., errors: str = ... + self, value: str, /, encoding: str = ..., errors: str = ... ) -> None: ... def item( - self, - __args: Union[L[0], Tuple[()], Tuple[L[0]]] = ..., + self, args: L[0] | Tuple[()] | Tuple[L[0]] = ..., /, ) -> bytes: ... def tolist(self) -> bytes: ... @@ -3471,14 +3472,13 @@ bytes0 = bytes_ class str_(character, str): @overload - def __init__(self, __value: object = ...) -> None: ... + def __init__(self, value: object = ..., /) -> None: ... @overload def __init__( - self, __value: bytes, encoding: str = ..., errors: str = ... + self, value: bytes, /, encoding: str = ..., errors: str = ... ) -> None: ... def item( - self, - __args: Union[L[0], Tuple[()], Tuple[L[0]]] = ..., + self, args: L[0] | Tuple[()] | Tuple[L[0]] = ..., /, ) -> str: ... def tolist(self) -> str: ... @@ -3712,9 +3712,10 @@ class errstate(Generic[_CallType], ContextDecorator): def __enter__(self) -> None: ... def __exit__( self, - __exc_type: Optional[Type[BaseException]], - __exc_value: Optional[BaseException], - __traceback: Optional[TracebackType], + exc_type: Optional[Type[BaseException]], + exc_value: Optional[BaseException], + traceback: Optional[TracebackType], + /, ) -> None: ... class ndenumerate(Generic[_ScalarType]): diff --git a/numpy/core/_ufunc_config.pyi b/numpy/core/_ufunc_config.pyi index aa48ddba7..9c8cc8ab6 100644 --- a/numpy/core/_ufunc_config.pyi +++ b/numpy/core/_ufunc_config.pyi @@ -4,7 +4,7 @@ _ErrKind = Literal["ignore", "warn", "raise", "call", "print", "log"] _ErrFunc = Callable[[str, int], Any] class _SupportsWrite(Protocol): - def write(self, __msg: str) -> Any: ... + def write(self, msg: str, /) -> Any: ... class _ErrDict(TypedDict): divide: _ErrKind diff --git a/numpy/core/einsumfunc.pyi b/numpy/core/einsumfunc.pyi index bb02590e6..52025d502 100644 --- a/numpy/core/einsumfunc.pyi +++ b/numpy/core/einsumfunc.pyi @@ -46,7 +46,8 @@ __all__: List[str] # Something like `is_scalar = bool(__subscripts.partition("->")[-1])` @overload def einsum( - __subscripts: str, + subscripts: str, + /, *operands: _ArrayLikeBool_co, out: None = ..., dtype: Optional[_DTypeLikeBool] = ..., @@ -56,7 +57,8 @@ def einsum( ) -> Any: ... @overload def einsum( - __subscripts: str, + subscripts: str, + /, *operands: _ArrayLikeUInt_co, out: None = ..., dtype: Optional[_DTypeLikeUInt] = ..., @@ -66,7 +68,8 @@ def einsum( ) -> Any: ... @overload def einsum( - __subscripts: str, + subscripts: str, + /, *operands: _ArrayLikeInt_co, out: None = ..., dtype: Optional[_DTypeLikeInt] = ..., @@ -76,7 +79,8 @@ def einsum( ) -> Any: ... @overload def einsum( - __subscripts: str, + subscripts: str, + /, *operands: _ArrayLikeFloat_co, out: None = ..., dtype: Optional[_DTypeLikeFloat] = ..., @@ -86,7 +90,8 @@ def einsum( ) -> Any: ... @overload def einsum( - __subscripts: str, + subscripts: str, + /, *operands: _ArrayLikeComplex_co, out: None = ..., dtype: Optional[_DTypeLikeComplex] = ..., @@ -96,7 +101,8 @@ def einsum( ) -> Any: ... @overload def einsum( - __subscripts: str, + subscripts: str, + /, *operands: Any, casting: _CastingUnsafe, dtype: Optional[_DTypeLikeComplex_co] = ..., @@ -106,7 +112,8 @@ def einsum( ) -> Any: ... @overload def einsum( - __subscripts: str, + subscripts: str, + /, *operands: _ArrayLikeComplex_co, out: _ArrayType, dtype: Optional[_DTypeLikeComplex_co] = ..., @@ -116,7 +123,8 @@ def einsum( ) -> _ArrayType: ... @overload def einsum( - __subscripts: str, + subscripts: str, + /, *operands: Any, out: _ArrayType, casting: _CastingUnsafe, @@ -130,7 +138,8 @@ def einsum( # NOTE: In practice the list consists of a `str` (first element) # and a variable number of integer tuples. def einsum_path( - __subscripts: str, + subscripts: str, + /, *operands: _ArrayLikeComplex_co, optimize: _OptimizeKind = ..., ) -> Tuple[List[Any], str]: ... diff --git a/numpy/core/multiarray.pyi b/numpy/core/multiarray.pyi index b807ddff0..3e2873cb3 100644 --- a/numpy/core/multiarray.pyi +++ b/numpy/core/multiarray.pyi @@ -306,7 +306,8 @@ def ravel_multi_index( @overload def concatenate( # type: ignore[misc] - __arrays: _ArrayLike[_SCT], + arrays: _ArrayLike[_SCT], + /, axis: Optional[SupportsIndex] = ..., out: None = ..., *, @@ -315,7 +316,8 @@ def concatenate( # type: ignore[misc] ) -> NDArray[_SCT]: ... @overload def concatenate( # type: ignore[misc] - __arrays: ArrayLike, + arrays: ArrayLike, + /, axis: Optional[SupportsIndex] = ..., out: None = ..., *, @@ -324,7 +326,8 @@ def concatenate( # type: ignore[misc] ) -> NDArray[Any]: ... @overload def concatenate( # type: ignore[misc] - __arrays: ArrayLike, + arrays: ArrayLike, + /, axis: Optional[SupportsIndex] = ..., out: None = ..., *, @@ -333,7 +336,8 @@ def concatenate( # type: ignore[misc] ) -> NDArray[_SCT]: ... @overload def concatenate( # type: ignore[misc] - __arrays: ArrayLike, + arrays: ArrayLike, + /, axis: Optional[SupportsIndex] = ..., out: None = ..., *, @@ -342,7 +346,8 @@ def concatenate( # type: ignore[misc] ) -> NDArray[Any]: ... @overload def concatenate( - __arrays: ArrayLike, + arrays: ArrayLike, + /, axis: Optional[SupportsIndex] = ..., out: _ArrayType = ..., *, @@ -351,19 +356,22 @@ def concatenate( ) -> _ArrayType: ... def inner( - __a: ArrayLike, - __b: ArrayLike, + a: ArrayLike, + b: ArrayLike, + /, ) -> Any: ... @overload def where( - __condition: ArrayLike, + condition: ArrayLike, + /, ) -> Tuple[NDArray[intp], ...]: ... @overload def where( - __condition: ArrayLike, - __x: ArrayLike, - __y: ArrayLike, + condition: ArrayLike, + x: ArrayLike, + y: ArrayLike, + /, ) -> NDArray[Any]: ... def lexsort( @@ -378,7 +386,7 @@ def can_cast( ) -> bool: ... def min_scalar_type( - __a: ArrayLike, + a: ArrayLike, /, ) -> dtype[Any]: ... def result_type( @@ -391,24 +399,25 @@ def dot(a: ArrayLike, b: ArrayLike, out: None = ...) -> Any: ... def dot(a: ArrayLike, b: ArrayLike, out: _ArrayType) -> _ArrayType: ... @overload -def vdot(__a: _ArrayLikeBool_co, __b: _ArrayLikeBool_co) -> bool_: ... # type: ignore[misc] +def vdot(a: _ArrayLikeBool_co, b: _ArrayLikeBool_co, /) -> bool_: ... # type: ignore[misc] @overload -def vdot(__a: _ArrayLikeUInt_co, __b: _ArrayLikeUInt_co) -> unsignedinteger[Any]: ... # type: ignore[misc] +def vdot(a: _ArrayLikeUInt_co, b: _ArrayLikeUInt_co, /) -> unsignedinteger[Any]: ... # type: ignore[misc] @overload -def vdot(__a: _ArrayLikeInt_co, __b: _ArrayLikeInt_co) -> signedinteger[Any]: ... # type: ignore[misc] +def vdot(a: _ArrayLikeInt_co, b: _ArrayLikeInt_co, /) -> signedinteger[Any]: ... # type: ignore[misc] @overload -def vdot(__a: _ArrayLikeFloat_co, __b: _ArrayLikeFloat_co) -> floating[Any]: ... # type: ignore[misc] +def vdot(a: _ArrayLikeFloat_co, b: _ArrayLikeFloat_co, /) -> floating[Any]: ... # type: ignore[misc] @overload -def vdot(__a: _ArrayLikeComplex_co, __b: _ArrayLikeComplex_co) -> complexfloating[Any, Any]: ... # type: ignore[misc] +def vdot(a: _ArrayLikeComplex_co, b: _ArrayLikeComplex_co, /) -> complexfloating[Any, Any]: ... # type: ignore[misc] @overload -def vdot(__a: _ArrayLikeTD64_co, __b: _ArrayLikeTD64_co) -> timedelta64: ... +def vdot(a: _ArrayLikeTD64_co, b: _ArrayLikeTD64_co, /) -> timedelta64: ... @overload -def vdot(__a: _ArrayLikeObject_co, __b: Any) -> Any: ... +def vdot(a: _ArrayLikeObject_co, b: Any, /) -> Any: ... @overload -def vdot(__a: Any, __b: _ArrayLikeObject_co) -> Any: ... +def vdot(a: Any, b: _ArrayLikeObject_co, /) -> Any: ... def bincount( - __x: ArrayLike, + x: ArrayLike, + /, weights: Optional[ArrayLike] = ..., minlength: SupportsIndex = ..., ) -> NDArray[intp]: ... @@ -427,27 +436,31 @@ def putmask( ) -> None: ... def packbits( - __a: _ArrayLikeInt_co, + a: _ArrayLikeInt_co, + /, axis: Optional[SupportsIndex] = ..., bitorder: L["big", "little"] = ..., ) -> NDArray[uint8]: ... def unpackbits( - __a: _ArrayLike[uint8], + a: _ArrayLike[uint8], + /, axis: Optional[SupportsIndex] = ..., count: Optional[SupportsIndex] = ..., bitorder: L["big", "little"] = ..., ) -> NDArray[uint8]: ... def shares_memory( - __a: object, - __b: object, + a: object, + b: object, + /, max_work: Optional[int] = ..., ) -> bool: ... def may_share_memory( - __a: object, - __b: object, + a: object, + b: object, + /, max_work: Optional[int] = ..., ) -> bool: ... @@ -586,7 +599,7 @@ def asfortranarray( # In practice `List[Any]` is list with an int, int and a valid # `np.seterrcall()` object def geterrobj() -> List[Any]: ... -def seterrobj(__errobj: List[Any]) -> None: ... +def seterrobj(errobj: List[Any], /) -> None: ... def promote_types(__type1: DTypeLike, __type2: DTypeLike) -> dtype[Any]: ... @@ -620,7 +633,7 @@ def fromstring( ) -> NDArray[Any]: ... def frompyfunc( - __func: Callable[..., Any], + func: Callable[..., Any], /, nin: SupportsIndex, nout: SupportsIndex, *, @@ -705,8 +718,8 @@ def frombuffer( @overload def arange( # type: ignore[misc] - __stop: _IntLike_co, - *, + stop: _IntLike_co, + /, *, dtype: None = ..., like: ArrayLike = ..., ) -> NDArray[signedinteger[Any]]: ... @@ -721,8 +734,8 @@ def arange( # type: ignore[misc] ) -> NDArray[signedinteger[Any]]: ... @overload def arange( # type: ignore[misc] - __stop: _FloatLike_co, - *, + stop: _FloatLike_co, + /, *, dtype: None = ..., like: ArrayLike = ..., ) -> NDArray[floating[Any]]: ... @@ -737,8 +750,8 @@ def arange( # type: ignore[misc] ) -> NDArray[floating[Any]]: ... @overload def arange( - __stop: _TD64Like_co, - *, + stop: _TD64Like_co, + /, *, dtype: None = ..., like: ArrayLike = ..., ) -> NDArray[timedelta64]: ... @@ -762,8 +775,8 @@ def arange( # both start and stop must always be specified for datetime64 ) -> NDArray[datetime64]: ... @overload def arange( - __stop: Any, - *, + stop: Any, + /, *, dtype: _DTypeLike[_SCT], like: ArrayLike = ..., ) -> NDArray[_SCT]: ... @@ -778,7 +791,7 @@ def arange( ) -> NDArray[_SCT]: ... @overload def arange( - __stop: Any, + stop: Any, /, *, dtype: DTypeLike, like: ArrayLike = ..., @@ -794,7 +807,7 @@ def arange( ) -> NDArray[Any]: ... def datetime_data( - __dtype: str | _DTypeLike[datetime64] | _DTypeLike[timedelta64], + dtype: str | _DTypeLike[datetime64] | _DTypeLike[timedelta64], /, ) -> Tuple[str, int]: ... # The datetime functions perform unsafe casts to `datetime64[D]`, @@ -945,7 +958,7 @@ def compare_chararrays( rstrip: bool, ) -> NDArray[bool_]: ... -def add_docstring(__obj: Callable[..., Any], __docstring: str) -> None: ... +def add_docstring(obj: Callable[..., Any], docstring: str, /) -> None: ... _GetItemKeys = L[ "C", "CONTIGUOUS", "C_CONTIGUOUS", diff --git a/numpy/core/shape_base.pyi b/numpy/core/shape_base.pyi index a640991d3..d7914697d 100644 --- a/numpy/core/shape_base.pyi +++ b/numpy/core/shape_base.pyi @@ -11,23 +11,23 @@ _ArrayLike = _NestedSequence[_SupportsArray[dtype[_SCT]]] __all__: List[str] @overload -def atleast_1d(__arys: _ArrayLike[_SCT]) -> NDArray[_SCT]: ... +def atleast_1d(arys: _ArrayLike[_SCT], /) -> NDArray[_SCT]: ... @overload -def atleast_1d(__arys: ArrayLike) -> NDArray[Any]: ... +def atleast_1d(arys: ArrayLike, /) -> NDArray[Any]: ... @overload def atleast_1d(*arys: ArrayLike) -> List[NDArray[Any]]: ... @overload -def atleast_2d(__arys: _ArrayLike[_SCT]) -> NDArray[_SCT]: ... +def atleast_2d(arys: _ArrayLike[_SCT], /) -> NDArray[_SCT]: ... @overload -def atleast_2d(__arys: ArrayLike) -> NDArray[Any]: ... +def atleast_2d(arys: ArrayLike, /) -> NDArray[Any]: ... @overload def atleast_2d(*arys: ArrayLike) -> List[NDArray[Any]]: ... @overload -def atleast_3d(__arys: _ArrayLike[_SCT]) -> NDArray[_SCT]: ... +def atleast_3d(arys: _ArrayLike[_SCT], /) -> NDArray[_SCT]: ... @overload -def atleast_3d(__arys: ArrayLike) -> NDArray[Any]: ... +def atleast_3d(arys: ArrayLike, /) -> NDArray[Any]: ... @overload def atleast_3d(*arys: ArrayLike) -> List[NDArray[Any]]: ... diff --git a/numpy/lib/arraypad.pyi b/numpy/lib/arraypad.pyi index d6e07a6bd..49ce8e683 100644 --- a/numpy/lib/arraypad.pyi +++ b/numpy/lib/arraypad.pyi @@ -24,10 +24,11 @@ _SCT = TypeVar("_SCT", bound=generic) class _ModeFunc(Protocol): def __call__( self, - __vector: NDArray[Any], - __iaxis_pad_width: Tuple[int, int], - __iaxis: int, - __kwargs: Dict[str, Any], + vector: NDArray[Any], + iaxis_pad_width: Tuple[int, int], + iaxis: int, + kwargs: Dict[str, Any], + /, ) -> None: ... _ModeKind = L[ diff --git a/numpy/lib/npyio.pyi b/numpy/lib/npyio.pyi index 1321afb55..1fa689bbe 100644 --- a/numpy/lib/npyio.pyi +++ b/numpy/lib/npyio.pyi @@ -80,9 +80,10 @@ class NpzFile(Mapping[str, NDArray[Any]]): def __enter__(self: _T) -> _T: ... def __exit__( self, - __exc_type: None | Type[BaseException], - __exc_value: None | BaseException, - __traceback: None | types.TracebackType, + exc_type: None | Type[BaseException], + exc_value: None | BaseException, + traceback: None | types.TracebackType, + /, ) -> None: ... def close(self) -> None: ... def __del__(self) -> None: ... diff --git a/numpy/lib/shape_base.pyi b/numpy/lib/shape_base.pyi index 4c275cc8c..1598dc36c 100644 --- a/numpy/lib/shape_base.pyi +++ b/numpy/lib/shape_base.pyi @@ -38,15 +38,17 @@ _ArrayLike = _NestedSequence[_SupportsDType[dtype[_SCT]]] class _ArrayWrap(Protocol): def __call__( self, - __array: NDArray[Any], - __context: None | Tuple[ufunc, Tuple[Any, ...], int] = ..., + array: NDArray[Any], + context: None | Tuple[ufunc, Tuple[Any, ...], int] = ..., + /, ) -> Any: ... class _ArrayPrepare(Protocol): def __call__( self, - __array: NDArray[Any], - __context: None | Tuple[ufunc, Tuple[Any, ...], int] = ..., + array: NDArray[Any], + context: None | Tuple[ufunc, Tuple[Any, ...], int] = ..., + /, ) -> Any: ... class _SupportsArrayWrap(Protocol): diff --git a/numpy/lib/utils.pyi b/numpy/lib/utils.pyi index c13a219b5..f0a8797ad 100644 --- a/numpy/lib/utils.pyi +++ b/numpy/lib/utils.pyi @@ -26,7 +26,7 @@ _FuncType = TypeVar("_FuncType", bound=Callable[..., Any]) # A file-like object opened in `w` mode class _SupportsWrite(Protocol[_T_contra]): - def write(self, __s: _T_contra) -> Any: ... + def write(self, s: _T_contra, /) -> Any: ... __all__: List[str] @@ -55,7 +55,8 @@ def deprecate( ) -> _Deprecate: ... @overload def deprecate( - __func: _FuncType, + func: _FuncType, + /, old_name: Optional[str] = ..., new_name: Optional[str] = ..., message: Optional[str] = ..., diff --git a/numpy/testing/_private/utils.pyi b/numpy/testing/_private/utils.pyi index ab5ca8784..26ce52e40 100644 --- a/numpy/testing/_private/utils.pyi +++ b/numpy/testing/_private/utils.pyi @@ -259,8 +259,9 @@ def raises(*args: Type[BaseException]) -> Callable[[_FT], _FT]: ... @overload def assert_raises( # type: ignore - __expected_exception: Type[BaseException] | Tuple[Type[BaseException], ...], - __callable: Callable[..., Any], + expected_exception: Type[BaseException] | Tuple[Type[BaseException], ...], + callable: Callable[..., Any], + /, *args: Any, **kwargs: Any, ) -> None: ... @@ -273,9 +274,10 @@ def assert_raises( @overload def assert_raises_regex( - __expected_exception: Type[BaseException] | Tuple[Type[BaseException], ...], - __expected_regex: str | bytes | Pattern[Any], - __callable: Callable[..., Any], + expected_exception: Type[BaseException] | Tuple[Type[BaseException], ...], + expected_regex: str | bytes | Pattern[Any], + callable: Callable[..., Any], + /, *args: Any, **kwargs: Any, ) -> None: ... @@ -339,8 +341,9 @@ def assert_warns( ) -> contextlib._GeneratorContextManager[None]: ... @overload def assert_warns( - __warning_class: Type[Warning], - __func: Callable[..., _T], + warning_class: Type[Warning], + func: Callable[..., _T], + /, *args: Any, **kwargs: Any, ) -> _T: ... @@ -349,7 +352,8 @@ def assert_warns( def assert_no_warnings() -> contextlib._GeneratorContextManager[None]: ... @overload def assert_no_warnings( - __func: Callable[..., _T], + func: Callable[..., _T], + /, *args: Any, **kwargs: Any, ) -> _T: ... @@ -386,7 +390,8 @@ def temppath( def assert_no_gc_cycles() -> contextlib._GeneratorContextManager[None]: ... @overload def assert_no_gc_cycles( - __func: Callable[..., Any], + func: Callable[..., Any], + /, *args: Any, **kwargs: Any, ) -> None: ... -- cgit v1.2.1 From 82396851773c37220b0ac543c51b7a896ea75d96 Mon Sep 17 00:00:00 2001 From: Bas van Beek Date: Tue, 31 Aug 2021 12:05:51 +0200 Subject: STY: Use the PEP 457 positional-only syntax in `numpy.typing` --- numpy/typing/_callable.py | 178 +++++++++++++++++++++++----------------------- numpy/typing/_ufunc.pyi | 24 ++++--- 2 files changed, 102 insertions(+), 100 deletions(-) (limited to 'numpy') diff --git a/numpy/typing/_callable.py b/numpy/typing/_callable.py index 63a8153af..44ad5c291 100644 --- a/numpy/typing/_callable.py +++ b/numpy/typing/_callable.py @@ -62,264 +62,264 @@ _GenericType_co = TypeVar("_GenericType_co", covariant=True, bound=generic) class _BoolOp(Protocol[_GenericType_co]): @overload - def __call__(self, __other: _BoolLike_co) -> _GenericType_co: ... + def __call__(self, other: _BoolLike_co, /) -> _GenericType_co: ... @overload # platform dependent - def __call__(self, __other: int) -> int_: ... + def __call__(self, other: int, /) -> int_: ... @overload - def __call__(self, __other: float) -> float64: ... + def __call__(self, other: float, /) -> float64: ... @overload - def __call__(self, __other: complex) -> complex128: ... + def __call__(self, other: complex, /) -> complex128: ... @overload - def __call__(self, __other: _NumberType) -> _NumberType: ... + def __call__(self, other: _NumberType, /) -> _NumberType: ... class _BoolBitOp(Protocol[_GenericType_co]): @overload - def __call__(self, __other: _BoolLike_co) -> _GenericType_co: ... + def __call__(self, other: _BoolLike_co, /) -> _GenericType_co: ... @overload # platform dependent - def __call__(self, __other: int) -> int_: ... + def __call__(self, other: int, /) -> int_: ... @overload - def __call__(self, __other: _IntType) -> _IntType: ... + def __call__(self, other: _IntType, /) -> _IntType: ... class _BoolSub(Protocol): - # Note that `__other: bool_` is absent here + # Note that `other: bool_` is absent here @overload - def __call__(self, __other: bool) -> NoReturn: ... + def __call__(self, other: bool, /) -> NoReturn: ... @overload # platform dependent - def __call__(self, __other: int) -> int_: ... + def __call__(self, other: int, /) -> int_: ... @overload - def __call__(self, __other: float) -> float64: ... + def __call__(self, other: float, /) -> float64: ... @overload - def __call__(self, __other: complex) -> complex128: ... + def __call__(self, other: complex, /) -> complex128: ... @overload - def __call__(self, __other: _NumberType) -> _NumberType: ... + def __call__(self, other: _NumberType, /) -> _NumberType: ... class _BoolTrueDiv(Protocol): @overload - def __call__(self, __other: float | _IntLike_co) -> float64: ... + def __call__(self, other: float | _IntLike_co, /) -> float64: ... @overload - def __call__(self, __other: complex) -> complex128: ... + def __call__(self, other: complex, /) -> complex128: ... @overload - def __call__(self, __other: _NumberType) -> _NumberType: ... + def __call__(self, other: _NumberType, /) -> _NumberType: ... class _BoolMod(Protocol): @overload - def __call__(self, __other: _BoolLike_co) -> int8: ... + def __call__(self, other: _BoolLike_co, /) -> int8: ... @overload # platform dependent - def __call__(self, __other: int) -> int_: ... + def __call__(self, other: int, /) -> int_: ... @overload - def __call__(self, __other: float) -> float64: ... + def __call__(self, other: float, /) -> float64: ... @overload - def __call__(self, __other: _IntType) -> _IntType: ... + def __call__(self, other: _IntType, /) -> _IntType: ... @overload - def __call__(self, __other: _FloatType) -> _FloatType: ... + def __call__(self, other: _FloatType, /) -> _FloatType: ... class _BoolDivMod(Protocol): @overload - def __call__(self, __other: _BoolLike_co) -> _2Tuple[int8]: ... + def __call__(self, other: _BoolLike_co, /) -> _2Tuple[int8]: ... @overload # platform dependent - def __call__(self, __other: int) -> _2Tuple[int_]: ... + def __call__(self, other: int, /) -> _2Tuple[int_]: ... @overload - def __call__(self, __other: float) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... + def __call__(self, other: float, /) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... @overload - def __call__(self, __other: _IntType) -> _2Tuple[_IntType]: ... + def __call__(self, other: _IntType, /) -> _2Tuple[_IntType]: ... @overload - def __call__(self, __other: _FloatType) -> _2Tuple[_FloatType]: ... + def __call__(self, other: _FloatType, /) -> _2Tuple[_FloatType]: ... class _TD64Div(Protocol[_NumberType_co]): @overload - def __call__(self, __other: timedelta64) -> _NumberType_co: ... + def __call__(self, other: timedelta64, /) -> _NumberType_co: ... @overload - def __call__(self, __other: _BoolLike_co) -> NoReturn: ... + def __call__(self, other: _BoolLike_co, /) -> NoReturn: ... @overload - def __call__(self, __other: _FloatLike_co) -> timedelta64: ... + def __call__(self, other: _FloatLike_co, /) -> timedelta64: ... class _IntTrueDiv(Protocol[_NBit1]): @overload - def __call__(self, __other: bool) -> floating[_NBit1]: ... + def __call__(self, other: bool, /) -> floating[_NBit1]: ... @overload - def __call__(self, __other: int) -> floating[_NBit1 | _NBitInt]: ... + def __call__(self, other: int, /) -> floating[_NBit1 | _NBitInt]: ... @overload - def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... + def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ... @overload def __call__( - self, __other: complex + self, other: complex, /, ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... @overload - def __call__(self, __other: integer[_NBit2]) -> floating[_NBit1 | _NBit2]: ... + def __call__(self, other: integer[_NBit2], /) -> floating[_NBit1 | _NBit2]: ... class _UnsignedIntOp(Protocol[_NBit1]): # NOTE: `uint64 + signedinteger -> float64` @overload - def __call__(self, __other: bool) -> unsignedinteger[_NBit1]: ... + def __call__(self, other: bool, /) -> unsignedinteger[_NBit1]: ... @overload def __call__( - self, __other: int | signedinteger[Any] + self, other: int | signedinteger[Any], / ) -> Any: ... @overload - def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... + def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ... @overload def __call__( - self, __other: complex + self, other: complex, /, ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... @overload def __call__( - self, __other: unsignedinteger[_NBit2] + self, other: unsignedinteger[_NBit2], / ) -> unsignedinteger[_NBit1 | _NBit2]: ... class _UnsignedIntBitOp(Protocol[_NBit1]): @overload - def __call__(self, __other: bool) -> unsignedinteger[_NBit1]: ... + def __call__(self, other: bool, /) -> unsignedinteger[_NBit1]: ... @overload - def __call__(self, __other: int) -> signedinteger[Any]: ... + def __call__(self, other: int, /) -> signedinteger[Any]: ... @overload - def __call__(self, __other: signedinteger[Any]) -> signedinteger[Any]: ... + def __call__(self, other: signedinteger[Any], /) -> signedinteger[Any]: ... @overload def __call__( - self, __other: unsignedinteger[_NBit2] + self, other: unsignedinteger[_NBit2], / ) -> unsignedinteger[_NBit1 | _NBit2]: ... class _UnsignedIntMod(Protocol[_NBit1]): @overload - def __call__(self, __other: bool) -> unsignedinteger[_NBit1]: ... + def __call__(self, other: bool, /) -> unsignedinteger[_NBit1]: ... @overload def __call__( - self, __other: int | signedinteger[Any] + self, other: int | signedinteger[Any], / ) -> Any: ... @overload - def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... + def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ... @overload def __call__( - self, __other: unsignedinteger[_NBit2] + self, other: unsignedinteger[_NBit2], / ) -> unsignedinteger[_NBit1 | _NBit2]: ... class _UnsignedIntDivMod(Protocol[_NBit1]): @overload - def __call__(self, __other: bool) -> _2Tuple[signedinteger[_NBit1]]: ... + def __call__(self, other: bool, /) -> _2Tuple[signedinteger[_NBit1]]: ... @overload def __call__( - self, __other: int | signedinteger[Any] + self, other: int | signedinteger[Any], / ) -> _2Tuple[Any]: ... @overload - def __call__(self, __other: float) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... + def __call__(self, other: float, /) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... @overload def __call__( - self, __other: unsignedinteger[_NBit2] + self, other: unsignedinteger[_NBit2], / ) -> _2Tuple[unsignedinteger[_NBit1 | _NBit2]]: ... class _SignedIntOp(Protocol[_NBit1]): @overload - def __call__(self, __other: bool) -> signedinteger[_NBit1]: ... + def __call__(self, other: bool, /) -> signedinteger[_NBit1]: ... @overload - def __call__(self, __other: int) -> signedinteger[_NBit1 | _NBitInt]: ... + def __call__(self, other: int, /) -> signedinteger[_NBit1 | _NBitInt]: ... @overload - def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... + def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ... @overload def __call__( - self, __other: complex + self, other: complex, /, ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... @overload def __call__( - self, __other: signedinteger[_NBit2] + self, other: signedinteger[_NBit2], /, ) -> signedinteger[_NBit1 | _NBit2]: ... class _SignedIntBitOp(Protocol[_NBit1]): @overload - def __call__(self, __other: bool) -> signedinteger[_NBit1]: ... + def __call__(self, other: bool, /) -> signedinteger[_NBit1]: ... @overload - def __call__(self, __other: int) -> signedinteger[_NBit1 | _NBitInt]: ... + def __call__(self, other: int, /) -> signedinteger[_NBit1 | _NBitInt]: ... @overload def __call__( - self, __other: signedinteger[_NBit2] + self, other: signedinteger[_NBit2], /, ) -> signedinteger[_NBit1 | _NBit2]: ... class _SignedIntMod(Protocol[_NBit1]): @overload - def __call__(self, __other: bool) -> signedinteger[_NBit1]: ... + def __call__(self, other: bool, /) -> signedinteger[_NBit1]: ... @overload - def __call__(self, __other: int) -> signedinteger[_NBit1 | _NBitInt]: ... + def __call__(self, other: int, /) -> signedinteger[_NBit1 | _NBitInt]: ... @overload - def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... + def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ... @overload def __call__( - self, __other: signedinteger[_NBit2] + self, other: signedinteger[_NBit2], /, ) -> signedinteger[_NBit1 | _NBit2]: ... class _SignedIntDivMod(Protocol[_NBit1]): @overload - def __call__(self, __other: bool) -> _2Tuple[signedinteger[_NBit1]]: ... + def __call__(self, other: bool, /) -> _2Tuple[signedinteger[_NBit1]]: ... @overload - def __call__(self, __other: int) -> _2Tuple[signedinteger[_NBit1 | _NBitInt]]: ... + def __call__(self, other: int, /) -> _2Tuple[signedinteger[_NBit1 | _NBitInt]]: ... @overload - def __call__(self, __other: float) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... + def __call__(self, other: float, /) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... @overload def __call__( - self, __other: signedinteger[_NBit2] + self, other: signedinteger[_NBit2], /, ) -> _2Tuple[signedinteger[_NBit1 | _NBit2]]: ... class _FloatOp(Protocol[_NBit1]): @overload - def __call__(self, __other: bool) -> floating[_NBit1]: ... + def __call__(self, other: bool, /) -> floating[_NBit1]: ... @overload - def __call__(self, __other: int) -> floating[_NBit1 | _NBitInt]: ... + def __call__(self, other: int, /) -> floating[_NBit1 | _NBitInt]: ... @overload - def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... + def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ... @overload def __call__( - self, __other: complex + self, other: complex, /, ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... @overload def __call__( - self, __other: integer[_NBit2] | floating[_NBit2] + self, other: integer[_NBit2] | floating[_NBit2], / ) -> floating[_NBit1 | _NBit2]: ... class _FloatMod(Protocol[_NBit1]): @overload - def __call__(self, __other: bool) -> floating[_NBit1]: ... + def __call__(self, other: bool, /) -> floating[_NBit1]: ... @overload - def __call__(self, __other: int) -> floating[_NBit1 | _NBitInt]: ... + def __call__(self, other: int, /) -> floating[_NBit1 | _NBitInt]: ... @overload - def __call__(self, __other: float) -> floating[_NBit1 | _NBitDouble]: ... + def __call__(self, other: float, /) -> floating[_NBit1 | _NBitDouble]: ... @overload def __call__( - self, __other: integer[_NBit2] | floating[_NBit2] + self, other: integer[_NBit2] | floating[_NBit2], / ) -> floating[_NBit1 | _NBit2]: ... class _FloatDivMod(Protocol[_NBit1]): @overload - def __call__(self, __other: bool) -> _2Tuple[floating[_NBit1]]: ... + def __call__(self, other: bool, /) -> _2Tuple[floating[_NBit1]]: ... @overload - def __call__(self, __other: int) -> _2Tuple[floating[_NBit1 | _NBitInt]]: ... + def __call__(self, other: int, /) -> _2Tuple[floating[_NBit1 | _NBitInt]]: ... @overload - def __call__(self, __other: float) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... + def __call__(self, other: float, /) -> _2Tuple[floating[_NBit1 | _NBitDouble]]: ... @overload def __call__( - self, __other: integer[_NBit2] | floating[_NBit2] + self, other: integer[_NBit2] | floating[_NBit2], / ) -> _2Tuple[floating[_NBit1 | _NBit2]]: ... class _ComplexOp(Protocol[_NBit1]): @overload - def __call__(self, __other: bool) -> complexfloating[_NBit1, _NBit1]: ... + def __call__(self, other: bool, /) -> complexfloating[_NBit1, _NBit1]: ... @overload - def __call__(self, __other: int) -> complexfloating[_NBit1 | _NBitInt, _NBit1 | _NBitInt]: ... + def __call__(self, other: int, /) -> complexfloating[_NBit1 | _NBitInt, _NBit1 | _NBitInt]: ... @overload def __call__( - self, __other: complex + self, other: complex, /, ) -> complexfloating[_NBit1 | _NBitDouble, _NBit1 | _NBitDouble]: ... @overload def __call__( self, - __other: Union[ + other: Union[ integer[_NBit2], floating[_NBit2], complexfloating[_NBit2, _NBit2], - ] + ], /, ) -> complexfloating[_NBit1 | _NBit2, _NBit1 | _NBit2]: ... class _NumberOp(Protocol): - def __call__(self, __other: _NumberLike_co) -> Any: ... + def __call__(self, other: _NumberLike_co, /) -> Any: ... class _ComparisonOp(Protocol[_T1, _T2]): @overload - def __call__(self, __other: _T1) -> bool_: ... + def __call__(self, other: _T1, /) -> bool_: ... @overload - def __call__(self, __other: _T2) -> NDArray[bool_]: ... + def __call__(self, other: _T2, /) -> NDArray[bool_]: ... diff --git a/numpy/typing/_ufunc.pyi b/numpy/typing/_ufunc.pyi index 37e6c008f..1be3500c1 100644 --- a/numpy/typing/_ufunc.pyi +++ b/numpy/typing/_ufunc.pyi @@ -105,8 +105,9 @@ class _UFunc_Nin1_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]): def at( self, - __a: NDArray[Any], - __indices: _ArrayLikeInt_co, + a: NDArray[Any], + indices: _ArrayLikeInt_co, + /, ) -> None: ... class _UFunc_Nin2_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]): @@ -158,9 +159,10 @@ class _UFunc_Nin2_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]): def at( self, - __a: NDArray[Any], - __indices: _ArrayLikeInt_co, - __b: ArrayLike, + a: NDArray[Any], + indices: _ArrayLikeInt_co, + b: ArrayLike, + /, ) -> None: ... def reduce( @@ -195,9 +197,9 @@ class _UFunc_Nin2_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]): @overload def outer( self, - __A: _ScalarLike_co, - __B: _ScalarLike_co, - *, + A: _ScalarLike_co, + B: _ScalarLike_co, + /, *, out: None = ..., where: None | _ArrayLikeBool_co = ..., casting: _CastingKind = ..., @@ -210,9 +212,9 @@ class _UFunc_Nin2_Nout1(ufunc, Generic[_NameType, _NTypes, _IDType]): @overload def outer( # type: ignore[misc] self, - __A: ArrayLike, - __B: ArrayLike, - *, + A: ArrayLike, + B: ArrayLike, + /, *, out: None | NDArray[Any] | Tuple[NDArray[Any]] = ..., where: None | _ArrayLikeBool_co = ..., casting: _CastingKind = ..., -- cgit v1.2.1 From 42483387d0bdfce0b48db94400054ad16bfd9ef1 Mon Sep 17 00:00:00 2001 From: Bas van Beek Date: Tue, 31 Aug 2021 16:36:58 +0200 Subject: ENH: Add `is_integer` to the `np.floating` subclasses --- numpy/__init__.pyi | 2 +- numpy/core/_add_newdocs_scalars.py | 29 +++++++++++++++++++++------ numpy/core/src/multiarray/scalartypes.c.src | 31 +++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 7 deletions(-) (limited to 'numpy') diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index f398f67b7..b2c64d17c 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -3342,7 +3342,7 @@ class floating(inexact[_NBit1]): __args: Union[L[0], Tuple[()], Tuple[L[0]]] = ..., ) -> float: ... def tolist(self) -> float: ... - def is_integer(self: float64) -> bool: ... + def is_integer(self) -> bool: ... def hex(self: float64) -> str: ... @classmethod def fromhex(cls: Type[float64], __string: str) -> float64: ... diff --git a/numpy/core/_add_newdocs_scalars.py b/numpy/core/_add_newdocs_scalars.py index 602b1db6e..709a71889 100644 --- a/numpy/core/_add_newdocs_scalars.py +++ b/numpy/core/_add_newdocs_scalars.py @@ -205,12 +205,12 @@ add_newdoc_for_scalar_type('bytes_', ['string_'], add_newdoc_for_scalar_type('void', [], r""" Either an opaque sequence of bytes, or a structure. - + >>> np.void(b'abcd') void(b'\x61\x62\x63\x64') - + Structured `void` scalars can only be constructed via extraction from :ref:`structured_arrays`: - + >>> arr = np.array((1, 2), dtype=[('x', np.int8), ('y', np.int8)]) >>> arr[()] (1, 2) # looks like a tuple, but is `np.void` @@ -226,17 +226,17 @@ add_newdoc_for_scalar_type('datetime64', [], >>> np.datetime64(10, 'Y') numpy.datetime64('1980') >>> np.datetime64('1980', 'Y') - numpy.datetime64('1980') + numpy.datetime64('1980') >>> np.datetime64(10, 'D') numpy.datetime64('1970-01-11') - + See :ref:`arrays.datetime` for more information. """) add_newdoc_for_scalar_type('timedelta64', [], """ A timedelta stored as a 64-bit integer. - + See :ref:`arrays.datetime` for more information. """) @@ -257,3 +257,20 @@ for float_name in ('half', 'single', 'double', 'longdouble'): >>> np.{ftype}(-.25).as_integer_ratio() (-1, 4) """.format(ftype=float_name))) + + add_newdoc('numpy.core.numerictypes', float_name, ('is_integer', + f""" + {float_name}.is_integer() -> bool + + Return ``True`` if the floating point number is finite with integral + value, and ``False`` otherwise. + + .. versionadded:: 1.22 + + Examples + -------- + >>> np.{float_name}(-2.0).is_integer() + True + >>> np.{float_name}(3.2).is_integer() + False + """)) diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src index 40f736125..bf22acfec 100644 --- a/numpy/core/src/multiarray/scalartypes.c.src +++ b/numpy/core/src/multiarray/scalartypes.c.src @@ -1908,6 +1908,34 @@ error: } /**end repeat**/ +/**begin repeat + * #name = half, float, double, longdouble# + * #Name = Half, Float, Double, LongDouble# + * #is_half = 1,0,0,0# + * #c = f, f, , l# + */ +static PyObject * +@name@_is_integer(PyObject *self) +{ +#if @is_half@ + npy_double val = npy_half_to_double(PyArrayScalar_VAL(self, @Name@)); +#else + npy_@name@ val = PyArrayScalar_VAL(self, @Name@); +#endif + PyObject *o; + + if (npy_isnan(val)) { + Py_RETURN_FALSE; + } + if (!npy_isfinite(val)) { + Py_RETURN_FALSE; + } + + o = (npy_floor@c@(val) == val) ? Py_True : Py_False; + Py_INCREF(o); + return o; +} +/**end repeat**/ /* * need to fill in doc-strings for these methods on import -- copy from @@ -2185,6 +2213,9 @@ static PyMethodDef @name@type_methods[] = { {"as_integer_ratio", (PyCFunction)@name@_as_integer_ratio, METH_NOARGS, NULL}, + {"is_integer", + (PyCFunction)@name@_is_integer, + METH_NOARGS, NULL}, {NULL, NULL, 0, NULL} }; /**end repeat**/ -- cgit v1.2.1 From 5d86d8c9cc70f87b4aab10682d101acd8c4fb781 Mon Sep 17 00:00:00 2001 From: Bas van Beek Date: Tue, 31 Aug 2021 16:38:14 +0200 Subject: TST: Add tests for `np.floating.is_integer` --- numpy/core/tests/test_scalar_methods.py | 22 ++++++++++++++++++++++ numpy/typing/tests/data/fail/scalars.py | 1 - 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/core/tests/test_scalar_methods.py b/numpy/core/tests/test_scalar_methods.py index 3693bba59..7bad6bebf 100644 --- a/numpy/core/tests/test_scalar_methods.py +++ b/numpy/core/tests/test_scalar_methods.py @@ -102,3 +102,25 @@ class TestAsIntegerRatio: pytest.skip("longdouble too small on this platform") assert_equal(nf / df, f, "{}/{}".format(n, d)) + + +@pytest.mark.parametrize("code", np.typecodes["Float"]) +class TestIsInteger: + @pytest.mark.parametrize("str_value", ["inf", "nan"]) + def test_special(self, code: str, str_value: str) -> None: + cls = np.dtype(code).type + value = cls(str_value) + assert not value.is_integer() + + def test_true(self, code: str) -> None: + float_array = np.arange(-5, 5).astype(code) + for value in float_array: + assert value.is_integer() + + def test_false(self, code: str) -> None: + float_array = np.arange(-5, 5).astype(code) + float_array *= 1.1 + for value in float_array: + if value == 0: + continue + assert not value.is_integer() diff --git a/numpy/typing/tests/data/fail/scalars.py b/numpy/typing/tests/data/fail/scalars.py index 099418e67..94fe3f71e 100644 --- a/numpy/typing/tests/data/fail/scalars.py +++ b/numpy/typing/tests/data/fail/scalars.py @@ -87,7 +87,6 @@ round(c8) # E: No overload variant c8.__getnewargs__() # E: Invalid self argument f2.__getnewargs__() # E: Invalid self argument -f2.is_integer() # E: Invalid self argument f2.hex() # E: Invalid self argument np.float16.fromhex("0x0.0p+0") # E: Invalid self argument f2.__trunc__() # E: Invalid self argument -- cgit v1.2.1 From 9f11564c455f00fe5faa0a92aa02f1f3f59fc901 Mon Sep 17 00:00:00 2001 From: Bas van Beek Date: Tue, 31 Aug 2021 17:41:36 +0200 Subject: ENH: Add `integer.is_integer` Match `int.is_integer`, which was added in python/cpython#6121 --- numpy/__init__.pyi | 1 + numpy/core/_add_newdocs_scalars.py | 12 ++++++++++++ numpy/core/src/multiarray/scalartypes.c.src | 18 +++++++++++++++++- numpy/core/tests/test_scalar_methods.py | 6 +++++- numpy/typing/tests/data/reveal/scalars.py | 2 ++ 5 files changed, 37 insertions(+), 2 deletions(-) (limited to 'numpy') diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index b2c64d17c..f19655616 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -3190,6 +3190,7 @@ class integer(number[_NBit1]): # type: ignore __args: Union[L[0], Tuple[()], Tuple[L[0]]] = ..., ) -> int: ... def tolist(self) -> int: ... + def is_integer(self) -> L[True]: ... def __index__(self) -> int: ... __truediv__: _IntTrueDiv[_NBit1] __rtruediv__: _IntTrueDiv[_NBit1] diff --git a/numpy/core/_add_newdocs_scalars.py b/numpy/core/_add_newdocs_scalars.py index 709a71889..306adbc55 100644 --- a/numpy/core/_add_newdocs_scalars.py +++ b/numpy/core/_add_newdocs_scalars.py @@ -240,6 +240,18 @@ add_newdoc_for_scalar_type('timedelta64', [], See :ref:`arrays.datetime` for more information. """) +add_newdoc('numpy.core.numerictypes', "integer", ('is_integer', + f""" + integer.is_integer() -> bool + + Return ``True`` if the number is finite with integral value. + + >>> np.int64(-2).is_integer() + True + >>> np.uint32(5).is_integer() + True + """)) + # TODO: work out how to put this on the base class, np.floating for float_name in ('half', 'single', 'double', 'longdouble'): add_newdoc('numpy.core.numerictypes', float_name, ('as_integer_ratio', diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src index bf22acfec..c9f3341fe 100644 --- a/numpy/core/src/multiarray/scalartypes.c.src +++ b/numpy/core/src/multiarray/scalartypes.c.src @@ -1937,6 +1937,11 @@ static PyObject * } /**end repeat**/ +static PyObject * +integer_is_integer(PyObject *self) { + Py_RETURN_TRUE; +} + /* * need to fill in doc-strings for these methods on import -- copy from * array docstrings @@ -2195,7 +2200,7 @@ static PyMethodDef @name@type_methods[] = { /**end repeat**/ /**begin repeat - * #name = integer,floating, complexfloating# + * #name = floating, complexfloating# */ static PyMethodDef @name@type_methods[] = { /* Hook for the round() builtin */ @@ -2206,6 +2211,17 @@ static PyMethodDef @name@type_methods[] = { }; /**end repeat**/ +static PyMethodDef integertype_methods[] = { + /* Hook for the round() builtin */ + {"__round__", + (PyCFunction)integertype_dunder_round, + METH_VARARGS | METH_KEYWORDS, NULL}, + {"is_integer", + (PyCFunction)integer_is_integer, + METH_NOARGS, NULL}, + {NULL, NULL, 0, NULL} /* sentinel */ +}; + /**begin repeat * #name = half,float,double,longdouble# */ diff --git a/numpy/core/tests/test_scalar_methods.py b/numpy/core/tests/test_scalar_methods.py index 7bad6bebf..94b2dd3c9 100644 --- a/numpy/core/tests/test_scalar_methods.py +++ b/numpy/core/tests/test_scalar_methods.py @@ -104,19 +104,23 @@ class TestAsIntegerRatio: assert_equal(nf / df, f, "{}/{}".format(n, d)) -@pytest.mark.parametrize("code", np.typecodes["Float"]) class TestIsInteger: @pytest.mark.parametrize("str_value", ["inf", "nan"]) + @pytest.mark.parametrize("code", np.typecodes["Float"]) def test_special(self, code: str, str_value: str) -> None: cls = np.dtype(code).type value = cls(str_value) assert not value.is_integer() + @pytest.mark.parametrize( + "code", np.typecodes["Float"] + np.typecodes["AllInteger"] + ) def test_true(self, code: str) -> None: float_array = np.arange(-5, 5).astype(code) for value in float_array: assert value.is_integer() + @pytest.mark.parametrize("code", np.typecodes["Float"]) def test_false(self, code: str) -> None: float_array = np.arange(-5, 5).astype(code) float_array *= 1.1 diff --git a/numpy/typing/tests/data/reveal/scalars.py b/numpy/typing/tests/data/reveal/scalars.py index c36813004..e83d579e9 100644 --- a/numpy/typing/tests/data/reveal/scalars.py +++ b/numpy/typing/tests/data/reveal/scalars.py @@ -156,3 +156,5 @@ reveal_type(round(f8, 3)) # E: {float64} if sys.version_info >= (3, 9): reveal_type(f8.__ceil__()) # E: int reveal_type(f8.__floor__()) # E: int + +reveal_type(i8.is_integer()) # E: Literal[True] -- cgit v1.2.1 From 11ae8fef08251d19207f1e7a08bca580298514b4 Mon Sep 17 00:00:00 2001 From: Bas van Beek <43369155+BvB93@users.noreply.github.com> Date: Tue, 31 Aug 2021 21:26:35 +0200 Subject: DOC: Misc documentation improvements --- numpy/core/_add_newdocs_scalars.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/core/_add_newdocs_scalars.py b/numpy/core/_add_newdocs_scalars.py index 306adbc55..8773d6c96 100644 --- a/numpy/core/_add_newdocs_scalars.py +++ b/numpy/core/_add_newdocs_scalars.py @@ -241,11 +241,15 @@ add_newdoc_for_scalar_type('timedelta64', [], """) add_newdoc('numpy.core.numerictypes', "integer", ('is_integer', - f""" + """ integer.is_integer() -> bool Return ``True`` if the number is finite with integral value. + .. versionadded:: 1.22 + + Examples + -------- >>> np.int64(-2).is_integer() True >>> np.uint32(5).is_integer() -- cgit v1.2.1 From 64f15a94708095bf9d29af5dbfcc4b654a57248a Mon Sep 17 00:00:00 2001 From: Mike Taves Date: Mon, 30 Aug 2021 11:01:34 +1200 Subject: MAINT: refactor "for ... in range(len(" statements --- numpy/core/records.py | 12 +++---- numpy/core/tests/test_umath_complex.py | 16 +++++----- numpy/f2py/f2py2e.py | 57 ++++++++++++++++++---------------- numpy/lib/index_tricks.py | 18 +++++------ numpy/lib/polynomial.py | 23 +++++++------- numpy/lib/tests/test_function_base.py | 5 --- numpy/lib/tests/test_shape_base.py | 10 ++++-- numpy/lib/utils.py | 6 ++-- 8 files changed, 72 insertions(+), 75 deletions(-) (limited to 'numpy') diff --git a/numpy/core/records.py b/numpy/core/records.py index b3474ad01..2c20b7d45 100644 --- a/numpy/core/records.py +++ b/numpy/core/records.py @@ -664,17 +664,17 @@ def fromarrays(arrayList, dtype=None, shape=None, formats=None, if nn > 0: shape = shape[:-nn] + _array = recarray(shape, descr) + + # populate the record array (makes a copy) for k, obj in enumerate(arrayList): nn = descr[k].ndim testshape = obj.shape[:obj.ndim - nn] + name = _names[k] if testshape != shape: - raise ValueError("array-shape mismatch in array %d" % k) + raise ValueError(f'array-shape mismatch in array {k} ("{name}")') - _array = recarray(shape, descr) - - # populate the record array (makes a copy) - for i in range(len(arrayList)): - _array[_names[i]] = arrayList[i] + _array[name] = obj return _array diff --git a/numpy/core/tests/test_umath_complex.py b/numpy/core/tests/test_umath_complex.py index ad09830d4..af5bbe59e 100644 --- a/numpy/core/tests/test_umath_complex.py +++ b/numpy/core/tests/test_umath_complex.py @@ -134,8 +134,7 @@ class TestClog: x = np.array([1+0j, 1+2j]) y_r = np.log(np.abs(x)) + 1j * np.angle(x) y = np.log(x) - for i in range(len(x)): - assert_almost_equal(y[i], y_r[i]) + assert_almost_equal(y, y_r) @platform_skip @pytest.mark.skipif(platform.machine() == "armv5tel", reason="See gh-413.") @@ -365,8 +364,7 @@ class TestCpow: x = np.array([1+1j, 0+2j, 1+2j, np.inf, np.nan]) y_r = x ** 2 y = np.power(x, 2) - for i in range(len(x)): - assert_almost_equal(y[i], y_r[i]) + assert_almost_equal(y, y_r) def test_scalar(self): x = np.array([1, 1j, 2, 2.5+.37j, np.inf, np.nan]) @@ -419,8 +417,7 @@ class TestCabs: x = np.array([1+1j, 0+2j, 1+2j, np.inf, np.nan]) y_r = np.array([np.sqrt(2.), 2, np.sqrt(5), np.inf, np.nan]) y = np.abs(x) - for i in range(len(x)): - assert_almost_equal(y[i], y_r[i]) + assert_almost_equal(y, y_r) def test_fabs(self): # Test that np.abs(x +- 0j) == np.abs(x) (as mandated by C99 for cabs) @@ -466,9 +463,10 @@ class TestCabs: return np.abs(complex(a, b)) xa = np.array(x, dtype=complex) - for i in range(len(xa)): - ref = g(x[i], y[i]) - check_real_value(f, x[i], y[i], ref) + assert len(xa) == len(x) == len(y) + for xi, yi in zip(x, y): + ref = g(xi, yi) + check_real_value(f, xi, yi, ref) class TestCarg: def test_simple(self): diff --git a/numpy/f2py/f2py2e.py b/numpy/f2py/f2py2e.py index a14f068f1..f2093d76c 100755 --- a/numpy/f2py/f2py2e.py +++ b/numpy/f2py/f2py2e.py @@ -359,33 +359,34 @@ def buildmodules(lst): cfuncs.buildcfuncs() outmess('Building modules...\n') modules, mnames, isusedby = [], [], {} - for i in range(len(lst)): - if '__user__' in lst[i]['name']: - cb_rules.buildcallbacks(lst[i]) + for item in lst: + if '__user__' in item['name']: + cb_rules.buildcallbacks(item) else: - if 'use' in lst[i]: - for u in lst[i]['use'].keys(): + if 'use' in item: + for u in item['use'].keys(): if u not in isusedby: isusedby[u] = [] - isusedby[u].append(lst[i]['name']) - modules.append(lst[i]) - mnames.append(lst[i]['name']) + isusedby[u].append(item['name']) + modules.append(item) + mnames.append(item['name']) ret = {} - for i in range(len(mnames)): - if mnames[i] in isusedby: + for module, name in zip(modules, mnames): + if name in isusedby: outmess('\tSkipping module "%s" which is used by %s.\n' % ( - mnames[i], ','.join(['"%s"' % s for s in isusedby[mnames[i]]]))) + name, ','.join('"%s"' % s for s in isusedby[name]))) else: um = [] - if 'use' in modules[i]: - for u in modules[i]['use'].keys(): + if 'use' in module: + for u in module['use'].keys(): if u in isusedby and u in mnames: um.append(modules[mnames.index(u)]) else: outmess( - '\tModule "%s" uses nonexisting "%s" which will be ignored.\n' % (mnames[i], u)) - ret[mnames[i]] = {} - dict_append(ret[mnames[i]], rules.buildmodule(modules[i], um)) + f'\tModule "{name}" uses nonexisting "{u}" ' + 'which will be ignored.\n') + ret[name] = {} + dict_append(ret[name], rules.buildmodule(module, um)) return ret @@ -429,18 +430,20 @@ def run_main(comline_list): capi_maps.load_f2cmap_file(options['f2cmap_file']) postlist = callcrackfortran(files, options) isusedby = {} - for i in range(len(postlist)): - if 'use' in postlist[i]: - for u in postlist[i]['use'].keys(): + for plist in postlist: + if 'use' in plist: + for u in plist['use'].keys(): if u not in isusedby: isusedby[u] = [] - isusedby[u].append(postlist[i]['name']) - for i in range(len(postlist)): - if postlist[i]['block'] == 'python module' and '__user__' in postlist[i]['name']: - if postlist[i]['name'] in isusedby: + isusedby[u].append(plist['name']) + for plist in postlist: + if plist['block'] == 'python module' and '__user__' in plist['name']: + if plist['name'] in isusedby: # if not quiet: - outmess('Skipping Makefile build for module "%s" which is used by %s\n' % ( - postlist[i]['name'], ','.join(['"%s"' % s for s in isusedby[postlist[i]['name']]]))) + outmess( + f'Skipping Makefile build for module "{plist["name"]}" ' + 'which is used by {}\n'.format( + ','.join(f'"{s}"' for s in isusedby[plist['name']]))) if 'signsfile' in options: if options['verbose'] > 1: outmess( @@ -448,8 +451,8 @@ def run_main(comline_list): outmess('%s %s\n' % (os.path.basename(sys.argv[0]), options['signsfile'])) return - for i in range(len(postlist)): - if postlist[i]['block'] != 'python module': + for plist in postlist: + if plist['block'] != 'python module': if 'python module' not in options: errmess( 'Tip: If your original code is Fortran source then you must use -m option.\n') diff --git a/numpy/lib/index_tricks.py b/numpy/lib/index_tricks.py index 8d1b6e5be..2a4402c89 100644 --- a/numpy/lib/index_tricks.py +++ b/numpy/lib/index_tricks.py @@ -149,9 +149,9 @@ class nd_grid: try: size = [] typ = int - for k in range(len(key)): - step = key[k].step - start = key[k].start + for kk in key: + step = kk.step + start = kk.start if start is None: start = 0 if step is None: @@ -161,19 +161,19 @@ class nd_grid: typ = float else: size.append( - int(math.ceil((key[k].stop - start)/(step*1.0)))) + int(math.ceil((kk.stop - start) / (step * 1.0)))) if (isinstance(step, (_nx.floating, float)) or isinstance(start, (_nx.floating, float)) or - isinstance(key[k].stop, (_nx.floating, float))): + isinstance(kk.stop, (_nx.floating, float))): typ = float if self.sparse: nn = [_nx.arange(_x, dtype=_t) for _x, _t in zip(size, (typ,)*len(size))] else: nn = _nx.indices(size, typ) - for k in range(len(size)): - step = key[k].step - start = key[k].start + for k, kk in enumerate(key): + step = kk.step + start = kk.start if start is None: start = 0 if step is None: @@ -181,7 +181,7 @@ class nd_grid: if isinstance(step, (_nx.complexfloating, complex)): step = int(abs(step)) if step != 1: - step = (key[k].stop - start)/float(step-1) + step = (kk.stop - start) / float(step - 1) nn[k] = (nn[k]*step+start) if self.sparse: slobj = [_nx.newaxis]*len(size) diff --git a/numpy/lib/polynomial.py b/numpy/lib/polynomial.py index 23021cafa..c40e50a57 100644 --- a/numpy/lib/polynomial.py +++ b/numpy/lib/polynomial.py @@ -152,9 +152,8 @@ def poly(seq_of_zeros): return 1.0 dt = seq_of_zeros.dtype a = ones((1,), dtype=dt) - for k in range(len(seq_of_zeros)): - a = NX.convolve(a, array([1, -seq_of_zeros[k]], dtype=dt), - mode='full') + for zero in seq_of_zeros: + a = NX.convolve(a, array([1, -zero], dtype=dt), mode='full') if issubclass(a.dtype.type, NX.complexfloating): # if complex roots are all complex conjugates, the roots are real. @@ -770,8 +769,8 @@ def polyval(p, x): else: x = NX.asanyarray(x) y = NX.zeros_like(x) - for i in range(len(p)): - y = y * x + p[i] + for pv in p: + y = y * x + pv return y @@ -1273,14 +1272,14 @@ class poly1d: s = s[:-5] return s - for k in range(len(coeffs)): - if not iscomplex(coeffs[k]): - coefstr = fmt_float(real(coeffs[k])) - elif real(coeffs[k]) == 0: - coefstr = '%sj' % fmt_float(imag(coeffs[k])) + for k, coeff in enumerate(coeffs): + if not iscomplex(coeff): + coefstr = fmt_float(real(coeff)) + elif real(coeff) == 0: + coefstr = '%sj' % fmt_float(imag(coeff)) else: - coefstr = '(%s + %sj)' % (fmt_float(real(coeffs[k])), - fmt_float(imag(coeffs[k]))) + coefstr = '(%s + %sj)' % (fmt_float(real(coeff)), + fmt_float(imag(coeff))) power = (N-k) if power == 0: diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index 1d694e92f..829691b1c 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -2772,11 +2772,6 @@ class TestInterp: assert_almost_equal(np.interp(x, xp, fp, period=360), y) -def compare_results(res, desired): - for i in range(len(desired)): - assert_array_equal(res[i], desired[i]) - - class TestPercentile: def test_basic(self): diff --git a/numpy/lib/tests/test_shape_base.py b/numpy/lib/tests/test_shape_base.py index fb7ba7874..a148e53da 100644 --- a/numpy/lib/tests/test_shape_base.py +++ b/numpy/lib/tests/test_shape_base.py @@ -392,7 +392,7 @@ class TestArraySplit: assert_(a.dtype.type is res[-1].dtype.type) # Same thing for manual splits: - res = array_split(a, [0, 1, 2], axis=0) + res = array_split(a, [0, 1], axis=0) tgt = [np.zeros((0, 10)), np.array([np.arange(10)]), np.array([np.arange(10)])] compare_results(res, tgt) @@ -713,5 +713,9 @@ class TestMayShareMemory: # Utility def compare_results(res, desired): - for i in range(len(desired)): - assert_array_equal(res[i], desired[i]) + """Compare lists of arrays.""" + if len(res) != len(desired): + raise ValueError("Iterables have different lengths") + # See also PEP 618 for Python 3.10 + for x, y in zip(res, desired): + assert_array_equal(x, y) diff --git a/numpy/lib/utils.py b/numpy/lib/utils.py index b1a916d4a..1f2cb66fa 100644 --- a/numpy/lib/utils.py +++ b/numpy/lib/utils.py @@ -351,8 +351,7 @@ def who(vardict=None): maxshape = 0 maxbyte = 0 totalbytes = 0 - for k in range(len(sta)): - val = sta[k] + for val in sta: if maxname < len(val[0]): maxname = len(val[0]) if maxshape < len(val[1]): @@ -369,8 +368,7 @@ def who(vardict=None): prval = "Name %s Shape %s Bytes %s Type" % (sp1*' ', sp2*' ', sp3*' ') print(prval + "\n" + "="*(len(prval)+5) + "\n") - for k in range(len(sta)): - val = sta[k] + for val in sta: print("%s %s %s %s %s %s %s" % (val[0], ' '*(sp1-len(val[0])+4), val[1], ' '*(sp2-len(val[1])+5), val[2], ' '*(sp3-len(val[2])+5), -- cgit v1.2.1 From dee09e69f3824cf7fecd71b25392c1b254124e9c Mon Sep 17 00:00:00 2001 From: Mike Taves Date: Thu, 2 Sep 2021 13:37:17 +1200 Subject: DEP: Deprecate quote_args (from numpy.distutils.misc_util) --- numpy/distutils/misc_util.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/distutils/misc_util.py b/numpy/distutils/misc_util.py index 9c65ff43e..a903f3ea3 100644 --- a/numpy/distutils/misc_util.py +++ b/numpy/distutils/misc_util.py @@ -42,7 +42,7 @@ __all__ = ['Configuration', 'get_numpy_include_dirs', 'default_config_dict', 'get_script_files', 'get_lib_source_files', 'get_data_files', 'dot_join', 'get_frame', 'minrelpath', 'njoin', 'is_sequence', 'is_string', 'as_list', 'gpaths', 'get_language', - 'quote_args', 'get_build_architecture', 'get_info', 'get_pkg_info', + 'get_build_architecture', 'get_info', 'get_pkg_info', 'get_num_build_jobs'] class InstallableLib: @@ -110,6 +110,13 @@ def get_num_build_jobs(): return max(x for x in cmdattr if x is not None) def quote_args(args): + """Quote list of arguments. + + .. deprecated:: 1.22. + """ + import warnings + warnings.warn('"quote_args" is deprecated.', + DeprecationWarning, stacklevel=2) # don't used _nt_quote_args as it does not check if # args items already have quotes or not. args = list(args) -- cgit v1.2.1 From 7ad8ea7b11e3544b133d8b397dd3bbe4833d3308 Mon Sep 17 00:00:00 2001 From: Mike Taves Date: Thu, 2 Sep 2021 21:32:21 +1200 Subject: MAINT: revise OSError aliases (IOError, EnvironmentError) --- numpy/core/records.py | 2 +- numpy/core/setup.py | 6 +++--- numpy/core/src/multiarray/convert.c | 12 ++++++------ numpy/core/src/multiarray/ctors.c | 2 +- numpy/core/src/multiarray/multiarraymodule.c | 2 +- numpy/core/tests/test_multiarray.py | 12 ++++++------ numpy/distutils/ccompiler_opt.py | 4 ++-- numpy/distutils/cpuinfo.py | 4 ++-- numpy/distutils/exec_command.py | 2 +- numpy/distutils/fcompiler/compaq.py | 4 ++-- numpy/distutils/npy_pkg_config.py | 4 ++-- numpy/distutils/unixccompiler.py | 2 +- numpy/f2py/crackfortran.py | 4 ++-- numpy/f2py/f2py2e.py | 5 ++--- numpy/f2py/tests/util.py | 2 +- numpy/lib/_datasource.py | 2 +- numpy/lib/format.py | 5 ++--- numpy/lib/npyio.py | 10 ++++++---- numpy/lib/tests/test__datasource.py | 6 +++--- numpy/ma/mrecords.py | 4 ++-- 20 files changed, 47 insertions(+), 47 deletions(-) (limited to 'numpy') diff --git a/numpy/core/records.py b/numpy/core/records.py index 2c20b7d45..fd5f1ab39 100644 --- a/numpy/core/records.py +++ b/numpy/core/records.py @@ -939,7 +939,7 @@ def fromfile(fd, dtype=None, shape=None, offset=0, formats=None, _array = recarray(shape, descr) nbytesread = fd.readinto(_array.data) if nbytesread != nbytes: - raise IOError("Didn't read as many bytes as expected") + raise OSError("Didn't read as many bytes as expected") return _array diff --git a/numpy/core/setup.py b/numpy/core/setup.py index c20320910..ba7d83787 100644 --- a/numpy/core/setup.py +++ b/numpy/core/setup.py @@ -381,9 +381,9 @@ def check_mathlib(config_cmd): mathlibs = libs break else: - raise EnvironmentError("math library missing; rerun " - "setup.py after setting the " - "MATHLIB env variable") + raise RuntimeError( + "math library missing; rerun setup.py after setting the " + "MATHLIB env variable") return mathlibs def visibility_define(config): diff --git a/numpy/core/src/multiarray/convert.c b/numpy/core/src/multiarray/convert.c index 29a2bb0e8..2ad8d6d0e 100644 --- a/numpy/core/src/multiarray/convert.c +++ b/numpy/core/src/multiarray/convert.c @@ -61,7 +61,7 @@ npy_fallocate(npy_intp nbytes, FILE * fp) * early exit on no space, other errors will also get found during fwrite */ if (r == -1 && errno == ENOSPC) { - PyErr_Format(PyExc_IOError, "Not enough free space to write " + PyErr_Format(PyExc_OSError, "Not enough free space to write " "%"NPY_INTP_FMT" bytes", nbytes); return -1; } @@ -138,7 +138,7 @@ PyArray_ToFile(PyArrayObject *self, FILE *fp, char *sep, char *format) if (n3 == 0) { /* binary data */ if (PyDataType_FLAGCHK(PyArray_DESCR(self), NPY_LIST_PICKLE)) { - PyErr_SetString(PyExc_IOError, + PyErr_SetString(PyExc_OSError, "cannot write object arrays to a file in binary mode"); return -1; } @@ -182,7 +182,7 @@ PyArray_ToFile(PyArrayObject *self, FILE *fp, char *sep, char *format) #endif NPY_END_ALLOW_THREADS; if (n < size) { - PyErr_Format(PyExc_IOError, + PyErr_Format(PyExc_OSError, "%ld requested and %ld written", (long) size, (long) n); return -1; @@ -198,7 +198,7 @@ PyArray_ToFile(PyArrayObject *self, FILE *fp, char *sep, char *format) (size_t) PyArray_DESCR(self)->elsize, 1, fp) < 1) { NPY_END_THREADS; - PyErr_Format(PyExc_IOError, + PyErr_Format(PyExc_OSError, "problem writing element %" NPY_INTP_FMT " to file", it->index); Py_DECREF(it); @@ -266,7 +266,7 @@ PyArray_ToFile(PyArrayObject *self, FILE *fp, char *sep, char *format) NPY_END_ALLOW_THREADS; Py_DECREF(byteobj); if (n < n2) { - PyErr_Format(PyExc_IOError, + PyErr_Format(PyExc_OSError, "problem writing element %" NPY_INTP_FMT " to file", it->index); Py_DECREF(strobj); @@ -276,7 +276,7 @@ PyArray_ToFile(PyArrayObject *self, FILE *fp, char *sep, char *format) /* write separator for all but last one */ if (it->index != it->size-1) { if (fwrite(sep, 1, n3, fp) < n3) { - PyErr_Format(PyExc_IOError, + PyErr_Format(PyExc_OSError, "problem writing separator to file"); Py_DECREF(strobj); Py_DECREF(it); diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index aaa645c16..ee8f27ebb 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -3321,7 +3321,7 @@ array_fromfile_binary(FILE *fp, PyArray_Descr *dtype, npy_intp num, size_t *nrea fail = 1; } if (fail) { - PyErr_SetString(PyExc_IOError, + PyErr_SetString(PyExc_OSError, "could not seek in file"); return NULL; } diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c index ea9c10543..232b29b5e 100644 --- a/numpy/core/src/multiarray/multiarraymodule.c +++ b/numpy/core/src/multiarray/multiarraymodule.c @@ -2270,7 +2270,7 @@ array_fromfile(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *keywds) return NULL; } if (npy_fseek(fp, offset, SEEK_CUR) != 0) { - PyErr_SetFromErrno(PyExc_IOError); + PyErr_SetFromErrno(PyExc_OSError); goto cleanup; } if (type == NULL) { diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 5f0a725d2..b5f9f8af3 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -4885,9 +4885,9 @@ class TestIO: # this should probably be supported as a file # but for now test for proper errors b = io.BytesIO() - assert_raises(IOError, np.fromfile, b, np.uint8, 80) + assert_raises(OSError, np.fromfile, b, np.uint8, 80) d = np.ones(7) - assert_raises(IOError, lambda x: x.tofile(b), d) + assert_raises(OSError, lambda x: x.tofile(b), d) def test_bool_fromstring(self): v = np.array([True, False, True, False], dtype=np.bool_) @@ -4970,12 +4970,12 @@ class TestIO: x.tofile(tmp_filename) def fail(*args, **kwargs): - raise IOError('Can not tell or seek') + raise OSError('Can not tell or seek') with io.open(tmp_filename, 'rb', buffering=0) as f: f.seek = fail f.tell = fail - assert_raises(IOError, np.fromfile, f, dtype=x.dtype) + assert_raises(OSError, np.fromfile, f, dtype=x.dtype) def test_io_open_unbuffered_fromfile(self, x, tmp_filename): # gh-6632 @@ -5284,12 +5284,12 @@ class TestIO: def test_tofile_cleanup(self, tmp_filename): x = np.zeros((10), dtype=object) with open(tmp_filename, 'wb') as f: - assert_raises(IOError, lambda: x.tofile(f, sep='')) + assert_raises(OSError, lambda: x.tofile(f, sep='')) # Dup-ed file handle should be closed or remove will fail on Windows OS os.remove(tmp_filename) # Also make sure that we close the Python handle - assert_raises(IOError, lambda: x.tofile(tmp_filename)) + assert_raises(OSError, lambda: x.tofile(tmp_filename)) os.remove(tmp_filename) def test_fromfile_subarray_binary(self, tmp_filename): diff --git a/numpy/distutils/ccompiler_opt.py b/numpy/distutils/ccompiler_opt.py index 1942aa06e..e7fd494d3 100644 --- a/numpy/distutils/ccompiler_opt.py +++ b/numpy/distutils/ccompiler_opt.py @@ -521,7 +521,7 @@ class _Config: def rm_temp(): try: shutil.rmtree(tmp) - except IOError: + except OSError: pass atexit.register(rm_temp) self.conf_tmp_path = tmp @@ -2500,7 +2500,7 @@ class CCompilerOpt(_Config, _Distutils, _Cache, _CCompiler, _Feature, _Parse): last_hash = f.readline().split("cache_hash:") if len(last_hash) == 2 and int(last_hash[1]) == cache_hash: return True - except IOError: + except OSError: pass self.dist_log("generate dispatched config -> ", config_path) diff --git a/numpy/distutils/cpuinfo.py b/numpy/distutils/cpuinfo.py index 51ce3c129..776202109 100644 --- a/numpy/distutils/cpuinfo.py +++ b/numpy/distutils/cpuinfo.py @@ -27,7 +27,7 @@ from subprocess import getstatusoutput def getoutput(cmd, successful_status=(0,), stacklevel=1): try: status, output = getstatusoutput(cmd) - except EnvironmentError as e: + except OSError as e: warnings.warn(str(e), UserWarning, stacklevel=stacklevel) return False, "" if os.WIFEXITED(status) and os.WEXITSTATUS(status) in successful_status: @@ -109,7 +109,7 @@ class LinuxCPUInfo(CPUInfoBase): info[0]['uname_m'] = output.strip() try: fo = open('/proc/cpuinfo') - except EnvironmentError as e: + except OSError as e: warnings.warn(str(e), UserWarning, stacklevel=2) else: for line in fo: diff --git a/numpy/distutils/exec_command.py b/numpy/distutils/exec_command.py index fb10d2470..79998cf5d 100644 --- a/numpy/distutils/exec_command.py +++ b/numpy/distutils/exec_command.py @@ -284,7 +284,7 @@ def _exec_command(command, use_shell=None, use_tee = None, **env): stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=False) - except EnvironmentError: + except OSError: # Return 127, as os.spawn*() and /bin/sh do return 127, '' diff --git a/numpy/distutils/fcompiler/compaq.py b/numpy/distutils/fcompiler/compaq.py index 351a43dd7..01314c136 100644 --- a/numpy/distutils/fcompiler/compaq.py +++ b/numpy/distutils/fcompiler/compaq.py @@ -84,9 +84,9 @@ class CompaqVisualFCompiler(FCompiler): print('Ignoring "%s" (I think it is msvccompiler.py bug)' % (e)) else: raise - except IOError as e: + except OSError as e: if not "vcvarsall.bat" in str(e): - print("Unexpected IOError in", __file__) + print("Unexpected OSError in", __file__) raise except ValueError as e: if not "'path'" in str(e): diff --git a/numpy/distutils/npy_pkg_config.py b/numpy/distutils/npy_pkg_config.py index 951ce5fb8..f6e3ad397 100644 --- a/numpy/distutils/npy_pkg_config.py +++ b/numpy/distutils/npy_pkg_config.py @@ -9,7 +9,7 @@ __all__ = ['FormatError', 'PkgNotFound', 'LibraryInfo', 'VariableSet', _VAR = re.compile(r'\$\{([a-zA-Z0-9_-]+)\}') -class FormatError(IOError): +class FormatError(OSError): """ Exception thrown when there is a problem parsing a configuration file. @@ -20,7 +20,7 @@ class FormatError(IOError): def __str__(self): return self.msg -class PkgNotFound(IOError): +class PkgNotFound(OSError): """Exception raised when a package can not be located.""" def __init__(self, msg): self.msg = msg diff --git a/numpy/distutils/unixccompiler.py b/numpy/distutils/unixccompiler.py index fb91f1789..733a9fc50 100644 --- a/numpy/distutils/unixccompiler.py +++ b/numpy/distutils/unixccompiler.py @@ -105,7 +105,7 @@ def UnixCCompiler_create_static_lib(self, objects, output_libname, # and recreate. # Also, ar on OS X doesn't handle updating universal archives os.unlink(output_filename) - except (IOError, OSError): + except OSError: pass self.mkpath(os.path.dirname(output_filename)) tmp_objects = objects + self.objects diff --git a/numpy/f2py/crackfortran.py b/numpy/f2py/crackfortran.py index 3ac9b80c8..c3ec792e3 100755 --- a/numpy/f2py/crackfortran.py +++ b/numpy/f2py/crackfortran.py @@ -3414,8 +3414,8 @@ if __name__ == "__main__": try: open(l).close() files.append(l) - except IOError as detail: - errmess('IOError: %s\n' % str(detail)) + except OSError as detail: + errmess(f'OSError: {detail!s}\n') else: funcs.append(l) if not strictf77 and f77modulename and not skipemptyends: diff --git a/numpy/f2py/f2py2e.py b/numpy/f2py/f2py2e.py index f2093d76c..f45374be6 100755 --- a/numpy/f2py/f2py2e.py +++ b/numpy/f2py/f2py2e.py @@ -275,9 +275,8 @@ def scaninputline(inputline): with open(l): pass files.append(l) - except IOError as detail: - errmess('IOError: %s. Skipping file "%s".\n' % - (str(detail), l)) + except OSError as detail: + errmess(f'OSError: {detail!s}. Skipping file "{l!s}".\n') elif f == -1: skipfuncs.append(l) elif f == 0: diff --git a/numpy/f2py/tests/util.py b/numpy/f2py/tests/util.py index d5fa76fed..eace3c9fc 100644 --- a/numpy/f2py/tests/util.py +++ b/numpy/f2py/tests/util.py @@ -36,7 +36,7 @@ def _cleanup(): pass try: shutil.rmtree(_module_dir) - except (IOError, OSError): + except OSError: pass _module_dir = None diff --git a/numpy/lib/_datasource.py b/numpy/lib/_datasource.py index c790a6462..56b94853d 100644 --- a/numpy/lib/_datasource.py +++ b/numpy/lib/_datasource.py @@ -530,7 +530,7 @@ class DataSource: return _file_openers[ext](found, mode=mode, encoding=encoding, newline=newline) else: - raise IOError("%s not found." % path) + raise FileNotFoundError(f"{path} not found.") class Repository (DataSource): diff --git a/numpy/lib/format.py b/numpy/lib/format.py index 6ac66c22a..e566e253d 100644 --- a/numpy/lib/format.py +++ b/numpy/lib/format.py @@ -162,7 +162,6 @@ evolved with time and this document is more current. """ import numpy -import io import warnings from numpy.lib.utils import safe_eval from numpy.compat import ( @@ -831,7 +830,7 @@ def open_memmap(filename, mode='r+', dtype=None, shape=None, ------ ValueError If the data or the mode is invalid. - IOError + OSError If the file is not found or cannot be opened correctly. See Also @@ -909,7 +908,7 @@ def _read_bytes(fp, size, error_template="ran out of data"): data += r if len(r) == 0 or len(data) == size: break - except io.BlockingIOError: + except BlockingIOError: pass if len(data) != size: msg = "EOF: reading %s, expected %d bytes got %d" diff --git a/numpy/lib/npyio.py b/numpy/lib/npyio.py index 6f2a211b6..b91bf440f 100644 --- a/numpy/lib/npyio.py +++ b/numpy/lib/npyio.py @@ -324,10 +324,12 @@ def load(file, mmap_mode=None, allow_pickle=False, fix_imports=True, Raises ------ - IOError + OSError If the input file does not exist or cannot be read. + UnpicklingError + If ``allow_pickle=True``, but the file cannot be loaded as a pickle. ValueError - The file contains an object array, but allow_pickle=False given. + The file contains an object array, but ``allow_pickle=False`` given. See Also -------- @@ -436,8 +438,8 @@ def load(file, mmap_mode=None, allow_pickle=False, fix_imports=True, try: return pickle.load(fid, **pickle_kwargs) except Exception as e: - raise IOError( - "Failed to interpret file %s as a pickle" % repr(file)) from e + raise pickle.UnpicklingError( + f"Failed to interpret file {file!r} as a pickle") from e def _save_dispatcher(file, arr, allow_pickle=None, fix_imports=None): diff --git a/numpy/lib/tests/test__datasource.py b/numpy/lib/tests/test__datasource.py index 1ed7815d9..2738d41c4 100644 --- a/numpy/lib/tests/test__datasource.py +++ b/numpy/lib/tests/test__datasource.py @@ -102,10 +102,10 @@ class TestDataSourceOpen: def test_InvalidHTTP(self): url = invalid_httpurl() - assert_raises(IOError, self.ds.open, url) + assert_raises(OSError, self.ds.open, url) try: self.ds.open(url) - except IOError as e: + except OSError as e: # Regression test for bug fixed in r4342. assert_(e.errno is None) @@ -120,7 +120,7 @@ class TestDataSourceOpen: def test_InvalidFile(self): invalid_file = invalid_textfile(self.tmpdir) - assert_raises(IOError, self.ds.open, invalid_file) + assert_raises(OSError, self.ds.open, invalid_file) def test_ValidGzipFile(self): try: diff --git a/numpy/ma/mrecords.py b/numpy/ma/mrecords.py index 6814931b0..10b1b209c 100644 --- a/numpy/ma/mrecords.py +++ b/numpy/ma/mrecords.py @@ -658,8 +658,8 @@ def openfile(fname): # Try to open the file and guess its type try: f = open(fname) - except IOError as e: - raise IOError(f"No such file: '{fname}'") from e + except FileNotFoundError as e: + raise FileNotFoundError(f"No such file: '{fname}'") from e if f.readline()[:2] != "\\x": f.seek(0, 0) return f -- cgit v1.2.1 From 0275d624cf2d980faca5829c2a4147cf4c8becf7 Mon Sep 17 00:00:00 2001 From: Bas van Beek <43369155+BvB93@users.noreply.github.com> Date: Thu, 2 Sep 2021 16:09:41 +0200 Subject: ENH: Use custom file-like protocols instead of `typing.IO` --- numpy/__init__.pyi | 13 ++++++++++++- numpy/core/multiarray.pyi | 9 +++++---- numpy/lib/npyio.pyi | 49 +++++++++++++++++++++++++++++++---------------- 3 files changed, 49 insertions(+), 22 deletions(-) (limited to 'numpy') diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index ca13cffb8..2e7d90d68 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -621,6 +621,14 @@ from numpy.matrixlib import ( bmat as bmat, ) +# Protocol for representing file-like-objects accepted +# by `ndarray.tofile` and `fromfile` +class _IOProtocol(Protocol): + def flush(self) -> object: ... + def fileno(self) -> int: ... + def tell(self) -> SupportsIndex: ... + def seek(self, offset: int, whence: int, /) -> object: ... + __all__: List[str] __path__: List[str] __version__: str @@ -1225,7 +1233,10 @@ class _ArrayOrScalarCommon: # NOTE: `tostring()` is deprecated and therefore excluded # def tostring(self, order=...): ... def tofile( - self, fid: Union[IO[bytes], str, bytes, os.PathLike[Any]], sep: str = ..., format: str = ... + self, + fid: str | bytes | os.PathLike[str] | os.PathLike[bytes] | _IOProtocol, + sep: str = ..., + format: str = ..., ) -> None: ... # generics and 0d arrays return builtin scalars def tolist(self) -> Any: ... diff --git a/numpy/core/multiarray.pyi b/numpy/core/multiarray.pyi index 3e2873cb3..cad6047c9 100644 --- a/numpy/core/multiarray.pyi +++ b/numpy/core/multiarray.pyi @@ -6,7 +6,6 @@ from typing import ( Literal as L, Any, Callable, - IO, Iterable, Optional, overload, @@ -19,6 +18,7 @@ from typing import ( SupportsIndex, final, Final, + Protocol, ) from numpy import ( @@ -50,6 +50,7 @@ from numpy import ( _CastingKind, _ModeKind, _SupportsBuffer, + _IOProtocol, ) from numpy.typing import ( @@ -642,7 +643,7 @@ def frompyfunc( @overload def fromfile( - file: str | bytes | os.PathLike[Any] | IO[Any], + file: str | bytes | os.PathLike[Any] | _IOProtocol, dtype: None = ..., count: SupportsIndex = ..., sep: str = ..., @@ -652,7 +653,7 @@ def fromfile( ) -> NDArray[float64]: ... @overload def fromfile( - file: str | bytes | os.PathLike[Any] | IO[Any], + file: str | bytes | os.PathLike[Any] | _IOProtocol, dtype: _DTypeLike[_SCT], count: SupportsIndex = ..., sep: str = ..., @@ -662,7 +663,7 @@ def fromfile( ) -> NDArray[_SCT]: ... @overload def fromfile( - file: str | bytes | os.PathLike[Any] | IO[Any], + file: str | bytes | os.PathLike[Any] | _IOProtocol, dtype: DTypeLike, count: SupportsIndex = ..., sep: str = ..., diff --git a/numpy/lib/npyio.pyi b/numpy/lib/npyio.pyi index 1fa689bbe..edf3daf07 100644 --- a/numpy/lib/npyio.pyi +++ b/numpy/lib/npyio.pyi @@ -18,6 +18,7 @@ from typing import ( Callable, Pattern, Protocol, + Iterable, ) from numpy import ( @@ -42,6 +43,8 @@ _T = TypeVar("_T") _T_contra = TypeVar("_T_contra", contravariant=True) _T_co = TypeVar("_T_co", covariant=True) _SCT = TypeVar("_SCT", bound=generic) +_CharType_co = TypeVar("_CharType_co", str, bytes, covariant=True) +_CharType_contra = TypeVar("_CharType_contra", str, bytes, contravariant=True) _DTypeLike = Union[ Type[_SCT], @@ -52,6 +55,16 @@ _DTypeLike = Union[ class _SupportsGetItem(Protocol[_T_contra, _T_co]): def __getitem__(self, key: _T_contra) -> _T_co: ... +class _SupportsRead(Protocol[_CharType_co]): + def read(self) -> _CharType_co: ... + +class _SupportsReadSeek(Protocol[_CharType_co]): + def read(self, n: int, /) -> _CharType_co: ... + def seek(self, offset: int, whence: int, /) -> object: ... + +class _SupportsWrite(Protocol[_CharType_contra]): + def write(self, s: _CharType_contra, /) -> object: ... + __all__: List[str] class BagObj(Generic[_T_co]): @@ -94,7 +107,7 @@ class NpzFile(Mapping[str, NDArray[Any]]): # NOTE: Returns a `NpzFile` if file is a zip file; # returns an `ndarray`/`memmap` otherwise def load( - file: str | bytes | os.PathLike[Any] | IO[bytes], + file: str | bytes | os.PathLike[Any] | _SupportsReadSeek[bytes], mmap_mode: L[None, "r+", "r", "w+", "c"] = ..., allow_pickle: bool = ..., fix_imports: bool = ..., @@ -102,27 +115,29 @@ def load( ) -> Any: ... def save( - file: str | os.PathLike[str] | IO[bytes], + file: str | os.PathLike[str] | _SupportsWrite[bytes], arr: ArrayLike, allow_pickle: bool = ..., fix_imports: bool = ..., ) -> None: ... def savez( - file: str | os.PathLike[str] | IO[bytes], + file: str | os.PathLike[str] | _SupportsWrite[bytes], *args: ArrayLike, **kwds: ArrayLike, ) -> None: ... def savez_compressed( - file: str | os.PathLike[str] | IO[bytes], + file: str | os.PathLike[str] | _SupportsWrite[bytes], *args: ArrayLike, **kwds: ArrayLike, ) -> None: ... +# File-like objects only have to implement `__iter__` and, +# optionally, `encoding` @overload def loadtxt( - fname: str | os.PathLike[str] | IO[Any], + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], dtype: None = ..., comments: str | Sequence[str] = ..., delimiter: None | str = ..., @@ -138,7 +153,7 @@ def loadtxt( ) -> NDArray[float64]: ... @overload def loadtxt( - fname: str | os.PathLike[str] | IO[Any], + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], dtype: _DTypeLike[_SCT], comments: str | Sequence[str] = ..., delimiter: None | str = ..., @@ -154,7 +169,7 @@ def loadtxt( ) -> NDArray[_SCT]: ... @overload def loadtxt( - fname: str | os.PathLike[str] | IO[Any], + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], dtype: DTypeLike, comments: str | Sequence[str] = ..., delimiter: None | str = ..., @@ -170,7 +185,7 @@ def loadtxt( ) -> NDArray[Any]: ... def savetxt( - fname: str | os.PathLike[str] | IO[Any], + fname: str | os.PathLike[str] | _SupportsWrite[str] | _SupportsWrite[bytes], X: ArrayLike, fmt: str | Sequence[str] = ..., delimiter: str = ..., @@ -183,14 +198,14 @@ def savetxt( @overload def fromregex( - file: str | os.PathLike[str] | IO[Any], + file: str | os.PathLike[str] | _SupportsRead[str] | _SupportsRead[bytes], regexp: str | bytes | Pattern[Any], dtype: _DTypeLike[_SCT], encoding: None | str = ... ) -> NDArray[_SCT]: ... @overload def fromregex( - file: str | os.PathLike[str] | IO[Any], + file: str | os.PathLike[str] | _SupportsRead[str] | _SupportsRead[bytes], regexp: str | bytes | Pattern[Any], dtype: DTypeLike, encoding: None | str = ... @@ -199,21 +214,21 @@ def fromregex( # TODO: Sort out arguments @overload def genfromtxt( - fname: str | os.PathLike[str] | IO[Any], + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], dtype: None = ..., *args: Any, **kwargs: Any, ) -> NDArray[float64]: ... @overload def genfromtxt( - fname: str | os.PathLike[str] | IO[Any], + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], dtype: _DTypeLike[_SCT], *args: Any, **kwargs: Any, ) -> NDArray[_SCT]: ... @overload def genfromtxt( - fname: str | os.PathLike[str] | IO[Any], + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], dtype: DTypeLike, *args: Any, **kwargs: Any, @@ -221,14 +236,14 @@ def genfromtxt( @overload def recfromtxt( - fname: str | os.PathLike[str] | IO[Any], + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], *, usemask: L[False] = ..., **kwargs: Any, ) -> recarray[Any, dtype[void]]: ... @overload def recfromtxt( - fname: str | os.PathLike[str] | IO[Any], + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], *, usemask: L[True], **kwargs: Any, @@ -236,14 +251,14 @@ def recfromtxt( @overload def recfromcsv( - fname: str | os.PathLike[str] | IO[Any], + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], *, usemask: L[False] = ..., **kwargs: Any, ) -> recarray[Any, dtype[void]]: ... @overload def recfromcsv( - fname: str | os.PathLike[str] | IO[Any], + fname: str | os.PathLike[str] | Iterable[str] | Iterable[bytes], *, usemask: L[True], **kwargs: Any, -- cgit v1.2.1 From 15dc945ba7bd18da65396a20b8368bcb721e28fc Mon Sep 17 00:00:00 2001 From: Bas van Beek <43369155+BvB93@users.noreply.github.com> Date: Thu, 2 Sep 2021 16:10:01 +0200 Subject: MAINT: Make the `_SupportsGetItem` protocol positional-only --- numpy/lib/npyio.pyi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy') diff --git a/numpy/lib/npyio.pyi b/numpy/lib/npyio.pyi index edf3daf07..4841e9e71 100644 --- a/numpy/lib/npyio.pyi +++ b/numpy/lib/npyio.pyi @@ -53,7 +53,7 @@ _DTypeLike = Union[ ] class _SupportsGetItem(Protocol[_T_contra, _T_co]): - def __getitem__(self, key: _T_contra) -> _T_co: ... + def __getitem__(self, key: _T_contra, /) -> _T_co: ... class _SupportsRead(Protocol[_CharType_co]): def read(self) -> _CharType_co: ... -- cgit v1.2.1 From f384f9ec18713fb3c17b36bd0beecdd06322d1cf Mon Sep 17 00:00:00 2001 From: Bas van Beek <43369155+BvB93@users.noreply.github.com> Date: Thu, 2 Sep 2021 16:27:13 +0200 Subject: TST: Update the IO-related typing tests --- numpy/typing/tests/data/fail/npyio.py | 6 +++--- numpy/typing/tests/data/reveal/npyio.py | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) (limited to 'numpy') diff --git a/numpy/typing/tests/data/fail/npyio.py b/numpy/typing/tests/data/fail/npyio.py index 8edabf2b3..c91b4c9cb 100644 --- a/numpy/typing/tests/data/fail/npyio.py +++ b/numpy/typing/tests/data/fail/npyio.py @@ -21,10 +21,10 @@ np.savez(str_file, AR_i8) # E: incompatible type np.savez_compressed(bytes_path, AR_i8) # E: incompatible type np.savez_compressed(str_file, AR_i8) # E: incompatible type -np.loadtxt(bytes_path) # E: No overload variant +np.loadtxt(bytes_path) # E: incompatible type np.fromregex(bytes_path, ".", np.int64) # E: No overload variant -np.recfromtxt(bytes_path) # E: No overload variant +np.recfromtxt(bytes_path) # E: incompatible type -np.recfromcsv(bytes_path) # E: No overload variant +np.recfromcsv(bytes_path) # E: incompatible type diff --git a/numpy/typing/tests/data/reveal/npyio.py b/numpy/typing/tests/data/reveal/npyio.py index 05005eb1c..d66201dd3 100644 --- a/numpy/typing/tests/data/reveal/npyio.py +++ b/numpy/typing/tests/data/reveal/npyio.py @@ -16,6 +16,16 @@ npz_file: np.lib.npyio.NpzFile AR_i8: npt.NDArray[np.int64] AR_LIKE_f8: List[float] +class BytesWriter: + def write(self, data: bytes) -> None: ... + +class BytesReader: + def read(self, n: int = ...) -> bytes: ... + def seek(self, offset: int, whence: int = ...) -> int: ... + +bytes_writer: BytesWriter +bytes_reader: BytesReader + reveal_type(bag_obj.a) # E: int reveal_type(bag_obj.b) # E: int @@ -33,18 +43,22 @@ with npz_file as f: reveal_type(np.load(bytes_file)) # E: Any reveal_type(np.load(pathlib_path, allow_pickle=True)) # E: Any reveal_type(np.load(str_path, encoding="bytes")) # E: Any +reveal_type(np.load(bytes_reader)) # E: Any reveal_type(np.save(bytes_file, AR_LIKE_f8)) # E: None reveal_type(np.save(pathlib_path, AR_i8, allow_pickle=True)) # E: None reveal_type(np.save(str_path, AR_LIKE_f8)) # E: None +reveal_type(np.save(bytes_writer, AR_LIKE_f8)) # E: None reveal_type(np.savez(bytes_file, AR_LIKE_f8)) # E: None reveal_type(np.savez(pathlib_path, ar1=AR_i8, ar2=AR_i8)) # E: None reveal_type(np.savez(str_path, AR_LIKE_f8, ar1=AR_i8)) # E: None +reveal_type(np.savez(bytes_writer, AR_LIKE_f8, ar1=AR_i8)) # E: None reveal_type(np.savez_compressed(bytes_file, AR_LIKE_f8)) # E: None reveal_type(np.savez_compressed(pathlib_path, ar1=AR_i8, ar2=AR_i8)) # E: None reveal_type(np.savez_compressed(str_path, AR_LIKE_f8, ar1=AR_i8)) # E: None +reveal_type(np.savez_compressed(bytes_writer, AR_LIKE_f8, ar1=AR_i8)) # E: None reveal_type(np.loadtxt(bytes_file)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(np.loadtxt(pathlib_path, dtype=np.str_)) # E: numpy.ndarray[Any, numpy.dtype[numpy.str_]] @@ -52,11 +66,13 @@ reveal_type(np.loadtxt(str_path, dtype=str, skiprows=2)) # E: numpy.ndarray[Any reveal_type(np.loadtxt(str_file, comments="test")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(np.loadtxt(str_path, delimiter="\n")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(np.loadtxt(str_path, ndmin=2)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.loadtxt(["1", "2", "3"])) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(np.fromregex(bytes_file, "test", np.float64)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(np.fromregex(str_file, b"test", dtype=float)) # E: numpy.ndarray[Any, numpy.dtype[Any]] reveal_type(np.fromregex(str_path, re.compile("test"), dtype=np.str_, encoding="utf8")) # E: numpy.ndarray[Any, numpy.dtype[numpy.str_]] reveal_type(np.fromregex(pathlib_path, "test", np.float64)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.fromregex(bytes_reader, "test", np.float64)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(np.genfromtxt(bytes_file)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(np.genfromtxt(pathlib_path, dtype=np.str_)) # E: numpy.ndarray[Any, numpy.dtype[numpy.str_]] @@ -64,9 +80,12 @@ reveal_type(np.genfromtxt(str_path, dtype=str, skiprows=2)) # E: numpy.ndarray[ reveal_type(np.genfromtxt(str_file, comments="test")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(np.genfromtxt(str_path, delimiter="\n")) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(np.genfromtxt(str_path, ndmin=2)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] +reveal_type(np.genfromtxt(["1", "2", "3"], ndmin=2)) # E: numpy.ndarray[Any, numpy.dtype[{float64}]] reveal_type(np.recfromtxt(bytes_file)) # E: numpy.recarray[Any, numpy.dtype[numpy.void]] reveal_type(np.recfromtxt(pathlib_path, usemask=True)) # E: numpy.ma.mrecords.MaskedRecords[Any, numpy.dtype[numpy.void]] +reveal_type(np.recfromtxt(["1", "2", "3"])) # E: numpy.recarray[Any, numpy.dtype[numpy.void]] reveal_type(np.recfromcsv(bytes_file)) # E: numpy.recarray[Any, numpy.dtype[numpy.void]] reveal_type(np.recfromcsv(pathlib_path, usemask=True)) # E: numpy.ma.mrecords.MaskedRecords[Any, numpy.dtype[numpy.void]] +reveal_type(np.recfromcsv(["1", "2", "3"])) # E: numpy.recarray[Any, numpy.dtype[numpy.void]] -- cgit v1.2.1 From bb3e4077a32078deb31d7c31034303303fc268f4 Mon Sep 17 00:00:00 2001 From: Bas van Beek <43369155+BvB93@users.noreply.github.com> Date: Thu, 2 Sep 2021 21:05:54 +0200 Subject: STY: Use a more descriptive variable name Co-Authored-By: Charles Harris --- numpy/core/src/multiarray/scalartypes.c.src | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'numpy') diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src index c9f3341fe..740ec8cc2 100644 --- a/numpy/core/src/multiarray/scalartypes.c.src +++ b/numpy/core/src/multiarray/scalartypes.c.src @@ -1922,7 +1922,7 @@ static PyObject * #else npy_@name@ val = PyArrayScalar_VAL(self, @Name@); #endif - PyObject *o; + PyObject *ret; if (npy_isnan(val)) { Py_RETURN_FALSE; @@ -1931,9 +1931,9 @@ static PyObject * Py_RETURN_FALSE; } - o = (npy_floor@c@(val) == val) ? Py_True : Py_False; - Py_INCREF(o); - return o; + ret = (npy_floor@c@(val) == val) ? Py_True : Py_False; + Py_INCREF(ret); + return ret; } /**end repeat**/ -- cgit v1.2.1