summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
Diffstat (limited to 'numpy')
-rw-r--r--numpy/core/arrayprint.py6
-rw-r--r--numpy/core/code_generators/numpy_api.py4
-rw-r--r--numpy/core/src/multiarray/convert_datatype.c2
-rw-r--r--numpy/core/src/multiarray/na_mask.c8
-rw-r--r--numpy/core/src/multiarray/shape.c196
-rw-r--r--numpy/core/src/umath/ufunc_object.c298
-rw-r--r--numpy/core/tests/test_maskna.py19
7 files changed, 416 insertions, 117 deletions
diff --git a/numpy/core/arrayprint.py b/numpy/core/arrayprint.py
index fcda825c7..525fcab8a 100644
--- a/numpy/core/arrayprint.py
+++ b/numpy/core/arrayprint.py
@@ -218,13 +218,16 @@ def _boolFormatter(x):
def _array2string(a, max_line_width, precision, suppress_small, separator=' ',
prefix="", formatter=None):
+ print "DEBUG: in array2string!"
if max_line_width is None:
max_line_width = _line_width
+ print "DEBUG: A"
if precision is None:
precision = _float_output_precision
+ print "DEBUG: B"
if suppress_small is None:
suppress_small = _float_output_suppress_small
@@ -238,6 +241,7 @@ def _array2string(a, max_line_width, precision, suppress_small, separator=' ',
summary_insert = ""
data = ravel(a)
+ print "DEBUG: making formatdict"
formatdict = {'bool' : _boolFormatter,
'int' : IntegerFormat(data),
'float' : FloatFormat(data, precision, suppress_small),
@@ -249,6 +253,8 @@ def _array2string(a, max_line_width, precision, suppress_small, separator=' ',
'timedelta' : TimedeltaFormat(data),
'numpystr' : repr,
'str' : str}
+ print "DEBUG: made formatdict"
+
if formatter is not None:
fkeys = [k for k in formatter.keys() if formatter[k] is not None]
if 'all' in fkeys:
diff --git a/numpy/core/code_generators/numpy_api.py b/numpy/core/code_generators/numpy_api.py
index 0dadd0574..a066e6968 100644
--- a/numpy/core/code_generators/numpy_api.py
+++ b/numpy/core/code_generators/numpy_api.py
@@ -326,10 +326,12 @@ multiarray_funcs_api = {
'PyArray_AllocateMaskNA': 286,
'NpyIter_GetFirstMaskNAOp': 288,
'NpyIter_GetMaskNAIndexArray': 289,
- 'PyArray_ReduceMaskArray': 290,
+ 'PyArray_ReduceMaskNAArray': 290,
'PyArray_CreateSortedStridePerm': 291,
'PyArray_FillWithZero': 292,
'PyArray_FillWithOne': 293,
+ 'PyArray_AssignNA': 294,
+ 'PyArray_AssignMaskNA': 295,
}
ufunc_types_api = {
diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c
index 1eb9931a4..e5cd965aa 100644
--- a/numpy/core/src/multiarray/convert_datatype.c
+++ b/numpy/core/src/multiarray/convert_datatype.c
@@ -1836,7 +1836,7 @@ PyArray_ConvertToCommonType(PyObject *op, int *retn)
/* Make sure all arrays are actual array objects. */
for (i = 0; i < n; i++) {
- int flags = NPY_ARRAY_CARRAY;
+ int flags = NPY_ARRAY_CARRAY | NPY_ARRAY_ALLOWNA;
if ((otmp = PySequence_GetItem(op, i)) == NULL) {
goto fail;
diff --git a/numpy/core/src/multiarray/na_mask.c b/numpy/core/src/multiarray/na_mask.c
index e649e9e1b..70b6e7838 100644
--- a/numpy/core/src/multiarray/na_mask.c
+++ b/numpy/core/src/multiarray/na_mask.c
@@ -123,7 +123,8 @@ fill_raw_byte_array(int ndim, npy_intp *shape,
return 0;
}
-/*
+/*NUMPY_API
+ *
* Assigns the mask value to all the NA mask elements of
* the array.
*
@@ -290,7 +291,8 @@ PyArray_AllocateMaskNA(PyArrayObject *arr,
return 0;
}
-/*
+/*NUMPY_API
+ *
* Assigns the given NA value to all the elements in the array. If
* 'arr' has a mask, masks all the elements of the array.
*
@@ -463,7 +465,7 @@ PyArray_IsNA(PyObject *obj)
* Returns 0 on success, -1 on failure.
*/
NPY_NO_EXPORT int
-PyArray_ReduceMaskArray(int ndim, npy_intp *shape,
+PyArray_ReduceMaskNAArray(int ndim, npy_intp *shape,
PyArray_Descr *src_dtype, char *src_data, npy_intp *src_strides,
PyArray_Descr *dst_dtype, char *dst_data, npy_intp *dst_strides)
{
diff --git a/numpy/core/src/multiarray/shape.c b/numpy/core/src/multiarray/shape.c
index da44f032c..56c08304c 100644
--- a/numpy/core/src/multiarray/shape.c
+++ b/numpy/core/src/multiarray/shape.c
@@ -18,17 +18,19 @@
#include "shape.h"
-#define PyAO PyArrayObject
+#define PyArrayObject PyArrayObject
static int
-_check_ones(PyArrayObject *self, int newnd, intp* newdims, intp *strides);
+_check_ones(PyArrayObject *self, int newnd,
+ npy_intp* newdims, npy_intp *strides, npy_intp *masknastrides);
static int
-_fix_unknown_dimension(PyArray_Dims *newshape, intp s_original);
+_fix_unknown_dimension(PyArray_Dims *newshape, npy_intp s_original);
static int
-_attempt_nocopy_reshape(PyArrayObject *self, int newnd, intp* newdims,
- intp *newstrides, int is_f_order);
+_attempt_nocopy_reshape(PyArrayObject *self, int newnd, npy_intp* newdims,
+ npy_intp *newstrides, npy_intp *newmasknastrides,
+ int is_f_order);
static void
_putzero(char *optr, PyObject *zero, PyArray_Descr *dtype);
@@ -43,15 +45,15 @@ NPY_NO_EXPORT PyObject *
PyArray_Resize(PyArrayObject *self, PyArray_Dims *newshape, int refcheck,
NPY_ORDER order)
{
- intp oldsize, newsize;
+ npy_intp oldsize, newsize;
int new_nd=newshape->len, k, n, elsize;
int refcnt;
- intp* new_dimensions=newshape->ptr;
- intp new_strides[MAX_DIMS];
+ npy_intp* new_dimensions=newshape->ptr;
+ npy_intp new_strides[NPY_MAXDIMS];
size_t sd;
- intp *dimptr;
+ npy_intp *dimptr;
char *new_data;
- intp largest;
+ npy_intp largest;
if (!PyArray_ISONESEGMENT(self)) {
PyErr_SetString(PyExc_ValueError,
@@ -158,8 +160,8 @@ PyArray_Resize(PyArrayObject *self, PyArray_Dims *newshape, int refcheck,
sd = (size_t) PyArray_DESCR(self)->elsize;
sd = (size_t) _array_fill_strides(new_strides, new_dimensions, new_nd, sd,
PyArray_FLAGS(self), &(((PyArrayObject_fieldaccess *)self)->flags));
- memmove(PyArray_DIMS(self), new_dimensions, new_nd*sizeof(intp));
- memmove(PyArray_STRIDES(self), new_strides, new_nd*sizeof(intp));
+ memmove(PyArray_DIMS(self), new_dimensions, new_nd*sizeof(npy_intp));
+ memmove(PyArray_STRIDES(self), new_strides, new_nd*sizeof(npy_intp));
Py_INCREF(Py_None);
return Py_None;
}
@@ -178,29 +180,33 @@ NPY_NO_EXPORT PyObject *
PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims,
NPY_ORDER order)
{
- intp i;
- intp *dimensions = newdims->ptr;
+ npy_intp i;
+ npy_intp *dimensions = newdims->ptr;
PyArrayObject *ret;
- int n = newdims->len;
- Bool same, incref = TRUE;
- intp *strides = NULL;
- intp newstrides[MAX_DIMS];
- int flags;
+ int ndim = newdims->len;
+ npy_bool same, incref = TRUE;
+ npy_intp *strides = NULL;
+ npy_intp newstrides[NPY_MAXDIMS];
+ npy_intp newmasknastrides[NPY_MAXDIMS];
+ int flags, build_maskna_strides = 0;
+
+ printf("in newshape\n"); fflush(stdout);
if (order == NPY_ANYORDER) {
order = PyArray_ISFORTRAN(self);
}
/* Quick check to make sure anything actually needs to be done */
- if (n == PyArray_NDIM(self)) {
+ if (ndim == PyArray_NDIM(self)) {
same = TRUE;
i = 0;
- while (same && i < n) {
+ while (same && i < ndim) {
if (PyArray_DIM(self,i) != dimensions[i]) {
same=FALSE;
}
i++;
}
if (same) {
+ printf("returning view\n"); fflush(stdout);
return PyArray_View(self, NULL, NULL);
}
}
@@ -213,13 +219,16 @@ PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims,
* In this case we don't need to do anything but update strides and
* dimensions. So, we can handle non single-segment cases.
*/
- i = _check_ones(self, n, dimensions, newstrides);
+ i = _check_ones(self, ndim, dimensions, newstrides, newmasknastrides);
if (i == 0) {
+ printf("setting strides to newstrides\n"); fflush(stdout);
strides = newstrides;
}
- flags = PyArray_FLAGS(self);
+ flags = PyArray_FLAGS(self) & ~(NPY_ARRAY_OWNMASKNA |
+ NPY_ARRAY_MASKNA);
if (strides == NULL) {
+ printf("strides are null\n"); fflush(stdout);
/*
* we are really re-shaping not just adding ones to the shape somewhere
* fix any -1 dimensions and check new-dimensions against old size
@@ -239,29 +248,35 @@ PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims,
(PyArray_CHKFLAGS(self, NPY_ARRAY_F_CONTIGUOUS) &&
order == NPY_CORDER)) && (PyArray_NDIM(self) > 1))) {
int success = 0;
- success = _attempt_nocopy_reshape(self,n,dimensions,
- newstrides,order);
+ success = _attempt_nocopy_reshape(self, ndim, dimensions,
+ newstrides, newmasknastrides, order);
if (success) {
+ printf("nocopy reshape succeeded\n"); fflush(stdout);
/* no need to copy the array after all */
strides = newstrides;
- flags = PyArray_FLAGS(self);
}
else {
- PyObject *new;
- new = PyArray_NewCopy(self, order);
- if (new == NULL) {
+ printf("nocopy reshape failed\n"); fflush(stdout);
+ PyObject *newcopy;
+ newcopy = PyArray_NewCopy(self, order);
+ if (newcopy == NULL) {
return NULL;
}
incref = FALSE;
- self = (PyArrayObject *)new;
- flags = PyArray_FLAGS(self);
+ self = (PyArrayObject *)newcopy;
+ build_maskna_strides = 1;
}
+ flags = PyArray_FLAGS(self) & ~(NPY_ARRAY_OWNMASKNA |
+ NPY_ARRAY_MASKNA);
+ }
+ else {
+ build_maskna_strides = 1;
}
/* We always have to interpret the contiguous buffer correctly */
/* Make sure the flags argument is set. */
- if (n > 1) {
+ if (ndim > 1) {
if (order == NPY_FORTRANORDER) {
flags &= ~NPY_ARRAY_C_CONTIGUOUS;
flags |= NPY_ARRAY_F_CONTIGUOUS;
@@ -272,7 +287,7 @@ PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims,
}
}
}
- else if (n > 0) {
+ else if (ndim > 0) {
/*
* replace any 0-valued strides with
* appropriate value to preserve contiguousness
@@ -281,17 +296,17 @@ PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims,
if (strides[0] == 0) {
strides[0] = PyArray_DESCR(self)->elsize;
}
- for (i = 1; i < n; i++) {
+ for (i = 1; i < ndim; i++) {
if (strides[i] == 0) {
strides[i] = strides[i-1] * dimensions[i-1];
}
}
}
else {
- if (strides[n-1] == 0) {
- strides[n-1] = PyArray_DESCR(self)->elsize;
+ if (strides[ndim-1] == 0) {
+ strides[ndim-1] = PyArray_DESCR(self)->elsize;
}
- for (i = n - 2; i > -1; i--) {
+ for (i = ndim - 2; i > -1; i--) {
if (strides[i] == 0) {
strides[i] = strides[i+1] * dimensions[i+1];
}
@@ -300,9 +315,9 @@ PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims,
}
Py_INCREF(PyArray_DESCR(self));
- ret = (PyAO *)PyArray_NewFromDescr(Py_TYPE(self),
+ ret = (PyArrayObject *)PyArray_NewFromDescr(Py_TYPE(self),
PyArray_DESCR(self),
- n, dimensions,
+ ndim, dimensions,
strides,
PyArray_DATA(self),
flags, (PyObject *)self);
@@ -310,6 +325,7 @@ PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims,
if (ret == NULL) {
goto fail;
}
+
if (incref) {
Py_INCREF(self);
}
@@ -317,6 +333,37 @@ PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims,
Py_DECREF(ret);
return NULL;
}
+
+ /* If there's an NA mask, make sure to view it too */
+ if (PyArray_HASMASKNA(self)) {
+ PyArrayObject_fieldaccess *fa = (PyArrayObject_fieldaccess *)ret;
+ fa->maskna_dtype = PyArray_MASKNA_DTYPE(self);
+ Py_INCREF(fa->maskna_dtype);
+ fa->maskna_data = PyArray_MASKNA_DATA(self);
+ if (build_maskna_strides) {
+ npy_intp stride = 1;
+ if (order == NPY_FORTRANORDER) {
+ printf("building fortran strides\n"); fflush(stdout);
+ for (i = 0; i < ndim; ++i) {
+ fa->maskna_strides[i] = stride;
+ printf("stride %d\n", stride);
+ stride *= fa->dimensions[i];
+ }
+ }
+ else {
+ for (i = ndim; i >= 0; --i) {
+ fa->maskna_strides[i] = stride;
+ stride *= fa->dimensions[i];
+ }
+ }
+ }
+ else {
+ memcpy(fa->maskna_strides, newmasknastrides,
+ fa->nd * sizeof(npy_intp));
+ }
+ fa->flags |= NPY_ARRAY_MASKNA;
+ }
+
PyArray_UpdateFlags(ret, NPY_ARRAY_C_CONTIGUOUS | NPY_ARRAY_F_CONTIGUOUS);
return (PyObject *)ret;
@@ -350,12 +397,14 @@ PyArray_Reshape(PyArrayObject *self, PyObject *shape)
/* inserts 0 for strides where dimension will be 1 */
static int
-_check_ones(PyArrayObject *self, int newnd, intp* newdims, intp *strides)
+_check_ones(PyArrayObject *self, int newnd,
+ npy_intp* newdims, npy_intp *strides, npy_intp *masknastrides)
{
int nd;
- intp *dims;
- Bool done=FALSE;
+ npy_intp *dims;
+ npy_bool done=FALSE;
int j, k;
+ int has_maskna = PyArray_HASMASKNA(self);
nd = PyArray_NDIM(self);
dims = PyArray_DIMS(self);
@@ -363,11 +412,17 @@ _check_ones(PyArrayObject *self, int newnd, intp* newdims, intp *strides)
for (k = 0, j = 0; !done && (j < nd || k < newnd);) {
if ((j<nd) && (k<newnd) && (newdims[k] == dims[j])) {
strides[k] = PyArray_STRIDES(self)[j];
+ if (has_maskna) {
+ masknastrides[k] = PyArray_MASKNA_STRIDES(self)[j];
+ }
j++;
k++;
}
else if ((k < newnd) && (newdims[k] == 1)) {
strides[k] = 0;
+ if (has_maskna) {
+ masknastrides[k] = 0;
+ }
k++;
}
else if ((j<nd) && (dims[j] == 1)) {
@@ -431,20 +486,25 @@ _putzero(char *optr, PyObject *zero, PyArray_Descr *dtype)
* stride of the next-fastest index.
*/
static int
-_attempt_nocopy_reshape(PyArrayObject *self, int newnd, intp* newdims,
- intp *newstrides, int is_f_order)
+_attempt_nocopy_reshape(PyArrayObject *self, int newnd, npy_intp* newdims,
+ npy_intp *newstrides, npy_intp *newmasknastrides,
+ int is_f_order)
{
int oldnd;
- intp olddims[MAX_DIMS];
- intp oldstrides[MAX_DIMS];
+ npy_intp olddims[NPY_MAXDIMS];
+ npy_intp oldstrides[NPY_MAXDIMS], oldmasknastrides[NPY_MAXDIMS];
int oi, oj, ok, ni, nj, nk;
int np, op;
+ int has_maskna = PyArray_HASMASKNA(self);
oldnd = 0;
for (oi = 0; oi < PyArray_NDIM(self); oi++) {
if (PyArray_DIMS(self)[oi]!= 1) {
olddims[oldnd] = PyArray_DIMS(self)[oi];
oldstrides[oldnd] = PyArray_STRIDES(self)[oi];
+ if (has_maskna) {
+ oldmasknastrides[oldnd] = PyArray_MASKNA_STRIDES(self)[oi];
+ }
oldnd++;
}
}
@@ -495,14 +555,18 @@ _attempt_nocopy_reshape(PyArrayObject *self, int newnd, intp* newdims,
for (ok = oi; ok < oj - 1; ok++) {
if (is_f_order) {
- if (oldstrides[ok+1] != olddims[ok]*oldstrides[ok]) {
+ if (oldstrides[ok+1] != olddims[ok]*oldstrides[ok] ||
+ (has_maskna && oldmasknastrides[ok+1] !=
+ olddims[ok]*oldmasknastrides[ok])) {
/* not contiguous enough */
return 0;
}
}
else {
/* C order */
- if (oldstrides[ok] != olddims[ok+1]*oldstrides[ok+1]) {
+ if (oldstrides[ok] != olddims[ok+1]*oldstrides[ok+1] ||
+ (has_maskna && oldmasknastrides[ok] !=
+ olddims[ok+1]*oldmasknastrides[ok+1])) {
/* not contiguous enough */
return 0;
}
@@ -514,6 +578,13 @@ _attempt_nocopy_reshape(PyArrayObject *self, int newnd, intp* newdims,
for (nk = ni + 1; nk < nj; nk++) {
newstrides[nk] = newstrides[nk - 1]*newdims[nk - 1];
}
+ if (has_maskna) {
+ newmasknastrides[ni] = oldmasknastrides[oi];
+ for (nk = ni + 1; nk < nj; nk++) {
+ newmasknastrides[nk] =
+ newmasknastrides[nk - 1]*newdims[nk - 1];
+ }
+ }
}
else {
/* C order */
@@ -521,6 +592,13 @@ _attempt_nocopy_reshape(PyArrayObject *self, int newnd, intp* newdims,
for (nk = nj - 1; nk > ni; nk--) {
newstrides[nk - 1] = newstrides[nk]*newdims[nk];
}
+ if (has_maskna) {
+ newmasknastrides[nj - 1] = oldmasknastrides[oj - 1];
+ for (nk = nj - 1; nk > ni; nk--) {
+ newmasknastrides[nk - 1] =
+ newmasknastrides[nk]*newdims[nk];
+ }
+ }
}
ni = nj++;
oi = oj++;
@@ -540,10 +618,10 @@ _attempt_nocopy_reshape(PyArrayObject *self, int newnd, intp* newdims,
}
static int
-_fix_unknown_dimension(PyArray_Dims *newshape, intp s_original)
+_fix_unknown_dimension(PyArray_Dims *newshape, npy_intp s_original)
{
- intp *dimensions;
- intp i_unknown, s_known;
+ npy_intp *dimensions;
+ npy_intp i_unknown, s_known;
int i, n;
static char msg[] = "total size of new array must be unchanged";
@@ -596,8 +674,8 @@ PyArray_Squeeze(PyArrayObject *self)
{
int nd = PyArray_NDIM(self);
int newnd = nd;
- intp dimensions[MAX_DIMS];
- intp strides[MAX_DIMS];
+ npy_intp dimensions[NPY_MAXDIMS];
+ npy_intp strides[NPY_MAXDIMS];
int i, j;
PyArrayObject *ret;
PyArray_Descr *dtype;
@@ -643,7 +721,7 @@ NPY_NO_EXPORT PyObject *
PyArray_SwapAxes(PyArrayObject *ap, int a1, int a2)
{
PyArray_Dims new_axes;
- intp dims[MAX_DIMS];
+ npy_intp dims[NPY_MAXDIMS];
int n, i, val;
PyObject *ret;
@@ -699,9 +777,9 @@ PyArray_SwapAxes(PyArrayObject *ap, int a1, int a2)
NPY_NO_EXPORT PyObject *
PyArray_Transpose(PyArrayObject *ap, PyArray_Dims *permute)
{
- intp *axes, axis;
- intp i, n;
- intp permutation[MAX_DIMS], reverse_permutation[MAX_DIMS];
+ npy_intp *axes, axis;
+ npy_intp i, n;
+ npy_intp permutation[NPY_MAXDIMS], reverse_permutation[NPY_MAXDIMS];
PyArrayObject *ret = NULL;
if (permute == NULL) {
@@ -839,7 +917,7 @@ NPY_NO_EXPORT PyObject *
PyArray_Ravel(PyArrayObject *a, NPY_ORDER order)
{
PyArray_Dims newdim = {NULL,1};
- intp val[1] = {-1};
+ npy_intp val[1] = {-1};
newdim.ptr = val;
@@ -918,7 +996,7 @@ NPY_NO_EXPORT PyObject *
PyArray_Flatten(PyArrayObject *a, NPY_ORDER order)
{
PyArrayObject *ret;
- intp size;
+ npy_intp size;
if (order == NPY_ANYORDER) {
order = PyArray_ISFORTRAN(a) ? NPY_FORTRANORDER : NPY_CORDER;
diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c
index 1709795f1..1dd0ef730 100644
--- a/numpy/core/src/umath/ufunc_object.c
+++ b/numpy/core/src/umath/ufunc_object.c
@@ -2433,6 +2433,76 @@ get_binary_op_function(PyUFuncObject *self, int *otype,
}
/*
+ * Given the output type, finds the specified binary op, and
+ * returns a masked inner loop. The ufunc must have nin==2
+ * and nout==1. The function may modify otype if the given
+ * type isn't found.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+get_masked_binary_op_function(PyUFuncObject *self, PyArrayObject *arr,
+ int otype,
+ PyArray_Descr **out_dtype,
+ PyUFuncGenericMaskedFunction *out_innerloop,
+ NpyAuxData **out_innerloopdata)
+{
+ int i, retcode;
+ PyArrayObject *op[3] = {arr, arr, arr};
+ PyArray_Descr *dtype[3] = {NULL, NULL, NULL};
+ PyObject *type_tup = NULL;
+
+ NPY_UF_DBG_PRINT1("Getting masked binary op function for type number %d\n",
+ *otype);
+
+ *out_dtype = NULL;
+
+ /* Build a type tuple if otype is specified */
+ if (otype != NPY_NOTYPE) {
+ PyArray_Descr *otype_dtype = PyArray_DescrFromType(otype);
+ if (otype_dtype == NULL) {
+ return -1;
+ }
+ type_tup = Py_BuildValue("(N)", otype_dtype);
+ if (type_tup == NULL) {
+ return -1;
+ }
+ }
+
+ /* Use the type resolution function to find our loop */
+ retcode = self->type_resolution_masked_function(self, NPY_SAFE_CASTING,
+ op, type_tup, dtype,
+ out_innerloop, out_innerloopdata);
+ Py_XDECREF(type_tup);
+ if (retcode == -1) {
+ return -1;
+ }
+ else if (retcode == -2) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "type resolution returned NotImplemented");
+ return -1;
+ }
+
+ /* The selected dtypes should all be equivalent */
+ if (!PyArray_EquivTypes(dtype[0], dtype[1]) ||
+ !PyArray_EquivTypes(dtype[1], dtype[2])) {
+ for (i = 0; i < 3; ++i) {
+ Py_DECREF(dtype[i]);
+ }
+ PyErr_SetString(PyExc_RuntimeError,
+ "could not find a masked binary loop appropriate for "
+ "reduce ufunc");
+ return -1;
+ }
+
+ *out_dtype = dtype[0];
+ Py_DECREF(dtype[1]);
+ Py_DECREF(dtype[2]);
+
+ return 0;
+}
+
+/*
* Allocates a result array for a reduction operation, with
* dimensions matching 'arr' except set to 1 with 0 stride
* whereever axis_flags is True. Dropping the reduction axes
@@ -2574,34 +2644,15 @@ conform_reduce_result(int ndim, npy_bool *axis_flags, PyArrayObject *out)
/* Allocate 'result' or conform 'out' to 'self' (in 'result') */
static PyArrayObject *
allocate_or_conform_reduce_result(PyArrayObject *arr, PyArrayObject *out,
- npy_bool *axis_flags, int otype_final, int keepmask)
+ npy_bool *axis_flags, PyArray_Descr * otype_dtype,
+ int keepmask)
{
if (out == NULL) {
PyArrayObject *result;
- PyArray_Descr *dtype = NULL;
- /*
- * Set up the output data type, using the input's exact
- * data type if the type number didn't change to preserve
- * metadata
- */
- if (PyArray_DESCR(arr)->type_num == otype_final) {
- if (PyArray_ISNBO(PyArray_DESCR(arr)->byteorder)) {
- dtype = PyArray_DESCR(arr);
- Py_INCREF(dtype);
- }
- else {
- dtype = PyArray_DescrNewByteorder(PyArray_DESCR(arr),
- NPY_NATIVE);
- }
- }
- else {
- dtype = PyArray_DescrFromType(otype_final);
- }
- if (dtype == NULL) {
- return NULL;
- }
- result = allocate_reduce_result(arr, axis_flags, dtype);
+ printf("allocating result\n"); fflush(stdout);
+ Py_INCREF(otype_dtype);
+ result = allocate_reduce_result(arr, axis_flags, otype_dtype);
/* Allocate an NA mask if necessary */
if (keepmask && result != NULL && PyArray_HASMASKNA(arr)) {
@@ -2614,6 +2665,7 @@ allocate_or_conform_reduce_result(PyArrayObject *arr, PyArrayObject *out,
return result;
}
else {
+ printf("conforming result\n"); fflush(stdout);
return conform_reduce_result(PyArray_NDIM(arr), axis_flags, out);
}
}
@@ -2767,16 +2819,20 @@ static PyObject *
PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
int naxes, int *axes, int otype, int skipna)
{
- int ndim;
- int iaxes;
+ int iaxes, ndim, retcode;
+ PyArray_Descr *otype_dtype = NULL;
npy_bool axis_flags[NPY_MAXDIMS];
PyArrayObject *arr_view = NULL, *result = NULL;
- int otype_final;
- /* The selected inner loop */
+ /* The normal selected inner loop */
PyUFuncGenericFunction innerloop = NULL;
void *innerloopdata = NULL;
+ /* The masked selected inner loop */
+ int use_maskna = 0;
+ PyUFuncGenericMaskedFunction maskedinnerloop = NULL;
+ NpyAuxData *maskedinnerloopdata = NULL;
+
char *ufunc_name = self->name ? self->name : "(unknown)";
NPY_BEGIN_THREADS_DEF;
@@ -2808,10 +2864,45 @@ PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
axis_flags[axis] = 1;
}
+ use_maskna = PyArray_HASMASKNA(arr);
+
/* Get the appropriate ufunc inner loop */
- otype_final = otype;
- if (get_binary_op_function(self, &otype_final,
- &innerloop, &innerloopdata) < 0) {
+ if (use_maskna) {
+ retcode = get_masked_binary_op_function(self, arr, otype,
+ &otype_dtype, &maskedinnerloop, &maskedinnerloopdata);
+ }
+ else {
+ /*
+ * TODO: Switch to using the type resolution function like
+ * in the masked case.
+ */
+ int otype_final = otype;
+ retcode = get_binary_op_function(self, &otype_final,
+ &innerloop, &innerloopdata);
+
+ /*
+ * Set up the output data type, using the input's exact
+ * data type if the type number didn't change to preserve
+ * metadata
+ */
+ if (PyArray_DESCR(arr)->type_num == otype_final) {
+ if (PyArray_ISNBO(PyArray_DESCR(arr)->byteorder)) {
+ otype_dtype = PyArray_DESCR(arr);
+ Py_INCREF(otype_dtype);
+ }
+ else {
+ otype_dtype = PyArray_DescrNewByteorder(PyArray_DESCR(arr),
+ NPY_NATIVE);
+ }
+ }
+ else {
+ otype_dtype = PyArray_DescrFromType(otype_final);
+ }
+ if (otype_dtype == NULL) {
+ return NULL;
+ }
+ }
+ if (retcode < 0) {
PyArray_Descr *dtype = PyArray_DescrFromType(otype);
PyErr_Format(PyExc_ValueError,
"could not find a matching type for %s.reduce, "
@@ -2828,11 +2919,62 @@ PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
/* Allocate an output or conform 'out' to 'self' */
result = allocate_or_conform_reduce_result(arr, out,
- axis_flags, otype_final, !skipna);
+ axis_flags, otype_dtype, !skipna);
if (result == NULL) {
return NULL;
}
+ /* Prepare the NA mask if there is one */
+ if (use_maskna) {
+ printf("doing masked %s.reduce\n", ufunc_name); fflush(stdout);
+ /*
+ * Do the reduction on the NA mask before the data. This way
+ * we can avoid modifying the outputs which end up masked, obeying
+ * the required NA masking semantics.
+ */
+ if (!skipna) {
+ if (PyArray_HASMASKNA(result)) {
+ if (PyArray_ReduceMaskNAArray(ndim, PyArray_DIMS(arr),
+ PyArray_MASKNA_DTYPE(arr),
+ PyArray_MASKNA_DATA(arr),
+ PyArray_MASKNA_STRIDES(arr),
+ PyArray_MASKNA_DTYPE(result),
+ PyArray_MASKNA_DATA(result),
+ PyArray_MASKNA_STRIDES(result)) < 0) {
+ goto fail;
+ }
+ }
+ else if (PyArray_ContainsNA(arr)) {
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot assign NA value to an array which "
+ "does not support NAs");
+ goto fail;
+ }
+ else {
+ use_maskna = 0;
+ }
+
+ /* Short circuit any calculation if the result is a single NA */
+ if (PyArray_SIZE(result) == 1 &&
+ !NpyMaskValue_IsExposed(
+ (npy_mask)*PyArray_MASKNA_DATA(result))) {
+ goto finish;
+ }
+ }
+ else {
+ /*
+ * If there's no identity, validate that there is no
+ * reduction which is all NA values.
+ */
+ if (self->identity == PyUFunc_None) {
+ PyErr_SetString(PyExc_ValueError,
+ "skipna=True together with a non-identity reduction "
+ "isn't implemented yet");
+ goto fail;
+ }
+ }
+ }
+
/*
* Initialize 'result' to the identity or initial elements
* copied from 'arr', and create a view of 'arr' containing
@@ -2849,11 +2991,8 @@ PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
op[1] = arr_view;
/* op is length 3 in case the inner loop wanted these as its data */
op[2] = result;
- op_dtypes[0] = PyArray_DescrFromType(otype_final);
- if (op_dtypes[0] == NULL) {
- goto fail;
- }
- op_dtypes[1] = op_dtypes[0];
+ op_dtypes[0] = otype_dtype;
+ op_dtypes[1] = otype_dtype;
flags = NPY_ITER_BUFFERED |
NPY_ITER_EXTERNAL_LOOP |
@@ -2867,6 +3006,32 @@ PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
op_flags[1] = NPY_ITER_READONLY |
NPY_ITER_ALIGNED;
+ /* Add mask-related flags */
+ if (use_maskna) {
+ if (skipna) {
+ /* Need the input's mask to determine what to skip */
+ op_flags[0] |= NPY_ITER_USE_MASKNA;
+ /* The output's mask has been set to all exposed already */
+ op_flags[1] |= NPY_ITER_IGNORE_MASKNA;
+
+ /* TODO: allocate a temporary buffer for inverting the mask */
+ }
+ else {
+ /* The input's mask is already incorporated in the output's mask */
+ op_flags[0] |= NPY_ITER_IGNORE_MASKNA;
+ /* Iterate over the output's mask */
+ op_flags[1] |= NPY_ITER_USE_MASKNA;
+ }
+ }
+ else {
+ /*
+ * If 'out' had no mask, and 'arr' did, we checked that 'arr'
+ * contains no NA values and can ignore the masks.
+ */
+ op_flags[0] |= NPY_ITER_IGNORE_MASKNA;
+ op_flags[1] |= NPY_ITER_IGNORE_MASKNA;
+ }
+
iter = NpyIter_MultiNew(2, op, flags,
NPY_KEEPORDER, NPY_UNSAFE_CASTING,
op_flags,
@@ -2894,23 +3059,44 @@ PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
count_ptr = NpyIter_GetInnerLoopSizePtr(iter);
needs_api = NpyIter_IterationNeedsAPI(iter) ||
- otype_final == NPY_OBJECT;
+ PyDataType_REFCHK(otype_dtype);
if (!needs_api) {
NPY_BEGIN_THREADS;
}
- do {
- /* Turn the two items into three for the inner loop */
- dataptr_copy[0] = dataptr[0];
- dataptr_copy[1] = dataptr[1];
- dataptr_copy[2] = dataptr[0];
- stride_copy[0] = stride[0];
- stride_copy[1] = stride[1];
- stride_copy[2] = stride[0];
- innerloop(dataptr_copy, count_ptr,
- stride_copy, innerloopdata);
- } while (iternext(iter));
+ /* Straightforward reduction */
+ if (!use_maskna) {
+ do {
+ /* Turn the two items into three for the inner loop */
+ dataptr_copy[0] = dataptr[0];
+ dataptr_copy[1] = dataptr[1];
+ dataptr_copy[2] = dataptr[0];
+ stride_copy[0] = stride[0];
+ stride_copy[1] = stride[1];
+ stride_copy[2] = stride[0];
+ innerloop(dataptr_copy, count_ptr,
+ stride_copy, innerloopdata);
+ } while (iternext(iter));
+ }
+ /* Masked reduction */
+ else {
+ do {
+ /* Turn the two items into three for the inner loop */
+ dataptr_copy[0] = dataptr[0];
+ dataptr_copy[1] = dataptr[1];
+ dataptr_copy[2] = dataptr[0];
+ stride_copy[0] = stride[0];
+ stride_copy[1] = stride[1];
+ stride_copy[2] = stride[0];
+ /*
+ * If skipna=True, this masks based on the mask in 'arr',
+ * otherwise it masks based on the mask in 'result'
+ */
+ maskedinnerloop(dataptr_copy, dataptr[2], count_ptr,
+ stride_copy, stride[2], maskedinnerloopdata);
+ } while (iternext(iter));
+ }
if (!needs_api) {
NPY_END_THREADS;
@@ -2921,6 +3107,8 @@ PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
}
}
+finish:
+
/* Strip out the extra 'one' dimensions in the result */
if (out == NULL) {
strip_flagged_dimensions(result, axis_flags);
@@ -2931,9 +3119,12 @@ PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
Py_INCREF(result);
}
- NpyIter_Deallocate(iter);
- Py_DECREF(arr_view);
- Py_DECREF(op_dtypes[0]);
+ if (iter != NULL) {
+ NpyIter_Deallocate(iter);
+ }
+ Py_XDECREF(arr_view);
+ Py_XDECREF(otype_dtype);
+ NPY_AUXDATA_FREE(maskedinnerloopdata);
return (PyObject *)result;
fail:
@@ -2942,7 +3133,8 @@ fail:
}
Py_XDECREF(result);
Py_XDECREF(arr_view);
- Py_XDECREF(op_dtypes[0]);
+ Py_XDECREF(otype_dtype);
+ NPY_AUXDATA_FREE(maskedinnerloopdata);
return NULL;
}
diff --git a/numpy/core/tests/test_maskna.py b/numpy/core/tests/test_maskna.py
index c2ae14d86..dbd435c8c 100644
--- a/numpy/core/tests/test_maskna.py
+++ b/numpy/core/tests/test_maskna.py
@@ -225,6 +225,25 @@ def test_array_maskna_array_function_1D():
assert_(c.flags.ownmaskna)
assert_(not (c is b_view))
+def test_array_maskna_reshape():
+ # Simple reshape 1D -> 2D
+ a = np.arange(6, maskna=True)
+ a[1] = np.NA
+ a[5] = np.NA
+
+ b = a.reshape(2,3)
+ assert_(b.base is a)
+ assert_equal(b.shape, (2,3))
+ assert_(b.flags.maskna)
+ assert_(not b.flags.ownmaskna)
+ assert_equal(np.isna(b), [[0,1,0],[0,0,1]])
+
+ b = a.reshape(2,3,order='F')
+ assert_equal(b.shape, (2,3))
+ assert_(b.flags.maskna)
+ assert_(not b.flags.ownmaskna)
+ assert_equal(np.isna(b), [[0,0,0],[1,0,1]])
+
def test_array_maskna_view_NA_assignment_1D():
a = np.arange(10)
a_ref = a.copy()