diff options
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/include/numpy/ndarraytypes.h | 10 | ||||
-rw-r--r-- | numpy/core/src/multiarray/ctors.c | 5 | ||||
-rw-r--r-- | numpy/core/src/multiarray/dtype_transfer.c | 10 | ||||
-rw-r--r-- | numpy/core/src/multiarray/new_iterator.c.src | 43 | ||||
-rw-r--r-- | numpy/core/src/multiarray/new_iterator_pywrap.c | 65 | ||||
-rw-r--r-- | numpy/core/src/umath/loops.c.src | 5 | ||||
-rw-r--r-- | numpy/core/src/umath/ufunc_object.c | 183 | ||||
-rw-r--r-- | numpy/core/tests/test_numeric.py | 20 | ||||
-rw-r--r-- | numpy/ma/core.py | 4 | ||||
-rw-r--r-- | numpy/testing/tests/test_utils.py | 2 |
10 files changed, 258 insertions, 89 deletions
diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h index db7ffb183..99d81109d 100644 --- a/numpy/core/include/numpy/ndarraytypes.h +++ b/numpy/core/include/numpy/ndarraytypes.h @@ -884,14 +884,16 @@ typedef void (*NpyIter_GetCoords_Fn )(NpyIter *iter, #define NPY_ITER_COMMON_DTYPE 0x00000010 /* Operands may hold references, requiring API access during iteration */ #define NPY_ITER_REFS_OK 0x00000020 +/* Zero-sized operands should be permitted, iteration checks IterSize for 0 */ +#define NPY_ITER_ZEROSIZE_OK 0x00000040 /* Enables sub-range iteration */ -#define NPY_ITER_RANGED 0x00000040 +#define NPY_ITER_RANGED 0x00000080 /* Enables buffering */ -#define NPY_ITER_BUFFERED 0x00000080 +#define NPY_ITER_BUFFERED 0x00000100 /* When buffering is enabled, grows the inner loop if possible */ -#define NPY_ITER_GROWINNER 0x00000100 +#define NPY_ITER_GROWINNER 0x00000200 /* Delay allocation of buffers until first Reset* call */ -#define NPY_ITER_DELAY_BUFALLOC 0x00000200 +#define NPY_ITER_DELAY_BUFALLOC 0x00000400 /*** Per-operand flags that may be passed to the iterator constructors ***/ diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index 7802b8114..9acfd15b3 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -2344,7 +2344,10 @@ PyArray_CopyInto(PyArrayObject *dst, PyArrayObject *src) } if (PyArray_SIZE(src) == 0) { - if (PyArray_SIZE(dst) == 0) { + /* If src and dst have the same shapes, copying zero-sized is ok */ + if (PyArray_NDIM(src) == PyArray_NDIM(dst) && + PyArray_CompareLists(PyArray_DIMS(src), PyArray_DIMS(dst), + PyArray_NDIM(src))) { return 0; } else { diff --git a/numpy/core/src/multiarray/dtype_transfer.c b/numpy/core/src/multiarray/dtype_transfer.c index e04bac783..084683f96 100644 --- a/numpy/core/src/multiarray/dtype_transfer.c +++ b/numpy/core/src/multiarray/dtype_transfer.c @@ -17,6 +17,9 @@ #define NPY_LOWLEVEL_BUFFER_BLOCKSIZE 128 +#define DTYPE_TRANSFER_REFTRACE(x, y) +/*#define DTYPE_TRANSFER_REFTRACE printf*/ + /* * Returns a transfer function which DECREFs any references in src_type. * @@ -69,8 +72,10 @@ _strided_to_strided_move_references(char *dst, npy_intp dst_stride, NPY_COPY_PYOBJECT_PTR(&dst_ref, dst); /* Release the reference in dst */ + DTYPE_TRANSFER_REFTRACE("dec dst ref %p\n", dst_ref); Py_XDECREF(dst_ref); /* Move the reference */ + DTYPE_TRANSFER_REFTRACE("move src ref %p\n", src_ref); NPY_COPY_PYOBJECT_PTR(dst, &src_ref); /* Set the source reference to NULL */ src_ref = NULL; @@ -95,8 +100,10 @@ _strided_to_strided_copy_references(char *dst, npy_intp dst_stride, NPY_COPY_PYOBJECT_PTR(&dst_ref, dst); /* Release the reference in dst */ + DTYPE_TRANSFER_REFTRACE("dec dst ref %p\n", dst_ref); Py_XDECREF(dst_ref); /* Copy the reference */ + DTYPE_TRANSFER_REFTRACE("copy src ref %p\n", src_ref); NPY_COPY_PYOBJECT_PTR(dst, &src_ref); /* Claim the reference */ Py_XINCREF(src_ref); @@ -569,6 +576,7 @@ _aligned_strided_to_strided_cast_decref_src(char *dst, npy_intp dst_stride, /* After casting, decrement the source ref */ NPY_COPY_PYOBJECT_PTR(&src_ref, src); + DTYPE_TRANSFER_REFTRACE("dec src ref (cast object -> not object) %p\n", src_ref); Py_XDECREF(src_ref); dst += dst_stride; @@ -2476,6 +2484,7 @@ _null_to_strided_reference_setzero(char *dst, NPY_COPY_PYOBJECT_PTR(&dst_ref, dst); /* Release the reference in dst */ + DTYPE_TRANSFER_REFTRACE("dec dest ref (to set zero) %p\n", dst_ref); Py_XDECREF(dst_ref); /* Set it to zero */ @@ -2605,6 +2614,7 @@ _strided_to_null_dec_src_ref_reference(char *NPY_UNUSED(dst), NPY_COPY_PYOBJECT_PTR(&src_ref, src); /* Release the reference in src */ + DTYPE_TRANSFER_REFTRACE("dec src ref (null dst) %p\n", src_ref); Py_XDECREF(src_ref); src += src_stride; diff --git a/numpy/core/src/multiarray/new_iterator.c.src b/numpy/core/src/multiarray/new_iterator.c.src index cd1d5f6da..c9a4f4959 100644 --- a/numpy/core/src/multiarray/new_iterator.c.src +++ b/numpy/core/src/multiarray/new_iterator.c.src @@ -602,6 +602,7 @@ NpyIter_MultiNew(npy_intp niter, PyArrayObject **op_in, npy_uint32 flags, return NULL; } if (itflags&NPY_ITFLAG_DELAYBUF) { + bufferdata = NIT_BUFFERDATA(iter); /* Make the data pointers NULL */ memset(NBF_PTRS(bufferdata), 0, niter*NPY_SIZEOF_INTP); } @@ -2441,10 +2442,9 @@ npyiter_prepare_one_operand(PyArrayObject **op, "flagged as writeable"); return 0; } - if (PyArray_SIZE(*op) == 0) { + if (!(flags&NPY_ITER_ZEROSIZE_OK) && PyArray_SIZE(*op) == 0) { PyErr_SetString(PyExc_ValueError, - "Iterator does not support iteration of zero-sized " - "operands"); + "Iteration of zero-sized operands is not enabled"); return 0; } *op_dataptr = PyArray_BYTES(*op); @@ -2938,9 +2938,16 @@ npyiter_fill_axisdata(NpyIter *iter, char **op_dataptr, /* * Allow additional dimensions of size 1 to be added on, * but no broadcasting that would produce a one to many - * mapping + * mapping. + * + * TODO: either policy triggers unit test errors... + * should we disallow this (as previously): + * x = array(2) + * np.add(x, [1], x) + * or should we disallow this (allowed previously): + * <I forget at the moment>... */ - else if (NAD_SHAPE(axisdata) != 1) { + else { /*if (NAD_SHAPE(axisdata) != 1) {*/ if (op_flags[iiter]&NPY_ITER_WRITEONLY) { PyErr_SetString(PyExc_ValueError, "output operand cannot be broadcasted"); @@ -3563,7 +3570,7 @@ npyiter_new_temp_array(NpyIter *iter, PyTypeObject *subtype, /* Initially no strides have been set */ for (idim = 0; idim < op_ndim; ++idim) { - strides[idim] = 0; + strides[idim] = NPY_MAX_INTP; reversestride[idim] = 0; } @@ -3609,7 +3616,7 @@ npyiter_new_temp_array(NpyIter *iter, PyTypeObject *subtype, * otherwise fill in the rest. */ for (idim = 0; idim < op_ndim; ++idim) { - if (strides[idim] == 0) { + if (strides[idim] == NPY_MAX_INTP) { npy_intp factor, new_strides[NPY_MAXDIMS], itemsize; if (shape == NULL) { @@ -3624,7 +3631,7 @@ npyiter_new_temp_array(NpyIter *iter, PyTypeObject *subtype, factor = 1; itemsize = op_dtype->elsize; for (idim = op_ndim-1; idim >= 0; --idim) { - if (strides[idim] == 0) { + if (strides[idim] == NPY_MAX_INTP) { new_strides[idim] = factor * itemsize; factor *= shape[idim]; } @@ -3637,7 +3644,7 @@ npyiter_new_temp_array(NpyIter *iter, PyTypeObject *subtype, * loops. */ for (idim = 0; idim < op_ndim; ++idim) { - if (strides[idim] == 0) { + if (strides[idim] == NPY_MAX_INTP) { strides[idim] = new_strides[idim]; } else { @@ -4525,11 +4532,27 @@ NpyIter_DebugPrint(NpyIter *iter) printf("InitIndex: %d\n", (int)(npy_intp)NIT_RESETDATAPTR(iter)[niter]); } - printf("Objects: "); + printf("Operands: "); for (iiter = 0; iiter < niter; ++iiter) { printf("%p ", NIT_OBJECTS(iter)[iiter]); } printf("\n"); + printf("Operand DTypes: "); + for (iiter = 0; iiter < niter; ++iiter) { + PyArray_Descr *dtype; + if (NIT_OBJECTS(iter)[iiter] != NULL) { + dtype = PyArray_DESCR(NIT_OBJECTS(iter)[iiter]); + if (dtype != NULL) + PyObject_Print((PyObject *)dtype, stdout, 0); + else + printf("(nil) "); + } + else { + printf("(op nil) "); + } + printf(" "); + } + printf("\n"); printf("OpItFlags:\n"); for (iiter = 0; iiter < niter; ++iiter) { printf(" Flags[%d]: ", (int)iiter); diff --git a/numpy/core/src/multiarray/new_iterator_pywrap.c b/numpy/core/src/multiarray/new_iterator_pywrap.c index 286637772..cd476cd38 100644 --- a/numpy/core/src/multiarray/new_iterator_pywrap.c +++ b/numpy/core/src/multiarray/new_iterator_pywrap.c @@ -164,6 +164,11 @@ NpyIter_GlobalFlagsConverter(PyObject *flags_in, npy_uint32 *flags) flag = NPY_ITER_REFS_OK; } break; + case 'z': + if (strcmp(str, "zerosize_ok") == 0) { + flag = NPY_ITER_ZEROSIZE_OK; + } + break; } if (flag == 0) { PyErr_Format(PyExc_ValueError, @@ -757,8 +762,14 @@ npyiter_init(NewNpyArrayIterObject *self, PyObject *args, PyObject *kwds) /* Cache some values for the member functions to use */ npyiter_cache_values(self); - self->started = 0; - self->finished = 0; + if (NpyIter_GetIterSize(self->iter) == 0) { + self->started = 1; + self->finished = 1; + } + else { + self->started = 0; + self->finished = 0; + } /* Release the references we got to the ops and dtypes */ for (iiter = 0; iiter < niter; ++iiter) { @@ -1011,8 +1022,14 @@ NpyIter_NestedIters(PyObject *NPY_UNUSED(self), /* Cache some values for the member functions to use */ npyiter_cache_values(iter); - iter->started = 0; - iter->finished = 0; + if (NpyIter_GetIterSize(iter->iter) == 0) { + iter->started = 1; + iter->finished = 1; + } + else { + iter->started = 0; + iter->finished = 0; + } /* * If there are any allocated outputs or any copies were made, @@ -1102,8 +1119,14 @@ npyiter_resetbasepointers(NewNpyArrayIterObject *self) return NPY_FAIL; } self = self->nested_child; - self->started = 0; - self->finished = 0; + if (NpyIter_GetIterSize(self->iter) == 0) { + self->started = 1; + self->finished = 1; + } + else { + self->started = 0; + self->finished = 0; + } } return NPY_SUCCEED; @@ -1121,8 +1144,14 @@ npyiter_reset(NewNpyArrayIterObject *self) if (NpyIter_Reset(self->iter, NULL) != NPY_SUCCEED) { return NULL; } - self->started = 0; - self->finished = 0; + if (NpyIter_GetIterSize(self->iter) == 0) { + self->started = 1; + self->finished = 1; + } + else { + self->started = 0; + self->finished = 0; + } if (self->getcoords == NULL && NpyIter_HasCoords(self->iter)) { self->getcoords = NpyIter_GetGetCoords(self->iter, NULL); @@ -1204,8 +1233,14 @@ npyiter_remove_coords(NewNpyArrayIterObject *self) /* RemoveCoords invalidates cached values */ npyiter_cache_values(self); /* RemoveCoords also resets the iterator */ - self->started = 0; - self->finished = 0; + if (NpyIter_GetIterSize(self->iter) == 0) { + self->started = 1; + self->finished = 1; + } + else { + self->started = 0; + self->finished = 0; + } Py_RETURN_NONE; } @@ -1223,8 +1258,14 @@ npyiter_remove_inner_loop(NewNpyArrayIterObject *self) /* RemoveInnerLoop invalidates cached values */ npyiter_cache_values(self); /* RemoveInnerLoop also resets the iterator */ - self->started = 0; - self->finished = 0; + if (NpyIter_GetIterSize(self->iter) == 0) { + self->started = 1; + self->finished = 1; + } + else { + self->started = 0; + self->finished = 0; + } Py_RETURN_NONE; } diff --git a/numpy/core/src/umath/loops.c.src b/numpy/core/src/umath/loops.c.src index 492364a17..4bd2971c5 100644 --- a/numpy/core/src/umath/loops.c.src +++ b/numpy/core/src/umath/loops.c.src @@ -2058,7 +2058,7 @@ OBJECT_sign(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func)) PyObject **out = (PyObject **)op1; int v; PyObject *ret; - PyObject_Cmp(in1, zero, &v); + PyObject_Cmp(in1 ? in1 : Py_None, zero, &v); ret = PyLong_FromLong(v); if (PyErr_Occurred()) { return; @@ -2072,7 +2072,8 @@ OBJECT_sign(char **args, intp *dimensions, intp *steps, void *NPY_UNUSED(func)) UNARY_LOOP { PyObject *in1 = *(PyObject **)ip1; PyObject **out = (PyObject **)op1; - PyObject *ret = PyInt_FromLong(PyObject_Compare(in1, zero)); + PyObject *ret = PyInt_FromLong( + PyObject_Compare(in1 ? in1 : Py_None, zero)); if (PyErr_Occurred()) { return; } diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c index 7a40dfa8d..2e52f568f 100644 --- a/numpy/core/src/umath/ufunc_object.c +++ b/numpy/core/src/umath/ufunc_object.c @@ -43,6 +43,9 @@ #define USE_USE_DEFAULTS 1 +/*#define NPY_UFUNC_DBG_PRINTF(...) printf(__VA_ARGS__)*/ +#define NPY_UFUNC_DBG_PRINTF(...) + /* ---------------------------------------------------------------- */ static int @@ -2040,14 +2043,8 @@ fail: * */ -/*UFUNC_API - * - * This generic function is called with the ufunc object, the arguments to it, - * and an array of (pointers to) PyArrayObjects which are NULL. The - * arguments are parsed and placed in mps in construct_loop (construct_arrays) - */ NPY_NO_EXPORT int -PyUFunc_GenericFunction(PyUFuncObject *self, PyObject *args, PyObject *kwds, +PyUFunc_GenericFunction_Old(PyUFuncObject *self, PyObject *args, PyObject *kwds, PyArrayObject **mps) { PyUFuncLoopObject *loop; @@ -2567,6 +2564,25 @@ static int get_ufunc_arguments(PyUFuncObject *self, return 0; } +static const char * +_casting_to_string(NPY_CASTING casting) +{ + switch (casting) { + case NPY_NO_CASTING: + return "no"; + case NPY_EQUIV_CASTING: + return "equiv"; + case NPY_SAFE_CASTING: + return "safe"; + case NPY_SAME_KIND_CASTING: + return "same_kind"; + case NPY_UNSAFE_CASTING: + return "unsafe"; + default: + return "<unknown>"; + } +} + /* * Does a linear search for the best inner loop of the ufunc. * When op[i] is a scalar or a one dimensional array smaller than @@ -2588,9 +2604,15 @@ find_best_ufunc_inner_loop(PyUFuncObject *self, int *out_trivial_loop_ok) { npy_intp i, j, nin = self->nin, niter = nin + self->nout; + char *ufunc_name; NPY_CASTING input_casting; int no_castable_output, all_inputs_scalar; + /* For making a better error message on coercion error */ + char dst_typecode = '-', src_typecode = '-'; + + ufunc_name = self->name ? self->name : "<unnamed ufunc>"; + /* Check whether all the inputs are scalar */ all_inputs_scalar = 1; for(i = 0; i < nin; ++i) { @@ -2625,16 +2647,27 @@ find_best_ufunc_inner_loop(PyUFuncObject *self, no_castable_output = 0; for (i = 0; i < self->ntypes; ++i) { char *types = self->types + i*self->nargs; + NPY_UFUNC_DBG_PRINTF("Trying function loop %d\n", (int)i); /* * First check if all the inputs can be safely cast * to the types for this function */ for (j = 0; j < nin; ++j) { PyArray_Descr *tmp = PyArray_DescrFromType(types[j]); + if (tmp == NULL) { + return -1; + } /* * If all the inputs are scalars, use the regular * promotion rules, not the special value-checking ones. */ +#if 0 + printf("Checking type for op %d, type: ", (int)j); + PyObject_Print(tmp, stdout, 0); + printf(", operand type: "); + PyObject_Print(PyArray_DESCR(op[j]), stdout, 0); + printf("\n"); +#endif if (all_inputs_scalar) { if (!PyArray_CanCastTypeTo(PyArray_DESCR(op[j]), tmp, input_casting)) { @@ -2655,6 +2688,7 @@ find_best_ufunc_inner_loop(PyUFuncObject *self, * outputs. */ if (j == nin) { + NPY_UFUNC_DBG_PRINTF("The inputs all worked\n"); for (j = nin; j < niter; ++j) { if (op[j] != NULL) { PyArray_Descr *tmp = PyArray_DescrFromType(types[j]); @@ -2664,7 +2698,11 @@ find_best_ufunc_inner_loop(PyUFuncObject *self, if (!PyArray_CanCastTypeTo(tmp, PyArray_DESCR(op[j]), casting)) { Py_DECREF(tmp); - no_castable_output = 1; + if (!no_castable_output) { + no_castable_output = 1; + src_typecode = tmp->type; + dst_typecode = PyArray_DESCR(op[j])->type; + } break; } Py_DECREF(tmp); @@ -2673,6 +2711,7 @@ find_best_ufunc_inner_loop(PyUFuncObject *self, } /* If all the tests passed, we found our function */ if (j == niter) { + NPY_UFUNC_DBG_PRINTF("The outputs all worked\n"); *out_trivial_loop_ok = 1; /* Fill the dtypes array */ for (j = 0; j < niter; ++j) { @@ -2720,11 +2759,14 @@ find_best_ufunc_inner_loop(PyUFuncObject *self, /* If no function was found, throw an error */ if (i == self->ntypes) { + NPY_UFUNC_DBG_PRINTF("No loop was found\n"); if (no_castable_output) { PyErr_Format(PyExc_TypeError, - "function output could not be coerced to provided " - "output parameter according to the specified casting " - "rule"); + "ufunc '%s' output (typecode '%c') could not be coerced to " + "provided output parameter (typecode '%c') according " + "to the casting rule '%s'", + ufunc_name, src_typecode, dst_typecode, + _casting_to_string(casting)); } else { /* @@ -2732,13 +2774,17 @@ find_best_ufunc_inner_loop(PyUFuncObject *self, * or unsafe, and look for a function more liberally. */ PyErr_Format(PyExc_TypeError, - "function not supported for the input types, and the " + "ufunc '%s' not supported for the input types, and the " "inputs could not be safely coerced to any supported " - "types"); + "types according to the casting rule '%s'", + ufunc_name, + _casting_to_string(input_casting)); } return -1; } + NPY_UFUNC_DBG_PRINTF("Returning inner loop successfully\n"); + return 0; } @@ -2755,7 +2801,7 @@ trivial_two_operand_loop(PyArrayObject **op, data[0], data[1], stride[0], stride[1]); count[1] = count[0]; - //printf ("loop count %d\n", (int)count[0]); + NPY_UFUNC_DBG_PRINTF("two operand loop count %d\n", (int)count[0]); innerloop(data, count, stride, innerloopdata); } @@ -2773,7 +2819,7 @@ trivial_three_operand_loop(PyArrayObject **op, stride[0], stride[1], stride[2]); count[1] = count[0]; count[2] = count[0]; - //printf ("loop count %d\n", (int)count[0]); + NPY_UFUNC_DBG_PRINTF("three operand loop count %d\n", (int)count[0]); innerloop(data, count, stride, innerloopdata); } @@ -2884,6 +2930,7 @@ iterator_loop(PyUFuncObject *self, iter = NpyIter_MultiNew(niter, op, NPY_ITER_NO_INNER_ITERATION| NPY_ITER_REFS_OK| + NPY_ITER_ZEROSIZE_OK| NPY_ITER_BUFFERED| NPY_ITER_GROWINNER| NPY_ITER_DELAY_BUFALLOC, @@ -2912,35 +2959,39 @@ iterator_loop(PyUFuncObject *self, } } - /* Reset the iterator with the base pointers from the wrapped outputs */ - for (i = 0; i < niter; ++i) { - baseptrs[i] = PyArray_BYTES(op[i]); - } - if (NpyIter_ResetBasePointers(iter, baseptrs, NULL) != NPY_SUCCEED) { - NpyIter_Deallocate(iter); - return -1; - } - - /* Get the variables needed for the loop */ - iternext = NpyIter_GetIterNext(iter, NULL); - if (iternext == NULL) { - NpyIter_Deallocate(iter); - return -1; - } - dataptr = NpyIter_GetDataPtrArray(iter); - stride = NpyIter_GetInnerStrideArray(iter); - count_ptr = NpyIter_GetInnerLoopSizePtr(iter); + /* Only do the loop if the iteration size is non-zero */ + if (NpyIter_GetIterSize(iter) != 0) { - /* Execute the loop */ - do { - /* Copy the loop count a few times... :( */ - npy_intp count = *count_ptr; + /* Reset the iterator with the base pointers from the wrapped outputs */ for (i = 0; i < niter; ++i) { - copied_counts[i] = count; + baseptrs[i] = PyArray_BYTES(op[i]); + } + if (NpyIter_ResetBasePointers(iter, baseptrs, NULL) != NPY_SUCCEED) { + NpyIter_Deallocate(iter); + return -1; } - //printf ("loop count %d\n", (int)count); - innerloop(dataptr, count_ptr, stride, innerloopdata); - } while (iternext(iter)); + + /* Get the variables needed for the loop */ + iternext = NpyIter_GetIterNext(iter, NULL); + if (iternext == NULL) { + NpyIter_Deallocate(iter); + return -1; + } + dataptr = NpyIter_GetDataPtrArray(iter); + stride = NpyIter_GetInnerStrideArray(iter); + count_ptr = NpyIter_GetInnerLoopSizePtr(iter); + + /* Execute the loop */ + do { + /* Copy the loop count a few times... :( */ + npy_intp count = *count_ptr; + for (i = 0; i < niter; ++i) { + copied_counts[i] = count; + } + NPY_UFUNC_DBG_PRINTF("iterator loop count %d\n", (int)count); + innerloop(dataptr, count_ptr, stride, innerloopdata); + } while (iternext(iter)); + } NpyIter_Deallocate(iter); return 0; @@ -2992,7 +3043,7 @@ execute_ufunc_loop(PyUFuncObject *self, return -1; } - //printf("trivial 1 input with allocated output\n"); + NPY_UFUNC_DBG_PRINTF("trivial 1 input with allocated output\n"); trivial_two_operand_loop(op, innerloop, innerloopdata); return 0; @@ -3007,7 +3058,7 @@ execute_ufunc_loop(PyUFuncObject *self, return -1; } - //printf("trivial 1 input\n"); + NPY_UFUNC_DBG_PRINTF("trivial 1 input\n"); trivial_two_operand_loop(op, innerloop, innerloopdata); return 0; @@ -3043,7 +3094,7 @@ execute_ufunc_loop(PyUFuncObject *self, return -1; } - //printf("trivial 2 input with allocated output\n"); + NPY_UFUNC_DBG_PRINTF("trivial 2 input with allocated output\n"); trivial_three_operand_loop(op, innerloop, innerloopdata); return 0; @@ -3059,7 +3110,7 @@ execute_ufunc_loop(PyUFuncObject *self, return -1; } - //printf("trivial 2 input\n"); + NPY_UFUNC_DBG_PRINTF("trivial 2 input\n"); trivial_three_operand_loop(op, innerloop, innerloopdata); return 0; @@ -3072,7 +3123,7 @@ execute_ufunc_loop(PyUFuncObject *self, * resolve broadcasting, etc */ - //printf("iterator loop\n"); + NPY_UFUNC_DBG_PRINTF("iterator loop\n"); if (iterator_loop(self, op, dtype, order, buffersize, arr_prep, arr_prep_args, innerloop, innerloopdata) < 0) { @@ -3120,8 +3171,13 @@ make_arr_prep_args(npy_intp nin, PyObject *args, PyObject *kwds) } } +/*UFUNC_API + * + * This generic function is called with the ufunc object, the arguments to it, + * and an array of (pointers to) PyArrayObjects which are NULL. + */ NPY_NO_EXPORT int -PyUFunc_GenericFunction_Iter(PyUFuncObject *self, +PyUFunc_GenericFunction(PyUFuncObject *self, PyObject *args, PyObject *kwds, PyArrayObject **op) { @@ -3153,7 +3209,8 @@ PyUFunc_GenericFunction_Iter(PyUFuncObject *self, /* TODO: For 1.6, the default should probably be NPY_CORDER */ NPY_ORDER order = NPY_KEEPORDER; - NPY_CASTING casting = NPY_SAFE_CASTING; + /* If NPY_SAFE_CASTING is default, it breaks += in many cases */ + NPY_CASTING casting = NPY_SAME_KIND_CASTING; /* When provided, extobj and typetup contain borrowed references */ PyObject *extobj = NULL, *typetup = NULL; @@ -3175,6 +3232,8 @@ PyUFunc_GenericFunction_Iter(PyUFuncObject *self, ufunc_name = self->name ? self->name : "<unnamed ufunc>"; + NPY_UFUNC_DBG_PRINTF("Evaluating ufunc %s\n", ufunc_name); + /* Initialize all the operands and dtypes to NULL */ for (i = 0; i < niter; ++i) { op[i] = NULL; @@ -3182,6 +3241,8 @@ PyUFunc_GenericFunction_Iter(PyUFuncObject *self, arr_prep[i] = NULL; } + NPY_UFUNC_DBG_PRINTF("Getting arguments\n"); + /* Get all the arguments */ retval = get_ufunc_arguments(self, args, kwds, op, &order, &casting, &extobj, &typetup); @@ -3193,19 +3254,24 @@ PyUFunc_GenericFunction_Iter(PyUFuncObject *self, if (extobj == NULL) { if (PyUFunc_GetPyValues(ufunc_name, &buffersize, &errormask, &errobj) < 0) { + retval = -1; goto fail; } } else { if (_extract_pyvals(extobj, ufunc_name, &buffersize, &errormask, &errobj) < 0) { + retval = -1; goto fail; } } + NPY_UFUNC_DBG_PRINTF("Finding inner loop\n"); + /* Find the best ufunc inner loop, and fill in the dtypes */ - if (find_best_ufunc_inner_loop(self, op, casting, buffersize, - dtype, &innerloop, &innerloopdata, &trivial_loop_ok) < 0) { + retval = find_best_ufunc_inner_loop(self, op, casting, buffersize, + dtype, &innerloop, &innerloopdata, &trivial_loop_ok); + if (retval < 0) { goto fail; } @@ -3223,7 +3289,7 @@ PyUFunc_GenericFunction_Iter(PyUFuncObject *self, && PyObject_HasAttrString(_obj, "__array_priority__") && _has_reflected_op(_obj, ufunc_name)) { retval = -2; - goto fail; + goto fail; } } @@ -3258,16 +3324,20 @@ PyUFunc_GenericFunction_Iter(PyUFuncObject *self, /* Start with the floating-point exception flags cleared */ PyUFunc_clearfperr(); + NPY_UFUNC_DBG_PRINTF("Executing inner loop\n"); + /* Do the ufunc loop */ - if (execute_ufunc_loop(self, trivial_loop_ok, op, dtype, order, + retval = execute_ufunc_loop(self, trivial_loop_ok, op, dtype, order, buffersize, arr_prep, arr_prep_args, - innerloop, innerloopdata) < 0) { + innerloop, innerloopdata); + if (retval < 0) { goto fail; } /* Check whether any errors occurred during the loop */ if (PyErr_Occurred() || (errormask && PyUFunc_checkfperr(errormask, errobj, &first_error))) { + retval = -1; goto fail; } @@ -3279,9 +3349,12 @@ PyUFunc_GenericFunction_Iter(PyUFuncObject *self, Py_XDECREF(errobj); Py_XDECREF(arr_prep_args); + NPY_UFUNC_DBG_PRINTF("Returning Success\n"); + return 0; fail: + NPY_UFUNC_DBG_PRINTF("Returning failure code %d\n", retval); for (i = 0; i < niter; ++i) { Py_XDECREF(op[i]); op[i] = NULL; @@ -4474,7 +4547,7 @@ ufunc_generic_call_iter(PyUFuncObject *self, PyObject *args, PyObject *kwds) for(i = 0; i < self->nargs; i++) { mps[i] = NULL; } - errval = PyUFunc_GenericFunction_Iter(self, args, kwds, mps); + errval = PyUFunc_GenericFunction(self, args, kwds, mps); if (errval < 0) { for (i = 0; i < self->nargs; i++) { PyArray_XDECREF_ERR(mps[i]); @@ -4587,7 +4660,7 @@ ufunc_generic_call(PyUFuncObject *self, PyObject *args, PyObject *kwds) for(i = 0; i < self->nargs; i++) { mps[i] = NULL; } - errval = PyUFunc_GenericFunction(self, args, kwds, mps); + errval = PyUFunc_GenericFunction_Old(self, args, kwds, mps); if (errval < 0) { for (i = 0; i < self->nargs; i++) { PyArray_XDECREF_ERR(mps[i]); diff --git a/numpy/core/tests/test_numeric.py b/numpy/core/tests/test_numeric.py index 34ba89348..7dc94315d 100644 --- a/numpy/core/tests/test_numeric.py +++ b/numpy/core/tests/test_numeric.py @@ -375,9 +375,9 @@ class TestTypes(TestCase): assert_equal(promote_func(array([f64]),fld), np.dtype(float64)) assert_equal(promote_func(fld,array([c64])), np.dtype(complex64)) - def test_coercion(self): + def test_old_coercion(self): def res_type(a, b): - return np.add(a, b).dtype + return np.add.f(a, b).dtype self.check_promotion_cases(res_type) f64 = float64(0) @@ -385,6 +385,19 @@ class TestTypes(TestCase): # Scalars used to coerce to complex even if the value was real assert_equal(res_type(c64,array([f64])), np.dtype(complex128)) + def test_coercion(self): + def res_type(a, b): + return np.add(a, b).dtype + self.check_promotion_cases(res_type) + + f64 = float64(0) + c64 = complex64(0) + # Scalars do not coerce to complex if the value is real + assert_equal(res_type(c64,array([f64])), np.dtype(float64)) + # But they do if the value is complex + assert_equal(res_type(complex64(3j),array([f64])), + np.dtype(complex128)) + def test_result_type(self): self.check_promotion_cases(np.result_type) @@ -395,6 +408,7 @@ class TestTypes(TestCase): # But they do if the value is complex assert_equal(np.result_type(complex64(3j),array([f64])), np.dtype(complex128)) + def can_cast(self): assert_(np.can_cast(np.int32, np.int64)) assert_(np.can_cast(np.float64, np.complex)) @@ -745,7 +759,7 @@ class TestClip(TestCase): assert_array_strict_equal(ac, act) def test_simple_int64_inout(self): - """Test native in32 input with double array min/max and int32 out.""" + """Test native int32 input with double array min/max and int32 out.""" a = self._generate_int32_data(self.nr, self.nc) m = zeros(a.shape, float64) M = float64(1) diff --git a/numpy/ma/core.py b/numpy/ma/core.py index 98100621e..673cfb1ab 100644 --- a/numpy/ma/core.py +++ b/numpy/ma/core.py @@ -940,7 +940,9 @@ class _MaskedBinaryOperation: # Revert result to da where masked if m.any(): np.putmask(result, m, 0) - result += m * da + # This only makes sense if the operation preserved the dtype + if result.dtype == da.dtype: + result += m * da # Transforms to a (subclass of) MaskedArray result = result.view(get_masked_subclass(a, b)) result._mask = m diff --git a/numpy/testing/tests/test_utils.py b/numpy/testing/tests/test_utils.py index 5106c1184..9798a25be 100644 --- a/numpy/testing/tests/test_utils.py +++ b/numpy/testing/tests/test_utils.py @@ -413,7 +413,7 @@ class TestULP(unittest.TestCase): def test_double(self): # Generate 1 + small deviation, check that adding eps gives a few UNL - x = np.ones(10).astype(np.float32) + x = np.ones(10).astype(np.float64) x += 0.01 * np.random.randn(10).astype(np.float64) eps = np.finfo(np.float64).eps assert_array_max_ulp(x, x+eps, maxulp=200) |