xref: /petsc/src/snes/impls/tr/tr.c (revision 91e9ee9fc0200d5446579c7185d9e655300e8525)
1 #ifdef PETSC_RCS_HEADER
2 static char vcid[] = "$Id: tr.c,v 1.94 1999/02/10 23:11:31 bsmith Exp bsmith $";
3 #endif
4 
5 #include "src/snes/impls/tr/tr.h"                /*I   "snes.h"   I*/
6 
7 /*
8    This convergence test determines if the two norm of the
9    solution lies outside the trust region, if so it halts.
10 */
11 #undef __FUNC__
12 #define __FUNC__ "SNES_TR_KSPConverged_Private"
13 int SNES_TR_KSPConverged_Private(KSP ksp,int n, double rnorm, void *ctx)
14 {
15   SNES                snes = (SNES) ctx;
16   SNES_KSP_EW_ConvCtx *kctx = (SNES_KSP_EW_ConvCtx*)snes->kspconvctx;
17   SNES_TR             *neP = (SNES_TR*)snes->data;
18   Vec                 x;
19   double              norm;
20   int                 ierr, convinfo;
21 
22   PetscFunctionBegin;
23   if (snes->ksp_ewconv) {
24     if (!kctx) SETERRQ(PETSC_ERR_ARG_WRONGSTATE,0,"Eisenstat-Walker onvergence context not created");
25     if (n == 0) {ierr = SNES_KSP_EW_ComputeRelativeTolerance_Private(snes,ksp); CHKERRQ(ierr);}
26     kctx->lresid_last = rnorm;
27   }
28   convinfo = KSPDefaultConverged(ksp,n,rnorm,ctx);
29   if (convinfo) {
30     PLogInfo(snes,"SNES_TR_KSPConverged_Private: KSP iterations=%d, rnorm=%g\n",n,rnorm);
31     PetscFunctionReturn(convinfo);
32   }
33 
34   /* Determine norm of solution */
35   ierr = KSPBuildSolution(ksp,0,&x); CHKERRQ(ierr);
36   ierr = VecNorm(x,NORM_2,&norm); CHKERRQ(ierr);
37   if (norm >= neP->delta) {
38     PLogInfo(snes,"SNES_TR_KSPConverged_Private: KSP iterations=%d, rnorm=%g\n",n,rnorm);
39     PLogInfo(snes,"SNES_TR_KSPConverged_Private: Ending linear iteration early, delta=%g, length=%g\n",
40              neP->delta,norm);
41     PetscFunctionReturn(1);
42   }
43   PetscFunctionReturn(0);
44 }
45 
46 /*
47    SNESSolve_EQ_TR - Implements Newton's Method with a very simple trust
48    region approach for solving systems of nonlinear equations.
49 
50    The basic algorithm is taken from "The Minpack Project", by More',
51    Sorensen, Garbow, Hillstrom, pages 88-111 of "Sources and Development
52    of Mathematical Software", Wayne Cowell, editor.
53 
54    This is intended as a model implementation, since it does not
55    necessarily have many of the bells and whistles of other
56    implementations.
57 */
58 #undef __FUNC__
59 #define __FUNC__ "SNESSolve_EQ_TR"
60 static int SNESSolve_EQ_TR(SNES snes,int *its)
61 {
62   SNES_TR      *neP = (SNES_TR *) snes->data;
63   Vec          X, F, Y, G, TMP, Ytmp;
64   int          maxits, i, ierr, lits, breakout = 0;
65   MatStructure flg = DIFFERENT_NONZERO_PATTERN;
66   double       rho, fnorm, gnorm, gpnorm, xnorm, delta,norm,ynorm,norm1;
67   Scalar       mone = -1.0,cnorm;
68   KSP          ksp;
69   SLES         sles;
70 
71   PetscFunctionBegin;
72   maxits	= snes->max_its;	/* maximum number of iterations */
73   X		= snes->vec_sol;	/* solution vector */
74   F		= snes->vec_func;	/* residual vector */
75   Y		= snes->work[0];	/* work vectors */
76   G		= snes->work[1];
77   Ytmp          = snes->work[2];
78 
79   ierr       = VecNorm(X,NORM_2,&xnorm); CHKERRQ(ierr);         /* xnorm = || X || */
80 
81   ierr = SNESComputeFunction(snes,X,F); CHKERRQ(ierr);          /* F(X) */
82   ierr = VecNorm(F, NORM_2,&fnorm ); CHKERRQ(ierr);             /* fnorm <- || F || */
83   PetscAMSTakeAccess(snes);
84   snes->norm = fnorm;
85   snes->iter = 0;
86   PetscAMSGrantAccess(snes);
87   delta = neP->delta0*fnorm;
88   neP->delta = delta;
89   SNESLogConvHistory(snes,fnorm,0);
90   SNESMonitor(snes,0,fnorm);
91 
92  if (fnorm < snes->atol) {*its = 0; PetscFunctionReturn(0);}
93 
94   /* set parameter for default relative tolerance convergence test */
95   snes->ttol = fnorm*snes->rtol;
96 
97   /* Set the stopping criteria to use the More' trick. */
98   ierr = SNESGetSLES(snes,&sles); CHKERRQ(ierr);
99   ierr = SLESGetKSP(sles,&ksp); CHKERRQ(ierr);
100   ierr = KSPSetConvergenceTest(ksp,SNES_TR_KSPConverged_Private,(void *)snes);CHKERRQ(ierr);
101 
102   for ( i=0; i<maxits; i++ ) {
103     ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);CHKERRQ(ierr);
104     ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre,flg);CHKERRQ(ierr);
105 
106     /* Solve J Y = F, where J is Jacobian matrix */
107     ierr = SLESSolve(snes->sles,F,Ytmp,&lits); CHKERRQ(ierr);
108     snes->linear_its += PetscAbsInt(lits);
109     PLogInfo(snes,"SNESSolve_EQ_TR: iter=%d, linear solve iterations=%d\n",snes->iter,lits);
110     ierr = VecNorm(Ytmp,NORM_2,&norm); CHKERRQ(ierr);
111     norm1 = norm;
112     while(1) {
113       ierr = VecCopy(Ytmp,Y); CHKERRQ(ierr);
114       norm = norm1;
115 
116       /* Scale Y if need be and predict new value of F norm */
117       if (norm >= delta) {
118         norm = delta/norm;
119         gpnorm = (1.0 - norm)*fnorm;
120         cnorm = norm;
121         PLogInfo(snes,"SNESSolve_EQ_TR: Scaling direction by %g\n",norm );
122         ierr = VecScale(&cnorm,Y); CHKERRQ(ierr);
123         norm = gpnorm;
124         ynorm = delta;
125       } else {
126         gpnorm = 0.0;
127         PLogInfo(snes,"SNESSolve_EQ_TR: Direction is in Trust Region\n" );
128         ynorm = norm;
129       }
130       ierr = VecAYPX(&mone,X,Y); CHKERRQ(ierr);            /* Y <- X - Y */
131       ierr = VecCopy(X,snes->vec_sol_update_always); CHKERRQ(ierr);
132       ierr = SNESComputeFunction(snes,Y,G); CHKERRQ(ierr); /*  F(X) */
133       ierr = VecNorm(G,NORM_2,&gnorm); CHKERRQ(ierr);      /* gnorm <- || g || */
134       if (fnorm == gpnorm) rho = 0.0;
135       else rho = (fnorm*fnorm - gnorm*gnorm)/(fnorm*fnorm - gpnorm*gpnorm);
136 
137       /* Update size of trust region */
138       if      (rho < neP->mu)  delta *= neP->delta1;
139       else if (rho < neP->eta) delta *= neP->delta2;
140       else                     delta *= neP->delta3;
141       PLogInfo(snes,"SNESSolve_EQ_TR: fnorm=%g, gnorm=%g, ynorm=%g\n",fnorm,gnorm,ynorm);
142       PLogInfo(snes,"SNESSolve_EQ_TR: gpred=%g, rho=%g, delta=%g\n",gpnorm,rho,delta);
143       neP->delta = delta;
144       if (rho > neP->sigma) break;
145       PLogInfo(snes,"SNESSolve_EQ_TR: Trying again in smaller region\n");
146       /* check to see if progress is hopeless */
147       neP->itflag = 0;
148       if ((*snes->converged)(snes,xnorm,ynorm,fnorm,snes->cnvP)) {
149         /* We're not progressing, so return with the current iterate */
150         breakout = 1; break;
151       }
152       snes->nfailures++;
153     }
154     if (!breakout) {
155       fnorm = gnorm;
156       PetscAMSTakeAccess(snes);
157       snes->iter = i+1;
158       snes->norm = fnorm;
159       PetscAMSGrantAccess(snes);
160       TMP = F; F = G; snes->vec_func_always = F; G = TMP;
161       TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP;
162       VecNorm(X, NORM_2,&xnorm );		/* xnorm = || X || */
163       SNESLogConvHistory(snes,fnorm,lits);
164       SNESMonitor(snes,i+1,fnorm);
165 
166       /* Test for convergence */
167       neP->itflag = 1;
168       if ((*snes->converged)( snes, xnorm, ynorm, fnorm,snes->cnvP )) {
169         break;
170       }
171     } else {
172       break;
173     }
174   }
175   if (X != snes->vec_sol) {
176     /* Verify solution is in corect location */
177     ierr = VecCopy(X,snes->vec_sol); CHKERRQ(ierr);
178     snes->vec_sol_always  = snes->vec_sol;
179     snes->vec_func_always = snes->vec_func;
180   }
181   if (i == maxits) {
182     PLogInfo(snes,"SNESSolve_EQ_TR: Maximum number of iterations has been reached: %d\n",maxits);
183     i--;
184   }
185   *its = i+1;
186   PetscFunctionReturn(0);
187 }
188 /*------------------------------------------------------------*/
189 #undef __FUNC__
190 #define __FUNC__ "SNESSetUp_EQ_TR"
191 static int SNESSetUp_EQ_TR(SNES snes)
192 {
193   int ierr;
194 
195   PetscFunctionBegin;
196   snes->nwork = 4;
197   ierr = VecDuplicateVecs(snes->vec_sol,snes->nwork,&snes->work ); CHKERRQ(ierr);
198   PLogObjectParents(snes,snes->nwork,snes->work);
199   snes->vec_sol_update_always = snes->work[3];
200   PetscFunctionReturn(0);
201 }
202 /*------------------------------------------------------------*/
203 #undef __FUNC__
204 #define __FUNC__ "SNESDestroy_EQ_TR"
205 static int SNESDestroy_EQ_TR(SNES snes )
206 {
207   int  ierr;
208 
209   PetscFunctionBegin;
210   if (snes->nwork) {
211     ierr = VecDestroyVecs(snes->work,snes->nwork); CHKERRQ(ierr);
212   }
213   PetscFree(snes->data);
214   PetscFunctionReturn(0);
215 }
216 /*------------------------------------------------------------*/
217 
218 #undef __FUNC__
219 #define __FUNC__ "SNESSetFromOptions_EQ_TR"
220 static int SNESSetFromOptions_EQ_TR(SNES snes)
221 {
222   SNES_TR *ctx = (SNES_TR *)snes->data;
223   double  tmp;
224   int     ierr,flg;
225 
226   PetscFunctionBegin;
227   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_mu",&tmp, &flg); CHKERRQ(ierr);
228   if (flg) {ctx->mu = tmp;}
229   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_eta",&tmp, &flg); CHKERRQ(ierr);
230   if (flg) {ctx->eta = tmp;}
231   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_sigma",&tmp, &flg); CHKERRQ(ierr);
232   if (flg) {ctx->sigma = tmp;}
233   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_delta0",&tmp, &flg); CHKERRQ(ierr);
234   if (flg) {ctx->delta0 = tmp;}
235   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_delta1",&tmp, &flg); CHKERRQ(ierr);
236   if (flg) {ctx->delta1 = tmp;}
237   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_delta2",&tmp, &flg); CHKERRQ(ierr);
238   if (flg) {ctx->delta2 = tmp;}
239   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_delta3",&tmp, &flg); CHKERRQ(ierr);
240   if (flg) {ctx->delta3 = tmp;}
241   PetscFunctionReturn(0);
242 }
243 
244 #undef __FUNC__
245 #define __FUNC__ "SNESPrintHelp_EQ_TR"
246 static int SNESPrintHelp_EQ_TR(SNES snes,char *p)
247 {
248   SNES_TR *ctx = (SNES_TR *)snes->data;
249 
250   PetscFunctionBegin;
251   PetscFPrintf(snes->comm,stdout," method SNES_EQ_TR (tr) for systems of nonlinear equations:\n");
252   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_mu <mu> (default %g)\n",p,ctx->mu);
253   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_eta <eta> (default %g)\n",p,ctx->eta);
254   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_sigma <sigma> (default %g)\n",p,ctx->sigma);
255   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_delta0 <delta0> (default %g)\n",p,ctx->delta0);
256   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_delta1 <delta1> (default %g)\n",p,ctx->delta1);
257   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_delta2 <delta2> (default %g)\n",p,ctx->delta2);
258   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_delta3 <delta3> (default %g)\n",p,ctx->delta3);
259   PetscFunctionReturn(0);
260 }
261 
262 #undef __FUNC__
263 #define __FUNC__ "SNESView_EQ_TR"
264 static int SNESView_EQ_TR(SNES snes,Viewer viewer)
265 {
266   SNES_TR    *tr = (SNES_TR *)snes->data;
267   int        ierr;
268   ViewerType vtype;
269 
270   PetscFunctionBegin;
271   ierr = ViewerGetType(viewer,&vtype); CHKERRQ(ierr);
272   if (PetscTypeCompare(vtype,ASCII_VIEWER)) {
273     ViewerASCIIPrintf(viewer,"  mu=%g, eta=%g, sigma=%g\n",tr->mu,tr->eta,tr->sigma);
274     ViewerASCIIPrintf(viewer,"  delta0=%g, delta1=%g, delta2=%g, delta3=%g\n",tr->delta0,tr->delta1,tr->delta2,tr->delta3);
275   } else {
276     SETERRQ(1,1,"Viewer type not supported for this object");
277   }
278   PetscFunctionReturn(0);
279 }
280 
281 /* ---------------------------------------------------------------- */
282 #undef __FUNC__
283 #define __FUNC__ "SNESConverged_EQ_TR"
284 /*@
285    SNESConverged_EQ_TR - Monitors the convergence of the trust region
286    method SNES_EQ_TR for solving systems of nonlinear equations (default).
287 
288    Collective on SNES
289 
290    Input Parameters:
291 +  snes - the SNES context
292 .  xnorm - 2-norm of current iterate
293 .  pnorm - 2-norm of current step
294 .  fnorm - 2-norm of function
295 -  dummy - unused context
296 
297    Returns:
298 +  1  if  ( delta < xnorm*deltatol ),
299 .  2  if  ( fnorm < atol ),
300 .  3  if  ( pnorm < xtol*xnorm ),
301 . -2  if  ( nfct > maxf ),
302 . -1  if  ( delta < xnorm*epsmch ),
303 -  0  otherwise
304 
305    where
306 +    delta    - trust region paramenter
307 .    deltatol - trust region size tolerance,
308                 set with SNESSetTrustRegionTolerance()
309 .    maxf - maximum number of function evaluations,
310             set with SNESSetTolerances()
311 .    nfct - number of function evaluations,
312 .    atol - absolute function norm tolerance,
313             set with SNESSetTolerances()
314 -    xtol - relative function norm tolerance,
315             set with SNESSetTolerances()
316 
317 .keywords: SNES, nonlinear, default, converged, convergence
318 
319 .seealso: SNESSetConvergenceTest(), SNESEisenstatWalkerConverged()
320 @*/
321 int SNESConverged_EQ_TR(SNES snes,double xnorm,double pnorm,double fnorm,void *dummy)
322 {
323   SNES_TR *neP = (SNES_TR *)snes->data;
324   double  epsmch = 1.0e-14;   /* This must be fixed */
325   int     info;
326 
327   PetscFunctionBegin;
328   if (snes->method_class != SNES_NONLINEAR_EQUATIONS) {
329     SETERRQ(PETSC_ERR_ARG_WRONG,0,"For SNES_NONLINEAR_EQUATIONS only");
330   }
331 
332   if (fnorm != fnorm) {
333     PLogInfo(snes,"SNESConverged_EQ_TR:Failed to converged, function norm is NaN\n");
334     PetscFunctionReturn(-3);
335   }
336   if (neP->delta < xnorm * snes->deltatol) {
337     PLogInfo(snes,"SNESConverged_EQ_TR: Converged due to trust region param %g<%g*%g\n",
338              neP->delta,xnorm,snes->deltatol);
339     PetscFunctionReturn(1);
340   }
341   if (neP->itflag) {
342     info = SNESConverged_EQ_LS(snes,xnorm,pnorm,fnorm,dummy);
343     if (info) PetscFunctionReturn(info);
344   } else if (snes->nfuncs > snes->max_funcs) {
345     PLogInfo(snes,"SNESConverged_EQ_TR: Exceeded maximum number of function evaluations: %d > %d\n",
346              snes->nfuncs, snes->max_funcs );
347     PetscFunctionReturn(-2);
348   }
349   if (neP->delta < xnorm * epsmch) {
350     PLogInfo(snes,"SNESConverged_EQ_TR: Converged due to trust region param %g < %g * %g\n",
351              neP->delta,xnorm, epsmch);
352     PetscFunctionReturn(-1);
353   }
354   PetscFunctionReturn(0);
355 }
356 /* ------------------------------------------------------------ */
357 EXTERN_C_BEGIN
358 #undef __FUNC__
359 #define __FUNC__ "SNESCreate_EQ_TR"
360 int SNESCreate_EQ_TR(SNES snes )
361 {
362   SNES_TR *neP;
363 
364   PetscFunctionBegin;
365   if (snes->method_class != SNES_NONLINEAR_EQUATIONS) {
366     SETERRQ(PETSC_ERR_ARG_WRONG,0,"For SNES_NONLINEAR_EQUATIONS only");
367   }
368   snes->setup		= SNESSetUp_EQ_TR;
369   snes->solve		= SNESSolve_EQ_TR;
370   snes->destroy		= SNESDestroy_EQ_TR;
371   snes->converged	= SNESConverged_EQ_TR;
372   snes->printhelp       = SNESPrintHelp_EQ_TR;
373   snes->setfromoptions  = SNESSetFromOptions_EQ_TR;
374   snes->view            = SNESView_EQ_TR;
375   snes->nwork           = 0;
376 
377   neP			= PetscNew(SNES_TR); CHKPTRQ(neP);
378   PLogObjectMemory(snes,sizeof(SNES_TR));
379   snes->data	        = (void *) neP;
380   neP->mu		= 0.25;
381   neP->eta		= 0.75;
382   neP->delta		= 0.0;
383   neP->delta0		= 0.2;
384   neP->delta1		= 0.3;
385   neP->delta2		= 0.75;
386   neP->delta3		= 2.0;
387   neP->sigma		= 0.0001;
388   neP->itflag		= 0;
389   neP->rnorm0		= 0;
390   neP->ttol		= 0;
391   PetscFunctionReturn(0);
392 }
393 EXTERN_C_END
394 
395