Actual source code: ngmres.c

  2: #include <../src/ksp/ksp/impls/ngmres/ngmresimpl.h>       /*I "petscksp.h" I*/

  4: static PetscErrorCode    BuildNGmresSoln(PetscScalar*,Vec,KSP,PetscInt,PetscInt);
  5: /*
  6:      KSPSetUp_NGMRES - Sets up the workspace needed by the NGMRES method. 

  8:       This is called once, usually automatically by KSPSolve() or KSPSetUp()
  9: */
 12: PetscErrorCode KSPSetUp_NGMRES(KSP ksp)
 13: {

 15:   PetscInt       hh;
 16:   PetscInt       msize;
 17:   KSP_NGMRES     *ngmres = (KSP_NGMRES*)ksp->data;
 19:   PetscBool      diagonalscale;

 22:   /* 
 23:        This implementation of NGMRES only handles left preconditioning
 24:      so generate an error otherwise.
 25:   */
 26:   if (ksp->pc_side == PC_RIGHT) SETERRQ(((PetscObject)ksp)->comm,PETSC_ERR_SUP,"No right preconditioning for KSPNGMRES");
 27:   else if (ksp->pc_side == PC_SYMMETRIC) SETERRQ(((PetscObject)ksp)->comm,PETSC_ERR_SUP,"No symmetric preconditioning for KSPNGMRES");
 28:   PCGetDiagonalScale(ksp->pc,&diagonalscale);
 29:   if (diagonalscale) SETERRQ1(((PetscObject)ksp)->comm,PETSC_ERR_SUP,"Krylov method %s does not support diagonal scaling",((PetscObject)ksp)->type_name);
 30:   ngmres->beta  = 1.0;
 31:   msize         = ngmres->msize;  /* restart size */
 32:   hh            = msize * msize;
 33: 
 34:   /*  PetscMalloc1(hh,PetscScalar,&ngmres->hh_origin,bb,PetscScalar,&ngmres->bb_origin,rs,PetscScalar,&ngmres->rs_origin,cc,PetscScalar,&ngmres->cc_origin,cc,PetscScalar,&ngmres->ss_origin); */
 35:   PetscMalloc2(hh,PetscScalar,&ngmres->hh_origin,msize,PetscScalar,&ngmres->nrs);

 37:   PetscMemzero(ngmres->hh_origin,hh*sizeof(PetscScalar));
 38:   PetscMemzero(ngmres->nrs,msize*sizeof(PetscScalar));

 40:   PetscLogObjectMemory(ksp,(hh+msize)*sizeof(PetscScalar));

 42:   KSPGetVecs(ksp,ngmres->msize,&ngmres->v,ngmres->msize*2,&ngmres->w);
 43:   VecDuplicateVecs(ngmres->w[0], ngmres->msize, &ngmres->q);
 44: 
 45:   KSPDefaultGetWork(ksp,3);
 46:   return(0);
 47: }

 49: /*
 50:        KSPSolve_NGMRES - This routine actually applies the method


 53:    Input Parameter:
 54: .     ksp - the Krylov space object that was set to use conjugate gradient, by, for 
 55:             example, KSPCreate(MPI_Comm,KSP *ksp); KSPSetType(ksp,KSPNGMRES);
 56: */
 59: PetscErrorCode  KSPSolve_NGMRES(KSP ksp)
 60: {
 62:   PetscInt       i,ivec,j,k,l,flag,it;
 63:   KSP_NGMRES    *ngmres = (KSP_NGMRES*)ksp->data;
 64:   Mat            Amat;
 65:   /*  Vec            X,F,R, B,Fold, Xold,temp,*dX = ngmres->w,*dF = ngmres->w+ngmres->msize; */
 66:   Vec            X,F,R, B,Fold, Xold,temp,*dX = ngmres->w,*dF = ngmres->w+ngmres->msize;
 67:   PetscScalar    *nrs=ngmres->nrs;
 68:   PetscReal      gnorm;
 69: 

 72:   X             = ksp->vec_sol;
 73:   B             = ksp->vec_rhs;
 74:   F             = ksp->work[0];
 75:   Fold          = ksp->work[1];
 76:   R             = ksp->work[2];
 77:   VecDuplicate(X,&Fold);
 78:   VecDuplicate(X,&Xold);
 79:   VecDuplicate(X,&temp);


 82:   PCGetOperators(ksp->pc,&Amat,PETSC_NULL,PETSC_NULL);

 84:   if (!ksp->guess_zero) {
 85:     KSP_MatMult(ksp,Amat,X,R);            /*     r <- b - Ax     */
 86:     VecAYPX(R,-1.0,B);
 87:   } else {
 88:     VecCopy(B,R);                         /*     r <- b (x is 0) */
 89:   }
 90:   PCApply(ksp->pc,R,F);                /*     F = B r */
 91:   if (ksp->normtype == KSP_NORM_UNPRECONDITIONED) {
 92:     VecNorm(R,NORM_2,&gnorm);
 93:   } else if (ksp->normtype == KSP_NORM_PRECONDITIONED) {
 94:     VecNorm(F,NORM_2,&gnorm);
 95:   } else SETERRQ(((PetscObject)ksp)->comm,PETSC_ERR_SUP,"NormType not supported");
 96:   KSPLogResidualHistory(ksp,gnorm);
 97:   KSPMonitor(ksp,0,gnorm);
 98:   (*ksp->converged)(ksp,0,gnorm,&ksp->reason,ksp->cnvP);


101:  /* for k=0 */

103:   k=0;
104:   ierr= VecCopy(X,Xold);  /* Xbar_0= X_0 */
105:   ierr= VecCopy(F,Fold);  /* Fbar_0 = f_0= B(b-Ax_0) */
106:   ierr= VecWAXPY(X, ngmres->beta, Fold, Xold);        /*X_1 = X_bar + beta * Fbar */

108:   /* to calculate f_1 */
109:   KSP_MatMult(ksp,Amat,X,R);            /*     r <- b - Ax     */
110:   VecAYPX(R,-1.0,B);
111:   PCApply(ksp->pc,R,F);                /*     F= f_1 = B(b-Ax) */

113:   /* calculate dX and dF for k=0 */
114:   ierr= VecWAXPY(dX[k],-1.0, Xold, X);  /* dX= X_1 - X_0 */
115:   ierr= VecWAXPY(dF[k],-1.0, Fold, F);  /* dF= f_1 - f_0 */
116: 
117: 
118:   ierr= VecCopy(X,Xold);  /* Xbar_0= X_0 */
119:   ierr= VecCopy(F,Fold);  /* Fbar_0 = f_0= B(b-Ax_0) */

121:   flag=0;

123:   for (k=1; k<ksp->max_it; k += 1) {  /* begin the iteration */
124:     l=ngmres->msize;
125:     if(k<l) {
126:       l=k;
127:       ivec=l;
128:     }else{
129:       ivec=l-1;
130:     }
131:     it=l-1;
132:     BuildNGmresSoln(nrs,Fold,ksp,it,flag);
133: 
134:     /* to obtain the solution at k+1 step */
135:     ierr= VecCopy(Xold,X);  /* X=Xold+Fold-(dX + dF) *nrd */
136:      ierr= VecAXPY(X,1.0,Fold);  /* X= Xold+Fold */
137:     for(i=0;i<l;i++){      /*X= Xold+Fold- (dX+dF*beta) *innerb */
138:       ierr= VecAXPY(X,-nrs[i], dX[i]);
139:       ierr= VecAXPY(X,-nrs[i]*ngmres->beta, dF[i]);
140:     }


143:     /* to test with GMRES */
144: #if 0
145:     ierr= VecCopy(Xold,temp);  /* X=Xold-(dX ) *nrd */
146:     for(i=0;i<l;i++){
147:       ierr= VecAXPY(temp,-nrs[i], dX[i]);
148:     }
149:     KSP_MatMult(ksp,Amat,temp,R);
150:     VecAYPX(R,-1.0,B);
151:     PCApply(ksp->pc,R,F);
152:      VecNorm(R,NORM_2,&gnorm);
153:      printf("gmres residual norm=%g\n",gnorm);
154: #endif

156:     /* to calculate f_k+1 */
157:     KSP_MatMult(ksp,Amat,X,R);            /*     r <- b - Ax     */
158:     VecAYPX(R,-1.0,B);
159:     PCApply(ksp->pc,R,F);                /*     F= f_1 = B(b-Ax) */
160: 

162:     /* check the convegence */
163: 
164:     if (ksp->normtype == KSP_NORM_UNPRECONDITIONED) {
165:         VecNorm(R,NORM_2,&gnorm);
166:       } else if (ksp->normtype == KSP_NORM_PRECONDITIONED) {
167:         VecNorm(F,NORM_2,&gnorm);
168:       } else SETERRQ(((PetscObject)ksp)->comm,PETSC_ERR_SUP,"NormType not supported");
169:       KSPLogResidualHistory(ksp,gnorm);

171:       KSPMonitor(ksp,k,gnorm);
172:       ksp->its=k;
173:       (*ksp->converged)(ksp,k,gnorm,&ksp->reason,ksp->cnvP);
174:       if (ksp->reason) return(0);


177:     /* calculate dX and dF for k=0 */
178:       if( k>ivec) {/* we need to replace the old vectors */
179:         flag=1;
180:         for(i=0;i<it;i++){
181:           ierr= VecCopy(dX[i+1],dX[i]);  /* X=Xold+Fold-(dX + dF) *nrd */
182:           ierr= VecCopy(dF[i+1],dF[i]);  /* X=Xold+Fold-(dX + dF) *nrd */
183:           for(j=0;j<l;j++)
184:             *HH(j,i)=*HH(j,i+1);
185:         }
186:       }
187:       ierr= VecWAXPY(dX[ivec],-1.0, Xold, X);  /* dX= X_1 - X_0 */
188:       ierr= VecWAXPY(dF[ivec],-1.0, Fold, F);  /* dF= f_1 - f_0 */
189:       ierr= VecCopy(X,Xold);
190:       ierr= VecCopy(F,Fold);
191: 
192:   }
193:   ksp->reason = KSP_DIVERGED_ITS;
194:   return(0);
195: }
198: PetscErrorCode KSPReset_NGMRES(KSP ksp)
199: {
200:   KSP_NGMRES    *ngmres = (KSP_NGMRES*)ksp->data;

204:   VecDestroyVecs(ngmres->msize,&ngmres->v);
205:   VecDestroyVecs(ngmres->msize,&ngmres->w);
206:   VecDestroyVecs(ngmres->msize,&ngmres->q);
207:   return(0);
208: }

