xref: /petsc/src/ksp/ksp/impls/lsqr/lsqr.c (revision 4e8208cbcbc709572b8abe32f33c78b69c819375)
1 /* lourens.vanzanen@shell.com contributed the standard error estimates of the solution, Jul 25, 2006 */
2 /* Bas van't Hof contributed the preconditioned aspects Feb 10, 2010 */
3 
4 #define SWAP(a, b, c) \
5   do { \
6     c = a; \
7     a = b; \
8     b = c; \
9   } while (0)
10 
11 #include <petsc/private/kspimpl.h> /*I "petscksp.h" I*/
12 #include <petscdraw.h>
13 
14 typedef struct {
15   PetscInt  nwork_n, nwork_m;
16   Vec      *vwork_m;    /* work vectors of length m, where the system is size m x n */
17   Vec      *vwork_n;    /* work vectors of length n */
18   Vec       se;         /* Optional standard error vector */
19   PetscBool se_flg;     /* flag for -ksp_lsqr_set_standard_error */
20   PetscBool exact_norm; /* flag for -ksp_lsqr_exact_mat_norm */
21   PetscReal arnorm;     /* Good estimate of norm((A*inv(Pmat))'*r), where r = A*x - b, used in specific stopping criterion */
22   PetscReal anorm;      /* Poor estimate of norm(A*inv(Pmat),'fro') used in specific stopping criterion */
23   /* Backup previous convergence test */
24   KSPConvergenceTestFn *converged;
25   PetscCtxDestroyFn    *convergeddestroy;
26   void                 *cnvP;
27 } KSP_LSQR;
28 
VecSquare(Vec v)29 static PetscErrorCode VecSquare(Vec v)
30 {
31   PetscScalar *x;
32   PetscInt     i, n;
33 
34   PetscFunctionBegin;
35   PetscCall(VecGetLocalSize(v, &n));
36   PetscCall(VecGetArray(v, &x));
37   for (i = 0; i < n; i++) x[i] *= PetscConj(x[i]);
38   PetscCall(VecRestoreArray(v, &x));
39   PetscFunctionReturn(PETSC_SUCCESS);
40 }
41 
KSPSetUp_LSQR(KSP ksp)42 static PetscErrorCode KSPSetUp_LSQR(KSP ksp)
43 {
44   KSP_LSQR *lsqr = (KSP_LSQR *)ksp->data;
45   PetscBool nopreconditioner;
46 
47   PetscFunctionBegin;
48   PetscCall(PetscObjectTypeCompare((PetscObject)ksp->pc, PCNONE, &nopreconditioner));
49 
50   if (lsqr->vwork_m) PetscCall(VecDestroyVecs(lsqr->nwork_m, &lsqr->vwork_m));
51 
52   if (lsqr->vwork_n) PetscCall(VecDestroyVecs(lsqr->nwork_n, &lsqr->vwork_n));
53 
54   lsqr->nwork_m = 2;
55   if (nopreconditioner) lsqr->nwork_n = 4;
56   else lsqr->nwork_n = 5;
57   PetscCall(KSPCreateVecs(ksp, lsqr->nwork_n, &lsqr->vwork_n, lsqr->nwork_m, &lsqr->vwork_m));
58 
59   if (lsqr->se_flg && !lsqr->se) {
60     PetscCall(VecDuplicate(lsqr->vwork_n[0], &lsqr->se));
61     PetscCall(VecSet(lsqr->se, PETSC_INFINITY));
62   } else if (!lsqr->se_flg) {
63     PetscCall(VecDestroy(&lsqr->se));
64   }
65   PetscFunctionReturn(PETSC_SUCCESS);
66 }
67 
KSPSolve_LSQR(KSP ksp)68 static PetscErrorCode KSPSolve_LSQR(KSP ksp)
69 {
70   PetscInt    i, size1, size2;
71   PetscScalar rho, rhobar, phi, phibar, theta, c, s, tmp, tau;
72   PetscReal   beta, alpha, rnorm;
73   Vec         X, B, V, V1, U, U1, TMP, W, W2, Z = NULL;
74   Mat         Amat, Pmat;
75   KSP_LSQR   *lsqr = (KSP_LSQR *)ksp->data;
76   PetscBool   diagonalscale, nopreconditioner;
77 
78   PetscFunctionBegin;
79   PetscCall(PCGetDiagonalScale(ksp->pc, &diagonalscale));
80   PetscCheck(!diagonalscale, PetscObjectComm((PetscObject)ksp), PETSC_ERR_SUP, "Krylov method %s does not support diagonal scaling", ((PetscObject)ksp)->type_name);
81 
82   PetscCall(PCGetOperators(ksp->pc, &Amat, &Pmat));
83   PetscCall(PetscObjectTypeCompare((PetscObject)ksp->pc, PCNONE, &nopreconditioner));
84 
85   /* vectors of length m, where system size is mxn */
86   B  = ksp->vec_rhs;
87   U  = lsqr->vwork_m[0];
88   U1 = lsqr->vwork_m[1];
89 
90   /* vectors of length n */
91   X  = ksp->vec_sol;
92   W  = lsqr->vwork_n[0];
93   V  = lsqr->vwork_n[1];
94   V1 = lsqr->vwork_n[2];
95   W2 = lsqr->vwork_n[3];
96   if (!nopreconditioner) Z = lsqr->vwork_n[4];
97 
98   /* standard error vector */
99   if (lsqr->se) PetscCall(VecSet(lsqr->se, 0.0));
100 
101   /* Compute initial residual, temporarily use work vector u */
102   if (!ksp->guess_zero) {
103     PetscCall(KSP_MatMult(ksp, Amat, X, U)); /*   u <- b - Ax     */
104     PetscCall(VecAYPX(U, -1.0, B));
105   } else {
106     PetscCall(VecCopy(B, U)); /*   u <- b (x is 0) */
107   }
108 
109   /* Test for nothing to do */
110   PetscCall(VecNorm(U, NORM_2, &rnorm));
111   KSPCheckNorm(ksp, rnorm);
112   PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
113   ksp->its   = 0;
114   ksp->rnorm = rnorm;
115   PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
116   PetscCall(KSPLogResidualHistory(ksp, rnorm));
117   PetscCall(KSPMonitor(ksp, 0, rnorm));
118   PetscCall((*ksp->converged)(ksp, 0, rnorm, &ksp->reason, ksp->cnvP));
119   if (ksp->reason) PetscFunctionReturn(PETSC_SUCCESS);
120 
121   beta = rnorm;
122   PetscCall(VecScale(U, 1.0 / beta));
123   PetscCall(KSP_MatMultHermitianTranspose(ksp, Amat, U, V));
124   if (nopreconditioner) {
125     PetscCall(VecNorm(V, NORM_2, &alpha));
126     KSPCheckNorm(ksp, rnorm);
127   } else {
128     /* this is an application of the preconditioner for the normal equations; not the operator, see the manual page */
129     PetscCall(PCApply(ksp->pc, V, Z));
130     PetscCall(VecDotRealPart(V, Z, &alpha));
131     if (alpha <= 0.0) {
132       ksp->reason = KSP_DIVERGED_BREAKDOWN;
133       PetscCall(PetscInfo(ksp, "Diverging due to breakdown alpha (%g) <= 0\n", (double)alpha));
134       PetscCheck(!ksp->errorifnotconverged, PetscObjectComm((PetscObject)ksp), PETSC_ERR_NOT_CONVERGED, "KSPSolve breakdown alpha (%g) <= 0", (double)alpha);
135       PetscFunctionReturn(PETSC_SUCCESS);
136     }
137     alpha = PetscSqrtReal(alpha);
138     PetscCall(VecScale(Z, 1.0 / alpha));
139   }
140   PetscCall(VecScale(V, 1.0 / alpha));
141 
142   if (nopreconditioner) {
143     PetscCall(VecCopy(V, W));
144   } else {
145     PetscCall(VecCopy(Z, W));
146   }
147 
148   if (lsqr->exact_norm) PetscCall(MatNorm(Amat, NORM_FROBENIUS, &lsqr->anorm));
149   else lsqr->anorm = 0.0;
150 
151   lsqr->arnorm = alpha * beta;
152   phibar       = beta;
153   rhobar       = alpha;
154   i            = 0;
155   do {
156     if (nopreconditioner) {
157       PetscCall(KSP_MatMult(ksp, Amat, V, U1));
158     } else {
159       PetscCall(KSP_MatMult(ksp, Amat, Z, U1));
160     }
161     PetscCall(VecAXPY(U1, -alpha, U));
162     PetscCall(VecNorm(U1, NORM_2, &beta));
163     KSPCheckNorm(ksp, beta);
164     if (beta > 0.0) {
165       PetscCall(VecScale(U1, 1.0 / beta)); /* beta*U1 = Amat*V - alpha*U */
166       if (!lsqr->exact_norm) lsqr->anorm = PetscSqrtReal(PetscSqr(lsqr->anorm) + PetscSqr(alpha) + PetscSqr(beta));
167     }
168 
169     PetscCall(KSP_MatMultHermitianTranspose(ksp, Amat, U1, V1));
170     PetscCall(VecAXPY(V1, -beta, V));
171     if (nopreconditioner) {
172       PetscCall(VecNorm(V1, NORM_2, &alpha));
173       KSPCheckNorm(ksp, alpha);
174     } else {
175       PetscCall(PCApply(ksp->pc, V1, Z));
176       PetscCall(VecDotRealPart(V1, Z, &alpha));
177       if (alpha < 0.0) {
178         PetscCall(PetscInfo(ksp, "Diverging due to breakdown alpha (%g) < 0\n", (double)alpha));
179         PetscCheck(!ksp->errorifnotconverged, PetscObjectComm((PetscObject)ksp), PETSC_ERR_NOT_CONVERGED, "KSPSolve breakdown alpha (%g) < 0", (double)alpha);
180         ksp->reason = KSP_DIVERGED_BREAKDOWN;
181         break;
182       }
183     }
184     if (alpha > 0.0) {
185       if (!nopreconditioner) {
186         alpha = PetscSqrtReal(alpha);
187         PetscCall(VecScale(Z, 1.0 / alpha));
188       }
189       PetscCall(VecScale(V1, 1.0 / alpha)); /* alpha*V1 = Amat^T*U1 - beta*V */
190     }
191     rho    = PetscSqrtScalar(rhobar * rhobar + beta * beta);
192     c      = rhobar / rho;
193     s      = beta / rho;
194     theta  = s * alpha;
195     rhobar = -c * alpha;
196     phi    = c * phibar;
197     phibar = s * phibar;
198     tau    = s * phi;
199 
200     PetscCall(VecAXPY(X, phi / rho, W)); /*    x <- x + (phi/rho) w   */
201 
202     if (lsqr->se) {
203       PetscCall(VecCopy(W, W2));
204       PetscCall(VecSquare(W2));
205       PetscCall(VecScale(W2, 1.0 / (rho * rho)));
206       PetscCall(VecAXPY(lsqr->se, 1.0, W2)); /* lsqr->se <- lsqr->se + (w^2/rho^2) */
207     }
208     if (nopreconditioner) {
209       PetscCall(VecAYPX(W, -theta / rho, V1)); /* w <- v - (theta/rho) w */
210     } else {
211       PetscCall(VecAYPX(W, -theta / rho, Z)); /* w <- z - (theta/rho) w */
212     }
213 
214     lsqr->arnorm = alpha * PetscAbsScalar(tau);
215     rnorm        = PetscRealPart(phibar);
216 
217     PetscCall(PetscObjectSAWsTakeAccess((PetscObject)ksp));
218     ksp->its++;
219     ksp->rnorm = rnorm;
220     PetscCall(PetscObjectSAWsGrantAccess((PetscObject)ksp));
221     PetscCall(KSPLogResidualHistory(ksp, rnorm));
222     PetscCall(KSPMonitor(ksp, i + 1, rnorm));
223     PetscCall((*ksp->converged)(ksp, i + 1, rnorm, &ksp->reason, ksp->cnvP));
224     if (ksp->reason) break;
225     SWAP(U1, U, TMP);
226     SWAP(V1, V, TMP);
227 
228     i++;
229   } while (i < ksp->max_it);
230   if (i >= ksp->max_it && !ksp->reason) ksp->reason = KSP_DIVERGED_ITS;
231 
232   /* Finish off the standard error estimates */
233   if (lsqr->se) {
234     tmp = 1.0;
235     PetscCall(MatGetSize(Amat, &size1, &size2));
236     if (size1 > size2) tmp = size1 - size2;
237     tmp = rnorm / PetscSqrtScalar(tmp);
238     PetscCall(VecSqrtAbs(lsqr->se));
239     PetscCall(VecScale(lsqr->se, tmp));
240   }
241   PetscFunctionReturn(PETSC_SUCCESS);
242 }
243 
KSPDestroy_LSQR(KSP ksp)244 static PetscErrorCode KSPDestroy_LSQR(KSP ksp)
245 {
246   KSP_LSQR *lsqr = (KSP_LSQR *)ksp->data;
247 
248   PetscFunctionBegin;
249   /* Free work vectors */
250   if (lsqr->vwork_n) PetscCall(VecDestroyVecs(lsqr->nwork_n, &lsqr->vwork_n));
251   if (lsqr->vwork_m) PetscCall(VecDestroyVecs(lsqr->nwork_m, &lsqr->vwork_m));
252   PetscCall(VecDestroy(&lsqr->se));
253   /* Revert convergence test */
254   PetscCall(KSPSetConvergenceTest(ksp, lsqr->converged, lsqr->cnvP, lsqr->convergeddestroy));
255   /* Free the KSP_LSQR context */
256   PetscCall(PetscFree(ksp->data));
257   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPLSQRMonitorResidual_C", NULL));
258   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPLSQRMonitorResidualDrawLG_C", NULL));
259   PetscFunctionReturn(PETSC_SUCCESS);
260 }
261 
262 /*@
263   KSPLSQRSetComputeStandardErrorVec - Compute a vector of standard error estimates during `KSPSolve()` for  `KSPLSQR`.
264 
265   Logically Collective
266 
267   Input Parameters:
268 + ksp - iterative context
269 - flg - compute the vector of standard estimates or not
270 
271   Level: intermediate
272 
273   Developer Notes:
274   Vaclav: I'm not sure whether this vector is useful for anything.
275 
276 .seealso: [](ch_ksp), `KSPSolve()`, `KSPLSQR`, `KSPLSQRGetStandardErrorVec()`
277 @*/
KSPLSQRSetComputeStandardErrorVec(KSP ksp,PetscBool flg)278 PetscErrorCode KSPLSQRSetComputeStandardErrorVec(KSP ksp, PetscBool flg)
279 {
280   KSP_LSQR *lsqr = (KSP_LSQR *)ksp->data;
281 
282   PetscFunctionBegin;
283   lsqr->se_flg = flg;
284   PetscFunctionReturn(PETSC_SUCCESS);
285 }
286 
287 /*@
288   KSPLSQRSetExactMatNorm - Compute exact matrix norm instead of iteratively refined estimate.
289 
290   Not Collective
291 
292   Input Parameters:
293 + ksp - iterative context
294 - flg - compute exact matrix norm or not
295 
296   Level: intermediate
297 
298   Notes:
299   By default, `flg` = `PETSC_FALSE`. This is usually preferred to avoid possibly expensive computation of the norm.
300   For `flg` = `PETSC_TRUE`, we call `MatNorm`(Amat,`NORM_FROBENIUS`,&lsqr->anorm) which will work only for some types of explicitly assembled matrices.
301   This can affect convergence rate as `KSPLSQRConvergedDefault()` assumes different value of $||A||$ used in normal equation stopping criterion.
302 
303 .seealso: [](ch_ksp), `KSPSolve()`, `KSPLSQR`, `KSPLSQRGetNorms()`, `KSPLSQRConvergedDefault()`
304 @*/
KSPLSQRSetExactMatNorm(KSP ksp,PetscBool flg)305 PetscErrorCode KSPLSQRSetExactMatNorm(KSP ksp, PetscBool flg)
306 {
307   KSP_LSQR *lsqr = (KSP_LSQR *)ksp->data;
308 
309   PetscFunctionBegin;
310   lsqr->exact_norm = flg;
311   PetscFunctionReturn(PETSC_SUCCESS);
312 }
313 
314 /*@
315   KSPLSQRGetStandardErrorVec - Get vector of standard error estimates.
316   Only available if -ksp_lsqr_set_standard_error was set to true
317   or `KSPLSQRSetComputeStandardErrorVec`(ksp, `PETSC_TRUE`) was called.
318   Otherwise returns `NULL`.
319 
320   Not Collective
321 
322   Input Parameter:
323 . ksp - iterative context
324 
325   Output Parameter:
326 . se - vector of standard estimates
327 
328   Level: intermediate
329 
330   Developer Notes:
331   Vaclav: I'm not sure whether this vector is useful for anything.
332 
333 .seealso: [](ch_ksp), `KSPSolve()`, `KSPLSQR`, `KSPLSQRSetComputeStandardErrorVec()`
334 @*/
KSPLSQRGetStandardErrorVec(KSP ksp,Vec * se)335 PetscErrorCode KSPLSQRGetStandardErrorVec(KSP ksp, Vec *se)
336 {
337   KSP_LSQR *lsqr = (KSP_LSQR *)ksp->data;
338 
339   PetscFunctionBegin;
340   *se = lsqr->se;
341   PetscFunctionReturn(PETSC_SUCCESS);
342 }
343 
344 /*@
345   KSPLSQRGetNorms - Get the norm estimates that `KSPLSQR` computes internally during `KSPSolve()`.
346 
347   Not Collective
348 
349   Input Parameter:
350 . ksp - iterative context
351 
352   Output Parameters:
353 + arnorm - good estimate of $\|(A*Pmat^{-T})*r\|$, where $r = A x - b$, used in specific stopping criterion
354 - anorm  - poor estimate of $\|A*Pmat^{-T}\|_{frobenius}$ used in specific stopping criterion
355 
356   Level: intermediate
357 
358   Notes:
359   Output parameters are meaningful only after `KSPSolve()`.
360 
361   These are the same quantities as `normar` and `norma` in MATLAB's `lsqr()`, whose output `lsvec` is a vector of `normar` / `norma` for all iterations.
362 
363   If `-ksp_lsqr_exact_mat_norm` is set or `KSPLSQRSetExactMatNorm`(ksp, `PETSC_TRUE`) called, then `anorm` is the exact Frobenius norm.
364 
365 .seealso: [](ch_ksp), `KSPSolve()`, `KSPLSQR`, `KSPLSQRSetExactMatNorm()`
366 @*/
KSPLSQRGetNorms(KSP ksp,PetscReal * arnorm,PetscReal * anorm)367 PetscErrorCode KSPLSQRGetNorms(KSP ksp, PetscReal *arnorm, PetscReal *anorm)
368 {
369   KSP_LSQR *lsqr = (KSP_LSQR *)ksp->data;
370 
371   PetscFunctionBegin;
372   if (arnorm) *arnorm = lsqr->arnorm;
373   if (anorm) *anorm = lsqr->anorm;
374   PetscFunctionReturn(PETSC_SUCCESS);
375 }
376 
KSPLSQRMonitorResidual_LSQR(KSP ksp,PetscInt n,PetscReal rnorm,PetscViewerAndFormat * vf)377 static PetscErrorCode KSPLSQRMonitorResidual_LSQR(KSP ksp, PetscInt n, PetscReal rnorm, PetscViewerAndFormat *vf)
378 {
379   KSP_LSQR         *lsqr   = (KSP_LSQR *)ksp->data;
380   PetscViewer       viewer = vf->viewer;
381   PetscViewerFormat format = vf->format;
382   char              normtype[256];
383   PetscInt          tablevel;
384   const char       *prefix;
385 
386   PetscFunctionBegin;
387   PetscCall(PetscObjectGetTabLevel((PetscObject)ksp, &tablevel));
388   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)ksp, &prefix));
389   PetscCall(PetscStrncpy(normtype, KSPNormTypes[ksp->normtype], sizeof(normtype)));
390   PetscCall(PetscStrtolower(normtype));
391   PetscCall(PetscViewerPushFormat(viewer, format));
392   PetscCall(PetscViewerASCIIAddTab(viewer, tablevel));
393   if (n == 0 && prefix) PetscCall(PetscViewerASCIIPrintf(viewer, "  Residual norm, norm of normal equations, and matrix norm for %s solve.\n", prefix));
394   if (!n) {
395     PetscCall(PetscViewerASCIIPrintf(viewer, "%3" PetscInt_FMT " KSP resid norm %14.12e\n", n, (double)rnorm));
396   } else {
397     PetscCall(PetscViewerASCIIPrintf(viewer, "%3" PetscInt_FMT " KSP resid norm %14.12e normal eq resid norm %14.12e matrix norm %14.12e\n", n, (double)rnorm, (double)lsqr->arnorm, (double)lsqr->anorm));
398   }
399   PetscCall(PetscViewerASCIISubtractTab(viewer, tablevel));
400   PetscCall(PetscViewerPopFormat(viewer));
401   PetscFunctionReturn(PETSC_SUCCESS);
402 }
403 
404 /*@C
405   KSPLSQRMonitorResidual - Prints the residual norm, as well as the normal equation residual norm, at each iteration of an iterative solver for the `KSPLSQR` solver
406 
407   Collective
408 
409   Input Parameters:
410 + ksp   - iterative context
411 . n     - iteration number
412 . rnorm - 2-norm (preconditioned) residual value (may be estimated).
413 - vf    - The viewer context
414 
415   Options Database Key:
416 . -ksp_lsqr_monitor - Activates `KSPLSQRMonitorResidual()`
417 
418   Level: intermediate
419 
420 .seealso: [](ch_ksp), `KSPLSQR`, `KSPMonitorSet()`, `KSPMonitorResidual()`, `KSPMonitorTrueResidualMaxNorm()`, `KSPLSQRMonitorResidualDrawLG()`
421 @*/
KSPLSQRMonitorResidual(KSP ksp,PetscInt n,PetscReal rnorm,PetscViewerAndFormat * vf)422 PetscErrorCode KSPLSQRMonitorResidual(KSP ksp, PetscInt n, PetscReal rnorm, PetscViewerAndFormat *vf)
423 {
424   PetscFunctionBegin;
425   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
426   PetscAssertPointer(vf, 4);
427   PetscValidHeaderSpecific(vf->viewer, PETSC_VIEWER_CLASSID, 4);
428   PetscTryMethod(ksp, "KSPLSQRMonitorResidual_C", (KSP, PetscInt, PetscReal, PetscViewerAndFormat *), (ksp, n, rnorm, vf));
429   PetscFunctionReturn(PETSC_SUCCESS);
430 }
431 
KSPLSQRMonitorResidualDrawLG_LSQR(KSP ksp,PetscInt n,PetscReal rnorm,PetscViewerAndFormat * vf)432 static PetscErrorCode KSPLSQRMonitorResidualDrawLG_LSQR(KSP ksp, PetscInt n, PetscReal rnorm, PetscViewerAndFormat *vf)
433 {
434   KSP_LSQR          *lsqr   = (KSP_LSQR *)ksp->data;
435   PetscViewer        viewer = vf->viewer;
436   PetscViewerFormat  format = vf->format;
437   KSPConvergedReason reason;
438   PetscReal          x[2], y[2];
439   PetscDrawLG        lg;
440 
441   PetscFunctionBegin;
442   PetscCall(PetscViewerPushFormat(viewer, format));
443   PetscCall(PetscViewerDrawGetDrawLG(viewer, 0, &lg));
444   if (!n) PetscCall(PetscDrawLGReset(lg));
445   x[0] = (PetscReal)n;
446   if (rnorm > 0.0) y[0] = PetscLog10Real(rnorm);
447   else y[0] = -15.0;
448   x[1] = (PetscReal)n;
449   if (lsqr->arnorm > 0.0) y[1] = PetscLog10Real(lsqr->arnorm);
450   else y[1] = -15.0;
451   PetscCall(PetscDrawLGAddPoint(lg, x, y));
452   PetscCall(KSPGetConvergedReason(ksp, &reason));
453   if (n <= 20 || !(n % 5) || reason) {
454     PetscCall(PetscDrawLGDraw(lg));
455     PetscCall(PetscDrawLGSave(lg));
456   }
457   PetscCall(PetscViewerPopFormat(viewer));
458   PetscFunctionReturn(PETSC_SUCCESS);
459 }
460 
461 /*@C
462   KSPLSQRMonitorResidualDrawLG - Plots the true residual norm at each iteration of an iterative solver for the `KSPLSQR` solver
463 
464   Collective
465 
466   Input Parameters:
467 + ksp   - iterative context
468 . n     - iteration number
469 . rnorm - 2-norm (preconditioned) residual value (may be estimated).
470 - vf    - The viewer context
471 
472   Options Database Key:
473 . -ksp_lsqr_monitor draw::draw_lg - Activates `KSPMonitorTrueResidualDrawLG()`
474 
475   Level: intermediate
476 
477 .seealso: [](ch_ksp), `KSPLSQR`, `KSPMonitorSet()`, `KSPMonitorTrueResidual()`, `KSPLSQRMonitorResidual()`, `KSPLSQRMonitorResidualDrawLGCreate()`
478 @*/
KSPLSQRMonitorResidualDrawLG(KSP ksp,PetscInt n,PetscReal rnorm,PetscViewerAndFormat * vf)479 PetscErrorCode KSPLSQRMonitorResidualDrawLG(KSP ksp, PetscInt n, PetscReal rnorm, PetscViewerAndFormat *vf)
480 {
481   PetscFunctionBegin;
482   PetscValidHeaderSpecific(ksp, KSP_CLASSID, 1);
483   PetscAssertPointer(vf, 4);
484   PetscValidHeaderSpecific(vf->viewer, PETSC_VIEWER_CLASSID, 4);
485   PetscTryMethod(ksp, "KSPLSQRMonitorResidualDrawLG_C", (KSP, PetscInt, PetscReal, PetscViewerAndFormat *), (ksp, n, rnorm, vf));
486   PetscFunctionReturn(PETSC_SUCCESS);
487 }
488 
489 /*@C
490   KSPLSQRMonitorResidualDrawLGCreate - Creates the line graph object for the `KSPLSQR` residual and normal equation residual norm
491 
492   Collective
493 
494   Input Parameters:
495 + viewer - The `PetscViewer`
496 . format - The viewer format
497 - ctx    - An optional user context
498 
499   Output Parameter:
500 . vf - The `PetscViewerAndFormat`
501 
502   Level: intermediate
503 
504 .seealso: [](ch_ksp), `KSPLSQR`, `KSPMonitorSet()`, `KSPLSQRMonitorResidual()`, `KSPLSQRMonitorResidualDrawLG()`
505 @*/
KSPLSQRMonitorResidualDrawLGCreate(PetscViewer viewer,PetscViewerFormat format,PetscCtx ctx,PetscViewerAndFormat ** vf)506 PetscErrorCode KSPLSQRMonitorResidualDrawLGCreate(PetscViewer viewer, PetscViewerFormat format, PetscCtx ctx, PetscViewerAndFormat **vf)
507 {
508   const char *names[] = {"residual", "normal eqn residual"};
509 
510   PetscFunctionBegin;
511   PetscCall(PetscViewerAndFormatCreate(viewer, format, vf));
512   (*vf)->data = ctx;
513   PetscCall(PetscViewerMonitorLGSetUp(viewer, NULL, NULL, "Log Residual Norm", 2, names, PETSC_DECIDE, PETSC_DECIDE, 400, 300));
514   PetscFunctionReturn(PETSC_SUCCESS);
515 }
516 
KSPSetFromOptions_LSQR(KSP ksp,PetscOptionItems PetscOptionsObject)517 static PetscErrorCode KSPSetFromOptions_LSQR(KSP ksp, PetscOptionItems PetscOptionsObject)
518 {
519   KSP_LSQR *lsqr = (KSP_LSQR *)ksp->data;
520 
521   PetscFunctionBegin;
522   PetscOptionsHeadBegin(PetscOptionsObject, "KSP LSQR Options");
523   PetscCall(PetscOptionsBool("-ksp_lsqr_compute_standard_error", "Set Standard Error Estimates of Solution", "KSPLSQRSetComputeStandardErrorVec", lsqr->se_flg, &lsqr->se_flg, NULL));
524   PetscCall(PetscOptionsBool("-ksp_lsqr_exact_mat_norm", "Compute exact matrix norm instead of iteratively refined estimate", "KSPLSQRSetExactMatNorm", lsqr->exact_norm, &lsqr->exact_norm, NULL));
525   PetscCall(KSPMonitorSetFromOptions(ksp, "-ksp_lsqr_monitor", "lsqr_residual", NULL));
526   PetscOptionsHeadEnd();
527   PetscFunctionReturn(PETSC_SUCCESS);
528 }
529 
KSPView_LSQR(KSP ksp,PetscViewer viewer)530 static PetscErrorCode KSPView_LSQR(KSP ksp, PetscViewer viewer)
531 {
532   KSP_LSQR *lsqr = (KSP_LSQR *)ksp->data;
533   PetscBool isascii;
534 
535   PetscFunctionBegin;
536   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
537   if (isascii) {
538     if (lsqr->se) {
539       PetscReal rnorm;
540       PetscCall(VecNorm(lsqr->se, NORM_2, &rnorm));
541       PetscCall(PetscViewerASCIIPrintf(viewer, "  norm of standard error %g, iterations %" PetscInt_FMT "\n", (double)rnorm, ksp->its));
542     } else {
543       PetscCall(PetscViewerASCIIPrintf(viewer, "  standard error not computed\n"));
544     }
545     if (lsqr->exact_norm) {
546       PetscCall(PetscViewerASCIIPrintf(viewer, "  using exact matrix norm\n"));
547     } else {
548       PetscCall(PetscViewerASCIIPrintf(viewer, "  using inexact matrix norm\n"));
549     }
550   }
551   PetscFunctionReturn(PETSC_SUCCESS);
552 }
553 
554 /*@C
555   KSPLSQRConvergedDefault - Determines convergence of the `KSPLSQR` Krylov method, including a check on the residual norm of the normal equations.
556 
557   Collective
558 
559   Input Parameters:
560 + ksp   - iterative context
561 . n     - iteration number
562 . rnorm - 2-norm residual value (may be estimated)
563 - ctx   - convergence context which must have been created by `KSPConvergedDefaultCreate()`
564 
565   Output Parameter:
566 . reason - the convergence reason
567 
568   Level: advanced
569 
570   Notes:
571   This is not called directly but rather is passed to `KSPSetConvergenceTest()`. It is used automatically by `KSPLSQR`
572 
573   `KSPConvergedDefault()` is called first to check for convergence in $A*x=b$.
574   If that does not determine convergence then checks convergence for the least squares problem, i.e., in $ \min_x |b - A x| $.
575   Possible convergence for the least squares problem (which is based on the residual of the normal equations) are `KSP_CONVERGED_RTOL_NORMAL_EQUATIONS`
576   and `KSP_CONVERGED_ATOL_NORMAL_EQUATIONS`.
577 
578   `KSP_CONVERGED_RTOL_NORMAL_EQUATIONS` is returned if $||A^T r|| < rtol ||A|| ||r||$.
579   The matrix norm $||A||$ is an iteratively refined estimate, see `KSPLSQRGetNorms()`.
580   This criterion is largely compatible with that in MATLAB `lsqr()`.
581 
582 .seealso: [](ch_ksp), `KSPLSQR`, `KSPSetConvergenceTest()`, `KSPSetTolerances()`, `KSPConvergedSkip()`, `KSPConvergedReason`, `KSPGetConvergedReason()`,
583           `KSPConvergedDefaultSetUIRNorm()`, `KSPConvergedDefaultSetUMIRNorm()`, `KSPConvergedDefaultCreate()`, `KSPConvergedDefaultDestroy()`,
584           `KSPConvergedDefault()`, `KSPLSQRGetNorms()`, `KSPLSQRSetExactMatNorm()`
585 @*/
KSPLSQRConvergedDefault(KSP ksp,PetscInt n,PetscReal rnorm,KSPConvergedReason * reason,PetscCtx ctx)586 PetscErrorCode KSPLSQRConvergedDefault(KSP ksp, PetscInt n, PetscReal rnorm, KSPConvergedReason *reason, PetscCtx ctx)
587 {
588   KSP_LSQR *lsqr = (KSP_LSQR *)ksp->data;
589   PetscReal xnorm;
590 
591   PetscFunctionBegin;
592   /* check for convergence in A*x=b */
593   PetscCall(KSPConvergedDefault(ksp, n, rnorm, reason, ctx));
594   if (!n || *reason) PetscFunctionReturn(PETSC_SUCCESS);
595 
596   PetscCall(VecNorm(ksp->vec_sol, NORM_2, &xnorm));
597   /* check for convergence in min{|b-A*x|} */
598   if (lsqr->arnorm < ksp->rtol * ksp->rnorm0 + ksp->abstol * lsqr->anorm * xnorm) {
599     PetscCall(PetscInfo(ksp, "LSQR solver has converged. Normal equation residual %14.12e is less than relative tolerance %14.12e times initial rhs norm %14.12e + absolute tolerance %14.12e times %s Frobenius norm of matrix %14.12e times solution %14.12e at iteration %" PetscInt_FMT "\n",
600                         (double)lsqr->arnorm, (double)ksp->rtol, (double)ksp->rnorm0, (double)ksp->abstol, lsqr->exact_norm ? "exact" : "approx.", (double)lsqr->anorm, (double)xnorm, n));
601     *reason = KSP_CONVERGED_RTOL_NORMAL_EQUATIONS;
602   } else if (lsqr->arnorm < ksp->abstol * lsqr->anorm * rnorm) {
603     PetscCall(PetscInfo(ksp, "LSQR solver has converged. Normal equation residual %14.12e is less than absolute tolerance %14.12e times %s Frobenius norm of matrix %14.12e times residual %14.12e at iteration %" PetscInt_FMT "\n", (double)lsqr->arnorm,
604                         (double)ksp->abstol, lsqr->exact_norm ? "exact" : "approx.", (double)lsqr->anorm, (double)rnorm, n));
605     *reason = KSP_CONVERGED_ATOL_NORMAL_EQUATIONS;
606   }
607   PetscFunctionReturn(PETSC_SUCCESS);
608 }
609 
610 /*MC
611    KSPLSQR - Implements LSQR  {cite}`paige.saunders:lsqr`
612 
613    Options Database Keys:
614 +  -ksp_lsqr_set_standard_error - set standard error estimates of solution, see `KSPLSQRSetComputeStandardErrorVec()` and `KSPLSQRGetStandardErrorVec()`
615 .  -ksp_lsqr_exact_mat_norm     - compute the exact matrix norm instead of using an iteratively refined estimate, see `KSPLSQRSetExactMatNorm()`
616 -  -ksp_lsqr_monitor            - monitor residual norm, norm of residual of normal equations $A^T A x = A^T b $, and estimate of matrix norm $||A||$
617 
618    Level: beginner
619 
620    Notes:
621    Supports non-square (rectangular) matrices.  See `PETSCREGRESSORLINEAR` for the PETSc toolkit for solving linear regression problems, including least squares.
622 
623    This variant, when applied with no preconditioning is identical to the original published algorithm in exact arithmetic; however, in practice, with no preconditioning
624    due to inexact arithmetic, it can converge differently. Hence when no preconditioner is used (`PCType` `PCNONE`) it automatically reverts to the original algorithm.
625 
626    With the PETSc built-in preconditioners, such as `PCICC`, one should call `KSPSetOperators`(ksp,A,A^T*A)) since the preconditioner needs to work
627    for the normal equations $^T A$. For example, use `MatCreateNormal()`.
628 
629    Supports only left preconditioning.
630 
631    For least squares problems with nonzero residual $A x - b$, there are additional convergence tests for the residual of the normal equations, $A^T (b - Ax)$, see `KSPLSQRConvergedDefault()`.
632    see `KSPLSQRConvergedDefault()`.
633 
634    In exact arithmetic the LSQR method (with no preconditioning) is identical to the `KSPCG` algorithm applied to the normal equations.
635    The preconditioned variant was implemented by Bas van't Hof and is essentially a left preconditioning for the normal equations.
636    It appears the implementation with preconditioning tracks the true (unpreconditioned) norm of the residual and uses that in the convergence test.
637 
638    Developer Note:
639    How is this related to the `KSPCGNE` implementation? One difference is that `KSPCGNE` applies
640    the preconditioner transpose times the preconditioner,  so one does not need to pass $A^T*A$ as the third argument to `KSPSetOperators()`.
641 
642 .seealso: [](ch_ksp), `KSPCreate()`, `KSPSetType()`, `KSPType`, `KSP`, `KSPSolve()`, `KSPLSQRConvergedDefault()`, `KSPLSQRSetComputeStandardErrorVec()`, `KSPLSQRGetStandardErrorVec()`, `KSPLSQRSetExactMatNorm()`, `KSPLSQRMonitorResidualDrawLGCreate()`, `KSPLSQRMonitorResidualDrawLG()`, `KSPLSQRMonitorResidual()`, `PETSCREGRESSORLINEAR`
643 M*/
KSPCreate_LSQR(KSP ksp)644 PETSC_EXTERN PetscErrorCode KSPCreate_LSQR(KSP ksp)
645 {
646   KSP_LSQR *lsqr;
647   void     *ctx;
648 
649   PetscFunctionBegin;
650   PetscCall(PetscNew(&lsqr));
651   lsqr->se         = NULL;
652   lsqr->se_flg     = PETSC_FALSE;
653   lsqr->exact_norm = PETSC_FALSE;
654   lsqr->anorm      = -1.0;
655   lsqr->arnorm     = -1.0;
656   ksp->data        = (void *)lsqr;
657   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_UNPRECONDITIONED, PC_LEFT, 3));
658 
659   ksp->ops->setup          = KSPSetUp_LSQR;
660   ksp->ops->solve          = KSPSolve_LSQR;
661   ksp->ops->destroy        = KSPDestroy_LSQR;
662   ksp->ops->setfromoptions = KSPSetFromOptions_LSQR;
663   ksp->ops->view           = KSPView_LSQR;
664 
665   /* Backup current convergence test; remove destroy routine from KSP to prevent destroying the convergence context in KSPSetConvergenceTest() */
666   PetscCall(KSPGetAndClearConvergenceTest(ksp, &lsqr->converged, &lsqr->cnvP, &lsqr->convergeddestroy));
667   /* Override current convergence test */
668   PetscCall(KSPConvergedDefaultCreate(&ctx));
669   PetscCall(KSPSetConvergenceTest(ksp, KSPLSQRConvergedDefault, ctx, KSPConvergedDefaultDestroy));
670   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPLSQRMonitorResidual_C", KSPLSQRMonitorResidual_LSQR));
671   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPLSQRMonitorResidualDrawLG_C", KSPLSQRMonitorResidualDrawLG_LSQR));
672   PetscFunctionReturn(PETSC_SUCCESS);
673 }
674