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