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