[Scipy-svn] r2514 - trunk/Lib/sparse

scipy-svn at scipy.org scipy-svn at scipy.org
Tue Jan 9 22:19:37 EST 2007


Author: timl
Date: 2007-01-09 21:19:32 -0600 (Tue, 09 Jan 2007)
New Revision: 2514

Modified:
   trunk/Lib/sparse/sparse.py
Log:
introduce a new base class to handle the commonalities between csc and csr matrices and reduce duplicated code.

Modified: trunk/Lib/sparse/sparse.py
===================================================================
--- trunk/Lib/sparse/sparse.py	2007-01-09 16:52:09 UTC (rev 2513)
+++ trunk/Lib/sparse/sparse.py	2007-01-10 03:19:32 UTC (rev 2514)
@@ -374,6 +374,13 @@
         csc = self.tocsc()
         return csc.tocoo()
 
+    def toself(self, copy=False):
+        if copy:
+            new = self.copy()
+        else:
+            new = self
+        return new
+
     def copy(self):
         csc = self.tocsc()
         return csc.copy()
@@ -459,7 +466,177 @@
             fd.write(format % (ir, ic, data))
         fd.close()
 
-class csc_matrix(spmatrix):
+class _sc_matrix(spmatrix):
+
+    def astype(self, t):
+        out = self.copy()
+        out.data = out.data.astype(t)
+        out.dtype = out.data.dtype
+        out.ftype = _transtabl[out.dtype.char]
+        return out
+
+    def __repr__(self):
+        format = self.getformat()
+        return "<%dx%d sparse matrix of type '%s'\n\twith %d stored "\
+               "elements (space for %d)\n\tin %s format>" % \
+               (self.shape + (self.dtype.type, self.getnnz(), self.nzmax, \
+                   _formats[format][1]))
+
+
+
+    def __add__(self, other, self_ind, other_ind, fn, cls):
+        # First check if argument is a scalar
+        if isscalarlike(other):
+            # Now we would add this scalar to every element.
+            raise NotImplementedError, 'adding a scalar to a CSC or CSR ' \
+                  'matrix is not yet supported'
+        elif isspmatrix(other):
+            other = other.tocsc()
+            if (other.shape != self.shape):
+                raise ValueError, "inconsistent shapes"
+            indptr, ind, data = fn(self.shape[0], self.shape[1], \
+                                         self.indptr, self_ind, \
+                                         self.data, other.indptr, \
+                                         other_ind, other.data)
+            return cls((data, ind, indptr), self.shape)
+        elif isdense(other):
+            # Convert this matrix to a dense matrix and add them
+            return other + self.todense()
+        else:
+            raise TypeError, "unsupported type for sparse matrix addition"
+
+
+    def __mul__(self, other):
+        """ Scalar, vector, or matrix multiplication
+        """
+        if isscalarlike(other):
+            new = self.copy()
+            new.data = other * new.data         # allows type conversion
+            new.dtype = new.data.dtype
+            new.ftype = _transtabl[new.dtype.char]
+            return new
+        else:
+            return self.dot(other)
+
+
+    def __rmul__(self, other):  # other * self
+        if isscalarlike(other):
+            new = self.copy()
+            new.data = other * new.data         # allows type conversion
+            new.dtype = new.data.dtype
+            new.ftype = _transtabl[new.dtype.char]
+            return new
+        else:
+            # Don't use asarray unless we have to
+            try:
+                tr = other.transpose()
+            except AttributeError:
+                tr = asarray(other).transpose()
+            return self.transpose().dot(tr).transpose()
+
+
+    def __neg__(self):
+        new = self.copy()
+        new.data *= -1
+        return new
+
+    def __pow__(self, other, self_ind, other_ind, fn, cls):
+        """ Element-by-element power (unless other is a scalar, in which
+        case return the matrix power.)
+        """
+        if isscalarlike(other):
+            new = self.copy()
+            new.data = new.data ** other
+            new.dtype = new.data.dtype
+            new.ftype = _transtabl[new.dtype.char]
+            return new
+        elif isspmatrix(other):
+            other = other.tocsr()
+            if (other.shape != self.shape):
+                raise ValueError, "inconsistent shapes"
+            indptr, ind, data = fn(self.shape[0], self.shape[1], \
+                                               self.indptr, self_ind, \
+                                               self.data, other.indptr, \
+                                               other_ind, other.data)
+            return cls((data, ind, indptr), (self.shape[0], other.shape[1]))
+        else:
+            raise TypeError, "unsupported type for sparse matrix power"
+
+
+    def matmat(self, other, self_ind, other_ind, cls):
+        if isspmatrix(other):
+            M, K1 = self.shape
+            K2, N = other.shape
+            if (K1 != K2):
+                raise ValueError, "shape mismatch error"
+            other = other.tocsc()
+            indptr, ind, data = cscmucsc(M, N, self.indptr, self_ind, \
+                                            self.data, other.indptr, \
+                                            other_ind, other.data)
+            return cls((data, ind, indptr), (M, N))      
+        elif isdense(other):
+            # This is SLOW!  We need a more efficient implementation
+            # of sparse * dense matrix multiplication!
+            return self.matmat(csc_matrix(other))
+        else:
+            raise TypeError, "need a dense or sparse matrix"
+
+    def matvec(self, other, self_ind, fn):
+        if isdense(other):
+            # This check is too harsh -- it prevents a column vector from
+            # being created on-the-fly like dense matrix objects can.
+            #if len(other) != self.shape[1]:
+            #    raise ValueError, "dimension mismatch"
+            oth = numpy.ravel(other)            
+            y = fn(self.shape[0], self.shape[1], \
+                   self.indptr, self_ind, self.data, oth)
+            if isinstance(other, matrix):
+                y = asmatrix(y)
+                # If 'other' was an (nx1) column vector, transpose the result
+                # to obtain an (mx1) column vector.
+                if other.ndim == 2 and other.shape[1] == 1:
+                    y = y.T
+            return y
+        elif isspmatrix(other):
+            raise TypeError, "use matmat() for sparse * sparse"
+        else:
+            raise TypeError, "need a dense vector"
+
+    def rmatvec(self, other, shape0, shape1, fn, conjugate=True):
+        if isdense(other):
+            # This check is too harsh -- it prevents a column vector from
+            # being created on-the-fly like dense matrix objects can.
+            # if len(other) != self.shape[0]:
+            #    raise ValueError, "dimension mismatch"
+            if conjugate:
+                cd = conj(self.data)
+            else:
+                cd = self.data
+            oth = numpy.ravel(other)            
+            y = fn(shape0, shape1, self.indptr, self.rowind, cd, oth)
+            if isinstance(other, matrix):
+                y = asmatrix(y)
+                # In the (unlikely) event that this matrix is 1x1 and 'other' was an
+                # (mx1) column vector, transpose the result.
+                if other.ndim == 2 and other.shape[1] == 1:
+                    y = y.T
+            return y
+        elif isspmatrix(other):
+            raise TypeError, "use matmat() for sparse * sparse"
+        else:
+            raise TypeError, "need a dense vector"
+
+    def getdata(self, ind):
+        return self.data[ind]
+
+    def tocoo(self, fn, self_ind):
+        rows, cols, data = fn(self.shape[0], self.shape[1], \
+                              self.indptr, self_ind, self.data)
+        return coo_matrix((data, (rows, cols)), self.shape)
+
+
+
+class csc_matrix(_cs_matrix):
     """ Compressed sparse column matrix
         This can be instantiated in several ways:
           - csc_matrix(d)
@@ -481,7 +658,7 @@
             standard CSC representation
     """
     def __init__(self, arg1, dims=None, nzmax=NZMAX, dtype=None, copy=False):
