xref: /petsc/src/snes/impls/qn/qn.c (revision 05ee83fe610054268d48d6f49b2c021ba5138ac7)
10c777b0cSPeter Brune #include <petsc-private/snesimpl.h> /*I "petscsnes.h" I*/
24b11644fSPeter Brune 
38e231d97SPeter Brune #define H(i,j)  qn->dXdFmat[i*qn->m + j]
48e231d97SPeter Brune 
56a6fc655SJed Brown const char *const SNESQNCompositionTypes[] =  {"SEQUENTIAL","COMPOSED","SNESQNCompositionType","SNES_QN_",0};
66a6fc655SJed Brown const char *const SNESQNScaleTypes[] =        {"NONE","SHANNO","LINESEARCH","JACOBIAN","SNESQNScaleType","SNES_QN_SCALING_",0};
76a6fc655SJed Brown const char *const SNESQNRestartTypes[] =      {"NONE","POWELL","PERIODIC","SNESQNRestartType","SNES_QN_RESTART_",0};
8b8840d6bSPeter Brune const char *const SNESQNTypes[] =             {"LBFGS","BROYDEN","BADBROYDEN","SNESQNType","SNES_QN_RESTART_",0};
9b8840d6bSPeter Brune 
10b8840d6bSPeter Brune typedef enum {SNES_QN_LBFGS      = 0,
11b8840d6bSPeter Brune               SNES_QN_BROYDEN    = 1,
12b8840d6bSPeter Brune               SNES_QN_BADBROYDEN = 2} SNESQNType;
136bf1b2e5SPeter Brune 
144b11644fSPeter Brune typedef struct {
15b8840d6bSPeter Brune   Vec          *U;               /* Stored past states (vary from method to method) */
16b8840d6bSPeter Brune   Vec          *V;               /* Stored past states (vary from method to method) */
178e231d97SPeter Brune   PetscInt     m;                /* The number of kept previous steps */
188e231d97SPeter Brune   PetscScalar  *alpha, *beta;
198e231d97SPeter Brune   PetscScalar  *dXtdF, *dFtdX, *YtdX;
2018aa0c0cSPeter Brune   PetscBool    singlereduction;  /* Aggregated reduction implementation */
218e231d97SPeter Brune   PetscScalar  *dXdFmat;         /* A matrix of values for dX_i dot dF_j */
2244f7e39eSPeter Brune   PetscViewer  monitor;
236bf1b2e5SPeter Brune   PetscReal    powell_gamma;     /* Powell angle restart condition */
246bf1b2e5SPeter Brune   PetscReal    powell_downhill;  /* Powell descent restart condition */
25b21d5a53SPeter Brune   PetscReal    scaling;          /* scaling of H0 */
2688d374b2SPeter Brune 
27b8840d6bSPeter Brune   SNESQNType            type;             /* the type of quasi-newton method used */
280c777b0cSPeter Brune   SNESQNCompositionType composition_type; /* determine if the composition is done sequentially or as a composition */
290c777b0cSPeter Brune   SNESQNScaleType       scale_type;       /* determine if the composition is done sequentially or as a composition */
300c777b0cSPeter Brune   SNESQNRestartType     restart_type;     /* determine the frequency and type of restart conditions */
319f83bee8SJed Brown } SNES_QN;
324b11644fSPeter Brune 
334b11644fSPeter Brune #undef __FUNCT__
34b8840d6bSPeter Brune #define __FUNCT__ "SNESQNApply_Broyden"
35b8840d6bSPeter Brune PetscErrorCode SNESQNApply_Broyden(SNES snes,PetscInt it,Vec Y,Vec X,Vec Xold, Vec D, Vec Dold) {
364b11644fSPeter Brune 
374b11644fSPeter Brune   PetscErrorCode ierr;
384b11644fSPeter Brune 
399f83bee8SJed Brown   SNES_QN *qn = (SNES_QN*)snes->data;
404b11644fSPeter Brune 
41b8840d6bSPeter Brune   Vec W = snes->work[3];
420ecc9e1dSPeter Brune 
43b8840d6bSPeter Brune   Vec *U = qn->U;
44b8840d6bSPeter Brune   Vec *V = qn->V;
45b8840d6bSPeter Brune 
46b8840d6bSPeter Brune   /* ksp thing for jacobian scaling */
47b8840d6bSPeter Brune   KSPConvergedReason kspreason;
48b8840d6bSPeter Brune   MatStructure       flg = DIFFERENT_NONZERO_PATTERN;
49b8840d6bSPeter Brune 
50b8840d6bSPeter Brune   PetscInt k,i,lits;
51b8840d6bSPeter Brune   PetscInt m = qn->m;
52b8840d6bSPeter Brune   PetscScalar gdot;
53b8840d6bSPeter Brune   PetscInt l = m;
54b8840d6bSPeter Brune 
55b8840d6bSPeter Brune   Mat jac,jac_pre;
56b8840d6bSPeter Brune   PetscFunctionBegin;
57b8840d6bSPeter Brune 
58b8840d6bSPeter Brune   if (it < m) l = it;
59b8840d6bSPeter Brune 
60b8840d6bSPeter Brune   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
61b8840d6bSPeter Brune     ierr = SNESGetJacobian(snes,&jac,&jac_pre,PETSC_NULL,PETSC_NULL);CHKERRQ(ierr);
62b8840d6bSPeter Brune     ierr = KSPSetOperators(snes->ksp,jac,jac_pre,flg);CHKERRQ(ierr);
63b8840d6bSPeter Brune     ierr = SNES_KSPSolve(snes,snes->ksp,D,W);CHKERRQ(ierr);
64b8840d6bSPeter Brune     ierr = KSPGetConvergedReason(snes->ksp,&kspreason);CHKERRQ(ierr);
65b8840d6bSPeter Brune     if (kspreason < 0) {
66b8840d6bSPeter Brune       if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
67b8840d6bSPeter Brune         ierr = PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);CHKERRQ(ierr);
68b8840d6bSPeter Brune         snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
69b8840d6bSPeter Brune         PetscFunctionReturn(0);
70b8840d6bSPeter Brune       }
71b8840d6bSPeter Brune     }
72b8840d6bSPeter Brune     ierr = KSPGetIterationNumber(snes->ksp,&lits);CHKERRQ(ierr);
73b8840d6bSPeter Brune     snes->linear_its += lits;
74b8840d6bSPeter Brune     ierr = VecCopy(W,Y);CHKERRQ(ierr);
75b8840d6bSPeter Brune   } else {
76b8840d6bSPeter Brune     ierr = VecCopy(D,Y);CHKERRQ(ierr);
77b8840d6bSPeter Brune     ierr = VecScale(Y,qn->scaling);CHKERRQ(ierr);
78b8840d6bSPeter Brune   }
79b8840d6bSPeter Brune 
80b8840d6bSPeter Brune   /* inward recursion starting at the first update and working forward */
81b8840d6bSPeter Brune   if (it > 1) {
82b8840d6bSPeter Brune     for (i = 0; i < l-1; i++) {
83b8840d6bSPeter Brune       k = (it+i-l)%l;
84b8840d6bSPeter Brune       ierr = VecDot(U[k],Y,&gdot);CHKERRQ(ierr);
85b8840d6bSPeter Brune       ierr = VecAXPY(Y,gdot,V[k]);CHKERRQ(ierr);
86b8840d6bSPeter Brune       if (qn->monitor) {
87b8840d6bSPeter Brune         ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
88b8840d6bSPeter Brune         ierr = PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));CHKERRQ(ierr);
89b8840d6bSPeter Brune         ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
90b8840d6bSPeter Brune       }
91b8840d6bSPeter Brune     }
92b8840d6bSPeter Brune   }
93b8840d6bSPeter Brune   if (it < m) l = it;
94b8840d6bSPeter Brune 
95b8840d6bSPeter Brune   /* set W to be the last step, post-linesearch */
96b8840d6bSPeter Brune   ierr = VecCopy(Xold,W);CHKERRQ(ierr);
97b8840d6bSPeter Brune   ierr = VecAXPY(W,-1.0,X);CHKERRQ(ierr);
98b8840d6bSPeter Brune 
99b8840d6bSPeter Brune   if (l > 0) {
100b8840d6bSPeter Brune     k = (it-1)%l;
101b8840d6bSPeter Brune     ierr = VecCopy(W,U[k]);CHKERRQ(ierr);
102b8840d6bSPeter Brune     ierr = VecAXPY(W,-1.0,Y);CHKERRQ(ierr);
103b8840d6bSPeter Brune     ierr = VecDot(U[k],W,&gdot);CHKERRQ(ierr);
104b8840d6bSPeter Brune     ierr = VecCopy(Y,V[k]);CHKERRQ(ierr);
105b8840d6bSPeter Brune     ierr = VecScale(V[k],1.0/gdot);CHKERRQ(ierr);
106b8840d6bSPeter Brune     ierr = VecDot(U[k],Y,&gdot);CHKERRQ(ierr);
107b8840d6bSPeter Brune     ierr = VecAXPY(Y,gdot,V[k]);CHKERRQ(ierr);
108b8840d6bSPeter Brune     if (qn->monitor) {
109b8840d6bSPeter Brune       ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
110b8840d6bSPeter Brune       ierr = PetscViewerASCIIPrintf(qn->monitor, "update: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));CHKERRQ(ierr);
111b8840d6bSPeter Brune       ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
112b8840d6bSPeter Brune     }
113b8840d6bSPeter Brune   }
114b8840d6bSPeter Brune   PetscFunctionReturn(0);
115b8840d6bSPeter Brune }
116b8840d6bSPeter Brune 
117b8840d6bSPeter Brune #undef __FUNCT__
118b8840d6bSPeter Brune #define __FUNCT__ "SNESQNApply_BadBroyden"
119b8840d6bSPeter Brune PetscErrorCode SNESQNApply_BadBroyden(SNES snes,PetscInt it,Vec Y,Vec X,Vec Xold,Vec D,Vec Dold) {
120b8840d6bSPeter Brune 
121b8840d6bSPeter Brune   PetscErrorCode ierr;
122b8840d6bSPeter Brune 
123b8840d6bSPeter Brune   SNES_QN *qn = (SNES_QN*)snes->data;
124b8840d6bSPeter Brune 
125b8840d6bSPeter Brune   Vec W = snes->work[3];
126b8840d6bSPeter Brune 
127b8840d6bSPeter Brune   Vec *U = qn->U;
128b8840d6bSPeter Brune   Vec *T = qn->V;
129b8840d6bSPeter Brune 
130b8840d6bSPeter Brune   /* ksp thing for jacobian scaling */
131b8840d6bSPeter Brune   KSPConvergedReason kspreason;
132b8840d6bSPeter Brune   MatStructure       flg = DIFFERENT_NONZERO_PATTERN;
133b8840d6bSPeter Brune 
134b8840d6bSPeter Brune   PetscInt k, i, lits;
135b8840d6bSPeter Brune   PetscInt m = qn->m;
136b8840d6bSPeter Brune   PetscScalar gdot;
137b8840d6bSPeter Brune   PetscInt l = m;
138b8840d6bSPeter Brune 
139b8840d6bSPeter Brune   Mat jac, jac_pre;
140b8840d6bSPeter Brune   PetscFunctionBegin;
141b8840d6bSPeter Brune 
142b8840d6bSPeter Brune   if (it < m) l = it;
143b8840d6bSPeter Brune 
144b8840d6bSPeter Brune   ierr = VecCopy(D,Y);CHKERRQ(ierr);
145b8840d6bSPeter Brune 
146b8840d6bSPeter Brune   if (l > 0) {
147b8840d6bSPeter Brune     k = (it-1)%l;
148b8840d6bSPeter Brune     ierr = VecCopy(Dold,U[k]);CHKERRQ(ierr);
149b8840d6bSPeter Brune     ierr = VecAXPY(U[k],-1.0,D);CHKERRQ(ierr);
150b8840d6bSPeter Brune     ierr = VecDot(U[k],U[k],&gdot);CHKERRQ(ierr);
151b8840d6bSPeter Brune     ierr = VecCopy(D,T[k]);CHKERRQ(ierr);
152b8840d6bSPeter Brune     ierr = VecScale(T[k],1.0/gdot);CHKERRQ(ierr);
153b8840d6bSPeter Brune     ierr = VecDot(U[k],Y,&gdot);CHKERRQ(ierr);
154b8840d6bSPeter Brune     ierr = VecAXPY(Y,gdot,T[k]);CHKERRQ(ierr);
155b8840d6bSPeter Brune     if (qn->monitor) {
156b8840d6bSPeter Brune       ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
157b8840d6bSPeter Brune       ierr = PetscViewerASCIIPrintf(qn->monitor, "update: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));CHKERRQ(ierr);
158b8840d6bSPeter Brune       ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
159b8840d6bSPeter Brune     }
160b8840d6bSPeter Brune   }
161b8840d6bSPeter Brune 
162b8840d6bSPeter Brune   /* inward recursion starting at the first update and working forward */
163b8840d6bSPeter Brune   if (it > 1) {
164b8840d6bSPeter Brune     for (i = 0; i < l-1; i++) {
165b8840d6bSPeter Brune       k = (it+i-l)%l;
166b8840d6bSPeter Brune       ierr = VecDot(U[k],Y,&gdot);CHKERRQ(ierr);
167b8840d6bSPeter Brune       ierr = VecAXPY(Y,gdot,T[k]);CHKERRQ(ierr);
168b8840d6bSPeter Brune       if (qn->monitor) {
169b8840d6bSPeter Brune         ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
170b8840d6bSPeter Brune         ierr = PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d gdot: %14.12e\n", it, k, PetscRealPart(gdot));CHKERRQ(ierr);
171b8840d6bSPeter Brune         ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
172b8840d6bSPeter Brune       }
173b8840d6bSPeter Brune     }
174b8840d6bSPeter Brune   }
175b8840d6bSPeter Brune 
176b8840d6bSPeter Brune   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
177b8840d6bSPeter Brune     ierr = SNESGetJacobian(snes, &jac, &jac_pre, PETSC_NULL, PETSC_NULL);CHKERRQ(ierr);
178b8840d6bSPeter Brune     ierr = KSPSetOperators(snes->ksp,jac,jac_pre,flg);CHKERRQ(ierr);
179b8840d6bSPeter Brune     ierr = SNES_KSPSolve(snes,snes->ksp,Y,W);CHKERRQ(ierr);
180b8840d6bSPeter Brune     ierr = KSPGetConvergedReason(snes->ksp,&kspreason);CHKERRQ(ierr);
181b8840d6bSPeter Brune     if (kspreason < 0) {
182b8840d6bSPeter Brune       if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
183b8840d6bSPeter Brune         ierr = PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);CHKERRQ(ierr);
184b8840d6bSPeter Brune         snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
185b8840d6bSPeter Brune         PetscFunctionReturn(0);
186b8840d6bSPeter Brune       }
187b8840d6bSPeter Brune     }
188b8840d6bSPeter Brune     ierr = KSPGetIterationNumber(snes->ksp,&lits);CHKERRQ(ierr);
189b8840d6bSPeter Brune     snes->linear_its += lits;
190b8840d6bSPeter Brune     ierr = VecCopy(W,Y);CHKERRQ(ierr);
191b8840d6bSPeter Brune   }  else {
192b8840d6bSPeter Brune     ierr = VecScale(Y,qn->scaling);CHKERRQ(ierr);
193b8840d6bSPeter Brune   }
194b8840d6bSPeter Brune   PetscFunctionReturn(0);
195b8840d6bSPeter Brune }
196b8840d6bSPeter Brune 
197b8840d6bSPeter Brune #undef __FUNCT__
198b8840d6bSPeter Brune #define __FUNCT__ "SNESQNApply_LBFGS"
199b8840d6bSPeter Brune PetscErrorCode SNESQNApply_LBFGS(SNES snes,PetscInt it,Vec Y,Vec X,Vec Xold,Vec D,Vec Dold) {
200b8840d6bSPeter Brune 
201b8840d6bSPeter Brune   PetscErrorCode ierr;
202b8840d6bSPeter Brune 
203b8840d6bSPeter Brune   SNES_QN *qn = (SNES_QN*)snes->data;
204b8840d6bSPeter Brune 
205b8840d6bSPeter Brune   Vec W = snes->work[3];
206b8840d6bSPeter Brune 
207b8840d6bSPeter Brune   Vec *dX = qn->U;
208b8840d6bSPeter Brune   Vec *dF = qn->V;
2094b11644fSPeter Brune 
210bd052dfeSPeter Brune   PetscScalar *alpha    = qn->alpha;
211bd052dfeSPeter Brune   PetscScalar *beta     = qn->beta;
2128e231d97SPeter Brune   PetscScalar *dXtdF    = qn->dXtdF;
213b8840d6bSPeter Brune   PetscScalar *dFtdX    = qn->dFtdX;
2148e231d97SPeter Brune   PetscScalar *YtdX     = qn->YtdX;
215bd052dfeSPeter Brune 
2160ecc9e1dSPeter Brune   /* ksp thing for jacobian scaling */
2170ecc9e1dSPeter Brune   KSPConvergedReason kspreason;
2180ecc9e1dSPeter Brune   MatStructure       flg = DIFFERENT_NONZERO_PATTERN;
2190ecc9e1dSPeter Brune 
2208e231d97SPeter Brune   PetscInt k,i,j,g,lits;
2214b11644fSPeter Brune   PetscInt m = qn->m;
2224b11644fSPeter Brune   PetscScalar t;
2234b11644fSPeter Brune   PetscInt l = m;
2244b11644fSPeter Brune 
2258e231d97SPeter Brune   Mat jac,jac_pre;
2268e231d97SPeter Brune 
2274b11644fSPeter Brune   PetscFunctionBegin;
2284b11644fSPeter Brune 
2295ba6227bSPeter Brune   if (it < m) l = it;
2304b11644fSPeter Brune 
231b8840d6bSPeter Brune   if (it > 0) {
232b8840d6bSPeter Brune     k = (it - 1) % l;
233b8840d6bSPeter Brune     ierr = VecCopy(D, dF[k]);CHKERRQ(ierr);
234b8840d6bSPeter Brune     ierr = VecAXPY(dF[k], -1.0, Dold);CHKERRQ(ierr);
235b8840d6bSPeter Brune     ierr = VecCopy(X, dX[k]);CHKERRQ(ierr);
236b8840d6bSPeter Brune     ierr = VecAXPY(dX[k], -1.0, Xold);CHKERRQ(ierr);
23718aa0c0cSPeter Brune     if (qn->singlereduction) {
238b8840d6bSPeter Brune       ierr = VecMDot(dF[k], l, dX, dXtdF);CHKERRQ(ierr);
239b8840d6bSPeter Brune       ierr = VecMDot(dX[k], l, dF, dFtdX);CHKERRQ(ierr);
240b8840d6bSPeter Brune       for (j = 0; j < l; j++) {
241b8840d6bSPeter Brune         H(k, j) = dFtdX[j];
242b8840d6bSPeter Brune         H(j, k) = dXtdF[j];
243b8840d6bSPeter Brune       }
244b8840d6bSPeter Brune       /* copy back over to make the computation of alpha and beta easier */
245b8840d6bSPeter Brune       for (j = 0; j < l; j++) {
246b8840d6bSPeter Brune         dXtdF[j] = H(j, j);
247b8840d6bSPeter Brune       }
248b8840d6bSPeter Brune     } else {
249b8840d6bSPeter Brune       ierr = VecDot(dX[k], dF[k], &dXtdF[k]);CHKERRQ(ierr);
250b8840d6bSPeter Brune     }
251b8840d6bSPeter Brune     if (qn->scale_type == SNES_QN_SCALE_SHANNO) {
252b8840d6bSPeter Brune       PetscScalar dFtdF;
253b8840d6bSPeter Brune       ierr = VecDot(dF[k],dF[k],&dFtdF);CHKERRQ(ierr);
254b8840d6bSPeter Brune       qn->scaling = PetscRealPart(dXtdF[k]) / PetscRealPart(dFtdF);
255b8840d6bSPeter Brune     } else if (qn->scale_type == SNES_QN_SCALE_LINESEARCH) {
256b8840d6bSPeter Brune       ierr = SNESLineSearchGetLambda(snes->linesearch,&qn->scaling);CHKERRQ(ierr);
257b8840d6bSPeter Brune     }
258b8840d6bSPeter Brune   }
259b8840d6bSPeter Brune 
260b8840d6bSPeter Brune   ierr = VecCopy(D,Y);CHKERRQ(ierr);
261b8840d6bSPeter Brune 
262b8840d6bSPeter Brune   if (qn->singlereduction) {
263b8840d6bSPeter Brune     ierr = VecMDot(Y,l,dX,YtdX);CHKERRQ(ierr);
2648e231d97SPeter Brune   }
2654b11644fSPeter Brune   /* outward recursion starting at iteration k's update and working back */
2664b11644fSPeter Brune   for (i=0;i<l;i++) {
267b21d5a53SPeter Brune     k = (it-i-1)%l;
26818aa0c0cSPeter Brune     if (qn->singlereduction) {
2698e231d97SPeter Brune       /* construct t = dX[k] dot Y as Y_0 dot dX[k] + sum(-alpha[j]dX[k]dF[j]) */
2708e231d97SPeter Brune       t = YtdX[k];
2718e231d97SPeter Brune       for (j=0;j<i;j++) {
2728e231d97SPeter Brune         g = (it-j-1)%l;
2738e231d97SPeter Brune         t += -alpha[g]*H(g, k);
2748e231d97SPeter Brune       }
2758e231d97SPeter Brune       alpha[k] = t/H(k,k);
2768e231d97SPeter Brune     } else {
2773af51624SPeter Brune       ierr = VecDot(dX[k],Y,&t);CHKERRQ(ierr);
2788e231d97SPeter Brune       alpha[k] = t/dXtdF[k];
2798e231d97SPeter Brune     }
28044f7e39eSPeter Brune     if (qn->monitor) {
2813af51624SPeter Brune       ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
2825ba6227bSPeter Brune       ierr = PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d alpha:        %14.12e\n", it, k, PetscRealPart(alpha[k]));CHKERRQ(ierr);
2833af51624SPeter Brune       ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
28444f7e39eSPeter Brune     }
2856bf1b2e5SPeter Brune     ierr = VecAXPY(Y,-alpha[k],dF[k]);CHKERRQ(ierr);
2864b11644fSPeter Brune   }
2874b11644fSPeter Brune 
2880c777b0cSPeter Brune   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
2898e231d97SPeter Brune     ierr = SNESGetJacobian(snes, &jac, &jac_pre, PETSC_NULL, PETSC_NULL);CHKERRQ(ierr);
2908e231d97SPeter Brune     ierr = KSPSetOperators(snes->ksp,jac,jac_pre,flg);CHKERRQ(ierr);
291b8840d6bSPeter Brune     ierr = SNES_KSPSolve(snes,snes->ksp,Y,W);CHKERRQ(ierr);
2920ecc9e1dSPeter Brune     ierr = KSPGetConvergedReason(snes->ksp,&kspreason);CHKERRQ(ierr);
2930ecc9e1dSPeter Brune     if (kspreason < 0) {
2940ecc9e1dSPeter Brune       if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
2950ecc9e1dSPeter Brune         ierr = PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);CHKERRQ(ierr);
2960ecc9e1dSPeter Brune         snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
2970ecc9e1dSPeter Brune         PetscFunctionReturn(0);
2980ecc9e1dSPeter Brune       }
2990ecc9e1dSPeter Brune     }
3000ecc9e1dSPeter Brune     ierr = KSPGetIterationNumber(snes->ksp,&lits);CHKERRQ(ierr);
3010ecc9e1dSPeter Brune     snes->linear_its += lits;
302b8840d6bSPeter Brune     ierr = VecCopy(W, Y);CHKERRQ(ierr);
3030ecc9e1dSPeter Brune   } else {
304b21d5a53SPeter Brune     ierr = VecScale(Y, qn->scaling);CHKERRQ(ierr);
3050ecc9e1dSPeter Brune   }
30618aa0c0cSPeter Brune   if (qn->singlereduction) {
307b8840d6bSPeter Brune     ierr = VecMDot(Y,l,dF,YtdX);CHKERRQ(ierr);
3088e231d97SPeter Brune   }
3094b11644fSPeter Brune   /* inward recursion starting at the first update and working forward */
310bd052dfeSPeter Brune   for (i = 0; i < l; i++) {
311b21d5a53SPeter Brune     k = (it + i - l) % l;
31218aa0c0cSPeter Brune     if (qn->singlereduction) {
3138e231d97SPeter Brune       t = YtdX[k];
3148e231d97SPeter Brune       for (j = 0; j < i; j++) {
3158e231d97SPeter Brune         g = (it + j - l) % l;
3168e231d97SPeter Brune         t += (alpha[g] - beta[g])*H(k, g);
3178e231d97SPeter Brune       }
3188e231d97SPeter Brune       beta[k] = t / H(k, k);
3198e231d97SPeter Brune     } else {
3206bf1b2e5SPeter Brune       ierr = VecDot(dF[k], Y, &t);CHKERRQ(ierr);
3218e231d97SPeter Brune       beta[k] = t / dXtdF[k];
3228e231d97SPeter Brune     }
3233af51624SPeter Brune     ierr = VecAXPY(Y, (alpha[k] - beta[k]), dX[k]);
32444f7e39eSPeter Brune     if (qn->monitor) {
3253af51624SPeter Brune       ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
3265ba6227bSPeter Brune       ierr = PetscViewerASCIIPrintf(qn->monitor, "it: %d k: %d alpha - beta: %14.12e\n", it, k, PetscRealPart(alpha[k] - beta[k]));CHKERRQ(ierr);
3273af51624SPeter Brune       ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
32844f7e39eSPeter Brune     }
3294b11644fSPeter Brune   }
3304b11644fSPeter Brune   PetscFunctionReturn(0);
3314b11644fSPeter Brune }
3324b11644fSPeter Brune 
3334b11644fSPeter Brune #undef __FUNCT__
3344b11644fSPeter Brune #define __FUNCT__ "SNESSolve_QN"
3354b11644fSPeter Brune static PetscErrorCode SNESSolve_QN(SNES snes)
3364b11644fSPeter Brune {
3374b11644fSPeter Brune 
3384b11644fSPeter Brune   PetscErrorCode ierr;
3399f83bee8SJed Brown   SNES_QN *qn = (SNES_QN*) snes->data;
3404b11644fSPeter Brune 
34115f5eeeaSPeter Brune   Vec X,Xold;
342335fb975SPeter Brune   Vec F,B;
343335fb975SPeter Brune   Vec Y,FPC,D,Dold;
3443af51624SPeter Brune   SNESConvergedReason reason;
345b8840d6bSPeter Brune   PetscInt i, i_r;
3464b11644fSPeter Brune 
347335fb975SPeter Brune   PetscReal fnorm,xnorm,ynorm,gnorm;
3480c777b0cSPeter Brune   PetscBool lssucceed,powell,periodic;
3494b11644fSPeter Brune 
350b8840d6bSPeter Brune   PetscScalar DolddotD,DolddotDold,DdotD,YdotD;
3514b11644fSPeter Brune 
3520ecc9e1dSPeter Brune   MatStructure       flg = DIFFERENT_NONZERO_PATTERN;
3530ecc9e1dSPeter Brune 
3544b11644fSPeter Brune   /* basically just a regular newton's method except for the application of the jacobian */
3554b11644fSPeter Brune   PetscFunctionBegin;
3564b11644fSPeter Brune 
3579f3a0142SPeter Brune   F             = snes->vec_func;       /* residual vector */
3583af51624SPeter Brune   Y             = snes->vec_sol_update; /* search direction generated by J^-1D*/
3593af51624SPeter Brune   B             = snes->vec_rhs;
360b8840d6bSPeter Brune 
361b8840d6bSPeter Brune   X             = snes->vec_sol;        /* solution vector */
362335fb975SPeter Brune   Xold          = snes->work[0];
3633af51624SPeter Brune 
3643af51624SPeter Brune   /* directions generated by the preconditioned problem with F_pre = F or x - M(x, b) */
365335fb975SPeter Brune   D             = snes->work[1];
366335fb975SPeter Brune   Dold          = snes->work[2];
3674b11644fSPeter Brune 
3684b11644fSPeter Brune   snes->reason = SNES_CONVERGED_ITERATING;
3694b11644fSPeter Brune 
3704b11644fSPeter Brune   ierr = PetscObjectTakeAccess(snes);CHKERRQ(ierr);
3714b11644fSPeter Brune   snes->iter = 0;
3724b11644fSPeter Brune   snes->norm = 0.;
3734b11644fSPeter Brune   ierr = PetscObjectGrantAccess(snes);CHKERRQ(ierr);
374e4ed7901SPeter Brune   if (!snes->vec_func_init_set){
37515f5eeeaSPeter Brune     ierr = SNESComputeFunction(snes,X,F);CHKERRQ(ierr);
3764b11644fSPeter Brune     if (snes->domainerror) {
3774b11644fSPeter Brune       snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
3784b11644fSPeter Brune       PetscFunctionReturn(0);
3794b11644fSPeter Brune     }
380e4ed7901SPeter Brune   } else {
381e4ed7901SPeter Brune     snes->vec_func_init_set = PETSC_FALSE;
382e4ed7901SPeter Brune   }
383e4ed7901SPeter Brune 
384e4ed7901SPeter Brune   if (!snes->norm_init_set) {
38515f5eeeaSPeter Brune     ierr = VecNorm(F, NORM_2, &fnorm);CHKERRQ(ierr); /* fnorm <- ||F||  */
3864b11644fSPeter Brune     if (PetscIsInfOrNanReal(fnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"Infinite or not-a-number generated in norm");
387e4ed7901SPeter Brune   } else {
388e4ed7901SPeter Brune     fnorm = snes->norm_init;
389e4ed7901SPeter Brune     snes->norm_init_set = PETSC_FALSE;
390e4ed7901SPeter Brune   }
391e4ed7901SPeter Brune 
3924b11644fSPeter Brune   ierr = PetscObjectTakeAccess(snes);CHKERRQ(ierr);
3934b11644fSPeter Brune   snes->norm = fnorm;
3944b11644fSPeter Brune   ierr = PetscObjectGrantAccess(snes);CHKERRQ(ierr);
3954b11644fSPeter Brune   SNESLogConvHistory(snes,fnorm,0);
3964b11644fSPeter Brune   ierr = SNESMonitor(snes,0,fnorm);CHKERRQ(ierr);
3974b11644fSPeter Brune 
3984b11644fSPeter Brune   /* set parameter for default relative tolerance convergence test */
3994b11644fSPeter Brune    snes->ttol = fnorm*snes->rtol;
4004b11644fSPeter Brune   /* test convergence */
4014b11644fSPeter Brune   ierr = (*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP);CHKERRQ(ierr);
4024b11644fSPeter Brune   if (snes->reason) PetscFunctionReturn(0);
40370d3b23bSPeter Brune 
40488d374b2SPeter Brune   /* composed solve -- either sequential or composed */
40588d374b2SPeter Brune   if (snes->pc) {
4060c777b0cSPeter Brune     if (qn->composition_type == SNES_QN_SEQUENTIAL) {
407217b9c2eSPeter Brune       ierr = SNESSetInitialFunction(snes->pc, F);CHKERRQ(ierr);
408217b9c2eSPeter Brune       ierr = SNESSetInitialFunctionNorm(snes->pc, fnorm);CHKERRQ(ierr);
40988d374b2SPeter Brune       ierr = SNESSolve(snes->pc, B, X);CHKERRQ(ierr);
41088d374b2SPeter Brune       ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
41188d374b2SPeter Brune       if (reason < 0 && (reason != SNES_DIVERGED_MAX_IT)) {
41288d374b2SPeter Brune         snes->reason = SNES_DIVERGED_INNER;
41388d374b2SPeter Brune         PetscFunctionReturn(0);
41488d374b2SPeter Brune       }
41588d374b2SPeter Brune       ierr = SNESGetFunction(snes->pc, &FPC, PETSC_NULL, PETSC_NULL);CHKERRQ(ierr);
41688d374b2SPeter Brune       ierr = VecCopy(FPC, F);CHKERRQ(ierr);
41788d374b2SPeter Brune       ierr = SNESGetFunctionNorm(snes->pc, &fnorm);CHKERRQ(ierr);
4186bf1b2e5SPeter Brune       ierr = VecCopy(F, Y);CHKERRQ(ierr);
41988d374b2SPeter Brune     } else {
42088d374b2SPeter Brune       ierr = VecCopy(X, Y);CHKERRQ(ierr);
421217b9c2eSPeter Brune       ierr = SNESSetInitialFunction(snes->pc, F);CHKERRQ(ierr);
422217b9c2eSPeter Brune       ierr = SNESSetInitialFunctionNorm(snes->pc, fnorm);CHKERRQ(ierr);
42388d374b2SPeter Brune       ierr = SNESSolve(snes->pc, B, Y);CHKERRQ(ierr);
42488d374b2SPeter Brune       ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
42588d374b2SPeter Brune       if (reason < 0 && (reason != SNES_DIVERGED_MAX_IT)) {
42688d374b2SPeter Brune         snes->reason = SNES_DIVERGED_INNER;
42788d374b2SPeter Brune         PetscFunctionReturn(0);
42888d374b2SPeter Brune       }
42988d374b2SPeter Brune       ierr = VecAYPX(Y,-1.0,X);CHKERRQ(ierr);
43088d374b2SPeter Brune     }
43188d374b2SPeter Brune   } else {
43288d374b2SPeter Brune     ierr = VecCopy(F, Y);CHKERRQ(ierr);
43388d374b2SPeter Brune   }
43488d374b2SPeter Brune   ierr = VecCopy(Y, D);CHKERRQ(ierr);
4353af51624SPeter Brune 
436f8e15203SPeter Brune   /* scale the initial update */
4370c777b0cSPeter Brune   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
4380ecc9e1dSPeter Brune     ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);CHKERRQ(ierr);
4390ecc9e1dSPeter Brune   }
4400ecc9e1dSPeter Brune 
4415ba6227bSPeter Brune   for(i = 0, i_r = 0; i < snes->max_its; i++, i_r++) {
442b8840d6bSPeter Brune     switch(qn->type) {
443b8840d6bSPeter Brune     case SNES_QN_BADBROYDEN:
444b8840d6bSPeter Brune       ierr = SNESQNApply_BadBroyden(snes,i_r,Y,X,Xold,D,Dold);CHKERRQ(ierr);
445b8840d6bSPeter Brune       break;
446b8840d6bSPeter Brune     case SNES_QN_BROYDEN:
447b8840d6bSPeter Brune       ierr = SNESQNApply_Broyden(snes,i_r,Y,X,Xold,D,Dold);CHKERRQ(ierr);
448b8840d6bSPeter Brune       break;
449b8840d6bSPeter Brune     case SNES_QN_LBFGS:
450b8840d6bSPeter Brune       SNESQNApply_LBFGS(snes,i_r,Y,X,Xold,D,Dold);CHKERRQ(ierr);
451b8840d6bSPeter Brune       break;
452b8840d6bSPeter Brune     }
45370d3b23bSPeter Brune     /* line search for lambda */
45470d3b23bSPeter Brune     ynorm = 1; gnorm = fnorm;
45588d374b2SPeter Brune     ierr = VecCopy(D, Dold);CHKERRQ(ierr);
45615f5eeeaSPeter Brune     ierr = VecCopy(X, Xold);CHKERRQ(ierr);
457f1c6b773SPeter Brune     ierr = SNESLineSearchApply(snes->linesearch, X, F, &fnorm, Y);CHKERRQ(ierr);
4589f3a0142SPeter Brune     if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
4599f3a0142SPeter Brune     if (snes->domainerror) {
4609f3a0142SPeter Brune       snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
4619f3a0142SPeter Brune       PetscFunctionReturn(0);
4629f3a0142SPeter Brune       }
463f1c6b773SPeter Brune     ierr = SNESLineSearchGetSuccess(snes->linesearch, &lssucceed);CHKERRQ(ierr);
4649f3a0142SPeter Brune     if (!lssucceed) {
4659f3a0142SPeter Brune       if (++snes->numFailures >= snes->maxFailures) {
4669f3a0142SPeter Brune         snes->reason = SNES_DIVERGED_LINE_SEARCH;
4679f3a0142SPeter Brune         break;
4689f3a0142SPeter Brune       }
4699f3a0142SPeter Brune     }
470f1c6b773SPeter Brune     ierr = SNESLineSearchGetNorms(snes->linesearch, &xnorm, &fnorm, &ynorm);CHKERRQ(ierr);
4710c777b0cSPeter Brune     if (qn->scale_type == SNES_QN_SCALE_LINESEARCH) {
472*05ee83feSPeter Brune       ierr = SNESLineSearchGetLambda(snes->linesearch, &qn->scaling);CHKERRQ(ierr);
4730ecc9e1dSPeter Brune     }
4743af51624SPeter Brune 
47588d374b2SPeter Brune     /* convergence monitoring */
476b21d5a53SPeter Brune     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);
477b21d5a53SPeter Brune 
478360c497dSPeter Brune     ierr = SNESSetIterationNumber(snes, i+1);CHKERRQ(ierr);
479360c497dSPeter Brune     ierr = SNESSetFunctionNorm(snes, fnorm);CHKERRQ(ierr);
480360c497dSPeter Brune 
4818409ca45SMatthew G Knepley     SNESLogConvHistory(snes,snes->norm,snes->iter);
4828409ca45SMatthew G Knepley     ierr = SNESMonitor(snes,snes->iter,snes->norm);CHKERRQ(ierr);
4834b11644fSPeter Brune     /* set parameter for default relative tolerance convergence test */
484d484d688SPeter Brune     ierr = (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);CHKERRQ(ierr);
4854b11644fSPeter Brune     if (snes->reason) PetscFunctionReturn(0);
4864b11644fSPeter Brune 
48788d374b2SPeter Brune 
48888d374b2SPeter Brune     if (snes->pc) {
4890c777b0cSPeter Brune       if (qn->composition_type == SNES_QN_SEQUENTIAL) {
490217b9c2eSPeter Brune         ierr = SNESSetInitialFunction(snes->pc, F);CHKERRQ(ierr);
491217b9c2eSPeter Brune         ierr = SNESSetInitialFunctionNorm(snes->pc, fnorm);CHKERRQ(ierr);
49288d374b2SPeter Brune         ierr = SNESSolve(snes->pc, B, X);CHKERRQ(ierr);
49388d374b2SPeter Brune         ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
49488d374b2SPeter Brune         if (reason < 0 && (reason != SNES_DIVERGED_MAX_IT)) {
49588d374b2SPeter Brune           snes->reason = SNES_DIVERGED_INNER;
49688d374b2SPeter Brune           PetscFunctionReturn(0);
49788d374b2SPeter Brune         }
49888d374b2SPeter Brune         ierr = SNESGetFunction(snes->pc, &FPC, PETSC_NULL, PETSC_NULL);CHKERRQ(ierr);
49988d374b2SPeter Brune         ierr = VecCopy(FPC, F);CHKERRQ(ierr);
50088d374b2SPeter Brune         ierr = SNESGetFunctionNorm(snes->pc, &fnorm);CHKERRQ(ierr);
50188d374b2SPeter Brune         ierr = VecCopy(F, D);CHKERRQ(ierr);
50288d374b2SPeter Brune       } else {
50388d374b2SPeter Brune         ierr = VecCopy(X, D);CHKERRQ(ierr);
504217b9c2eSPeter Brune         ierr = SNESSetInitialFunction(snes->pc, F);CHKERRQ(ierr);
505217b9c2eSPeter Brune         ierr = SNESSetInitialFunctionNorm(snes->pc, fnorm);CHKERRQ(ierr);
50688d374b2SPeter Brune         ierr = SNESSolve(snes->pc, B, D);CHKERRQ(ierr);
50788d374b2SPeter Brune         ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
50888d374b2SPeter Brune         if (reason < 0 && (reason != SNES_DIVERGED_MAX_IT)) {
50988d374b2SPeter Brune           snes->reason = SNES_DIVERGED_INNER;
51088d374b2SPeter Brune           PetscFunctionReturn(0);
51188d374b2SPeter Brune         }
51288d374b2SPeter Brune         ierr = VecAYPX(D,-1.0,X);CHKERRQ(ierr);
51388d374b2SPeter Brune       }
51488d374b2SPeter Brune     } else {
51588d374b2SPeter Brune       ierr = VecCopy(F, D);CHKERRQ(ierr);
51688d374b2SPeter Brune     }
51788d374b2SPeter Brune 
5180c777b0cSPeter Brune     powell = PETSC_FALSE;
5190c777b0cSPeter Brune     if (qn->restart_type == SNES_QN_RESTART_POWELL) {
5206bf1b2e5SPeter Brune       /* check restart by Powell's Criterion: |F^T H_0 Fold| > 0.2 * |Fold^T H_0 Fold| */
5218e231d97SPeter Brune       ierr = VecDotBegin(Dold, Dold, &DolddotDold);CHKERRQ(ierr);
5228e231d97SPeter Brune       ierr = VecDotBegin(Dold, D, &DolddotD);CHKERRQ(ierr);
5238e231d97SPeter Brune       ierr = VecDotBegin(D, D, &DdotD);CHKERRQ(ierr);
5248e231d97SPeter Brune       ierr = VecDotBegin(Y, D, &YdotD);CHKERRQ(ierr);
5258e231d97SPeter Brune       ierr = VecDotEnd(Dold, Dold, &DolddotDold);CHKERRQ(ierr);
5268e231d97SPeter Brune       ierr = VecDotEnd(Dold, D, &DolddotD);CHKERRQ(ierr);
5278e231d97SPeter Brune       ierr = VecDotEnd(D, D, &DdotD);CHKERRQ(ierr);
5288e231d97SPeter Brune       ierr = VecDotEnd(Y, D, &YdotD);CHKERRQ(ierr);
5290c777b0cSPeter Brune       if (PetscAbs(PetscRealPart(DolddotD)) > qn->powell_gamma*PetscAbs(PetscRealPart(DolddotDold))) powell = PETSC_TRUE;
5300c777b0cSPeter Brune     }
5310c777b0cSPeter Brune     periodic = PETSC_FALSE;
532b8840d6bSPeter Brune     if (qn->restart_type == SNES_QN_RESTART_PERIODIC) {
533b8840d6bSPeter Brune       if (i_r>qn->m-1) periodic = PETSC_TRUE;
5340c777b0cSPeter Brune     }
5350c777b0cSPeter Brune     /* restart if either powell or periodic restart is satisfied. */
5360c777b0cSPeter Brune     if (powell || periodic) {
5375ba6227bSPeter Brune       if (qn->monitor) {
5385ba6227bSPeter Brune         ierr = PetscViewerASCIIAddTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
539516fe3c3SPeter Brune         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);
5405ba6227bSPeter Brune         ierr = PetscViewerASCIISubtractTab(qn->monitor,((PetscObject)snes)->tablevel+2);CHKERRQ(ierr);
5415ba6227bSPeter Brune       }
5425ba6227bSPeter Brune       i_r = -1;
5435ba6227bSPeter Brune       /* general purpose update */
5445ba6227bSPeter Brune       if (snes->ops->update) {
5455ba6227bSPeter Brune         ierr = (*snes->ops->update)(snes, snes->iter);CHKERRQ(ierr);
5465ba6227bSPeter Brune       }
5470c777b0cSPeter Brune       if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
5480ecc9e1dSPeter Brune         ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);CHKERRQ(ierr);
5490ecc9e1dSPeter Brune       }
5500ecc9e1dSPeter Brune     }
55170d3b23bSPeter Brune     /* general purpose update */
55270d3b23bSPeter Brune     if (snes->ops->update) {
55370d3b23bSPeter Brune       ierr = (*snes->ops->update)(snes, snes->iter);CHKERRQ(ierr);
55470d3b23bSPeter Brune     }
5555ba6227bSPeter Brune   }
5564b11644fSPeter Brune   if (i == snes->max_its) {
5574b11644fSPeter Brune     ierr = PetscInfo1(snes, "Maximum number of iterations has been reached: %D\n", snes->max_its);CHKERRQ(ierr);
5584b11644fSPeter Brune     if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
5594b11644fSPeter Brune   }
5604b11644fSPeter Brune   PetscFunctionReturn(0);
5614b11644fSPeter Brune }
5624b11644fSPeter Brune 
5634b11644fSPeter Brune 
5644b11644fSPeter Brune #undef __FUNCT__
5654b11644fSPeter Brune #define __FUNCT__ "SNESSetUp_QN"
5664b11644fSPeter Brune static PetscErrorCode SNESSetUp_QN(SNES snes)
5674b11644fSPeter Brune {
5689f83bee8SJed Brown   SNES_QN        *qn = (SNES_QN*)snes->data;
5694b11644fSPeter Brune   PetscErrorCode ierr;
570335fb975SPeter Brune 
5714b11644fSPeter Brune   PetscFunctionBegin;
572b8840d6bSPeter Brune   ierr = VecDuplicateVecs(snes->vec_sol, qn->m, &qn->U);CHKERRQ(ierr);
573b8840d6bSPeter Brune   ierr = VecDuplicateVecs(snes->vec_sol, qn->m, &qn->V);CHKERRQ(ierr);
5748e231d97SPeter Brune   ierr = PetscMalloc3(qn->m, PetscScalar, &qn->alpha,
5758e231d97SPeter Brune                       qn->m, PetscScalar, &qn->beta,
5768e231d97SPeter Brune                       qn->m, PetscScalar, &qn->dXtdF);CHKERRQ(ierr);
5778e231d97SPeter Brune 
57818aa0c0cSPeter Brune   if (qn->singlereduction) {
5798e231d97SPeter Brune     ierr = PetscMalloc3(qn->m*qn->m, PetscScalar, &qn->dXdFmat,
5808e231d97SPeter Brune                         qn->m, PetscScalar, &qn->dFtdX,
5818e231d97SPeter Brune                         qn->m, PetscScalar, &qn->YtdX);CHKERRQ(ierr);
5828e231d97SPeter Brune   }
583335fb975SPeter Brune   ierr = SNESDefaultGetWork(snes,4);CHKERRQ(ierr);
584335fb975SPeter Brune 
585335fb975SPeter Brune   /* set up the line search */
5860c777b0cSPeter Brune   if (qn->scale_type == SNES_QN_SCALE_JACOBIAN) {
5878e231d97SPeter Brune     ierr = SNESSetUpMatrices(snes);CHKERRQ(ierr);
5888e231d97SPeter Brune   }
5894b11644fSPeter Brune   PetscFunctionReturn(0);
5904b11644fSPeter Brune }
5914b11644fSPeter Brune 
5924b11644fSPeter Brune #undef __FUNCT__
5934b11644fSPeter Brune #define __FUNCT__ "SNESReset_QN"
5944b11644fSPeter Brune static PetscErrorCode SNESReset_QN(SNES snes)
5954b11644fSPeter Brune {
5964b11644fSPeter Brune   PetscErrorCode ierr;
5979f83bee8SJed Brown   SNES_QN *qn;
5984b11644fSPeter Brune   PetscFunctionBegin;
5994b11644fSPeter Brune   if (snes->data) {
6009f83bee8SJed Brown     qn = (SNES_QN*)snes->data;
601b8840d6bSPeter Brune     if (qn->U) {
602b8840d6bSPeter Brune       ierr = VecDestroyVecs(qn->m, &qn->U);CHKERRQ(ierr);
6034b11644fSPeter Brune     }
604b8840d6bSPeter Brune     if (qn->V) {
605b8840d6bSPeter Brune       ierr = VecDestroyVecs(qn->m, &qn->V);CHKERRQ(ierr);
6064b11644fSPeter Brune     }
60718aa0c0cSPeter Brune     if (qn->singlereduction) {
6088e231d97SPeter Brune       ierr = PetscFree3(qn->dXdFmat, qn->dFtdX, qn->YtdX);CHKERRQ(ierr);
6098e231d97SPeter Brune     }
6108e231d97SPeter Brune     ierr = PetscFree3(qn->alpha, qn->beta, qn->dXtdF);CHKERRQ(ierr);
6114b11644fSPeter Brune   }
6124b11644fSPeter Brune   PetscFunctionReturn(0);
6134b11644fSPeter Brune }
6144b11644fSPeter Brune 
6154b11644fSPeter Brune #undef __FUNCT__
6164b11644fSPeter Brune #define __FUNCT__ "SNESDestroy_QN"
6174b11644fSPeter Brune static PetscErrorCode SNESDestroy_QN(SNES snes)
6184b11644fSPeter Brune {
6194b11644fSPeter Brune   PetscErrorCode ierr;
6204b11644fSPeter Brune   PetscFunctionBegin;
6214b11644fSPeter Brune   ierr = SNESReset_QN(snes);CHKERRQ(ierr);
6224b11644fSPeter Brune   ierr = PetscFree(snes->data);CHKERRQ(ierr);
6239c05af5eSJed Brown   ierr = PetscObjectComposeFunctionDynamic((PetscObject)snes,"","",PETSC_NULL);CHKERRQ(ierr);
6244b11644fSPeter Brune   PetscFunctionReturn(0);
6254b11644fSPeter Brune }
6264b11644fSPeter Brune 
6274b11644fSPeter Brune #undef __FUNCT__
6284b11644fSPeter Brune #define __FUNCT__ "SNESSetFromOptions_QN"
6294b11644fSPeter Brune static PetscErrorCode SNESSetFromOptions_QN(SNES snes)
6304b11644fSPeter Brune {
6314b11644fSPeter Brune 
6324b11644fSPeter Brune   PetscErrorCode ierr;
6339f83bee8SJed Brown   SNES_QN    *qn;
63444f7e39eSPeter Brune   PetscBool  monflg = PETSC_FALSE;
635f1c6b773SPeter Brune   SNESLineSearch linesearch;
6364b11644fSPeter Brune   PetscFunctionBegin;
6374b11644fSPeter Brune 
6389f83bee8SJed Brown   qn = (SNES_QN*)snes->data;
6394b11644fSPeter Brune 
6404b11644fSPeter Brune   ierr = PetscOptionsHead("SNES QN options");CHKERRQ(ierr);
6415b4627e8SPeter Brune   ierr = PetscOptionsInt("-snes_qn_m","Number of past states saved for L-BFGS methods","SNESQN",qn->m,&qn->m,PETSC_NULL);CHKERRQ(ierr);
6425b4627e8SPeter Brune   ierr = PetscOptionsReal("-snes_qn_powell_gamma","Powell angle tolerance",          "SNESQN", qn->powell_gamma, &qn->powell_gamma, PETSC_NULL);CHKERRQ(ierr);
6435b4627e8SPeter Brune   ierr = PetscOptionsReal("-snes_qn_powell_downhill","Powell descent tolerance",        "SNESQN", qn->powell_downhill, &qn->powell_downhill, PETSC_NULL);CHKERRQ(ierr);
6445b4627e8SPeter Brune   ierr = PetscOptionsBool("-snes_qn_monitor",         "Monitor for the QN methods",      "SNESQN", monflg, &monflg, PETSC_NULL);CHKERRQ(ierr);
6454d116c7dSPeter Brune   ierr = PetscOptionsBool("-snes_qn_single_reduction", "Aggregate reductions",           "SNESQN", qn->singlereduction, &qn->singlereduction, PETSC_NULL);CHKERRQ(ierr);
6460c777b0cSPeter Brune   ierr = PetscOptionsEnum("-snes_qn_scale_type","Scaling type","SNESQNSetScaleType",SNESQNScaleTypes,(PetscEnum)qn->scale_type,(PetscEnum*)&qn->scale_type,PETSC_NULL);CHKERRQ(ierr);
6470c777b0cSPeter Brune   ierr = PetscOptionsEnum("-snes_qn_composition_type","Composition type","SNESQNSetCompositionType",SNESQNCompositionTypes,
6480c777b0cSPeter Brune                           (PetscEnum)qn->composition_type,(PetscEnum*)&qn->composition_type,PETSC_NULL);CHKERRQ(ierr);
6490c777b0cSPeter Brune   ierr = PetscOptionsEnum("-snes_qn_restart_type","Restart type","SNESQNSetRestartType",SNESQNRestartTypes,
6500c777b0cSPeter Brune                           (PetscEnum)qn->restart_type,(PetscEnum*)&qn->restart_type,PETSC_NULL);CHKERRQ(ierr);
651b8840d6bSPeter Brune   ierr = PetscOptionsEnum("-snes_qn_type","Quasi-Newton update type","",SNESQNTypes,
652b8840d6bSPeter Brune                           (PetscEnum)qn->type,(PetscEnum*)&qn->type,PETSC_NULL);CHKERRQ(ierr);
6534b11644fSPeter Brune   ierr = PetscOptionsTail();CHKERRQ(ierr);
6549e764e56SPeter Brune   if (!snes->linesearch) {
655f1c6b773SPeter Brune     ierr = SNESGetSNESLineSearch(snes, &linesearch);CHKERRQ(ierr);
6560c777b0cSPeter Brune     if (!snes->pc || qn->composition_type == SNES_QN_SEQUENTIAL) {
6571a4f838cSPeter Brune       ierr = SNESLineSearchSetType(linesearch, SNESLINESEARCHCP);CHKERRQ(ierr);
6589e764e56SPeter Brune     } else {
6591a4f838cSPeter Brune       ierr = SNESLineSearchSetType(linesearch, SNESLINESEARCHL2);CHKERRQ(ierr);
6609e764e56SPeter Brune     }
6619e764e56SPeter Brune   }
66244f7e39eSPeter Brune   if (monflg) {
66344f7e39eSPeter Brune     qn->monitor = PETSC_VIEWER_STDOUT_(((PetscObject)snes)->comm);CHKERRQ(ierr);
66444f7e39eSPeter Brune   }
6654b11644fSPeter Brune   PetscFunctionReturn(0);
6664b11644fSPeter Brune }
6674b11644fSPeter Brune 
66892c02d66SPeter Brune 
6690c777b0cSPeter Brune #undef __FUNCT__
6700c777b0cSPeter Brune #define __FUNCT__ "SNESQNSetRestartType"
6710c777b0cSPeter Brune /*@
6720c777b0cSPeter Brune     SNESQNSetRestartType - Sets the restart type for SNESQN.
6730c777b0cSPeter Brune 
6740c777b0cSPeter Brune     Logically Collective on SNES
6750c777b0cSPeter Brune 
6760c777b0cSPeter Brune     Input Parameters:
6770c777b0cSPeter Brune +   snes - the iterative context
6780c777b0cSPeter Brune -   rtype - restart type
6790c777b0cSPeter Brune 
6800c777b0cSPeter Brune     Options Database:
6810c777b0cSPeter Brune +   -snes_qn_restart_type<powell,periodic,none> - set the restart type
682b8840d6bSPeter Brune -   -snes_qn_restart[10] - sets the number of iterations before restart for periodic
6830c777b0cSPeter Brune 
6840c777b0cSPeter Brune     Level: intermediate
6850c777b0cSPeter Brune 
6860c777b0cSPeter Brune     SNESQNRestartTypes:
6870c777b0cSPeter Brune +   SNES_QN_RESTART_NONE - never restart
6880c777b0cSPeter Brune .   SNES_QN_RESTART_POWELL - restart based upon descent criteria
6890c777b0cSPeter Brune -   SNES_QN_RESTART_PERIODIC - restart after a fixed number of iterations
6900c777b0cSPeter Brune 
6910c777b0cSPeter Brune     Notes:
6920c777b0cSPeter Brune     The default line search used is the L2 line search and it requires two additional function evaluations.
6930c777b0cSPeter Brune 
6940c777b0cSPeter Brune .keywords: SNES, SNESQN, restart, type, set SNESLineSearch
6950c777b0cSPeter Brune @*/
6960c777b0cSPeter Brune PetscErrorCode SNESQNSetRestartType(SNES snes, SNESQNRestartType rtype) {
6970c777b0cSPeter Brune   PetscErrorCode ierr;
6980c777b0cSPeter Brune   PetscFunctionBegin;
6990c777b0cSPeter Brune   PetscValidHeaderSpecific(snes,SNES_CLASSID,1);
7000c777b0cSPeter Brune   ierr = PetscTryMethod(snes,"SNESQNSetRestartType_C",(SNES,SNESQNRestartType),(snes,rtype));CHKERRQ(ierr);
7010c777b0cSPeter Brune   PetscFunctionReturn(0);
7020c777b0cSPeter Brune }
7030c777b0cSPeter Brune 
7040c777b0cSPeter Brune #undef __FUNCT__
7050c777b0cSPeter Brune #define __FUNCT__ "SNESQNSetScaleType"
7060c777b0cSPeter Brune /*@
7070c777b0cSPeter Brune     SNESQNSetScaleType - Sets the scaling type for the inner inverse jacobian in SNESQN.
7080c777b0cSPeter Brune 
7090c777b0cSPeter Brune     Logically Collective on SNES
7100c777b0cSPeter Brune 
7110c777b0cSPeter Brune     Input Parameters:
7120c777b0cSPeter Brune +   snes - the iterative context
7130c777b0cSPeter Brune -   stype - scale type
7140c777b0cSPeter Brune 
7150c777b0cSPeter Brune     Options Database:
7160c777b0cSPeter Brune .   -snes_qn_scale_type<shanno,none,linesearch,jacobian>
7170c777b0cSPeter Brune 
7180c777b0cSPeter Brune     Level: intermediate
7190c777b0cSPeter Brune 
7200c777b0cSPeter Brune     SNESQNSelectTypes:
7210c777b0cSPeter Brune +   SNES_QN_SCALE_NONE - don't scale the problem
7220c777b0cSPeter Brune .   SNES_QN_SCALE_SHANNO - use shanno scaling
7230c777b0cSPeter Brune .   SNES_QN_SCALE_LINESEARCH - scale based upon line search lambda
7240c777b0cSPeter Brune -   SNES_QN_SCALE_JACOBIAN - scale by inverting a previously computed Jacobian.
7250c777b0cSPeter Brune 
7260c777b0cSPeter Brune .keywords: SNES, SNESQN, scaling, type, set SNESLineSearch
7270c777b0cSPeter Brune @*/
7280c777b0cSPeter Brune 
7290c777b0cSPeter Brune PetscErrorCode SNESQNSetScaleType(SNES snes, SNESQNScaleType stype) {
7300c777b0cSPeter Brune   PetscErrorCode ierr;
7310c777b0cSPeter Brune   PetscFunctionBegin;
7320c777b0cSPeter Brune   PetscValidHeaderSpecific(snes,SNES_CLASSID,1);
7330c777b0cSPeter Brune   ierr = PetscTryMethod(snes,"SNESQNSetScaleType_C",(SNES,SNESQNScaleType),(snes,stype));CHKERRQ(ierr);
7340c777b0cSPeter Brune   PetscFunctionReturn(0);
7350c777b0cSPeter Brune }
7360c777b0cSPeter Brune 
7370c777b0cSPeter Brune #undef __FUNCT__
7380c777b0cSPeter Brune #define __FUNCT__ "SNESQNSetCompositionType"
7390c777b0cSPeter Brune /*@
7400c777b0cSPeter Brune     SNESQNSetCompositionType - Sets the composition type
7410c777b0cSPeter Brune 
7420c777b0cSPeter Brune     Logically Collective on SNES
7430c777b0cSPeter Brune 
7440c777b0cSPeter Brune     Input Parameters:
7450c777b0cSPeter Brune +   snes - the iterative context
7460c777b0cSPeter Brune -   stype - composition type
7470c777b0cSPeter Brune 
7480c777b0cSPeter Brune     Options Database:
7490c777b0cSPeter Brune .   -snes_qn_composition_type<sequential, composed>
7500c777b0cSPeter Brune 
7510c777b0cSPeter Brune     Level: intermediate
7520c777b0cSPeter Brune 
7530c777b0cSPeter Brune     SNESQNSelectTypes:
7540c777b0cSPeter Brune +   SNES_QN_COMPOSITION_SEQUENTIAL - Solve the system with X = PC(X) and D = F(PC(X))
7550c777b0cSPeter Brune -   SNES_QN_COMPOSITION_COMPOSED - solve the system with X = X and D = PC(X) - X
7560c777b0cSPeter Brune 
7570c777b0cSPeter Brune .keywords: SNES, SNESQN, scaling, type, set SNESLineSearch
7580c777b0cSPeter Brune @*/
7590c777b0cSPeter Brune 
7600c777b0cSPeter Brune PetscErrorCode SNESQNSetCompositionType(SNES snes, SNESQNCompositionType ctype) {
7610c777b0cSPeter Brune   PetscErrorCode ierr;
7620c777b0cSPeter Brune   PetscFunctionBegin;
7630c777b0cSPeter Brune   PetscValidHeaderSpecific(snes,SNES_CLASSID,1);
7640c777b0cSPeter Brune   ierr = PetscTryMethod(snes,"SNESQNSetCompositionType_C",(SNES,SNESQNCompositionType),(snes,ctype));CHKERRQ(ierr);
7650c777b0cSPeter Brune   PetscFunctionReturn(0);
7660c777b0cSPeter Brune }
7670c777b0cSPeter Brune 
7680c777b0cSPeter Brune EXTERN_C_BEGIN
7690c777b0cSPeter Brune #undef __FUNCT__
7700c777b0cSPeter Brune #define __FUNCT__ "SNESQNSetScaleType_QN"
7710c777b0cSPeter Brune PetscErrorCode SNESQNSetScaleType_QN(SNES snes, SNESQNScaleType stype) {
7720c777b0cSPeter Brune   SNES_QN *qn = (SNES_QN *)snes->data;
7730c777b0cSPeter Brune   PetscFunctionBegin;
7740c777b0cSPeter Brune   qn->scale_type = stype;
7750c777b0cSPeter Brune   PetscFunctionReturn(0);
7760c777b0cSPeter Brune }
7770c777b0cSPeter Brune 
7780c777b0cSPeter Brune #undef __FUNCT__
7790c777b0cSPeter Brune #define __FUNCT__ "SNESQNSetRestartType_QN"
7800c777b0cSPeter Brune PetscErrorCode SNESQNSetRestartType_QN(SNES snes, SNESQNRestartType rtype) {
7810c777b0cSPeter Brune   SNES_QN *qn = (SNES_QN *)snes->data;
7820c777b0cSPeter Brune   PetscFunctionBegin;
7830c777b0cSPeter Brune   qn->restart_type = rtype;
7840c777b0cSPeter Brune   PetscFunctionReturn(0);
7850c777b0cSPeter Brune }
7860c777b0cSPeter Brune 
7870c777b0cSPeter Brune #undef __FUNCT__
7880c777b0cSPeter Brune #define __FUNCT__ "SNESQNSetCompositionType_QN"
7890c777b0cSPeter Brune 
7900c777b0cSPeter Brune PetscErrorCode SNESQNSetCompositionType_QN(SNES snes, SNESQNCompositionType ctype) {
7910c777b0cSPeter Brune   SNES_QN *qn = (SNES_QN *)snes->data;
7920c777b0cSPeter Brune   PetscFunctionBegin;
7930c777b0cSPeter Brune   qn->composition_type = ctype;
7940c777b0cSPeter Brune   PetscFunctionReturn(0);
7950c777b0cSPeter Brune }
7960c777b0cSPeter Brune EXTERN_C_END
7970c777b0cSPeter Brune 
7984b11644fSPeter Brune /* -------------------------------------------------------------------------- */
7994b11644fSPeter Brune /*MC
8004b11644fSPeter Brune       SNESQN - Limited-Memory Quasi-Newton methods for the solution of nonlinear systems.
8014b11644fSPeter Brune 
8026cc8130cSPeter Brune       Options Database:
8036cc8130cSPeter Brune 
8046cc8130cSPeter Brune +     -snes_qn_m - Number of past states saved for the L-Broyden methods.
8051867fe5bSPeter Brune .     -snes_qn_powell_angle - Angle condition for restart.
8061867fe5bSPeter Brune .     -snes_qn_powell_descent - Descent condition for restart.
8071867fe5bSPeter Brune .     -snes_qn_composition <sequential, composed>- Type of composition.
80872835e02SPeter Brune .     -snes_linesearch_type <cp, l2, basic> - Type of line search.
80944f7e39eSPeter Brune -     -snes_qn_monitor - Monitors the quasi-newton jacobian.
8106cc8130cSPeter Brune 
811b8840d6bSPeter Brune       Notes: This implements the L-BFGS, Broyden, and "Bad" Broyden algorithms for the solution of F(x) = b using
812b8840d6bSPeter Brune       previous change in F(x) and x to form the approximate inverse Jacobian using a series of multiplicative rank-one
813b8840d6bSPeter Brune       updates.
8146cc8130cSPeter Brune 
8151867fe5bSPeter Brune       When using a nonlinear preconditioner, one has two options as to how the preconditioner is applied.  The first of
8161867fe5bSPeter Brune       these options, sequential, uses the preconditioner to generate a new solution and function and uses those at this
8171867fe5bSPeter Brune       iteration as the current iteration's values when constructing the approximate jacobian.  The second, composed,
8181867fe5bSPeter Brune       perturbs the problem the jacobian represents to be P(x, b) - x = 0, where P(x, b) is the preconditioner.
8191867fe5bSPeter Brune 
8206cc8130cSPeter Brune       References:
8216cc8130cSPeter Brune 
8226cc8130cSPeter Brune       L-Broyden Methods: a generalization of the L-BFGS method to the limited memory Broyden family, M. B. Reed,
8236cc8130cSPeter Brune       International Journal of Computer Mathematics, vol. 86, 2009.
8246cc8130cSPeter Brune 
825b8840d6bSPeter Brune       Experiments with Quasi-Newton Methods in Solving Stiff ODE Systems, Peter N. Brown, Alan C. Hindmarsh, Homer F. Walker
826b8840d6bSPeter Brune       SIAM J. Sci. Stat. Comput. Vol 6(2), April 1985.
827b8840d6bSPeter Brune 
8284b11644fSPeter Brune 
8294b11644fSPeter Brune       Level: beginner
8304b11644fSPeter Brune 
8314b11644fSPeter Brune .seealso:  SNESCreate(), SNES, SNESSetType(), SNESLS, SNESTR
8326cc8130cSPeter Brune 
8334b11644fSPeter Brune M*/
8344b11644fSPeter Brune EXTERN_C_BEGIN
8354b11644fSPeter Brune #undef __FUNCT__
8364b11644fSPeter Brune #define __FUNCT__ "SNESCreate_QN"
8374b11644fSPeter Brune PetscErrorCode  SNESCreate_QN(SNES snes)
8384b11644fSPeter Brune {
8394b11644fSPeter Brune 
8404b11644fSPeter Brune   PetscErrorCode ierr;
8419f83bee8SJed Brown   SNES_QN *qn;
8424b11644fSPeter Brune 
8434b11644fSPeter Brune   PetscFunctionBegin;
8444b11644fSPeter Brune   snes->ops->setup           = SNESSetUp_QN;
8454b11644fSPeter Brune   snes->ops->solve           = SNESSolve_QN;
8464b11644fSPeter Brune   snes->ops->destroy         = SNESDestroy_QN;
8474b11644fSPeter Brune   snes->ops->setfromoptions  = SNESSetFromOptions_QN;
8484b11644fSPeter Brune   snes->ops->view            = 0;
8494b11644fSPeter Brune   snes->ops->reset           = SNESReset_QN;
8504b11644fSPeter Brune 
85142f4f86dSBarry Smith   snes->usespc          = PETSC_TRUE;
85242f4f86dSBarry Smith   snes->usesksp         = PETSC_FALSE;
85342f4f86dSBarry Smith 
85488976e71SPeter Brune   if (!snes->tolerancesset) {
8550e444f03SPeter Brune     snes->max_funcs = 30000;
8560e444f03SPeter Brune     snes->max_its   = 10000;
85788976e71SPeter Brune   }
8580e444f03SPeter Brune 
8599f83bee8SJed Brown   ierr = PetscNewLog(snes,SNES_QN,&qn);CHKERRQ(ierr);
8604b11644fSPeter Brune   snes->data = (void *) qn;
8610ecc9e1dSPeter Brune   qn->m               = 10;
862b21d5a53SPeter Brune   qn->scaling         = 1.0;
863b8840d6bSPeter Brune   qn->U               = PETSC_NULL;
864b8840d6bSPeter Brune   qn->V               = PETSC_NULL;
8658e231d97SPeter Brune   qn->dXtdF           = PETSC_NULL;
8668e231d97SPeter Brune   qn->dFtdX           = PETSC_NULL;
8678e231d97SPeter Brune   qn->dXdFmat         = PETSC_NULL;
86844f7e39eSPeter Brune   qn->monitor         = PETSC_NULL;
86918aa0c0cSPeter Brune   qn->singlereduction = PETSC_FALSE;
870b8840d6bSPeter Brune   qn->powell_gamma    = 0.9999;
8710c777b0cSPeter Brune   qn->composition_type= SNES_QN_SEQUENTIAL;
8720c777b0cSPeter Brune   qn->scale_type      = SNES_QN_SCALE_SHANNO;
8730c777b0cSPeter Brune   qn->restart_type    = SNES_QN_RESTART_POWELL;
874b8840d6bSPeter Brune   qn->type            = SNES_QN_LBFGS;
875ea630c6eSPeter Brune 
8764b11644fSPeter Brune   PetscFunctionReturn(0);
8774b11644fSPeter Brune }
8788e231d97SPeter Brune 
8794b11644fSPeter Brune EXTERN_C_END
880