14800dd8cSBarry Smith #ifndef lint 2*614e12f8SBarry Smith static char vcid[] = "$Id: tr.c,v 1.10 1995/05/18 22:48:06 bsmith Exp bsmith $"; 34800dd8cSBarry Smith #endif 44800dd8cSBarry Smith 54800dd8cSBarry Smith #include <math.h> 6fbe28522SBarry Smith #include "tr.h" 74800dd8cSBarry Smith 8fbe28522SBarry Smith /* 9df60cc22SBarry Smith This convergence test determines if the two norm of the 10df60cc22SBarry Smith solution lies outside the trust region, if so it halts. 11df60cc22SBarry Smith */ 12df60cc22SBarry Smith int TRConverged_Private(KSP ksp,int n, double rnorm, void *ctx) 13df60cc22SBarry Smith { 14df60cc22SBarry Smith SNES snes = (SNES) ctx; 15df60cc22SBarry Smith SNES_TR *neP = (SNES_TR*)snes->data; 16df60cc22SBarry Smith double rtol,atol,dtol,norm; 17df60cc22SBarry Smith Vec x; 18df60cc22SBarry Smith int ierr; 19df60cc22SBarry Smith 20df60cc22SBarry Smith KSPGetTolerances(ksp,&rtol,&atol,&dtol); 21df60cc22SBarry Smith 22df60cc22SBarry Smith if ( n == 0 ) { 23df60cc22SBarry Smith neP->ttol = MAX(rtol*rnorm,atol); 24df60cc22SBarry Smith neP->rnorm0 = rnorm; 25df60cc22SBarry Smith } 26df60cc22SBarry Smith if ( rnorm <= neP->ttol ) return 1; 27df60cc22SBarry Smith if ( rnorm >= dtol*neP->rnorm0 || rnorm != rnorm) return -1; 28df60cc22SBarry Smith 29df60cc22SBarry Smith /* determine norm of solution */ 30df60cc22SBarry Smith ierr = KSPBuildSolution(ksp,0,&x); CHKERR(ierr); 31df60cc22SBarry Smith ierr = VecNorm(x,&norm); CHKERR(ierr); 32df60cc22SBarry Smith if (norm >= neP->delta) { 33df60cc22SBarry Smith PLogInfo((PetscObject)snes,"Ending linear iteration early, delta %g length %g\n",neP->delta,norm); 34df60cc22SBarry Smith return 1; 35df60cc22SBarry Smith } 36df60cc22SBarry Smith return(0); 37df60cc22SBarry Smith } 38df60cc22SBarry Smith /* 3939e2f89bSBarry Smith Implements Newton's Method with a very simple trust region 404800dd8cSBarry Smith approach for solving systems of nonlinear equations. 414800dd8cSBarry Smith 424800dd8cSBarry Smith Input parameters: 436b5873e3SBarry Smith . nlP - nonlinear context obtained from SNESCreate() 444800dd8cSBarry Smith 454800dd8cSBarry Smith The basic algorithm is taken from "The Minpack Project", by More', 464800dd8cSBarry Smith Sorensen, Garbow, Hillstrom, pages 88-111 of "Sources and Development 474800dd8cSBarry Smith of Mathematical Software", Wayne Cowell, editor. See the examples 484800dd8cSBarry Smith in nonlin/examples. 49fbe28522SBarry Smith */ 504800dd8cSBarry Smith /* 514800dd8cSBarry Smith This is intended as a model implementation, since it does not 524800dd8cSBarry Smith necessarily have many of the bells and whistles of other 534800dd8cSBarry Smith implementations. 544800dd8cSBarry Smith 554800dd8cSBarry Smith */ 56fbe28522SBarry Smith static int SNESSolve_TR(SNES snes, int *its ) 574800dd8cSBarry Smith { 58fbe28522SBarry Smith SNES_TR *neP = (SNES_TR *) snes->data; 596b5873e3SBarry Smith Vec X, F, Y, G, TMP, Ytmp; 60edd2f0e1SBarry Smith int maxits, i, history_len, nlconv,ierr,lits; 61df60cc22SBarry Smith MatStructure flg = ALLMAT_DIFFERENT_NONZERO_PATTERN; 62fbe28522SBarry Smith double rho, fnorm, gnorm, gpnorm, xnorm, delta,norm; 634800dd8cSBarry Smith double *history, ynorm; 64eafb4bcbSBarry Smith Scalar one = 1.0,cnorm; 656b5873e3SBarry Smith double epsmch = 1.0e-14; /* This must be fixed */ 66df60cc22SBarry Smith KSP ksp; 67df60cc22SBarry Smith SLES sles; 684800dd8cSBarry Smith 694800dd8cSBarry Smith nlconv = 0; /* convergence monitor */ 70fbe28522SBarry Smith history = snes->conv_hist; /* convergence history */ 71fbe28522SBarry Smith history_len = snes->conv_hist_len; /* convergence history length */ 72fbe28522SBarry Smith maxits = snes->max_its; /* maximum number of iterations */ 73fbe28522SBarry Smith X = snes->vec_sol; /* solution vector */ 7439e2f89bSBarry Smith F = snes->vec_func; /* residual vector */ 75fbe28522SBarry Smith Y = snes->work[0]; /* work vectors */ 76fbe28522SBarry Smith G = snes->work[1]; 776b5873e3SBarry Smith Ytmp = snes->work[2]; 784800dd8cSBarry Smith 79fbe28522SBarry Smith ierr = SNESComputeInitialGuess(snes,X); CHKERR(ierr); /* X <- X_0 */ 80fbe28522SBarry Smith VecNorm(X, &xnorm ); /* xnorm = || X || */ 814800dd8cSBarry Smith 82eafb4bcbSBarry Smith ierr = SNESComputeFunction(snes,X,F); CHKERR(ierr); /* (+/-) F(X) */ 83fbe28522SBarry Smith VecNorm(F, &fnorm ); /* fnorm <- || F || */ 84fbe28522SBarry Smith snes->norm = fnorm; 854800dd8cSBarry Smith if (history && history_len > 0) history[0] = fnorm; 864800dd8cSBarry Smith delta = neP->delta0*fnorm; 874800dd8cSBarry Smith neP->delta = delta; 88eafb4bcbSBarry Smith if (snes->Monitor)(*snes->Monitor)(snes,0,fnorm,snes->monP); 894800dd8cSBarry Smith 90df60cc22SBarry Smith /* et the stopping criteria to use the More' trick. */ 91df60cc22SBarry Smith ierr = SNESGetSLES(snes,&sles); CHKERR(ierr); 92df60cc22SBarry Smith ierr = SLESGetKSP(sles,&ksp); CHKERR(ierr); 93df60cc22SBarry Smith ierr = KSPSetConvergenceTest(ksp,TRConverged_Private,(void *) snes); 94df60cc22SBarry Smith CHKERR(ierr); 95df60cc22SBarry Smith 964800dd8cSBarry Smith for ( i=0; i<maxits; i++ ) { 97fbe28522SBarry Smith snes->iter = i+1; 984800dd8cSBarry Smith 99df60cc22SBarry Smith ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre, 100df60cc22SBarry Smith &flg,snes->jacP); CHKERR(ierr); 10123242f5aSBarry Smith ierr = SLESSetOperators(snes->sles,snes->jacobian,snes->jacobian_pre,flg); 1026b5873e3SBarry Smith ierr = SLESSolve(snes->sles,F,Ytmp,&lits); CHKERR(ierr); 1036b5873e3SBarry Smith VecNorm( Ytmp, &norm ); 1046b5873e3SBarry Smith while(1) { 1056b5873e3SBarry Smith VecCopy(Ytmp,Y); 106fbe28522SBarry Smith /* Scale Y if need be and predict new value of F norm */ 1076b5873e3SBarry Smith 108eafb4bcbSBarry Smith if (norm >= delta) { 109fbe28522SBarry Smith norm = delta/norm; 110fbe28522SBarry Smith gpnorm = (1.0 - norm)*fnorm; 111eafb4bcbSBarry Smith cnorm = norm; 112df60cc22SBarry Smith PLogInfo((PetscObject)snes, "Scaling direction by %g \n",norm ); 113eafb4bcbSBarry Smith VecScale( &cnorm, Y ); 114eafb4bcbSBarry Smith norm = gpnorm; 115fbe28522SBarry Smith ynorm = delta; 116fbe28522SBarry Smith } else { 117fbe28522SBarry Smith gpnorm = 0.0; 118fbe28522SBarry Smith PLogInfo((PetscObject)snes,"Direction is in Trust Region \n" ); 119fbe28522SBarry Smith ynorm = norm; 120fbe28522SBarry Smith } 121fbe28522SBarry Smith VecAXPY(&one, X, Y ); /* Y <- X + Y */ 122eafb4bcbSBarry Smith ierr = SNESComputeFunction(snes,Y,G); CHKERR(ierr); /* (+/-) F(X) */ 123fbe28522SBarry Smith VecNorm( G, &gnorm ); /* gnorm <- || g || */ 1244800dd8cSBarry Smith if (fnorm == gpnorm) rho = 0.0; 125fbe28522SBarry Smith else rho = (fnorm*fnorm - gnorm*gnorm)/(fnorm*fnorm - gpnorm*gpnorm); 1264800dd8cSBarry Smith 1274800dd8cSBarry Smith /* Update size of trust region */ 1284800dd8cSBarry Smith if (rho < neP->mu) delta *= neP->delta1; 1294800dd8cSBarry Smith else if (rho < neP->eta) delta *= neP->delta2; 1304800dd8cSBarry Smith else delta *= neP->delta3; 1314800dd8cSBarry Smith 132fbe28522SBarry Smith PLogInfo((PetscObject)snes,"%d: f_norm=%g, g_norm=%g, ynorm=%g\n", 1334800dd8cSBarry Smith i, fnorm, gnorm, ynorm ); 134fbe28522SBarry Smith PLogInfo((PetscObject)snes,"gpred=%g, rho=%g, delta=%g,iters=%d\n", 135fbe28522SBarry Smith gpnorm, rho, delta, lits); 1364800dd8cSBarry Smith 1374800dd8cSBarry Smith neP->delta = delta; 1384800dd8cSBarry Smith if (rho > neP->sigma) break; 139eafb4bcbSBarry Smith PLogInfo((PetscObject)snes,"Trying again in smaller region\n"); 1406b5873e3SBarry Smith /* check to see if progress is hopeless */ 1416b5873e3SBarry Smith if (neP->delta < xnorm * epsmch) return -1; 1424800dd8cSBarry Smith } 1434800dd8cSBarry Smith fnorm = gnorm; 144fbe28522SBarry Smith snes->norm = fnorm; 1454800dd8cSBarry Smith if (history && history_len > i+1) history[i+1] = fnorm; 14639e2f89bSBarry Smith TMP = F; F = G; snes->vec_func_always = F; G = TMP; 14739e2f89bSBarry Smith TMP = X; X = Y;snes->vec_sol_always = X; Y = TMP; 148fbe28522SBarry Smith VecNorm(X, &xnorm ); /* xnorm = || X || */ 149*614e12f8SBarry Smith if (snes->Monitor) (*snes->Monitor)(snes,i+1,fnorm,snes->monP); 1504800dd8cSBarry Smith 1514800dd8cSBarry Smith /* Test for convergence */ 152fbe28522SBarry Smith if ((*snes->Converged)( snes, xnorm, ynorm, fnorm,snes->cnvP )) { 1534800dd8cSBarry Smith /* Verify solution is in corect location */ 15439e2f89bSBarry Smith if (X != snes->vec_sol) { 15539e2f89bSBarry Smith VecCopy(X, snes->vec_sol ); 15639e2f89bSBarry Smith snes->vec_sol_always = snes->vec_sol; 15739e2f89bSBarry Smith snes->vec_func_always = snes->vec_func; 15839e2f89bSBarry Smith } 1594800dd8cSBarry Smith break; 1604800dd8cSBarry Smith } 1614800dd8cSBarry Smith } 162*614e12f8SBarry Smith if (i == maxits) *its = i-1; else *its = i + 1; 163fbe28522SBarry Smith return 0; 1644800dd8cSBarry Smith } 1654800dd8cSBarry Smith /* -------------------------------------------------------------*/ 1664800dd8cSBarry Smith 1674800dd8cSBarry Smith /*------------------------------------------------------------*/ 168fbe28522SBarry Smith static int SNESSetUp_TR( SNES snes ) 1694800dd8cSBarry Smith { 170fbe28522SBarry Smith int ierr; 1716b5873e3SBarry Smith snes->nwork = 3; 172fbe28522SBarry Smith ierr = VecGetVecs(snes->vec_sol,snes->nwork,&snes->work ); CHKERR(ierr); 173fbe28522SBarry Smith return 0; 1744800dd8cSBarry Smith } 1754800dd8cSBarry Smith /*------------------------------------------------------------*/ 176fbe28522SBarry Smith static int SNESDestroy_TR(PetscObject obj ) 1774800dd8cSBarry Smith { 178fbe28522SBarry Smith SNES snes = (SNES) obj; 179fbe28522SBarry Smith VecFreeVecs(snes->work, snes->nwork ); 180df60cc22SBarry Smith FREE(snes->data); 181fbe28522SBarry Smith return 0; 1824800dd8cSBarry Smith } 1834800dd8cSBarry Smith /*------------------------------------------------------------*/ 1844800dd8cSBarry Smith 185fbe28522SBarry Smith static int SNESSetFromOptions_TR(SNES snes) 1864800dd8cSBarry Smith { 187fbe28522SBarry Smith SNES_TR *ctx = (SNES_TR *)snes->data; 188fbe28522SBarry Smith double tmp; 1894800dd8cSBarry Smith 190df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-mu",&tmp)) {ctx->mu = tmp;} 191df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-eta",&tmp)) {ctx->eta = tmp;} 192df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-sigma",&tmp)) {ctx->sigma = tmp;} 193df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-delta0",&tmp)) {ctx->delta0 = tmp;} 194df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-delta1",&tmp)) {ctx->delta1 = tmp;} 195df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-delta2",&tmp)) {ctx->delta2 = tmp;} 196df60cc22SBarry Smith if (OptionsGetDouble(snes->prefix,"-delta3",&tmp)) {ctx->delta3 = tmp;} 197fbe28522SBarry Smith return 0; 1984800dd8cSBarry Smith } 1994800dd8cSBarry Smith 200fbe28522SBarry Smith static int SNESPrintHelp_TR(SNES snes) 2014800dd8cSBarry Smith { 202fbe28522SBarry Smith SNES_TR *ctx = (SNES_TR *)snes->data; 203fbe28522SBarry Smith char *prefix = "-"; 204fbe28522SBarry Smith if (snes->prefix) prefix = snes->prefix; 205fbe28522SBarry Smith fprintf(stderr,"%smu mu (default %g)\n",prefix,ctx->mu); 206fbe28522SBarry Smith fprintf(stderr,"%seta eta (default %g)\n",prefix,ctx->eta); 207fbe28522SBarry Smith fprintf(stderr,"%ssigma sigma (default %g)\n",prefix,ctx->sigma); 208fbe28522SBarry Smith fprintf(stderr,"%sdelta0 delta0 (default %g)\n",prefix,ctx->delta0); 209fbe28522SBarry Smith fprintf(stderr,"%sdelta1 delta1 (default %g)\n",prefix,ctx->delta1); 210fbe28522SBarry Smith fprintf(stderr,"%sdelta2 delta2 (default %g)\n",prefix,ctx->delta2); 211fbe28522SBarry Smith fprintf(stderr,"%sdelta3 delta3 (default %g)\n",prefix,ctx->delta3); 212fbe28522SBarry Smith return 0; 2134800dd8cSBarry Smith } 2144800dd8cSBarry Smith 215fbe28522SBarry Smith int SNESCreate_TR(SNES snes ) 2164800dd8cSBarry Smith { 217fbe28522SBarry Smith SNES_TR *neP; 2184800dd8cSBarry Smith 219fbe28522SBarry Smith snes->type = SNES_NTR; 22006be10caSBarry Smith snes->setup = SNESSetUp_TR; 22106be10caSBarry Smith snes->solve = SNESSolve_TR; 222fbe28522SBarry Smith snes->destroy = SNESDestroy_TR; 2236b5873e3SBarry Smith snes->Converged = SNESDefaultConverged; 22406be10caSBarry Smith snes->printhelp = SNESPrintHelp_TR; 22506be10caSBarry Smith snes->setfromoptions = SNESSetFromOptions_TR; 226fbe28522SBarry Smith 227fbe28522SBarry Smith neP = NEW(SNES_TR); CHKPTR(neP); 228fbe28522SBarry Smith snes->data = (void *) neP; 229fbe28522SBarry Smith neP->mu = 0.25; 230fbe28522SBarry Smith neP->eta = 0.75; 231fbe28522SBarry Smith neP->delta = 0.0; 232fbe28522SBarry Smith neP->delta0 = 0.2; 233fbe28522SBarry Smith neP->delta1 = 0.3; 234fbe28522SBarry Smith neP->delta2 = 0.75; 235fbe28522SBarry Smith neP->delta3 = 2.0; 236fbe28522SBarry Smith neP->sigma = 0.0001; 237fbe28522SBarry Smith neP->itflag = 0; 2386b5873e3SBarry Smith return 0; 2394800dd8cSBarry Smith } 240