Actual source code: stcg.c

  1: #define PETSCKSP_DLL

 3:  #include src/ksp/ksp/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 PETSCKSP_DLLEXPORT 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:     KSPSTCGGetQuadratic - Gets the value of the quadratic function, evaluated at the new iterate:

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

 45:     which satisfies the trust region constraint

 47:        || s ||_M <= radius,

 49:     where

 51:      radius is the trust region radius,
 52:      g is the gradient vector, and
 53:      H is the Hessian matrix,
 54:      M is the positive definite preconditioner matrix.

 56:     Collective on KSP

 58:     Input Parameter:
 59: .   ksp - the iterative context

 61:     Output Parameter:
 62: .   quadratic - the quadratic function evaluated at the new iterate

 64:     Level: advanced
 65: @*/
 66: PetscErrorCode PETSCKSP_DLLEXPORT KSPSTCGGetQuadratic(KSP ksp,PetscReal *quadratic)
 67: {
 68:   PetscErrorCode ierr,(*f)(KSP,PetscReal*);

 72:   PetscObjectQueryFunction((PetscObject)ksp, "KSPSTCGGetQuadratic_C", (void (**)(void))&f);
 73:   if (f) {
 74:     (*f)(ksp,quadratic);
 75:   }
 76:   return(0);
 77: }

 81: /*
 82:   KSPSolve_STCG - Use preconditioned conjugate gradient to compute
 83:   an approximate minimizer of the quadratic function

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

 87:    subject to the trust region constraint

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

 91:    where

 93:      delta is the trust region radius,
 94:      g is the gradient vector, and
 95:      H is the Hessian matrix,
 96:      M is the positive definite preconditioner matrix.

 98:    KSPConvergedReason may be
 99: $  KSP_CONVERGED_STCG_NEG_CURVE if convergence is reached along a negative curvature direction,
100: $  KSP_CONVERGED_STCG_CONSTRAINED if convergence is reached along a constrained step,
101: $  other KSP converged/diverged reasons


104:   Notes:
105:   The preconditioner supplied should be symmetric and positive definite.
106: */
107: PetscErrorCode KSPSolve_STCG(KSP ksp)
108: {
110:   MatStructure   pflag;
111:   Mat            Qmat, Mmat;
112:   Vec            r, z, p, w, d;
113:   PC             pc;
114:   KSP_STCG        *cg;
115:   PetscReal      zero = 0.0, pfive = 0.5, negone = -1.0;
116:   PetscReal      norm_r, norm_d, norm_dp1, norm_p, dMp;
117:   PetscReal      alpha, beta, kappa, rz, rzm1;
118:   PetscReal      radius;
119:   PetscInt       i, maxit;
120: #if defined(PETSC_USE_COMPLEX)
121:   PetscScalar    crz, ckappa, cquadratic;
122: #endif
123:   PetscTruth     diagonalscale;

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

129:   cg       = (KSP_STCG *)ksp->data;
130:   radius   = cg->radius;
131:   maxit    = ksp->max_it;
132:   r        = ksp->work[0];
133:   z        = ksp->work[1];
134:   p        = ksp->work[2];
135:   w        = ksp->work[3];
136:   d        = ksp->vec_sol;
137:   pc       = ksp->pc;

139:   ksp->its = 0;
140:   if (radius <= zero) SETERRQ(PETSC_ERR_ARG_OUTOFRANGE,"Input error: radius <= 0");

142:   /* Initialize variables */
143:   PCGetOperators(pc, &Qmat, &Mmat, &pflag);

145:   VecSet(d, zero);                  /* d = 0        */
146:   VecCopy(ksp->vec_rhs, r);         /* r = rhs      */
147:   VecScale(r, negone);              /* r = grad     */
148:   VecNorm(r, NORM_2, &norm_r);      /* norm_r = |r| */

150:   KSPLogResidualHistory(ksp, norm_r);
151:   KSPMonitor(ksp, 0, norm_r);
152:   ksp->rnorm = norm_r;

154:   (*ksp->converged)(ksp, 0, norm_r, &ksp->reason, ksp->cnvP);
155:   if (ksp->reason) return(0);

157:   /* Compute the initial vectors */
158:   PCApply(pc, r, z);                /* z = M_{-1} r */
159:   VecCopy(z, p);                    /* p = -z       */
160:   VecScale(p, negone);
161: #if defined(PETSC_USE_COMPLEX)
162:   VecDot(r, z, &crz); rz = PetscRealPart(crz);
163: #else
164:   VecDot(r, z, &rz);                /* rz = r^T z   */
165: #endif

167:   dMp    = 0;
168:   norm_p = rz;
169:   norm_d = 0;

171:   /* Begin iterating */
172:   for (i=0; i<=maxit; i++) {
173:     ksp->its++;

175:     MatMult(Qmat, p, w);             /* w = Q * p   */
176: #if defined(PETSC_USE_COMPLEX)
177:     VecDot(p, w, &ckappa); kappa = PetscRealPart(ckappa);
178: #else
179:     VecDot(p, w, &kappa);            /* kappa = p^T w */
180: #endif

182:     if (kappa <= zero) {
183:       /* Direction of negative curvature, calculate intersection and sol */

185:       alpha = (sqrt(dMp*dMp+norm_p*(radius-norm_d))-dMp)/norm_p;
186:       VecAXPY(d, alpha, p);         /* d = d + alpha p */

188:       ksp->reason  = KSP_CONVERGED_STCG_NEG_CURVE;  /* negative curvature */
189:       PetscInfo1(ksp, "KSPSolve_STCG: negative curvature: radius=%g\n", cg->radius);
190:       break;
191:     }

193:     alpha = rz / kappa;

195:     norm_dp1 = norm_d + 2.0*alpha*dMp + alpha*alpha*norm_p;
196:     if (norm_dp1 >= radius) {
197:       alpha = (sqrt(dMp*dMp+norm_p*(radius-norm_d))-dMp)/norm_p;
198:       VecAXPY(d, alpha, p);         /* d = d + alpha p */

200:       ksp->reason  = KSP_CONVERGED_STCG_CONSTRAINED;     /* step */
201:       PetscInfo1(ksp, "KSPSolve_STCG: constrained step: radius=%g\n",cg->radius);
202:       break;
203:     }

205:     VecAXPY(d, alpha, p);          /* d = d + alpha p */
206:     VecAXPY(r, alpha, w);                        /* r = r + alpha w */

208:     VecNorm(r, NORM_2, &norm_r);
209:     ksp->rnorm = norm_r;
210:     KSPLogResidualHistory(ksp, norm_r);
211:     KSPMonitor(ksp, i+1, norm_r);
212:     (*ksp->converged)(ksp, i+1, norm_r, &ksp->reason, ksp->cnvP);
213:     if (ksp->reason) {                 /* convergence */
214:       PetscInfo2(ksp,"KSPSolve_STCG: truncated step: rnorm=%g, radius=%g\n",norm_r,cg->radius);
215:       break;
216:     }

218:     PCApply(pc, r, z);

220:     rzm1 = rz;
221: #if defined(PETSC_USE_COMPLEX)
222:     VecDot(r, z, &crz); rz = PetscRealPart(crz);
223: #else
224:     VecDot(r, z, &rz);
225: #endif
226:     beta = rz / rzm1;

228:     VecAXPBY(p, negone, beta, z);                    /* p = beta p - z */
229:     dMp = beta*dMp + alpha*norm_p;
230:     norm_p = rz + beta*beta*norm_p;
231:     norm_d = norm_dp1;
232:   }
233:   if (!ksp->reason) {
234:     ksp->reason = KSP_DIVERGED_ITS;
235:   }

237:   /* Compute Q(x) */
238:   MatMult(Qmat, d, p);
239:   VecAXPBY(p, negone, pfive, ksp->vec_rhs);
240: #if defined(PETSC_USE_COMPLEX)
241:   VecDot(d, p, &cquadratic); cg->quadratic = PetscRealPart(cquadratic);
242: #else
243:   VecDot(d, p, &(cg->quadratic));
244: #endif
245:   return(0);
246: }

