大型稀疏矩阵的所有特征向量均为零

时间:2019-12-29 16:53:26

标签: python numpy scipy eigenvector arpack

我有一个50,000 x 50,000的密集矩阵或更大的矩阵。如果我使用numpy或scipy-package,则所有本征向量的条目均为0。如果我使用scipy.sparse仅计算1000-8000个本征向量,则将得到正确的本征向量。但我都需要。

这可能是内存问题吗?或出现此问题的原因是什么? 我可以使用LAPACK或ARPACK来计算正确的特征向量吗?

请注意,我的矩阵是networkx图的表示,因此是稀疏矩阵。我使用numpy.linalg将它们转换为密集矩阵,否则我将使用scipy.sparse.linalg

1 个答案:

答案 0 :(得分:2)

numpy.linalg.eig()scipy.linalg.eig()有关的问题可能是与矩阵大小有关的内存错误:50000x50000双精度密集矩阵占用18Go的内存。

numpy.linalg.eig()scipy.linalg.eig()依赖于LAPACK的例程DGEEV()。 LAPACK DGEEV()DGEEVX()利用QR algorithm来计算密集矩阵的所有特征值和特征向量。首先,使用dgehrd()将矩阵简化为上Hessenberg形式,然后在dhseqr()中执行QR迭代。在DGEEVX()中,首先对矩阵进行平衡和缩放。

另一方面,scipy.sparse.linalg.eigs()scipy.sparse.linalg.eigsh()依靠ARPACK函数来实现稀疏矩阵上的Implicitly Restarted Arnoldi MethodImplicitly Restarted Lanczos Method。两者都是幂方法的改进,并且在以改进的精度计算最大特征值/特征向量方面非常有效。如果Ax = b可以被快速求解,那么这些迭代方法对于找到最小的特征值/特征向量或接近给定值的特征值/特征向量也非常有效。

Lloyd N. Trefethen and David Bau, NUMERICAL LINEAR ALGEBRA, Lecture 33. The Arnoldi Iteration中说明了这两种方法之间的区别。

...,而Arnoldi迭代基于矩阵的QR分解(33.7),其列为b,A b,...,A ^ {n-1} b,同时迭代和QR算法基于矩阵的QR分解(28.16),其列为A ^ n e_1 ...,A ^ n e_n。

