Repository navigation
Enable TV, L1-wavelet and other sparse regularisation in LSQR and CGLS, via a new IRLS algorithm and Tikhonov operators - #2369
Conversation
|
This looks fantastic @bryceshirley, thank you! Would you be able to join a developer meeting to walk us through it (I think the 22nd is our next one)? I know you've already spoken to/worked with the team, but it would be good to go through the current status together. |
Sounds good! I can present that pull request and explain the decisions in terms of interface and memory management, as well as alternatives. How much time should it be? |
MargaretDuff
left a comment
There was a problem hiding this comment.
I have left a bunch of comments, none of them very substantial. A lot of it was deleting unnecessary comments.
What I suggest, and would like to discuss at the developer meeting next week, is a change to the user interaction with IRLS. Currently, to use IRLS the user must pass a set up CGLS or LSQR algorithm, with the right operators, the right choice of the weighted flag in CGLS or LSQR. This confuses the standalone CGLS or LSQR algorithm with parameters that might not be relevant and makes things like the form parameter difficult to explain. It would also allow us to hide most of the new create_tikhonov_operator, resolve_form, WeightedStructOpeartor, BlockTikhonovOperator - they become implementation details rather than things a user must understand to call IRLS.
On the other hand, it looses some flexibility - it would be harder to pass a custom inner solver. Though possibly later on we could have a IRLS.from_solver(inner_solver,..) alternative set up later?
For IRLS it would allow us to have a safe default inner solver - LSQR is better conditions.
MargaretDuff
left a comment
There was a problem hiding this comment.
I have left a bunch of comments, none of them very substantial. A lot of it was deleting unnecessary comments.
What I suggest, and would like to discuss at the developer meeting next week, is a change to the user interaction with IRLS. Currently, to use IRLS the user must pass a set up CGLS or LSQR algorithm, with the right operators, the right choice of the weighted flag in CGLS or LSQR. This confuses the standalone CGLS or LSQR algorithm with parameters that might not be relevant and makes things like the form parameter difficult to explain. It would also allow us to hide most of the new create_tikhonov_operator, resolve_form, WeightedStructOpeartor, BlockTikhonovOperator - they become implementation details rather than things a user must understand to call IRLS.
On the other hand, it looses some flexibility - it would be harder to pass a custom inner solver. Though possibly later on we could have a IRLS.from_solver(inner_solver,..) alternative set up later?
For IRLS it would allow us to have a safe default inner solver - LSQR is better conditions.
Co-authored-by: Margaret Duff <43645617+MargaretDuff@users.noreply.github.com>
7fd6acb to
aebf28e
Compare
68e9097 to
172c647
Compare
Signed-off-by: Bryce Shirley <114733577+bryceshirley@users.noreply.github.com>
172c647 to
050128e
Compare
|
I have updated the PR to now make IRLS a stand alone algorithm with default inner loop solver LSQR in block form and moved the demo notebook into a CIL-Demos pull request. |
MargaretDuff
left a comment
There was a problem hiding this comment.
A quick review of the CGLS algorithm. Will try and keep looking this afternoon
| # Catch self.regalpha and the operator's regalpha disagreeing. | ||
| operator_alpha = getattr(self.operator, 'regalpha', None) | ||
| if operator_alpha is not None and operator_alpha != self.regalpha: | ||
| raise ValueError( |
There was a problem hiding this comment.
I am not sure a user would understand what this means if they see this error! When would you expect it to come up?
There was a problem hiding this comment.
There are a lot of if block and if standard statements in this file. I wonder if instead we use a factory method? Could the user call CGLS and then inside that file it either sets up and returns classes _standardCGLS or _blockCGLS ? Or perhaps a standard CGLS that factories off to _blockCGLS when needed but that _blockCGLS also uses things from CGLS?
Realise we don't do this elsewhere in our algorithm class - @gfardell or @paskino would appreciate if you had any thoughts?
Co-authored-by: Margaret Duff <43645617+MargaretDuff@users.noreply.github.com> Signed-off-by: Bryce Shirley <114733577+bryceshirley@users.noreply.github.com>
MargaretDuff
left a comment
There was a problem hiding this comment.
A pass at IRLS - Thanks Bryce!
|
|
||
| Allowing the inner solver to solve | ||
| .. math:: | ||
| \min_x \|Ax - b\|_2^2 + \alpha^2 \|Lx\|_1 |
There was a problem hiding this comment.
Can we put this as the first equation on the algorithm? So,
- This equation solves
- It does this by
- Any notes
- Parameters
| ---------- | ||
| initial : DataContainer, optional | ||
| Starting point of the first inner solve, and the point the first | ||
| weights are computed from. Defaults to zeros. |
There was a problem hiding this comment.
Maybe not a code question but for when we recommend this to users - are the first weights defaulted zeros? Would initialising the weights with a FBP recon be a good idea? If we put in bad initial weights, could the algorithm recover?
| struct_operator : Operator, optional | ||
| The structural operator :math:`L`. ``None`` means the identity, | ||
| i.e. a plain L1 penalty on the solution. | ||
| inner_solver : class, default LSQR |
There was a problem hiding this comment.
Do you pass the class or pass a string? Could we pass both? I.e. a string either LSQR or CGLS or a class with these properties...
I think our basic users would prefer just a string but our mathematical users might appreciate being able to pass their own custom solver.
|
|
||
| # Solution buffer for standard form's (WL)^-1 mapping; block form uses the live iterate. | ||
| self.tmp_solution = ( | ||
| self.inner_solver.solution_geometry().allocate(0) |
There was a problem hiding this comment.
Note that this is probably not SIRF compatible
| u = self.inner_solver.get_output() | ||
|
|
||
| residual = self.inner_solver.operator.operator.direct(u) | ||
| residual.sapyb(1.0, self.inner_solver.data, -1.0, out=residual) | ||
| data_term = residual.squared_norm() |
There was a problem hiding this comment.
Any way to reuse memory for this and not reallocate?
|
@lauramurgatroyd @gfardell @paskino - Just to say that I ran out of time looking more at this. It would be great if you have time to give Bryce some feedback! Thank-you! |
61b70d8 to
e7f2821
Compare
Description
Adds an IRLS (Iteratively Reweighted Least Squares) outer algorithm that solves the sparsity-promoting problem
as a sequence of reweighted least-squares problems, each handled by an inner
LSQRorCGLS. To support it,LSQRandCGLSgain structured Tikhonov regularisation in block and standard form.See the notebook regularisation-for-krylov-methods.ipynb in CIL-Demos/misc pull request 292 for example usage.
Contribution Notes
Checklist