diff options
author | David Cournapeau <cournape@gmail.com> | 2009-04-30 08:39:44 +0000 |
---|---|---|
committer | David Cournapeau <cournape@gmail.com> | 2009-04-30 08:39:44 +0000 |
commit | 32a5fe9739bf34b1d6ff071dbb12968666831718 (patch) | |
tree | ac2244ef8bcb10c4f2aff42764de456803a3ff8f /numpy/core/src | |
parent | 96a1f08f4f92f8a3dc39979f440c41e0f4fa018a (diff) | |
download | numpy-32a5fe9739bf34b1d6ff071dbb12968666831718.tar.gz |
Put shape manipulation code into separate file.
Diffstat (limited to 'numpy/core/src')
-rw-r--r-- | numpy/core/src/arrayconvert.c | 40 | ||||
-rw-r--r-- | numpy/core/src/arrayshape.c | 652 | ||||
-rw-r--r-- | numpy/core/src/arrayshape.h | 4 | ||||
-rw-r--r-- | numpy/core/src/multiarraymodule.c | 669 |
4 files changed, 696 insertions, 669 deletions
diff --git a/numpy/core/src/arrayconvert.c b/numpy/core/src/arrayconvert.c index 17cb9aad0..9321a05cb 100644 --- a/numpy/core/src/arrayconvert.c +++ b/numpy/core/src/arrayconvert.c @@ -344,3 +344,43 @@ PyArray_NewCopy(PyArrayObject *m1, NPY_ORDER fortran) return (PyObject *)ret; } +/*NUMPY_API + * View + * steals a reference to type -- accepts NULL + */ +NPY_NO_EXPORT PyObject * +PyArray_View(PyArrayObject *self, PyArray_Descr *type, PyTypeObject *pytype) +{ + PyObject *new = NULL; + PyTypeObject *subtype; + + if (pytype) { + subtype = pytype; + } + else { + subtype = self->ob_type; + } + Py_INCREF(self->descr); + new = PyArray_NewFromDescr(subtype, + self->descr, + self->nd, self->dimensions, + self->strides, + self->data, + self->flags, (PyObject *)self); + if (new == NULL) { + return NULL; + } + Py_INCREF(self); + PyArray_BASE(new) = (PyObject *)self; + + if (type != NULL) { + if (PyObject_SetAttrString(new, "dtype", + (PyObject *)type) < 0) { + Py_DECREF(new); + Py_DECREF(type); + return NULL; + } + Py_DECREF(type); + } + return new; +} diff --git a/numpy/core/src/arrayshape.c b/numpy/core/src/arrayshape.c new file mode 100644 index 000000000..0800371cd --- /dev/null +++ b/numpy/core/src/arrayshape.c @@ -0,0 +1,652 @@ +#define PY_SSIZE_T_CLEAN +#include <Python.h> +#include "structmember.h" + +#define _MULTIARRAYMODULE +#define NPY_NO_PREFIX +#include "numpy/arrayobject.h" +#include "numpy/arrayscalars.h" +#include "numpy/npy_math.h" + +#include "arrayobject.h" + +#include "arrayshape.h" + +#define PyAO PyArrayObject + +static int +_check_ones(PyArrayObject *self, int newnd, intp* newdims, intp *strides); + +static int +_fix_unknown_dimension(PyArray_Dims *newshape, intp s_original); + +static int +_attempt_nocopy_reshape(PyArrayObject *self, int newnd, intp* newdims, + intp *newstrides, int fortran); + +/* + * 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 + */ + +/*NUMPY_API + * New shape for an array + */ +NPY_NO_EXPORT PyObject * +PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims, + NPY_ORDER fortran) +{ + intp i; + intp *dimensions = newdims->ptr; + PyArrayObject *ret; + int n = newdims->len; + Bool same, incref = TRUE; + intp *strides = NULL; + intp newstrides[MAX_DIMS]; + int flags; + + if (fortran == PyArray_ANYORDER) { + fortran = PyArray_ISFORTRAN(self); + } + /* Quick check to make sure anything actually needs to be done */ + if (n == self->nd) { + same = TRUE; + i = 0; + while (same && i < n) { + if (PyArray_DIM(self,i) != dimensions[i]) { + same=FALSE; + } + i++; + } + if (same) { + return PyArray_View(self, NULL, NULL); + } + } + + /* + * 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 + * dimensions. So, we can handle non single-segment cases. + */ + i = _check_ones(self, n, dimensions, newstrides); + if (i == 0) { + strides = newstrides; + } + 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 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 + * data in the order it is in. + */ + if (!(PyArray_ISONESEGMENT(self)) || + (((PyArray_CHKFLAGS(self, NPY_CONTIGUOUS) && + fortran == NPY_FORTRANORDER) || + (PyArray_CHKFLAGS(self, NPY_FORTRAN) && + fortran == NPY_CORDER)) && (self->nd > 1))) { + int success = 0; + success = _attempt_nocopy_reshape(self,n,dimensions, + newstrides,fortran); + if (success) { + /* no need to copy the array after all */ + strides = newstrides; + flags = self->flags; + } + else { + PyObject *new; + new = PyArray_NewCopy(self, fortran); + if (new == NULL) { + return NULL; + } + incref = FALSE; + self = (PyArrayObject *)new; + flags = self->flags; + } + } + + /* We always have to interpret the contiguous buffer correctly */ + + /* Make sure the flags argument is set. */ + if (n > 1) { + if (fortran == NPY_FORTRANORDER) { + flags &= ~NPY_CONTIGUOUS; + flags |= NPY_FORTRAN; + } + else { + flags &= ~NPY_FORTRAN; + flags |= NPY_CONTIGUOUS; + } + } + } + else if (n > 0) { + /* + * replace any 0-valued strides with + * appropriate value to preserve contiguousness + */ + if (fortran == PyArray_FORTRANORDER) { + if (strides[0] == 0) { + strides[0] = self->descr->elsize; + } + for (i = 1; i < n; i++) { + if (strides[i] == 0) { + strides[i] = strides[i-1] * dimensions[i-1]; + } + } + } + else { + if (strides[n-1] == 0) { + strides[n-1] = self->descr->elsize; + } + for (i = n - 2; i > -1; i--) { + if (strides[i] == 0) { + strides[i] = strides[i+1] * dimensions[i+1]; + } + } + } + } + + Py_INCREF(self->descr); + ret = (PyAO *)PyArray_NewFromDescr(self->ob_type, + self->descr, + n, dimensions, + strides, + self->data, + flags, (PyObject *)self); + + if (ret == NULL) { + goto fail; + } + if (incref) { + Py_INCREF(self); + } + ret->base = (PyObject *)self; + PyArray_UpdateFlags(ret, CONTIGUOUS | FORTRAN); + return (PyObject *)ret; + + fail: + if (!incref) { + Py_DECREF(self); + } + return NULL; +} + + + +/* For back-ward compatability -- Not recommended */ + +/*NUMPY_API + * Reshape + */ +NPY_NO_EXPORT PyObject * +PyArray_Reshape(PyArrayObject *self, PyObject *shape) +{ + PyObject *ret; + PyArray_Dims newdims; + + if (!PyArray_IntpConverter(shape, &newdims)) { + return NULL; + } + ret = PyArray_Newshape(self, &newdims, PyArray_CORDER); + PyDimMem_FREE(newdims.ptr); + return ret; +} + +/* inserts 0 for strides where dimension will be 1 */ +static int +_check_ones(PyArrayObject *self, int newnd, intp* newdims, intp *strides) +{ + int nd; + intp *dims; + Bool done=FALSE; + int j, k; + + nd = self->nd; + dims = self->dimensions; + + for (k = 0, j = 0; !done && (j < nd || k < newnd);) { + if ((j<nd) && (k<newnd) && (newdims[k] == dims[j])) { + strides[k] = self->strides[j]; + j++; + k++; + } + else if ((k < newnd) && (newdims[k] == 1)) { + strides[k] = 0; + k++; + } + else if ((j<nd) && (dims[j] == 1)) { + j++; + } + else { + done = TRUE; + } + } + if (done) { + return -1; + } + return 0; +} + +/* + * attempt to reshape an array without copying data + * + * 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 + * with appropriate strides + * + * The "fortran" argument describes how the array should be viewed + * 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 + * them are arbitrary. In the current implementation, they are the + * stride of the next-fastest index. + */ +static int +_attempt_nocopy_reshape(PyArrayObject *self, int newnd, intp* newdims, + intp *newstrides, int fortran) +{ + int oldnd; + intp olddims[MAX_DIMS]; + intp oldstrides[MAX_DIMS]; + int oi, oj, ok, ni, nj, nk; + int np, op; + + oldnd = 0; + for (oi = 0; oi < self->nd; oi++) { + if (self->dimensions[oi]!= 1) { + olddims[oldnd] = self->dimensions[oi]; + oldstrides[oldnd] = self->strides[oi]; + oldnd++; + } + } + + /* + fprintf(stderr, "_attempt_nocopy_reshape( ("); + for (oi=0; oi<oldnd; oi++) + fprintf(stderr, "(%d,%d), ", olddims[oi], oldstrides[oi]); + fprintf(stderr, ") -> ("); + for (ni=0; ni<newnd; ni++) + fprintf(stderr, "(%d,*), ", newdims[ni]); + fprintf(stderr, "), fortran=%d)\n", fortran); + */ + + + np = 1; + for (ni = 0; ni < newnd; ni++) { + np *= newdims[ni]; + } + op = 1; + for (oi = 0; oi < oldnd; oi++) { + op *= olddims[oi]; + } + if (np != op) { + /* different total sizes; no hope */ + return 0; + } + /* the current code does not handle 0-sized arrays, so give up */ + if (np == 0) { + return 0; + } + + oi = 0; + oj = 1; + ni = 0; + nj = 1; + while(ni < newnd && oi < oldnd) { + np = newdims[ni]; + op = olddims[oi]; + + while (np != op) { + if (np < op) { + np *= newdims[nj++]; + } else { + op *= olddims[oj++]; + } + } + + for (ok = oi; ok < oj - 1; ok++) { + if (fortran) { + if (oldstrides[ok+1] != olddims[ok]*oldstrides[ok]) { + /* not contiguous enough */ + return 0; + } + } + else { + /* C order */ + if (oldstrides[ok] != olddims[ok+1]*oldstrides[ok+1]) { + /* not contiguous enough */ + return 0; + } + } + } + + if (fortran) { + newstrides[ni] = oldstrides[oi]; + 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--) { + newstrides[nk - 1] = newstrides[nk]*newdims[nk]; + } + } + ni = nj++; + oi = oj++; + } + + /* + fprintf(stderr, "success: _attempt_nocopy_reshape ("); + for (oi=0; oi<oldnd; oi++) + fprintf(stderr, "(%d,%d), ", olddims[oi], oldstrides[oi]); + fprintf(stderr, ") -> ("); + for (ni=0; ni<newnd; ni++) + fprintf(stderr, "(%d,%d), ", newdims[ni], newstrides[ni]); + fprintf(stderr, ")\n"); + */ + + return 1; +} + +static int +_fix_unknown_dimension(PyArray_Dims *newshape, intp s_original) +{ + intp *dimensions; + intp i_unknown, s_known; + int i, n; + static char msg[] = "total size of new array must be unchanged"; + + dimensions = newshape->ptr; + n = newshape->len; + s_known = 1; + i_unknown = -1; + + for (i = 0; i < n; i++) { + if (dimensions[i] < 0) { + if (i_unknown == -1) { + i_unknown = i; + } + else { + PyErr_SetString(PyExc_ValueError, + "can only specify one" \ + " unknown dimension"); + return -1; + } + } + else { + s_known *= dimensions[i]; + } + } + + if (i_unknown >= 0) { + if ((s_known == 0) || (s_original % s_known != 0)) { + PyErr_SetString(PyExc_ValueError, msg); + return -1; + } + dimensions[i_unknown] = s_original/s_known; + } + else { + if (s_original != s_known) { + PyErr_SetString(PyExc_ValueError, msg); + return -1; + } + } + return 0; +} + +/*NUMPY_API + * + * return a new view of the array object with all of its unit-length + * dimensions squeezed out if needed, otherwise + * return the same array. + */ +NPY_NO_EXPORT PyObject * +PyArray_Squeeze(PyArrayObject *self) +{ + int nd = self->nd; + int newnd = nd; + intp dimensions[MAX_DIMS]; + intp strides[MAX_DIMS]; + int i, j; + PyObject *ret; + + if (nd == 0) { + Py_INCREF(self); + return (PyObject *)self; + } + for (j = 0, i = 0; i < nd; i++) { + if (self->dimensions[i] == 1) { + newnd -= 1; + } + else { + dimensions[j] = self->dimensions[i]; + strides[j++] = self->strides[i]; + } + } + + Py_INCREF(self->descr); + ret = PyArray_NewFromDescr(self->ob_type, + self->descr, + newnd, dimensions, + strides, self->data, + self->flags, + (PyObject *)self); + if (ret == NULL) { + return NULL; + } + PyArray_FLAGS(ret) &= ~OWNDATA; + PyArray_BASE(ret) = (PyObject *)self; + Py_INCREF(self); + return (PyObject *)ret; +} + +/*NUMPY_API + * SwapAxes + */ +NPY_NO_EXPORT PyObject * +PyArray_SwapAxes(PyArrayObject *ap, int a1, int a2) +{ + PyArray_Dims new_axes; + intp dims[MAX_DIMS]; + int n, i, val; + PyObject *ret; + + if (a1 == a2) { + Py_INCREF(ap); + return (PyObject *)ap; + } + + n = ap->nd; + if (n <= 1) { + Py_INCREF(ap); + return (PyObject *)ap; + } + + if (a1 < 0) { + a1 += n; + } + if (a2 < 0) { + a2 += n; + } + if ((a1 < 0) || (a1 >= n)) { + PyErr_SetString(PyExc_ValueError, + "bad axis1 argument to swapaxes"); + return NULL; + } + if ((a2 < 0) || (a2 >= n)) { + PyErr_SetString(PyExc_ValueError, + "bad axis2 argument to swapaxes"); + return NULL; + } + new_axes.ptr = dims; + new_axes.len = n; + + for (i = 0; i < n; i++) { + if (i == a1) { + val = a2; + } + else if (i == a2) { + val = a1; + } + else { + val = i; + } + new_axes.ptr[i] = val; + } + ret = PyArray_Transpose(ap, &new_axes); + return ret; +} + +/*NUMPY_API + * Return Transpose. + */ +NPY_NO_EXPORT PyObject * +PyArray_Transpose(PyArrayObject *ap, PyArray_Dims *permute) +{ + intp *axes, axis; + intp i, n; + intp permutation[MAX_DIMS], reverse_permutation[MAX_DIMS]; + PyArrayObject *ret = NULL; + + if (permute == NULL) { + n = ap->nd; + for (i = 0; i < n; i++) { + permutation[i] = n-1-i; + } + } + else { + n = permute->len; + axes = permute->ptr; + if (n != ap->nd) { + PyErr_SetString(PyExc_ValueError, + "axes don't match array"); + return NULL; + } + for (i = 0; i < n; i++) { + reverse_permutation[i] = -1; + } + for (i = 0; i < n; i++) { + axis = axes[i]; + if (axis < 0) { + axis = ap->nd + axis; + } + if (axis < 0 || axis >= ap->nd) { + PyErr_SetString(PyExc_ValueError, + "invalid axis for this array"); + return NULL; + } + if (reverse_permutation[axis] != -1) { + PyErr_SetString(PyExc_ValueError, + "repeated axis in transpose"); + return NULL; + } + reverse_permutation[axis] = i; + permutation[i] = axis; + } + for (i = 0; i < n; i++) { + } + } + + /* + * this allocates memory for dimensions and strides (but fills them + * incorrectly), sets up descr, and points data at ap->data. + */ + Py_INCREF(ap->descr); + ret = (PyArrayObject *)\ + PyArray_NewFromDescr(ap->ob_type, + ap->descr, + n, ap->dimensions, + NULL, ap->data, ap->flags, + (PyObject *)ap); + if (ret == NULL) { + return NULL; + } + /* point at true owner of memory: */ + ret->base = (PyObject *)ap; + Py_INCREF(ap); + + /* fix the dimensions and strides of the return-array */ + for (i = 0; i < n; i++) { + ret->dimensions[i] = ap->dimensions[permutation[i]]; + ret->strides[i] = ap->strides[permutation[i]]; + } + PyArray_UpdateFlags(ret, CONTIGUOUS | FORTRAN); + return (PyObject *)ret; +} + +/*NUMPY_API + * Ravel + * Returns a contiguous array + */ +NPY_NO_EXPORT PyObject * +PyArray_Ravel(PyArrayObject *a, NPY_ORDER fortran) +{ + PyArray_Dims newdim = {NULL,1}; + intp val[1] = {-1}; + + if (fortran == PyArray_ANYORDER) { + fortran = PyArray_ISFORTRAN(a); + } + newdim.ptr = val; + if (!fortran && PyArray_ISCONTIGUOUS(a)) { + return PyArray_Newshape(a, &newdim, PyArray_CORDER); + } + else if (fortran && PyArray_ISFORTRAN(a)) { + return PyArray_Newshape(a, &newdim, PyArray_FORTRANORDER); + } + else { + return PyArray_Flatten(a, fortran); + } +} + +/*NUMPY_API + * Flatten + */ +NPY_NO_EXPORT PyObject * +PyArray_Flatten(PyArrayObject *a, NPY_ORDER order) +{ + PyObject *ret; + intp size; + + if (order == PyArray_ANYORDER) { + order = PyArray_ISFORTRAN(a); + } + size = PyArray_SIZE(a); + Py_INCREF(a->descr); + ret = PyArray_NewFromDescr(a->ob_type, + a->descr, + 1, &size, + NULL, + NULL, + 0, (PyObject *)a); + + if (ret == NULL) { + return NULL; + } + if (_flat_copyinto(ret, (PyObject *)a, order) < 0) { + Py_DECREF(ret); + return NULL; + } + return ret; +} + + diff --git a/numpy/core/src/arrayshape.h b/numpy/core/src/arrayshape.h new file mode 100644 index 000000000..1a5991a50 --- /dev/null +++ b/numpy/core/src/arrayshape.h @@ -0,0 +1,4 @@ +#ifndef _NPY_ARRAY_SHAPE_H_ +#define _NPY_ARRAY_SHAPE_H_ + +#endif diff --git a/numpy/core/src/multiarraymodule.c b/numpy/core/src/multiarraymodule.c index 85beaad49..2b7a2a86b 100644 --- a/numpy/core/src/multiarraymodule.c +++ b/numpy/core/src/multiarraymodule.c @@ -218,73 +218,6 @@ PyArray_CompareLists(intp *l1, intp *l2, int n) return 1; } -/*NUMPY_API - * View - * steals a reference to type -- accepts NULL - */ -NPY_NO_EXPORT PyObject * -PyArray_View(PyArrayObject *self, PyArray_Descr *type, PyTypeObject *pytype) -{ - PyObject *new = NULL; - PyTypeObject *subtype; - - if (pytype) { - subtype = pytype; - } - else { - subtype = self->ob_type; - } - Py_INCREF(self->descr); - new = PyArray_NewFromDescr(subtype, - self->descr, - self->nd, self->dimensions, - self->strides, - self->data, - self->flags, (PyObject *)self); - if (new == NULL) { - return NULL; - } - Py_INCREF(self); - PyArray_BASE(new) = (PyObject *)self; - - if (type != NULL) { - if (PyObject_SetAttrString(new, "dtype", - (PyObject *)type) < 0) { - Py_DECREF(new); - Py_DECREF(type); - return NULL; - } - Py_DECREF(type); - } - return new; -} - - -/*NUMPY_API - * Ravel - * Returns a contiguous array - */ -NPY_NO_EXPORT PyObject * -PyArray_Ravel(PyArrayObject *a, NPY_ORDER fortran) -{ - PyArray_Dims newdim = {NULL,1}; - intp val[1] = {-1}; - - if (fortran == PyArray_ANYORDER) { - fortran = PyArray_ISFORTRAN(a); - } - newdim.ptr = val; - if (!fortran && PyArray_ISCONTIGUOUS(a)) { - return PyArray_Newshape(a, &newdim, PyArray_CORDER); - } - else if (fortran && PyArray_ISFORTRAN(a)) { - return PyArray_Newshape(a, &newdim, PyArray_FORTRANORDER); - } - else { - return PyArray_Flatten(a, fortran); - } -} - static double power_of_ten(int n) { @@ -462,474 +395,6 @@ PyArray_Round(PyArrayObject *a, int decimals, PyArrayObject *out) /*NUMPY_API - * Flatten - */ -NPY_NO_EXPORT PyObject * -PyArray_Flatten(PyArrayObject *a, NPY_ORDER order) -{ - PyObject *ret; - intp size; - - if (order == PyArray_ANYORDER) { - order = PyArray_ISFORTRAN(a); - } - size = PyArray_SIZE(a); - Py_INCREF(a->descr); - ret = PyArray_NewFromDescr(a->ob_type, - a->descr, - 1, &size, - NULL, - NULL, - 0, (PyObject *)a); - - if (ret == NULL) { - return NULL; - } - if (_flat_copyinto(ret, (PyObject *)a, order) < 0) { - Py_DECREF(ret); - return NULL; - } - return ret; -} - - -/* For back-ward compatability -- Not recommended */ - -/*NUMPY_API - * Reshape - */ -NPY_NO_EXPORT PyObject * -PyArray_Reshape(PyArrayObject *self, PyObject *shape) -{ - PyObject *ret; - PyArray_Dims newdims; - - if (!PyArray_IntpConverter(shape, &newdims)) { - return NULL; - } - ret = PyArray_Newshape(self, &newdims, PyArray_CORDER); - PyDimMem_FREE(newdims.ptr); - return ret; -} - -/* inserts 0 for strides where dimension will be 1 */ -static int -_check_ones(PyArrayObject *self, int newnd, intp* newdims, intp *strides) -{ - int nd; - intp *dims; - Bool done=FALSE; - int j, k; - - nd = self->nd; - dims = self->dimensions; - - for (k = 0, j = 0; !done && (j < nd || k < newnd);) { - if ((j<nd) && (k<newnd) && (newdims[k] == dims[j])) { - strides[k] = self->strides[j]; - j++; - k++; - } - else if ((k < newnd) && (newdims[k] == 1)) { - strides[k] = 0; - k++; - } - else if ((j<nd) && (dims[j] == 1)) { - j++; - } - else { - done = TRUE; - } - } - if (done) { - return -1; - } - return 0; -} - -/* - * attempt to reshape an array without copying data - * - * 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 - * with appropriate strides - * - * The "fortran" argument describes how the array should be viewed - * 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 - * them are arbitrary. In the current implementation, they are the - * stride of the next-fastest index. - */ -static int -_attempt_nocopy_reshape(PyArrayObject *self, int newnd, intp* newdims, - intp *newstrides, int fortran) -{ - int oldnd; - intp olddims[MAX_DIMS]; - intp oldstrides[MAX_DIMS]; - int oi, oj, ok, ni, nj, nk; - int np, op; - - oldnd = 0; - for (oi = 0; oi < self->nd; oi++) { - if (self->dimensions[oi]!= 1) { - olddims[oldnd] = self->dimensions[oi]; - oldstrides[oldnd] = self->strides[oi]; - oldnd++; - } - } - - /* - fprintf(stderr, "_attempt_nocopy_reshape( ("); - for (oi=0; oi<oldnd; oi++) - fprintf(stderr, "(%d,%d), ", olddims[oi], oldstrides[oi]); - fprintf(stderr, ") -> ("); - for (ni=0; ni<newnd; ni++) - fprintf(stderr, "(%d,*), ", newdims[ni]); - fprintf(stderr, "), fortran=%d)\n", fortran); - */ - - - np = 1; - for (ni = 0; ni < newnd; ni++) { - np *= newdims[ni]; - } - op = 1; - for (oi = 0; oi < oldnd; oi++) { - op *= olddims[oi]; - } - if (np != op) { - /* different total sizes; no hope */ - return 0; - } - /* the current code does not handle 0-sized arrays, so give up */ - if (np == 0) { - return 0; - } - - oi = 0; - oj = 1; - ni = 0; - nj = 1; - while(ni < newnd && oi < oldnd) { - np = newdims[ni]; - op = olddims[oi]; - - while (np != op) { - if (np < op) { - np *= newdims[nj++]; - } else { - op *= olddims[oj++]; - } - } - - for (ok = oi; ok < oj - 1; ok++) { - if (fortran) { - if (oldstrides[ok+1] != olddims[ok]*oldstrides[ok]) { - /* not contiguous enough */ - return 0; - } - } - else { - /* C order */ - if (oldstrides[ok] != olddims[ok+1]*oldstrides[ok+1]) { - /* not contiguous enough */ - return 0; - } - } - } - - if (fortran) { - newstrides[ni] = oldstrides[oi]; - 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--) { - newstrides[nk - 1] = newstrides[nk]*newdims[nk]; - } - } - ni = nj++; - oi = oj++; - } - - /* - fprintf(stderr, "success: _attempt_nocopy_reshape ("); - for (oi=0; oi<oldnd; oi++) - fprintf(stderr, "(%d,%d), ", olddims[oi], oldstrides[oi]); - fprintf(stderr, ") -> ("); - for (ni=0; ni<newnd; ni++) - fprintf(stderr, "(%d,%d), ", newdims[ni], newstrides[ni]); - fprintf(stderr, ")\n"); - */ - - return 1; -} - -static int -_fix_unknown_dimension(PyArray_Dims *newshape, intp s_original) -{ - intp *dimensions; - intp i_unknown, s_known; - int i, n; - static char msg[] = "total size of new array must be unchanged"; - - dimensions = newshape->ptr; - n = newshape->len; - s_known = 1; - i_unknown = -1; - - for (i = 0; i < n; i++) { - if (dimensions[i] < 0) { - if (i_unknown == -1) { - i_unknown = i; - } - else { - PyErr_SetString(PyExc_ValueError, - "can only specify one" \ - " unknown dimension"); - return -1; - } - } - else { - s_known *= dimensions[i]; - } - } - - if (i_unknown >= 0) { - if ((s_known == 0) || (s_original % s_known != 0)) { - PyErr_SetString(PyExc_ValueError, msg); - return -1; - } - dimensions[i_unknown] = s_original/s_known; - } - else { - if (s_original != s_known) { - PyErr_SetString(PyExc_ValueError, msg); - return -1; - } - } - return 0; -} - -/* - * 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 - */ - -/*NUMPY_API - * New shape for an array - */ -NPY_NO_EXPORT PyObject * -PyArray_Newshape(PyArrayObject *self, PyArray_Dims *newdims, - NPY_ORDER fortran) -{ - intp i; - intp *dimensions = newdims->ptr; - PyArrayObject *ret; - int n = newdims->len; - Bool same, incref = TRUE; - intp *strides = NULL; - intp newstrides[MAX_DIMS]; - int flags; - - if (fortran == PyArray_ANYORDER) { - fortran = PyArray_ISFORTRAN(self); - } - /* Quick check to make sure anything actually needs to be done */ - if (n == self->nd) { - same = TRUE; - i = 0; - while (same && i < n) { - if (PyArray_DIM(self,i) != dimensions[i]) { - same=FALSE; - } - i++; - } - if (same) { - return PyArray_View(self, NULL, NULL); - } - } - - /* - * 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 - * dimensions. So, we can handle non single-segment cases. - */ - i = _check_ones(self, n, dimensions, newstrides); - if (i == 0) { - strides = newstrides; - } - 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 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 - * data in the order it is in. - */ - if (!(PyArray_ISONESEGMENT(self)) || - (((PyArray_CHKFLAGS(self, NPY_CONTIGUOUS) && - fortran == NPY_FORTRANORDER) || - (PyArray_CHKFLAGS(self, NPY_FORTRAN) && - fortran == NPY_CORDER)) && (self->nd > 1))) { - int success = 0; - success = _attempt_nocopy_reshape(self,n,dimensions, - newstrides,fortran); - if (success) { - /* no need to copy the array after all */ - strides = newstrides; - flags = self->flags; - } - else { - PyObject *new; - new = PyArray_NewCopy(self, fortran); - if (new == NULL) { - return NULL; - } - incref = FALSE; - self = (PyArrayObject *)new; - flags = self->flags; - } - } - - /* We always have to interpret the contiguous buffer correctly */ - - /* Make sure the flags argument is set. */ - if (n > 1) { - if (fortran == NPY_FORTRANORDER) { - flags &= ~NPY_CONTIGUOUS; - flags |= NPY_FORTRAN; - } - else { - flags &= ~NPY_FORTRAN; - flags |= NPY_CONTIGUOUS; - } - } - } - else if (n > 0) { - /* - * replace any 0-valued strides with - * appropriate value to preserve contiguousness - */ - if (fortran == PyArray_FORTRANORDER) { - if (strides[0] == 0) { - strides[0] = self->descr->elsize; - } - for (i = 1; i < n; i++) { - if (strides[i] == 0) { - strides[i] = strides[i-1] * dimensions[i-1]; - } - } - } - else { - if (strides[n-1] == 0) { - strides[n-1] = self->descr->elsize; - } - for (i = n - 2; i > -1; i--) { - if (strides[i] == 0) { - strides[i] = strides[i+1] * dimensions[i+1]; - } - } - } - } - - Py_INCREF(self->descr); - ret = (PyAO *)PyArray_NewFromDescr(self->ob_type, - self->descr, - n, dimensions, - strides, - self->data, - flags, (PyObject *)self); - - if (ret == NULL) { - goto fail; - } - if (incref) { - Py_INCREF(self); - } - ret->base = (PyObject *)self; - PyArray_UpdateFlags(ret, CONTIGUOUS | FORTRAN); - return (PyObject *)ret; - - fail: - if (!incref) { - Py_DECREF(self); - } - return NULL; -} - - - -/*NUMPY_API - * - * return a new view of the array object with all of its unit-length - * dimensions squeezed out if needed, otherwise - * return the same array. - */ -NPY_NO_EXPORT PyObject * -PyArray_Squeeze(PyArrayObject *self) -{ - int nd = self->nd; - int newnd = nd; - intp dimensions[MAX_DIMS]; - intp strides[MAX_DIMS]; - int i, j; - PyObject *ret; - - if (nd == 0) { - Py_INCREF(self); - return (PyObject *)self; - } - for (j = 0, i = 0; i < nd; i++) { - if (self->dimensions[i] == 1) { - newnd -= 1; - } - else { - dimensions[j] = self->dimensions[i]; - strides[j++] = self->strides[i]; - } - } - - Py_INCREF(self->descr); - ret = PyArray_NewFromDescr(self->ob_type, - self->descr, - newnd, dimensions, - strides, self->data, - self->flags, - (PyObject *)self); - if (ret == NULL) { - return NULL; - } - PyArray_FLAGS(ret) &= ~OWNDATA; - PyArray_BASE(ret) = (PyObject *)self; - Py_INCREF(self); - return (PyObject *)ret; -} - - -/*NUMPY_API * Mean */ NPY_NO_EXPORT PyObject * @@ -2132,140 +1597,6 @@ PyArray_Concatenate(PyObject *op, int axis) } /*NUMPY_API - * SwapAxes - */ -NPY_NO_EXPORT PyObject * -PyArray_SwapAxes(PyArrayObject *ap, int a1, int a2) -{ - PyArray_Dims new_axes; - intp dims[MAX_DIMS]; - int n, i, val; - PyObject *ret; - - if (a1 == a2) { - Py_INCREF(ap); - return (PyObject *)ap; - } - - n = ap->nd; - if (n <= 1) { - Py_INCREF(ap); - return (PyObject *)ap; - } - - if (a1 < 0) { - a1 += n; - } - if (a2 < 0) { - a2 += n; - } - if ((a1 < 0) || (a1 >= n)) { - PyErr_SetString(PyExc_ValueError, - "bad axis1 argument to swapaxes"); - return NULL; - } - if ((a2 < 0) || (a2 >= n)) { - PyErr_SetString(PyExc_ValueError, - "bad axis2 argument to swapaxes"); - return NULL; - } - new_axes.ptr = dims; - new_axes.len = n; - - for (i = 0; i < n; i++) { - if (i == a1) { - val = a2; - } - else if (i == a2) { - val = a1; - } - else { - val = i; - } - new_axes.ptr[i] = val; - } - ret = PyArray_Transpose(ap, &new_axes); - return ret; -} - -/*NUMPY_API - * Return Transpose. - */ -NPY_NO_EXPORT PyObject * -PyArray_Transpose(PyArrayObject *ap, PyArray_Dims *permute) -{ - intp *axes, axis; - intp i, n; - intp permutation[MAX_DIMS], reverse_permutation[MAX_DIMS]; - PyArrayObject *ret = NULL; - - if (permute == NULL) { - n = ap->nd; - for (i = 0; i < n; i++) { - permutation[i] = n-1-i; - } - } - else { - n = permute->len; - axes = permute->ptr; - if (n != ap->nd) { - PyErr_SetString(PyExc_ValueError, - "axes don't match array"); - return NULL; - } - for (i = 0; i < n; i++) { - reverse_permutation[i] = -1; - } - for (i = 0; i < n; i++) { - axis = axes[i]; - if (axis < 0) { - axis = ap->nd + axis; - } - if (axis < 0 || axis >= ap->nd) { - PyErr_SetString(PyExc_ValueError, - "invalid axis for this array"); - return NULL; - } - if (reverse_permutation[axis] != -1) { - PyErr_SetString(PyExc_ValueError, - "repeated axis in transpose"); - return NULL; - } - reverse_permutation[axis] = i; - permutation[i] = axis; - } - for (i = 0; i < n; i++) { - } - } - - /* - * this allocates memory for dimensions and strides (but fills them - * incorrectly), sets up descr, and points data at ap->data. - */ - Py_INCREF(ap->descr); - ret = (PyArrayObject *)\ - PyArray_NewFromDescr(ap->ob_type, - ap->descr, - n, ap->dimensions, - NULL, ap->data, ap->flags, - (PyObject *)ap); - if (ret == NULL) { - return NULL; - } - /* point at true owner of memory: */ - ret->base = (PyObject *)ap; - Py_INCREF(ap); - - /* fix the dimensions and strides of the return-array */ - for (i = 0; i < n; i++) { - ret->dimensions[i] = ap->dimensions[permutation[i]]; - ret->strides[i] = ap->strides[permutation[i]]; - } - PyArray_UpdateFlags(ret, CONTIGUOUS | FORTRAN); - return (PyObject *)ret; -} - -/*NUMPY_API * Repeat the array. */ NPY_NO_EXPORT PyObject * |