11import numpy as np
22
33from pyproximal .ProxOperator import _check_tau
4- from pyproximal .projection import BoxProj
54from pyproximal import ProxOperator
5+ from pyproximal .proximal import L2 , L1
66
77
88class Huber (ProxOperator ):
99 r"""Huber norm proximal operator.
1010
11- Proximal operator of the Huber norm defined as:
11+ Proximal operator of the Huber norm defined as
12+ :math:`H_\alpha(\mathbf{x}) = \sum_i H_\alpha(x_i)` where:
13+
14+ .. math::
15+
16+ H_\alpha(x_i) =
17+ \begin{cases}
18+ \frac{|x_i|^2}{2 \alpha}, & |x_i| \leq \alpha \\
19+ |x_i| - \frac{\alpha}{2}, & |x_i| > \alpha
20+ \end{cases}
21+
22+ which behaves like a :math:`\ell_2` norm for :math:`|x_i| \leq \alpha` and a
23+ :math:`\ell_1` norm for :math:`|x_i| > \alpha`.
24+
25+ Parameters
26+ ----------
27+ alpha : :obj:`float`
28+ Huber parameter
29+
30+ Notes
31+ -----
32+ The Huber proximal operator is defined as:
33+
34+ .. math::
35+
36+ \prox_{\tau H_\alpha(\cdot)}(\mathbf{x}) =
37+ \begin{cases}
38+ \prox_{\frac{\tau}{2 \alpha} |x_i|^2}(x_i), & |x_i| \leq \alpha \\
39+ \prox_{\tau |x_i|}(x_i), & |x_i| > \alpha
40+ \end{cases}
41+
42+ """
43+ def __init__ (self , alpha ):
44+ super ().__init__ (None , False )
45+ self .alpha = alpha
46+ self .l2 = L2 (sigma = 1. / self .alpha )
47+ self .l1 = L1 ()
48+
49+ def __call__ (self , x ):
50+ h = np .zeros_like (x )
51+ xabs = np .abs (x )
52+ mask = xabs > self .alpha
53+ h [~ mask ] = xabs [~ mask ] ** 2 / (2. * self .alpha )
54+ h [mask ] = xabs [mask ] - self .alpha / 2.
55+ return np .sum (h )
56+
57+ @_check_tau
58+ def prox (self , x , tau ):
59+ y = np .zeros_like (x )
60+ xabs = np .abs (x )
61+ mask = xabs > self .alpha
62+ y [~ mask ] = self .l2 .prox (x [~ mask ], tau )
63+ y [mask ] = self .l1 .prox (x [mask ], tau )
64+ # alternative from https://math.stackexchange.com/questions/1650411/
65+ # proximal-operator-of-the-huber-loss-function... currently commented
66+ # as it does not provide the same result
67+ # y = (1. - tau / np.maximum(np.abs(x), tau + self.alpha)) * x
68+
69+ return y
70+
71+
72+ class HuberCircular (ProxOperator ):
73+ r"""Circular Huber norm proximal operator.
74+
75+ Proximal operator of the Circular Huber norm defined as:
1276
1377 .. math::
1478
@@ -18,8 +82,8 @@ class Huber(ProxOperator):
1882 \|\mathbf{x}\|_2 - \frac{\alpha}{2}, & \|\mathbf{x}\|_2 > \alpha \\
1983 \end{cases}
2084
21- which behaves like a :math:`\ell_2` norm for :math:`|x_i| \leq \alpha` and a
22- :math:`\ell_1` norm for :math:`|x_i| < \alpha`.
85+ which behaves like a :math:`\ell_2` norm for :math:`\|\mathbf{x}\|_2 \leq \alpha` and a
86+ :math:`\ell_1` norm for :math:`\|\mathbf{x}\|_2 > \alpha`.
2387
2488 Parameters
2589 ----------
@@ -28,13 +92,16 @@ class Huber(ProxOperator):
2892
2993 Notes
3094 -----
31- The Huber proximal operator is defined as:
95+ The Circular Huber proximal operator is defined as [1]_ :
3296
3397 .. math::
3498
3599 \prox_{\tau H_\alpha(\cdot)}(\mathbf{x}) =
36100 \left( 1 - \frac{\tau}{\max\{\|\mathbf{x}\|_2, \tau + \alpha \} } \right) \mathbf{x}
37101
102+ .. [1] O’Donoghue, B. and Stathopoulos, G. and Boyd, S. "A Splitting Method for Optimal Control",
103+ In the IEEE Transactions on Control Systems Technology, 2013.
104+
38105 """
39106 def __init__ (self , alpha ):
40107 super ().__init__ (None , False )
@@ -51,4 +118,4 @@ def __call__(self, x):
51118 @_check_tau
52119 def prox (self , x , tau ):
53120 x = (1. - tau / max (np .linalg .norm (x ), tau + self .alpha )) * x
54- return x
121+ return x
0 commit comments