Actual source code: qcg.c

petsc-3.7.7 2017-09-25
Report Typos and Errors
  2: #include <petsc/private/kspimpl.h>             /*I "petscksp.h" I*/
  3: #include <../src/ksp/ksp/impls/qcg/qcgimpl.h>

  5: static PetscErrorCode KSPQCGQuadraticRoots(Vec,Vec,PetscReal,PetscReal*,PetscReal*);

  9: /*@
 10:     KSPQCGSetTrustRegionRadius - Sets the radius of the trust region.

 12:     Logically Collective on KSP

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

 18:     Options Database Key:
 19: .   -ksp_qcg_trustregionradius <delta>

 21:     Level: advanced

 23: .keywords: KSP, QCG, set, trust region radius
 24: @*/
 25: PetscErrorCode  KSPQCGSetTrustRegionRadius(KSP ksp,PetscReal delta)
 26: {

 31:   if (delta < 0.0) SETERRQ(PetscObjectComm((PetscObject)ksp),PETSC_ERR_ARG_OUTOFRANGE,"Tolerance must be non-negative");
 32:   PetscTryMethod(ksp,"KSPQCGSetTrustRegionRadius_C",(KSP,PetscReal),(ksp,delta));
 33:   return(0);
 34: }

 38: /*@
 39:     KSPQCGGetTrialStepNorm - Gets the norm of a trial step vector.  The WCG step may be
 40:     constrained, so this is not necessarily the length of the ultimate step taken in QCG.

 42:     Not Collective

 44:     Input Parameter:
 45: .   ksp - the iterative context

 47:     Output Parameter:
 48: .   tsnorm - the norm

 50:     Level: advanced
 51: @*/
 52: PetscErrorCode  KSPQCGGetTrialStepNorm(KSP ksp,PetscReal *tsnorm)
 53: {

 58:   PetscUseMethod(ksp,"KSPQCGGetTrialStepNorm_C",(KSP,PetscReal*),(ksp,tsnorm));
 59:   return(0);
 60: }

 64: /*@
 65:     KSPQCGGetQuadratic - Gets the value of the quadratic function, evaluated at the new iterate:

 67:        q(s) = g^T * s + 0.5 * s^T * H * s

 69:     which satisfies the Euclidian Norm trust region constraint

 71:        || D * s || <= delta,

 73:     where

 75:      delta is the trust region radius,
 76:      g is the gradient vector, and
 77:      H is Hessian matrix,
 78:      D is a scaling matrix.

 80:     Collective on KSP

 82:     Input Parameter:
 83: .   ksp - the iterative context

 85:     Output Parameter:
 86: .   quadratic - the quadratic function evaluated at the new iterate

 88:     Level: advanced
 89: @*/
 90: PetscErrorCode  KSPQCGGetQuadratic(KSP ksp,PetscReal *quadratic)
 91: {

 96:   PetscUseMethod(ksp,"KSPQCGGetQuadratic_C",(KSP,PetscReal*),(ksp,quadratic));
 97:   return(0);
 98: }


