Imported Upstream version 1.17.2 upstream/1.17.2
authorDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 31 Dec 2020 00:49:29 +0000 (09:49 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 31 Dec 2020 00:49:29 +0000 (09:49 +0900)
16 files changed:
doc/changelog/1.17.2-changelog.rst [new file with mode: 0644]
doc/release/1.17.2-notes.rst [new file with mode: 0644]
doc/source/release.rst
numpy/core/_exceptions.py
numpy/core/src/npysort/radixsort.c.src
numpy/core/tests/test_multiarray.py
numpy/core/tests/test_regression.py
numpy/ctypeslib.py
numpy/doc/dispatch.py
numpy/fft/pocketfft.py
numpy/lib/arraypad.py
numpy/lib/tests/test_arraypad.py
numpy/testing/_private/utils.py
numpy/testing/tests/test_utils.py
pavement.py
setup.py

diff --git a/doc/changelog/1.17.2-changelog.rst b/doc/changelog/1.17.2-changelog.rst
new file mode 100644 (file)
index 0000000..144f400
--- /dev/null
@@ -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 (file)
index 0000000..65cdaf9
--- /dev/null
@@ -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.
index 5750e402fec03e766edc5672217ff4a875715084..b09e877d89edeebb39bfafa7c0b7a200d0d1e401 100644 (file)
@@ -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
index a1af7a78d5d6117d6d285b60f38d47b317c88970..b3805af043ffad0c7092fb4c8be971c8ccee0c6f 100644 (file)
@@ -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
 
 
index c90b069744fd966bc50b864fb911fb06f9a5bd89..72887d7e4d5a249f994768865a34aeae7cb13d17 100644 (file)
@@ -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;
index b8ec84824629db91f57dbbd7c648b2815beb9fdc..ee01eb6be55c63c12b03d9865a778b478e1eb23e 100644 (file)
@@ -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]')
index 794d30287bc2ece6b4a0e1666e47022d2e658a34..cbd4ceafc9e76e1af390b92b2ccbae296001ff0d 100644 (file)
@@ -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)
index 02490a1c08091c5cb11a17929cf25f8f14104080..c9016cf8f9ae2af6d09b4ea138dbefca8ed236cf 100644 (file)
@@ -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
index 09a3e51346cbcabd76edc2c034bbfd8a8d7e5763..15e9fff6ad472e9b9d7db9d34b63a77afd2605b7 100644 (file)
@@ -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
 ...
index 45dc162f6359ba1d70d8fa515d461cb4f46d37e6..1f6201c7c75fe2162312c0cbb6763705af9006ac 100644 (file)
@@ -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.
index f08d425d609f39ee9ed85b74ef8fd1127a7c053c..600a32ddebfaab628515d731abd62199c64975db 100644 (file)
@@ -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
 
index b7630cdcdc47f4c8c1f816ca35f3446fab3a4260..4e1f3bcaaa119427a386c24e42efd84f84121dbf 100644 (file)
@@ -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)
index ead5d264d00082766d33ad55cd111a9b9198690c..09fe85e5f447f14c5afdc1b453ed91462c49d412 100644 (file)
@@ -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))
index bf60772d3e11f35843eaae93444c758f8ab3c3f7..76c842f25113d5a24db7ad10a32b3d618e30bd71 100644 (file)
@@ -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):
 
index b6d8fcf5b4138abb2978c3ea6ddaee2aad939381..3612dc8abf17d4d9bb2728f8a201cf11685b76bc 100644 (file)
@@ -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'
 
 
 #-------------------------------------------------------
index fc5944a4100309c5f94bfc2365b7a46a74bc79d6..d6687193232f8e1b39fb09f131389facec5e2a52 100755 (executable)
--- 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)