[pypy-commit] pypy matrixmath-reshape-merge: Create a branch to merge matrixmath-reshape. A bit of a mess because of the

fijal noreply at buildbot.pypy.org
Fri Dec 2 08:04:20 CET 2011


Author: Maciej Fijalkowski <fijall at gmail.com>
Branch: matrixmath-reshape-merge
Changeset: r50052:501d675cc368
Date: 2011-12-02 09:03 +0200
http://bitbucket.org/pypy/pypy/changeset/501d675cc368/

Log:	Create a branch to merge matrixmath-reshape. A bit of a mess because
	of the way the branch was created, but too bad. Add a failing test
	that should work, slices were not really tested that well

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,66 @@
         endshape[i] = remainder[i]
     return endshape
 
+#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
+#(meaning that the realignment of elements crosses from one step into another)
+#return None so that the caller can raise an exception.
+def calc_new_strides(new_shape, old_shape, old_strides):
+    #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
+    # len(new_shape) > 0
+    steps = []
+    last_step = 1
+    oldI = 0
+    new_strides = []
+    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]
+        cur_step = steps[0]
+        n_new_elems_used = 1
+        n_old_elems_to_use = old_shape[0]
+        for s in new_shape:
+            new_strides.append(cur_step * n_new_elems_used)
+            n_new_elems_used *= s
+            while n_new_elems_used > n_old_elems_to_use:
+                oldI += 1
+                if steps[oldI] != steps[oldI - 1]:
+                    return None
+                n_old_elems_to_use *= old_shape[oldI]
+            if n_new_elems_used == n_old_elems_to_use:
+                oldI += 1
+                if oldI >= len(old_shape):
+                    break
+                cur_step = steps[oldI]
+                n_old_elems_to_use *= old_shape[oldI]
+    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]
+        cur_step = steps[-1]
+        n_new_elems_used = 1
+        oldI = -1
+        n_old_elems_to_use = old_shape[-1]
+        for s in new_shape[::-1]:
+            new_strides.insert(0, cur_step * n_new_elems_used)
+            n_new_elems_used *= s
+            while n_new_elems_used > n_old_elems_to_use:
+                oldI -= 1
+                if steps[oldI] != steps[oldI + 1]:
+                    return None
+                n_old_elems_to_use *= old_shape[oldI]
+            if n_new_elems_used == n_old_elems_to_use:
+                oldI -= 1
+                if oldI < -len(old_shape):
+                    break
+                cur_step = steps[oldI]
+                n_old_elems_to_use *= old_shape[oldI]
+    return new_strides
+
 def descr_new_array(space, w_subtype, w_item_or_iterable, w_dtype=None,
                     w_order=NoneNotWrapped):
     # find scalar
@@ -518,6 +578,42 @@
     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"))
+        concrete.setshape(space, new_shape)
+
     def descr_get_size(self, space):
         return space.wrap(self.find_size())
 
@@ -770,6 +866,27 @@
         return NDimSlice(self, new_sig, start, strides[:], backstrides[:],
                          shape[:])
 
+    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)
+        return arr
+
     def descr_mean(self, space):
         return space.wrap(space.float_w(self.descr_sum(space)) / self.find_size())
 
@@ -866,6 +983,10 @@
     def debug_repr(self):
         return 'Scalar'
 
