Actual source code: cheby.c

  2: #include <private/kspimpl.h>                    /*I "petscksp.h" I*/
  3: #include <../src/ksp/ksp/impls/cheby/chebychevimpl.h>

  7: PetscErrorCode KSPSetUp_Chebychev(KSP ksp)
  8: {
  9:   KSP_Chebychev *cheb = (KSP_Chebychev*)ksp->data;

 13:   KSPDefaultGetWork(ksp,3);
 14:   cheb->estimate_current = PETSC_FALSE;
 15:   return(0);
 16: }

 21: PetscErrorCode  KSPChebychevSetEigenvalues_Chebychev(KSP ksp,PetscReal emax,PetscReal emin)
 22: {
 23:   KSP_Chebychev *chebychevP = (KSP_Chebychev*)ksp->data;

 26:   if (emax <= emin) SETERRQ2(((PetscObject)ksp)->comm,PETSC_ERR_ARG_INCOMP,"Maximum eigenvalue must be larger than minimum: max %g min %G",emax,emin);
 27:   if (emax*emin <= 0.0) SETERRQ2(((PetscObject)ksp)->comm,PETSC_ERR_ARG_INCOMP,"Both eigenvalues must be of the same sign: max %G min %G",emax,emin);
 28:   chebychevP->emax = emax;
 29:   chebychevP->emin = emin;
 30:   return(0);
 31: }

 37: PetscErrorCode  KSPChebychevSetEstimateEigenvalues_Chebychev(KSP ksp,PetscReal a,PetscReal b,PetscReal c,PetscReal d)
 38: {
 39:   KSP_Chebychev *cheb = (KSP_Chebychev*)ksp->data;

 43:   if (a != 0.0 || b != 0.0 || c != 0.0 || d != 0.0) {
 44:     if (!cheb->kspest) {
 45:       PetscBool nonzero;

 47:       KSPCreate(((PetscObject)ksp)->comm,&cheb->kspest);

 49:       KSPGetPC(cheb->kspest,&cheb->pcnone);
 50:       PetscObjectReference((PetscObject)cheb->pcnone);
 51:       PCSetType(cheb->pcnone,PCNONE);
 52:       KSPSetPC(cheb->kspest,ksp->pc);

 54:       KSPGetInitialGuessNonzero(ksp,&nonzero);
 55:       KSPSetInitialGuessNonzero(cheb->kspest,nonzero);
 56:       KSPSetComputeSingularValues(cheb->kspest,PETSC_TRUE);

 58:       /* Estimate with a fixed number of iterations */
 59:       KSPSetConvergenceTest(cheb->kspest,KSPSkipConverged,0,0);
 60:       KSPSetNormType(cheb->kspest,KSP_NORM_NONE);
 61:       KSPSetTolerances(cheb->kspest,PETSC_DEFAULT,PETSC_DEFAULT,PETSC_DEFAULT,5);

 63:       if (a >= 0) cheb->tform[0] = a;
 64:       if (b >= 0) cheb->tform[1] = b;
 65:       if (c >= 0) cheb->tform[2] = c;
 66:       if (d >= 0) cheb->tform[3] = d;
 67:     }
 68:   } else {
 69:     KSPDestroy(&cheb->kspest);
 70:     PCDestroy(&cheb->pcnone);
 71:   }
 72:   return(0);
 73: }

 78: /*@
 79:    KSPChebychevSetEigenvalues - Sets estimates for the extreme eigenvalues
 80:    of the preconditioned problem.

 82:    Logically Collective on KSP

 84:    Input Parameters:
 85: +  ksp - the Krylov space context
 86: -  emax, emin - the eigenvalue estimates

 88:   Options Database:
 89: .  -ksp_chebychev_eigenvalues emin,emax

 91:    Note: If you run with the Krylov method of KSPCG with the option -ksp_monitor_singular_value it will 
 92:     for that given matrix and preconditioner an estimate of the extreme eigenvalues.

 94:    Level: intermediate

 96: .keywords: KSP, Chebyshev, set, eigenvalues
 97: @*/
 98: PetscErrorCode  KSPChebychevSetEigenvalues(KSP ksp,PetscReal emax,PetscReal emin)
 99: {

106:   PetscTryMethod(ksp,"KSPChebychevSetEigenvalues_C",(KSP,PetscReal,PetscReal),(ksp,emax,emin));
107:   return(0);
108: }

