Estoy tratando de calcular pocos (5-500) vectores propios correspondientes a los valores propios más pequeños de matrices dispersas cuadradas simétricas grandes (hasta 30000x30000) con menos del 0.1% de los valores que no son cero.
Actualmente estoy usando scipy.sparse.linalg.eigsh en modo shift-invert (sigma = 0.0), que descubrí a través de varias publicaciones sobre el tema es la solución preferida. Sin embargo, lleva hasta 1 hora resolver el problema en la mayoría de los casos. Por otro lado, la función es muy rápida, si solicito los valores propios más grandes (segundos secundarios en mi sistema), que se esperaba de la documentación.
Como estoy más familiarizado con Matlab desde el trabajo, intenté resolver el problema en Octave, que me dio el mismo resultado usando eigs (sigma = 0) en solo unos segundos (sub 10s). Dado que quiero hacer un barrido de parámetros del algoritmo, incluido el cálculo del vector propio, ese tipo de ganancia de tiempo también sería genial tener en Python.
Primero cambié los parámetros (especialmente la tolerancia), pero eso no cambió mucho en las escalas de tiempo.
Estoy usando Anaconda en Windows, pero intenté cambiar el LAPACK / BLAS utilizado por scipy (que fue un gran dolor) de mkl (Anaconda predeterminado) a OpenBlas (utilizado por Octave según la documentación), pero no pude ver un cambio en actuación.
No pude averiguar si había algo que cambiar sobre el ARPACK usado (y cómo).
Subí un caso de prueba para el siguiente código a la siguiente carpeta de dropbox: https://www.dropbox.com/sh/l6aa6izufzyzqr3/AABqij95hZOvRpnnjRaETQmka?dl=0
En 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)
En octava:
M=dlmread('M.txt');
M=spconvert(M);
[evecs,evals] = eigs(M,6,0);
Cualquier ayuda es solicitada!
Algunas opciones adicionales que probé en función de los comentarios y sugerencias:
Octave:
eigs(M,6,0)
y eigs(M,6,'sm')
dame el mismo resultado:
[1.8725e-05 1.0189e-05 7.5622e-06 7.5420e-07 -1.2239e-18 -2.5674e-16]
mientras eigs(M,6,'sa',struct('tol',2))
converge a
[1.0423 2.7604 6.1548 11.1310 18.0207 25.3933]
mucho más rápido, pero solo si los valores de tolerancia son superiores a 2, de lo contrario no converge en absoluto y los valores son muy diferentes.
Python:
eigsh(M,k=6,which='SA')
y eigsh(M,k=6,which='SM')
ambos no convergen (error ARPACK al no alcanzarse la convergencia). Solo eigsh(M,k=6,sigma=0.0)
da algunos valores propios (después de casi una hora), que son diferentes de octava para los más pequeños (incluso se encuentra 1 valor pequeño adicional):
[3.82923317e-17 3.32269886e-16 2.78039665e-10 7.54202273e-07 7.56251500e-06 1.01893934e-05]
Si la tolerancia es lo suficientemente alta, también obtengo resultados eigsh(M,k=6,which='SA',tol='1')
, que se acercan a los otros valores obtenidos
[4.28732218e-14 7.54194948e-07 7.56220703e-06 1.01889544e-05, 1.87247350e-05 2.02652719e-05]
de nuevo con un número diferente de pequeños valores propios. El tiempo de cálculo aún es de casi 30 minutos. Si bien los diferentes valores muy pequeños pueden ser comprensibles, ya que pueden representar múltiplos de 0, la multiplicidad diferente me desconcierta.
Además, parece haber algunas diferencias fundamentales en SciPy y Octave, que todavía no puedo entender.
fuente
Respuestas:
Una conjetura y algunos comentarios, ya que no tengo Matlab / Octave:
Para encontrar pequeños valores propios de matrices simétricas con valores propios> = 0, como el suyo, lo siguiente es mucho más rápido que shift-invert:
eigsh( Aflip )
para grandes pares propios es más rápido que shift-invert para pequeños, porqueA * x
es más rápido que losolve()
que debe hacer shift-invert. Matlab / Octave podría hacer estoAflip
automáticamente, después de una prueba rápida para positivo definitivo con Cholesky.¿Puedes correr
eigsh( Aflip )
en Matlab / Octave?Otros factores que pueden afectar la precisión / velocidad:
El valor predeterminado de Arpack para el vector de inicio
v0
es un vector aleatorio. Yo usov0 = np.ones(n)
, lo que puede ser terrible para algunosA
pero es reproducible :)Esta
A
matriz es casi exactamente sigular,A * ones
~ 0.Multinúcleo: scipy-arpack con openblas / Lapack utiliza ~ 3.9 de los 4 núcleos en mi iMac; Cómo utiliza Matlab / Octave todos los núcleos?
Aquí están los valores propios de scipy-Arpack para varios
k
ytol
, extraídos de archivos de registro bajo gist.github :¿Son Matlab / Octave más o menos iguales? Si no, todas las apuestas están apagadas: primero verifique la corrección, luego la velocidad.
¿Por qué los valores propios se tambalean tanto? Diminuto <0 para una matriz supuestamente no negativa definida es un signo de error de redondeo , pero el truco habitual de un pequeño cambio,
A += n * eps * sparse.eye(n)
no ayuda.¿De dónde
A
viene esto , qué área problemática? ¿Se puede generar similarA
, más pequeño o más escaso?Espero que esto ayude.
fuente
tol
es complicado para pequeños valores propios: haga una nueva pregunta sobre eso, si lo desea, hágamelo saber.Sé que esto es viejo ahora, pero tuve el mismo problema. ¿Revisaste aquí ( https://docs.scipy.org/doc/scipy/reference/tutorial/arpack.html )?
Parece que cuando establece sigma en un número bajo (0), debe establecer which = 'LM', aunque desee valores bajos. Esto se debe a que la configuración de sigma transforma los valores que desea (bajo en este caso) para que parezcan altos, por lo que aún puede aprovechar los métodos 'LM', que son mucho más rápidos para obtener lo que desea (los valores propios bajos )
fuente
Quiero decir primero que no tengo idea de por qué los resultados que usted y @Bill informaron son como son. Simplemente me pregunto si
eigs(M,6,0)
en Octave correspondek=6 & sigma=0
, o tal vez es algo más?Con scipy, si no proporciono sigma, puedo obtener un resultado en un tiempo decente de esta manera.
Sin embargo, no estoy completamente seguro de si esto tiene sentido.
La única forma en que encontré usar sigma y obtener un resultado en un tiempo decente es proporcionar M como LinearOperator. No estoy muy familiarizado con esto, pero por lo que entendí, mi implementación representa una matriz de identidad, que es lo que M debería ser si no se especifica en la llamada. La razón de esto es que, en lugar de realizar una resolución directa (descomposición LU), scipy utilizará un solucionador iterativo, que es potencialmente más adecuado. En comparación, si proporciona
M = np.identity(a.shape[0])
, que debería ser exactamente el mismo, entonces eigsh tarda una eternidad en producir un resultado. Tenga en cuenta que este enfoque no funciona sisigma=0
se proporciona. Pero no estoy seguro de sisigma=0
es realmente tan útil.De nuevo, no tengo idea si es correcto pero definitivamente diferente de antes. Sería genial tener el aporte de alguien desde scipy.
fuente