xref: /petsc/src/snes/impls/ngmres/snesngmres.c (revision d45987f3cb8da7dca4e7c09f0fedfc3d8f6afad7)
1 #include <../src/snes/impls/ngmres/snesngmres.h> /*I "petscsnes.h" I*/
2 #include <petscblaslapack.h>
3 
4 const char *const SNESNGMRESRestartTypes[] = {"NONE","PERIODIC","DIFFERENCE","SNESNGMRESRestartType","SNES_NGMRES_RESTART_",0};
5 const char *const SNESNGMRESSelectTypes[] = {"NONE","DIFFERENCE","LINESEARCH","SNESNGMRESSelectType","SNES_NGMRES_SELECT_",0};
6 
7 #undef __FUNCT__
8 #define __FUNCT__ "SNESReset_NGMRES"
9 PetscErrorCode SNESReset_NGMRES(SNES snes)
10 {
11   SNES_NGMRES   *ngmres = (SNES_NGMRES*) snes->data;
12   PetscErrorCode ierr;
13 
14   PetscFunctionBegin;
15   ierr = VecDestroyVecs(ngmres->msize, &ngmres->Fdot);CHKERRQ(ierr);
16   ierr = VecDestroyVecs(ngmres->msize, &ngmres->Xdot);CHKERRQ(ierr);
17   ierr = SNESLineSearchDestroy(&ngmres->additive_linesearch);CHKERRQ(ierr);
18 
19   PetscFunctionReturn(0);
20 }
21 
22 #undef __FUNCT__
23 #define __FUNCT__ "SNESDestroy_NGMRES"
24 PetscErrorCode SNESDestroy_NGMRES(SNES snes)
25 {
26   PetscErrorCode ierr;
27   SNES_NGMRES *ngmres = (SNES_NGMRES*)snes->data;
28 
29   PetscFunctionBegin;
30   ierr = SNESReset_NGMRES(snes);CHKERRQ(ierr);
31   ierr = PetscFree5(ngmres->h, ngmres->beta, ngmres->xi, ngmres->fnorms, ngmres->q);CHKERRQ(ierr);
32   ierr = PetscFree(ngmres->s);CHKERRQ(ierr);
33   ierr = PetscFree(ngmres->xnorms);CHKERRQ(ierr);
34 #if PETSC_USE_COMPLEX
35   ierr = PetscFree(ngmres->rwork);
36 #endif
37   ierr = PetscFree(ngmres->work);
38   ierr = PetscFree(snes->data);
39   PetscFunctionReturn(0);
40 }
41 
42 #undef __FUNCT__
43 #define __FUNCT__ "SNESSetUp_NGMRES"
44 PetscErrorCode SNESSetUp_NGMRES(SNES snes)
45 {
46   SNES_NGMRES    *ngmres = (SNES_NGMRES *) snes->data;
47   const char     *optionsprefix;
48   PetscInt       msize,hsize;
49   PetscErrorCode ierr;
50 
51   PetscFunctionBegin;
52   ierr = SNESDefaultGetWork(snes,5);CHKERRQ(ierr);
53   if (!ngmres->Xdot) {ierr = VecDuplicateVecs(snes->vec_sol,ngmres->msize,&ngmres->Xdot);CHKERRQ(ierr);}
54   if (!ngmres->Fdot) {ierr = VecDuplicateVecs(snes->vec_sol,ngmres->msize,&ngmres->Fdot);CHKERRQ(ierr);}
55   if (!ngmres->setup_called) {
56     msize         = ngmres->msize;  /* restart size */
57     hsize         = msize * msize;
58 
59     /* explicit least squares minimization solve */
60     ierr = PetscMalloc5(hsize,PetscScalar,&ngmres->h,
61                         msize,PetscScalar,&ngmres->beta,
62                         msize,PetscScalar,&ngmres->xi,
63                         msize,PetscReal,  &ngmres->fnorms,
64                         hsize,PetscScalar,&ngmres->q);CHKERRQ(ierr);
65     if (ngmres->singlereduction) {
66       ierr = PetscMalloc(msize*sizeof(PetscReal),&ngmres->xnorms);CHKERRQ(ierr);
67     }
68     ngmres->nrhs = 1;
69     ngmres->lda = msize;
70     ngmres->ldb = msize;
71     ierr = PetscMalloc(msize*sizeof(PetscScalar),&ngmres->s);CHKERRQ(ierr);
72     ierr = PetscMemzero(ngmres->h,   hsize*sizeof(PetscScalar));CHKERRQ(ierr);
73     ierr = PetscMemzero(ngmres->q,   hsize*sizeof(PetscScalar));CHKERRQ(ierr);
74     ierr = PetscMemzero(ngmres->xi,  msize*sizeof(PetscScalar));CHKERRQ(ierr);
75     ierr = PetscMemzero(ngmres->beta,msize*sizeof(PetscScalar));CHKERRQ(ierr);
76     ngmres->lwork = 12*msize;
77 #if PETSC_USE_COMPLEX
78     ierr = PetscMalloc(sizeof(PetscReal)*ngmres->lwork,&ngmres->rwork);
79 #endif
80     ierr = PetscMalloc(sizeof(PetscScalar)*ngmres->lwork,&ngmres->work);
81   }
82 
83   /* linesearch setup */
84   ierr = SNESGetOptionsPrefix(snes, &optionsprefix);CHKERRQ(ierr);
85 
86   if (ngmres->select_type == SNES_NGMRES_SELECT_LINESEARCH) {
87     ierr = SNESLineSearchCreate(((PetscObject)snes)->comm, &ngmres->additive_linesearch);CHKERRQ(ierr);
88     ierr = SNESLineSearchSetSNES(ngmres->additive_linesearch, snes);CHKERRQ(ierr);
89     ierr = SNESLineSearchSetType(ngmres->additive_linesearch, SNESLINESEARCHL2);CHKERRQ(ierr);
90     ierr = SNESLineSearchAppendOptionsPrefix(ngmres->additive_linesearch, "additive_");CHKERRQ(ierr);
91     ierr = SNESLineSearchAppendOptionsPrefix(ngmres->additive_linesearch, optionsprefix);CHKERRQ(ierr);
92     ierr = SNESLineSearchSetFromOptions(ngmres->additive_linesearch);CHKERRQ(ierr);
93   }
94 
95   ngmres->setup_called = PETSC_TRUE;
96   PetscFunctionReturn(0);
97 }
98 
99 #undef __FUNCT__
100 #define __FUNCT__ "SNESSetFromOptions_NGMRES"
101 PetscErrorCode SNESSetFromOptions_NGMRES(SNES snes)
102 {
103   SNES_NGMRES   *ngmres = (SNES_NGMRES *) snes->data;
104   PetscErrorCode ierr;
105   PetscBool      debug;
106   SNESLineSearch linesearch;
107   PetscFunctionBegin;
108   ierr = PetscOptionsHead("SNES NGMRES options");CHKERRQ(ierr);
109   ierr = PetscOptionsEnum("-snes_ngmres_select_type","Select type","SNESNGMRESSetSelectType",SNESNGMRESSelectTypes,
110                           (PetscEnum)ngmres->select_type,(PetscEnum*)&ngmres->select_type,PETSC_NULL);CHKERRQ(ierr);
111   ierr = PetscOptionsEnum("-snes_ngmres_restart_type","Restart type","SNESNGMRESSetRestartType",SNESNGMRESRestartTypes,
112                           (PetscEnum)ngmres->restart_type,(PetscEnum*)&ngmres->restart_type,PETSC_NULL);CHKERRQ(ierr);
113   ierr = PetscOptionsBool("-snes_ngmres_anderson", "Use Anderson mixing storage",        "SNES", ngmres->anderson,  &ngmres->anderson, PETSC_NULL);CHKERRQ(ierr);
114   ierr = PetscOptionsInt("-snes_ngmres_m",         "Number of directions",               "SNES", ngmres->msize,  &ngmres->msize, PETSC_NULL);CHKERRQ(ierr);
115   ierr = PetscOptionsInt("-snes_ngmres_restart",   "Iterations before forced restart",   "SNES", ngmres->restart_periodic,  &ngmres->restart_periodic, PETSC_NULL);CHKERRQ(ierr);
116   ierr = PetscOptionsInt("-snes_ngmres_restart_it","Tolerance iterations before restart","SNES", ngmres->restart_it,  &ngmres->restart_it, PETSC_NULL);CHKERRQ(ierr);
117   ierr = PetscOptionsBool("-snes_ngmres_monitor",  "Monitor actions of NGMRES",          "SNES", ngmres->monitor ? PETSC_TRUE: PETSC_FALSE, &debug, PETSC_NULL);CHKERRQ(ierr);
118   if (debug) {
119     ngmres->monitor = PETSC_VIEWER_STDOUT_(((PetscObject)snes)->comm);CHKERRQ(ierr);
120   }
121   ierr = PetscOptionsReal("-snes_ngmres_gammaA",   "Residual selection constant",   "SNES", ngmres->gammaA, &ngmres->gammaA, PETSC_NULL);CHKERRQ(ierr);
122   ierr = PetscOptionsReal("-snes_ngmres_gammaC", "  Residual restart constant",     "SNES", ngmres->gammaC, &ngmres->gammaC, PETSC_NULL);CHKERRQ(ierr);
123   ierr = PetscOptionsReal("-snes_ngmres_epsilonB", "Difference selection constant", "SNES", ngmres->epsilonB, &ngmres->epsilonB, PETSC_NULL);CHKERRQ(ierr);
124   ierr = PetscOptionsReal("-snes_ngmres_deltaB",   "Difference residual selection constant", "SNES", ngmres->deltaB, &ngmres->deltaB, PETSC_NULL);CHKERRQ(ierr);
125   ierr = PetscOptionsBool("-snes_ngmres_single_reduction", "Aggregate reductions",  "SNES", ngmres->singlereduction, &ngmres->singlereduction, PETSC_NULL);CHKERRQ(ierr);
126   ierr = PetscOptionsTail();CHKERRQ(ierr);
127   if ((ngmres->gammaA > ngmres->gammaC) && (ngmres->gammaC > 2.)) ngmres->gammaC = ngmres->gammaA;
128 
129   /* set the default type of the line search if the user hasn't already. */
130   if (!snes->linesearch) {
131     ierr = SNESGetSNESLineSearch(snes, &linesearch);CHKERRQ(ierr);
132     ierr = SNESLineSearchSetType(linesearch, SNESLINESEARCHBASIC);CHKERRQ(ierr);
133   }
134   PetscFunctionReturn(0);
135 }
136 
137 #undef __FUNCT__
138 #define __FUNCT__ "SNESView_NGMRES"
139 PetscErrorCode SNESView_NGMRES(SNES snes, PetscViewer viewer)
140 {
141   SNES_NGMRES   *ngmres = (SNES_NGMRES *) snes->data;
142   PetscBool      iascii;
143   PetscErrorCode ierr;
144 
145   PetscFunctionBegin;
146   ierr = PetscObjectTypeCompare((PetscObject) viewer, PETSCVIEWERASCII, &iascii);CHKERRQ(ierr);
147   if (iascii) {
148 
149     ierr = PetscViewerASCIIPrintf(viewer, "  Number of stored past updates: %d\n", ngmres->msize);CHKERRQ(ierr);
150     ierr = PetscViewerASCIIPrintf(viewer, "  Residual selection: gammaA=%1.0e, gammaC=%1.0e\n", ngmres->gammaA, ngmres->gammaC);CHKERRQ(ierr);
151     ierr = PetscViewerASCIIPrintf(viewer, "  Difference restart: epsilonB=%1.0e, deltaB=%1.0e\n", ngmres->epsilonB, ngmres->deltaB);CHKERRQ(ierr);
152   }
153   PetscFunctionReturn(0);
154 }
155 
156 #undef __FUNCT__
157 #define __FUNCT__ "SNESSolve_NGMRES"
158 
159 PetscErrorCode SNESSolve_NGMRES(SNES snes)
160 {
161   SNES_NGMRES        *ngmres = (SNES_NGMRES *) snes->data;
162   /* present solution, residual, and preconditioned residual */
163   Vec                 X, F, B, D, Y;
164 
165   /* candidate linear combination answers */
166   Vec                 XA, FA, XM, FM, FPC;
167 
168   /* previous iterations to construct the subspace */
169   Vec                 *Fdot = ngmres->Fdot;
170   Vec                 *Xdot = ngmres->Xdot;
171 
172   /* coefficients and RHS to the minimization problem */
173   PetscScalar         *beta = ngmres->beta;
174   PetscScalar         *xi   = ngmres->xi;
175   PetscReal           fnorm, fMnorm, fAnorm;
176   PetscReal           nu;
177   PetscScalar         alph_total = 0.;
178   PetscInt            i, j, k, k_restart, l, ivec, restart_count = 0;
179 
180   /* solution selection data */
181   PetscBool           selectA, selectRestart;
182   PetscReal           dnorm, dminnorm = 0.0, dcurnorm;
183   PetscReal           fminnorm,xnorm,ynorm;
184 
185   SNESConvergedReason reason;
186   PetscBool           lssucceed,changed_y,changed_w;
187   PetscErrorCode      ierr;
188 
189   PetscFunctionBegin;
190   /* variable initialization */
191   snes->reason  = SNES_CONVERGED_ITERATING;
192   X             = snes->vec_sol;
193   F             = snes->vec_func;
194   B             = snes->vec_rhs;
195   XA            = snes->vec_sol_update;
196   FA            = snes->work[0];
197   D             = snes->work[1];
198 
199   /* work for the line search */
200   Y             = snes->work[2];
201   XM            = snes->work[3];
202   FM            = snes->work[4];
203 
204   ierr = PetscObjectTakeAccess(snes);CHKERRQ(ierr);
205   snes->iter = 0;
206   snes->norm = 0.;
207   ierr = PetscObjectGrantAccess(snes);CHKERRQ(ierr);
208 
209   /* initialization */
210 
211   /* r = F(x) */
212   if (!snes->vec_func_init_set) {
213     ierr = SNESComputeFunction(snes, X, F);CHKERRQ(ierr);
214     if (snes->domainerror) {
215       snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
216       PetscFunctionReturn(0);
217     }
218   } else {
219     snes->vec_func_init_set = PETSC_FALSE;
220   }
221 
222   if (!snes->norm_init_set) {
223     ierr = VecNorm(F, NORM_2, &fnorm);CHKERRQ(ierr);
224     if (PetscIsInfOrNanReal(fnorm)) SETERRQ(((PetscObject)snes)->comm, PETSC_ERR_FP, "Infinite or not-a-number generated in function evaluation");
225   } else {
226     fnorm = snes->norm_init;
227     snes->norm_init_set = PETSC_FALSE;
228   }
229   fminnorm = fnorm;
230   /* nu = (r, r) */
231   nu = fnorm*fnorm;
232 
233   /* q_{00} = nu  */
234   Q(0,0) = nu;
235   ngmres->fnorms[0] = fnorm;
236   /* Fdot[0] = F */
237   ierr = VecCopy(X, Xdot[0]);CHKERRQ(ierr);
238   ierr = VecCopy(F, Fdot[0]);CHKERRQ(ierr);
239 
240   ierr = PetscObjectTakeAccess(snes);CHKERRQ(ierr);
241   snes->norm = fnorm;
242   ierr = PetscObjectGrantAccess(snes);CHKERRQ(ierr);
243   SNESLogConvHistory(snes, fnorm, 0);
244   ierr = SNESMonitor(snes, 0, fnorm);CHKERRQ(ierr);
245   ierr = (*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP);CHKERRQ(ierr);
246   if (snes->reason) PetscFunctionReturn(0);
247 
248   k_restart = 1;
249   l = 1;
250   for (k=1; k < snes->max_its+1; k++) {
251     /* select which vector of the stored subspace will be updated */
252     ivec = k_restart % ngmres->msize; /* replace the last used part of the subspace */
253 
254     /* Computation of x^M */
255     if (snes->pc) {
256       ierr = VecCopy(X, XM);CHKERRQ(ierr);
257       ierr = SNESSetInitialFunction(snes->pc, F);CHKERRQ(ierr);
258       ierr = SNESSetInitialFunctionNorm(snes->pc, fnorm);CHKERRQ(ierr);
259       ierr = SNESSolve(snes->pc, B, XM);CHKERRQ(ierr);
260       ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
261       if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
262         snes->reason = SNES_DIVERGED_INNER;
263         PetscFunctionReturn(0);
264       }
265       ierr = SNESGetFunction(snes->pc, &FPC, PETSC_NULL, PETSC_NULL);CHKERRQ(ierr);
266       ierr = VecCopy(FPC, FM);CHKERRQ(ierr);
267       ierr = SNESGetFunctionNorm(snes->pc, &fMnorm);CHKERRQ(ierr);
268     } else {
269       /* no preconditioner -- just take gradient descent with line search */
270       ierr = VecCopy(F, Y);CHKERRQ(ierr);
271       ierr = VecCopy(F, FM);CHKERRQ(ierr);
272       ierr = VecCopy(X, XM);CHKERRQ(ierr);
273       fMnorm = fnorm;
274       ierr = SNESLineSearchApply(snes->linesearch,XM,FM,&fMnorm,Y);CHKERRQ(ierr);
275       ierr = SNESLineSearchGetSuccess(snes->linesearch, &lssucceed);CHKERRQ(ierr);
276       if (!lssucceed) {
277         if (++snes->numFailures >= snes->maxFailures) {
278           snes->reason = SNES_DIVERGED_LINE_SEARCH;
279           PetscFunctionReturn(0);
280         }
281       }
282     }
283 
284     /* r = F(x) */
285     nu = fMnorm*fMnorm;
286     if (fminnorm > fMnorm) fminnorm = fMnorm;  /* the minimum norm is now of F^M */
287 
288     /* construct the right hand side and xi factors */
289     ierr = VecMDot(FM, l, Fdot, xi);CHKERRQ(ierr);
290 
291     for (i = 0; i < l; i++) {
292       beta[i] = nu - xi[i];
293     }
294 
295     /* construct h */
296     for (j = 0; j < l; j++) {
297       for (i = 0; i < l; i++) {
298         H(i, j) = Q(i, j) - xi[i] - xi[j] + nu;
299       }
300     }
301 
302     if(l == 1) {
303       /* simply set alpha[0] = beta[0] / H[0, 0] */
304       if (H(0, 0) != 0.) {
305         beta[0] = beta[0] / H(0, 0);
306       } else {
307         beta[0] = 0.;
308       }
309     } else {
310 #ifdef PETSC_MISSING_LAPACK_GELSS
311       SETERRQ(((PetscObject)snes)->comm, PETSC_ERR_SUP, "NGMRES with LS requires the LAPACK GELSS routine.");
312 #else
313     ngmres->m = PetscBLASIntCast(l);
314     ngmres->n = PetscBLASIntCast(l);
315     ngmres->info = PetscBLASIntCast(0);
316     ngmres->rcond = -1.;
317     ierr = PetscFPTrapPush(PETSC_FP_TRAP_OFF);CHKERRQ(ierr);
318 #ifdef PETSC_USE_COMPLEX
319     LAPACKgelss_(&ngmres->m,
320                  &ngmres->n,
321                  &ngmres->nrhs,
322                  ngmres->h,
323                  &ngmres->lda,
324                  ngmres->beta,
325                  &ngmres->ldb,
326                  ngmres->s,
327                  &ngmres->rcond,
328                  &ngmres->rank,
329                  ngmres->work,
330                  &ngmres->lwork,
331                  ngmres->rwork,
332                  &ngmres->info);
333 #else
334     LAPACKgelss_(&ngmres->m,
335                  &ngmres->n,
336                  &ngmres->nrhs,
337                  ngmres->h,
338                  &ngmres->lda,
339                  ngmres->beta,
340                  &ngmres->ldb,
341                  ngmres->s,
342                  &ngmres->rcond,
343                  &ngmres->rank,
344                  ngmres->work,
345                  &ngmres->lwork,
346                  &ngmres->info);
347 #endif
348     ierr = PetscFPTrapPop();CHKERRQ(ierr);
349     if (ngmres->info < 0) SETERRQ(((PetscObject)snes)->comm,PETSC_ERR_LIB,"Bad argument to GELSS");
350     if (ngmres->info > 0) SETERRQ(((PetscObject)snes)->comm,PETSC_ERR_LIB,"SVD failed to converge");
351 #endif
352     }
353 
354     for (i=0;i<l;i++) {
355       if (PetscIsInfOrNanScalar(beta[i])) {
356         SETERRQ(((PetscObject)snes)->comm,PETSC_ERR_LIB,"SVD generated inconsistent output");
357       }
358     }
359 
360     alph_total = 0.;
361     for (i = 0; i < l; i++) {
362       alph_total += beta[i];
363     }
364 
365     ierr = VecCopy(XM, XA);CHKERRQ(ierr);
366     ierr = VecScale(XA, 1. - alph_total);CHKERRQ(ierr);
367 
368     ierr = VecMAXPY(XA, l, beta, Xdot);CHKERRQ(ierr);
369 
370     /* check the validity of the step */
371     ierr = VecCopy(XA,Y);CHKERRQ(ierr);
372     ierr = VecAXPY(Y,-1.0,X);CHKERRQ(ierr);
373     ierr = SNESLineSearchPostCheck(snes->linesearch,X,Y,XA,&changed_y,&changed_w);CHKERRQ(ierr);
374     ierr = SNESComputeFunction(snes, XA, FA);CHKERRQ(ierr);
375 
376     /* differences for selection and restart */
377     if (ngmres->restart_type == SNES_NGMRES_RESTART_DIFFERENCE || ngmres->select_type == SNES_NGMRES_SELECT_DIFFERENCE) {
378       if (ngmres->singlereduction) {
379         dminnorm = -1.0;
380         ierr=VecCopy(XA, D);CHKERRQ(ierr);
381         ierr=VecAXPY(D, -1.0, XM);CHKERRQ(ierr);
382         for(i=0;i<l;i++) {
383           ierr=VecAXPY(Xdot[i],-1.0,XA);CHKERRQ(ierr);
384         }
385         ierr = VecNormBegin(FA, NORM_2, &fAnorm);CHKERRQ(ierr);
386         ierr = VecNormBegin(D, NORM_2, &dnorm);CHKERRQ(ierr);
387         for (i=0;i<l;i++) {
388           ierr = VecNormBegin(Xdot[i], NORM_2, &ngmres->xnorms[i]);CHKERRQ(ierr);
389         }
390         ierr = VecNormEnd(FA, NORM_2, &fAnorm);CHKERRQ(ierr);
391         ierr = VecNormEnd(D, NORM_2, &dnorm);CHKERRQ(ierr);
392         for (i=0;i<l;i++) {
393           ierr = VecNormEnd(Xdot[i], NORM_2, &ngmres->xnorms[i]);CHKERRQ(ierr);
394         }
395         for(i=0;i<l;i++) {
396           dcurnorm = ngmres->xnorms[i];
397           if ((dcurnorm < dminnorm) || (dminnorm < 0.0)) dminnorm = dcurnorm;
398           ierr=VecAXPY(Xdot[i],1.0,XA);CHKERRQ(ierr);
399         }
400       } else {
401         ierr=VecCopy(XA, D);CHKERRQ(ierr);
402         ierr=VecAXPY(D, -1.0, XM);CHKERRQ(ierr);
403         ierr=VecNormBegin(D, NORM_2, &dnorm);CHKERRQ(ierr);
404         ierr=VecNormBegin(FA, NORM_2, &fAnorm);CHKERRQ(ierr);
405         ierr=VecNormEnd(D, NORM_2, &dnorm);CHKERRQ(ierr);
406         ierr=VecNormEnd(FA, NORM_2, &fAnorm);CHKERRQ(ierr);
407         dminnorm = -1.0;
408         for(i=0;i<l;i++) {
409           ierr=VecCopy(XA, D);CHKERRQ(ierr);
410           ierr=VecAXPY(D, -1.0, Xdot[i]);CHKERRQ(ierr);
411           ierr=VecNorm(D, NORM_2, &dcurnorm);CHKERRQ(ierr);
412           if((dcurnorm < dminnorm) || (dminnorm < 0.0)) dminnorm = dcurnorm;
413         }
414       }
415     } else {
416       ierr = VecNorm(FA, NORM_2, &fAnorm);CHKERRQ(ierr);
417     }
418     if (PetscIsInfOrNanReal(fAnorm)) SETERRQ(((PetscObject)snes)->comm, PETSC_ERR_FP, "Infinite or not-a-number generated in function evaluation");
419     /* combination (additive) or selection (multiplicative) of the N-GMRES solution */
420     if (ngmres->select_type == SNES_NGMRES_SELECT_LINESEARCH) {
421       /* X = X + \lambda(XA - X) */
422       if (ngmres->monitor) {
423         ierr = PetscViewerASCIIPrintf(ngmres->monitor, "||F_A||_2 = %e, ||F_M||_2 = %e\n", fAnorm, fMnorm);CHKERRQ(ierr);
424       }
425       ierr = VecCopy(FM, F);CHKERRQ(ierr);
426       ierr = VecCopy(XM, X);CHKERRQ(ierr);
427       ierr = VecCopy(XA, Y);CHKERRQ(ierr);
428       ierr = VecAYPX(Y, -1.0, X);CHKERRQ(ierr);
429       fnorm = fMnorm;
430       ierr = SNESLineSearchApply(ngmres->additive_linesearch,X,F,&fnorm,Y);CHKERRQ(ierr);
431       ierr = SNESLineSearchGetSuccess(ngmres->additive_linesearch, &lssucceed);CHKERRQ(ierr);
432       if (!lssucceed) {
433         if (++snes->numFailures >= snes->maxFailures) {
434           snes->reason = SNES_DIVERGED_LINE_SEARCH;
435           PetscFunctionReturn(0);
436         }
437       }
438       if (ngmres->monitor) {
439         ierr = PetscViewerASCIIPrintf(ngmres->monitor, "Additive solution: ||F||_2 = %e\n", fnorm);CHKERRQ(ierr);
440       }
441     } else if (ngmres->select_type == SNES_NGMRES_SELECT_DIFFERENCE) {
442       selectA = PETSC_TRUE;
443       /* Conditions for choosing the accelerated answer */
444       /* Criterion A -- the norm of the function isn't increased above the minimum by too much */
445       if (fAnorm >= ngmres->gammaA*fminnorm) {
446         selectA = PETSC_FALSE;
447       }
448       /* Criterion B -- the choice of x^A isn't too close to some other choice */
449       if (ngmres->epsilonB*dnorm<dminnorm || PetscSqrtReal(fnorm)<ngmres->deltaB*PetscSqrtReal(fminnorm)) {
450       } else {
451         selectA=PETSC_FALSE;
452       }
453       if (selectA) {
454         if (ngmres->monitor) {
455           ierr = PetscViewerASCIIPrintf(ngmres->monitor, "picked X_A, ||F_A||_2 = %e, ||F_M||_2 = %e\n", fAnorm, fMnorm);CHKERRQ(ierr);
456         }
457         /* copy it over */
458         fnorm = fAnorm;
459         nu = fnorm*fnorm;
460         ierr = VecCopy(FA, F);CHKERRQ(ierr);
461         ierr = VecCopy(XA, X);CHKERRQ(ierr);
462       } else {
463         if (ngmres->monitor) {
464           ierr = PetscViewerASCIIPrintf(ngmres->monitor, "picked X_M, ||F_A||_2 = %e, ||F_M||_2 = %e\n", fAnorm, fMnorm);CHKERRQ(ierr);
465         }
466         fnorm = fMnorm;
467         nu = fnorm*fnorm;
468         ierr = VecCopy(XM, Y);CHKERRQ(ierr);
469         ierr = VecAXPY(Y,-1.0,X);CHKERRQ(ierr);
470         ierr = VecCopy(FM, F);CHKERRQ(ierr);
471         ierr = VecCopy(XM, X);CHKERRQ(ierr);
472       }
473     } else { /* none */
474       fnorm = fAnorm;
475       nu = fnorm*fnorm;
476       ierr = VecCopy(FA, F);CHKERRQ(ierr);
477       ierr = VecCopy(XA, X);CHKERRQ(ierr);
478     }
479 
480     selectRestart = PETSC_FALSE;
481     if (ngmres->restart_type == SNES_NGMRES_RESTART_DIFFERENCE) {
482       /* difference stagnation restart */
483       if((ngmres->epsilonB*dnorm > dminnorm) && (PetscSqrtReal(fAnorm) > ngmres->deltaB*PetscSqrtReal(fminnorm))) {
484         if (ngmres->monitor) {
485           ierr = PetscViewerASCIIPrintf(ngmres->monitor, "difference restart: %e > %e\n", ngmres->epsilonB*dnorm, dminnorm);CHKERRQ(ierr);
486         }
487         selectRestart = PETSC_TRUE;
488       }
489       /* residual stagnation restart */
490       if (PetscSqrtReal(fAnorm) > ngmres->gammaC*PetscSqrtReal(fminnorm)) {
491         if (ngmres->monitor) {
492           ierr = PetscViewerASCIIPrintf(ngmres->monitor, "residual restart: %e > %e\n", PetscSqrtReal(fAnorm), ngmres->gammaC*PetscSqrtReal(fminnorm));CHKERRQ(ierr);
493         }
494         selectRestart = PETSC_TRUE;
495       }
496       /* if the restart conditions persist for more than restart_it iterations, restart. */
497       if (selectRestart) {
498         restart_count++;
499       } else {
500         restart_count = 0;
501       }
502     } else if (ngmres->restart_type == SNES_NGMRES_RESTART_PERIODIC) {
503       if (k_restart > ngmres->restart_periodic) {
504         if (ngmres->monitor) ierr = PetscViewerASCIIPrintf(ngmres->monitor, "periodic restart after %D iterations\n", k_restart);CHKERRQ(ierr);
505         restart_count = ngmres->restart_it;
506       }
507     }
508     /* restart after restart conditions have persisted for a fixed number of iterations */
509     if (restart_count >= ngmres->restart_it) {
510       if (ngmres->monitor){
511         ierr = PetscViewerASCIIPrintf(ngmres->monitor, "Restarted at iteration %d\n", k_restart);CHKERRQ(ierr);
512       }
513       restart_count = 0;
514       k_restart = 1;
515       l = 1;
516       /* q_{00} = nu */
517       if (ngmres->anderson) {
518         ngmres->fnorms[0] = fMnorm;
519         nu = fMnorm*fMnorm;
520         Q(0,0) = nu;
521         /* Fdot[0] = F */
522         ierr = VecCopy(XM, Xdot[0]);CHKERRQ(ierr);
523         ierr = VecCopy(FM, Fdot[0]);CHKERRQ(ierr);
524       } else {
525         ngmres->fnorms[0] = fnorm;
526         nu = fnorm*fnorm;
527         Q(0,0) = nu;
528         /* Fdot[0] = F */
529         ierr = VecCopy(X, Xdot[0]);CHKERRQ(ierr);
530         ierr = VecCopy(F, Fdot[0]);CHKERRQ(ierr);
531       }
532     } else {
533       /* select the current size of the subspace */
534       if (l < ngmres->msize) l++;
535       k_restart++;
536       /* place the current entry in the list of previous entries */
537       if (ngmres->anderson) {
538         ierr = VecCopy(FM, Fdot[ivec]);CHKERRQ(ierr);
539         ierr = VecCopy(XM, Xdot[ivec]);CHKERRQ(ierr);
540         ngmres->fnorms[ivec] = fMnorm;
541         if (fminnorm > fMnorm) fminnorm = fMnorm;  /* the minimum norm is now of FM */
542         xi[ivec] = fMnorm*fMnorm;
543         for (i = 0; i < l; i++) {
544           Q(i, ivec) = xi[i];
545           Q(ivec, i) = xi[i];
546         }
547       } else {
548         ierr = VecCopy(F, Fdot[ivec]);CHKERRQ(ierr);
549         ierr = VecCopy(X, Xdot[ivec]);CHKERRQ(ierr);
550         ngmres->fnorms[ivec] = fnorm;
551         if (fminnorm > fnorm) fminnorm = fnorm;  /* the minimum norm is now of FA */
552         ierr = VecMDot(F, l, Fdot, xi);CHKERRQ(ierr);
553         for (i = 0; i < l; i++) {
554           Q(i, ivec) = xi[i];
555           Q(ivec, i) = xi[i];
556         }
557       }
558     }
559 
560     ierr = PetscObjectTakeAccess(snes);CHKERRQ(ierr);
561     snes->iter = k;
562     snes->norm = fnorm;
563     ierr = PetscObjectGrantAccess(snes);CHKERRQ(ierr);
564     SNESLogConvHistory(snes, snes->norm, snes->iter);
565     ierr = SNESMonitor(snes, snes->iter, snes->norm);CHKERRQ(ierr);
566     ierr = VecNormBegin(Y,NORM_2,&ynorm);CHKERRQ(ierr);
567     ierr = VecNormBegin(X,NORM_2,&xnorm);CHKERRQ(ierr);
568     ierr = VecNormEnd(Y,NORM_2,&ynorm);CHKERRQ(ierr);
569     ierr = VecNormEnd(X,NORM_2,&xnorm);CHKERRQ(ierr);
570     ierr = (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);CHKERRQ(ierr);
571     if (snes->reason) PetscFunctionReturn(0);
572   }
573   snes->reason = SNES_DIVERGED_MAX_IT;
574   PetscFunctionReturn(0);
575 }
576 
577 #undef __FUNCT__
578 #define __FUNCT__ "SNESNGMRESSetRestartType"
579 /*@
580     SNESNGMRESSetRestartType - Sets the restart type for SNESNGMRES.
581 
582     Logically Collective on SNES
583 
584     Input Parameters:
585 +   snes - the iterative context
586 -   rtype - restart type
587 
588     Options Database:
589 +   -snes_ngmres_restart_type<difference,periodic,none> - set the restart type
590 -   -snes_ngmres_restart[30] - sets the number of iterations before restart for periodic
591 
592     Level: intermediate
593 
594     SNESNGMRESRestartTypes:
595 +   SNES_NGMRES_RESTART_NONE - never restart
596 .   SNES_NGMRES_RESTART_DIFFERENCE - restart based upon difference criteria
597 -   SNES_NGMRES_RESTART_PERIODIC - restart after a fixed number of iterations
598 
599     Notes:
600     The default line search used is the L2 line search and it requires two additional function evaluations.
601 
602 .keywords: SNES, SNESNGMRES, restart, type, set SNESLineSearch
603 @*/
604 PetscErrorCode SNESNGMRESSetRestartType(SNES snes, SNESNGMRESRestartType rtype) {
605   PetscErrorCode ierr;
606   PetscFunctionBegin;
607   PetscValidHeaderSpecific(snes,SNES_CLASSID,1);
608   ierr = PetscTryMethod(snes,"SNESNGMRESSetRestartType_C",(SNES,SNESNGMRESRestartType),(snes,rtype));CHKERRQ(ierr);
609   PetscFunctionReturn(0);
610 }
611 
612 #undef __FUNCT__
613 #define __FUNCT__ "SNESNGMRESSetSelectType"
614 /*@
615     SNESNGMRESSetSelectType - Sets the selection type for SNESNGMRES.  This determines how the candidate solution and
616     combined solution are used to create the next iterate.
617 
618     Logically Collective on SNES
619 
620     Input Parameters:
621 +   snes - the iterative context
622 -   stype - selection type
623 
624     Options Database:
625 .   -snes_ngmres_select_type<difference,none,linesearch>
626 
627     Level: intermediate
628 
629     SNESNGMRESSelectTypes:
630 +   SNES_NGMRES_SELECT_NONE - choose the combined solution all the time
631 .   SNES_NGMRES_SELECT_DIFFERENCE - choose based upon the selection criteria
632 -   SNES_NGMRES_SELECT_LINESEARCH - choose based upon line search combination
633 
634     Notes:
635     The default line search used is the L2 line search and it requires two additional function evaluations.
636 
637 .keywords: SNES, SNESNGMRES, selection, type, set SNESLineSearch
638 @*/
639 
640 PetscErrorCode SNESNGMRESSetSelectType(SNES snes, SNESNGMRESSelectType stype) {
641   PetscErrorCode ierr;
642   PetscFunctionBegin;
643   PetscValidHeaderSpecific(snes,SNES_CLASSID,1);
644   ierr = PetscTryMethod(snes,"SNESNGMRESSetSelectType_C",(SNES,SNESNGMRESSelectType),(snes,stype));CHKERRQ(ierr);
645   PetscFunctionReturn(0);
646 }
647 
648 
649 EXTERN_C_BEGIN
650 #undef __FUNCT__
651 #define __FUNCT__ "SNESNGMRESSetSelectType_NGMRES"
652 
653 PetscErrorCode SNESNGMRESSetSelectType_NGMRES(SNES snes, SNESNGMRESSelectType stype) {
654   SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data;
655   PetscFunctionBegin;
656   ngmres->select_type = stype;
657   PetscFunctionReturn(0);
658 }
659 
660 #undef __FUNCT__
661 #define __FUNCT__ "SNESNGMRESSetRestartType_NGMRES"
662 
663 PetscErrorCode SNESNGMRESSetRestartType_NGMRES(SNES snes, SNESNGMRESRestartType rtype) {
664   SNES_NGMRES *ngmres = (SNES_NGMRES *)snes->data;
665   PetscFunctionBegin;
666   ngmres->restart_type = rtype;
667   PetscFunctionReturn(0);
668 }
669 EXTERN_C_END
670 
671 
672 /*MC
673   SNESNGMRES - The Nonlinear Generalized Minimum Residual method.
674 
675    Level: beginner
676 
677    Options Database:
678 +  -snes_ngmres_select_type<difference,none,linesearch> - choose the select between candidate and combined solution
679 +  -snes_ngmres_restart_type<difference,none,periodic> - choose the restart conditions
680 .  -snes_ngmres_anderson         - Use Anderson mixing NGMRES variant which combines candidate solutions instead of actual solutions
681 .  -snes_ngmres_m                - Number of stored previous solutions and residuals
682 .  -snes_ngmres_restart_it       - Number of iterations the restart conditions hold before restart
683 .  -snes_ngmres_gammaA           - Residual tolerance for solution select between the candidate and combination
684 .  -snes_ngmres_gammaC           - Residual tolerance for restart
685 .  -snes_ngmres_epsilonB         - Difference tolerance between subsequent solutions triggering restart
686 .  -snes_ngmres_deltaB           - Difference tolerance between residuals triggering restart
687 .  -snes_ngmres_monitor          - Prints relevant information about the ngmres iteration
688 .  -snes_linesearch_type <basic,basicnonorms,quadratic,critical> - Line search type used for the default smoother
689 -  -additive_snes_linesearch_type - linesearch type used to select between the candidate and combined solution with additive select type
690 
691    Notes:
692 
693    The N-GMRES method combines m previous solutions into a minimum-residual solution by solving a small linearized
694    optimization problem at each iteration.
695 
696    References:
697 
698    "Krylov Subspace Acceleration of Nonlinear Multigrid with Application to Recirculating Flows", C. W. Oosterlee and T. Washio,
699    SIAM Journal on Scientific Computing, 21(5), 2000.
700 
701    This is also the same as the algorithm called Anderson acceleration introduced in "D. G. Anderson. Iterative procedures for nonlinear integral equations.
702    J. Assoc. Comput. Mach., 12:547–560, 1965."
703 
704 .seealso: SNESCreate(), SNES, SNESSetType(), SNESType (for list of available types)
705 M*/
706 
707 EXTERN_C_BEGIN
708 #undef __FUNCT__
709 #define __FUNCT__ "SNESCreate_NGMRES"
710 PetscErrorCode SNESCreate_NGMRES(SNES snes)
711 {
712   SNES_NGMRES   *ngmres;
713   PetscErrorCode ierr;
714 
715   PetscFunctionBegin;
716   snes->ops->destroy        = SNESDestroy_NGMRES;
717   snes->ops->setup          = SNESSetUp_NGMRES;
718   snes->ops->setfromoptions = SNESSetFromOptions_NGMRES;
719   snes->ops->view           = SNESView_NGMRES;
720   snes->ops->solve          = SNESSolve_NGMRES;
721   snes->ops->reset          = SNESReset_NGMRES;
722 
723   snes->usespc          = PETSC_TRUE;
724   snes->usesksp         = PETSC_FALSE;
725 
726   ierr = PetscNewLog(snes, SNES_NGMRES, &ngmres);CHKERRQ(ierr);
727   snes->data = (void*) ngmres;
728   ngmres->msize = 30;
729 
730   if (!snes->tolerancesset) {
731     snes->max_funcs = 30000;
732     snes->max_its   = 10000;
733   }
734 
735   ngmres->anderson   = PETSC_FALSE;
736 
737   ngmres->additive_linesearch = PETSC_NULL;
738 
739   ngmres->restart_it = 2;
740   ngmres->restart_periodic = 30;
741   ngmres->gammaA     = 2.0;
742   ngmres->gammaC     = 2.0;
743   ngmres->deltaB     = 0.9;
744   ngmres->epsilonB   = 0.1;
745 
746   ngmres->restart_type   = SNES_NGMRES_RESTART_DIFFERENCE;
747   ngmres->select_type    = SNES_NGMRES_SELECT_DIFFERENCE;
748 
749   ierr = PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESNGMRESSetSelectType_C","SNESNGMRESSetSelectType_NGMRES", SNESNGMRESSetSelectType_NGMRES);CHKERRQ(ierr);
750   ierr = PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESNGMRESSetRestartType_C","SNESNGMRESSetRestartType_NGMRES", SNESNGMRESSetRestartType_NGMRES);CHKERRQ(ierr);
751 
752   PetscFunctionReturn(0);
753 }
754 EXTERN_C_END
755