Source code for cil.optimisation.functions.L1Sparsity

#  Copyright 2023 United Kingdom Research and Innovation
#  Copyright 2023 The University of Manchester
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#
# Authors:
# CIL Developers, listed at: https://github.com/TomographicImaging/CIL/blob/master/NOTICE.txt
from cil.optimisation.functions import Function, L1Norm
import warnings

[docs] class L1Sparsity(Function): r"""L1Sparsity function Calculates the following cases, depending on if the optional parameter `weight` or data `b` is passed. For `weight=None`: a) .. math:: F(x) = ||Qx||_{1} b) .. math:: F(x) = ||Qx - b||_{1} In the weighted case, `weight` = :math:`w` is an array of non-negative weights. a) .. math:: F(x) = ||Qx||_{L^1(w)} b) .. math:: F(x) = ||Qx - b||_{L^1(w)} with :math:`||x||_{L^1(w)} = || x \cdot w||_1 = \sum_{i=1}^{n} |x_i| w_i`. In all cases :math:`Q` is an orthogonal operator. Parameters --------- Q: orthogonal Operator Note that for the correct calculation of the proximal the provided operator must be orthogonal b : Data, DataContainer, default is None weight: array, optional, default=None non-negative weight array matching the size of the range of operator :math:`Q`. """ def __init__(self, Q, b=None, weight=None): '''creator ''' if not Q.is_orthogonal(): warnings.warn( f"Invalid operator: `{Q}`. L1Sparsity is properly defined only for orthogonal operators!", UserWarning) super(L1Sparsity, self).__init__() self.Q = Q self.l1norm = L1Norm(b=b, weight=weight) def __call__(self, x): r"""Returns the value of the L1Sparsity function at x. Consider the following cases: a) .. math:: F(x) = ||Qx||_{1} b) .. math:: F(x) = ||Qx - b||_{1} In the weighted case, `weight` = :math:`w` is an array of non-negative weights. a) .. math:: F(x) = ||Qx||_{L^1(w)} b) .. math:: F(x) = ||Qx - b||_{L^1(w)} with :math:`|| y ||_{L^1(w)} = || y w ||_1 = \sum_{i=1}^{n} | y_i | w_i`. """ y = self.Q.direct(x) return self.l1norm(y)
[docs] def convex_conjugate(self, x): r"""Returns the value of the convex conjugate of the L1Sparsity function at x. Here, we need to use the convex conjugate of L1Sparsity, which is the Indicator of the unit :math:`\ell^{\infty}` norm on the range of the (bijective) operator Q. Consider the non-weighted case: a) .. math:: F^{*}(x^{*}) = \mathbb{I}_{\{\|\cdot\|_{\infty}\leq1\}}(Qx^{*}) b) .. math:: F^{*}(x^{*}) = \mathbb{I}_{\{\|\cdot\|_{\infty}\leq1\}}(Qx^{*}) + \langle Qx^{*},b\rangle .. math:: \mathbb{I}_{\{\|\cdot\|_{\infty}\leq1\}}(x^{*}) = \begin{cases} 0, \mbox{if } \|x^{*}\|_{\infty}\leq1\\ \infty, \mbox{otherwise} \end{cases} In the weighted case the convex conjugate is the indicator of the unit :math:`L^{\infty}( w^{-1} )` norm. See: https://math.stackexchange.com/questions/1533217/convex-conjugate-of-l1-norm-function-with-weight a) .. math:: F^{*}(x^{*}) = \mathbb{I}_{\{\|\cdot\|_{L^\infty(w^{-1})}\leq 1\}}(Qx^{*}) b) .. math:: F^{*}(x^{*}) = \mathbb{I}_{\{\|\cdot\|_{L^\infty(w^{-1})}\leq 1\}}(Qx^{*}) + \langle Qx^{*},b\rangle with :math:`\|x\|_{L^\infty(w^{-1})} = \max_{i} \frac{|x_i|}{w_i}` and possible cases of 0 / 0 are defined to be 1. """ y = self.Q.direct(x) return self.l1norm.convex_conjugate(y)
[docs] def proximal(self, x, tau, out=None): r"""Returns the value of the proximal operator of the L1 Norm function at x with scaling parameter `tau`. Consider the following cases: a) .. math:: \mathrm{prox}_{\tau F}(x) = Q^T \mathrm{ShinkOperator}_{\tau}(Qx) b) .. math:: \mathrm{prox}_{\tau F}(x) = Q^T \left( \mathrm{ShinkOperator}_\tau(Qx- b) + b \right) where, .. math :: \mathrm{prox}_{\tau | \cdot |}(x) = \mathrm{ShinkOperator}(x) = sgn(x) * \max\{ |x| - \tau, 0 \} The weighted case follows from Example 6.23 in Chapter 6 of "First-Order Methods in Optimization" by Amir Beck, SIAM 2017 https://archive.siam.org/books/mo25/mo25_ch6.pdf a) .. math:: \mathrm{prox}_{\tau F}(x) = Q^T \mathrm{ShinkOperator}_{\tau*w}(Qx) b) .. math:: \mathrm{prox}_{\tau F}(x) = Q^T \left( \mathrm{ShinkOperator}_{\tau*w}(Qx-b) + b \right) Parameters ----------- x: DataContainer tau: float, ndarray, DataContainer out: DataContainer, default None If not None, the result will be stored in this object. Returns -------- The value of the proximal operator of the L1 norm function at x: DataContainer. """ y = self.Q.direct(x) self.l1norm.proximal(y, tau, out=y) return self.Q.adjoint(y, out)