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