diff options
-rw-r--r-- | doc/source/reference/ufuncs.rst | 9 | ||||
-rw-r--r-- | numpy/core/src/multiarray/ctors.c | 66 | ||||
-rw-r--r-- | numpy/core/src/multiarray/item_selection.c | 286 | ||||
-rw-r--r-- | numpy/core/src/npysort/heapsort.c.src | 44 | ||||
-rw-r--r-- | numpy/core/src/npysort/mergesort.c.src | 78 | ||||
-rw-r--r-- | numpy/core/src/npysort/npysort_common.h | 7 | ||||
-rw-r--r-- | numpy/core/src/npysort/quicksort.c.src | 89 | ||||
-rw-r--r-- | numpy/core/src/npysort/selection.c.src | 48 | ||||
-rw-r--r-- | numpy/core/src/private/npy_partition.h.src | 16 | ||||
-rw-r--r-- | numpy/core/src/private/npy_sort.h | 7 | ||||
-rw-r--r-- | numpy/core/src/private/ufunc_override.h | 9 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_object.c | 39 | ||||
-rw-r--r-- | numpy/core/src/umath/umathmodule.c | 7 | ||||
-rw-r--r-- | numpy/core/tests/test_multiarray.py | 17 | ||||
-rw-r--r-- | numpy/core/tests/test_regression.py | 8 | ||||
-rw-r--r-- | numpy/core/tests/test_ufunc.py | 26 | ||||
-rw-r--r-- | numpy/lib/shape_base.py | 7 | ||||
-rw-r--r-- | numpy/lib/stride_tricks.py | 6 | ||||
-rw-r--r-- | numpy/lib/tests/test_shape_base.py | 6 | ||||
-rw-r--r-- | numpy/lib/tests/test_stride_tricks.py | 23 |
20 files changed, 399 insertions, 399 deletions
diff --git a/doc/source/reference/ufuncs.rst b/doc/source/reference/ufuncs.rst index f7142bc47..a97534612 100644 --- a/doc/source/reference/ufuncs.rst +++ b/doc/source/reference/ufuncs.rst @@ -350,7 +350,8 @@ advanced usage and will not typically be used. .. versionadded:: 1.6 - Overrides the dtype of the calculation and output arrays. Similar to *sig*. + Overrides the dtype of the calculation and output arrays. Similar to + *signature*. *subok* @@ -359,7 +360,7 @@ advanced usage and will not typically be used. Defaults to true. If set to false, the output will always be a strict array, not a subtype. -*sig* +*signature* Either a data-type, a tuple of data-types, or a special signature string indicating the input and output types of a ufunc. This argument @@ -370,7 +371,9 @@ advanced usage and will not typically be used. available and searching for a loop with data-types to which all inputs can be cast safely. This keyword argument lets you bypass that search and choose a particular loop. A list of available signatures is - provided by the **types** attribute of the ufunc object. + provided by the **types** attribute of the ufunc object. For backwards + compatibility this argument can also be provided as *sig*, although + the long form is preferred. *extobj* diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index 010420826..e88e17028 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -2069,6 +2069,51 @@ PyArray_FromStructInterface(PyObject *input) return NULL; } +/* + * Checks if the object in descr is the default 'descr' member for the + * __array_interface__ dictionary with 'typestr' member typestr. + */ +NPY_NO_EXPORT int +_is_default_descr(PyObject *descr, PyObject *typestr) { + PyObject *tuple, *name, *typestr2; +#if defined(NPY_PY3K) + PyObject *tmp = NULL; +#endif + int ret = 0; + + if (!PyList_Check(descr) || PyList_GET_SIZE(descr) != 1) { + return 0; + } + tuple = PyList_GET_ITEM(descr, 0); + if (!(PyTuple_Check(tuple) && PyTuple_GET_SIZE(tuple) == 2)) { + return 0; + } + name = PyTuple_GET_ITEM(tuple, 0); + if (!(PyUString_Check(name) && PyUString_GET_SIZE(name) == 0)) { + return 0; + } + typestr2 = PyTuple_GET_ITEM(tuple, 1); +#if defined(NPY_PY3K) + /* Allow unicode type strings */ + if (PyUnicode_Check(typestr2)) { + tmp = PyUnicode_AsASCIIString(typestr2); + if (tmp == NULL) { + return 0; + } + typestr2 = tmp; + } +#endif + if (PyBytes_Check(typestr2) && + PyObject_RichCompareBool(typestr, typestr2, Py_EQ)) { + ret = 1; + } +#if defined(NPY_PY3K) + Py_XDECREF(tmp); +#endif + + return ret; +} + #define PyIntOrLong_Check(obj) (PyInt_Check(obj) || PyLong_Check(obj)) /*NUMPY_API*/ @@ -2087,11 +2132,6 @@ PyArray_FromInterface(PyObject *origin) npy_intp dims[NPY_MAXDIMS], strides[NPY_MAXDIMS]; int dataflags = NPY_ARRAY_BEHAVED; - /* Get the typestring -- ignore array_descr */ - /* Get the shape */ - /* Get the memory from __array_data__ and __array_offset__ */ - /* Get the strides */ - iface = PyArray_GetAttrString_SuppressException(origin, "__array_interface__"); if (iface == NULL) { @@ -2135,6 +2175,22 @@ PyArray_FromInterface(PyObject *origin) goto fail; } + /* + * If the dtype is NPY_VOID, see if there is extra information in + * the 'descr' attribute. + */ + if (dtype->type_num == NPY_VOID) { + PyObject *descr = PyDict_GetItemString(iface, "descr"); + PyArray_Descr *new_dtype = NULL; + + if (descr != NULL && !_is_default_descr(descr, attr) && + PyArray_DescrConverter2(descr, &new_dtype) == NPY_SUCCEED && + new_dtype != NULL) { + Py_DECREF(dtype); + dtype = new_dtype; + } + } + /* Get shape tuple from interface specification */ attr = PyDict_GetItemString(iface, "shape"); if (attr == NULL) { diff --git a/numpy/core/src/multiarray/item_selection.c b/numpy/core/src/multiarray/item_selection.c index e2ef8efdd..0d9474be3 100644 --- a/numpy/core/src/multiarray/item_selection.c +++ b/numpy/core/src/multiarray/item_selection.c @@ -1093,87 +1093,20 @@ fail: } -/* Be sure to save this global_compare when necessary */ -static PyArrayObject *global_obj; - -static int -sortCompare (const void *a, const void *b) -{ - return PyArray_DESCR(global_obj)->f->compare(a,b,global_obj); -} - -/* - * Consumes reference to ap (op gets it) op contains a version of - * the array with axes swapped if local variable axis is not the - * last dimension. Origin must be defined locally. - */ -#define SWAPAXES(op, ap) { \ - orign = PyArray_NDIM(ap)-1; \ - if (axis != orign) { \ - (op) = (PyArrayObject *)PyArray_SwapAxes((ap), axis, orign); \ - Py_DECREF((ap)); \ - if ((op) == NULL) return NULL; \ - } \ - else (op) = (ap); \ - } - -/* - * Consumes reference to ap (op gets it) origin must be previously - * defined locally. SWAPAXES must have been called previously. - * op contains the swapped version of the array. - */ -#define SWAPBACK(op, ap) { \ - if (axis != orign) { \ - (op) = (PyArrayObject *)PyArray_SwapAxes((ap), axis, orign); \ - Py_DECREF((ap)); \ - if ((op) == NULL) return NULL; \ - } \ - else (op) = (ap); \ - } - -/* These swap axes in-place if necessary */ -#define SWAPINTP(a,b) {npy_intp c; c=(a); (a) = (b); (b) = c;} -#define SWAPAXES2(ap) { \ - orign = PyArray_NDIM(ap)-1; \ - if (axis != orign) { \ - SWAPINTP(PyArray_DIMS(ap)[axis], PyArray_DIMS(ap)[orign]); \ - SWAPINTP(PyArray_STRIDES(ap)[axis], PyArray_STRIDES(ap)[orign]); \ - PyArray_UpdateFlags(ap, NPY_ARRAY_C_CONTIGUOUS | \ - NPY_ARRAY_F_CONTIGUOUS); \ - } \ - } - -#define SWAPBACK2(ap) { \ - if (axis != orign) { \ - SWAPINTP(PyArray_DIMS(ap)[axis], PyArray_DIMS(ap)[orign]); \ - SWAPINTP(PyArray_STRIDES(ap)[axis], PyArray_STRIDES(ap)[orign]); \ - PyArray_UpdateFlags(ap, NPY_ARRAY_C_CONTIGUOUS | \ - NPY_ARRAY_F_CONTIGUOUS); \ - } \ - } - /*NUMPY_API * Sort an array in-place */ NPY_NO_EXPORT int PyArray_Sort(PyArrayObject *op, int axis, NPY_SORTKIND which) { - PyArrayObject *ap = NULL, *store_arr = NULL; - char *ip; - npy_intp i, n, m; - int elsize, orign; - int res = 0; + PyArray_SortFunc *sort; int axis_orig = axis; - int (*sort)(void *, size_t, size_t, npy_comparator); + int n = PyArray_NDIM(op); - n = PyArray_NDIM(op); - if ((n == 0) || (PyArray_SIZE(op) == 1)) { - return 0; - } if (axis < 0) { axis += n; } - if ((axis < 0) || (axis >= n)) { + if (axis < 0 || axis >= n) { PyErr_Format(PyExc_ValueError, "axis(=%d) out of bounds", axis_orig); return -1; } @@ -1181,83 +1114,35 @@ PyArray_Sort(PyArrayObject *op, int axis, NPY_SORTKIND which) return -1; } - /* Determine if we should use type-specific algorithm or not */ - if (PyArray_DESCR(op)->f->sort[which] != NULL) { - return _new_sortlike(op, axis, PyArray_DESCR(op)->f->sort[which], - NULL, NULL, 0); - } - - if (PyArray_DESCR(op)->f->compare == NULL) { - PyErr_SetString(PyExc_TypeError, - "type does not have compare function"); + if (which < 0 || which >= NPY_NSORTS) { + PyErr_SetString(PyExc_ValueError, "not a valid sort kind"); return -1; } - SWAPAXES2(op); - - switch (which) { - case NPY_QUICKSORT : - sort = npy_quicksort; - break; - case NPY_HEAPSORT : - sort = npy_heapsort; - break; - case NPY_MERGESORT : - sort = npy_mergesort; - break; - default: + sort = PyArray_DESCR(op)->f->sort[which]; + if (sort == NULL) { + if (PyArray_DESCR(op)->f->compare) { + switch (which) { + default: + case NPY_QUICKSORT: + sort = npy_quicksort; + break; + case NPY_HEAPSORT: + sort = npy_heapsort; + break; + case NPY_MERGESORT: + sort = npy_mergesort; + break; + } + } + else { PyErr_SetString(PyExc_TypeError, - "requested sort kind is not supported"); - goto fail; - } - - ap = (PyArrayObject *)PyArray_FromAny((PyObject *)op, - NULL, 1, 0, - NPY_ARRAY_DEFAULT | NPY_ARRAY_UPDATEIFCOPY, NULL); - if (ap == NULL) { - goto fail; - } - elsize = PyArray_DESCR(ap)->elsize; - m = PyArray_DIMS(ap)[PyArray_NDIM(ap)-1]; - if (m == 0) { - goto finish; - } - n = PyArray_SIZE(ap)/m; - - /* Store global -- allows re-entry -- restore before leaving*/ - store_arr = global_obj; - global_obj = ap; - for (ip = PyArray_DATA(ap), i = 0; i < n; i++, ip += elsize*m) { - res = sort(ip, m, elsize, sortCompare); - if (res < 0) { - break; + "type does not have compare function"); + return -1; } } - global_obj = store_arr; - if (PyErr_Occurred()) { - goto fail; - } - else if (res == -NPY_ENOMEM) { - PyErr_NoMemory(); - goto fail; - } - else if (res == -NPY_ECOMP) { - PyErr_SetString(PyExc_TypeError, - "sort comparison failed"); - goto fail; - } - - - finish: - Py_DECREF(ap); /* Should update op if needed */ - SWAPBACK2(op); - return 0; - - fail: - Py_XDECREF(ap); - SWAPBACK2(op); - return -1; + return _new_sortlike(op, axis, sort, NULL, NULL, 0); } @@ -1297,7 +1182,8 @@ partition_prep_kth_array(PyArrayObject * ktharray, if (kth[i] < 0) { kth[i] += shape[axis]; } - if (PyArray_SIZE(op) != 0 && ((kth[i] < 0) || (kth[i] >= shape[axis]))) { + if (PyArray_SIZE(op) != 0 && + (kth[i] < 0 || kth[i] >= shape[axis])) { PyErr_Format(PyExc_ValueError, "kth(=%zd) out of bounds (%zd)", kth[i], shape[axis]); Py_XDECREF(kthrvl); @@ -1309,124 +1195,68 @@ partition_prep_kth_array(PyArrayObject * ktharray, * sort the array of kths so the partitions will * not trample on each other */ - PyArray_Sort(kthrvl, -1, NPY_QUICKSORT); + if (PyArray_SIZE(kthrvl) > 1) { + PyArray_Sort(kthrvl, -1, NPY_QUICKSORT); + } return kthrvl; } - /*NUMPY_API * Partition an array in-place */ NPY_NO_EXPORT int -PyArray_Partition(PyArrayObject *op, PyArrayObject * ktharray, int axis, NPY_SELECTKIND which) +PyArray_Partition(PyArrayObject *op, PyArrayObject * ktharray, int axis, + NPY_SELECTKIND which) { - PyArrayObject *ap = NULL, *store_arr = NULL; - char *ip; - npy_intp i, n, m; - int elsize, orign; - int res = 0; + PyArrayObject *kthrvl; + PyArray_PartitionFunc *part; + PyArray_SortFunc *sort; int axis_orig = axis; - int (*sort)(void *, size_t, size_t, npy_comparator); - PyArray_PartitionFunc * part = get_partition_func(PyArray_TYPE(op), which); + int n = PyArray_NDIM(op); + int ret; - n = PyArray_NDIM(op); - if (n == 0) { - return 0; - } if (axis < 0) { axis += n; } - if ((axis < 0) || (axis >= n)) { + if (axis < 0 || axis >= n) { PyErr_Format(PyExc_ValueError, "axis(=%d) out of bounds", axis_orig); return -1; } - if (PyArray_FailUnlessWriteable(op, "sort array") < 0) { + if (PyArray_FailUnlessWriteable(op, "partition array") < 0) { return -1; } - if (part) { - PyArrayObject * kthrvl = partition_prep_kth_array(ktharray, op, axis); - if (kthrvl == NULL) - return -1; - - res = _new_sortlike(op, axis, NULL, - part, - PyArray_DATA(kthrvl), - PyArray_SIZE(kthrvl)); - Py_DECREF(kthrvl); - return res; - } - - - if (PyArray_DESCR(op)->f->compare == NULL) { - PyErr_SetString(PyExc_TypeError, - "type does not have compare function"); - return -1; + if (which < 0 || which >= NPY_NSELECTS) { + PyErr_SetString(PyExc_ValueError, "not a valid partition kind"); + return NULL; } - - SWAPAXES2(op); - - /* select not implemented, use quicksort, slower but equivalent */ - switch (which) { - case NPY_INTROSELECT : + part = get_partition_func(PyArray_TYPE(op), which); + if (part == NULL) { + /* Use sorting, slower but equivalent */ + if (PyArray_DESCR(op)->f->compare) { sort = npy_quicksort; - break; - default: + } + else { PyErr_SetString(PyExc_TypeError, - "requested sort kind is not supported"); - goto fail; - } - - ap = (PyArrayObject *)PyArray_FromAny((PyObject *)op, - NULL, 1, 0, - NPY_ARRAY_DEFAULT | NPY_ARRAY_UPDATEIFCOPY, NULL); - if (ap == NULL) { - goto fail; - } - elsize = PyArray_DESCR(ap)->elsize; - m = PyArray_DIMS(ap)[PyArray_NDIM(ap)-1]; - if (m == 0) { - goto finish; - } - n = PyArray_SIZE(ap)/m; - - /* Store global -- allows re-entry -- restore before leaving*/ - store_arr = global_obj; - global_obj = ap; - /* we don't need to care about kth here as we are using a full sort */ - for (ip = PyArray_DATA(ap), i = 0; i < n; i++, ip += elsize*m) { - res = sort(ip, m, elsize, sortCompare); - if (res < 0) { - break; + "type does not have compare function"); + return NULL; } } - global_obj = store_arr; - if (PyErr_Occurred()) { - goto fail; - } - else if (res == -NPY_ENOMEM) { - PyErr_NoMemory(); - goto fail; - } - else if (res == -NPY_ECOMP) { - PyErr_SetString(PyExc_TypeError, - "sort comparison failed"); - goto fail; + /* Process ktharray even if using sorting to do bounds checking */ + kthrvl = partition_prep_kth_array(ktharray, op, axis); + if (kthrvl == NULL) { + return -1; } + ret = _new_sortlike(op, axis, sort, part, + PyArray_DATA(kthrvl), PyArray_SIZE(kthrvl)); - finish: - Py_DECREF(ap); /* Should update op if needed */ - SWAPBACK2(op); - return 0; + Py_DECREF(kthrvl); - fail: - Py_XDECREF(ap); - SWAPBACK2(op); - return -1; + return ret; } diff --git a/numpy/core/src/npysort/heapsort.c.src b/numpy/core/src/npysort/heapsort.c.src index bec8cc032..88f7978cc 100644 --- a/numpy/core/src/npysort/heapsort.c.src +++ b/numpy/core/src/npysort/heapsort.c.src @@ -287,30 +287,27 @@ aheapsort_@suff@(@type@ *v, npy_intp *tosort, npy_intp n, PyArrayObject *arr) */ -/* - * This sort has almost the same signature as libc qsort and is intended to - * provide a heapsort for array types that don't have type specific sorts. - * The difference in the signature is an error return, as it might be the - * case that a memory allocation fails. - */ int -npy_heapsort(void *base, size_t num, size_t size, npy_comparator cmp) +npy_heapsort(char *start, npy_intp num, PyArrayObject *arr) { - char *tmp = malloc(size); - char *a = (char *) base - size; - size_t i, j, l; + npy_intp elsize = PyArray_ITEMSIZE(arr); + PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare; + char *tmp = malloc(elsize); + char *a = start - elsize; + npy_intp i, j, l; if (tmp == NULL) { return -NPY_ENOMEM; } for (l = num >> 1; l > 0; --l) { - GENERIC_COPY(tmp, a + l*size, size); + GENERIC_COPY(tmp, a + l*elsize, elsize); for (i = l, j = l << 1; j <= num;) { - if (j < num && GENERIC_LT(a + j*size, a + (j+1)*size, cmp)) - j += 1; - if (GENERIC_LT(tmp, a + j*size, cmp)) { - GENERIC_COPY(a + i*size, a + j*size, size); + if (j < num && cmp(a + j*elsize, a + (j+1)*elsize, arr) < 0) { + ++j; + } + if (cmp(tmp, a + j*elsize, arr) < 0) { + GENERIC_COPY(a + i*elsize, a + j*elsize, elsize); i = j; j += j; } @@ -318,18 +315,19 @@ npy_heapsort(void *base, size_t num, size_t size, npy_comparator cmp) break; } } - GENERIC_COPY(a + i*size, tmp, size); + GENERIC_COPY(a + i*elsize, tmp, elsize); } for (; num > 1;) { - GENERIC_COPY(tmp, a + num*size, size); - GENERIC_COPY(a + num*size, a + size, size); + GENERIC_COPY(tmp, a + num*elsize, elsize); + GENERIC_COPY(a + num*elsize, a + elsize, elsize); num -= 1; for (i = 1, j = 2; j <= num;) { - if (j < num && GENERIC_LT(a + j*size, a + (j+1)*size, cmp)) - j++; - if (GENERIC_LT(tmp, a + j*size, cmp)) { - GENERIC_COPY(a + i*size, a + j*size, size); + if (j < num && cmp(a + j*elsize, a + (j+1)*elsize, arr) < 0) { + ++j; + } + if (cmp(tmp, a + j*elsize, arr) < 0) { + GENERIC_COPY(a + i*elsize, a + j*elsize, elsize); i = j; j += j; } @@ -337,7 +335,7 @@ npy_heapsort(void *base, size_t num, size_t size, npy_comparator cmp) break; } } - GENERIC_COPY(a + i*size, tmp, size); + GENERIC_COPY(a + i*elsize, tmp, elsize); } free(tmp); diff --git a/numpy/core/src/npysort/mergesort.c.src b/numpy/core/src/npysort/mergesort.c.src index ecf39720a..406be66d0 100644 --- a/numpy/core/src/npysort/mergesort.c.src +++ b/numpy/core/src/npysort/mergesort.c.src @@ -350,80 +350,70 @@ amergesort_@suff@(@type@ *v, npy_intp *tosort, npy_intp num, PyArrayObject *arr) static void -npy_mergesort0(char *pl, char *pr, char *pw, char *vp, size_t size, npy_comparator cmp) +npy_mergesort0(char *pl, char *pr, char *pw, char *vp, npy_intp elsize, + PyArray_CompareFunc *cmp, PyArrayObject *arr) { char *pi, *pj, *pk, *pm; - if ((size_t)(pr - pl) > SMALL_MERGESORT*size) { + if (pr - pl > SMALL_MERGESORT*elsize) { /* merge sort */ - pm = pl + (((pr - pl)/size) >> 1)*size; - npy_mergesort0(pl, pm, pw, vp, size, cmp); - npy_mergesort0(pm, pr, pw, vp, size, cmp); + pm = pl + (((pr - pl)/elsize) >> 1)*elsize; + npy_mergesort0(pl, pm, pw, vp, elsize, cmp, arr); + npy_mergesort0(pm, pr, pw, vp, elsize, cmp, arr); GENERIC_COPY(pw, pl, pm - pl); pi = pw + (pm - pl); pj = pw; pk = pl; while (pj < pi && pm < pr) { - if (GENERIC_LT(pm, pj, cmp)) { - GENERIC_COPY(pk, pm, size); - pm += size; - pk += size; + if (cmp(pm, pj, arr) < 0) { + GENERIC_COPY(pk, pm, elsize); + pm += elsize; + pk += elsize; } else { - GENERIC_COPY(pk, pj, size); - pj += size; - pk += size; + GENERIC_COPY(pk, pj, elsize); + pj += elsize; + pk += elsize; } } GENERIC_COPY(pk, pj, pi - pj); } else { /* insertion sort */ - for (pi = pl + size; pi < pr; pi += size) { - GENERIC_COPY(vp, pi, size); + for (pi = pl + elsize; pi < pr; pi += elsize) { + GENERIC_COPY(vp, pi, elsize); pj = pi; - pk = pi - size; - while (pj > pl && GENERIC_LT(vp, pk, cmp)) { - GENERIC_COPY(pj, pk, size); - pj -= size; - pk -= size; + pk = pi - elsize; + while (pj > pl && cmp(vp, pk, arr) < 0) { + GENERIC_COPY(pj, pk, elsize); + pj -= elsize; + pk -= elsize; } - GENERIC_COPY(pj, vp, size); + GENERIC_COPY(pj, vp, elsize); } } } -/* - * This sort has almost the same signature as libc qsort and is intended to - * provide a mergesort for array types that don't have type specific sorts. - * The difference in the signature is an error return, as it might be the - * case that a memory allocation fails. - */ int -npy_mergesort(void *base, size_t num, size_t size, npy_comparator cmp) +npy_mergesort(char *start, npy_intp num, PyArrayObject *arr) { - char *pl, *pr, *pw, *vp; - int err = 0; - - pl = base; - pr = pl + num*size; - pw = malloc((num/2) * size); - if (pw == NULL) { - err = -NPY_ENOMEM; - goto fail_0; - } - vp = malloc(size); - if (vp == NULL) { - err = -NPY_ENOMEM; - goto fail_1; + npy_intp elsize = PyArray_ITEMSIZE(arr); + PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare; + char *pl = start; + char *pr = pl + num*elsize; + char *pw = malloc((num >> 1) *elsize); + char *vp = malloc(elsize); + int err = -NPY_ENOMEM; + + if (pw != NULL && vp != NULL) { + npy_mergesort0(pl, pr, pw, vp, elsize, cmp, arr); + err = 0; } - npy_mergesort0(pl, pr, pw, vp, size, cmp); free(vp); -fail_1: free(pw); -fail_0: + return err; } diff --git a/numpy/core/src/npysort/npysort_common.h b/numpy/core/src/npysort/npysort_common.h index a3ce7664d..a22045b41 100644 --- a/numpy/core/src/npysort/npysort_common.h +++ b/numpy/core/src/npysort/npysort_common.h @@ -357,11 +357,4 @@ GENERIC_SWAP(char *a, char *b, size_t len) } } - -NPY_INLINE static int -GENERIC_LT(char *a, char *b, int (*cmp)(const void *, const void *)) -{ - return cmp(a, b) < 0; -} - #endif diff --git a/numpy/core/src/npysort/quicksort.c.src b/numpy/core/src/npysort/quicksort.c.src index fcde70957..5334aca76 100644 --- a/numpy/core/src/npysort/quicksort.c.src +++ b/numpy/core/src/npysort/quicksort.c.src @@ -216,6 +216,10 @@ quicksort_@suff@(@type@ *start, npy_intp num, PyArrayObject *arr) @type@ *pr = start + (num - 1)*len; @type@ *stack[PYA_QS_STACK], **sptr = stack, *pm, *pi, *pj, *pk; + if (vp == NULL) { + return -NPY_ENOMEM; + } + for (;;) { while ((size_t)(pr - pl) > SMALL_QUICKSORT*len) { /* quicksort partition */ @@ -350,15 +354,86 @@ aquicksort_@suff@(@type@ *v, npy_intp* tosort, npy_intp num, PyArrayObject *arr) */ -/* - * This sort has almost the same signature as libc qsort and is intended to - * supply an error return for compatibility with the other generic sort - * kinds. - */ int -npy_quicksort(void *base, size_t num, size_t size, npy_comparator cmp) +npy_quicksort(char *start, npy_intp num, PyArrayObject *arr) { - qsort(base, num, size, cmp); + npy_intp elsize = PyArray_ITEMSIZE(arr); + PyArray_CompareFunc *cmp = PyArray_DESCR(arr)->f->compare; + char *vp = malloc(elsize); + char *pl = start; + char *pr = start + (num - 1)*elsize; + char *stack[PYA_QS_STACK]; + char **sptr = stack; + char *pm, *pi, *pj, *pk; + + if (vp == NULL) { + return -NPY_ENOMEM; + } + + for (;;) { + while(pr - pl > SMALL_QUICKSORT*elsize) { + /* quicksort partition */ + pm = pl + (((pr - pl) / elsize) >> 1) * elsize; + if (cmp(pm, pl, arr) < 0) { + GENERIC_SWAP(pm, pl, elsize); + } + if (cmp(pr, pm, arr) < 0) { + GENERIC_SWAP(pr, pm, elsize); + } + if (cmp(pm, pl, arr) < 0) { + GENERIC_SWAP(pm, pl, elsize); + } + GENERIC_COPY(vp, pm, elsize); + pi = pl; + pj = pr - elsize; + GENERIC_SWAP(pm, pj, elsize); + for (;;) { + do { + pi += elsize; + } while (cmp(pi, vp, arr) < 0); + do { + pj -= elsize; + } while (cmp(vp, pj, arr) < 0); + if (pi >= pj) { + break; + } + GENERIC_SWAP(pi, pj, elsize); + } + pk = pr - elsize; + GENERIC_SWAP(pi, pk, elsize); + /* push largest partition on stack */ + if (pi - pl < pr - pi) { + *sptr++ = pi + elsize; + *sptr++ = pr; + pr = pi - elsize; + } + else { + *sptr++ = pl; + *sptr++ = pi - elsize; + pl = pi + elsize; + } + } + + /* insertion sort */ + for (pi = pl + elsize; pi <= pr; pi += elsize) { + GENERIC_COPY(vp, pi, elsize); + pj = pi; + pk = pi - elsize; + while (pj > pl && cmp(vp, pk, arr) < 0) { + GENERIC_COPY(pj, pk, elsize); + pj -= elsize; + pk -= elsize; + } + GENERIC_COPY(pj, vp, elsize); + } + if (sptr == stack) { + break; + } + pr = *(--sptr); + pl = *(--sptr); + } + + free(vp); return 0; } diff --git a/numpy/core/src/npysort/selection.c.src b/numpy/core/src/npysort/selection.c.src index 4167b2694..51c7d6721 100644 --- a/numpy/core/src/npysort/selection.c.src +++ b/numpy/core/src/npysort/selection.c.src @@ -424,51 +424,3 @@ int /**end repeat1**/ /**end repeat**/ - - -/* - ***************************************************************************** - ** STRING SORTS ** - ***************************************************************************** - */ - - -/**begin repeat - * - * #TYPE = STRING, UNICODE# - * #suff = string, unicode# - * #type = npy_char, npy_ucs4# - */ - -int -introselect_@suff@(@type@ *start, npy_intp num, npy_intp kth, PyArrayObject *arr) -{ - return quicksort_@suff@(start, num, arr); -} - -int -aintroselect_@suff@(@type@ *v, npy_intp* tosort, npy_intp num, npy_intp kth, void *null) -{ - return aquicksort_@suff@(v, tosort, num, null); -} - -/**end repeat**/ - - -/* - ***************************************************************************** - ** GENERIC SORT ** - ***************************************************************************** - */ - - -/* - * This sort has almost the same signature as libc qsort and is intended to - * supply an error return for compatibility with the other generic sort - * kinds. - */ -int -npy_introselect(void *base, size_t num, size_t size, size_t kth, npy_comparator cmp) -{ - return npy_quicksort(base, num, size, cmp); -} diff --git a/numpy/core/src/private/npy_partition.h.src b/numpy/core/src/private/npy_partition.h.src index fd79068f7..07aecd4f8 100644 --- a/numpy/core/src/private/npy_partition.h.src +++ b/numpy/core/src/private/npy_partition.h.src @@ -55,22 +55,6 @@ NPY_VISIBILITY_HIDDEN int aintroselect_@suff@(@type@ *v, npy_intp* tosort, npy_i /**end repeat**/ -NPY_VISIBILITY_HIDDEN int introselect_string(npy_char *vec, npy_intp cnt, - npy_intp kth, PyArrayObject *arr); -NPY_VISIBILITY_HIDDEN int aintroselect_string(npy_char *vec, npy_intp *ind, - npy_intp cnt, npy_intp kth, - void *null); - - -NPY_VISIBILITY_HIDDEN int introselect_unicode(npy_ucs4 *vec, npy_intp cnt, - npy_intp kth, PyArrayObject *arr); -NPY_VISIBILITY_HIDDEN int aintroselect_unicode(npy_ucs4 *vec, npy_intp *ind, - npy_intp cnt, npy_intp kth, - void *null); - -NPY_VISIBILITY_HIDDEN int npy_introselect(void *base, size_t num, size_t size, - size_t kth, npy_comparator cmp); - typedef struct { enum NPY_TYPES typenum; PyArray_PartitionFunc * part[NPY_NSELECTS]; diff --git a/numpy/core/src/private/npy_sort.h b/numpy/core/src/private/npy_sort.h index 7c5701f88..85630b2df 100644 --- a/numpy/core/src/private/npy_sort.h +++ b/numpy/core/src/private/npy_sort.h @@ -9,7 +9,6 @@ #define NPY_ENOMEM 1 #define NPY_ECOMP 2 -typedef int (*npy_comparator)(const void *, const void *); int quicksort_bool(npy_bool *vec, npy_intp cnt, void *null); int heapsort_bool(npy_bool *vec, npy_intp cnt, void *null); @@ -187,9 +186,9 @@ int aheapsort_timedelta(npy_timedelta *vec, npy_intp *ind, npy_intp cnt, void *n int amergesort_timedelta(npy_timedelta *vec, npy_intp *ind, npy_intp cnt, void *null); -int npy_quicksort(void *base, size_t num, size_t size, npy_comparator cmp); -int npy_heapsort(void *base, size_t num, size_t size, npy_comparator cmp); -int npy_mergesort(void *base, size_t num, size_t size, npy_comparator cmp); +int npy_quicksort(char *vec, npy_intp cnt, PyArrayObject *arr); +int npy_heapsort(char *vec, npy_intp cnt, PyArrayObject *arr); +int npy_mergesort(char *vec, npy_intp cnt, PyArrayObject *arr); int npy_aquicksort(char *vec, npy_intp *ind, npy_intp cnt, PyArrayObject *arr); int npy_aheapsort(char *vec, npy_intp *ind, npy_intp cnt, PyArrayObject *arr); int npy_amergesort(char *vec, npy_intp *ind, npy_intp cnt, PyArrayObject *arr); diff --git a/numpy/core/src/private/ufunc_override.h b/numpy/core/src/private/ufunc_override.h index c47c46a66..c3f9f601e 100644 --- a/numpy/core/src/private/ufunc_override.h +++ b/numpy/core/src/private/ufunc_override.h @@ -13,7 +13,14 @@ normalize___call___args(PyUFuncObject *ufunc, PyObject *args, { /* ufunc.__call__(*args, **kwds) */ int nargs = PyTuple_GET_SIZE(args); - PyObject *obj; + PyObject *obj = PyDict_GetItemString(*normal_kwds, "sig"); + + /* ufuncs accept 'sig' or 'signature' normalize to 'signature' */ + if (obj != NULL) { + Py_INCREF(obj); + PyDict_SetItemString(*normal_kwds, "signature", obj); + PyDict_DelItemString(*normal_kwds, "sig"); + } *normal_args = PyTuple_GetSlice(args, 0, nin); diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index 5e4a49553..36201b3ea 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -808,6 +808,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc, int type_num; int any_flexible = 0, any_object = 0, any_flexible_userloops = 0; + int has_sig = 0; ufunc_name = ufunc->name ? ufunc->name : "<unnamed ufunc>"; @@ -945,7 +946,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc, switch (str[0]) { case 'c': /* Provides a policy for allowed casting */ - if (strncmp(str, "casting", 7) == 0) { + if (strcmp(str, "casting") == 0) { if (!PyArray_CastingConverter(value, out_casting)) { goto fail; } @@ -954,7 +955,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc, break; case 'd': /* Another way to specify 'sig' */ - if (strncmp(str, "dtype", 5) == 0) { + if (strcmp(str, "dtype") == 0) { /* Allow this parameter to be None */ PyArray_Descr *dtype; if (!PyArray_DescrConverter2(value, &dtype)) { @@ -976,7 +977,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc, * Overrides the global parameters buffer size, * error mask, and error object */ - if (strncmp(str, "extobj", 6) == 0) { + if (strcmp(str, "extobj") == 0) { *out_extobj = value; bad_arg = 0; } @@ -987,7 +988,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc, * either as a single array or None for single output * ufuncs, or as a tuple of arrays and Nones. */ - if (strncmp(str, "out", 3) == 0) { + if (strcmp(str, "out") == 0) { if (nargs > nin) { PyErr_SetString(PyExc_ValueError, "cannot specify 'out' as both a " @@ -1048,7 +1049,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc, bad_arg = 0; } /* Allows the default output layout to be overridden */ - else if (strncmp(str,"order",5) == 0) { + else if (strcmp(str, "order") == 0) { if (!PyArray_OrderConverter(value, out_order)) { goto fail; } @@ -1057,7 +1058,13 @@ get_ufunc_arguments(PyUFuncObject *ufunc, break; case 's': /* Allows a specific function inner loop to be selected */ - if (strncmp(str,"sig",3) == 0) { + if (strcmp(str, "sig") == 0 || + strcmp(str, "signature") == 0) { + if (has_sig == 1) { + PyErr_SetString(PyExc_ValueError, + "cannot specify both 'sig' and 'signature'"); + goto fail; + } if (*out_typetup != NULL) { PyErr_SetString(PyExc_RuntimeError, "cannot specify both 'sig' and 'dtype'"); @@ -1066,8 +1073,9 @@ get_ufunc_arguments(PyUFuncObject *ufunc, *out_typetup = value; Py_INCREF(value); bad_arg = 0; + has_sig = 1; } - else if (strncmp(str,"subok",5) == 0) { + else if (strcmp(str, "subok") == 0) { if (!PyBool_Check(value)) { PyErr_SetString(PyExc_TypeError, "'subok' must be a boolean"); @@ -1082,8 +1090,7 @@ get_ufunc_arguments(PyUFuncObject *ufunc, * Provides a boolean array 'where=' mask if * out_wheremask is supplied. */ - if (out_wheremask != NULL && - strncmp(str,"where",5) == 0) { + if (out_wheremask != NULL && strcmp(str, "where") == 0) { PyArray_Descr *dtype; dtype = PyArray_DescrFromType(NPY_BOOL); if (dtype == NULL) { @@ -4450,6 +4457,14 @@ PyUFunc_FromFuncAndDataAndSignature(PyUFuncGenericFunction *func, void **data, { PyUFuncObject *ufunc; + if (nin + nout > NPY_MAXARGS) { + PyErr_Format(PyExc_ValueError, + "Cannot construct a ufunc with more than %d operands " + "(requested number were: inputs = %d and outputs = %d)", + NPY_MAXARGS, nin, nout); + return NULL; + } + ufunc = PyArray_malloc(sizeof(PyUFuncObject)); if (ufunc == NULL) { return NULL; @@ -5094,6 +5109,12 @@ ufunc_at(PyUFuncObject *ufunc, PyObject *args) return NULL; } + if (ufunc->nout != 1) { + PyErr_SetString(PyExc_ValueError, + "Only single output ufuncs supported at this time"); + return NULL; + } + if (!PyArg_ParseTuple(args, "OO|O", &op1, &idx, &op2)) { return NULL; } diff --git a/numpy/core/src/umath/umathmodule.c b/numpy/core/src/umath/umathmodule.c index d792e8b24..624588410 100644 --- a/numpy/core/src/umath/umathmodule.c +++ b/numpy/core/src/umath/umathmodule.c @@ -103,6 +103,13 @@ ufunc_frompyfunc(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *NPY_UNUS PyErr_SetString(PyExc_TypeError, "function must be callable"); return NULL; } + if (nin + nout > NPY_MAXARGS) { + PyErr_Format(PyExc_ValueError, + "Cannot construct a ufunc with more than %d operands " + "(requested number were: inputs = %d and outputs = %d)", + NPY_MAXARGS, nin, nout); + return NULL; + } self = PyArray_malloc(sizeof(PyUFuncObject)); if (self == NULL) { return NULL; diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 0c13cff6a..f6ed6dd84 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -1436,7 +1436,6 @@ class TestMethods(TestCase): assert_raises(ValueError, d_obj.argpartition, 10) assert_raises(ValueError, d_obj.argpartition, -11) - @dec.knownfailureif(True, "Ticket #5469 fixed for argpartition only") def test_partition_out_of_range(self): # Test out of range values in kth raise an error, gh-5469 d = np.arange(10) @@ -2313,6 +2312,22 @@ class TestBinop(object): assert_equal(obj2.sum(), 42) assert_(isinstance(obj2, SomeClass2)) + def test_ufunc_override_normalize_signature(self): + # gh-5674 + class SomeClass(object): + def __numpy_ufunc__(self, ufunc, method, i, inputs, **kw): + return kw + + a = SomeClass() + kw = np.add(a, [1]) + assert_('sig' not in kw and 'signature' not in kw) + kw = np.add(a, [1], sig='ii->i') + assert_('sig' not in kw and 'signature' in kw) + assert_equal(kw['signature'], 'ii->i') + kw = np.add(a, [1], signature='ii->i') + assert_('sig' not in kw and 'signature' in kw) + assert_equal(kw['signature'], 'ii->i') + class TestCAPI(TestCase): def test_IsPythonScalar(self): diff --git a/numpy/core/tests/test_regression.py b/numpy/core/tests/test_regression.py index 431f80534..19c8d4457 100644 --- a/numpy/core/tests/test_regression.py +++ b/numpy/core/tests/test_regression.py @@ -2110,6 +2110,14 @@ class TestRegression(TestCase): test_string = np.string_('') assert_equal(pickle.loads(pickle.dumps(test_string)), test_string) + def test_frompyfunc_many_args(self): + # gh-5672 + + def passer(*args): + pass + + assert_raises(ValueError, np.frompyfunc, passer, 32, 1) + if __name__ == "__main__": run_module_suite() diff --git a/numpy/core/tests/test_ufunc.py b/numpy/core/tests/test_ufunc.py index a285d5334..b3c281e2a 100644 --- a/numpy/core/tests/test_ufunc.py +++ b/numpy/core/tests/test_ufunc.py @@ -9,6 +9,29 @@ import numpy.core.operand_flag_tests as opflag_tests from numpy.compat import asbytes from numpy.core.test_rational import * + +class TestUfuncKwargs(TestCase): + def test_kwarg_exact(self): + assert_raises(TypeError, np.add, 1, 2, castingx='safe') + assert_raises(TypeError, np.add, 1, 2, dtypex=np.int) + assert_raises(TypeError, np.add, 1, 2, extobjx=[4096]) + assert_raises(TypeError, np.add, 1, 2, outx=None) + assert_raises(TypeError, np.add, 1, 2, sigx='ii->i') + assert_raises(TypeError, np.add, 1, 2, signaturex='ii->i') + assert_raises(TypeError, np.add, 1, 2, subokx=False) + assert_raises(TypeError, np.add, 1, 2, wherex=[True]) + + def test_sig_signature(self): + assert_raises(ValueError, np.add, 1, 2, sig='ii->i', + signature='ii->i') + + def test_sig_dtype(self): + assert_raises(RuntimeError, np.add, 1, 2, sig='ii->i', + dtype=np.int) + assert_raises(RuntimeError, np.add, 1, 2, signature='ii->i', + dtype=np.int) + + class TestUfunc(TestCase): def test_pickle(self): import pickle @@ -1072,6 +1095,9 @@ class TestUfunc(TestCase): self.assertRaises(TypeError, np.add.at, values, [0, 1], 1) assert_array_equal(values, np.array(['a', 1], dtype=np.object)) + # Test multiple output ufuncs raise error, gh-5665 + assert_raises(ValueError, np.modf.at, np.arange(10), [1]) + def test_reduce_arguments(self): f = np.add.reduce d = np.ones((5,2), dtype=int) diff --git a/numpy/lib/shape_base.py b/numpy/lib/shape_base.py index 2d18c5bc8..011434dda 100644 --- a/numpy/lib/shape_base.py +++ b/numpy/lib/shape_base.py @@ -850,7 +850,12 @@ def tile(A, reps): except TypeError: tup = (reps,) d = len(tup) - c = _nx.array(A, copy=False, subok=True, ndmin=d) + if all(x == 1 for x in tup) and isinstance(A, _nx.ndarray): + # Fixes the problem that the function does not make a copy if A is a + # numpy array and the repetitions are 1 in all dimensions + return _nx.array(A, copy=True, subok=True, ndmin=d) + else: + c = _nx.array(A, copy=False, subok=True, ndmin=d) shape = list(c.shape) n = max(c.size, 1) if (d < c.ndim): diff --git a/numpy/lib/stride_tricks.py b/numpy/lib/stride_tricks.py index a5f247abf..e7649cb60 100644 --- a/numpy/lib/stride_tricks.py +++ b/numpy/lib/stride_tricks.py @@ -46,9 +46,11 @@ def as_strided(x, shape=None, strides=None, subok=False): if strides is not None: interface['strides'] = tuple(strides) array = np.asarray(DummyArray(interface, base=x)) - # Make sure dtype is correct in case of custom dtype - if array.dtype.kind == 'V': + + if array.dtype.fields is None and x.dtype.fields is not None: + # This should only happen if x.dtype is [('', 'Vx')] array.dtype = x.dtype + return _maybe_view_as_subclass(x, array) diff --git a/numpy/lib/tests/test_shape_base.py b/numpy/lib/tests/test_shape_base.py index 23f3edfbe..fb9d7f364 100644 --- a/numpy/lib/tests/test_shape_base.py +++ b/numpy/lib/tests/test_shape_base.py @@ -324,6 +324,12 @@ class TestTile(TestCase): assert_equal(tile(b, (2, 2)), [[1, 2, 1, 2], [3, 4, 3, 4], [1, 2, 1, 2], [3, 4, 3, 4]]) + def test_tile_one_repetition_on_array_gh4679(self): + a = np.arange(5) + b = tile(a, 1) + b += 2 + assert_equal(a, np.arange(5)) + def test_empty(self): a = np.array([[[]]]) d = tile(a, (3, 2, 5)).shape diff --git a/numpy/lib/tests/test_stride_tricks.py b/numpy/lib/tests/test_stride_tricks.py index ef483921c..e079e0bf4 100644 --- a/numpy/lib/tests/test_stride_tricks.py +++ b/numpy/lib/tests/test_stride_tricks.py @@ -290,6 +290,29 @@ def test_as_strided(): expected = np.array([[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]) assert_array_equal(a_view, expected) + # Regression test for gh-5081 + dt = np.dtype([('num', 'i4'), ('obj', 'O')]) + a = np.empty((4,), dtype=dt) + a['num'] = np.arange(1, 5) + a_view = as_strided(a, shape=(3, 4), strides=(0, a.itemsize)) + expected_num = [[1, 2, 3, 4]] * 3 + expected_obj = [[None]*4]*3 + assert_equal(a_view.dtype, dt) + assert_array_equal(expected_num, a_view['num']) + assert_array_equal(expected_obj, a_view['obj']) + + # Make sure that void types without fields are kept unchanged + a = np.empty((4,), dtype='V4') + a_view = as_strided(a, shape=(3, 4), strides=(0, a.itemsize)) + assert_equal(a.dtype, a_view.dtype) + + # Make sure that the only type that could fail is properly handled + dt = np.dtype({'names': [''], 'formats': ['V4']}) + a = np.empty((4,), dtype=dt) + a_view = as_strided(a, shape=(3, 4), strides=(0, a.itemsize)) + assert_equal(a.dtype, a_view.dtype) + + class VerySimpleSubClass(np.ndarray): def __new__(cls, *args, **kwargs): |