103: PetscErrorCode KSPSolve_QCG(KSP ksp)
104: {
105: /*
106:    Correpondence with documentation above:
107:       B = g = gradient,
108:       X = s = step
109:    Note:  This is not coded correctly for complex arithmetic!
110:  */

112:   KSP_QCG        *pcgP = (KSP_QCG*)ksp->data;
113:   Mat            Amat,Pmat;
114:   Vec            W,WA,WA2,R,P,ASP,BS,X,B;
115:   PetscScalar    scal,beta,rntrn,step;
116:   PetscReal      q1,q2,xnorm,step1,step2,rnrm,btx,xtax;
117:   PetscReal      ptasp,rtr,wtasp,bstp;
118:   PetscReal      dzero = 0.0,bsnrm;
120:   PetscInt       i,maxit;
121:   PC             pc = ksp->pc;
122:   PCSide         side;
123:   PetscBool      diagonalscale;

126:   PCGetDiagonalScale(ksp->pc,&diagonalscale);
127:   if (diagonalscale) SETERRQ1(PetscObjectComm((PetscObject)ksp),PETSC_ERR_SUP,"Krylov method %s does not support diagonal scaling",((PetscObject)ksp)->type_name);
128:   if (ksp->transpose_solve) SETERRQ(PetscObjectComm((PetscObject)ksp),PETSC_ERR_SUP,"Currently does not support transpose solve");

130:   ksp->its = 0;
131:   maxit    = ksp->max_it;
132:   WA       = ksp->work[0];
133:   R        = ksp->work[1];
134:   P        = ksp->work[2];
135:   ASP      = ksp->work[3];
136:   BS       = ksp->work[4];
137:   W        = ksp->work[5];
138:   WA2      = ksp->work[6];
139:   X        = ksp->vec_sol;
140:   B        = ksp->vec_rhs;

142:   if (pcgP->delta <= dzero) SETERRQ(PetscObjectComm((PetscObject)ksp),PETSC_ERR_ARG_OUTOFRANGE,"Input error: delta <= 0");
143:   KSPGetPCSide(ksp,&side);
144:   if (side != PC_SYMMETRIC) SETERRQ(PetscObjectComm((PetscObject)ksp),PETSC_ERR_ARG_OUTOFRANGE,"Requires symmetric preconditioner!");

146:   /* Initialize variables */
147:   VecSet(W,0.0);  /* W = 0 */
148:   VecSet(X,0.0);  /* X = 0 */
149:   PCGetOperators(pc,&Amat,&Pmat);

151:   /* Compute:  BS = D^{-1} B */
152:   PCApplySymmetricLeft(pc,B,BS);

154:   VecNorm(BS,NORM_2,&bsnrm);
155:   PetscObjectSAWsTakeAccess((PetscObject)ksp);
156:   ksp->its   = 0;
157:   ksp->rnorm = bsnrm;
158:   PetscObjectSAWsGrantAccess((PetscObject)ksp);
159:   KSPLogResidualHistory(ksp,bsnrm);
160:   KSPMonitor(ksp,0,bsnrm);
161:   (*ksp->converged)(ksp,0,bsnrm,&ksp->reason,ksp->cnvP);
162:   if (ksp->reason) return(0);

164:   /* Compute the initial scaled direction and scaled residual */
165:   VecCopy(BS,R);
166:   VecScale(R,-1.0);
167:   VecCopy(R,P);
168:   VecDotRealPart(R,R,&rtr);

170:   for (i=0; i<=maxit; i++) {
171:     PetscObjectSAWsTakeAccess((PetscObject)ksp);
172:     ksp->its++;
173:     PetscObjectSAWsGrantAccess((PetscObject)ksp);

175:     /* Compute:  asp = D^{-T}*A*D^{-1}*p  */
176:     PCApplySymmetricRight(pc,P,WA);
177:     KSP_MatMult(ksp,Amat,WA,WA2);
178:     PCApplySymmetricLeft(pc,WA2,ASP);

180:     /* Check for negative curvature */
181:     VecDotRealPart(P,ASP,&ptasp);
182:     if (ptasp <= dzero) {

184:       /* Scaled negative curvature direction:  Compute a step so that
185:         ||w + step*p|| = delta and QS(w + step*p) is least */

187:       if (!i) {
188:         VecCopy(P,X);
189:         VecNorm(X,NORM_2,&xnorm);
190:         scal = pcgP->delta / xnorm;
191:         VecScale(X,scal);
192:       } else {
193:         /* Compute roots of quadratic */
194:         KSPQCGQuadraticRoots(W,P,pcgP->delta,&step1,&step2);
195:         VecDotRealPart(W,ASP,&wtasp);
196:         VecDotRealPart(BS,P,&bstp);
197:         VecCopy(W,X);
198:         q1   = step1*(bstp + wtasp + .5*step1*ptasp);
199:         q2   = step2*(bstp + wtasp + .5*step2*ptasp);
200:         if (q1 <= q2) {
201:           VecAXPY(X,step1,P);
202:         } else {
203:           VecAXPY(X,step2,P);
204:         }
205:       }
206:       pcgP->ltsnrm = pcgP->delta;                       /* convergence in direction of */
207:       ksp->reason  = KSP_CONVERGED_CG_NEG_CURVE;  /* negative curvature */
208:       if (!i) {
209:         PetscInfo1(ksp,"negative curvature: delta=%g\n",(double)pcgP->delta);
210:       } else {
211:         PetscInfo3(ksp,"negative curvature: step1=%g, step2=%g, delta=%g\n",(double)step1,(double)step2,(double)pcgP->delta);
212:       }

214:     } else {
215:       /* Compute step along p */
216:       step = rtr/ptasp;
217:       VecCopy(W,X);        /*  x = w  */
218:       VecAXPY(X,step,P);   /*  x <- step*p + x  */
219:       VecNorm(X,NORM_2,&pcgP->ltsnrm);

221:       if (pcgP->ltsnrm > pcgP->delta) {
222:         /* Since the trial iterate is outside the trust region,
223:             evaluate a constrained step along p so that
224:                     ||w + step*p|| = delta
225:           The positive step is always better in this case. */
226:         if (!i) {
227:           scal = pcgP->delta / pcgP->ltsnrm;
228:           VecScale(X,scal);
229:         } else {
230:           /* Compute roots of quadratic */
231:           KSPQCGQuadraticRoots(W,P,pcgP->delta,&step1,&step2);
232:           VecCopy(W,X);
233:           VecAXPY(X,step1,P);  /*  x <- step1*p + x  */
234:         }
235:         pcgP->ltsnrm = pcgP->delta;
236:         ksp->reason  = KSP_CONVERGED_CG_CONSTRAINED; /* convergence along constrained step */
237:         if (!i) {
238:           PetscInfo1(ksp,"constrained step: delta=%g\n",(double)pcgP->delta);
239:         } else {
240:           PetscInfo3(ksp,"constrained step: step1=%g, step2=%g, delta=%g\n",(double)step1,(double)step2,(double)pcgP->delta);
241:         }

243:       } else {
244:         /* Evaluate the current step */
245:         VecCopy(X,W);  /* update interior iterate */
246:         VecAXPY(R,-step,ASP); /* r <- -step*asp + r */
247:         VecNorm(R,NORM_2,&rnrm);

249:         PetscObjectSAWsTakeAccess((PetscObject)ksp);
250:         ksp->rnorm = rnrm;
251:         PetscObjectSAWsGrantAccess((PetscObject)ksp);
252:         KSPLogResidualHistory(ksp,rnrm);
253:         KSPMonitor(ksp,i+1,rnrm);
254:         (*ksp->converged)(ksp,i+1,rnrm,&ksp->reason,ksp->cnvP);
255:         if (ksp->reason) {                 /* convergence for */
256:           PetscInfo3(ksp,"truncated step: step=%g, rnrm=%g, delta=%g\n",(double)PetscRealPart(step),(double)rnrm,(double)pcgP->delta);
257:         }
258:       }
259:     }
260:     if (ksp->reason) break;  /* Convergence has been attained */
261:     else {                   /* Compute a new AS-orthogonal direction */
262:       VecDot(R,R,&rntrn);
263:       beta = rntrn/rtr;
264:       VecAYPX(P,beta,R);  /*  p <- r + beta*p  */
265:       rtr  = PetscRealPart(rntrn);
266:     }
267:   }
268:   if (!ksp->reason) ksp->reason = KSP_DIVERGED_ITS;

270:   /* Unscale x */
271:   VecCopy(X,WA2);
272:   PCApplySymmetricRight(pc,WA2,X);

274:   KSP_MatMult(ksp,Amat,X,WA);
275:   VecDotRealPart(B,X,&btx);
276:   VecDotRealPart(X,WA,&xtax);

278:   pcgP->quadratic = btx + .5*xtax;
279:   return(0);
280: }

