Actual source code: gcr.c

  1: #include <petsc/private/kspimpl.h>

  3: typedef struct {
  4:   PetscInt     restart;
  5:   PetscInt     n_restarts;
  6:   PetscScalar *val;
  7:   Vec         *VV, *SS;
  8:   Vec          R;

 10:   KSPFlexibleModifyPCFn *modifypc;         /* function to modify the preconditioner*/
 11:   PetscCtxDestroyFn     *modifypc_destroy; /* function to destroy the user context for the modifypc function */

 13:   void *modifypc_ctx; /* user defined data for the modifypc function */
 14: } KSP_GCR;

 16: static PetscErrorCode KSPSolve_GCR_cycle(KSP ksp)
 17: {
 18:   KSP_GCR    *ctx = (KSP_GCR *)ksp->data;
 19:   PetscScalar r_dot_v;
 20:   Mat         A, B;
 21:   PC          pc;
 22:   Vec         s, v, r;
 23:   /*
 24:      The residual norm will not be computed when ksp->its > ksp->chknorm hence need to initialize norm_r with some dummy value
 25:   */
 26:   PetscReal norm_r = 0.0, nrm;
 27:   PetscInt  k, i, restart;
 28:   Vec       x;

 30:   PetscFunctionBegin;
 31:   restart = ctx->restart;
 32:   PetscCall(KSPGetPC(ksp, &pc));
 33:   PetscCall(KSPGetOperators(ksp, &A, &B));

 35:   x = ksp->vec_sol;
 36:   r = ctx->R;

 38:   for (k = 0; k < restart; k++) {
 39:     v = ctx->VV[k];
 40:     s = ctx->SS[k];
 41:     if (ctx->modifypc) PetscCall((*ctx->modifypc)(ksp, ksp->its, k, ksp->rnorm, ctx->modifypc_ctx));

 43:     PetscCall(KSP_PCApply(ksp, r, s));    /* s = B^{-1} r */
 44:     PetscCall(KSP_MatMult(ksp, A, s, v)); /* v = A s */

 46:     PetscCall(VecMDot(v, k, ctx->VV, ctx->val));
 47:     for (i = 0; i < k; i++) ctx->val[i] = -ctx->val[i];
 48:     PetscCall(VecMAXPY(v, k, ctx->val, ctx->VV)); /* v = v - sum_{i=0}^{k-1} alpha_i v_i */
 49:     PetscCall(VecMAXPY(s, k, ctx->val, ctx->SS)); /* s = s - sum_{i=0}^{k-1} alpha_i s_i */

 51:     PetscCall(VecDotNorm2(r, v, &r_dot_v, &nrm));
 52:     nrm     = PetscSqrtReal(nrm);
 53:     r_dot_v = r_dot_v / nrm;
 54:     PetscCall(VecScale(v, 1.0 / nrm));
 55:     PetscCall(VecScale(s, 1.0 / nrm));
 56:     PetscCall(VecAXPY(x, r_dot_v, s));
 57:     PetscCall(VecAXPY(r, -r_dot_v, v));
 58:     if (ksp->its > ksp->chknorm && ksp->normtype != KSP_NORM_NONE) {
 59:       PetscCall(VecNorm(r, NORM_2, &norm_r));
 60:       KSPCheckNorm(ksp, norm_r);
 61:     }
 62:     /* update the local counter and the global counter */
 63:     ksp->its++;
 64:     ksp->rnorm = norm_r;

 66:     PetscCall(KSPLogResidualHistory(ksp, norm_r));
 67:     PetscCall(KSPMonitor(ksp, ksp->its, norm_r));

 69:     if (ksp->its - 1 > ksp->chknorm) {
 70:       PetscCall((*ksp->converged)(ksp, ksp->its, norm_r, &ksp->reason, ksp->cnvP));
 71:       if (ksp->reason) break;
 72:     }

 74:     if (ksp->its >= ksp->max_it) {
 75:       ksp->reason = KSP_CONVERGED_ITS;
 76:       break;
 77:     }
 78:   }
 79:   ctx->n_restarts++;
 80:   PetscFunctionReturn(PETSC_SUCCESS);
 81: }

 83: static PetscErrorCode KSPSolve_GCR(KSP ksp)
 84: {
 85:   KSP_GCR  *ctx = (KSP_GCR *)ksp->data;
 86:   Mat       A, B;
 87:   Vec       r, b, x;
 88:   PetscReal norm_r = 0.0;

 90:   PetscFunctionBegin;
 91:   PetscCall(KSPGetOperators(ksp, &A, &B));
 92:   x = ksp->vec_sol;
 93:   b = ksp->vec_rhs;
 94:   r = ctx->R;

 96:   /* compute initial residual */
 97:   PetscCall(KSP_MatMult(ksp, A, x, r));
 98:   PetscCall(VecAYPX(r, -1.0, b)); /* r = b - A x  */
 99:   if (ksp->normtype != KSP_NORM_NONE) {
100:     PetscCall(VecNorm(r, NORM_2, &norm_r));
101:     KSPCheckNorm(ksp, norm_r);
102:   }
103:   ksp->its    = 0;
104:   ksp->rnorm0 = norm_r;

106:   PetscCall(KSPLogResidualHistory(ksp, ksp->rnorm0));
107:   PetscCall(KSPMonitor(ksp, ksp->its, ksp->rnorm0));
108:   PetscCall((*ksp->converged)(ksp, ksp->its, ksp->rnorm0, &ksp->reason, ksp->cnvP));
109:   if (ksp->reason) PetscFunctionReturn(PETSC_SUCCESS);

111:   do {
112:     PetscCall(KSPSolve_GCR_cycle(ksp));
113:     if (ksp->reason) PetscFunctionReturn(PETSC_SUCCESS); /* catch case when convergence occurs inside the cycle */
114:   } while (ksp->its < ksp->max_it);

116:   if (ksp->its >= ksp->max_it) ksp->reason = KSP_DIVERGED_ITS;
117:   PetscFunctionReturn(PETSC_SUCCESS);
118: }

