[Scipy-svn] r3280 - in trunk/scipy/sparse: . tests

scipy-svn@scip... scipy-svn@scip...
Wed Aug 29 10:34:50 CDT 2007


Author: stefan
Date: 2007-08-29 10:34:30 -0500 (Wed, 29 Aug 2007)
New Revision: 3280

Modified:
   trunk/scipy/sparse/sparse.py
   trunk/scipy/sparse/tests/test_sparse.py
Log:
Fix broken functionality in sparse matrices.

lil_matrix:
  Fix inplace operations.
  Fix fancy-indexed assignments.
  Add row iteration.
  Remove scalar addition.  Warn on attempt.
sp_matrix:
  Add generic row iteration.
  Add generic inplace operators.
csc_matrix and csr_matrix:
  More helpful error messages for fancy indexed assignments.
dok_matrix:
  Allow iteration over rows.

Partially addresses ticket #226.


Modified: trunk/scipy/sparse/sparse.py
===================================================================
--- trunk/scipy/sparse/sparse.py	2007-08-29 15:08:33 UTC (rev 3279)
+++ trunk/scipy/sparse/sparse.py	2007-08-29 15:34:30 UTC (rev 3280)
@@ -124,6 +124,10 @@
         csc = self.tocsc()
         return csc.astype(t)
 
+    def __iter__(self):
+        for r in xrange(self.shape[0]):
+            yield self[r,:]
+
     def getmaxprint(self):
         try:
             maxprint = self.maxprint
@@ -259,6 +263,18 @@
         csc = self.tocsc()
         return -csc
 
+    def __iadd__(self, other):
+        raise NotImplementedError
+
+    def __isub__(self, other):
+        raise NotImplementedError
+
+    def __imul__(self, other):
+        raise NotImplementedError
+
+    def __idiv__(self, other):
+        raise TypeError("No support for matrix division.")
+
     def __getattr__(self, attr):
         if attr == 'A':
             return self.toarray()
@@ -925,6 +941,10 @@
         else:
             return _cs_matrix.__getattr__(self, attr)
 
+    def __iter__(self):
+        csr = self.tocsr()
+        for r in xrange(self.shape[0]):
+            yield csr[r,:]
 
     def __add__(self, other):
         return _cs_matrix.__add__(self, other, csc_plus_csc)
@@ -985,6 +1005,9 @@
         if isinstance(key, tuple):
             row = key[0]
             col = key[1]
+            if not (isscalarlike(row) and isscalarlike(col)):
+                raise NotImplementedError("Fancy indexing in assignments not"
+                                          "supported for csc matrices.")
             M, N = self.shape
             if (row < 0):
                 row = M + row
@@ -1330,6 +1353,9 @@
         if isinstance(key, tuple):
             row = key[0]
             col = key[1]
+            if not (isscalarlike(row) and isscalarlike(col)):
+                raise NotImplementedError("Fancy indexing in assignment not "
+                                          "supported for csr matrices.")
             M, N = self.shape
             if (row < 0):
                 row = M + row
@@ -1573,7 +1599,7 @@
                     #         [self.get((element, j), 0) for element in seq]
                     # ** Instead just add the non-zero elements.  This uses
                     # ** linear time in the number of non-zeros:
-                    for (ii, jj) in self:
+                    for (ii, jj) in self.keys():
                         if jj == j and ii >= first and ii <= last:
                             dict.__setitem__(new, (ii-first, 0), \
                                              dict.__getitem__(self, (ii,jj)))
@@ -1608,7 +1634,7 @@
             #         [self.get((i, element), 0) for element in seq]
             # ** Instead loop over the non-zero elements.  This is slower
             # ** if there are many non-zeros
-            for (ii, jj) in self:
+            for (ii, jj) in self.keys():
                 if ii == i and jj >= first and jj <= last:
                     dict.__setitem__(new, (0, jj-first), \
                                      dict.__getitem__(self, (ii,jj)))
@@ -1634,7 +1660,7 @@
             if i < 0 or i >= self.shape[0] or j < 0 or j >= self.shape[1]:
                 raise IndexError, "index out of bounds"
             if isintlike(value) and value == 0:
-                if key in self:  # get rid of it something already there
+                if key in self.keys():  # get rid of it something already there
                     del self[key]
             else:
                 # Ensure value is a single element, not a sequence
@@ -1745,7 +1771,7 @@
             # the two matrices to be summed.  Would this be a good idea?
             new = dok_matrix(self.shape, dtype=self.dtype)
             new.update(self)
