Source code for femorph_solver.solvers.eigen._primme

"""PRIMME via ``primme.eigsh`` — a state-of-the-art block-Davidson solver.

Often beats ARPACK on large SPD generalized eigenproblems because of its
restart + soft-locking scheme.  Pip-installable and self-contained (no
extra system libs).

References
----------
- Stathopoulos, A. & McCombs, J. R.  "PRIMME: PReconditioned
  Iterative MultiMethod Eigensolver — Methods and software
  description."  ACM Trans. Math. Softw. 37 (2), 21:1-21:30 (2010).
  [the shipped PRIMME library: block-JD + GD+k restart + the
  soft-locking convergence machinery]
- Stathopoulos, A.  "Nearly Optimal Preconditioned Methods for
  Hermitian Eigenproblems Under Limited Memory.  Part I: Seeking
  One Eigenvalue."  SIAM J. Sci. Comput. 29 (2), 481-514 (2007).
  [the GD+k restart scheme inside PRIMME]
- Davidson, E. R.  "The Iterative Calculation of a Few of the
  Lowest Eigenvalues and Corresponding Eigenvectors of Large
  Real-Symmetric Matrices."  J. Comp. Phys. 17 (1), 87-94 (1975).
  [the original Davidson method PRIMME descends from]
"""

from __future__ import annotations

from typing import ClassVar

import numpy as np
import scipy.sparse as sp

from ._base import EigenSolverBase

try:
    import primme

    _HAVE = True
except ImportError:
    _HAVE = False


[docs] class PrimmeSolver(EigenSolverBase): """primme.eigsh for generalized SPD eigenproblems.""" name: ClassVar[str] = "primme" kind: ClassVar[str] = "sparse-davidson" spd_only: ClassVar[bool] = True install_hint: ClassVar[str] = "`pip install primme`" @staticmethod def available() -> bool: return _HAVE
[docs] @staticmethod def solve( K: sp.spmatrix | sp.sparray, M: sp.spmatrix | sp.sparray, n_modes: int, *, sigma: float = 0.0, linear_solver: str = "auto", tol: float = 1e-10, ) -> tuple[np.ndarray, np.ndarray]: w, v = primme.eigsh( K.tocsr(), M=M.tocsr(), k=n_modes, which="SM", sigma=sigma, tol=tol, ) order = np.argsort(w) return w[order], v[:, order]