[pypy-commit] pypy matrixmath-reshape-merge: add tests, reimplement reshape so that it can create a copy if necessary, fix bug

mattip noreply at buildbot.pypy.org
Sat Dec 3 19:41:12 CET 2011


Author: mattip
Branch: matrixmath-reshape-merge
Changeset: r50089:50916c4a552b
Date: 2011-12-02 15:03 +0200
http://bitbucket.org/pypy/pypy/changeset/50916c4a552b/

Log:	add tests, reimplement reshape so that it can create a copy if
	necessary, fix bug

diff --git a/pypy/module/micronumpy/interp_numarray.py b/pypy/module/micronumpy/interp_numarray.py
--- a/pypy/module/micronumpy/interp_numarray.py
+++ b/pypy/module/micronumpy/interp_numarray.py
@@ -98,6 +98,41 @@
         endshape[i] = remainder[i]
     return endshape
 
+def get_shape_from_iterable(space, old_size, w_iterable):
+    new_size = 0
+    new_shape = []
+    if not space.issequence_w(w_iterable):
+        new_size = space.int_w(w_iterable)
+        if new_size < 0:
+            new_size = old_size
+        new_shape = [new_size, ]
+    else:
+        neg_dim = -1
+        batch = space.listview(w_iterable)
+        new_size = 1
+        if len(batch) < 1:
+            new_size = 0
+        new_shape = []
+        i = 0
+        for elem in batch:
+            s = space.int_w(elem)
+            if s < 0:
+                if neg_dim >= 0:
+                    raise OperationError(space.w_ValueError, space.wrap(
+                             "can only specify one unknown dimension"))
+                s = 1
+                neg_dim = i
+            new_size *= s
+            new_shape.append(s)
+            i += 1
+        if neg_dim >= 0:
+            new_shape[neg_dim] = old_size / new_size
+            new_size *= new_shape[neg_dim]
+    if new_size != old_size:
+        raise OperationError(space.w_ValueError,
+                space.wrap("total size of new array must be unchanged"))
+    return new_shape
+
 #Recalculating strides. Find the steps that the iteration does for each
 #dimension, given the stride and shape. Then try to create a new stride that
 #fits the new shape, using those steps. If there is a shape/step mismatch
@@ -107,7 +142,7 @@
     #Return the proper strides for new_shape, or None
     # if the mapping crosses stepping boundaries
 
-    #Assumes that nelems have been matched, len(shape) > 1 for old_shape and
+    #Assumes that prod(old_shape) ==prod(new_shape), len(old_shape) > 1 and
     # len(new_shape) > 0
     steps = []
     last_step = 1
@@ -116,7 +151,7 @@
     if old_strides[0] < old_strides[-1]:
         for i in range(len(old_shape)):
             steps.append(old_strides[i] / last_step)
-            last_step = old_shape[i] * old_strides[i]
+            last_step *= old_shape[i]
         cur_step = steps[0]
         n_new_elems_used = 1
         n_old_elems_to_use = old_shape[0]
@@ -137,7 +172,7 @@
     else:
         for i in range(len(old_shape) - 1, -1, -1):
             steps.insert(0, old_strides[i] / last_step)
-            last_step = old_shape[i] * old_strides[i]
+            last_step *= old_shape[i]
         cur_step = steps[-1]
         n_new_elems_used = 1
         oldI = -1
@@ -578,40 +613,10 @@
     def descr_get_shape(self, space):
         return space.newtuple([space.wrap(i) for i in self.shape])
 
+
     def descr_set_shape(self, space, w_iterable):
         concrete = self.get_concrete()
-        new_size = 0
-        new_shape = []
-        if not space.issequence_w(w_iterable):
-            new_size = space.int_w(w_iterable)
-            if new_size < 0:
-                new_size = self.find_size()
-            new_shape = [new_size, ]
-        else:
-            neg_dim = -1
-            batch = space.listview(w_iterable)
-            new_size = 1
-            if len(batch) < 1:
-                new_size = 0
-            new_shape = []
-            i = 0
-            for elem in batch:
-                s = space.int_w(elem)
-                if s < 0:
-                    if neg_dim >= 0:
-                        raise OperationError(space.w_ValueError, space.wrap(
-                                 "can only specify one unknown dimension"))
-                    s = 1
-                    neg_dim = i
-                new_size *= s
-                new_shape.append(s)
-                i += 1
-            if neg_dim >= 0:
-                new_shape[neg_dim] = self.find_size() / new_size
-                new_size *= new_shape[neg_dim]
-        if new_size != self.find_size():
-            raise OperationError(space.w_ValueError,
-                    space.wrap("total size of new array must be unchanged"))
+        new_shape = get_shape_from_iterable(space, concrete.find_size(), w_iterable)
         concrete.setshape(space, new_shape)
 
     def descr_get_size(self, space):
