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