summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDongHun Kwak <dh0128.kwak@samsung.com>2020-12-31 09:37:58 +0900
committerDongHun Kwak <dh0128.kwak@samsung.com>2020-12-31 09:37:58 +0900
commit95c2afc1c5b1680fe2499d65a5f95b2df50e61d7 (patch)
tree577459d08e4af226e0a6d0c4cbb604f469fba6b9
parent82f01297d523a2cc774fe856c582b8021b368d0a (diff)
downloadpython-numpy-95c2afc1c5b1680fe2499d65a5f95b2df50e61d7.tar.gz
python-numpy-95c2afc1c5b1680fe2499d65a5f95b2df50e61d7.tar.bz2
python-numpy-95c2afc1c5b1680fe2499d65a5f95b2df50e61d7.zip
Imported Upstream version 1.17.2upstream/1.17.2
-rw-r--r--doc/changelog/1.17.2-changelog.rst28
-rw-r--r--doc/release/1.17.2-notes.rst49
-rw-r--r--doc/source/release.rst1
-rw-r--r--numpy/core/_exceptions.py1
-rw-r--r--numpy/core/src/npysort/radixsort.c.src4
-rw-r--r--numpy/core/tests/test_multiarray.py22
-rw-r--r--numpy/core/tests/test_regression.py30
-rw-r--r--numpy/ctypeslib.py15
-rw-r--r--numpy/doc/dispatch.py2
-rw-r--r--numpy/fft/pocketfft.py75
-rw-r--r--numpy/lib/arraypad.py84
-rw-r--r--numpy/lib/tests/test_arraypad.py33
-rw-r--r--numpy/testing/_private/utils.py9
-rw-r--r--numpy/testing/tests/test_utils.py9
-rw-r--r--pavement.py2
-rwxr-xr-xsetup.py2
16 files changed, 250 insertions, 116 deletions
diff --git a/doc/changelog/1.17.2-changelog.rst b/doc/changelog/1.17.2-changelog.rst
new file mode 100644
index 000000000..144f40038
--- /dev/null
+++ b/doc/changelog/1.17.2-changelog.rst
@@ -0,0 +1,28 @@
+
+Contributors
+============
+
+A total of 7 people contributed to this release. People with a "+" by their
+names contributed a patch for the first time.
+
+* CakeWithSteak +
+* Charles Harris
+* Dan Allan
+* Hameer Abbasi
+* Lars Grueter
+* Matti Picus
+* Sebastian Berg
+
+Pull requests merged
+====================
+
+A total of 8 pull requests were merged for this release.
+
+* `#14418 <https://github.com/numpy/numpy/pull/14418>`__: BUG: Fix aradixsort indirect indexing.
+* `#14420 <https://github.com/numpy/numpy/pull/14420>`__: DOC: Fix a minor typo in dispatch documentation.
+* `#14421 <https://github.com/numpy/numpy/pull/14421>`__: BUG: test, fix regression in converting to ctypes
+* `#14430 <https://github.com/numpy/numpy/pull/14430>`__: BUG: Do not show Override module in private error classes.
+* `#14432 <https://github.com/numpy/numpy/pull/14432>`__: BUG: Fixed maximum relative error reporting in assert_allclose.
+* `#14433 <https://github.com/numpy/numpy/pull/14433>`__: BUG: Fix uint-overflow if padding with linear_ramp and negative...
+* `#14436 <https://github.com/numpy/numpy/pull/14436>`__: BUG: Update 1.17.x with 1.18.0-dev pocketfft.py.
+* `#14446 <https://github.com/numpy/numpy/pull/14446>`__: REL: Prepare for NumPy 1.17.2 release.
diff --git a/doc/release/1.17.2-notes.rst b/doc/release/1.17.2-notes.rst
new file mode 100644
index 000000000..65cdaf903
--- /dev/null
+++ b/doc/release/1.17.2-notes.rst
@@ -0,0 +1,49 @@
+.. currentmodule:: numpy
+
+==========================
+NumPy 1.17.2 Release Notes
+==========================
+
+This release contains fixes for bugs reported against NumPy 1.17.1 along with a
+some documentation improvements. The most important fix is for lexsort when the
+keys are of type (u)int8 or (u)int16. If you are currently using 1.17 you
+should upgrade.
+
+The Python versions supported in this release are 3.5-3.7, Python 2.7 has been
+dropped. Python 3.8b4 should work with the released source packages, but there
+are no future guarantees.
+
+Downstream developers should use Cython >= 0.29.13 for Python 3.8 support and
+OpenBLAS >= 3.7 to avoid errors on the Skylake architecture. The NumPy wheels
+on PyPI are built from the OpenBLAS development branch in order to avoid those
+errors.
+
+
+Contributors
+============
+
+A total of 7 people contributed to this release. People with a "+" by their
+names contributed a patch for the first time.
+
+* CakeWithSteak +
+* Charles Harris
+* Dan Allan
+* Hameer Abbasi
+* Lars Grueter
+* Matti Picus
+* Sebastian Berg
+
+
+Pull requests merged
+====================
+
+A total of 8 pull requests were merged for this release.
+
+* `#14418 <https://github.com/numpy/numpy/pull/14418>`__: BUG: Fix aradixsort indirect indexing.
+* `#14420 <https://github.com/numpy/numpy/pull/14420>`__: DOC: Fix a minor typo in dispatch documentation.
+* `#14421 <https://github.com/numpy/numpy/pull/14421>`__: BUG: test, fix regression in converting to ctypes
+* `#14430 <https://github.com/numpy/numpy/pull/14430>`__: BUG: Do not show Override module in private error classes.
+* `#14432 <https://github.com/numpy/numpy/pull/14432>`__: BUG: Fixed maximum relative error reporting in assert_allclose.
+* `#14433 <https://github.com/numpy/numpy/pull/14433>`__: BUG: Fix uint-overflow if padding with linear_ramp and negative...
+* `#14436 <https://github.com/numpy/numpy/pull/14436>`__: BUG: Update 1.17.x with 1.18.0-dev pocketfft.py.
+* `#14446 <https://github.com/numpy/numpy/pull/14446>`__: REL: Prepare for NumPy 1.17.2 release.
diff --git a/doc/source/release.rst b/doc/source/release.rst
index 5750e402f..b09e877d8 100644
--- a/doc/source/release.rst
+++ b/doc/source/release.rst
@@ -2,6 +2,7 @@
Release Notes
*************
+.. include:: ../release/1.17.2-notes.rst
.. include:: ../release/1.17.1-notes.rst
.. include:: ../release/1.17.0-notes.rst
.. include:: ../release/1.16.4-notes.rst
diff --git a/numpy/core/_exceptions.py b/numpy/core/_exceptions.py
index a1af7a78d..b3805af04 100644
--- a/numpy/core/_exceptions.py
+++ b/numpy/core/_exceptions.py
@@ -27,6 +27,7 @@ def _display_as_base(cls):
assert issubclass(cls, Exception)
cls.__name__ = cls.__base__.__name__
cls.__qualname__ = cls.__base__.__qualname__
+ set_module(cls.__base__.__module__)(cls)
return cls
diff --git a/numpy/core/src/npysort/radixsort.c.src b/numpy/core/src/npysort/radixsort.c.src
index c90b06974..72887d7e4 100644
--- a/numpy/core/src/npysort/radixsort.c.src
+++ b/numpy/core/src/npysort/radixsort.c.src
@@ -198,9 +198,9 @@ aradixsort_@suff@(void *start, npy_intp* tosort, npy_intp num, void *NPY_UNUSED(
return 0;
}
- k1 = KEY_OF(arr[0]);
+ k1 = KEY_OF(arr[tosort[0]]);
for (npy_intp i = 1; i < num; i++) {
- k2 = KEY_OF(arr[i]);
+ k2 = KEY_OF(arr[tosort[i]]);
if (k1 > k2) {
all_sorted = 0;
break;
diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py
index b8ec84824..ee01eb6be 100644
--- a/numpy/core/tests/test_multiarray.py
+++ b/numpy/core/tests/test_multiarray.py
@@ -4562,18 +4562,26 @@ class TestTake(object):
assert_equal(y, np.array([1, 2, 3]))
class TestLexsort(object):
- def test_basic(self):
- a = [1, 2, 1, 3, 1, 5]
- b = [0, 4, 5, 6, 2, 3]
+ @pytest.mark.parametrize('dtype',[
+ np.uint8, np.uint16, np.uint32, np.uint64,
+ np.int8, np.int16, np.int32, np.int64,
+ np.float16, np.float32, np.float64
+ ])
+ def test_basic(self, dtype):
+ a = np.array([1, 2, 1, 3, 1, 5], dtype=dtype)
+ b = np.array([0, 4, 5, 6, 2, 3], dtype=dtype)
idx = np.lexsort((b, a))
expected_idx = np.array([0, 4, 2, 1, 3, 5])
assert_array_equal(idx, expected_idx)
+ assert_array_equal(a[idx], np.sort(a))
- x = np.vstack((b, a))
- idx = np.lexsort(x)
- assert_array_equal(idx, expected_idx)
+ def test_mixed(self):
+ a = np.array([1, 2, 1, 3, 1, 5])
+ b = np.array([0, 4, 5, 6, 2, 3], dtype='datetime64[D]')
- assert_array_equal(x[1][idx], np.sort(x[1]))
+ idx = np.lexsort((b, a))
+ expected_idx = np.array([0, 4, 2, 1, 3, 5])
+ assert_array_equal(idx, expected_idx)
def test_datetime(self):
a = np.array([0,0,0], dtype='datetime64[D]')
diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py
index 794d30287..cbd4ceafc 100644
--- a/numpy/core/tests/test_regression.py
+++ b/numpy/core/tests/test_regression.py
@@ -2480,3 +2480,33 @@ class TestRegression(object):
__array_interface__ = {}
np.array([T()])
+
+ def test_2d__array__shape(self):
+ class T(object):
+ def __array__(self):
+ return np.ndarray(shape=(0,0))
+
+ # Make sure __array__ is used instead of Sequence methods.
+ def __iter__(self):
+ return iter([])
+
+ def __getitem__(self, idx):
+ raise AssertionError("__getitem__ was called")
+
+ def __len__(self):
+ return 0
+
+
+ t = T()
+ #gh-13659, would raise in broadcasting [x=t for x in result]
+ np.array([t])
+
+ @pytest.mark.skipif(sys.maxsize < 2 ** 31 + 1, reason='overflows 32-bit python')
+ @pytest.mark.skipif(sys.platform == 'win32' and sys.version_info[:2] < (3, 8),
+ reason='overflows on windows, fixed in bpo-16865')
+ def test_to_ctypes(self):
+ #gh-14214
+ arr = np.zeros((2 ** 31 + 1,), 'b')
+ assert arr.size * arr.itemsize > 2 ** 31
+ c_arr = np.ctypeslib.as_ctypes(arr)
+ assert_equal(c_arr._length_, arr.size)
diff --git a/numpy/ctypeslib.py b/numpy/ctypeslib.py
index 02490a1c0..c9016cf8f 100644
--- a/numpy/ctypeslib.py
+++ b/numpy/ctypeslib.py
@@ -92,11 +92,11 @@ else:
# Adapted from Albert Strasheim
def load_library(libname, loader_path):
"""
- It is possible to load a library using
+ It is possible to load a library using
>>> lib = ctypes.cdll[<full_path_name>] # doctest: +SKIP
But there are cross-platform considerations, such as library file extensions,
- plus the fact Windows will just load the first library it finds with that name.
+ plus the fact Windows will just load the first library it finds with that name.
NumPy supplies the load_library function as a convenience.
Parameters
@@ -110,12 +110,12 @@ else:
Returns
-------
ctypes.cdll[libpath] : library object
- A ctypes library object
+ A ctypes library object
Raises
------
OSError
- If there is no library with the expected extension, or the
+ If there is no library with the expected extension, or the
library is defective and cannot be loaded.
"""
if ctypes.__version__ < '1.0.1':
@@ -535,7 +535,10 @@ if ctypes is not None:
if readonly:
raise TypeError("readonly arrays unsupported")
- dtype = _dtype((ai["typestr"], ai["shape"]))
- result = as_ctypes_type(dtype).from_address(addr)
+ # can't use `_dtype((ai["typestr"], ai["shape"]))` here, as it overflows
+ # dtype.itemsize (gh-14214)
+ ctype_scalar = as_ctypes_type(ai["typestr"])
+ result_type = _ctype_ndarray(ctype_scalar, ai["shape"])
+ result = result_type.from_address(addr)
result.__keep = obj
return result
diff --git a/numpy/doc/dispatch.py b/numpy/doc/dispatch.py
index 09a3e5134..15e9fff6a 100644
--- a/numpy/doc/dispatch.py
+++ b/numpy/doc/dispatch.py
@@ -223,7 +223,7 @@ calls ``numpy.sum(self)``, and the same for ``mean``.
... return arr._i * arr._N
...
>>> @implements(np.mean)
-... def sum(a):
+... def mean(arr):
... "Implementation of np.mean for DiagonalArray objects"
... return arr._i / arr._N
...
diff --git a/numpy/fft/pocketfft.py b/numpy/fft/pocketfft.py
index 45dc162f6..1f6201c7c 100644
--- a/numpy/fft/pocketfft.py
+++ b/numpy/fft/pocketfft.py
@@ -44,7 +44,11 @@ array_function_dispatch = functools.partial(
overrides.array_function_dispatch, module='numpy.fft')
-def _raw_fft(a, n, axis, is_real, is_forward, fct):
+# `inv_norm` is a float by which the result of the transform needs to be
+# divided. This replaces the original, more intuitive 'fct` parameter to avoid
+# divisions by zero (or alternatively additional checks) in the case of
+# zero-length axes during its computation.
+def _raw_fft(a, n, axis, is_real, is_forward, inv_norm):
axis = normalize_axis_index(axis, a.ndim)
if n is None:
n = a.shape[axis]
@@ -53,6 +57,8 @@ def _raw_fft(a, n, axis, is_real, is_forward, fct):
raise ValueError("Invalid number of FFT data points (%d) specified."
% n)
+ fct = 1/inv_norm
+
if a.shape[axis] != n:
s = list(a.shape)
if s[axis] > n:
@@ -176,10 +182,10 @@ def fft(a, n=None, axis=-1, norm=None):
a = asarray(a)
if n is None:
n = a.shape[axis]
- fct = 1
+ inv_norm = 1
if norm is not None and _unitary(norm):
- fct = 1 / sqrt(n)
- output = _raw_fft(a, n, axis, False, True, fct)
+ inv_norm = sqrt(n)
+ output = _raw_fft(a, n, axis, False, True, inv_norm)
return output
@@ -271,10 +277,11 @@ def ifft(a, n=None, axis=-1, norm=None):
a = asarray(a)
if n is None:
n = a.shape[axis]
- fct = 1/n
if norm is not None and _unitary(norm):
- fct = 1/sqrt(n)
- output = _raw_fft(a, n, axis, False, False, fct)
+ inv_norm = sqrt(max(n, 1))
+ else:
+ inv_norm = n
+ output = _raw_fft(a, n, axis, False, False, inv_norm)
return output
@@ -359,12 +366,12 @@ def rfft(a, n=None, axis=-1, norm=None):
"""
a = asarray(a)
- fct = 1
+ inv_norm = 1
if norm is not None and _unitary(norm):
if n is None:
n = a.shape[axis]
- fct = 1/sqrt(n)
- output = _raw_fft(a, n, axis, True, True, fct)
+ inv_norm = sqrt(n)
+ output = _raw_fft(a, n, axis, True, True, inv_norm)
return output
@@ -392,8 +399,9 @@ def irfft(a, n=None, axis=-1, norm=None):
Length of the transformed axis of the output.
For `n` output points, ``n//2+1`` input points are necessary. If the
input is longer than this, it is cropped. If it is shorter than this,
- it is padded with zeros. If `n` is not given, it is determined from
- the length of the input along the axis specified by `axis`.
+ it is padded with zeros. If `n` is not given, it is taken to be
+ ``2*(m-1)`` where ``m`` is the length of the input along the axis
+ specified by `axis`.
axis : int, optional
Axis over which to compute the inverse FFT. If not given, the last
axis is used.
@@ -436,6 +444,14 @@ def irfft(a, n=None, axis=-1, norm=None):
thus resample a series to `m` points via Fourier interpolation by:
``a_resamp = irfft(rfft(a), m)``.
+ The correct interpretation of the hermitian input depends on the length of
+ the original data, as given by `n`. This is because each input shape could
+ correspond to either an odd or even length signal. By default, `irfft`
+ assumes an even output length which puts the last entry at the Nyquist
+ frequency; aliasing with its symmetric counterpart. By Hermitian symmetry,
+ the value is thus treated as purely real. To avoid losing information, the
+ correct length of the real input **must** be given.
+
Examples
--------
>>> np.fft.ifft([1, -1j, -1, 1j])
@@ -452,10 +468,10 @@ def irfft(a, n=None, axis=-1, norm=None):
a = asarray(a)
if n is None:
n = (a.shape[axis] - 1) * 2
- fct = 1/n
+ inv_norm = n
if norm is not None and _unitary(norm):
- fct = 1/sqrt(n)
- output = _raw_fft(a, n, axis, True, False, fct)
+ inv_norm = sqrt(n)
+ output = _raw_fft(a, n, axis, True, False, inv_norm)
return output
@@ -473,8 +489,9 @@ def hfft(a, n=None, axis=-1, norm=None):
Length of the transformed axis of the output. For `n` output
points, ``n//2 + 1`` input points are necessary. If the input is
longer than this, it is cropped. If it is shorter than this, it is
- padded with zeros. If `n` is not given, it is determined from the
- length of the input along the axis specified by `axis`.
+ padded with zeros. If `n` is not given, it is taken to be ``2*(m-1)``
+ where ``m`` is the length of the input along the axis specified by
+ `axis`.
axis : int, optional
Axis over which to compute the FFT. If not given, the last
axis is used.
@@ -513,6 +530,14 @@ def hfft(a, n=None, axis=-1, norm=None):
* even: ``ihfft(hfft(a, 2*len(a) - 2) == a``, within roundoff error,
* odd: ``ihfft(hfft(a, 2*len(a) - 1) == a``, within roundoff error.
+ The correct interpretation of the hermitian input depends on the length of
+ the original data, as given by `n`. This is because each input shape could
+ correspond to either an odd or even length signal. By default, `hfft`
+ assumes an even output length which puts the last entry at the Nyquist
+ frequency; aliasing with its symmetric counterpart. By Hermitian symmetry,
+ the value is thus treated as purely real. To avoid losing information, the
+ shape of the full signal **must** be given.
+
Examples
--------
>>> signal = np.array([1, 2, 3, 4, 3, 2])
@@ -1167,8 +1192,9 @@ def irfftn(a, s=None, axes=None, norm=None):
where ``s[-1]//2+1`` points of the input are used.
Along any axis, if the shape indicated by `s` is smaller than that of
the input, the input is cropped. If it is larger, the input is padded
- with zeros. If `s` is not given, the shape of the input along the
- axes specified by `axes` is used.
+ with zeros. If `s` is not given, the shape of the input along the axes
+ specified by axes is used. Except for the last axis which is taken to be
+ ``2*(m-1)`` where ``m`` is the length of the input along that axis.
axes : sequence of ints, optional
Axes over which to compute the inverse FFT. If not given, the last
`len(s)` axes are used, or all axes if `s` is also not specified.
@@ -1213,6 +1239,15 @@ def irfftn(a, s=None, axes=None, norm=None):
See `rfft` for definitions and conventions used for real input.
+ The correct interpretation of the hermitian input depends on the shape of
+ the original data, as given by `s`. This is because each input shape could
+ correspond to either an odd or even length signal. By default, `irfftn`
+ assumes an even output length which puts the last entry at the Nyquist
+ frequency; aliasing with its symmetric counterpart. When performing the
+ final complex to real transform, the last value is thus treated as purely
+ real. To avoid losing information, the correct shape of the real input
+ **must** be given.
+
Examples
--------
>>> a = np.zeros((3, 2, 2))
@@ -1244,7 +1279,7 @@ def irfft2(a, s=None, axes=(-2, -1), norm=None):
a : array_like
The input array
s : sequence of ints, optional
- Shape of the inverse FFT.
+ Shape of the real output to the inverse FFT.
axes : sequence of ints, optional
The axes over which to compute the inverse fft.
Default is the last two axes.
diff --git a/numpy/lib/arraypad.py b/numpy/lib/arraypad.py
index f08d425d6..600a32dde 100644
--- a/numpy/lib/arraypad.py
+++ b/numpy/lib/arraypad.py
@@ -17,66 +17,6 @@ __all__ = ['pad']
# Private utility functions.
-def _linear_ramp(ndim, axis, start, stop, size, reverse=False):
- """
- Create a linear ramp of `size` in `axis` with `ndim`.
-
- This algorithm behaves like a vectorized version of `numpy.linspace`.
- The resulting linear ramp is broadcastable to any array that matches the
- ramp in `shape[axis]` and `ndim`.
-
- Parameters
- ----------
- ndim : int
- Number of dimensions of the resulting array. All dimensions except
- the one specified by `axis` will have the size 1.
- axis : int
- The dimension that contains the linear ramp of `size`.
- start : int or ndarray
- The starting value(s) of the linear ramp. If given as an array, its
- size must match `size`.
- stop : int or ndarray
- The stop value(s) (not included!) of the linear ramp. If given as an
- array, its size must match `size`.
- size : int
- The number of elements in the linear ramp. If this argument is 0 the
- dimensions of `ramp` will all be of length 1 except for the one given
- by `axis` which will be 0.
- reverse : bool
- If False, increment in a positive fashion, otherwise decrement.
-
- Returns
- -------
- ramp : ndarray
- Output array of dtype np.float64 that in- or decrements along the given
- `axis`.
-
- Examples
- --------
- >>> _linear_ramp(ndim=2, axis=0, start=np.arange(3), stop=10, size=2)
- array([[0. , 1. , 2. ],
- [5. , 5.5, 6. ]])
- >>> _linear_ramp(ndim=3, axis=0, start=2, stop=0, size=0)
- array([], shape=(0, 1, 1), dtype=float64)
- """
- # Create initial ramp
- ramp = np.arange(size, dtype=np.float64)
- if reverse:
- ramp = ramp[::-1]
-
- # Make sure, that ramp is broadcastable
- init_shape = (1,) * axis + (size,) + (1,) * (ndim - axis - 1)
- ramp = ramp.reshape(init_shape)
-
- if size != 0:
- # And scale to given start and stop values
- gain = (stop - start) / float(size)
- ramp = ramp * gain
- ramp += start
-
- return ramp
-
-
def _round_if_needed(arr, dtype):
"""
Rounds arr inplace if destination dtype is integer.
@@ -269,17 +209,25 @@ def _get_linear_ramps(padded, axis, width_pair, end_value_pair):
"""
edge_pair = _get_edges(padded, axis, width_pair)
- left_ramp = _linear_ramp(
- padded.ndim, axis, start=end_value_pair[0], stop=edge_pair[0],
- size=width_pair[0], reverse=False
+ left_ramp = np.linspace(
+ start=end_value_pair[0],
+ stop=edge_pair[0].squeeze(axis), # Dimensions is replaced by linspace
+ num=width_pair[0],
+ endpoint=False,
+ dtype=padded.dtype,
+ axis=axis,
)
- _round_if_needed(left_ramp, padded.dtype)
- right_ramp = _linear_ramp(
- padded.ndim, axis, start=end_value_pair[1], stop=edge_pair[1],
- size=width_pair[1], reverse=True
+ right_ramp = np.linspace(
+ start=end_value_pair[1],
+ stop=edge_pair[1].squeeze(axis), # Dimension is replaced by linspace
+ num=width_pair[1],
+ endpoint=False,
+ dtype=padded.dtype,
+ axis=axis,
)
- _round_if_needed(right_ramp, padded.dtype)
+ # Reverse linear space in appropriate dimension
+ right_ramp = right_ramp[_slice_at_axis(slice(None, None, -1), axis)]
return left_ramp, right_ramp
diff --git a/numpy/lib/tests/test_arraypad.py b/numpy/lib/tests/test_arraypad.py
index b7630cdcd..4e1f3bcaa 100644
--- a/numpy/lib/tests/test_arraypad.py
+++ b/numpy/lib/tests/test_arraypad.py
@@ -2,7 +2,6 @@
"""
from __future__ import division, absolute_import, print_function
-from itertools import chain
import pytest
@@ -11,6 +10,12 @@ from numpy.testing import assert_array_equal, assert_allclose, assert_equal
from numpy.lib.arraypad import _as_pairs
+_numeric_dtypes = (
+ np.sctypes["uint"]
+ + np.sctypes["int"]
+ + np.sctypes["float"]
+ + np.sctypes["complex"]
+)
_all_modes = {
'constant': {'constant_values': 0},
'edge': {},
@@ -715,6 +720,24 @@ class TestLinearRamp(object):
assert_equal(a[0, :], 0.)
assert_equal(a[-1, :], 0.)
+ @pytest.mark.parametrize("dtype", _numeric_dtypes)
+ def test_negative_difference(self, dtype):
+ """
+ Check correct behavior of unsigned dtypes if there is a negative
+ difference between the edge to pad and `end_values`. Check both cases
+ to be independent of implementation. Test behavior for all other dtypes
+ in case dtype casting interferes with complex dtypes. See gh-14191.
+ """
+ x = np.array([3], dtype=dtype)
+ result = np.pad(x, 3, mode="linear_ramp", end_values=0)
+ expected = np.array([0, 1, 2, 3, 2, 1, 0], dtype=dtype)
+ assert_equal(result, expected)
+
+ x = np.array([0], dtype=dtype)
+ result = np.pad(x, 3, mode="linear_ramp", end_values=3)
+ expected = np.array([3, 2, 1, 0, 1, 2, 3], dtype=dtype)
+ assert_equal(result, expected)
+
class TestReflect(object):
def test_check_simple(self):
@@ -1307,13 +1330,7 @@ def test_memory_layout_persistence(mode):
assert np.pad(x, 5, mode).flags["F_CONTIGUOUS"]
-@pytest.mark.parametrize("dtype", chain(
- # Skip "other" dtypes as they are not supported by all modes
- np.sctypes["int"],
- np.sctypes["uint"],
- np.sctypes["float"],
- np.sctypes["complex"]
-))
+@pytest.mark.parametrize("dtype", _numeric_dtypes)
@pytest.mark.parametrize("mode", _all_modes.keys())
def test_dtype_persistence(dtype, mode):
arr = np.zeros((3, 2, 1), dtype=dtype)
diff --git a/numpy/testing/_private/utils.py b/numpy/testing/_private/utils.py
index ead5d264d..09fe85e5f 100644
--- a/numpy/testing/_private/utils.py
+++ b/numpy/testing/_private/utils.py
@@ -703,7 +703,7 @@ def assert_array_compare(comparison, x, y, err_msg='', verbose=True,
header='', precision=6, equal_nan=True,
equal_inf=True):
__tracebackhide__ = True # Hide traceback for py.test
- from numpy.core import array, array2string, isnan, inf, bool_, errstate
+ from numpy.core import array, array2string, isnan, inf, bool_, errstate, all
x = array(x, copy=False, subok=True)
y = array(y, copy=False, subok=True)
@@ -821,7 +821,12 @@ def assert_array_compare(comparison, x, y, err_msg='', verbose=True,
# note: this definition of relative error matches that one
# used by assert_allclose (found in np.isclose)
- max_rel_error = (error / abs(y)).max()
+ # Filter values where the divisor would be zero
+ nonzero = bool_(y != 0)
+ if all(~nonzero):
+ max_rel_error = array(inf)
+ else:
+ max_rel_error = (error[nonzero] / abs(y[nonzero])).max()
if error.dtype == 'object':
remarks.append('Max relative difference: '
+ str(max_rel_error))
diff --git a/numpy/testing/tests/test_utils.py b/numpy/testing/tests/test_utils.py
index bf60772d3..76c842f25 100644
--- a/numpy/testing/tests/test_utils.py
+++ b/numpy/testing/tests/test_utils.py
@@ -880,6 +880,15 @@ class TestAssertAllclose(object):
assert_array_less(a, b)
assert_allclose(a, b)
+ def test_report_max_relative_error(self):
+ a = np.array([0, 1])
+ b = np.array([0, 2])
+
+ with pytest.raises(AssertionError) as exc_info:
+ assert_allclose(a, b)
+ msg = str(exc_info.value)
+ assert_('Max relative difference: 0.5' in msg)
+
class TestArrayAlmostEqualNulp(object):
diff --git a/pavement.py b/pavement.py
index b6d8fcf5b..3612dc8ab 100644
--- a/pavement.py
+++ b/pavement.py
@@ -42,7 +42,7 @@ from paver.easy import Bunch, options, task, sh
#-----------------------------------
# Path to the release notes
-RELEASE_NOTES = 'doc/release/1.17.1-notes.rst'
+RELEASE_NOTES = 'doc/release/1.17.2-notes.rst'
#-------------------------------------------------------
diff --git a/setup.py b/setup.py
index fc5944a41..d66871932 100755
--- a/setup.py
+++ b/setup.py
@@ -55,7 +55,7 @@ Operating System :: MacOS
MAJOR = 1
MINOR = 17
-MICRO = 1
+MICRO = 2
ISRELEASED = True
VERSION = '%d.%d.%d' % (MAJOR, MINOR, MICRO)