Skip to content

Commit 26d9f57

Browse files
committed
Added ADMML2 solver
This commit is partially addressing #79.
1 parent 01beccd commit 26d9f57

File tree

3 files changed

+132
-12
lines changed

3 files changed

+132
-12
lines changed

docs/source/api/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ Primal
135135

136136
AcceleratedProximalGradient
137137
ADMM
138+
ADMML2
138139
LinearizedADMM
139140
ProximalGradient
140141
ProximalPoint

pyproximal/optimization/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
ProximalGradient Proximal gradient algorithm
1313
AcceleratedProximalGradient Accelerated Proximal gradient algorithm
1414
ADMM Alternating Direction Method of Multipliers
15+
ADMML2 ADMM with L2 misfit term
1516
LinearizedADMM Linearized ADMM
1617
TwIST Two-step Iterative Shrinkage/Threshold
1718
PlugAndPlay Plug-and-Play Prior with ADMM

pyproximal/optimization/primal.py

Lines changed: 130 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import numpy as np
33

44
from math import sqrt
5+
from pylops.optimization.leastsquares import RegularizedInversion
56
from pyproximal.proximal import L2
67

78

@@ -365,20 +366,26 @@ def ADMM(proxf, proxg, x0, tau, niter=10, callback=None, show=False):
365366
366367
\mathbf{x},\mathbf{z} = \argmin_{\mathbf{x},\mathbf{z}}
367368
f(\mathbf{x}) + g(\mathbf{z}) \\
368-
s.t \; \mathbf{Ax}+\mathbf{Bz}=c
369+
s.t. \; \mathbf{x}=\mathbf{z}
369370
370371
where :math:`f(\mathbf{x})` and :math:`g(\mathbf{z})` are any convex
371-
function that has a known proximal operator, :math:`\mathbf{A}=\mathbf{I}`,
372-
:math:`\mathbf{B}=-\mathbf{I}`, and :math:`c=0`.
373-
374-
Note that ADMM can solve the problem of the form above with any
375-
:math:`\mathbf{A}`, :math:`\mathbf{B}`, and :math:`c`: however, the solution
376-
of such problems is not generalizable as it depends on che choice of
377-
:math:`f` and :math:`g`. For this reason, we currently do not provide a
378-
solver for the more general case. One special case that is however provided
379-
is when :math:`\mathbf{B}=-\mathbf{I}` and :math:`c=0` (for any
380-
:math:`\mathbf{A}`), which can be solved by the
381-
:func:`pyproximal.optimization.primal.LinearizedADMM` solver.
372+
function that has a known proximal operator.
373+
374+
ADMM can also solve the problem of the form above with a more general
375+
constraint: :math:`\mathbf{Ax}+\mathbf{Bz}=c`. This routine implements
376+
the special case where :math:`\mathbf{A}=\mathbf{I}`, :math:`\mathbf{B}=-\mathbf{I}`,
377+
and :math:`c=0`, as a general algorithm can be obtained for any choice of
378+
:math:`f` and :math:`g` provided they have a known proximal operator.
379+
380+
On the other hand, for more general choice of :math:`\mathbf{A}`, :math:`\mathbf{B}`,
381+
and :math:`c`, the iterations are not generalizable, i.e. thye depends on the choice of
382+
:math:`f` and :math:`g` functions. For this reason, we currently only provide an additional
383+
solver for the special case where :math:`f` is a :class:`pyproximal.proximal.L2`
384+
operator with a linear operator :math:`\mathbf{G}` and data :math:`\mathbf{y}`,
385+
:math:`\mathbf{B}=-\mathbf{I}` and :math:`c=0`,
386+
called :func:`pyproximal.optimization.primal.ADMML2`. Note that for the very same choice
387+
of :math:`\mathbf{B}` and :math:`c`, the :func:`pyproximal.optimization.primal.LinearizedADMM`
388+
can also be used (and this does not require a specific choice of :math:`f`).
382389
383390
Parameters
384391
----------
@@ -407,6 +414,11 @@ def ADMM(proxf, proxg, x0, tau, niter=10, callback=None, show=False):
407414
z : :obj:`numpy.ndarray`
408415
Inverted second model
409416
417+
See Also
418+
--------
419+
ADMML2: ADMM with L2 misfit function
420+
LinearizedADMM: Linearized ADMM
421+
410422
Notes
411423
-----
412424
The ADMM algorithm can be expressed by the following recursion:
@@ -457,6 +469,107 @@ def ADMM(proxf, proxg, x0, tau, niter=10, callback=None, show=False):
457469
return x, z
458470

459471