-        spmatrix.__init__(self)
+        _cs_matrix.__init__(self)
         if isdense(arg1):
             self.dtype = getdtype(dtype, arg1)
             # Convert the dense array or matrix arg1 to CSC format
@@ -591,7 +768,6 @@
             M = max(oldM, M)
         N = max(0, oldN, N, len(self.indptr) - 1)
         self.shape = (M, N)
-
         self._check()
 
     def _check(self):
@@ -601,7 +777,6 @@
         M, N = self.shape
         nnz = self.indptr[-1]
         nzmax = len(self.rowind)
-
         if (rank(self.data) != 1) or (rank(self.rowind) != 1) or \
            (rank(self.indptr) != 1):
             raise ValueError, "data, rowind, and indptr arrays "\
@@ -630,19 +805,6 @@
 
         self.ftype = _transtabl[self.dtype.char]
 
-    def astype(self, t):
-        out = self.copy()
-        out.data = out.data.astype(t)
-        out.dtype = out.data.dtype
-        out.ftype = _transtabl[out.dtype.char]
-        return out
-
-    def __repr__(self):
-        format = self.getformat()
-        return "<%dx%d sparse matrix of type '%s'\n\twith %d stored "\
-               "elements (space for %d)\n\tin %s format>" % \
-               (self.shape + (self.dtype.type, self.getnnz(), self.nzmax, \
-                   _formats[format][1]))
     
     def __radd__(self, other):
         """ Function supporting the operation: self + other.
@@ -666,75 +828,10 @@
             raise TypeError, "unsupported type for sparse matrix addition"
 
     def __add__(self, other):
-        if isscalarlike(other):
-            raise NotImplementedError, 'adding a scalar to a CSC matrix is ' \
-                    'not yet supported'
-        elif isspmatrix(other):
-            ocs = other.tocsc()
-            if (ocs.shape != self.shape):
-                raise ValueError, "inconsistent shapes"
-            indptr, rowind, data = cscplcsc(self.shape[0], self.shape[1], \
-                                            self.indptr, self.rowind, \
-                                            self.data, ocs.indptr, ocs.rowind, \
-                                            ocs.data)
-            return csc_matrix((data, rowind, indptr), self.shape)
-        elif isdense(other):
-            # Convert this matrix to a dense matrix and add them
-            return other + self.todense()
-        else:
-            raise TypeError, "unsupported type for sparse matrix addition"
+        _cs_matrix.__add__(self, other, self.rowind, other.rowind, cscplcsc, csc_matrix)
 
-    def __mul__(self, other):
-        """ Scalar, vector, or matrix multiplication
-        """
-        if isscalarlike(other):
-            new = self.copy()
-            new.data *= other
-            new.dtype = new.data.dtype
-            new.ftype = _transtabl[new.dtype.char]
-            return new
-        else:
-            return self.dot(other)
-
-    def __rmul__(self, other):  # other * self
-        if isscalarlike(other):
-            new = self.copy()
-            new.data = other * new.data
-            new.dtype = new.data.dtype
-            new.ftype = _transtabl[new.dtype.char]
-            return new
-        else:
-            # Don't use asarray unless we have to
-            try:
-                tr = other.transpose()
-            except AttributeError:
-                tr = asarray(other).transpose()
-            return self.transpose().dot(tr).transpose()
-
-    def __neg__(self):
-        new = self.copy()
-        new.data *= -1
-        return new
-
     def __pow__(self, other):
-        """ Element-by-element power (unless other is a scalar, in which
-        case return the matrix power.)
-        """
-        if isscalarlike(other):
-            new = self.copy()
-            new.data = new.data ** other
-            new.dtype = new.data.dtype
-            new.ftype = _transtabl[new.dtype.char]
-            return new
-        else:
-            ocs = other.tocsc()
-            if (ocs.shape != self.shape):
-                raise ValueError, "inconsistent shapes"
-            indptr, rowind, data = cscelmulcsc(self.shape[0], self.shape[1], \
-                                               self.indptr, self.rowind, \
-                                               self.data, ocs.indptr, \
-                                               ocs.rowind, ocs.data)
-            return csc_matrix((data, rowind, indptr), (self.shape[0], ocs.shape[1]))
+        _cs_matrix.__pow__(self, other, self.rowind, other.rowind, cscelmulcsc, csc_matrix)
 
     def transpose(self, copy=False):
         M, N = self.shape
@@ -769,10 +866,10 @@
         m, n = self.shape
         data = self.data
         if axis in (0, None):
-            indptr = self.indptr
             out = empty(n, dtype=self.dtype)
             # The first element in column j has index indptr[j], the last
             # indptr[j+1]
+            indptr = self.indptr
             for j in xrange(n):
                 out[j] = data[indptr[j] : indptr[j+1]].sum()
             if axis == 0:
@@ -788,70 +885,15 @@
                 out[rowind[k]] += data[k]
             # Output is a (m x 1) dense matrix
             return asmatrix(out).T
-            
-        
+                    
     def matvec(self, other):
-        if isdense(other):
-            # This check is too harsh -- it prevents a column vector from
-            # being created on-the-fly like with dense matrix objects.
-            #if len(other) != self.shape[1]:
-            #    raise ValueError, "dimension mismatch"
-            oth = numpy.ravel(other)
-            y = cscmux(self.shape[0], self.shape[1], \
-                       self.indptr, self.rowind, self.data, oth)
-            if isinstance(other, matrix):
-                y = asmatrix(y)
-                # If 'other' was an (nx1) column vector, transpose the result
-                # to obtain an (mx1) column vector.
-                if other.ndim == 2 and other.shape[1] == 1:
-                    y = y.T
-            return y
-        elif isspmatrix(other):
-            raise TypeError, "use matmat() for sparse * sparse"
-        else:
-            raise TypeError, "need a dense vector"
+        _cs_matrix.matvec(self, other, self.rowind, cscmux)
 
     def rmatvec(self, other, conjugate=True):
-        if isdense(other):
-            # This check is too harsh -- it prevents a column vector from
-            # being created on-the-fly like with dense matrix objects.
-            #if len(other) != self.shape[0]:
-            #    raise ValueError, "dimension mismatch"
-            oth = numpy.ravel(other)
-            if conjugate:
-                cd = conj(self.data)
-            else:
-                cd = self.data
-            y = csrmux(self.shape[1], self.shape[0], self.indptr, self.rowind, cd, oth)
-            if isinstance(other, matrix):
-                y = asmatrix(y)
-                # In the (unlikely) event that this matrix is 1x1 and 'other' was an
-                # (mx1) column vector, transpose the result.
-                if other.ndim == 2 and other.shape[1] == 1:
-                    y = y.T
-            return y
-        elif isspmatrix(other):
-            raise TypeError, "use matmat() for sparse * sparse"
-        else:
-            raise TypeError, "need a dense vector"
+        _cs_matrix.rmatvec(self, other, shape[1], shape[0], cscmux, conjugate=conjugate)
 
     def matmat(self, other):
-        if isspmatrix(other):
-            M, K1 = self.shape
-            K2, N = other.shape
-            if (K1 != K2):
-                raise ValueError, "shape mismatch error"
-            other = other.tocsc()
-            indptr, rowind, data = cscmucsc(M, N, self.indptr, self.rowind, \
-                                            self.data, other.indptr, \
-                                            other.rowind, other.data)
-            return csc_matrix((data, rowind, indptr), (M, N))      
-        elif isdense(other):
-            # This is SLOW!  We need a more efficient implementation
-            # of sparse * dense matrix multiplication!
-            return self.matmat(csc_matrix(other))
-        else:
-            raise TypeError, "need a dense or sparse matrix"
+        _cs_matrix.matmat(self, other, self.rowind, other.rowind, csc_matrix)
 
 
     def __getitem__(self, key):
@@ -958,24 +1000,15 @@
         col = searchsorted(self.indptr, ind+1)-1
         return (row, col)
 
-    def getdata(self, ind):
-        return self.data[ind]
-
     def tocsc(self, copy=False):
-        if copy:
-            new = self.copy()
-        else:
-            new = self
-        return new
+        return self.toself(copy)
 
     def tocoo(self):
-        rows, cols, data = csctocoo(self.shape[0], self.shape[1], \
-                                  self.indptr, self.rowind, self.data)
-        return coo_matrix((data, (rows, cols)), self.shape)
+        _cs_matrix.tocoo(self, csctocoo, self.rowind)
 
     def tocsr(self):
         indptr, colind, data = csctocsr(self.shape[0], self.shape[1], \
-                                      self.indptr, self.rowind, self.data)
+                                        self.indptr, self.rowind, self.data)
         return csr_matrix((data, colind, indptr), self.shape)
     
     def toarray(self):
@@ -1019,7 +1052,7 @@
         return new
 
 
-class csr_matrix(spmatrix):
+class csr_matrix(_cs_matrix):
     """ Compressed sparse row matrix
         This can be instantiated in several ways:
           - csr_matrix(d)
