Source code for femorph_solver.solvers.eigen

"""Pluggable generalized-eigenvalue solvers.

All backends solve ``K φ = λ M φ`` for the ``n_modes`` smallest-λ
eigenpairs.  The default backend (``"arpack"``) uses
:func:`scipy.sparse.linalg.eigsh` in shift-invert mode with σ = 0 —
identical to femorph-solver's behaviour up to this release.  Alternative backends
let callers trade accuracy / convergence / memory against speed and
dependency footprint.

Usage::

    from femorph_solver.solvers.eigen import get_eigen_solver

    Solver = get_eigen_solver("arpack")
    w, v = Solver.solve(K, M, n_modes=10, linear_solver="cholmod")
"""

from __future__ import annotations

from ._arpack import ArpackSolver
from ._base import EigenSolverBase
from ._lapack_dense import LapackDenseSolver
from ._lobpcg import LobpcgSolver
from ._primme import PrimmeSolver

_REGISTRY: dict[str, type[EigenSolverBase]] = {
    cls.name: cls for cls in (ArpackSolver, LobpcgSolver, PrimmeSolver, LapackDenseSolver)
}


[docs] def get_eigen_solver(name: str) -> type[EigenSolverBase]: """Look up a registered eigen-solver class by name.""" try: cls = _REGISTRY[name] except KeyError as exc: known = ", ".join(sorted(_REGISTRY)) raise KeyError(f"unknown eigen solver {name!r}; registered: {known}") from exc if not cls.available(): from ..linear._base import SolverUnavailableError raise SolverUnavailableError( f"eigen solver {name!r} is registered but unavailable — {cls.install_hint}" ) return cls
[docs] def list_eigen_solvers() -> list[dict[str, object]]: """Return a list of ``{name, available, kind, spd_only, install_hint}`` rows.""" out: list[dict[str, object]] = [] for name, cls in _REGISTRY.items(): out.append( { "name": name, "available": cls.available(), "kind": cls.kind, "spd_only": cls.spd_only, "install_hint": cls.install_hint, } ) return out
__all__ = [ "ArpackSolver", "EigenSolverBase", "LapackDenseSolver", "LobpcgSolver", "PrimmeSolver", "get_eigen_solver", "list_eigen_solvers", ]