120: static PetscErrorCode KSPView_GCR(KSP ksp, PetscViewer viewer)
121: {
122:   KSP_GCR  *ctx = (KSP_GCR *)ksp->data;
123:   PetscBool isascii;

125:   PetscFunctionBegin;
126:   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &isascii));
127:   if (isascii) {
128:     PetscCall(PetscViewerASCIIPrintf(viewer, "  restart = %" PetscInt_FMT " \n", ctx->restart));
129:     PetscCall(PetscViewerASCIIPrintf(viewer, "  restarts performed = %" PetscInt_FMT " \n", ctx->n_restarts));
130:   }
131:   PetscFunctionReturn(PETSC_SUCCESS);
132: }

134: static PetscErrorCode KSPSetUp_GCR(KSP ksp)
135: {
136:   KSP_GCR *ctx = (KSP_GCR *)ksp->data;
137:   Mat      A;

139:   PetscFunctionBegin;
140:   PetscCall(KSPGetOperators(ksp, &A, NULL));
141:   PetscCall(MatCreateVecs(A, &ctx->R, NULL));
142:   PetscCall(VecDuplicateVecs(ctx->R, ctx->restart, &ctx->VV));
143:   PetscCall(VecDuplicateVecs(ctx->R, ctx->restart, &ctx->SS));

145:   PetscCall(PetscMalloc1(ctx->restart, &ctx->val));
146:   PetscFunctionReturn(PETSC_SUCCESS);
147: }

149: static PetscErrorCode KSPReset_GCR(KSP ksp)
150: {
151:   KSP_GCR *ctx = (KSP_GCR *)ksp->data;

153:   PetscFunctionBegin;
154:   PetscCall(VecDestroy(&ctx->R));
155:   PetscCall(VecDestroyVecs(ctx->restart, &ctx->VV));
156:   PetscCall(VecDestroyVecs(ctx->restart, &ctx->SS));
157:   if (ctx->modifypc_destroy) PetscCall((*ctx->modifypc_destroy)(&ctx->modifypc_ctx));
158:   PetscCall(PetscFree(ctx->val));
159:   PetscFunctionReturn(PETSC_SUCCESS);
160: }

162: static PetscErrorCode KSPDestroy_GCR(KSP ksp)
163: {
164:   PetscFunctionBegin;
165:   PetscCall(KSPReset_GCR(ksp));
166:   PetscCall(KSPDestroyDefault(ksp));
167:   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGCRSetRestart_C", NULL));
168:   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGCRGetRestart_C", NULL));
169:   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPFlexibleSetModifyPC_C", NULL));
170:   PetscFunctionReturn(PETSC_SUCCESS);
171: }

173: static PetscErrorCode KSPSetFromOptions_GCR(KSP ksp, PetscOptionItems PetscOptionsObject)
174: {
175:   KSP_GCR  *ctx = (KSP_GCR *)ksp->data;
176:   PetscInt  restart;
177:   PetscBool flg;

179:   PetscFunctionBegin;
180:   PetscOptionsHeadBegin(PetscOptionsObject, "KSP GCR options");
181:   PetscCall(PetscOptionsInt("-ksp_gcr_restart", "Number of Krylov search directions", "KSPGCRSetRestart", ctx->restart, &restart, &flg));
182:   if (flg) PetscCall(KSPGCRSetRestart(ksp, restart));
183:   PetscOptionsHeadEnd();
184:   PetscFunctionReturn(PETSC_SUCCESS);
185: }

187: static PetscErrorCode KSPFlexibleSetModifyPC_GCR(KSP ksp, KSPFlexibleModifyPCFn *function, PetscCtx ctx, PetscCtxDestroyFn *destroy)
188: {
189:   KSP_GCR *gcr = (KSP_GCR *)ksp->data;

191:   PetscFunctionBegin;
193:   gcr->modifypc         = function;
194:   gcr->modifypc_destroy = destroy;
195:   gcr->modifypc_ctx     = ctx;
196:   PetscFunctionReturn(PETSC_SUCCESS);
197: }

199: static PetscErrorCode KSPGCRSetRestart_GCR(KSP ksp, PetscInt restart)
200: {
201:   KSP_GCR *ctx;

203:   PetscFunctionBegin;
204:   ctx          = (KSP_GCR *)ksp->data;
205:   ctx->restart = restart;
206:   PetscFunctionReturn(PETSC_SUCCESS);
207: }

209: static PetscErrorCode KSPGCRGetRestart_GCR(KSP ksp, PetscInt *restart)
210: {
211:   KSP_GCR *ctx;

213:   PetscFunctionBegin;
214:   ctx      = (KSP_GCR *)ksp->data;
215:   *restart = ctx->restart;
216:   PetscFunctionReturn(PETSC_SUCCESS);
217: }

219: /*@
220:   KSPGCRSetRestart - Sets number of iterations at which `KSPGCR` restarts.

222:   Not Collective

224:   Input Parameters:
225: + ksp     - the Krylov space context
226: - restart - integer restart value

228:   Options Database Key:
229: . -ksp_gcr_restart restart - the number of stored vectors to orthogonalize against

231:   Level: intermediate

233:   Note:
234:   The default value is 30.

236:   Developer Note:
237:   The API could be made uniform for all `KSP` methods that have a restart.

239: .seealso: [](ch_ksp), `KSPGCR`, `KSPSetTolerances()`, `KSPGCRGetRestart()`, `KSPGMRESSetRestart()`
240: @*/
241: PetscErrorCode KSPGCRSetRestart(KSP ksp, PetscInt restart)
242: {
243:   PetscFunctionBegin;
244:   PetscTryMethod(ksp, "KSPGCRSetRestart_C", (KSP, PetscInt), (ksp, restart));
245:   PetscFunctionReturn(PETSC_SUCCESS);
246: }

248: /*@
249:   KSPGCRGetRestart - Gets number of iterations at which `KSPGCR` restarts.

251:   Not Collective

253:   Input Parameter:
254: . ksp - the Krylov space context

256:   Output Parameter:
257: . restart - integer restart value

259:   Level: intermediate

261: .seealso: [](ch_ksp), `KSPGCR`, `KSPSetTolerances()`, `KSPGCRSetRestart()`, `KSPGMRESGetRestart()`
262: @*/
263: PetscErrorCode KSPGCRGetRestart(KSP ksp, PetscInt *restart)
264: {
265:   PetscFunctionBegin;
266:   PetscTryMethod(ksp, "KSPGCRGetRestart_C", (KSP, PetscInt *), (ksp, restart));
267:   PetscFunctionReturn(PETSC_SUCCESS);
268: }