@@ -869,22 +874,24 @@
     def descr_reshape(self, space, w_iterable):
         """Return a reshaped view into the original array's data
         """
-        new_sig = signature.Signature.find_sig([
-            NDimSlice.signature, self.signature,
-        ])
         concrete = self.get_concrete()
-        #concrete = self
-        ndims = len(concrete.shape)
-        strides = [0] * ndims
-        backstrides = [0] * ndims
-        shape = [0] * ndims
-        for i in range(len(concrete.shape)):
-            strides[i] = concrete.strides[i]
-            backstrides[i] = concrete.backstrides[i]
-            shape[i] = concrete.shape[i]
-        arr = NDimSlice(self, new_sig, self.start, strides,
-                backstrides, shape)
-        arr.descr_set_shape(space, w_iterable)
+        new_shape = get_shape_from_iterable(space, concrete.find_size(), w_iterable)
+        #Since we got to here, prod(new_shape) == self.size
+        new_strides = calc_new_strides(new_shape, concrete.shape, concrete.strides)
+        if new_strides:
+            #We can create a view, strides somehow match up.
+            new_sig = signature.Signature.find_sig([
+                NDimSlice.signature, self.signature, ])
+            ndims = len(new_shape)
+            new_backstrides = [0] * ndims
+            for nd in range(ndims):
+                new_backstrides[nd] = (new_shape[nd] - 1) * new_strides[nd]
+            arr = NDimSlice(self, new_sig, self.start, new_strides,
+                    new_backstrides, new_shape)
+        else:
+            #Create copy with contiguous data
+            arr = concrete.copy()
+            arr.set_shape(space, new_shape)
         return arr
 
     def descr_mean(self, space):
@@ -984,7 +991,8 @@
         return 'Scalar'
 
     def setshape(self, space, new_shape):
-        # XXX shouldn't it raise?
+        # In order to get here, we already checked that prod(new_shape)==1,
+        # so in order to have a consistent API, let it go through.
         pass
 
 class VirtualArray(BaseArray):
@@ -1183,7 +1191,7 @@
         if len(self.shape) < 1:
             return
         elif len(self.shape) < 2:
-            #REVIEWER: this code could be refactored into calc_strides
+            #TODO: this code could be refactored into calc_strides
             #but then calc_strides would have to accept a stepping factor
             strides = []
             backstrides = []
diff --git a/pypy/module/micronumpy/test/test_numarray.py b/pypy/module/micronumpy/test/test_numarray.py
--- a/pypy/module/micronumpy/test/test_numarray.py
+++ b/pypy/module/micronumpy/test/test_numarray.py
@@ -160,6 +160,7 @@
 
     def test_calc_new_strides(self):
 	from pypy.module.micronumpy.interp_numarray import calc_new_strides
+        assert calc_new_strides([2, 4], [4, 2], [4, 2]) == [8, 2]
         assert calc_new_strides([2, 4, 3], [8, 3], [1, 16]) == [1, 2, 16]
         assert calc_new_strides([2, 3, 4], [8, 3], [1, 16]) is None
         assert calc_new_strides([8, 3], [2, 4, 3], [48, 6, 1]) == [6, 1]
@@ -346,6 +347,10 @@
         assert a.shape == (12, )
         exc = raises(ValueError, "a.shape = 10")
         assert str(exc.value) == "total size of new array must be unchanged"
+        a = array(3)
+        a.shape = ()
+        #numpy allows this
+        a.shape = (1,)
 
     def test_reshape(self):
         from numpypy import array, zeros
@@ -369,12 +374,28 @@
         assert str(exc.value) == \
                            "incompatible shape for a non-contiguous array"
         b = a[::2, :, :].reshape((2, 6))
+        assert b.shape == (2, 6)
         b = arange(20)[1:17:2]
         b.shape = (4, 2)
         assert (b == [[1, 3], [5, 7], [9, 11], [13, 15]]).all()
         b.reshape((2, 4))
         assert (b == [[1, 3, 5, 7], [9, 11, 13, 15]]).all()
 
+        z=arange(96).reshape((12, -1))
+        assert z.shape == (12, 8)
+        y=z.reshape(4,3,8)
+        v=y[:,::2,:]
+        w = y.reshape(96)
+        u = v.reshape(64)
+        assert y[1, 2, 1] == z[5, 1]
+        y[1, 2, 1] = 1000
+        #z, y, w, v are views of eachother
+        assert z[5, 1] == 1000
+        assert v[1, 1, 1] == 1000
+        assert w[41] == 1000
+        #u is not a view, it is a copy!
+        assert u[25] == 41 
+        
     def test_add(self):
         from numpypy import array
         a = array(range(5))


More information about the pypy-commit mailing list