112: /*@
113:    KSPChebychevSetEstimateEigenvalues - Automatically estimate the eigenvalues to use for Chebychev

115:    Logically Collective on KSP

117:    Input Parameters:
118: +  ksp - the Krylov space context
119: .  a - multiple of min eigenvalue estimate to use for min Chebychev bound (or PETSC_DECIDE)
120: .  b - multiple of max eigenvalue estimate to use for min Chebychev bound (or PETSC_DECIDE)
121: .  c - multiple of min eigenvalue estimate to use for max Chebychev bound (or PETSC_DECIDE)
122: -  d - multiple of max eigenvalue estimate to use for max Chebychev bound (or PETSC_DECIDE)

124:   Options Database:
125: .  -ksp_chebychev_estimate_eigenvalues a,b,c,d

127:    Notes:
128:    The Chebychev bounds are estimated using
129: .vb
130:    minbound = a*minest + b*maxest
131:    maxbound = c*minest + d*maxest
132: .ve
133:    The default configuration targets the upper part of the spectrum for use as a multigrid smoother, so only the maximum eigenvalue estimate is used.
134:    The minimum eigenvalue estimate obtained by Krylov iteration is typically not accurate until the method has converged.

136:    If 0.0 is passed for all transform arguments (a,b,c,d), eigenvalue estimation is disabled.

138:    Level: intermediate

140: .keywords: KSP, Chebyshev, set, eigenvalues
141: @*/
142: PetscErrorCode KSPChebychevSetEstimateEigenvalues(KSP ksp,PetscReal a,PetscReal b,PetscReal c,PetscReal d)
143: {

152:   PetscTryMethod(ksp,"KSPChebychevSetEstimateEigenvalues_C",(KSP,PetscReal,PetscReal,PetscReal,PetscReal),(ksp,a,b,c,d));
153:   return(0);
154: }

158: PetscErrorCode KSPSetFromOptions_Chebychev(KSP ksp)
159: {
160:   KSP_Chebychev *cheb = (KSP_Chebychev*)ksp->data;
162:   PetscInt       two = 2,four = 4;
163:   PetscReal      tform[4] = {PETSC_DECIDE,PETSC_DECIDE,PETSC_DECIDE,PETSC_DECIDE};
164:   PetscBool      flg;

167:   PetscOptionsHead("KSP Chebychev Options");
168:   PetscOptionsRealArray("-ksp_chebychev_eigenvalues","extreme eigenvalues","KSPChebychevSetEigenvalues",&cheb->emin,&two,0);
169:   PetscOptionsRealArray("-ksp_chebychev_estimate_eigenvalues","estimate eigenvalues using a Krylov method, then use this transform for Chebychev eigenvalue bounds","KSPChebychevSetEstimateEigenvalues",tform,&four,&flg);
170:   if (flg) {KSPChebychevSetEstimateEigenvalues(ksp,tform[0],tform[1],tform[2],tform[3]);}
171:   if (cheb->kspest) {
172:     /* Mask the PC so that PCSetFromOptions does not do anything */
173:     KSPSetPC(cheb->kspest,cheb->pcnone);
174:     KSPSetOptionsPrefix(cheb->kspest,((PetscObject)ksp)->prefix);
175:     KSPAppendOptionsPrefix(cheb->kspest,"est_");
176:     if (!((PetscObject)cheb->kspest)->type_name) {
177:       KSPSetType(cheb->kspest,KSPGMRES);
178:     }
179:     KSPSetFromOptions(cheb->kspest);
180:     KSPSetPC(cheb->kspest,ksp->pc);
181:   }
182:   PetscOptionsTail();
183:   return(0);
184: }

