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