-            for key in other:
+            for key in other.keys():
                 new[key] += other[key]
         elif isspmatrix(other):
             csc = self.tocsc()
@@ -1785,7 +1811,7 @@
 
     def __neg__(self):
         new = dok_matrix(self.shape, dtype=self.dtype)
-        for key in self:
+        for key in self.keys():
             new[key] = -self[key]
         return new
 
@@ -1851,13 +1877,13 @@
         indx = int((columns == 1))
         N = len(cols_or_rows)
         if indx: # columns
-            for key in self:
+            for key in self.keys():
                 num = searchsorted(cols_or_rows, key[1])
                 if num < N:
                     newkey = (key[0], num)
                     new[newkey] = self[key]
         else:
-            for key in self:
+            for key in self.keys():
                 num = searchsorted(cols_or_rows, key[0])
                 if num < N:
                     newkey = (num, key[1])
@@ -1871,7 +1897,7 @@
         ext = dok_matrix()
         indx = int((columns == 1))
         if indx:
-            for key in self:
+            for key in self.keys():
                 num = searchsorted(cols_or_rows, key[1])
                 if cols_or_rows[num] == key[1]:
                     newkey = (key[0], num)
@@ -1880,7 +1906,7 @@
                     newkey = (key[0], key[1]-num)
                     base[newkey] = self[key]
         else:
-            for key in self:
+            for key in self.keys():
                 num = searchsorted(cols_or_rows, key[0])
                 if cols_or_rows[num] == key[0]:
                     newkey = (num, key[1])
@@ -1896,7 +1922,7 @@
             if other.shape[0] != self.shape[1]:
                 raise ValueError, "dimensions do not match"
             new = [0] * self.shape[0]
-            for key in self:
+            for key in self.keys():
                 new[int(key[0])] += self[key] * other[int(key[1]), ...]
             new = array(new)
             if isinstance(other, matrix):
@@ -1913,7 +1939,7 @@
             if other.shape[-1] != self.shape[0]:
                 raise ValueError, "dimensions do not match"
             new = [0] * self.shape[1]
-            for key in self:
+            for key in self.keys():
                 new[int(key[1])] += other[..., int(key[0])] * conj(self[key])
             new = array(new)
             if isinstance(other, matrix):
@@ -1959,7 +1985,7 @@
         """ Return Compressed Sparse Column format arrays for this matrix
         """
         # Fast sort on columns using the Schwartzian transform
-        keys = [(k[1], k[0]) for k in self]
+        keys = [(k[1], k[0]) for k in self.keys()]
         keys.sort()
         keys = [(k[1], k[0]) for k in keys]
 
@@ -1986,7 +2012,7 @@
 
     def toarray(self):
         new = zeros(self.shape, dtype=self.dtype)
-        for key in self:
+        for key in self.keys():
             ikey0 = int(key[0])
             ikey1 = int(key[1])
             new[ikey0, ikey1] = self[key]
@@ -2220,7 +2246,21 @@
             for i in xrange(A.shape[0]):
                 self[i, :] = A[i, :]
 
+    def __iadd__(self,other):
+        self[:,:] = self + other
+        return self
 
+    def __isub__(self,other):
+        self[:,:] = self - other
+        return self
+
+    def __imul__(self,other):
+        if isscalarlike(other):
+            self[:,:] = self * other
+            return self
+        else:
+            raise TypeError("In-place matrix multiplication not supported.")
+
     # Whenever the dimensions change, empty lists should be created for each
     # row
 
@@ -2256,8 +2296,6 @@
         new.data[0] = self.data[i][:]
         return new
 
-
-
     def _get1(self, i, j):
         row = self.rows[i]
         data = self.data[i]
@@ -2473,11 +2511,9 @@
         return new
 
     def __add__(self, other):
-        if isscalar(other):
-            new = self.copy()
-            new.data = numpy.array([[val+other for val in rowvals] for
-                                    rowvals in new.data], dtype=object)
-            return new
+        if isscalar(other) and other != 0:
+            raise ValueError("Refusing to destroy sparsity. "
+                             "Use x.todense() + c instead.")
         else:
             return spmatrix.__add__(self, other)
 

Modified: trunk/scipy/sparse/tests/test_sparse.py
===================================================================
--- trunk/scipy/sparse/tests/test_sparse.py	2007-08-29 15:08:33 UTC (rev 3279)
+++ trunk/scipy/sparse/tests/test_sparse.py	2007-08-29 15:34:30 UTC (rev 3280)
@@ -27,7 +27,6 @@
 restore_path()
 
 class _test_cs:
