Estou tentando calcular alguns (5-500) autovetores correspondentes aos menores autovalores de matrizes esparsas quadradas simétricas grandes (até 30000x30000) com menos de 0,1% dos valores diferentes de zero.
Atualmente, estou usando scipy.sparse.linalg.eigsh no modo shift-invert (sigma = 0,0), que eu descobri através de várias postagens sobre o tópico como a solução preferida. No entanto, leva até 1 hora para resolver o problema na maioria dos casos. Por outro lado, a função é muito rápida, se eu pedir os maiores valores próprios (sub-segundos no meu sistema), o que era esperado na documentação.
Como eu estou mais familiarizado com o Matlab do trabalho, tentei resolver o problema no Octave, o que me deu o mesmo resultado usando eigs (sigma = 0) em apenas alguns segundos (sub 10s). Como eu quero fazer uma varredura de parâmetro do algoritmo, incluindo o cálculo do vetor próprio, esse tipo de ganho de tempo também seria ótimo em python.
Mudei primeiro os parâmetros (especialmente a tolerância), mas isso não mudou muito nas escalas de tempo.
Estou usando o Anaconda no Windows, mas tentei alternar o LAPACK / BLAS usado pelo scipy (que era uma grande dor) de mkl (padrão Anaconda) para OpenBlas (usado pelo Octave de acordo com a documentação), mas não consegui ver uma alteração no desempenho.
Não consegui descobrir se havia algo a mudar sobre o ARPACK usado (e como)?
Fiz upload de um caso de teste para o código abaixo na seguinte pasta dropbox: https://www.dropbox.com/sh/l6aa6izufzyzqr3/AABqij95hZOvRpnnjRaETQmka?dl=0
Em Python
import numpy as np
from scipy.sparse import csr_matrix, csc_matrix, linalg, load_npz
M = load_npz('M.npz')
evals, evecs = linalg.eigsh(M,k=6,sigma=0.0)
Na oitava:
M=dlmread('M.txt');
M=spconvert(M);
[evecs,evals] = eigs(M,6,0);
Qualquer ajuda é apreciada!
Tentei algumas opções adicionais com base nos comentários e sugestões:
Oitava:
eigs(M,6,0)
e eigs(M,6,'sm')
me dê o mesmo resultado:
[1.8725e-05 1.0189e-05 7.5622e-06 7.5420e-07 -1.2239e-18 -2.5674e-16]
enquanto eigs(M,6,'sa',struct('tol',2))
converge para
[1.0423 2.7604 6.1548 11.1310 18.0207 25.3933]
muito mais rápido, mas somente se os valores de tolerância estiverem acima de 2, caso contrário, eles não convergirão e os valores serão fortemente diferentes.
Python:
eigsh(M,k=6,which='SA')
e eigsh(M,k=6,which='SM')
ambos não convergem (erro ARPACK em nenhuma convergência alcançada). eigsh(M,k=6,sigma=0.0)
Fornece apenas alguns autovalores (após quase uma hora), que são diferentes da oitava para os menores (até 1 pequeno valor adicional é encontrado):
[3.82923317e-17 3.32269886e-16 2.78039665e-10 7.54202273e-07 7.56251500e-06 1.01893934e-05]
Se a tolerância é alta o suficiente, também obtenho resultados eigsh(M,k=6,which='SA',tol='1')
, que se aproximam dos outros valores obtidos
[4.28732218e-14 7.54194948e-07 7.56220703e-06 1.01889544e-05, 1.87247350e-05 2.02652719e-05]
novamente com um número diferente de pequenos autovalores. O tempo de computação ainda é quase 30min. Embora os diferentes valores muito pequenos possam ser compreensíveis, uma vez que podem representar múltiplos de 0, a multiplicidade diferente me confunde.
Além disso, parece haver algumas diferenças fundamentais em SciPy e Octave, que ainda não consigo descobrir.
fonte
Respostas:
Uma conjectura e alguns comentários, já que não tenho Matlab / Octave:
Para encontrar pequenos autovalores de matrizes simétricas com autovalores> = 0, como o seu, o seguinte é muito mais rápido que o shift-invert:
eigsh( Aflip )
para grandes pares próprios é mais rápido que o inverso por turno para pequenos, porqueA * x
é mais rápido do que osolve()
inverso por turno deve fazer. É possível que o Matlab / Octave possa fazer issoAflip
automaticamente, após um rápido teste de definição positiva com Cholesky.Você pode executar
eigsh( Aflip )
no Matlab / Octave?Outros fatores que podem afetar a precisão / velocidade:
O padrão do Arpack para o vetor inicial
v0
é um vetor aleatório. Eu usov0 = np.ones(n)
, o que pode ser terrível para alguns,A
mas é reproduzível :)Essa
A
matriz é quase exatamente sigular,A * ones
~ 0.Multicore: o scipy-arpack com openblas / Lapack usa ~ 3.9 dos 4 núcleos no meu iMac; o Matlab / Octave usa todos os núcleos?
Aqui estão os autovalores do scipy-Arpack para vários
k
etol
, recebidos de arquivos de log em gist.github :Matlab / Octave são a mesma coisa? Caso contrário, todas as apostas estão fora - primeiro verifique a exatidão e depois a velocidade.
Por que os autovalores oscilam tanto? Minúsculo <0 para uma matriz definida supostamente não negativa é um sinal de erro de arredondamento , mas o truque usual de uma pequena mudança,
A += n * eps * sparse.eye(n)
não ajuda.De onde isso
A
vem, qual área problemática? Você pode gerar similarA
, menor ou mais escasso?Espero que isto ajude.
fonte
tol
é complicado para pequenos autovalores - faça uma nova pergunta sobre isso, se você quiser, me avise.Sei que agora é antigo, mas tive o mesmo problema. Você revisou aqui ( https://docs.scipy.org/doc/scipy/reference/tutorial/arpack.html )?
Parece que quando você define sigma para um número baixo (0), deve definir qual = 'LM', mesmo que você queira valores baixos. Isso ocorre porque a configuração sigma transforma os valores que você deseja (baixo neste caso) para parecer alto e, portanto, ainda é possível tirar proveito dos métodos 'LM', que são muito mais rápidos para obter o que você deseja (os baixos valores próprios )
fonte
Quero dizer primeiro que não tenho idéia de por que os resultados que você e a @Bill relataram são do jeito que são. Eu simplesmente me pergunto se
eigs(M,6,0)
em Octave corresponde ak=6 & sigma=0
, ou talvez seja outra coisa?Com o scipy, se eu não fornecer sigma, posso obter um resultado em um tempo decente dessa maneira.
Não tenho muita certeza se isso faz sentido.
A única maneira que encontrei de usar o sigma e obter um resultado em um tempo decente é fornecer M como um LinearOperator. Não estou muito familiarizado com isso, mas pelo que entendi minha implementação representa uma matriz de identidade, que é o que M deveria ser se não for especificado na chamada. A razão para isso é que, em vez de executar uma resolução direta (decomposição da LU), o scipy usará um solucionador iterativo, potencialmente mais adequado. Como comparação, se você fornecer o
M = np.identity(a.shape[0])
que deve ser exatamente o mesmo, o eigsh levará uma eternidade para produzir um resultado. Observe que essa abordagem não funciona sesigma=0
for fornecida. Mas não tenho certeza se issosigma=0
é realmente útil?Novamente, não faço ideia se está correto, mas definitivamente diferente de antes. Seria ótimo ter a contribuição de alguém do scipy.
fonte