diff options
author | Stefan van der Walt <stefan@sun.ac.za> | 2007-08-21 07:57:01 +0000 |
---|---|---|
committer | Stefan van der Walt <stefan@sun.ac.za> | 2007-08-21 07:57:01 +0000 |
commit | a46f16e2ec8ca594adc8a0c0203852e23c041901 (patch) | |
tree | a99dfc011bf5ba3acab753d6fb34338245b02426 /numpy | |
parent | 859439e5ebcdab8f5a5ced4dd42be537e4b037e5 (diff) | |
download | numpy-a46f16e2ec8ca594adc8a0c0203852e23c041901.tar.gz |
Fast putmask implementation (patch by Eric Firing).
Diffstat (limited to 'numpy')
-rw-r--r-- | numpy/core/include/numpy/ndarrayobject.h | 5 | ||||
-rw-r--r-- | numpy/core/src/arraytypes.inc.src | 157 | ||||
-rw-r--r-- | numpy/core/src/multiarraymodule.c | 189 | ||||
-rw-r--r-- | numpy/core/tests/test_multiarray.py | 23 |
4 files changed, 222 insertions, 152 deletions
diff --git a/numpy/core/include/numpy/ndarrayobject.h b/numpy/core/include/numpy/ndarrayobject.h index 7df85d8fc..e9ed7029e 100644 --- a/numpy/core/include/numpy/ndarrayobject.h +++ b/numpy/core/include/numpy/ndarrayobject.h @@ -1049,6 +1049,8 @@ typedef int (PyArray_ScalarKindFunc)(void *); typedef void (PyArray_FastClipFunc)(void *in, npy_intp n_in, void *min, void *max, void *out); +typedef void (PyArray_FastPutmaskFunc)(void *in, void *mask, npy_intp n_in, + void *values, npy_intp nv); typedef struct { npy_intp *ptr; @@ -1126,6 +1128,7 @@ typedef struct { int *cancastto; PyArray_FastClipFunc *fastclip; + PyArray_FastPutmaskFunc *fastputmask; } PyArray_ArrFuncs; #define NPY_ITEM_REFCOUNT 0x01 /* The item must be reference counted @@ -1933,7 +1936,7 @@ typedef struct { #define PyArray_GETPTR3(obj, i, j, k) ((void *)(PyArray_BYTES(obj) + \ (i)*PyArray_STRIDES(obj)[0] + \ (j)*PyArray_STRIDES(obj)[1] + \ - (k)*PyArray_STRIDES(obj)[2])) + (k)*PyArray_STRIDES(obj)[2])) #define PyArray_GETPTR4(obj, i, j, k, l) ((void *)(PyArray_BYTES(obj) + \ (i)*PyArray_STRIDES(obj)[0] + \ diff --git a/numpy/core/src/arraytypes.inc.src b/numpy/core/src/arraytypes.inc.src index 3ebc52f05..3208b86ca 100644 --- a/numpy/core/src/arraytypes.inc.src +++ b/numpy/core/src/arraytypes.inc.src @@ -312,9 +312,9 @@ UNICODE_setitem(PyObject *op, char *ov, PyArrayObject *ap) char *buffer; #endif - if (!PyString_Check(op) && !PyUnicode_Check(op) && + if (!PyString_Check(op) && !PyUnicode_Check(op) && PySequence_Check(op) && PySequence_Size(op) > 0) { - PyErr_SetString(PyExc_ValueError, + PyErr_SetString(PyExc_ValueError, "setting an array element with a sequence"); return -1; } @@ -360,9 +360,9 @@ UNICODE_setitem(PyObject *op, char *ov, PyArrayObject *ap) return 0; } -/* STRING -- can handle both NULL-terminated and not NULL-terminated cases +/* STRING -- can handle both NULL-terminated and not NULL-terminated cases will truncate all ending NULLs in returned string. -*/ +*/ static PyObject * STRING_getitem(char *ip, PyArrayObject *ap) { @@ -384,7 +384,7 @@ STRING_setitem(PyObject *op, char *ov, PyArrayObject *ap) if (!PyString_Check(op) && !PyUnicode_Check(op) && PySequence_Check(op) && PySequence_Size(op) > 0) { - PyErr_SetString(PyExc_ValueError, + PyErr_SetString(PyExc_ValueError, "setting an array element with a sequence"); return -1; } @@ -520,7 +520,7 @@ VOID_getitem(char *ip, PyArrayObject *ap) finish: if (PyDataType_FLAGCHK(descr, NPY_ITEM_HASOBJECT) || - PyDataType_FLAGCHK(descr, NPY_ITEM_IS_POINTER)) { + PyDataType_FLAGCHK(descr, NPY_ITEM_IS_POINTER)) { PyErr_SetString(PyExc_ValueError, "tried to get void-array with object" " members as buffer."); @@ -1104,15 +1104,15 @@ static void memcpy(dst, src, n*sizeof(@type@)); } else { - _unaligned_strided_byte_copy(dst, dstride, src, - sstride, n, + _unaligned_strided_byte_copy(dst, dstride, src, + sstride, n, sizeof(@type@)); } } if (swap) { _strided_byte_swap(dst, dstride, n, SIZEOF_@fsize@); - _strided_byte_swap(((char *)dst + SIZEOF_@fsize@), dstride, + _strided_byte_swap(((char *)dst + SIZEOF_@fsize@), dstride, n, SIZEOF_@fsize@); } } @@ -1306,7 +1306,7 @@ VOID_copyswapn (char *dst, intp dstride, char *src, intp sstride, arr->descr=descr;return; } arr->descr = new; - new->f->copyswapn(dst+offset, dstride, + new->f->copyswapn(dst+offset, dstride, (src != NULL ? src+offset : NULL), sstride, n, swap, arr); } @@ -1315,7 +1315,7 @@ VOID_copyswapn (char *dst, intp dstride, char *src, intp sstride, } if (swap && arr->descr->subarray != NULL) { PyArray_Descr *descr, *new; - npy_intp num; + npy_intp num; npy_intp i; int subitemsize; char *dstptr, *srcptr; @@ -1357,8 +1357,8 @@ VOID_copyswap (char *dst, char *src, int swap, PyArrayObject *arr) arr->descr=descr;return; } arr->descr = new; - new->f->copyswap(dst+offset, - (src != NULL ? src+offset : NULL), + new->f->copyswap(dst+offset, + (src != NULL ? src+offset : NULL), swap, arr); } arr->descr = descr; @@ -1366,7 +1366,7 @@ VOID_copyswap (char *dst, char *src, int swap, PyArrayObject *arr) } if (swap && arr->descr->subarray != NULL) { PyArray_Descr *descr, *new; - npy_intp num; + npy_intp num; int itemsize; descr = arr->descr; new = descr->subarray->base; @@ -1668,7 +1668,7 @@ OBJECT_compare(PyObject **ip1, PyObject **ip2, PyArrayObject *ap) if (ip1 == ip2) return 1; if (ip1 == NULL) return -1; return 1; - } + } return PyObject_Compare(*ip1, *ip2); } @@ -1700,9 +1700,9 @@ UNICODE_compare(register PyArray_UCS4 *ip1, register PyArray_UCS4 *ip2, /* If fields are defined, then compare on first field and if equal compare on second field. Continue until done or comparison results - in not_equal. + in not_equal. - Must align data passed on to sub-comparisons. + Must align data passed on to sub-comparisons. */ static int @@ -1714,9 +1714,9 @@ VOID_compare(char *ip1, char *ip2, PyArrayObject *ap) char *nip1, *nip2; int i, offset, res=0; - if (!PyArray_HASFIELDS(ap)) + if (!PyArray_HASFIELDS(ap)) return STRING_compare(ip1, ip2, ap); - + descr = ap->descr; /* Compare on the first-field. If equal, then compare on the second-field, etc. @@ -1743,7 +1743,7 @@ VOID_compare(char *ip1, char *ip2, PyArrayObject *ap) /* copy data to a buffer */ nip2 = _pya_malloc(new->elsize); if (nip2 == NULL) { - if (nip1 != ip1+offset) + if (nip1 != ip1+offset) _pya_free(nip1); goto finish; } @@ -2044,62 +2044,97 @@ static void -/************************ - * Fast clip functions - *************************/ - -/**begin repeat -#name=BOOL,BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE# -#type= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble# -*/ +/************************ + * Fast clip functions + *************************/ + +/**begin repeat +#name=BOOL,BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE# +#type= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble# +*/ static void @name@_fastclip(@type@ *in, intp ni, @type@ *min, @type@ *max, @type@ *out) -{ - register npy_intp i; +{ + register npy_intp i; @type@ max_val, min_val; - + max_val = *max; min_val = *min; - - for (i = 0; i < ni; i++) { - if (in[i] < min_val) { - out[i] = min_val; - } else if (in[i] > max_val) { - out[i] = max_val; - } - } - + + for (i = 0; i < ni; i++) { + if (in[i] < min_val) { + out[i] = min_val; + } else if (in[i] > max_val) { + out[i] = max_val; + } + } + return; -} -/**end repeat**/ +} +/**end repeat**/ -/**begin repeat -#name=CFLOAT, CDOUBLE, CLONGDOUBLE# -#type= cfloat, cdouble, clongdouble# -*/ +/**begin repeat +#name=CFLOAT, CDOUBLE, CLONGDOUBLE# +#type= cfloat, cdouble, clongdouble# +*/ static void -@name@_fastclip(@type@ *in, intp ni, @type@ *min, @type@ *max, @type@ *out) -{ - register npy_intp i; +@name@_fastclip(@type@ *in, intp ni, @type@ *min, @type@ *max, @type@ *out) +{ + register npy_intp i; @type@ max_val, min_val; - + min_val = *min; max_val = *max; - for (i = 0; i < ni; i++) { - if (PyArray_CLT(in[i], min_val)) { + for (i = 0; i < ni; i++) { + if (PyArray_CLT(in[i], min_val)) { out[i] = min_val; - } else if (PyArray_CGT(in[i], max_val)) { + } else if (PyArray_CGT(in[i], max_val)) { out[i] = max_val; - } - } + } + } return; -} - -/**end repeat**/ +} + +/**end repeat**/ #define OBJECT_fastclip NULL +/************************ + * Fast putmask functions + *************************/ + +/**begin repeat +#name=BOOL,BYTE, UBYTE, SHORT, USHORT, INT, UINT, LONG, ULONG, LONGLONG, ULONGLONG, FLOAT, DOUBLE, LONGDOUBLE,CFLOAT, CDOUBLE, CLONGDOUBLE# +#type= Bool, byte, ubyte, short, ushort, int, uint, long, ulong, longlong, ulonglong, float, double, longdouble,cfloat, cdouble, clongdouble# +*/ +static void +@name@_fastputmask(@type@ *in, Bool *mask, intp ni, @type@ *vals, intp nv) +{ + register npy_intp i; + @type@ s_val; + if (nv == 1) { + s_val = *vals; + for (i = 0; i < ni; i++) { + if (mask[i]) { + in[i] = s_val; + } + } + } + else { + for (i = 0; i < ni; i++) { + if (mask[i]) { + in[i] = vals[i%nv]; + } + } + } + return; +} +/**end repeat**/ + +#define OBJECT_fastputmask NULL + + #define _ALIGN(type) offsetof(struct {char c; type v;},v) /* Disable harmless compiler warning "4116: unnamed type definition in @@ -2164,7 +2199,8 @@ static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = { (PyArray_ScalarKindFunc*)NULL, NULL, NULL, - (PyArray_FastClipFunc *)NULL + (PyArray_FastClipFunc *)NULL, + (PyArray_FastPutmaskFunc *)NULL }; static PyArray_Descr @from@_Descr = { @@ -2241,7 +2277,8 @@ static PyArray_ArrFuncs _Py@NAME@_ArrFuncs = { (PyArray_ScalarKindFunc*)NULL, NULL, NULL, - (PyArray_FastClipFunc*)@from@_fastclip + (PyArray_FastClipFunc*)@from@_fastclip, + (PyArray_FastPutmaskFunc*)@from@_fastputmask }; static PyArray_Descr @from@_Descr = { diff --git a/numpy/core/src/multiarraymodule.c b/numpy/core/src/multiarraymodule.c index c63ebff6c..4df198344 100644 --- a/numpy/core/src/multiarraymodule.c +++ b/numpy/core/src/multiarraymodule.c @@ -23,7 +23,7 @@ #include "numpy/arrayobject.h" #define PyAO PyArrayObject - + static PyObject *typeDict=NULL; /* Must be explicitly loaded */ @@ -68,7 +68,7 @@ _arraydescr_fromobj(PyObject *obj) return NULL; } /* Understand ctypes structures -- - bit-fields are not supported + bit-fields are not supported automatically aligns */ dtypedescr = PyObject_GetAttrString(obj, "_fields_"); PyErr_Clear(); @@ -356,7 +356,7 @@ PyArray_Round(PyArrayObject *a, int decimals, PyArrayObject *out) Py_DECREF(out); if (ret_int) { Py_INCREF(a->descr); - tmp = PyArray_CastToType((PyArrayObject *)ret, + tmp = PyArray_CastToType((PyArrayObject *)ret, a->descr, PyArray_ISFORTRAN(a)); Py_DECREF(ret); return tmp; @@ -447,15 +447,15 @@ _check_ones(PyArrayObject *self, int newnd, intp* newdims, intp *strides) /* attempt to reshape an array without copying data * - * This function should correctly handle all reshapes, including + * This function should correctly handle all reshapes, including * axes of length 1. Zero strides should work but are untested. * * If a copy is needed, returns 0 - * If no copy is needed, returns 1 and fills newstrides + * If no copy is needed, returns 1 and fills newstrides * with appropriate strides * * The "fortran" argument describes how the array should be viewed - * during the reshape, not how it is stored in memory (that + * during the reshape, not how it is stored in memory (that * information is in self->strides). * * If some output dimensions have length 1, the strides assigned to @@ -463,7 +463,7 @@ _check_ones(PyArrayObject *self, int newnd, intp* newdims, intp *strides) * stride of the next-fastest index. */ static int -_attempt_nocopy_reshape(PyArrayObject *self, int newnd, intp* newdims, +_attempt_nocopy_reshape(PyArrayObject *self, int newnd, intp* newdims, intp *newstrides, int fortran) { int oldnd; @@ -483,10 +483,10 @@ _attempt_nocopy_reshape(PyArrayObject *self, int newnd, intp* newdims, /* fprintf(stderr, "_attempt_nocopy_reshape( ("); - for (oi=0; oi<oldnd; oi++) + for (oi=0; oi<oldnd; oi++) fprintf(stderr, "(%d,%d), ", olddims[oi], oldstrides[oi]); fprintf(stderr, ") -> ("); - for (ni=0; ni<newnd; ni++) + for (ni=0; ni<newnd; ni++) fprintf(stderr, "(%d,*), ", newdims[ni]); fprintf(stderr, "), fortran=%d)\n", fortran); */ @@ -522,22 +522,22 @@ _attempt_nocopy_reshape(PyArrayObject *self, int newnd, intp* newdims, for(ok=oi; ok<oj-1; ok++) { if (fortran) { if (oldstrides[ok+1] != \ - olddims[ok]*oldstrides[ok]) + olddims[ok]*oldstrides[ok]) return 0; /* not contiguous enough */ } else { /* C order */ if (oldstrides[ok] != \ - olddims[ok+1]*oldstrides[ok+1]) + olddims[ok+1]*oldstrides[ok+1]) return 0; /* not contiguous enough */ } } if (fortran) { newstrides[ni]=oldstrides[oi]; - for (nk=ni+1;nk<nj;nk++) + for (nk=ni+1;nk<nj;nk++) newstrides[nk]=newstrides[nk-1]*newdims[nk-1]; } else { /* C order */ newstrides[nj-1]=oldstrides[oj-1]; - for (nk=nj-1;nk>ni;nk--) + for (nk=nj-1;nk>ni;nk--) newstrides[nk-1]=newstrides[nk]*newdims[nk]; } @@ -548,10 +548,10 @@ _attempt_nocopy_reshape(PyArrayObject *self, int newnd, intp* newdims, /* fprintf(stderr, "success: _attempt_nocopy_reshape ("); - for (oi=0; oi<oldnd; oi++) + for (oi=0; oi<oldnd; oi++) fprintf(stderr, "(%d,%d), ", olddims[oi], oldstrides[oi]); fprintf(stderr, ") -> ("); - for (ni=0; ni<newnd; ni++) + for (ni=0; ni<newnd; ni++) fprintf(stderr, "(%d,%d), ", newdims[ni], newstrides[ni]); fprintf(stderr, ")\n"); */ @@ -605,7 +605,7 @@ _fix_unknown_dimension(PyArray_Dims *newshape, intp s_original) /* Returns a new array with the new shape from the data in the old array --- order-perspective depends on fortran argument. - copy-only-if-necessary + copy-only-if-necessary */ /*MULTIARRAY_API @@ -642,8 +642,8 @@ PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims, /* Returns a pointer to an appropriate strides array if all we are doing is inserting ones into the shape, or removing ones from the shape - or doing a combination of the two - In this case we don't need to do anything but update strides and + or doing a combination of the two + 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); @@ -652,22 +652,22 @@ PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims, flags = self->flags; if (strides==NULL) { /* we are really re-shaping not just adding ones - to the shape somewhere */ - - /* fix any -1 dimensions and check new-dimensions against + to the shape somewhere */ + + /* fix any -1 dimensions and check new-dimensions against old size */ if (_fix_unknown_dimension(newdims, PyArray_SIZE(self)) < 0) return NULL; /* sometimes we have to create a new copy of the array in order to get the right orientation and - because we can't just re-use the buffer with the + because we can't just re-use the buffer with the data in the order it is in. */ - if (!(PyArray_ISONESEGMENT(self)) || - (((PyArray_CHKFLAGS(self, NPY_CONTIGUOUS) && + if (!(PyArray_ISONESEGMENT(self)) || + (((PyArray_CHKFLAGS(self, NPY_CONTIGUOUS) && fortran == NPY_FORTRANORDER) - || (PyArray_CHKFLAGS(self, NPY_FORTRAN) && + || (PyArray_CHKFLAGS(self, NPY_FORTRAN) && fortran == NPY_CORDER)) && (self->nd > 1))) { int success=0; @@ -690,7 +690,7 @@ PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims, /* We always have to interpret the contiguous buffer correctly */ - /* Make sure the flags argument is set. + /* Make sure the flags argument is set. */ if (n > 1) { if (fortran == NPY_FORTRANORDER) { @@ -1139,7 +1139,7 @@ static PyObject * PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *out) { PyArray_FastClipFunc *func; - int outgood=0, ingood=0; + int outgood=0, ingood=0; PyArrayObject *maxa=NULL; PyArrayObject *mina=NULL; PyArrayObject *newout=NULL, *newin=NULL; @@ -1147,7 +1147,7 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o PyObject *zero; func = self->descr->f->fastclip; - if (func == NULL || !PyArray_CheckAnyScalar(min) || + if (func == NULL || !PyArray_CheckAnyScalar(min) || !PyArray_CheckAnyScalar(max)) return _slow_array_clip(self, min, max, out); @@ -1164,7 +1164,7 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o KIND than the input array (and then find the type that matches both). */ - if (PyArray_ScalarKind(newdescr->type_num, NULL) > + if (PyArray_ScalarKind(newdescr->type_num, NULL) > PyArray_ScalarKind(self->descr->type_num, NULL)) { indescr = _array_small_type(newdescr, self->descr); func = indescr->f->fastclip; @@ -1174,7 +1174,7 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o Py_INCREF(indescr); } Py_DECREF(newdescr); - + if (!PyDataType_ISNOTSWAPPED(indescr)) { PyArray_Descr *descr2; descr2 = PyArray_DescrNewByteorder(indescr, '='); @@ -1184,19 +1184,19 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o } /* Convert max to an array */ - maxa = (NPY_AO *)PyArray_FromAny(max, indescr, 0, 0, + maxa = (NPY_AO *)PyArray_FromAny(max, indescr, 0, 0, NPY_DEFAULT, NULL); if (maxa == NULL) return NULL; /* If we are unsigned, then make sure min is not <0 */ - /* This is to match the behavior of + /* This is to match the behavior of _slow_array_clip We allow min and max to go beyond the limits for other data-types in which case they are interpreted as their modular counterparts. - */ + */ if (PyArray_ISUNSIGNED(self)) { int cmp; zero = PyInt_FromLong(0); @@ -1216,7 +1216,7 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o /* Convert min to an array */ Py_INCREF(indescr); - mina = (NPY_AO *)PyArray_FromAny(min, indescr, 0, 0, + mina = (NPY_AO *)PyArray_FromAny(min, indescr, 0, 0, NPY_DEFAULT, NULL); Py_DECREF(min); if (mina == NULL) goto fail; @@ -1244,11 +1244,11 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o /* At this point, newin is a single-segment, aligned, and correct byte-order array of the correct type - if ingood == 0, then it is a copy, otherwise, + if ingood == 0, then it is a copy, otherwise, it is the original input. */ - /* If we have already made a copy of the data, then use + /* If we have already made a copy of the data, then use that as the output array */ if (out == NULL && !ingood) { @@ -1258,10 +1258,10 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o /* Now, we know newin is a usable array for fastclip, we need to make sure the output array is available and usable */ - if (out == NULL) { + if (out == NULL) { Py_INCREF(indescr); out = (NPY_AO*)PyArray_NewFromDescr(self->ob_type, - indescr, self->nd, + indescr, self->nd, self->dimensions, NULL, NULL, PyArray_ISFORTRAN(self), @@ -1274,8 +1274,8 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o if (out == newin) { outgood = 1; } - if (!outgood && PyArray_ISONESEGMENT(out) && - PyArray_CHKFLAGS(out, ALIGNED) && PyArray_ISNOTSWAPPED(out) && + if (!outgood && PyArray_ISONESEGMENT(out) && + PyArray_CHKFLAGS(out, ALIGNED) && PyArray_ISNOTSWAPPED(out) && PyArray_EquivTypes(out->descr, indescr)) { outgood = 1; } @@ -1284,9 +1284,9 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o /* Create one, now */ if (!outgood) { int oflags; - if (PyArray_ISFORTRAN(out)) + if (PyArray_ISFORTRAN(out)) oflags = NPY_FARRAY; - else + else oflags = NPY_CARRAY; oflags |= NPY_UPDATEIFCOPY | NPY_FORCECAST; Py_INCREF(indescr); @@ -1308,12 +1308,12 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o if (newout->data != newin->data) { memcpy(newout->data, newin->data, PyArray_NBYTES(newin)); } - + /* Now we can call the fast-clip function */ func(newin->data, PyArray_SIZE(newin), mina->data, maxa->data, newout->data); - + /* Clean up temporary variables */ Py_DECREF(mina); Py_DECREF(maxa); @@ -1321,7 +1321,7 @@ PyArray_Clip(PyArrayObject *self, PyObject *min, PyObject *max, PyArrayObject *o /* Copy back into out if out was not already a nice array. */ Py_DECREF(newout); return (PyObject *)out; - + fail: Py_XDECREF(maxa); Py_XDECREF(mina); @@ -1339,11 +1339,11 @@ PyArray_Conjugate(PyArrayObject *self, PyArrayObject *out) { if (PyArray_ISCOMPLEX(self)) { if (out == NULL) { - return PyArray_GenericUnaryFunction(self, + return PyArray_GenericUnaryFunction(self, n_ops.conjugate); } else { - return PyArray_GenericBinaryFunction(self, + return PyArray_GenericBinaryFunction(self, (PyObject *)out, n_ops.conjugate); } @@ -2114,7 +2114,7 @@ PyArray_ConvertToCommonType(PyObject *op, int *retn) Py_DECREF(mps[i]); mps[i] = (PyArrayObject *)obj; } - } + } return mps; } @@ -2186,7 +2186,7 @@ PyArray_ConvertToCommonType(PyObject *op, int *retn) if (mps[i] == NULL) goto fail; } Py_DECREF(intype); - Py_XDECREF(stype); + Py_XDECREF(stype); return mps; fail: @@ -2567,7 +2567,7 @@ PyArray_Sort(PyArrayObject *op, int axis, NPY_SORTKIND which) n = op->nd; if ((n==0) || (PyArray_SIZE(op)==1)) return 0; - + if (axis < 0) axis += n; if ((axis < 0) || (axis >= n)) { PyErr_Format(PyExc_ValueError, @@ -2667,7 +2667,7 @@ PyArray_ArgSort(PyArrayObject *op, int axis, NPY_SORTKIND which) /* Creates new reference op2 */ if ((op2=(PyAO *)_check_axis(op, &axis, 0))==NULL) return NULL; - + /* Determine if we should use new algorithm or not */ if (op2->descr->f->argsort[which] != NULL) { ret = (PyArrayObject *)_new_argsort(op2, axis, which); @@ -2790,7 +2790,7 @@ PyArray_LexSort(PyObject *sort_keys, int axis) "merge sort not available for item %d", i); goto fail; } - if (!object && + if (!object && PyDataType_FLAGCHK(mps[i]->descr, NPY_NEEDS_PYAPI)) object = 1; its[i] = (PyArrayIterObject *)PyArray_IterAllButAxis \ @@ -2805,7 +2805,7 @@ PyArray_LexSort(PyObject *sort_keys, int axis) mps[0]->dimensions, PyArray_INTP, NULL, NULL, 0, 0, NULL); - + if (ret == NULL) goto fail; *((intp *)(ret->data)) = 0; goto finish; @@ -3288,7 +3288,7 @@ PyArray_MatrixProduct(PyObject *op1, PyObject *op2) nd = ap1->nd+ap2->nd-2; if (nd > NPY_MAXDIMS) { - PyErr_SetString(PyExc_ValueError, + PyErr_SetString(PyExc_ValueError, "dot: too many dimensions in result"); goto fail; } @@ -4047,8 +4047,8 @@ static PyObject * array_putmask(PyObject *module, PyObject *args, PyObject *kwds) { PyObject *mask, *values; - PyObject *array; - + PyObject *array; + static char *kwlist[] = {"arr", "mask", "values", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!OO:putmask", kwlist, @@ -4065,6 +4065,7 @@ array_putmask(PyObject *module, PyObject *args, PyObject *kwds) static PyObject * PyArray_PutMask(PyArrayObject *self, PyObject* values0, PyObject* mask0) { + PyArray_FastPutmaskFunc *func; PyArrayObject *mask, *values; int i, chunk, ni, max_item, nv, tmp; char *src, *dest; @@ -4075,8 +4076,8 @@ PyArray_PutMask(PyArrayObject *self, PyObject* values0, PyObject* mask0) if (!PyArray_Check(self)) { PyErr_SetString(PyExc_TypeError, - "putmask: first argument must "\ - "be an array"); + "putmask: first argument must "\ + "be an array"); return NULL; } if (!PyArray_ISCONTIGUOUS(self)) { @@ -4094,45 +4095,51 @@ PyArray_PutMask(PyArrayObject *self, PyObject* values0, PyObject* mask0) chunk = self->descr->elsize; mask = (PyArrayObject *)\ - PyArray_FROM_OTF(mask0, PyArray_BOOL, CARRAY | FORCECAST); - if (mask == NULL) goto fail; + PyArray_FROM_OTF(mask0, PyArray_BOOL, CARRAY | FORCECAST); + if (mask == NULL) goto fail; ni = PyArray_SIZE(mask); if (ni != max_item) { PyErr_SetString(PyExc_ValueError, - "putmask: mask and data must be "\ - "the same size"); + "putmask: mask and data must be "\ + "the same size"); goto fail; } values = (PyArrayObject *)\ - PyArray_ContiguousFromAny(values0, self->descr->type_num, 0, 0); - if (values == NULL) goto fail; - nv = PyArray_SIZE(values); /* zero if null array */ + PyArray_ContiguousFromAny(values0, self->descr->type_num, 0, 0); + if (values == NULL) goto fail; + nv = PyArray_SIZE(values); /* zero if null array */ if (nv <= 0) { Py_XDECREF(values); Py_XDECREF(mask); Py_INCREF(Py_None); return Py_None; } - if (nv > 0) { - if (PyDataType_REFCHK(self->descr)) { - for(i=0; i<ni; i++) { + if (PyDataType_REFCHK(self->descr)) { + for(i=0; i<ni; i++) { + tmp = ((Bool *)(mask->data))[i]; + if (tmp) { src = values->data + chunk * (i % nv); + PyArray_Item_INCREF(src, self->descr); + PyArray_Item_XDECREF(dest+i*chunk, self->descr); + memmove(dest + i * chunk, src, chunk); + } + } + } + else { + func = self->descr->f->fastputmask; + if (func == NULL) { + for(i=0; i<ni; i++) { tmp = ((Bool *)(mask->data))[i]; if (tmp) { - PyArray_Item_INCREF(src, self->descr); - PyArray_Item_XDECREF(dest+i*chunk, self->descr); + src = values->data + chunk * (i % nv); memmove(dest + i * chunk, src, chunk); } - } + } } else { - for(i=0; i<ni; i++) { - src = values->data + chunk * (i % nv); - tmp = ((Bool *)(mask->data))[i]; - if (tmp) memmove(dest + i * chunk, src, chunk); - } - } + func(dest, mask->data, ni, values->data, nv); + } } Py_XDECREF(values); @@ -4625,7 +4632,7 @@ _convert_from_tuple(PyObject *obj) "invalid shape in fixed-type tuple."); goto fail; } - /* If (type, 1) was given, it is equivalent to type... + /* If (type, 1) was given, it is equivalent to type... or (type, ()) was given it is equivalent to type... */ if ((shape.len == 1 && shape.ptr[0] == 1 && PyNumber_Check(val)) || \ (shape.len == 0 && PyTuple_Check(val))) { @@ -4763,7 +4770,7 @@ _convert_from_array_descr(PyObject *obj, int align) if (maxalign > 1) { totalsize = ((totalsize+maxalign-1)/maxalign)*maxalign; } - if (align) new->alignment = maxalign; + if (align) new->alignment = maxalign; return new; fail: @@ -5092,16 +5099,16 @@ _check_for_commastring(char *type, int len) /* Check for ints at start of string */ if ((type[0] >= '0' && type[0] <= '9') || - ((len > 1) && _chk_byteorder(type[0]) && + ((len > 1) && _chk_byteorder(type[0]) && (type[1] >= '0' && type[1] <= '9'))) return 1; /* Check for empty tuple */ if (((len > 1) && (type[0] == '(' && type[1] == ')')) || - ((len > 3) && _chk_byteorder(type[0]) && + ((len > 3) && _chk_byteorder(type[0]) && (type[1] == '(' && type[2] == ')'))) return 1; - + /* Check for presence of commas */ for (i=1;i<len;i++) if (type[i] == ',') return 1; @@ -5148,7 +5155,7 @@ PyArray_DescrAlignConverter(PyObject *obj, PyArray_Descr **at) "data-type-descriptor not understood"); } return PY_FAIL; - } + } return PY_SUCCEED; } @@ -5177,7 +5184,7 @@ PyArray_DescrAlignConverter2(PyObject *obj, PyArray_Descr **at) "data-type-descriptor not understood"); } return PY_FAIL; - } + } return PY_SUCCEED; } @@ -5724,7 +5731,7 @@ array_scalar(PyObject *ignored, PyObject *args, PyObject *kwds) "itemsize cannot be zero"); return NULL; } - + if (PyDataType_FLAGCHK(typecode, NPY_ITEM_IS_POINTER)) { if (obj == NULL) obj = Py_None; dptr = &obj; @@ -6652,7 +6659,7 @@ _calc_length(PyObject *start, PyObject *stop, PyObject *step, PyObject **next, i if (!(*next)) { if (PyTuple_Check(stop)) { PyErr_Clear(); - PyErr_SetString(PyExc_TypeError, + PyErr_SetString(PyExc_TypeError, "arange: scalar arguments expected "\ "instead of a tuple."); } @@ -7089,10 +7096,10 @@ as_buffer(PyObject *dummy, PyObject *args, PyObject *kwds) __except(1) { err = 1; } -#else +#else PyOS_sighandler_t _npy_sig_save; _npy_sig_save = PyOS_setsig(SIGSEGV, _SigSegv_Handler); - + if (setjmp(_NPY_SIGSEGV_BUF) == 0) { _test_code(); } @@ -7102,10 +7109,10 @@ as_buffer(PyObject *dummy, PyObject *args, PyObject *kwds) PyOS_setsig(SIGSEGV, _npy_sig_save); #endif if (err) { - PyErr_SetString(PyExc_ValueError, + PyErr_SetString(PyExc_ValueError, "cannot use memory location as " \ "a buffer."); - return NULL; + return NULL; } } @@ -7493,7 +7500,7 @@ set_flaginfo(PyObject *d) _addnew(WRITEABLE, W); _addone(C_CONTIGUOUS); _addone(F_CONTIGUOUS); - + #undef _addone #undef _addnew diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index d7ab0be93..7284de3ac 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -420,6 +420,29 @@ class test_clip(NumpyTestCase): y = rec['x'].clip(-0.3,0.5) self._check_range(y,-0.3,0.5) +class test_putmask(ParametricTestCase): + def tst_basic(self,x,T,mask,val): + N.putmask(x,mask,val) + assert N.all(x[mask] == T(val)) + assert x.dtype == T + + def testip_types(self): + unchecked_types = [str,unicode,N.void,object] + + x = N.random.random(1000)*100 + mask = x < 40 + + tests = [] + for val in [-100,0,15]: + for types in N.sctypes.itervalues(): + tests.extend((self.tst_basic,x.copy().astype(T),T,mask,val) + for T in types if T not in unchecked_types) + return tests + + def test_mask_size(self): + self.failUnlessRaises(ValueError,N.putmask, + N.array([1,2,3]),[True],5) + # Import tests from unicode set_local_path() from test_unicode import * |