从实际的角度来看,总是将Arnoldi迭代用于检索有限数量的特征向量,这些特征向量显着低于矩阵的大小。但是,通过使用{strong> scipy.sparse.linalg.eigs()scipy.sparse.linalg.eigsh()的自变量sigma,可以找到sigma附近的内部特征值和特征向量。因此,可以使用不同的sigma多次调用scipy.sparse.linalg.eigsh()。如果特征值均具有有限的多重性,则可以恢复所有特征向量。应通过分离特征值并跟踪其多重性来避免电位重复。

使用sigma的基本调用写道:

sparsevalue, sparsevector = sp.sparse.linalg.eigsh(M.copy(), k = kkeig,sigma=sig, which='LM', mode='normal')

如果矩阵是Hermitian半 这是一个基于您先前删除的帖子的示例代码,该代码计算一个正半定稀疏矩阵的所有特征值。由于特征值都是实数和正值,因此sigma会逐渐增加以找到所有特征值:

import numpy as np
import networkx as nx
import scipy as sp




def tolist(sparsevalue, keeplast=False):
    listofeigval=[]
    listofmultiplicity=[]

    curreig=sparsevalue[0]-1.1*np.abs(sparsevalue[0])
    for i in range(len(sparsevalue)):
           #print curreig, sparsevalue[i], len(listofeigval)
           if np.abs(curreig-sparsevalue[i])<1e-11*(np.abs(curreig)+np.abs(sparsevalue[i])):
                listofmultiplicity[-1]=listofmultiplicity[-1]+1
           else :
                curreig=sparsevalue[i]
                listofeigval.append(curreig)
                listofmultiplicity.append(1)

    if keeplast==False:
        #the last one is not sure regarding multiplicity:
        listofeigval.pop()
        listofmultiplicity.pop()

    return listofeigval,listofmultiplicity

def test():
    N_1 = 2000
    R_1 = 0.1
    k = 0
    iterations = 1
    while k < iterations:
      G = nx.random_geometric_graph(N_1, R_1)
      if nx.is_connected(G) == True:
          print 'got connected network'
          k = k+1
          M=nx.laplacian_matrix(G)      #M is here a sparse matrix
          M = M.astype(float)
          #M[0,0]=M[0,0]+1.  # this makes the laplacian_matrix positive definite.
          #sparsevalue, sparsevector = sp.sparse.linalg.eigsh(M, k = N_1-2)
          kkeig=400
          sparsevalue, sparsevector = sp.sparse.linalg.eigsh(M.copy(), k = kkeig, which='SM')
          print sparsevalue

          listofeigval=[]
          listofmultiplicity=[]
          listofeigval,listofmultiplicity=tolist(sparsevalue)
          print len(listofeigval), len(listofmultiplicity)

          nbeigfound=0
          for mul in listofmultiplicity:
              nbeigfound=nbeigfound+mul
          keepgoing=True
          while( nbeigfound<N_1):
               print '(',nbeigfound,'/',N_1,')  is ', listofeigval[-1]
               meanspacing=0.
               meanspacingnb=0.

               for i in range(10):
                     meanspacingnb=meanspacingnb+listofmultiplicity[len(listofeigval)-i-1]
               meanspacing=(listofeigval[-1]-listofeigval[len(listofeigval)-10])/meanspacingnb
               sig=listofeigval[-1]+0.1*kkeig*meanspacing

               keeplast=False
               if nbeigfound<N_1-0.5*kkeig:
                   sparsevalue, sparsevector = sp.sparse.linalg.eigsh(M.copy(), k = kkeig,sigma=sig, which='LM', mode='normal')
               else:
                   keeplast=True
                   sparsevalue, sparsevector = sp.sparse.linalg.eigsh(M.copy(), k = kkeig, which='LM')
               listofneweigval,listofnewmultiplicity=tolist(sparsevalue,keeplast)
               #need to retreive the starting point
               index=len(listofeigval)-2*kkeig/10
               if listofneweigval[1]>listofeigval[index]:
                   while(np.abs(listofneweigval[1]-listofeigval[index])>1e-10*(np.abs(listofneweigval[1])+np.abs(listofeigval[index]))):
                       index=index+1
               else:
                   while(np.abs(listofneweigval[1]-listofeigval[index])>1e-10*(np.abs(listofneweigval[1])+np.abs(listofeigval[index]))):
                       index=index-1
               #The first matching eigenvalue is found.
               #zipping the list and checking if it works.
               i=1
               while index<len(listofeigval) and i<len(listofneweigval) :
                    if (np.abs(listofneweigval[i]-listofeigval[index])>1e-10*(np.abs(listofneweigval[i])+np.abs(listofeigval[index]))):
                         print 'failed after ', index, ' different eigenvalues', ': wrong eigenvalue'
                         return listofeigval[0:index], listofmultiplicity[0:index], 1
                    if listofmultiplicity[index] != listofnewmultiplicity[i] :
                         print 'failed after ', index, ' different eigenvalues', ': wrong multiplicity'
                         return listofeigval[0:index], listofmultiplicity[0:index], 2
                    index=index+1
                    i=i+1
               #adding the remaining eigenvalues.
               while i<len(listofneweigval) :
                    listofeigval.append(listofneweigval[i])
                    listofmultiplicity.append(listofnewmultiplicity[i])
                    nbeigfound=nbeigfound+listofnewmultiplicity[i]
                    i=i+1
          print 'number of eigenvalues: ', nbeigfound
          nbl=0
          for i in range(len(listofeigval)):
               print 'eigenvalue ',i,' (',nbl,'/',N_1,')  is %14.8f'% listofeigval[i], ' with multiplicity', listofmultiplicity[i]
               nbl=nbl+listofmultiplicity[i]

          return listofeigval, listofmultiplicity, 0


          #sig=39.1
          #sparsevalue, sparsevector = sp.sparse.linalg.eigsh(M.copy(), k = 100,sigma=sig, which='LM', mode='normal')
          #print sparsevalue

test()

对于具有真实正负特征值的埃尔米特矩阵,将探索sigma的正负值。如果矩阵不是Hermitian,则特征值可能不是实数,并且将选择复平面上的sigma值。首先搜索A的最大特征值的大小会将区域限制为磁盘。

提出的方法非常慢,可能并不总是有效。使用1Go内存,它一次可用于20000x20000矩阵。