284: PetscErrorCode KSPSetUp_QCG(KSP ksp)
285: {

289:   /* Get work vectors from user code */
290:   KSPSetWorkVecs(ksp,7);
291:   return(0);
292: }

296: PetscErrorCode KSPDestroy_QCG(KSP ksp)
297: {

301:   PetscObjectComposeFunction((PetscObject)ksp,"KSPQCGGetQuadratic_C",NULL);
302:   PetscObjectComposeFunction((PetscObject)ksp,"KSPQCGGetTrialStepNorm_C",NULL);
303:   PetscObjectComposeFunction((PetscObject)ksp,"KSPQCGSetTrustRegionRadius_C",NULL);
304:   KSPDestroyDefault(ksp);
305:   return(0);
306: }

310: static PetscErrorCode  KSPQCGSetTrustRegionRadius_QCG(KSP ksp,PetscReal delta)
311: {
312:   KSP_QCG *cgP = (KSP_QCG*)ksp->data;

315:   cgP->delta = delta;
316:   return(0);
317: }

321: static PetscErrorCode  KSPQCGGetTrialStepNorm_QCG(KSP ksp,PetscReal *ltsnrm)
322: {
323:   KSP_QCG *cgP = (KSP_QCG*)ksp->data;

326:   *ltsnrm = cgP->ltsnrm;
327:   return(0);
328: }