@@ -1041,7 +1074,7 @@
             standard CSR representation
     """
     def __init__(self, arg1, dims=None, nzmax=NZMAX, dtype=None, copy=False):
-        spmatrix.__init__(self)
+        _cs_matrix.__init__(self)
         if isdense(arg1):
             self.dtype = getdtype(dtype, arg1)
             # Convert the dense array or matrix arg1 to CSR format
@@ -1179,94 +1212,13 @@
 
         self.ftype = _transtabl[self.dtype.char]
 
-    def astype(self, t):
-        out = self.copy()
-        out.data = out.data.astype(t)
-        out.dtype = out.data.dtype
-        out.ftype = _transtabl[out.dtype.char]
-        return out
-
-    def __repr__(self):
-        format = self.getformat()
-        return "<%dx%d sparse matrix of type '%s'\n\twith %d stored "\
-               "elements (space for %d)\n\tin %s format>" % \
-               (self.shape + (self.dtype.type, self.getnnz(), self.nzmax, \
-                   _formats[format][1]))
     
     def __add__(self, other):
-        # First check if argument is a scalar
-        if isscalarlike(other):
-            # Now we would add this scalar to every element.
-            raise NotImplementedError, 'adding a scalar to a CSR matrix ' \
-                    'is not yet supported'
-        elif isspmatrix(other):
-            other = other.tocsr()
-            if (other.shape != self.shape):
-                raise ValueError, "inconsistent shapes"
-            indptr, colind, data = csrplcsr(self.shape[0], other.shape[1], \
-                                            self.indptr, self.colind,
-                                            self.data, other.indptr, \
-                                            other.colind, other.data)
-            return csr_matrix((data, colind, indptr), self.shape)
-        elif isdense(other):
-            # Convert this matrix to a dense matrix and add them.
-            return self.todense() + other
-        else:
-            raise TypeError, "unsupported type for sparse matrix addition"
+        _cs_matrix.__add__(self, other, self.colind, other.colind, csrplcsr, csr_matrix)
 
-    def __mul__(self, other):
-        """ Scalar, vector, or matrix multiplication
-        """
-        if isscalarlike(other):
-            new = self.copy()
-            new.data = other * new.data         # allows type conversion
-            new.dtype = new.data.dtype
-            new.ftype = _transtabl[new.dtype.char]
-            return new
-        else:
-            return self.dot(other)
 
-    def __rmul__(self, other):  # other * self
-        if isscalarlike(other):
-            new = self.copy()
-            new.data = other * new.data         # allows type conversion
-            new.dtype = new.data.dtype
-            new.ftype = _transtabl[new.dtype.char]
-            return new
-        else:
-            # Don't use asarray unless we have to
-            try:
-                tr = other.transpose()
-            except AttributeError:
-                tr = asarray(other).transpose()
-            return self.transpose().dot(tr).transpose()
-
-    def __neg__(self):
-        new = self.copy()
-        new.data *= -1
-        return new
-
     def __pow__(self, other):
-        """ Element-by-element power (unless other is a scalar, in which
-        case return the matrix power.)
-        """
-        if isscalarlike(other):
-            new = self.copy()
-            new.data = new.data ** other
-            new.dtype = new.data.dtype
-            new.ftype = _transtabl[new.dtype.char]
-            return new
-        elif isspmatrix(other):
-            other = other.tocsr()
-            if (other.shape != self.shape):
-                raise ValueError, "inconsistent shapes"
-            indptr, colind, data = csrelmulcsr(self.shape[0], other.shape[1], \
-                                               self.indptr, self.colind, \
-                                               self.data, other.indptr, \
-                                               other.colind, other.data)
-            return csr_matrix((data, colind, indptr), (self.shape[0], other.shape[1]))
-        else:
-            raise TypeError, "unsupported type for sparse matrix power"
+        _cs_matrix.__pow__(self, other, self.colind, other.colind, csrelmulcsr, csr_matrix)
 
     def transpose(self, copy=False):
         M, N = self.shape
@@ -1309,60 +1261,13 @@
             return asmatrix(out)
 
     def matvec(self, other):
-        if isdense(other):
-            # This check is too harsh -- it prevents a column vector from
-            # being created on-the-fly like dense matrix objects can.
-            #if len(other) != self.shape[1]:
-            #    raise ValueError, "dimension mismatch"
-            oth = numpy.ravel(other)            
-            y = csrmux(self.shape[0], self.shape[1], self.indptr, self.colind, self.data, oth)
-            if isinstance(other, matrix):
-                y = asmatrix(y)
-                # If 'other' was an (nx1) column vector, transpose the result
-                # to obtain an (mx1) column vector.
-                if other.ndim == 2 and other.shape[1] == 1:
-                    y = y.T
-            return y
-
+        _cs_matrix.matvec(self, other, self.colind, csrmux)
+        
     def rmatvec(self, other, conjugate=True):
-        # This check is too harsh -- it prevents a column vector from
-        # being created on-the-fly like dense matrix objects can.
-        #if len(other) != self.shape[0]:
-        #    raise ValueError, "dimension mismatch"
-        func = getattr(sparsetools, self.ftype+'cscmux')
-        if conjugate:
-            cd = conj(self.data)
-        else:
-            cd = self.data
+        _cs_matrix.rmatvec(self, other, shape[0], shape[1], csrmux, conjugate=conjugate)
 
-        oth = numpy.ravel(other)            
-        y = cscmux(self.shape[0], self.shape[1], self.indptr, self.rowind, cd, oth)
-        if isinstance(other, matrix):
-            y = asmatrix(y)
-            # In the (unlikely) event that this matrix is 1x1 and 'other' was an
-            # (mx1) column vector, transpose the result.
-            if other.ndim == 2 and other.shape[1] == 1:
-                y = y.T
-        return y
-
     def matmat(self, other):
-        if isspmatrix(other):
-            M, K1 = self.shape
-            K2, N = other.shape
-            if (K1 != K2):
-                raise ValueError, "shape mismatch error"
-            other = other.tocsr()
-            indptr, colind, data = csrmucsr(M, N, \
-                                            self.indptr, self.colind, \
-                                            self.data, other.indptr, \
-                                            other.colind, other.data)
-            return csr_matrix((data, colind, indptr), (self.shape[0], other.shape[1]))
-        elif isdense(other):
-            # This is SLOW!  We need a more efficient implementation
-            # of sparse * dense matrix multiplication!
-            return self.matmat(csc_matrix(other))
-        else:
-            raise TypeError, "need a dense or sparse matrix"
+        _cs_matrix.matmat(self, other, self.colind, other.colind, csr_matrix)
 
 
     def __getitem__(self, key):
@@ -1466,19 +1371,11 @@
         row = searchsorted(self.indptr, ind+1)-1
         return (row, col)
 
-    def getdata(self, ind):
-        return self.data[ind]
-
     def tocsr(self, copy=False):
-        if copy:
-            return self.copy()
-        else:
-            return self
+        return self.toself(copy)
 
     def tocoo(self):
-        rows, cols, data = csrtocoo(self.shape[0], self.shape[1], \
-                                    self.indptr, self.colind, self.data)
-        return coo_matrix((data, (rows, cols)), self.shape)
+        _cs_matrix.tocoo(self, csrtocoo, self.colind)
 
     def tocsc(self):
         indptr, rowind, data = csrtocsc(self.shape[0], self.shape[1], \
@@ -2253,10 +2150,7 @@
             return csr_matrix((data, colind, indptr), self.shape)
             
     def tocoo(self, copy=False):
-        if copy:
-            return self.copy()
-        else:
-            return self
+        return self.toself(copy)
 
 
 class lil_matrix(spmatrix):




More information about the Scipy-svn mailing list