188: PetscErrorCode KSPSolve_Chebychev(KSP ksp)
189: {
190:   KSP_Chebychev  *cheb = (KSP_Chebychev*)ksp->data;
192:   PetscInt       k,kp1,km1,maxit,ktmp,i;
193:   PetscScalar    alpha,omegaprod,mu,omega,Gamma,c[3],scale;
194:   PetscReal      rnorm = 0.0;
195:   Vec            x,b,p[3],r;
196:   Mat            Amat,Pmat;
197:   MatStructure   pflag;
198:   PetscBool      diagonalscale;

201:   PCGetDiagonalScale(ksp->pc,&diagonalscale);
202:   if (diagonalscale) SETERRQ1(((PetscObject)ksp)->comm,PETSC_ERR_SUP,"Krylov method %s does not support diagonal scaling",((PetscObject)ksp)->type_name);

204:   if (cheb->kspest && !cheb->estimate_current) {
205:     PetscReal max,min;
206:     PetscBool nonzero;
207:     Vec X = ksp->vec_sol;
208:     KSPGetInitialGuessNonzero(ksp,&nonzero);
209:     if (nonzero) {VecDuplicate(ksp->vec_sol,&X);}
210:     KSPSolve(cheb->kspest,ksp->vec_rhs,X);
211:     if (nonzero) {VecDestroy(&X);}
212:     else {VecZeroEntries(X);}
213:     KSPComputeExtremeSingularValues(cheb->kspest,&max,&min);
214:     cheb->emin = cheb->tform[0]*min + cheb->tform[1]*max;
215:     cheb->emax = cheb->tform[2]*min + cheb->tform[3]*max;
216:     cheb->estimate_current = PETSC_TRUE;
217:   }

219:   ksp->its = 0;
220:   PCGetOperators(ksp->pc,&Amat,&Pmat,&pflag);
221:   maxit    = ksp->max_it;

223:   /* These three point to the three active solutions, we
224:      rotate these three at each solution update */
225:   km1    = 0; k = 1; kp1 = 2;
226:   x      = ksp->vec_sol;
227:   b      = ksp->vec_rhs;
228:   p[km1] = x;
229:   p[k]   = ksp->work[0];
230:   p[kp1] = ksp->work[1];
231:   r      = ksp->work[2];

233:   /* use scale*B as our preconditioner */
234:   scale  = 2.0/(cheb->emax + cheb->emin);

236:   /*   -alpha <=  scale*lambda(B^{-1}A) <= alpha   */
237:   alpha  = 1.0 - scale*(cheb->emin); ;
238:   Gamma  = 1.0;
239:   mu     = 1.0/alpha;
240:   omegaprod = 2.0/alpha;

242:   c[km1] = 1.0;
243:   c[k]   = mu;

245:   if (!ksp->guess_zero) {
246:     KSP_MatMult(ksp,Amat,x,r);     /*  r = b - Ax     */
247:     VecAYPX(r,-1.0,b);
248:   } else {
249:     VecCopy(b,r);
250:   }
251: 
252:   KSP_PCApply(ksp,r,p[k]);  /* p[k] = scale B^{-1}r + x */
253:   VecAYPX(p[k],scale,x);

255:   for (i=0; i<maxit; i++) {
256:     PetscObjectTakeAccess(ksp);
257:     ksp->its++;
258:     PetscObjectGrantAccess(ksp);
259:     c[kp1] = 2.0*mu*c[k] - c[km1];
260:     omega = omegaprod*c[k]/c[kp1];

262:     KSP_MatMult(ksp,Amat,p[k],r);                 /*  r = b - Ap[k]    */
263:     VecAYPX(r,-1.0,b);
264:     KSP_PCApply(ksp,r,p[kp1]);             /*  p[kp1] = B^{-1}z  */

266:     /* calculate residual norm if requested */
267:     if (ksp->normtype != KSP_NORM_NONE) {
268:       if (ksp->normtype == KSP_NORM_UNPRECONDITIONED) {VecNorm(r,NORM_2,&rnorm);}
269:       else {VecNorm(p[kp1],NORM_2,&rnorm);}
270:       PetscObjectTakeAccess(ksp);
271:       ksp->rnorm                              = rnorm;
272:       PetscObjectGrantAccess(ksp);
273:       ksp->vec_sol = p[k];
274:       KSPLogResidualHistory(ksp,rnorm);
275:       KSPMonitor(ksp,i,rnorm);
276:       (*ksp->converged)(ksp,i,rnorm,&ksp->reason,ksp->cnvP);
277:       if (ksp->reason) break;
278:     }

280:     /* y^{k+1} = omega(y^{k} - y^{k-1} + Gamma*r^{k}) + y^{k-1} */
281:     VecScale(p[kp1],omega*Gamma*scale);
282:     VecAXPY(p[kp1],1.0-omega,p[km1]);
283:     VecAXPY(p[kp1],omega,p[k]);

285:     ktmp = km1;
286:     km1  = k;
287:     k    = kp1;
288:     kp1  = ktmp;
289:   }
290:   if (!ksp->reason) {
291:     if (ksp->normtype != KSP_NORM_NONE) {
292:       KSP_MatMult(ksp,Amat,p[k],r);       /*  r = b - Ap[k]    */
293:       VecAYPX(r,-1.0,b);
294:       if (ksp->normtype == KSP_NORM_UNPRECONDITIONED) {
295:         VecNorm(r,NORM_2,&rnorm);
296:       } else {
297:         KSP_PCApply(ksp,r,p[kp1]); /* p[kp1] = B^{-1}z */
298:         VecNorm(p[kp1],NORM_2,&rnorm);
299:       }
300:       PetscObjectTakeAccess(ksp);
301:       ksp->rnorm = rnorm;
302:       PetscObjectGrantAccess(ksp);
303:       ksp->vec_sol = p[k];
304:       KSPLogResidualHistory(ksp,rnorm);
305:       KSPMonitor(ksp,i,rnorm);
306:     }
307:     if (ksp->its >= ksp->max_it) {
308:       if (ksp->normtype != KSP_NORM_NONE) {
309:         (*ksp->converged)(ksp,i,rnorm,&ksp->reason,ksp->cnvP);
310:         if (!ksp->reason) ksp->reason = KSP_DIVERGED_ITS;
311:       } else {
312:         ksp->reason = KSP_CONVERGED_ITS;
313:       }
314:     }
315:   }

317:   /* make sure solution is in vector x */
318:   ksp->vec_sol = x;
319:   if (k) {
320:     VecCopy(p[k],x);
321:   }
322:   return(0);
323: }