210: /*
211:        KSPDestroy_NGMRES - Frees all memory space used by the Krylov method

213: */
216: PetscErrorCode KSPDestroy_NGMRES(KSP ksp)
217: {

221:   KSPReset_NGMRES(ksp);
222:   KSPDefaultDestroy(ksp);
223:   return(0);
224: }

226: /*
227:      KSPView_NGMRES - Prints information about the current Krylov method being used

229:       Currently this only prints information to a file (or stdout) about the 
230:       symmetry of the problem. If your Krylov method has special options or 
231:       flags that information should be printed here.

233: */
236: PetscErrorCode KSPView_NGMRES(KSP ksp,PetscViewer viewer)
237: {
238:   KSP_NGMRES    *ngmres = (KSP_NGMRES *)ksp->data;
240:   PetscBool      iascii;

243:   PetscTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);
244:   if (iascii) {
245:     PetscViewerASCIIPrintf(viewer,"  Size of space %d\n",ngmres->msize);
246:   } else {
247:     SETERRQ1(((PetscObject)ksp)->comm,PETSC_ERR_SUP,"Viewer type %s not supported for KSP ngmres",((PetscObject)viewer)->type_name);
248:   }
249:   return(0);
250: }

252: /*
253:     KSPSetFromOptions_NGMRES - Checks the options database for options related to the  method
254: */
257: PetscErrorCode KSPSetFromOptions_NGMRES(KSP ksp)
258: {
260:   KSP_NGMRES    *ngmres = (KSP_NGMRES *)ksp->data;

263:   PetscOptionsHead("KSP NGMRES options");
264:     PetscOptionsInt("-ksp_ngmres_restart","Number of directions","None",ngmres->msize,&ngmres->msize,PETSC_NULL);
265:   PetscOptionsTail();
266:   return(0);
267: }

