xref: /petsc/src/snes/impls/ls/ls.c (revision 785e854f82a3c614b452fca2cf5ad4f2afe8bdde)
1 
2 #include <../src/snes/impls/ls/lsimpl.h>
3 
4 /*
5      Checks if J^T F = 0 which implies we've found a local minimum of the norm of the function,
6     || F(u) ||_2 but not a zero, F(u) = 0. In the case when one cannot compute J^T F we use the fact that
7     0 = (J^T F)^T W = F^T J W iff W not in the null space of J. Thanks for Jorge More
8     for this trick. One assumes that the probability that W is in the null space of J is very, very small.
9 */
10 #undef __FUNCT__
11 #define __FUNCT__ "SNESNEWTONLSCheckLocalMin_Private"
12 PetscErrorCode SNESNEWTONLSCheckLocalMin_Private(SNES snes,Mat A,Vec F,Vec W,PetscReal fnorm,PetscBool  *ismin)
13 {
14   PetscReal      a1;
15   PetscErrorCode ierr;
16   PetscBool      hastranspose;
17 
18   PetscFunctionBegin;
19   *ismin = PETSC_FALSE;
20   ierr   = MatHasOperation(A,MATOP_MULT_TRANSPOSE,&hastranspose);CHKERRQ(ierr);
21   if (hastranspose) {
22     /* Compute || J^T F|| */
23     ierr = MatMultTranspose(A,F,W);CHKERRQ(ierr);
24     ierr = VecNorm(W,NORM_2,&a1);CHKERRQ(ierr);
25     ierr = PetscInfo1(snes,"|| J^T F|| %14.12e near zero implies found a local minimum\n",(double)(a1/fnorm));CHKERRQ(ierr);
26     if (a1/fnorm < 1.e-4) *ismin = PETSC_TRUE;
27   } else {
28     Vec         work;
29     PetscScalar result;
30     PetscReal   wnorm;
31 
32     ierr = VecSetRandom(W,NULL);CHKERRQ(ierr);
33     ierr = VecNorm(W,NORM_2,&wnorm);CHKERRQ(ierr);
34     ierr = VecDuplicate(W,&work);CHKERRQ(ierr);
35     ierr = MatMult(A,W,work);CHKERRQ(ierr);
36     ierr = VecDot(F,work,&result);CHKERRQ(ierr);
37     ierr = VecDestroy(&work);CHKERRQ(ierr);
38     a1   = PetscAbsScalar(result)/(fnorm*wnorm);
39     ierr = PetscInfo1(snes,"(F^T J random)/(|| F ||*||random|| %14.12e near zero implies found a local minimum\n",(double)a1);CHKERRQ(ierr);
40     if (a1 < 1.e-4) *ismin = PETSC_TRUE;
41   }
42   PetscFunctionReturn(0);
43 }
44 
45 /*
46      Checks if J^T(F - J*X) = 0
47 */
48 #undef __FUNCT__
49 #define __FUNCT__ "SNESNEWTONLSCheckResidual_Private"
50 PetscErrorCode SNESNEWTONLSCheckResidual_Private(SNES snes,Mat A,Vec F,Vec X,Vec W1,Vec W2)
51 {
52   PetscReal      a1,a2;
53   PetscErrorCode ierr;
54   PetscBool      hastranspose;
55 
56   PetscFunctionBegin;
57   ierr = MatHasOperation(A,MATOP_MULT_TRANSPOSE,&hastranspose);CHKERRQ(ierr);
58   if (hastranspose) {
59     ierr = MatMult(A,X,W1);CHKERRQ(ierr);
60     ierr = VecAXPY(W1,-1.0,F);CHKERRQ(ierr);
61 
62     /* Compute || J^T W|| */
63     ierr = MatMultTranspose(A,W1,W2);CHKERRQ(ierr);
64     ierr = VecNorm(W1,NORM_2,&a1);CHKERRQ(ierr);
65     ierr = VecNorm(W2,NORM_2,&a2);CHKERRQ(ierr);
66     if (a1 != 0.0) {
67       ierr = PetscInfo1(snes,"||J^T(F-Ax)||/||F-AX|| %14.12e near zero implies inconsistent rhs\n",(double)(a2/a1));CHKERRQ(ierr);
68     }
69   }
70   PetscFunctionReturn(0);
71 }
72 
73 /*  --------------------------------------------------------------------
74 
75      This file implements a truncated Newton method with a line search,
76      for solving a system of nonlinear equations, using the KSP, Vec,
77      and Mat interfaces for linear solvers, vectors, and matrices,
78      respectively.
79 
80      The following basic routines are required for each nonlinear solver:
81           SNESCreate_XXX()          - Creates a nonlinear solver context
82           SNESSetFromOptions_XXX()  - Sets runtime options
83           SNESSolve_XXX()           - Solves the nonlinear system
84           SNESDestroy_XXX()         - Destroys the nonlinear solver context
85      The suffix "_XXX" denotes a particular implementation, in this case
86      we use _NEWTONLS (e.g., SNESCreate_NEWTONLS, SNESSolve_NEWTONLS) for solving
87      systems of nonlinear equations with a line search (LS) method.
88      These routines are actually called via the common user interface
89      routines SNESCreate(), SNESSetFromOptions(), SNESSolve(), and
90      SNESDestroy(), so the application code interface remains identical
91      for all nonlinear solvers.
92 
93      Another key routine is:
94           SNESSetUp_XXX()           - Prepares for the use of a nonlinear solver
95      by setting data structures and options.   The interface routine SNESSetUp()
96      is not usually called directly by the user, but instead is called by
97      SNESSolve() if necessary.
98 
99      Additional basic routines are:
100           SNESView_XXX()            - Prints details of runtime options that
101                                       have actually been used.
102      These are called by application codes via the interface routines
103      SNESView().
104 
105      The various types of solvers (preconditioners, Krylov subspace methods,
106      nonlinear solvers, timesteppers) are all organized similarly, so the
107      above description applies to these categories also.
108 
109     -------------------------------------------------------------------- */
110 /*
111    SNESSolve_NEWTONLS - Solves a nonlinear system with a truncated Newton
112    method with a line search.
113 
114    Input Parameters:
115 .  snes - the SNES context
116 
117    Output Parameter:
118 .  outits - number of iterations until termination
119 
120    Application Interface Routine: SNESSolve()
121 
122    Notes:
123    This implements essentially a truncated Newton method with a
124    line search.  By default a cubic backtracking line search
125    is employed, as described in the text "Numerical Methods for
126    Unconstrained Optimization and Nonlinear Equations" by Dennis
127    and Schnabel.
128 */
129 #undef __FUNCT__
130 #define __FUNCT__ "SNESSolve_NEWTONLS"
131 PetscErrorCode SNESSolve_NEWTONLS(SNES snes)
132 {
133   PetscErrorCode      ierr;
134   PetscInt            maxits,i,lits;
135   PetscBool           lssucceed;
136   MatStructure        flg = DIFFERENT_NONZERO_PATTERN;
137   PetscReal           fnorm,gnorm,xnorm,ynorm;
138   Vec                 Y,X,F,G,W;
139   KSPConvergedReason  kspreason;
140   PetscBool           domainerror;
141   SNESLineSearch      linesearch;
142   SNESConvergedReason reason;
143 
144   PetscFunctionBegin;
145   snes->numFailures            = 0;
146   snes->numLinearSolveFailures = 0;
147   snes->reason                 = SNES_CONVERGED_ITERATING;
148 
149   maxits = snes->max_its;               /* maximum number of iterations */
150   X      = snes->vec_sol;               /* solution vector */
151   F      = snes->vec_func;              /* residual vector */
152   Y      = snes->vec_sol_update;        /* newton step */
153   G      = snes->work[0];
154   W      = snes->work[1];
155 
156   ierr       = PetscObjectSAWsTakeAccess((PetscObject)snes);CHKERRQ(ierr);
157   snes->iter = 0;
158   snes->norm = 0.0;
159   ierr       = PetscObjectSAWsGrantAccess((PetscObject)snes);CHKERRQ(ierr);
160   ierr       = SNESGetLineSearch(snes, &linesearch);CHKERRQ(ierr);
161 
162   /* compute the preconditioned function first in the case of left preconditioning with preconditioned function */
163   if (snes->pc && snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_PRECONDITIONED) {
164     ierr = SNESApplyPC(snes,X,PETSC_NULL,PETSC_NULL,F);CHKERRQ(ierr);
165     ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
166     if (reason < 0  && reason != SNES_DIVERGED_MAX_IT) {
167       snes->reason = SNES_DIVERGED_INNER;
168       PetscFunctionReturn(0);
169     }
170 
171     ierr = VecNormBegin(F,NORM_2,&fnorm);CHKERRQ(ierr);
172     ierr = VecNormEnd(F,NORM_2,&fnorm);CHKERRQ(ierr);
173   } else {
174     if (!snes->vec_func_init_set) {
175       ierr = SNESComputeFunction(snes,X,F);CHKERRQ(ierr);
176       ierr = SNESGetFunctionDomainError(snes, &domainerror);CHKERRQ(ierr);
177       if (domainerror) {
178         snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
179         PetscFunctionReturn(0);
180       }
181     } else snes->vec_func_init_set = PETSC_FALSE;
182   }
183 
184   ierr = VecNorm(F,NORM_2,&fnorm);CHKERRQ(ierr);        /* fnorm <- ||F||  */
185   if (PetscIsInfOrNanReal(fnorm)) {
186     snes->reason = SNES_DIVERGED_FNORM_NAN;
187     PetscFunctionReturn(0);
188   }
189 
190   ierr       = PetscObjectSAWsTakeAccess((PetscObject)snes);CHKERRQ(ierr);
191   snes->norm = fnorm;
192   ierr       = PetscObjectSAWsGrantAccess((PetscObject)snes);CHKERRQ(ierr);
193   ierr       = SNESLogConvergenceHistory(snes,fnorm,0);CHKERRQ(ierr);
194   ierr       = SNESMonitor(snes,0,fnorm);CHKERRQ(ierr);
195 
196   /* test convergence */
197   ierr = (*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP);CHKERRQ(ierr);
198   if (snes->reason) PetscFunctionReturn(0);
199 
200   for (i=0; i<maxits; i++) {
201 
202     /* Call general purpose update function */
203     if (snes->ops->update) {
204       ierr = (*snes->ops->update)(snes, snes->iter);CHKERRQ(ierr);
205     }
206 
207     /* apply the nonlinear preconditioner */
208     if (snes->pc) {
209       if (snes->pcside == PC_RIGHT) {
210         ierr = SNESSetInitialFunction(snes->pc, F);CHKERRQ(ierr);
211         ierr = PetscLogEventBegin(SNES_NPCSolve,snes->pc,X,snes->vec_rhs,0);CHKERRQ(ierr);
212         ierr = SNESSolve(snes->pc, snes->vec_rhs, X);CHKERRQ(ierr);
213         ierr = PetscLogEventEnd(SNES_NPCSolve,snes->pc,X,snes->vec_rhs,0);CHKERRQ(ierr);
214         ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
215         if (reason < 0  && reason != SNES_DIVERGED_MAX_IT) {
216           snes->reason = SNES_DIVERGED_INNER;
217           PetscFunctionReturn(0);
218         }
219         ierr = SNESGetPCFunction(snes,F,&fnorm);CHKERRQ(ierr);
220       } else if (snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_UNPRECONDITIONED) {
221         ierr = SNESApplyPC(snes,X,F,&fnorm,F);CHKERRQ(ierr);
222         ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
223         if (reason < 0  && reason != SNES_DIVERGED_MAX_IT) {
224           snes->reason = SNES_DIVERGED_INNER;
225           PetscFunctionReturn(0);
226         }
227       }
228     }
229 
230     /* Solve J Y = F, where J is Jacobian matrix */
231     ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);CHKERRQ(ierr);
232     ierr = KSPSetOperators(snes->ksp,snes->jacobian,snes->jacobian_pre,flg);CHKERRQ(ierr);
233     ierr = KSPSolve(snes->ksp,F,Y);CHKERRQ(ierr);
234     ierr = KSPGetConvergedReason(snes->ksp,&kspreason);CHKERRQ(ierr);
235     if (kspreason < 0) {
236       if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
237         ierr         = PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);CHKERRQ(ierr);
238         snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
239         break;
240       }
241     }
242     ierr              = KSPGetIterationNumber(snes->ksp,&lits);CHKERRQ(ierr);
243     snes->linear_its += lits;
244     ierr              = PetscInfo2(snes,"iter=%D, linear solve iterations=%D\n",snes->iter,lits);CHKERRQ(ierr);
245 
246     if (PetscLogPrintInfo) {
247       ierr = SNESNEWTONLSCheckResidual_Private(snes,snes->jacobian,F,Y,G,W);CHKERRQ(ierr);
248     }
249 
250     /* Compute a (scaled) negative update in the line search routine:
251          X <- X - lambda*Y
252        and evaluate F = function(X) (depends on the line search).
253     */
254     gnorm = fnorm;
255     ierr  = SNESLineSearchApply(linesearch, X, F, &fnorm, Y);CHKERRQ(ierr);
256     ierr  = SNESLineSearchGetSuccess(linesearch, &lssucceed);CHKERRQ(ierr);
257     ierr  = SNESLineSearchGetNorms(linesearch, &xnorm, &fnorm, &ynorm);CHKERRQ(ierr);
258     ierr  = PetscInfo4(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n",(double)gnorm,(double)fnorm,(double)ynorm,(int)lssucceed);CHKERRQ(ierr);
259     if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
260     ierr = SNESGetFunctionDomainError(snes, &domainerror);CHKERRQ(ierr);
261     if (domainerror) {
262       snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
263       PetscFunctionReturn(0);
264     }
265     if (!lssucceed) {
266       if (snes->stol*xnorm > ynorm) {
267         snes->reason = SNES_CONVERGED_SNORM_RELATIVE;
268         PetscFunctionReturn(0);
269       }
270       if (++snes->numFailures >= snes->maxFailures) {
271         PetscBool ismin;
272         snes->reason = SNES_DIVERGED_LINE_SEARCH;
273         ierr         = SNESNEWTONLSCheckLocalMin_Private(snes,snes->jacobian,F,W,fnorm,&ismin);CHKERRQ(ierr);
274         if (ismin) snes->reason = SNES_DIVERGED_LOCAL_MIN;
275         break;
276       }
277     }
278     /* Monitor convergence */
279     ierr       = PetscObjectSAWsTakeAccess((PetscObject)snes);CHKERRQ(ierr);
280     snes->iter = i+1;
281     snes->norm = fnorm;
282     ierr       = PetscObjectSAWsGrantAccess((PetscObject)snes);CHKERRQ(ierr);
283     ierr       = SNESLogConvergenceHistory(snes,snes->norm,lits);CHKERRQ(ierr);
284     ierr       = SNESMonitor(snes,snes->iter,snes->norm);CHKERRQ(ierr);
285     /* Test for convergence */
286     ierr = (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);CHKERRQ(ierr);
287     if (snes->reason) break;
288   }
289   if (i == maxits) {
290     ierr = PetscInfo1(snes,"Maximum number of iterations has been reached: %D\n",maxits);CHKERRQ(ierr);
291     if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
292   }
293   PetscFunctionReturn(0);
294 }
295 /* -------------------------------------------------------------------------- */
296 /*
297    SNESSetUp_NEWTONLS - Sets up the internal data structures for the later use
298    of the SNESNEWTONLS nonlinear solver.
299 
300    Input Parameter:
301 .  snes - the SNES context
302 .  x - the solution vector
303 
304    Application Interface Routine: SNESSetUp()
305 
306    Notes:
307    For basic use of the SNES solvers, the user need not explicitly call
308    SNESSetUp(), since these actions will automatically occur during
309    the call to SNESSolve().
310  */
311 #undef __FUNCT__
312 #define __FUNCT__ "SNESSetUp_NEWTONLS"
313 PetscErrorCode SNESSetUp_NEWTONLS(SNES snes)
314 {
315   PetscErrorCode ierr;
316 
317   PetscFunctionBegin;
318   ierr = SNESSetWorkVecs(snes,2);CHKERRQ(ierr);
319   ierr = SNESSetUpMatrices(snes);CHKERRQ(ierr);
320   if (snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_DEFAULT) snes->functype = SNES_FUNCTION_PRECONDITIONED;
321   PetscFunctionReturn(0);
322 }
323 /* -------------------------------------------------------------------------- */
324 
325 #undef __FUNCT__
326 #define __FUNCT__ "SNESReset_NEWTONLS"
327 PetscErrorCode SNESReset_NEWTONLS(SNES snes)
328 {
329   PetscFunctionBegin;
330   PetscFunctionReturn(0);
331 }
332 
333 /*
334    SNESDestroy_NEWTONLS - Destroys the private SNES_NEWTONLS context that was created
335    with SNESCreate_NEWTONLS().
336 
337    Input Parameter:
338 .  snes - the SNES context
339 
340    Application Interface Routine: SNESDestroy()
341  */
342 #undef __FUNCT__
343 #define __FUNCT__ "SNESDestroy_NEWTONLS"
344 PetscErrorCode SNESDestroy_NEWTONLS(SNES snes)
345 {
346   PetscErrorCode ierr;
347 
348   PetscFunctionBegin;
349   ierr = SNESReset_NEWTONLS(snes);CHKERRQ(ierr);
350   ierr = PetscFree(snes->data);CHKERRQ(ierr);
351   PetscFunctionReturn(0);
352 }
353 /* -------------------------------------------------------------------------- */
354 
355 /*
356    SNESView_NEWTONLS - Prints info from the SNESNEWTONLS data structure.
357 
358    Input Parameters:
359 .  SNES - the SNES context
360 .  viewer - visualization context
361 
362    Application Interface Routine: SNESView()
363 */
364 #undef __FUNCT__
365 #define __FUNCT__ "SNESView_NEWTONLS"
366 static PetscErrorCode SNESView_NEWTONLS(SNES snes,PetscViewer viewer)
367 {
368   PetscErrorCode ierr;
369   PetscBool      iascii;
370 
371   PetscFunctionBegin;
372   ierr = PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);CHKERRQ(ierr);
373   if (iascii) {
374   }
375   PetscFunctionReturn(0);
376 }
377 
378 /* -------------------------------------------------------------------------- */
379 /*
380    SNESSetFromOptions_NEWTONLS - Sets various parameters for the SNESNEWTONLS method.
381 
382    Input Parameter:
383 .  snes - the SNES context
384 
385    Application Interface Routine: SNESSetFromOptions()
386 */
387 #undef __FUNCT__
388 #define __FUNCT__ "SNESSetFromOptions_NEWTONLS"
389 static PetscErrorCode SNESSetFromOptions_NEWTONLS(SNES snes)
390 {
391   PetscErrorCode ierr;
392   SNESLineSearch linesearch;
393 
394   PetscFunctionBegin;
395   ierr = PetscOptionsHead("SNESNEWTONLS options");CHKERRQ(ierr);
396   ierr = PetscOptionsTail();CHKERRQ(ierr);
397   /* set the default line search type */
398   if (!snes->linesearch) {
399     ierr = SNESGetLineSearch(snes, &linesearch);CHKERRQ(ierr);
400     ierr = SNESLineSearchSetType(linesearch, SNESLINESEARCHBT);CHKERRQ(ierr);
401   }
402   PetscFunctionReturn(0);
403 }
404 
405 /* -------------------------------------------------------------------------- */
406 /*MC
407       SNESNEWTONLS - Newton based nonlinear solver that uses a line search
408 
409    Options Database:
410 +   -snes_linesearch_type <bt> - bt,basic.  Select line search type
411 .   -snes_linesearch_order <3> - 2, 3. Selects the order of the line search for bt
412 .   -snes_linesearch_norms <true> - Turns on/off computation of the norms for basic linesearch
413 .   -snes_linesearch_alpha <alpha> - Sets alpha used in determining if reduction in function norm is sufficient
414 .   -snes_linesearch_maxstep <maxstep> - Sets the maximum stepsize the line search will use (if the 2-norm(y) > maxstep then scale y to be y = (maxstep/2-norm(y)) *y)
415 .   -snes_linesearch_minlambda <minlambda>  - Sets the minimum lambda the line search will tolerate
416 .   -snes_linesearch_monitor - print information about progress of line searches
417 -   -snes_linesearch_damping - damping factor used for basic line search
418 
419     Notes: This is the default nonlinear solver in SNES
420 
421    Level: beginner
422 
423 .seealso:  SNESCreate(), SNES, SNESSetType(), SNESNEWTONTR, SNESQN, SNESLineSearchSetType(), SNESLineSearchSetOrder()
424            SNESLineSearchSetPostCheck(), SNESLineSearchSetPreCheck() SNESLineSearchSetComputeNorms()
425 
426 M*/
427 #undef __FUNCT__
428 #define __FUNCT__ "SNESCreate_NEWTONLS"
429 PETSC_EXTERN PetscErrorCode SNESCreate_NEWTONLS(SNES snes)
430 {
431   PetscErrorCode ierr;
432   SNES_NEWTONLS  *neP;
433 
434   PetscFunctionBegin;
435   snes->ops->setup          = SNESSetUp_NEWTONLS;
436   snes->ops->solve          = SNESSolve_NEWTONLS;
437   snes->ops->destroy        = SNESDestroy_NEWTONLS;
438   snes->ops->setfromoptions = SNESSetFromOptions_NEWTONLS;
439   snes->ops->view           = SNESView_NEWTONLS;
440   snes->ops->reset          = SNESReset_NEWTONLS;
441 
442   snes->pcside  = PC_RIGHT;
443   snes->usesksp = PETSC_TRUE;
444   snes->usespc  = PETSC_TRUE;
445   ierr          = PetscNewLog(snes,SNES_NEWTONLS,&neP);CHKERRQ(ierr);
446   snes->data    = (void*)neP;
447   PetscFunctionReturn(0);
448 }
449