xref: /petsc/src/snes/impls/tr/tr.c (revision 4c740f8e329a64087ca643ab5ae5cbdea8fb0703)
1 #ifdef PETSC_RCS_HEADER
2 static char vcid[] = "$Id: tr.c,v 1.89 1998/12/17 22:12:34 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, history_len, ierr, lits, breakout = 0;
65   MatStructure flg = DIFFERENT_NONZERO_PATTERN;
66   double       rho, fnorm, gnorm, gpnorm, xnorm, delta,norm,*history, ynorm,norm1;
67   Scalar       mone = -1.0,cnorm;
68   KSP          ksp;
69   SLES         sles;
70 
71   PetscFunctionBegin;
72   history	= snes->conv_hist;	/* convergence history */
73   history_len	= snes->conv_hist_size;	/* convergence history length */
74   maxits	= snes->max_its;	/* maximum number of iterations */
75   X		= snes->vec_sol;	/* solution vector */
76   F		= snes->vec_func;	/* residual vector */
77   Y		= snes->work[0];	/* work vectors */
78   G		= snes->work[1];
79   Ytmp          = snes->work[2];
80 
81   ierr       = VecNorm(X,NORM_2,&xnorm); CHKERRQ(ierr);         /* xnorm = || X || */
82   snes->iter = 0;
83 
84   ierr = SNESComputeFunction(snes,X,F); CHKERRQ(ierr);          /* F(X) */
85   ierr = VecNorm(F, NORM_2,&fnorm ); CHKERRQ(ierr);             /* fnorm <- || F || */
86   snes->norm = fnorm;
87   if (history) history[0] = fnorm;
88   delta = neP->delta0*fnorm;
89   neP->delta = delta;
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     snes->iter = i+1;
104     ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);CHKERRQ(ierr);
105     ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre,flg);CHKERRQ(ierr);
106 
107     /* Solve J Y = F, where J is Jacobian matrix */
108     ierr = SLESSolve(snes->sles,F,Ytmp,&lits); CHKERRQ(ierr);
109     snes->linear_its += PetscAbsInt(lits);
110     PLogInfo(snes,"SNESSolve_EQ_TR: iter=%d, linear solve iterations=%d\n",snes->iter,lits);
111     ierr = VecNorm(Ytmp,NORM_2,&norm); CHKERRQ(ierr);
112     norm1 = norm;
113     while(1) {
114       ierr = VecCopy(Ytmp,Y); CHKERRQ(ierr);
115       norm = norm1;
116 
117       /* Scale Y if need be and predict new value of F norm */
118       if (norm >= delta) {
119         norm = delta/norm;
120         gpnorm = (1.0 - norm)*fnorm;
121         cnorm = norm;
122         PLogInfo(snes,"SNESSolve_EQ_TR: Scaling direction by %g\n",norm );
123         ierr = VecScale(&cnorm,Y); CHKERRQ(ierr);
124         norm = gpnorm;
125         ynorm = delta;
126       } else {
127         gpnorm = 0.0;
128         PLogInfo(snes,"SNESSolve_EQ_TR: Direction is in Trust Region\n" );
129         ynorm = norm;
130       }
131       ierr = VecAYPX(&mone,X,Y); CHKERRQ(ierr);            /* Y <- X - Y */
132       ierr = VecCopy(X,snes->vec_sol_update_always); CHKERRQ(ierr);
133       ierr = SNESComputeFunction(snes,Y,G); CHKERRQ(ierr); /*  F(X) */
134       ierr = VecNorm(G,NORM_2,&gnorm); CHKERRQ(ierr);      /* gnorm <- || g || */
135       if (fnorm == gpnorm) rho = 0.0;
136       else rho = (fnorm*fnorm - gnorm*gnorm)/(fnorm*fnorm - gpnorm*gpnorm);
137 
138       /* Update size of trust region */
139       if      (rho < neP->mu)  delta *= neP->delta1;
140       else if (rho < neP->eta) delta *= neP->delta2;
141       else                     delta *= neP->delta3;
142       PLogInfo(snes,"SNESSolve_EQ_TR: fnorm=%g, gnorm=%g, ynorm=%g\n",fnorm,gnorm,ynorm);
143       PLogInfo(snes,"SNESSolve_EQ_TR: gpred=%g, rho=%g, delta=%g\n",gpnorm,rho,delta);
144       neP->delta = delta;
145       if (rho > neP->sigma) break;
146       PLogInfo(snes,"SNESSolve_EQ_TR: Trying again in smaller region\n");
147       /* check to see if progress is hopeless */
148       neP->itflag = 0;
149       if ((*snes->converged)(snes,xnorm,ynorm,fnorm,snes->cnvP)) {
150         /* We're not progressing, so return with the current iterate */
151         breakout = 1; break;
152       }
153       snes->nfailures++;
154     }
155     if (!breakout) {
156       fnorm = gnorm;
157       snes->norm = fnorm;
158       if (history && history_len > i+1) history[i+1] = fnorm;
159       TMP = F; F = G; snes->vec_func_always = F; G = TMP;
160       TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP;
161       VecNorm(X, NORM_2,&xnorm );		/* xnorm = || X || */
162       SNESMonitor(snes,i+1,fnorm);
163 
164       /* Test for convergence */
165       neP->itflag = 1;
166       if ((*snes->converged)( snes, xnorm, ynorm, fnorm,snes->cnvP )) {
167         break;
168       }
169     } else {
170       break;
171     }
172   }
173   if (X != snes->vec_sol) {
174     /* Verify solution is in corect location */
175     ierr = VecCopy(X,snes->vec_sol); CHKERRQ(ierr);
176     snes->vec_sol_always  = snes->vec_sol;
177     snes->vec_func_always = snes->vec_func;
178   }
179   if (i == maxits) {
180     PLogInfo(snes,"SNESSolve_EQ_TR: Maximum number of iterations has been reached: %d\n",maxits);
181     i--;
182   }
183   if (history) snes->conv_act_size = (history_len < i+1) ? history_len : i+1;
184   *its = i+1;
185   PetscFunctionReturn(0);
186 }
187 /*------------------------------------------------------------*/
188 #undef __FUNC__
189 #define __FUNC__ "SNESSetUp_EQ_TR"
190 static int SNESSetUp_EQ_TR(SNES snes)
191 {
192   int ierr;
193 
194   PetscFunctionBegin;
195   snes->nwork = 4;
196   ierr = VecDuplicateVecs(snes->vec_sol,snes->nwork,&snes->work ); CHKERRQ(ierr);
197   PLogObjectParents(snes,snes->nwork,snes->work);
198   snes->vec_sol_update_always = snes->work[3];
199   PetscFunctionReturn(0);
200 }
201 /*------------------------------------------------------------*/
202 #undef __FUNC__
203 #define __FUNC__ "SNESDestroy_EQ_TR"
204 static int SNESDestroy_EQ_TR(SNES snes )
205 {
206   int  ierr;
207 
208   PetscFunctionBegin;
209   if (snes->nwork) {
210     ierr = VecDestroyVecs(snes->work,snes->nwork); CHKERRQ(ierr);
211   }
212   PetscFree(snes->data);
213   PetscFunctionReturn(0);
214 }
215 /*------------------------------------------------------------*/
216 
217 #undef __FUNC__
218 #define __FUNC__ "SNESSetFromOptions_EQ_TR"
219 static int SNESSetFromOptions_EQ_TR(SNES snes)
220 {
221   SNES_TR *ctx = (SNES_TR *)snes->data;
222   double  tmp;
223   int     ierr,flg;
224 
225   PetscFunctionBegin;
226   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_mu",&tmp, &flg); CHKERRQ(ierr);
227   if (flg) {ctx->mu = tmp;}
228   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_eta",&tmp, &flg); CHKERRQ(ierr);
229   if (flg) {ctx->eta = tmp;}
230   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_sigma",&tmp, &flg); CHKERRQ(ierr);
231   if (flg) {ctx->sigma = tmp;}
232   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_delta0",&tmp, &flg); CHKERRQ(ierr);
233   if (flg) {ctx->delta0 = tmp;}
234   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_delta1",&tmp, &flg); CHKERRQ(ierr);
235   if (flg) {ctx->delta1 = tmp;}
236   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_delta2",&tmp, &flg); CHKERRQ(ierr);
237   if (flg) {ctx->delta2 = tmp;}
238   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_delta3",&tmp, &flg); CHKERRQ(ierr);
239   if (flg) {ctx->delta3 = tmp;}
240   PetscFunctionReturn(0);
241 }
242 
243 #undef __FUNC__
244 #define __FUNC__ "SNESPrintHelp_EQ_TR"
245 static int SNESPrintHelp_EQ_TR(SNES snes,char *p)
246 {
247   SNES_TR *ctx = (SNES_TR *)snes->data;
248 
249   PetscFunctionBegin;
250   PetscFPrintf(snes->comm,stdout," method SNES_EQ_TR (tr) for systems of nonlinear equations:\n");
251   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_mu <mu> (default %g)\n",p,ctx->mu);
252   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_eta <eta> (default %g)\n",p,ctx->eta);
253   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_sigma <sigma> (default %g)\n",p,ctx->sigma);
254   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_delta0 <delta0> (default %g)\n",p,ctx->delta0);
255   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_delta1 <delta1> (default %g)\n",p,ctx->delta1);
256   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_delta2 <delta2> (default %g)\n",p,ctx->delta2);
257   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_delta3 <delta3> (default %g)\n",p,ctx->delta3);
258   PetscFunctionReturn(0);
259 }
260 
261 #undef __FUNC__
262 #define __FUNC__ "SNESView_EQ_TR"
263 static int SNESView_EQ_TR(SNES snes,Viewer viewer)
264 {
265   SNES_TR    *tr = (SNES_TR *)snes->data;
266   int        ierr;
267   ViewerType vtype;
268 
269   PetscFunctionBegin;
270   ierr = ViewerGetType(viewer,&vtype); CHKERRQ(ierr);
271   if (PetscTypeCompare(vtype,ASCII_VIEWER)) {
272     ViewerASCIIPrintf(viewer,"    mu=%g, eta=%g, sigma=%g\n",tr->mu,tr->eta,tr->sigma);
273     ViewerASCIIPrintf(viewer,"    delta0=%g, delta1=%g, delta2=%g, delta3=%g\n",tr->delta0,tr->delta1,tr->delta2,tr->delta3);
274   } else {
275     SETERRQ(1,1,"Viewer type not supported for this object");
276   }
277   PetscFunctionReturn(0);
278 }
279 
280 /* ---------------------------------------------------------------- */
281 #undef __FUNC__
282 #define __FUNC__ "SNESConverged_EQ_TR"
283 /*@
284    SNESConverged_EQ_TR - Monitors the convergence of the trust region
285    method SNES_EQ_TR for solving systems of nonlinear equations (default).
286 
287    Collective on SNES
288 
289    Input Parameters:
290 +  snes - the SNES context
291 .  xnorm - 2-norm of current iterate
292 .  pnorm - 2-norm of current step
293 .  fnorm - 2-norm of function
294 -  dummy - unused context
295 
296    Returns:
297 +  1  if  ( delta < xnorm*deltatol ),
298 .  2  if  ( fnorm < atol ),
299 .  3  if  ( pnorm < xtol*xnorm ),
300 . -2  if  ( nfct > maxf ),
301 . -1  if  ( delta < xnorm*epsmch ),
302 -  0  otherwise
303 
304    where
305 +    delta    - trust region paramenter
306 .    deltatol - trust region size tolerance,
307                 set with SNESSetTrustRegionTolerance()
308 .    maxf - maximum number of function evaluations,
309             set with SNESSetTolerances()
310 .    nfct - number of function evaluations,
311 .    atol - absolute function norm tolerance,
312             set with SNESSetTolerances()
313 -    xtol - relative function norm tolerance,
314             set with SNESSetTolerances()
315 
316 .keywords: SNES, nonlinear, default, converged, convergence
317 
318 .seealso: SNESSetConvergenceTest(), SNESEisenstatWalkerConverged()
319 @*/
320 int SNESConverged_EQ_TR(SNES snes,double xnorm,double pnorm,double fnorm,void *dummy)
321 {
322   SNES_TR *neP = (SNES_TR *)snes->data;
323   double  epsmch = 1.0e-14;   /* This must be fixed */
324   int     info;
325 
326   PetscFunctionBegin;
327   if (snes->method_class != SNES_NONLINEAR_EQUATIONS) {
328     SETERRQ(PETSC_ERR_ARG_WRONG,0,"For SNES_NONLINEAR_EQUATIONS only");
329   }
330 
331   if (fnorm != fnorm) {
332     PLogInfo(snes,"SNESConverged_EQ_TR:Failed to converged, function norm is NaN\n");
333     PetscFunctionReturn(-3);
334   }
335   if (neP->delta < xnorm * snes->deltatol) {
336     PLogInfo(snes,"SNESConverged_EQ_TR: Converged due to trust region param %g<%g*%g\n",
337              neP->delta,xnorm,snes->deltatol);
338     PetscFunctionReturn(1);
339   }
340   if (neP->itflag) {
341     info = SNESConverged_EQ_LS(snes,xnorm,pnorm,fnorm,dummy);
342     if (info) PetscFunctionReturn(info);
343   } else if (snes->nfuncs > snes->max_funcs) {
344     PLogInfo(snes,"SNESConverged_EQ_TR: Exceeded maximum number of function evaluations: %d > %d\n",
345              snes->nfuncs, snes->max_funcs );
346     PetscFunctionReturn(-2);
347   }
348   if (neP->delta < xnorm * epsmch) {
349     PLogInfo(snes,"SNESConverged_EQ_TR: Converged due to trust region param %g < %g * %g\n",
350              neP->delta,xnorm, epsmch);
351     PetscFunctionReturn(-1);
352   }
353   PetscFunctionReturn(0);
354 }
355 /* ------------------------------------------------------------ */
356 EXTERN_C_BEGIN
357 #undef __FUNC__
358 #define __FUNC__ "SNESCreate_EQ_TR"
359 int SNESCreate_EQ_TR(SNES snes )
360 {
361   SNES_TR *neP;
362 
363   PetscFunctionBegin;
364   if (snes->method_class != SNES_NONLINEAR_EQUATIONS) {
365     SETERRQ(PETSC_ERR_ARG_WRONG,0,"For SNES_NONLINEAR_EQUATIONS only");
366   }
367   snes->setup		= SNESSetUp_EQ_TR;
368   snes->solve		= SNESSolve_EQ_TR;
369   snes->destroy		= SNESDestroy_EQ_TR;
370   snes->converged	= SNESConverged_EQ_TR;
371   snes->printhelp       = SNESPrintHelp_EQ_TR;
372   snes->setfromoptions  = SNESSetFromOptions_EQ_TR;
373   snes->view            = SNESView_EQ_TR;
374   snes->nwork           = 0;
375 
376   neP			= PetscNew(SNES_TR); CHKPTRQ(neP);
377   PLogObjectMemory(snes,sizeof(SNES_TR));
378   snes->data	        = (void *) neP;
379   neP->mu		= 0.25;
380   neP->eta		= 0.75;
381   neP->delta		= 0.0;
382   neP->delta0		= 0.2;
383   neP->delta1		= 0.3;
384   neP->delta2		= 0.75;
385   neP->delta3		= 2.0;
386   neP->sigma		= 0.0001;
387   neP->itflag		= 0;
388   neP->rnorm0		= 0;
389   neP->ttol		= 0;
390   PetscFunctionReturn(0);
391 }
392 EXTERN_C_END
393 
394