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