static PetscErrorCode SNESLineSearchApply_CP(SNESLineSearch linesearch) { PetscBool changed_y, changed_w; PetscErrorCode ierr; Vec X, Y, F, W; SNES snes; PetscReal xnorm, ynorm, gnorm, steptol, atol, rtol, ltol, maxstep; PetscReal lambda, lambda_old, lambda_update, delLambda; PetscScalar fty, fty_init, fty_old, fty_mid1, fty_mid2, s; PetscInt i, max_its; PetscViewer monitor; PetscFunctionBegin; ierr = SNESLineSearchGetVecs(linesearch, &X, &F, &Y, &W, NULL);CHKERRQ(ierr); ierr = SNESLineSearchGetNorms(linesearch, &xnorm, &gnorm, &ynorm);CHKERRQ(ierr); ierr = SNESLineSearchGetSNES(linesearch, &snes);CHKERRQ(ierr); ierr = SNESLineSearchGetLambda(linesearch, &lambda);CHKERRQ(ierr); ierr = SNESLineSearchGetTolerances(linesearch, &steptol, &maxstep, &rtol, &atol, <ol, &max_its);CHKERRQ(ierr); ierr = SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_SUCCEEDED);CHKERRQ(ierr); ierr = SNESLineSearchGetDefaultMonitor(linesearch, &monitor);CHKERRQ(ierr); /* precheck */ ierr = SNESLineSearchPreCheck(linesearch,X,Y,&changed_y);CHKERRQ(ierr); lambda_old = 0.0; ierr = VecDot(F,Y,&fty_old);CHKERRQ(ierr); fty_init = fty_old; for (i = 0; i < max_its; i++) { /* compute the norm at lambda */ ierr = VecCopy(X, W);CHKERRQ(ierr); ierr = VecAXPY(W, -lambda, Y);CHKERRQ(ierr); if (linesearch->ops->viproject) { ierr = (*linesearch->ops->viproject)(snes, W);CHKERRQ(ierr); } ierr = (*linesearch->ops->snesfunc)(snes,W,F);CHKERRQ(ierr); ierr = VecDot(F,Y,&fty);CHKERRQ(ierr); delLambda = lambda - lambda_old; /* check for convergence */ if (PetscAbsReal(delLambda) < steptol*lambda) break; if (PetscAbsScalar(fty) / PetscAbsScalar(fty_init) < rtol) break; if (PetscAbsScalar(fty) < atol && i > 0) break; if (monitor) { ierr = PetscViewerASCIIAddTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); ierr = PetscViewerASCIIPrintf(monitor," Line search: lambdas = [%g, %g], ftys = [%g, %g]\n",(double)lambda, (double)lambda_old, (double)PetscRealPart(fty), (double)PetscRealPart(fty_old));CHKERRQ(ierr); ierr = PetscViewerASCIISubtractTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); } /* compute the search direction */ if (linesearch->order == SNES_LINESEARCH_ORDER_LINEAR) { s = (fty - fty_old) / delLambda; } else if (linesearch->order == SNES_LINESEARCH_ORDER_QUADRATIC) { ierr = VecCopy(X, W);CHKERRQ(ierr); ierr = VecAXPY(W, -0.5*(lambda + lambda_old), Y);CHKERRQ(ierr); if (linesearch->ops->viproject) { ierr = (*linesearch->ops->viproject)(snes, W);CHKERRQ(ierr); } ierr = (*linesearch->ops->snesfunc)(snes,W,F);CHKERRQ(ierr); ierr = VecDot(F, Y, &fty_mid1);CHKERRQ(ierr); s = (3.*fty - 4.*fty_mid1 + fty_old) / delLambda; } else { ierr = VecCopy(X, W);CHKERRQ(ierr); ierr = VecAXPY(W, -0.5*(lambda + lambda_old), Y);CHKERRQ(ierr); if (linesearch->ops->viproject) { ierr = (*linesearch->ops->viproject)(snes, W);CHKERRQ(ierr); } ierr = (*linesearch->ops->snesfunc)(snes,W,F);CHKERRQ(ierr); ierr = VecDot(F, Y, &fty_mid1);CHKERRQ(ierr); ierr = VecCopy(X, W);CHKERRQ(ierr); ierr = VecAXPY(W, -(lambda + 0.5*(lambda - lambda_old)), Y);CHKERRQ(ierr); if (linesearch->ops->viproject) { ierr = (*linesearch->ops->viproject)(snes, W);CHKERRQ(ierr); } ierr = (*linesearch->ops->snesfunc)(snes, W, F);CHKERRQ(ierr); ierr = VecDot(F, Y, &fty_mid2);CHKERRQ(ierr); s = (2.*fty_mid2 + 3.*fty - 6.*fty_mid1 + fty_old) / (3.*delLambda); } /* if the solve is going in the wrong direction, fix it */ if (PetscRealPart(s) > 0.) s = -s; lambda_update = lambda - PetscRealPart(fty / s); /* switch directions if we stepped out of bounds */ if (lambda_update < steptol) lambda_update = lambda + PetscRealPart(fty / s); if (PetscIsInfOrNanReal(lambda_update)) break; if (lambda_update > maxstep) break; /* compute the new state of the line search */ lambda_old = lambda; lambda = lambda_update; fty_old = fty; } /* construct the solution */ ierr = VecCopy(X, W);CHKERRQ(ierr); ierr = VecAXPY(W, -lambda, Y);CHKERRQ(ierr); if (linesearch->ops->viproject) { ierr = (*linesearch->ops->viproject)(snes, W);CHKERRQ(ierr); } /* postcheck */ ierr = SNESLineSearchPostCheck(linesearch,X,Y,W,&changed_y,&changed_w);CHKERRQ(ierr); if (changed_y) { ierr = VecAXPY(X, -lambda, Y);CHKERRQ(ierr); if (linesearch->ops->viproject) { ierr = (*linesearch->ops->viproject)(snes, X);CHKERRQ(ierr); } } else { ierr = VecCopy(W, X);CHKERRQ(ierr); } ierr = (*linesearch->ops->snesfunc)(snes,X,F);CHKERRQ(ierr); ierr = SNESLineSearchComputeNorms(linesearch);CHKERRQ(ierr); ierr = SNESLineSearchGetNorms(linesearch, &xnorm, &gnorm, &ynorm);CHKERRQ(ierr); ierr = SNESLineSearchSetLambda(linesearch, lambda);CHKERRQ(ierr); if (monitor) { ierr = PetscViewerASCIIAddTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); ierr = PetscViewerASCIIPrintf(monitor," Line search terminated: lambda = %g, fnorms = %g\n", (double)lambda, (double)gnorm);CHKERRQ(ierr); ierr = PetscViewerASCIISubtractTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); } if (lambda <= steptol) { ierr = SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_REDUCT);CHKERRQ(ierr); } PetscFunctionReturn(0); }
static PetscErrorCode SNESLineSearchApply_Basic(SNESLineSearch linesearch) { PetscBool changed_y, changed_w; PetscErrorCode ierr; Vec X, F, Y, W; SNES snes; PetscReal gnorm, xnorm, ynorm, lambda; PetscBool domainerror; PetscFunctionBegin; ierr = SNESLineSearchGetVecs(linesearch, &X, &F, &Y, &W, NULL);CHKERRQ(ierr); ierr = SNESLineSearchGetNorms(linesearch, &xnorm, &gnorm, &ynorm);CHKERRQ(ierr); ierr = SNESLineSearchGetLambda(linesearch, &lambda);CHKERRQ(ierr); ierr = SNESLineSearchGetSNES(linesearch, &snes);CHKERRQ(ierr); ierr = SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_SUCCEEDED);CHKERRQ(ierr); /* precheck */ ierr = SNESLineSearchPreCheck(linesearch,X,Y,&changed_y);CHKERRQ(ierr); /* update */ ierr = VecWAXPY(W,-lambda,Y,X);CHKERRQ(ierr); if (linesearch->ops->viproject) { ierr = (*linesearch->ops->viproject)(snes, W);CHKERRQ(ierr); } /* postcheck */ ierr = SNESLineSearchPostCheck(linesearch,X,Y,W,&changed_y,&changed_w);CHKERRQ(ierr); if (changed_y) { ierr = VecWAXPY(W,-lambda,Y,X);CHKERRQ(ierr); if (linesearch->ops->viproject) { ierr = (*linesearch->ops->viproject)(snes, W);CHKERRQ(ierr); } } if (linesearch->norms || snes->iter < snes->max_its-1) { ierr = (*linesearch->ops->snesfunc)(snes,W,F);CHKERRQ(ierr); ierr = SNESGetFunctionDomainError(snes, &domainerror);CHKERRQ(ierr); if (domainerror) { ierr = SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_DOMAIN);CHKERRQ(ierr); PetscFunctionReturn(0); } } if (linesearch->norms) { if (!linesearch->ops->vinorm) VecNormBegin(F, NORM_2, &linesearch->fnorm); ierr = VecNormBegin(Y, NORM_2, &linesearch->ynorm);CHKERRQ(ierr); ierr = VecNormBegin(W, NORM_2, &linesearch->xnorm);CHKERRQ(ierr); if (!linesearch->ops->vinorm) VecNormEnd(F, NORM_2, &linesearch->fnorm); ierr = VecNormEnd(Y, NORM_2, &linesearch->ynorm);CHKERRQ(ierr); ierr = VecNormEnd(W, NORM_2, &linesearch->xnorm);CHKERRQ(ierr); if (linesearch->ops->vinorm) { linesearch->fnorm = gnorm; ierr = (*linesearch->ops->vinorm)(snes, F, W, &linesearch->fnorm);CHKERRQ(ierr); } else { ierr = VecNorm(F,NORM_2,&linesearch->fnorm);CHKERRQ(ierr); } } /* copy the solution over */ ierr = VecCopy(W, X);CHKERRQ(ierr); PetscFunctionReturn(0); }
static PetscErrorCode SNESLineSearchApply_NLEQERR(SNESLineSearch linesearch) { PetscBool changed_y,changed_w; PetscErrorCode ierr; Vec X,F,Y,W,G; SNES snes; PetscReal fnorm, xnorm, ynorm, gnorm, wnorm; PetscReal lambda, minlambda, stol; PetscViewer monitor; PetscInt max_its, count, snes_iteration; PetscReal theta, mudash, lambdadash; SNESLineSearch_NLEQERR *nleqerr = (SNESLineSearch_NLEQERR*)linesearch->data; KSPConvergedReason kspreason; PetscFunctionBegin; ierr = PetscCitationsRegister(NLEQERR_citation, &NLEQERR_cited);CHKERRQ(ierr); ierr = SNESLineSearchGetVecs(linesearch, &X, &F, &Y, &W, &G);CHKERRQ(ierr); ierr = SNESLineSearchGetNorms(linesearch, &xnorm, &fnorm, &ynorm);CHKERRQ(ierr); ierr = SNESLineSearchGetLambda(linesearch, &lambda);CHKERRQ(ierr); ierr = SNESLineSearchGetSNES(linesearch, &snes);CHKERRQ(ierr); ierr = SNESLineSearchGetDefaultMonitor(linesearch, &monitor);CHKERRQ(ierr); ierr = SNESLineSearchGetTolerances(linesearch,&minlambda,NULL,NULL,NULL,NULL,&max_its);CHKERRQ(ierr); ierr = SNESGetTolerances(snes,NULL,NULL,&stol,NULL,NULL);CHKERRQ(ierr); /* reset the state of the Lipschitz estimates */ ierr = SNESGetIterationNumber(snes, &snes_iteration);CHKERRQ(ierr); if (!snes_iteration) { ierr = SNESLineSearchReset_NLEQERR(linesearch);CHKERRQ(ierr); } /* precheck */ ierr = SNESLineSearchPreCheck(linesearch,X,Y,&changed_y);CHKERRQ(ierr); ierr = SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_SUCCEEDED);CHKERRQ(ierr); ierr = VecNormBegin(Y, NORM_2, &ynorm);CHKERRQ(ierr); ierr = VecNormBegin(X, NORM_2, &xnorm);CHKERRQ(ierr); ierr = VecNormEnd(Y, NORM_2, &ynorm);CHKERRQ(ierr); ierr = VecNormEnd(X, NORM_2, &xnorm);CHKERRQ(ierr); /* Note: Y is *minus* the Newton step. For whatever reason PETSc doesn't solve with the minus on the RHS. */ if (ynorm == 0.0) { if (monitor) { ierr = PetscViewerASCIIAddTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); ierr = PetscViewerASCIIPrintf(monitor," Line search: Initial direction and size is 0\n");CHKERRQ(ierr); ierr = PetscViewerASCIISubtractTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); } ierr = VecCopy(X,W);CHKERRQ(ierr); ierr = VecCopy(F,G);CHKERRQ(ierr); ierr = SNESLineSearchSetNorms(linesearch,xnorm,fnorm,ynorm);CHKERRQ(ierr); ierr = SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_REDUCT);CHKERRQ(ierr); PetscFunctionReturn(0); } /* At this point, we've solved the Newton system for delta_x, and we assume that its norm is greater than the solution tolerance (otherwise we wouldn't be in here). So let's go ahead and estimate the Lipschitz constant. W contains bar_delta_x_prev at this point. */ if (monitor) { ierr = PetscViewerASCIIAddTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); ierr = PetscViewerASCIIPrintf(monitor," Line search: norm of Newton step: %14.12e\n", (double) ynorm);CHKERRQ(ierr); ierr = PetscViewerASCIISubtractTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); } /* this needs information from a previous iteration, so can't do it on the first one */ if (nleqerr->norm_delta_x_prev > 0 && nleqerr->norm_bar_delta_x_prev > 0) { ierr = VecWAXPY(G, +1.0, Y, W);CHKERRQ(ierr); /* bar_delta_x - delta_x; +1 because Y is -delta_x */ ierr = VecNormBegin(G, NORM_2, &gnorm);CHKERRQ(ierr); ierr = VecNormEnd(G, NORM_2, &gnorm);CHKERRQ(ierr); nleqerr->mu_curr = nleqerr->lambda_prev * (nleqerr->norm_delta_x_prev * nleqerr->norm_bar_delta_x_prev) / (gnorm * ynorm); lambda = PetscMin(1.0, nleqerr->mu_curr); if (monitor) { ierr = PetscViewerASCIIAddTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); ierr = PetscViewerASCIIPrintf(monitor," Line search: Lipschitz estimate: %14.12e; lambda: %14.12e\n", (double) nleqerr->mu_curr, (double) lambda);CHKERRQ(ierr); ierr = PetscViewerASCIISubtractTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); } } else { lambda = linesearch->damping; } /* The main while loop of the algorithm. At the end of this while loop, G should have the accepted new X in it. */ count = 0; while (PETSC_TRUE) { if (monitor) { ierr = PetscViewerASCIIAddTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); ierr = PetscViewerASCIIPrintf(monitor," Line search: entering iteration with lambda: %14.12e\n", lambda);CHKERRQ(ierr); ierr = PetscViewerASCIISubtractTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); } /* Check that we haven't performed too many iterations */ count += 1; if (count >= max_its) { if (monitor) { ierr = PetscViewerASCIIAddTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); ierr = PetscViewerASCIIPrintf(monitor," Line search: maximum iterations reached\n");CHKERRQ(ierr); ierr = PetscViewerASCIISubtractTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); } ierr = SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_REDUCT);CHKERRQ(ierr); PetscFunctionReturn(0); } /* Now comes the Regularity Test. */ if (lambda <= minlambda) { /* This isn't what is suggested by Deuflhard, but it works better in my experience */ if (monitor) { ierr = PetscViewerASCIIAddTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); ierr = PetscViewerASCIIPrintf(monitor," Line search: lambda has reached lambdamin, taking full Newton step\n");CHKERRQ(ierr); ierr = PetscViewerASCIISubtractTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); } lambda = 1.0; ierr = VecWAXPY(G, -lambda, Y, X);CHKERRQ(ierr); /* and clean up the state for next time */ ierr = SNESLineSearchReset_NLEQERR(linesearch);CHKERRQ(ierr); /* The clang static analyzer detected a problem here; once the loop is broken the values nleqerr->norm_delta_x_prev = ynorm; nleqerr->norm_bar_delta_x_prev = wnorm; are set, but wnorm has not even been computed. I don't know if this is the correct fix but by setting ynorm and wnorm to -1.0 at least the linesearch object is kept in the state set by the SNESLineSearchReset_NLEQERR() call above */ ynorm = wnorm = -1.0; break; } /* Compute new trial iterate */ ierr = VecWAXPY(W, -lambda, Y, X);CHKERRQ(ierr); ierr = SNESComputeFunction(snes, W, G);CHKERRQ(ierr); /* Solve linear system for bar_delta_x_curr: old Jacobian, new RHS. Note absence of minus sign, compared to Deuflhard, in keeping with PETSc convention */ ierr = KSPSolve(snes->ksp, G, W);CHKERRQ(ierr); ierr = KSPGetConvergedReason(snes->ksp, &kspreason);CHKERRQ(ierr); if (kspreason < 0) { ierr = PetscInfo(snes,"Solution for \\bar{delta x}^{k+1} failed.");CHKERRQ(ierr); } /* W now contains -bar_delta_x_curr. */ ierr = VecNorm(W, NORM_2, &wnorm);CHKERRQ(ierr); if (monitor) { ierr = PetscViewerASCIIAddTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); ierr = PetscViewerASCIIPrintf(monitor," Line search: norm of simplified Newton update: %14.12e\n", (double) wnorm);CHKERRQ(ierr); ierr = PetscViewerASCIISubtractTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); } /* compute the monitoring quantities theta and mudash. */ theta = wnorm / ynorm; ierr = VecWAXPY(G, -(1.0 - lambda), Y, W);CHKERRQ(ierr); ierr = VecNorm(G, NORM_2, &gnorm);CHKERRQ(ierr); mudash = (0.5 * ynorm * lambda * lambda) / gnorm; /* Check for termination of the linesearch */ if (theta >= 1.0) { /* need to go around again with smaller lambda */ if (monitor) { ierr = PetscViewerASCIIAddTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); ierr = PetscViewerASCIIPrintf(monitor," Line search: monotonicity check failed, ratio: %14.12e\n", (double) theta);CHKERRQ(ierr); ierr = PetscViewerASCIISubtractTab(monitor,((PetscObject)linesearch)->tablevel);CHKERRQ(ierr); } lambda = PetscMin(mudash, 0.5 * lambda); lambda = PetscMax(lambda, minlambda); /* continue through the loop, i.e. go back to regularity test */ } else { /* linesearch terminated */ lambdadash = PetscMin(1.0, mudash); if (lambdadash == 1.0 && lambda == 1.0 && wnorm <= stol) { /* store the updated state, X - Y - W, in G: I need to keep W for the next linesearch */ ierr = VecCopy(X, G);CHKERRQ(ierr); ierr = VecAXPY(G, -1.0, Y);CHKERRQ(ierr); ierr = VecAXPY(G, -1.0, W);CHKERRQ(ierr); break; } /* Deuflhard suggests to add the following: else if (lambdadash >= 4.0 * lambda) { lambda = lambdadash; } to continue through the loop, i.e. go back to regularity test. I deliberately exclude this, as I have practical experience of this getting stuck in infinite loops (on e.g. an Allen--Cahn problem). */ else { /* accept iterate without adding on, i.e. don't use bar_delta_x; again, I need to keep W for the next linesearch */ ierr = VecWAXPY(G, -lambda, Y, X);CHKERRQ(ierr); break; } } } if (linesearch->ops->viproject) { ierr = (*linesearch->ops->viproject)(snes, G);CHKERRQ(ierr); } /* W currently contains -bar_delta_u. Scale it so that it contains bar_delta_u. */ ierr = VecScale(W, -1.0);CHKERRQ(ierr); /* postcheck */ ierr = SNESLineSearchPostCheck(linesearch,X,Y,G,&changed_y,&changed_w);CHKERRQ(ierr); if (changed_y || changed_w) { ierr = SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_USER);CHKERRQ(ierr); ierr = PetscInfo(snes,"Changing the search direction here doesn't make sense.\n");CHKERRQ(ierr); PetscFunctionReturn(0); } /* copy the solution and information from this iteration over */ nleqerr->norm_delta_x_prev = ynorm; nleqerr->norm_bar_delta_x_prev = wnorm; nleqerr->lambda_prev = lambda; ierr = VecCopy(G, X);CHKERRQ(ierr); ierr = SNESComputeFunction(snes, X, F);CHKERRQ(ierr); ierr = VecNorm(X, NORM_2, &xnorm);CHKERRQ(ierr); ierr = VecNorm(F, NORM_2, &fnorm);CHKERRQ(ierr); ierr = SNESLineSearchSetLambda(linesearch, lambda);CHKERRQ(ierr); ierr = SNESLineSearchSetNorms(linesearch, xnorm, fnorm, (ynorm < 0 ? PETSC_INFINITY : ynorm));CHKERRQ(ierr); PetscFunctionReturn(0); }