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",
]