xref: /petsc/src/snes/impls/tr/tr.c (revision 3a40ed3dce77c081171d005ae1a6ff4bb9d13b6f)
1 #ifdef PETSC_RCS_HEADER
2 static char vcid[] = "$Id: tr.c,v 1.77 1997/08/22 15:18:05 bsmith Exp bsmith $";
3 #endif
4 
5 #include <math.h>
6 #include "src/snes/impls/tr/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 #undef __FUNC__
14 #define __FUNC__ "SNES_TR_KSPConverged_Private"
15 int SNES_TR_KSPConverged_Private(KSP ksp,int n, double rnorm, void *ctx)
16 {
17   SNES                snes = (SNES) ctx;
18   SNES_KSP_EW_ConvCtx *kctx = (SNES_KSP_EW_ConvCtx*)snes->kspconvctx;
19   SNES_TR             *neP = (SNES_TR*)snes->data;
20   Vec                 x;
21   double              norm;
22   int                 ierr, convinfo;
23 
24   PetscFunctionBegin;
25   if (snes->ksp_ewconv) {
26     if (!kctx) SETERRQ(1,0,"Convergence context does not exist");
27     if (n == 0) SNES_KSP_EW_ComputeRelativeTolerance_Private(snes,ksp);
28     kctx->lresid_last = rnorm;
29   }
30   convinfo = KSPDefaultConverged(ksp,n,rnorm,ctx);
31   if (convinfo) {
32     PLogInfo(snes,"SNES: KSP iterations=%d, rnorm=%g\n",n,rnorm);
33     PetscFunctionReturn(convinfo);
34   }
35 
36   /* Determine norm of solution */
37   ierr = KSPBuildSolution(ksp,0,&x); CHKERRQ(ierr);
38   ierr = VecNorm(x,NORM_2,&norm); CHKERRQ(ierr);
39   if (norm >= neP->delta) {
40     PLogInfo(snes,"SNES: KSP iterations=%d, rnorm=%g\n",n,rnorm);
41     PLogInfo(snes,"SNES: Ending linear iteration early, delta=%g, length=%g\n",neP->delta,norm);
42     PetscFunctionReturn(1);
43   }
44   PetscFunctionReturn(0);
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(PetscObject obj )
205 {
206   SNES snes = (SNES) obj;
207   int  ierr;
208 
209   PetscFunctionBegin;
210   if (snes->nwork) {
211     ierr = VecDestroyVecs(snes->work,snes->nwork); CHKERRQ(ierr);
212   }
213   PetscFree(snes->data);
214   PetscFunctionReturn(0);
215 }
216 /*------------------------------------------------------------*/
217 
218 #undef __FUNC__
219 #define __FUNC__ "SNESSetFromOptions_EQ_TR"
220 static int SNESSetFromOptions_EQ_TR(SNES snes)
221 {
222   SNES_TR *ctx = (SNES_TR *)snes->data;
223   double  tmp;
224   int     ierr,flg;
225 
226   PetscFunctionBegin;
227   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_mu",&tmp, &flg); CHKERRQ(ierr);
228   if (flg) {ctx->mu = tmp;}
229   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_eta",&tmp, &flg); CHKERRQ(ierr);
230   if (flg) {ctx->eta = tmp;}
231   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_sigma",&tmp, &flg); CHKERRQ(ierr);
232   if (flg) {ctx->sigma = tmp;}
233   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_delta0",&tmp, &flg); CHKERRQ(ierr);
234   if (flg) {ctx->delta0 = tmp;}
235   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_delta1",&tmp, &flg); CHKERRQ(ierr);
236   if (flg) {ctx->delta1 = tmp;}
237   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_delta2",&tmp, &flg); CHKERRQ(ierr);
238   if (flg) {ctx->delta2 = tmp;}
239   ierr = OptionsGetDouble(snes->prefix,"-snes_eq_tr_delta3",&tmp, &flg); CHKERRQ(ierr);
240   if (flg) {ctx->delta3 = tmp;}
241   PetscFunctionReturn(0);
242 }
243 
244 #undef __FUNC__
245 #define __FUNC__ "SNESPrintHelp_EQ_TR"
246 static int SNESPrintHelp_EQ_TR(SNES snes,char *p)
247 {
248   SNES_TR *ctx = (SNES_TR *)snes->data;
249 
250   PetscFunctionBegin;
251   PetscFPrintf(snes->comm,stdout," method SNES_EQ_TR (tr) for systems of nonlinear equations:\n");
252   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_mu <mu> (default %g)\n",p,ctx->mu);
253   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_eta <eta> (default %g)\n",p,ctx->eta);
254   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_sigma <sigma> (default %g)\n",p,ctx->sigma);
255   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_delta0 <delta0> (default %g)\n",p,ctx->delta0);
256   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_delta1 <delta1> (default %g)\n",p,ctx->delta1);
257   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_delta2 <delta2> (default %g)\n",p,ctx->delta2);
258   PetscFPrintf(snes->comm,stdout,"   %ssnes_eq_tr_delta3 <delta3> (default %g)\n",p,ctx->delta3);
259   PetscFunctionReturn(0);
260 }
261 
262 #undef __FUNC__
263 #define __FUNC__ "SNESView_EQ_TR"
264 static int SNESView_EQ_TR(PetscObject obj,Viewer viewer)
265 {
266   SNES       snes = (SNES)obj;
267   SNES_TR    *tr = (SNES_TR *)snes->data;
268   FILE       *fd;
269   int        ierr;
270   ViewerType vtype;
271 
272   PetscFunctionBegin;
273   ierr = ViewerGetType(viewer,&vtype); CHKERRQ(ierr);
274   if (vtype  == ASCII_FILE_VIEWER || vtype == ASCII_FILES_VIEWER) {
275     ierr = ViewerASCIIGetPointer(viewer,&fd); CHKERRQ(ierr);
276     PetscFPrintf(snes->comm,fd,"    mu=%g, eta=%g, sigma=%g\n",tr->mu,tr->eta,tr->sigma);
277     PetscFPrintf(snes->comm,fd,"    delta0=%g, delta1=%g, delta2=%g, delta3=%g\n",
278                  tr->delta0,tr->delta1,tr->delta2,tr->delta3);
279   }
280   PetscFunctionReturn(0);
281 }
282 
283 /* ---------------------------------------------------------------- */
284 #undef __FUNC__
285 #define __FUNC__ "SNESConverged_EQ_TR"
286 /*@
287    SNESConverged_EQ_TR - Monitors the convergence of the trust region
288    method SNES_EQ_TR for solving systems of nonlinear equations (default).
289 
290    Input Parameters:
291 .  snes - the SNES context
292 .  xnorm - 2-norm of current iterate
293 .  pnorm - 2-norm of current step
294 .  fnorm - 2-norm of function
295 .  dummy - unused context
296 
297    Returns:
298 $  1  if  ( delta < xnorm*deltatol ),
299 $  2  if  ( fnorm < atol ),
300 $  3  if  ( pnorm < xtol*xnorm ),
301 $ -2  if  ( nfct > maxf ),
302 $ -1  if  ( delta < xnorm*epsmch ),
303 $  0  otherwise,
304 
305    where
306 $    delta    - trust region paramenter
307 $    deltatol - trust region size tolerance,
308 $               set with SNESSetTrustRegionTolerance()
309 $    maxf - maximum number of function evaluations,
310 $           set with SNESSetTolerances()
311 $    nfct - number of function evaluations,
312 $    atol - absolute function norm tolerance,
313 $           set with SNESSetTolerances()
314 $    xtol - relative function norm tolerance,
315 $           set with SNESSetTolerances()
316 
317 .keywords: SNES, nonlinear, default, converged, convergence
318 
319 .seealso: SNESSetConvergenceTest(), SNESEisenstatWalkerConverged()
320 @*/
321 int SNESConverged_EQ_TR(SNES snes,double xnorm,double pnorm,double fnorm,void *dummy)
322 {
323   SNES_TR *neP = (SNES_TR *)snes->data;
324   double  epsmch = 1.0e-14;   /* This must be fixed */
325   int     info;
326 
327   PetscFunctionBegin;
328   if (snes->method_class != SNES_NONLINEAR_EQUATIONS) {
329     SETERRQ(1,0,"For SNES_NONLINEAR_EQUATIONS only");
330   }
331 
332   if (fnorm != fnorm) {
333     PLogInfo(snes,"SNES:Failed to converged, function norm is NaN\n");
334     PetscFunctionReturn(-3);
335   }
336   if (neP->delta < xnorm * snes->deltatol) {
337     PLogInfo(snes,
338       "SNESConverged_EQ_TR: Converged due to trust region param %g<%g*%g\n",neP->delta,xnorm,snes->deltatol);
339     PetscFunctionReturn(1);
340   }
341   if (neP->itflag) {
342     info = SNESConverged_EQ_LS(snes,xnorm,pnorm,fnorm,dummy);
343     if (info) PetscFunctionReturn(info);
344   } else if (snes->nfuncs > snes->max_funcs) {
345     PLogInfo(snes,
346       "SNES: Exceeded maximum number of function evaluations: %d > %d\n",
347       snes->nfuncs, snes->max_funcs );
348     PetscFunctionReturn(-2);
349   }
350   if (neP->delta < xnorm * epsmch) {
351     PLogInfo(snes,
352       "SNESConverged_EQ_TR: Converged due to trust region param %g < %g * %g\n",neP->delta,xnorm, epsmch);
353     PetscFunctionReturn(-1);
354   }
355   PetscFunctionReturn(0);
356 }
357 /* ------------------------------------------------------------ */
358 #undef __FUNC__
359 #define __FUNC__ "SNESCreate_EQ_TR"
360 int SNESCreate_EQ_TR(SNES snes )
361 {
362   SNES_TR *neP;
363 
364   PetscFunctionBegin;
365   if (snes->method_class != SNES_NONLINEAR_EQUATIONS) {
366     SETERRQ(1,0,"For SNES_NONLINEAR_EQUATIONS only");
367   }
368   snes->type 		= SNES_EQ_TR;
369   snes->setup		= SNESSetUp_EQ_TR;
370   snes->solve		= SNESSolve_EQ_TR;
371   snes->destroy		= SNESDestroy_EQ_TR;
372   snes->converged	= SNESConverged_EQ_TR;
373   snes->printhelp       = SNESPrintHelp_EQ_TR;
374   snes->setfromoptions  = SNESSetFromOptions_EQ_TR;
375   snes->view            = SNESView_EQ_TR;
376   snes->nwork           = 0;
377 
378   neP			= PetscNew(SNES_TR); CHKPTRQ(neP);
379   PLogObjectMemory(snes,sizeof(SNES_TR));
380   snes->data	        = (void *) neP;
381   neP->mu		= 0.25;
382   neP->eta		= 0.75;
383   neP->delta		= 0.0;
384   neP->delta0		= 0.2;
385   neP->delta1		= 0.3;
386   neP->delta2		= 0.75;
387   neP->delta3		= 2.0;
388   neP->sigma		= 0.0001;
389   neP->itflag		= 0;
390   neP->rnorm0		= 0;
391   neP->ttol		= 0;
392   PetscFunctionReturn(0);
393 }
394