@@ -372,7 +372,106 @@ 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 , callback = None , show = False ):
376+ r"""Half Quadratic splitting
377+
378+ Solves the following minimization problem using Half Quadratic splitting
379+ algorithm:
380+
381+ .. math::
382+
383+ \mathbf{x},\mathbf{z} = \argmin_{\mathbf{x},\mathbf{z}}
384+ f(\mathbf{x}) + g(\mathbf{z}) \\
385+ s.t. \; \mathbf{x}=\mathbf{z}
386+
387+ where :math:`f(\mathbf{x})` and :math:`g(\mathbf{z})` are any convex
388+ function that has a known proximal operator.
389+
390+ Parameters
391+ ----------
392+ proxf : :obj:`pyproximal.ProxOperator`
393+ Proximal operator of f function
394+ proxg : :obj:`pyproximal.ProxOperator`
395+ Proximal operator of g function
396+ x0 : :obj:`numpy.ndarray`
397+ Initial vector
398+ tau : :obj:`float`, optional
399+ CHECK!!!
400+ niter : :obj:`int`, optional
401+ Number of iterations of iterative scheme
402+ gfirst : :obj:`bool`, optional
403+ Apply Proximal of operator ``g`` first (``True``) or Proximal of
404+ operator ``f`` first (``False``)
405+ callback : :obj:`callable`, optional
406+ Function with signature (``callback(x)``) to call after each iteration
407+ where ``x`` is the current model vector
408+ show : :obj:`bool`, optional
409+ Display iterations log
410+
411+ Returns
412+ -------
413+ x : :obj:`numpy.ndarray`
414+ Inverted model
415+ z : :obj:`numpy.ndarray`
416+ Inverted second model
417+
418+ Notes
419+ -----
420+ The HQS algorithm can be expressed by the following recursion [1]_:
421+
422+ .. math::
423+
424+ \mathbf{z}^{k+1} = \prox_{\tau g}(\mathbf{x}^{k})
425+ \mathbf{x}^{k+1} = \prox_{\tau f}(\mathbf{z}^{k+1})\\
426+
427+ Note that ``x`` and ``z`` converge to each other, however if iterations are
428+ stopped too early ``x`` is guaranteed to belong to the domain of ``f``
429+ while ``z`` is guaranteed to belong to the domain of ``g``. Depending on
430+ the problem either of the two may be the best solution.
431+
432+ .. [1] D., Geman, and C., Yang, "Nonlinear image recovery with halfquadratic
433+ regularization", IEEE Transactions on Image Processing,
434+ 4, 7, pp. 932-946, 1995.
435+
436+ """
437+ if show :
438+ tstart = time .time ()
439+ print ('HQS\n '
440+ '---------------------------------------------------------\n '
441+ 'Proximal operator (f): %s\n '
442+ 'Proximal operator (g): %s\n '
443+ 'tau = %10e\t niter = %d\n ' % (type (proxf ), type (proxg ),
444+ tau , niter ))
445+ head = ' Itn x[0] f g J = f + g'
446+ print (head )
447+
448+ x = x0 .copy ()
449+ z = np .zeros_like (x )
450+ for iiter in range (niter ):
451+ if gfirst :
452+ z = proxg .prox (x , tau )
453+ x = proxf .prox (z , tau )
454+ else :
455+ x = proxf .prox (z , tau )
456+ z = proxg .prox (x , tau )
457+
458+ # run callback
459+ if callback is not None :
460+ callback (x )
461+
462+ if show :
463+ if iiter < 10 or niter - iiter < 10 or iiter % (niter // 10 ) == 0 :
464+ pf , pg = proxf (x ), proxg (x )
465+ msg = '%6g %12.5e %10.3e %10.3e %10.3e' % \
466+ (iiter + 1 , x [0 ], pf , pg , pf + pg )
467+ print (msg )
468+ if show :
469+ print ('\n Total time (s) = %.2f' % (time .time () - tstart ))
470+ print ('---------------------------------------------------------\n ' )
471+ return x , z
472+
473+
474+ def ADMM (proxf , proxg , x0 , tau , niter = 10 , gfirst = False , callback = None , show = False ):
376475 r"""Alternating Direction Method of Multipliers
377476
378477 Solves the following minimization problem using Alternating Direction
@@ -417,6 +516,9 @@ def ADMM(proxf, proxg, x0, tau, niter=10, callback=None, show=False):
417516 the Lipschitz constant of :math:`\nabla f`.
418517 niter : :obj:`int`, optional
419518 Number of iterations of iterative scheme
519+ gfirst : :obj:`bool`, optional
520+ Apply Proximal of operator ``g`` first (``True``) or Proximal of
521+ operator ``f`` first (``False``)
420522 callback : :obj:`callable`, optional
421523 Function with signature (``callback(x)``) to call after each iteration
422524 where ``x`` is the current model vector
@@ -445,7 +547,7 @@ def ADMM(proxf, proxg, x0, tau, niter=10, callback=None, show=False):
445547 \mathbf{z}^{k+1} = \prox_{\tau g}(\mathbf{x}^{k+1} + \mathbf{u}^{k})\\
446548 \mathbf{u}^{k+1} = \mathbf{u}^{k} + \mathbf{x}^{k+1} - \mathbf{z}^{k+1}
447549
448- Note that ``x`` and ``z`` converge to each other, but if iterations are
550+ Note that ``x`` and ``z`` converge to each other, however if iterations are
449551 stopped too early ``x`` is guaranteed to belong to the domain of ``f``
450552 while ``z`` is guaranteed to belong to the domain of ``g``. Depending on
451553 the problem either of the two may be the best solution.
@@ -465,8 +567,12 @@ def ADMM(proxf, proxg, x0, tau, niter=10, callback=None, show=False):
465567 x = x0 .copy ()
466568 u = z = np .zeros_like (x )
467569 for iiter in range (niter ):
468- x = proxf .prox (z - u , tau )
469- z = proxg .prox (x + u , tau )
570+ if gfirst :
571+ z = proxg .prox (x + u , tau )
572+ x = proxf .prox (z - u , tau )
573+ else :
574+ x = proxf .prox (z - u , tau )
575+ z = proxg .prox (x + u , tau )
470576 u = u + x - z
471577
472578 # run callback
0 commit comments