Skip to content

Commit 13723bb

Browse files
authored
Merge pull request #101 from mrava87/main
Added Log1 proximal
2 parents 45e2f22 + 785019b commit 13723bb

File tree

4 files changed

+88
-3
lines changed

4 files changed

+88
-3
lines changed

docs/source/api/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ Non-Convex
8989
ETP
9090
Geman
9191
Log
92+
Log1
9293
QuadraticEnvelopeCard
9394
QuadraticEnvelopeCardIndicator
9495
SCAD

pyproximal/proximal/Log.py

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,76 @@ def prox(self, x, tau):
9696
r = np.vstack((np.zeros_like(c), (b[idx] + c) / (2 * self.gamma)))
9797
val = tau * self.elementwise(r) + (r - np.abs(x[idx])) ** 2 / 2
9898
idx_minima = np.argmin(val, axis=0)
99-
out[idx] = r[idx_minima, range(r.shape[1])]
99+
out[idx] = r[idx_minima, list(range(r.shape[1]))]
100100
out *= np.sign(x)
101101
return out
102+
103+
104+
class Log1(ProxOperator):
105+
r"""Logarithmic penalty 2.
106+
107+
The logarithmic penalty (Log) is defined as
108+
109+
.. math::
110+
111+
\mathrm{Log}_{\sigma,\delta}(\mathbf{x}) = \sigma \sum_i \log(|x_i| + \delta)
112+
113+
where :math:`{\sigma>0}`, :math:`{\gamma>0}`.
114+
115+
Parameters
116+
----------
117+
sigma : :obj:`float`
118+
Multiplicative coefficient of Log norm.
119+
delta : :obj:`float`, optional
120+
Regularization parameter. Default is 1e-10.
121+
122+
Notes
123+
-----
124+
The logarithmic penalty gives rise to a log-thresholding that is
125+
a smooth alternative falling in between the hard and soft thresholding.
126+
127+
The proximal operator is defined as [1]_:
128+
129+
.. math::
130+
131+
\prox_{\tau \sigma log}(\mathbf{x}) =
132+
\begin{cases}
133+
0.5 (x_i + \delta - \sqrt{(x_i-\delta)^2-2\tau \sigma}), & x_i < -x_0 \\
134+
0, & -x_0 \leq x_i \leq x_0 \\
135+
0.5 (x_i - \delta + \sqrt{(x_i+\delta)^2-2\tau \sigma}), & x_i > x_0\\
136+
\end{cases}
137+
138+
where :math:`x_0=\sqrt{2 \tau \sigma} - \delta`.
139+
140+
.. [1] Malioutov, D., and Aravkin, A. "Iterative log thresholding",
141+
Arxiv, 2013.
142+
143+
"""
144+
145+
def __init__(self, sigma, delta=1e-10):
146+
super().__init__(None, False)
147+
if delta < 0:
148+
raise ValueError('Variable "delta" must be positive.')
149+
self.sigma = sigma
150+
self.delta = delta
151+
152+
def __call__(self, x):
153+
return np.sum(self.elementwise(x))
154+
155+
def elementwise(self, x):
156+
return np.log(np.abs(x) + self.delta)
157+
158+
@_check_tau
159+
def prox(self, x, tau):
160+
tau1 = self.sigma * tau
161+
thresh = np.sqrt(2*tau1) - self.delta
162+
x1 = np.zeros_like(x, dtype=x.dtype)
163+
if np.iscomplexobj(x):
164+
x1[np.abs(x) > thresh] = 0.5 * np.exp(1j * np.angle(x[np.abs(x) > thresh])) * \
165+
(np.abs(x[np.abs(x) > thresh]) - self.delta +
166+
np.sqrt(np.abs(x[np.abs(x) > thresh] + self.delta) ** 2 - 2 * tau1))
167+
else:
168+
x1[x > thresh] = 0.5 * (x[x > thresh] - self.delta + np.sqrt(np.abs(x[x > thresh] + self.delta) ** 2 - 2 * tau1))
169+
x1[x < -thresh] = 0.5 * (x[x < -thresh] + self.delta - np.sqrt(np.abs(x[x < -thresh] - self.delta) ** 2 - 2 * tau1))
170+
return x1
171+

pyproximal/proximal/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
VStack Stack of proximal operators
2929
SCAD Smoothly clipped absolute deviation
3030
Log Logarithmic
31+
Log1 Another form of logarithmic
3132
ETP Exponential-type penalty
3233
Geman Geman penalty
3334
QuadraticEnvelopeCard The quadratic envelope of the cardinality function
@@ -67,6 +68,6 @@
6768
'Euclidean', 'EuclideanBall', 'L0', 'L0Ball', 'L1', 'L1Ball', 'L2',
6869
'L2Convolve', 'L21', 'L21_plus_L1', 'Huber', 'TV', 'Nuclear',
6970
'NuclearBall', 'Orthogonal', 'VStack', 'Nonlinear', 'SCAD',
70-
'Log', 'ETP', 'Geman', 'QuadraticEnvelopeCard', 'SingularValuePenalty',
71+
'Log', 'Log1', 'ETP', 'Geman', 'QuadraticEnvelopeCard', 'SingularValuePenalty',
7172
'QuadraticEnvelopeCardIndicator', 'QuadraticEnvelopeRankL2',
7273
'Hankel']

pytests/test_concave_penalties.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import numpy as np
44

55
from pyproximal.utils import moreau
6-
from pyproximal.proximal import ETP, Geman, Log, SCAD, QuadraticEnvelopeCard
6+
from pyproximal.proximal import ETP, Geman, Log, Log1, SCAD, QuadraticEnvelopeCard
77

88
par1 = {'nx': 10, 'sigma': 1., 'a': 2.1, 'gamma': 0.5, 'mu': 0.5, 'dtype': 'float32'} # even float32
99
par2 = {'nx': 11, 'sigma': 2., 'a': 3.7, 'gamma': 5.0, 'mu': 1.5, 'dtype': 'float64'} # odd float64
@@ -49,6 +49,19 @@ def test_Log(par):
4949
assert moreau(log, x, tau)
5050

5151

52+
@pytest.mark.parametrize("par", [(par1), (par2)])
53+
def test_Log1(par):
54+
"""Log1 penalty and proximal/dual proximal
55+
"""
56+
np.random.seed(10)
57+
log = Log1(sigma=par['sigma'])
58+
59+
# Check proximal operator
60+
tau = 2.
61+
x = np.random.normal(0., 10.0, par['nx']).astype(par['dtype'])
62+
assert moreau(log, x, tau)
63+
64+
5265
@pytest.mark.parametrize("par", [(par1), (par2)])
5366
def test_ETP(par):
5467
"""Exponential-type penalty and proximal/dual proximal

0 commit comments

Comments
 (0)