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