Source code for pylops.optimization.basic

__all__ = [
    "cg",
    "cgls",
    "lsqr",
]

from collections.abc import Callable
from typing import TYPE_CHECKING

from pylops.optimization.callback import CostToDataCallback, CostToInitialCallback
from pylops.optimization.cls_basic import CG, CGLS, LSQR
from pylops.utils.decorators import add_ndarray_support_to_solver
from pylops.utils.typing import NDArray

if TYPE_CHECKING:
    from pylops.linearoperator import LinearOperator


[docs] @add_ndarray_support_to_solver def cg( Op: "LinearOperator", y: NDArray, x0: NDArray | None = None, niter: int = 10, tol: float = 1e-4, rtol: float = 0.0, rtol1: float = 0.0, show: bool = False, itershow: tuple[int, int, int] = (10, 10, 10), callback: Callable | None = None, preallocate: bool = False, ) -> tuple[NDArray, int, NDArray]: r"""Conjugate gradient Solve a square system of equations given an operator ``Op`` and data ``y`` using conjugate gradient iterations. Parameters ---------- Op : :obj:`pylops.LinearOperator` Operator to invert of size :math:`[N \times N]` y : :obj:`numpy.ndarray` Data of size :math:`[N \times 1]` x0 : :obj:`numpy.ndarray`, optional Initial guess niter : :obj:`int`, optional Number of iterations tol : :obj:`float`, optional Absolute tolerance on residual norm. Stops the solver when the residual norm is below this value. rtol : :obj:`float`, optional Relative tolerance on residual norm wrt initial residual norm. Stops the solver when the ratio of the current residual norm to the initial residual norm is below this value. rtol1 : :obj:`float`, optional Relative tolerance on residual norm wrt to data. Stops the solver when the ratio of the current residual norm to the data norm is below this value. show : :obj:`bool`, optional Display iterations log itershow : :obj:`tuple`, optional Display set log for the first N1 steps, last N2 steps, and every N3 steps in between where N1, N2, N3 are the three element of the list. callback : :obj:`callable`, optional Function with signature (``callback(x)``) to call after each iteration where ``x`` is the current model vector preallocate : :obj:`bool`, optional .. versionadded:: 2.6.0 Pre-allocate all variables used by the solver Returns ------- x : :obj:`numpy.ndarray` Estimated model of size :math:`[N \times 1]` iit : :obj:`int` Number of executed iterations cost : :obj:`numpy.ndarray`, optional History of the L2 norm of the residual Notes ----- See :class:`pylops.optimization.cls_basic.CG` """ callbacks = [] if rtol > 0.0: callbacks.append(CostToInitialCallback(rtol)) if rtol1 > 0.0: callbacks.append(CostToDataCallback(rtol1)) cgsolve = CG( Op, callbacks=callbacks if len(callbacks) > 0 else None, ) if callback is not None: cgsolve.callback = callback x, iiter, cost = cgsolve.solve( y=y, x0=x0, tol=tol, niter=niter, show=show, itershow=itershow, preallocate=preallocate, ) return x, iiter, cost
[docs] @add_ndarray_support_to_solver def cgls( Op: "LinearOperator", y: NDArray, x0: NDArray | None = None, niter: int = 10, damp: float = 0.0, tol: float = 1e-4, rtol: float = 0.0, rtol1: float = 0.0, show: bool = False, itershow: tuple[int, int, int] = (10, 10, 10), callback: Callable | None = None, preallocate: bool = False, ) -> tuple[NDArray, int, int, float, float, NDArray]: r"""Conjugate gradient least squares Solve an overdetermined system of equations given an operator ``Op`` and data ``y`` using conjugate gradient iterations. Parameters ---------- Op : :obj:`pylops.LinearOperator` Operator to invert of size :math:`[N \times M]` y : :obj:`numpy.ndarray` Data of size :math:`[N \times 1]` x0 : :obj:`numpy.ndarray`, optional Initial guess niter : :obj:`int`, optional Number of iterations damp : :obj:`float`, optional Damping coefficient tol : :obj:`float`, optional Absolute tolerance on residual norm. Stops the solver when the residual norm is below this value. rtol : :obj:`float`, optional Relative tolerance on residual norm wrt initial residual norm. Stops the solver when the ratio of the current residual norm to the initial residual norm is below this value. rtol1 : :obj:`float`, optional Relative tolerance on residual norm wrt to data. Stops the solver when the ratio of the current residual norm to the data norm is below this value. show : :obj:`bool`, optional Display iterations log itershow : :obj:`tuple`, optional Display set log for the first N1 steps, last N2 steps, and every N3 steps in between where N1, N2, N3 are the three element of the list. callback : :obj:`callable`, optional Function with signature (``callback(x)``) to call after each iteration where ``x`` is the current model vector preallocate : :obj:`bool`, optional .. versionadded:: 2.5.0 Pre-allocate all variables used by the solver Returns ------- x : :obj:`numpy.ndarray` Estimated model of size :math:`[M \times 1]` istop : :obj:`int` Gives the reason for termination ``1`` means :math:`\mathbf{x}` is an approximate solution to :math:`\mathbf{y} = \mathbf{Op}\,\mathbf{x}` ``2`` means :math:`\mathbf{x}` approximately solves the least-squares problem iit : :obj:`int` Iteration number upon termination r1norm : :obj:`float` :math:`||\mathbf{r}||_2`, where :math:`\mathbf{r} = \mathbf{y} - \mathbf{Op}\,\mathbf{x}` r2norm : :obj:`float` :math:`\sqrt{\mathbf{r}^T\mathbf{r} + \epsilon^2 \mathbf{x}^T\mathbf{x}}`. Equal to ``r1norm`` if :math:`\epsilon=0` cost : :obj:`numpy.ndarray`, optional History of r1norm through iterations Notes ----- See :class:`pylops.optimization.cls_basic.CGLS` """ callbacks = [] if rtol > 0.0: callbacks.append(CostToInitialCallback(rtol)) if rtol1 > 0.0: callbacks.append(CostToDataCallback(rtol1)) cgsolve = CGLS( Op, callbacks=callbacks if len(callbacks) > 0 else None, ) if callback is not None: cgsolve.callback = callback x, istop, iiter, r1norm, r2norm, cost = cgsolve.solve( y=y, x0=x0, tol=tol, niter=niter, damp=damp, show=show, itershow=itershow, preallocate=preallocate, ) return x, istop, iiter, r1norm, r2norm, cost
[docs] @add_ndarray_support_to_solver def lsqr( Op: "LinearOperator", y: NDArray, x0: NDArray | None = None, damp: float = 0.0, atol: float = 1e-08, btol: float = 1e-08, conlim: float = 100000000.0, niter: int = 10, calc_var: bool = True, show: bool = False, itershow: tuple[int, int, int] = (10, 10, 10), callback: Callable | None = None, preallocate: bool = False, ) -> tuple[NDArray, int, int, float, float, float, float, float, float, float, NDArray]: r"""LSQR Solve an overdetermined system of equations given an operator ``Op`` and data ``y`` using LSQR iterations. .. math:: \DeclareMathOperator{\cond}{cond} Parameters ---------- Op : :obj:`pylops.LinearOperator` Operator to invert of size :math:`[N \times M]` y : :obj:`numpy.ndarray` Data of size :math:`[N \times 1]` x0 : :obj:`numpy.ndarray`, optional Initial guess of size :math:`[M \times 1]` damp : :obj:`float`, optional Damping coefficient atol, btol : :obj:`float`, optional Stopping tolerances. If both are 1.0e-9, the final residual norm should be accurate to about 9 digits. (The solution will usually have fewer correct digits, depending on :math:`\cond(\mathbf{Op})` and the size of ``damp``.) conlim : :obj:`float`, optional Stopping tolerance on :math:`\cond(\mathbf{Op})` exceeds ``conlim``. For square, ``conlim`` could be as large as 1.0e+12. For least-squares problems, ``conlim`` should be less than 1.0e+8. Maximum precision can be obtained by setting ``atol = btol = conlim = 0``, but the number of iterations may then be excessive. niter : :obj:`int`, optional Number of iterations calc_var : :obj:`bool`, optional Estimate diagonals of :math:`(\mathbf{Op}^H\mathbf{Op} + \epsilon^2\mathbf{I})^{-1}`. show : :obj:`bool`, optional Display iterations log itershow : :obj:`tuple`, optional Display set log for the first N1 steps, last N2 steps, and every N3 steps in between where N1, N2, N3 are the three element of the list. callback : :obj:`callable`, optional Function with signature (``callback(x)``) to call after each iteration where ``x`` is the current model vector preallocate : :obj:`bool`, optional .. versionadded:: 2.6.0 Pre-allocate all variables used by the solver Returns ------- x : :obj:`numpy.ndarray` Estimated model of size :math:`[M \times 1]` istop : :obj:`int` Gives the reason for termination ``0`` means the exact solution is :math:`\mathbf{x}=0` ``1`` means :math:`\mathbf{x}` is an approximate solution to :math:`\mathbf{y} = \mathbf{Op}\,\mathbf{x}` ``2`` means :math:`\mathbf{x}` approximately solves the least-squares problem ``3`` means the estimate of :math:`\cond(\overline{\mathbf{Op}})` has exceeded ``conlim`` ``4`` means :math:`\mathbf{y} - \mathbf{Op}\,\mathbf{x}` is small enough for this machine ``5`` means the least-squares solution is good enough for this machine ``6`` means :math:`\cond(\overline{\mathbf{Op}})` seems to be too large for this machine ``7`` means the iteration limit has been reached iiter : :obj:`int` Iteration number upon termination r1norm : :obj:`float` :math:`||\mathbf{r}||_2^2`, where :math:`\mathbf{r} = \mathbf{y} - \mathbf{Op}\,\mathbf{x}` r2norm : :obj:`float` :math:`\sqrt{\mathbf{r}^T\mathbf{r} + \epsilon^2 \mathbf{x}^T\mathbf{x}}`. Equal to ``r1norm`` if :math:`\epsilon=0` anorm : :obj:`float` Estimate of Frobenius norm of :math:`\overline{\mathbf{Op}} = [\mathbf{Op} \; \epsilon \mathbf{I}]` acond : :obj:`float` Estimate of :math:`\cond(\overline{\mathbf{Op}})` arnorm : :obj:`float` Estimate of norm of :math:`\cond(\mathbf{Op}^H\mathbf{r}- \epsilon^2\mathbf{x})` xnorm : :obj:`float` :math:`||\mathbf{x}||_2` var : :obj:`float` Diagonals of :math:`(\mathbf{Op}^H\mathbf{Op})^{-1}` (if ``damp=0``) or more generally :math:`(\mathbf{Op}^H\mathbf{Op} + \epsilon^2\mathbf{I})^{-1}`. cost : :obj:`numpy.ndarray`, optional History of r1norm through iterations Notes ----- See :class:`pylops.optimization.cls_basic.LSQR` """ lsqrsolve = LSQR(Op) if callback is not None: lsqrsolve.callback = callback ( x, istop, iiter, r1norm, r2norm, anorm, acond, arnorm, xnorm, var, cost, ) = lsqrsolve.solve( y=y, x0=x0, damp=damp, atol=atol, btol=btol, conlim=conlim, niter=niter, calc_var=calc_var, show=show, itershow=itershow, preallocate=preallocate, ) return x, istop, iiter, r1norm, r2norm, anorm, acond, arnorm, xnorm, var, cost