The SciPy tutorial explicitly states
Note that ARPACK is generally better at finding extremal eigenvalues: that is, eigenvalues with large magnitudes. In particular, using which = 'SM'
may lead to slow execution time and/or anomalous results. A better approach is to use shift-invert mode.
and goes on to describe that. Basically, if $\lambda$ is the smallest by magnitude eigenvalue of $A$, then
$\nu = \frac{1}{\lambda}$
is the largest by magnitude eigenvalue of $A^{-1}$. (With the same trick, you can get the eigenvalue closest to a given $\sigma$ by looking at the largest for $(A-\sigma I)^{-1}$.) Hence, to compute, say, the three smallest by magnitude eigenvalues, you can instead use
eigenval, eigenvec = eigsh(A, 3, sigma=0, which='LM')
If you already know that $A$ has a zero eigenvalue and is therefore not invertible (and you have some idea where the next closest are), you can instead use a nonzero shift, e.g., sigma=0.01
to shift your matrix away from singular. If sigma
is small enough, that should still get you the ones closest to zero.
(Also, as noted in my comment, make sure you have a proper optimized BLAS such as OpenBLAS installed -- for sparse eigenvalues, SciPy calls ARPACK (written in Fortran) which in turn makes heavy use of BLAS.)
EDIT: If it's the smallest algebraic eigenvalue you're after, you can use this neat trick: If you have an estimate $\sigma$ of the largest eigenvalue (say, by computing it using which='LM'
), you can use that to shift all eigenvalues to be negative -- so that the smallest algebraic eigenvalues are also the largest magnitude ones. Then simply use which='LM'
on $A-\sigma I$ and shift back.