Actual source code: ls.c
1: #define PETSCSNES_DLL
3: #include src/snes/impls/ls/ls.h
5: /*
6: Checks if J^T F = 0 which implies we've found a local minimum of the function,
7: but not a zero. In the case when one cannot compute J^T F we use the fact that
8: 0 = (J^T F)^T W = F^T J W iff W not in the null space of J. Thanks for Jorge More
9: for this trick.
10: */
13: PetscErrorCode SNESLSCheckLocalMin_Private(SNES snes,Mat A,Vec F,Vec W,PetscReal fnorm,PetscTruth *ismin)
14: {
15: PetscReal a1;
17: PetscTruth hastranspose;
20: *ismin = PETSC_FALSE;
21: MatHasOperation(A,MATOP_MULT_TRANSPOSE,&hastranspose);
22: if (hastranspose) {
23: /* Compute || J^T F|| */
24: MatMultTranspose(A,F,W);
25: VecNorm(W,NORM_2,&a1);
26: PetscInfo1(snes,"|| J^T F|| %G near zero implies found a local minimum\n",a1/fnorm);
27: if (a1/fnorm < 1.e-4) *ismin = PETSC_TRUE;
28: } else {
29: Vec work;
30: PetscScalar result;
31: PetscReal wnorm;
33: VecSetRandom(W,PETSC_NULL);
34: VecNorm(W,NORM_2,&wnorm);
35: VecDuplicate(W,&work);
36: MatMult(A,W,work);
37: VecDot(F,work,&result);
38: VecDestroy(work);
39: a1 = PetscAbsScalar(result)/(fnorm*wnorm);
40: PetscInfo1(snes,"(F^T J random)/(|| F ||*||random|| %G near zero implies found a local minimum\n",a1);
41: if (a1 < 1.e-4) *ismin = PETSC_TRUE;
42: }
43: return(0);
44: }
46: /*
47: Checks if J^T(F - J*X) = 0
48: */
51: PetscErrorCode SNESLSCheckResidual_Private(SNES snes,Mat A,Vec F,Vec X,Vec W1,Vec W2)
52: {
53: PetscReal a1,a2;
55: PetscTruth hastranspose;
58: MatHasOperation(A,MATOP_MULT_TRANSPOSE,&hastranspose);
59: if (hastranspose) {
60: MatMult(A,X,W1);
61: VecAXPY(W1,-1.0,F);
63: /* Compute || J^T W|| */
64: MatMultTranspose(A,W1,W2);
65: VecNorm(W1,NORM_2,&a1);
66: VecNorm(W2,NORM_2,&a2);
67: if (a1) {
68: PetscInfo1(snes,"||J^T(F-Ax)||/||F-AX|| %G near zero implies inconsistent rhs\n",a2/a1);
69: }
70: }
71: return(0);
72: }
74: /* --------------------------------------------------------------------
76: This file implements a truncated Newton method with a line search,
77: for solving a system of nonlinear equations, using the KSP, Vec,
78: and Mat interfaces for linear solvers, vectors, and matrices,
79: respectively.
81: The following basic routines are required for each nonlinear solver:
82: SNESCreate_XXX() - Creates a nonlinear solver context
83: SNESSetFromOptions_XXX() - Sets runtime options
84: SNESSolve_XXX() - Solves the nonlinear system
85: SNESDestroy_XXX() - Destroys the nonlinear solver context
86: The suffix "_XXX" denotes a particular implementation, in this case
87: we use _LS (e.g., SNESCreate_LS, SNESSolve_LS) for solving
88: systems of nonlinear equations with a line search (LS) method.
89: These routines are actually called via the common user interface
90: routines SNESCreate(), SNESSetFromOptions(), SNESSolve(), and
91: SNESDestroy(), so the application code interface remains identical
92: for all nonlinear solvers.
94: Another key routine is:
95: SNESSetUp_XXX() - Prepares for the use of a nonlinear solver
96: by setting data structures and options. The interface routine SNESSetUp()
97: is not usually called directly by the user, but instead is called by
98: SNESSolve() if necessary.
100: Additional basic routines are:
101: SNESView_XXX() - Prints details of runtime options that
102: have actually been used.
103: These are called by application codes via the interface routines
104: SNESView().
106: The various types of solvers (preconditioners, Krylov subspace methods,
107: nonlinear solvers, timesteppers) are all organized similarly, so the
108: above description applies to these categories also.
110: -------------------------------------------------------------------- */
111: /*
112: SNESSolve_LS - Solves a nonlinear system with a truncated Newton
113: method with a line search.
115: Input Parameters:
116: . snes - the SNES context
118: Output Parameter:
119: . outits - number of iterations until termination
121: Application Interface Routine: SNESSolve()
123: Notes:
124: This implements essentially a truncated Newton method with a
125: line search. By default a cubic backtracking line search
126: is employed, as described in the text "Numerical Methods for
127: Unconstrained Optimization and Nonlinear Equations" by Dennis
128: and Schnabel.
129: */
132: PetscErrorCode SNESSolve_LS(SNES snes)
133: {
134: SNES_LS *neP = (SNES_LS*)snes->data;
135: PetscErrorCode ierr;
136: PetscInt maxits,i,lits;
137: PetscTruth lssucceed;
138: MatStructure flg = DIFFERENT_NONZERO_PATTERN;
139: PetscReal fnorm,gnorm,xnorm=0,ynorm;
140: Vec Y,X,F,G,W;
141: KSPConvergedReason kspreason;
144: snes->numFailures = 0;
145: snes->numLinearSolveFailures = 0;
146: snes->reason = SNES_CONVERGED_ITERATING;
148: maxits = snes->max_its; /* maximum number of iterations */
149: X = snes->vec_sol; /* solution vector */
150: F = snes->vec_func; /* residual vector */
151: Y = snes->work[0]; /* work vectors */
152: G = snes->work[1];
153: W = snes->work[2];
155: PetscObjectTakeAccess(snes);
156: snes->iter = 0;
157: snes->norm = 0;
158: PetscObjectGrantAccess(snes);
159: SNESComputeFunction(snes,X,F);
160: VecNorm(F,NORM_2,&fnorm); /* fnorm <- ||F|| */
161: if (fnorm != fnorm) SETERRQ(PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
162: PetscObjectTakeAccess(snes);
163: snes->norm = fnorm;
164: PetscObjectGrantAccess(snes);
165: SNESLogConvHistory(snes,fnorm,0);
166: SNESMonitor(snes,0,fnorm);
168: /* set parameter for default relative tolerance convergence test */
169: snes->ttol = fnorm*snes->rtol;
170: /* test convergence */
171: (*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP);
172: if (snes->reason) return(0);
174: for (i=0; i<maxits; i++) {
176: /* Call general purpose update function */
177: if (snes->ops->update) {
178: (*snes->ops->update)(snes, snes->iter);
179: }
181: /* Solve J Y = F, where J is Jacobian matrix */
182: SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);
183: KSPSetOperators(snes->ksp,snes->jacobian,snes->jacobian_pre,flg);
184: SNES_KSPSolve(snes,snes->ksp,F,Y);
185: KSPGetConvergedReason(snes->ksp,&kspreason);
186: if (kspreason < 0) {
187: if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
188: PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);
189: snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
190: break;
191: }
192: }
193: KSPGetIterationNumber(snes->ksp,&lits);
194: snes->linear_its += lits;
195: PetscInfo2(snes,"iter=%D, linear solve iterations=%D\n",snes->iter,lits);
197: if (neP->precheckstep) {
198: PetscTruth changed_y = PETSC_FALSE;
199: (*neP->precheckstep)(snes,X,Y,neP->precheck,&changed_y);
200: }
202: if (PetscLogPrintInfo){
203: SNESLSCheckResidual_Private(snes,snes->jacobian,F,Y,G,W);
204: }
206: /* Compute a (scaled) negative update in the line search routine:
207: Y <- X - lambda*Y
208: and evaluate G = function(Y) (depends on the line search).
209: */
210: VecCopy(Y,snes->vec_sol_update);
211: ynorm = 1; gnorm = fnorm;
212: (*neP->LineSearch)(snes,neP->lsP,X,F,G,Y,W,fnorm,&ynorm,&gnorm,&lssucceed);
213: PetscInfo4(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n",fnorm,gnorm,ynorm,(int)lssucceed);
214: if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
215: if (!lssucceed) {
216: if (++snes->numFailures >= snes->maxFailures) {
217: PetscTruth ismin;
218: snes->reason = SNES_DIVERGED_LS_FAILURE;
219: SNESLSCheckLocalMin_Private(snes,snes->jacobian,G,W,gnorm,&ismin);
220: if (ismin) snes->reason = SNES_DIVERGED_LOCAL_MIN;
221: break;
222: }
223: }
224: /* Update function and solution vectors */
225: fnorm = gnorm;
226: VecCopy(G,F);
227: VecCopy(W,X);
228: /* Monitor convergence */
229: PetscObjectTakeAccess(snes);
230: snes->iter = i+1;
231: snes->norm = fnorm;
232: PetscObjectGrantAccess(snes);
233: SNESLogConvHistory(snes,snes->norm,lits);
234: SNESMonitor(snes,snes->iter,snes->norm);
235: /* Test for convergence, xnorm = || X || */
236: if (snes->ops->converged != SNESSkipConverged) { VecNorm(X,NORM_2,&xnorm); }
237: (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);
238: if (snes->reason) break;
239: }
240: if (i == maxits) {
241: PetscInfo1(snes,"Maximum number of iterations has been reached: %D\n",maxits);
242: if(!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
243: }
244: return(0);
245: }
246: /* -------------------------------------------------------------------------- */
247: /*
248: SNESSetUp_LS - Sets up the internal data structures for the later use
249: of the SNESLS nonlinear solver.
251: Input Parameter:
252: . snes - the SNES context
253: . x - the solution vector
255: Application Interface Routine: SNESSetUp()
257: Notes:
258: For basic use of the SNES solvers, the user need not explicitly call
259: SNESSetUp(), since these actions will automatically occur during
260: the call to SNESSolve().
261: */
264: PetscErrorCode SNESSetUp_LS(SNES snes)
265: {
269: if (!snes->vec_sol_update) {
270: VecDuplicate(snes->vec_sol,&snes->vec_sol_update);
271: PetscLogObjectParent(snes,snes->vec_sol_update);
272: }
273: if (!snes->work) {
274: snes->nwork = 3;
275: VecDuplicateVecs(snes->vec_sol,snes->nwork,&snes->work);
276: PetscLogObjectParents(snes,snes->nwork,snes->work);
277: }
278: return(0);
279: }
280: /* -------------------------------------------------------------------------- */
281: /*
282: SNESDestroy_LS - Destroys the private SNES_LS context that was created
283: with SNESCreate_LS().
285: Input Parameter:
286: . snes - the SNES context
288: Application Interface Routine: SNESDestroy()
289: */
292: PetscErrorCode SNESDestroy_LS(SNES snes)
293: {
297: if (snes->vec_sol_update) {
298: VecDestroy(snes->vec_sol_update);
299: snes->vec_sol_update = PETSC_NULL;
300: }
301: if (snes->nwork) {
302: VecDestroyVecs(snes->work,snes->nwork);
303: snes->nwork = 0;
304: snes->work = PETSC_NULL;
305: }
306: PetscFree(snes->data);
308: /* clear composed functions */
309: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSet_C","",PETSC_NULL);
310: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSetPostCheck_C","",PETSC_NULL);
311: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSetPreCheck_C","",PETSC_NULL);
313: return(0);
314: }
315: /* -------------------------------------------------------------------------- */
319: /*@C
320: SNESLineSearchNo - This routine is not a line search at all;
321: it simply uses the full Newton step. Thus, this routine is intended
322: to serve as a template and is not recommended for general use.
324: Collective on SNES and Vec
326: Input Parameters:
327: + snes - nonlinear context
328: . lsctx - optional context for line search (not used here)
329: . x - current iterate
330: . f - residual evaluated at x
331: . y - search direction
332: . w - work vector
333: - fnorm - 2-norm of f
335: Output Parameters:
336: + g - residual evaluated at new iterate y
337: . w - new iterate
338: . gnorm - 2-norm of g
339: . ynorm - 2-norm of search length
340: - flag - PETSC_TRUE on success, PETSC_FALSE on failure
342: Options Database Key:
343: . -snes_ls basic - Activates SNESLineSearchNo()
345: Level: advanced
347: .keywords: SNES, nonlinear, line search, cubic
349: .seealso: SNESLineSearchCubic(), SNESLineSearchQuadratic(),
350: SNESLineSearchSet(), SNESLineSearchNoNorms()
351: @*/
352: PetscErrorCode SNESLineSearchNo(SNES snes,void *lsctx,Vec x,Vec f,Vec g,Vec y,Vec w,PetscReal fnorm,PetscReal *ynorm,PetscReal *gnorm,PetscTruth *flag)
353: {
355: SNES_LS *neP = (SNES_LS*)snes->data;
356: PetscTruth changed_w = PETSC_FALSE,changed_y = PETSC_FALSE;
359: *flag = PETSC_TRUE;
361: VecNorm(y,NORM_2,ynorm); /* ynorm = || y || */
362: VecWAXPY(w,-1.0,y,x); /* w <- x - y */
363: if (neP->postcheckstep) {
364: (*neP->postcheckstep)(snes,x,y,w,neP->postcheck,&changed_y,&changed_w);
365: }
366: if (changed_y) {
367: VecWAXPY(w,-1.0,y,x); /* w <- x - y */
368: }
369: SNESComputeFunction(snes,w,g);
370: if (PetscExceptionValue(ierr)) {
372: }
373:
375: VecNorm(g,NORM_2,gnorm); /* gnorm = || g || */
376: if (*gnorm != *gnorm) SETERRQ(PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
378: return(0);
379: }
380: /* -------------------------------------------------------------------------- */
384: /*@C
385: SNESLineSearchNoNorms - This routine is not a line search at
386: all; it simply uses the full Newton step. This version does not
387: even compute the norm of the function or search direction; this
388: is intended only when you know the full step is fine and are
389: not checking for convergence of the nonlinear iteration (for
390: example, you are running always for a fixed number of Newton steps).
392: Collective on SNES and Vec
394: Input Parameters:
395: + snes - nonlinear context
396: . lsctx - optional context for line search (not used here)
397: . x - current iterate
398: . f - residual evaluated at x
399: . y - search direction
400: . w - work vector
401: - fnorm - 2-norm of f
403: Output Parameters:
404: + g - residual evaluated at new iterate y
405: . w - new iterate
406: . gnorm - not changed
407: . ynorm - not changed
408: - flag - set to PETSC_TRUE indicating a successful line search
410: Options Database Key:
411: . -snes_ls basicnonorms - Activates SNESLineSearchNoNorms()
413: Notes:
414: SNESLineSearchNoNorms() must be used in conjunction with
415: either the options
416: $ -snes_no_convergence_test -snes_max_it <its>
417: or alternatively a user-defined custom test set via
418: SNESSetConvergenceTest(); or a -snes_max_it of 1,
419: otherwise, the SNES solver will generate an error.
421: During the final iteration this will not evaluate the function at
422: the solution point. This is to save a function evaluation while
423: using pseudo-timestepping.
425: The residual norms printed by monitoring routines such as
426: SNESMonitorDefault() (as activated via -snes_monitor) will not be
427: correct, since they are not computed.
429: Level: advanced
431: .keywords: SNES, nonlinear, line search, cubic
433: .seealso: SNESLineSearchCubic(), SNESLineSearchQuadratic(),
434: SNESLineSearchSet(), SNESLineSearchNo()
435: @*/
436: PetscErrorCode SNESLineSearchNoNorms(SNES snes,void *lsctx,Vec x,Vec f,Vec g,Vec y,Vec w,PetscReal fnorm,PetscReal *ynorm,PetscReal *gnorm,PetscTruth *flag)
437: {
439: SNES_LS *neP = (SNES_LS*)snes->data;
440: PetscTruth changed_w = PETSC_FALSE,changed_y = PETSC_FALSE;
443: *flag = PETSC_TRUE;
445: VecWAXPY(w,-1.0,y,x); /* w <- x - y */
446: if (neP->postcheckstep) {
447: (*neP->postcheckstep)(snes,x,y,w,neP->postcheck,&changed_y,&changed_w);
448: }
449: if (changed_y) {
450: VecWAXPY(w,-1.0,y,x); /* w <- x - y */
451: }
452:
453: /* don't evaluate function the last time through */
454: if (snes->iter < snes->max_its-1) {
455: SNESComputeFunction(snes,w,g);
456: if (PetscExceptionValue(ierr)) {
458: }
459:
460: }
462: return(0);
463: }
464: /* -------------------------------------------------------------------------- */
467: /*@C
468: SNESLineSearchCubic - Performs a cubic line search (default line search method).
470: Collective on SNES
472: Input Parameters:
473: + snes - nonlinear context
474: . lsctx - optional context for line search (not used here)
475: . x - current iterate
476: . f - residual evaluated at x
477: . y - search direction
478: . w - work vector
479: - fnorm - 2-norm of f
481: Output Parameters:
482: + g - residual evaluated at new iterate y
483: . w - new iterate
484: . gnorm - 2-norm of g
485: . ynorm - 2-norm of search length
486: - flag - PETSC_TRUE if line search succeeds; PETSC_FALSE on failure.
488: Options Database Key:
489: $ -snes_ls cubic - Activates SNESLineSearchCubic()
491: Notes:
492: This line search is taken from "Numerical Methods for Unconstrained
493: Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325.
495: Level: advanced
497: .keywords: SNES, nonlinear, line search, cubic
499: .seealso: SNESLineSearchQuadratic(), SNESLineSearchNo(), SNESLineSearchSet(), SNESLineSearchNoNorms()
500: @*/
501: PetscErrorCode SNESLineSearchCubic(SNES snes,void *lsctx,Vec x,Vec f,Vec g,Vec y,Vec w,PetscReal fnorm,PetscReal *ynorm,PetscReal *gnorm,PetscTruth *flag)
502: {
503: /*
504: Note that for line search purposes we work with with the related
505: minimization problem:
506: min z(x): R^n -> R,
507: where z(x) = .5 * fnorm*fnorm, and fnorm = || f ||_2.
508: */
509:
510: PetscReal steptol,initslope,lambdaprev,gnormprev,a,b,d,t1,t2,rellength;
511: PetscReal maxstep,minlambda,alpha,lambda,lambdatemp;
512: #if defined(PETSC_USE_COMPLEX)
513: PetscScalar cinitslope,clambda;
514: #endif
516: PetscInt count;
517: SNES_LS *neP = (SNES_LS*)snes->data;
518: PetscScalar scale;
519: PetscTruth changed_w = PETSC_FALSE,changed_y = PETSC_FALSE;
523: *flag = PETSC_TRUE;
524: alpha = neP->alpha;
525: maxstep = neP->maxstep;
526: steptol = neP->steptol;
528: VecNorm(y,NORM_2,ynorm);
529: if (!*ynorm) {
530: PetscInfo(snes,"Search direction and size is 0\n");
531: *gnorm = fnorm;
532: VecCopy(x,w);
533: VecCopy(f,g);
534: *flag = PETSC_FALSE;
535: goto theend1;
536: }
537: if (*ynorm > maxstep) { /* Step too big, so scale back */
538: scale = maxstep/(*ynorm);
539: PetscInfo2(snes,"Scaling step by %G old ynorm %G\n",PetscRealPart(scale),*ynorm);
540: VecScale(y,scale);
541: *ynorm = maxstep;
542: }
543: VecMaxPointwiseDivide(y,x,&rellength);
544: minlambda = steptol/rellength;
545: MatMult(snes->jacobian,y,w);
546: #if defined(PETSC_USE_COMPLEX)
547: VecDot(f,w,&cinitslope);
548: initslope = PetscRealPart(cinitslope);
549: #else
550: VecDot(f,w,&initslope);
551: #endif
552: if (initslope > 0.0) initslope = -initslope;
553: if (initslope == 0.0) initslope = -1.0;
555: VecWAXPY(w,-1.0,y,x);
556: if (snes->nfuncs >= snes->max_funcs) {
557: PetscInfo(snes,"Exceeded maximum function evaluations, while checking full step length!\n");
558: *flag = PETSC_FALSE;
559: snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
560: goto theend1;
561: }
562: SNESComputeFunction(snes,w,g);
563: if (PetscExceptionValue(ierr)) {
565: }
566:
567: VecNorm(g,NORM_2,gnorm);
568: if (*gnorm != *gnorm) SETERRQ(PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
569: if (.5*(*gnorm)*(*gnorm) <= .5*fnorm*fnorm + alpha*initslope) { /* Sufficient reduction */
570: PetscInfo(snes,"Using full step\n");
571: goto theend1;
572: }
574: /* Fit points with quadratic */
575: lambda = 1.0;
576: lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope);
577: lambdaprev = lambda;
578: gnormprev = *gnorm;
579: if (lambdatemp > .5*lambda) lambdatemp = .5*lambda;
580: if (lambdatemp <= .1*lambda) lambda = .1*lambda;
581: else lambda = lambdatemp;
583: #if defined(PETSC_USE_COMPLEX)
584: clambda = lambda; VecWAXPY(w,-clambda,y,x);
585: #else
586: VecWAXPY(w,-lambda,y,x);
587: #endif
588: if (snes->nfuncs >= snes->max_funcs) {
589: PetscInfo1(snes,"Exceeded maximum function evaluations, while attempting quadratic backtracking! %D \n",snes->nfuncs);
590: *flag = PETSC_FALSE;
591: snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
592: goto theend1;
593: }
594: SNESComputeFunction(snes,w,g);
595: if (PetscExceptionValue(ierr)) {
597: }
598:
599: VecNorm(g,NORM_2,gnorm);
600: if (*gnorm != *gnorm) SETERRQ(PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
601: if (.5*(*gnorm)*(*gnorm) < .5*fnorm*fnorm + lambda*alpha*initslope) { /* sufficient reduction */
602: PetscInfo1(snes,"Quadratically determined step, lambda=%18.16e\n",lambda);
603: goto theend1;
604: }
606: /* Fit points with cubic */
607: count = 1;
608: while (count < 10000) {
609: if (lambda <= minlambda) { /* bad luck; use full step */
610: PetscInfo1(snes,"Unable to find good step length! %D \n",count);
611: PetscInfo5(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lambda=%18.16e, initial slope=%18.16e\n",fnorm,*gnorm,*ynorm,lambda,initslope);
612: *flag = PETSC_FALSE;
613: break;
614: }
615: t1 = .5*((*gnorm)*(*gnorm) - fnorm*fnorm) - lambda*initslope;
616: t2 = .5*(gnormprev*gnormprev - fnorm*fnorm) - lambdaprev*initslope;
617: a = (t1/(lambda*lambda) - t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
618: b = (-lambdaprev*t1/(lambda*lambda) + lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev);
619: d = b*b - 3*a*initslope;
620: if (d < 0.0) d = 0.0;
621: if (a == 0.0) {
622: lambdatemp = -initslope/(2.0*b);
623: } else {
624: lambdatemp = (-b + sqrt(d))/(3.0*a);
625: }
626: lambdaprev = lambda;
627: gnormprev = *gnorm;
628: if (lambdatemp > .5*lambda) lambdatemp = .5*lambda;
629: if (lambdatemp <= .1*lambda) lambda = .1*lambda;
630: else lambda = lambdatemp;
631: #if defined(PETSC_USE_COMPLEX)
632: clambda = lambda;
633: VecWAXPY(w,-clambda,y,x);
634: #else
635: VecWAXPY(w,-lambda,y,x);
636: #endif
637: if (snes->nfuncs >= snes->max_funcs) {
638: PetscInfo1(snes,"Exceeded maximum function evaluations, while looking for good step length! %D \n",count);
639: PetscInfo5(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lambda=%18.16e, initial slope=%18.16e\n",fnorm,*gnorm,*ynorm,lambda,initslope);
640: *flag = PETSC_FALSE;
641: snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
642: break;
643: }
644: SNESComputeFunction(snes,w,g);
645: if (PetscExceptionValue(ierr)) {
647: }
648:
649: VecNorm(g,NORM_2,gnorm);
650: if (*gnorm != *gnorm) SETERRQ(PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
651: if (.5*(*gnorm)*(*gnorm) < .5*fnorm*fnorm + lambda*alpha*initslope) { /* is reduction enough? */
652: PetscInfo1(snes,"Cubically determined step, lambda=%18.16e\n",lambda);
653: break;
654: } else {
655: PetscInfo1(snes,"Cubic step no good, shrinking lambda, lambda=%18.16e\n",lambda);
656: }
657: count++;
658: }
659: if (count >= 10000) {
660: SETERRQ(PETSC_ERR_LIB, "Lambda was decreased more than 10,000 times, so something is probably wrong with the function evaluation");
661: }
662: theend1:
663: /* Optional user-defined check for line search step validity */
664: if (neP->postcheckstep && *flag) {
665: (*neP->postcheckstep)(snes,x,y,w,neP->postcheck,&changed_y,&changed_w);
666: if (changed_y) {
667: VecWAXPY(w,-1.0,y,x);
668: }
669: if (changed_y || changed_w) { /* recompute the function if the step has changed */
670: SNESComputeFunction(snes,w,g);
671: if (PetscExceptionValue(ierr)) {
673: }
674:
675: VecNormBegin(g,NORM_2,gnorm);
676: if (*gnorm != *gnorm) SETERRQ(PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
677: VecNormBegin(w,NORM_2,ynorm);
678: VecNormEnd(g,NORM_2,gnorm);
679: VecNormEnd(w,NORM_2,ynorm);
680: }
681: }
683: return(0);
684: }
685: /* -------------------------------------------------------------------------- */
688: /*@C
689: SNESLineSearchQuadratic - Performs a quadratic line search.
691: Collective on SNES and Vec
693: Input Parameters:
694: + snes - the SNES context
695: . lsctx - optional context for line search (not used here)
696: . x - current iterate
697: . f - residual evaluated at x
698: . y - search direction
699: . w - work vector
700: - fnorm - 2-norm of f
702: Output Parameters:
703: + g - residual evaluated at new iterate w
704: . w - new iterate (x + alpha*y)
705: . gnorm - 2-norm of g
706: . ynorm - 2-norm of search length
707: - flag - PETSC_TRUE if line search succeeds; PETSC_FALSE on failure.
709: Options Database Keys:
710: + -snes_ls quadratic - Activates SNESLineSearchQuadratic()
711: . -snes_ls_alpha <alpha> - Sets alpha
712: . -snes_ls_maxstep <max> - Sets maxstep
713: - -snes_ls_steptol <steptol> - Sets steptol, this is the minimum step size that the line search code
714: will accept; min p[i]/x[i] < steptol. The -snes_stol <stol> is the minimum step length
715: the default convergence test will use and is based on 2-norm(p) < stol*2-norm(x)
716: Notes:
717: Use SNESLineSearchSet() to set this routine within the SNESLS method.
719: Level: advanced
721: .keywords: SNES, nonlinear, quadratic, line search
723: .seealso: SNESLineSearchCubic(), SNESLineSearchNo(), SNESLineSearchSet(), SNESLineSearchNoNorms()
724: @*/
725: PetscErrorCode SNESLineSearchQuadratic(SNES snes,void *lsctx,Vec x,Vec f,Vec g,Vec y,Vec w,PetscReal fnorm,PetscReal *ynorm,PetscReal *gnorm,PetscTruth *flag)
726: {
727: /*
728: Note that for line search purposes we work with with the related
729: minimization problem:
730: min z(x): R^n -> R,
731: where z(x) = .5 * fnorm*fnorm,and fnorm = || f ||_2.
732: */
733: PetscReal steptol,initslope,maxstep,minlambda,alpha,lambda,lambdatemp,rellength;
734: #if defined(PETSC_USE_COMPLEX)
735: PetscScalar cinitslope,clambda;
736: #endif
738: PetscInt count;
739: SNES_LS *neP = (SNES_LS*)snes->data;
740: PetscScalar scale;
741: PetscTruth changed_w = PETSC_FALSE,changed_y = PETSC_FALSE;
745: *flag = PETSC_TRUE;
746: alpha = neP->alpha;
747: maxstep = neP->maxstep;
748: steptol = neP->steptol;
750: VecNorm(y,NORM_2,ynorm);
751: if (*ynorm == 0.0) {
752: PetscInfo(snes,"Search direction and size is 0\n");
753: *gnorm = fnorm;
754: VecCopy(x,w);
755: VecCopy(f,g);
756: *flag = PETSC_FALSE;
757: goto theend2;
758: }
759: if (*ynorm > maxstep) { /* Step too big, so scale back */
760: scale = maxstep/(*ynorm);
761: VecScale(y,scale);
762: *ynorm = maxstep;
763: }
764: VecMaxPointwiseDivide(y,x,&rellength);
765: minlambda = steptol/rellength;
766: MatMult(snes->jacobian,y,w);
767: #if defined(PETSC_USE_COMPLEX)
768: VecDot(f,w,&cinitslope);
769: initslope = PetscRealPart(cinitslope);
770: #else
771: VecDot(f,w,&initslope);
772: #endif
773: if (initslope > 0.0) initslope = -initslope;
774: if (initslope == 0.0) initslope = -1.0;
776: VecWAXPY(w,-1.0,y,x);
777: if (snes->nfuncs >= snes->max_funcs) {
778: PetscInfo(snes,"Exceeded maximum function evaluations, while checking full step length!\n");
779: *flag = PETSC_FALSE;
780: snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
781: goto theend2;
782: }
783: SNESComputeFunction(snes,w,g);
784: if (PetscExceptionValue(ierr)) {
786: }
787:
788: VecNorm(g,NORM_2,gnorm);
789: if (*gnorm != *gnorm) SETERRQ(PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
790: if (.5*(*gnorm)*(*gnorm) <= .5*fnorm*fnorm + alpha*initslope) { /* Sufficient reduction */
791: PetscInfo(snes,"Using full step\n");
792: goto theend2;
793: }
795: /* Fit points with quadratic */
796: lambda = 1.0;
797: count = 1;
798: while (PETSC_TRUE) {
799: if (lambda <= minlambda) { /* bad luck; use full step */
800: PetscInfo1(snes,"Unable to find good step length! %D \n",count);
801: PetscInfo5(snes,"fnorm=%G, gnorm=%G, ynorm=%G, lambda=%G, initial slope=%G\n",fnorm,*gnorm,*ynorm,lambda,initslope);
802: VecCopy(x,w);
803: *flag = PETSC_FALSE;
804: break;
805: }
806: lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope);
807: if (lambdatemp > .5*lambda) lambdatemp = .5*lambda;
808: if (lambdatemp <= .1*lambda) lambda = .1*lambda;
809: else lambda = lambdatemp;
810:
811: #if defined(PETSC_USE_COMPLEX)
812: clambda = lambda; VecWAXPY(w,-clambda,y,x);
813: #else
814: VecWAXPY(w,-lambda,y,x);
815: #endif
816: if (snes->nfuncs >= snes->max_funcs) {
817: PetscInfo1(snes,"Exceeded maximum function evaluations, while looking for good step length! %D \n",count);
818: PetscInfo5(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lambda=%18.16e, initial slope=%18.16e\n",fnorm,*gnorm,*ynorm,lambda,initslope);
819: *flag = PETSC_FALSE;
820: snes->reason = SNES_DIVERGED_FUNCTION_COUNT;
821: break;
822: }
823: SNESComputeFunction(snes,w,g);
824: if (PetscExceptionValue(ierr)) {
826: }
827:
828: VecNorm(g,NORM_2,gnorm);
829: if (*gnorm != *gnorm) SETERRQ(PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
830: if (.5*(*gnorm)*(*gnorm) < .5*fnorm*fnorm + lambda*alpha*initslope) { /* sufficient reduction */
831: PetscInfo1(snes,"Quadratically determined step, lambda=%G\n",lambda);
832: break;
833: }
834: count++;
835: }
836: theend2:
837: /* Optional user-defined check for line search step validity */
838: if (neP->postcheckstep) {
839: (*neP->postcheckstep)(snes,x,y,w,neP->postcheck,&changed_y,&changed_w);
840: if (changed_y) {
841: VecWAXPY(w,-1.0,y,x);
842: }
843: if (changed_y || changed_w) { /* recompute the function if the step has changed */
844: SNESComputeFunction(snes,w,g);
845: if (PetscExceptionValue(ierr)) {
847: }
848:
849: VecNormBegin(g,NORM_2,gnorm);
850: VecNormBegin(w,NORM_2,ynorm);
851: VecNormEnd(g,NORM_2,gnorm);
852: VecNormEnd(w,NORM_2,ynorm);
853: if (*gnorm != *gnorm) SETERRQ(PETSC_ERR_FP,"User provided compute function generated a Not-a-Number");
854: }
855: }
857: return(0);
858: }
860: /* -------------------------------------------------------------------------- */
863: /*@C
864: SNESLineSearchSet - Sets the line search routine to be used
865: by the method SNESLS.
867: Input Parameters:
868: + snes - nonlinear context obtained from SNESCreate()
869: . lsctx - optional user-defined context for use by line search
870: - func - pointer to int function
872: Collective on SNES
874: Available Routines:
875: + SNESLineSearchCubic() - default line search
876: . SNESLineSearchQuadratic() - quadratic line search
877: . SNESLineSearchNo() - the full Newton step (actually not a line search)
878: - SNESLineSearchNoNorms() - the full Newton step (calculating no norms; faster in parallel)
880: Options Database Keys:
881: + -snes_ls [cubic,quadratic,basic,basicnonorms] - Selects line search
882: . -snes_ls_alpha <alpha> - Sets alpha
883: . -snes_ls_maxstep <max> - Sets maxstep
884: - -snes_ls_steptol <steptol> - Sets steptol, this is the minimum step size that the line search code
885: will accept; min p[i]/x[i] < steptol. The -snes_stol <stol> is the minimum step length
886: the default convergence test will use and is based on 2-norm(p) < stol*2-norm(x)
888: Calling sequence of func:
889: .vb
890: func (SNES snes,void *lsctx,Vec x,Vec f,Vec g,Vec y,Vec w,
891: PetscReal fnorm,PetscReal *ynorm,PetscReal *gnorm,PetscTruth *flag)
892: .ve
894: Input parameters for func:
895: + snes - nonlinear context
896: . lsctx - optional user-defined context for line search
897: . x - current iterate
898: . f - residual evaluated at x
899: . y - search direction
900: . w - work vector
901: - fnorm - 2-norm of f
903: Output parameters for func:
904: + g - residual evaluated at new iterate y
905: . w - new iterate
906: . gnorm - 2-norm of g
907: . ynorm - 2-norm of search length
908: - flag - set to PETSC_TRUE if the line search succeeds; PETSC_FALSE on failure.
910: Level: advanced
912: .keywords: SNES, nonlinear, set, line search, routine
914: .seealso: SNESLineSearchCubic(), SNESLineSearchQuadratic(), SNESLineSearchNo(), SNESLineSearchNoNorms(),
915: SNESLineSearchSetPostCheck(), SNESLineSearchSetParams(), SNESLineSearchGetParams(), SNESLineSearchSetPreCheck()
916: @*/
917: PetscErrorCode SNESLineSearchSet(SNES snes,PetscErrorCode (*func)(SNES,void*,Vec,Vec,Vec,Vec,Vec,PetscReal,PetscReal*,PetscReal*,PetscTruth*),void *lsctx)
918: {
919: PetscErrorCode ierr,(*f)(SNES,PetscErrorCode (*)(SNES,void*,Vec,Vec,Vec,Vec,Vec,PetscReal,PetscReal*,PetscReal*,PetscTruth*),void*);
922: PetscObjectQueryFunction((PetscObject)snes,"SNESLineSearchSet_C",(void (**)(void))&f);
923: if (f) {
924: (*f)(snes,func,lsctx);
925: }
926: return(0);
927: }
930: /* -------------------------------------------------------------------------- */
934: PetscErrorCode SNESLineSearchSet_LS(SNES snes,FCN2 func,void *lsctx)
935: {
937: ((SNES_LS *)(snes->data))->LineSearch = func;
938: ((SNES_LS *)(snes->data))->lsP = lsctx;
939: return(0);
940: }
942: /* -------------------------------------------------------------------------- */
945: /*@C
946: SNESLineSearchSetPostCheck - Sets a routine to check the validity of new iterate computed
947: by the line search routine in the Newton-based method SNESLS.
949: Input Parameters:
950: + snes - nonlinear context obtained from SNESCreate()
951: . func - pointer to function
952: - checkctx - optional user-defined context for use by step checking routine
954: Collective on SNES
956: Calling sequence of func:
957: .vb
958: int func (SNES snes, Vec x,Vec y,Vec w,void *checkctx, PetscTruth *changed_y,PetscTruth *changed_w)
959: .ve
960: where func returns an error code of 0 on success and a nonzero
961: on failure.
963: Input parameters for func:
964: + snes - nonlinear context
965: . checkctx - optional user-defined context for use by step checking routine
966: . x - previous iterate
967: . y - new search direction and length
968: - w - current candidate iterate
970: Output parameters for func:
971: + y - search direction (possibly changed)
972: . w - current iterate (possibly modified)
973: . changed_y - indicates search direction was changed by this routine
974: - changed_w - indicates current iterate was changed by this routine
976: Level: advanced
978: Notes: All line searches accept the new iterate computed by the line search checking routine.
980: Only one of changed_y and changed_w can be PETSC_TRUE
982: On input w = x + y
984: SNESLineSearchNo() and SNESLineSearchNoNorms() (1) compute a candidate iterate u_{i+1}, (2) pass control
985: to the checking routine, and then (3) compute the corresponding nonlinear
986: function f(u_{i+1}) with the (possibly altered) iterate u_{i+1}.
988: SNESLineSearchQuadratic() and SNESLineSearchCubic() (1) compute a candidate iterate u_{i+1} as well as a
989: candidate nonlinear function f(u_{i+1}), (2) pass control to the checking
990: routine, and then (3) force a re-evaluation of f(u_{i+1}) if any changes
991: were made to the candidate iterate in the checking routine (as indicated
992: by flag=PETSC_TRUE). The overhead of this extra function re-evaluation can be
993: very costly, so use this feature with caution!
995: .keywords: SNES, nonlinear, set, line search check, step check, routine
997: .seealso: SNESLineSearchSet(), SNESLineSearchSetPreCheck()
998: @*/
999: PetscErrorCode SNESLineSearchSetPostCheck(SNES snes,PetscErrorCode (*func)(SNES,Vec,Vec,Vec,void*,PetscTruth*,PetscTruth*),void *checkctx)
1000: {
1001: PetscErrorCode ierr,(*f)(SNES,PetscErrorCode (*)(SNES,Vec,Vec,Vec,void*,PetscTruth*,PetscTruth*),void*);
1004: PetscObjectQueryFunction((PetscObject)snes,"SNESLineSearchSetPostCheck_C",(void (**)(void))&f);
1005: if (f) {
1006: (*f)(snes,func,checkctx);
1007: }
1008: return(0);
1009: }
1012: /*@C
1013: SNESLineSearchSetPreCheck - Sets a routine to check the validity of a new direction given by the linear solve
1014: before the line search is called.
1016: Input Parameters:
1017: + snes - nonlinear context obtained from SNESCreate()
1018: . func - pointer to function
1019: - checkctx - optional user-defined context for use by step checking routine
1021: Collective on SNES
1023: Calling sequence of func:
1024: .vb
1025: int func (SNES snes, Vec x,Vec y,void *checkctx, PetscTruth *changed_y)
1026: .ve
1027: where func returns an error code of 0 on success and a nonzero
1028: on failure.
1030: Input parameters for func:
1031: + snes - nonlinear context
1032: . checkctx - optional user-defined context for use by step checking routine
1033: . x - previous iterate
1034: - y - new search direction and length
1036: Output parameters for func:
1037: + y - search direction (possibly changed)
1038: - changed_y - indicates search direction was changed by this routine
1040: Level: advanced
1042: Notes: All line searches accept the new iterate computed by the line search checking routine.
1044: .keywords: SNES, nonlinear, set, line search check, step check, routine
1046: .seealso: SNESLineSearchSet(), SNESLineSearchSetPostCheck(), SNESSetUpdate()
1047: @*/
1048: PetscErrorCode SNESLineSearchSetPreCheck(SNES snes,PetscErrorCode (*func)(SNES,Vec,Vec,void*,PetscTruth*),void *checkctx)
1049: {
1050: PetscErrorCode ierr,(*f)(SNES,PetscErrorCode (*)(SNES,Vec,Vec,void*,PetscTruth*),void*);
1053: PetscObjectQueryFunction((PetscObject)snes,"SNESLineSearchSetPreCheck_C",(void (**)(void))&f);
1054: if (f) {
1055: (*f)(snes,func,checkctx);
1056: }
1057: return(0);
1058: }
1060: /* -------------------------------------------------------------------------- */
1066: PetscErrorCode SNESLineSearchSetPostCheck_LS(SNES snes,FCN1 func,void *checkctx)
1067: {
1069: ((SNES_LS *)(snes->data))->postcheckstep = func;
1070: ((SNES_LS *)(snes->data))->postcheck = checkctx;
1071: return(0);
1072: }
1078: PetscErrorCode SNESLineSearchSetPreCheck_LS(SNES snes,FCN3 func,void *checkctx)
1079: {
1081: ((SNES_LS *)(snes->data))->precheckstep = func;
1082: ((SNES_LS *)(snes->data))->precheck = checkctx;
1083: return(0);
1084: }
1087: /*
1088: SNESView_LS - Prints info from the SNESLS data structure.
1090: Input Parameters:
1091: . SNES - the SNES context
1092: . viewer - visualization context
1094: Application Interface Routine: SNESView()
1095: */
1098: static PetscErrorCode SNESView_LS(SNES snes,PetscViewer viewer)
1099: {
1100: SNES_LS *ls = (SNES_LS *)snes->data;
1101: const char *cstr;
1103: PetscTruth iascii;
1106: PetscTypeCompare((PetscObject)viewer,PETSC_VIEWER_ASCII,&iascii);
1107: if (iascii) {
1108: if (ls->LineSearch == SNESLineSearchNo) cstr = "SNESLineSearchNo";
1109: else if (ls->LineSearch == SNESLineSearchQuadratic) cstr = "SNESLineSearchQuadratic";
1110: else if (ls->LineSearch == SNESLineSearchCubic) cstr = "SNESLineSearchCubic";
1111: else cstr = "unknown";
1112: PetscViewerASCIIPrintf(viewer," line search variant: %s\n",cstr);
1113: PetscViewerASCIIPrintf(viewer," alpha=%G, maxstep=%G, steptol=%G\n",ls->alpha,ls->maxstep,ls->steptol);
1114: } else {
1115: SETERRQ1(PETSC_ERR_SUP,"Viewer type %s not supported for SNES EQ LS",((PetscObject)viewer)->type_name);
1116: }
1117: return(0);
1118: }
1119: /* -------------------------------------------------------------------------- */
1120: /*
1121: SNESSetFromOptions_LS - Sets various parameters for the SNESLS method.
1123: Input Parameter:
1124: . snes - the SNES context
1126: Application Interface Routine: SNESSetFromOptions()
1127: */
1130: static PetscErrorCode SNESSetFromOptions_LS(SNES snes)
1131: {
1132: SNES_LS *ls = (SNES_LS *)snes->data;
1133: const char *lses[] = {"basic","basicnonorms","quadratic","cubic"};
1135: PetscInt indx;
1136: PetscTruth flg;
1139: PetscOptionsHead("SNES Line search options");
1140: PetscOptionsReal("-snes_ls_alpha","Function norm must decrease by","None",ls->alpha,&ls->alpha,0);
1141: PetscOptionsReal("-snes_ls_maxstep","Step must be less than","None",ls->maxstep,&ls->maxstep,0);
1142: PetscOptionsReal("-snes_ls_steptol","Step must be greater than","None",ls->steptol,&ls->steptol,0);
1144: PetscOptionsEList("-snes_ls","Line search used","SNESLineSearchSet",lses,4,"cubic",&indx,&flg);
1145: if (flg) {
1146: switch (indx) {
1147: case 0:
1148: SNESLineSearchSet(snes,SNESLineSearchNo,PETSC_NULL);
1149: break;
1150: case 1:
1151: SNESLineSearchSet(snes,SNESLineSearchNoNorms,PETSC_NULL);
1152: break;
1153: case 2:
1154: SNESLineSearchSet(snes,SNESLineSearchQuadratic,PETSC_NULL);
1155: break;
1156: case 3:
1157: SNESLineSearchSet(snes,SNESLineSearchCubic,PETSC_NULL);
1158: break;
1159: }
1160: }
1161: PetscOptionsTail();
1162: return(0);
1163: }
1164: /* -------------------------------------------------------------------------- */
1165: /*MC
1166: SNESLS - Newton based nonlinear solver that uses a line search
1168: Options Database:
1169: + -snes_ls [cubic,quadratic,basic,basicnonorms] - Selects line search
1170: . -snes_ls_alpha <alpha> - Sets alpha
1171: . -snes_ls_maxstep <max> - Sets maxstep
1172: - -snes_ls_steptol <steptol> - Sets steptol, this is the minimum step size that the line search code
1173: will accept; min p[i]/x[i] < steptol. The -snes_stol <stol> is the minimum step length
1174: the default convergence test will use and is based on 2-norm(p) < stol*2-norm(x)
1176: Notes: This is the default nonlinear solver in SNES
1178: Level: beginner
1180: .seealso: SNESCreate(), SNES, SNESSetType(), SNESTR, SNESLineSearchSet(),
1181: SNESLineSearchSetPostCheck(), SNESLineSearchNo(), SNESLineSearchCubic(), SNESLineSearchQuadratic(),
1182: SNESLineSearchSet(), SNESLineSearchNoNorms(), SNESLineSearchSetPreCheck()
1184: M*/
1188: PetscErrorCode SNESCreate_LS(SNES snes)
1189: {
1191: SNES_LS *neP;
1194: snes->ops->setup = SNESSetUp_LS;
1195: snes->ops->solve = SNESSolve_LS;
1196: snes->ops->destroy = SNESDestroy_LS;
1197: snes->ops->setfromoptions = SNESSetFromOptions_LS;
1198: snes->ops->view = SNESView_LS;
1200: PetscNewLog(snes,SNES_LS,&neP);
1201: snes->data = (void*)neP;
1202: neP->alpha = 1.e-4;
1203: neP->maxstep = 1.e8;
1204: neP->steptol = 1.e-12;
1205: neP->LineSearch = SNESLineSearchCubic;
1206: neP->lsP = PETSC_NULL;
1207: neP->postcheckstep = PETSC_NULL;
1208: neP->postcheck = PETSC_NULL;
1209: neP->precheckstep = PETSC_NULL;
1210: neP->precheck = PETSC_NULL;
1212: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSet_C",
1213: "SNESLineSearchSet_LS",
1214: SNESLineSearchSet_LS);
1215: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSetPostCheck_C",
1216: "SNESLineSearchSetPostCheck_LS",
1217: SNESLineSearchSetPostCheck_LS);
1218: PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSetPreCheck_C",
1219: "SNESLineSearchSetPreCheck_LS",
1220: SNESLineSearchSetPreCheck_LS);
1222: return(0);
1223: }