11import time
2+ import warnings
23import numpy as np
34
45from math import sqrt
@@ -101,11 +102,12 @@ def ProximalPoint(prox, x0, tau, niter=10, callback=None, show=False):
101102
102103def ProximalGradient (proxf , proxg , x0 , tau = None , beta = 0.5 ,
103104 epsg = 1. , niter = 10 , niterback = 100 ,
105+ acceleration = None ,
104106 callback = None , show = False ):
105- r"""Proximal gradient
107+ r"""Proximal gradient (optionnally accelerated)
106108
107- Solves the following minimization problem using Proximal gradient
108- algorithm:
109+ Solves the following minimization problem using (Accelerated) Proximal
110+ gradient algorithm:
109111
110112 .. math::
111113
@@ -130,14 +132,16 @@ def ProximalGradient(proxf, proxg, x0, tau=None, beta=0.5,
130132 backtracking is used to adaptively estimate the best tau at each
131133 iteration. Finally note that :math:`\tau` can be chosen to be a vector
132134 when dealing with problems with multiple right-hand-sides
133- beta : obj:`float`, optional
135+ beta : : obj:`float`, optional
134136 Backtracking parameter (must be between 0 and 1)
135137 epsg : :obj:`float` or :obj:`np.ndarray`, optional
136138 Scaling factor of g function
137139 niter : :obj:`int`, optional
138140 Number of iterations of iterative scheme
139141 niterback : :obj:`int`, optional
140142 Max number of iterations of backtracking
143+ acceleration: :obj:`str`, optional
144+ Acceleration (``None``, ``vandenberghe`` or ``fista``)
141145 callback : :obj:`callable`, optional
142146 Function with signature (``callback(x)``) to call after each iteration
143147 where ``x`` is the current model vector
@@ -151,12 +155,15 @@ def ProximalGradient(proxf, proxg, x0, tau=None, beta=0.5,
151155
152156 Notes
153157 -----
154- The Proximal point algorithm can be expressed by the following recursion:
158+ The (Accelerated) Proximal point algorithm can be expressed by the
159+ following recursion:
155160
156161 .. math::
157162
158- \mathbf{x}^{k+1} = \prox_{\tau^k \epsilon g}(\mathbf{x}^k -
159- \tau^k \nabla f(\mathbf{x}^k))
163+ \mathbf{y}^{k+1} = \mathbf{x}^k + \omega^k
164+ (\mathbf{x}^k - \mathbf{x}^{k-1})
165+ \mathbf{x}^{k+1} = \prox_{\tau^k \epsilon g}(\mathbf{y}^{k+1} -
166+ \tau^k \nabla f(\mathbf{y}^{k+1})) \\
160167
161168 where at each iteration :math:`\tau^k` can be estimated by back-tracking
162169 as follows:
@@ -173,7 +180,17 @@ def ProximalGradient(proxf, proxg, x0, tau=None, beta=0.5,
173180
174181 where :math:`\tilde{f}_\tau(\mathbf{x}, \mathbf{y}) = f(\mathbf{y}) +
175182 \nabla f(\mathbf{y})^T (\mathbf{x} - \mathbf{y}) +
176- 1/(2\tau)||\mathbf{x} - \mathbf{y}||_2^2`.
183+ 1/(2\tau)||\mathbf{x} - \mathbf{y}||_2^2`,
184+ and
185+ :math:`\omega^k = 0` for ``acceleration=None``,
186+ :math:`\omega^k = k / (k + 3)` for ``acceleration=vandenberghe`` [1]_
187+ or :math:`\omega^k = (t_{k-1}-1)/t_k` for ``acceleration=fista`` where
188+ :math:`t_k = (1 + \sqrt{1+4t_{k-1}^{2}}) / 2` [2]_
189+
190+ .. [1] Vandenberghe, L., "Fast proximal gradient methods", 2010.
191+ .. [2] Beck, A., and Teboulle, M. "A Fast Iterative Shrinkage-Thresholding
192+ Algorithm for Linear Inverse Problems", SIAM Journal on
193+ Imaging Sciences, vol. 2, pp. 183-202. 2009.
177194
178195 """
179196 # check if epgs is a ve
@@ -182,9 +199,12 @@ def ProximalGradient(proxf, proxg, x0, tau=None, beta=0.5,
182199 else :
183200 epsg_print = 'Multi'
184201
202+ if acceleration not in [None , 'None' , 'vandenberghe' , 'fista' ]:
203+ raise NotImplementedError ('Acceleration should be None, vandenberghe '
204+ 'or fista' )
185205 if show :
186206 tstart = time .time ()
187- print ('Proximal Gradient\n '
207+ print ('Accelerated Proximal Gradient\n '
188208 '---------------------------------------------------------\n '
189209 'Proximal operator (f): %s\n '
190210 'Proximal operator (g): %s\n '
@@ -201,13 +221,32 @@ def ProximalGradient(proxf, proxg, x0, tau=None, beta=0.5,
201221 backtracking = True
202222 tau = 1.
203223
224+ # initialize model
225+ t = 1.
204226 x = x0 .copy ()
227+ y = x .copy ()
228+
229+ # iterate
205230 for iiter in range (niter ):
231+ xold = x .copy ()
232+
233+ # proximal step
206234 if not backtracking :
207- x = proxg .prox (x - tau * proxf .grad (x ), epsg * tau )
235+ x = proxg .prox (y - tau * proxf .grad (y ), epsg * tau )
208236 else :
209- x , tau = _backtracking (x , tau , proxf , proxg , epsg ,
237+ x , tau = _backtracking (y , tau , proxf , proxg , epsg ,
210238 beta = beta , niterback = niterback )
239+
240+ # update y
241+ if acceleration == 'vandenberghe' :
242+ omega = iiter / (iiter + 3 )
243+ elif acceleration == 'fista' :
244+ told = t
245+ t = (1. + np .sqrt (1. + 4. * t ** 2 )) / 2.
246+ omega = ((told - 1. ) / t )
247+ else :
248+ omega = 0
249+ y = x + omega * (x - xold )
211250
212251 # run callback
213252 if callback is not None :
@@ -233,40 +272,57 @@ def AcceleratedProximalGradient(proxf, proxg, x0, tau=None, beta=0.5,
233272 callback = None , show = False ):
234273 r"""Accelerated Proximal gradient
235274
236- Solves the following minimization problem using Accelerated Proximal
275+ This is a thin wrapper around :func:`pyproximal.optimization.primal.ProximalGradient` with
276+ ``vandenberghe`` or ``fista``acceleration. See :func:`pyproximal.optimization.primal.ProximalGradient`
277+ for details.
278+
279+ """
280+ warnings .warn ('AcceleratedProximalGradient has been integrated directly into ProximalGradient '
281+ 'from v0.5.0. It is recommended to start using ProximalGradient by selecting the '
282+ 'appropriate acceleration parameter as this behaviour will become default in '
283+ 'version v1.0.0 and AcceleratedProximalGradient will be removed.' , FutureWarning )
284+ return ProximalGradient (proxf , proxg , x0 , tau = tau , beta = beta ,
285+ epsg = epsg , niter = niter , niterback = niterback ,
286+ acceleration = acceleration ,
287+ callback = callback , show = show )
288+
289+
290+ def GeneralizedProximalGradient (proxfs , proxgs , x0 , tau = None ,
291+ epsg = 1. , niter = 10 ,
292+ acceleration = None ,
293+ callback = None , show = False ):
294+ r"""Generalized Proximal gradient
295+
296+ Solves the following minimization problem using Generalized Proximal
237297 gradient algorithm:
238298
239299 .. math::
240300
241- \mathbf{x} = \argmin_\mathbf{x} f(\mathbf{x}) + \epsilon g(\mathbf{x})
301+ \mathbf{x} = \argmin_\mathbf{x} \sum_{i=1}^n f_i(\mathbf{x})
302+ + \sum_{j=1}^m \tau_j g_j(\mathbf{x}),~~n,m \in \mathbb{N}^+
242303
243- where :math:`f (\mathbf{x})` is a smooth convex function with a uniquely
244- defined gradient and :math:`g (\mathbf{x})` is any convex function that
245- has a known proximal operator.
304+ where the :math:`f_i (\mathbf{x})` are smooth convex functions with a uniquely
305+ defined gradient and the :math:`g_j (\mathbf{x})` are any convex function that
306+ have a known proximal operator.
246307
247308 Parameters
248309 ----------
249- proxf : :obj:`pyproximal.ProxOperator`
250- Proximal operator of f function (must have ``grad`` implemented)
251- proxg : :obj:`pyproximal.ProxOperator`
252- Proximal operator of g function
310+ proxfs : :obj:`List of pyproximal.ProxOperator`
311+ Proximal operators of the :math:`f_i` functions (must have ``grad`` implemented)
312+ proxgs : :obj:`List of pyproximal.ProxOperator`
313+ Proximal operators of the :math:`g_j` functions
253314 x0 : :obj:`numpy.ndarray`
254315 Initial vector
255316 tau : :obj:`float` or :obj:`numpy.ndarray`, optional
256317 Positive scalar weight, which should satisfy the following condition
257318 to guarantees convergence: :math:`\tau \in (0, 1/L]` where ``L`` is
258- the Lipschitz constant of :math:`\nabla f `. When ``tau=None``,
319+ the Lipschitz constant of :math:`\sum_{i=1}^n \ nabla f_i `. When ``tau=None``,
259320 backtracking is used to adaptively estimate the best tau at each
260- iteration. Finally note that :math:`\tau` can be chosen to be a vector
261- when dealing with problems with multiple right-hand-sides
262- beta : obj:`float`, optional
263- Backtracking parameter (must be between 0 and 1)
321+ iteration.
264322 epsg : :obj:`float` or :obj:`np.ndarray`, optional
265323 Scaling factor of g function
266324 niter : :obj:`int`, optional
267325 Number of iterations of iterative scheme
268- niterback : :obj:`int`, optional
269- Max number of iterations of backtracking
270326 acceleration: :obj:`str`, optional
271327 Acceleration (``vandenberghe`` or ``fista``)
272328 callback : :obj:`callable`, optional
@@ -282,76 +338,75 @@ def AcceleratedProximalGradient(proxf, proxg, x0, tau=None, beta=0.5,
282338
283339 Notes
284340 -----
285- The Accelerated Proximal point algorithm can be expressed by the
341+ The Generalized Proximal point algorithm can be expressed by the
286342 following recursion:
287343
288344 .. math::
289-
290- \mathbf{x}^{k+1} = \prox_{\tau^k \epsilon g}(\mathbf{y}^{k+1} -
291- \tau^k \nabla f(\mathbf{y}^{k+1})) \\
292- \mathbf{y}^{k+1} = \mathbf{x}^k + \omega^k
293- (\mathbf{x}^k - \mathbf{x}^{k-1})
294-
295- where :math:`\omega^k = k / (k + 3)` for ``acceleration=vandenberghe`` [1]_
296- or :math:`\omega^k = (t_{k-1}-1)/t_k` for ``acceleration=fista`` where
297- :math:`t_k = (1 + \sqrt{1+4t_{k-1}^{2}}) / 2` [2]_
298-
299- .. [1] Vandenberghe, L., "Fast proximal gradient methods", 2010.
300- .. [2] Beck, A., and Teboulle, M. "A Fast Iterative Shrinkage-Thresholding
301- Algorithm for Linear Inverse Problems", SIAM Journal on
302- Imaging Sciences, vol. 2, pp. 183-202. 2009.
303-
345+ \text{for } j=1,\cdots,n, \\
346+ ~~~~\mathbf z_j^{k+1} = \mathbf z_j^{k} + \eta_k (prox_{\frac{\tau^k}{\omega_j} g_j}(2 \mathbf{x}^{k} - z_j^{k})
347+ - \tau^k \sum_{i=1}^n \nabla f_i(\mathbf{x}^{k})) - \mathbf{x}^{k} \\
348+ \mathbf{x}^{k+1} = \sum_{j=1}^n \omega_j f_j \\
349+
350+ where :math:`\sum_{j=1}^n \omega_j=1`.
304351 """
305- # check if epgs is a ve
352+ # check if epgs is a vector
306353 if np .asarray (epsg ).size == 1. :
307354 epsg_print = str (epsg )
308355 else :
309356 epsg_print = 'Multi'
310357
311- if acceleration not in ['vandenberghe' , 'fista' ]:
312- raise NotImplementedError ('Acceleration should be vandenberghe '
358+ if acceleration not in [None , 'None' , 'vandenberghe' , 'fista' ]:
359+ raise NotImplementedError ('Acceleration should be None, vandenberghe '
313360 'or fista' )
314361 if show :
315362 tstart = time .time ()
316- print ('Accelerated Proximal Gradient\n '
363+ print ('Generalized Proximal Gradient\n '
317364 '---------------------------------------------------------\n '
318- 'Proximal operator (f): %s\n '
319- 'Proximal operator (g): %s\n '
320- 'tau = %10e\t epsg = %s\t niter = %d\n ' % (type (proxf ),
321- type (proxg ),
322- 0 if tau is None else tau ,
365+ 'Proximal operators (f): %s\n '
366+ 'Proximal operators (g): %s\n '
367+ 'tau = %10e\n epsg = %s\t niter = %d\n ' % ([ type (proxf ) for proxf in proxfs ] ,
368+ [ type (proxg ) for proxg in proxgs ] ,
369+ 0 if tau is None else tau ,
323370 epsg_print , niter ))
324371 head = ' Itn x[0] f g J=f+eps*g'
325372 print (head )
326373
327- backtracking = False
328374 if tau is None :
329- backtracking = True
330375 tau = 1.
331376
332377 # initialize model
333378 t = 1.
334379 x = x0 .copy ()
335380 y = x .copy ()
381+ zs = [x .copy () for _ in range (len (proxgs ))]
336382
337383 # iterate
338384 for iiter in range (niter ):
339385 xold = x .copy ()
340386
341387 # proximal step
342- if not backtracking :
343- x = proxg .prox (y - tau * proxf .grad (y ), epsg * tau )
344- else :
345- x , tau = _backtracking (y , tau , proxf , proxg , epsg ,
346- beta = beta , niterback = niterback )
388+ grad = np .zeros_like (x )
389+ for i , proxf in enumerate (proxfs ):
390+ grad += proxf .grad (x )
391+
392+ sol = np .zeros_like (x )
393+ for i , proxg in enumerate (proxgs ):
394+ tmp = 2 * y - zs [i ] - tau * grad
395+ tmp [:] = proxg .prox (tmp , tau * len (proxgs ) )
396+ zs [i ] += epsg * (tmp - y )
397+ sol += zs [i ] / len (proxgs )
398+ x [:] = sol .copy ()
347399
348400 # update y
349401 if acceleration == 'vandenberghe' :
350402 omega = iiter / (iiter + 3 )
351- else :
403+ elif acceleration == 'fista' :
352404 told = t
353405 t = (1. + np .sqrt (1. + 4. * t ** 2 )) / 2.
354406 omega = ((told - 1. ) / t )
407+ else :
408+ omega = 0
409+
355410 y = x + omega * (x - xold )
356411
357412 # run callback
@@ -360,7 +415,7 @@ def AcceleratedProximalGradient(proxf, proxg, x0, tau=None, beta=0.5,
360415
361416 if show :
362417 if iiter < 10 or niter - iiter < 10 or iiter % (niter // 10 ) == 0 :
363- pf , pg = proxf (x ), proxg (x )
418+ pf , pg = np . sum ([ proxf (x ) for proxf in proxfs ]), np . sum ([ proxg (x ) for proxg in proxgs ] )
364419 msg = '%6g %12.5e %10.3e %10.3e %10.3e' % \
365420 (iiter + 1 , x [0 ] if x .ndim == 1 else x [0 , 0 ],
366421 pf , pg [0 ] if epsg_print == 'Multi' else pg ,
0 commit comments