/* * check if in "alhs @op@ orhs" that alhs is a temporary (refcnt == 1) so we * can do in-place operations instead of creating a new temporary * "cannot" is set to true if it cannot be done even with swapped arguments */ static int can_elide_temp(PyArrayObject * alhs, PyObject * orhs, int * cannot) { /* * to be a candidate the array needs to have reference count 1, be an exact * array of a basic type, own its data and size larger than threshold */ if (Py_REFCNT(alhs) != 1 || !PyArray_CheckExact(alhs) || !PyArray_ISNUMBER(alhs) || !PyArray_CHKFLAGS(alhs, NPY_ARRAY_OWNDATA) || !PyArray_ISWRITEABLE(alhs) || PyArray_CHKFLAGS(alhs, NPY_ARRAY_UPDATEIFCOPY) || PyArray_CHKFLAGS(alhs, NPY_ARRAY_WRITEBACKIFCOPY) || PyArray_NBYTES(alhs) < NPY_MIN_ELIDE_BYTES) { return 0; } if (PyArray_CheckExact(orhs) || PyArray_CheckAnyScalar(orhs)) { PyArrayObject * arhs; /* create array from right hand side */ Py_INCREF(orhs); arhs = (PyArrayObject *)PyArray_EnsureArray(orhs); if (arhs == NULL) { return 0; } /* * if rhs is not a scalar dimensions must match * TODO: one could allow broadcasting on equal types */ if (!(PyArray_NDIM(arhs) == 0 || (PyArray_NDIM(arhs) == PyArray_NDIM(alhs) && PyArray_CompareLists(PyArray_DIMS(alhs), PyArray_DIMS(arhs), PyArray_NDIM(arhs))))) { Py_DECREF(arhs); return 0; } /* must be safe to cast (checks values for scalar in rhs) */ if (PyArray_CanCastArrayTo(arhs, PyArray_DESCR(alhs), NPY_SAFE_CASTING)) { Py_DECREF(arhs); return check_callers(cannot); } Py_DECREF(arhs); } return 0; }
static PyObject * PyUFunc_Accumulate(PyUFuncObject *ufunc, PyArrayObject *arr, PyArrayObject *out, int axis, int otype) { PyArrayObject *op[2]; PyArray_Descr *op_dtypes[2] = {NULL, NULL}; int op_axes_arrays[2][NPY_MAXDIMS]; int *op_axes[2] = {op_axes_arrays[0], op_axes_arrays[1]}; npy_uint32 op_flags[2]; int idim, ndim, otype_final; int needs_api, need_outer_iterator; NpyIter *iter = NULL, *iter_inner = NULL; /* The selected inner loop */ PyUFuncGenericFunction innerloop = NULL; void *innerloopdata = NULL; const char *ufunc_name = ufunc->name ? ufunc->name : "(unknown)"; /* These parameters come from extobj= or from a TLS global */ int buffersize = 0, errormask = 0; NPY_BEGIN_THREADS_DEF; NPY_UF_DBG_PRINT1("\nEvaluating ufunc %s.accumulate\n", ufunc_name); #if 0 printf("Doing %s.accumulate on array with dtype : ", ufunc_name); PyObject_Print((PyObject *)PyArray_DESCR(arr), stdout, 0); printf("\n"); #endif if (_get_bufsize_errmask(NULL, "accumulate", &buffersize, &errormask) < 0) { return NULL; } /* Take a reference to out for later returning */ Py_XINCREF(out); otype_final = otype; if (get_binary_op_function(ufunc, &otype_final, &innerloop, &innerloopdata) < 0) { PyArray_Descr *dtype = PyArray_DescrFromType(otype); PyErr_Format(PyExc_ValueError, "could not find a matching type for %s.accumulate, " "requested type has type code '%c'", ufunc_name, dtype ? dtype->type : '-'); Py_XDECREF(dtype); goto fail; } ndim = PyArray_NDIM(arr); /* * 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)) { op_dtypes[0] = PyArray_DESCR(arr); Py_INCREF(op_dtypes[0]); } else { op_dtypes[0] = PyArray_DescrNewByteorder(PyArray_DESCR(arr), NPY_NATIVE); } } else { op_dtypes[0] = PyArray_DescrFromType(otype_final); } if (op_dtypes[0] == NULL) { goto fail; } #if NPY_UF_DBG_TRACING printf("Found %s.accumulate inner loop with dtype : ", ufunc_name); PyObject_Print((PyObject *)op_dtypes[0], stdout, 0); printf("\n"); #endif /* Set up the op_axes for the outer loop */ for (idim = 0; idim < ndim; ++idim) { op_axes_arrays[0][idim] = idim; op_axes_arrays[1][idim] = idim; } /* The per-operand flags for the outer loop */ op_flags[0] = NPY_ITER_READWRITE | NPY_ITER_NO_BROADCAST | NPY_ITER_ALLOCATE | NPY_ITER_NO_SUBTYPE; op_flags[1] = NPY_ITER_READONLY; op[0] = out; op[1] = arr; need_outer_iterator = (ndim > 1); /* We can't buffer, so must do UPDATEIFCOPY */ if (!PyArray_ISALIGNED(arr) || (out && !PyArray_ISALIGNED(out)) || !PyArray_EquivTypes(op_dtypes[0], PyArray_DESCR(arr)) || (out && !PyArray_EquivTypes(op_dtypes[0], PyArray_DESCR(out)))) { need_outer_iterator = 1; } if (need_outer_iterator) { int ndim_iter = 0; npy_uint32 flags = NPY_ITER_ZEROSIZE_OK| NPY_ITER_REFS_OK; PyArray_Descr **op_dtypes_param = NULL; /* * The way accumulate is set up, we can't do buffering, * so make a copy instead when necessary. */ ndim_iter = ndim; flags |= NPY_ITER_MULTI_INDEX; /* Add some more flags */ op_flags[0] |= NPY_ITER_UPDATEIFCOPY|NPY_ITER_ALIGNED; op_flags[1] |= NPY_ITER_COPY|NPY_ITER_ALIGNED; op_dtypes_param = op_dtypes; op_dtypes[1] = op_dtypes[0]; NPY_UF_DBG_PRINT("Allocating outer iterator\n"); iter = NpyIter_AdvancedNew(2, op, flags, NPY_KEEPORDER, NPY_UNSAFE_CASTING, op_flags, op_dtypes_param, ndim_iter, op_axes, NULL, 0); if (iter == NULL) { goto fail; } /* In case COPY or UPDATEIFCOPY occurred */ op[0] = NpyIter_GetOperandArray(iter)[0]; op[1] = NpyIter_GetOperandArray(iter)[1]; if (PyArray_SIZE(op[0]) == 0) { if (out == NULL) { out = op[0]; Py_INCREF(out); } goto finish; } if (NpyIter_RemoveAxis(iter, axis) != NPY_SUCCEED) { goto fail; } if (NpyIter_RemoveMultiIndex(iter) != NPY_SUCCEED) { goto fail; } } /* Get the output */ if (out == NULL) { if (iter) { op[0] = out = NpyIter_GetOperandArray(iter)[0]; Py_INCREF(out); } else { PyArray_Descr *dtype = op_dtypes[0]; Py_INCREF(dtype); op[0] = out = (PyArrayObject *)PyArray_NewFromDescr( &PyArray_Type, dtype, ndim, PyArray_DIMS(op[1]), NULL, NULL, 0, NULL); if (out == NULL) { goto fail; } } } /* * If the reduction axis has size zero, either return the reduction * unit for UFUNC_REDUCE, or return the zero-sized output array * for UFUNC_ACCUMULATE. */ if (PyArray_DIM(op[1], axis) == 0) { goto finish; } else if (PyArray_SIZE(op[0]) == 0) { goto finish; } if (iter && NpyIter_GetIterSize(iter) != 0) { char *dataptr_copy[3]; npy_intp stride_copy[3]; npy_intp count_m1, stride0, stride1; NpyIter_IterNextFunc *iternext; char **dataptr; int itemsize = op_dtypes[0]->elsize; /* Get the variables needed for the loop */ iternext = NpyIter_GetIterNext(iter, NULL); if (iternext == NULL) { goto fail; } dataptr = NpyIter_GetDataPtrArray(iter); /* Execute the loop with just the outer iterator */ count_m1 = PyArray_DIM(op[1], axis)-1; stride0 = 0, stride1 = PyArray_STRIDE(op[1], axis); NPY_UF_DBG_PRINT("UFunc: Reduce loop with just outer iterator\n"); stride0 = PyArray_STRIDE(op[0], axis); stride_copy[0] = stride0; stride_copy[1] = stride1; stride_copy[2] = stride0; needs_api = NpyIter_IterationNeedsAPI(iter); NPY_BEGIN_THREADS_NDITER(iter); do { dataptr_copy[0] = dataptr[0]; dataptr_copy[1] = dataptr[1]; dataptr_copy[2] = dataptr[0]; /* * Copy the first element to start the reduction. * * Output (dataptr[0]) and input (dataptr[1]) may point to * the same memory, e.g. np.add.accumulate(a, out=a). */ if (otype == NPY_OBJECT) { /* * Incref before decref to avoid the possibility of the * reference count being zero temporarily. */ Py_XINCREF(*(PyObject **)dataptr_copy[1]); Py_XDECREF(*(PyObject **)dataptr_copy[0]); *(PyObject **)dataptr_copy[0] = *(PyObject **)dataptr_copy[1]; } else { memmove(dataptr_copy[0], dataptr_copy[1], itemsize); } if (count_m1 > 0) { /* Turn the two items into three for the inner loop */ dataptr_copy[1] += stride1; dataptr_copy[2] += stride0; NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)count_m1); innerloop(dataptr_copy, &count_m1, stride_copy, innerloopdata); } } while (iternext(iter)); NPY_END_THREADS; } else if (iter == NULL) { char *dataptr_copy[3]; npy_intp stride_copy[3]; int itemsize = op_dtypes[0]->elsize; /* Execute the loop with no iterators */ npy_intp count = PyArray_DIM(op[1], axis); npy_intp stride0 = 0, stride1 = PyArray_STRIDE(op[1], axis); NPY_UF_DBG_PRINT("UFunc: Reduce loop with no iterators\n"); if (PyArray_NDIM(op[0]) != PyArray_NDIM(op[1]) || !PyArray_CompareLists(PyArray_DIMS(op[0]), PyArray_DIMS(op[1]), PyArray_NDIM(op[0]))) { PyErr_SetString(PyExc_ValueError, "provided out is the wrong size " "for the reduction"); goto fail; } stride0 = PyArray_STRIDE(op[0], axis); stride_copy[0] = stride0; stride_copy[1] = stride1; stride_copy[2] = stride0; /* Turn the two items into three for the inner loop */ dataptr_copy[0] = PyArray_BYTES(op[0]); dataptr_copy[1] = PyArray_BYTES(op[1]); dataptr_copy[2] = PyArray_BYTES(op[0]); /* * Copy the first element to start the reduction. * * Output (dataptr[0]) and input (dataptr[1]) may point to the * same memory, e.g. np.add.accumulate(a, out=a). */ if (otype == NPY_OBJECT) { /* * Incref before decref to avoid the possibility of the * reference count being zero temporarily. */ Py_XINCREF(*(PyObject **)dataptr_copy[1]); Py_XDECREF(*(PyObject **)dataptr_copy[0]); *(PyObject **)dataptr_copy[0] = *(PyObject **)dataptr_copy[1]; } else { memmove(dataptr_copy[0], dataptr_copy[1], itemsize); } if (count > 1) { --count; dataptr_copy[1] += stride1; dataptr_copy[2] += stride0; NPY_UF_DBG_PRINT1("iterator loop count %d\n", (int)count); needs_api = PyDataType_REFCHK(op_dtypes[0]); if (!needs_api) { NPY_BEGIN_THREADS_THRESHOLDED(count); } innerloop(dataptr_copy, &count, stride_copy, innerloopdata); NPY_END_THREADS; } } finish: Py_XDECREF(op_dtypes[0]); NpyIter_Deallocate(iter); NpyIter_Deallocate(iter_inner); return (PyObject *)out; fail: Py_XDECREF(out); Py_XDECREF(op_dtypes[0]); NpyIter_Deallocate(iter); NpyIter_Deallocate(iter_inner); return NULL; }
/*NUMPY_API * ArgMax */ NPY_NO_EXPORT PyObject * PyArray_ArgMax(PyArrayObject *op, int axis, PyArrayObject *out) { PyArrayObject *ap = NULL, *rp = NULL; PyArray_ArgFunc* arg_func; char *ip; npy_intp *rptr; npy_intp i, n, m; int elsize; NPY_BEGIN_THREADS_DEF; if ((ap = (PyArrayObject *)PyArray_CheckAxis(op, &axis, 0)) == NULL) { return NULL; } /* * We need to permute the array so that axis is placed at the end. * And all other dimensions are shifted left. */ if (axis != PyArray_NDIM(ap)-1) { PyArray_Dims newaxes; npy_intp dims[NPY_MAXDIMS]; int j; newaxes.ptr = dims; newaxes.len = PyArray_NDIM(ap); for (j = 0; j < axis; j++) { dims[j] = j; } for (j = axis; j < PyArray_NDIM(ap) - 1; j++) { dims[j] = j + 1; } dims[PyArray_NDIM(ap) - 1] = axis; op = (PyArrayObject *)PyArray_Transpose(ap, &newaxes); Py_DECREF(ap); if (op == NULL) { return NULL; } } else { op = ap; } /* Will get native-byte order contiguous copy. */ ap = (PyArrayObject *)PyArray_ContiguousFromAny((PyObject *)op, PyArray_DESCR(op)->type_num, 1, 0); Py_DECREF(op); if (ap == NULL) { return NULL; } arg_func = PyArray_DESCR(ap)->f->argmax; if (arg_func == NULL) { PyErr_SetString(PyExc_TypeError, "data type not ordered"); goto fail; } elsize = PyArray_DESCR(ap)->elsize; m = PyArray_DIMS(ap)[PyArray_NDIM(ap)-1]; if (m == 0) { PyErr_SetString(PyExc_ValueError, "attempt to get argmax of an empty sequence"); goto fail; } if (!out) { rp = (PyArrayObject *)PyArray_NewFromDescr( Py_TYPE(ap), PyArray_DescrFromType(NPY_INTP), PyArray_NDIM(ap) - 1, PyArray_DIMS(ap), NULL, NULL, 0, (PyObject *)ap); if (rp == NULL) { goto fail; } } else { if ((PyArray_NDIM(out) != PyArray_NDIM(ap) - 1) || !PyArray_CompareLists(PyArray_DIMS(out), PyArray_DIMS(ap), PyArray_NDIM(out))) { PyErr_SetString(PyExc_ValueError, "output array does not match result of np.argmax."); goto fail; } rp = (PyArrayObject *)PyArray_FromArray(out, PyArray_DescrFromType(NPY_INTP), NPY_ARRAY_CARRAY | NPY_ARRAY_WRITEBACKIFCOPY); if (rp == NULL) { goto fail; } } NPY_BEGIN_THREADS_DESCR(PyArray_DESCR(ap)); n = PyArray_SIZE(ap)/m; rptr = (npy_intp *)PyArray_DATA(rp); for (ip = PyArray_DATA(ap), i = 0; i < n; i++, ip += elsize*m) { arg_func(ip, m, rptr, ap); rptr += 1; } NPY_END_THREADS_DESCR(PyArray_DESCR(ap)); Py_DECREF(ap); /* Trigger the UPDATEIFCOPY/WRTIEBACKIFCOPY if necessary */ if (out != NULL && out != rp) { PyArray_ResolveWritebackIfCopy(rp); Py_DECREF(rp); rp = out; Py_INCREF(rp); } return (PyObject *)rp; fail: Py_DECREF(ap); Py_XDECREF(rp); return NULL; }
/*NUMPY_API*/ NPY_NO_EXPORT int PyArray_CopyObject(PyArrayObject *dest, PyObject *src_object) { int ret; PyArrayObject *src; PyArray_Descr *dtype = NULL; int ndim = 0; npy_intp dims[NPY_MAXDIMS]; Py_INCREF(src_object); /* * Special code to mimic Numeric behavior for * character arrays. */ if (dest->descr->type == PyArray_CHARLTR && dest->nd > 0 \ && PyString_Check(src_object)) { npy_intp n_new, n_old; char *new_string; PyObject *tmp; n_new = dest->dimensions[dest->nd-1]; n_old = PyString_Size(src_object); if (n_new > n_old) { new_string = (char *)malloc(n_new); memmove(new_string, PyString_AS_STRING(src_object), n_old); memset(new_string + n_old, ' ', n_new - n_old); tmp = PyString_FromStringAndSize(new_string, n_new); free(new_string); Py_DECREF(src_object); src_object = tmp; } } /* * Get either an array object we can copy from, or its parameters * if there isn't a convenient array available. */ if (PyArray_GetArrayParamsFromObject(src_object, PyArray_DESCR(dest), 0, &dtype, &ndim, dims, &src, NULL) < 0) { Py_DECREF(src_object); return -1; } /* If it's not an array, either assign from a sequence or as a scalar */ if (src == NULL) { /* If the input is scalar */ if (ndim == 0) { /* If there's one dest element and src is a Python scalar */ if (PyArray_IsScalar(src_object, Generic)) { src = (PyArrayObject *)PyArray_FromScalar(src_object, dtype); if (src == NULL) { Py_DECREF(src_object); return -1; } } else { if (PyArray_SIZE(dest) == 1) { Py_DECREF(dtype); return PyArray_DESCR(dest)->f->setitem(src_object, PyArray_DATA(dest), dest); } else { src = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, dtype, 0, NULL, NULL, NULL, 0, NULL); if (src == NULL) { Py_DECREF(src_object); return -1; } if (PyArray_DESCR(src)->f->setitem(src_object, PyArray_DATA(src), src) < 0) { Py_DECREF(src_object); Py_DECREF(src); return -1; } } } } else { /* If the dims match exactly, can assign directly */ if (ndim == PyArray_NDIM(dest) && PyArray_CompareLists(dims, PyArray_DIMS(dest), ndim)) { int res; Py_DECREF(dtype); res = PyArray_AssignFromSequence(dest, src_object); Py_DECREF(src_object); return res; } /* Otherwise convert to an array and do an array-based copy */ src = (PyArrayObject *)PyArray_NewFromDescr(&PyArray_Type, dtype, ndim, dims, NULL, NULL, PyArray_ISFORTRAN(dest), NULL); if (src == NULL) { Py_DECREF(src_object); return -1; } if (PyArray_AssignFromSequence(src, src_object) < 0) { Py_DECREF(src); Py_DECREF(src_object); return -1; } } } /* If it's an array, do a move (handling possible overlapping data) */ ret = PyArray_MoveInto(dest, src); Py_DECREF(src); Py_DECREF(src_object); return ret; }