Actual source code: cheby.c
petsc-3.3-p5 2012-12-01
2: #include <petsc-private/kspimpl.h> /*I "petscksp.h" I*/
3: #include <../src/ksp/ksp/impls/cheby/chebyshevimpl.h>
7: PetscErrorCode KSPReset_Chebyshev(KSP ksp)
8: {
9: KSP_Chebyshev *cheb = (KSP_Chebyshev*)ksp->data;
13: KSPReset(cheb->kspest);
14: return(0);
15: }
19: PetscErrorCode KSPSetUp_Chebyshev(KSP ksp)
20: {
21: KSP_Chebyshev *cheb = (KSP_Chebyshev*)ksp->data;
25: KSPDefaultGetWork(ksp,3);
26: if (cheb->emin == 0. || cheb->emax == 0.) { /* We need to estimate eigenvalues */
27: KSPChebyshevSetEstimateEigenvalues(ksp,PETSC_DECIDE,PETSC_DECIDE,PETSC_DECIDE,PETSC_DECIDE);
28: }
29: return(0);
30: }
35: {
36: KSP_Chebyshev *chebyshevP = (KSP_Chebyshev*)ksp->data;
40: if (emax <= emin) SETERRQ2(((PetscObject)ksp)->comm,PETSC_ERR_ARG_INCOMP,"Maximum eigenvalue must be larger than minimum: max %g min %G",emax,emin);
41: 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);
42: chebyshevP->emax = emax;
43: chebyshevP->emin = emin;
44: KSPChebyshevSetEstimateEigenvalues(ksp,0.,0.,0.,0.); /* Destroy any estimation setup */
45: return(0);
46: }
51: {
52: KSP_Chebyshev *cheb = (KSP_Chebyshev*)ksp->data;
56: if (a != 0.0 || b != 0.0 || c != 0.0 || d != 0.0) {
57: if (!cheb->kspest) {
58: PetscBool nonzero;
60: KSPCreate(((PetscObject)ksp)->comm,&cheb->kspest);
61: PetscObjectIncrementTabLevel((PetscObject)cheb->kspest,(PetscObject)ksp,1);
63: KSPGetPC(cheb->kspest,&cheb->pcnone);
64: PetscObjectReference((PetscObject)cheb->pcnone);
65: PCSetType(cheb->pcnone,PCNONE);
66: KSPSetPC(cheb->kspest,ksp->pc);
68: KSPGetInitialGuessNonzero(ksp,&nonzero);
69: KSPSetInitialGuessNonzero(cheb->kspest,nonzero);
70: KSPSetComputeSingularValues(cheb->kspest,PETSC_TRUE);
72: /* Estimate with a fixed number of iterations */
73: KSPSetConvergenceTest(cheb->kspest,KSPSkipConverged,0,0);
74: KSPSetNormType(cheb->kspest,KSP_NORM_NONE);
75: KSPSetTolerances(cheb->kspest,PETSC_DEFAULT,PETSC_DEFAULT,PETSC_DEFAULT,10);
76: }
77: if (a >= 0) cheb->tform[0] = a;
78: if (b >= 0) cheb->tform[1] = b;
79: if (c >= 0) cheb->tform[2] = c;
80: if (d >= 0) cheb->tform[3] = d;
81: cheb->estimate_current = PETSC_FALSE;
82: } else {
83: KSPDestroy(&cheb->kspest);
84: PCDestroy(&cheb->pcnone);
85: }
86: return(0);
87: }
92: {
93: KSP_Chebyshev *cheb = (KSP_Chebyshev*)ksp->data;
96: cheb->estimate_current = PETSC_FALSE;
97: return(0);
98: }
102: /*@
103: KSPChebyshevSetEigenvalues - Sets estimates for the extreme eigenvalues
104: of the preconditioned problem.
106: Logically Collective on KSP
108: Input Parameters:
109: + ksp - the Krylov space context
110: - emax, emin - the eigenvalue estimates
112: Options Database:
113: . -ksp_chebyshev_eigenvalues emin,emax
115: Note: If you run with the Krylov method of KSPCG with the option -ksp_monitor_singular_value it will
116: for that given matrix and preconditioner an estimate of the extreme eigenvalues.
118: Level: intermediate
120: .keywords: KSP, Chebyshev, set, eigenvalues
121: @*/
122: PetscErrorCode KSPChebyshevSetEigenvalues(KSP ksp,PetscReal emax,PetscReal emin)
123: {
130: PetscTryMethod(ksp,"KSPChebyshevSetEigenvalues_C",(KSP,PetscReal,PetscReal),(ksp,emax,emin));
131: return(0);
132: }
136: /*@
137: KSPChebyshevSetEstimateEigenvalues - Automatically estimate the eigenvalues to use for Chebyshev
139: Logically Collective on KSP
141: Input Parameters:
142: + ksp - the Krylov space context
143: . a - multiple of min eigenvalue estimate to use for min Chebyshev bound (or PETSC_DECIDE)
144: . b - multiple of max eigenvalue estimate to use for min Chebyshev bound (or PETSC_DECIDE)
145: . c - multiple of min eigenvalue estimate to use for max Chebyshev bound (or PETSC_DECIDE)
146: - d - multiple of max eigenvalue estimate to use for max Chebyshev bound (or PETSC_DECIDE)
148: Options Database:
149: . -ksp_chebyshev_estimate_eigenvalues a,b,c,d
151: Notes:
152: The Chebyshev bounds are estimated using
153: .vb
154: minbound = a*minest + b*maxest
155: maxbound = c*minest + d*maxest
156: .ve
157: The default configuration targets the upper part of the spectrum for use as a multigrid smoother, so only the maximum eigenvalue estimate is used.
158: The minimum eigenvalue estimate obtained by Krylov iteration is typically not accurate until the method has converged.
160: If 0.0 is passed for all transform arguments (a,b,c,d), eigenvalue estimation is disabled.
162: The default transform is (0,0.1; 0,1.1) which targets the "upper" part of the spectrum, as desirable for use with multigrid.
164: Level: intermediate
166: .keywords: KSP, Chebyshev, set, eigenvalues, PCMG
167: @*/
168: PetscErrorCode KSPChebyshevSetEstimateEigenvalues(KSP ksp,PetscReal a,PetscReal b,PetscReal c,PetscReal d)
169: {
178: PetscTryMethod(ksp,"KSPChebyshevSetEstimateEigenvalues_C",(KSP,PetscReal,PetscReal,PetscReal,PetscReal),(ksp,a,b,c,d));
179: return(0);
180: }
184: /*@
185: KSPChebyshevSetNewMatrix - Indicates that the matrix has changed, causes eigenvalue estimates to be recomputed if appropriate.
187: Logically Collective on KSP
189: Input Parameter:
190: . ksp - the Krylov space context
192: Level: developer
194: .keywords: KSP, Chebyshev, set, eigenvalues
196: .seealso: KSPChebyshevSetEstimateEigenvalues()
197: @*/
198: PetscErrorCode KSPChebyshevSetNewMatrix(KSP ksp)
199: {
204: PetscTryMethod(ksp,"KSPChebyshevSetNewMatrix_C",(KSP),(ksp));
205: return(0);
206: }
210: PetscErrorCode KSPSetFromOptions_Chebyshev(KSP ksp)
211: {
212: KSP_Chebyshev *cheb = (KSP_Chebyshev*)ksp->data;
214: PetscInt two = 2,four = 4;
215: PetscReal tform[4] = {PETSC_DECIDE,PETSC_DECIDE,PETSC_DECIDE,PETSC_DECIDE};
216: PetscBool flg;
219: PetscOptionsHead("KSP Chebyshev Options");
220: PetscOptionsRealArray("-ksp_chebyshev_eigenvalues","extreme eigenvalues","KSPChebyshevSetEigenvalues",&cheb->emin,&two,0);
221: PetscOptionsRealArray("-ksp_chebyshev_estimate_eigenvalues","estimate eigenvalues using a Krylov method, then use this transform for Chebyshev eigenvalue bounds","KSPChebyshevSetEstimateEigenvalues",tform,&four,&flg);
222: if (flg) {
223: switch (four) {
224: case 0:
225: KSPChebyshevSetEstimateEigenvalues(ksp,PETSC_DECIDE,PETSC_DECIDE,PETSC_DECIDE,PETSC_DECIDE);
226: break;
227: case 2: /* Base everything on the max eigenvalues */
228: KSPChebyshevSetEstimateEigenvalues(ksp,PETSC_DECIDE,tform[0],PETSC_DECIDE,tform[1]);
229: break;
230: case 4: /* Use the full 2x2 linear transformation */
231: KSPChebyshevSetEstimateEigenvalues(ksp,tform[0],tform[1],tform[2],tform[3]);
232: break;
233: default: SETERRQ(((PetscObject)ksp)->comm,PETSC_ERR_ARG_INCOMP,"Must specify either 0, 2, or 4 parameters for eigenvalue estimation");
234: }
235: }
237: /*
238: Use hybrid Chebyshev.
239: Ref: "A hybrid Chebyshev Krylov-subspace algorithm for solving nonsymmetric systems of linear equations",
240: Howard Elman and Y. Saad and P. E. Saylor, SIAM Journal on Scientific and Statistical Computing, 1986.
241: */
242: PetscOptionsBool("-ksp_chebyshev_hybrid","Use hybrid Chebyshev","",cheb->hybrid,&cheb->hybrid,PETSC_NULL);
243: PetscOptionsInt("-ksp_chebyshev_hybrid_chebysteps","Number of Chebyshev steps in hybrid Chebyshev","",cheb->chebysteps,&cheb->chebysteps,PETSC_NULL);
244: PetscOptionsInt("-ksp_chebyshev_hybrid_purification","Use purification in hybrid Chebyshev","",cheb->purification,&cheb->purification,PETSC_NULL);
246: if (cheb->kspest) {
247: /* Mask the PC so that PCSetFromOptions does not do anything */
248: KSPSetPC(cheb->kspest,cheb->pcnone);
249: KSPSetOptionsPrefix(cheb->kspest,((PetscObject)ksp)->prefix);
250: KSPAppendOptionsPrefix(cheb->kspest,"est_");
251: if (!((PetscObject)cheb->kspest)->type_name) {
252: KSPSetType(cheb->kspest,KSPGMRES);
253: }
254: KSPSetFromOptions(cheb->kspest);
255: KSPSetPC(cheb->kspest,ksp->pc);
256: }
257: PetscOptionsTail();
258: return(0);
259: }
263: PetscErrorCode KSPSolve_Chebyshev(KSP ksp)
264: {
265: KSP_Chebyshev *cheb = (KSP_Chebyshev*)ksp->data;
267: PetscInt k,kp1,km1,maxit,ktmp,i;
268: PetscScalar alpha,omegaprod,mu,omega,Gamma,c[3],scale;
269: PetscReal rnorm = 0.0;
270: Vec sol_orig,b,p[3],r;
271: Mat Amat,Pmat;
272: MatStructure pflag;
273: PetscBool diagonalscale,hybrid=cheb->hybrid;
274: PetscInt purification=cheb->purification;
277: PCGetDiagonalScale(ksp->pc,&diagonalscale);
278: if (diagonalscale) SETERRQ1(((PetscObject)ksp)->comm,PETSC_ERR_SUP,"Krylov method %s does not support diagonal scaling",((PetscObject)ksp)->type_name);
280: if (cheb->kspest && !cheb->estimate_current) {
281: PetscReal max,min;
282: Vec X;
283:
284: if (hybrid && purification){
285: X = ksp->vec_sol;
286: } else {
287: X = ksp->work[0];
288: }
289: KSPSolve(cheb->kspest,ksp->vec_rhs,X);
290: if (hybrid){
291: cheb->its = 0; /* initialize Chebyshev iteration associated to kspest */
292: KSPSetInitialGuessNonzero(cheb->kspest,PETSC_TRUE);
293: } else {
294: if (ksp->guess_zero) {
295: VecZeroEntries(X);
296: }
297: }
298: KSPComputeExtremeSingularValues(cheb->kspest,&max,&min);
299: cheb->emin = cheb->tform[0]*min + cheb->tform[1]*max;
300: cheb->emax = cheb->tform[2]*min + cheb->tform[3]*max;
301: cheb->estimate_current = PETSC_TRUE;
302: }
304: ksp->its = 0;
305: PCGetOperators(ksp->pc,&Amat,&Pmat,&pflag);
306: maxit = ksp->max_it;
308: /* These three point to the three active solutions, we
309: rotate these three at each solution update */
310: km1 = 0; k = 1; kp1 = 2;
311: sol_orig = ksp->vec_sol; /* ksp->vec_sol will be asigned to rotating vector p[k], thus save its address */
312: b = ksp->vec_rhs;
313: p[km1] = sol_orig;
314: p[k] = ksp->work[0];
315: p[kp1] = ksp->work[1];
316: r = ksp->work[2];
318: /* use scale*B as our preconditioner */
319: scale = 2.0/(cheb->emax + cheb->emin);
321: /* -alpha <= scale*lambda(B^{-1}A) <= alpha */
322: alpha = 1.0 - scale*(cheb->emin);
323: Gamma = 1.0;
324: mu = 1.0/alpha;
325: omegaprod = 2.0/alpha;
327: c[km1] = 1.0;
328: c[k] = mu;
330: if (!ksp->guess_zero || (hybrid && cheb->its== 0)) {
331: KSP_MatMult(ksp,Amat,p[km1],r); /* r = b - A*p[km1] */
332: VecAYPX(r,-1.0,b);
333: } else {
334: VecCopy(b,r);
335: }
336:
337: KSP_PCApply(ksp,r,p[k]); /* p[k] = scale B^{-1}r + p[km1] */
338: VecAYPX(p[k],scale,p[km1]);
340: for (i=0; i<maxit; i++) {
341: PetscObjectTakeAccess(ksp);
342: if (hybrid && cheb->its && (cheb->its%cheb->chebysteps==0)){
343: /* Adaptive step: update eigenvalue estimate - does not seem to improve convergence */
344: PetscReal max,min;
345: Vec X = ksp->vec_sol; /* = previous p[k] */
346:
347: if (purification <= 1){ /* no purification here */
348: X = p[km1]; /* a tmp vector, != ksp->vec_sol */
349: }
351: VecCopy(p[k],X); /* p[k] = previous p[kp1] */
352: KSPSolve(cheb->kspest,ksp->vec_rhs,X);
353: KSPComputeExtremeSingularValues(cheb->kspest,&max,&min);
354: cheb->emin = cheb->tform[0]*min + cheb->tform[1]*max;
355: cheb->emax = cheb->tform[2]*min + cheb->tform[3]*max;
356: cheb->estimate_current = PETSC_TRUE;
357: if (purification <= 1){ /* no purification here */
358: X = ksp->vec_sol;
359: VecCopy(p[k],X);
360: }
362: b = ksp->vec_rhs;
363: p[km1] = X;
364: scale = 2.0/(cheb->emax + cheb->emin);
365: alpha = 1.0 - scale*(cheb->emin);
366: mu = 1.0/alpha;
367: omegaprod = 2.0/alpha;
368:
369: c[km1] = 1.0;
370: c[k] = mu;
371: KSP_MatMult(ksp,Amat,p[km1],r); /* r = b - A*p[km1] */
372: VecAYPX(r,-1.0,b);
374: KSP_PCApply(ksp,r,p[k]); /* p[k] = scale B^{-1}r + p[km1] */
375: VecAYPX(p[k],scale,p[km1]);
376: }
378: ksp->its++;
379: cheb->its++;
380: PetscObjectGrantAccess(ksp);
381: c[kp1] = 2.0*mu*c[k] - c[km1];
382: omega = omegaprod*c[k]/c[kp1];
384: KSP_MatMult(ksp,Amat,p[k],r); /* r = b - Ap[k] */
385: VecAYPX(r,-1.0,b);
386: KSP_PCApply(ksp,r,p[kp1]); /* p[kp1] = B^{-1}r */
388: /* calculate residual norm if requested */
389: if (ksp->normtype != KSP_NORM_NONE || ksp->numbermonitors) {
390: if (ksp->normtype == KSP_NORM_UNPRECONDITIONED) {VecNorm(r,NORM_2,&rnorm);}
391: else {VecNorm(p[kp1],NORM_2,&rnorm);}
392: PetscObjectTakeAccess(ksp);
393: ksp->rnorm = rnorm;
394: PetscObjectGrantAccess(ksp);
395: ksp->vec_sol = p[k];
396: KSPLogResidualHistory(ksp,rnorm);
397: KSPMonitor(ksp,i,rnorm);
398: (*ksp->converged)(ksp,i,rnorm,&ksp->reason,ksp->cnvP);
399: if (ksp->reason) break;
400: }
402: /* y^{k+1} = omega(y^{k} - y^{k-1} + Gamma*r^{k}) + y^{k-1} */
403: VecScale(p[kp1],omega*Gamma*scale);
404: VecAXPY(p[kp1],1.0-omega,p[km1]);
405: VecAXPY(p[kp1],omega,p[k]);
407: ktmp = km1;
408: km1 = k;
409: k = kp1;
410: kp1 = ktmp;
411: }
412: if (!ksp->reason) {
413: if (ksp->normtype != KSP_NORM_NONE) {
414: KSP_MatMult(ksp,Amat,p[k],r); /* r = b - Ap[k] */
415: VecAYPX(r,-1.0,b);
416: if (ksp->normtype == KSP_NORM_UNPRECONDITIONED) {
417: VecNorm(r,NORM_2,&rnorm);
418: } else {
419: KSP_PCApply(ksp,r,p[kp1]); /* p[kp1] = B^{-1}r */
420: VecNorm(p[kp1],NORM_2,&rnorm);
421: }
422: PetscObjectTakeAccess(ksp);
423: ksp->rnorm = rnorm;
424: PetscObjectGrantAccess(ksp);
425: ksp->vec_sol = p[k];
426: KSPLogResidualHistory(ksp,rnorm);
427: KSPMonitor(ksp,i,rnorm);
428: }
429: if (ksp->its >= ksp->max_it) {
430: if (ksp->normtype != KSP_NORM_NONE) {
431: (*ksp->converged)(ksp,i,rnorm,&ksp->reason,ksp->cnvP);
432: if (!ksp->reason) ksp->reason = KSP_DIVERGED_ITS;
433: } else {
434: ksp->reason = KSP_CONVERGED_ITS;
435: }
436: }
437: }
439: /* make sure solution is in vector x */
440: ksp->vec_sol = sol_orig;
441: if (k) {
442: VecCopy(p[k],sol_orig);
443: }
444: return(0);
445: }
449: PetscErrorCode KSPView_Chebyshev(KSP ksp,PetscViewer viewer)
450: {
451: KSP_Chebyshev *cheb = (KSP_Chebyshev*)ksp->data;
453: PetscBool iascii;
456: PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);
457: if (iascii) {
458: PetscViewerASCIIPrintf(viewer," Chebyshev: eigenvalue estimates: min = %G, max = %G\n",cheb->emin,cheb->emax);
459: if (cheb->kspest) {
460: PetscViewerASCIIPrintf(viewer," Chebyshev: estimated using: [%G %G; %G %G]\n",cheb->tform[0],cheb->tform[1],cheb->tform[2],cheb->tform[3]);
461: if (cheb->hybrid){ /* display info about hybrid options being used */
462: PetscViewerASCIIPrintf(viewer," Chebyshev: hybrid is used, chebysteps %D, purification %D\n",cheb->chebysteps,cheb->purification);
463: }
464: PetscViewerASCIIPushTab(viewer);
465: KSPView(cheb->kspest,viewer);
466: PetscViewerASCIIPopTab(viewer);
467: }
468: } else {
469: SETERRQ1(((PetscObject)ksp)->comm,PETSC_ERR_SUP,"Viewer type %s not supported for KSP Chebyshev",((PetscObject)viewer)->type_name);
470: }
471: return(0);
472: }
476: PetscErrorCode KSPDestroy_Chebyshev(KSP ksp)
477: {
478: KSP_Chebyshev *cheb = (KSP_Chebyshev*)ksp->data;
482: KSPDestroy(&cheb->kspest);
483: PCDestroy(&cheb->pcnone);
484: PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPChebyshevSetEigenvalues_C","",PETSC_NULL);
485: PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPChebyshevSetEstimateEigenvalues_C","",PETSC_NULL);
486: PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPChebyshevSetNewMatrix_C","",PETSC_NULL);
487: KSPDefaultDestroy(ksp);
488: return(0);
489: }
491: /*MC
492: KSPCHEBYSHEV - The preconditioned Chebyshev iterative method
494: Options Database Keys:
495: + -ksp_chebyshev_eigenvalues <emin,emax> - set approximations to the smallest and largest eigenvalues
496: of the preconditioned operator. If these are accurate you will get much faster convergence.
497: - -ksp_chebyshev_estimate_eigenvalues <a,b,c,d> - estimate eigenvalues using a Krylov method, then use this
498: transform for Chebyshev eigenvalue bounds (KSPChebyshevSetEstimateEigenvalues)
501: Level: beginner
503: Notes: The Chebyshev method requires both the matrix and preconditioner to
504: be symmetric positive (semi) definite.
505: Only support for left preconditioning.
507: Chebyshev is configured as a smoother by default, targetting the "upper" part of the spectrum.
508: The user should call KSPChebyshevSetEigenvalues() if they have eigenvalue estimates.
510: .seealso: KSPCreate(), KSPSetType(), KSPType (for list of available types), KSP,
511: KSPChebyshevSetEigenvalues(), KSPChebyshevSetEstimateEigenvalues(), KSPRICHARDSON, KSPCG, PCMG
513: M*/
518: {
520: KSP_Chebyshev *chebyshevP;
523: PetscNewLog(ksp,KSP_Chebyshev,&chebyshevP);
525: ksp->data = (void*)chebyshevP;
526: KSPSetSupportedNorm(ksp,KSP_NORM_PRECONDITIONED,PC_LEFT,2);
527: KSPSetSupportedNorm(ksp,KSP_NORM_UNPRECONDITIONED,PC_LEFT,1);
529: chebyshevP->emin = 0.;
530: chebyshevP->emax = 0.;
532: chebyshevP->tform[0] = 0.0;
533: chebyshevP->tform[1] = 0.1;
534: chebyshevP->tform[2] = 0;
535: chebyshevP->tform[3] = 1.1;
537: chebyshevP->hybrid = PETSC_FALSE;
538: chebyshevP->chebysteps = 20000;
539: chebyshevP->its = 0;
540: chebyshevP->purification = 0; /* no purification */
542: ksp->ops->setup = KSPSetUp_Chebyshev;
543: ksp->ops->solve = KSPSolve_Chebyshev;
544: ksp->ops->destroy = KSPDestroy_Chebyshev;
545: ksp->ops->buildsolution = KSPDefaultBuildSolution;
546: ksp->ops->buildresidual = KSPDefaultBuildResidual;
547: ksp->ops->setfromoptions = KSPSetFromOptions_Chebyshev;
548: ksp->ops->view = KSPView_Chebyshev;
549: ksp->ops->reset = KSPReset_Chebyshev;
551: PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPChebyshevSetEigenvalues_C",
552: "KSPChebyshevSetEigenvalues_Chebyshev",
553: KSPChebyshevSetEigenvalues_Chebyshev);
554: PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPChebyshevSetEstimateEigenvalues_C",
555: "KSPChebyshevSetEstimateEigenvalues_Chebyshev",
556: KSPChebyshevSetEstimateEigenvalues_Chebyshev);
557: PetscObjectComposeFunctionDynamic((PetscObject)ksp,"KSPChebyshevSetNewMatrix_C",
558: "KSPChebyshevSetNewMatrix_Chebyshev",
559: KSPChebyshevSetNewMatrix_Chebyshev);
560: return(0);
561: }