327: PetscErrorCode KSPView_Chebychev(KSP ksp,PetscViewer viewer)
328: {
329:   KSP_Chebychev  *cheb = (KSP_Chebychev*)ksp->data;
331:   PetscBool      iascii;

334:   PetscTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);
335:   if (iascii) {
336:     PetscViewerASCIIPrintf(viewer,"  Chebychev: eigenvalue estimates:  min = %G, max = %G\n",cheb->emin,cheb->emax);
337:     if (cheb->kspest) {
338:       PetscViewerASCIIPushTab(viewer);
339:       KSPView(cheb->kspest,viewer);
340:       PetscViewerASCIIPopTab(viewer);
341:     }
342:   } else {
343:     SETERRQ1(((PetscObject)ksp)->comm,PETSC_ERR_SUP,"Viewer type %s not supported for KSP Chebychev",((PetscObject)viewer)->type_name);
344:   }
345:   return(0);
346: }

350: PetscErrorCode KSPDestroy_Chebychev(KSP ksp)
351: {
352:   KSP_Chebychev  *cheb = (KSP_Chebychev*)ksp->data;

356:   KSPDestroy(&cheb->kspest);
357:   PCDestroy(&cheb->pcnone);
358:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPChebychevSetEigenvalues_C","",PETSC_NULL);
359:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPChebychevSetEstimateEigenvalues_C","",PETSC_NULL);
360:   KSPDefaultDestroy(ksp);
361:   return(0);
362: }

364: /*MC
365:      KSPCHEBYCHEV - The preconditioned Chebychev iterative method

367:    Options Database Keys:
368: +   -ksp_chebychev_eigenvalues <emin,emax> - set approximations to the smallest and largest eigenvalues
369:                   of the preconditioned operator. If these are accurate you will get much faster convergence.
370: -   -ksp_chebychev_estimate_eigenvalues <a,b,c,d> - estimate eigenvalues using a Krylov method, then use this
371:                   transform for Chebychev eigenvalue bounds (KSPChebychevSetEstimateEigenvalues)


374:    Level: beginner

376:    Notes: The Chebychev method requires both the matrix and preconditioner to 
377:           be symmetric positive (semi) definite.
378:           Only support for left preconditioning.

380: .seealso:  KSPCreate(), KSPSetType(), KSPType (for list of available types), KSP,
381:            KSPChebychevSetEigenvalues(), KSPRICHARDSON, KSPCG

383: M*/

388: PetscErrorCode  KSPCreate_Chebychev(KSP ksp)
389: {
391:   KSP_Chebychev  *chebychevP;

394:   PetscNewLog(ksp,KSP_Chebychev,&chebychevP);

396:   ksp->data                      = (void*)chebychevP;
397:   KSPSetSupportedNorm(ksp,KSP_NORM_PRECONDITIONED,PC_LEFT,2);
398:   KSPSetSupportedNorm(ksp,KSP_NORM_UNPRECONDITIONED,PC_LEFT,1);

400:   chebychevP->emin               = 1.e-2;
401:   chebychevP->emax               = 1.e+2;

403:   chebychevP->tform[0]           = 0.0;
404:   chebychevP->tform[1]           = 0.02;
405:   chebychevP->tform[1]           = 0;
406:   chebychevP->tform[2]           = 1.1;

408:   ksp->ops->setup                = KSPSetUp_Chebychev;
409:   ksp->ops->solve                = KSPSolve_Chebychev;
410:   ksp->ops->destroy              = KSPDestroy_Chebychev;
411:   ksp->ops->buildsolution        = KSPDefaultBuildSolution;
412:   ksp->ops->buildresidual        = KSPDefaultBuildResidual;
413:   ksp->ops->setfromoptions       = KSPSetFromOptions_Chebychev;
414:   ksp->ops->view                 = KSPView_Chebychev;

416:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPChebychevSetEigenvalues_C",
417:                                     "KSPChebychevSetEigenvalues_Chebychev",
418:                                     KSPChebychevSetEigenvalues_Chebychev);
419:   PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPChebychevSetEstimateEigenvalues_C",
420:                                     "KSPChebychevSetEstimateEigenvalues_Chebychev",
421:                                     KSPChebychevSetEstimateEigenvalues_Chebychev);
422:   return(0);
423: }