summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--numpy/core/SConscript1
-rw-r--r--numpy/core/setup.py2
-rw-r--r--numpy/core/src/multiarray/array_assign.c191
-rw-r--r--numpy/core/src/multiarray/array_assign.h71
-rw-r--r--numpy/core/src/multiarray/convert_datatype.c95
-rw-r--r--numpy/core/src/multiarray/convert_datatype.h5
-rw-r--r--numpy/core/src/multiarray/na_mask.c5
-rw-r--r--numpy/core/src/umath/ufunc_object.c55
8 files changed, 383 insertions, 42 deletions
diff --git a/numpy/core/SConscript b/numpy/core/SConscript
index 339b604a9..60fde8e30 100644
--- a/numpy/core/SConscript
+++ b/numpy/core/SConscript
@@ -443,6 +443,7 @@ if ENABLE_SEPARATE_COMPILATION:
multiarray_src = [pjoin('src', 'multiarray', 'multiarraymodule.c'),
pjoin('src', 'multiarray', 'hashdescr.c'),
pjoin('src', 'multiarray', 'arrayobject.c'),
+ pjoin('src', 'multiarray', 'array_assign.c'),
pjoin('src', 'multiarray', 'datetime.c'),
pjoin('src', 'multiarray', 'datetime_strings.c'),
pjoin('src', 'multiarray', 'datetime_busday.c'),
diff --git a/numpy/core/setup.py b/numpy/core/setup.py
index afe52f54f..c721cf219 100644
--- a/numpy/core/setup.py
+++ b/numpy/core/setup.py
@@ -700,6 +700,7 @@ def configuration(parent_package='',top_path=None):
multiarray_deps = [
join('src', 'multiarray', 'arrayobject.h'),
join('src', 'multiarray', 'arraytypes.h'),
+ join('src', 'multiarray', 'array_assign.h'),
join('src', 'multiarray', 'buffer.h'),
join('src', 'multiarray', 'calculation.h'),
join('src', 'multiarray', 'common.h'),
@@ -750,6 +751,7 @@ def configuration(parent_package='',top_path=None):
multiarray_src = [
join('src', 'multiarray', 'arrayobject.c'),
join('src', 'multiarray', 'arraytypes.c.src'),
+ join('src', 'multiarray', 'array_assign.c'),
join('src', 'multiarray', 'buffer.c'),
join('src', 'multiarray', 'calculation.c'),
join('src', 'multiarray', 'common.c'),
diff --git a/numpy/core/src/multiarray/array_assign.c b/numpy/core/src/multiarray/array_assign.c
new file mode 100644
index 000000000..7ef80ca3b
--- /dev/null
+++ b/numpy/core/src/multiarray/array_assign.c
@@ -0,0 +1,191 @@
+/*
+ * This file implements several array assignment routines.
+ *
+ * Written by Mark Wiebe (mwwiebe@gmail.com)
+ * Copyright (c) 2011 by Enthought, Inc.
+ *
+ * See LICENSE.txt for the license.
+ */
+
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+
+#define NPY_NO_DEPRECATED_API
+#define _MULTIARRAYMODULE
+#include <numpy/ndarraytypes.h>
+
+#include "npy_config.h"
+#include "numpy/npy_3kcompat.h"
+
+#include "convert_datatype.h"
+#include "methods.h"
+#include "lowlevel_strided_loops.h"
+#include "array_assign.h"
+
+
+/*
+ * Assigns the scalar value to every element of the destination raw array.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+raw_array_assign_scalar(int ndim, npy_intp *shape,
+ PyArray_Descr *dst_dtype, char *dst_data, npy_intp *dst_strides,
+ PyArray_Descr *src_dtype, char *src_data)
+{
+ int idim;
+ npy_intp shape_it[NPY_MAXDIMS], dst_strides_it[NPY_MAXDIMS];
+ npy_intp coord[NPY_MAXDIMS];
+ NPY_BEGIN_THREADS_DEF;
+
+ PyArray_StridedTransferFn *stransfer = NULL;
+ NpyAuxData *transferdata = NULL;
+ int aligned = 1, needs_api = 0;
+ npy_intp src_itemsize = src_dtype->elsize;
+
+ /* Check alignment */
+ if (dst_dtype->alignment > 1) {
+ npy_intp align_check = (npy_intp)dst_data;
+ for (idim = 0; idim < ndim; ++idim) {
+ align_check |= dst_strides[idim];
+ }
+ if ((align_check & (dst_dtype->alignment - 1)) != 0) {
+ aligned = 0;
+ }
+ }
+ if (((npy_intp)src_data & (src_dtype->alignment - 1)) != 0) {
+ aligned = 0;
+ }
+
+ /* Use raw iteration with no heap allocation */
+ if (PyArray_PrepareOneRawArrayIter(
+ ndim, shape,
+ dst_data, dst_strides,
+ &ndim, shape_it,
+ &dst_data, dst_strides_it) < 0) {
+ return -1;
+ }
+
+ /* Get the function to do the casting */
+ if (PyArray_GetDTypeTransferFunction(aligned,
+ 0, dst_strides_it[0],
+ src_dtype, dst_dtype,
+ 0,
+ &stransfer, &transferdata,
+ &needs_api) != NPY_SUCCEED) {
+ return -1;
+ }
+
+ if (!needs_api) {
+ NPY_BEGIN_THREADS;
+ }
+
+ NPY_RAW_ITER_START(idim, ndim, coord, shape_it) {
+ /* Process the innermost dimension */
+ stransfer(dst_data, dst_strides_it[0], src_data, 0,
+ shape_it[0], src_itemsize, transferdata);
+ } NPY_RAW_ITER_ONE_NEXT(idim, ndim, coord,
+ shape_it, dst_data, dst_strides_it);
+
+ if (!needs_api) {
+ NPY_END_THREADS;
+ }
+
+ NPY_AUXDATA_FREE(transferdata);
+
+ return (needs_api && PyErr_Occurred()) ? -1 : 0;
+}
+
+/* See array_assign.h for documentation */
+NPY_NO_EXPORT int
+array_assign_scalar(PyArrayObject *dst,
+ PyArray_Descr *src_dtype, char *src_data,
+ PyArrayObject *wheremask,
+ NPY_CASTING casting, npy_bool overwritena)
+{
+ int copied_src_data = 0, dst_has_maskna = PyArray_HASMASKNA(dst);
+
+ /* Check the casting rule */
+ if (!can_cast_scalar_to(src_dtype, src_data,
+ PyArray_DESCR(dst), casting)) {
+ PyObject *errmsg;
+ errmsg = PyUString_FromString("Cannot cast scalar from ");
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)src_dtype));
+ PyUString_ConcatAndDel(&errmsg,
+ PyUString_FromString(" to "));
+ PyUString_ConcatAndDel(&errmsg,
+ PyObject_Repr((PyObject *)PyArray_DESCR(dst)));
+ PyUString_ConcatAndDel(&errmsg,
+ PyUString_FromFormat(" according to the rule %s",
+ npy_casting_to_string(casting)));
+ PyErr_SetObject(PyExc_TypeError, errmsg);
+ return -1;
+ }
+
+ /*
+ * Make a copy of the src data if it's a different dtype than 'dst'
+ * or isn't aligned, and the destination we're copying to has
+ * more than one element.
+ */
+ if ((!PyArray_EquivTypes(PyArray_DESCR(dst), src_dtype) ||
+ ((npy_intp)src_data & (src_dtype->alignment - 1)) != 0) &&
+ PyArray_SIZE(dst) > 1) {
+ char *tmp_src_data;
+
+ /* Allocate a new buffer to store the copied src data */
+ tmp_src_data = PyArray_malloc(PyArray_DESCR(dst)->elsize);
+ copied_src_data = 1;
+ if (PyArray_CastRawArrays(1, src_data, tmp_src_data, 0, 0,
+ src_dtype, PyArray_DESCR(dst), 0) != NPY_SUCCEED) {
+ goto fail;
+ }
+
+ /* Replace src_data/src_dtype */
+ src_data = tmp_src_data;
+ src_dtype = PyArray_DESCR(dst);
+ }
+
+ if (wheremask == NULL) {
+ /* This is the case of a straightforward value assignment */
+ if (overwritena || !dst_has_maskna) {
+ /* If we're assigning to an array with a mask, set to all exposed */
+ if (dst_has_maskna) {
+ if (PyArray_AssignMaskNA(dst, 1) < 0) {
+ goto fail;
+ }
+ }
+
+ /* TODO: continuing here */
+ if (raw_array_assign_scalar(PyArray_NDIM(dst), PyArray_DIMS(dst),
+ PyArray_DESCR(dst), PyArray_DATA(dst), PyArray_STRIDES(dst),
+ src_dtype, src_data) < 0) {
+ goto fail;
+ }
+ }
+ /* This is value assignment without overwriting NA values */
+ else {
+ }
+ }
+ else {
+ /* This is the case of a straightforward masked assignment */
+ if (overwritena || !dst_has_maskna) {
+ }
+ /* This is masked value assignment without overwriting NA values */
+ else {
+ }
+ }
+
+ if (copied_src_data) {
+ PyArray_free(src_data);
+ }
+
+ return 0;
+
+fail:
+ if (copied_src_data) {
+ PyArray_free(src_data);
+ }
+
+ return -1;
+}
diff --git a/numpy/core/src/multiarray/array_assign.h b/numpy/core/src/multiarray/array_assign.h
new file mode 100644
index 000000000..68263c157
--- /dev/null
+++ b/numpy/core/src/multiarray/array_assign.h
@@ -0,0 +1,71 @@
+#ifndef _NPY_PRIVATE__ARRAY_ASSIGN_H_
+#define _NPY_PRIVATE__ARRAY_ASSIGN_H_
+
+/*
+ * Assigns a scalar value specified by 'src_dtype' and 'src_data'
+ * to elements of 'dst'.
+ *
+ * dst: The destination array.
+ * src_dtype: The data type of the source scalar.
+ * src_data: The memory element of the source scalar.
+ * wheremask: If non-NULL, a boolean mask specifying where to copy.
+ * casting: An exception is raised if the assignment violates this
+ * casting rule.
+ * overwritena: If 1, overwrites everything in 'dst', if 0, it
+ * does not overwrite elements in 'dst' which are NA.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+array_assign_scalar(PyArrayObject *dst,
+ PyArray_Descr *src_dtype, char *src_data,
+ PyArrayObject *wheremask,
+ NPY_CASTING casting, npy_bool overwritena);
+
+/*
+ * An array assignment function for copying arrays, broadcasting 'src' into
+ * 'dst'. This function makes a temporary copy of 'src' if 'src' and
+ * 'dst' overlap, to be able to handle views of the same data with
+ * different strides.
+ *
+ * dst: The destination array.
+ * src: The source array.
+ * wheremask: If non-NULL, a boolean mask specifying where to copy.
+ * casting: An exception is raised if the copy violates this
+ * casting rule.
+ * overwritena: If 1, overwrites everything in 'dst', if 0, it
+ * does not overwrite elements in 'dst' which are NA.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+array_assign_broadcast(PyArrayObject *dst, PyArrayObject *src,
+ PyArrayObject *wheremask,
+ NPY_CASTING casting, npy_bool overwritena);
+
+/*
+ * An array assignment function for copying arrays, treating the
+ * arrays as flat according to their respective ordering rules.
+ * This function makes a temporary copy of 'src' if 'src' and
+ * 'dst' overlap, to be able to handle views of the same data with
+ * different strides.
+ *
+ * dst: The destination array.
+ * dst_order: The rule for how 'dst' is to be made flat.
+ * src: The source array.
+ * src_order: The rule for how 'src' is to be made flat.
+ * wheremask: If non-NULL, a boolean mask specifying where to copy.
+ * casting: An exception is raised if the copy violates this
+ * casting rule.
+ * overwritena: If 1, overwrites everything in 'dst', if 0, it
+ * does not overwrite elements in 'dst' which are NA.
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+NPY_NO_EXPORT int
+array_assign_flat(PyArrayObject *dst, NPY_ORDER dst_order,
+ PyArrayObject *src, NPY_ORDER src_order,
+ PyArrayObject *wheremask,
+ NPY_CASTING casting, npy_bool overwritena);
+
+#endif
diff --git a/numpy/core/src/multiarray/convert_datatype.c b/numpy/core/src/multiarray/convert_datatype.c
index e5cd965aa..fa0d6b4bb 100644
--- a/numpy/core/src/multiarray/convert_datatype.c
+++ b/numpy/core/src/multiarray/convert_datatype.c
@@ -639,6 +639,62 @@ PyArray_CanCastTypeTo(PyArray_Descr *from, PyArray_Descr *to,
static int min_scalar_type_num(char *valueptr, int type_num,
int *is_small_unsigned);
+NPY_NO_EXPORT npy_bool
+can_cast_scalar_to(PyArray_Descr *scal_type, char *scal_data,
+ PyArray_Descr *to, NPY_CASTING casting)
+{
+ int swap;
+ int is_small_unsigned = 0, type_num;
+ npy_bool ret;
+ PyArray_Descr *dtype;
+
+ /* An aligned memory buffer large enough to hold any type */
+ npy_longlong value[4];
+
+ if (casting == NPY_UNSAFE_CASTING) {
+ return 1;
+ }
+
+ /*
+ * If the scalar isn't a number, or the rule is stricter than
+ * NPY_SAFE_CASTING, use the straight type-based rules
+ */
+ if (!PyTypeNum_ISNUMBER(scal_type->type_num) ||
+ casting < NPY_SAFE_CASTING) {
+ return PyArray_CanCastTypeTo(scal_type, to, casting);
+ }
+
+ swap = !PyArray_ISNBO(scal_type->byteorder);
+ scal_type->f->copyswap(&value, scal_data, swap, NULL);
+
+ type_num = min_scalar_type_num((char *)&value, scal_type->type_num,
+ &is_small_unsigned);
+
+ /*
+ * If we've got a small unsigned scalar, and the 'to' type
+ * is not unsigned, then make it signed to allow the value
+ * to be cast more appropriately.
+ */
+ if (is_small_unsigned && !(PyTypeNum_ISUNSIGNED(to->type_num))) {
+ type_num = type_num_unsigned_to_signed(type_num);
+ }
+
+ dtype = PyArray_DescrFromType(type_num);
+ if (dtype == NULL) {
+ return 0;
+ }
+#if 0
+ printf("min scalar cast ");
+ PyObject_Print(dtype, stdout, 0);
+ printf(" to ");
+ PyObject_Print(to, stdout, 0);
+ printf("\n");
+#endif
+ ret = PyArray_CanCastTypeTo(dtype, to, casting);
+ Py_DECREF(dtype);
+ return ret;
+}
+
/*NUMPY_API
* Returns 1 if the array object may be cast to the given data type using
* the casting rule, 0 otherwise. This differs from PyArray_CanCastTo in
@@ -652,47 +708,12 @@ PyArray_CanCastArrayTo(PyArrayObject *arr, PyArray_Descr *to,
PyArray_Descr *from = PyArray_DESCR(arr);
/* If it's not a scalar, use the standard rules */
- if (PyArray_NDIM(arr) > 0 || !PyTypeNum_ISNUMBER(from->type_num)) {
+ if (PyArray_NDIM(arr) > 0) {
return PyArray_CanCastTypeTo(from, to, casting);
}
/* Otherwise, check the value */
else {
- int swap = !PyArray_ISNBO(from->byteorder);
- int is_small_unsigned = 0, type_num;
- npy_bool ret;
- PyArray_Descr *dtype;
-
- /* An aligned memory buffer large enough to hold any type */
- npy_longlong value[4];
-
- from->f->copyswap(&value, PyArray_BYTES(arr), swap, NULL);
-
- type_num = min_scalar_type_num((char *)&value, from->type_num,
- &is_small_unsigned);
-
- /*
- * If we've got a small unsigned scalar, and the 'to' type
- * is not unsigned, then make it signed to allow the value
- * to be cast more appropriately.
- */
- if (is_small_unsigned && !(PyTypeNum_ISUNSIGNED(to->type_num))) {
- type_num = type_num_unsigned_to_signed(type_num);
- }
-
- dtype = PyArray_DescrFromType(type_num);
- if (dtype == NULL) {
- return 0;
- }
-#if 0
- printf("min scalar cast ");
- PyObject_Print(dtype, stdout, 0);
- printf(" to ");
- PyObject_Print(to, stdout, 0);
- printf("\n");
-#endif
- ret = PyArray_CanCastTypeTo(dtype, to, casting);
- Py_DECREF(dtype);
- return ret;
+ return can_cast_scalar_to(from, PyArray_DATA(arr), to, casting);
}
}
diff --git a/numpy/core/src/multiarray/convert_datatype.h b/numpy/core/src/multiarray/convert_datatype.h
index 71001b1c4..bf77d699a 100644
--- a/numpy/core/src/multiarray/convert_datatype.h
+++ b/numpy/core/src/multiarray/convert_datatype.h
@@ -13,6 +13,11 @@ PyArray_ConvertToCommonType(PyObject *op, int *retn);
NPY_NO_EXPORT int
PyArray_ValidType(int type);
+/* Like PyArray_CanCastArrayTo */
+NPY_NO_EXPORT npy_bool
+can_cast_scalar_to(PyArray_Descr *scal_type, char *scal_data,
+ PyArray_Descr *to, NPY_CASTING casting);
+
/*
* This function calls Py_DECREF on flex_dtype, and replaces it with
* a new dtype that has been adapted based on the values in data_dtype
diff --git a/numpy/core/src/multiarray/na_mask.c b/numpy/core/src/multiarray/na_mask.c
index bfc79a4eb..286fe5c1b 100644
--- a/numpy/core/src/multiarray/na_mask.c
+++ b/numpy/core/src/multiarray/na_mask.c
@@ -477,6 +477,8 @@ PyArray_ReduceMaskNAArray(int ndim, npy_intp *shape,
npy_intp src_strides_it[NPY_MAXDIMS];
npy_intp dst_strides_it[NPY_MAXDIMS];
+ char *saved_dst_data = dst_data;
+
/* Confirm that dst is not larger than src */
for (idim = 0; idim < ndim; ++idim) {
if (src_strides[idim] == 0 && dst_strides[idim] != 0) {
@@ -513,6 +515,8 @@ PyArray_ReduceMaskNAArray(int ndim, npy_intp *shape,
{
int i;
printf("Dump of raw iter:\n");
+ printf("dst data: %p\n", dst_data);
+ printf("original dst data: %p\n", saved_dst_data);
printf("ndim: %d\n", ndim);
printf("shape: ");
for (i = 0; i < ndim; ++i) printf("%d ", (int)shape[i]);
@@ -561,6 +565,7 @@ PyArray_ReduceMaskNAArray(int ndim, npy_intp *shape,
printf("s%d/d%d>>", (int)*src_d, (int)*dst_d);
*dst_d &= *src_d;
printf("d%d ", (int)*dst_d);
+printf("%p ", dst_d);
src_d += src_strides_it[0];
dst_d += dst_strides_it[0];
}
diff --git a/numpy/core/src/umath/ufunc_object.c b/numpy/core/src/umath/ufunc_object.c
index 3586e2ac2..9102f92b1 100644
--- a/numpy/core/src/umath/ufunc_object.c
+++ b/numpy/core/src/umath/ufunc_object.c
@@ -2705,9 +2705,9 @@ allocate_or_conform_reduce_result(PyArrayObject *arr, PyArrayObject *out,
npy_bool *axis_flags, PyArray_Descr * otype_dtype,
int addmask)
{
- if (out == NULL) {
- PyArrayObject *result;
+ PyArrayObject *result;
+ if (out == NULL) {
Py_INCREF(otype_dtype);
result = allocate_reduce_result(arr, axis_flags, otype_dtype);
@@ -2718,12 +2718,12 @@ allocate_or_conform_reduce_result(PyArrayObject *arr, PyArrayObject *out,
return NULL;
}
}
-
- return result;
}
else {
- return conform_reduce_result(PyArray_NDIM(arr), axis_flags, out);
+ result = conform_reduce_result(PyArray_NDIM(arr), axis_flags, out);
}
+
+ return result;
}
/*
@@ -3077,6 +3077,13 @@ PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
goto fail;
}
+if (ndim == 2 && PyArray_HASMASKNA(result)) {
+ printf ("after reduce maskna %d %d %d\n",
+ (int)PyArray_MASKNA_DATA(result)[0],
+ (int)PyArray_MASKNA_DATA(result)[1],
+ (int)PyArray_MASKNA_DATA(result)[2]);
+}
+
/* Short circuit any calculation if the result 0-dim NA */
if (PyArray_SIZE(result) == 1 &&
!NpyMaskValue_IsExposed(
@@ -3125,6 +3132,12 @@ PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
goto fail;
}
+if (ndim == 2 && PyArray_HASMASKNA(result)) {
+ printf ("after initialize reduce result maskna %d %d %d\n",
+ (int)PyArray_MASKNA_DATA(result)[0],
+ (int)PyArray_MASKNA_DATA(result)[1],
+ (int)PyArray_MASKNA_DATA(result)[2]);
+}
/* Now we can do a loop applying the ufunc in a straightforward manner */
op[0] = result;
op[1] = arr_view;
@@ -3220,6 +3233,12 @@ PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
}
/* Masked reduction */
else {
+if (ndim == 2 && PyArray_HASMASKNA(result)) {
+ printf ("before inner loop %d %d %d\n",
+ (int)PyArray_MASKNA_DATA(result)[0],
+ (int)PyArray_MASKNA_DATA(result)[1],
+ (int)PyArray_MASKNA_DATA(result)[2]);
+}
do {
/* Turn the two items into three for the inner loop */
dataptr_copy[0] = dataptr[0];
@@ -3236,6 +3255,12 @@ PyUFunc_Reduce(PyUFuncObject *self, PyArrayObject *arr, PyArrayObject *out,
stride_copy, stride[2], maskedinnerloopdata);
} while (iternext(iter));
}
+if (ndim == 2 && PyArray_HASMASKNA(result)) {
+ printf ("after inner loop %d %d %d\n",
+ (int)PyArray_MASKNA_DATA(result)[0],
+ (int)PyArray_MASKNA_DATA(result)[1],
+ (int)PyArray_MASKNA_DATA(result)[2]);
+}
if (!needs_api) {
NPY_END_THREADS;
@@ -3250,7 +3275,27 @@ finish:
/* Strip out the extra 'one' dimensions in the result */
if (out == NULL) {
+ int print = PyArray_NDIM(result) == 2 && PyArray_HASMASKNA(result);
+if (print) {
+ printf("maskna data %p\n", PyArray_MASKNA_DATA(result));
+ printf("maskna strides %d %d\n", (int)PyArray_MASKNA_STRIDES(result)[0],(int)PyArray_MASKNA_STRIDES(result)[1]);
+ printf ("maskna %d %d %d\n",
+ (int)PyArray_MASKNA_DATA(result)[0],
+ (int)PyArray_MASKNA_DATA(result)[1],
+ (int)PyArray_MASKNA_DATA(result)[2]);
+ PyObject_Print(result, stdout, 0);
+ printf("\n");
+}
strip_flagged_dimensions(result, axis_flags);
+if (print) {
+ printf("maskna stride %d\n", (int)PyArray_MASKNA_STRIDES(result)[0]);
+ printf ("maskna %d %d %d\n",
+ (int)PyArray_MASKNA_DATA(result)[0],
+ (int)PyArray_MASKNA_DATA(result)[1],
+ (int)PyArray_MASKNA_DATA(result)[2]);
+ PyObject_Print(result, stdout, 0);
+ printf("\n\n");
+}
}
else {
Py_DECREF(result);