269: /*
270:     KSPCreate_NGMRES - Creates the data structure for the Krylov method NGMRES and sets the 
271:        function pointers for all the routines it needs to call (KSPSolve_NGMRES() etc)

274: */
275: /*MC
276:      KSPNGMRES - The nonlinear generalized minimum residual (NGMRES) iterative method

278:    Level: beginner

280:    Notes: Supports only left preconditioning

282: .seealso:  KSPCreate(), KSPSetType(), KSPType (for list of available types), KSP

284: M*/
288: PetscErrorCode  KSPCreate_NGMRES(KSP ksp)
289: {
291:   KSP_NGMRES     *ngmres;

294:   PetscNewLog(ksp,KSP_NGMRES,&ngmres);
295:   ksp->data = (void*)ngmres;
296:   ngmres->msize = 30;
297:   ngmres->csize = 0;

299:   KSPSetSupportedNorm(ksp,KSP_NORM_PRECONDITIONED,PC_LEFT,2);
300:   KSPSetSupportedNorm(ksp,KSP_NORM_UNPRECONDITIONED,PC_LEFT,1);

302:   /*
303:        Sets the functions that are associated with this data structure 
304:        (in C++ this is the same as defining virtual functions)
305:   */
306:   ksp->ops->setup                = KSPSetUp_NGMRES;
307:   ksp->ops->solve                = KSPSolve_NGMRES;
308:   ksp->ops->reset                = KSPReset_NGMRES;
309:   ksp->ops->destroy              = KSPDestroy_NGMRES;
310:   ksp->ops->view                 = KSPView_NGMRES;
311:   ksp->ops->setfromoptions       = KSPSetFromOptions_NGMRES;
312:   ksp->ops->buildsolution        = KSPDefaultBuildSolution;
313:   ksp->ops->buildresidual        = KSPDefaultBuildResidual;
314:   return(0);
315: }




