summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/include/numpy/ndarraytypes.h10
-rw-r--r--numpy/core/src/multiarray/ctors.c5
-rw-r--r--numpy/core/src/multiarray/dtype_transfer.c10
-rw-r--r--numpy/core/src/multiarray/new_iterator.c.src43
-rw-r--r--numpy/core/src/multiarray/new_iterator_pywrap.c65
-rw-r--r--numpy/core/src/umath/loops.c.src5
-rw-r--r--numpy/core/src/umath/ufunc_object.c183
-rw-r--r--numpy/core/tests/test_numeric.py20
-rw-r--r--numpy/ma/core.py4
-rw-r--r--numpy/testing/tests/test_utils.py2
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)