[Scipy-svn] r6245 - in trunk/scipy/sparse/linalg/eigen/arpack: . tests

scipy-svn at scipy.org scipy-svn at scipy.org
Mon Feb 22 04:02:18 EST 2010


Author: cdavid
Date: 2010-02-22 03:02:18 -0600 (Mon, 22 Feb 2010)
New Revision: 6245

Modified:
   trunk/scipy/sparse/linalg/eigen/arpack/arpack.py
   trunk/scipy/sparse/linalg/eigen/arpack/tests/test_arpack.py
Log:
ENH: implement naive SVD for sparse real matrices.

Modified: trunk/scipy/sparse/linalg/eigen/arpack/arpack.py
===================================================================
--- trunk/scipy/sparse/linalg/eigen/arpack/arpack.py	2010-02-21 21:04:04 UTC (rev 6244)
+++ trunk/scipy/sparse/linalg/eigen/arpack/arpack.py	2010-02-22 09:02:18 UTC (rev 6245)
@@ -39,13 +39,14 @@
 
 __docformat__ = "restructuredtext en"
 
-__all___=['eigen','eigen_symmetric']
+__all___=['eigen','eigen_symmetric', 'svd']
 
 import warnings
 
 import _arpack
 import numpy as np
 from scipy.sparse.linalg.interface import aslinearoperator
+from scipy.sparse import csc_matrix, csr_matrix
 
 _type_conv = {'f':'s', 'd':'d', 'F':'c', 'D':'z'}
 _ndigits = {'f':5, 'd':12, 'F':5, 'D':12}
@@ -488,3 +489,58 @@
     if return_eigenvectors:
         return d,z
     return d
+
+def svd(A, k=6):
+    """Compute a few singular values/vectors for a sparse matrix using ARPACK.
+
+    Parameters
+    ----------
+    A: sparse matrix
+        Array to compute the SVD on.
+    k: int
+        Number of singular values and vectors to compute.
+
+    Note
+    ----
+    This is a naive implementation using the symmetric eigensolver on A.T * A
+    or A * A.T, depending on which one is more efficient.
+
+    Complex support is not implemented yet
+    """
+    # TODO: implement complex support once ARPACK-based eigen_hermitian is
+    # available
+    n, m = A.shape
+
+    if np.iscomplexobj(A):
+        raise NotImplementedError("Complex support for sparse SVD not " \
+                                  "implemented yet")
+        op = lambda x: x.T.conjugate()
+    else:
+        op = lambda x: x.T
+
+    def _left(x):
+        x = csc_matrix(x)
+        m = op(x) * x
+
+        eigvals, eigvec = eigen_symmetric(m, k)
+        s = np.sqrt(eigvals)
+
+        v = eigvec
+        u = (x * v) / s
+        return u, s, op(v)
+
+    def _right(x):
+        x = csr_matrix(x)
+        m = x * op(x)
+
+        eigvals, eigvec = eigen_symmetric(m, k)
+        s = np.sqrt(eigvals)
+
+        u = eigvec
+        vh = (op(u) * x) / s[:, None]
+        return u, s, vh
+
+    if n > m:
+        return _left(A)
+    else:
+        return _right(A)

Modified: trunk/scipy/sparse/linalg/eigen/arpack/tests/test_arpack.py
===================================================================
--- trunk/scipy/sparse/linalg/eigen/arpack/tests/test_arpack.py	2010-02-21 21:04:04 UTC (rev 6244)
+++ trunk/scipy/sparse/linalg/eigen/arpack/tests/test_arpack.py	2010-02-22 09:02:18 UTC (rev 6245)
@@ -4,12 +4,15 @@
   python tests/test_arpack.py [-l<int>] [-v<int>]
 
 """
+import numpy as np
 
 from numpy.testing import *
 
 from numpy import array, finfo, argsort, dot, round, conj, random
-from scipy.sparse.linalg.eigen.arpack import eigen_symmetric, eigen
+from scipy.sparse.linalg.eigen.arpack import eigen_symmetric, eigen, svd
 
+from scipy.linalg import svd as dsvd
+
 def assert_almost_equal_cc(actual,desired,decimal=7,err_msg='',verbose=True):
     # almost equal or complex conjugates almost equal
     try:
@@ -263,5 +266,51 @@
                 for m in self.nonsymmetric:
                     self.eval_evec(m,typ,k,which)
 
+def sorted_svd(m, k):
+    """Compute svd of a dense matrix m, and return singular vectors/values
+    sorted."""
+    u, s, vh = dsvd(m)
+    ii = np.argsort(s)[-k:]
+
+    return u[:, ii], s[ii], vh[ii]
+
+def svd_estimate(u, s, vh):
+    return np.dot(u, np.dot(np.diag(s), vh))
+
+class TestSparseSvd(TestCase):
+    def test_simple_real(self):
+        x = np.array([[1, 2, 3],
+                      [3, 4, 3],
+                      [1, 0, 2],
+                      [0, 0, 1]], np.float)
+
+        for m in [x.T, x]:
+            for k in range(1, 3):
+                u, s, vh = sorted_svd(m, k)
+                su, ss, svh = svd(m, k)
+
+                m_hat = svd_estimate(u, s, vh)
+                sm_hat = svd_estimate(su, ss, svh)
+
+                assert_array_almost_equal_nulp(m_hat, sm_hat, nulp=1000)
+
+    @dec.knownfailureif(True, "Complex sparse SVD not implemented (depends on "
+                              "Hermitian support in eigen_symmetric")
+    def test_simple_complex(self):
+        x = np.array([[1, 2, 3],
+                      [3, 4, 3],
+                      [1+1j, 0, 2],
+                      [0, 0, 1]], np.complex)
+
+        for m in [x, x.T.conjugate()]:
+            for k in range(1, 3):
+                u, s, vh = sorted_svd(m, k)
+                su, ss, svh = svd(m, k)
+
+                m_hat = svd_estimate(u, s, vh)
+                sm_hat = svd_estimate(su, ss, svh)
+
+                assert_array_almost_equal_nulp(m_hat, sm_hat, nulp=1000)
+
 if __name__ == "__main__":
     run_module_suite()




More information about the Scipy-svn mailing list