332: static PetscErrorCode  KSPQCGGetQuadratic_QCG(KSP ksp,PetscReal *quadratic)
333: {
334:   KSP_QCG *cgP = (KSP_QCG*)ksp->data;

337:   *quadratic = cgP->quadratic;
338:   return(0);
339: }

343: PetscErrorCode KSPSetFromOptions_QCG(PetscOptionItems *PetscOptionsObject,KSP ksp)
344: {
346:   PetscReal      delta;
347:   KSP_QCG        *cgP = (KSP_QCG*)ksp->data;
348:   PetscBool      flg;

351:   PetscOptionsHead(PetscOptionsObject,"KSP QCG Options");
352:   PetscOptionsReal("-ksp_qcg_trustregionradius","Trust Region Radius","KSPQCGSetTrustRegionRadius",cgP->delta,&delta,&flg);
353:   if (flg) { KSPQCGSetTrustRegionRadius(ksp,delta); }
354:   PetscOptionsTail();
355:   return(0);
356: }

358: /*MC
359:      KSPQCG -   Code to run conjugate gradient method subject to a constraint
360:          on the solution norm. This is used in Trust Region methods for nonlinear equations, SNESNEWTONTR

362:    Options Database Keys:
363: .      -ksp_qcg_trustregionradius <r> - Trust Region Radius

365:    Notes: This is rarely used directly

367:    Level: developer

369:   Notes:  Use preconditioned conjugate gradient to compute
370:       an approximate minimizer of the quadratic function

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

374:    subject to the Euclidean norm trust region constraint

376:             || D * s || <= delta,

378:    where

380:      delta is the trust region radius,
381:      g is the gradient vector, and
382:      H is Hessian matrix,
383:      D is a scaling matrix.

385:    KSPConvergedReason may be
386: $  KSP_CONVERGED_CG_NEG_CURVE if convergence is reached along a negative curvature direction,
387: $  KSP_CONVERGED_CG_CONSTRAINED if convergence is reached along a constrained step,
388: $  other KSP converged/diverged reasons


391:   Notes:
392:   Currently we allow symmetric preconditioning with the following scaling matrices:
393:       PCNONE:   D = Identity matrix
394:       PCJACOBI: D = diag [d_1, d_2, ...., d_n], where d_i = sqrt(H[i,i])
395:       PCICC:    D = L^T, implemented with forward and backward solves.
396:                 Here L is an incomplete Cholesky factor of H.

398:   References:
399: .  1. - Trond Steihaug, The Conjugate Gradient Method and Trust Regions in Large Scale Optimization,
400:    SIAM Journal on Numerical Analysis, Vol. 20, No. 3 (Jun., 1983).

402: .seealso:  KSPCreate(), KSPSetType(), KSPType (for list of available types), KSP, KSPQCGSetTrustRegionRadius()
403:            KSPQCGGetTrialStepNorm(), KSPQCGGetQuadratic()
404: M*/

