xref: /petsc/src/snes/impls/tr/tr.c (revision d636dbe3a9c413b447e52fc0f7f06f5e4018d190)
1 #ifndef lint
2 static char vcid[] = "$Id: tr.c,v 1.26 1995/08/24 22:30:50 bsmith Exp bsmith $";
3 #endif
4 
5 #include <math.h>
6 #include "tr.h"                /*I   "snes.h"   I*/
7 #include "pinclude/pviewer.h"
8 
9 /*
10    This convergence test determines if the two norm of the
11    solution lies outside the trust region, if so it halts.
12 */
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   if (snes->ksp_ewconv) {
23     if (!kctx) SETERRQ(1,
24       "SNES_KSP_EW_Converged_Private: Convergence context does not exist");
25     if (n == 0) SNES_KSP_EW_ComputeRelativeTolerance_Private(snes,ksp);
26     kctx->lresid_last = rnorm;
27   }
28   convinfo = KSPDefaultConverged(ksp,n,rnorm,ctx);
29   if (convinfo) {
30     PLogInfo((PetscObject)snes,"SNES: KSP iterations=%d, rnorm=%g\n",n,rnorm);
31     return convinfo;
32   }
33 
34   /* Determine norm of solution */
35   ierr = KSPBuildSolution(ksp,0,&x); CHKERRQ(ierr);
36   ierr = VecNorm(x,&norm); CHKERRQ(ierr);
37   if (norm >= neP->delta) {
38     PLogInfo((PetscObject)snes,"SNES: KSP iterations=%d, rnorm=%g\n",n,rnorm);
39     PLogInfo((PetscObject)snes,
40       "SNES: Ending linear iteration early, delta %g length %g\n",neP->delta,norm);
41     return 1;
42   }
43   return(0);
44 }
45 /*
46    SNESSolve_TR - Implements Newton's Method with a very simple trust
47    region approach for solving systems of nonlinear equations.
48 
49    The basic algorithm is taken from "The Minpack Project", by More',
50    Sorensen, Garbow, Hillstrom, pages 88-111 of "Sources and Development
51    of Mathematical Software", Wayne Cowell, editor.
52 
53    This is intended as a model implementation, since it does not
54    necessarily have many of the bells and whistles of other
55    implementations.
56 */
57 static int SNESSolve_TR(SNES snes,int *its)
58 {
59   SNES_TR      *neP = (SNES_TR *) snes->data;
60   Vec          X, F, Y, G, TMP, Ytmp;
61   int          maxits, i, history_len, ierr, lits;
62   MatStructure flg = ALLMAT_DIFFERENT_NONZERO_PATTERN;
63   double       rho, fnorm, gnorm, gpnorm, xnorm, delta,norm;
64   double       *history, ynorm;
65   Scalar       one = 1.0,cnorm;
66   KSP          ksp;
67   SLES         sles;
68 
69   history	= snes->conv_hist;	/* convergence history */
70   history_len	= snes->conv_hist_len;	/* convergence history length */
71   maxits	= snes->max_its;	/* maximum number of iterations */
72   X		= snes->vec_sol;	/* solution vector */
73   F		= snes->vec_func;	/* residual vector */
74   Y		= snes->work[0];	/* work vectors */
75   G		= snes->work[1];
76   Ytmp          = snes->work[2];
77 
78   ierr = SNESComputeInitialGuess(snes,X); CHKERRQ(ierr); /* X <- X_0 */
79   ierr = VecNorm(X,&xnorm); CHKERRQ(ierr);               /* xnorm = || X || */
80 
81   ierr = SNESComputeFunction(snes,X,F); CHKERRQ(ierr);   /* (+/-) F(X) */
82   ierr = VecNorm(F, &fnorm ); CHKERRQ(ierr);             /* fnorm <- || F || */
83   snes->norm = fnorm;
84   if (history && history_len > 0) history[0] = fnorm;
85   delta = neP->delta0*fnorm;
86   neP->delta = delta;
87   if (snes->monitor)
88     {ierr = (*snes->monitor)(snes,0,fnorm,snes->monP); CHKERRQ(ierr);}
89 
90   /* Set the stopping criteria to use the More' trick. */
91   ierr = SNESGetSLES(snes,&sles); CHKERRQ(ierr);
92   ierr = SLESGetKSP(sles,&ksp); CHKERRQ(ierr);
93   ierr = KSPSetConvergenceTest(ksp,SNES_TR_KSPConverged_Private,(void *)snes);
94   CHKERRQ(ierr);
95 
96   for ( i=0; i<maxits; i++ ) {
97      snes->iter = i+1;
98      ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,
99                                              &flg); CHKERRQ(ierr);
100      ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre,
101                             flg); CHKERRQ(ierr);
102      ierr = SLESSolve(snes->sles,F,Ytmp,&lits); CHKERRQ(ierr);
103      ierr = VecNorm(Ytmp,&norm); CHKERRQ(ierr);
104      while(1) {
105        ierr = VecCopy(Ytmp,Y); CHKERRQ(ierr);
106        /* Scale Y if need be and predict new value of F norm */
107 
108        if (norm >= delta) {
109          norm = delta/norm;
110          gpnorm = (1.0 - norm)*fnorm;
111          cnorm = norm;
112          PLogInfo((PetscObject)snes, "Scaling direction by %g \n",norm );
113          ierr = VecScale(&cnorm,Y); CHKERRQ(ierr);
114          norm = gpnorm;
115          ynorm = delta;
116        } else {
117          gpnorm = 0.0;
118          PLogInfo((PetscObject)snes,"Direction is in Trust Region \n" );
119          ynorm = norm;
120        }
121        ierr = VecAXPY(&one,X,Y); CHKERRQ(ierr);             /* Y <- X + Y */
122        ierr = VecCopy(X,snes->vec_sol_update_always); CHKERRQ(ierr);
123        ierr = SNESComputeFunction(snes,Y,G); CHKERRQ(ierr); /* (+/-) F(X) */
124        ierr = VecNorm(G,&gnorm); CHKERRQ(ierr);             /* gnorm <- || g || */
125        if (fnorm == gpnorm) rho = 0.0;
126        else rho = (fnorm*fnorm - gnorm*gnorm)/(fnorm*fnorm - gpnorm*gpnorm);
127 
128        /* Update size of trust region */
129        if      (rho < neP->mu)  delta *= neP->delta1;
130        else if (rho < neP->eta) delta *= neP->delta2;
131        else                     delta *= neP->delta3;
132        PLogInfo((PetscObject)snes,"%d:  f_norm=%g, g_norm=%g, ynorm=%g\n",
133                                              i, fnorm, gnorm, ynorm );
134        PLogInfo((PetscObject)snes,"gpred=%g, rho=%g, delta=%g,iters=%d\n",
135                                                gpnorm, rho, delta, lits);
136        neP->delta = delta;
137        if (rho > neP->sigma) break;
138        PLogInfo((PetscObject)snes,"Trying again in smaller region\n");
139        /* check to see if progress is hopeless */
140        neP->itflag = 0;
141        if ((*snes->converged)(snes,xnorm,ynorm,fnorm,snes->cnvP)) {
142          /* We're not progressing, so return with the current iterate */
143          if (X != snes->vec_sol) {
144            ierr = VecCopy(X,snes->vec_sol); CHKERRQ(ierr);
145            snes->vec_sol_always = snes->vec_sol;
146            snes->vec_func_always = snes->vec_func;
147          }
148        }
149        snes->nfailures++;
150      }
151      fnorm = gnorm;
152      snes->norm = fnorm;
153      if (history && history_len > i+1) history[i+1] = fnorm;
154      TMP = F; F = G; snes->vec_func_always = F; G = TMP;
155      TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP;
156      VecNorm(X, &xnorm );		/* xnorm = || X || */
157      if (snes->monitor)
158        {ierr = (*snes->monitor)(snes,i+1,fnorm,snes->monP); CHKERRQ(ierr);}
159 
160      /* Test for convergence */
161      neP->itflag = 1;
162      if ((*snes->converged)( snes, xnorm, ynorm, fnorm,snes->cnvP )) {
163        /* Verify solution is in corect location */
164        if (X != snes->vec_sol) {
165          ierr = VecCopy(X,snes->vec_sol); CHKERRQ(ierr);
166          snes->vec_sol_always = snes->vec_sol;
167          snes->vec_func_always = snes->vec_func;
168        }
169        break;
170      }
171    }
172   if (i == maxits) {
173     PLogInfo((PetscObject)snes,
174       "Maximum number of iterations has been reached: %d\n",maxits);
175     i--;
176   }
177   *its = i+1;
178   return 0;
179 }
180 /*------------------------------------------------------------*/
181 static int SNESSetUp_TR( SNES snes )
182 {
183   int ierr;
184   snes->nwork = 4;
185   ierr = VecGetVecs(snes->vec_sol,snes->nwork,&snes->work ); CHKERRQ(ierr);
186   PLogObjectParents(snes,snes->nwork,snes->work);
187   snes->vec_sol_update_always = snes->work[3];
188   return 0;
189 }
190 /*------------------------------------------------------------*/
191 static int SNESDestroy_TR(PetscObject obj )
192 {
193   SNES snes = (SNES) obj;
194   int  ierr;
195   ierr = VecFreeVecs(snes->work,snes->nwork); CHKERRQ(ierr);
196   PETSCFREE(snes->data);
197   return 0;
198 }
199 /*------------------------------------------------------------*/
200 
201 static int SNESSetFromOptions_TR(SNES snes)
202 {
203   SNES_TR *ctx = (SNES_TR *)snes->data;
204   double  tmp;
205 
206   if (OptionsGetDouble(snes->prefix,"-mu",&tmp)) {ctx->mu = tmp;}
207   if (OptionsGetDouble(snes->prefix,"-eta",&tmp)) {ctx->eta = tmp;}
208   if (OptionsGetDouble(snes->prefix,"-sigma",&tmp)) {ctx->sigma = tmp;}
209   if (OptionsGetDouble(snes->prefix,"-delta0",&tmp)) {ctx->delta0 = tmp;}
210   if (OptionsGetDouble(snes->prefix,"-delta1",&tmp)) {ctx->delta1 = tmp;}
211   if (OptionsGetDouble(snes->prefix,"-delta2",&tmp)) {ctx->delta2 = tmp;}
212   if (OptionsGetDouble(snes->prefix,"-delta3",&tmp)) {ctx->delta3 = tmp;}
213   return 0;
214 }
215 
216 static int SNESPrintHelp_TR(SNES snes)
217 {
218   SNES_TR *ctx = (SNES_TR *)snes->data;
219   char    *p;
220   if (snes->prefix) p = snes->prefix; else p = "-";
221   MPIU_fprintf(snes->comm,stdout," method tr (system of nonlinear equations):\n");
222   MPIU_fprintf(snes->comm,stdout,"   %ssnes_trust_region_mu mu (default %g)\n",p,ctx->mu);
223   MPIU_fprintf(snes->comm,stdout,"   %ssnes_trust_region_eta eta (default %g)\n",p,ctx->eta);
224   MPIU_fprintf(snes->comm,stdout,"   %ssnes_trust_region_sigma sigma (default %g)\n",p,ctx->sigma);
225   MPIU_fprintf(snes->comm,stdout,"   %ssnes_trust_region_delta0 delta0 (default %g)\n",p,ctx->delta0);
226   MPIU_fprintf(snes->comm,stdout,"   %ssnes_trust_region_delta1 delta1 (default %g)\n",p,ctx->delta1);
227   MPIU_fprintf(snes->comm,stdout,"   %ssnes_trust_region_delta2 delta2 (default %g)\n",p,ctx->delta2);
228   MPIU_fprintf(snes->comm,stdout,"   %ssnes_trust_region_delta3 delta3 (default %g)\n",p,ctx->delta3);
229   return 0;
230 }
231 
232 static int SNESView_TR(PetscObject obj,Viewer viewer)
233 {
234   SNES    snes = (SNES)obj;
235   SNES_TR *tr = (SNES_TR *)snes->data;
236   FILE    *fd;
237   ierr = ViewerFileGetPointer_Private(viewer,&fd); CHKERRQ(ierr);
238 
239   MPIU_fprintf(snes->comm,fd,"    mu=%g, eta=%g, sigma=%g\n",
240     tr->mu,tr->eta,tr->sigma);
241   MPIU_fprintf(snes->comm,fd,
242     "    delta0=%g, delta1=%g, delta2=%g, delta3=%g\n",
243     tr->delta0,tr->delta1,tr->delta2,tr->delta3);
244   return 0;
245 }
246 
247 /* ---------------------------------------------------------------- */
248 /*@
249    SNESTrustRegionDefaultConverged - Default test for monitoring the
250    convergence of the trust region method SNES_EQ_TR for solving systems
251    of nonlinear equations.
252 
253    Input Parameters:
254 .  snes - the SNES context
255 .  xnorm - 2-norm of current iterate
256 .  pnorm - 2-norm of current step
257 .  fnorm - 2-norm of function
258 .  dummy - unused context
259 
260    Returns:
261 $  1  if  ( delta < xnorm*deltatol ),
262 $  2  if  ( fnorm < atol ),
263 $  3  if  ( pnorm < xtol*xnorm ),
264 $ -2  if  ( nfct > maxf ),
265 $ -1  if  ( delta < xnorm*epsmch ),
266 $  0  otherwise,
267 
268    where
269 $    delta    - trust region paramenter
270 $    deltatol - trust region size tolerance,
271 $               set with SNESSetTrustRegionTolerance()
272 $    maxf - maximum number of function evaluations,
273 $           set with SNESSetMaxFunctionEvaluations()
274 $    nfct - number of function evaluations,
275 $    atol - absolute function norm tolerance,
276 $           set with SNESSetAbsoluteTolerance()
277 $    xtol - relative function norm tolerance,
278 $           set with SNESSetRelativeTolerance()
279 
280 .keywords: SNES, nonlinear, default, converged, convergence
281 
282 .seealso: SNESSetConvergenceTest(), SNESEisenstatWalkerConverged()
283 @*/
284 int SNESTrustRegionDefaultConverged(SNES snes,double xnorm,double pnorm,
285                          double fnorm,void *dummy)
286 {
287   SNES_TR *neP = (SNES_TR *)snes->data;
288   double  epsmch = 1.0e-14;   /* This must be fixed */
289   int     info;
290   if (snes->method_class != SNES_NONLINEAR_EQUATIONS) SETERRQ(1,
291     "SNESDefaultConverged:For SNES_NONLINEAR_EQUATIONS method only");
292 
293   if (neP->delta < xnorm * snes->deltatol) {
294     PLogInfo((PetscObject)snes,
295       "SNES: Converged due to trust region param %g < %g * %g\n",neP->delta,
296        xnorm, snes->deltatol);
297     return 1;
298   }
299   if (neP->itflag) {
300     info = SNESDefaultConverged(snes,xnorm,pnorm,fnorm,dummy);
301     if (info) return info;
302   }
303   if (neP->delta < xnorm * epsmch) {
304     PLogInfo((PetscObject)snes,
305       "SNES: Converged due to trust region param %g < %g * %g\n",neP->delta,
306        xnorm, epsmch);
307     return -1;
308   }
309   return 0;
310 }
311 /* ------------------------------------------------------------ */
312 int SNESCreate_TR(SNES snes )
313 {
314   SNES_TR *neP;
315 
316   if (snes->method_class != SNES_NONLINEAR_EQUATIONS) SETERRQ(1,
317     "SNESCreate_TR: Valid for SNES_NONLINEAR_EQUATIONS problems only");
318   snes->type 		= SNES_EQ_NTR;
319   snes->setup		= SNESSetUp_TR;
320   snes->solve		= SNESSolve_TR;
321   snes->destroy		= SNESDestroy_TR;
322   snes->converged	= SNESTrustRegionDefaultConverged;
323   snes->printhelp       = SNESPrintHelp_TR;
324   snes->setfromoptions  = SNESSetFromOptions_TR;
325   snes->view            = SNESView_TR;
326 
327   neP			= PETSCNEW(SNES_TR); CHKPTRQ(neP);
328   PLogObjectMemory(snes,sizeof(SNES_TR));
329   snes->data	        = (void *) neP;
330   neP->mu		= 0.25;
331   neP->eta		= 0.75;
332   neP->delta		= 0.0;
333   neP->delta0		= 0.2;
334   neP->delta1		= 0.3;
335   neP->delta2		= 0.75;
336   neP->delta3		= 2.0;
337   neP->sigma		= 0.0001;
338   neP->itflag		= 0;
339   neP->rnorm0		= 0;
340   neP->ttol		= 0;
341   return 0;
342 }
343