Add damp support to CG solver (closes #406)#752
Conversation
The CG solver only solved Op x = y. Re-introduce the damp coefficient (removed in v2) so it can solve the damped system (Op + damp*I) x = y, consistent with cgls/lsqr. Damping is applied by adding damp*c to every operator application Op*c in the iterations (and to the initial residual when x0 is given); damp=0 reproduces the previous behaviour exactly. Thread damp through cg(), CG.solve and CG.setup, and document it. Add test_cg_damp verifying that internal damping matches both adding damp*I to the operator (with damp=0) and the dense (Op + damp*I)^-1 y, and that damp=0 still recovers the undamped solution.
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 0 |
| Duplication | 6 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
- shorten damp docstrings to 'Damping coefficient' (cls_basic CG.setup/solve, basic.cg) and simplify CG Notes to a single damped-system sentence - follow preallocate vs non-preallocate residual-update pattern for the initial damping correction in setup - shorten the damping comment in step to '# add damping contribution' - test_cg_damp: build y from a known x (y = Aop * x), rename loop var to 'preallocate', drop issue ref from docstring
|
Thanks for the review! Addressed all points in 6828c57:
(The red |
Thanks. Please reply to each comment with link to commit you fixed the issue. |
Motivation
CGcurrently only solvesconsistent with
cgls/lsqr(which both exposedamp). Thedampargument used to exist onCGas an unused stub and was removed in v2; this PR re-introduces it with a working implementation.This matches the issue's definition of done ("Added implementation with$(A + \text{damp},I)x = y$ , equivalent to adding
damp != 0") and the semantics @mrava87 confirmed there: solvedamp*Ito the operator and usingdamp=0.Implementation
Damping is applied by adding$\epsilon\mathbf{c}$ to every application of the operator $\mathbf{Op},\mathbf{c}$ in the iterations (and $\epsilon\mathbf{x}_0$ to the initial residual when $\mathbf{Op} + \epsilon\mathbf{I}$ :
x0is given) — i.e. the operator is implicitly replaced bydampis threaded throughcg(),CG.solveandCG.setup(default0.0). Withdamp=0.0the code path is identical to before, so existing behaviour is unchanged.Opmust remain square and SPD, whichVerification
Added
test_cg_dampwhich checks, for bothpreallocatemodes, the equivalence @mrava87 suggested in the issue:cg(Op, y, damp=d)== addingd*Ito the operatorcg(Op + d*I, y, damp=0)(Op + d*I)^{-1} ydamp=0still recovers the undampedOp^{-1} yFull
pytests/test_solver.pypasses (100 tests),ruff checkclean.