321: /*
322:     BuildNGmresSoln - create the solution of the least square problem to determine the coefficients

324:     Input parameters:
325:         nrs - the solution
326:         Fold  - the RHS vector
327:         flag - =1, we need to replace the old vector by new one, =0, we still add new vector 
328:      This is an internal routine that knows about the NGMRES internals.
329:  */
332: static PetscErrorCode BuildNGmresSoln(PetscScalar* nrs, Vec Fold, KSP ksp,PetscInt it, PetscInt flag)
333: {
334:   PetscScalar    tt,temps;
336:   PetscInt       i,ii,j,l;
337:   KSP_NGMRES      *ngmres = (KSP_NGMRES *)(ksp->data);
338:   /* Vec *dF=ngmres->w+ngmres->msize, *Q=ngmres->w+ngmres->msize*2,temp; */
339:   Vec *dF=ngmres->w+ngmres->msize,*Q=ngmres->q,temp;
340:   PetscReal      gam,areal;
341:   PetscScalar    a,b,c,s;
342: 
344:   VecDuplicate(Fold,&temp);
345:   l=it+1;
346: 
347:   /* Solve for solution vector that minimizes the residual */

349:   if(flag==1) { /* we need to replace the old vector and need to modify the QR factors, use Givens rotation */
350:       for(i=0;i<it;i++){
351:         /* calculate the Givens rotation */
352:         a=*HH(i,i);
353:         b=*HH(i+1,i);
354:         gam=1.0/PetscRealPart(PetscSqrtScalar(PetscConj(a)*a + PetscConj(b)*b));
355:         c= a*gam;
356:         s= b*gam;
357: 
358: #if defined(PETSC_USE_COMPLEX)
359:         /* update the Q factor */
360:         ierr= VecCopy(Q[i],temp);
361:         VecAXPBY(temp,s,PetscConj(c),Q[i+1]); /*temp= c*Q[i]+s*Q[i+1] */
362:         VecAXPBY(Q[i+1],-s,c,Q[i]); /* Q[i+1]= -s*Q[i] + c*Q[i+1] */
363:         ierr= VecCopy(temp,Q[i]);    /* Q[i]= c*Q[i] + s*Q[i+1] */
364:         /* update the R factor */
365:         for(j=0;j<l;j++){
366:           a= *HH(i,j);
367:           b=*HH(i+1,j);
368:           temps=PetscConj(c)* a+s* b;
369:           *HH(i+1,j)=-s*a+c*b;
370:           *HH(i,j)=temps;
371:         }
372: #else
373:         /* update the Q factor */
374:         ierr= VecCopy(Q[i],temp);
375:         VecAXPBY(temp,s,c,Q[i+1]); /*temp= c*Q[i]+s*Q[i+1] */
376:         VecAXPBY(Q[i+1],-s,c,Q[i]); /* Q[i+1]= -s*Q[i] + c*Q[i+1] */
377:         ierr= VecCopy(temp,Q[i]);    /* Q[i]= c*Q[i] + s*Q[i+1] */
378:         /* update the R factor */
379:         for(j=0;j<l;j++){
380:           a= *HH(i,j);
381:           b=*HH(i+1,j);
382:           temps=c* a+s* b;
383:           *HH(i+1,j)=-s*a+c*b;
384:           *HH(i,j)=temps;
385:         }
386: #endif 
387:       }
388:     }

390:     /* add a new vector, use modified Gram-Schmidt */
391:     ierr= VecCopy(dF[it],temp);
392:     for(i=0;i<it;i++){
393:       ierr=VecDot(temp,Q[i],HH(i,it)); /* h(i,l-1)= dF[l-1]'*Q[i] */
394:       VecAXPBY(temp,-*HH(i,it),1.0,Q[i]); /* temp= temp- h(i,l-1)*Q[i] */
395:     }
396:     ierr=VecCopy(temp,Q[it]);
397:     ierr=VecNormalize(Q[it],&areal);
398:     *HH(it,it) = areal;
399: 


402:     /* modify the RHS with Q'*Fold*/
403: 
404:   for(i=0;i<l;i++)
405:           ierr=VecDot(Fold,Q[i],ngmres->nrs+i); /* nrs= Fold'*Q[i] */
406: 
407:     /* start backsubstitution to solve the least square problem */

409:      if (*HH(it,it) != 0.0) {
410:       nrs[it] =  nrs[it]/ *HH(it,it);
411:     } else {
412:     ksp->reason = KSP_DIVERGED_BREAKDOWN;
413:     PetscInfo2(ksp,"Likely your matrix or preconditioner is singular. HH(it,it) is identically zero; it = %D nrs(it) = %G",it,10);
414:     return(0);
415:   }
416:   for (ii=1; ii<=it; ii++) {
417:     i   = it - ii;
418:     tt  = nrs[i];
419:     for (j=i+1; j<=it; j++) tt  = tt - *HH(i,j) * nrs[j];
420:     if (*HH(i,i) == 0.0) {
421:       ksp->reason = KSP_DIVERGED_BREAKDOWN;
422:       PetscInfo1(ksp,"Likely your matrix or preconditioner is singular. HH(k,k) is identically zero; i = %D",i);
423:       return(0);
424:     }
425:     nrs[i]   = tt / *HH(i,i);
426:   }

428: 
429:   return(0);
430: }