|
2 | 2 | import numpy as np |
3 | 3 |
|
4 | 4 | from math import sqrt |
| 5 | +from pylops.optimization.leastsquares import RegularizedInversion |
5 | 6 | from pyproximal.proximal import L2 |
6 | 7 |
|
7 | 8 |
|
@@ -365,20 +366,26 @@ def ADMM(proxf, proxg, x0, tau, niter=10, callback=None, show=False): |
365 | 366 |
|
366 | 367 | \mathbf{x},\mathbf{z} = \argmin_{\mathbf{x},\mathbf{z}} |
367 | 368 | f(\mathbf{x}) + g(\mathbf{z}) \\ |
368 | | - s.t \; \mathbf{Ax}+\mathbf{Bz}=c |
| 369 | + s.t. \; \mathbf{x}=\mathbf{z} |
369 | 370 |
|
370 | 371 | 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`). |
382 | 389 |
|
383 | 390 | Parameters |
384 | 391 | ---------- |
@@ -407,6 +414,11 @@ def ADMM(proxf, proxg, x0, tau, niter=10, callback=None, show=False): |
407 | 414 | z : :obj:`numpy.ndarray` |
408 | 415 | Inverted second model |
409 | 416 |
|
| 417 | + See Also |
| 418 | + -------- |
| 419 | + ADMML2: ADMM with L2 misfit function |
| 420 | + LinearizedADMM: Linearized ADMM |
| 421 | +
|
410 | 422 | Notes |
411 | 423 | ----- |
412 | 424 | 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): |
457 | 469 | return x, z |
458 | 470 |
|
459 | 471 |
|
| 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 | + |
460 | 573 | def LinearizedADMM(proxf, proxg, A, x0, tau, mu, niter=10, |
461 | 574 | callback=None, show=False): |
462 | 575 | r"""Linearized Alternating Direction Method of Multipliers |
@@ -505,6 +618,11 @@ def LinearizedADMM(proxf, proxg, A, x0, tau, mu, niter=10, |
505 | 618 | z : :obj:`numpy.ndarray` |
506 | 619 | Inverted second model |
507 | 620 |
|
| 621 | + See Also |
| 622 | + -------- |
| 623 | + ADMM: ADMM |
| 624 | + ADMML2: ADMM with L2 misfit function |
| 625 | +
|
508 | 626 | Notes |
509 | 627 | ----- |
510 | 628 | The Linearized-ADMM algorithm can be expressed by the following recursion: |
|
0 commit comments