1 2 #include <../src/snes/impls/ls/lsimpl.h> 3 4 /* 5 Checks if J^T F = 0 which implies we've found a local minimum of the norm of the function, 6 || F(u) ||_2 but not a zero, F(u) = 0. In the case when one cannot compute J^T F we use the fact that 7 0 = (J^T F)^T W = F^T J W iff W not in the null space of J. Thanks for Jorge More 8 for this trick. One assumes that the probability that W is in the null space of J is very, very small. 9 */ 10 #undef __FUNCT__ 11 #define __FUNCT__ "SNESLSCheckLocalMin_Private" 12 PetscErrorCode SNESLSCheckLocalMin_Private(SNES snes,Mat A,Vec F,Vec W,PetscReal fnorm,PetscBool *ismin) 13 { 14 PetscReal a1; 15 PetscErrorCode ierr; 16 PetscBool hastranspose; 17 18 PetscFunctionBegin; 19 *ismin = PETSC_FALSE; 20 ierr = MatHasOperation(A,MATOP_MULT_TRANSPOSE,&hastranspose);CHKERRQ(ierr); 21 if (hastranspose) { 22 /* Compute || J^T F|| */ 23 ierr = MatMultTranspose(A,F,W);CHKERRQ(ierr); 24 ierr = VecNorm(W,NORM_2,&a1);CHKERRQ(ierr); 25 ierr = PetscInfo1(snes,"|| J^T F|| %14.12e near zero implies found a local minimum\n",(double)(a1/fnorm));CHKERRQ(ierr); 26 if (a1/fnorm < 1.e-4) *ismin = PETSC_TRUE; 27 } else { 28 Vec work; 29 PetscScalar result; 30 PetscReal wnorm; 31 32 ierr = VecSetRandom(W,PETSC_NULL);CHKERRQ(ierr); 33 ierr = VecNorm(W,NORM_2,&wnorm);CHKERRQ(ierr); 34 ierr = VecDuplicate(W,&work);CHKERRQ(ierr); 35 ierr = MatMult(A,W,work);CHKERRQ(ierr); 36 ierr = VecDot(F,work,&result);CHKERRQ(ierr); 37 ierr = VecDestroy(&work);CHKERRQ(ierr); 38 a1 = PetscAbsScalar(result)/(fnorm*wnorm); 39 ierr = PetscInfo1(snes,"(F^T J random)/(|| F ||*||random|| %14.12e near zero implies found a local minimum\n",(double)a1);CHKERRQ(ierr); 40 if (a1 < 1.e-4) *ismin = PETSC_TRUE; 41 } 42 PetscFunctionReturn(0); 43 } 44 45 /* 46 Checks if J^T(F - J*X) = 0 47 */ 48 #undef __FUNCT__ 49 #define __FUNCT__ "SNESLSCheckResidual_Private" 50 PetscErrorCode SNESLSCheckResidual_Private(SNES snes,Mat A,Vec F,Vec X,Vec W1,Vec W2) 51 { 52 PetscReal a1,a2; 53 PetscErrorCode ierr; 54 PetscBool hastranspose; 55 56 PetscFunctionBegin; 57 ierr = MatHasOperation(A,MATOP_MULT_TRANSPOSE,&hastranspose);CHKERRQ(ierr); 58 if (hastranspose) { 59 ierr = MatMult(A,X,W1);CHKERRQ(ierr); 60 ierr = VecAXPY(W1,-1.0,F);CHKERRQ(ierr); 61 62 /* Compute || J^T W|| */ 63 ierr = MatMultTranspose(A,W1,W2);CHKERRQ(ierr); 64 ierr = VecNorm(W1,NORM_2,&a1);CHKERRQ(ierr); 65 ierr = VecNorm(W2,NORM_2,&a2);CHKERRQ(ierr); 66 if (a1 != 0.0) { 67 ierr = PetscInfo1(snes,"||J^T(F-Ax)||/||F-AX|| %14.12e near zero implies inconsistent rhs\n",(double)(a2/a1));CHKERRQ(ierr); 68 } 69 } 70 PetscFunctionReturn(0); 71 } 72 73 #undef __FUNCT__ 74 #define __FUNCT__ "SNESLineSearchCubic_LS" 75 /*@C 76 SNESLineSearchCubic - Performs a cubic line search (default line search method). 77 78 Collective on SNES 79 80 Input Parameters: 81 + snes - nonlinear context 82 . lsctx - optional context for line search (not used here) 83 . x - current iterate 84 . f - residual evaluated at x 85 . y - search direction 86 . fnorm - 2-norm of f 87 - xnorm - norm of x if known, otherwise 0 88 89 Output Parameters: 90 + g - residual evaluated at new iterate y 91 . w - new iterate 92 . gnorm - 2-norm of g 93 . ynorm - 2-norm of search length 94 - flag - PETSC_TRUE if line search succeeds; PETSC_FALSE on failure. 95 96 Options Database Key: 97 + -snes_ls cubic - Activates SNESLineSearchCubic() 98 . -snes_ls_alpha <alpha> - Sets alpha 99 . -snes_ls_maxstep <maxstep> - Sets the maximum stepsize the line search will use (if the 2-norm(y) > maxstep then scale y to be y = (maxstep/2-norm(y)) *y) 100 - -snes_ls_minlambda <minlambda> - Sets the minimum lambda the line search will use minlambda/ max_i ( y[i]/x[i] ) 101 102 103 Notes: 104 This line search is taken from "Numerical Methods for Unconstrained 105 Optimization and Nonlinear Equations" by Dennis and Schnabel, page 325. 106 107 Level: advanced 108 109 .keywords: SNES, nonlinear, line search, cubic 110 111 .seealso: SNESLineSearchQuadratic(), SNESLineSearchNo(), SNESLineSearchSet(), SNESLineSearchNoNorms() 112 @*/ 113 PetscErrorCode SNESLineSearchCubic_LS(SNES snes,void *lsctx,Vec x,Vec f,Vec y,PetscReal fnorm,PetscReal xnorm,Vec g,Vec w,PetscReal *ynorm,PetscReal *gnorm,PetscBool *flag) 114 { 115 /* 116 Note that for line search purposes we work with with the related 117 minimization problem: 118 min z(x): R^n -> R, 119 where z(x) = .5 * fnorm*fnorm, and fnorm = || f ||_2. 120 */ 121 122 PetscReal initslope,lambdaprev,gnormprev,a,b,d,t1,t2,rellength; 123 PetscReal minlambda,lambda,lambdatemp; 124 #if defined(PETSC_USE_COMPLEX) 125 PetscScalar cinitslope; 126 #endif 127 PetscErrorCode ierr; 128 PetscInt count; 129 PetscBool changed_w = PETSC_FALSE,changed_y = PETSC_FALSE; 130 MPI_Comm comm; 131 132 PetscFunctionBegin; 133 ierr = PetscObjectGetComm((PetscObject)snes,&comm);CHKERRQ(ierr); 134 ierr = PetscLogEventBegin(SNES_LineSearch,snes,x,f,g);CHKERRQ(ierr); 135 *flag = PETSC_TRUE; 136 137 ierr = VecNorm(y,NORM_2,ynorm);CHKERRQ(ierr); 138 if (*ynorm == 0.0) { 139 if (snes->ls_monitor) { 140 ierr = PetscViewerASCIIAddTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 141 ierr = PetscViewerASCIIPrintf(snes->ls_monitor," Line search: Initial direction and size is 0\n");CHKERRQ(ierr); 142 ierr = PetscViewerASCIISubtractTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 143 } 144 *gnorm = fnorm; 145 ierr = VecCopy(x,w);CHKERRQ(ierr); 146 ierr = VecCopy(f,g);CHKERRQ(ierr); 147 *flag = PETSC_FALSE; 148 goto theend1; 149 } 150 if (*ynorm > snes->maxstep) { /* Step too big, so scale back */ 151 if (snes->ls_monitor) { 152 ierr = PetscViewerASCIIAddTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 153 ierr = PetscViewerASCIIPrintf(snes->ls_monitor," Line search: Scaling step by %14.12e old ynorm %14.12e\n",(double)(snes->maxstep/(*ynorm)),(double)(*ynorm));CHKERRQ(ierr); 154 ierr = PetscViewerASCIISubtractTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 155 } 156 ierr = VecScale(y,snes->maxstep/(*ynorm));CHKERRQ(ierr); 157 *ynorm = snes->maxstep; 158 } 159 ierr = VecMaxPointwiseDivide(y,x,&rellength);CHKERRQ(ierr); 160 minlambda = snes->steptol/rellength; 161 ierr = MatMult(snes->jacobian,y,w);CHKERRQ(ierr); 162 #if defined(PETSC_USE_COMPLEX) 163 ierr = VecDot(f,w,&cinitslope);CHKERRQ(ierr); 164 initslope = PetscRealPart(cinitslope); 165 #else 166 ierr = VecDot(f,w,&initslope);CHKERRQ(ierr); 167 #endif 168 if (initslope > 0.0) initslope = -initslope; 169 if (initslope == 0.0) initslope = -1.0; 170 171 ierr = VecWAXPY(w,-1.0,y,x);CHKERRQ(ierr); 172 if (snes->nfuncs >= snes->max_funcs) { 173 ierr = PetscInfo(snes,"Exceeded maximum function evaluations, while checking full step length!\n");CHKERRQ(ierr); 174 *flag = PETSC_FALSE; 175 snes->reason = SNES_DIVERGED_FUNCTION_COUNT; 176 goto theend1; 177 } 178 ierr = SNESComputeFunction(snes,w,g);CHKERRQ(ierr); 179 if (snes->domainerror) { 180 ierr = PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);CHKERRQ(ierr); 181 PetscFunctionReturn(0); 182 } 183 ierr = VecNorm(g,NORM_2,gnorm);CHKERRQ(ierr); 184 if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number"); 185 ierr = PetscInfo2(snes,"Initial fnorm %14.12e gnorm %14.12e\n",(double)fnorm,(double)*gnorm);CHKERRQ(ierr); 186 if (.5*(*gnorm)*(*gnorm) <= .5*fnorm*fnorm + snes->ls_alpha*initslope) { /* Sufficient reduction */ 187 if (snes->ls_monitor) { 188 ierr = PetscViewerASCIIAddTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 189 ierr = PetscViewerASCIIPrintf(snes->ls_monitor," Line search: Using full step: fnorm %14.12e gnorm %14.12e\n",(double)fnorm,(double)*gnorm);CHKERRQ(ierr); 190 ierr = PetscViewerASCIISubtractTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 191 } 192 goto theend1; 193 } 194 195 /* Fit points with quadratic */ 196 lambda = 1.0; 197 lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope); 198 lambdaprev = lambda; 199 gnormprev = *gnorm; 200 if (lambdatemp > .5*lambda) lambdatemp = .5*lambda; 201 if (lambdatemp <= .1*lambda) lambda = .1*lambda; 202 else lambda = lambdatemp; 203 204 ierr = VecWAXPY(w,-lambda,y,x);CHKERRQ(ierr); 205 if (snes->nfuncs >= snes->max_funcs) { 206 ierr = PetscInfo1(snes,"Exceeded maximum function evaluations, while attempting quadratic backtracking! %D \n",snes->nfuncs);CHKERRQ(ierr); 207 *flag = PETSC_FALSE; 208 snes->reason = SNES_DIVERGED_FUNCTION_COUNT; 209 goto theend1; 210 } 211 ierr = SNESComputeFunction(snes,w,g);CHKERRQ(ierr); 212 if (snes->domainerror) { 213 ierr = PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);CHKERRQ(ierr); 214 PetscFunctionReturn(0); 215 } 216 ierr = VecNorm(g,NORM_2,gnorm);CHKERRQ(ierr); 217 if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number"); 218 if (snes->ls_monitor) { 219 ierr = PetscViewerASCIIAddTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 220 ierr = PetscViewerASCIIPrintf(snes->ls_monitor," Line search: gnorm after quadratic fit %14.12e\n",(double)(*gnorm));CHKERRQ(ierr); 221 ierr = PetscViewerASCIISubtractTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 222 } 223 if (.5*(*gnorm)*(*gnorm) < .5*fnorm*fnorm + lambda*snes->ls_alpha*initslope) { /* sufficient reduction */ 224 if (snes->ls_monitor) { 225 ierr = PetscViewerASCIIAddTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 226 ierr = PetscViewerASCIIPrintf(snes->ls_monitor," Line search: Quadratically determined step, lambda=%18.16e\n",(double)lambda);CHKERRQ(ierr); 227 ierr = PetscViewerASCIISubtractTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 228 } 229 goto theend1; 230 } 231 232 /* Fit points with cubic */ 233 count = 1; 234 while (PETSC_TRUE) { 235 if (lambda <= minlambda) { 236 if (snes->ls_monitor) { 237 ierr = PetscViewerASCIIAddTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 238 ierr = PetscViewerASCIIPrintf(snes->ls_monitor," Line search: unable to find good step length! After %D tries \n",count);CHKERRQ(ierr); 239 ierr = PetscViewerASCIIPrintf(snes->ls_monitor," Line search: fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, minlambda=%18.16e, lambda=%18.16e, initial slope=%18.16e\n",(double)fnorm,(double)(*gnorm),(double)(*ynorm),(double)minlambda,(double)lambda,(double)initslope);CHKERRQ(ierr); 240 ierr = PetscViewerASCIISubtractTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 241 } 242 *flag = PETSC_FALSE; 243 break; 244 } 245 t1 = .5*((*gnorm)*(*gnorm) - fnorm*fnorm) - lambda*initslope; 246 t2 = .5*(gnormprev*gnormprev - fnorm*fnorm) - lambdaprev*initslope; 247 a = (t1/(lambda*lambda) - t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 248 b = (-lambdaprev*t1/(lambda*lambda) + lambda*t2/(lambdaprev*lambdaprev))/(lambda-lambdaprev); 249 d = b*b - 3*a*initslope; 250 if (d < 0.0) d = 0.0; 251 if (a == 0.0) { 252 lambdatemp = -initslope/(2.0*b); 253 } else { 254 lambdatemp = (-b + PetscSqrtReal(d))/(3.0*a); 255 } 256 lambdaprev = lambda; 257 gnormprev = *gnorm; 258 if (lambdatemp > .5*lambda) lambdatemp = .5*lambda; 259 if (lambdatemp <= .1*lambda) lambda = .1*lambda; 260 else lambda = lambdatemp; 261 ierr = VecWAXPY(w,-lambda,y,x);CHKERRQ(ierr); 262 if (snes->nfuncs >= snes->max_funcs) { 263 ierr = PetscInfo1(snes,"Exceeded maximum function evaluations, while looking for good step length! %D \n",count);CHKERRQ(ierr); 264 ierr = PetscInfo5(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lambda=%18.16e, initial slope=%18.16e\n",(double)fnorm,(double)(*gnorm),(double)(*ynorm),(double)lambda,(double)initslope);CHKERRQ(ierr); 265 *flag = PETSC_FALSE; 266 snes->reason = SNES_DIVERGED_FUNCTION_COUNT; 267 break; 268 } 269 ierr = SNESComputeFunction(snes,w,g);CHKERRQ(ierr); 270 if (snes->domainerror) { 271 ierr = PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);CHKERRQ(ierr); 272 PetscFunctionReturn(0); 273 } 274 ierr = VecNorm(g,NORM_2,gnorm);CHKERRQ(ierr); 275 if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number"); 276 if (.5*(*gnorm)*(*gnorm) < .5*fnorm*fnorm + lambda*snes->ls_alpha*initslope) { /* is reduction enough? */ 277 if (snes->ls_monitor) { 278 ierr = PetscPrintf(comm," Line search: Cubically determined step, current gnorm %14.12e lambda=%18.16e\n",(double)(*gnorm),(double)lambda);CHKERRQ(ierr); 279 } 280 break; 281 } else { 282 if (snes->ls_monitor) { 283 ierr = PetscPrintf(comm," Line search: Cubic step no good, shrinking lambda, current gnorm %12.12e lambda=%18.16e\n",(double)(*gnorm),(double)lambda);CHKERRQ(ierr); 284 } 285 } 286 count++; 287 } 288 theend1: 289 /* Optional user-defined check for line search step validity */ 290 if (snes->ops->postcheckstep && *flag) { 291 ierr = (*snes->ops->postcheckstep)(snes,x,y,w,snes->postcheck,&changed_y,&changed_w);CHKERRQ(ierr); 292 if (changed_y) { 293 ierr = VecWAXPY(w,-1.0,y,x);CHKERRQ(ierr); 294 } 295 if (changed_y || changed_w) { /* recompute the function if the step has changed */ 296 ierr = SNESComputeFunction(snes,w,g);CHKERRQ(ierr); 297 if (snes->domainerror) { 298 ierr = PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);CHKERRQ(ierr); 299 PetscFunctionReturn(0); 300 } 301 ierr = VecNormBegin(g,NORM_2,gnorm);CHKERRQ(ierr); 302 if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number"); 303 ierr = VecNormBegin(y,NORM_2,ynorm);CHKERRQ(ierr); 304 ierr = VecNormEnd(g,NORM_2,gnorm);CHKERRQ(ierr); 305 ierr = VecNormEnd(y,NORM_2,ynorm);CHKERRQ(ierr); 306 } 307 } 308 ierr = PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);CHKERRQ(ierr); 309 PetscFunctionReturn(0); 310 } 311 /* -------------------------------------------------------------------------- */ 312 313 #undef __FUNCT__ 314 #define __FUNCT__ "SNESLineSearchQuadratic_LS" 315 /*@C 316 SNESLineSearchQuadratic_LS - Performs a quadratic line search. 317 318 Collective on SNES and Vec 319 320 Input Parameters: 321 + snes - the SNES context 322 . lsctx - optional context for line search (not used here) 323 . x - current iterate 324 . f - residual evaluated at x 325 . y - search direction 326 . fnorm - 2-norm of f 327 - xnorm - norm of x if known, otherwise 0 328 329 Output Parameters: 330 + g - residual evaluated at new iterate w 331 . w - new iterate (x + lambda*y) 332 . gnorm - 2-norm of g 333 . ynorm - 2-norm of search length 334 - flag - PETSC_TRUE if line search succeeds; PETSC_FALSE on failure. 335 336 Options Database Keys: 337 + -snes_ls quadratic - Activates SNESLineSearchQuadratic() 338 . -snes_ls_alpha <alpha> - Sets alpha 339 . -snes_ls_maxstep <maxstep> - Sets the maximum stepsize the line search will use (if the 2-norm(y) > maxstep then scale y to be y = (maxstep/2-norm(y)) *y) 340 - -snes_ls_minlambda <minlambda> - Sets the minimum lambda the line search will use minlambda/ max_i ( y[i]/x[i] ) 341 342 Notes: 343 Use SNESLineSearchSet() to set this routine within the SNESLS method. 344 345 Level: advanced 346 347 .keywords: SNES, nonlinear, quadratic, line search 348 349 .seealso: SNESLineSearchCubic(), SNESLineSearchNo(), SNESLineSearchSet(), SNESLineSearchNoNorms() 350 @*/ 351 PetscErrorCode SNESLineSearchQuadratic_LS(SNES snes,void *lsctx,Vec x,Vec f,Vec y,PetscReal fnorm,PetscReal xnorm,Vec g,Vec w,PetscReal *ynorm,PetscReal *gnorm,PetscBool *flag) 352 { 353 /* 354 Note that for line search purposes we work with with the related 355 minimization problem: 356 min z(x): R^n -> R, 357 where z(x) = .5 * fnorm*fnorm,and fnorm = || f ||_2. 358 */ 359 PetscReal initslope,minlambda,lambda,lambdatemp,rellength; 360 #if defined(PETSC_USE_COMPLEX) 361 PetscScalar cinitslope; 362 #endif 363 PetscErrorCode ierr; 364 PetscInt count; 365 PetscBool changed_w = PETSC_FALSE,changed_y = PETSC_FALSE; 366 367 PetscFunctionBegin; 368 ierr = PetscLogEventBegin(SNES_LineSearch,snes,x,f,g);CHKERRQ(ierr); 369 *flag = PETSC_TRUE; 370 371 ierr = VecNorm(y,NORM_2,ynorm);CHKERRQ(ierr); 372 if (*ynorm == 0.0) { 373 if (snes->ls_monitor) { 374 ierr = PetscViewerASCIIAddTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 375 ierr = PetscViewerASCIIPrintf(snes->ls_monitor,"Line search: Direction and size is 0\n");CHKERRQ(ierr); 376 ierr = PetscViewerASCIISubtractTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 377 } 378 *gnorm = fnorm; 379 ierr = VecCopy(x,w);CHKERRQ(ierr); 380 ierr = VecCopy(f,g);CHKERRQ(ierr); 381 *flag = PETSC_FALSE; 382 goto theend2; 383 } 384 if (*ynorm > snes->maxstep) { /* Step too big, so scale back */ 385 ierr = VecScale(y,snes->maxstep/(*ynorm));CHKERRQ(ierr); 386 *ynorm = snes->maxstep; 387 } 388 ierr = VecMaxPointwiseDivide(y,x,&rellength);CHKERRQ(ierr); 389 minlambda = snes->steptol/rellength; 390 ierr = MatMult(snes->jacobian,y,w);CHKERRQ(ierr); 391 #if defined(PETSC_USE_COMPLEX) 392 ierr = VecDot(f,w,&cinitslope);CHKERRQ(ierr); 393 initslope = PetscRealPart(cinitslope); 394 #else 395 ierr = VecDot(f,w,&initslope);CHKERRQ(ierr); 396 #endif 397 if (initslope > 0.0) initslope = -initslope; 398 if (initslope == 0.0) initslope = -1.0; 399 ierr = PetscInfo1(snes,"Initslope %14.12e \n",(double)initslope);CHKERRQ(ierr); 400 401 ierr = VecWAXPY(w,-1.0,y,x);CHKERRQ(ierr); 402 if (snes->nfuncs >= snes->max_funcs) { 403 ierr = PetscInfo(snes,"Exceeded maximum function evaluations, while checking full step length!\n");CHKERRQ(ierr); 404 *flag = PETSC_FALSE; 405 snes->reason = SNES_DIVERGED_FUNCTION_COUNT; 406 goto theend2; 407 } 408 ierr = SNESComputeFunction(snes,w,g);CHKERRQ(ierr); 409 if (snes->domainerror) { 410 ierr = PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);CHKERRQ(ierr); 411 PetscFunctionReturn(0); 412 } 413 ierr = VecNorm(g,NORM_2,gnorm);CHKERRQ(ierr); 414 if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number"); 415 if (.5*(*gnorm)*(*gnorm) <= .5*fnorm*fnorm + snes->ls_alpha*initslope) { /* Sufficient reduction */ 416 if (snes->ls_monitor) { 417 ierr = PetscViewerASCIIAddTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 418 ierr = PetscViewerASCIIPrintf(snes->ls_monitor," Line Search: Using full step\n");CHKERRQ(ierr); 419 ierr = PetscViewerASCIISubtractTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 420 } 421 goto theend2; 422 } 423 424 /* Fit points with quadratic */ 425 lambda = 1.0; 426 count = 1; 427 while (PETSC_TRUE) { 428 if (lambda <= minlambda) { /* bad luck; use full step */ 429 if (snes->ls_monitor) { 430 ierr = PetscViewerASCIIAddTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 431 ierr = PetscViewerASCIIPrintf(snes->ls_monitor,"Line search: Unable to find good step length! %D \n",count);CHKERRQ(ierr); 432 ierr = PetscViewerASCIIPrintf(snes->ls_monitor,"Line search: fnorm=%14.12e, gnorm=%14.12e, ynorm=%14.12e, lambda=%14.12e, initial slope=%14.12e\n",(double)fnorm,(double)(*gnorm),(double)(*ynorm),(double)lambda,(double)initslope);CHKERRQ(ierr); 433 ierr = PetscViewerASCIISubtractTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 434 } 435 ierr = VecCopy(x,w);CHKERRQ(ierr); 436 *flag = PETSC_FALSE; 437 break; 438 } 439 lambdatemp = -initslope/((*gnorm)*(*gnorm) - fnorm*fnorm - 2.0*initslope); 440 if (lambdatemp > .5*lambda) lambdatemp = .5*lambda; 441 if (lambdatemp <= .1*lambda) lambda = .1*lambda; 442 else lambda = lambdatemp; 443 444 ierr = VecWAXPY(w,-lambda,y,x);CHKERRQ(ierr); 445 if (snes->nfuncs >= snes->max_funcs) { 446 ierr = PetscInfo1(snes,"Exceeded maximum function evaluations, while looking for good step length! %D \n",count);CHKERRQ(ierr); 447 ierr = PetscInfo5(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lambda=%18.16e, initial slope=%18.16e\n",(double)fnorm,(double)*gnorm,(double)*ynorm,(double)lambda,(double)initslope);CHKERRQ(ierr); 448 *flag = PETSC_FALSE; 449 snes->reason = SNES_DIVERGED_FUNCTION_COUNT; 450 break; 451 } 452 ierr = SNESComputeFunction(snes,w,g);CHKERRQ(ierr); 453 if (snes->domainerror) { 454 ierr = PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);CHKERRQ(ierr); 455 PetscFunctionReturn(0); 456 } 457 ierr = VecNorm(g,NORM_2,gnorm);CHKERRQ(ierr); 458 if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number"); 459 if (.5*(*gnorm)*(*gnorm) < .5*fnorm*fnorm + lambda*snes->ls_alpha*initslope) { /* sufficient reduction */ 460 if (snes->ls_monitor) { 461 ierr = PetscViewerASCIIAddTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 462 ierr = PetscViewerASCIIPrintf(snes->ls_monitor," Line Search: Quadratically determined step, lambda=%14.12e\n",(double)lambda);CHKERRQ(ierr); 463 ierr = PetscViewerASCIISubtractTab(snes->ls_monitor,((PetscObject)snes)->tablevel);CHKERRQ(ierr); 464 } 465 break; 466 } 467 count++; 468 } 469 theend2: 470 /* Optional user-defined check for line search step validity */ 471 if (snes->ops->postcheckstep) { 472 ierr = (*snes->ops->postcheckstep)(snes,x,y,w,snes->postcheck,&changed_y,&changed_w);CHKERRQ(ierr); 473 if (changed_y) { 474 ierr = VecWAXPY(w,-1.0,y,x);CHKERRQ(ierr); 475 } 476 if (changed_y || changed_w) { /* recompute the function if the step has changed */ 477 ierr = SNESComputeFunction(snes,w,g); 478 if (snes->domainerror) { 479 ierr = PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);CHKERRQ(ierr); 480 PetscFunctionReturn(0); 481 } 482 ierr = VecNormBegin(g,NORM_2,gnorm);CHKERRQ(ierr); 483 ierr = VecNormBegin(y,NORM_2,ynorm);CHKERRQ(ierr); 484 ierr = VecNormEnd(g,NORM_2,gnorm);CHKERRQ(ierr); 485 ierr = VecNormEnd(y,NORM_2,ynorm);CHKERRQ(ierr); 486 if (PetscIsInfOrNanReal(*gnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number"); 487 } 488 } 489 ierr = PetscLogEventEnd(SNES_LineSearch,snes,x,f,g);CHKERRQ(ierr); 490 PetscFunctionReturn(0); 491 } 492 493 494 EXTERN_C_BEGIN 495 #undef __FUNCT__ 496 #define __FUNCT__ "SNESLineSearchSetType_LS" 497 PetscErrorCode SNESLineSearchSetType_LS(SNES snes, SNESLineSearchType type) 498 { 499 PetscErrorCode ierr; 500 PetscFunctionBegin; 501 502 switch (type) { 503 case SNES_LS_BASIC: 504 ierr = SNESLineSearchSet(snes,SNESLineSearchNo,PETSC_NULL);CHKERRQ(ierr); 505 break; 506 case SNES_LS_BASIC_NONORMS: 507 ierr = SNESLineSearchSet(snes,SNESLineSearchNoNorms,PETSC_NULL);CHKERRQ(ierr); 508 break; 509 case SNES_LS_QUADRATIC: 510 ierr = SNESLineSearchSet(snes,SNESLineSearchQuadratic_LS,PETSC_NULL);CHKERRQ(ierr); 511 break; 512 case SNES_LS_CUBIC: 513 ierr = SNESLineSearchSet(snes,SNESLineSearchCubic_LS,PETSC_NULL);CHKERRQ(ierr); 514 break; 515 default: 516 SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP,"Unknown line search type."); 517 break; 518 } 519 snes->ls_type = type; 520 PetscFunctionReturn(0); 521 } 522 EXTERN_C_END 523 524 525 /* -------------------------------------------------------------------- 526 527 This file implements a truncated Newton method with a line search, 528 for solving a system of nonlinear equations, using the KSP, Vec, 529 and Mat interfaces for linear solvers, vectors, and matrices, 530 respectively. 531 532 The following basic routines are required for each nonlinear solver: 533 SNESCreate_XXX() - Creates a nonlinear solver context 534 SNESSetFromOptions_XXX() - Sets runtime options 535 SNESSolve_XXX() - Solves the nonlinear system 536 SNESDestroy_XXX() - Destroys the nonlinear solver context 537 The suffix "_XXX" denotes a particular implementation, in this case 538 we use _LS (e.g., SNESCreate_LS, SNESSolve_LS) for solving 539 systems of nonlinear equations with a line search (LS) method. 540 These routines are actually called via the common user interface 541 routines SNESCreate(), SNESSetFromOptions(), SNESSolve(), and 542 SNESDestroy(), so the application code interface remains identical 543 for all nonlinear solvers. 544 545 Another key routine is: 546 SNESSetUp_XXX() - Prepares for the use of a nonlinear solver 547 by setting data structures and options. The interface routine SNESSetUp() 548 is not usually called directly by the user, but instead is called by 549 SNESSolve() if necessary. 550 551 Additional basic routines are: 552 SNESView_XXX() - Prints details of runtime options that 553 have actually been used. 554 These are called by application codes via the interface routines 555 SNESView(). 556 557 The various types of solvers (preconditioners, Krylov subspace methods, 558 nonlinear solvers, timesteppers) are all organized similarly, so the 559 above description applies to these categories also. 560 561 -------------------------------------------------------------------- */ 562 /* 563 SNESSolve_LS - Solves a nonlinear system with a truncated Newton 564 method with a line search. 565 566 Input Parameters: 567 . snes - the SNES context 568 569 Output Parameter: 570 . outits - number of iterations until termination 571 572 Application Interface Routine: SNESSolve() 573 574 Notes: 575 This implements essentially a truncated Newton method with a 576 line search. By default a cubic backtracking line search 577 is employed, as described in the text "Numerical Methods for 578 Unconstrained Optimization and Nonlinear Equations" by Dennis 579 and Schnabel. 580 */ 581 #undef __FUNCT__ 582 #define __FUNCT__ "SNESSolve_LS" 583 PetscErrorCode SNESSolve_LS(SNES snes) 584 { 585 PetscErrorCode ierr; 586 PetscInt maxits,i,lits; 587 PetscBool lssucceed; 588 MatStructure flg = DIFFERENT_NONZERO_PATTERN; 589 PetscReal fnorm,gnorm,xnorm=0,ynorm; 590 Vec Y,X,F,G,W; 591 KSPConvergedReason kspreason; 592 593 PetscFunctionBegin; 594 snes->numFailures = 0; 595 snes->numLinearSolveFailures = 0; 596 snes->reason = SNES_CONVERGED_ITERATING; 597 598 maxits = snes->max_its; /* maximum number of iterations */ 599 X = snes->vec_sol; /* solution vector */ 600 F = snes->vec_func; /* residual vector */ 601 Y = snes->work[0]; /* work vectors */ 602 G = snes->work[1]; 603 W = snes->work[2]; 604 605 ierr = PetscObjectTakeAccess(snes);CHKERRQ(ierr); 606 snes->iter = 0; 607 snes->norm = 0.0; 608 ierr = PetscObjectGrantAccess(snes);CHKERRQ(ierr); 609 ierr = SNESComputeFunction(snes,X,F);CHKERRQ(ierr); 610 if (snes->domainerror) { 611 snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN; 612 PetscFunctionReturn(0); 613 } 614 ierr = VecNormBegin(F,NORM_2,&fnorm);CHKERRQ(ierr); /* fnorm <- ||F|| */ 615 ierr = VecNormBegin(X,NORM_2,&xnorm);CHKERRQ(ierr); /* xnorm <- ||x|| */ 616 ierr = VecNormEnd(F,NORM_2,&fnorm);CHKERRQ(ierr); 617 ierr = VecNormEnd(X,NORM_2,&xnorm);CHKERRQ(ierr); 618 if (PetscIsInfOrNanReal(fnorm)) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_FP,"User provided compute function generated a Not-a-Number"); 619 ierr = PetscObjectTakeAccess(snes);CHKERRQ(ierr); 620 snes->norm = fnorm; 621 ierr = PetscObjectGrantAccess(snes);CHKERRQ(ierr); 622 SNESLogConvHistory(snes,fnorm,0); 623 ierr = SNESMonitor(snes,0,fnorm);CHKERRQ(ierr); 624 625 /* set parameter for default relative tolerance convergence test */ 626 snes->ttol = fnorm*snes->rtol; 627 /* test convergence */ 628 ierr = (*snes->ops->converged)(snes,0,0.0,0.0,fnorm,&snes->reason,snes->cnvP);CHKERRQ(ierr); 629 if (snes->reason) PetscFunctionReturn(0); 630 631 for (i=0; i<maxits; i++) { 632 633 /* Call general purpose update function */ 634 if (snes->ops->update) { 635 ierr = (*snes->ops->update)(snes, snes->iter);CHKERRQ(ierr); 636 } 637 638 /* Solve J Y = F, where J is Jacobian matrix */ 639 ierr = SNESComputeJacobian(snes,X,&snes->jacobian,&snes->jacobian_pre,&flg);CHKERRQ(ierr); 640 ierr = KSPSetOperators(snes->ksp,snes->jacobian,snes->jacobian_pre,flg);CHKERRQ(ierr); 641 ierr = SNES_KSPSolve(snes,snes->ksp,F,Y);CHKERRQ(ierr); 642 ierr = KSPGetConvergedReason(snes->ksp,&kspreason);CHKERRQ(ierr); 643 if (kspreason < 0) { 644 if (++snes->numLinearSolveFailures >= snes->maxLinearSolveFailures) { 645 ierr = PetscInfo2(snes,"iter=%D, number linear solve failures %D greater than current SNES allowed, stopping solve\n",snes->iter,snes->numLinearSolveFailures);CHKERRQ(ierr); 646 snes->reason = SNES_DIVERGED_LINEAR_SOLVE; 647 break; 648 } 649 } 650 ierr = KSPGetIterationNumber(snes->ksp,&lits);CHKERRQ(ierr); 651 snes->linear_its += lits; 652 ierr = PetscInfo2(snes,"iter=%D, linear solve iterations=%D\n",snes->iter,lits);CHKERRQ(ierr); 653 654 if (snes->ops->precheckstep) { 655 PetscBool changed_y = PETSC_FALSE; 656 ierr = (*snes->ops->precheckstep)(snes,X,Y,snes->precheck,&changed_y);CHKERRQ(ierr); 657 } 658 659 if (PetscLogPrintInfo){ 660 ierr = SNESLSCheckResidual_Private(snes,snes->jacobian,F,Y,G,W);CHKERRQ(ierr); 661 } 662 663 /* Compute a (scaled) negative update in the line search routine: 664 Y <- X - lambda*Y 665 and evaluate G = function(Y) (depends on the line search). 666 */ 667 ierr = VecCopy(Y,snes->vec_sol_update);CHKERRQ(ierr); 668 ynorm = 1; gnorm = fnorm; 669 ierr = (*snes->ops->linesearch)(snes,snes->lsP,X,F,Y,fnorm,xnorm,G,W,&ynorm,&gnorm,&lssucceed);CHKERRQ(ierr); 670 ierr = PetscInfo4(snes,"fnorm=%18.16e, gnorm=%18.16e, ynorm=%18.16e, lssucceed=%d\n",(double)fnorm,(double)gnorm,(double)ynorm,(int)lssucceed);CHKERRQ(ierr); 671 if (snes->reason == SNES_DIVERGED_FUNCTION_COUNT) break; 672 if (snes->domainerror) { 673 snes->reason = SNES_DIVERGED_FUNCTION_DOMAIN; 674 PetscFunctionReturn(0); 675 } 676 if (!lssucceed) { 677 if (++snes->numFailures >= snes->maxFailures) { 678 PetscBool ismin; 679 snes->reason = SNES_DIVERGED_LINE_SEARCH; 680 ierr = SNESLSCheckLocalMin_Private(snes,snes->jacobian,G,W,gnorm,&ismin);CHKERRQ(ierr); 681 if (ismin) snes->reason = SNES_DIVERGED_LOCAL_MIN; 682 break; 683 } 684 } 685 /* Update function and solution vectors */ 686 fnorm = gnorm; 687 ierr = VecCopy(G,F);CHKERRQ(ierr); 688 ierr = VecCopy(W,X);CHKERRQ(ierr); 689 /* Monitor convergence */ 690 ierr = PetscObjectTakeAccess(snes);CHKERRQ(ierr); 691 snes->iter = i+1; 692 snes->norm = fnorm; 693 ierr = PetscObjectGrantAccess(snes);CHKERRQ(ierr); 694 SNESLogConvHistory(snes,snes->norm,lits); 695 ierr = SNESMonitor(snes,snes->iter,snes->norm);CHKERRQ(ierr); 696 /* Test for convergence, xnorm = || X || */ 697 if (snes->ops->converged != SNESSkipConverged) { ierr = VecNorm(X,NORM_2,&xnorm);CHKERRQ(ierr); } 698 ierr = (*snes->ops->converged)(snes,snes->iter,xnorm,ynorm,fnorm,&snes->reason,snes->cnvP);CHKERRQ(ierr); 699 if (snes->reason) break; 700 } 701 if (i == maxits) { 702 ierr = PetscInfo1(snes,"Maximum number of iterations has been reached: %D\n",maxits);CHKERRQ(ierr); 703 if(!snes->reason) snes->reason = SNES_DIVERGED_MAX_IT; 704 } 705 PetscFunctionReturn(0); 706 } 707 /* -------------------------------------------------------------------------- */ 708 /* 709 SNESSetUp_LS - Sets up the internal data structures for the later use 710 of the SNESLS nonlinear solver. 711 712 Input Parameter: 713 . snes - the SNES context 714 . x - the solution vector 715 716 Application Interface Routine: SNESSetUp() 717 718 Notes: 719 For basic use of the SNES solvers, the user need not explicitly call 720 SNESSetUp(), since these actions will automatically occur during 721 the call to SNESSolve(). 722 */ 723 #undef __FUNCT__ 724 #define __FUNCT__ "SNESSetUp_LS" 725 PetscErrorCode SNESSetUp_LS(SNES snes) 726 { 727 PetscErrorCode ierr; 728 729 PetscFunctionBegin; 730 ierr = SNESDefaultGetWork(snes,3);CHKERRQ(ierr); 731 PetscFunctionReturn(0); 732 } 733 /* -------------------------------------------------------------------------- */ 734 735 #undef __FUNCT__ 736 #define __FUNCT__ "SNESReset_LS" 737 PetscErrorCode SNESReset_LS(SNES snes) 738 { 739 PetscErrorCode ierr; 740 741 PetscFunctionBegin; 742 if (snes->work) {ierr = VecDestroyVecs(snes->nwork,&snes->work);CHKERRQ(ierr);} 743 PetscFunctionReturn(0); 744 } 745 746 /* 747 SNESDestroy_LS - Destroys the private SNES_LS context that was created 748 with SNESCreate_LS(). 749 750 Input Parameter: 751 . snes - the SNES context 752 753 Application Interface Routine: SNESDestroy() 754 */ 755 #undef __FUNCT__ 756 #define __FUNCT__ "SNESDestroy_LS" 757 PetscErrorCode SNESDestroy_LS(SNES snes) 758 { 759 PetscErrorCode ierr; 760 761 PetscFunctionBegin; 762 ierr = SNESReset_LS(snes);CHKERRQ(ierr); 763 ierr = PetscFree(snes->data);CHKERRQ(ierr); 764 765 PetscFunctionReturn(0); 766 } 767 /* -------------------------------------------------------------------------- */ 768 769 /* 770 SNESView_LS - Prints info from the SNESLS data structure. 771 772 Input Parameters: 773 . SNES - the SNES context 774 . viewer - visualization context 775 776 Application Interface Routine: SNESView() 777 */ 778 #undef __FUNCT__ 779 #define __FUNCT__ "SNESView_LS" 780 static PetscErrorCode SNESView_LS(SNES snes,PetscViewer viewer) 781 { 782 const char *cstr; 783 PetscErrorCode ierr; 784 PetscBool iascii; 785 786 PetscFunctionBegin; 787 ierr = PetscTypeCompare((PetscObject)viewer,PETSCVIEWERASCII,&iascii);CHKERRQ(ierr); 788 if (iascii) { 789 cstr = SNESLineSearchTypeName(snes->ls_type); 790 ierr = PetscViewerASCIIPrintf(viewer," line search variant: %s\n",cstr);CHKERRQ(ierr); 791 ierr = PetscViewerASCIIPrintf(viewer," alpha=%14.12e, maxstep=%14.12e, minlambda=%14.12e\n",(double)snes->ls_alpha,(double)snes->maxstep,(double)snes->steptol);CHKERRQ(ierr); 792 ierr = PetscViewerASCIIPrintf(viewer," damping factor=%14.12e\n",(double)snes->damping);CHKERRQ(ierr); 793 } 794 PetscFunctionReturn(0); 795 } 796 797 /* -------------------------------------------------------------------------- */ 798 /* 799 SNESSetFromOptions_LS - Sets various parameters for the SNESLS method. 800 801 Input Parameter: 802 . snes - the SNES context 803 804 Application Interface Routine: SNESSetFromOptions() 805 */ 806 #undef __FUNCT__ 807 #define __FUNCT__ "SNESSetFromOptions_LS" 808 static PetscErrorCode SNESSetFromOptions_LS(SNES snes) 809 { 810 PetscErrorCode ierr; 811 812 PetscFunctionBegin; 813 ierr = PetscOptionsHead("SNES Line search options");CHKERRQ(ierr); 814 ierr = PetscOptionsTail();CHKERRQ(ierr); 815 PetscFunctionReturn(0); 816 } 817 818 /* -------------------------------------------------------------------------- */ 819 /*MC 820 SNESLS - Newton based nonlinear solver that uses a line search 821 822 Options Database: 823 + -snes_ls [cubic,quadratic,basic,basicnonorms] - Selects line search 824 . -snes_ls_alpha <alpha> - Sets alpha used in determining if reduction in function norm is sufficient 825 . -snes_ls_maxstep <maxstep> - Sets the maximum stepsize the line search will use (if the 2-norm(y) > maxstep then scale y to be y = (maxstep/2-norm(y)) *y) 826 . -snes_ls_minlambda <minlambda> - Sets the minimum lambda the line search will use minlambda / max_i ( y[i]/x[i] ) 827 . -snes_ls_monitor - print information about progress of line searches 828 - -snes_ls_damping - damping factor used if -snes_ls is basic or basicnonorms 829 830 831 Notes: This is the default nonlinear solver in SNES 832 833 Level: beginner 834 835 .seealso: SNESCreate(), SNES, SNESSetType(), SNESTR, SNESLineSearchSet(), 836 SNESLineSearchSetPostCheck(), SNESLineSearchNo(), SNESLineSearchCubic(), SNESLineSearchQuadratic(), 837 SNESLineSearchSet(), SNESLineSearchNoNorms(), SNESLineSearchSetPreCheck(), SNESLineSearchSetParams(), SNESLineSearchGetParams() 838 839 M*/ 840 EXTERN_C_BEGIN 841 #undef __FUNCT__ 842 #define __FUNCT__ "SNESCreate_LS" 843 PetscErrorCode SNESCreate_LS(SNES snes) 844 { 845 PetscErrorCode ierr; 846 SNES_LS *neP; 847 848 PetscFunctionBegin; 849 snes->ops->setup = SNESSetUp_LS; 850 snes->ops->solve = SNESSolve_LS; 851 snes->ops->destroy = SNESDestroy_LS; 852 snes->ops->setfromoptions = SNESSetFromOptions_LS; 853 snes->ops->view = SNESView_LS; 854 snes->ops->reset = SNESReset_LS; 855 856 snes->usesksp = PETSC_TRUE; 857 snes->usespc = PETSC_FALSE; 858 ierr = PetscNewLog(snes,SNES_LS,&neP);CHKERRQ(ierr); 859 snes->data = (void*)neP; 860 ierr = PetscObjectComposeFunctionDynamic((PetscObject)snes,"SNESLineSearchSetType_C","SNESLineSearchSetType_LS",SNESLineSearchSetType_LS);CHKERRQ(ierr); 861 ierr = SNESLineSearchSetType(snes, SNES_LS_CUBIC);CHKERRQ(ierr); 862 PetscFunctionReturn(0); 863 } 864 EXTERN_C_END 865