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