Source code for pyproximal.proximal.L21

import numpy as np

from pyproximal.ProxOperator import _check_tau
from pyproximal import ProxOperator


[docs]class L21(ProxOperator): r""":math:`L_{2,1}` proximal operator. Proximal operator for :math:`L_{2,1}` matrix norm. Parameters ---------- 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 vector ``x`` should be created by stacking vectors from different dimensions. sigma : :obj:`float`, optional Multiplicative coefficient of :math:`L_{2,1}` norm Notes ----- Given the :math:`L_{2,1}` norm of a matrix of size :math:`N_{dim} \times N'_x` defined as: .. math:: \sigma \|\mathbf{X}\|_{2,1} = \sigma \sum_{j=0}^{N'_x} \|\mathbf{x}_j\|_2 = \sigma \sum_{j=0}^{N'_x} \sqrt{\sum_{i=0}^{N_{dim}}} |x_{ij}|^2 the proximal operator is: .. math:: \prox_{\tau \sigma \|\cdot\|_{2,1}}(\mathbf{x}_j) = \left(1 - \frac{\sigma \tau}{max\{||\mathbf{x}_j||_2, \sigma \tau \}}\right) \mathbf{x}_j \quad \forall j Similar to the Euclidean norm, the dual operator is defined as: .. math:: \prox^*_{\tau \sigma \||\cdot\|_{2,1}}(\mathbf{x}_j) = \frac{\sigma \mathbf{x}_j}{\max\{||\mathbf{x}_j||_2, \sigma\}} \quad \forall j Finally, we note that the :math:`L_{2,1}` norm is a separable function on each column on the matrix :math:`\mathbf{X}`. Taking advantage of the property of proximal operator of separable function [1]_, its proximal and dual proximal operators can be interpreted as a series of :class:`pyproximal.proximal.Euclidean` operators on each column of the matrix :math:`\mathbf{X}`. .. [1] N., Parikh, "Proximal Algorithms", Foundations and Trends in Optimization. 2013. """ def __init__(self, ndim, sigma=1.): super().__init__(None, False) self.ndim = ndim self.sigma = sigma def __call__(self, x): x = x.reshape(self.ndim, len(x) // self.ndim) f = self.sigma * np.sum(np.sqrt(np.sum(x ** 2, axis=0))) return f @_check_tau def prox(self, x, tau): x = x.reshape(self.ndim, len(x) // self.ndim) aux = np.sqrt(np.sum(x ** 2, axis=0)) aux = np.vstack([aux] * self.ndim).ravel() x = (1 - (tau * self.sigma) / np.maximum(aux, tau * self.sigma)) * x.ravel() return x @_check_tau def proxdual(self, x, tau): x = x.reshape(self.ndim, len(x) // self.ndim) aux = np.sqrt(np.sum(x ** 2, axis=0)) aux = np.vstack([aux] * self.ndim).ravel() x = self.sigma * x.ravel() / np.maximum(aux, self.sigma) return x