@@ -372,7 +372,115 @@ def AcceleratedProximalGradient(proxf, proxg, x0, tau=None, beta=0.5,
372372 return x
373373
374374
375- def ADMM (proxf , proxg , x0 , tau , niter = 10 , callback = None , show = False ):
375+ def HQS (proxf , proxg , x0 , tau , niter = 10 , gfirst = True ,
376+ callback = None , callbackz = False , show = False ):
377+ r"""Half Quadratic splitting
378+
379+ Solves the following minimization problem using Half Quadratic splitting
380+ algorithm:
381+
382+ .. math::
383+
384+ \mathbf{x},\mathbf{z} = \argmin_{\mathbf{x},\mathbf{z}}
385+ f(\mathbf{x}) + g(\mathbf{z}) \\
386+ s.t. \; \mathbf{x}=\mathbf{z}
387+
388+ where :math:`f(\mathbf{x})` and :math:`g(\mathbf{z})` are any convex
389+ function that has a known proximal operator.
390+
391+ Parameters
392+ ----------
393+ proxf : :obj:`pyproximal.ProxOperator`
394+ Proximal operator of f function
395+ proxg : :obj:`pyproximal.ProxOperator`
396+ Proximal operator of g function
397+ x0 : :obj:`numpy.ndarray`
398+ Initial vector
399+ tau : :obj:`float`, optional
400+ Positive scalar weight, which should satisfy the following condition
401+ to guarantees convergence: :math:`\tau \in (0, 1/L]` where ``L`` is
402+ the Lipschitz constant of :math:`\nabla f`.
403+ niter : :obj:`int`, optional
404+ Number of iterations of iterative scheme
405+ gfirst : :obj:`bool`, optional
406+ Apply Proximal of operator ``g`` first (``True``) or Proximal of
407+ operator ``f`` first (``False``)
408+ callback : :obj:`callable`, optional
409+ Function with signature (``callback(x)``) to call after each iteration
410+ where ``x`` is the current model vector
411+ callbackz : :obj:`bool`, optional
412+ Modify callback signature to (``callback(x, z)``) when ``callbackz=True``
413+ show : :obj:`bool`, optional
414+ Display iterations log
415+
416+ Returns
417+ -------
418+ x : :obj:`numpy.ndarray`
419+ Inverted model
420+ z : :obj:`numpy.ndarray`
421+ Inverted second model
422+
423+ Notes
424+ -----
425+ The HQS algorithm can be expressed by the following recursion [1]_:
426+
427+ .. math::
428+
429+ \mathbf{z}^{k+1} = \prox_{\tau g}(\mathbf{x}^{k})
430+ \mathbf{x}^{k+1} = \prox_{\tau f}(\mathbf{z}^{k+1})\\
431+
432+ Note that ``x`` and ``z`` converge to each other, however if iterations are
433+ stopped too early ``x`` is guaranteed to belong to the domain of ``f``
434+ while ``z`` is guaranteed to belong to the domain of ``g``. Depending on
435+ the problem either of the two may be the best solution.
436+
437+ .. [1] D., Geman, and C., Yang, "Nonlinear image recovery with halfquadratic
438+ regularization", IEEE Transactions on Image Processing,
439+ 4, 7, pp. 932-946, 1995.
440+
441+ """
442+ if show :
443+ tstart = time .time ()
444+ print ('HQS\n '
445+ '---------------------------------------------------------\n '
446+ 'Proximal operator (f): %s\n '
447+ 'Proximal operator (g): %s\n '
448+ 'tau = %10e\t niter = %d\n ' % (type (proxf ), type (proxg ),
449+ tau , niter ))
450+ head = ' Itn x[0] f g J = f + g'
451+ print (head )
452+
453+ x = x0 .copy ()
454+ z = np .zeros_like (x )
455+ for iiter in range (niter ):
456+ if gfirst :
457+ z = proxg .prox (x , tau )
458+ x = proxf .prox (z , tau )
459+ else :
460+ x = proxf .prox (z , tau )
461+ z = proxg .prox (x , tau )
462+
463+ # run callback
464+ if callback is not None :
465+ if callbackz :
466+ callback (x , z )
467+ else :
468+ callback (x )
469+
470+ if show :
471+ if iiter < 10 or niter - iiter < 10 or iiter % (niter // 10 ) == 0 :
472+ pf , pg = proxf (x ), proxg (x )
473+ msg = '%6g %12.5e %10.3e %10.3e %10.3e' % \
474+ (iiter + 1 , x [0 ], pf , pg , pf + pg )
475+ print (msg )
476+ if show :
477+ print ('\n Total time (s) = %.2f' % (time .time () - tstart ))
478+ print ('---------------------------------------------------------\n ' )
479+ return x , z
480+
481+
482+ def ADMM (proxf , proxg , x0 , tau , niter = 10 , gfirst = False ,
483+ callback = None , callbackz = False , show = False ):
376484 r"""Alternating Direction Method of Multipliers
377485
378486 Solves the following minimization problem using Alternating Direction
@@ -417,9 +525,14 @@ def ADMM(proxf, proxg, x0, tau, niter=10, callback=None, show=False):
417525 the Lipschitz constant of :math:`\nabla f`.
418526 niter : :obj:`int`, optional
419527 Number of iterations of iterative scheme
528+ gfirst : :obj:`bool`, optional
529+ Apply Proximal of operator ``g`` first (``True``) or Proximal of
530+ operator ``f`` first (``False``)
420531 callback : :obj:`callable`, optional
421532 Function with signature (``callback(x)``) to call after each iteration
422533 where ``x`` is the current model vector
534+ callbackz : :obj:`bool`, optional
535+ Modify callback signature to (``callback(x, z)``) when ``callbackz=True``
423536 show : :obj:`bool`, optional
424537 Display iterations log
425538
@@ -445,7 +558,7 @@ def ADMM(proxf, proxg, x0, tau, niter=10, callback=None, show=False):
445558 \mathbf{z}^{k+1} = \prox_{\tau g}(\mathbf{x}^{k+1} + \mathbf{u}^{k})\\
446559 \mathbf{u}^{k+1} = \mathbf{u}^{k} + \mathbf{x}^{k+1} - \mathbf{z}^{k+1}
447560
448- Note that ``x`` and ``z`` converge to each other, but if iterations are
561+ Note that ``x`` and ``z`` converge to each other, however if iterations are
449562 stopped too early ``x`` is guaranteed to belong to the domain of ``f``
450563 while ``z`` is guaranteed to belong to the domain of ``g``. Depending on
451564 the problem either of the two may be the best solution.
@@ -465,14 +578,20 @@ def ADMM(proxf, proxg, x0, tau, niter=10, callback=None, show=False):
465578 x = x0 .copy ()
466579 u = z = np .zeros_like (x )
467580 for iiter in range (niter ):
468- x = proxf .prox (z - u , tau )
469- z = proxg .prox (x + u , tau )
581+ if gfirst :
582+ z = proxg .prox (x + u , tau )
583+ x = proxf .prox (z - u , tau )
584+ else :
585+ x = proxf .prox (z - u , tau )
586+ z = proxg .prox (x + u , tau )
470587 u = u + x - z
471588
472589 # run callback
473590 if callback is not None :
474- callback (x )
475-
591+ if callbackz :
592+ callback (x , z )
593+ else :
594+ callback (x )
476595 if show :
477596 if iiter < 10 or niter - iiter < 10 or iiter % (niter // 10 ) == 0 :
478597 pf , pg = proxf (x ), proxg (x )
0 commit comments