|
1 | 1 | import numpy as np |
2 | | - |
| 2 | +from scipy.linalg import cho_factor, cho_solve |
3 | 3 | from scipy.sparse.linalg import lsqr |
4 | 4 | from pylops import MatrixMult, Identity |
5 | 5 | from pyproximal.ProxOperator import _check_tau |
@@ -39,9 +39,12 @@ class L2(ProxOperator): |
39 | 39 | Warm start (``True``) or not (``False``). Uses estimate from previous |
40 | 40 | call of ``prox`` method. |
41 | 41 | densesolver : :obj:`str`, optional |
42 | | - Use ``numpy`` or ``scipy`` solver when dealing with explicit operators. |
43 | | - Choose ``densesolver=None`` when using PyLops versions earlier than |
44 | | - v1.18.1 or v2.0.0 |
| 42 | + Use ``numpy``, ``scipy``, or ``factorize`` when dealing with explicit |
| 43 | + operators. The former two rely on dense solvers from either library, |
| 44 | + whilst the last computes a factorization of the matrix to invert and |
| 45 | + avoids to do so unless the :math:`\tau` or :math:`\sigma` paramets |
| 46 | + have changed. Choose ``densesolver=None`` when using PyLops versions |
| 47 | + earlier than v1.18.1 or v2.0.0 |
45 | 48 |
|
46 | 49 | Notes |
47 | 50 | ----- |
@@ -96,6 +99,11 @@ def __init__(self, Op=None, b=None, q=None, sigma=1., alpha=1., |
96 | 99 | self.densesolver = densesolver |
97 | 100 | self.count = 0 |
98 | 101 |
|
| 102 | + # when using factorize, store the first tau*sigma=0 so that the |
| 103 | + # first time it will be recomputed (as tau cannot be 0) |
| 104 | + if self.densesolver == 'factorize': |
| 105 | + self.tausigma = 0 |
| 106 | + |
99 | 107 | # create data term |
100 | 108 | if self.Op is not None and self.b is not None: |
101 | 109 | self.OpTb = self.sigma * self.Op.H @ self.b |
@@ -137,14 +145,23 @@ def prox(self, x, tau): |
137 | 145 | if self.q is not None: |
138 | 146 | y -= tau * self.alpha * self.q |
139 | 147 | if self.Op.explicit: |
140 | | - Op1 = MatrixMult(np.eye(self.Op.shape[1]) + |
141 | | - tau * self.sigma * self.ATA) |
142 | | - if self.densesolver is None: |
143 | | - # to allow backward compatibility with PyLops versions earlier |
144 | | - # than v1.18.1 and v2.0.0 |
145 | | - x = Op1.div(y) |
| 148 | + if self.densesolver != 'factorize': |
| 149 | + Op1 = MatrixMult(np.eye(self.Op.shape[1]) + |
| 150 | + tau * self.sigma * self.ATA) |
| 151 | + if self.densesolver is None: |
| 152 | + # to allow backward compatibility with PyLops versions earlier |
| 153 | + # than v1.18.1 and v2.0.0 |
| 154 | + x = Op1.div(y) |
| 155 | + else: |
| 156 | + x = Op1.div(y, densesolver=self.densesolver) |
146 | 157 | else: |
147 | | - x = Op1.div(y, densesolver=self.densesolver) |
| 158 | + if self.tausigma != tau * self.sigma: |
| 159 | + # recompute factorization |
| 160 | + self.tausigma = tau * self.sigma |
| 161 | + ATA = np.eye(self.Op.shape[1]) + \ |
| 162 | + self.tausigma * self.ATA |
| 163 | + self.cl = cho_factor(ATA) |
| 164 | + x = cho_solve(self.cl, y) |
148 | 165 | else: |
149 | 166 | Op1 = Identity(self.Op.shape[1], dtype=self.Op.dtype) + \ |
150 | 167 | tau * self.sigma * self.Op.H * self.Op |
|
0 commit comments