CPU sparse linear and eigen solver constructors.
This module follows the naming and keyword conventions of
scipy.sparse.linalg where possible. The main difference is that constructors return
OpenSeesPy-compatible solver objects; OpenSeesPy supplies A, b, K, and M at solve
time.
Solving Linear Problems
Direct methods:
| Constructor |
scipy analogue |
Description |
spsolve |
scipy.sparse.linalg.spsolve / splu |
Sparse direct solver using SuperLU |
umfpack |
scikits.umfpack |
Sparse direct solver using UMFPACK |
Iterative methods:
| Constructor |
scipy analogue |
Description |
cg |
scipy.sparse.linalg.cg |
Conjugate Gradient |
gmres |
scipy.sparse.linalg.gmres |
Generalized Minimal Residual |
Eigenvalue Problems
| Constructor |
scipy analogue |
Description |
eigsh |
scipy.sparse.linalg.eigsh |
ARPACK-based generalized symmetric eigen solve |
lobpcg |
scipy.sparse.linalg.lobpcg |
Locally Optimal Block Preconditioned Conjugate Gradient |
OpenSeesPy Usage
from openseespy_solvers.scipy import spsolve
solver = spsolve()
ops.system("PythonSparse", solver.to_openseespy())
For eigen analysis:
from openseespy_solvers.scipy import eigsh
solver = eigsh(tol=1e-8)
lam = ops.eigen("PythonSparse", num_modes, solver.to_openseespy())
Notes
spsolve and umfpack are both sparse direct solvers. spsolve uses scipy.sparse.linalg's
SuperLU path and is available with the base install. umfpack requires the optional
scikit-umfpack package:
python -m pip install "openseespy-solvers[umfpack]"
On Windows, install scikit-umfpack from conda-forge instead.
umfpack defaults to scheme='CSC' and prefers the
di (int32) family — matching OpenSees native UmfPack — widening to dl
(int64) only when needed (index_dtype='auto'). For shift-invert
eigsh, consider scheme='CSC' so OpenSees
assembles K/M for SciPy's SuperLU/UMFPACK factor; plain ARPACK matvecs are
usually happier with CSR (the default).
Parameters documented below follow scipy naming where possible. Extra parameters such as
scheme, writable, debug, record_stats, and dtype control the OpenSeesPy integration.
Function Reference
Sparse linear algebra solvers for OpenSeesPy (scipy.sparse.linalg backend).
This module provides solver objects that wrap :mod:scipy.sparse.linalg for
OpenSeesPy's PythonSparse linear and eigen commands. Factory signatures
match scipy.sparse.linalg except that A and b (or K and M) are supplied by
OpenSees at solve time.
Each factory returns a solver object. Register it with OpenSees using
:meth:~openseespy_solvers._base.BaseOpenSeesSolver.to_openseespy::
from openseespy_solvers.scipy import cg
solver = cg(rtol=1e-8, M=precond.jacobi)
ops.system("PythonSparse", solver.to_openseespy())
Submodules
precond
Preconditioner factories for use with M=.
See Also
scipy.sparse.linalg
Underlying sparse linear algebra routines.
openseespy_solvers.cupy
GPU backend with the same interface.
spsolve
spsolve(*, permc_spec: str = 'COLAMD', scheme: str | None = None, writable: str | list[str] = 'none', debug: bool = False, dtype: Any = float64, record_stats: bool = False) -> _SpSolve
Source code in src/openseespy_solvers/scipy/__init__.py
| def spsolve(
*,
permc_spec: str = "COLAMD",
scheme: str | None = None,
writable: str | list[str] = "none",
debug: bool = False,
dtype: Any = np.float64,
record_stats: bool = False,
) -> _SpSolve:
r"""Configure a sparse direct solver for OpenSees ``PythonSparse``.
Uses :func:`scipy.sparse.linalg.splu` internally. The LU factorization is
reused when OpenSees reports ``matrix_status='UNCHANGED'``.
Parameters
----------
permc_spec : str, optional
Column permutation applied during LU factorization. Passed to
:func:`scipy.sparse.linalg.splu`. Default is ``'COLAMD'``.
""" + _OPENSEES_LINEAR + _LINEAR_RETURNS + _LINEAR_NOTES + """
See Also
--------
scipy.sparse.linalg.spsolve
scipy.sparse.linalg.splu
Examples
--------
>>> from openseespy_solvers.scipy import spsolve
>>> solver = spsolve(permc_spec="COLAMD")
>>> cfg = solver.to_openseespy()
>>> cfg["scheme"]
'CSR'
"""
return _SpSolve(
permc_spec=permc_spec,
scheme=scheme or "CSR",
writable=writable,
debug=debug,
dtype=dtype,
record_stats=record_stats,
)
|
umfpack
umfpack(*, scheme: str | None = None, writable: str | list[str] = 'none', debug: bool = False, dtype: Any = float64, index_dtype: str = 'auto', record_stats: bool = False) -> _Umfpack
Source code in src/openseespy_solvers/scipy/__init__.py
| def umfpack(
*,
scheme: str | None = None,
writable: str | list[str] = "none",
debug: bool = False,
dtype: Any = np.float64,
index_dtype: str = "auto",
record_stats: bool = False,
) -> _Umfpack:
r"""Configure a UMFPACK direct solver for OpenSees ``PythonSparse``.
Wraps ``scikits.umfpack.UmfpackContext``. By default prefers the ``di``
(int32) family — matching OpenSees native ``UmfPack`` — and only widens to
``dl`` (int64) when indices require it (``index_dtype='auto'``). Prefer
``scheme='CSC'`` so OpenSees stores UMFPACK's native layout (avoids a
CSR→CSC conversion).
The symbolic factorization is reused while sparsity is unchanged and is
recomputed only on ``matrix_status='STRUCTURE_CHANGED'``; numeric is
refreshed on ``'COEFFICIENTS_CHANGED'`` and reused on ``'UNCHANGED'``.
Requires the optional ``scikit-umfpack`` package
(``python -m pip install "openseespy-solvers[umfpack]"``).
Parameters
----------
index_dtype : {'auto', 'int32', 'int64', 'di', 'dl'}, optional
UMFPACK index width. ``auto`` (default) uses int32 when safe.
""" + _OPENSEES_LINEAR + _LINEAR_RETURNS + _LINEAR_NOTES + """
Raises
------
ImportError
If ``scikit-umfpack`` is not installed (raised when the solver is
instantiated, not on import).
See Also
--------
spsolve
scipy.sparse.linalg.spsolve
Examples
--------
>>> from openseespy_solvers.scipy import umfpack # doctest: +SKIP
>>> solver = umfpack(scheme="CSC") # doctest: +SKIP
>>> solver.to_openseespy()["scheme"] # doctest: +SKIP
'CSC'
"""
return _Umfpack(
scheme=scheme or "CSC",
writable=writable,
debug=debug,
dtype=dtype,
index_dtype=index_dtype,
record_stats=record_stats,
)
|
cg
cg(x0: Any = None, *, rtol: float = 1e-05, atol: float = 0.0, maxiter: int | None = None, M: Any = None, callback: Any = None, scheme: str | None = None, writable: str | list[str] = 'none', debug: bool = False, dtype: Any = float64, record_stats: bool = False) -> _CG
Source code in src/openseespy_solvers/scipy/__init__.py
| def cg(
x0: Any = None,
*,
rtol: float = 1e-5,
atol: float = 0.0,
maxiter: int | None = None,
M: Any = None,
callback: Any = None,
scheme: str | None = None,
writable: str | list[str] = "none",
debug: bool = False,
dtype: Any = np.float64,
record_stats: bool = False,
) -> _CG:
r"""Configure a Conjugate Gradient solver for OpenSees ``PythonSparse``.
Uses :func:`scipy.sparse.linalg.cg` internally to solve ``Ax = b`` for
symmetric, positive-definite ``A``.
Parameters
----------
x0 : ndarray, optional
Starting guess for the solution. Forwarded to `scipy.sparse.linalg` when not ``None``.
rtol, atol : float, optional
Relative and absolute tolerances for convergence. For convergence,
``norm(b - A @ x) <= max(rtol*norm(b), atol)`` must hold. Defaults are
``rtol=1e-5`` and ``atol=0.0``.
maxiter : int, optional
Maximum number of iterations.
M : {sparse matrix, LinearOperator, callable}, optional
Preconditioner for ``A``. If ``M`` is callable, it must accept the
assembled matrix ``A`` and return a preconditioner; see
:mod:`openseespy_solvers.scipy.precond`.
callback : callable, optional
User-supplied function called after each iteration as ``callback()``.
""" + _OPENSEES_LINEAR + _LINEAR_RETURNS + _LINEAR_NOTES + """
See Also
--------
scipy.sparse.linalg.cg
openseespy_solvers.scipy.precond.jacobi
openseespy_solvers.scipy.precond.ilu
Examples
--------
>>> from openseespy_solvers.scipy import cg, precond
>>> solver = cg(rtol=1e-8, M=precond.jacobi)
>>> solver.to_openseespy()["scheme"]
'CSR'
"""
return _CG(
x0,
rtol=rtol,
atol=atol,
maxiter=maxiter,
M=M,
callback=callback,
scheme=scheme or "CSR",
writable=writable,
debug=debug,
dtype=dtype,
record_stats=record_stats,
)
|
gmres
gmres(x0: Any = None, *, rtol: float = 1e-05, atol: float = 0.0, restart: int | None = None, maxiter: int | None = None, M: Any = None, callback: Any = None, scheme: str | None = None, writable: str | list[str] = 'none', debug: bool = False, dtype: Any = float64, record_stats: bool = False) -> _GMRES
Source code in src/openseespy_solvers/scipy/__init__.py
| def gmres(
x0: Any = None,
*,
rtol: float = 1e-5,
atol: float = 0.0,
restart: int | None = None,
maxiter: int | None = None,
M: Any = None,
callback: Any = None,
scheme: str | None = None,
writable: str | list[str] = "none",
debug: bool = False,
dtype: Any = np.float64,
record_stats: bool = False,
) -> _GMRES:
r"""Configure a GMRES solver for OpenSees ``PythonSparse``.
Uses :func:`scipy.sparse.linalg.gmres` internally to solve ``Ax = b``.
Parameters
----------
x0 : ndarray, optional
Starting guess for the solution.
rtol, atol : float, optional
Relative and absolute tolerances for convergence. Defaults are
``rtol=1e-5`` and ``atol=0.0``.
restart : int, optional
Number of iterations between restarts.
maxiter : int, optional
Maximum number of iterations.
M : {sparse matrix, LinearOperator, callable}, optional
Preconditioner for ``A``.
callback : callable, optional
User-supplied function called after each iteration.
""" + _OPENSEES_LINEAR + _LINEAR_RETURNS + _LINEAR_NOTES + """
See Also
--------
scipy.sparse.linalg.gmres
cg
Examples
--------
>>> from openseespy_solvers.scipy import gmres
>>> solver = gmres(rtol=1e-8, restart=30)
>>> solver.backend
'scipy'
"""
return _GMRES(
x0,
rtol=rtol,
atol=atol,
restart=restart,
maxiter=maxiter,
M=M,
callback=callback,
scheme=scheme or "CSR",
writable=writable,
debug=debug,
dtype=dtype,
record_stats=record_stats,
)
|
eigsh
eigsh(*, sigma: float | None = None, which: str = 'LM', v0: Any = None, ncv: int | None = None, maxiter: int | None = None, tol: float = 0.0, mode: str = 'normal', scheme: str | None = None, debug: bool = False, dtype: Any = float64) -> _Eigsh
Source code in src/openseespy_solvers/scipy/__init__.py
| def eigsh(
*,
sigma: float | None = None,
which: str = "LM",
v0: Any = None,
ncv: int | None = None,
maxiter: int | None = None,
tol: float = 0.0,
mode: str = "normal",
scheme: str | None = None,
debug: bool = False,
dtype: Any = np.float64,
) -> _Eigsh:
r"""Configure a sparse eigenvalue solver for OpenSees ``PythonSparse``.
Matches OpenSees ``eigsh``: shift-invert with ``sigma=0`` when
``find_smallest=True``; plain ARPACK when ``find_smallest=False`` unless
``sigma`` is set. ARPACK always uses ``which='LM'`` (OpenSees reference).
For shift-invert (smallest modes), prefer ``scheme='CSC'`` so OpenSees
assembles ``K``/``M`` in the layout SciPy's SuperLU/UMFPACK factor expects.
Default remains ``'CSR'`` (better for plain ARPACK matvecs).
Parameters
----------
sigma : float, optional
Shift for shift-invert. Default ``0.0`` when ``find_smallest=True``.
which : {'LM', 'SM', ...}, optional
Accepted for API compatibility; solves always use ``'LM'`` like OpenSees.
v0 : ndarray, optional
Starting vector for ARPACK.
ncv : int, optional
Number of Lanczos vectors.
maxiter : int, optional
Maximum number of iterations.
tol : float, optional
Convergence tolerance. Default is ``0.0``.
mode : {'normal', 'buckling', 'cayley'}, optional
Eigenproblem mode. Default is ``'normal'``.
""" + _OPENSEES_EIGEN + _EIGEN_RETURNS + _EIGEN_NOTES + """
See Also
--------
scipy.sparse.linalg.eigsh
lobpcg
Examples
--------
>>> from openseespy_solvers.scipy import eigsh
>>> solver = eigsh(tol=1e-8)
>>> solver.scheme
'CSR'
"""
return _Eigsh(
sigma=sigma,
which=which,
v0=v0,
ncv=ncv,
maxiter=maxiter,
tol=tol,
mode=mode,
scheme=scheme or "CSR",
debug=debug,
dtype=dtype,
)
|
lobpcg
lobpcg(*, X: Any = None, M: Any = None, tol: float | None = None, maxiter: int = 20, largest: bool = False, rng: Any = None, scheme: str | None = None, debug: bool = False, dtype: Any = float64) -> _Lobpcg
Source code in src/openseespy_solvers/scipy/__init__.py
| def lobpcg(
*,
X: Any = None,
M: Any = None,
tol: float | None = None,
maxiter: int = 20,
largest: bool = False,
rng: Any = None,
scheme: str | None = None,
debug: bool = False,
dtype: Any = np.float64,
) -> _Lobpcg:
r"""Configure a LOBPCG eigenvalue solver for OpenSees ``PythonSparse``.
Uses :func:`scipy.sparse.linalg.lobpcg` internally to solve the generalized
eigenproblem ``K x = \lambda M x``.
Parameters
----------
X : ndarray, optional
Initial approximation to the eigenvectors. If ``None``, a random matrix
with ``num_modes`` columns is generated using ``rng``.
M : {sparse matrix, LinearOperator, callable}, optional
Optional preconditioner passed to LOBPCG as ``M=``. A callable is
invoked as ``M(K)``; see :mod:`openseespy_solvers.scipy.precond`.
tol : float, optional
Convergence tolerance.
maxiter : int, optional
Maximum number of iterations. Default is ``20``.
largest : bool, optional
If ``True``, compute largest eigenvalues. Default is ``False``.
rng : {None, int, `numpy.random.Generator`}, optional
Random number generator used when ``X`` is ``None``.
""" + _OPENSEES_EIGEN + _EIGEN_RETURNS + _EIGEN_NOTES + """
See Also
--------
scipy.sparse.linalg.lobpcg
eigsh
Examples
--------
>>> from openseespy_solvers.scipy import lobpcg
>>> solver = lobpcg(tol=1e-8, maxiter=40)
>>> solver.backend
'scipy'
"""
return _Lobpcg(
X=X,
M=M,
tol=tol,
maxiter=maxiter,
largest=largest,
rng=rng,
scheme=scheme or "CSR",
debug=debug,
dtype=dtype,
)
|