diff --git a/docs/source/adding.rst b/docs/source/adding.rst index bc8087f..ff129c9 100644 --- a/docs/source/adding.rst +++ b/docs/source/adding.rst @@ -156,7 +156,7 @@ Once the operator has been created, we can add it to the documentation of PyProx the operator within the ``index.rst`` file in ``docs/source/api`` directory. Moreover, in order to facilitate the user of your operator by other users, a simple example should be provided as part of the -Sphinx-gallery of the documentation of the PyProximal library. The directory ``examples`` containes several scripts that +Sphinx-gallery of the documentation of the PyProximal library. The directory ``examples`` contains several scripts that can be used as template. diff --git a/docs/source/api/index.rst b/docs/source/api/index.rst index b8b92be..8ad19c2 100755 --- a/docs/source/api/index.rst +++ b/docs/source/api/index.rst @@ -75,6 +75,7 @@ Convex Orthogonal Quadratic Simplex + TV Non-Convex @@ -140,6 +141,8 @@ Primal HQS LinearizedADMM ProximalGradient + AcceleratedProximalGradient + GeneralizedProximalGradient ProximalPoint TwIST diff --git a/examples/plot_norms.py b/examples/plot_norms.py index bfeef4c..6d84553 100755 --- a/examples/plot_norms.py +++ b/examples/plot_norms.py @@ -123,4 +123,22 @@ plt.xlabel('x') plt.title(r'$||x||_1$') plt.legend() +plt.tight_layout() + +############################################################################### +# We consider now the TV norm. +TV = pyproximal.TV(dim=1, sigma=1.) + +x = np.arange(-1, 1, 0.1) +print('||x||_{TV}: ', l1(x)) + +tau = 0.5 +xp = TV.prox(x, tau) + +plt.figure(figsize=(7, 2)) +plt.plot(x, x, 'k', lw=2, label='x') +plt.plot(x, xp, 'r', lw=2, label='prox(x)') +plt.xlabel('x') +plt.title(r'$||x||_{TV}$') +plt.legend() plt.tight_layout() \ No newline at end of file diff --git a/pyproximal/optimization/primal.py b/pyproximal/optimization/primal.py index cd4e8ab..0d486c9 100644 --- a/pyproximal/optimization/primal.py +++ b/pyproximal/optimization/primal.py @@ -1,4 +1,5 @@ import time +import warnings import numpy as np from math import sqrt @@ -101,11 +102,12 @@ def ProximalPoint(prox, x0, tau, niter=10, callback=None, show=False): def ProximalGradient(proxf, proxg, x0, tau=None, beta=0.5, epsg=1., niter=10, niterback=100, + acceleration=None, callback=None, show=False): - r"""Proximal gradient + r"""Proximal gradient (optionnally accelerated) - Solves the following minimization problem using Proximal gradient - algorithm: + Solves the following minimization problem using (Accelerated) Proximal + gradient algorithm: .. math:: @@ -130,7 +132,7 @@ def ProximalGradient(proxf, proxg, x0, tau=None, beta=0.5, backtracking is used to adaptively estimate the best tau at each iteration. Finally note that :math:`\tau` can be chosen to be a vector when dealing with problems with multiple right-hand-sides - beta : obj:`float`, optional + beta : :obj:`float`, optional Backtracking parameter (must be between 0 and 1) epsg : :obj:`float` or :obj:`np.ndarray`, optional Scaling factor of g function @@ -138,6 +140,8 @@ def ProximalGradient(proxf, proxg, x0, tau=None, beta=0.5, Number of iterations of iterative scheme niterback : :obj:`int`, optional Max number of iterations of backtracking + acceleration: :obj:`str`, optional + Acceleration (``None``, ``vandenberghe`` or ``fista``) callback : :obj:`callable`, optional Function with signature (``callback(x)``) to call after each iteration where ``x`` is the current model vector @@ -151,12 +155,15 @@ def ProximalGradient(proxf, proxg, x0, tau=None, beta=0.5, Notes ----- - The Proximal point algorithm can be expressed by the following recursion: + The (Accelerated) Proximal point algorithm can be expressed by the + following recursion: .. math:: - \mathbf{x}^{k+1} = \prox_{\tau^k \epsilon g}(\mathbf{x}^k - - \tau^k \nabla f(\mathbf{x}^k)) + \mathbf{y}^{k+1} = \mathbf{x}^k + \omega^k + (\mathbf{x}^k - \mathbf{x}^{k-1}) + \mathbf{x}^{k+1} = \prox_{\tau^k \epsilon g}(\mathbf{y}^{k+1} - + \tau^k \nabla f(\mathbf{y}^{k+1})) \\ where at each iteration :math:`\tau^k` can be estimated by back-tracking as follows: @@ -173,7 +180,17 @@ def ProximalGradient(proxf, proxg, x0, tau=None, beta=0.5, where :math:`\tilde{f}_\tau(\mathbf{x}, \mathbf{y}) = f(\mathbf{y}) + \nabla f(\mathbf{y})^T (\mathbf{x} - \mathbf{y}) + - 1/(2\tau)||\mathbf{x} - \mathbf{y}||_2^2`. + 1/(2\tau)||\mathbf{x} - \mathbf{y}||_2^2`, + and + :math:`\omega^k = 0` for ``acceleration=None``, + :math:`\omega^k = k / (k + 3)` for ``acceleration=vandenberghe`` [1]_ + or :math:`\omega^k = (t_{k-1}-1)/t_k` for ``acceleration=fista`` where + :math:`t_k = (1 + \sqrt{1+4t_{k-1}^{2}}) / 2` [2]_ + + .. [1] Vandenberghe, L., "Fast proximal gradient methods", 2010. + .. [2] Beck, A., and Teboulle, M. "A Fast Iterative Shrinkage-Thresholding + Algorithm for Linear Inverse Problems", SIAM Journal on + Imaging Sciences, vol. 2, pp. 183-202. 2009. """ # check if epgs is a ve @@ -182,9 +199,12 @@ def ProximalGradient(proxf, proxg, x0, tau=None, beta=0.5, else: epsg_print = 'Multi' + if acceleration not in [None, 'None', 'vandenberghe', 'fista']: + raise NotImplementedError('Acceleration should be None, vandenberghe ' + 'or fista') if show: tstart = time.time() - print('Proximal Gradient\n' + print('Accelerated Proximal Gradient\n' '---------------------------------------------------------\n' 'Proximal operator (f): %s\n' 'Proximal operator (g): %s\n' @@ -201,13 +221,32 @@ def ProximalGradient(proxf, proxg, x0, tau=None, beta=0.5, backtracking = True tau = 1. + # initialize model + t = 1. x = x0.copy() + y = x.copy() + + # iterate for iiter in range(niter): + xold = x.copy() + + # proximal step if not backtracking: - x = proxg.prox(x - tau * proxf.grad(x), epsg * tau) + x = proxg.prox(y - tau * proxf.grad(y), epsg * tau) else: - x, tau = _backtracking(x, tau, proxf, proxg, epsg, + x, tau = _backtracking(y, tau, proxf, proxg, epsg, beta=beta, niterback=niterback) + + # update y + if acceleration == 'vandenberghe': + omega = iiter / (iiter + 3) + elif acceleration == 'fista': + told = t + t = (1. + np.sqrt(1. + 4. * t ** 2)) / 2. + omega = ((told - 1.) / t) + else: + omega = 0 + y = x + omega * (x - xold) # run callback if callback is not None: @@ -233,40 +272,57 @@ def AcceleratedProximalGradient(proxf, proxg, x0, tau=None, beta=0.5, callback=None, show=False): r"""Accelerated Proximal gradient - Solves the following minimization problem using Accelerated Proximal + This is a thin wrapper around :func:`pyproximal.optimization.primal.ProximalGradient` with + ``vandenberghe`` or ``fista``acceleration. See :func:`pyproximal.optimization.primal.ProximalGradient` + for details. + + """ + warnings.warn('AcceleratedProximalGradient has been integrated directly into ProximalGradient ' + 'from v0.5.0. It is recommended to start using ProximalGradient by selecting the ' + 'appropriate acceleration parameter as this behaviour will become default in ' + 'version v1.0.0 and AcceleratedProximalGradient will be removed.', FutureWarning) + return ProximalGradient(proxf, proxg, x0, tau=tau, beta=beta, + epsg=epsg, niter=niter, niterback=niterback, + acceleration=acceleration, + callback=callback, show=show) + + +def GeneralizedProximalGradient(proxfs, proxgs, x0, tau=None, + epsg=1., niter=10, + acceleration=None, + callback=None, show=False): + r"""Generalized Proximal gradient + + Solves the following minimization problem using Generalized Proximal gradient algorithm: .. math:: - \mathbf{x} = \argmin_\mathbf{x} f(\mathbf{x}) + \epsilon g(\mathbf{x}) + \mathbf{x} = \argmin_\mathbf{x} \sum_{i=1}^n f_i(\mathbf{x}) + + \sum_{j=1}^m \tau_j g_j(\mathbf{x}),~~n,m \in \mathbb{N}^+ - where :math:`f(\mathbf{x})` is a smooth convex function with a uniquely - defined gradient and :math:`g(\mathbf{x})` is any convex function that - has a known proximal operator. + where the :math:`f_i(\mathbf{x})` are smooth convex functions with a uniquely + defined gradient and the :math:`g_j(\mathbf{x})` are any convex function that + have a known proximal operator. Parameters ---------- - proxf : :obj:`pyproximal.ProxOperator` - Proximal operator of f function (must have ``grad`` implemented) - proxg : :obj:`pyproximal.ProxOperator` - Proximal operator of g function + proxfs : :obj:`List of pyproximal.ProxOperator` + Proximal operators of the :math:`f_i` functions (must have ``grad`` implemented) + proxgs : :obj:`List of pyproximal.ProxOperator` + Proximal operators of the :math:`g_j` functions x0 : :obj:`numpy.ndarray` Initial vector tau : :obj:`float` or :obj:`numpy.ndarray`, optional Positive scalar weight, which should satisfy the following condition to guarantees convergence: :math:`\tau \in (0, 1/L]` where ``L`` is - the Lipschitz constant of :math:`\nabla f`. When ``tau=None``, + the Lipschitz constant of :math:`\sum_{i=1}^n \nabla f_i`. When ``tau=None``, backtracking is used to adaptively estimate the best tau at each - iteration. Finally note that :math:`\tau` can be chosen to be a vector - when dealing with problems with multiple right-hand-sides - beta : obj:`float`, optional - Backtracking parameter (must be between 0 and 1) + iteration. epsg : :obj:`float` or :obj:`np.ndarray`, optional Scaling factor of g function niter : :obj:`int`, optional Number of iterations of iterative scheme - niterback : :obj:`int`, optional - Max number of iterations of backtracking acceleration: :obj:`str`, optional Acceleration (``vandenberghe`` or ``fista``) callback : :obj:`callable`, optional @@ -282,76 +338,75 @@ def AcceleratedProximalGradient(proxf, proxg, x0, tau=None, beta=0.5, Notes ----- - The Accelerated Proximal point algorithm can be expressed by the + The Generalized Proximal point algorithm can be expressed by the following recursion: .. math:: - - \mathbf{x}^{k+1} = \prox_{\tau^k \epsilon g}(\mathbf{y}^{k+1} - - \tau^k \nabla f(\mathbf{y}^{k+1})) \\ - \mathbf{y}^{k+1} = \mathbf{x}^k + \omega^k - (\mathbf{x}^k - \mathbf{x}^{k-1}) - - where :math:`\omega^k = k / (k + 3)` for ``acceleration=vandenberghe`` [1]_ - or :math:`\omega^k = (t_{k-1}-1)/t_k` for ``acceleration=fista`` where - :math:`t_k = (1 + \sqrt{1+4t_{k-1}^{2}}) / 2` [2]_ - - .. [1] Vandenberghe, L., "Fast proximal gradient methods", 2010. - .. [2] Beck, A., and Teboulle, M. "A Fast Iterative Shrinkage-Thresholding - Algorithm for Linear Inverse Problems", SIAM Journal on - Imaging Sciences, vol. 2, pp. 183-202. 2009. - + \text{for } j=1,\cdots,n, \\ + ~~~~\mathbf z_j^{k+1} = \mathbf z_j^{k} + \eta_k (prox_{\frac{\tau^k}{\omega_j} g_j}(2 \mathbf{x}^{k} - z_j^{k}) + - \tau^k \sum_{i=1}^n \nabla f_i(\mathbf{x}^{k})) - \mathbf{x}^{k} \\ + \mathbf{x}^{k+1} = \sum_{j=1}^n \omega_j f_j \\ + + where :math:`\sum_{j=1}^n \omega_j=1`. """ - # check if epgs is a ve + # check if epgs is a vector if np.asarray(epsg).size == 1.: epsg_print = str(epsg) else: epsg_print = 'Multi' - if acceleration not in ['vandenberghe', 'fista']: - raise NotImplementedError('Acceleration should be vandenberghe ' + if acceleration not in [None, 'None', 'vandenberghe', 'fista']: + raise NotImplementedError('Acceleration should be None, vandenberghe ' 'or fista') if show: tstart = time.time() - print('Accelerated Proximal Gradient\n' + print('Generalized Proximal Gradient\n' '---------------------------------------------------------\n' - 'Proximal operator (f): %s\n' - 'Proximal operator (g): %s\n' - 'tau = %10e\tepsg = %s\tniter = %d\n' % (type(proxf), - type(proxg), - 0 if tau is None else tau, + 'Proximal operators (f): %s\n' + 'Proximal operators (g): %s\n' + 'tau = %10e\nepsg = %s\tniter = %d\n' % ([type(proxf) for proxf in proxfs], + [type(proxg) for proxg in proxgs], + 0 if tau is None else tau, epsg_print, niter)) head = ' Itn x[0] f g J=f+eps*g' print(head) - backtracking = False if tau is None: - backtracking = True tau = 1. # initialize model t = 1. x = x0.copy() y = x.copy() + zs = [x.copy() for _ in range(len(proxgs))] # iterate for iiter in range(niter): xold = x.copy() # proximal step - if not backtracking: - x = proxg.prox(y - tau * proxf.grad(y), epsg * tau) - else: - x, tau = _backtracking(y, tau, proxf, proxg, epsg, - beta=beta, niterback=niterback) + grad = np.zeros_like(x) + for i, proxf in enumerate(proxfs): + grad += proxf.grad(x) + + sol = np.zeros_like(x) + for i, proxg in enumerate(proxgs): + tmp = 2 * y - zs[i] - tau * grad + tmp[:] = proxg.prox(tmp, tau *len(proxgs) ) + zs[i] += epsg * (tmp - y) + sol += zs[i] / len(proxgs) + x[:] = sol.copy() # update y if acceleration == 'vandenberghe': omega = iiter / (iiter + 3) - else: + elif acceleration== 'fista': told = t t = (1. + np.sqrt(1. + 4. * t ** 2)) / 2. omega = ((told - 1.) / t) + else: + omega = 0 + y = x + omega * (x - xold) # run callback @@ -360,7 +415,7 @@ def AcceleratedProximalGradient(proxf, proxg, x0, tau=None, beta=0.5, if show: if iiter < 10 or niter - iiter < 10 or iiter % (niter // 10) == 0: - pf, pg = proxf(x), proxg(x) + pf, pg = np.sum([proxf(x) for proxf in proxfs]), np.sum([proxg(x) for proxg in proxgs]) msg = '%6g %12.5e %10.3e %10.3e %10.3e' % \ (iiter + 1, x[0] if x.ndim == 1 else x[0, 0], pf, pg[0] if epsg_print == 'Multi' else pg, diff --git a/pyproximal/proximal/L21.py b/pyproximal/proximal/L21.py index c7064d8..8cc41d9 100644 --- a/pyproximal/proximal/L21.py +++ b/pyproximal/proximal/L21.py @@ -11,7 +11,7 @@ class L21(ProxOperator): Parameters ---------- - ndims : :obj:`int` + ndim : :obj:`int` Number of dimensions :math:`N_{dim}`. Used to reshape the input array in a matrix of size :math:`N_{dim} \times N'_{x}` where :math:`N'_x = \frac{N_x}{N_{dim}}`. Note that the input diff --git a/pyproximal/proximal/TV.py b/pyproximal/proximal/TV.py new file mode 100644 index 0000000..6ac2df8 --- /dev/null +++ b/pyproximal/proximal/TV.py @@ -0,0 +1,270 @@ +import numpy as np + +from copy import deepcopy +from scipy.sparse.linalg import lsqr +from pylops import FirstDerivative, Gradient +from pyproximal.ProxOperator import _check_tau +from pyproximal import ProxOperator + + +class TV(ProxOperator): + r"""TV Norm proximal operator. + + Proximal operator for the TV norm defined as: :math:`f(\mathbf{x}) = + \sigma ||\mathbf{x}||_{\text{TV}}`. + + Parameters + ---------- + dims : :obj:`tuple` + Number of samples for each dimension + (``None`` if only one dimension is available) + sigma : :obj:`int`, optional + Multiplicative coefficient of TV norm + niter : :obj:`int` or :obj:`func`, optional + Number of iterations of iterative scheme used to compute the proximal. + This can be a constant number or a function that is called passing a + counter which keeps track of how many times the ``prox`` method has + been invoked before and returns the ``niter`` to be used. + rtol : :obj:`float`, optional + Relative tolerance for stopping criterion. + + Notes + ----- + The proximal algorithm is implemented following [1]. + + .. [1] Beck, A. and Teboulle, M., "Fast gradient-based algorithms for constrained total + variation image denoising and deblurring problems", 2009. + """ + def __init__(self, dims, sigma=1., niter=10, rtol=1e-4, **kwargs): + super().__init__(None, True) + self.dims = dims + self.ndim = len(dims) + self.sigma = sigma + self.niter = niter + self.count = 0 + self.rtol = rtol + self.kwargs = kwargs + + def __call__(self, x): + x = x.reshape(self.dims) + if self.ndim == 1: + derivOp = FirstDerivative(self.dims[0], dims=None, dir=0, edge=False, + dtype=x.dtype, kind="forward") + dx = derivOp @ x + y = np.sum(np.abs(dx), axis=0) + elif self.ndim >= 2: + y = 0 + grads = [] + gradOp = Gradient(self.dims, edge=False, dtype=x.dtype, kind="forward") + grads = gradOp.matvec(x.ravel()) + grads = grads.reshape((self.ndim, ) + self.dims) + for g in grads: + y += np.power(abs(g), 2) + y = np.sqrt(y) + return self.sigma * np.sum(y) + + def _increment_count(func): + """Increment counter + """ + def wrapped(self, *args, **kwargs): + self.count += 1 + return func(self, *args, **kwargs) + return wrapped + + @_increment_count + @_check_tau + def prox(self, x, tau): + # define current number of iterations + if isinstance(self.niter, int): + niter = self.niter + else: + niter = self.niter(self.count) + + gamma = self.sigma * tau + rtol = self.rtol + # TODO implement test_gamma + + # Initialization + x = x.reshape(self.dims) + sol = x + if self.ndim == 1: + derivOp = FirstDerivative(self.dims[0], dims=None, dir=0, edge=False, + dtype=x.dtype, kind="forward") + else: + gradOp = Gradient(x.shape, edge=False, dtype=x.dtype, kind="forward") + + if self.ndim == 1: + r = derivOp @ (x * 0) + rr = deepcopy(r) + elif self.ndim == 2: + r, s = gradOp.matvec( (x * 0).ravel()).reshape((self.ndim, ) + x.shape) + rr, ss = deepcopy(r), deepcopy(s) + elif self.ndim == 3: + r, s, k = gradOp.matvec( (x*0).ravel()).reshape((self.ndim, ) + x.shape) + rr, ss, kk = deepcopy(r), deepcopy(s), deepcopy(k) + elif self.ndim == 4: + r, s, k, u = gradOp.matvec( (x*0).ravel()).reshape((self.ndim, ) + x.shape) + rr, ss, kk, uu = deepcopy(r), deepcopy(s), deepcopy(k), deepcopy(u) + + if self.ndim >= 1: + pold = r + if self.ndim >= 2: + qold = s + if self.ndim >= 3: + oold = k + if self.ndim >= 4: + mold = u + + told, prev_obj = 1., 0. + + # Initialization for weights + if self.ndim >= 1: + try: + wx = self.kwargs["wx"] + except (KeyError, TypeError): + wx = 1. + if self.ndim >= 2: + try: + wy = self.kwargs["wy"] + except (KeyError, TypeError): + wy = 1. + if self.ndim >= 3: + try: + wz = self.kwargs["wz"] + except (KeyError, TypeError): + wz = 1. + if self.ndim >= 4: + try: + wt = self.kwargs["wt"] + except (KeyError, TypeError): + wt = 1. + + if self.ndim == 1: + mt = wx + elif self.ndim == 2: + mt = np.maximum(wx, wy) + elif self.ndim == 3: + mt = np.maximum(wx, np.maximum(wy, wz)) + elif self.ndim == 4: + mt = np.maximum(np.maximum(wx, wy), np.maximum(wz, wt)) + + if self.ndim >= 1: + try: + rr *= np.conjugate(wx) + except KeyError: + pass + if self.ndim >= 2: + try: + ss *= np.conjugate(wy) + except KeyError: + pass + if self.ndim >= 3: + try: + kk *= np.conjugate(wz) + except KeyError: + pass + if self.ndim >= 4: + try: + uu *= np.conjugate(wt) + except KeyError: + pass + + iter = 0 + while iter <= niter: + # Current Solution + if self.ndim == 0: + raise ValueError("Need to input at least one value") + + if self.ndim >= 1: + div = np.concatenate((np.expand_dims(rr[0, ], axis=0), + rr[1:-1, ] - rr[:-2, ], + -np.expand_dims(rr[-2, ], axis=0)), + axis=0) + if self.ndim >= 2: + div += np.concatenate((np.expand_dims(ss[:, 0, ], axis=1), + ss[:, 1:-1, ] - ss[:, :-2, ], + -np.expand_dims(ss[:, -2, ], axis=1)), + axis=1) + if self.ndim >= 3: + div += np.concatenate((np.expand_dims(kk[:, :, 0, ], axis=2), + kk[:, :, 1:-1, ] - kk[:, :, :-2, ], + -np.expand_dims(kk[:, :, -2, ], axis=2)), + axis=2) + if self.ndim >= 4: + div += np.concatenate((np.expand_dims(uu[:, :, :, 0, ], axis=3), + uu[:, :, :, 1:-1, ] - uu[:, :, :, :-2, ], + -np.expand_dims(uu[:, :, :, -2, ], axis=3)), + axis=3) + sol = x - gamma * div + + # Objective function value + obj = 0.5 * np.power(np.linalg.norm(x[:] - sol[:]), 2) + \ + gamma * np.sum(self.__call__(sol), axis=0) + if obj > 1e-10: + rel_obj = np.abs(obj - prev_obj) / obj + else: + rel_obj = 2 * rtol + prev_obj = obj + + # Stopping criterion + if rel_obj < rtol: + break + + # Update divergence vectors and project + if self.ndim == 1: + dx = derivOp(sol) + r -= 1. / (4 * gamma * mt**2) * dx + weights = np.maximum(1, np.abs(r)) + elif self.ndim == 2: + dx, dy = gradOp.matvec(sol.ravel()).reshape((self.ndim, ) + x.shape) + r -= (1. / (8. * gamma * mt**2.)) * dx + s -= (1. / (8. * gamma * mt**2.)) * dy + weights = np.maximum(1, np.sqrt(np.power(np.abs(r), 2) + + np.power(np.abs(s), 2))) + elif self.ndim == 3: + dx, dy, dz = gradOp.matvec(sol.ravel()).reshape((self.ndim, ) + x.shape) + r -= 1. / (12. * gamma * mt**2) * dx + s -= 1. / (12. * gamma * mt**2) * dy + k -= 1. / (12. * gamma * mt**2) * dz + weights = np.maximum(1, np.sqrt(np.power(np.abs(r), 2) + + np.power(np.abs(s), 2) + + np.power(np.abs(k), 2))) + elif self.ndim == 4: + dx, dy, dz, dt = gradOp.matvec(sol.ravel()).reshape((self.ndim, ) + x.shape) + r -= 1. / (16 * gamma * mt**2) * dx + s -= 1. / (16 * gamma * mt**2) * dy + k -= 1. / (16 * gamma * mt**2) * dz + u -= 1. / (16 * gamma * mt**2) * dt + weights = np.maximum(1, np.sqrt(np.power(np.abs(r), 2) + + np.power(np.abs(s), 2) + + np.power(np.abs(k), 2) + + np.power(np.abs(u), 2))) + + # FISTA update + t = (1 + np.sqrt(4 * told**2)) / 2. + + if self.ndim >= 1: + p = r / weights + r = p + (told - 1) / t * (p - pold) + pold = p + rr = deepcopy(r) + if self.ndim >= 2: + q = s / weights + s = q + (told - 1) / t * (q - qold) + qold = q + ss = deepcopy(s) + if self.ndim >= 3: + o = k / weights + k = o + (told - 1) / t * (o - oold) + oold = o + kk = deepcopy(k) + if self.ndim >= 4: + m = u / weights + u = m + (told - 1) / t * (m - mold) + mold = m + uu = deepcopy(u) + + told = t + iter += 1 + + return sol.ravel() diff --git a/pyproximal/proximal/__init__.py b/pyproximal/proximal/__init__.py index fa933a4..eebf544 100644 --- a/pyproximal/proximal/__init__.py +++ b/pyproximal/proximal/__init__.py @@ -7,20 +7,21 @@ Box Box indicator Simplex Simplex indicator Intersection Intersection indicator - AffineSet Affines set indicator + AffineSet Affines set indicator Quadratic Quadratic function - Nonlinear Nonlinear function + Nonlinear Nonlinear function L0 L0 Norm L0Ball L0 Ball L1 L1 Norm L1Ball L1 Ball - Euclidean Euclidean Norm - EuclideanBall Euclidean Ball + Euclidean Euclidean Norm + EuclideanBall Euclidean Ball L2 L2 Norm L2Convolve L2 Norm of convolution operator L21 L2,1 Norm L21_plus_L1 L2,1 + L1 mixed-norm - Huber Huber Norm + Huber Huber Norm + TV Total Variation Norm Nuclear Nuclear Norm NuclearBall Nuclear Ball Orthogonal Product between orthogonal operator and vector @@ -47,6 +48,7 @@ from .L21 import * from .L21_plus_L1 import * from .Huber import * +from .TV import * from .Nuclear import * from .Orthogonal import * from .VStack import * @@ -59,6 +61,6 @@ __all__ = ['Box', 'Simplex', 'Intersection', 'AffineSet', 'Quadratic', 'Euclidean', 'EuclideanBall', 'L0', 'L0Ball', 'L1', 'L1Ball', 'L2', - 'L2Convolve', 'L21', 'L21_plus_L1', 'Huber', 'Nuclear', + 'L2Convolve', 'L21', 'L21_plus_L1', 'Huber', 'TV', 'Nuclear', 'NuclearBall', 'Orthogonal', 'VStack', 'Nonlinear', 'SCAD', - 'Log', 'ETP', 'Geman', 'QuadraticEnvelopeCard', 'SingularValuePenalty'] + 'Log', 'ETP', 'Geman', 'QuadraticEnvelopeCard', 'SingularValuePenalty'] \ No newline at end of file diff --git a/pytests/test_norms.py b/pytests/test_norms.py index 60be030..add2afb 100644 --- a/pytests/test_norms.py +++ b/pytests/test_norms.py @@ -3,9 +3,9 @@ import numpy as np from numpy.testing import assert_array_almost_equal -from pylops.basicoperators import Identity, Diagonal, MatrixMult +from pylops.basicoperators import Identity, Diagonal, MatrixMult, FirstDerivative from pyproximal.utils import moreau -from pyproximal.proximal import Box, Euclidean, L2, L1, L21, L21_plus_L1, Huber, Nuclear +from pyproximal.proximal import Box, Euclidean, L2, L1, L21, L21_plus_L1, Huber, Nuclear, TV par1 = {'nx': 10, 'sigma': 1., 'dtype': 'float32'} # even float32 par2 = {'nx': 11, 'sigma': 2., 'dtype': 'float64'} # odd float64 @@ -189,6 +189,18 @@ def test_Huber(par): assert moreau(hub, x, tau) +@pytest.mark.parametrize("par", [(par1), (par2)]) +def test_TV(par): + """TV norm of x and proximal + """ + tv = TV(dims=(par['nx'], ), sigma=par['sigma']) + # norm + x = np.random.normal(0., 1., par['nx']).astype(par['dtype']) + derivOp = FirstDerivative(par['nx'], dtype=par['dtype'], kind='forward') + dx = derivOp @ x + assert_array_almost_equal(tv(x), par['sigma'] * np.sum(np.abs(dx), axis=0)) + + def test_Nuclear_FOM(): """Nuclear norm benchmark with FOM solver """ @@ -228,7 +240,7 @@ def test_Weighted_Nuclear(par): # the exact same singular values) X = np.random.uniform(0., 0.1, (par['nx'], 2 * par['nx'])).astype(par['dtype']) S = np.linalg.svd(X, compute_uv=False) - assert (nucl(X.ravel()) - np.sum(weights[:S.size] * S)) < 1e-3 + assert (nucl(X.ravel()) - np.sum(weights[:S.size] * S)) < 1e-2 # prox / dualprox tau = 2. diff --git a/tutorials/denoising.py b/tutorials/denoising.py index 7a57dd0..3c24c1e 100644 --- a/tutorials/denoising.py +++ b/tutorials/denoising.py @@ -95,7 +95,18 @@ iml1 = iml1.reshape(img.shape) -# Isotropic TV +# Isotropic TV with Proximal Gradient +sigma = .1 +tv = pyproximal.TV(dims=img.shape, sigma=sigma) + +# Solve +tau = 1 / L + +imtv = pyproximal.optimization.primal.ProximalGradient(l2, tv, tau=tau, x0=np.zeros_like(img.ravel()), + niter=100) +imtv = imtv.reshape(img.shape) + +# Isotropic TV with Primal Dual sigma = .1 l1iso = pyproximal.L21(ndim=2, sigma=sigma) @@ -109,23 +120,27 @@ niter=100) iml12 = iml12.reshape(img.shape) -fig, axs = plt.subplots(2, 2, figsize=(14, 14)) -axs[0][0].imshow(img, cmap='gray', vmin=0, vmax=1) -axs[0][0].set_title('Original') -axs[0][0].axis('off') -axs[0][0].axis('tight') -axs[0][1].imshow(noise_img, cmap='gray', vmin=0, vmax=1) -axs[0][1].set_title('Noisy') -axs[0][1].axis('off') -axs[0][1].axis('tight') -axs[1][0].imshow(iml1, cmap='gray', vmin=0, vmax=1) -axs[1][0].set_title('TVaniso') -axs[1][0].axis('off') -axs[1][0].axis('tight') -axs[1][1].imshow(iml12, cmap='gray', vmin=0, vmax=1) -axs[1][1].set_title('TViso') -axs[1][1].axis('off') -axs[1][1].axis('tight') +fig, axs = plt.subplots(1, 5, figsize=(14, 4)) +axs[0].imshow(img, cmap='gray', vmin=0, vmax=1) +axs[0].set_title('Original') +axs[0].axis('off') +axs[0].axis('tight') +axs[1].imshow(noise_img, cmap='gray', vmin=0, vmax=1) +axs[1].set_title('Noisy') +axs[1].axis('off') +axs[1].axis('tight') +axs[2].imshow(iml1, cmap='gray', vmin=0, vmax=1) +axs[2].set_title('TVaniso') +axs[2].axis('off') +axs[2].axis('tight') +axs[3].imshow(imtv, cmap='gray', vmin=0, vmax=1) +axs[3].set_title('TViso (with ProxGrad)') +axs[3].axis('off') +axs[3].axis('tight') +axs[4].imshow(iml12, cmap='gray', vmin=0, vmax=1) +axs[4].set_title('TViso (with PD)') +axs[4].axis('off') +axs[4].axis('tight') plt.tight_layout() ############################################################################### @@ -196,4 +211,4 @@ axs[1][1].set_title('L1data + TViso') axs[1][1].axis('off') axs[1][1].axis('tight') -plt.tight_layout() +plt.tight_layout() \ No newline at end of file diff --git a/tutorials/lowrankcompletion.py b/tutorials/lowrankcompletion.py index 61e982b..c9e3e0d 100644 --- a/tutorials/lowrankcompletion.py +++ b/tutorials/lowrankcompletion.py @@ -42,6 +42,7 @@ from scipy import misc +np.random.seed(0) plt.close('all') ############################################################################### @@ -92,8 +93,8 @@ f = pyproximal.L2(Rop, y) g = pyproximal.Nuclear((ny, nx), mu) -Xpg = pyproximal.optimization.primal.AcceleratedProximalGradient(f, g, np.zeros(ny*nx), - tau=1., niter=100, show=True) +Xpg = pyproximal.optimization.primal.ProximalGradient(f, g, np.zeros(ny*nx), acceleration='vandenberghe', + tau=1., niter=100, show=True) Xpg = Xpg.reshape(ny, nx) # Recompute SVD and see how the singular values look like @@ -104,8 +105,8 @@ mu1 = 0.8 * np.sum(Sx) g = pyproximal.proximal.NuclearBall((ny, nx), mu1) -Xpgc = pyproximal.optimization.primal.AcceleratedProximalGradient(f, g, np.zeros(ny*nx), - tau=1., niter=100, show=True) +Xpgc = pyproximal.optimization.primal.ProximalGradient(f, g, np.zeros(ny*nx), acceleration='vandenberghe', + tau=1., niter=100, show=True) Xpgc = Xpgc.reshape(ny, nx) # Recompute SVD and see how the singular values look like diff --git a/tutorials/totalvariation.ipynb b/tutorials/totalvariation.ipynb new file mode 100644 index 0000000..34dda25 --- /dev/null +++ b/tutorials/totalvariation.ipynb @@ -0,0 +1,565 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"\n", + " Copyright (c) 2021 Olivier Leblanc\n", + "\n", + " Permission is hereby granted, free of charge, to any person obtaining\n", + " a copy of this software and associated documentation files (the \"Software\"),\n", + " to deal in the Software without restriction, including without limitation\n", + " the rights to use, copy, modify, merge, publish, distribute, and to permit\n", + " persons to whom the Software is furnished to do so.\n", + " However, anyone intending to sublicense and/or sell copies of the Software\n", + " will require the official permission of the author.\n", + " ----------------------------------------------------------------------------\n", + "\n", + " Author : Olivier Leblanc\n", + " Date : 06/06/2022\n", + "\n", + " Code description :\n", + " __________________\n", + " Consider the inverse problem of a noisy linear forward model: y=Ax+n.\n", + " Calibrate the choice of the regularization parameters using PyProximal.\n", + "\"\"\"\n", + "import matplotlib.pyplot as plt\n", + "import types\n", + "import numpy as np\n", + "import pylops\n", + "import pyproximal" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Define some Util functions" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "snr = lambda ref, rec : 20*np.log10(np.linalg.norm(ref)/np.linalg.norm(rec-ref))\n", + "\n", + "def eval_nu(init, A, A_star=None, nb_iter = 10):\n", + " \"\"\"Estimate the square norm of the operator B (i.e. ||B||^2) thanks to the power method.\n", + " Useful to bound the norm of B, i.e. give |B(x)|^2 \\leq \\nu |x|^2`.\n", + " \"\"\" \n", + " if isinstance(A, types.FunctionType):\n", + " if (A_star is None):\n", + " raise ValueError(\"A_star must be provided\")\n", + " A_op = A\n", + " At_op = A_star\n", + " else:\n", + " if (A_star is None):\n", + " A_star = A.T\n", + " A_op = lambda x: A@x\n", + " At_op = lambda y: A_star@y\n", + "\n", + " u = init\n", + " for k in range(nb_iter):\n", + " u = u/np.linalg.norm(u) # Normalize current matrix\n", + " u = At_op(A_op(u)) \n", + " return np.linalg.norm(u)\n", + "\n", + "def subplot_axs(fig,ny,nx):\n", + " \"\"\"Create all axes for a subplot.\n", + "\n", + " Args:\n", + " fig (figure) : the figure onto creating the axes.\n", + " ny (int) : number of subplots along y-axis.\n", + " nx (int) : number of subplots along x-axis.\n", + "\n", + " Returns:\n", + " axs (array of axes) : all the created axes.\n", + "\n", + " \"\"\"\n", + " axs=[None]*nx*ny\n", + " wx = (1/(1.5*nx))\n", + " wy = (1/(1.5*ny))\n", + "\n", + " for j in np.arange(ny):\n", + " for i in np.arange(nx):\n", + " axs[j*nx+i] = fig.add_axes([i/nx, 1-j/ny, wx, wy])\n", + "\n", + " if (ny>1 and nx>1):\n", + " axs=np.asarray(axs).reshape(ny,nx) # create 2D axis if necessary\n", + "\n", + " return axs\n", + "\n", + "def show_objective(ax, objective, labels, log_scale=True, linewidth=1.2):\n", + " \"\"\"Subplot the objective vs iteration in axis `ax`.\n", + "\n", + " Args:\n", + " ax (axis) : axis onto plot.\n", + " objective (float array) : objective function values vs iteration\n", + " labels (list) : list of labels for each dimension in `objective`.\n", + " log_scale (bool) : whether to plot in log scale.\n", + " line_width (float) : line width.\n", + "\n", + " Returns:\n", + " /\n", + "\n", + " \"\"\"\n", + "\n", + " objective=np.real(np.array(objective))\n", + " nplots = objective.ndim\n", + "\n", + " if nplots==1:\n", + " objective = objective[..., np.newaxis]\n", + " else:\n", + " ax.plot(np.sum(objective, axis=1), label='Global objective')\n", + "\n", + " if (labels is not None and len(labels)==nplots):\n", + " for i in range(nplots):\n", + " ax.plot(objective[:, i], label=labels[i])\n", + " else:\n", + " for i in range(nplots):\n", + " ax.plot(objective[:, i], label='Objective'+str(i))\n", + "\n", + " for line in ax.lines:\n", + " line.set_linewidth(linewidth)\n", + " ax.set_ylim(1e-30)\n", + " ax.grid(True)\n", + " ax.set_title('Convergence')\n", + " ax.legend()\n", + " ax.set_xlabel('Iteration number')\n", + " ax.set_ylabel('Objective function value')\n", + " if (log_scale):\n", + " ax.set_yscale('log')\n", + " \n", + "\n", + "def show_rec1D(x, xhat, objective=None, log_scale=True, labels=None, show_Fourier=False, linewidth=1.2):\n", + " \"\"\"Subplot GT x, reconstruction xhat, and eventually the objective vs iteration and Fourier transforms.\n", + " Also print the SNR of reconstruction.\n", + "\n", + " Args:\n", + " x (1D array) : Ground truth \n", + " xhat (1D array) : Reconstruction of X \n", + " objective (float array) : objective function values vs iteration\n", + " show_Fourier (bool) : if True, plot Fourier transforms\n", + "\n", + " Returns:\n", + " /\n", + "\n", + " \"\"\"\n", + " fig=plt.figure(figsize=(12,4))\n", + "\n", + " if (objective is None):\n", + " axs=subplot_axs(fig, 1,1)\n", + "\n", + " else:\n", + " objective=np.real(np.array(objective))\n", + " nplots = objective.ndim\n", + " axs = [fig.add_subplot(1, 2, i+1) for i in range(2)]\n", + " show_objective(axs[1], objective=objective, labels=labels, log_scale=log_scale, linewidth=linewidth)\n", + "\n", + "\n", + " axs[0].plot(x, 'o', label='Original')\n", + " axs[0].plot(xhat, 'xr', label='Reconstructed')\n", + " axs[0].grid(True)\n", + " axs[0].set_title('Achieved reconstruction')\n", + " axs[0].legend(numpoints=1)\n", + " axs[0].set_xlabel('Signal dimension number')\n", + " axs[0].set_ylabel('Signal value')\n", + " plt.show()\n", + "\n", + " if (show_Fourier):\n", + " \"Compare reconstructions in Fourier domain\"\n", + " fig=plt.figure(figsize=(8,3))\n", + " axs = subplot_axs(fig, 1,2)\n", + " axs[0].plot(np.abs(np.fft.fftshift(np.fft.fft2(x))))\n", + " axs[1].plot(np.abs(np.fft.fftshift(np.fft.fft2(xhat))))\n", + " axs[0].set_title('Ground truth FFT')\n", + " axs[1].set_title('Reconstruction FFT')\n", + " plt.show()\n", + "\n", + " print('SNR: {:.2f} dB'.format(snr(x, xhat)) )" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "$\n", + " \\boldsymbol y = \\boldsymbol{Ax} \n", + "$" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "\"Set the parameters of the problem\"\n", + "np.random.seed(0)\n", + "N = 500\n", + "M = 100\n", + "maxit=500\n", + "maxit_prox_TV = 80\n", + "rtol=1e-8\n", + "\n", + "x = np.zeros(N)\n", + "n_piece = 4\n", + "randvals = np.random.randint(30, size=n_piece) \n", + "cuts = np.sort((np.random.rand(n_piece-1)*N).astype(int))\n", + "x[:cuts[0]] = randvals[0]\n", + "x[cuts[-1]:] = randvals[-1]\n", + "for i in range(n_piece-2):\n", + " x[cuts[i]:cuts[i+1]] = randvals[i+1]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lagrangian formulation\n", + "\n", + "$\n", + " \\boldsymbol x^* = \\argmin_{\\boldsymbol u}~\\frac{1}{2} \\lVert \\boldsymbol y - \\boldsymbol{Au} \\rVert^2 + \\lambda \\lVert \\boldsymbol u \\rVert_{\\text{TV}}\n", + "$\n", + "\n", + "Using Forward-Backward, the iterates go like\n", + "\n", + "$\n", + " \\boldsymbol x^{(k+1)} = \\text{prox}_{\\gamma \\lambda \\lVert \\cdot \\rVert_{\\text{TV}}} \\left( \\boldsymbol x^{(k)} - \\gamma \\nabla f(\\boldsymbol x^{(k)}) \\right)\n", + "$\n", + "\n", + "with converge rate in $\\mathcal O(1/k)$ if $\\gamma<2/L$ where $L$ is the Lipschitz constant of $\\nabla f$, i.e. \n", + "$\n", + " \\lvert \\nabla f(x)-\\nabla f(y) \\rvert \\le L\\lvert x-y \\rvert.\n", + "$" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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Rr7xjW9QU1ODu3PHHXfQr1+/bKe1nY56F2qdgoICRo8enfL26bwL1YB7gOXu/qukVU8ApwHXhr8fT1cOIiIiEj09e/bc4eYH2V46z8AdBJwKvGFmi8K2nxEUbjPN7IfAB8CJacxBREREZKeTtgLO3ecSvHavMYelq18RERGRnZ3ehSoiIiISMSrgRERERCJGBZyIiIikRSwWo6SkhJEjR3LcccdRWVmZtVzKy8uZN29eh8V78sknWbZsWav366hH+KiAExER6eyuvx7mzNm+bc6coL0dunfvzqJFi1iyZAl9+/bltttua1e89miugKt7BVdrtLWA6ygq4ERERDq7sWPhpJO+KOLmzAmWx47tsC7GjRvHRx99BMB7773HUUcdRWlpKQcffDBvvvkmAJ988gknnHACo0aNYtSoUfUF169+9StGjhzJyJEj+fWvfw1ARUUFe++9N6effjojRozgiCOOYMuWLQDccsst7LPPPhQXF3PyySdTUVHBHXfcwU033URJSQkvvPACU6dO5bzzzqOsrIwLL7yQK664ghtvvLE+35EjR1JRUQHAAw88QHFxMaNGjeLUU09l3rx5PPXUU1xwwQWUlJTw3nvvNXlMK1asYNy4cYwdO5bLLrusw75P3D3nf0pLSz0T5syZk5F+pO00RrlPY5T7NEa5ryPGaNmyZa3bYfZs9/793S+7LPg9e3a7c+jRo4e7u9fW1vqUKVP8r3/9q7u7H3roof7222+7u/vLL7/sZWVl7u5+0kkn+U033VS/T2Vlpc+fP99HjhzpVVVVvnHjRt9nn3184cKFvmLFCo/FYv7aa6+5u/uJJ57oDz74oLu7Dxw40Kurq93d/bPPPnN398svv9xvuOGG+txOO+00P+aYY7y2trbR9SNGjPAVK1b4kiVLfK+99vK1a9e6u/u6devc3f073/mOP/zww/XbN3VMxx13nN9///3u7n7rrbfWfycNNTZewHxvojZK65sYREREJCLKyuA//xOuugouuyxYbqctW7ZQUlJCRUUFpaWlHH744VRVVTFv3jxOPPGLx8Bu3boVgNmzZ9e/+D4Wi9GrVy/mzp3LCSecQI8ePQCYPHkyL7zwAhMnTqSoqIiSkhIASktL68+YFRcX893vfpdJkyYxadKkJvM78cQTW3wd1uzZs5kyZQr9+/cHoG/fvjts09wxvfjiizzy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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SNR: 60.50 dB\n" + ] + } + ], + "source": [ + "A = np.random.randn(M, N)/np.sqrt(M)\n", + "y = A @ x \n", + "\n", + "fig=plt.figure(figsize=(6,3))\n", + "axs = subplot_axs(fig, 1,2)\n", + "axs[0].plot(x, 'b-')\n", + "axs[1].plot(y, 'r-')\n", + "axs[0].set_title('x')\n", + "axs[1].set_title('y')\n", + "plt.show()\n", + "\n", + "A_op = pylops.MatrixMult(A)\n", + "\n", + "nu = eval_nu(np.random.randn(N), A_op, A_op.T, nb_iter=50)*1.01\n", + "adjoint = A_op.T @ y\n", + "\n", + "\"TV norm regularization\"\n", + "TV = pyproximal.TV(dim=1, niter=maxit_prox_TV, rtol=1e-9, sigma=0.5)\n", + "\"L2 data fidelity term\"\n", + "l2 = pyproximal.L2(Op=A_op, b=y)\n", + "\"Solve\"\n", + "tau=0.5\n", + "x0 = adjoint\n", + "\n", + "\"Unused slow version\"\n", + "# xhat = pyproximal.optimization.primal.ProximalGradient( \\\n", + "# proxf=l2, proxg=TV, x0=x0, tau=1/nu, epsg=0.5e2, niter=maxit, show=False)\n", + "\"if tau=None, backtracking line search is used\"\n", + "xhat = pyproximal.optimization.primal.ProximalGradient(\\\n", + " proxf=l2, proxg=TV, x0=x0, tau=1/nu, \n", + " epsg=1., niter=800, acceleration='fista', show=False)\n", + "\n", + "show_rec1D(x, xhat, objective=None, log_scale=True, linewidth=1.2)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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Rr7xjW9QU1ODu3PHHXfQr1+/bKe1nY56F2qdgoICRo8enfL26bwL1YB7gOXu/qukVU8ApwHXhr8fT1cOIiIiEj09e/bc4eYH2V46z8AdBJwKvGFmi8K2nxEUbjPN7IfAB8CJacxBREREZKeTtgLO3ecSvHavMYelq18RERGRnZ3ehSoiIiISMSrgRERERCJGBZyIiIikRSwWo6SkhJEjR3LcccdRWVmZtVzKy8uZN29eh8V78sknWbZsWav366hH+KiAExER6eyuvx7mzNm+bc6coL0dunfvzqJFi1iyZAl9+/bltttua1e89miugKt7BVdrtLWA6ygq4ERERDq7sWPhpJO+KOLmzAmWx47tsC7GjRvHRx99BMB7773HUUcdRWlpKQcffDBvvvkmAJ988gknnHACo0aNYtSoUfUF169+9StGjhzJyJEj+fWvfw1ARUUFe++9N6effjojRozgiCOOYMuWLQDccsst7LPPPhQXF3PyySdTUVHBHXfcwU033URJSQkvvPACU6dO5bzzzqOsrIwLL7yQK664ghtvvLE+35EjR1JRUQHAAw88QHFxMaNGjeLUU09l3rx5PPXUU1xwwQWUlJTw3nvvNXlMK1asYNy4cYwdO5bLLrusw75P3D3nf0pLSz0T5syZk5F+pO00RrlPY5T7NEa5ryPGaNmyZa3bYfZs9/793S+7LPg9e3a7c+jRo4e7u9fW1vqUKVP8r3/9q7u7H3roof7222+7u/vLL7/sZWVl7u5+0kkn+U033VS/T2Vlpc+fP99HjhzpVVVVvnHjRt9nn3184cKFvmLFCo/FYv7aa6+5u/uJJ57oDz74oLu7Dxw40Kurq93d/bPPPnN398svv9xvuOGG+txOO+00P+aYY7y2trbR9SNGjPAVK1b4kiVLfK+99vK1a9e6u/u6devc3f073/mOP/zww/XbN3VMxx13nN9///3u7n7rrbfWfycNNTZewHxvojZK65sYREREJCLKyuA//xOuugouuyxYbqctW7ZQUlJCRUUFpaWlHH744VRVVTFv3jxOPPGLx8Bu3boVgNmzZ9e/+D4Wi9GrVy/mzp3LCSecQI8ePQCYPHkyL7zwAhMnTqSoqIiSkhIASktL68+YFRcX893vfpdJkyYxadKkJvM78cQTW3wd1uzZs5kyZQr9+/cHoG/fvjts09wxvfjiizzy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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SNR: 60.42 dB\n" + ] + } + ], + "source": [ + "\"Positivity constrain\"\n", + "pos = pyproximal.Box(lower=0)\n", + "\n", + "xhat2 = pyproximal.optimization.primal.GeneralizedProximalGradient(\\\n", + " proxfs=[l2], proxgs=[TV, pos], x0=x0, tau=1/nu, \n", + " epsg=1, niter=800, acceleration='None', show=False)\n", + "\n", + "show_rec1D(x, xhat2, objective=None, log_scale=True, linewidth=1.2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Adapting $\\lambda$ between meta-iterations\n", + "\n", + "The idea is that as $\\boldsymbol y = \\boldsymbol{Ax} + \\boldsymbol n$ with $\\epsilon \\approx \\lVert \\boldsymbol n \\rVert$, we should expect from the error at the end of a meta-iteration to tend towards $\\epsilon$. If the error is too big, we can have $\\underbrace{\\lVert \\boldsymbol{Ax} - \\boldsymbol y\\rVert}_{\\epsilon^{(k)}} \\gg \\epsilon$. In this case, one must decrease the weight of the prior term $\\lambda$. This parameter is adapted as\n", + "$\n", + " \\lambda^{(k+1)} = \\lambda^{(k)} \\frac{\\epsilon}{\\epsilon^{(k)}}\n", + "$" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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Rr7xjW9QU1ODu3PHHXfQr1+/bKe1nY56F2qdgoICRo8enfL26bwL1YB7gOXu/qukVU8ApwHXhr8fT1cOIiIiEj09e/bc4eYH2V46z8AdBJwKvGFmi8K2nxEUbjPN7IfAB8CJacxBREREZKeTtgLO3ecSvHavMYelq18RERGRnZ3ehSoiIiISMSrgRERERCJGBZyIiIikRSwWo6SkhJEjR3LcccdRWVmZtVzKy8uZN29eh8V78sknWbZsWav366hH+KiAExER6eyuvx7mzNm+bc6coL0dunfvzqJFi1iyZAl9+/bltttua1e89miugKt7BVdrtLWA6ygq4ERERDq7sWPhpJO+KOLmzAmWx47tsC7GjRvHRx99BMB7773HUUcdRWlpKQcffDBvvvkmAJ988gknnHACo0aNYtSoUfUF169+9StGjhzJyJEj+fWvfw1ARUUFe++9N6effjojRozgiCOOYMuWLQDccsst7LPPPhQXF3PyySdTUVHBHXfcwU033URJSQkvvPACU6dO5bzzzqOsrIwLL7yQK664ghtvvLE+35EjR1JRUQHAAw88QHFxMaNGjeLUU09l3rx5PPXUU1xwwQWUlJTw3nvvNXlMK1asYNy4cYwdO5bLLrusw75P3D3nf0pLSz0T5syZk5F+pO00RrlPY5T7NEa5ryPGaNmyZa3bYfZs9/793S+7LPg9e3a7c+jRo4e7u9fW1vqUKVP8r3/9q7u7H3roof7222+7u/vLL7/sZWVl7u5+0kkn+U033VS/T2Vlpc+fP99HjhzpVVVVvnHjRt9nn3184cKFvmLFCo/FYv7aa6+5u/uJJ57oDz74oLu7Dxw40Kurq93d/bPPPnN398svv9xvuOGG+txOO+00P+aYY7y2trbR9SNGjPAVK1b4kiVLfK+99vK1a9e6u/u6devc3f073/mOP/zww/XbN3VMxx13nN9///3u7n7rrbfWfycNNTZewHxvojZK65sYREREJCLKyuA//xOuugouuyxYbqctW7ZQUlJCRUUFpaWlHH744VRVVTFv3jxOPPGLx8Bu3boVgNmzZ9e/+D4Wi9GrVy/mzp3LCSecQI8ePQCYPHkyL7zwAhMnTqSoqIiSkhIASktL68+YFRcX893vfpdJkyYxadKkJvM78cQTW3wd1uzZs5kyZQr9+/cHoG/fvjts09wxvfjiizzy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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SNR: 112.84 dB\n" + ] + } + ], + "source": [ + "SNR_target = 100\n", + "epsilon = np.linalg.norm(x)*10**(-SNR_target/20)\n", + "\n", + "\"TV norm regularization\"\n", + "TV = pyproximal.TV(dim=1, niter=maxit_prox_TV, rtol=1e-9, sigma=0.5)\n", + "\"L2 data fidelity term\"\n", + "l2 = pyproximal.L2(Op=A_op, b=y)\n", + "\"Solve\"\n", + "tau=0.5\n", + "x0 = adjoint\n", + "\n", + "\"if tau=None, backtracking line search is used\"\n", + "xhat = pyproximal.optimization.primal.ProximalGradient(\\\n", + " proxf=l2, proxg=TV, x0=x0, tau=None, \n", + " epsg=epsilon, niter=8000, acceleration='fista', show=False)\n", + "\n", + "show_rec1D(x, xhat, objective=None, log_scale=True, linewidth=1.2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Linearized ADMM\n", + "\n", + " Note: there is probably a difference between this optimisation problem and the one above, cause the TV norm is squared with proximal.L2.
\n", + "\n", + "\\begin{align*}\n", + " \\boldsymbol x^* &= \\argmin_{\\boldsymbol u}~f(\\boldsymbol u) + g(\\boldsymbol{Au}) \\\\\n", + " &= \\argmin_{\\boldsymbol u}~\\frac{1}{2} \\lVert \\boldsymbol y - \\boldsymbol{Au} \\rVert^2 + \\lambda \\lVert \\nabla \\boldsymbol u \\rVert_2\n", + "\\end{align*}\n", + "\n", + "The Linearized-ADMM algorithm can be expressed by the following recursion:\n", + "\n", + "$\n", + " \\boldsymbol{x}^{(k+1)} = \\text{prox}_{\\mu f} (\\boldsymbol x^{(k)} - \\frac{\\mu}{\\tau} \\boldsymbol A^H (\\boldsymbol{Ax}^{(k)} - \\boldsymbol z^{(k)} + \\boldsymbol u^{(k)} ) ) \\\\\n", + " \\boldsymbol z^{(k+1)} = \\text{prox}_{\\tau g} (\\boldsymbol{Ax}^{(k+1)} + \\boldsymbol u^{(k)} \n", + " ) \\\\\n", + " \\boldsymbol u^{(k+1)} = \\boldsymbol u^{(k)} + \\boldsymbol{Ax}^{(k+1)} - \\boldsymbol z^{(k+1)}\n", + "$" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SNR: 20.07 dB\n" + ] + } + ], + "source": [ + "# Gradient operator\n", + "Gop = pylops.FirstDerivative(N=len(x), kind='forward', dtype='float64')\n", + "L = 8. # maxeig(Gop^H Gop)\n", + "\n", + "\"L2 regularization\"\n", + "sigma = 2.\n", + "thik = pyproximal.L2(sigma=sigma)\n", + "\n", + "# Solve\n", + "tau = 1.\n", + "mu = 1. / (tau*L)\n", + "\n", + "xhat = pyproximal.optimization.primal.LinearizedADMM( \\\n", + " proxf=l2, proxg=thik, A=Gop, tau=tau, mu=2/nu, x0=x0, niter=500)[0]\n", + "\n", + "show_rec1D(x, xhat, objective=None, log_scale=True, linewidth=1.2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "TwIST (Two-step Iterative Shrinkage/Threshold)\n", + "\n", + "$\n", + " \\boldsymbol x = \\argmin_{\\boldsymbol x}~\\frac{1}{2} \\lVert \\boldsymbol y - \\boldsymbol{Ax} \\rVert_2^2 + g(\\boldsymbol x)\n", + "$\n", + "\n", + "The TwIST algorithm can be expressed by the following recursion:\n", + "$\n", + " \\boldsymbol x^{(k+1)} = (1-\\alpha) \\boldsymbol x^{(k-1)} + (\\alpha-\\beta) \\boldsymbol x^{(k)} + \\beta \\text{prox}_g (\\boldsymbol x^{(k)}) + \\boldsymbol A^H(\\boldsymbol y- \\boldsymbol{Ax}^{(k)}),\n", + "$\n", + "\n", + "where $\\boldsymbol x^{(1)} = \\text{prox}_g (\\boldsymbol x^{(0)} + \\boldsymbol A^H (\\boldsymbol y- \\boldsymbol{Ax}^{(0)}) ) $\n", + "\n", + "The optimal weighting parameters $\\alpha$ and $\\beta$ are linked to the smallest and largest eigenvalues of $\\boldsymbol A^H \\boldsymbol A$ as follows:\n", + "\n", + "$\n", + " \\alpha = 1 + \\rho^2 \\\\\n", + " \\beta = \\frac{2\\alpha}{\\Lambda_{\\text{max}} + \\lambda_{\\text{min}} }\n", + "$\n", + "\n", + "where $\\rho = \\frac{1-\\sqrt k}{1 + \\sqrt k}$ with $k = \\frac{\\lambda_{\\text{min}}}{\\Lambda_{\\text{max}}}$ and $\\Lambda_{\\text{max}} = \\text{max}(1, \\lambda_{\\text{max}})$." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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ccwxx3DIIYew//77M3fuXMaNGwfAihUrOPbYYxk5ciTHHXcc48aNq7618vDhw/noo4+YO3cuO+ywA6eddho77bQT+++/PytXrgTguuuuY7fddmPUqFEcddRRrFix4dQXaxuSiiU9IukaScWtfb5xnxjIk3M+bu3TmFk96uv7V6xYwbvvvssBBxzA9ttvz09/+tPq/Xr37l29/Ktf/YrddtuNkSNH8uMf/7i6/frrr2fkyJGMGjWKU045hccff5w777yT7373u4wePZo333yTiRMncuutt3LPPfdw7LHHVu9bUlLCIYccAsB9993HnnvuyS677MIxxxxDWVkZEcGDDz7I0UcfDcCpp57K9OnTW+Q18Zxms0a66K1/ETM3/q/VLgSruxRwwczJaMKOQP1X0GisX937KivXVK7XtnJNJb+691UOHzO02cedPXs2u+6663ptffv2ZauttqKiooInnniCWbNmMXDgQObOnVu9zVVXXcWAAQOYNWsWL774IqNHj671+K+//jo33XQT1113Hcceeyy33XYbJ598MkceeSSnnXYaABdccAGTJ0/mzDPPbHYetj5JU4AJwIKI2Dmn/QDgd0AB8MeIuITk770yoAjYcDJ/C9vzE4P43q2zmLd4JVv097xms8q1wUdl5Q1v2ESDe3ev87mG+v6nnnqKF198kZ49e7Lbbrtx8MEHM3bs2Opt77vvPl5//XWeeuopIoJDDz2Uhx9+mEGDBnHxxRfz2GOPMXjwYD7++GMGDhzIoYceyoQJE6qL3Sr77bcfX/3qV1m+fDm9evXib3/7G8cddxwfffQRF110Effffz+9evXil7/8JZdddhlf//rX6d+/P127JiXusGHDeO+9jf/UFVw0mzXe/fejT31q3fo228ALL8D7769r69kTJFi+fMO2wkIYNIiy5cvpvfnmsOmmcP/9cM45LRLevMUrm9TeWBFR6zeMq9r3228/Bg4cuMHzjz76KGeddRYAO++8MyNHjqz1+CNGjKguqHfdddfqwvvFF1/kggsuYPHixZSVlfGFL3xho/KwDUwFfg9cX9UgqQC4EtiPpDh+WtKdwCMR8ZCkIcBlwEmtGdiwAT0Y2r8HT85ZyBFjhrXmqcw6hI/Kyhn38wda/LhP/mBfetYxS7Axff+gQYMAOPLII3n00Uc3KJrvu+8+xowZA0BZWRmvv/46//3vfzn66KMZPHgwQK3vH7m6du3KAQccwF133cXRRx/NP/7xDy699FIeeughXnrpJfbaay8AVq9ezZ577knEht88aqmrkLhoNmusGTNa5DDPlJRQXFzcIsfKtUX/HrxXS4G8sSN1O+20E7fddtt6bUuXLuXdd9+loKCAXr161bpfbR1Xbbp3XzfSUVBQUD09Y+LEiUyfPp1Ro0YxdepUSkpKmpeA1SoiHpY0vEbz7sAbEfEWgKSbgcMiomqi4SKg1qEpSacDpwMMGTKkWb+vsrKy6v227rmGaY/NZsCSN5p8nPYoN7csynJ++cqtX79+LFu2DIDuETzwv+Na/BzdYzWVa9dWnyfXiBEjuOWWW9Z7bunSpbzzzjusWbOGioqK6ufKy8spLy+vXl+2bBnl5eWcffbZfPnLX17vuFdffTWrV6/e4Jxr1qxh5cqV1e2564cccgjXXXcdRUVF1UX4ihUrKC4u5k9/+tN6x4kIFi1axKJFi5DEq6++yqabblprjpBcD7uxv99WK5olbUkygrEZsBa4NiJ+J2kg8DdgODAXODYiFtV1HDNrnO9+YXvOu/2F9aZo9Cgs4Ltf2H6jjrvvvvty7rnncv311/PFL36RyspKvv3tbzNx4kR69uxZ53577703t9xyC+PHj+ell17ihRdeaNJ5ly1bxuabb86aNWu44YYbGDq0+VNMrNGGAu/mrJcC4yQdCXwB6E8yOr2BiLgWuBZg7Nix0Zw/DEty/qBc0Otdrip5o1X+wMyHklb6Y7m9yHJ++crt5ZdfXu9udv37tc556rpr3iGHHMLPfvYzpk2bVt33n3POOXzpS19i4MCBlJSUsGbNGnr06ME999zDlClTqo/Tp08fDjnkEH74wx8yadIkevfuzXvvvUdhYSEHH3wwRxxxBOeeey6DBg2qnp4xcOBAKioqqo9RWFhIjx496NOnDwcddBBnnnkmN9xwAyeddBJ9+vRh/PjxfOc732H+/Plsu+22rFixgtLSUj75yU/yuc99jnvvvZeDDz6YW2+9laOOOqrOOwPmFuINac0vAlYA346IHYA9gG9I2hE4F3ggIrYDHkjXzWwjHT5mKL848tMM7d8DAUP79+AXR356o+YzQ/Kx1rRp0/j73//Odtttxyc/+UmKior4+c9/Xu9+X//61/nwww8ZOXIkv/zlLxk5ciT9+jW+17/wwgsZN24c++23H5/KnRZjram2zzAjIm6PiK9GxHERUdIWgew+YiBzF67gw2UtP4/TzBrWUN+/9957c8oppzB69GiOOuqo6qkZVVMh9t9/f0488UT23HNPPv3pT3P00UezbNkydtppJ84//3z22WcfRo0axTnpFMXjjz+eX/3qV4wZM4Y333xzvVgKCgqYMGEC99xzDxMmTABgk002YerUqZxwwgmMHDmSPfbYg1deeQWgen7zqFGjWLhwIZMmTWqZFyUi2uQB3EEyT+5VYPO0bXPg1Yb23XXXXaM5Zs6c2az9OoIs5xaR7fyakttLL73UeoG0kqVLl0ZEREVFRaxcuTIiIt54443Yeuuto7y8PJ+hbbSq3OpS2+8LeCbaqJ9t6oPkE78Xc9b3BO7NWT8POK+px22JPnvt2rWx84//GTNfmd+sY7U3We7TIrKdX75ya6v+v6F+rSk++uij2GqrrVrseBurMbk1pd9ukznN6by5McCTwJCIeD8t2N+XtGlbxGBmbWvFihWMHz+eNWvWEBFcffXVdOvWLd9hWf2eBraTNAJ4DzgeODEfgUhix837MnveUoq399uEWXs3b948iouL+c53vpPvUFpNqxfNknoDtwHfioiljf0GY0t/qSRrspwbZDu/puSW+0WQjqKysrI65pkzZ673XEfLpabc3GrTlC+U5Jukm4BiYLCkUuDHETFZ0jeBe0kuOTclImbnK8adtujH7HlL8nV6M2uCLbbYgtdeey3fYbSqVi2aJRWSFMw3RMTtafN8SZuno8ybAwtq2zda+EslWZPl3CDb+TUlt5pfBOkI6vpSSRY0lFtTvlCSbxFxQh3tM4CWuVTMRtp5aF8eeGV+vsMwy5uo47Jv1jKikVd5qtJqXwRU8lueDLwcEZflPHUncGq6fCrJXGczM7P17LRFP95euIIlK9fkOxSzNldUVMTChQubXNhZ40QECxcupKioqNH7tOZI817AKcALkp5P234AXALcImkS8A5wTCvGYGZmHdQ2m/RicO9u/Oul+Ry9q29yYp3LsGHDKC0t5cMPP2zV86xatapJhWNH0lBuRUVFDBvW+L6l1YrmiHiU2i9fBLBva53XzMyyoWtBF07ZYzi//ddr7PGJgQwbUPd1wc2yprCwkBEjRrT6eUpKSjrMtLKmauncWvM6zWaWAQUFBYwePZqdd96ZQw45hMWLF+ctlpKSEh5//PEWO9706dN56aWXGt6wht69e7dYDFa/M4q3YYfN+3DA/z3ChXe/RMmrC1i0fHW+wzKzTsi30TbLiksvhd12g/Hj17XNnAlPPw3f+16zD9ujRw+ef/55AE499VSuvPJKzj///I0MtnlKSkro3bs3n/nMZzZ4rqKigq5dm9alTZ8+nQkTJrDjjju2VIjWwrp17cJ1XxzLXbPeZ9pzpdzy9LssK69g60E92XaT3mw9qBfDB/dkq4E9GT6oF5v1K6KosCDfYZtZBrloNsuK3XaDY4+FW25JCueZM9ett5A999yTWbNmAfDmm2/yjW98gw8//JCePXty3XXX8alPfYoFCxbwxS9+kbfeeguAq6++ms985jNcdtllTJkyBYCvfOUrfOtb32Lu3LkceOCB7L333jz++OMMHTqUO+64gx49enD55ZdzzTXX0LVrV3bccUcuueQSrrnmGgoKCvjrX//KFVdcweTJkxk4cCD/+c9/2GWXXejTpw+9e/euvk7ozjvvzN13383w4cO5/vrr+fWvf40kRo4cyRlnnMGdd97JQw89xEUXXcRtt90GUGtOc+bM4cQTT6S8vJyDDz64xV5PaxxJHDpqCw4dtQVr1wZvfVTGf99dwpyPljN34XKeeftj5ny0nGWrKgAY0LOQIX2LGNK3iM36FjGkXxGDe3djYK9uDOzZjQG9kuX+PQvp3tUFtpk1jotms6wYPz4pkI89Fs44A66+el0B3QIqKyt54IEHqm9Hevrpp3PNNdew3Xbb8eSTT/L1r3+dBx98kO9973vss88+TJs2jcrKSsrKynj22Wf505/+xJNPPklEMG7cOPbZZx8GDBjA66+/zk033cR1113Hsccey2233cbJJ5/MJZdcwpw5c+jevTuLFy+mf//+fO1rX1uvKJ48eTKvvfYa999/PwUFBfzkJz+pNfbZs2dz8cUX89hjjzF48GA+/vhjBg4cyKGHHsqECRM4+uijAdh3331rzemss87ijDPO4IgjjuD6669vkdfTmqdLF7Htpn3YdtP1L/0XESxesYb3l6xi/rJVzF+yig+WrmL+0lW8ULqYj5ev5uMVq1m0fA1l5RXV+/Xu3pWBvZJCekDPQvoWFdK3R1f6FhXSr0chfXus39a3R9Lep6grhQWe4WjWmbhoNsuS8eOTgvnCC+GHP2yRgnnlypWMHj2auXPnsuuuu7LffvtRVlbG448/zjHHrLv4TXl5OQAPPfQQN954I5DMh+7Xrx+PPvooRxxxBL169QLgyCOP5JFHHuHQQw9lxIgRjB49GoBdd92VuXPnAjBy5EhOOukkDj/8cA4//PA64zvmmGMoKKh/tPDBBx/k6KOPZvDgwQAMHDhwg23qy+mxxx7jtttuY9WqVZxyyil8//vfr/d81vYkJYVvr27sSN96t121ppLFK9bw8fLVLFqxOimo0+VlqypYunINby4rY8nKNSxdWcHSVWtYunINy8oryL36V4/CAnoXdaV396706l5A7+5Vy103WC4tXcPyWe+n2xdssI0LcLP2z0WzWZbMnJmMMP/wh8nP8eM3unCumtO8ZMkSJkyYwJVXXsnEiRPp379/9VznhtR3ndHu3btXLxcUFLBy5UoA/vGPf/Dwww9z5513cuGFFzJ7du03pqsqxAG6du3K2rVrq9dXrVpVff6GbhCwdu3aenPyDQayo6iwgM36FbBZv6ZdZmvt2qBsdVJUVxXTy8srKEsfy8srKFtVQVl5JWXlayhdtLL6ufkL13Bv6cssX51sU7F2/f8TRYVdqovoqkK8d/dkRDu3rXo9bevTvXC957p37eJ/q2atxEWzWVbkzmGuKpZz1zdSv379uPzyyznssMM444wzGDFiBH//+9855phjiAhmzZrFqFGj2Geffbj66qv51re+RWVlJcuXL+ezn/0sEydO5NxzzyUimDZtGn/5y1/qPNfatWt59913GT9+PHvvvTc33ngjZWVl9OnTh6VLl9a53/Dhw7n77rsBeO6555gzZw6QTLs44ogjOPvssxk0aFD19Iw+ffpU3xa7b9++dea01157cfPNN3PYYYdxww03bPRraR1Tly5KpmgUFcKApu2beyfQiKC8Yi3LyytYXl7JsvI1LE8L7WWr0iI8/Vm1/uGy8mS9vIKyVWuqt1m+unK983TtopyiO6fILipcb71X9670yS3Qi9Zf79WtK126uPg2y+Wi2Swrnn56/QK5ao7z00+32LzmMWPGMGrUKG6++WZuuOEGzjjjDC666CLWrFnD8ccfz6hRo7j00ks555xzmDx5MgUFBVx99dXsueeeTJw4kd133x1Ivgg4ZsyY6qkYNVVWVnLyySezZMkSIoKzzz6b/v37c8ghh3D00Udzxx13cMUV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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "SNR: 59.35 dB\n" + ] + } + ], + "source": [ + "eigs = (A_op.H * A_op).eigs()\n", + "eigs = (np.abs(eigs[0]), 5e-1)\n", + "\n", + "xhat, objective = pyproximal.optimization.primal.TwIST(\\\n", + " proxg=TV, A=A_op, b=y, x0=x0, \\\n", + " eigs=eigs, niter=500, show=False, \\\n", + " returncost=True)\n", + "\n", + "show_rec1D(x, xhat, objective=objective, log_scale=True, linewidth=1.2) " + ] + } + ], + "metadata": { + "interpreter": { + "hash": "46df200377d403be22c796785365123e6a374b5da08e8292e6b2afda659c5a28" + }, + "kernelspec": { + "display_name": "Python 3.8.8 ('base')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +}