Actual source code: stcg.c

  1: #define PETSCKSP_DLL

 3:  #include include/private/kspimpl.h
 4:  #include src/ksp/ksp/impls/cg/stcg/stcg.h

  8: /*@
  9:     KSPSTCGSetRadius - Sets the radius of the trust region.

 11:     Collective on KSP

 13:     Input Parameters:
 14: +   ksp    - the iterative context
 15: -   radius - the trust region radius (Infinity is the default)

 17:     Options Database Key:
 18: .   -ksp_stcg_radius <r>

 20:     Level: advanced

 22: .keywords: KSP, STCG, set, trust region radius
 23: @*/
 24: PetscErrorCode  KSPSTCGSetRadius(KSP ksp,PetscReal radius)
 25: {
 26:   PetscErrorCode ierr, (*f)(KSP, PetscReal);

 30:   if (radius < 0.0) SETERRQ(PETSC_ERR_ARG_OUTOFRANGE, "Tolerance must be positive");
 31:   PetscObjectQueryFunction((PetscObject)ksp, "KSPSTCGSetRadius_C", (void (**)(void))&f);
 32:   if (f) {
 33:     (*f)(ksp, radius);
 34:   }
 35:   return(0);
 36: }

 40: /*@
 41:     KSPSTCGGetNormD - Got norm of the direction.

 43:     Collective on KSP

 45:     Input Parameters:
 46: +   ksp    - the iterative context
 47: -   norm_d - the norm of the direction

 49:     Level: advanced

 51: .keywords: KSP, STCG, get, norm direction
 52: @*/
 53: PetscErrorCode  KSPSTCGGetNormD(KSP ksp,PetscReal *norm_d)
 54: {
 55:   PetscErrorCode ierr, (*f)(KSP, PetscReal *);

 59:   PetscObjectQueryFunction((PetscObject)ksp, "KSPSTCGGetNormD_C", (void (**)(void))&f);
 60:   if (f) {
 61:     (*f)(ksp, norm_d);
 62:   }
 63:   return(0);
 64: }

 68: /*@
 69:     KSPSTCGGetObjFcn - Get objective function value.

 71:     Collective on KSP

 73:     Input Parameters:
 74: +   ksp   - the iterative context
 75: -   o_fcn - the objective function value

 77:     Level: advanced

 79: .keywords: KSP, STCG, get, objective function
 80: @*/
 81: PetscErrorCode  KSPSTCGGetObjFcn(KSP ksp,PetscReal *o_fcn)
 82: {
 83:   PetscErrorCode ierr, (*f)(KSP, PetscReal *);

 87:   PetscObjectQueryFunction((PetscObject)ksp, "KSPSTCGGetObjFcn_C", (void (**)(void))&f);
 88:   if (f) {
 89:     (*f)(ksp, o_fcn);
 90:   }
 91:   return(0);
 92: }

 96: /*
 97:   KSPSolve_STCG - Use preconditioned conjugate gradient to compute
 98:   an approximate minimizer of the quadratic function

100:             q(s) = g^T * s + .5 * s^T * H * s

102:    subject to the trust region constraint

104:             || s ||_M <= delta,

106:    where

108:      delta is the trust region radius,
109:      g is the gradient vector, and
110:      H is the Hessian matrix,
111:      M is the positive definite preconditioner matrix.

113:    KSPConvergedReason may be
114: $  KSP_CONVERGED_CG_NEG_CURVE if convergence is reached along a negative curvature direction,
115: $  KSP_CONVERGED_CG_CONSTRAINED if convergence is reached along a constrained step,
116: $  other KSP converged/diverged reasons


119:   Notes:
120:   The preconditioner supplied should be symmetric and positive definite.
121: */

123: PetscErrorCode KSPSolve_STCG(KSP ksp)
124: {
125: #ifdef PETSC_USE_COMPLEX
126:   SETERRQ(PETSC_ERR_SUP, "STCG is not available for complex systems");
127: #else
128:   KSP_STCG *cg = (KSP_STCG *)ksp->data;
130:   MatStructure  pflag;
131:   Mat Qmat, Mmat;
132:   Vec r, z, p, d;
133:   PC  pc;
134:   PetscReal norm_r, norm_d, norm_dp1, norm_p, dMp;
135:   PetscReal alpha, beta, kappa, rz, rzm1;
136:   PetscReal r2;
137:   PetscInt  i, maxit;
138:   PetscTruth diagonalscale;

141:   PCDiagonalScale(ksp->pc, &diagonalscale);
142:   if (diagonalscale) SETERRQ1(PETSC_ERR_SUP, "Krylov method %s does not support diagonal scaling", ksp->type_name);

144:   if (cg->radius < 0.0) {
145:     SETERRQ(PETSC_ERR_ARG_OUTOFRANGE, "Input error: radius < 0");
146:   }

148:   cg       = (KSP_STCG *)ksp->data;
149:   r2       = cg->radius * cg->radius;
150:   maxit    = ksp->max_it;
151:   r        = ksp->work[0];
152:   z        = ksp->work[1];
153:   p        = ksp->work[2];
154:   d        = ksp->vec_sol;
155:   pc       = ksp->pc;

157:   /* Initialize variables */
158:   PCGetOperators(pc, &Qmat, &Mmat, &pflag);

160:   ksp->its = 0;
161:   cg->o_fcn = 0.0;

163:   VecSet(d, 0.0);                         /* d = 0        */
164:   VecCopy(ksp->vec_rhs, r);         /* r = -grad    */
165:   KSP_PCApply(ksp, r, z);                 /* z = M_{-1} r */
166:   cg->norm_d = 0.0;

168:   /* Check for numerical problems with the preconditioner */
169:   VecDot(r, z, &rz);                 /* rz = r^T z   */
170:   if ((rz != rz) || (rz && (rz / rz != rz / rz))) {
171:     /* In this case, either the right-hand side contains infinity or not a  */
172:     /* number or the precontioner contains intinity or not a number.   We   */
173:     /* just take the gradient step.  These tests for numerical problems     */
174:     /* need to be performed only once since the preconditioner does not     */
175:     /* change.                                                              */

177:     ksp->reason = KSP_DIVERGED_NAN;
178:     VecDot(r, r, &rz);                 /* rz = r^T r   */
179:     if ((rz != rz) || (rz && (rz / rz != rz / rz))) {
180:       /* In this case, the right-hand side contains not a number or an      */
181:       /* infinite value.  The gradient step does not work; return a zero    */
182:       /* value for the step.                                                */

184:       PetscInfo1(ksp, "KSPSolve_STCG: bad right-hand side: rz=%g\n", rz);
185:     }
186:     else {
187:       /* In this case, the preconditioner contains not a number or an       */
188:       /* infinite value.                                                    */

190:       PetscInfo1(ksp, "KSPSolve_STCG: bad preconditioner: rz=%g\n", rz);

192:       if (cg->radius) {
193:         alpha = sqrt(r2 / rz);
194:         VecAXPY(d, alpha, r);         /* d = d + alpha r  */
195:         cg->norm_d = cg->radius;

197:         KSP_MatMult(ksp, Qmat, d, z); CHKERRQ(ierr)
198:         VecAYPX(z, -0.5, ksp->vec_rhs);
199:         VecDot(d, z, &cg->o_fcn);
200:         cg->o_fcn = -cg->o_fcn;
201:       }
202:     }
203:     return(0);
204:   }

206:   /* Check that the preconditioner is positive definite */
207:   if (rz < 0.0) {
208:     VecNorm(r, NORM_2, &norm_r);
209:     /* In this case, the preconditioner is indefinite, so we cannot        */
210:     /* measure the direction in the preconditioned norm.  Therefore, we    */
211:     /* must use an unpreconditioned calculation.  The direction in this    */
212:     /* case uses the right-hand side, which should be the negative         */
213:     /* gradient intersected with the trust region.                         */

215:     ksp->reason = KSP_DIVERGED_INDEFINITE_PC;
216:     PetscInfo1(ksp, "KSPSolve_STCG: indefinite preconditioner: rz=%g\n", rz);

218:     if (cg->radius) {
219:       VecDot(r, r, &rz);                 /* rz = r^T r   */
220:       alpha = sqrt(r2 / rz);
221:       VecAXPY(d, alpha, r);         /* d = d + alpha r */
222:       cg->norm_d = cg->radius;

224:       KSP_MatMult(ksp, Qmat, d, z); CHKERRQ(ierr)
225:       VecAYPX(z, -0.5, ksp->vec_rhs);
226:       VecDot(d, z, &cg->o_fcn);
227:       cg->o_fcn = -cg->o_fcn;
228:     }
229:     return(0);
230:   }

232:   /* As far as we know, the preconditioner is positive definite.  Compute   */
233:   /* the appropriate residual depending on what the user has set.           */
234:   if (ksp->normtype == KSP_NORM_PRECONDITIONED) {
235:     VecNorm(z, NORM_2, &norm_r);         /* norm_r = |z| */
236:   }
237:   else if (ksp->normtype == KSP_NORM_UNPRECONDITIONED) {
238:     VecNorm(r, NORM_2, &norm_r);         /* norm_r = |r| */
239:   }
240:   else if (ksp->normtype == KSP_NORM_NATURAL) {
241:     norm_r = sqrt(rz);                                        /* norm_r = |r|_B */
242:   }
243:   else {
244:     norm_r = 0;
245:   }

247:   /* Log the residual and call any registered moitor routines */
248:   KSPLogResidualHistory(ksp, norm_r);
249:   KSPMonitor(ksp, 0, norm_r);
250:   ksp->rnorm = norm_r;

252:   /* Test for convergence */
253:   (*ksp->converged)(ksp, 0, norm_r, &ksp->reason, ksp->cnvP);
254:   if (ksp->reason) {
255:     return(0);
256:   }

258:   /* Compute the initial vectors and variables for trust-region computations */
259:   VecCopy(z, p);                    /* p = z       */

261:   if (STCG_PRECONDITIONED_DIRECTION == cg->dtype) {
262:     dMp = 0.0;
263:     norm_p = rz;
264:     norm_d = 0.0;
265:   }
266:   else {
267:     dMp = 0.0;
268:     VecDot(p, p, &norm_p);
269:     norm_d = 0.0;
270:   }

272:   /* Compute the direction */
273:   KSP_MatMult(ksp, Qmat, p, z);         /* z = Q * p   */
274:   VecDot(p, z, &kappa);                 /* kappa = p^T z */

276:   if ((kappa != kappa) || (kappa && (kappa / kappa != kappa / kappa))) {
277:     /* In this case, the matrix Q contains an infinite value or not a number */
278:     /* We just take the gradient step.  Only needs to be checked once.       */
279:     ksp->reason = KSP_DIVERGED_NAN;
280:     PetscInfo1(ksp, "KSPSolve_STCG: bad matrix: kappa=%g\n", kappa);

282:     if (cg->radius) {
283:       VecDot(r, r, &rz);                 /* rz = r^T r   */

285:       alpha = sqrt(r2 / rz);
286:       VecAXPY(d, alpha, r);         /* d = d + alpha r */
287:       cg->norm_d = cg->radius;

289:       KSP_MatMult(ksp, Qmat, d, z); CHKERRQ(ierr)
290:       VecAYPX(z, -0.5, ksp->vec_rhs);
291:       VecDot(d, z, &cg->o_fcn);
292:       cg->o_fcn = -cg->o_fcn;
293:     }
294:     return(0);
295:   }

297:   /* Begin iterating */
298:   for (i = 0; i <= maxit; i++) {
299:     ++ksp->its;

301:     /* Check for negative curvature */
302:     if (kappa <= 0.0) {
303:       /* In this case, the matrix is indefinite and we have encountered     */
304:       /* a direction of negative curvature.  Follow the direction to the    */
305:       /* boundary of the trust region.                                      */

307:       ksp->reason = KSP_CONVERGED_CG_NEG_CURVE;
308:       PetscInfo1(ksp, "KSPSolve_STCG: negative curvature: kappa=%g\n", kappa);

310:       if (cg->radius) {
311:         alpha = (sqrt(dMp*dMp+norm_p*(r2-norm_d))-dMp)/norm_p;
312:         VecAXPY(d, alpha, p);         /* d = d + alpha p */
313:         cg->norm_d = cg->radius;

315:         cg->o_fcn += alpha * (0.5 * alpha * kappa - rz);
316:       }
317:       break;
318:     }
319:     alpha = rz / kappa;

321:     /* Now we can update the direction and residual.  This is perhaps not   */
322:     /* the best order to perform the breakdown checks, but the optimization */
323:     /* codes need a direction and this is the best we can do.               */

325:     /* First test if the new direction intersects the trust region. */
326:     norm_dp1 = norm_d + alpha*(2.0*dMp + alpha*norm_p);
327:     if (cg->radius && norm_dp1 >= r2) {
328:       /* In this case, the matrix is  positive definite as far as we know.  */
329:       /* However, the direction does beyond the trust region.  Follow the   */
330:       /* direction to the boundary of the trust region.                     */

332:       ksp->reason = KSP_CONVERGED_CG_CONSTRAINED;
333:       PetscInfo1(ksp, "KSPSolve_STCG: constrained step: radius=%g\n", cg->radius);

335:       alpha = (sqrt(dMp*dMp+norm_p*(r2-norm_d))-dMp)/norm_p;
336:       VecAXPY(d, alpha, p);         /* d = d + alpha p */
337:       cg->norm_d = cg->radius;

339:       cg->o_fcn += alpha * (0.5 * alpha * kappa - rz);
340:       break;
341:     }

343:     /* Update the direction and residual */
344:     VecAXPY(d, alpha, p);                 /* d = d + alpha p */
345:     VecAXPY(r, -alpha, z);                        /* r = r - alpha z */
346:     KSP_PCApply(ksp, r, z);

348:     if (STCG_PRECONDITIONED_DIRECTION == cg->dtype) {
349:       norm_d = norm_dp1;
350:     }
351:     else {
352:       VecDot(d, d, &norm_d);
353:     }
354:     cg->norm_d = sqrt(norm_d);

356:     cg->o_fcn -= 0.5 * alpha * rz;

358:     /* Check that the preconditioner is positive semidefinite */
359:     rzm1 = rz;
360:     VecDot(r, z, &rz);                 /* rz = r^T z   */
361:     if (rz <= 0.0) {
362:       VecNorm(r, NORM_2, &norm_r);
363:       if (rz < 0.0 || norm_r > 0.0) {
364:         /* In this case, the preconditioner is indefinite.  We could follow  */
365:         /* the direction to the boundary of the trust region, but it seems   */
366:         /* best to stop at the current point.                                */

368:         ksp->reason = KSP_DIVERGED_INDEFINITE_PC;
369:         PetscInfo1(ksp, "KSPSolve_STCG: indefinite preconditioner: rz=%g\n", rz);
370:         break;
371:       }
372:       else {
373:         /* rz == 0 and norm_r == 0; converged */
374:       }
375:     }

377:     /* As far as we know, the matrix and preconditioner are positive        */
378:     /* definite.  Compute the appropriate residual depending on what the    */
379:     /* user has set.                                                        */
380:     if (ksp->normtype == KSP_NORM_PRECONDITIONED) {
381:       VecNorm(z, NORM_2, &norm_r); /* norm_r = |z| */
382:     }
383:     else if (ksp->normtype == KSP_NORM_UNPRECONDITIONED) {
384:       VecNorm(r, NORM_2, &norm_r); /* norm_r = |r| */
385:     }
386:     else if (ksp->normtype == KSP_NORM_NATURAL) {
387:       norm_r = sqrt(rz);                                /* norm_r = |r|_B */
388:     }
389:     else {
390:       norm_r = 0;
391:     }

393:     /* Log the residual and call any registered moitor routines */
394:     KSPLogResidualHistory(ksp, norm_r);
395:     KSPMonitor(ksp, 0, norm_r);
396:     ksp->rnorm = norm_r;
397: 
398:     /* Test for convergence */
399:     (*ksp->converged)(ksp, i+1, norm_r, &ksp->reason, ksp->cnvP);
400:     if (ksp->reason) {                 /* convergence */
401:       PetscInfo2(ksp,"KSPSolve_STCG: truncated step: rnorm=%g, radius=%g\n",norm_r,cg->radius);
402:       break;
403:     }

405:     /* Update p and the norms */
406:     beta = rz / rzm1;
407:     VecAYPX(p, beta, z);                    /* p = z + beta p */

409:     if (STCG_PRECONDITIONED_DIRECTION == cg->dtype) {
410:       dMp = beta*(dMp + alpha*norm_p);
411:       norm_p = beta*(rzm1 + beta*norm_p);
412:     }
413:     else {
414:       VecDot(d, p, &dMp);
415:       VecDot(p, p, &norm_p);
416:     }

418:     /* Compute new direction */
419:     KSP_MatMult(ksp, Qmat, p, z);  /* z = Q * p   */
420:     VecDot(p, z, &kappa);                 /* kappa = p^T z */
421:   }

423:   if (!ksp->reason) {
424:     ksp->reason = KSP_DIVERGED_ITS;
425:   }
426:   return(0);
427: #endif
428: }

432: PetscErrorCode KSPSetUp_STCG(KSP ksp)
433: {

437:   /* This implementation of CG only handles left preconditioning
438:    * so generate an error otherwise.
439:    */
440:   if (ksp->pc_side == PC_RIGHT) {
441:     SETERRQ(PETSC_ERR_SUP, "No right preconditioning for KSPSTCG");
442:   }
443:   else if (ksp->pc_side == PC_SYMMETRIC) {
444:     SETERRQ(PETSC_ERR_SUP, "No symmetric preconditioning for KSPSTCG");
445:   }

447:   /* get work vectors needed by CG */
448:   KSPDefaultGetWork(ksp, 3);
449:   return(0);
450: }

454: PetscErrorCode KSPDestroy_STCG(KSP ksp)
455: {

459:   KSPDefaultDestroy(ksp);
460:   /* clear composed functions */
461:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPSTCGSetRadius_C","",PETSC_NULL);
462:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPSTCGGetNormD_C","",PETSC_NULL);
463:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPSTCGGetObjFcn_C","",PETSC_NULL);
464:   return(0);
465: }

470: PetscErrorCode  KSPSTCGSetRadius_STCG(KSP ksp,PetscReal radius)
471: {
472:   KSP_STCG *cg = (KSP_STCG *)ksp->data;

475:   cg->radius = radius;
476:   return(0);
477: }

481: PetscErrorCode  KSPSTCGGetNormD_STCG(KSP ksp,PetscReal *norm_d)
482: {
483:   KSP_STCG *cg = (KSP_STCG *)ksp->data;

486:   *norm_d = cg->norm_d;
487:   return(0);
488: }

492: PetscErrorCode  KSPSTCGGetObjFcn_STCG(KSP ksp,PetscReal *o_fcn){
493:   KSP_STCG *cg = (KSP_STCG *)ksp->data;

496:   *o_fcn = cg->o_fcn;
497:   return(0);
498: }

501: static const char *DType_Table[64] = {
502:   "preconditioned", "unpreconditioned"
503: };

507: PetscErrorCode KSPSetFromOptions_STCG(KSP ksp)
508: {
510:   KSP_STCG *cg = (KSP_STCG *)ksp->data;

513:   PetscOptionsHead("KSP STCG options");
514:   PetscOptionsReal("-ksp_stcg_radius", "Trust Region Radius", "KSPSTCGSetRadius", cg->radius, &cg->radius, PETSC_NULL);
515:   PetscOptionsEList("-ksp_stcg_dtype", "Norm used for direction", "", DType_Table, STCG_DIRECTION_TYPES, DType_Table[cg->dtype], &cg->dtype, PETSC_NULL);
516:   PetscOptionsTail();
517:   return(0);
518: }

520: /*MC
521:      KSPSTCG -   Code to run conjugate gradient method subject to a constraint
522:          on the solution norm. This is used in Trust Region methods for
523:          nonlinear equations, SNESTR

525:    Options Database Keys:
526: .      -ksp_stcg_radius <r> - Trust Region Radius

528:    Notes: This is rarely used directly

530:    Level: developer

532: .seealso:  KSPCreate(), KSPSetType(), KSPType (for list of available types), KSP, KSPSTCGSetRadius(), KSPSTCGGetNormD(), KSPSTCGGetObjFcn()
533: M*/

538: PetscErrorCode  KSPCreate_STCG(KSP ksp)
539: {
541:   KSP_STCG        *cg;

544:   PetscNew(KSP_STCG, &cg);
545:   PetscLogObjectMemory(ksp, sizeof(KSP_STCG));
546:   cg->radius                     = PETSC_MAX;
547:   cg->dtype                         = STCG_UNPRECONDITIONED_DIRECTION;
548:   ksp->data                      = (void *)cg;
549:   ksp->pc_side                   = PC_LEFT;

551:   /* Sets the functions that are associated with this data structure
552:    * (in C++ this is the same as defining virtual functions)
553:    */
554:   ksp->ops->setup                = KSPSetUp_STCG;
555:   ksp->ops->solve                = KSPSolve_STCG;
556:   ksp->ops->destroy              = KSPDestroy_STCG;
557:   ksp->ops->setfromoptions       = KSPSetFromOptions_STCG;
558:   ksp->ops->buildsolution        = KSPDefaultBuildSolution;
559:   ksp->ops->buildresidual        = KSPDefaultBuildResidual;
560:   ksp->ops->view                 = 0;

562:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,
563:                                     "KSPSTCGSetRadius_C",
564:                                     "KSPSTCGSetRadius_STCG",
565:                                      KSPSTCGSetRadius_STCG);
566:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,
567:                                     "KSPSTCGGetNormD_C",
568:                                     "KSPSTCGGetNormD_STCG",
569:                                      KSPSTCGGetNormD_STCG);
570:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,
571:                                     "KSPSTCGGetObjFcn_C",
572:                                     "KSPSTCGGetObjFcn_STCG",
573:                                      KSPSTCGGetObjFcn_STCG);
574:   return(0);
575: }