408: PETSC_EXTERN PetscErrorCode KSPCreate_QCG(KSP ksp)
409: {
411:   KSP_QCG        *cgP;

414:   KSPSetSupportedNorm(ksp,KSP_NORM_PRECONDITIONED,PC_SYMMETRIC,3);
415:   PetscNewLog(ksp,&cgP);

417:   ksp->data                = (void*)cgP;
418:   ksp->ops->setup          = KSPSetUp_QCG;
419:   ksp->ops->setfromoptions = KSPSetFromOptions_QCG;
420:   ksp->ops->solve          = KSPSolve_QCG;
421:   ksp->ops->destroy        = KSPDestroy_QCG;
422:   ksp->ops->buildsolution  = KSPBuildSolutionDefault;
423:   ksp->ops->buildresidual  = KSPBuildResidualDefault;
424:   ksp->ops->setfromoptions = 0;
425:   ksp->ops->view           = 0;

427:   PetscObjectComposeFunction((PetscObject)ksp,"KSPQCGGetQuadratic_C",KSPQCGGetQuadratic_QCG);
428:   PetscObjectComposeFunction((PetscObject)ksp,"KSPQCGGetTrialStepNorm_C",KSPQCGGetTrialStepNorm_QCG);
429:   PetscObjectComposeFunction((PetscObject)ksp,"KSPQCGSetTrustRegionRadius_C",KSPQCGSetTrustRegionRadius_QCG);
430:   cgP->delta = PETSC_MAX_REAL; /* default trust region radius is infinite */
431:   return(0);
432: }

434: /* ---------------------------------------------------------- */
437: /*
438:   KSPQCGQuadraticRoots - Computes the roots of the quadratic,
439:          ||s + step*p|| - delta = 0
440:    such that step1 >= 0 >= step2.
441:    where
442:       delta:
443:         On entry delta must contain scalar delta.
444:         On exit delta is unchanged.
445:       step1:
446:         On entry step1 need not be specified.
447:         On exit step1 contains the non-negative root.
448:       step2:
449:         On entry step2 need not be specified.
450:         On exit step2 contains the non-positive root.
451:    C code is translated from the Fortran version of the MINPACK-2 Project,
452:    Argonne National Laboratory, Brett M. Averick and Richard G. Carter.
453: */
454: static PetscErrorCode KSPQCGQuadraticRoots(Vec s,Vec p,PetscReal delta,PetscReal *step1,PetscReal *step2)
455: {
456:   PetscReal      dsq,ptp,pts,rad,sts;

460:   VecDotRealPart(p,s,&pts);
461:   VecDotRealPart(p,p,&ptp);
462:   VecDotRealPart(s,s,&sts);
463:   dsq  = delta*delta;
464:   rad  = PetscSqrtReal((pts*pts) - ptp*(sts - dsq));
465:   if (pts > 0.0) {
466:     *step2 = -(pts + rad)/ptp;
467:     *step1 = (sts - dsq)/(ptp * *step2);
468:   } else {
469:     *step1 = -(pts - rad)/ptp;
470:     *step2 = (sts - dsq)/(ptp * *step1);
471:   }
472:   return(0);
473: }