[Numpy-svn] r3382 - in trunk/numpy/core: src tests

numpy-svn at scipy.org numpy-svn at scipy.org
Mon Oct 23 15:44:11 CDT 2006


Author: oliphant
Date: 2006-10-23 15:44:07 -0500 (Mon, 23 Oct 2006)
New Revision: 3382

Modified:
   trunk/numpy/core/src/multiarraymodule.c
   trunk/numpy/core/tests/test_regression.py
Log:
Add ticket #358 to improve detection of reshape that does not require a copy.

Modified: trunk/numpy/core/src/multiarraymodule.c
===================================================================
--- trunk/numpy/core/src/multiarraymodule.c	2006-10-23 19:42:59 UTC (rev 3381)
+++ trunk/numpy/core/src/multiarraymodule.c	2006-10-23 20:44:07 UTC (rev 3382)
@@ -411,7 +411,121 @@
 	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) return 0; /* different total sizes; no hope */
+
+
+	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]) 
+					return 0; /* not contiguous enough */
+			} else { /* C order */
+				if (oldstrides[ok] !=			\
+				    olddims[ok+1]*oldstrides[ok+1]) 
+					return 0; /* not contiguous enough */
+			}
+		}
+
+		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;
@@ -454,25 +568,6 @@
 	return 0;
 }
 
-/* returns True if self->nd > 1 and all 
-   there is more than one dimension filled with 1.
- */
-static int
-_nd_bigger_than_one(PyArrayObject *arr)
-{
-        int i, nd;
-        int count=0;
-        nd = arr->nd;
-        if (nd > 1) {
-                for (i=0; i<nd; i++) {
-                        if (arr->dimensions[i] > 1)
-                                count++;
-                        if (count > 1) 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.
@@ -541,14 +636,23 @@
                     (((PyArray_CHKFLAGS(self, NPY_CONTIGUOUS) && 
                        fortran == NPY_FORTRANORDER)
                       || (PyArray_CHKFLAGS(self, NPY_FORTRAN) && 
-                          fortran == NPY_CORDER)) && 
-                     _nd_bigger_than_one(self))) {
+                          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

Modified: trunk/numpy/core/tests/test_regression.py
===================================================================
--- trunk/numpy/core/tests/test_regression.py	2006-10-23 19:42:59 UTC (rev 3381)
+++ trunk/numpy/core/tests/test_regression.py	2006-10-23 20:44:07 UTC (rev 3382)
@@ -84,10 +84,10 @@
 
     def check_noncontiguous_fill(self,level=rlevel):
         """Ticket #58."""
-        a = N.zeros((4,2))
-        b = a[:,1]
+        a = N.zeros((5,3))
+	b = a[:,:2,]
         def rs():
-            b.shape = (2,2)
+            b.shape = (10,)
         self.failUnlessRaises(AttributeError,rs)
         
     def check_bool(self,level=rlevel):



More information about the Numpy-svn mailing list