-
     def setUp(self):
         self.dat = matrix([[1,0,0,2],[3,0,1,0],[0,2,0,0]],'d')
         self.datsp = self.spmatrix(self.dat)
@@ -623,7 +622,7 @@
         for ir in range( asp.shape[0] ):
             for ic in range( asp.shape[1] ):
                 assert_equal( asp[ir, ic], bsp[ir, ic] )
-                
+
 class test_csc(_test_cs, _test_vert_slicing, _test_arith, NumpyTestCase):
     spmatrix = csc_matrix
 
@@ -698,7 +697,7 @@
         A = dok_matrix((3,2))
         A[0,1] = -10
         A[2,0] = 20
-        A += 10
+        A = A + 10
         B = matrix([[10, 0], [10, 10], [30, 10]])
         assert_array_equal(A.todense(), B)
 
@@ -790,8 +789,16 @@
         assert_equal(caught,5)
 
 
-class test_lil(_test_cs, _test_horiz_slicing, NumpyTestCase):
+class test_lil(_test_cs, _test_horiz_slicing, NumpyTestCase,
+               ParametricTestCase):
     spmatrix = lil_matrix
+
+    B = lil_matrix((4,3))
+    B[0,0] = 2
+    B[1,2] = 7
+    B[2,1] = 3
+    B[3,0] = 10
+
     def check_dot(self):
         A = matrix(zeros((10,10)))
         A[0,3] = 10
@@ -828,17 +835,54 @@
         """ Tests whether a row of one lil_matrix can be assigned to
         another.
         """
-        B = lil_matrix((10,10))
-        B[0,3] = 10
-        B[5,6] = 20
-        B[8,3] = 30
-        B[3,8] = 40
-        B[8,9] = 50
+        B = self.B.copy()
         A = B / 10
-        B[0, :] = A[0, :]
-        assert_array_equal(A[0, :].A, B[0, :].A)
-        assert_array_equal(A[0, :].A, array([[0, 0, 0, 1, 0, 0, 0, 0, 0, 0.]]))
+        B[0,:] = A[0,:]
+        assert_array_equal(A[0,:].A, B[0,:].A)
 
+    def tst_inplace_op(self,op,arr,other,result):
+        cpy = arr
+        getattr(arr,"__i%s__" % op)(other)
+
+        assert_array_equal(cpy.todense(),arr.todense())
+        assert_array_equal(arr.todense(),result)
+
+    def testip_inplace_ops(self):
+        B = self.B[:3,:3].copy()
+        B[:,:] = B-B
+        C = B.todense()
+
+        data = {'add':(B,C+C),
+                'sub':(B,zeros(B.shape)),
+                'mul':(3,C*3)}
+
+        return [(self.tst_inplace_op,op,B,other,result)
+                for op,(other,result) in data.iteritems()]
+
+    def check_lil_slice_assignment(self):
+        B = lil_matrix((4,3))
+        B[0,0] = 5
+        B[1,2] = 3
+        B[2,1] = 7
+
+        expected = array([[10,0,0],
+                          [0,0,6],
+                          [0,14,0],
+                          [0,0,0]])
+
+        B[:,:] = B+B
+        assert_array_equal(B.todense(),expected)
+
+        block = [[1,0],[0,4]]
+        B[:2,:2] = csc_matrix(array(block))
+        assert_array_equal(B.todense()[:2,:2],block)
+
+    def check_lil_iteration(self):
+        row_data = [[1,2,3],[4,5,6]]
+        B = lil_matrix(array(row_data))
+        for r,row in enumerate(B):
+            assert_array_equal(row.todense(),array(row_data[r],ndmin=2))
+
     def check_lil_from_csr(self):
         """ Tests whether a lil_matrix can be constructed from a
         csr_matrix.
@@ -853,19 +897,6 @@
         D = lil_matrix(C)
         assert_array_equal(C.A, D.A)
 
-    def check_scalar_add(self):
-        a = lil_matrix((3,3))
-        a[0,0] = 1
-        a[0,1] = 2
-        a[1,1] = 3
-        a[2,1] = 4
-        a[2,2] = 5
-
-        assert_array_equal((a-5).todense(),
-                           [[-4,-3,0],
-                            [ 0,-2,0],
-                            [ 0,-1,0]])
-
     def check_point_wise_multiply(self):
         l = lil_matrix((4,3))
         l[0,0] = 1



More information about the Scipy-svn mailing list