xref: /petsc/src/snes/impls/qn/qn.c (revision 0a3905e196b1b6aa9204fa9f2e7485de9f360605)
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           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 = VecCopy(Dold,U[k]);CHKERRQ(ierr);
151     ierr = VecAXPY(U[k],-1.0,D);CHKERRQ(ierr);
152     ierr = VecCopy(Xold,T[k]);CHKERRQ(ierr);
153     ierr = VecAXPY(T[k],-1.0,X);CHKERRQ(ierr);
154   }
155 
156   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
157     ierr = KSPSolve(snes->ksp,Y,W);CHKERRQ(ierr);
158     ierr = KSPGetConvergedReason(snes->ksp,&kspreason);CHKERRQ(ierr);
159     if (kspreason < 0) {
160       if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
161         ierr         = PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);CHKERRQ(ierr);
162         snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
163         PetscFunctionReturn(0);
164       }
165     }
166     ierr              = KSPGetIterationNumber(snes->ksp,&lits);CHKERRQ(ierr);
167     snes->linear_its += lits;
168     ierr              = VecCopy(W,Y);CHKERRQ(ierr);
169   } else {
170     ierr = VecScale(Y,qn->scaling);CHKERRQ(ierr);
171   }
172 
173   /* inward recursion starting at the first update and working forward */
174   if (l > 0) {
175     for (i = 0; i < l-1; i++) {
176       k    = (it+i-l)%l;
177       j    = (it-1)%l;
178       ierr = VecDotBegin(U[k],U[j],&gdot);CHKERRQ(ierr);
179       ierr = VecDotBegin(U[k],U[k],&udot);CHKERRQ(ierr);
180       ierr = VecDotEnd(U[k],U[j],&gdot);CHKERRQ(ierr);
181       ierr = VecDotEnd(U[k],U[k],&udot);CHKERRQ(ierr);
182       ierr = VecAXPY(Y,PetscRealPart(gdot)/PetscRealPart(udot),T[k]);CHKERRQ(ierr);
183       if (qn->monitor) {
184         ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
185         ierr = PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));CHKERRQ(ierr);
186         ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
187       }
188     }
189   }
190   PetscFunctionReturn(0);
191 }
192 
193 #undef __FUNCT__
194 #define __FUNCT__ "SNESQNApply_LBFGS"
195 PetscErrorCode SNESQNApply_LBFGS(SNES snes,PetscInt it,Vec Y,Vec X,Vec Xold,Vec D,Vec Dold)
196 {
197   PetscErrorCode ierr;
198   SNES_QN        *qn    = (SNES_QN*)snes->data;
199   Vec            W      = snes->work[3];
200   Vec            *dX    = qn->U;
201   Vec            *dF    = qn->V;
202   PetscScalar    *alpha = qn->alpha;
203   PetscScalar    *beta  = qn->beta;
204   PetscScalar    *dXtdF = qn->dXtdF;
205   PetscScalar    *dFtdX = qn->dFtdX;
206   PetscScalar    *YtdX  = qn->YtdX;
207 
208   /* ksp thing for jacobian scaling */
209   KSPConvergedReason kspreason;
210   PetscInt           k,i,j,g,lits;
211   PetscInt           m = qn->m;
212   PetscScalar        t;
213   PetscInt           l = m;
214 
215   PetscFunctionBegin;
216   if (it < m) l = it;
217   ierr = VecCopy(D,Y);CHKERRQ(ierr);
218   if (it > 0) {
219     k    = (it - 1) % l;
220     ierr = VecCopy(D,dF[k]);CHKERRQ(ierr);
221     ierr = VecAXPY(dF[k], -1.0, Dold);CHKERRQ(ierr);
222     ierr = VecCopy(X, dX[k]);CHKERRQ(ierr);
223     ierr = VecAXPY(dX[k], -1.0, Xold);CHKERRQ(ierr);
224     if (qn->singlereduction) {
225       ierr = VecMDotBegin(dF[k],l,dX,dXtdF);CHKERRQ(ierr);
226       ierr = VecMDotBegin(dX[k],l,dF,dFtdX);CHKERRQ(ierr);
227       ierr = VecMDotBegin(Y,l,dX,YtdX);CHKERRQ(ierr);
228       ierr = VecMDotEnd(dF[k],l,dX,dXtdF);CHKERRQ(ierr);
229       ierr = VecMDotEnd(dX[k],l,dF,dFtdX);CHKERRQ(ierr);
230       ierr = VecMDotEnd(Y,l,dX,YtdX);CHKERRQ(ierr);
231       for (j = 0; j < l; j++) {
232         H(k, j) = dFtdX[j];
233         H(j, k) = dXtdF[j];
234       }
235       /* copy back over to make the computation of alpha and beta easier */
236       for (j = 0; j < l; j++) dXtdF[j] = H(j, j);
237     } else {
238       ierr = VecDot(dX[k], dF[k], &dXtdF[k]);CHKERRQ(ierr);
239     }
240     if (qn->scale_type == SNES_QN_SCALE_LINESEARCH) {
241       ierr = SNESLineSearchGetLambda(snes->linesearch,&qn->scaling);CHKERRQ(ierr);
242     }
243   }
244 
245   /* outward recursion starting at iteration k's update and working back */
246   for (i=0; i<l; i++) {
247     k = (it-i-1)%l;
248     if (qn->singlereduction) {
249       /* construct t = dX[k] dot Y as Y_0 dot dX[k] + sum(-alpha[j]dX[k]dF[j]) */
250       t = YtdX[k];
251       for (j=0; j<i; j++) {
252         g  = (it-j-1)%l;
253         t -= alpha[g]*H(k, g);
254       }
255       alpha[k] = t/H(k,k);
256     } else {
257       ierr     = VecDot(dX[k],Y,&t);CHKERRQ(ierr);
258       alpha[k] = t/dXtdF[k];
259     }
260     if (qn->monitor) {
261       ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
262       ierr = PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d alpha:        %14.12e\n", it, k, PetscRealPart(alpha[k]));CHKERRQ(ierr);
263       ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
264     }
265     ierr = VecAXPY(Y,-alpha[k],dF[k]);CHKERRQ(ierr);
266   }
267 
268   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
269     ierr = KSPSolve(snes->ksp,Y,W);CHKERRQ(ierr);
270     ierr = KSPGetConvergedReason(snes->ksp,&kspreason);CHKERRQ(ierr);
271     if (kspreason < 0) {
272       if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
273         ierr         = PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);CHKERRQ(ierr);
274         snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
275         PetscFunctionReturn(0);
276       }
277     }
278     ierr              = KSPGetIterationNumber(snes->ksp,&lits);CHKERRQ(ierr);
279     snes->linear_its += lits;
280     ierr              = VecCopy(W, Y);CHKERRQ(ierr);
281   } else {
282     ierr = VecScale(Y, qn->scaling);CHKERRQ(ierr);
283   }
284   if (qn->singlereduction) {
285     ierr = VecMDot(Y,l,dF,YtdX);CHKERRQ(ierr);
286   }
287   /* inward recursion starting at the first update and working forward */
288   for (i = 0; i < l; i++) {
289     k = (it + i - l) % l;
290     if (qn->singlereduction) {
291       t = YtdX[k];
292       for (j = 0; j < i; j++) {
293         g  = (it + j - l) % l;
294         t += (alpha[g] - beta[g])*H(g, k);
295       }
296       beta[k] = t / H(k, k);
297     } else {
298       ierr    = VecDot(dF[k], Y, &t);CHKERRQ(ierr);
299       beta[k] = t / dXtdF[k];
300     }
301     ierr = VecAXPY(Y, (alpha[k] - beta[k]), dX[k]);CHKERRQ(ierr);
302     if (qn->monitor) {
303       ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
304       ierr = PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d alpha - beta: %14.12e\n", it, k, PetscRealPart(alpha[k] - beta[k]));CHKERRQ(ierr);
305       ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
306     }
307   }
308   PetscFunctionReturn(0);
309 }
310 
311 #undef __FUNCT__
312 #define __FUNCT__ "SNESSolve_QN"
313 static PetscErrorCode SNESSolve_QN(SNES snes)
314 {
315   PetscErrorCode      ierr;
316   SNES_QN             *qn = (SNES_QN*) snes->data;
317   Vec                 X,Xold;
318   Vec                 F,W;
319   Vec                 Y,D,Dold;
320   PetscInt            i, i_r;
321   PetscReal           fnorm,xnorm,ynorm,gnorm;
322   PetscBool           lssucceed,powell,periodic;
323   PetscScalar         DolddotD,DolddotDold;
324   MatStructure        flg = DIFFERENT_NONZERO_PATTERN;
325   SNESConvergedReason reason;
326 
327   /* basically just a regular newton's method except for the application of the jacobian */
328 
329   PetscFunctionBegin;
330   F    = snes->vec_func;                /* residual vector */
331   Y    = snes->vec_sol_update;          /* search direction generated by J^-1D*/
332   W    = snes->work[3];
333   X    = snes->vec_sol;                 /* solution vector */
334   Xold = snes->work[0];
335 
336   /* directions generated by the preconditioned problem with F_pre = F or x - M(x, b) */
337   D    = snes->work[1];
338   Dold = snes->work[2];
339 
340   snes->reason = SNES_CONVERGED_ITERATING;
341 
342   ierr       = PetscObjectAMSTakeAccess((PetscObject)snes);CHKERRQ(ierr);
343   snes->iter = 0;
344   snes->norm = 0.;
345   ierr       = PetscObjectAMSGrantAccess((PetscObject)snes);CHKERRQ(ierr);
346 
347   if (snes->pc && snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_PRECONDITIONED) {
348     ierr = SNESApplyPC(snes,X,NULL,NULL,F);CHKERRQ(ierr);
349     ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
350     if (reason < 0  && reason != SNES_DIVERGED_MAX_IT) {
351       snes->reason = SNES_DIVERGED_INNER;
352       PetscFunctionReturn(0);
353     }
354     ierr = VecNorm(F,NORM_2,&fnorm);CHKERRQ(ierr);
355   } else {
356     if (!snes->vec_func_init_set) {
357       ierr = SNESComputeFunction(snes,X,F);CHKERRQ(ierr);
358       if (snes->domainerror) {
359         snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
360         PetscFunctionReturn(0);
361       }
362     } else snes->vec_func_init_set = PETSC_FALSE;
363 
364     ierr = VecNorm(F,NORM_2,&fnorm);CHKERRQ(ierr);
365     if (PetscIsInfOrNanReal(fnorm)) {
366       snes->reason = SNES_DIVERGED_FNORM_NAN;
367       PetscFunctionReturn(0);
368     }
369   }
370   if (snes->pc && snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_UNPRECONDITIONED) {
371       ierr = SNESApplyPC(snes,X,F,&fnorm,D);CHKERRQ(ierr);
372       ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
373       if (reason < 0  && reason != SNES_DIVERGED_MAX_IT) {
374         snes->reason = SNES_DIVERGED_INNER;
375         PetscFunctionReturn(0);
376       }
377   } else {
378     ierr = VecCopy(F,D);CHKERRQ(ierr);
379   }
380 
381   ierr       = PetscObjectAMSTakeAccess((PetscObject)snes);CHKERRQ(ierr);
382   snes->norm = fnorm;
383   ierr       = PetscObjectAMSGrantAccess((PetscObject)snes);CHKERRQ(ierr);
384   ierr       = SNESLogConvergenceHistory(snes,fnorm,0);CHKERRQ(ierr);
385   ierr       = SNESMonitor(snes,0,fnorm);CHKERRQ(ierr);
386 
387   /* test convergence */
388   ierr = (*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP);CHKERRQ(ierr);
389   if (snes->reason) PetscFunctionReturn(0);
390 
391   if (snes->pc && snes->pcside == PC_RIGHT) {
392     ierr = PetscLogEventBegin(SNES_NPCSolve,snes->pc,X,0,0);CHKERRQ(ierr);
393     ierr = SNESSolve(snes->pc,snes->vec_rhs,X);CHKERRQ(ierr);
394     ierr = PetscLogEventEnd(SNES_NPCSolve,snes->pc,X,0,0);CHKERRQ(ierr);
395     ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
396     if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
397       snes->reason = SNES_DIVERGED_INNER;
398       PetscFunctionReturn(0);
399     }
400     ierr = SNESGetPCFunction(snes,F,&fnorm);CHKERRQ(ierr);
401     ierr = VecCopy(F,D);CHKERRQ(ierr);
402   }
403 
404   /* scale the initial update */
405   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
406     ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);CHKERRQ(ierr);
407     ierr = KSPSetOperators(snes->ksp,snes->jacobian,snes->jacobian_pre,flg);CHKERRQ(ierr);
408   }
409 
410   for (i = 0, i_r = 0; i < snes->max_its; i++, i_r++) {
411     if (qn->scale_type == SNES_QN_SCALE_SHANNO && i_r > 0) {
412       PetscScalar ff,xf;
413       ierr = VecCopy(Dold,Y);CHKERRQ(ierr);
414       ierr = VecCopy(Xold,W);CHKERRQ(ierr);
415       ierr = VecAXPY(Y,-1.0,D);CHKERRQ(ierr);
416       ierr = VecAXPY(W,-1.0,X);CHKERRQ(ierr);
417       ierr = VecDotBegin(Y,Y,&ff);CHKERRQ(ierr);
418       ierr = VecDotBegin(W,Y,&xf);CHKERRQ(ierr);
419       ierr = VecDotEnd(Y,Y,&ff);CHKERRQ(ierr);
420       ierr = VecDotEnd(W,Y,&xf);CHKERRQ(ierr);
421       qn->scaling = PetscRealPart(xf)/PetscRealPart(ff);
422     }
423     switch (qn->type) {
424     case SNES_QN_BADBROYDEN:
425       ierr = SNESQNApply_BadBroyden(snes,i_r,Y,X,Xold,D,Dold);CHKERRQ(ierr);
426       break;
427     case SNES_QN_BROYDEN:
428       ierr = SNESQNApply_Broyden(snes,i_r,Y,X,Xold,D);CHKERRQ(ierr);
429       break;
430     case SNES_QN_LBFGS:
431       SNESQNApply_LBFGS(snes,i_r,Y,X,Xold,D,Dold);CHKERRQ(ierr);
432       break;
433     }
434     /* line search for lambda */
435     ynorm = 1; gnorm = fnorm;
436     ierr  = VecCopy(D, Dold);CHKERRQ(ierr);
437     ierr  = VecCopy(X, Xold);CHKERRQ(ierr);
438     ierr  = SNESLineSearchApply(snes->linesearch, X, F, &fnorm, Y);CHKERRQ(ierr);
439     if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
440     if (snes->domainerror) {
441       snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
442       PetscFunctionReturn(0);
443     }
444     ierr = SNESLineSearchGetSuccess(snes->linesearch, &lssucceed);CHKERRQ(ierr);
445     if (!lssucceed) {
446       if (++snes->numFailures >= snes->maxFailures) {
447         snes->reason = SNES_DIVERGED_LINE_SEARCH;
448         break;
449       }
450     }
451     ierr = SNESLineSearchGetNorms(snes->linesearch, &xnorm, &fnorm, &ynorm);CHKERRQ(ierr);
452     if (qn->scale_type == SNES_QN_SCALE_LINESEARCH) {
453       ierr = SNESLineSearchGetLambda(snes->linesearch, &qn->scaling);CHKERRQ(ierr);
454     }
455 
456     /* convergence monitoring */
457     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);
458 
459     if (snes->pc && snes->pcside == PC_RIGHT) {
460       ierr = PetscLogEventBegin(SNES_NPCSolve,snes->pc,X,0,0);CHKERRQ(ierr);
461       ierr = SNESSolve(snes->pc,snes->vec_rhs,X);CHKERRQ(ierr);
462       ierr = PetscLogEventEnd(SNES_NPCSolve,snes->pc,X,0,0);CHKERRQ(ierr);
463       ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
464       if (reason < 0 && reason != SNES_DIVERGED_MAX_IT) {
465         snes->reason = SNES_DIVERGED_INNER;
466         PetscFunctionReturn(0);
467       }
468       ierr = SNESGetPCFunction(snes,F,&fnorm);CHKERRQ(ierr);
469     }
470 
471     ierr = SNESSetIterationNumber(snes, i+1);CHKERRQ(ierr);
472     ierr = SNESSetFunctionNorm(snes, fnorm);CHKERRQ(ierr);
473 
474     ierr = SNESLogConvergenceHistory(snes,snes->norm,snes->iter);CHKERRQ(ierr);
475     ierr = SNESMonitor(snes,snes->iter,snes->norm);CHKERRQ(ierr);
476     /* set parameter for default relative tolerance convergence test */
477     ierr = (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);CHKERRQ(ierr);
478     if (snes->reason) PetscFunctionReturn(0);
479     if (snes->pc && snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_UNPRECONDITIONED) {
480       ierr = SNESApplyPC(snes,X,F,&fnorm,D);CHKERRQ(ierr);
481       ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
482       if (reason < 0  && reason != SNES_DIVERGED_MAX_IT) {
483         snes->reason = SNES_DIVERGED_INNER;
484         PetscFunctionReturn(0);
485       }
486     } else {
487       ierr = VecCopy(F, D);CHKERRQ(ierr);
488     }
489     powell = PETSC_FALSE;
490     if (qn->restart_type == SNES_QN_RESTART_POWELL) {
491       /* check restart by Powell's Criterion: |F^T H_0 Fold| > 0.2 * |Fold^T H_0 Fold| */
492       ierr = VecDotBegin(Dold, Dold, &DolddotDold);CHKERRQ(ierr);
493       ierr = VecDotBegin(Dold, D, &DolddotD);CHKERRQ(ierr);
494       ierr = VecDotEnd(Dold, Dold, &DolddotDold);CHKERRQ(ierr);
495       ierr = VecDotEnd(Dold, D, &DolddotD);CHKERRQ(ierr);
496       if (PetscAbs(PetscRealPart(DolddotD)) > qn->powell_gamma*PetscAbs(PetscRealPart(DolddotDold))) powell = PETSC_TRUE;
497     }
498     periodic = PETSC_FALSE;
499     if (qn->restart_type == SNES_QN_RESTART_PERIODIC) {
500       if (i_r>qn->m-1) periodic = PETSC_TRUE;
501     }
502     /* restart if either powell or periodic restart is satisfied. */
503     if (powell || periodic) {
504       if (qn->monitor) {
505         ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
506         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);
507         ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
508       }
509       i_r = -1;
510       /* general purpose update */
511       if (snes->ops->update) {
512         ierr = (*snes->ops->update)(snes, snes->iter);CHKERRQ(ierr);
513       }
514       if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
515         ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);CHKERRQ(ierr);
516         ierr = KSPSetOperators(snes->ksp,snes->jacobian,snes->jacobian_pre,flg);CHKERRQ(ierr);
517       }
518     }
519     /* general purpose update */
520     if (snes->ops->update) {
521       ierr = (*snes->ops->update)(snes, snes->iter);CHKERRQ(ierr);
522     }
523   }
524   if (i == snes->max_its) {
525     ierr = PetscInfo1(snes, "Maximum number of iterations has been reached: %D\n", snes->max_its);CHKERRQ(ierr);
526     if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
527   }
528   PetscFunctionReturn(0);
529 }
530 
531 #undef __FUNCT__
532 #define __FUNCT__ "SNESSetUp_QN"
533 static PetscErrorCode SNESSetUp_QN(SNES snes)
534 {
535   SNES_QN        *qn = (SNES_QN*)snes->data;
536   PetscErrorCode ierr;
537 
538   PetscFunctionBegin;
539   ierr = VecDuplicateVecs(snes->vec_sol, qn->m, &qn->U);CHKERRQ(ierr);
540   if (qn->type != SNES_QN_BROYDEN) ierr = VecDuplicateVecs(snes->vec_sol, qn->m, &qn->V);CHKERRQ(ierr);
541   ierr = PetscMalloc4(qn->m, PetscScalar, &qn->alpha,
542                       qn->m, PetscScalar, &qn->beta,
543                       qn->m, PetscScalar, &qn->dXtdF,
544                       qn->m, PetscReal, &qn->lambda);CHKERRQ(ierr);
545 
546   if (qn->singlereduction) {
547     ierr = PetscMalloc3(qn->m*qn->m, PetscScalar, &qn->dXdFmat,
548                         qn->m, PetscScalar, &qn->dFtdX,
549                         qn->m, PetscScalar, &qn->YtdX);CHKERRQ(ierr);
550   }
551   ierr = SNESSetWorkVecs(snes,4);CHKERRQ(ierr);
552 
553   /* set up the line search */
554   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
555     ierr = SNESSetUpMatrices(snes);CHKERRQ(ierr);
556   }
557 
558   if (snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_DEFAULT) {snes->functype = SNES_FUNCTION_UNPRECONDITIONED;}
559 
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