8000 API: Bump NPY_MAXDIMS to 64 and NPY_MAXARGS to 128 by seberg · Pull Request #24072 · numpy/numpy · GitHub
[go: up one dir, main page]

Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions doc/release/upcoming_changes/24071.compatibility.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
``NPY_MAXARGS`` and ``NPY_MAXDIMS`` increased
---------------------------------------------
``NPY_MAXDIMS`` is now 64 and ``NPY_MAXARGS`` is 128, downstream should
not run into issues due to this, as these are typically only used for stack
allocations, such as::

npy_intp shape[NPY_MAXDIMS];

so that an increase is safe. It will increase the stack usage a little.

There is however a possible bigger complication if a downstream library uses
one of these in its public headers.
The library user and library might compile against different versions of
NumPy and thus break ABI compatibility.
In this event the downstream library will have to hard-code the old value or
itself force downstream to recompile (if viable).
3 changes: 3 additions & 0 deletions doc/release/upcoming_changes/24071.improvement.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
* Maximu number of dimensions increased to 64
* The maximum number of operands for some operations such as
``einsum`` was increased to 128 (or close to it).
5 changes: 2 additions & 3 deletions numpy/core/include/numpy/ndarraytypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,8 @@
* the places where static allocation is used would need to be changed
* to dynamic (including inside of several structures)
*/

#define NPY_MAXDIMS 32
#define NPY_MAXARGS 32
#define NPY_MAXDIMS 64
#define NPY_MAXARGS 128

/* Used for Converter Functions "O&" code in ParseTuple */
#define NPY_FAIL 0
Expand Down
2 changes: 1 addition & 1 deletion numpy/core/multiarray.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ BUFSIZE: L[8192]
CLIP: L[0]
WRAP: L[1]
RAISE: L[2]
MAXDIMS: L[32]
MAXDIMS: L[64]
MAY_SHARE_BOUNDS: L[0]
MAY_SHARE_EXACT: L[-1]
tracemalloc_domain: L[389047]
Expand Down
4 changes: 2 additions & 2 deletions numpy/core/tests/test_conversion_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,6 @@ def test_too_large(self):
self.conv(2**64)

def test_too_many_dims(self):
assert self.conv([1]*32) == (1,)*32
assert self.conv([1]*34) == (1,)*34
with pytest.raises(ValueError):
self.conv([1]*33)
self.conv([1]*65)
23 changes: 23 additions & 0 deletions numpy/core/tests/test_einsum.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import itertools
import sys
import platform
import string

import pytest

Expand Down Expand Up @@ -1062,6 +1063,28 @@ def test_output_order(self):
tmp = np.einsum('...ft,mf->...mt', d, c, order='a', optimize=opt)
assert_(tmp.flags.c_contiguous)

def test_huge_number_of_arguments(self):
# einsum limits the number of arguments to the function already,
# so the limit is a bit lower than that of the iterator (currently)
chars = ",".join(string.ascii_letters)
# With an index:
res = np.einsum(chars + "->", *[np.array([2.])] * 26*2)
assert res == 2.**(26*2)
# And without:
res = np.einsum("," * 100 + "->", *[np.array(2.)] * 101)
assert res == 2.**101

with pytest.raises(ValueError):
res = np.einsum("," * 130 + "->", *[np.array(2.)] * 130)

# Using the alternative call syntax:
res = np.einsum(*[np.array(2.), ()] * 127)
assert res == 2.**127

with pytest.raises(ValueError, match="too many operands"):
np.einsum(*[np.array(2.), ()] * 128)


class TestEinsumPath:
def build_operands(self, string, size_dict=global_size_dict):

Expand Down
10 changes: 5 additions & 5 deletions numpy/core/tests/test_indexing.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,8 +296,8 @@ def test_uncontiguous_subspace_assignment(self):

def test_too_many_fancy_indices_special_case(self):
# Just documents behaviour, this is a small limitation.
a = np.ones((1,) * 32) # 32 is NPY_MAXDIMS
assert_raises(IndexError, a.__getitem__, (np.array([0]),) * 32)
a = np.ones((1,) * 64) # 64 is NPY_MAXDIMS
assert_raises(IndexError, a.__getitem__, (np.array([0]),) * 64)

def test_scalar_array_bool(self):
# NumPy bools can be used as boolean index (python ones as of yet not)
Expand Down Expand Up @@ -551,11 +551,11 @@ def test_character_assignment(self):

