1*a99ef635SJonas Heinzmann #include <petsc/private/linesearchimpl.h> 2*a99ef635SJonas Heinzmann #include <petscsnes.h> 3*a99ef635SJonas Heinzmann 4*a99ef635SJonas Heinzmann static PetscErrorCode SNESLineSearchApply_Secant(SNESLineSearch linesearch) 5*a99ef635SJonas Heinzmann { 6*a99ef635SJonas Heinzmann PetscBool changed_y, changed_w; 7*a99ef635SJonas Heinzmann Vec X; 8*a99ef635SJonas Heinzmann Vec F; 9*a99ef635SJonas Heinzmann Vec Y; 10*a99ef635SJonas Heinzmann Vec W; 11*a99ef635SJonas Heinzmann SNES snes; 12*a99ef635SJonas Heinzmann PetscReal gnorm; 13*a99ef635SJonas Heinzmann PetscReal ynorm; 14*a99ef635SJonas Heinzmann PetscReal xnorm; 15*a99ef635SJonas Heinzmann PetscReal minlambda, maxlambda, atol, ltol; 16*a99ef635SJonas Heinzmann PetscViewer monitor; 17*a99ef635SJonas Heinzmann PetscReal lambda, lambda_old, lambda_mid, lambda_update, delLambda; 18*a99ef635SJonas Heinzmann PetscReal fnrm, fnrm_old, fnrm_mid; 19*a99ef635SJonas Heinzmann PetscReal delFnrm, delFnrm_old, del2Fnrm; 20*a99ef635SJonas Heinzmann PetscInt i, max_it; 21*a99ef635SJonas Heinzmann SNESObjectiveFn *objective; 22*a99ef635SJonas Heinzmann 23*a99ef635SJonas Heinzmann PetscFunctionBegin; 24*a99ef635SJonas Heinzmann PetscCall(SNESLineSearchGetVecs(linesearch, &X, &F, &Y, &W, NULL)); 25*a99ef635SJonas Heinzmann PetscCall(SNESLineSearchGetNorms(linesearch, &xnorm, &gnorm, &ynorm)); 26*a99ef635SJonas Heinzmann PetscCall(SNESLineSearchGetLambda(linesearch, &lambda)); 27*a99ef635SJonas Heinzmann PetscCall(SNESLineSearchGetSNES(linesearch, &snes)); 28*a99ef635SJonas Heinzmann PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_SUCCEEDED)); 29*a99ef635SJonas Heinzmann PetscCall(SNESLineSearchGetTolerances(linesearch, &minlambda, &maxlambda, NULL, &atol, <ol, &max_it)); 30*a99ef635SJonas Heinzmann PetscCall(SNESLineSearchGetDefaultMonitor(linesearch, &monitor)); 31*a99ef635SJonas Heinzmann 32*a99ef635SJonas Heinzmann PetscCall(SNESGetObjective(snes, &objective, NULL)); 33*a99ef635SJonas Heinzmann 34*a99ef635SJonas Heinzmann /* precheck */ 35*a99ef635SJonas Heinzmann PetscCall(SNESLineSearchPreCheck(linesearch, X, Y, &changed_y)); 36*a99ef635SJonas Heinzmann lambda_old = 0.0; 37*a99ef635SJonas Heinzmann if (!objective) { 38*a99ef635SJonas Heinzmann fnrm_old = gnorm * gnorm; 39*a99ef635SJonas Heinzmann } else { 40*a99ef635SJonas Heinzmann PetscCall(SNESComputeObjective(snes, X, &fnrm_old)); 41*a99ef635SJonas Heinzmann } 42*a99ef635SJonas Heinzmann lambda_mid = 0.5 * (lambda + lambda_old); 43*a99ef635SJonas Heinzmann 44*a99ef635SJonas Heinzmann for (i = 0; i < max_it; i++) { 45*a99ef635SJonas Heinzmann /* check whether new lambda is NaN or Inf - if so, iteratively shrink towards lambda_old */ 46*a99ef635SJonas Heinzmann while (PETSC_TRUE) { 47*a99ef635SJonas Heinzmann PetscCall(VecWAXPY(W, -lambda_mid, Y, X)); 48*a99ef635SJonas Heinzmann if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W)); 49*a99ef635SJonas Heinzmann if (!objective) { 50*a99ef635SJonas Heinzmann /* compute the norm at the midpoint */ 51*a99ef635SJonas Heinzmann PetscCall((*linesearch->ops->snesfunc)(snes, W, F)); 52*a99ef635SJonas Heinzmann if (linesearch->ops->vinorm) { 53*a99ef635SJonas Heinzmann fnrm_mid = gnorm; 54*a99ef635SJonas Heinzmann PetscCall((*linesearch->ops->vinorm)(snes, F, W, &fnrm_mid)); 55*a99ef635SJonas Heinzmann } else { 56*a99ef635SJonas Heinzmann PetscCall(VecNorm(F, NORM_2, &fnrm_mid)); 57*a99ef635SJonas Heinzmann } 58*a99ef635SJonas Heinzmann 59*a99ef635SJonas Heinzmann /* compute the norm at the new endpoint */ 60*a99ef635SJonas Heinzmann PetscCall(VecWAXPY(W, -lambda, Y, X)); 61*a99ef635SJonas Heinzmann if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W)); 62*a99ef635SJonas Heinzmann PetscCall((*linesearch->ops->snesfunc)(snes, W, F)); 63*a99ef635SJonas Heinzmann if (linesearch->ops->vinorm) { 64*a99ef635SJonas Heinzmann fnrm = gnorm; 65*a99ef635SJonas Heinzmann PetscCall((*linesearch->ops->vinorm)(snes, F, W, &fnrm)); 66*a99ef635SJonas Heinzmann } else { 67*a99ef635SJonas Heinzmann PetscCall(VecNorm(F, NORM_2, &fnrm)); 68*a99ef635SJonas Heinzmann } 69*a99ef635SJonas Heinzmann fnrm_mid = fnrm_mid * fnrm_mid; 70*a99ef635SJonas Heinzmann fnrm = fnrm * fnrm; 71*a99ef635SJonas Heinzmann } else { 72*a99ef635SJonas Heinzmann /* compute the objective at the midpoint */ 73*a99ef635SJonas Heinzmann PetscCall(SNESComputeObjective(snes, W, &fnrm_mid)); 74*a99ef635SJonas Heinzmann 75*a99ef635SJonas Heinzmann /* compute the objective at the new endpoint */ 76*a99ef635SJonas Heinzmann PetscCall(VecWAXPY(W, -lambda, Y, X)); 77*a99ef635SJonas Heinzmann PetscCall(SNESComputeObjective(snes, W, &fnrm)); 78*a99ef635SJonas Heinzmann } 79*a99ef635SJonas Heinzmann 80*a99ef635SJonas Heinzmann /* if new endpoint is viable, exit */ 81*a99ef635SJonas Heinzmann if (!PetscIsInfOrNanReal(fnrm)) break; 82*a99ef635SJonas Heinzmann if (monitor) { 83*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel)); 84*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIPrintf(monitor, " Line search: objective function at lambda = %g is Inf or Nan, cutting lambda\n", (double)lambda)); 85*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel)); 86*a99ef635SJonas Heinzmann } 87*a99ef635SJonas Heinzmann 88*a99ef635SJonas Heinzmann /* if smallest allowable lambda gives NaN or Inf, exit line search */ 89*a99ef635SJonas Heinzmann if (lambda <= minlambda) { 90*a99ef635SJonas Heinzmann if (monitor) { 91*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel)); 92*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIPrintf(monitor, " Line search: objective function at lambda = %g <= lambda_min = %g is Inf or Nan, can not further cut lambda\n", (double)lambda, (double)lambda)); 93*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel)); 94*a99ef635SJonas Heinzmann } 95*a99ef635SJonas Heinzmann PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_REDUCT)); 96*a99ef635SJonas Heinzmann PetscFunctionReturn(PETSC_SUCCESS); 97*a99ef635SJonas Heinzmann } 98*a99ef635SJonas Heinzmann 99*a99ef635SJonas Heinzmann /* forbid the search from ever going back to the "failed" length that generates Nan or Inf */ 100*a99ef635SJonas Heinzmann maxlambda = .95 * lambda; 101*a99ef635SJonas Heinzmann 102*a99ef635SJonas Heinzmann /* shrink lambda towards the previous one which was viable */ 103*a99ef635SJonas Heinzmann lambda = .5 * (lambda + lambda_old); 104*a99ef635SJonas Heinzmann lambda_mid = .5 * (lambda + lambda_old); 105*a99ef635SJonas Heinzmann } 106*a99ef635SJonas Heinzmann 107*a99ef635SJonas Heinzmann /* monitoring output */ 108*a99ef635SJonas Heinzmann if (monitor) { 109*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel)); 110*a99ef635SJonas Heinzmann if (!objective) { 111*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIPrintf(monitor, " Line search: lambdas = [%g, %g, %g], fnorms = [%g, %g, %g]\n", (double)lambda, (double)lambda_mid, (double)lambda_old, (double)PetscSqrtReal(fnrm), (double)PetscSqrtReal(fnrm_mid), (double)PetscSqrtReal(fnrm_old))); 112*a99ef635SJonas Heinzmann } else { 113*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIPrintf(monitor, " Line search: lambdas = [%g, %g, %g], obj = [%g, %g, %g]\n", (double)lambda, (double)lambda_mid, (double)lambda_old, (double)fnrm, (double)fnrm_mid, (double)fnrm_old)); 114*a99ef635SJonas Heinzmann } 115*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel)); 116*a99ef635SJonas Heinzmann } 117*a99ef635SJonas Heinzmann 118*a99ef635SJonas Heinzmann /* determine change of lambda */ 119*a99ef635SJonas Heinzmann delLambda = lambda - lambda_old; 120*a99ef635SJonas Heinzmann 121*a99ef635SJonas Heinzmann /* check change of lambda tolerance */ 122*a99ef635SJonas Heinzmann if (PetscAbsReal(delLambda) < ltol) { 123*a99ef635SJonas Heinzmann if (monitor) { 124*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel)); 125*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIPrintf(monitor, " Line search: abs(delLambda) = %g < ltol = %g\n", (double)PetscAbsReal(delLambda), (double)ltol)); 126*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel)); 127*a99ef635SJonas Heinzmann } 128*a99ef635SJonas Heinzmann break; 129*a99ef635SJonas Heinzmann } 130*a99ef635SJonas Heinzmann 131*a99ef635SJonas Heinzmann /* compute f'() at the end points using second order one sided differencing */ 132*a99ef635SJonas Heinzmann delFnrm = (3. * fnrm - 4. * fnrm_mid + 1. * fnrm_old) / delLambda; 133*a99ef635SJonas Heinzmann delFnrm_old = (-3. * fnrm_old + 4. * fnrm_mid - 1. * fnrm) / delLambda; 134*a99ef635SJonas Heinzmann 135*a99ef635SJonas Heinzmann /* compute f''() at the midpoint using centered differencing */ 136*a99ef635SJonas Heinzmann del2Fnrm = (delFnrm - delFnrm_old) / delLambda; 137*a99ef635SJonas Heinzmann 138*a99ef635SJonas Heinzmann /* check absolute tolerance */ 139*a99ef635SJonas Heinzmann if (PetscAbsReal(delFnrm) <= atol) { 140*a99ef635SJonas Heinzmann if (monitor) { 141*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel)); 142*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIPrintf(monitor, " Line search: abs(delFnrm) = %g <= atol = %g\n", (double)PetscAbsReal(delFnrm), (double)atol)); 143*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel)); 144*a99ef635SJonas Heinzmann } 145*a99ef635SJonas Heinzmann break; 146*a99ef635SJonas Heinzmann } 147*a99ef635SJonas Heinzmann 148*a99ef635SJonas Heinzmann /* compute the secant (Newton) update -- always go downhill */ 149*a99ef635SJonas Heinzmann if (del2Fnrm > 0.) lambda_update = lambda - delFnrm / del2Fnrm; 150*a99ef635SJonas Heinzmann else if (del2Fnrm < 0.) lambda_update = lambda + delFnrm / del2Fnrm; 151*a99ef635SJonas Heinzmann else { 152*a99ef635SJonas Heinzmann if (monitor) { 153*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel)); 154*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIPrintf(monitor, " Line search: del2Fnrm = 0\n")); 155*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel)); 156*a99ef635SJonas Heinzmann } 157*a99ef635SJonas Heinzmann break; 158*a99ef635SJonas Heinzmann } 159*a99ef635SJonas Heinzmann 160*a99ef635SJonas Heinzmann /* prevent secant method from stepping out of bounds */ 161*a99ef635SJonas Heinzmann if (lambda_update < minlambda) lambda_update = 0.5 * (lambda + lambda_old); 162*a99ef635SJonas Heinzmann if (lambda_update > maxlambda) { 163*a99ef635SJonas Heinzmann lambda_update = maxlambda; 164*a99ef635SJonas Heinzmann break; 165*a99ef635SJonas Heinzmann } 166*a99ef635SJonas Heinzmann 167*a99ef635SJonas Heinzmann /* if lambda is NaN or Inf, do not accept update but exit with previous lambda */ 168*a99ef635SJonas Heinzmann if (PetscIsInfOrNanReal(lambda_update)) { 169*a99ef635SJonas Heinzmann if (monitor) { 170*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel)); 171*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIPrintf(monitor, " Line search: lambda_update is NaN or Inf\n")); 172*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel)); 173*a99ef635SJonas Heinzmann } 174*a99ef635SJonas Heinzmann PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_NANORINF)); 175*a99ef635SJonas Heinzmann break; 176*a99ef635SJonas Heinzmann } 177*a99ef635SJonas Heinzmann 178*a99ef635SJonas Heinzmann /* update the endpoints and the midpoint of the bracketed secant region */ 179*a99ef635SJonas Heinzmann lambda_old = lambda; 180*a99ef635SJonas Heinzmann lambda = lambda_update; 181*a99ef635SJonas Heinzmann fnrm_old = fnrm; 182*a99ef635SJonas Heinzmann lambda_mid = 0.5 * (lambda + lambda_old); 183*a99ef635SJonas Heinzmann 184*a99ef635SJonas Heinzmann if ((i == max_it - 1) && monitor) { 185*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel)); 186*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIPrintf(monitor, " Line search: reached maximum number of iterations!\n")); 187*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel)); 188*a99ef635SJonas Heinzmann } 189*a99ef635SJonas Heinzmann } 190*a99ef635SJonas Heinzmann 191*a99ef635SJonas Heinzmann /* construct the solution */ 192*a99ef635SJonas Heinzmann PetscCall(VecWAXPY(W, -lambda, Y, X)); 193*a99ef635SJonas Heinzmann if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W)); 194*a99ef635SJonas Heinzmann 195*a99ef635SJonas Heinzmann /* postcheck */ 196*a99ef635SJonas Heinzmann PetscCall(SNESLineSearchSetLambda(linesearch, lambda)); 197*a99ef635SJonas Heinzmann PetscCall(SNESLineSearchPostCheck(linesearch, X, Y, W, &changed_y, &changed_w)); 198*a99ef635SJonas Heinzmann if (changed_y) { 199*a99ef635SJonas Heinzmann if (!changed_w) PetscCall(VecWAXPY(W, -lambda, Y, X)); 200*a99ef635SJonas Heinzmann if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W)); 201*a99ef635SJonas Heinzmann } 202*a99ef635SJonas Heinzmann PetscCall(VecCopy(W, X)); 203*a99ef635SJonas Heinzmann PetscCall((*linesearch->ops->snesfunc)(snes, X, F)); 204*a99ef635SJonas Heinzmann 205*a99ef635SJonas Heinzmann PetscCall(SNESLineSearchComputeNorms(linesearch)); 206*a99ef635SJonas Heinzmann 207*a99ef635SJonas Heinzmann if (monitor) { 208*a99ef635SJonas Heinzmann PetscCall(SNESLineSearchGetNorms(linesearch, NULL, &gnorm, NULL)); 209*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel)); 210*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIIPrintf(monitor, " Line search terminated: lambda = %g, fnorm = %g\n", (double)lambda, (double)gnorm)); 211*a99ef635SJonas Heinzmann PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel)); 212*a99ef635SJonas Heinzmann } 213*a99ef635SJonas Heinzmann PetscFunctionReturn(PETSC_SUCCESS); 214*a99ef635SJonas Heinzmann } 215*a99ef635SJonas Heinzmann 216*a99ef635SJonas Heinzmann /*MC 217*a99ef635SJonas Heinzmann SNESLINESEARCHSECANT - Secant search in the L2 norm of the function or the objective function. 218*a99ef635SJonas Heinzmann 219*a99ef635SJonas Heinzmann Attempts to solve $ \min_{\lambda} f(x_k + \lambda Y_k) $ using the secant method with the initial bracketing of $ \lambda $ between [0,damping]. 220*a99ef635SJonas Heinzmann $f(x_k + \lambda Y_k)$ is either the squared L2-norm of the function $||F(x_k + \lambda Y_k)||_2^2$, 221*a99ef635SJonas Heinzmann or the objective function $G(x_k + \lambda Y_k)$ if it is provided with `SNESSetObjective()` 222*a99ef635SJonas Heinzmann Differences of $f()$ are used to approximate the first and second derivative of $f()$ with respect to 223*a99ef635SJonas Heinzmann $\lambda$, $f'()$ and $f''()$. 224*a99ef635SJonas Heinzmann 225*a99ef635SJonas Heinzmann When an objective function is provided $f(w)$ is the objective function otherwise $f(w) = ||F(w)||^2$. 226*a99ef635SJonas Heinzmann $x$ is the current step and $y$ is the search direction. 227*a99ef635SJonas Heinzmann 228*a99ef635SJonas Heinzmann Options Database Keys: 229*a99ef635SJonas Heinzmann + -snes_linesearch_max_it <1> - maximum number of iterations within the line search 230*a99ef635SJonas Heinzmann . -snes_linesearch_damping <1.0> - initial `lambda` on entry to the line search 231*a99ef635SJonas Heinzmann . -snes_linesearch_minlambda <1e\-12> - minimum allowable `lambda` 232*a99ef635SJonas Heinzmann . -snes_linesearch_maxlambda <1.0> - maximum `lambda` (scaling of solution update) allowed 233*a99ef635SJonas Heinzmann . -snes_linesearch_atol <1e\-15> - absolute tolerance for the secant method $ f'() < atol $ 234*a99ef635SJonas Heinzmann - -snes_linesearch_ltol <1e\-8> - minimum absolute change in `lambda` allowed 235*a99ef635SJonas Heinzmann 236*a99ef635SJonas Heinzmann Level: advanced 237*a99ef635SJonas Heinzmann 238*a99ef635SJonas Heinzmann .seealso: [](ch_snes), `SNESLINESEARCHBT`, `SNESLINESEARCHCP`, `SNESLineSearch`, `SNESLineSearchType`, `SNESLineSearchCreate()`, `SNESLineSearchSetType()` 239*a99ef635SJonas Heinzmann M*/ 240*a99ef635SJonas Heinzmann PETSC_EXTERN PetscErrorCode SNESLineSearchCreate_Secant(SNESLineSearch linesearch) 241*a99ef635SJonas Heinzmann { 242*a99ef635SJonas Heinzmann PetscFunctionBegin; 243*a99ef635SJonas Heinzmann linesearch->ops->apply = SNESLineSearchApply_Secant; 244*a99ef635SJonas Heinzmann linesearch->ops->destroy = NULL; 245*a99ef635SJonas Heinzmann linesearch->ops->setfromoptions = NULL; 246*a99ef635SJonas Heinzmann linesearch->ops->reset = NULL; 247*a99ef635SJonas Heinzmann linesearch->ops->view = NULL; 248*a99ef635SJonas Heinzmann linesearch->ops->setup = NULL; 249*a99ef635SJonas Heinzmann 250*a99ef635SJonas Heinzmann linesearch->max_it = 1; 251*a99ef635SJonas Heinzmann PetscFunctionReturn(PETSC_SUCCESS); 252*a99ef635SJonas Heinzmann } 253