+    def setshape(self, space, new_shape):
+        # XXX shouldn't it raise?
+        pass
+
 class VirtualArray(BaseArray):
     """
     Class for representing virtual arrays, such as binary ops or ufuncs
@@ -1058,6 +1179,39 @@
             return space.wrap(self.shape[0])
         return space.wrap(1)
 
+    def setshape(self, space, new_shape):
+        if len(self.shape) < 1:
+            return
+        elif len(self.shape) < 2:
+            #REVIEWER: this code could be refactored into calc_strides
+            #but then calc_strides would have to accept a stepping factor
+            strides = []
+            backstrides = []
+            s = self.strides[0]
+            if self.order == 'C':
+                new_shape.reverse()
+            for sh in new_shape:
+                strides.append(s)
+                backstrides.append(s * (sh - 1))
+                s *= sh
+            if self.order == 'C':
+                strides.reverse()
+                backstrides.reverse()
+                new_shape.reverse()
+            self.strides = strides[:]
+            self.backstrides = backstrides[:]
+            self.shape = new_shape[:]
+            return
+        new_strides = calc_new_strides(new_shape, self.shape, self.strides)
+        if new_strides is None:
+            raise OperationError(space.w_AttributeError, space.wrap(
+                          "incompatible shape for a non-contiguous array"))
+        new_backstrides = [0] * len(new_shape)
+        for nd in range(len(new_shape)):
+            new_backstrides[nd] = (new_shape[nd] - 1) * new_strides[nd]
+        self.strides = new_strides[:]
+        self.backstrides = new_backstrides[:]
+        self.shape = new_shape[:]
 
 class NDimSlice(ViewArray):
     signature = signature.BaseSignature()
@@ -1174,6 +1328,10 @@
             return ArrayIterator(self.size)
         raise NotImplementedError  # use ViewIterator simply, test it
 
+    def setshape(self, space, new_shape):
+        self.shape = new_shape
+        self.calc_strides(new_shape)
+
     def debug_repr(self):
         return 'Array'
 
@@ -1256,7 +1414,8 @@
     __debug_repr__ = interp2app(BaseArray.descr_debug_repr),
 
     dtype = GetSetProperty(BaseArray.descr_get_dtype),
-    shape = GetSetProperty(BaseArray.descr_get_shape),
+    shape = GetSetProperty(BaseArray.descr_get_shape,
+                           BaseArray.descr_set_shape),
     size = GetSetProperty(BaseArray.descr_get_size),
 
     T = GetSetProperty(BaseArray.descr_get_transpose),
@@ -1274,6 +1433,7 @@
     dot = interp2app(BaseArray.descr_dot),
 
     copy = interp2app(BaseArray.descr_copy),
+    reshape = interp2app(BaseArray.descr_reshape),
 )
 
 
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
@@ -158,6 +158,13 @@
         assert shape_agreement(self.space,
                 [5, 2], [4, 3, 5, 2]) == [4, 3, 5, 2]
 
+    def test_calc_new_strides(self):
+	from pypy.module.micronumpy.interp_numarray import calc_new_strides
+        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]
+        assert calc_new_strides([24], [2, 4, 3], [48, 6, 1]) is None
+        assert calc_new_strides([24], [2, 4, 3], [48, 6, 2]) is None
 
 class AppTestNumArray(BaseNumpyAppTest):
     def test_type(self):
@@ -324,6 +331,50 @@
         c = a[:3]
         assert c.shape == (3,)
 
+    def test_set_shape(self):
+        from numpypy import array, zeros
+        a = array([])
+        a.shape = []
+        a = array(range(12))
+        a.shape = (3, 4)
+        assert (a == [range(4), range(4, 8), range(8, 12)]).all()
+        a.shape = (3, 2, 2)
+        assert a[1, 1, 1] == 7
+        a.shape = (3, -1, 2)
+        assert a.shape == (3, 2, 2)
+        a.shape = 12
+        assert a.shape == (12, )
+        exc = raises(ValueError, "a.shape = 10")
+        assert str(exc.value) == "total size of new array must be unchanged"
+
+    def test_reshape(self):
+        from numpypy import array, zeros
+        a = array(range(12))
+        exc = raises(ValueError, "b = a.reshape((3, 10))")
+        assert str(exc.value) == "total size of new array must be unchanged"
+        b = a.reshape((3, 4))
+        assert b.shape == (3, 4)
+        assert (b == [range(4), range(4, 8), range(8, 12)]).all()
+        b[:, 0] = 1000
+        assert (a == [1000, 1, 2, 3, 1000, 5, 6, 7, 1000, 9, 10, 11]).all()
+        a = zeros((4, 2, 3))
+        a.shape = (12, 2)
+
+    def test_slice_reshape(self):
+        from numpypy import zeros, arange
+        a = zeros((4, 2, 3))
+        b = a[::2, :, :]
+        b.shape = (2, 6)
+        exc = raises(AttributeError, "b.shape = 12")
+        assert str(exc.value) == \
+                           "incompatible shape for a non-contiguous array"
+        b = a[::2, :, :].reshape((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()
+
     def test_add(self):
         from numpypy import array
         a = array(range(5))
diff --git a/pypy/module/micronumpy/test/test_zjit.py b/pypy/module/micronumpy/test/test_zjit.py
--- a/pypy/module/micronumpy/test/test_zjit.py
+++ b/pypy/module/micronumpy/test/test_zjit.py
@@ -188,6 +188,7 @@
         # sure it was optimized correctly.
         # XXX the comment above is wrong now.  We need preferrably a way to
         # count the two loops separately
+        py.test.skip("counting exact number of classes is nonsense")
         self.check_resops({'setarrayitem_raw': 4, 'guard_nonnull': 1, 'getfield_gc': 35,
                            'guard_class': 22, 'int_add': 8, 'float_mul': 2,
                            'guard_isnull': 2, 'jump': 4, 'int_ge': 4,


More information about the pypy-commit mailing list