@pytest.mark.parametrize("index",
[True, False, np.array([0])])
@pytest.mark.parametrize("num", [32, 40])
@pytest.mark.parametrize("original_ndim", [1, 32])
@pytest.mark.parametrize("num", [64, 70])
@pytest.mark.parametrize("original_ndim", [1, 64])
def test_too_many_advanced_indices(self, index, num, original_ndim):
# These are limitations based on the number of arguments we can process.
# For `num=32` (and all boolean cases), the result is actually define;
# For `num=64` (and all boolean cases), the result is actually define;
# but the use of NpyIter (NPY_MAXARGS) limits it for technical reasons.
arr = np.ones((1,) * original_ndim)
with pytest.raises(IndexError):
Expand Down
4 changes: 2 additions & 2 deletions numpy/core/tests/test_multiarray.py
Original file line number Diff line number Diff line change
8076 Expand Up @@ -733,7 +733,7 @@ def subscript(x, i):
x[i]

assert_raises(IndexError, subscript, a, (np.newaxis, 0))
assert_raises(IndexError, subscript, a, (np.newaxis,)*50)
assert_raises(IndexError, subscript, a, (np.newaxis,)*65)

def test_constructor(self):
x = np.ndarray(())
Expand Down Expand Up @@ -815,7 +815,7 @@ def subscript(x, i):
x[i]

assert_raises(IndexError, subscript, a, (np.newaxis, 0))
assert_raises(IndexError, subscript, a, (np.newaxis,)*50)
assert_raises(IndexError, subscript, a, (np.newaxis,)*65)

def test_overlapping_assignment(self):
# With positive strides
Expand Down
2 changes: 1 addition & 1 deletion numpy/core/tests/test_nditer.py
Original file line number Diff line number Diff line change
Expand Up @@ -718,7 +718,7 @@ def test_iter_flags_errors():
# Not enough operands
assert_raises(ValueError, nditer, [], [], [])
# Too many operands
assert_raises(ValueError, nditer, [a]*100, [], [['readonly']]*100)
assert_raises(ValueError, nditer, [a]*129, [], [['readonly']]*129)
# Bad global flag
assert_raises(ValueError, nditer, [a], ['bad flag'], [['readonly']])
# Bad op flag
Expand Down
4 changes: 2 additions & 2 deletions numpy/core/tests/test_numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -3488,9 +3488,9 @@ def test_broadcast_single_arg(self):

def test_number_of_arguments(self):
arr = np.empty((5,))
for j in range(35):
for j in range(140):
arrs = [arr] * j
if j > 32:
if j > 128:
assert_raises(ValueError, np.broadcast, *arrs)
else:
mit = np.broadcast(*arrs)
Expand Down
8 changes: 4 additions & 4 deletions numpy/core/tests/test_ove 8076 rrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,11 @@ class D:

def test_too_many_duck_arrays(self):
namespace = dict(__array_function__=_return_not_implemented)
types = [type('A' + str(i), (object,), namespace) for i in range(33)]
types = [type('A' + str(i), (object,), namespace) for i in range(129)]
relevant_args = [t() for t in types]

actual = _get_implementing_args(relevant_args[:32])
assert_equal(actual, relevant_args[:32])
actual = _get_implementing_args(relevant_args[:128])
assert_equal(actual, relevant_args[:128])

with assert_raises_regex(TypeError, 'distinct argument types'):
_get_implementing_args(relevant_args)
Expand Down Expand Up @@ -448,7 +448,7 @@ def func(*, like=None):
def test_too_many_args(self):
# Mainly a unit-test to increase coverage
objs = []
for i in range(40):
for i in range(140):
class MyArr:
def __array_function__(self, *args, **kwargs):
return NotImplemented
Expand Down
4 changes: 2 additions & 2 deletions numpy/core/tests/test_regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -1250,7 +1250,7 @@ def test_for_zero_length_in_choose(self):

def test_array_ndmin_overflow(self):
"Ticket #947."
assert_raises(ValueError, lambda: np.array([1], ndmin=33))
assert_raises(ValueError, lambda: np.array([1], ndmin=65))

def test_void_scalar_with_titles(self):
# No ticket
Expand Down Expand Up @@ -2238,7 +2238,7 @@ def test_frompyfunc_many_args(self):
def passer(*args):
pass

assert_raises(ValueError, np.frompyfunc, passer, 32, 1)
assert_raises(ValueError, np.frompyfunc, passer, 128, 1)

def test_repeat_broadcasting(self):
# gh-5743
Expand Down
2 changes: 1 addition & 1 deletion numpy/core/tests/test_umath.py
Original file line number Diff line number Diff line change
Expand Up @@ -4676,7 +4676,7 @@ def __array_prepare__(self, obj, context=None):
assert type(np.add.outer([1, 2], arr)) is cls

def test_outer_exceeds_maxdims():
deep = np.ones((1,) * 17)
deep = np.ones((1,) * 33)
with assert_raises(ValueError):
np.add.outer(deep, deep)

Expand Down
0