summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--numpy/core/code_generators/numpy_api.py3
-rw-r--r--numpy/core/include/numpy/ndarraytypes.h72
-rw-r--r--numpy/core/src/multiarray/ctors.c180
-rw-r--r--numpy/core/src/multiarray/dtype_transfer.c124
-rw-r--r--numpy/core/src/multiarray/flagsobject.c110
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c68
-rw-r--r--numpy/core/src/multiarray/na_mask.c135
-rw-r--r--numpy/core/src/multiarray/nditer_api.c1
-rw-r--r--numpy/core/src/multiarray/shape.c11
-rw-r--r--numpy/core/src/multiarray/shape.h3
-rw-r--r--numpy/core/src/private/lowlevel_strided_loops.h16
11 files changed, 605 insertions, 118 deletions
diff --git a/numpy/core/code_generators/numpy_api.py b/numpy/core/code_generators/numpy_api.py
index c8a7bdb01..ec8388311 100644
--- a/numpy/core/code_generators/numpy_api.py
+++ b/numpy/core/code_generators/numpy_api.py
@@ -69,7 +69,7 @@ multiarray_types_api = {
'PyHalfArrType_Type': 217,
'NpyIter_Type': 218,
# End 1.6 API
- 'NpyNA_Type': 286,
+ 'NpyNA_Type': 287,
}
#define NPY_NUMUSERTYPES (*(int *)PyArray_API[6])
@@ -323,6 +323,7 @@ multiarray_funcs_api = {
'PyArray_SetBaseObject': 283,
'PyArray_HasNASupport': 284,
'PyArray_ContainsNA': 285,
+ 'PyArray_AllocateMaskNA': 286,
}
ufunc_types_api = {
diff --git a/numpy/core/include/numpy/ndarraytypes.h b/numpy/core/include/numpy/ndarraytypes.h
index e99b3fb55..1b3d0b2a1 100644
--- a/numpy/core/include/numpy/ndarraytypes.h
+++ b/numpy/core/include/numpy/ndarraytypes.h
@@ -662,7 +662,7 @@ typedef struct tagPyArrayObject_fieldaccess {
* If no mask: NULL
* If mask : bool/uint8/structured dtype of mask dtypes
*/
- PyArray_Descr *maskna_descr;
+ PyArray_Descr *maskna_dtype;
/*
* Raw data buffer for mask. If the array has the flag
* NPY_ARRAY_OWNMASKNA enabled, it owns this memory and
@@ -672,7 +672,8 @@ typedef struct tagPyArrayObject_fieldaccess {
/*
* Just like dimensions and strides point into the same memory
* buffer, we now just make that buffer 3x the nd instead of 2x
- * and use the same buffer.
+ * and use the same buffer. This is always allocated, regardless
+ * of whether there is an NA mask or not.
*/
npy_intp *maskna_strides;
} PyArrayObject_fieldaccess;
@@ -746,6 +747,9 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *);
/*
* Means c-style contiguous (last index varies the fastest). The data
* elements right after each other.
+ *
+ * This flag may be requested in constructor functions.
+ * This flag may be tested for in PyArray_FLAGS(arr).
*/
#define NPY_ARRAY_C_CONTIGUOUS 0x0001
@@ -753,6 +757,9 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *);
* Set if array is a contiguous Fortran array: the first index varies
* the fastest in memory (strides array is reverse of C-contiguous
* array)
+ *
+ * This flag may be requested in constructor functions.
+ * This flag may be tested for in PyArray_FLAGS(arr).
*/
#define NPY_ARRAY_F_CONTIGUOUS 0x0002
@@ -764,12 +771,16 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *);
/*
* If set, the array owns the data: it will be free'd when the array
* is deleted.
+ *
+ * This flag may be tested for in PyArray_FLAGS(arr).
*/
#define NPY_ARRAY_OWNDATA 0x0004
/*
* An array never has the next four set; they're only used as parameter
* flags to the the various FromAny functions
+ *
+ * This flag may be requested in constructor functions.
*/
/* Cause a cast to occur regardless of whether or not it is safe. */
@@ -778,15 +789,23 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *);
/*
* Always copy the array. Returned arrays are always CONTIGUOUS,
* ALIGNED, and WRITEABLE.
+ *
+ * This flag may be requested in constructor functions.
*/
#define NPY_ARRAY_ENSURECOPY 0x0020
-/* Make sure the returned array is a base-class ndarray */
+/*
+ * Make sure the returned array is a base-class ndarray
+ *
+ * This flag may be requested in constructor functions.
+ */
#define NPY_ARRAY_ENSUREARRAY 0x0040
/*
* Make sure that the strides are in units of the element size Needed
* for some operations with record-arrays.
+ *
+ * This flag may be requested in constructor functions.
*/
#define NPY_ARRAY_ELEMENTSTRIDES 0x0080
@@ -795,27 +814,64 @@ typedef int (PyArray_FinalizeFunc)(PyArrayObject *, PyObject *);
* stored according to how the compiler would align things (e.g., an
* array of integers (4 bytes each) starts on a memory address that's
* a multiple of 4)
+ *
+ * This flag may be requested in constructor functions.
+ * This flag may be tested for in PyArray_FLAGS(arr).
*/
#define NPY_ARRAY_ALIGNED 0x0100
-/* Array data has the native endianness */
+/*
+ * Array data has the native endianness
+ *
+ * This flag may be requested in constructor functions.
+ */
#define NPY_ARRAY_NOTSWAPPED 0x0200
-/* Array data is writeable */
+/*
+ * Array data is writeable
+ *
+ * This flag may be requested in constructor functions.
+ * This flag may be tested for in PyArray_FLAGS(arr).
+ */
#define NPY_ARRAY_WRITEABLE 0x0400
/*
* If this flag is set, then base contains a pointer to an array of
* the same size that should be updated with the current contents of
* this array when this array is deallocated
+ *
+ * This flag may be requested in constructor functions.
+ * This flag may be tested for in PyArray_FLAGS(arr).
*/
#define NPY_ARRAY_UPDATEIFCOPY 0x1000
/*
+ * If this flag is set, then the array has an NA mask corresponding
+ * to the array data. If the flag NPY_ARRAY_OWNMASKNA is requested
+ * in a constructor, this flag is also implied even if it is not set.
+ *
+ * This flag may be requested in constructor functions.
+ * This flag may be tested for in PyArray_FLAGS(arr).
+ */
+#define NPY_ARRAY_MASKNA 0x2000
+
+/*
* If this flag is set, then the array owns the memory for the
* missing values NA mask.
+ *
+ * This flag may be requested in constructor functions.
+ * This flag may be tested for in PyArray_FLAGS(arr).
+ */
+#define NPY_ARRAY_OWNMASKNA 0x4000
+
+/*
+ * If this flag is set, then arrays which have an NA mask, or arrays
+ * which have an NA dtype are permitted to pass through. If not,
+ * a array with NA support causes an error to be thrown.
+ *
+ * This flag may be requested in constructor functions.
*/
-#define NPY_ARRAY_OWNMASKNA 0x2000
+#define NPY_ARRAY_ALLOWNA 0x8000
#define NPY_ARRAY_BEHAVED (NPY_ARRAY_ALIGNED | \
@@ -1511,9 +1567,9 @@ PyArray_CLEARFLAGS(PyArrayObject *arr, int flags)
/* Access to the missing values NA mask, added in 1.7 */
static NPY_INLINE PyArray_Descr *
-PyArray_MASKNA_DESCR(PyArrayObject *arr)
+PyArray_MASKNA_DTYPE(PyArrayObject *arr)
{
- return ((PyArrayObject_fieldaccess *)arr)->maskna_descr;
+ return ((PyArrayObject_fieldaccess *)arr)->maskna_dtype;
}
static NPY_INLINE npy_mask *
diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c
index b527a1074..7d2c0c488 100644
--- a/numpy/core/src/multiarray/ctors.c
+++ b/numpy/core/src/multiarray/ctors.c
@@ -770,7 +770,7 @@ discover_itemsize(PyObject *s, int nd, int *itemsize)
static int
discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it,
int stop_at_string, int stop_at_tuple,
- int *out_is_object)
+ int *out_is_object, int *out_contains_na)
{
PyObject *e;
int r, n, i;
@@ -802,6 +802,11 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it,
return 0;
}
+ if (NpyNA_Check(obj)) {
+ *out_contains_na = 1;
+ return 0;
+ }
+
/* obj is not a Sequence */
if (!PySequence_Check(obj) ||
#if defined(NPY_PY3K)
@@ -969,7 +974,7 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it,
}
r = discover_dimensions(e, &maxndim_m1, d + 1, check_it,
stop_at_string, stop_at_tuple,
- out_is_object);
+ out_is_object, out_contains_na);
Py_DECREF(e);
if (r < 0) {
return r;
@@ -993,7 +998,7 @@ discover_dimensions(PyObject *obj, int *maxndim, npy_intp *d, int check_it,
}
r = discover_dimensions(e, &maxndim_m1, dtmp, check_it,
stop_at_string, stop_at_tuple,
- out_is_object);
+ out_is_object, out_contains_na);
Py_DECREF(e);
if (r < 0) {
return r;
@@ -1144,12 +1149,13 @@ PyArray_NewFromDescr(PyTypeObject *subtype, PyArray_Descr *descr, int nd,
fa->weakreflist = (PyObject *)NULL;
if (nd > 0) {
- fa->dimensions = PyDimMem_NEW(2*nd);
+ fa->dimensions = PyDimMem_NEW(3*nd);
if (fa->dimensions == NULL) {
PyErr_NoMemory();
goto fail;
}
fa->strides = fa->dimensions + nd;
+ fa->maskna_strides = fa->dimensions + 2 * nd;
memcpy(fa->dimensions, dims, sizeof(npy_intp)*nd);
if (strides == NULL) { /* fill it in */
sd = _array_fill_strides(fa->strides, dims, nd, sd,
@@ -1323,7 +1329,9 @@ PyArray_NewLikeArray(PyArrayObject *prototype, NPY_ORDER order,
_npy_stride_sort_item strideperm[NPY_MAXDIMS];
int i;
- PyArray_CreateSortedStridePerm(prototype, strideperm);
+ PyArray_CreateSortedStridePerm(PyArray_NDIM(prototype),
+ PyArray_STRIDES(prototype),
+ strideperm);
/* Build the new strides */
stride = dtype->elsize;
@@ -1473,72 +1481,26 @@ fail:
#endif
}
-/*NUMPY_API
- * Retrieves the array parameters for viewing/converting an arbitrary
- * PyObject* to a NumPy array. This allows the "innate type and shape"
- * of Python list-of-lists to be discovered without
- * actually converting to an array.
- *
- * In some cases, such as structured arrays and the __array__ interface,
- * a data type needs to be used to make sense of the object. When
- * this is needed, provide a Descr for 'requested_dtype', otherwise
- * provide NULL. This reference is not stolen. Also, if the requested
- * dtype doesn't modify the interpretation of the input, out_dtype will
- * still get the "innate" dtype of the object, not the dtype passed
- * in 'requested_dtype'.
- *
- * If writing to the value in 'op' is desired, set the boolean
- * 'writeable' to 1. This raises an error when 'op' is a scalar, list
- * of lists, or other non-writeable 'op'.
- *
- * Result: When success (0 return value) is returned, either out_arr
- * is filled with a non-NULL PyArrayObject and
- * the rest of the parameters are untouched, or out_arr is
- * filled with NULL, and the rest of the parameters are
- * filled.
- *
- * Typical usage:
- *
- * PyArrayObject *arr = NULL;
- * PyArray_Descr *dtype = NULL;
- * int ndim = 0;
- * npy_intp dims[NPY_MAXDIMS];
+/*
+ * A slight generalization of PyArray_GetArrayParamsFromObject,
+ * which also returns whether the input data contains any numpy.NA
+ * values.
*
- * if (PyArray_GetArrayParamsFromObject(op, NULL, 1, &dtype,
- * &ndim, &dims, &arr, NULL) < 0) {
- * return NULL;
- * }
- * if (arr == NULL) {
- * ... validate/change dtype, validate flags, ndim, etc ...
- * // Could make custom strides here too
- * arr = PyArray_NewFromDescr(&PyArray_Type, dtype, ndim,
- * dims, NULL,
- * is_f_order ? NPY_ARRAY_F_CONTIGUOUS : 0,
- * NULL);
- * if (arr == NULL) {
- * return NULL;
- * }
- * if (PyArray_CopyObject(arr, op) < 0) {
- * Py_DECREF(arr);
- * return NULL;
- * }
- * }
- * else {
- * ... in this case the other parameters weren't filled, just
- * validate and possibly copy arr itself ...
- * }
- * ... use arr ...
+ * This isn't exposed in the public API.
*/
NPY_NO_EXPORT int
-PyArray_GetArrayParamsFromObject(PyObject *op,
+PyArray_GetArrayParamsFromObjectEx(PyObject *op,
PyArray_Descr *requested_dtype,
npy_bool writeable,
PyArray_Descr **out_dtype,
int *out_ndim, npy_intp *out_dims,
+ int *out_contains_na,
PyArrayObject **out_arr, PyObject *context)
{
PyObject *tmp;
+ *out_contains_na = 0;
+
/* If op is an array */
if (PyArray_Check(op)) {
if (writeable && !PyArray_ISWRITEABLE((PyArrayObject *)op)) {
@@ -1687,7 +1649,7 @@ PyArray_GetArrayParamsFromObject(PyObject *op,
is_object = 0;
if (discover_dimensions(op, out_ndim, out_dims, check_it,
stop_at_string, stop_at_tuple,
- &is_object) < 0) {
+ &is_object, out_contains_na) < 0) {
Py_DECREF(*out_dtype);
if (PyErr_Occurred()) {
return -1;
@@ -1763,6 +1725,76 @@ PyArray_GetArrayParamsFromObject(PyObject *op,
}
/*NUMPY_API
+ * Retrieves the array parameters for viewing/converting an arbitrary
+ * PyObject* to a NumPy array. This allows the "innate type and shape"
+ * of Python list-of-lists to be discovered without
+ * actually converting to an array.
+ *
+ * In some cases, such as structured arrays and the __array__ interface,
+ * a data type needs to be used to make sense of the object. When
+ * this is needed, provide a Descr for 'requested_dtype', otherwise
+ * provide NULL. This reference is not stolen. Also, if the requested
+ * dtype doesn't modify the interpretation of the input, out_dtype will
+ * still get the "innate" dtype of the object, not the dtype passed
+ * in 'requested_dtype'.
+ *
+ * If writing to the value in 'op' is desired, set the boolean
+ * 'writeable' to 1. This raises an error when 'op' is a scalar, list
+ * of lists, or other non-writeable 'op'.
+ *
+ * Result: When success (0 return value) is returned, either out_arr
+ * is filled with a non-NULL PyArrayObject and
+ * the rest of the parameters are untouched, or out_arr is
+ * filled with NULL, and the rest of the parameters are
+ * filled.
+ *
+ * Typical usage:
+ *
+ * PyArrayObject *arr = NULL;
+ * PyArray_Descr *dtype = NULL;
+ * int ndim = 0;
+ * npy_intp dims[NPY_MAXDIMS];
+ *
+ * if (PyArray_GetArrayParamsFromObject(op, NULL, 1, &dtype,
+ * &ndim, &dims, &arr, NULL) < 0) {
+ * return NULL;
+ * }
+ * if (arr == NULL) {
+ * ... validate/change dtype, validate flags, ndim, etc ...
+ * // Could make custom strides here too
+ * arr = PyArray_NewFromDescr(&PyArray_Type, dtype, ndim,
+ * dims, NULL,
+ * is_f_order ? NPY_ARRAY_F_CONTIGUOUS : 0,
+ * NULL);
+ * if (arr == NULL) {
+ * return NULL;
+ * }
+ * if (PyArray_CopyObject(arr, op) < 0) {
+ * Py_DECREF(arr);
+ * return NULL;
+ * }
+ * }
+ * else {
+ * ... in this case the other parameters weren't filled, just
+ * validate and possibly copy arr itself ...
+ * }
+ * ... use arr ...
+ */
+NPY_NO_EXPORT int
+PyArray_GetArrayParamsFromObject(PyObject *op,
+ PyArray_Descr *requested_dtype,
+ npy_bool writeable,
+ PyArray_Descr **out_dtype,
+ int *out_ndim, npy_intp *out_dims,
+ PyArrayObject **out_arr, PyObject *context)
+{
+ int contains_na = 0;
+ return PyArray_GetArrayParamsFromObjectEx(op, requested_dtype,
+ writeable, out_dtype, out_ndim, out_dims,
+ &contains_na, out_arr, context);
+}
+
+/*NUMPY_API
* Does not check for NPY_ARRAY_ENSURECOPY and NPY_ARRAY_NOTSWAPPED in flags
* Steals a reference to newtype --- which can be NULL
*/
@@ -1776,13 +1808,13 @@ PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth,
*/
PyArrayObject *arr = NULL, *ret;
PyArray_Descr *dtype = NULL;
- int ndim = 0;
+ int ndim = 0, contains_na = 0;
npy_intp dims[NPY_MAXDIMS];
/* Get either the array or its parameters if it isn't an array */
- if (PyArray_GetArrayParamsFromObject(op, newtype,
+ if (PyArray_GetArrayParamsFromObjectEx(op, newtype,
0, &dtype,
- &ndim, dims, &arr, context) < 0) {
+ &ndim, dims, &contains_na, &arr, context) < 0) {
Py_XDECREF(newtype);
return NULL;
}
@@ -1851,9 +1883,21 @@ PyArray_FromAny(PyObject *op, PyArray_Descr *newtype, int min_depth,
/* Create an array and copy the data */
ret = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, newtype,
- ndim, dims,
- NULL, NULL,
- flags&NPY_ARRAY_F_CONTIGUOUS, NULL);
+ ndim, dims,
+ NULL, NULL,
+ flags&NPY_ARRAY_F_CONTIGUOUS, NULL);
+ /*
+ * Add an NA mask if requested, or if allowed and the data
+ * has NAs
+ */
+ if ((flags & (NPY_ARRAY_MASKNA | NPY_ARRAY_OWNMASKNA)) != 0 ||
+ (contains_na && (flags & NPY_ARRAY_ALLOWNA))) {
+ if (PyArray_AllocateMaskNA(ret,
+ (flags&NPY_ARRAY_OWNMASKNA) != 0, 0) < 0) {
+ Py_DECREF(ret);
+ return NULL;
+ }
+ }
if (ret != NULL) {
if (ndim > 0) {
if (PyArray_AssignFromSequence(ret, op) < 0) {
@@ -1952,7 +1996,7 @@ PyArray_CheckFromAny(PyObject *op, PyArray_Descr *descr, int min_depth,
PyArray_DESCR_REPLACE(descr);
}
if (descr) {
- descr->byteorder = PyArray_NATIVE;
+ descr->byteorder = NPY_NATIVE;
}
}
diff --git a/numpy/core/src/multiarray/dtype_transfer.c b/numpy/core/src/multiarray/dtype_transfer.c
index 045e6c4b5..a14c7de36 100644
--- a/numpy/core/src/multiarray/dtype_transfer.c
+++ b/numpy/core/src/multiarray/dtype_transfer.c
@@ -25,6 +25,7 @@
#include "_datetime.h"
#include "datetime_strings.h"
+#include "shape.h"
#include "lowlevel_strided_loops.h"
#define NPY_LOWLEVEL_BUFFER_BLOCKSIZE 128
@@ -3870,3 +3871,126 @@ PyArray_CastRawArrays(npy_intp count,
/* If needs_api was set to 1, it may have raised a Python exception */
return (needs_api && PyErr_Occurred()) ? NPY_FAIL : NPY_SUCCEED;
}
+
+/*
+ * Casts the elements from one n-dimensional array to another n-dimensional
+ * array with identical shape but possibly different strides and dtypes.
+ * Does not account for overlap.
+ *
+ * Returns NPY_SUCCEED or NPY_FAIL.
+ */
+NPY_NO_EXPORT int
+PyArray_CastRawNDimArrays(int ndim, npy_intp *shape,
+ char *src, char *dst,
+ npy_intp *src_strides, npy_intp *dst_strides,
+ PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype,
+ int move_references)
+{
+ PyArray_StridedTransferFn *stransfer = NULL;
+ NpyAuxData *transferdata = NULL;
+ int i, j;
+ npy_intp src_align, dst_align;
+ int aligned, needs_api = 0;
+ npy_intp coords[NPY_MAXDIMS];
+ npy_intp shape_copy[NPY_MAXDIMS];
+ npy_intp src_strides_copy[NPY_MAXDIMS];
+ npy_intp dst_strides_copy[NPY_MAXDIMS];
+ _npy_stride_sort_item strideperm[NPY_MAXDIMS];
+
+ /* Use the simpler function for 0 and 1 dimensional transfers */
+ if (ndim <= 1) {
+ if (ndim == 0) {
+ return PyArray_CastRawArrays(1, src, dst, 0, 0,
+ src_dtype, dst_dtype, move_references);
+ }
+ else {
+ return PyArray_CastRawArrays(shape[0], src, dst,
+ src_strides[0], dst_strides[0],
+ src_dtype, dst_dtype, move_references);
+ }
+ }
+
+ /* Determine data alignment */
+ src_align = (npy_intp)src;
+ for (i = 0; i < ndim; ++i) {
+ src_align |= src_strides[i];
+ }
+ dst_align = (npy_intp)dst;
+ for (i = 0; i < ndim; ++i) {
+ dst_align |= dst_strides[i];
+ }
+ aligned = (src_align & (src_dtype->alignment - 1)) == 0 &&
+ (dst_align & (dst_dtype->alignment - 1)) == 0;
+
+ /* Sort the axes based on the destination strides */
+ PyArray_CreateSortedStridePerm(ndim, dst_strides, strideperm);
+ for (i = 0; i < ndim; ++i) {
+ int iperm = strideperm[i].perm;
+ shape_copy[i] = shape[iperm];
+ src_strides_copy[i] = src_strides[iperm];
+ dst_strides_copy[i] = dst_strides[iperm];
+ }
+
+ /* Coalesce dimensions where it's possible */
+ i = 0;
+ for (j = 1; j < ndim; ++j) {
+ if (shape[i] == 1) {
+ /* Drop axis i */
+ shape[i] = shape[j];
+ src_strides_copy[i] = src_strides_copy[j];
+ dst_strides_copy[i] = dst_strides_copy[j];
+ }
+ else if (shape[j] == 1) {
+ /* Drop axis j */
+ }
+ else if (src_strides_copy[i] == src_strides_copy[j] * shape[j] &&
+ dst_strides_copy[i] == dst_strides_copy[j] * shape[j]) {
+ /* Coalesce axes i and j */
+ shape[i] *= shape[j];
+ src_strides_copy[i] = src_strides_copy[j];
+ dst_strides_copy[i] = dst_strides_copy[j];
+ }
+ else {
+ /* Can't coalesce, go to next i */
+ ++i;
+ }
+ }
+ ndim = i+1;
+
+ /* Get the function to do the casting */
+ if (PyArray_GetDTypeTransferFunction(aligned,
+ src_strides[ndim-1], dst_strides[ndim-1],
+ src_dtype, dst_dtype,
+ move_references,
+ &stransfer, &transferdata,
+ &needs_api) != NPY_SUCCEED) {
+ return NPY_FAIL;
+ }
+
+ /* Do the copying */
+ memset(coords, 0, ndim * sizeof(npy_intp));
+ do {
+ /* Copy along the last dimension */
+ i = ndim - 1;
+ stransfer(dst, dst_strides_copy[i], src, src_strides_copy[i], shape[i],
+ src_dtype->elsize, transferdata);
+ --i;
+ /* Increment to the next n-dimensional coordinate */
+ for (;i > 0; --i) {
+ if (++coords[i] == shape[i]) {
+ coords[i] = 0;
+ src -= (shape[i] - 1) * src_strides_copy[i];
+ }
+ else {
+ src += src_strides_copy[i];
+ break;
+ }
+ }
+ } while (i > 0);
+
+ /* Cleanup */
+ NPY_AUXDATA_FREE(transferdata);
+
+ /* If needs_api was set to 1, it may have raised a Python exception */
+ return (needs_api && PyErr_Occurred()) ? NPY_FAIL : NPY_SUCCEED;
+}
diff --git a/numpy/core/src/multiarray/flagsobject.c b/numpy/core/src/multiarray/flagsobject.c
index 31a7d041e..72acfd6ee 100644
--- a/numpy/core/src/multiarray/flagsobject.c
+++ b/numpy/core/src/multiarray/flagsobject.c
@@ -181,14 +181,14 @@ arrayflags_dealloc(PyArrayFlagsObject *self)
}
-#define _define_get(UPPER, lower) \
- static PyObject * \
- arrayflags_ ## lower ## _get(PyArrayFlagsObject *self) \
- { \
- PyObject *item; \
+#define _define_get(UPPER, lower) \
+ static PyObject * \
+ arrayflags_ ## lower ## _get(PyArrayFlagsObject *self) \
+ { \
+ PyObject *item; \
item = ((self->flags & (UPPER)) == (UPPER)) ? Py_True : Py_False; \
- Py_INCREF(item); \
- return item; \
+ Py_INCREF(item); \
+ return item; \
}
_define_get(NPY_ARRAY_C_CONTIGUOUS, contiguous)
@@ -260,6 +260,80 @@ arrayflags_num_get(PyArrayFlagsObject *self)
return PyInt_FromLong(self->flags);
}
+static PyObject *
+arrayflags_maskna_get(PyArrayFlagsObject *self)
+{
+ PyObject *item;
+ if (self->flags & NPY_ARRAY_MASKNA) {
+ item = Py_True;
+ }
+ else {
+ item = Py_False;
+ }
+ Py_INCREF(item);
+ return item;
+}
+
+static int
+arrayflags_maskna_set(PyArrayFlagsObject *self, PyObject *obj)
+{
+ if (self->arr == NULL) {
+ PyErr_SetString(PyExc_ValueError, "Cannot set flags on array scalars.");
+ return -1;
+ }
+
+ if (PyObject_IsTrue(obj)) {
+ return PyArray_AllocateMaskNA(self->arr, 0, 0);
+ }
+ else {
+ if (self->flags & NPY_ARRAY_MASKNA) {
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot remove a NumPy array's NA mask");
+ return -1;
+ }
+ else {
+ return 0;
+ }
+ }
+}
+
+static PyObject *
+arrayflags_ownmaskna_get(PyArrayFlagsObject *self)
+{
+ PyObject *item;
+ if (self->flags & NPY_ARRAY_MASKNA) {
+ item = Py_True;
+ }
+ else {
+ item = Py_False;
+ }
+ Py_INCREF(item);
+ return item;
+}
+
+static int
+arrayflags_ownmaskna_set(PyArrayFlagsObject *self, PyObject *obj)
+{
+ if (self->arr == NULL) {
+ PyErr_SetString(PyExc_ValueError, "Cannot set flags on array scalars.");
+ return -1;
+ }
+
+ if (PyObject_IsTrue(obj)) {
+ return PyArray_AllocateMaskNA(self->arr, 1, 0);
+ }
+ else {
+ if (self->flags & NPY_ARRAY_OWNMASKNA) {
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot remove a NumPy array's NA mask");
+ return -1;
+ }
+ else {
+ return 0;
+ }
+ }
+}
+
/* relies on setflags order being write, align, uic */
static int
arrayflags_updateifcopy_set(PyArrayFlagsObject *self, PyObject *obj)
@@ -348,6 +422,14 @@ static PyGetSetDef arrayflags_getsets[] = {
(getter)arrayflags_writeable_get,
(setter)arrayflags_writeable_set,
NULL, NULL},
+ {"maskna",
+ (getter)arrayflags_maskna_get,
+ (setter)arrayflags_maskna_set,
+ NULL, NULL},
+ {"ownmaskna",
+ (getter)arrayflags_ownmaskna_get,
+ (setter)arrayflags_ownmaskna_set,
+ NULL, NULL},
{"fnc",
(getter)arrayflags_fnc_get,
NULL,
@@ -450,6 +532,9 @@ arrayflags_getitem(PyArrayFlagsObject *self, PyObject *ind)
if (strncmp(key, "FARRAY", n) == 0) {
return arrayflags_farray_get(self);
}
+ if (strncmp(key, "MASKNA", n) == 0) {
+ return arrayflags_maskna_get(self);
+ }
break;
case 7:
if (strncmp(key,"FORTRAN",n) == 0) {
@@ -469,6 +554,9 @@ arrayflags_getitem(PyArrayFlagsObject *self, PyObject *ind)
if (strncmp(key,"WRITEABLE",n) == 0) {
return arrayflags_writeable_get(self);
}
+ if (strncmp(key, "OWNMASKNA", n) == 0) {
+ return arrayflags_ownmaskna_get(self);
+ }
break;
case 10:
if (strncmp(key,"CONTIGUOUS",n) == 0) {
@@ -528,6 +616,12 @@ arrayflags_setitem(PyArrayFlagsObject *self, PyObject *ind, PyObject *item)
((n==1) && (strncmp(key, "U", n) == 0))) {
return arrayflags_updateifcopy_set(self, item);
}
+ else if ((n==6) && (strncmp(key, "MASKNA", n) == 0)) {
+ return arrayflags_maskna_set(self, item);
+ }
+ else if ((n==9) && (strncmp(key, "OWNMASKNA", n) == 0)) {
+ return arrayflags_ownmaskna_set(self, item);
+ }
fail:
PyErr_SetString(PyExc_KeyError, "Unknown flag");
@@ -555,6 +649,8 @@ arrayflags_print(PyArrayFlagsObject *self)
"C_CONTIGUOUS", _torf_(fl, NPY_ARRAY_C_CONTIGUOUS),
"F_CONTIGUOUS", _torf_(fl, NPY_ARRAY_F_CONTIGUOUS),
"OWNDATA", _torf_(fl, NPY_ARRAY_OWNDATA),
+ "MASKNA", _torf_(fl, NPY_ARRAY_MASKNA),
+ "OWNMASKNA", _torf_(fl, NPY_ARRAY_OWNMASKNA),
"WRITEABLE", _torf_(fl, NPY_ARRAY_WRITEABLE),
"ALIGNED", _torf_(fl, NPY_ARRAY_ALIGNED),
"UPDATEIFCOPY", _torf_(fl, NPY_ARRAY_UPDATEIFCOPY));
diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c
index 9491bc631..d18721333 100644
--- a/numpy/core/src/multiarray/multiarraymodule.c
+++ b/numpy/core/src/multiarray/multiarraymodule.c
@@ -1542,13 +1542,11 @@ _prepend_ones(PyArrayObject *arr, int nd, int ndmin)
}
-#define _ARET(x) PyArray_Return((PyArrayObject *)(x))
-
-#define STRIDING_OK(op, order) ((order) == NPY_ANYORDER || \
- ((order) == NPY_CORDER && \
- PyArray_ISCONTIGUOUS(op)) || \
- ((order) == NPY_FORTRANORDER && \
- PyArray_ISFORTRAN(op)))
+#define STRIDING_OK(op, order) \
+ ((order) == NPY_ANYORDER || \
+ (order) == NPY_KEEPORDER || \
+ ((order) == NPY_CORDER && PyArray_ISCONTIGUOUS(op)) || \
+ ((order) == NPY_FORTRANORDER && PyArray_ISFORTRAN(op)))
static PyObject *
_array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws)
@@ -1561,37 +1559,49 @@ _array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws)
PyArray_Descr *type = NULL;
PyArray_Descr *oldtype = NULL;
NPY_ORDER order = NPY_ANYORDER;
- int flags = 0;
+ int flags = 0, maskna = 0, ownmaskna = 0;
static char *kwd[]= {"object", "dtype", "copy", "order", "subok",
- "ndmin", NULL};
+ "ndmin", "maskna", NULL};
if (PyTuple_GET_SIZE(args) > 2) {
PyErr_SetString(PyExc_ValueError,
"only 2 non-keyword arguments accepted");
return NULL;
}
- if(!PyArg_ParseTupleAndKeywords(args, kws, "O|O&O&O&O&i", kwd, &op,
+ if(!PyArg_ParseTupleAndKeywords(args, kws, "O|O&O&O&O&iii", kwd,
+ &op,
PyArray_DescrConverter2, &type,
PyArray_BoolConverter, &copy,
PyArray_OrderConverter, &order,
PyArray_BoolConverter, &subok,
- &ndmin)) {
+ &ndmin,
+ &maskna,
+ &ownmaskna)) {
goto clean_type;
}
+ /* 'ownmaskna' forces 'maskna' to be True */
+ if (ownmaskna) {
+ maskna = 1;
+ }
+
if (ndmin > NPY_MAXDIMS) {
PyErr_Format(PyExc_ValueError,
- "ndmin bigger than allowable number of dimensions "\
+ "ndmin bigger than allowable number of dimensions "
"NPY_MAXDIMS (=%d)", NPY_MAXDIMS);
goto clean_type;
}
/* fast exit if simple call */
- if ((subok && PyArray_Check(op))
- || (!subok && PyArray_CheckExact(op))) {
+ if (((subok && PyArray_Check(op)) ||
+ (!subok && PyArray_CheckExact(op))) &&
+ ((maskna &&
+ (PyArray_FLAGS((PyArrayObject *)op)&NPY_ARRAY_MASKNA) != 0) ||
+ (!maskna &&
+ (PyArray_FLAGS((PyArrayObject *)op)&NPY_ARRAY_MASKNA) == 0))) {
oparr = (PyArrayObject *)op;
if (type == NULL) {
- if (!copy && STRIDING_OK(oparr, order)) {
+ if (!copy && !ownmaskna && STRIDING_OK(oparr, order)) {
Py_INCREF(op);
ret = oparr;
goto finish;
@@ -1604,7 +1614,7 @@ _array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws)
/* One more chance */
oldtype = PyArray_DESCR(oparr);
if (PyArray_EquivTypes(oldtype, type)) {
- if (!copy && STRIDING_OK(oparr, order)) {
+ if (!copy && !ownmaskna && STRIDING_OK(oparr, order)) {
Py_INCREF(op);
ret = oparr;
goto finish;
@@ -1637,6 +1647,12 @@ _array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws)
if (!subok) {
flags |= NPY_ARRAY_ENSUREARRAY;
}
+ if (maskna) {
+ flags |= NPY_ARRAY_MASKNA;
+ }
+ if (maskna) {
+ flags |= NPY_ARRAY_OWNMASKNA;
+ }
flags |= NPY_ARRAY_FORCECAST;
Py_XINCREF(type);
@@ -1645,10 +1661,12 @@ _array_fromobject(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kws)
finish:
Py_XDECREF(type);
- if (!ret) {
- return (PyObject *)ret;
+ if (ret == NULL) {
+ return NULL;
}
- else if ((nd=PyArray_NDIM(ret)) >= ndmin) {
+
+ nd = PyArray_NDIM(ret);
+ if (nd >= ndmin) {
return (PyObject *)ret;
}
/*
@@ -2069,7 +2087,7 @@ array_innerproduct(PyObject *NPY_UNUSED(dummy), PyObject *args)
if (!PyArg_ParseTuple(args, "OO", &a0, &b0)) {
return NULL;
}
- return _ARET(PyArray_InnerProduct(a0, b0));
+ return PyArray_Return((PyArrayObject *)PyArray_InnerProduct(a0, b0));
}
static PyObject *
@@ -2089,7 +2107,7 @@ array_matrixproduct(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject* kwds)
"'out' must be an array");
return NULL;
}
- return _ARET(PyArray_MatrixProduct2(a, v, (PyArrayObject *)o));
+ return PyArray_Return((PyArrayObject *)PyArray_MatrixProduct2(a, v, (PyArrayObject *)o));
}
static int
@@ -2430,7 +2448,7 @@ array_einsum(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds)
/* If no output was supplied, possibly convert to a scalar */
if (ret != NULL && out == NULL) {
- ret = _ARET(ret);
+ ret = PyArray_Return((PyArrayObject *)ret);
}
finish:
@@ -2453,7 +2471,7 @@ array_fastCopyAndTranspose(PyObject *NPY_UNUSED(dummy), PyObject *args)
if (!PyArg_ParseTuple(args, "O", &a0)) {
return NULL;
}
- return _ARET(PyArray_CopyAndTranspose(a0));
+ return PyArray_Return((PyArrayObject *)PyArray_CopyAndTranspose(a0));
}
static PyObject *
@@ -2711,11 +2729,9 @@ array_lexsort(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|i", kwlist, &obj, &axis)) {
return NULL;
}
- return _ARET(PyArray_LexSort(obj, axis));
+ return PyArray_Return((PyArrayObject *)PyArray_LexSort(obj, axis));
}
-#undef _ARET
-
static PyObject *
array_can_cast_safely(PyObject *NPY_UNUSED(self), PyObject *args,
PyObject *kwds)
diff --git a/numpy/core/src/multiarray/na_mask.c b/numpy/core/src/multiarray/na_mask.c
index ce8dd1e4b..a7eab49c9 100644
--- a/numpy/core/src/multiarray/na_mask.c
+++ b/numpy/core/src/multiarray/na_mask.c
@@ -17,6 +17,9 @@
#include "npy_config.h"
#include "numpy/npy_3kcompat.h"
+#include "shape.h"
+#include "lowlevel_strided_loops.h"
+
/*NUMPY_API
*
* Returns true if the array has an NA mask. When
@@ -47,3 +50,135 @@ PyArray_ContainsNA(PyArrayObject *arr)
return 0;
}
+
+/*NUMPY_API
+ *
+ * If the array does not have an NA mask already, allocates one for it.
+ *
+ * If 'ownmaskna' is True, it also allocates one for it if the array does
+ * not already own its own mask, then copies the data from the old mask
+ * to the new mask.
+ *
+ * If 'multina' is True, the mask is allocated with an NPY_MASK dtype
+ * instead of NPY_BOOL.
+ *
+ * Returns -1 on failure, 0 on success.
+ */
+NPY_NO_EXPORT int
+PyArray_AllocateMaskNA(PyArrayObject *arr, npy_bool ownmaskna, npy_bool multina)
+{
+ PyArrayObject_fieldaccess *fa = (PyArrayObject_fieldaccess *)arr;
+ PyArray_Descr *maskna_dtype = NULL;
+ char *maskna_data = NULL;
+ npy_intp size;
+
+ /* If the array already owns a mask, done */
+ if (fa->flags & NPY_ARRAY_OWNMASKNA) {
+ return 0;
+ }
+
+ /* If ownership wasn't requested, and there's already a mask, done */
+ if (!ownmaskna && (fa->flags & NPY_ARRAY_MASKNA)) {
+ return 0;
+ }
+
+ size = PyArray_SIZE(arr);
+
+ /* Create the mask dtype */
+ if (PyArray_HASFIELDS(arr)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "NumPy field-NA isn't supported yet");
+ return -1;
+ }
+ else {
+ maskna_dtype = PyArray_DescrFromType(multina ? NPY_MASK
+ : NPY_BOOL);
+ if (maskna_dtype == NULL) {
+ return -1;
+ }
+ }
+
+ /* Allocate the mask memory */
+ maskna_data = PyArray_malloc(size * maskna_dtype->elsize);
+ if (maskna_data == NULL) {
+ Py_DECREF(maskna_dtype);
+ PyErr_NoMemory();
+ return -1;
+ }
+
+ /* Copy the data and fill in the strides */
+ if (fa->nd == 1) {
+ /* If there already was a mask copy it, otherwise set it to all ones */
+ if (fa->flags & NPY_ARRAY_MASKNA) {
+ if (fa->maskna_strides[0] == 1) {
+ memcpy(maskna_data, fa->maskna_data,
+ size * maskna_dtype->elsize);
+ }
+ else {
+ if (PyArray_CastRawArrays(fa->dimensions[0],
+ (char *)fa->maskna_data, maskna_data,
+ fa->maskna_strides[0], maskna_dtype->elsize,
+ fa->maskna_dtype, maskna_dtype, 0) < 0) {
+ Py_DECREF(maskna_dtype);
+ PyArray_free(maskna_data);
+ return -1;
+ }
+ }
+ }
+ else {
+ memset(maskna_data, 1, size * maskna_dtype->elsize);
+ }
+
+ fa->maskna_strides[0] = maskna_dtype->elsize;
+ }
+ else if (fa->nd > 1) {
+ _npy_stride_sort_item strideperm[NPY_MAXDIMS];
+ npy_intp stride, maskna_strides[NPY_MAXDIMS], *shape;
+ int i;
+
+ shape = fa->dimensions;
+
+ /* This causes the NA mask and data memory orderings to match */
+ PyArray_CreateSortedStridePerm(fa->nd, fa->strides, strideperm);
+ stride = maskna_dtype->elsize;
+ for (i = fa->nd-1; i >= 0; --i) {
+ npy_intp i_perm = strideperm[i].perm;
+ maskna_strides[i_perm] = stride;
+ stride *= shape[i_perm];
+ }
+
+ /* If there already was a mask copy it, otherwise set it to all ones */
+ if (fa->flags & NPY_ARRAY_MASKNA) {
+ if (PyArray_CastRawNDimArrays(fa->nd, fa->dimensions,
+ (char *)fa->maskna_data, maskna_data,
+ fa->maskna_strides, maskna_strides,
+ fa->maskna_dtype, maskna_dtype, 0) < 0) {
+ Py_DECREF(maskna_dtype);
+ PyArray_free(maskna_data);
+ return -1;
+ }
+ }
+ else {
+ memset(maskna_data, 1, size * maskna_dtype->elsize);
+ }
+
+ memcpy(fa->maskna_strides, maskna_strides, fa->nd * sizeof(npy_intp));
+ }
+ else {
+ /* If there already was a mask copy it, otherwise set it to all ones */
+ if (fa->flags & NPY_ARRAY_MASKNA) {
+ maskna_data[0] = fa->maskna_data[0];
+ }
+ else {
+ maskna_data[0] = 1;
+ }
+ }
+
+ /* Set the NA mask data in the array */
+ Py_XDECREF(maskna_dtype);
+ fa->maskna_dtype = maskna_dtype;
+ fa->maskna_data = (npy_mask *)maskna_data;
+ fa->flags |= (NPY_ARRAY_MASKNA | NPY_ARRAY_OWNMASKNA);
+
+ return 0;
+}
diff --git a/numpy/core/src/multiarray/nditer_api.c b/numpy/core/src/multiarray/nditer_api.c
index 4875c1e34..ba3f0b677 100644
--- a/numpy/core/src/multiarray/nditer_api.c
+++ b/numpy/core/src/multiarray/nditer_api.c
@@ -1599,7 +1599,6 @@ npyiter_coalesce_axes(NpyIter *iter)
}
/*
- *
* If errmsg is non-NULL, it should point to a variable which will
* receive the error message, and no Python exception will be set.
* This is so that the function can be called from code not holding
diff --git a/numpy/core/src/multiarray/shape.c b/numpy/core/src/multiarray/shape.c
index 3754e6a1e..532e8a2ba 100644
--- a/numpy/core/src/multiarray/shape.c
+++ b/numpy/core/src/multiarray/shape.c
@@ -807,21 +807,21 @@ int _npy_stride_sort_item_comparator(const void *a, const void *b)
}
/*
- * This function populates the first PyArray_NDIM(arr) elements
+ * This function populates the first ndim elements
* of strideperm with sorted descending by their absolute values.
* For example, the stride array (4, -2, 12) becomes
* [(2, 12), (0, 4), (1, -2)].
*/
NPY_NO_EXPORT void
-PyArray_CreateSortedStridePerm(PyArrayObject *arr,
+PyArray_CreateSortedStridePerm(int ndim, npy_intp * strides,
_npy_stride_sort_item *strideperm)
{
- int i, ndim = PyArray_NDIM(arr);
+ int i;
/* Set up the strideperm values */
for (i = 0; i < ndim; ++i) {
strideperm[i].perm = i;
- strideperm[i].stride = PyArray_STRIDE(arr, i);
+ strideperm[i].stride = strides[i];
}
/* Sort them */
@@ -865,7 +865,8 @@ PyArray_Ravel(PyArrayObject *a, NPY_ORDER order)
npy_intp stride;
int i, ndim = PyArray_NDIM(a);
- PyArray_CreateSortedStridePerm(a, strideperm);
+ PyArray_CreateSortedStridePerm(PyArray_NDIM(a), PyArray_STRIDES(a),
+ strideperm);
stride = PyArray_DESCR(a)->elsize;
for (i = ndim-1; i >= 0; --i) {
diff --git a/numpy/core/src/multiarray/shape.h b/numpy/core/src/multiarray/shape.h
index 8038a9f25..71dbd1562 100644
--- a/numpy/core/src/multiarray/shape.h
+++ b/numpy/core/src/multiarray/shape.h
@@ -12,7 +12,8 @@ typedef struct {
* [(2, 12), (0, 4), (1, -2)].
*/
NPY_NO_EXPORT void
-PyArray_CreateSortedStridePerm(PyArrayObject *arr,
+PyArray_CreateSortedStridePerm(int ndim, npy_intp * strides,
_npy_stride_sort_item *strideperm);
+
#endif
diff --git a/numpy/core/src/private/lowlevel_strided_loops.h b/numpy/core/src/private/lowlevel_strided_loops.h
index b6b53ba45..c606e58f3 100644
--- a/numpy/core/src/private/lowlevel_strided_loops.h
+++ b/numpy/core/src/private/lowlevel_strided_loops.h
@@ -220,7 +220,7 @@ PyArray_GetMaskedDTypeTransferFunction(int aligned,
* 'src_dtype' to 'dst' with 'dst_dtype'. See
* PyArray_GetDTypeTransferFunction for more details.
*
- * returns NPY_SUCCEED or NPY_FAIL.
+ * Returns NPY_SUCCEED or NPY_FAIL.
*/
NPY_NO_EXPORT int
PyArray_CastRawArrays(npy_intp count,
@@ -230,6 +230,20 @@ PyArray_CastRawArrays(npy_intp count,
int move_references);
/*
+ * Casts the elements from one n-dimensional array to another n-dimensional
+ * array with identical shape but possibly different strides and dtypes.
+ * Does not account for overlap.
+ *
+ * Returns NPY_SUCCEED or NPY_FAIL.
+ */
+NPY_NO_EXPORT int
+PyArray_CastRawNDimArrays(int ndim, npy_intp *shape,
+ char *src, char *dst,
+ npy_intp *src_strides, npy_intp *dst_strides,
+ PyArray_Descr *src_dtype, PyArray_Descr *dst_dtype,
+ int move_references);
+
+/*
* These two functions copy or convert the data of an n-dimensional array
* to/from a 1-dimensional strided buffer. These functions will only call
* 'stransfer' with the provided dst_stride/src_stride and