250: PetscErrorCode KSPSetUp_STCG(KSP ksp)
251: {

255:   /* This implementation of CG only handles left preconditioning
256:    * so generate an error otherwise.
257:    */
258:   if (ksp->pc_side == PC_RIGHT) {
259:     SETERRQ(PETSC_ERR_SUP, "No right preconditioning for KSPSTCG");
260:   }
261:   else if (ksp->pc_side == PC_SYMMETRIC) {
262:     SETERRQ(PETSC_ERR_SUP, "No symmetric preconditioning for KSPSTCG");
263:   }

265:   /* get work vectors needed by CG */
266:   KSPDefaultGetWork(ksp, 4);
267:   return(0);
268: }

272: PetscErrorCode KSPDestroy_STCG(KSP ksp)
273: {
274:   KSP_STCG        *cgP = (KSP_STCG *)ksp->data;

278:   KSPDefaultFreeWork(ksp);

280:   /* Free the context variable */
281:   PetscFree(cgP);
282:   return(0);
283: }

288: PetscErrorCode PETSCKSP_DLLEXPORT KSPSTCGSetRadius_STCG(KSP ksp,PetscReal radius)
289: {
290:   KSP_STCG *cgP = (KSP_STCG *)ksp->data;

293:   cgP->radius = radius;
294:   return(0);
295: }

