Source code for femorph_solver.solvers.eigen._lapack_dense

"""Dense LAPACK fallback via ``scipy.linalg.eigh``.

For very small problems (N ≲ 500) a dense generalized eigenproblem is
faster than any sparse solver and numerically bullet-proof.  Serves as
our reference path in tests.

References
----------
- Anderson, E. et al.  *LAPACK Users' Guide*, 3rd ed., SIAM (1999),
  Chapter 2.  [generalized symmetric-definite eigenproblem — the
  ``DSYGVD`` and ``DSYGVX`` drivers ``scipy.linalg.eigh`` calls]
- Parlett, B. N.  *The Symmetric Eigenvalue Problem*, SIAM
  Classics in Applied Mathematics (1998).  [the bisection +
  Rayleigh-quotient + divide-and-conquer algorithms underlying
  LAPACK's symmetric routines]
- Golub, G. H. & Van Loan, C. F.  *Matrix Computations*, 4th ed.,
  JHU Press (2013), §8.  [reference treatment of the symmetric
  eigenproblem used for the dense path]
"""

from __future__ import annotations

from typing import ClassVar

import numpy as np
import scipy.linalg as la
import scipy.sparse as sp

from ._base import EigenSolverBase


[docs] class LapackDenseSolver(EigenSolverBase): """Dense LAPACK eigh — correct at any size, but O(N^3).""" name: ClassVar[str] = "dense" kind: ClassVar[str] = "dense" spd_only: ClassVar[bool] = True install_hint: ClassVar[str] = "bundled with SciPy — no install needed"
[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 = 0.0, ) -> tuple[np.ndarray, np.ndarray]: w, v = la.eigh(np.asarray(K.toarray()), np.asarray(M.toarray())) w = w[:n_modes] v = v[:, :n_modes] return w, v