diff options
Diffstat (limited to 'numpy/core')
-rw-r--r-- | numpy/core/code_generators/generate_umath.py | 5 | ||||
-rw-r--r-- | numpy/core/fromnumeric.py | 119 | ||||
-rw-r--r-- | numpy/core/function_base.py | 17 | ||||
-rw-r--r-- | numpy/core/include/numpy/ufuncobject.h | 12 | ||||
-rw-r--r-- | numpy/core/numeric.py | 90 | ||||
-rw-r--r-- | numpy/core/src/multiarray/common.c | 3 | ||||
-rw-r--r-- | numpy/core/src/multiarray/compiled_base.c | 224 | ||||
-rw-r--r-- | numpy/core/src/multiarray/getset.c | 12 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_object.c | 69 | ||||
-rw-r--r-- | numpy/core/tests/test_deprecations.py | 60 | ||||
-rw-r--r-- | numpy/core/tests/test_multiarray.py | 14 | ||||
-rw-r--r-- | numpy/core/tests/test_numeric.py | 19 | ||||
-rw-r--r-- | numpy/core/tests/test_shape_base.py | 9 | ||||
-rw-r--r-- | numpy/core/tests/test_ufunc.py | 16 | ||||
-rw-r--r-- | numpy/core/tests/test_umath.py | 85 |
15 files changed, 483 insertions, 271 deletions
diff --git a/numpy/core/code_generators/generate_umath.py b/numpy/core/code_generators/generate_umath.py index a96437a42..e79cdd4a6 100644 --- a/numpy/core/code_generators/generate_umath.py +++ b/numpy/core/code_generators/generate_umath.py @@ -13,6 +13,7 @@ sys.path.pop(0) Zero = "PyUFunc_Zero" One = "PyUFunc_One" None_ = "PyUFunc_None" +AllOnes = "PyUFunc_MinusOne" ReorderableNone = "PyUFunc_ReorderableNone" # Sentinel value to specify using the full type description in the @@ -493,7 +494,7 @@ defdict = { TD(flts, f="logaddexp2", astype={'e':'f'}) ), 'bitwise_and': - Ufunc(2, 1, One, + Ufunc(2, 1, AllOnes, docstrings.get('numpy.core.umath.bitwise_and'), None, TD(bints), @@ -507,7 +508,7 @@ defdict = { TD(O, f='PyNumber_Or'), ), 'bitwise_xor': - Ufunc(2, 1, None, + Ufunc(2, 1, Zero, docstrings.get('numpy.core.umath.bitwise_xor'), None, TD(bints), diff --git a/numpy/core/fromnumeric.py b/numpy/core/fromnumeric.py index 52a15e30d..ed5e2410a 100644 --- a/numpy/core/fromnumeric.py +++ b/numpy/core/fromnumeric.py @@ -135,20 +135,21 @@ def reshape(a, newshape, order='C'): newshape : int or tuple of ints The new shape should be compatible with the original shape. If an integer, then the result will be a 1-D array of that length. - One shape dimension can be -1. In this case, the value is inferred - from the length of the array and remaining dimensions. + One shape dimension can be -1. In this case, the value is + inferred from the length of the array and remaining dimensions. order : {'C', 'F', 'A'}, optional - Read the elements of `a` using this index order, and place the elements - into the reshaped array using this index order. 'C' means to - read / write the elements using C-like index order, with the last axis - index changing fastest, back to the first axis index changing slowest. - 'F' means to read / write the elements using Fortran-like index order, - with the first index changing fastest, and the last index changing - slowest. - Note that the 'C' and 'F' options take no account of the memory layout - of the underlying array, and only refer to the order of indexing. 'A' - means to read / write the elements in Fortran-like index order if `a` - is Fortran *contiguous* in memory, C-like order otherwise. + Read the elements of `a` using this index order, and place the + elements into the reshaped array using this index order. 'C' + means to read / write the elements using C-like index order, + with the last axis index changing fastest, back to the first + axis index changing slowest. 'F' means to read / write the + elements using Fortran-like index order, with the first index + changing fastest, and the last index changing slowest. Note that + the 'C' and 'F' options take no account of the memory layout of + the underlying array, and only refer to the order of indexing. + 'A' means to read / write the elements in Fortran-like index + order if `a` is Fortran *contiguous* in memory, C-like order + otherwise. Returns ------- @@ -560,11 +561,12 @@ def partition(a, kth, axis=-1, kind='introselect', order=None): """ Return a partitioned copy of an array. - Creates a copy of the array with its elements rearranged in such a way that - the value of the element in kth position is in the position it would be in - a sorted array. All elements smaller than the kth element are moved before - this element and all equal or greater are moved behind it. The ordering of - the elements in the two partitions is undefined. + Creates a copy of the array with its elements rearranged in such a + way that the value of the element in k-th position is in the + position it would be in a sorted array. All elements smaller than + the k-th element are moved before this element and all equal or + greater are moved behind it. The ordering of the elements in the two + partitions is undefined. .. versionadded:: 1.8.0 @@ -573,23 +575,23 @@ def partition(a, kth, axis=-1, kind='introselect', order=None): a : array_like Array to be sorted. kth : int or sequence of ints - Element index to partition by. The kth value of the element will be in - its final sorted position and all smaller elements will be moved before - it and all equal or greater elements behind it. - The order all elements in the partitions is undefined. - If provided with a sequence of kth it will partition all elements - indexed by kth of them into their sorted position at once. + Element index to partition by. The k-th value of the element + will be in its final sorted position and all smaller elements + will be moved before it and all equal or greater elements behind + it. The order all elements in the partitions is undefined. If + provided with a sequence of k-th it will partition all elements + indexed by k-th of them into their sorted position at once. axis : int or None, optional Axis along which to sort. If None, the array is flattened before sorting. The default is -1, which sorts along the last axis. kind : {'introselect'}, optional Selection algorithm. Default is 'introselect'. order : str or list of str, optional - When `a` is an array with fields defined, this argument specifies - which fields to compare first, second, etc. A single field can - be specified as a string. Not all fields need be specified, but - unspecified fields will still be used, in the order in which they - come up in the dtype, to break ties. + When `a` is an array with fields defined, this argument + specifies which fields to compare first, second, etc. A single + field can be specified as a string. Not all fields need be + specified, but unspecified fields will still be used, in the + order in which they come up in the dtype, to break ties. Returns ------- @@ -604,10 +606,11 @@ def partition(a, kth, axis=-1, kind='introselect', order=None): Notes ----- - The various selection algorithms are characterized by their average speed, - worst case performance, work space size, and whether they are stable. A - stable sort keeps items with the same key in the same relative order. The - available algorithms have the following properties: + The various selection algorithms are characterized by their average + speed, worst case performance, work space size, and whether they are + stable. A stable sort keeps items with the same key in the same + relative order. The available algorithms have the following + properties: ================= ======= ============= ============ ======= kind speed worst case work space stable @@ -616,14 +619,14 @@ def partition(a, kth, axis=-1, kind='introselect', order=None): ================= ======= ============= ============ ======= All the partition algorithms make temporary copies of the data when - partitioning along any but the last axis. Consequently, partitioning - along the last axis is faster and uses less space than partitioning - along any other axis. + partitioning along any but the last axis. Consequently, + partitioning along the last axis is faster and uses less space than + partitioning along any other axis. - The sort order for complex numbers is lexicographic. If both the real - and imaginary parts are non-nan then the order is determined by the - real parts except when they are equal, in which case the order is - determined by the imaginary parts. + The sort order for complex numbers is lexicographic. If both the + real and imaginary parts are non-nan then the order is determined by + the real parts except when they are equal, in which case the order + is determined by the imaginary parts. Examples -------- @@ -646,10 +649,10 @@ def partition(a, kth, axis=-1, kind='introselect', order=None): def argpartition(a, kth, axis=-1, kind='introselect', order=None): """ - Perform an indirect partition along the given axis using the algorithm - specified by the `kind` keyword. It returns an array of indices of the - same shape as `a` that index data along the given axis in partitioned - order. + Perform an indirect partition along the given axis using the + algorithm specified by the `kind` keyword. It returns an array of + indices of the same shape as `a` that index data along the given + axis in partitioned order. .. versionadded:: 1.8.0 @@ -658,29 +661,29 @@ def argpartition(a, kth, axis=-1, kind='introselect', order=None): a : array_like Array to sort. kth : int or sequence of ints - Element index to partition by. The kth element will be in its final - sorted position and all smaller elements will be moved before it and - all larger elements behind it. - The order all elements in the partitions is undefined. - If provided with a sequence of kth it will partition all of them into - their sorted position at once. + Element index to partition by. The k-th element will be in its + final sorted position and all smaller elements will be moved + before it and all larger elements behind it. The order all + elements in the partitions is undefined. If provided with a + sequence of k-th it will partition all of them into their sorted + position at once. axis : int or None, optional - Axis along which to sort. The default is -1 (the last axis). If None, - the flattened array is used. + Axis along which to sort. The default is -1 (the last axis). If + None, the flattened array is used. kind : {'introselect'}, optional Selection algorithm. Default is 'introselect' order : str or list of str, optional - When `a` is an array with fields defined, this argument specifies - which fields to compare first, second, etc. A single field can - be specified as a string, and not all fields need be specified, - but unspecified fields will still be used, in the order in which - they come up in the dtype, to break ties. + When `a` is an array with fields defined, this argument + specifies which fields to compare first, second, etc. A single + field can be specified as a string, and not all fields need be + specified, but unspecified fields will still be used, in the + order in which they come up in the dtype, to break ties. Returns ------- index_array : ndarray, int Array of indices that partition `a` along the specified axis. - In other words, ``a[index_array]`` yields a sorted `a`. + In other words, ``a[index_array]`` yields a partitioned `a`. See Also -------- diff --git a/numpy/core/function_base.py b/numpy/core/function_base.py index 21ca1af01..750fbe838 100644 --- a/numpy/core/function_base.py +++ b/numpy/core/function_base.py @@ -1,10 +1,24 @@ from __future__ import division, absolute_import, print_function +import warnings +import operator + __all__ = ['logspace', 'linspace'] from . import numeric as _nx from .numeric import result_type, NaN, shares_memory, MAY_SHARE_BOUNDS, TooHardError +def _index_deprecate(i, stacklevel=2): + try: + i = operator.index(i) + except TypeError: + msg = ("object of type {} cannot be safely interpreted as " + "an integer.".format(type(i))) + i = int(i) + stacklevel += 1 + warnings.warn(msg, DeprecationWarning, stacklevel=stacklevel) + return i + def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None): """ @@ -81,7 +95,8 @@ def linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None): >>> plt.show() """ - num = int(num) + # 2016-02-25, 1.12 + num = _index_deprecate(num) if num < 0: raise ValueError("Number of samples, %s, must be non-negative." % num) div = (num - 1) if endpoint else num diff --git a/numpy/core/include/numpy/ufuncobject.h b/numpy/core/include/numpy/ufuncobject.h index 1cca64b75..e03a7a4a5 100644 --- a/numpy/core/include/numpy/ufuncobject.h +++ b/numpy/core/include/numpy/ufuncobject.h @@ -251,15 +251,21 @@ typedef struct _tagPyUFuncObject { #endif /* + * UFunc has unit of 0, and the order of operations can be reordered + * This case allows reduction with multiple axes at once. + */ +#define PyUFunc_Zero 0 +/* * UFunc has unit of 1, and the order of operations can be reordered * This case allows reduction with multiple axes at once. */ #define PyUFunc_One 1 /* - * UFunc has unit of 0, and the order of operations can be reordered - * This case allows reduction with multiple axes at once. + * UFunc has unit of -1, and the order of operations can be reordered + * This case allows reduction with multiple axes at once. Intended for + * bitwise_and reduction. */ -#define PyUFunc_Zero 0 +#define PyUFunc_MinusOne 2 /* * UFunc has no unit, and the order of operations cannot be reordered. * This case does not allow reduction with multiple axes at once. diff --git a/numpy/core/numeric.py b/numpy/core/numeric.py index 9ddc3c546..551d63a01 100644 --- a/numpy/core/numeric.py +++ b/numpy/core/numeric.py @@ -2090,31 +2090,6 @@ def isscalar(num): else: return type(num) in ScalarType -_lkup = { - '0':'0000', - '1':'0001', - '2':'0010', - '3':'0011', - '4':'0100', - '5':'0101', - '6':'0110', - '7':'0111', - '8':'1000', - '9':'1001', - 'a':'1010', - 'b':'1011', - 'c':'1100', - 'd':'1101', - 'e':'1110', - 'f':'1111', - 'A':'1010', - 'B':'1011', - 'C':'1100', - 'D':'1101', - 'E':'1110', - 'F':'1111', - 'L':''} - def binary_repr(num, width=None): """ Return the binary representation of the input number as a string. @@ -2134,8 +2109,18 @@ def binary_repr(num, width=None): num : int Only an integer decimal number can be used. width : int, optional - The length of the returned string if `num` is positive, the length of - the two's complement if `num` is negative. + The length of the returned string if `num` is positive, or the length + of the two's complement if `num` is negative, provided that `width` is + at least a sufficient number of bits for `num` to be represented in the + 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 + 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. + + .. deprecated:: 1.12.0 Returns ------- @@ -2146,6 +2131,7 @@ def binary_repr(num, width=None): -------- base_repr: Return a string representation of a number in the given base system. + bin: Python's built-in binary representation generator of an integer. Notes ----- @@ -2169,27 +2155,43 @@ def binary_repr(num, width=None): The two's complement is returned when the input number is negative and width is specified: - >>> np.binary_repr(-3, width=4) - '1101' + >>> np.binary_repr(-3, width=3) + '101' + >>> np.binary_repr(-3, width=5) + '11101' """ - # ' <-- unbreak Emacs fontification - sign = '' - if num < 0: + def warn_if_insufficient(width, binwdith): + if width is not None and width < binwidth: + warnings.warn( + "Insufficient bit width provided. This behavior " + "will raise an error in the future.", DeprecationWarning + ) + + if num == 0: + return '0' * (width or 1) + + elif num > 0: + binary = bin(num)[2:] + binwidth = len(binary) + outwidth = (binwidth if width is None + else max(binwidth, width)) + warn_if_insufficient(width, binwidth) + return binary.zfill(outwidth) + + else: if width is None: - sign = '-' - num = -num + return '-' + bin(-num)[2:] + else: - # replace num with its 2-complement - num = 2**width + num - elif num == 0: - return '0'*(width or 1) - ostr = hex(num) - bin = ''.join([_lkup[ch] for ch in ostr[2:]]) - bin = bin.lstrip('0') - if width is not None: - bin = bin.zfill(width) - return sign + bin + poswidth = len(bin(-num)[2:]) + 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 def base_repr(number, base=2, padding=0): """ diff --git a/numpy/core/src/multiarray/common.c b/numpy/core/src/multiarray/common.c index c216daa95..bd566b77b 100644 --- a/numpy/core/src/multiarray/common.c +++ b/numpy/core/src/multiarray/common.c @@ -476,6 +476,9 @@ PyArray_DTypeFromObjectHelper(PyObject *obj, int maxdims, * __len__ is not defined. */ if (maxdims == 0 || !PySequence_Check(obj) || PySequence_Size(obj) < 0) { + // clear any PySequence_Size error, which corrupts further calls to it + PyErr_Clear(); + if (*out_dtype == NULL || (*out_dtype)->type_num != NPY_OBJECT) { Py_XDECREF(*out_dtype); *out_dtype = PyArray_DescrFromType(NPY_OBJECT); diff --git a/numpy/core/src/multiarray/compiled_base.c b/numpy/core/src/multiarray/compiled_base.c index 932b94f15..711a0ab91 100644 --- a/numpy/core/src/multiarray/compiled_base.c +++ b/numpy/core/src/multiarray/compiled_base.c @@ -302,193 +302,111 @@ arr_digitize(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwds) } /* - * Insert values from an input array into an output array, at positions - * indicated by a mask. If the arrays are of dtype object (indicated by - * the objarray flag), take care of reference counting. - * - * This function implements the copying logic of arr_insert() defined - * below. - */ -static void -arr_insert_loop(char *mptr, char *vptr, char *input_data, char *zero, - char *avals_data, int melsize, int delsize, int objarray, - int totmask, int numvals, int nd, npy_intp *instrides, - npy_intp *inshape) -{ - int mindx, rem_indx, indx, i, copied; - - /* - * Walk through mask array, when non-zero is encountered - * copy next value in the vals array to the input array. - * If we get through the value array, repeat it as necessary. - */ - copied = 0; - for (mindx = 0; mindx < totmask; mindx++) { - if (memcmp(mptr,zero,melsize) != 0) { - /* compute indx into input array */ - rem_indx = mindx; - indx = 0; - for (i = nd - 1; i > 0; --i) { - indx += (rem_indx % inshape[i]) * instrides[i]; - rem_indx /= inshape[i]; - } - indx += rem_indx * instrides[0]; - /* fprintf(stderr, "mindx = %d, indx=%d\n", mindx, indx); */ - /* Copy value element over to input array */ - memcpy(input_data+indx,vptr,delsize); - if (objarray) { - Py_INCREF(*((PyObject **)vptr)); - } - vptr += delsize; - copied += 1; - /* If we move past value data. Reset */ - if (copied >= numvals) { - vptr = avals_data; - copied = 0; - } - } - mptr += melsize; - } -} - -/* * Returns input array with values inserted sequentially into places * indicated by the mask */ NPY_NO_EXPORT PyObject * arr_insert(PyObject *NPY_UNUSED(self), PyObject *args, PyObject *kwdict) { - PyObject *mask = NULL, *vals = NULL; - PyArrayObject *ainput = NULL, *amask = NULL, *avals = NULL, *tmp = NULL; - int numvals, totmask, sameshape; - char *input_data, *mptr, *vptr, *zero = NULL; - int melsize, delsize, nd, objarray, k; - npy_intp *instrides, *inshape; + char *src, *dest; + npy_bool *mask_data; + PyArray_Descr *dtype; + PyArray_CopySwapFunc *copyswap; + PyObject *array0, *mask0, *values0; + PyArrayObject *array, *mask, *values; + npy_intp i, j, chunk, nm, ni, nv; static char *kwlist[] = {"input", "mask", "vals", NULL}; + NPY_BEGIN_THREADS_DEF; + values = mask = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O&OO", kwlist, - PyArray_Converter, &ainput, - &mask, &vals)) { - goto fail; + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O!OO:place", kwlist, + &PyArray_Type, &array0, &mask0, &values0)) { + return NULL; } - amask = (PyArrayObject *)PyArray_FROM_OF(mask, NPY_ARRAY_CARRAY); - if (amask == NULL) { + array = (PyArrayObject *)PyArray_FromArray((PyArrayObject *)array0, NULL, + NPY_ARRAY_CARRAY | NPY_ARRAY_UPDATEIFCOPY); + if (array == NULL) { goto fail; } - /* Cast an object array */ - if (PyArray_DESCR(amask)->type_num == NPY_OBJECT) { - tmp = (PyArrayObject *)PyArray_Cast(amask, NPY_INTP); - if (tmp == NULL) { - goto fail; - } - Py_DECREF(amask); - amask = tmp; - } - sameshape = 1; - if (PyArray_NDIM(amask) == PyArray_NDIM(ainput)) { - for (k = 0; k < PyArray_NDIM(amask); k++) { - if (PyArray_DIMS(amask)[k] != PyArray_DIMS(ainput)[k]) { - sameshape = 0; - } - } - } - else { - /* Test to see if amask is 1d */ - if (PyArray_NDIM(amask) != 1) { - sameshape = 0; - } - else if ((PyArray_SIZE(ainput)) != PyArray_SIZE(amask)) { - sameshape = 0; - } - } - if (!sameshape) { - PyErr_SetString(PyExc_TypeError, - "mask array must be 1-d or same shape as input array"); + ni = PyArray_SIZE(array); + dest = PyArray_DATA(array); + chunk = PyArray_DESCR(array)->elsize; + mask = (PyArrayObject *)PyArray_FROM_OTF(mask0, NPY_BOOL, + NPY_ARRAY_CARRAY | NPY_ARRAY_FORCECAST); + if (mask == NULL) { goto fail; } - avals = (PyArrayObject *)PyArray_FromObject(vals, - PyArray_DESCR(ainput)->type_num, 0, 1); - if (avals == NULL) { + nm = PyArray_SIZE(mask); + if (nm != ni) { + PyErr_SetString(PyExc_ValueError, + "place: mask and data must be " + "the same size"); goto fail; } - numvals = PyArray_SIZE(avals); - nd = PyArray_NDIM(ainput); - input_data = PyArray_DATA(ainput); - mptr = PyArray_DATA(amask); - melsize = PyArray_DESCR(amask)->elsize; - vptr = PyArray_DATA(avals); - delsize = PyArray_DESCR(avals)->elsize; - zero = PyArray_Zero(amask); - if (zero == NULL) { + + mask_data = PyArray_DATA(mask); + dtype = PyArray_DESCR(array); + Py_INCREF(dtype); + + values = (PyArrayObject *)PyArray_FromAny(values0, dtype, + 0, 0, NPY_ARRAY_CARRAY, NULL); + if (values == NULL) { goto fail; } - objarray = (PyArray_DESCR(ainput)->type_num == NPY_OBJECT); - if (!numvals) { - /* nothing to insert! fail unless none of mask is true */ - const char *iter = mptr; - const char *const last = iter + PyArray_NBYTES(amask); - while (iter != last && !memcmp(iter, zero, melsize)) { - iter += melsize; + nv = PyArray_SIZE(values); /* zero if null array */ + if (nv <= 0) { + npy_bool allFalse = 1; + i = 0; + + while (allFalse && i < ni) { + if (mask_data[i]) { + allFalse = 0; + } else { + i++; + } } - if (iter != last) { + if (!allFalse) { PyErr_SetString(PyExc_ValueError, - "Cannot insert from an empty array!"); + "Cannot insert from an empty array!"); goto fail; + } else { + Py_XDECREF(values); + Py_XDECREF(mask); + Py_RETURN_NONE; } - goto finish; } - /* Handle zero-dimensional case separately */ - if (nd == 0) { - if (memcmp(mptr,zero,melsize) != 0) { - /* Copy value element over to input array */ - memcpy(input_data,vptr,delsize); - if (objarray) { - Py_INCREF(*((PyObject **)vptr)); + src = PyArray_DATA(values); + j = 0; + + copyswap = PyArray_DESCR(array)->f->copyswap; + NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(array)); + for (i = 0; i < ni; i++) { + if (mask_data[i]) { + if (j >= nv) { + j = 0; } - } - Py_DECREF(amask); - Py_DECREF(avals); - PyDataMem_FREE(zero); - Py_DECREF(ainput); - Py_RETURN_NONE; - } - totmask = (int) PyArray_SIZE(amask); - instrides = PyArray_STRIDES(ainput); - inshape = PyArray_DIMS(ainput); - if (objarray) { - /* object array, need to refcount, can't release the GIL */ - arr_insert_loop(mptr, vptr, input_data, zero, PyArray_DATA(avals), - melsize, delsize, objarray, totmask, numvals, nd, - instrides, inshape); - } - else { - /* No increfs take place in arr_insert_loop, so release the GIL */ - NPY_BEGIN_ALLOW_THREADS; - arr_insert_loop(mptr, vptr, input_data, zero, PyArray_DATA(avals), - melsize, delsize, objarray, totmask, numvals, nd, - instrides, inshape); - NPY_END_ALLOW_THREADS; + copyswap(dest + i*chunk, src + j*chunk, 0, array); + j++; + } } + NPY_END_THREADS; -finish: - Py_DECREF(amask); - Py_DECREF(avals); - PyDataMem_FREE(zero); - Py_DECREF(ainput); + Py_XDECREF(values); + Py_XDECREF(mask); + Py_DECREF(array); Py_RETURN_NONE; -fail: - PyDataMem_FREE(zero); - Py_XDECREF(ainput); - Py_XDECREF(amask); - Py_XDECREF(avals); + fail: + Py_XDECREF(mask); + Py_XDECREF(array); + Py_XDECREF(values); return NULL; } diff --git a/numpy/core/src/multiarray/getset.c b/numpy/core/src/multiarray/getset.c index c2a88e3b9..a49e56b7f 100644 --- a/numpy/core/src/multiarray/getset.c +++ b/numpy/core/src/multiarray/getset.c @@ -321,10 +321,6 @@ array_data_get(PyArrayObject *self) #endif } -/* - * TODO: Given view semantics, I think this function is a really - * bad idea, and should be removed! - */ static int array_data_set(PyArrayObject *self, PyObject *op) { @@ -332,6 +328,14 @@ array_data_set(PyArrayObject *self, PyObject *op) Py_ssize_t buf_len; int writeable=1; + /* 2016-19-02, 1.12 */ + int ret = DEPRECATE("Assigning the 'data' attribute is an " + "inherently unsafe operation and will " + "be removed in the future."); + if (ret < 0) { + return -1; + } + if (op == NULL) { PyErr_SetString(PyExc_AttributeError, "Cannot delete array data"); diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index 9e8c3c985..6eb0aae55 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -79,6 +79,9 @@ static int assign_reduce_identity_zero(PyArrayObject *result, void *data); static int +assign_reduce_identity_minusone(PyArrayObject *result, void *data); + +static int assign_reduce_identity_one(PyArrayObject *result, void *data); @@ -1948,6 +1951,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, npy_intp iter_shape[NPY_MAXARGS]; NpyIter *iter = NULL; npy_uint32 iter_flags; + npy_intp total_problem_size; /* These parameters come from extobj= or from a TLS global */ int buffersize = 0, errormask = 0; @@ -2343,6 +2347,16 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, } } + total_problem_size = NpyIter_GetIterSize(iter); + if (total_problem_size < 0) { + /* + * Only used for threading, if negative (this means that it is + * larger then ssize_t before axes removal) assume that the actual + * problem is large enough to be threaded usefully. + */ + total_problem_size = 1000; + } + /* Remove all the core output dimensions from the iterator */ for (i = broadcast_ndim; i < iter_ndim; ++i) { if (NpyIter_RemoveAxis(iter, broadcast_ndim) != NPY_SUCCEED) { @@ -2384,6 +2398,7 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, NpyIter_IterNextFunc *iternext; char **dataptr; npy_intp *count_ptr; + NPY_BEGIN_THREADS_DEF; /* Get the variables needed for the loop */ iternext = NpyIter_GetIterNext(iter, NULL); @@ -2394,10 +2409,17 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, dataptr = NpyIter_GetDataPtrArray(iter); count_ptr = NpyIter_GetInnerLoopSizePtr(iter); + if (!needs_api && !NpyIter_IterationNeedsAPI(iter)) { + NPY_BEGIN_THREADS_THRESHOLDED(total_problem_size); + } do { inner_dimensions[0] = *count_ptr; innerloop(dataptr, inner_dimensions, inner_strides, innerloopdata); } while (iternext(iter)); + + if (!needs_api && !NpyIter_IterationNeedsAPI(iter)) { + NPY_END_THREADS; + } } else { /** * For each output operand, check if it has non-zero size, @@ -2414,6 +2436,9 @@ PyUFunc_GeneralizedFunction(PyUFuncObject *ufunc, case PyUFunc_One: assign_reduce_identity_one(op[i], NULL); break; + case PyUFunc_MinusOne: + assign_reduce_identity_minusone(op[i], NULL); + break; case PyUFunc_None: case PyUFunc_ReorderableNone: PyErr_Format(PyExc_ValueError, @@ -2831,6 +2856,19 @@ assign_reduce_identity_one(PyArrayObject *result, void *NPY_UNUSED(data)) } static int +assign_reduce_identity_minusone(PyArrayObject *result, void *NPY_UNUSED(data)) +{ + static PyObject *MinusOne = NULL; + + if (MinusOne == NULL) { + if ((MinusOne = PyInt_FromLong(-1)) == NULL) { + return -1; + } + } + return PyArray_FillWithScalar(result, MinusOne); +} + +static int reduce_loop(NpyIter *iter, char **dataptrs, npy_intp *strides, npy_intp *countptr, NpyIter_IterNextFunc *iternext, int needs_api, npy_intp skip_first_count, void *data) @@ -2983,6 +3021,18 @@ PyUFunc_Reduce(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, assign_identity = NULL; } break; + case PyUFunc_MinusOne: + assign_identity = &assign_reduce_identity_minusone; + reorderable = 1; + /* + * The identity for a dynamic dtype like + * object arrays can't be used in general + */ + if (PyArray_ISOBJECT(arr) && PyArray_SIZE(arr) != 0) { + assign_identity = NULL; + } + break; + case PyUFunc_None: reorderable = 0; break; @@ -3239,17 +3289,22 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, NPY_BEGIN_THREADS_NDITER(iter); do { - dataptr_copy[0] = dataptr[0]; dataptr_copy[1] = dataptr[1]; dataptr_copy[2] = dataptr[0]; /* Copy the first element to start the reduction */ if (otype == NPY_OBJECT) { + /* + * Input (dataptr[0]) and output (dataptr[1]) may point + * to the same memory (i.e. np.add.accumulate(a, out=a)). + * In that case need to incref before decref to avoid the + * possibility of the reference count being zero temporarily. + */ + Py_XINCREF(*(PyObject **)dataptr_copy[1]); Py_XDECREF(*(PyObject **)dataptr_copy[0]); *(PyObject **)dataptr_copy[0] = *(PyObject **)dataptr_copy[1]; - Py_XINCREF(*(PyObject **)dataptr_copy[0]); } else { memcpy(dataptr_copy[0], dataptr_copy[1], itemsize); @@ -3302,10 +3357,16 @@ PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, /* Copy the first element to start the reduction */ if (otype == NPY_OBJECT) { + /* + * Input (dataptr[0]) and output (dataptr[1]) may point + * to the same memory (i.e. np.add.accumulate(a, out=a, axis=0)). + * In that case need to incref before decref to avoid the + * possibility of the reference count being zero temporarily. + */ + Py_XINCREF(*(PyObject **)dataptr_copy[1]); Py_XDECREF(*(PyObject **)dataptr_copy[0]); *(PyObject **)dataptr_copy[0] = *(PyObject **)dataptr_copy[1]; - Py_XINCREF(*(PyObject **)dataptr_copy[0]); } else { memcpy(dataptr_copy[0], dataptr_copy[1], itemsize); @@ -5572,6 +5633,8 @@ ufunc_get_identity(PyUFuncObject *ufunc) return PyInt_FromLong(1); case PyUFunc_Zero: return PyInt_FromLong(0); + case PyUFunc_MinusOne: + return PyInt_FromLong(-1); } Py_RETURN_NONE; } diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py index 80a792fbf..7d12ed466 100644 --- a/numpy/core/tests/test_deprecations.py +++ b/numpy/core/tests/test_deprecations.py @@ -560,6 +560,66 @@ class TestInvalidOrderParameterInputForFlattenArrayDeprecation(_DeprecationTestC self.assert_deprecated(x.flatten, args=("FACK",)) +class TestArrayDataAttributeAssignmentDeprecation(_DeprecationTestCase): + """Assigning the 'data' attribute of an ndarray is unsafe as pointed + out in gh-7093. Eventually, such assignment should NOT be allowed, but + in the interests of maintaining backwards compatibility, only a Deprecation- + Warning will be raised instead for the time being to give developers time to + refactor relevant code. + """ + + def test_data_attr_assignment(self): + a = np.arange(10) + b = np.linspace(0, 1, 10) + + self.message = ("Assigning the 'data' attribute is an " + "inherently unsafe operation and will " + "be removed in the future.") + self.assert_deprecated(a.__setattr__, args=('data', b.data)) + + +class TestLinspaceInvalidNumParameter(_DeprecationTestCase): + """Argument to the num parameter in linspace that cannot be + safely interpreted as an integer is deprecated in 1.12.0. + + Argument to the num parameter in linspace that cannot be + safely interpreted as an integer should not be allowed. + In the interest of not breaking code that passes + an argument that could still be interpreted as an integer, a + DeprecationWarning will be issued for the time being to give + developers time to refactor relevant code. + """ + def test_float_arg(self): + # 2016-02-25, PR#7328 + self.assert_deprecated(np.linspace, args=(0, 10, 2.5)) + + +class TestBinaryReprInsufficientWidthParameterForRepresentation(_DeprecationTestCase): + """ + If a 'width' parameter is passed into ``binary_repr`` that is insufficient to + represent the number in base 2 (positive) or 2's complement (negative) form, + the function used to silently ignore the parameter and return a representation + using the minimal number of bits needed for the form in question. Such behavior + is now considered unsafe from a user perspective and will raise an error in the future. + """ + + def test_insufficient_width_positive(self): + args = (10,) + kwargs = {'width': 2} + + self.message = ("Insufficient bit width provided. This behavior " + "will raise an error in the future.") + self.assert_deprecated(np.binary_repr, args=args, kwargs=kwargs) + + def test_insufficient_width_negative(self): + args = (-5,) + kwargs = {'width': 2} + + self.message = ("Insufficient bit width provided. This behavior " + "will raise an error in the future.") + self.assert_deprecated(np.binary_repr, args=args, kwargs=kwargs) + + class TestTestDeprecated(object): def test_assert_deprecated(self): test_case_instance = _DeprecationTestCase() diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index a6c8489ef..14a902b9c 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -716,6 +716,20 @@ class TestCreation(TestCase): assert_raises(ValueError, np.array, C()) # segfault? + def test_failed_len_sequence(self): + # gh-7393 + class A(object): + def __init__(self, data): + self._data = data + def __getitem__(self, item): + return type(self)(self._data[item]) + def __len__(self): + return len(self._data) + + # len(d) should give 3, but len(d[0]) will fail + d = A([1,2,3]) + assert_equal(len(np.array(d)), 3) + class TestStructured(TestCase): def test_subarray_field_access(self): diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index e22a5e193..a43ad96b7 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -1021,12 +1021,25 @@ class TestBinaryRepr(TestCase): def test_zero(self): assert_equal(np.binary_repr(0), '0') - def test_large(self): - assert_equal(np.binary_repr(10736848), '101000111101010011010000') + def test_positive(self): + assert_equal(np.binary_repr(10), '1010') + assert_equal(np.binary_repr(12522), + '11000011101010') + assert_equal(np.binary_repr(10736848), + '101000111101010011010000') def test_negative(self): assert_equal(np.binary_repr(-1), '-1') - assert_equal(np.binary_repr(-1, width=8), '11111111') + assert_equal(np.binary_repr(-10), '-1010') + assert_equal(np.binary_repr(-12522), + '-11000011101010') + assert_equal(np.binary_repr(-10736848), + '-101000111101010011010000') + + def test_sufficient_width(self): + assert_equal(np.binary_repr(0, width=5), '00000') + assert_equal(np.binary_repr(10, width=7), '0001010') + assert_equal(np.binary_repr(-5, width=7), '1111011') class TestBaseRepr(TestCase): diff --git a/numpy/core/tests/test_shape_base.py b/numpy/core/tests/test_shape_base.py index 0d163c1dc..ac8dc1eea 100644 --- a/numpy/core/tests/test_shape_base.py +++ b/numpy/core/tests/test_shape_base.py @@ -120,6 +120,9 @@ class TestAtleast3d(TestCase): class TestHstack(TestCase): + def test_non_iterable(self): + assert_raises(TypeError, hstack, 1) + def test_0D_array(self): a = array(1) b = array(2) @@ -143,6 +146,9 @@ class TestHstack(TestCase): class TestVstack(TestCase): + def test_non_iterable(self): + assert_raises(TypeError, vstack, 1) + def test_0D_array(self): a = array(1) b = array(2) @@ -265,6 +271,9 @@ class TestConcatenate(TestCase): def test_stack(): + # non-iterable input + assert_raises(TypeError, stack, 1) + # 0d input for input_ in [(1, 2, 3), [np.int32(1), np.int32(2), np.int32(3)], diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index eb0985386..ab8cecff0 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -649,6 +649,22 @@ class TestUfunc(TestCase): assert_equal(np.array([[1]], dtype=object).sum(), 1) assert_equal(np.array([[[1, 2]]], dtype=object).sum((0, 1)), [1, 2]) + def test_object_array_accumulate_inplace(self): + # Checks that in-place accumulates work, see also gh-7402 + arr = np.ones(4, dtype=object) + arr[:] = [[1] for i in range(4)] + # Twice reproduced also for tuples: + np.add.accumulate(arr, out=arr) + np.add.accumulate(arr, out=arr) + assert_array_equal(arr, np.array([[1]*i for i in [1, 3, 6, 10]])) + + # And the same if the axis argument is used + arr = np.ones((2, 4), dtype=object) + arr[0, :] = [[2] for i in range(4)] + np.add.accumulate(arr, out=arr, axis=-1) + np.add.accumulate(arr, out=arr, axis=-1) + assert_array_equal(arr[0, :], np.array([[2]*i for i in [1, 3, 6, 10]])) + def test_object_scalar_multiply(self): # Tickets #2469 and #4482 arr = np.matrix([1, 2], dtype=object) diff --git a/numpy/core/tests/test_umath.py b/numpy/core/tests/test_umath.py index da52e0dde..f0f664a6f 100644 --- a/numpy/core/tests/test_umath.py +++ b/numpy/core/tests/test_umath.py @@ -991,6 +991,91 @@ class TestBool(TestCase): assert_equal(np.bitwise_xor(arg1, arg2), out) +class TestBitwiseUFuncs(TestCase): + + bitwise_types = [np.dtype(c) for c in '?' + 'bBhHiIlLqQ' + 'O'] + + def test_values(self): + for dt in self.bitwise_types: + zeros = np.array([0], dtype=dt) + ones = np.array([-1], dtype=dt) + msg = "dt = '%s'" % dt.char + + assert_equal(np.bitwise_not(zeros), ones, err_msg=msg) + assert_equal(np.bitwise_not(ones), zeros, err_msg=msg) + + assert_equal(np.bitwise_or(zeros, zeros), zeros, err_msg=msg) + assert_equal(np.bitwise_or(zeros, ones), ones, err_msg=msg) + assert_equal(np.bitwise_or(ones, zeros), ones, err_msg=msg) + assert_equal(np.bitwise_or(ones, ones), ones, err_msg=msg) + + assert_equal(np.bitwise_xor(zeros, zeros), zeros, err_msg=msg) + assert_equal(np.bitwise_xor(zeros, ones), ones, err_msg=msg) + assert_equal(np.bitwise_xor(ones, zeros), ones, err_msg=msg) + assert_equal(np.bitwise_xor(ones, ones), zeros, err_msg=msg) + + assert_equal(np.bitwise_and(zeros, zeros), zeros, err_msg=msg) + assert_equal(np.bitwise_and(zeros, ones), zeros, err_msg=msg) + assert_equal(np.bitwise_and(ones, zeros), zeros, err_msg=msg) + assert_equal(np.bitwise_and(ones, ones), ones, err_msg=msg) + + def test_types(self): + for dt in self.bitwise_types: + zeros = np.array([0], dtype=dt) + ones = np.array([-1], dtype=dt) + msg = "dt = '%s'" % dt.char + + assert_(np.bitwise_not(zeros).dtype == dt, msg) + assert_(np.bitwise_or(zeros, zeros).dtype == dt, msg) + assert_(np.bitwise_xor(zeros, zeros).dtype == dt, msg) + assert_(np.bitwise_and(zeros, zeros).dtype == dt, msg) + + + def test_identity(self): + assert_(np.bitwise_or.identity == 0, 'bitwise_or') + assert_(np.bitwise_xor.identity == 0, 'bitwise_xor') + assert_(np.bitwise_and.identity == -1, 'bitwise_and') + + def test_reduction(self): + binary_funcs = (np.bitwise_or, np.bitwise_xor, np.bitwise_and) + + for dt in self.bitwise_types: + zeros = np.array([0], dtype=dt) + ones = np.array([-1], dtype=dt) + for f in binary_funcs: + msg = "dt: '%s', f: '%s'" % (dt, f) + assert_equal(f.reduce(zeros), zeros, err_msg=msg) + assert_equal(f.reduce(ones), ones, err_msg=msg) + + # Test empty reduction, no object dtype + for dt in self.bitwise_types[:-1]: + # No object array types + empty = np.array([], dtype=dt) + for f in binary_funcs: + msg = "dt: '%s', f: '%s'" % (dt, f) + tgt = np.array(f.identity, dtype=dt) + res = f.reduce(empty) + assert_equal(res, tgt, err_msg=msg) + assert_(res.dtype == tgt.dtype, msg) + + # Empty object arrays use the identity. Note that the types may + # differ, the actual type used is determined by the assign_identity + # function and is not the same as the type returned by the identity + # method. + for f in binary_funcs: + msg = "dt: '%s'" % (f,) + empty = np.array([], dtype=object) + tgt = f.identity + res = f.reduce(empty) + assert_equal(res, tgt, err_msg=msg) + + # Non-empty object arrays do not use the identity + for f in binary_funcs: + msg = "dt: '%s'" % (f,) + btype = np.array([True], dtype=object) + assert_(type(f.reduce(btype)) is bool, msg) + + class TestInt(TestCase): def test_logical_not(self): x = np.ones(10, dtype=np.int16) |