__all__ = [
"irls",
"omp",
"ista",
"fista",
"spgl1",
"splitbregman",
]
from collections.abc import Callable
from typing import TYPE_CHECKING, Any, Optional
from pylops.optimization.callback import (
CostNanInfCallback,
CostToDataCallback,
CostToInitialCallback,
)
from pylops.optimization.cls_sparsity import FISTA, IRLS, ISTA, OMP, SPGL1, SplitBregman
from pylops.utils.decorators import add_ndarray_support_to_solver
from pylops.utils.typing import (
NDArray,
SamplingLike,
Tirlskind,
Tsolverengine,
Tthreshkind,
)
if TYPE_CHECKING:
from pylops.linearoperator import LinearOperator
[docs]
def irls(
Op: "LinearOperator",
y: NDArray,
x0: NDArray | None = None,
nouter: int = 10,
threshR: bool = False,
epsR: float = 1e-10,
epsI: float = 1e-10,
tolIRLS: float = 1e-10,
warm: bool = False,
kind: Tirlskind = "data",
engine: Tsolverengine = "scipy",
show: bool = False,
itershow: tuple[int, int, int] = (10, 10, 10),
callback: Callable | None = None,
preallocate: bool = False,
**kwargs_solver,
) -> tuple[NDArray, int]:
r"""Iteratively reweighted least squares.
Solve an optimization problem with :math:`L_1` cost function (data IRLS)
or :math:`L_1` regularization term (model IRLS) given the operator ``Op``
and data ``y``.
In the *data IRLS*, the cost function is minimized by iteratively solving a
weighted least squares problem with the weight at iteration :math:`i`
being based on the data residual at iteration :math:`i-1`. This IRLS solver
is robust to *outliers* since the L1 norm given less weight to large
residuals than L2 norm does.
Similarly in the *model IRLS*, the weight at at iteration :math:`i`
is based on the model at iteration :math:`i-1`. This IRLS solver inverts
for a sparse model vector.
Parameters
----------
Op : :obj:`pylops.LinearOperator`
Operator to invert
y : :obj:`numpy.ndarray`
Data
x0 : :obj:`numpy.ndarray`, optional
Initial guess
nouter : :obj:`int`, optional
Number of outer iterations
threshR : :obj:`bool`, optional
Apply thresholding in creation of weight (``True``)
or damping (``False``)
epsR : :obj:`float`, optional
Damping to be applied to residuals for weighting term
epsI : :obj:`float`, optional
Tikhonov damping
tolIRLS : :obj:`float`, optional
Tolerance. Stop outer iterations if difference between inverted model
at subsequent iterations is smaller than ``tolIRLS``
warm : :obj:`bool`, optional
Warm start each inversion inner step with previous estimate (``True``) or not (``False``).
This only applies to ``kind="data"`` and ``kind="datamodel"``
kind : :obj:`str`, optional
Kind of solver (``model``, ``data`` or ``datamodel``)
engine : :obj:`str`, optional
Solver to use (``scipy`` or ``pylops``)
show : :obj:`bool`, optional
Display logs
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. Note that if ``y``
is a JAX array, this option is ignored and variables are not
pre-allocated since JAX does not support in-place operations.
**kwargs_solver
Arbitrary keyword arguments for
:py:func:`scipy.sparse.linalg.cg` solver for data IRLS and
:py:func:`scipy.sparse.linalg.lsqr` solver for model IRLS when using
numpy data(or :py:func:`pylops.optimization.solver.cg` and
:py:func:`pylops.optimization.solver.cgls` when using cupy data)
Returns
-------
xinv : :obj:`numpy.ndarray`
Inverted model
nouter : :obj:`int`
Number of effective outer iterations
Notes
-----
See :class:`pylops.optimization.cls_sparsity.IRLS`
"""
irlssolve = IRLS(Op)
if callback is not None:
irlssolve.callback = callback
(
x,
nouter,
) = irlssolve.solve(
y,
x0=x0,
nouter=nouter,
threshR=threshR,
epsR=epsR,
epsI=epsI,
tolIRLS=tolIRLS,
kind=kind,
warm=warm,
engine=engine,
preallocate=preallocate,
show=show,
itershow=itershow,
**kwargs_solver,
)
return x, nouter
[docs]
def omp(
Op: "LinearOperator",
y: NDArray,
niter_outer: int = 10,
niter_inner: int = 40,
sigma: float = 1e-4,
rtol: float = 0.0,
rtol1: float = 0.0,
normalizecols: bool = False,
Opbasis: Optional["LinearOperator"] = None,
optimal_coeff: bool = False,
engine: Tsolverengine = "scipy",
show: bool = False,
itershow: tuple[int, int, int] = (10, 10, 10),
callback: Callable | None = None,
preallocate: bool = False,
) -> tuple[NDArray, int, NDArray]:
r"""Orthogonal Matching Pursuit (OMP).
Solve an optimization problem with :math:`L^0` regularization function given
the operator ``Op`` and data ``y``. The operator can be real or complex,
and should ideally be either square :math:`N=M` or underdetermined
:math:`N<M`.
Parameters
----------
Op : :obj:`pylops.LinearOperator`
Operator to invert
y : :obj:`numpy.ndarray`
Data
niter_outer : :obj:`int`, optional
Number of iterations of outer loop
niter_inner : :obj:`int`, optional
Number of iterations of inner loop. By choosing ``niter_inner=0``, the
Matching Pursuit (MP) algorithm is implemented.
sigma : :obj:`float`, optional
Maximum :math:`L_2` norm of residual. When smaller stop iterations.
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.
normalizecols : :obj:`list`, optional
Normalize columns (``True``) or not (``False``). Note that this can be
expensive as it requires applying the forward operator
:math:`n_{cols}` times to unit vectors (i.e., containing 1 at
position j and zero otherwise); use only when the columns of the
operator are expected to have highly varying norms.
Opbasis : :obj:`pylops.LinearOperator`
Operator representing the basis functions. If not provided, the entire
operator used for inversion `Op` is used.
optimal_coeff : :obj:`bool`, optional
Estimate optimal coefficient that minimizes the norm of the residual
:math:`\mathbf{r} - c * \mathbf{Op}^j) norm (``True``) or use the
directly the value from the inner product
:math:`\mathbf{Op}_j^H\,\mathbf{r}_k`.
engine : :obj:`str`, optional
Solver to use (``scipy`` or ``pylops``)
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, cols)``) to call after each iteration
where ``x`` contains the non-zero model coefficient and ``cols`` are the
indices where the current model vector is non-zero
preallocate : :obj:`bool`, optional
.. versionadded:: 2.6.0
Pre-allocate all variables used by the solver. Note that if ``y``
is a JAX array, this option is ignored and variables are not
pre-allocated since JAX does not support in-place operations.
Returns
-------
xinv : :obj:`numpy.ndarray`
Inverted model
niter_outer : :obj:`int`
Number of effective outer iterations
cost : :obj:`numpy.ndarray`
History of cost function
See Also
--------
ISTA: Iterative Shrinkage-Thresholding Algorithm (ISTA).
FISTA: Fast Iterative Shrinkage-Thresholding Algorithm (FISTA).
SPGL1: Spectral Projected-Gradient for L1 norm (SPGL1).
SplitBregman: Split Bregman for mixed L2-L1 norms.
Notes
-----
See :class:`pylops.optimization.cls_sparsity.OMP`
"""
callbacks = [
CostNanInfCallback(),
]
if rtol > 0.0:
callbacks.append(CostToInitialCallback(rtol))
if rtol1 > 0.0:
callbacks.append(CostToDataCallback(rtol1))
ompsolve = OMP(
Op,
callbacks=callbacks,
)
if callback is not None:
ompsolve.callback = callback
x, niter_outer, cost = ompsolve.solve(
y,
niter_outer=niter_outer,
niter_inner=niter_inner,
sigma=sigma,
normalizecols=normalizecols,
Opbasis=Opbasis,
optimal_coeff=optimal_coeff,
engine=engine,
show=show,
itershow=itershow,
preallocate=preallocate,
)
return x, niter_outer, cost
[docs]
def ista(
Op: "LinearOperator",
y: NDArray,
x0: NDArray | None = None,
niter: int = 10,
SOp: Optional["LinearOperator"] = None,
eps: float = 0.1,
alpha: float | None = None,
eigsdict: dict[str, Any] | None = None,
tol: float = 1e-10,
rtol: float = 0.0,
rtol1: float = 0.0,
threshkind: Tthreshkind = "soft",
perc: float | None = None,
decay: NDArray | None = None,
monitorres: bool = False,
show: bool = False,
itershow: tuple[int, int, int] = (10, 10, 10),
callback: Callable | None = None,
preallocate: bool = False,
) -> tuple[NDArray, int, NDArray]:
r"""Iterative Shrinkage-Thresholding Algorithm (ISTA).
Solve an optimization problem with :math:`L^p, \; p=0, 0.5, 1`
regularization, given the operator ``Op`` and data ``y``. The operator
can be real or complex, and should ideally be either square :math:`N=M`
or underdetermined :math:`N<M`.
Parameters
----------
Op : :obj:`pylops.LinearOperator`
Operator to invert
y : :obj:`numpy.ndarray`
Data of size :math:`[N \times 1]`
x0: :obj:`numpy.ndarray`, optional
Initial guess
niter : :obj:`int`
Number of iterations
SOp : :obj:`pylops.LinearOperator`, optional
Regularization operator (use when solving the analysis problem)
eps : :obj:`float`, optional
Sparsity damping
alpha : :obj:`float`, optional
Step size. To guarantee convergence, ensure
:math:`\alpha \le 1/\lambda_\text{max}`, where :math:`\lambda_\text{max}`
is the largest eigenvalue of :math:`\mathbf{Op}^H\mathbf{Op}`.
If ``None``, the maximum eigenvalue is estimated and the optimal step size
is chosen as :math:`1/\lambda_\text{max}`. If provided, the
convergence criterion will not be checked internally.
eigsdict : :obj:`dict`, optional
Dictionary of parameters to be passed to :func:`pylops.LinearOperator.eigs` method
when computing the maximum eigenvalue
tol : :obj:`float`, optional
Absolute tolerance on model update. Stop iterations if difference between inverted model
at subsequent iterations is smaller than ``tol``
rtol : :obj:`float`, optional
Relative tolerance on total cost function wrt initial total cost
function. Stops the solver when the ratio of the current total cost function
to the initial total cost function is below this value.
rtol1 : :obj:`float`, optional
Relative tolerance on total cost function wrt to data. Stops the solver when
the ratio of the current total cost function to the data norm is below this value.
threshkind : :obj:`str`, optional
Kind of thresholding ('hard', 'soft', 'half', 'hard-percentile',
'soft-percentile', or 'half-percentile' - 'soft' used as default)
perc : :obj:`float`, optional
Percentile, as percentage of values to be kept by thresholding (to be
provided when thresholding is soft-percentile or half-percentile)
decay : :obj:`numpy.ndarray`, optional
Decay factor to be applied to thresholding during iterations
monitorres : :obj:`bool`, optional
Monitor that residual is decreasing
show : :obj:`bool`, optional
Display logs
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. Note that if ``y``
is a JAX array, this option is ignored and variables are not
pre-allocated since JAX does not support in-place operations.
Returns
-------
xinv : :obj:`numpy.ndarray`
Inverted model
niter : :obj:`int`
Number of effective iterations
cost : :obj:`numpy.ndarray`
History of cost (including regularization term)
Raises
------
NotImplementedError
If ``threshkind`` is different from hard, soft, half, soft-percentile,
or half-percentile
ValueError
If ``perc=None`` when ``threshkind`` is soft-percentile or
half-percentile
ValueError
If ``monitorres=True`` and residual increases
See Also
--------
OMP: Orthogonal Matching Pursuit (OMP).
FISTA: Fast Iterative Shrinkage-Thresholding Algorithm (FISTA).
SPGL1: Spectral Projected-Gradient for L1 norm (SPGL1).
SplitBregman: Split Bregman for mixed L2-L1 norms.
Notes
-----
See :class:`pylops.optimization.cls_sparsity.ISTA`
"""
callbacks = [
CostNanInfCallback(),
]
if rtol > 0.0:
callbacks.append(CostToInitialCallback(rtol))
if rtol1 > 0.0:
callbacks.append(CostToDataCallback(rtol1))
istasolve = ISTA(
Op,
callbacks=callbacks,
)
if callback is not None:
istasolve.callback = callback
x, iiter, cost = istasolve.solve(
y=y,
x0=x0,
niter=niter,
SOp=SOp,
eps=eps,
alpha=alpha,
eigsdict=eigsdict,
tol=tol,
threshkind=threshkind,
perc=perc,
decay=decay,
monitorres=monitorres,
show=show,
itershow=itershow,
preallocate=preallocate,
)
return x, iiter, cost
[docs]
def fista(
Op: "LinearOperator",
y: NDArray,
x0: NDArray | None = None,
niter: int = 10,
SOp: Optional["LinearOperator"] = None,
eps: float = 0.1,
alpha: float | None = None,
eigsdict: dict[str, Any] | None = None,
tol: float = 1e-10,
rtol: float = 0.0,
rtol1: float = 0.0,
threshkind: Tthreshkind = "soft",
perc: float | None = None,
decay: NDArray | None = None,
monitorres: bool = False,
show: bool = False,
itershow: tuple[int, int, int] = (10, 10, 10),
callback: Callable | None = None,
preallocate: bool = False,
) -> tuple[NDArray, int, NDArray]:
r"""Fast Iterative Shrinkage-Thresholding Algorithm (FISTA).
Solve an optimization problem with :math:`L^p, \; p=0, 0.5, 1`
regularization, given the operator ``Op`` and data ``y``.
The operator can be real or complex, and should ideally be either square
:math:`N=M` or underdetermined :math:`N<M`.
Parameters
----------
Op : :obj:`pylops.LinearOperator`
Operator to invert
y : :obj:`numpy.ndarray`
Data
x0: :obj:`numpy.ndarray`, optional
Initial guess
niter : :obj:`int`, optional
Number of iterations
SOp : :obj:`pylops.LinearOperator`, optional
Regularization operator (use when solving the analysis problem)
eps : :obj:`float`, optional
Sparsity damping
alpha : :obj:`float`, optional
Step size. To guarantee convergence, ensure
:math:`\alpha \le 1/\lambda_\text{max}`, where :math:`\lambda_\text{max}`
is the largest eigenvalue of :math:`\mathbf{Op}^H\mathbf{Op}`.
If ``None``, the maximum eigenvalue is estimated and the optimal step size
is chosen as :math:`1/\lambda_\text{max}`. If provided, the
convergence criterion will not be checked internally.
eigsdict : :obj:`dict`, optional
Dictionary of parameters to be passed to :func:`pylops.LinearOperator.eigs` method
when computing the maximum eigenvalue
tol : :obj:`float`, optional
Absolute tolerance on model update. Stop iterations if difference between inverted model
at subsequent iterations is smaller than ``tol``
rtol : :obj:`float`, optional
Relative tolerance on total cost function wrt initial total cost
function. Stops the solver when the ratio of the current total cost function
to the initial total cost function is below this value.
rtol1 : :obj:`float`, optional
Relative tolerance on total cost function wrt to data. Stops the solver when
the ratio of the current total cost function to the data norm is below this value.
threshkind : :obj:`str`, optional
Kind of thresholding ('hard', 'soft', 'half', 'soft-percentile', or
'half-percentile' - 'soft' used as default)
perc : :obj:`float`, optional
Percentile, as percentage of values to be kept by thresholding (to be
provided when thresholding is soft-percentile or half-percentile)
decay : :obj:`numpy.ndarray`, optional
Decay factor to be applied to thresholding during iterations
monitorres : :obj:`bool`, optional
Monitor that residual is decreasing
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. Note that if ``y``
is a JAX array, this option is ignored and variables are not
pre-allocated since JAX does not support in-place operations.
Returns
-------
xinv : :obj:`numpy.ndarray`
Inverted model
niter : :obj:`int`
Number of effective iterations
cost : :obj:`numpy.ndarray`, optional
History of cost (including regularization term)
Raises
------
NotImplementedError
If ``threshkind`` is different from hard, soft, half, soft-percentile,
or half-percentile
ValueError
If ``perc=None`` when ``threshkind`` is soft-percentile or
half-percentile
See Also
--------
OMP: Orthogonal Matching Pursuit (OMP).
ISTA: Iterative Shrinkage-Thresholding Algorithm (ISTA).
SPGL1: Spectral Projected-Gradient for L1 norm (SPGL1).
SplitBregman: Split Bregman for mixed L2-L1 norms.
Notes
-----
See :class:`pylops.optimization.cls_sparsity.FISTA`
"""
callbacks = [
CostNanInfCallback(),
]
if rtol > 0.0:
callbacks.append(CostToInitialCallback(rtol))
if rtol1 > 0.0:
callbacks.append(CostToDataCallback(rtol1))
fistasolve = FISTA(
Op,
callbacks=callbacks,
)
if callback is not None:
fistasolve.callback = callback
x, iiter, cost = fistasolve.solve(
y=y,
x0=x0,
niter=niter,
SOp=SOp,
eps=eps,
alpha=alpha,
eigsdict=eigsdict,
tol=tol,
threshkind=threshkind,
perc=perc,
decay=decay,
monitorres=monitorres,
show=show,
itershow=itershow,
preallocate=preallocate,
)
return x, iiter, cost
[docs]
@add_ndarray_support_to_solver
def spgl1(
Op: "LinearOperator",
y: NDArray,
x0: NDArray | None = None,
SOp: Optional["LinearOperator"] = None,
tau: float = 0.0,
sigma: float = 0.0,
show: bool = False,
**kwargs_spgl1,
) -> tuple[NDArray, NDArray, dict[str, Any]]:
r"""Spectral Projected-Gradient for L1 norm.
Solve a constrained system of equations given the operator ``Op``
and a sparsyfing transform ``SOp`` aiming to retrive a model that
is sparse in the sparsyfing domain.
This is a simple wrapper to :py:func:`spgl1.spgl1`
which is a porting of the well-known
`SPGL1 <https://www.cs.ubc.ca/~mpf/spgl1/>`_ MATLAB solver into Python.
In order to be able to use this solver you need to have installed the
``spgl1`` library.
Parameters
----------
Op : :obj:`pylops.LinearOperator`
Operator to invert
y : :obj:`numpy.ndarray`
Data
x0 : :obj:`numpy.ndarray`, optional
Initial guess
SOp : :obj:`pylops.LinearOperator`, optional
Sparsifying transform
tau : :obj:`float`, optional
Non-negative LASSO scalar. If different from ``0``,
SPGL1 will solve LASSO problem
sigma : :obj:`list`, optional
BPDN scalar. If different from ``0``,
SPGL1 will solve BPDN problem
show : :obj:`bool`, optional
Display iterations log
**kwargs_spgl1
Arbitrary keyword arguments for
:py:func:`spgl1.spgl1` solver
Returns
-------
xinv : :obj:`numpy.ndarray`
Inverted model in original domain.
pinv : :obj:`numpy.ndarray`
Inverted model in sparse domain.
info : :obj:`dict`
Dictionary with the following information:
- ``tau``, final value of tau (see sigma above)
- ``rnorm``, two-norm of the optimal residual
- ``rgap``, relative duality gap (an optimality measure)
- ``gnorm``, Lagrange multiplier of (LASSO)
- ``stat``, final status of solver
* ``1``: found a BPDN solution,
* ``2``: found a BP solution; exit based on small gradient,
* ``3``: found a BP solution; exit based on small residual,
* ``4``: found a LASSO solution,
* ``5``: error, too many iterations,
* ``6``: error, linesearch failed,
* ``7``: error, found suboptimal BP solution,
* ``8``: error, too many matrix-vector products.
- ``niters``, number of iterations
- ``nProdA``, number of multiplications with A
- ``nProdAt``, number of multiplications with A'
- ``n_newton``, number of Newton steps
- ``time_project``, projection time (seconds)
- ``time_matprod``, matrix-vector multiplications time (seconds)
- ``time_total``, total solution time (seconds)
- ``niters_lsqr``, number of lsqr iterations (if ``subspace_min=True``)
- ``xnorm1``, L1-norm model solution history through iterations
- ``rnorm2``, L2-norm residual history through iterations
- ``lambdaa``, Lagrange multiplier history through iterations
Raises
------
ModuleNotFoundError
If the ``spgl1`` library is not installed
Notes
-----
See :class:`pylops.optimization.cls_sparsity.SPGL1`
"""
spgl1solve = SPGL1(Op)
xinv, pinv, info = spgl1solve.solve(
y,
x0=x0,
SOp=SOp,
tau=tau,
sigma=sigma,
show=show,
**kwargs_spgl1,
)
return xinv, pinv, info
[docs]
def splitbregman(
Op: "LinearOperator",
y: NDArray,
RegsL1: list["LinearOperator"],
x0: NDArray | None = None,
niter_outer: int = 3,
niter_inner: int = 5,
RegsL2: list["LinearOperator"] | None = None,
dataregsL2: list[NDArray] | None = None,
mu: float = 1.0,
epsRL1s: SamplingLike | None = None,
epsRL2s: SamplingLike | None = None,
tol: float = 1e-10,
rtol: float = 0.0,
rtol1: float = 0.0,
tau: float = 1.0,
restart: bool = False,
engine: Tsolverengine = "scipy",
show: bool = False,
itershow: tuple[int, int, int] = (10, 10, 10),
show_inner: bool = False,
callback: Callable | None = None,
preallocate: bool = False,
**kwargs_lsqr,
) -> tuple[NDArray, int, NDArray]:
r"""Split Bregman for mixed L2-L1 norms.
Solve an unconstrained system of equations with mixed :math:`L_2` and :math:`L_1`
regularization terms given the operator ``Op``, a list of :math:`L_1`
regularization terms ``RegsL1``, and an optional list of :math:`L_2`
regularization terms ``RegsL2``.
Parameters
----------
Op : :obj:`pylops.LinearOperator`
Operator to invert
y : :obj:`numpy.ndarray`
Data
RegsL1 : :obj:`list`
:math:`L_1` regularization operators
x0 : :obj:`numpy.ndarray`, optional
Initial guess
niter_outer : :obj:`int`
Number of iterations of outer loop
niter_inner : :obj:`int`
Number of iterations of inner loop of first step of the Split Bregman
algorithm. A small number of iterations is generally sufficient and
for many applications optimal efficiency is obtained when only one
iteration is performed.
RegsL2 : :obj:`list`
Additional :math:`L_2` regularization operators
(if ``None``, :math:`L_2` regularization is not added to the problem)
dataregsL2 : :obj:`list`, optional
:math:`L_2` Regularization data (must have the same number of elements
of ``RegsL2`` or equal to ``None`` to use a zero data for every
regularization operator in ``RegsL2``)
mu : :obj:`float`, optional
Data term damping
epsRL1s : :obj:`list`
:math:`L_1` Regularization dampings (must have the same number of elements
as ``RegsL1``)
epsRL2s : :obj:`list`
:math:`L_2` Regularization dampings (must have the same number of elements
as ``RegsL2``)
tol : :obj:`float`, optional
Tolerance. Stop the solver if difference between inverted model
at subsequent iterations is smaller than ``tol``
rtol : :obj:`float`, optional
Relative tolerance on total cost function wrt initial total cost
function. Stops the solver when the ratio of the current total cost function
to the initial total cost function is below this value.
rtol1 : :obj:`float`, optional
Relative tolerance on total cost function wrt to data. Stops the solver when
the ratio of the current total cost function to the data norm is below this value.
tau : :obj:`float`, optional
Scaling factor in the Bregman update (must be close to 1)
restart : :obj:`bool`, optional
The unconstrained inverse problem in inner loop is initialized with
the initial guess (``True``) or with the last estimate (``False``)
engine : :obj:`str`, optional
Solver to use (``scipy`` or ``pylops``)
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.
show_inner : :obj:`bool`, optional
Display inner iteration logs of lsqr
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. Note that if ``y``
is a JAX array, this option is ignored and variables are not
pre-allocated since JAX does not support in-place operations.
**kwargs_lsqr
Arbitrary keyword arguments for
:py:func:`scipy.sparse.linalg.lsqr` solver used to solve the first
subproblem in the first step of the Split Bregman algorithm.
Returns
-------
xinv : :obj:`numpy.ndarray`
Inverted model
itn_out : :obj:`int`
Iteration number of outer loop upon termination
cost : :obj:`numpy.ndarray`, optional
History of the total cost function through iterations
Notes
-----
See :class:`pylops.optimization.cls_sparsity.SplitBregman`
"""
callbacks = [
CostNanInfCallback(),
]
if rtol > 0.0:
callbacks.append(CostToInitialCallback(rtol))
if rtol1 > 0.0:
callbacks.append(CostToDataCallback(rtol1))
sbsolve = SplitBregman(
Op,
callbacks=callbacks,
)
if callback is not None:
sbsolve.callback = callback
xinv, itn_out, cost = sbsolve.solve(
y,
RegsL1,
x0=x0,
niter_outer=niter_outer,
niter_inner=niter_inner,
RegsL2=RegsL2,
dataregsL2=dataregsL2,
mu=mu,
epsRL1s=epsRL1s,
epsRL2s=epsRL2s,
tol=tol,
tau=tau,
restart=restart,
engine=engine,
preallocate=preallocate,
show=show,
itershow=itershow,
show_inner=show_inner,
**kwargs_lsqr,
)
return xinv, itn_out, cost