/* * Conforms an output parameter 'out' to have 'ndim' dimensions * with dimensions of size one added in the appropriate places * indicated by 'axis_flags'. * * The return value is a view into 'out'. */ static PyArrayObject * conform_reduce_result(int ndim, npy_bool *axis_flags, PyArrayObject *out, int keepdims, const char *funcname, int need_copy) { npy_intp strides[NPY_MAXDIMS], shape[NPY_MAXDIMS]; npy_intp *strides_out = PyArray_STRIDES(out); npy_intp *shape_out = PyArray_DIMS(out); int idim, idim_out, ndim_out = PyArray_NDIM(out); PyArray_Descr *dtype; PyArrayObject_fields *ret; /* * If the 'keepdims' parameter is true, do a simpler validation and * return a new reference to 'out'. */ if (keepdims) { if (PyArray_NDIM(out) != ndim) { PyErr_Format(PyExc_ValueError, "output parameter for reduction operation %s " "has the wrong number of dimensions (must match " "the operand's when keepdims=True)", funcname); return NULL; } for (idim = 0; idim < ndim; ++idim) { if (axis_flags[idim]) { if (shape_out[idim] != 1) { PyErr_Format(PyExc_ValueError, "output parameter for reduction operation %s " "has a reduction dimension not equal to one " "(required when keepdims=True)", funcname); return NULL; } } } Py_INCREF(out); return out; } /* Construct the strides and shape */ idim_out = 0; for (idim = 0; idim < ndim; ++idim) { if (axis_flags[idim]) { strides[idim] = 0; shape[idim] = 1; } else { if (idim_out >= ndim_out) { PyErr_Format(PyExc_ValueError, "output parameter for reduction operation %s " "does not have enough dimensions", funcname); return NULL; } strides[idim] = strides_out[idim_out]; shape[idim] = shape_out[idim_out]; ++idim_out; } } if (idim_out != ndim_out) { PyErr_Format(PyExc_ValueError, "output parameter for reduction operation %s " "has too many dimensions", funcname); return NULL; } /* Allocate the view */ dtype = PyArray_DESCR(out); Py_INCREF(dtype); ret = (PyArrayObject_fields *)PyArray_NewFromDescr(&PyArray_Type, dtype, ndim, shape, strides, PyArray_DATA(out), PyArray_FLAGS(out), NULL); if (ret == NULL) { return NULL; } Py_INCREF(out); if (PyArray_SetBaseObject((PyArrayObject *)ret, (PyObject *)out) < 0) { Py_DECREF(ret); return NULL; } if (need_copy) { PyArrayObject *ret_copy; ret_copy = (PyArrayObject *)PyArray_NewLikeArray( (PyArrayObject *)ret, NPY_ANYORDER, NULL, 0); if (ret_copy == NULL) { Py_DECREF(ret); return NULL; } if (PyArray_CopyInto(ret_copy, (PyArrayObject *)ret) != 0) { Py_DECREF(ret); Py_DECREF(ret_copy); return NULL; } Py_INCREF(ret); if (PyArray_SetWritebackIfCopyBase(ret_copy, (PyArrayObject *)ret) < 0) { Py_DECREF(ret); Py_DECREF(ret_copy); return NULL; } return ret_copy; } else { return (PyArrayObject *)ret; } }
/* * This function initializes a result array for a reduction operation * which has no identity. This means it needs to copy the first element * it sees along the reduction axes to result, then return a view of * the operand which excludes that element. * * If a reduction has an identity, such as 0 or 1, the result should be * initialized by calling PyArray_AssignZero(result, NULL, NULL) or * PyArray_AssignOne(result, NULL, NULL), because this function raises an * exception when there are no elements to reduce (which appropriate iff the * reduction operation has no identity). * * This means it copies the subarray indexed at zero along each reduction axis * into 'result', then returns a view into 'operand' excluding those copied * elements. * * result : The array into which the result is computed. This must have * the same number of dimensions as 'operand', but for each * axis i where 'axis_flags[i]' is True, it has a single element. * operand : The array being reduced. * axis_flags : An array of boolean flags, one for each axis of 'operand'. * When a flag is True, it indicates to reduce along that axis. * reorderable : If True, the reduction being done is reorderable, which * means specifying multiple axes of reduction at once is ok, * and the reduction code may calculate the reduction in an * arbitrary order. The calculation may be reordered because * of cache behavior or multithreading requirements. * out_skip_first_count : This gets populated with the number of first-visit * elements that should be skipped during the * iteration loop. * funcname : The name of the reduction operation, for the purpose of * better quality error messages. For example, "numpy.max" * would be a good name for NumPy's max function. * * Returns a view which contains the remaining elements on which to do * the reduction. */ NPY_NO_EXPORT PyArrayObject * PyArray_InitializeReduceResult( PyArrayObject *result, PyArrayObject *operand, npy_bool *axis_flags, int reorderable, npy_intp *out_skip_first_count, const char *funcname) { npy_intp *strides, *shape, shape_orig[NPY_MAXDIMS]; PyArrayObject *op_view = NULL; int idim, ndim, nreduce_axes; ndim = PyArray_NDIM(operand); /* Default to no skipping first-visit elements in the iteration */ *out_skip_first_count = 0; /* * If this reduction is non-reorderable, make sure there are * only 0 or 1 axes in axis_flags. */ if (!reorderable && check_nonreorderable_axes(ndim, axis_flags, funcname) < 0) { return NULL; } /* Take a view into 'operand' which we can modify. */ op_view = (PyArrayObject *)PyArray_View(operand, NULL, &PyArray_Type); if (op_view == NULL) { return NULL; } /* * Now copy the subarray of the first element along each reduction axis, * then return a view to the rest. * * Adjust the shape to only look at the first element along * any of the reduction axes. We count the number of reduction axes * at the same time. */ shape = PyArray_SHAPE(op_view); nreduce_axes = 0; memcpy(shape_orig, shape, ndim * sizeof(npy_intp)); for (idim = 0; idim < ndim; ++idim) { if (axis_flags[idim]) { if (shape[idim] == 0) { PyErr_Format(PyExc_ValueError, "zero-size array to reduction operation %s " "which has no identity", funcname); Py_DECREF(op_view); return NULL; } shape[idim] = 1; ++nreduce_axes; } } /* * Copy the elements into the result to start. */ if (PyArray_CopyInto(result, op_view) < 0) { Py_DECREF(op_view); return NULL; } /* * If there is one reduction axis, adjust the view's * shape to only look at the remaining elements */ if (nreduce_axes == 1) { strides = PyArray_STRIDES(op_view); for (idim = 0; idim < ndim; ++idim) { if (axis_flags[idim]) { shape[idim] = shape_orig[idim] - 1; ((PyArrayObject_fields *)op_view)->data += strides[idim]; } } } /* If there are zero reduction axes, make the view empty */ else if (nreduce_axes == 0) { for (idim = 0; idim < ndim; ++idim) { shape[idim] = 0; } } /* * Otherwise iterate over the whole operand, but tell the inner loop * to skip the elements we already copied by setting the skip_first_count. */ else { *out_skip_first_count = PyArray_SIZE(result); Py_DECREF(op_view); Py_INCREF(operand); op_view = operand; } return op_view; }
extern PyArrayObject* array_from_pyobj(const int type_num, npy_intp *dims, const int rank, const int intent, PyObject *obj) { /* Note about reference counting ----------------------------- If the caller returns the array to Python, it must be done with Py_BuildValue("N",arr). Otherwise, if obj!=arr then the caller must call Py_DECREF(arr). Note on intent(cache,out,..) --------------------- Don't expect correct data when returning intent(cache) array. */ char mess[200]; PyArrayObject *arr = NULL; PyArray_Descr *descr; char typechar; int elsize; if ((intent & F2PY_INTENT_HIDE) || ((intent & F2PY_INTENT_CACHE) && (obj==Py_None)) || ((intent & F2PY_OPTIONAL) && (obj==Py_None)) ) { /* intent(cache), optional, intent(hide) */ if (count_nonpos(rank,dims)) { int i; strcpy(mess, "failed to create intent(cache|hide)|optional array" "-- must have defined dimensions but got ("); for(i=0;i<rank;++i) sprintf(mess+strlen(mess),"%" NPY_INTP_FMT ",",dims[i]); strcat(mess, ")"); PyErr_SetString(PyExc_ValueError,mess); return NULL; } arr = (PyArrayObject *) PyArray_New(&PyArray_Type, rank, dims, type_num, NULL,NULL,0, !(intent&F2PY_INTENT_C), NULL); if (arr==NULL) return NULL; if (!(intent & F2PY_INTENT_CACHE)) PyArray_FILLWBYTE(arr, 0); return arr; } descr = PyArray_DescrFromType(type_num); elsize = descr->elsize; typechar = descr->type; Py_DECREF(descr); if (PyArray_Check(obj)) { arr = (PyArrayObject *)obj; if (intent & F2PY_INTENT_CACHE) { /* intent(cache) */ if (PyArray_ISONESEGMENT(arr) && PyArray_ITEMSIZE(arr)>=elsize) { if (check_and_fix_dimensions(arr,rank,dims)) { return NULL; /*XXX: set exception */ } if (intent & F2PY_INTENT_OUT) Py_INCREF(arr); return arr; } strcpy(mess, "failed to initialize intent(cache) array"); if (!PyArray_ISONESEGMENT(arr)) strcat(mess, " -- input must be in one segment"); if (PyArray_ITEMSIZE(arr)<elsize) sprintf(mess+strlen(mess), " -- expected at least elsize=%d but got %" NPY_INTP_FMT, elsize, (npy_intp)PyArray_ITEMSIZE(arr) ); PyErr_SetString(PyExc_ValueError,mess); return NULL; } /* here we have always intent(in) or intent(inout) or intent(inplace) */ if (check_and_fix_dimensions(arr,rank,dims)) { return NULL; /*XXX: set exception */ } /* printf("intent alignement=%d\n", F2PY_GET_ALIGNMENT(intent)); printf("alignement check=%d\n", F2PY_CHECK_ALIGNMENT(arr, intent)); int i; for (i=1;i<=16;i++) printf("i=%d isaligned=%d\n", i, ARRAY_ISALIGNED(arr, i)); */ if ((! (intent & F2PY_INTENT_COPY)) && PyArray_ITEMSIZE(arr)==elsize && ARRAY_ISCOMPATIBLE(arr,type_num) && F2PY_CHECK_ALIGNMENT(arr, intent) ) { if ((intent & F2PY_INTENT_C)?PyArray_ISCARRAY(arr):PyArray_ISFARRAY(arr)) { if ((intent & F2PY_INTENT_OUT)) { Py_INCREF(arr); } /* Returning input array */ return arr; } } if (intent & F2PY_INTENT_INOUT) { strcpy(mess, "failed to initialize intent(inout) array"); if ((intent & F2PY_INTENT_C) && !PyArray_ISCARRAY(arr)) strcat(mess, " -- input not contiguous"); if (!(intent & F2PY_INTENT_C) && !PyArray_ISFARRAY(arr)) strcat(mess, " -- input not fortran contiguous"); if (PyArray_ITEMSIZE(arr)!=elsize) sprintf(mess+strlen(mess), " -- expected elsize=%d but got %" NPY_INTP_FMT, elsize, (npy_intp)PyArray_ITEMSIZE(arr) ); if (!(ARRAY_ISCOMPATIBLE(arr,type_num))) sprintf(mess+strlen(mess)," -- input '%c' not compatible to '%c'", PyArray_DESCR(arr)->type,typechar); if (!(F2PY_CHECK_ALIGNMENT(arr, intent))) sprintf(mess+strlen(mess)," -- input not %d-aligned", F2PY_GET_ALIGNMENT(intent)); PyErr_SetString(PyExc_ValueError,mess); return NULL; } /* here we have always intent(in) or intent(inplace) */ { PyArrayObject *retarr = (PyArrayObject *) \ PyArray_New(&PyArray_Type, PyArray_NDIM(arr), PyArray_DIMS(arr), type_num, NULL,NULL,0, !(intent&F2PY_INTENT_C), NULL); if (retarr==NULL) return NULL; F2PY_REPORT_ON_ARRAY_COPY_FROMARR; if (PyArray_CopyInto(retarr, arr)) { Py_DECREF(retarr); return NULL; } if (intent & F2PY_INTENT_INPLACE) { if (swap_arrays(arr,retarr)) return NULL; /* XXX: set exception */ Py_XDECREF(retarr); if (intent & F2PY_INTENT_OUT) Py_INCREF(arr); } else { arr = retarr; } } return arr; } if ((intent & F2PY_INTENT_INOUT) || (intent & F2PY_INTENT_INPLACE) || (intent & F2PY_INTENT_CACHE)) { PyErr_SetString(PyExc_TypeError, "failed to initialize intent(inout|inplace|cache) " "array, input not an array"); return NULL; } { F2PY_REPORT_ON_ARRAY_COPY_FROMANY; arr = (PyArrayObject *) \ PyArray_FromAny(obj,PyArray_DescrFromType(type_num), 0,0, ((intent & F2PY_INTENT_C)?NPY_ARRAY_CARRAY:NPY_ARRAY_FARRAY) \ | NPY_ARRAY_FORCECAST, NULL); if (arr==NULL) return NULL; if (check_and_fix_dimensions(arr,rank,dims)) return NULL; /*XXX: set exception */ return arr; } }
extern int copy_ND_array(const PyArrayObject *arr, PyArrayObject *out) { F2PY_REPORT_ON_ARRAY_COPY_FROMARR; return PyArray_CopyInto(out, (PyArrayObject *)arr); }
/** * Constructor */ dtnode :: dtnode(PyArrayObject* arg_edges, PyObject* arg_children, PyObject* arg_maxind, int arg_leafstart) { // Do some argument checking if(!PyList_Check(arg_children) || !PyList_Check(arg_maxind)) { // ERROR PyErr_SetString(PyExc_RuntimeError, "Non-List children/maxind element in dirtree node"); throw 1; } if(arg_leafstart < 0) { // ERROR PyErr_SetString(PyExc_RuntimeError, "Negative leafstart value in dirtree node"); throw 1; } if(PyList_Size(arg_children) != PyList_Size(arg_maxind)) { // ERROR PyErr_SetString(PyExc_RuntimeError, "Different sizes for children/maxind in dirtree node"); throw 1; } double edgemin = PyFloat_AsDouble(PyArray_Min(arg_edges,NPY_MAXDIMS,NULL)); if(edgemin <= 0) { // ERROR PyErr_SetString(PyExc_RuntimeError, "Negative/zero edge value in dirtree node"); throw 1; } // Populate data members int nci = PyList_Size(arg_children); this->leafstart = arg_leafstart; // Get edge values and sum // (note that this *must* be a copy!) npy_intp* edims = new npy_intp[1]; edims[0] = PyArray_DIM(arg_edges,0); this->edges = (PyArrayObject*) PyArray_ZEROS(1,edims,PyArray_DOUBLE,0); PyArray_CopyInto(this->edges,arg_edges); this->edgesum = PyFloat_AsDouble(PyArray_Sum(this->edges,NPY_MAXDIMS, PyArray_DOUBLE,NULL)); // Also make a copy of *original* edge values // (not augmented by counts) this->orig_edges = (PyArrayObject*) PyArray_ZEROS(1,edims,PyArray_DOUBLE,0); PyArray_CopyInto(this->orig_edges,arg_edges); this->orig_edgesum = PyFloat_AsDouble(PyArray_Sum(this->orig_edges,NPY_MAXDIMS, PyArray_DOUBLE,NULL)); delete[] edims; // Recursively build children // int c; for(c = 0; c < nci; c++) { // Get max leaf index under this child, check it maxind.push_back(PyInt_AsLong(PyList_GetItem(arg_maxind,c))); if(maxind[c] < 0) { // ERROR PyErr_SetString(PyExc_RuntimeError, "Negative maxind value in dirtree node"); throw 1; } // exploiting boolean short-circuit here... if(c > 0 && maxind[c] <= maxind[c-1]) { // ERROR PyErr_SetString(PyExc_RuntimeError, "Non-increasing maxind value in dirtree node"); throw 1; } // Recursively build child // try{ // Check that node tuple size is correct PyObject* pynode = PyList_GetItem(arg_children,c); // Is child a dtnode, or a multinode? // if(4 == PyTuple_Size(pynode)) { // dtnode // Unpack tuple contents PyArrayObject* cedges = (PyArrayObject*) PyTuple_GetItem(pynode,0); PyObject* cchildren = PyTuple_GetItem(pynode,1); PyObject* cmaxind = PyTuple_GetItem(pynode,2); int cleafstart = PyInt_AsLong(PyTuple_GetItem(pynode,3)); // Build child dtnode node* newchild = new dtnode(cedges,cchildren, cmaxind,cleafstart); ichildren.push_back(newchild); } else if(5 == PyTuple_Size(pynode)) { // multinode // Unpack tuple contents PyObject* cchildren = PyTuple_GetItem(pynode,1); PyObject* cmaxind = PyTuple_GetItem(pynode,2); int cleafstart = PyInt_AsLong(PyTuple_GetItem(pynode,3)); PyObject* variants = PyTuple_GetItem(pynode,4); // Build child multinode node* newchild = new multinode(cchildren,cmaxind, cleafstart,variants); ichildren.push_back(newchild); } else{ // ERROR PyErr_SetString(PyExc_RuntimeError, "Node has wrong number of elements"); throw 1; } } catch (int err) { throw 1; } } }