1 #ifndef lint 2 static char vcid[] = "$Id: tr.c,v 1.22 1995/07/30 15:20:22 curfman Exp bsmith $"; 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 VecNorm(X, &xnorm ); /* xnorm = || X || */ 80 81 ierr = SNESComputeFunction(snes,X,F); CHKERRQ(ierr); /* (+/-) F(X) */ 82 VecNorm(F, &fnorm ); /* 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 VecNorm( Ytmp, &norm ); 104 while(1) { 105 VecCopy(Ytmp,Y); 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 VecScale( &cnorm, Y ); 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 VecAXPY(&one, X, Y ); /* 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 VecNorm( G, &gnorm ); /* 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 133 PLogInfo((PetscObject)snes,"%d: f_norm=%g, g_norm=%g, ynorm=%g\n", 134 i, fnorm, gnorm, ynorm ); 135 PLogInfo((PetscObject)snes,"gpred=%g, rho=%g, delta=%g,iters=%d\n", 136 gpnorm, rho, delta, lits); 137 138 neP->delta = delta; 139 if (rho > neP->sigma) break; 140 PLogInfo((PetscObject)snes,"Trying again in smaller region\n"); 141 /* check to see if progress is hopeless */ 142 neP->itflag = 0; 143 if ((*snes->converged)(snes,xnorm,ynorm,fnorm,snes->cnvP)) { 144 /* We're not progressing, so return with the current iterate */ 145 if (X != snes->vec_sol) { 146 VecCopy(X,snes->vec_sol); 147 snes->vec_sol_always = snes->vec_sol; 148 snes->vec_func_always = snes->vec_func; 149 } 150 } 151 snes->nfailures++; 152 } 153 fnorm = gnorm; 154 snes->norm = fnorm; 155 if (history && history_len > i+1) history[i+1] = fnorm; 156 TMP = F; F = G; snes->vec_func_always = F; G = TMP; 157 TMP = X; X = Y; snes->vec_sol_always = X; Y = TMP; 158 VecNorm(X, &xnorm ); /* xnorm = || X || */ 159 if (snes->monitor) 160 {(*snes->monitor)(snes,i+1,fnorm,snes->monP); CHKERRQ(ierr);} 161 162 /* Test for convergence */ 163 neP->itflag = 1; 164 if ((*snes->converged)( snes, xnorm, ynorm, fnorm,snes->cnvP )) { 165 /* Verify solution is in corect location */ 166 if (X != snes->vec_sol) { 167 VecCopy(X,snes->vec_sol); 168 snes->vec_sol_always = snes->vec_sol; 169 snes->vec_func_always = snes->vec_func; 170 } 171 break; 172 } 173 } 174 if (i == maxits) { 175 PLogInfo((PetscObject)snes, 176 "Maximum number of iterations has been reached: %d\n",maxits); 177 i--; 178 } 179 *its = i+1; 180 return 0; 181 } 182 /*------------------------------------------------------------*/ 183 static int SNESSetUp_TR( SNES snes ) 184 { 185 int ierr; 186 snes->nwork = 4; 187 ierr = VecGetVecs(snes->vec_sol,snes->nwork,&snes->work ); CHKERRQ(ierr); 188 snes->vec_sol_update_always = snes->work[3]; 189 return 0; 190 } 191 /*------------------------------------------------------------*/ 192 static int SNESDestroy_TR(PetscObject obj ) 193 { 194 SNES snes = (SNES) obj; 195 VecFreeVecs(snes->work, snes->nwork ); 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 snes->data = (void *) neP; 328 neP->mu = 0.25; 329 neP->eta = 0.75; 330 neP->delta = 0.0; 331 neP->delta0 = 0.2; 332 neP->delta1 = 0.3; 333 neP->delta2 = 0.75; 334 neP->delta3 = 2.0; 335 neP->sigma = 0.0001; 336 neP->itflag = 0; 337 neP->rnorm0 = 0; 338 neP->ttol = 0; 339 return 0; 340 } 341