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