270: static PetscErrorCode KSPBuildSolution_GCR(KSP ksp, Vec v, Vec *V)
271: {
272:   Vec x;

274:   PetscFunctionBegin;
275:   x = ksp->vec_sol;
276:   if (v) {
277:     PetscCall(VecCopy(x, v));
278:     if (V) *V = v;
279:   } else if (V) {
280:     *V = ksp->vec_sol;
281:   }
282:   PetscFunctionReturn(PETSC_SUCCESS);
283: }

285: static PetscErrorCode KSPBuildResidual_GCR(KSP ksp, Vec t, Vec v, Vec *V)
286: {
287:   KSP_GCR *ctx;

289:   PetscFunctionBegin;
290:   ctx = (KSP_GCR *)ksp->data;
291:   if (v) {
292:     PetscCall(VecCopy(ctx->R, v));
293:     if (V) *V = v;
294:   } else if (V) {
295:     *V = ctx->R;
296:   }
297:   PetscFunctionReturn(PETSC_SUCCESS);
298: }

300: /*MC
301:    KSPGCR - Implements the preconditioned flexible Generalized Conjugate Residual (GCR) method {cite}`eisenstat1983variational`. [](sec_flexibleksp)

303:    Options Database Key:
304: .   -ksp_gcr_restart restart - the number of stored vectors to orthogonalize against

306:    Level: beginner

308:     Notes:
309:     This method supports non-symmetric matrices and permits the use of a preconditioner
310:     which may vary from one iteration to the next.

312:     Users can define a method to vary the preconditioner between iterates via `KSPFlexibleSetModifyPC()`.

314:     When a restart occurs, the initial starting solution is given by the current estimate for `x`.

316:     Unlike `KSPGMRES` and `KSPFGMRES`, when using GCR, the solution and residual vector can be directly accessed at any iterate,
317:     with zero computational cost, via a call to `KSPBuildSolution()` and `KSPBuildResidual()` respectively.

319:     The stopping condition test is only applied after the iteration count exceeds the value set by
320:     `KSPSetCheckNormIteration()` (also available as `-ksp_check_norm_iteration`). The residual norm
321:     reported by the monitor and stored in the residual history will be listed as 0.0 before that
322:     iteration; the norm is not actually zero, it is simply not computed until then.

324:     The method implemented requires the storage of 2 x restart + 1 vectors, twice as much as `KSPGMRES`.

326:     Supports only right preconditioning.

328:     Contributed by:
329:     Dave May

331: .seealso: [](ch_ksp), [](sec_flexibleksp), `KSPFCG`, `KSPPIPEGCR`, `KSPPIPEFCG`, `KSPFGMRES`, `KSPCG`, `KSPCreate()`, `KSPSetType()`, `KSPType`,
332:           `KSP`, `KSPGCRSetRestart()`, `KSPGCRGetRestart()`, `KSPFlexibleSetModifyPC()`, `KSPGMRES`
333: M*/
334: PETSC_EXTERN PetscErrorCode KSPCreate_GCR(KSP ksp)
335: {
336:   KSP_GCR *ctx;

338:   PetscFunctionBegin;
339:   PetscCall(PetscNew(&ctx));

341:   ctx->restart    = 30;
342:   ctx->n_restarts = 0;
343:   ksp->data       = (void *)ctx;

345:   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_NONE, PC_RIGHT, 1));
346:   PetscCall(KSPSetSupportedNorm(ksp, KSP_NORM_UNPRECONDITIONED, PC_RIGHT, 3));

348:   ksp->ops->setup          = KSPSetUp_GCR;
349:   ksp->ops->solve          = KSPSolve_GCR;
350:   ksp->ops->reset          = KSPReset_GCR;
351:   ksp->ops->destroy        = KSPDestroy_GCR;
352:   ksp->ops->view           = KSPView_GCR;
353:   ksp->ops->setfromoptions = KSPSetFromOptions_GCR;
354:   ksp->ops->buildsolution  = KSPBuildSolution_GCR;
355:   ksp->ops->buildresidual  = KSPBuildResidual_GCR;

357:   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGCRSetRestart_C", KSPGCRSetRestart_GCR));
358:   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPGCRGetRestart_C", KSPGCRGetRestart_GCR));
359:   PetscCall(PetscObjectComposeFunction((PetscObject)ksp, "KSPFlexibleSetModifyPC_C", KSPFlexibleSetModifyPC_GCR));
360:   PetscFunctionReturn(PETSC_SUCCESS);
361: }