xref: /petsc/src/snes/impls/qn/qn.c (revision d54338ec4fdf48b56b5efdd9bf04ca9d50d6ab04)
1 #include <petsc-private/snesimpl.h> /*I "petscsnes.h" I*/
2 
3 #define H(i,j)  qn->dXdFmat[i*qn->m + j]
4 
5 const char *const SNESQNScaleTypes[] =        {"NONE","SHANNO","LINESEARCH","JACOBIAN","SNESQNScaleType","SNES_QN_SCALING_",0};
6 const char *const SNESQNRestartTypes[] =      {"NONE","POWELL","PERIODIC","SNESQNRestartType","SNES_QN_RESTART_",0};
7 const char *const SNESQNTypes[] =             {"LBFGS","BROYDEN","BADBROYDEN","SNESQNType","SNES_QN_RESTART_",0};
8 
9 typedef enum {SNES_QN_LBFGS      = 0,
10               SNES_QN_BROYDEN    = 1,
11               SNES_QN_BADBROYDEN = 2} SNESQNType;
12 
13 typedef struct {
14   Vec                   *U;               /* Stored past states (vary from method to method) */
15   Vec                   *V;               /* Stored past states (vary from method to method) */
16   PetscInt              m;                /* The number of kept previous steps */
17   PetscScalar           *alpha, *beta;
18   PetscScalar           *dXtdF, *dFtdX, *YtdX;
19   PetscBool             singlereduction;  /* Aggregated reduction implementation */
20   PetscScalar           *dXdFmat;         /* A matrix of values for dX_i dot dF_j */
21   PetscViewer           monitor;
22   PetscReal             powell_gamma;     /* Powell angle restart condition */
23   PetscReal             powell_downhill;  /* Powell descent restart condition */
24   PetscReal             scaling;          /* scaling of H0 */
25 
26   SNESQNType            type;             /* the type of quasi-newton method used */
27   SNESQNScaleType       scale_type;       /* the type of scaling used */
28   SNESQNRestartType     restart_type;     /* determine the frequency and type of restart conditions */
29 } SNES_QN;
30 
31 #undef __FUNCT__
32 #define __FUNCT__ "SNESQNApply_Broyden"
33 PetscErrorCode SNESQNApply_Broyden(SNES snes,PetscInt it,Vec Y,Vec X,Vec Xold, Vec D, Vec Dold)
34 {
35   PetscErrorCode     ierr;
36   SNES_QN            *qn = (SNES_QN*)snes->data;
37   Vec                W = snes->work[3];
38   Vec                *U = qn->U;
39   Vec                *V = qn->V;
40   KSPConvergedReason kspreason;
41   MatStructure       flg = DIFFERENT_NONZERO_PATTERN;
42   PetscInt           k,i,lits;
43   PetscInt           m = qn->m;
44   PetscScalar        gdot;
45   PetscInt           l = m;
46   Mat                jac,jac_pre;
47 
48   PetscFunctionBegin;
49   if (it < m) l = it;
50   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
51     ierr = SNESGetJacobian(snes,&jac,&jac_pre,PETSC_NULL,PETSC_NULL);CHKERRQ(ierr);
52     ierr = KSPSetOperators(snes->ksp,jac,jac_pre,flg);CHKERRQ(ierr);
53     ierr = SNES_KSPSolve(snes,snes->ksp,D,W);CHKERRQ(ierr);
54     ierr = KSPGetConvergedReason(snes->ksp,&kspreason);CHKERRQ(ierr);
55     if (kspreason < 0) {
56       if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
57         ierr = PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);CHKERRQ(ierr);
58         snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
59         PetscFunctionReturn(0);
60       }
61     }
62     ierr = KSPGetIterationNumber(snes->ksp,&lits);CHKERRQ(ierr);
63     snes->linear_its += lits;
64     ierr = VecCopy(W,Y);CHKERRQ(ierr);
65   } else {
66     ierr = VecCopy(D,Y);CHKERRQ(ierr);
67     ierr = VecScale(Y,qn->scaling);CHKERRQ(ierr);
68   }
69 
70   /* inward recursion starting at the first update and working forward */
71   if (it > 1) {
72     for (i = 0; i < l-1; i++) {
73       k = (it+i-l)%l;
74       ierr = VecDot(U[k],Y,&gdot);CHKERRQ(ierr);
75       ierr = VecAXPY(Y,gdot,V[k]);CHKERRQ(ierr);
76       if (qn->monitor) {
77         ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
78         ierr = PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));CHKERRQ(ierr);
79         ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
80       }
81     }
82   }
83   if (it < m) l = it;
84 
85   /* set W to be the last step, post-linesearch */
86   ierr = VecCopy(Xold,W);CHKERRQ(ierr);
87   ierr = VecAXPY(W,-1.0,X);CHKERRQ(ierr);
88 
89   if (l > 0) {
90     k = (it-1)%l;
91     ierr = VecCopy(W,U[k]);CHKERRQ(ierr);
92     ierr = VecAXPY(W,-1.0,Y);CHKERRQ(ierr);
93     ierr = VecDot(U[k],W,&gdot);CHKERRQ(ierr);
94     ierr = VecCopy(Y,V[k]);CHKERRQ(ierr);
95     ierr = VecScale(V[k],1.0/gdot);CHKERRQ(ierr);
96     ierr = VecDot(U[k],Y,&gdot);CHKERRQ(ierr);
97     ierr = VecAXPY(Y,gdot,V[k]);CHKERRQ(ierr);
98     if (qn->monitor) {
99       ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
100       ierr = PetscViewerASCIIPrintf(qn->monitor, "update: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));CHKERRQ(ierr);
101       ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
102     }
103   }
104   PetscFunctionReturn(0);
105 }
106 
107 #undef __FUNCT__
108 #define __FUNCT__ "SNESQNApply_BadBroyden"
109 PetscErrorCode SNESQNApply_BadBroyden(SNES snes,PetscInt it,Vec Y,Vec X,Vec Xold,Vec D,Vec Dold)
110 {
111   PetscErrorCode     ierr;
112   SNES_QN            *qn = (SNES_QN*)snes->data;
113   Vec                W = snes->work[3];
114   Vec                *U = qn->U;
115   Vec                *T = qn->V;
116 
117   /* ksp thing for jacobian scaling */
118   KSPConvergedReason kspreason;
119   MatStructure       flg = DIFFERENT_NONZERO_PATTERN;
120   PetscInt           k, i, lits;
121   PetscInt           m = qn->m;
122   PetscScalar        gdot;
123   PetscInt           l = m;
124   Mat                jac, jac_pre;
125 
126   PetscFunctionBegin;
127   if (it < m) l = it;
128   ierr = VecCopy(D,Y);CHKERRQ(ierr);
129   if (l > 0) {
130     k = (it-1)%l;
131     ierr = VecCopy(Dold,U[k]);CHKERRQ(ierr);
132     ierr = VecAXPY(U[k],-1.0,D);CHKERRQ(ierr);
133     ierr = VecDot(U[k],U[k],&gdot);CHKERRQ(ierr);
134     ierr = VecCopy(D,T[k]);CHKERRQ(ierr);
135     ierr = VecScale(T[k],1.0/gdot);CHKERRQ(ierr);
136     ierr = VecDot(U[k],Y,&gdot);CHKERRQ(ierr);
137     ierr = VecAXPY(Y,gdot,T[k]);CHKERRQ(ierr);
138     if (qn->monitor) {
139       ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
140       ierr = PetscViewerASCIIPrintf(qn->monitor, "update: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));CHKERRQ(ierr);
141       ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
142     }
143   }
144 
145   /* inward recursion starting at the first update and working forward */
146   if (it > 1) {
147     for (i = 0; i < l-1; i++) {
148       k = (it+i-l)%l;
149       ierr = VecDot(U[k],Y,&gdot);CHKERRQ(ierr);
150       ierr = VecAXPY(Y,gdot,T[k]);CHKERRQ(ierr);
151       if (qn->monitor) {
152         ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
153         ierr = PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));CHKERRQ(ierr);
154         ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
155       }
156     }
157   }
158 
159   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
160     ierr = SNESGetJacobian(snes, &jac, &jac_pre, PETSC_NULL, PETSC_NULL);CHKERRQ(ierr);
161     ierr = KSPSetOperators(snes->ksp,jac,jac_pre,flg);CHKERRQ(ierr);
162     ierr = SNES_KSPSolve(snes,snes->ksp,Y,W);CHKERRQ(ierr);
163     ierr = KSPGetConvergedReason(snes->ksp,&kspreason);CHKERRQ(ierr);
164     if (kspreason < 0) {
165       if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
166         ierr = PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);CHKERRQ(ierr);
167         snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
168         PetscFunctionReturn(0);
169       }
170     }
171     ierr = KSPGetIterationNumber(snes->ksp,&lits);CHKERRQ(ierr);
172     snes->linear_its += lits;
173     ierr = VecCopy(W,Y);CHKERRQ(ierr);
174   }  else {
175     ierr = VecScale(Y,qn->scaling);CHKERRQ(ierr);
176   }
177   PetscFunctionReturn(0);
178 }
179 
180 #undef __FUNCT__
181 #define __FUNCT__ "SNESQNApply_LBFGS"
182 PetscErrorCode SNESQNApply_LBFGS(SNES snes,PetscInt it,Vec Y,Vec X,Vec Xold,Vec D,Vec Dold)
183 {
184   PetscErrorCode     ierr;
185   SNES_QN            *qn = (SNES_QN*)snes->data;
186   Vec                W = snes->work[3];
187   Vec                *dX = qn->U;
188   Vec                *dF = qn->V;
189   PetscScalar        *alpha    = qn->alpha;
190   PetscScalar        *beta     = qn->beta;
191   PetscScalar        *dXtdF    = qn->dXtdF;
192   PetscScalar        *dFtdX    = qn->dFtdX;
193   PetscScalar        *YtdX     = qn->YtdX;
194 
195   /* ksp thing for jacobian scaling */
196   KSPConvergedReason kspreason;
197   MatStructure       flg = DIFFERENT_NONZERO_PATTERN;
198   PetscInt           k,i,j,g,lits;
199   PetscInt           m = qn->m;
200   PetscScalar        t;
201   PetscInt           l = m;
202   Mat                jac,jac_pre;
203 
204   PetscFunctionBegin;
205   if (it < m) l = it;
206   if (it > 0) {
207     k = (it - 1) % l;
208     ierr = VecCopy(D, dF[k]);CHKERRQ(ierr);
209     ierr = VecAXPY(dF[k], -1.0, Dold);CHKERRQ(ierr);
210     ierr = VecCopy(X, dX[k]);CHKERRQ(ierr);
211     ierr = VecAXPY(dX[k], -1.0, Xold);CHKERRQ(ierr);
212     if (qn->singlereduction) {
213       ierr = VecMDot(dF[k], l, dX, dXtdF);CHKERRQ(ierr);
214       ierr = VecMDot(dX[k], l, dF, dFtdX);CHKERRQ(ierr);
215       for (j = 0; j < l; j++) {
216         H(k, j) = dFtdX[j];
217         H(j, k) = dXtdF[j];
218       }
219       /* copy back over to make the computation of alpha and beta easier */
220       for (j = 0; j < l; j++) {
221         dXtdF[j] = H(j, j);
222       }
223     } else {
224       ierr = VecDot(dX[k], dF[k], &dXtdF[k]);CHKERRQ(ierr);
225     }
226     if (qn->scale_type == SNES_QN_SCALE_SHANNO) {
227       PetscReal dFtdF;
228       ierr = VecDotRealPart(dF[k],dF[k],&dFtdF);CHKERRQ(ierr);
229       qn->scaling = PetscRealPart(dXtdF[k])/dFtdF;
230     } else if (qn->scale_type == SNES_QN_SCALE_LINESEARCH) {
231       ierr = SNESLineSearchGetLambda(snes->linesearch,&qn->scaling);CHKERRQ(ierr);
232     }
233   }
234 
235   ierr = VecCopy(D,Y);CHKERRQ(ierr);
236 
237   if (qn->singlereduction) {
238     ierr = VecMDot(Y,l,dX,YtdX);CHKERRQ(ierr);
239   }
240   /* outward recursion starting at iteration k's update and working back */
241   for (i=0;i<l;i++) {
242     k = (it-i-1)%l;
243     if (qn->singlereduction) {
244       /* construct t = dX[k] dot Y as Y_0 dot dX[k] + sum(-alpha[j]dX[k]dF[j]) */
245       t = YtdX[k];
246       for (j=0;j<i;j++) {
247         g = (it-j-1)%l;
248         t += -alpha[g]*H(g, k);
249       }
250       alpha[k] = t/H(k,k);
251     } else {
252       ierr = VecDot(dX[k],Y,&t);CHKERRQ(ierr);
253       alpha[k] = t/dXtdF[k];
254     }
255     if (qn->monitor) {
256       ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
257       ierr = PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d alpha:        %14.12e\n", it, k, PetscRealPart(alpha[k]));CHKERRQ(ierr);
258       ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
259     }
260     ierr = VecAXPY(Y,-alpha[k],dF[k]);CHKERRQ(ierr);
261   }
262 
263   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
264     ierr = SNESGetJacobian(snes, &jac, &jac_pre, PETSC_NULL, PETSC_NULL);CHKERRQ(ierr);
265     ierr = KSPSetOperators(snes->ksp,jac,jac_pre,flg);CHKERRQ(ierr);
266     ierr = SNES_KSPSolve(snes,snes->ksp,Y,W);CHKERRQ(ierr);
267     ierr = KSPGetConvergedReason(snes->ksp,&kspreason);CHKERRQ(ierr);
268     if (kspreason < 0) {
269       if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
270         ierr = PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);CHKERRQ(ierr);
271         snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
272         PetscFunctionReturn(0);
273       }
274     }
275     ierr = KSPGetIterationNumber(snes->ksp,&lits);CHKERRQ(ierr);
276     snes->linear_its += lits;
277     ierr = VecCopy(W, Y);CHKERRQ(ierr);
278   } else {
279     ierr = VecScale(Y, qn->scaling);CHKERRQ(ierr);
280   }
281   if (qn->singlereduction) {
282     ierr = VecMDot(Y,l,dF,YtdX);CHKERRQ(ierr);
283   }
284   /* inward recursion starting at the first update and working forward */
285   for (i = 0; i < l; i++) {
286     k = (it + i - l) % l;
287     if (qn->singlereduction) {
288       t = YtdX[k];
289       for (j = 0; j < i; j++) {
290         g = (it + j - l) % l;
291         t += (alpha[g] - beta[g])*H(k, g);
292       }
293       beta[k] = t / H(k, k);
294     } else {
295       ierr = VecDot(dF[k], Y, &t);CHKERRQ(ierr);
296       beta[k] = t / dXtdF[k];
297     }
298     ierr = VecAXPY(Y, (alpha[k] - beta[k]), dX[k]);CHKERRQ(ierr);
299     if (qn->monitor) {
300       ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
301       ierr = PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d alpha - beta: %14.12e\n", it, k, PetscRealPart(alpha[k] - beta[k]));CHKERRQ(ierr);
302       ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
303     }
304   }
305   PetscFunctionReturn(0);
306 }
307 
308 #undef __FUNCT__
309 #define __FUNCT__ "SNESSolve_QN"
310 static PetscErrorCode SNESSolve_QN(SNES snes)
311 {
312   PetscErrorCode      ierr;
313   SNES_QN             *qn = (SNES_QN*) snes->data;
314   Vec                 X,Xold;
315   Vec                 F,B;
316   Vec                 Y,FPC,D,Dold;
317   SNESConvergedReason reason;
318   PetscInt            i, i_r;
319   PetscReal           fnorm,xnorm,ynorm,gnorm;
320   PetscBool           lssucceed,powell,periodic;
321   PetscScalar         DolddotD,DolddotDold,DdotD,YdotD;
322   MatStructure        flg = DIFFERENT_NONZERO_PATTERN;
323 
324   /* basically just a regular newton's method except for the application of the jacobian */
325 
326   PetscFunctionBegin;
327   F             = snes->vec_func;       /* residual vector */
328   Y             = snes->vec_sol_update; /* search direction generated by J^-1D*/
329   B             = snes->vec_rhs;
330 
331   X             = snes->vec_sol;        /* solution vector */
332   Xold          = snes->work[0];
333 
334   /* directions generated by the preconditioned problem with F_pre = F or x - M(x, b) */
335   D             = snes->work[1];
336   Dold          = snes->work[2];
337 
338   snes->reason = SNES_CONVERGED_ITERATING;
339 
340   ierr = PetscObjectTakeAccess(snes);CHKERRQ(ierr);
341   snes->iter = 0;
342   snes->norm = 0.;
343   ierr = PetscObjectGrantAccess(snes);CHKERRQ(ierr);
344   if (!snes->vec_func_init_set) {
345     ierr = SNESComputeFunction(snes,X,F);CHKERRQ(ierr);
346     if (snes->domainerror) {
347       snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
348       PetscFunctionReturn(0);
349     }
350   } else {
351     snes->vec_func_init_set = PETSC_FALSE;
352   }
353 
354   if (!snes->norm_init_set) {
355     ierr = VecNorm(F, NORM_2, &fnorm);CHKERRQ(ierr); /* fnorm <- ||F||  */
356     if (PetscIsInfOrNanReal(fnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"Infinite or not-a-number generated in norm");
357   } else {
358     fnorm = snes->norm_init;
359     snes->norm_init_set = PETSC_FALSE;
360   }
361 
362   ierr = PetscObjectTakeAccess(snes);CHKERRQ(ierr);
363   snes->norm = fnorm;
364   ierr = PetscObjectGrantAccess(snes);CHKERRQ(ierr);
365   SNESLogConvHistory(snes,fnorm,0);
366   ierr = SNESMonitor(snes,0,fnorm);CHKERRQ(ierr);
367 
368   /* set parameter for default relative tolerance convergence test */
369    snes->ttol = fnorm*snes->rtol;
370   /* test convergence */
371   ierr = (*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP);CHKERRQ(ierr);
372   if (snes->reason) PetscFunctionReturn(0);
373 
374   /* composed solve */
375   if (snes->pc && snes->pcside == PC_RIGHT) {
376     ierr = SNESSetInitialFunction(snes->pc, F);CHKERRQ(ierr);
377     ierr = SNESSetInitialFunctionNorm(snes->pc, fnorm);CHKERRQ(ierr);
378 
379     ierr = PetscLogEventBegin(SNES_NPCSolve,snes->pc,X,B,0);CHKERRQ(ierr);
380     ierr = SNESSolve(snes->pc, B, X);CHKERRQ(ierr);
381     ierr = PetscLogEventEnd(SNES_NPCSolve,snes->pc,X,B,0);CHKERRQ(ierr);
382 
383     ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
384     if (reason < 0 && (reason != SNES_DIVERGED_MAX_IT)) {
385       snes->reason = SNES_DIVERGED_INNER;
386       PetscFunctionReturn(0);
387     }
388     ierr = SNESGetFunction(snes->pc, &FPC, PETSC_NULL, PETSC_NULL);CHKERRQ(ierr);
389     ierr = VecCopy(FPC, F);CHKERRQ(ierr);
390     ierr = SNESGetFunctionNorm(snes->pc, &fnorm);CHKERRQ(ierr);
391     ierr = VecCopy(F, Y);CHKERRQ(ierr);
392   } else {
393     ierr = VecCopy(F, Y);CHKERRQ(ierr);
394   }
395   ierr = VecCopy(Y, D);CHKERRQ(ierr);
396 
397   /* scale the initial update */
398   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
399     ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);CHKERRQ(ierr);
400   }
401 
402   for (i = 0, i_r = 0; i < snes->max_its; i++, i_r++) {
403     switch (qn->type) {
404     case SNES_QN_BADBROYDEN:
405       ierr = SNESQNApply_BadBroyden(snes,i_r,Y,X,Xold,D,Dold);CHKERRQ(ierr);
406       break;
407     case SNES_QN_BROYDEN:
408       ierr = SNESQNApply_Broyden(snes,i_r,Y,X,Xold,D,Dold);CHKERRQ(ierr);
409       break;
410     case SNES_QN_LBFGS:
411       SNESQNApply_LBFGS(snes,i_r,Y,X,Xold,D,Dold);CHKERRQ(ierr);
412       break;
413     }
414     /* line search for lambda */
415     ynorm = 1; gnorm = fnorm;
416     ierr = VecCopy(D, Dold);CHKERRQ(ierr);
417     ierr = VecCopy(X, Xold);CHKERRQ(ierr);
418     ierr = SNESLineSearchApply(snes->linesearch, X, F, &fnorm, Y);CHKERRQ(ierr);
419     if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
420     if (snes->domainerror) {
421       snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
422       PetscFunctionReturn(0);
423       }
424     ierr = SNESLineSearchGetSuccess(snes->linesearch, &lssucceed);CHKERRQ(ierr);
425     if (!lssucceed) {
426       if (++snes->numFailures >= snes->maxFailures) {
427         snes->reason = SNES_DIVERGED_LINE_SEARCH;
428         break;
429       }
430     }
431     ierr = SNESLineSearchGetNorms(snes->linesearch, &xnorm, &fnorm, &ynorm);CHKERRQ(ierr);
432     if (qn->scale_type == SNES_QN_SCALE_LINESEARCH) {
433       ierr = SNESLineSearchGetLambda(snes->linesearch, &qn->scaling);CHKERRQ(ierr);
434     }
435 
436     /* convergence monitoring */
437     ierr = PetscInfo4(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n",(double)fnorm,(double)gnorm,(double)ynorm,(int)lssucceed);CHKERRQ(ierr);
438 
439     ierr = SNESSetIterationNumber(snes, i+1);CHKERRQ(ierr);
440     ierr = SNESSetFunctionNorm(snes, fnorm);CHKERRQ(ierr);
441 
442     SNESLogConvHistory(snes,snes->norm,snes->iter);
443     ierr = SNESMonitor(snes,snes->iter,snes->norm);CHKERRQ(ierr);
444     /* set parameter for default relative tolerance convergence test */
445     ierr = (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);CHKERRQ(ierr);
446     if (snes->reason) PetscFunctionReturn(0);
447 
448     if (snes->pc && snes->pcside == PC_RIGHT) {
449       ierr = SNESSetInitialFunction(snes->pc, F);CHKERRQ(ierr);
450       ierr = SNESSetInitialFunctionNorm(snes->pc, fnorm);CHKERRQ(ierr);
451       ierr = SNESSolve(snes->pc, B, X);CHKERRQ(ierr);
452       ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
453       if (reason < 0 && (reason != SNES_DIVERGED_MAX_IT)) {
454         snes->reason = SNES_DIVERGED_INNER;
455         PetscFunctionReturn(0);
456       }
457       ierr = SNESGetFunction(snes->pc, &FPC, PETSC_NULL, PETSC_NULL);CHKERRQ(ierr);
458       ierr = VecCopy(FPC, F);CHKERRQ(ierr);
459       ierr = SNESGetFunctionNorm(snes->pc, &fnorm);CHKERRQ(ierr);
460       ierr = VecCopy(F, D);CHKERRQ(ierr);
461     } else {
462       ierr = VecCopy(F, D);CHKERRQ(ierr);
463     }
464 
465     powell = PETSC_FALSE;
466     if (qn->restart_type == SNES_QN_RESTART_POWELL) {
467       /* check restart by Powell's Criterion: |F^T H_0 Fold| > 0.2 * |Fold^T H_0 Fold| */
468       ierr = VecDotBegin(Dold, Dold, &DolddotDold);CHKERRQ(ierr);
469       ierr = VecDotBegin(Dold, D, &DolddotD);CHKERRQ(ierr);
470       ierr = VecDotBegin(D, D, &DdotD);CHKERRQ(ierr);
471       ierr = VecDotBegin(Y, D, &YdotD);CHKERRQ(ierr);
472       ierr = VecDotEnd(Dold, Dold, &DolddotDold);CHKERRQ(ierr);
473       ierr = VecDotEnd(Dold, D, &DolddotD);CHKERRQ(ierr);
474       ierr = VecDotEnd(D, D, &DdotD);CHKERRQ(ierr);
475       ierr = VecDotEnd(Y, D, &YdotD);CHKERRQ(ierr);
476       if (PetscAbs(PetscRealPart(DolddotD)) > qn->powell_gamma*PetscAbs(PetscRealPart(DolddotDold))) powell = PETSC_TRUE;
477     }
478     periodic = PETSC_FALSE;
479     if (qn->restart_type == SNES_QN_RESTART_PERIODIC) {
480       if (i_r>qn->m-1) periodic = PETSC_TRUE;
481     }
482     /* restart if either powell or periodic restart is satisfied. */
483     if (powell || periodic) {
484       if (qn->monitor) {
485         ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
486         ierr = PetscViewerASCIIPrintf(qn->monitor, "restart! |%14.12e| > %4.2f*|%14.12e| or i_r = %d\n", PetscRealPart(DolddotD), qn->powell_gamma, PetscRealPart(DolddotDold), i_r);CHKERRQ(ierr);
487         ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
488       }
489       i_r = -1;
490       /* general purpose update */
491       if (snes->ops->update) {
492         ierr = (*snes->ops->update)(snes, snes->iter);CHKERRQ(ierr);
493       }
494       if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
495         ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);CHKERRQ(ierr);
496       }
497     }
498     /* general purpose update */
499     if (snes->ops->update) {
500       ierr = (*snes->ops->update)(snes, snes->iter);CHKERRQ(ierr);
501     }
502   }
503   if (i == snes->max_its) {
504     ierr = PetscInfo1(snes, "Maximum number of iterations has been reached: %D\n", snes->max_its);CHKERRQ(ierr);
505     if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
506   }
507   PetscFunctionReturn(0);
508 }
509 
510 #undef __FUNCT__
511 #define __FUNCT__ "SNESSetUp_QN"
512 static PetscErrorCode SNESSetUp_QN(SNES snes)
513 {
514   SNES_QN        *qn = (SNES_QN*)snes->data;
515   PetscErrorCode ierr;
516 
517   PetscFunctionBegin;
518   ierr = VecDuplicateVecs(snes->vec_sol, qn->m, &qn->U);CHKERRQ(ierr);
519   ierr = VecDuplicateVecs(snes->vec_sol, qn->m, &qn->V);CHKERRQ(ierr);
520   ierr = PetscMalloc3(qn->m, PetscScalar, &qn->alpha,
521                       qn->m, PetscScalar, &qn->beta,
522                       qn->m, PetscScalar, &qn->dXtdF);CHKERRQ(ierr);
523 
524   if (qn->singlereduction) {
525     ierr = PetscMalloc3(qn->m*qn->m, PetscScalar, &qn->dXdFmat,
526                         qn->m, PetscScalar, &qn->dFtdX,
527                         qn->m, PetscScalar, &qn->YtdX);CHKERRQ(ierr);
528   }
529   ierr = SNESDefaultGetWork(snes,4);CHKERRQ(ierr);
530 
531   /* set up the line search */
532   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
533     ierr = SNESSetUpMatrices(snes);CHKERRQ(ierr);
534   }
535   PetscFunctionReturn(0);
536 }
537 
538 #undef __FUNCT__
539 #define __FUNCT__ "SNESReset_QN"
540 static PetscErrorCode SNESReset_QN(SNES snes)
541 {
542   PetscErrorCode ierr;
543   SNES_QN        *qn;
544 
545   PetscFunctionBegin;
546   if (snes->data) {
547     qn = (SNES_QN*)snes->data;
548     if (qn->U) {
549       ierr = VecDestroyVecs(qn->m, &qn->U);CHKERRQ(ierr);
550     }
551     if (qn->V) {
552       ierr = VecDestroyVecs(qn->m, &qn->V);CHKERRQ(ierr);
553     }
554     if (qn->singlereduction) {
555       ierr = PetscFree3(qn->dXdFmat, qn->dFtdX, qn->YtdX);CHKERRQ(ierr);
556     }
557     ierr = PetscFree3(qn->alpha, qn->beta, qn->dXtdF);CHKERRQ(ierr);
558   }
559   PetscFunctionReturn(0);
560 }
561 
562 #undef __FUNCT__
563 #define __FUNCT__ "SNESDestroy_QN"
564 static PetscErrorCode SNESDestroy_QN(SNES snes)
565 {
566   PetscErrorCode ierr;
567 
568   PetscFunctionBegin;
569   ierr = SNESReset_QN(snes);CHKERRQ(ierr);
570   ierr = PetscFree(snes->data);CHKERRQ(ierr);
571   ierr = PetscObjectComposeFunctionDynamic((PetscObject)snes,"","",PETSC_NULL);CHKERRQ(ierr);
572   PetscFunctionReturn(0);
573 }
574 
575 #undef __FUNCT__
576 #define __FUNCT__ "SNESSetFromOptions_QN"
577 static PetscErrorCode SNESSetFromOptions_QN(SNES snes)
578 {
579 
580   PetscErrorCode    ierr;
581   SNES_QN           *qn = (SNES_QN*)snes->data;
582   PetscBool         monflg = PETSC_FALSE,flg;
583   SNESLineSearch    linesearch;
584   SNESQNRestartType rtype = qn->restart_type;
585   SNESQNScaleType   stype = qn->scale_type;
586 
587   PetscFunctionBegin;
588   ierr = PetscOptionsHead("SNES QN options");CHKERRQ(ierr);
589   ierr = PetscOptionsInt("-snes_qn_m","Number of past states saved for L-BFGS methods","SNESQN",qn->m,&qn->m,PETSC_NULL);CHKERRQ(ierr);
590   ierr = PetscOptionsReal("-snes_qn_powell_gamma","Powell angle tolerance",          "SNESQN", qn->powell_gamma, &qn->powell_gamma, PETSC_NULL);CHKERRQ(ierr);
591   ierr = PetscOptionsReal("-snes_qn_powell_downhill","Powell descent tolerance",        "SNESQN", qn->powell_downhill, &qn->powell_downhill, PETSC_NULL);CHKERRQ(ierr);
592   ierr = PetscOptionsBool("-snes_qn_monitor",         "Monitor for the QN methods",      "SNESQN", monflg, &monflg, PETSC_NULL);CHKERRQ(ierr);
593   ierr = PetscOptionsBool("-snes_qn_single_reduction", "Aggregate reductions",           "SNESQN", qn->singlereduction, &qn->singlereduction, PETSC_NULL);CHKERRQ(ierr);
594   ierr = PetscOptionsEnum("-snes_qn_scale_type","Scaling type","SNESQNSetScaleType",SNESQNScaleTypes,(PetscEnum)stype,(PetscEnum*)&stype,&flg);CHKERRQ(ierr);
595   if (flg) ierr = SNESQNSetScaleType(snes,stype);CHKERRQ(ierr);
596 
597   ierr = PetscOptionsEnum("-snes_qn_restart_type","Restart type","SNESQNSetRestartType",SNESQNRestartTypes,(PetscEnum)rtype,(PetscEnum*)&rtype,&flg);CHKERRQ(ierr);
598   if (flg) ierr = SNESQNSetRestartType(snes,rtype);CHKERRQ(ierr);
599 
600   ierr = PetscOptionsEnum("-snes_qn_type","Quasi-Newton update type","",SNESQNTypes,
601                           (PetscEnum)qn->type,(PetscEnum*)&qn->type,PETSC_NULL);CHKERRQ(ierr);
602   ierr = PetscOptionsTail();CHKERRQ(ierr);
603   if (!snes->linesearch) {
604     ierr = SNESGetSNESLineSearch(snes, &linesearch);CHKERRQ(ierr);
605     ierr = SNESLineSearchSetType(linesearch, SNESLINESEARCHCP);CHKERRQ(ierr);
606   }
607   if (monflg) {
608     qn->monitor = PETSC_VIEWER_STDOUT_(((PetscObject)snes)->comm);CHKERRQ(ierr);
609   }
610   PetscFunctionReturn(0);
611 }
612 
613 
614 #undef __FUNCT__
615 #define __FUNCT__ "SNESQNSetRestartType"
616 /*@
617     SNESQNSetRestartType - Sets the restart type for SNESQN.
618 
619     Logically Collective on SNES
620 
621     Input Parameters:
622 +   snes - the iterative context
623 -   rtype - restart type
624 
625     Options Database:
626 +   -snes_qn_restart_type<powell,periodic,none> - set the restart type
627 -   -snes_qn_restart[10] - sets the number of iterations before restart for periodic
628 
629     Level: intermediate
630 
631     SNESQNRestartTypes:
632 +   SNES_QN_RESTART_NONE - never restart
633 .   SNES_QN_RESTART_POWELL - restart based upon descent criteria
634 -   SNES_QN_RESTART_PERIODIC - restart after a fixed number of iterations
635 
636     Notes:
637     The default line search used is the L2 line search and it requires two additional function evaluations.
638 
639 .keywords: SNES, SNESQN, restart, type, set SNESLineSearch
640 @*/
641 PetscErrorCode SNESQNSetRestartType(SNES snes, SNESQNRestartType rtype)
642 {
643   PetscErrorCode ierr;
644 
645   PetscFunctionBegin;
646   PetscValidHeaderSpecific(snes,SNES_CLASSID,1);
647   ierr = PetscTryMethod(snes,"SNESQNSetRestartType_C",(SNES,SNESQNRestartType),(snes,rtype));CHKERRQ(ierr);
648   PetscFunctionReturn(0);
649 }
650 
651 #undef __FUNCT__
652 #define __FUNCT__ "SNESQNSetScaleType"
653 /*@
654     SNESQNSetScaleType - Sets the scaling type for the inner inverse jacobian in SNESQN.
655 
656     Logically Collective on SNES
657 
658     Input Parameters:
659 +   snes - the iterative context
660 -   stype - scale type
661 
662     Options Database:
663 .   -snes_qn_scale_type<shanno,none,linesearch,jacobian>
664 
665     Level: intermediate
666 
667     SNESQNSelectTypes:
668 +   SNES_QN_SCALE_NONE - don't scale the problem
669 .   SNES_QN_SCALE_SHANNO - use shanno scaling
670 .   SNES_QN_SCALE_LINESEARCH - scale based upon line search lambda
671 -   SNES_QN_SCALE_JACOBIAN - scale by inverting a previously computed Jacobian.
672 
673 .keywords: SNES, SNESQN, scaling, type, set SNESLineSearch
674 @*/
675 
676 PetscErrorCode SNESQNSetScaleType(SNES snes, SNESQNScaleType stype)
677 {
678   PetscErrorCode ierr;
679 
680   PetscFunctionBegin;
681   PetscValidHeaderSpecific(snes,SNES_CLASSID,1);
682   ierr = PetscTryMethod(snes,"SNESQNSetScaleType_C",(SNES,SNESQNScaleType),(snes,stype));CHKERRQ(ierr);
683   PetscFunctionReturn(0);
684 }
685 
686 EXTERN_C_BEGIN
687 #undef __FUNCT__
688 #define __FUNCT__ "SNESQNSetScaleType_QN"
689 PetscErrorCode SNESQNSetScaleType_QN(SNES snes, SNESQNScaleType stype)
690 {
691   SNES_QN *qn = (SNES_QN *)snes->data;
692 
693   PetscFunctionBegin;
694   qn->scale_type = stype;
695   PetscFunctionReturn(0);
696 }
697 
698 #undef __FUNCT__
699 #define __FUNCT__ "SNESQNSetRestartType_QN"
700 PetscErrorCode SNESQNSetRestartType_QN(SNES snes, SNESQNRestartType rtype)
701 {
702   SNES_QN *qn = (SNES_QN *)snes->data;
703 
704   PetscFunctionBegin;
705   qn->restart_type = rtype;
706   PetscFunctionReturn(0);
707 }
708 EXTERN_C_END
709 
710 /* -------------------------------------------------------------------------- */
711 /*MC
712       SNESQN - Limited-Memory Quasi-Newton methods for the solution of nonlinear systems.
713 
714       Options Database:
715 
716 +     -snes_qn_m - Number of past states saved for the L-Broyden methods.
717 .     -snes_qn_powell_angle - Angle condition for restart.
718 .     -snes_qn_powell_descent - Descent condition for restart.
719 .     -snes_linesearch_type <cp, l2, basic> - Type of line search.
720 -     -snes_qn_monitor - Monitors the quasi-newton jacobian.
721 
722       Notes: This implements the L-BFGS, Broyden, and "Bad" Broyden algorithms for the solution of F(x) = b using
723       previous change in F(x) and x to form the approximate inverse Jacobian using a series of multiplicative rank-one
724       updates.
725 
726       When using a nonlinear preconditioner, one has two options as to how the preconditioner is applied.  The first of
727       these options, sequential, uses the preconditioner to generate a new solution and function and uses those at this
728       iteration as the current iteration's values when constructing the approximate jacobian.  The second, composed,
729       perturbs the problem the jacobian represents to be P(x, b) - x = 0, where P(x, b) is the preconditioner.
730 
731       References:
732 
733       L-Broyden Methods: a generalization of the L-BFGS method to the limited memory Broyden family, M. B. Reed,
734       International Journal of Computer Mathematics, vol. 86, 2009.
735 
736       Experiments with Quasi-Newton Methods in Solving Stiff ODE Systems, Peter N. Brown, Alan C. Hindmarsh, Homer F. Walker
737       SIAM J. Sci. Stat. Comput. Vol 6(2), April 1985.
738 
739 
740       Level: beginner
741 
742 .seealso:  SNESCreate(), SNES, SNESSetType(), SNESNEWTONLS, SNESNEWTONTR
743 
744 M*/
745 EXTERN_C_BEGIN
746 #undef __FUNCT__
747 #define __FUNCT__ "SNESCreate_QN"
748 PetscErrorCode  SNESCreate_QN(SNES snes)
749 {
750   PetscErrorCode ierr;
751   SNES_QN        *qn;
752 
753   PetscFunctionBegin;
754   snes->ops->setup           = SNESSetUp_QN;
755   snes->ops->solve           = SNESSolve_QN;
756   snes->ops->destroy         = SNESDestroy_QN;
757   snes->ops->setfromoptions  = SNESSetFromOptions_QN;
758   snes->ops->view            = 0;
759   snes->ops->reset           = SNESReset_QN;
760 
761   snes->usespc          = PETSC_TRUE;
762   snes->usesksp         = PETSC_FALSE;
763 
764   if (!snes->tolerancesset) {
765     snes->max_funcs = 30000;
766     snes->max_its   = 10000;
767   }
768 
769   ierr = PetscNewLog(snes,SNES_QN,&qn);CHKERRQ(ierr);
770   snes->data = (void *) qn;
771   qn->m               = 10;
772   qn->scaling         = 1.0;
773   qn->U               = PETSC_NULL;
774   qn->V               = PETSC_NULL;
775   qn->dXtdF           = PETSC_NULL;
776   qn->dFtdX           = PETSC_NULL;
777   qn->dXdFmat         = PETSC_NULL;
778   qn->monitor         = PETSC_NULL;
779   qn->singlereduction = PETSC_FALSE;
780   qn->powell_gamma    = 0.9999;
781   qn->scale_type      = SNES_QN_SCALE_SHANNO;
782   qn->restart_type    = SNES_QN_RESTART_POWELL;
783   qn->type            = SNES_QN_LBFGS;
784 
785   ierr = PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESQNSetScaleType_C","SNESQNSetScaleType_QN",SNESQNSetScaleType_QN);CHKERRQ(ierr);
786   ierr = PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESQNSetRestartType_C","SNESQNSetRestartType_QN",SNESQNSetRestartType_QN);CHKERRQ(ierr);
787   PetscFunctionReturn(0);
788 }
789 
790 EXTERN_C_END
791