472+
def ADMML2(proxg, Op, b, A, x0, tau, niter=10, callback=None, show=False, **kwargs_solver):
473+
r"""Alternating Direction Method of Multipliers for L2 misfit term
474+
475+
Solves the following minimization problem using Alternating Direction
476+
Method of Multipliers:
477+
478+
.. math::
479+
480+
\mathbf{x},\mathbf{z} = \argmin_{\mathbf{x},\mathbf{z}}
481+
\frac{1}{2}||\mathbf{Op}\mathbf{x} - \mathbf{b}||_2^2 + g(\mathbf{z}) \\
482+
s.t. \; \mathbf{Ax}=\mathbf{z}
483+
484+
where :math:`g(\mathbf{z})` is any convex function that has a known proximal operator.
485+
486+
Parameters
487+
----------
488+
proxg : :obj:`pyproximal.ProxOperator`
489+
Proximal operator of g function
490+
Op : :obj:`pylops.LinearOperator`
491+
Linear operator of data misfit term
492+
b : :obj:`numpy.ndarray`
493+
Data
494+
A : :obj:`pylops.LinearOperator`
495+
Linear operator of regularization term
496+
x0 : :obj:`numpy.ndarray`
497+
Initial vector
498+
tau : :obj:`float`, optional
499+
Positive scalar weight, which should satisfy the following condition
500+
to guarantees convergence: :math:`\tau \in (0, 1/\lambda_{max}(\mathbf{A}^H\mathbf{A})]`.
501+
niter : :obj:`int`, optional
502+
Number of iterations of iterative scheme
503+
callback : :obj:`callable`, optional
504+
Function with signature (``callback(x)``) to call after each iteration
505+
where ``x`` is the current model vector
506+
show : :obj:`bool`, optional
507+
Display iterations log
508+
**kwargs_solver
509+
Arbitrary keyword arguments for :py:func:`scipy.sparse.linalg.lsqr` used
510+
to solve the x-update
511+
512+
Returns
513+
-------
514+
x : :obj:`numpy.ndarray`
515+
Inverted model
516+
z : :obj:`numpy.ndarray`
517+
Inverted second model
518+
519+
See Also
520+
--------
521+
ADMM: ADMM
522+
LinearizedADMM: Linearized ADMM
523+
524+
Notes
525+
-----
526+
The ADMM algorithm can be expressed by the following recursion:
527+
528+
.. math::
529+
530+
\mathbf{x}^{k+1} = \argmin_{\mathbf{x}} \frac{1}{2}||\mathbf{Op}\mathbf{x}
531+
- \mathbf{b}||_2^2 + \frac{1}{2\tau} ||\mathbf{Ax} - \mathbf{z}^k + \mathbf{u}^k||_2^2\\
532+
\mathbf{z}^{k+1} = \prox_{\tau g}(\mathbf{Ax}^{k+1} + \mathbf{u}^{k})\\
533+
\mathbf{u}^{k+1} = \mathbf{u}^{k} + \mathbf{Ax}^{k+1} - \mathbf{z}^{k+1}
534+
535+
"""
536+
if show:
537+
tstart = time.time()
538+
print('ADMM\n'
539+
'---------------------------------------------------------\n'
540+
'Proximal operator (g): %s\n'
541+
'tau = %10e\tniter = %d\n' % (type(proxg), tau, niter))
542+
head = ' Itn x[0] f g J = f + g'
543+
print(head)
544+
545+
sqrttau = 1. / sqrt(tau)
546+
x = x0.copy()
547+
u = z = np.zeros(A.shape[0], dtype=A.dtype)
548+
for iiter in range(niter):
549+
# create augumented system
550+
x = RegularizedInversion(Op, [A, ], b,
551+
dataregs=[z - u, ], epsRs=[sqrttau, ],
552+
x0=x, **kwargs_solver)
553+
Ax = A @ x
554+
z = proxg.prox(Ax + u, tau)
555+
u = u + Ax - z
556+
557+
# run callback
558+
if callback is not None:
559+
callback(x)
560+
561+
if show:
562+
if iiter < 10 or niter - iiter < 10 or iiter % (niter // 10) == 0:
563+
pf, pg = np.linalg.norm(Op @ x - b), proxg(Ax)
564+
msg = '%6g %12.5e %10.3e %10.3e %10.3e' % \
565+
(iiter + 1, x[0], pf, pg, pf + pg)
566+
print(msg)
567+
if show:
568+
print('\nTotal time (s) = %.2f' % (time.time() - tstart))
569+
print('---------------------------------------------------------\n')
570+
return x, z
571+
572+
460573
def LinearizedADMM(proxf, proxg, A, x0, tau, mu, niter=10,
461574
callback=None, show=False):
462575
r"""Linearized Alternating Direction Method of Multipliers
@@ -505,6 +618,11 @@ def LinearizedADMM(proxf, proxg, A, x0, tau, mu, niter=10,
505618
z : :obj:`numpy.ndarray`
506619
Inverted second model
507620
621+
See Also
622+
--------
623+
ADMM: ADMM
624+
ADMML2: ADMM with L2 misfit function
625+
508626
Notes
509627
-----
510628
The Linearized-ADMM algorithm can be expressed by the following recursion:

0 commit comments

Comments
 (0)