xref: /petsc/src/snes/impls/tr/tr.c (revision c01c455d43b147fbcccdf73d28be4d01ad7032e0)
1 #ifndef lint
2 static char vcid[] = "$Id: tr.c,v 1.24 1995/08/14 23:16:35 curfman Exp curfman $";
3 #endif
4 
5 #include <math.h>
6 #include "tr.h"                /*I   "snes.h"   I*/
7 #include "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 = ViewerFileGetPointer_Private(viewer);
237 
238   MPIU_fprintf(snes->comm,fd,"    mu=%g, eta=%g, sigma=%g\n",
239     tr->mu,tr->eta,tr->sigma);
240   MPIU_fprintf(snes->comm,fd,
241     "    delta0=%g, delta1=%g, delta2=%g, delta3=%g\n",
242     tr->delta0,tr->delta1,tr->delta2,tr->delta3);
243   return 0;
244 }
245 
246 /* ---------------------------------------------------------------- */
247 /*@
248    SNESTrustRegionDefaultConverged - Default test for monitoring the
249    convergence of the trust region method SNES_EQ_TR for solving systems
250    of nonlinear equations.
251 
252    Input Parameters:
253 .  snes - the SNES context
254 .  xnorm - 2-norm of current iterate
255 .  pnorm - 2-norm of current step
256 .  fnorm - 2-norm of function
257 .  dummy - unused context
258 
259    Returns:
260 $  1  if  ( delta < xnorm*deltatol ),
261 $  2  if  ( fnorm < atol ),
262 $  3  if  ( pnorm < xtol*xnorm ),
263 $ -2  if  ( nfct > maxf ),
264 $ -1  if  ( delta < xnorm*epsmch ),
265 $  0  otherwise,
266 
267    where
268 $    delta    - trust region paramenter
269 $    deltatol - trust region size tolerance,
270 $               set with SNESSetTrustRegionTolerance()
271 $    maxf - maximum number of function evaluations,
272 $           set with SNESSetMaxFunctionEvaluations()
273 $    nfct - number of function evaluations,
274 $    atol - absolute function norm tolerance,
275 $           set with SNESSetAbsoluteTolerance()
276 $    xtol - relative function norm tolerance,
277 $           set with SNESSetRelativeTolerance()
278 
279 .keywords: SNES, nonlinear, default, converged, convergence
280 
281 .seealso: SNESSetConvergenceTest(), SNESEisenstatWalkerConverged()
282 @*/
283 int SNESTrustRegionDefaultConverged(SNES snes,double xnorm,double pnorm,
284                          double fnorm,void *dummy)
285 {
286   SNES_TR *neP = (SNES_TR *)snes->data;
287   double  epsmch = 1.0e-14;   /* This must be fixed */
288   int     info;
289   if (snes->method_class != SNES_NONLINEAR_EQUATIONS) SETERRQ(1,
290     "SNESDefaultConverged:For SNES_NONLINEAR_EQUATIONS method only");
291 
292   if (neP->delta < xnorm * snes->deltatol) {
293     PLogInfo((PetscObject)snes,
294       "SNES: Converged due to trust region param %g < %g * %g\n",neP->delta,
295        xnorm, snes->deltatol);
296     return 1;
297   }
298   if (neP->itflag) {
299     info = SNESDefaultConverged(snes,xnorm,pnorm,fnorm,dummy);
300     if (info) return info;
301   }
302   if (neP->delta < xnorm * epsmch) {
303     PLogInfo((PetscObject)snes,
304       "SNES: Converged due to trust region param %g < %g * %g\n",neP->delta,
305        xnorm, epsmch);
306     return -1;
307   }
308   return 0;
309 }
310 /* ------------------------------------------------------------ */
311 int SNESCreate_TR(SNES snes )
312 {
313   SNES_TR *neP;
314 
315   if (snes->method_class != SNES_NONLINEAR_EQUATIONS) SETERRQ(1,
316     "SNESCreate_TR: Valid for SNES_NONLINEAR_EQUATIONS problems only");
317   snes->type 		= SNES_EQ_NTR;
318   snes->setup		= SNESSetUp_TR;
319   snes->solve		= SNESSolve_TR;
320   snes->destroy		= SNESDestroy_TR;
321   snes->converged	= SNESTrustRegionDefaultConverged;
322   snes->printhelp       = SNESPrintHelp_TR;
323   snes->setfromoptions  = SNESSetFromOptions_TR;
324   snes->view            = SNESView_TR;
325 
326   neP			= PETSCNEW(SNES_TR); CHKPTRQ(neP);
327   PLogObjectMemory(snes,sizeof(SNES_TR));
328   snes->data	        = (void *) neP;
329   neP->mu		= 0.25;
330   neP->eta		= 0.75;
331   neP->delta		= 0.0;
332   neP->delta0		= 0.2;
333   neP->delta1		= 0.3;
334   neP->delta2		= 0.75;
335   neP->delta3		= 2.0;
336   neP->sigma		= 0.0001;
337   neP->itflag		= 0;
338   neP->rnorm0		= 0;
339   neP->ttol		= 0;
340   return 0;
341 }
342