From 82ce0a1e8ccf1b83badb9fac3c2160e5896051b5 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sat, 18 Feb 2017 16:06:21 +0000 Subject: ENH: Improve the efficiency of indices Previously, this special cased something that needed no special case, and did addition with zero to encourage broadcasting that would happen anyway. --- numpy/core/numeric.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'numpy/core/numeric.py') diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index b90b0a9c9..97d19f008 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -2089,15 +2089,12 @@ def indices(dimensions, dtype=int): """ dimensions = tuple(dimensions) N = len(dimensions) - if N == 0: - return array([], dtype=dtype) + shape = (1,)*N res = empty((N,)+dimensions, dtype=dtype) for i, dim in enumerate(dimensions): - tmp = arange(dim, dtype=dtype) - tmp.shape = (1,)*i + (dim,)+(1,)*(N-i-1) - newdim = dimensions[:i] + (1,) + dimensions[i+1:] - val = zeros(newdim, dtype) - add(tmp, val, res[i]) + res[i] = arange(dim, dtype=dtype).reshape( + shape[:i] + (dim,) + shape[i+1:] + ) return res -- cgit v1.2.1 From 370b6506f128460371484a50c813d66e64582f44 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 8 Feb 2017 22:05:11 +0000 Subject: MAINT: Use normalize_axis_index in all python axis checking As a result, some exceptions change from ValueError to IndexError This also changes the exception types raised in places where normalize_axis_index is not quite appropriate --- numpy/core/numeric.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'numpy/core/numeric.py') diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 97d19f008..a5cb5bb31 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -17,7 +17,7 @@ from .multiarray import ( inner, int_asbuffer, lexsort, matmul, may_share_memory, min_scalar_type, ndarray, nditer, nested_iters, promote_types, putmask, result_type, set_numeric_ops, shares_memory, vdot, where, - zeros) + zeros, normalize_axis_index) if sys.version_info[0] < 3: from .multiarray import newbuffer, getbuffer @@ -1527,15 +1527,12 @@ def rollaxis(a, axis, start=0): """ n = a.ndim - if axis < 0: - axis += n + axis = normalize_axis_index(axis, n) if start < 0: start += n msg = "'%s' arg requires %d <= %s < %d, but %d was passed in" - if not (0 <= axis < n): - raise ValueError(msg % ('axis', -n, 'axis', n, axis)) if not (0 <= start < n + 1): - raise ValueError(msg % ('start', -n, 'start', n + 1, start)) + raise IndexError(msg % ('start', -n, 'start', n + 1, start)) if axis < start: # it's been removed start -= 1 @@ -1554,7 +1551,7 @@ def _validate_axis(axis, ndim, argname): axis = list(axis) axis = [a + ndim if a < 0 else a for a in axis] if not builtins.all(0 <= a < ndim for a in axis): - raise ValueError('invalid axis for this array in `%s` argument' % + raise IndexError('invalid axis for this array in `%s` argument' % argname) if len(set(axis)) != len(axis): raise ValueError('repeated axis in `%s` argument' % argname) -- cgit v1.2.1 From eb642f1b6533fcd92e366377f5859e4ea56d5eed Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 9 Feb 2017 18:22:00 +0000 Subject: ENH: For compatibility, use an exception type that subclasses both original types --- numpy/core/numeric.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'numpy/core/numeric.py') diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index a5cb5bb31..e7307a870 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -27,7 +27,7 @@ from .umath import (invert, sin, UFUNC_BUFSIZE_DEFAULT, ERR_IGNORE, ERR_DEFAULT, PINF, NAN) from . import numerictypes from .numerictypes import longlong, intc, int_, float_, complex_, bool_ -from ._internal import TooHardError +from ._internal import TooHardError, AxisError bitwise_not = invert ufunc = type(sin) @@ -65,7 +65,7 @@ __all__ = [ 'True_', 'bitwise_not', 'CLIP', 'RAISE', 'WRAP', 'MAXDIMS', 'BUFSIZE', 'ALLOW_THREADS', 'ComplexWarning', 'full', 'full_like', 'matmul', 'shares_memory', 'may_share_memory', 'MAY_SHARE_BOUNDS', 'MAY_SHARE_EXACT', - 'TooHardError', + 'TooHardError', 'AxisError' ] -- cgit v1.2.1 From 8d6ec65c925ebef5e0567708de1d16df39077c9d Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Thu, 9 Feb 2017 18:51:08 +0000 Subject: MAINT: Be specific about where AxisError is raised These were tested by temporarily removing the base classes from AxisError --- numpy/core/numeric.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'numpy/core/numeric.py') diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index e7307a870..066697f3e 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -1532,7 +1532,7 @@ def rollaxis(a, axis, start=0): start += n msg = "'%s' arg requires %d <= %s < %d, but %d was passed in" if not (0 <= start < n + 1): - raise IndexError(msg % ('start', -n, 'start', n + 1, start)) + raise AxisError(msg % ('start', -n, 'start', n + 1, start)) if axis < start: # it's been removed start -= 1 @@ -1551,7 +1551,7 @@ def _validate_axis(axis, ndim, argname): axis = list(axis) axis = [a + ndim if a < 0 else a for a in axis] if not builtins.all(0 <= a < ndim for a in axis): - raise IndexError('invalid axis for this array in `%s` argument' % + raise AxisError('invalid axis for this array in `%s` argument' % argname) if len(set(axis)) != len(axis): raise ValueError('repeated axis in `%s` argument' % argname) -- cgit v1.2.1 From b10b4087132d51ee33bf5e0777282af23d6ee3cd Mon Sep 17 00:00:00 2001 From: gfyoung Date: Wed, 22 Feb 2017 20:44:38 -0500 Subject: BUG: Remove extra digit in binary_repr at limit For negative numbers at the limit for a given number of digits, we were appending an extra digit unnecessarily. Closes gh-8670. --- numpy/core/numeric.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'numpy/core/numeric.py') diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 066697f3e..d4d4045a0 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -2206,7 +2206,7 @@ def binary_repr(num, width=None): designated form. If the `width` value is insufficient, it will be ignored, and `num` will - be returned in binary(`num` > 0) or two's complement (`num` < 0) form + be returned in binary (`num` > 0) or two's complement (`num` < 0) form with its width equal to the minimum number of bits needed to represent the number in the designated form. This behavior is deprecated and will later raise an error. @@ -2276,10 +2276,16 @@ def binary_repr(num, width=None): else: poswidth = len(bin(-num)[2:]) - twocomp = 2**(poswidth + 1) + num + # See gh-8679: remove extra digit + # for numbers at boundaries. + if 2**(poswidth - 1) == -num: + poswidth -= 1 + + twocomp = 2**(poswidth + 1) + num binary = bin(twocomp)[2:] binwidth = len(binary) + outwidth = max(binwidth, width) warn_if_insufficient(width, binwidth) return '1' * (outwidth - binwidth) + binary -- cgit v1.2.1 From 48783e5ceb7f60c33db81ab72e5024f42b220990 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Fri, 24 Feb 2017 16:46:58 +0000 Subject: MAINT: replace len(x.shape) with x.ndim --- numpy/core/numeric.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'numpy/core/numeric.py') diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index d4d4045a0..896ad7f6a 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -1354,9 +1354,9 @@ def tensordot(a, b, axes=2): a, b = asarray(a), asarray(b) as_ = a.shape - nda = len(a.shape) + nda = a.ndim bs = b.shape - ndb = len(b.shape) + ndb = b.ndim equal = True if na != nb: equal = False @@ -1461,7 +1461,7 @@ def roll(a, shift, axis=None): else: broadcasted = broadcast(shift, axis) - if len(broadcasted.shape) > 1: + if broadcasted.ndim > 1: raise ValueError( "'shift' and 'axis' should be scalars or 1D sequences") shifts = {ax: 0 for ax in range(a.ndim)} -- cgit v1.2.1 From 3461ea868bc7520d4569f7d61397044cf4e593d0 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Wed, 22 Feb 2017 00:19:16 +0000 Subject: ENH: Fix alignment of repr for array subclasses --- numpy/core/numeric.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'numpy/core/numeric.py') diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 896ad7f6a..01dd46c3c 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -1890,35 +1890,35 @@ def array_repr(arr, max_line_width=None, precision=None, suppress_small=None): 'array([ 0.000001, 0. , 2. , 3. ])' """ + if type(arr) is not ndarray: + class_name = type(arr).__name__ + else: + class_name = "array" + if arr.size > 0 or arr.shape == (0,): lst = array2string(arr, max_line_width, precision, suppress_small, - ', ', "array(") + ', ', class_name + "(") else: # show zero-length shape unless it is (0,) lst = "[], shape=%s" % (repr(arr.shape),) - if arr.__class__ is not ndarray: - cName = arr.__class__.__name__ - else: - cName = "array" - skipdtype = (arr.dtype.type in _typelessdata) and arr.size > 0 if skipdtype: - return "%s(%s)" % (cName, lst) + return "%s(%s)" % (class_name, lst) else: typename = arr.dtype.name # Quote typename in the output if it is "complex". if typename and not (typename[0].isalpha() and typename.isalnum()): typename = "'%s'" % typename - lf = '' + lf = ' ' if issubclass(arr.dtype.type, flexible): if arr.dtype.names: typename = "%s" % str(arr.dtype) else: typename = "'%s'" % str(arr.dtype) - lf = '\n'+' '*len("array(") - return cName + "(%s, %sdtype=%s)" % (lst, lf, typename) + lf = '\n'+' '*len(class_name + "(") + return "%s(%s,%sdtype=%s)" % (class_name, lst, lf, typename) def array_str(a, max_line_width=None, precision=None, suppress_small=None): -- cgit v1.2.1 From 4feb97e66000b17baa15b8486a7c1f9608865e78 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 26 Mar 2017 10:22:21 +0100 Subject: MAINT: have _validate_axis delegate to normalize_axis_index They were only separate before due to a requirement of a better error message --- numpy/core/numeric.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'numpy/core/numeric.py') diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 01dd46c3c..eabf7962a 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -441,7 +441,7 @@ def count_nonzero(a, axis=None): nullstr = a.dtype.type('') return (a != nullstr).sum(axis=axis, dtype=np.intp) - axis = asarray(_validate_axis(axis, a.ndim, 'axis')) + axis = asarray(_validate_axis(axis, a.ndim)) counts = np.apply_along_axis(multiarray.count_nonzero, axis[0], a) if axis.size == 1: @@ -1544,17 +1544,17 @@ def rollaxis(a, axis, start=0): return a.transpose(axes) -def _validate_axis(axis, ndim, argname): +def _validate_axis(axis, ndim, argname=None): try: axis = [operator.index(axis)] except TypeError: axis = list(axis) - axis = [a + ndim if a < 0 else a for a in axis] - if not builtins.all(0 <= a < ndim for a in axis): - raise AxisError('invalid axis for this array in `%s` argument' % - argname) + axis = [normalize_axis_index(ax, ndim, argname) for ax in axis] if len(set(axis)) != len(axis): - raise ValueError('repeated axis in `%s` argument' % argname) + if argname: + raise ValueError('repeated axis in `%s` argument' % argname) + else: + raise ValueError('repeated axis') return axis -- cgit v1.2.1 From 09d4d35bb979dd0d5d0781946f59f362765eca66 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 26 Mar 2017 10:23:53 +0100 Subject: MAINT: Use normalize_axis_index in cross Previously we did not, as this wouldn't say which axis argument was the cause of the problem. --- numpy/core/numeric.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'numpy/core/numeric.py') diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index eabf7962a..e1352ee6c 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -1752,11 +1752,9 @@ def cross(a, b, axisa=-1, axisb=-1, axisc=-1, axis=None): a = asarray(a) b = asarray(b) # Check axisa and axisb are within bounds - axis_msg = "'axis{0}' out of bounds" - if axisa < -a.ndim or axisa >= a.ndim: - raise ValueError(axis_msg.format('a')) - if axisb < -b.ndim or axisb >= b.ndim: - raise ValueError(axis_msg.format('b')) + axisa = normalize_axis_index(axisa, a.ndim, msg_prefix='axisa') + axisb = normalize_axis_index(axisb, b.ndim, msg_prefix='axisb') + # Move working axis to the end of the shape a = rollaxis(a, axisa, a.ndim) b = rollaxis(b, axisb, b.ndim) @@ -1770,8 +1768,7 @@ def cross(a, b, axisa=-1, axisb=-1, axisc=-1, axis=None): if a.shape[-1] == 3 or b.shape[-1] == 3: shape += (3,) # Check axisc is within bounds - if axisc < -len(shape) or axisc >= len(shape): - raise ValueError(axis_msg.format('c')) + axisc = normalize_axis_index(axisc, len(shape), msg_prefix='axisc') dtype = promote_types(a.dtype, b.dtype) cp = empty(shape, dtype) -- cgit v1.2.1 From b6850e9c00055666fe9bd5f984750743ffcb85d2 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 26 Mar 2017 10:50:54 +0100 Subject: MAINT: use normalize_axis_index in np.roll Interestingly np.roll allows axes to be repeated. --- numpy/core/numeric.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'numpy/core/numeric.py') diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index e1352ee6c..afa10747c 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -1460,16 +1460,14 @@ def roll(a, shift, axis=None): return roll(a.ravel(), shift, 0).reshape(a.shape) else: + axis = _validate_axis(axis, a.ndim, allow_duplicate=True) broadcasted = broadcast(shift, axis) if broadcasted.ndim > 1: raise ValueError( "'shift' and 'axis' should be scalars or 1D sequences") shifts = {ax: 0 for ax in range(a.ndim)} for sh, ax in broadcasted: - if -a.ndim <= ax < a.ndim: - shifts[ax % a.ndim] += sh - else: - raise ValueError("'axis' entry is out of bounds") + shifts[ax] += sh rolls = [((slice(None), slice(None)),)] * a.ndim for ax, offset in shifts.items(): @@ -1544,13 +1542,13 @@ def rollaxis(a, axis, start=0): return a.transpose(axes) -def _validate_axis(axis, ndim, argname=None): +def _validate_axis(axis, ndim, argname=None, allow_duplicate=False): try: axis = [operator.index(axis)] except TypeError: axis = list(axis) axis = [normalize_axis_index(ax, ndim, argname) for ax in axis] - if len(set(axis)) != len(axis): + if not allow_duplicate and len(set(axis)) != len(axis): if argname: raise ValueError('repeated axis in `%s` argument' % argname) else: -- cgit v1.2.1 From 17466ad1839718c091c629bb647e881b7922a148 Mon Sep 17 00:00:00 2001 From: Eric Wieser Date: Sun, 26 Mar 2017 11:30:23 +0100 Subject: MAINT: Rename _validate_axis, and document it --- numpy/core/numeric.py | 58 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 8 deletions(-) (limited to 'numpy/core/numeric.py') diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index afa10747c..632266310 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -441,7 +441,7 @@ def count_nonzero(a, axis=None): nullstr = a.dtype.type('') return (a != nullstr).sum(axis=axis, dtype=np.intp) - axis = asarray(_validate_axis(axis, a.ndim)) + axis = asarray(normalize_axis_tuple(axis, a.ndim)) counts = np.apply_along_axis(multiarray.count_nonzero, axis[0], a) if axis.size == 1: @@ -1460,7 +1460,7 @@ def roll(a, shift, axis=None): return roll(a.ravel(), shift, 0).reshape(a.shape) else: - axis = _validate_axis(axis, a.ndim, allow_duplicate=True) + axis = normalize_axis_tuple(axis, a.ndim, allow_duplicate=True) broadcasted = broadcast(shift, axis) if broadcasted.ndim > 1: raise ValueError( @@ -1542,15 +1542,57 @@ def rollaxis(a, axis, start=0): return a.transpose(axes) -def _validate_axis(axis, ndim, argname=None, allow_duplicate=False): +def normalize_axis_tuple(axis, ndim, argname=None, allow_duplicate=False): + """ + Normalizes an axis argument into a tuple of non-negative integer axes. + + This handles shorthands such as ``1`` and converts them to ``(1,)``, + as well as performing the handling of negative indices covered by + `normalize_axis_index`. + + By default, this forbids axes from being specified multiple times. + + Used internally by multi-axis-checking logic. + + .. versionadded:: 1.13.0 + + Parameters + ---------- + axis : int, iterable of int + The un-normalized index or indices of the axis. + ndim : int + The number of dimensions of the array that `axis` should be normalized + against. + argname : str, optional + A prefix to put before the error message, typically the name of the + argument. + allow_duplicate : bool, optional + If False, the default, disallow an axis from being specified twice. + + Returns + ------- + normalized_axes : tuple of int + The normalized axis index, such that `0 <= normalized_axis < ndim` + + Raises + ------ + AxisError + If any axis provided is out of range + ValueError + If an axis is repeated + + See also + -------- + normalize_axis_index : normalizing a single scalar axis + """ try: axis = [operator.index(axis)] except TypeError: - axis = list(axis) - axis = [normalize_axis_index(ax, ndim, argname) for ax in axis] + axis = tuple(axis) + axis = tuple(normalize_axis_index(ax, ndim, argname) for ax in axis) if not allow_duplicate and len(set(axis)) != len(axis): if argname: - raise ValueError('repeated axis in `%s` argument' % argname) + raise ValueError('repeated axis in `{}` argument'.format(argname)) else: raise ValueError('repeated axis') return axis @@ -1612,8 +1654,8 @@ def moveaxis(a, source, destination): a = asarray(a) transpose = a.transpose - source = _validate_axis(source, a.ndim, 'source') - destination = _validate_axis(destination, a.ndim, 'destination') + source = normalize_axis_tuple(source, a.ndim, 'source') + destination = normalize_axis_tuple(destination, a.ndim, 'destination') if len(source) != len(destination): raise ValueError('`source` and `destination` arguments must have ' 'the same number of elements') -- cgit v1.2.1 From 8427866e4fb038f8d3af317e04a9cc9808bea8ca Mon Sep 17 00:00:00 2001 From: Stefan Peterson Date: Tue, 4 Apr 2017 10:58:20 +0200 Subject: DOC: Fix for issues #7622 and #7914 --- numpy/core/numeric.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'numpy/core/numeric.py') diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 632266310..c89b4097a 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -2145,8 +2145,8 @@ def fromfunction(function, shape, **kwargs): The function is called with N parameters, where N is the rank of `shape`. Each parameter represents the coordinates of the array varying along a specific axis. For example, if `shape` - were ``(2, 2)``, then the parameters in turn be (0, 0), (0, 1), - (1, 0), (1, 1). + were ``(2, 2)``, then the parameters would be + ``array([[0, 0], [1, 1]])`` and ``array([[0, 1], [0, 1]])`` shape : (N,) tuple of ints Shape of the output array, which also determines the shape of the coordinate arrays passed to `function`. -- cgit v1.2.1 From 0c4f621ad18ce4fbf6740943de59c4e2d5c7841c Mon Sep 17 00:00:00 2001 From: Juan Nunez-Iglesias Date: Sun, 9 Apr 2017 12:41:28 +1000 Subject: DOC: Replace reference to np.swapaxis with np.swapaxes (#8914) DOC: Replace reference to np.swapaxis with np.swapaxes The former function does not exist. --- numpy/core/numeric.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'numpy/core/numeric.py') diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index c89b4097a..2c0205175 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -1639,7 +1639,7 @@ def moveaxis(a, source, destination): >>> np.transpose(x).shape (5, 4, 3) - >>> np.swapaxis(x, 0, -1).shape + >>> np.swapaxes(x, 0, -1).shape (5, 4, 3) >>> np.moveaxis(x, [0, 1], [-1, -2]).shape (5, 4, 3) -- cgit v1.2.1