301: PetscErrorCode PETSCKSP_DLLEXPORT KSPSTCGGetQuadratic_STCG(KSP ksp,PetscReal *quadratic)
302: {
303:   KSP_STCG *cgP = (KSP_STCG *)ksp->data;

306:   *quadratic = cgP->quadratic;
307:   return(0);
308: }

313: PetscErrorCode KSPSetFromOptions_STCG(KSP ksp)
314: {
316:   KSP_STCG        *cgP = (KSP_STCG *)ksp->data;
317:   PetscReal      radius;
318:   PetscTruth     flg;

321:   PetscOptionsHead("KSP STCG options");
322:   PetscOptionsReal("-ksp_stcg_radius", "Trust Region Radius", "KSPSTCGSetRadius", cgP->radius, &radius, &flg);
323:   if (flg) { KSPSTCGSetRadius(ksp, radius);  }
324:   PetscOptionsTail();
325:   return(0);
326: }

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

333:    Options Database Keys:
334: .      -ksp_stcg_radius <r> - Trust Region Radius

336:    Notes: This is rarely used directly

338:    Level: developer

340: .seealso:  KSPCreate(), KSPSetType(), KSPType (for list of available types), KSP, KSPSTCGSetRadius()
341:            KSPSTCGGetQuadratic()
342: M*/

347: PetscErrorCode PETSCKSP_DLLEXPORT KSPCreate_STCG(KSP ksp)
348: {
350:   KSP_STCG        *cg;

353:   PetscNew(KSP_STCG, &cg);
354:   PetscLogObjectMemory(ksp, sizeof(KSP_STCG));
355:   cg->radius                     = PETSC_MAX;
356:   cg->quadratic                  = 0;
357:   ksp->data                      = (void *)cg;
358:   ksp->pc_side                   = PC_LEFT;

360:   /* Sets the functions that are associated with this data structure
361:    * (in C++ this is the same as defining virtual functions)
362:    */
363:   ksp->ops->setup                = KSPSetUp_STCG;
364:   ksp->ops->solve                = KSPSolve_STCG;
365:   ksp->ops->destroy              = KSPDestroy_STCG;
366:   ksp->ops->setfromoptions       = KSPSetFromOptions_STCG;
367:   ksp->ops->buildsolution        = KSPDefaultBuildSolution;
368:   ksp->ops->buildresidual        = KSPDefaultBuildResidual;
369:   ksp->ops->view                 = 0;

371:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPSTCGGetQuadratic_C",
372:                                     "KSPSTCGGetQuadratic_STCG",
373:                                      KSPSTCGGetQuadratic_STCG);
374:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPSTCGSetRadius_C",
375:                                     "KSPSTCGSetRadius_STCG",
376:                                      KSPSTCGSetRadius_STCG);
377:   return(0);
378: }