xref: /petsc/src/snes/impls/ls/ls.c (revision e07f7f948f87ca2cb8721c32a91a5a8f40991471)
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       = PetscObjectAMSTakeAccess((PetscObject)snes);CHKERRQ(ierr);
157   snes->iter = 0;
158   snes->norm = 0.0;
159   ierr       = PetscObjectAMSGrantAccess((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     if (!snes->norm_init_set) {
184       ierr = VecNormBegin(F,NORM_2,&fnorm);CHKERRQ(ierr);        /* fnorm <- ||F||  */
185       ierr = VecNormEnd(F,NORM_2,&fnorm);CHKERRQ(ierr);
186       if (PetscIsInfOrNanReal(fnorm)) {
187         snes->reason = SNES_DIVERGED_FNORM_NAN;
188         PetscFunctionReturn(0);
189       }
190     } else {
191       fnorm               = snes->norm_init;
192       snes->norm_init_set = PETSC_FALSE;
193     }
194   }
195 
196   ierr       = PetscObjectAMSTakeAccess((PetscObject)snes);CHKERRQ(ierr);
197   snes->norm = fnorm;
198   ierr       = PetscObjectAMSGrantAccess((PetscObject)snes);CHKERRQ(ierr);
199   ierr       = SNESLogConvergenceHistory(snes,fnorm,0);CHKERRQ(ierr);
200   ierr       = SNESMonitor(snes,0,fnorm);CHKERRQ(ierr);
201 
202   /* test convergence */
203   ierr = (*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP);CHKERRQ(ierr);
204   if (snes->reason) PetscFunctionReturn(0);
205 
206   for (i=0; i<maxits; i++) {
207 
208     /* Call general purpose update function */
209     if (snes->ops->update) {
210       ierr = (*snes->ops->update)(snes, snes->iter);CHKERRQ(ierr);
211     }
212 
213     /* apply the nonlinear preconditioner */
214     if (snes->pc) {
215       if (snes->pcside == PC_RIGHT) {
216         ierr = SNESSetInitialFunction(snes->pc, F);CHKERRQ(ierr);
217         ierr = SNESSetInitialFunctionNorm(snes->pc, fnorm);CHKERRQ(ierr);
218         ierr = PetscLogEventBegin(SNES_NPCSolve,snes->pc,X,snes->vec_rhs,0);CHKERRQ(ierr);
219         ierr = SNESSolve(snes->pc, snes->vec_rhs, X);CHKERRQ(ierr);
220         ierr = PetscLogEventEnd(SNES_NPCSolve,snes->pc,X,snes->vec_rhs,0);CHKERRQ(ierr);
221         ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
222         if (reason < 0  && reason != SNES_DIVERGED_MAX_IT) {
223           snes->reason = SNES_DIVERGED_INNER;
224           PetscFunctionReturn(0);
225         }
226         ierr = SNESGetPCFunction(snes,F,&fnorm);CHKERRQ(ierr);
227       } else if (snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_UNPRECONDITIONED) {
228         ierr = SNESApplyPC(snes,X,F,&fnorm,F);CHKERRQ(ierr);
229         ierr = SNESGetConvergedReason(snes->pc,&reason);CHKERRQ(ierr);
230         if (reason < 0  && reason != SNES_DIVERGED_MAX_IT) {
231           snes->reason = SNES_DIVERGED_INNER;
232           PetscFunctionReturn(0);
233         }
234       }
235     }
236 
237     /* Solve J Y = F, where J is Jacobian matrix */
238     ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);CHKERRQ(ierr);
239     ierr = KSPSetOperators(snes->ksp,snes->jacobian,snes->jacobian_pre,flg);CHKERRQ(ierr);
240     ierr = KSPSolve(snes->ksp,F,Y);CHKERRQ(ierr);
241     ierr = KSPGetConvergedReason(snes->ksp,&kspreason);CHKERRQ(ierr);
242     if (kspreason < 0) {
243       if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) {
244         ierr         = PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);CHKERRQ(ierr);
245         snes->reason = SNES_DIVERGED_LINEAR_SOLVE;
246         break;
247       }
248     }
249     ierr              = KSPGetIterationNumber(snes->ksp,&lits);CHKERRQ(ierr);
250     snes->linear_its += lits;
251     ierr              = PetscInfo2(snes,"iter=%D, linear solve iterations=%D\n",snes->iter,lits);CHKERRQ(ierr);
252 
253     if (PetscLogPrintInfo) {
254       ierr = SNESNEWTONLSCheckResidual_Private(snes,snes->jacobian,F,Y,G,W);CHKERRQ(ierr);
255     }
256 
257     /* Compute a (scaled) negative update in the line search routine:
258          X <- X - lambda*Y
259        and evaluate F = function(X) (depends on the line search).
260     */
261     gnorm = fnorm;
262     ierr  = SNESLineSearchApply(linesearch, X, F, &fnorm, Y);CHKERRQ(ierr);
263     ierr  = SNESLineSearchGetSuccess(linesearch, &lssucceed);CHKERRQ(ierr);
264     ierr  = SNESLineSearchGetNorms(linesearch, &xnorm, &fnorm, &ynorm);CHKERRQ(ierr);
265     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);
266     if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break;
267     ierr = SNESGetFunctionDomainError(snes, &domainerror);CHKERRQ(ierr);
268     if (domainerror) {
269       snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN;
270       PetscFunctionReturn(0);
271     }
272     if (!lssucceed) {
273       if (snes->stol*xnorm > ynorm) {
274         snes->reason = SNES_CONVERGED_SNORM_RELATIVE;
275         PetscFunctionReturn(0);
276       }
277       if (++snes->numFailures >= snes->maxFailures) {
278         PetscBool ismin;
279         snes->reason = SNES_DIVERGED_LINE_SEARCH;
280         ierr         = SNESNEWTONLSCheckLocalMin_Private(snes,snes->jacobian,F,W,fnorm,&ismin);CHKERRQ(ierr);
281         if (ismin) snes->reason = SNES_DIVERGED_LOCAL_MIN;
282         break;
283       }
284     }
285     /* Monitor convergence */
286     ierr       = PetscObjectAMSTakeAccess((PetscObject)snes);CHKERRQ(ierr);
287     snes->iter = i+1;
288     snes->norm = fnorm;
289     ierr       = PetscObjectAMSGrantAccess((PetscObject)snes);CHKERRQ(ierr);
290     ierr       = SNESLogConvergenceHistory(snes,snes->norm,lits);CHKERRQ(ierr);
291     ierr       = SNESMonitor(snes,snes->iter,snes->norm);CHKERRQ(ierr);
292     /* Test for convergence */
293     ierr = (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);CHKERRQ(ierr);
294     if (snes->reason) break;
295   }
296   if (i == maxits) {
297     ierr = PetscInfo1(snes,"Maximum number of iterations has been reached: %D\n",maxits);CHKERRQ(ierr);
298     if (!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT;
299   }
300   PetscFunctionReturn(0);
301 }
302 /* -------------------------------------------------------------------------- */
303 /*
304    SNESSetUp_NEWTONLS - Sets up the internal data structures for the later use
305    of the SNESNEWTONLS nonlinear solver.
306 
307    Input Parameter:
308 .  snes - the SNES context
309 .  x - the solution vector
310 
311    Application Interface Routine: SNESSetUp()
312 
313    Notes:
314    For basic use of the SNES solvers, the user need not explicitly call
315    SNESSetUp(), since these actions will automatically occur during
316    the call to SNESSolve().
317  */
318 #undef __FUNCT__
319 #define __FUNCT__ "SNESSetUp_NEWTONLS"
320 PetscErrorCode SNESSetUp_NEWTONLS(SNES snes)
321 {
322   PetscErrorCode ierr;
323 
324   PetscFunctionBegin;
325   ierr = SNESSetWorkVecs(snes,2);CHKERRQ(ierr);
326   ierr = SNESSetUpMatrices(snes);CHKERRQ(ierr);
327   if (snes->pcside == PC_LEFT && snes->functype == SNES_FUNCTION_DEFAULT) snes->functype = SNES_FUNCTION_PRECONDITIONED;
328   PetscFunctionReturn(0);
329 }
330 /* -------------------------------------------------------------------------- */
331 
332 #undef __FUNCT__
333 #define __FUNCT__ "SNESReset_NEWTONLS"
334 PetscErrorCode SNESReset_NEWTONLS(SNES snes)
335 {
336   PetscFunctionBegin;
337   PetscFunctionReturn(0);
338 }
339 
340 /*
341    SNESDestroy_NEWTONLS - Destroys the private SNES_NEWTONLS context that was created
342    with SNESCreate_NEWTONLS().
343 
344    Input Parameter:
345 .  snes - the SNES context
346 
347    Application Interface Routine: SNESDestroy()
348  */
349 #undef __FUNCT__
350 #define __FUNCT__ "SNESDestroy_NEWTONLS"
351 PetscErrorCode SNESDestroy_NEWTONLS(SNES snes)
352 {
353   PetscErrorCode ierr;
354 
355   PetscFunctionBegin;
356   ierr = SNESReset_NEWTONLS(snes);CHKERRQ(ierr);
357   ierr = PetscFree(snes->data);CHKERRQ(ierr);
358   PetscFunctionReturn(0);
359 }
360 /* -------------------------------------------------------------------------- */
361 
362 /*
363    SNESView_NEWTONLS - Prints info from the SNESNEWTONLS data structure.
364 
365    Input Parameters:
366 .  SNES - the SNES context
367 .  viewer - visualization context
368 
369    Application Interface Routine: SNESView()
370 */
371 #undef __FUNCT__
372 #define __FUNCT__ "SNESView_NEWTONLS"
373 static PetscErrorCode SNESView_NEWTONLS(SNES snes,PetscViewer viewer)
374 {
375   PetscErrorCode ierr;
376   PetscBool      iascii;
377 
378   PetscFunctionBegin;
379   ierr = PetscObjectTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);CHKERRQ(ierr);
380   if (iascii) {
381   }
382   PetscFunctionReturn(0);
383 }
384 
385 /* -------------------------------------------------------------------------- */
386 /*
387    SNESSetFromOptions_NEWTONLS - Sets various parameters for the SNESNEWTONLS method.
388 
389    Input Parameter:
390 .  snes - the SNES context
391 
392    Application Interface Routine: SNESSetFromOptions()
393 */
394 #undef __FUNCT__
395 #define __FUNCT__ "SNESSetFromOptions_NEWTONLS"
396 static PetscErrorCode SNESSetFromOptions_NEWTONLS(SNES snes)
397 {
398   PetscErrorCode ierr;
399   SNESLineSearch linesearch;
400 
401   PetscFunctionBegin;
402   ierr = PetscOptionsHead("SNESNEWTONLS options");CHKERRQ(ierr);
403   ierr = PetscOptionsTail();CHKERRQ(ierr);
404   /* set the default line search type */
405   if (!snes->linesearch) {
406     ierr = SNESGetLineSearch(snes, &linesearch);CHKERRQ(ierr);
407     ierr = SNESLineSearchSetType(linesearch, SNESLINESEARCHBT);CHKERRQ(ierr);
408   }
409   PetscFunctionReturn(0);
410 }
411 
412 /* -------------------------------------------------------------------------- */
413 /*MC
414       SNESNEWTONLS - Newton based nonlinear solver that uses a line search
415 
416    Options Database:
417 +   -snes_linesearch_type <bt> - bt,basic.  Select line search type
418 .   -snes_linesearch_order <3> - 2, 3. Selects the order of the line search for bt
419 .   -snes_linesearch_norms <true> - Turns on/off computation of the norms for basic linesearch
420 .   -snes_linesearch_alpha <alpha> - Sets alpha used in determining if reduction in function norm is sufficient
421 .   -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)
422 .   -snes_linesearch_minlambda <minlambda>  - Sets the minimum lambda the line search will tolerate
423 .   -snes_linesearch_monitor - print information about progress of line searches
424 -   -snes_linesearch_damping - damping factor used for basic line search
425 
426     Notes: This is the default nonlinear solver in SNES
427 
428    Level: beginner
429 
430 .seealso:  SNESCreate(), SNES, SNESSetType(), SNESNEWTONTR, SNESQN, SNESLineSearchSetType(), SNESLineSearchSetOrder()
431            SNESLineSearchSetPostCheck(), SNESLineSearchSetPreCheck() SNESLineSearchSetComputeNorms()
432 
433 M*/
434 #undef __FUNCT__
435 #define __FUNCT__ "SNESCreate_NEWTONLS"
436 PETSC_EXTERN PetscErrorCode SNESCreate_NEWTONLS(SNES snes)
437 {
438   PetscErrorCode ierr;
439   SNES_NEWTONLS  *neP;
440 
441   PetscFunctionBegin;
442   snes->ops->setup          = SNESSetUp_NEWTONLS;
443   snes->ops->solve          = SNESSolve_NEWTONLS;
444   snes->ops->destroy        = SNESDestroy_NEWTONLS;
445   snes->ops->setfromoptions = SNESSetFromOptions_NEWTONLS;
446   snes->ops->view           = SNESView_NEWTONLS;
447   snes->ops->reset          = SNESReset_NEWTONLS;
448 
449   snes->pcside  = PC_RIGHT;
450   snes->usesksp = PETSC_TRUE;
451   snes->usespc  = PETSC_TRUE;
452   ierr          = PetscNewLog(snes,SNES_NEWTONLS,&neP);CHKERRQ(ierr);
453   snes->data    = (void*)neP;
454   PetscFunctionReturn(0);
455 }
456