Thanks to visit codestin.com
Credit goes to github.com

Skip to content

API: Add lib.array_utils namespace #24540

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Sep 11, 2023
Merged
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
3 changes: 3 additions & 0 deletions doc/release/upcoming_changes/24540.change.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
* ``np.lib.array_utils`` public module has been introduced and in its initial version
it hosts three functions: ``byte_bounds`` (moved from ``np.lib.utils``),
``normalize_axis_tuple`` and ``normalize_axis_index``
4 changes: 3 additions & 1 deletion doc/source/reference/routines.other.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Memory ranges

shares_memory
may_share_memory
byte_bounds
lib.array_utils.byte_bounds

Array mixins
------------
Expand All @@ -47,6 +47,8 @@ Utility
show_config
show_runtime
broadcast_shapes
lib.array_utils.normalize_axis_index
lib.array_utils.normalize_axis_tuple

.. automodule:: numpy.exceptions

Expand Down
2 changes: 1 addition & 1 deletion numpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@
from .lib._ufunclike_impl import fix, isneginf, isposinf
from .lib._arraypad_impl import pad
from .lib._utils_impl import (
byte_bounds, show_runtime, get_include, info
show_runtime, get_include, info
)
from .lib._stride_tricks_impl import (
broadcast_arrays, broadcast_shapes, broadcast_to
Expand Down
1 change: 0 additions & 1 deletion numpy/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,6 @@ from numpy.lib._ufunclike_impl import (
from numpy.lib._utils_impl import (
get_include as get_include,
info as info,
byte_bounds as byte_bounds,
show_runtime as show_runtime,
)

Expand Down
3 changes: 2 additions & 1 deletion numpy/_expired_attrs_2_0.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@
"`scalar_types` argument, use `numpy.result_type` and pass the "
"Python values `0`, `0.0`, or `0j`.",
"round_": "Use `np.round` instead.",
"nbytes": "Use `np.dtype(<dtype>).itemsize` instead.",
"get_array_wrap": "",
"DataSource": "It's still available as `np.lib.npyio.DataSource`.",
"nbytes": "Use `np.dtype(<dtype>).itemsize` instead.",
"byte_bounds": "Now it's available under `np.lib.array_utils.byte_bounds`",
}
1 change: 1 addition & 0 deletions numpy/core/multiarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
zeros.__module__ = 'numpy'
_get_promotion_state.__module__ = 'numpy'
_set_promotion_state.__module__ = 'numpy'
normalize_axis_index.__module__ = 'numpy.lib.array_utils'


# We can't verify dispatcher signatures because NumPy's C functions don't
Expand Down
1 change: 1 addition & 0 deletions numpy/core/numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -1328,6 +1328,7 @@ def rollaxis(a, axis, start=0):
return a.transpose(axes)


@set_module("numpy.lib.array_utils")
def normalize_axis_tuple(axis, ndim, argname=None, allow_duplicate=False):
"""
Normalizes an axis argument into a tuple of non-negative integer axes.
Expand Down
1 change: 1 addition & 0 deletions numpy/lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from . import npyio
from . import arrayterator
from . import _arraypad_impl
from . import array_utils
from . import _version

from .arrayterator import Arrayterator
Expand Down
58 changes: 58 additions & 0 deletions numpy/lib/_array_utils_impl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
from numpy.core import asarray
from numpy.core.numeric import normalize_axis_tuple, normalize_axis_index
from numpy._utils import set_module

__all__ = ["byte_bounds", "normalize_axis_tuple", "normalize_axis_index"]


@set_module("numpy.lib.array_utils")
def byte_bounds(a):
"""
Returns pointers to the end-points of an array.

Parameters
----------
a : ndarray
Input array. It must conform to the Python-side of the array
interface.

Returns
-------
(low, high) : tuple of 2 integers
The first integer is the first byte of the array, the second
integer is just past the last byte of the array. If `a` is not
contiguous it will not use every byte between the (`low`, `high`)
values.

Examples
--------
>>> I = np.eye(2, dtype='f'); I.dtype
dtype('float32')
>>> low, high = np.byte_bounds(I)
>>> high - low == I.size*I.itemsize
True
>>> I = np.eye(2); I.dtype
dtype('float64')
>>> low, high = np.byte_bounds(I)
>>> high - low == I.size*I.itemsize
True

"""
ai = a.__array_interface__
a_data = ai['data'][0]
astrides = ai['strides']
ashape = ai['shape']
bytes_a = asarray(a).dtype.itemsize

a_low = a_high = a_data
if astrides is None:
# contiguous case
a_high += a.size * bytes_a
else:
for shape, stride in zip(ashape, astrides):
if stride < 0:
a_low += (shape-1)*stride
else:
a_high += (shape-1)*stride
a_high += bytes_a
return a_low, a_high
25 changes: 25 additions & 0 deletions numpy/lib/_array_utils_impl.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from typing import Any, Iterable, Tuple

from numpy import generic
from numpy.typing import NDArray

__all__: list[str]

# NOTE: In practice `byte_bounds` can (potentially) take any object
# implementing the `__array_interface__` protocol. The caveat is
# that certain keys, marked as optional in the spec, must be present for
# `byte_bounds`. This concerns `"strides"` and `"data"`.
def byte_bounds(a: generic | NDArray[Any]) -> tuple[int, int]: ...

def normalize_axis_tuple(
axis: int | Iterable[int],
ndim: int = ...,
argname: None | str = ...,
allow_duplicate: None | bool = ...,
) -> Tuple[int, int]: ...

def normalize_axis_index(
axis: int = ...,
ndim: int = ...,
msg_prefix: None | str = ...,
) -> int: ...
62 changes: 2 additions & 60 deletions numpy/lib/_utils_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
import functools
import platform

from numpy.core import ndarray, asarray
from numpy.core import ndarray
from numpy._utils import set_module
import numpy as np

__all__ = [
'get_include', 'info', 'byte_bounds', 'show_runtime'
'get_include', 'info', 'show_runtime'
]


Expand Down Expand Up @@ -287,64 +287,6 @@ def deprecate_with_doc(msg):
return _Deprecate(message=msg)


#--------------------------------------------
# Determine if two arrays can share memory
#--------------------------------------------


@set_module('numpy')
def byte_bounds(a):
"""
Returns pointers to the end-points of an array.

Parameters
----------
a : ndarray
Input array. It must conform to the Python-side of the array
interface.

Returns
-------
(low, high) : tuple of 2 integers
The first integer is the first byte of the array, the second
integer is just past the last byte of the array. If `a` is not
contiguous it will not use every byte between the (`low`, `high`)
values.

Examples
--------
>>> I = np.eye(2, dtype='f'); I.dtype
dtype('float32')
>>> low, high = np.byte_bounds(I)
>>> high - low == I.size*I.itemsize
True
>>> I = np.eye(2); I.dtype
dtype('float64')
>>> low, high = np.byte_bounds(I)
>>> high - low == I.size*I.itemsize
True

"""
ai = a.__array_interface__
a_data = ai['data'][0]
astrides = ai['strides']
ashape = ai['shape']
bytes_a = asarray(a).dtype.itemsize

a_low = a_high = a_data
if astrides is None:
# contiguous case
a_high += a.size * bytes_a
else:
for shape, stride in zip(ashape, astrides):
if stride < 0:
a_low += (shape-1)*stride
else:
a_high += (shape-1)*stride
a_high += bytes_a
return a_low, a_high


#-----------------------------------------------------------------------------


Expand Down
8 changes: 0 additions & 8 deletions numpy/lib/_utils_impl.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ from typing import (
Protocol,
)

from numpy import generic
from numpy.typing import NDArray
from numpy.core.numerictypes import (
issubdtype as issubdtype,
)
Expand Down Expand Up @@ -56,12 +54,6 @@ def deprecate(

def deprecate_with_doc(msg: None | str) -> _Deprecate: ...

# NOTE: In practice `byte_bounds` can (potentially) take any object
# implementing the `__array_interface__` protocol. The caveat is
# that certain keys, marked as optional in the spec, must be present for
# `byte_bounds`. This concerns `"strides"` and `"data"`.
def byte_bounds(a: generic | NDArray[Any]) -> tuple[int, int]: ...

def info(
object: object = ...,
maxwidth: int = ...,
Expand Down
6 changes: 6 additions & 0 deletions numpy/lib/array_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from ._array_utils_impl import (
__all__,
byte_bounds,
normalize_axis_index,
normalize_axis_tuple,
)
6 changes: 6 additions & 0 deletions numpy/lib/array_utils.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from ._array_utils_impl import (
__all__ as __all__,
byte_bounds as byte_bounds,
normalize_axis_index as normalize_axis_index,
normalize_axis_tuple as normalize_axis_tuple,
)
33 changes: 33 additions & 0 deletions numpy/lib/tests/test_array_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import numpy as np

from numpy.lib import array_utils
from numpy.testing import assert_equal


class TestByteBounds:
def test_byte_bounds(self):
# pointer difference matches size * itemsize
# due to contiguity
a = np.arange(12).reshape(3, 4)
low, high = array_utils.byte_bounds(a)
assert_equal(high - low, a.size * a.itemsize)

def test_unusual_order_positive_stride(self):
a = np.arange(12).reshape(3, 4)
b = a.T
low, high = array_utils.byte_bounds(b)
assert_equal(high - low, b.size * b.itemsize)

def test_unusual_order_negative_stride(self):
a = np.arange(12).reshape(3, 4)
b = a.T[::-1]
low, high = array_utils.byte_bounds(b)
assert_equal(high - low, b.size * b.itemsize)

def test_strided(self):
a = np.arange(12)
b = a[::2]
low, high = array_utils.byte_bounds(b)
# the largest pointer address is lost (even numbers only in the
# stride), and compensate addresses for striding by 2
assert_equal(high - low, b.size * 2 * b.itemsize - b.itemsize)
32 changes: 1 addition & 31 deletions numpy/lib/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,12 @@
import pytest

import numpy as np
from numpy.testing import assert_equal, assert_raises_regex
from numpy.testing import assert_raises_regex
import numpy.lib._utils_impl as _utils_impl

from io import StringIO


class TestByteBounds:

def test_byte_bounds(self):
# pointer difference matches size * itemsize
# due to contiguity
a = np.arange(12).reshape(3, 4)
low, high = np.byte_bounds(a)
assert_equal(high - low, a.size * a.itemsize)

def test_unusual_order_positive_stride(self):
a = np.arange(12).reshape(3, 4)
b = a.T
low, high = np.byte_bounds(b)
assert_equal(high - low, b.size * b.itemsize)

def test_unusual_order_negative_stride(self):
a = np.arange(12).reshape(3, 4)
b = a.T[::-1]
low, high = np.byte_bounds(b)
assert_equal(high - low, b.size * b.itemsize)

def test_strided(self):
a = np.arange(12)
b = a[::2]
low, high = np.byte_bounds(b)
# the largest pointer address is lost (even numbers only in the
# stride), and compensate addresses for striding by 2
assert_equal(high - low, b.size * 2 * b.itemsize - b.itemsize)


def test_assert_raises_regex_context_manager():
with assert_raises_regex(ValueError, 'no deprecation warning'):
raise ValueError('no deprecation warning')
Expand Down
1 change: 1 addition & 0 deletions numpy/tests/test_public_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ def test_NPY_NO_EXPORT():
"lib.stride_tricks",
"lib.npyio",
"lib.introspect",
"lib.array_utils",
"linalg",
"ma",
"ma.extras",
Expand Down
4 changes: 2 additions & 2 deletions numpy/typing/tests/data/fail/lib_utils.pyi
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import numpy as np
import numpy.lib.array_utils as array_utils

np.byte_bounds(1) # E: incompatible type
array_utils.byte_bounds(1) # E: incompatible type
6 changes: 3 additions & 3 deletions numpy/typing/tests/data/pass/lib_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from io import StringIO

import numpy as np
import numpy.lib.array_utils as array_utils

FILE = StringIO()
AR = np.arange(10, dtype=np.float64)
Expand All @@ -12,8 +13,7 @@ def func(a: int) -> bool:
return True


np.byte_bounds(AR)
np.byte_bounds(np.float64())
array_utils.byte_bounds(AR)
array_utils.byte_bounds(np.float64())

np.info(1, output=FILE)

1 change: 1 addition & 0 deletions numpy/typing/tests/data/pass/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
np.lib.mixins
np.lib.scimath
np.lib.stride_tricks
np.lib.array_utils
np.ma.extras
np.polynomial.chebyshev
np.polynomial.hermite
Expand Down
Loading