Source code for pyproximal.proximal.SingularValuePenalty

import numpy as np

from pyproximal.ProxOperator import _check_tau
from pyproximal import ProxOperator


[docs]class SingularValuePenalty(ProxOperator): r"""Proximal operator of a penalty acting on the singular values. Generic regularizer :math:`\mathcal{R}_f` acting on the singular values of a matrix, .. math:: \mathcal{R}_f(\mathbf{X}) = f(\boldsymbol\lambda) where :math:`\mathbf{X}` is a matrix of size :math:`M \times N` and :math:`\boldsymbol\lambda` is the corresponding singular value vector. Parameters ---------- dim : :obj:`tuple` Size of matrix :math:`\mathbf{X}`. penalty : :obj:`pyproximal.ProxOperator` Function acting on the singular values. Notes ----- The pyproximal implementation allows ``penalty`` to be any :class:`pyproximal.ProxOperator` acting on the singular values; however, not all penalties will result in a mathematically accurate proximal operator defined this way. Given a penalty :math:`f`, the proximal operator is assumed to be .. math:: \prox_{\tau \mathcal{R}_f}(\mathbf{X}) = \mathbf{U} \diag\left( \prox_{\tau f}(\boldsymbol\lambda)\right) \mathbf{V}^H where :math:`\mathbf{X} = \mathbf{U}\diag(\boldsymbol\lambda)\mathbf{V}^H`, is an SVD of :math:`\mathbf{X}`. It is the user's responsibility to check that this is true for their particular choice of ``penalty``. """ def __init__(self, dim, penalty): super().__init__(None, False) self.dim = dim self.penalty = penalty def __call__(self, x): X = x.reshape(self.dim) eigs = np.linalg.eigvalsh(X.T @ X) eigs[eigs < 0] = 0 # ensure all eigenvalues at positive return np.sum(self.penalty(np.sqrt(eigs))) @_check_tau def prox(self, x, tau): X = x.reshape(self.dim) U, S, Vh = np.linalg.svd(X, full_matrices=False) X = np.dot(U * self.penalty.prox(S, tau), Vh) return X.ravel()