1 #include <petsc/private/linesearchimpl.h> 2 #include <petsc/private/snesimpl.h> 3 4 static PetscErrorCode SNESLineSearchApply_Bisection(SNESLineSearch linesearch) 5 { 6 PetscBool changed_y, changed_w; 7 Vec X, F, Y, W, G; 8 SNES snes; 9 PetscReal ynorm; 10 PetscReal lambda_left, lambda, lambda_right, lambda_old; 11 PetscScalar fty_left, fty, fty_initial; 12 PetscViewer monitor; 13 PetscReal rtol, atol, ltol; 14 PetscInt it, max_it; 15 16 PetscFunctionBegin; 17 PetscCall(SNESLineSearchGetVecs(linesearch, &X, &F, &Y, &W, &G)); 18 PetscCall(SNESLineSearchGetLambda(linesearch, &lambda)); 19 PetscCall(SNESLineSearchGetSNES(linesearch, &snes)); 20 PetscCall(SNESLineSearchGetTolerances(linesearch, NULL, NULL, &rtol, &atol, <ol, &max_it)); 21 PetscCall(SNESLineSearchGetDefaultMonitor(linesearch, &monitor)); 22 23 /* pre-check */ 24 PetscCall(SNESLineSearchPreCheck(linesearch, X, Y, &changed_y)); 25 26 /* compute ynorm to normalize search direction */ 27 PetscCall(VecNorm(Y, NORM_2, &ynorm)); 28 29 /* initialize interval for bisection */ 30 lambda_left = 0.0; 31 lambda_right = lambda; 32 33 /* compute fty at left end of interval */ 34 if (linesearch->ops->vidirderiv) { 35 PetscCall((*linesearch->ops->vidirderiv)(snes, F, X, Y, &fty_left)); 36 } else { 37 PetscCall(VecDot(F, Y, &fty_left)); 38 } 39 fty_initial = fty_left; 40 41 /* compute fty at right end of interval (initial lambda) */ 42 PetscCall(VecWAXPY(W, -lambda, Y, X)); 43 if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W)); 44 PetscCall((*linesearch->ops->snesfunc)(snes, W, G)); 45 if (snes->nfuncs >= snes->max_funcs && snes->max_funcs >= 0) { 46 PetscCall(PetscInfo(snes, "Exceeded maximum function evaluations during line search!\n")); 47 snes->reason = SNES_DIVERGED_FUNCTION_COUNT; 48 PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_FUNCTION)); 49 PetscFunctionReturn(PETSC_SUCCESS); 50 } 51 if (linesearch->ops->vidirderiv) { 52 PetscCall((*linesearch->ops->vidirderiv)(snes, G, W, Y, &fty)); 53 } else { 54 PetscCall(VecDot(G, Y, &fty)); 55 } 56 /* check whether sign changes in interval */ 57 if (!PetscIsInfOrNanScalar(fty) && (PetscRealPart(fty_left * fty) > 0.0)) { 58 /* no change of sign: accept full step */ 59 if (monitor) { 60 PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel)); 61 PetscCall(PetscViewerASCIIPrintf(monitor, " Line search: sign of fty does not change in step interval, accepting full step\n")); 62 PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel)); 63 } 64 } else { 65 /* change of sign: iteratively bisect interval */ 66 lambda_old = 0.0; 67 it = 0; 68 69 while (PETSC_TRUE) { 70 /* check for NaN or Inf */ 71 if (PetscIsInfOrNanScalar(fty)) { 72 if (monitor) { 73 PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel)); 74 PetscCall(PetscViewerASCIIPrintf(monitor, " Line search fty is NaN or Inf!\n")); 75 PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel)); 76 } 77 PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_NANORINF)); 78 PetscFunctionReturn(PETSC_SUCCESS); 79 break; 80 } 81 82 /* check absolute tolerance */ 83 if (PetscAbsScalar(fty) <= atol * ynorm) { 84 if (monitor) { 85 PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel)); 86 PetscCall(PetscViewerASCIIPrintf(monitor, " Line search: abs(fty)/||y|| = %g <= atol = %g\n", (double)(PetscAbsScalar(fty) / ynorm), (double)atol)); 87 PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel)); 88 } 89 break; 90 } 91 92 /* check relative tolerance */ 93 if (PetscAbsScalar(fty) / PetscAbsScalar(fty_initial) <= rtol) { 94 if (monitor) { 95 PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel)); 96 PetscCall(PetscViewerASCIIPrintf(monitor, " Line search: abs(fty/fty_initial) = %g <= rtol = %g\n", (double)(PetscAbsScalar(fty) / PetscAbsScalar(fty_initial)), (double)rtol)); 97 PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel)); 98 } 99 break; 100 } 101 102 /* check maximum number of iterations */ 103 if (it > max_it) { 104 if (monitor) { 105 PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel)); 106 PetscCall(PetscViewerASCIIPrintf(monitor, " Line search: maximum iterations reached\n")); 107 PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel)); 108 } 109 PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_REDUCT)); 110 PetscFunctionReturn(PETSC_SUCCESS); 111 break; 112 } 113 114 /* check change of lambda tolerance */ 115 if (PetscAbsReal(lambda - lambda_old) < ltol) { 116 if (monitor) { 117 PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel)); 118 PetscCall(PetscViewerASCIIPrintf(monitor, " Line search: abs(dlambda) = %g < ltol = %g\n", (double)PetscAbsReal(lambda - lambda_old), (double)ltol)); 119 PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel)); 120 } 121 break; 122 } 123 124 /* determine direction of bisection (not necessary for 0th iteration) */ 125 if (it > 0) { 126 if (PetscRealPart(fty * fty_left) <= 0.0) { 127 lambda_right = lambda; 128 } else { 129 lambda_left = lambda; 130 /* also update fty_left for direction check in next iteration */ 131 fty_left = fty; 132 } 133 } 134 135 /* bisect interval */ 136 lambda_old = lambda; 137 lambda = 0.5 * (lambda_left + lambda_right); 138 139 /* compute fty at new lambda */ 140 PetscCall(VecWAXPY(W, -lambda, Y, X)); 141 if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W)); 142 PetscCall((*linesearch->ops->snesfunc)(snes, W, G)); 143 if (snes->nfuncs >= snes->max_funcs && snes->max_funcs >= 0) { 144 PetscCall(PetscInfo(snes, "Exceeded maximum function evaluations during line search!\n")); 145 snes->reason = SNES_DIVERGED_FUNCTION_COUNT; 146 PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_FAILED_FUNCTION)); 147 PetscFunctionReturn(PETSC_SUCCESS); 148 } 149 if (linesearch->ops->vidirderiv) { 150 PetscCall((*linesearch->ops->vidirderiv)(snes, G, W, Y, &fty)); 151 } else { 152 PetscCall(VecDot(G, Y, &fty)); 153 } 154 155 /* print iteration information */ 156 if (monitor) { 157 PetscCall(PetscViewerASCIIAddTab(monitor, ((PetscObject)linesearch)->tablevel)); 158 PetscCall(PetscViewerASCIIPrintf(monitor, " %3" PetscInt_FMT " Line search: fty/||y|| = %g, lambda = %g\n", it, (double)(PetscRealPart(fty) / ynorm), (double)lambda)); 159 PetscCall(PetscViewerASCIISubtractTab(monitor, ((PetscObject)linesearch)->tablevel)); 160 } 161 162 /* count up iteration */ 163 it++; 164 } 165 } 166 167 /* post-check */ 168 PetscCall(SNESLineSearchSetLambda(linesearch, lambda)); 169 PetscCall(SNESLineSearchPostCheck(linesearch, X, Y, W, &changed_y, &changed_w)); 170 if (changed_y) { 171 if (!changed_w) PetscCall(VecWAXPY(W, -lambda, Y, X)); 172 if (linesearch->ops->viproject) PetscCall((*linesearch->ops->viproject)(snes, W)); 173 } 174 175 /* update solution*/ 176 PetscCall(VecCopy(W, X)); 177 PetscCall((*linesearch->ops->snesfunc)(snes, X, F)); 178 PetscCall(SNESLineSearchComputeNorms(linesearch)); 179 180 /* finalization */ 181 PetscCall(SNESLineSearchSetReason(linesearch, SNES_LINESEARCH_SUCCEEDED)); 182 PetscFunctionReturn(PETSC_SUCCESS); 183 } 184 185 /*MC 186 SNESLINESEARCHBISECTION - Bisection line search. 187 Similar to the critical point line search, `SNESLINESEARCHCP`, the bisection line search assumes that there exists some $G(x)$ for which the `SNESFunctionFn` $F(x) = grad G(x)$. 188 This line search seeks to find the root of the directional derivative, that is $F(x_k - \lambda Y_k) \cdot Y_k / ||Y_k|| = 0$, along the search direction $Y_k$ through bisection. 189 190 Options Database Keys: 191 + -snes_linesearch_max_it <50> - maximum number of bisection iterations for the line search 192 . -snes_linesearch_damping <1.0> - initial `lambda` on entry to the line search 193 . -snes_linesearch_rtol <1e\-8> - relative tolerance for the directional derivative 194 . -snes_linesearch_atol <1e\-6> - absolute tolerance for the directional derivative 195 - -snes_linesearch_ltol <1e\-6> - minimum absolute change in `lambda` allowed 196 197 Level: intermediate 198 199 Notes: 200 `lambda` is the scaling of the search direction (vector) that is computed by this algorithm. 201 If there is no change of sign in the directional derivative from $\lambda=0$ to the initial `lambda` (the damping), then the initial `lambda` will be used. 202 Hence, this line search will always give a `lambda` in the interval $[0, damping]$. 203 This method does NOT use the objective function if it is provided with `SNESSetObjective()`. 204 205 .seealso: [](ch_snes), `SNESLineSearch`, `SNESLineSearchType`, `SNESLineSearchCreate()`, `SNESLineSearchSetType()`, `SNESLINESEARCHCP` 206 M*/ 207 PETSC_EXTERN PetscErrorCode SNESLineSearchCreate_Bisection(SNESLineSearch linesearch) 208 { 209 PetscFunctionBegin; 210 linesearch->ops->apply = SNESLineSearchApply_Bisection; 211 linesearch->ops->destroy = NULL; 212 linesearch->ops->setfromoptions = NULL; 213 linesearch->ops->reset = NULL; 214 linesearch->ops->view = NULL; 215 linesearch->ops->setup = NULL; 216 217 /* set default option values */ 218 linesearch->max_it = 50; 219 linesearch->rtol = 1e-8; 220 linesearch->atol = 1e-6; 221 linesearch->ltol = 1e-6; 222 PetscFunctionReturn(PETSC_SUCCESS); 223 } 224