Date: 2013-07-02T14:48:21+00:00

Actual source code: qcg.c
petsc-3.4.2 2013-07-02

      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:   MatStructure   pflag;
    114:   Mat            Amat,Pmat;
    115:   Vec            W,WA,WA2,R,P,ASP,BS,X,B;
    116:   PetscScalar    scal,beta,rntrn,step;
    117:   PetscReal      q1,q2,xnorm,step1,step2,rnrm,btx,xtax;
    118:   PetscReal      ptasp,rtr,wtasp,bstp;
    119:   PetscReal      dzero = 0.0,bsnrm;
    121:   PetscInt       i,maxit;
    122:   PC             pc = ksp->pc;
    123:   PCSide         side;
    124:   PetscBool      diagonalscale;

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

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

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

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

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

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

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

    171:   for (i=0; i<=maxit; i++) {
    172:     PetscObjectAMSTakeAccess((PetscObject)ksp);
    173:     ksp->its++;
    174:     PetscObjectAMSGrantAccess((PetscObject)ksp);

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

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

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

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

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

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

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

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

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

    275:   MatMult(Amat,X,WA);
    276:   VecDotRealPart(B,X,&btx);
    277:   VecDotRealPart(X,WA,&xtax);

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

    285: PetscErrorCode KSPSetUp_QCG(KSP ksp)
    286: {

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

    297: PetscErrorCode KSPDestroy_QCG(KSP ksp)
    298: {

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

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

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

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

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

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

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

    344: PetscErrorCode KSPSetFromOptions_QCG(KSP ksp)
    345: {
    347:   PetscReal      delta;
    348:   KSP_QCG        *cgP = (KSP_QCG*)ksp->data;
    349:   PetscBool      flg;

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

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

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

    366:    Notes: This is rarely used directly

    368:    Level: developer

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

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

    375:    subject to the Euclidean norm trust region constraint

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

    379:    where

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

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

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

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

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

    409: PETSC_EXTERN PetscErrorCode KSPCreate_QCG(KSP ksp)
    410: {
    412:   KSP_QCG        *cgP;

    415:   KSPSetSupportedNorm(ksp,KSP_NORM_PRECONDITIONED,PC_SYMMETRIC,2);
    416:   PetscNewLog(ksp,KSP_QCG,&cgP);

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

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

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

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