1 #include <../src/tao/bound/impls/bnk/bnk.h> 2 #include <petscksp.h> 3 4 /* 5 Implements Newton's Method with a line search approach for solving 6 unconstrained minimization problems. A More'-Thuente line search 7 is used to guarantee that the bfgs preconditioner remains positive 8 definite. 9 10 The method can shift the Hessian matrix. The shifting procedure is 11 adapted from the PATH algorithm for solving complementarity 12 problems. 13 14 The linear system solve should be done with a conjugate gradient 15 method, although any method can be used. 16 */ 17 18 static PetscErrorCode TaoSolve_BNLS(Tao tao) 19 { 20 PetscErrorCode ierr; 21 TAO_BNK *bnk = (TAO_BNK *)tao->data; 22 TaoLineSearchConvergedReason ls_reason; 23 24 PetscReal prered, actred, kappa; 25 PetscReal tau_1, tau_2, tau_max, tau_min; 26 PetscReal f_full, fold, gdx; 27 PetscReal step = 1.0; 28 PetscReal delta; 29 PetscReal norm_d = 0.0, e_min; 30 31 PetscInt stepType; 32 PetscInt bfgsUpdates = 0; 33 PetscInt needH = 1; 34 35 PetscFunctionBegin; 36 if (tao->XL || tao->XU || tao->ops->computebounds) { 37 ierr = PetscPrintf(((PetscObject)tao)->comm,"WARNING: Variable bounds have been set but will be ignored by nls algorithm\n");CHKERRQ(ierr); 38 } 39 40 /* Check convergence criteria */ 41 ierr = TaoComputeObjectiveAndGradient(tao, tao->solution, &bnk->f, tao->gradient);CHKERRQ(ierr); 42 ierr = TaoGradientNorm(tao, tao->gradient,NORM_2,&bnk->gnorm);CHKERRQ(ierr); 43 if (PetscIsInfOrNanReal(bnk->f) || PetscIsInfOrNanReal(bnk->gnorm)) SETERRQ(PETSC_COMM_SELF,1, "User provided compute function generated Inf or NaN"); 44 45 tao->reason = TAO_CONTINUE_ITERATING; 46 ierr = TaoLogConvergenceHistory(tao,bnk->f,bnk->gnorm,0.0,tao->ksp_its);CHKERRQ(ierr); 47 ierr = TaoMonitor(tao,tao->niter,bnk->f,bnk->gnorm,0.0,step);CHKERRQ(ierr); 48 ierr = (*tao->ops->convergencetest)(tao,tao->cnvP);CHKERRQ(ierr); 49 if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(0); 50 51 /* Initialize the preconditioner and trust radius */ 52 ierr = TaoBNKInitialize(tao);CHKERRQ(ierr); 53 54 /* Have not converged; continue with Newton method */ 55 while (tao->reason == TAO_CONTINUE_ITERATING) { 56 ++tao->niter; 57 tao->ksp_its=0; 58 59 /* Use the common BNK kernel to compute the step */ 60 ierr = TaoBNKComputeStep(tao, &stepType);CHKERRQ(ierr); 61 62 /* Perform the linesearch */ 63 fold = bnk->f; 64 ierr = VecCopy(tao->solution, bnk->Xold);CHKERRQ(ierr); 65 ierr = VecCopy(tao->gradient, bnk->Gold);CHKERRQ(ierr); 66 67 ierr = TaoLineSearchApply(tao->linesearch, tao->solution, &bnk->f, tao->gradient, bnk->D, &step, &ls_reason);CHKERRQ(ierr); 68 ierr = TaoAddLineSearchCounts(tao);CHKERRQ(ierr); 69 70 while (ls_reason != TAOLINESEARCH_SUCCESS && ls_reason != TAOLINESEARCH_SUCCESS_USER && stepType != BNK_GRADIENT) { 71 /* Linesearch failed, revert solution */ 72 bnk->f = fold; 73 ierr = VecCopy(bnk->Xold, tao->solution);CHKERRQ(ierr); 74 ierr = VecCopy(bnk->Gold, tao->gradient);CHKERRQ(ierr); 75 76 switch(stepType) { 77 case BNK_NEWTON: 78 /* Failed to obtain acceptable iterate with Newton 1step 79 Update the perturbation for next time */ 80 if (bnk->pert <= 0.0) { 81 /* Initialize the perturbation */ 82 bnk->pert = PetscMin(bnk->imax, PetscMax(bnk->imin, bnk->imfac * bnk->gnorm)); 83 if (bnk->is_gltr) { 84 ierr = KSPCGGLTRGetMinEig(tao->ksp,&e_min);CHKERRQ(ierr); 85 bnk->pert = PetscMax(bnk->pert, -e_min); 86 } 87 } else { 88 /* Increase the perturbation */ 89 bnk->pert = PetscMin(bnk->pmax, PetscMax(bnk->pgfac * bnk->pert, bnk->pmgfac * bnk->gnorm)); 90 } 91 92 if (BNK_PC_BFGS != bnk->pc_type) { 93 /* We don't have the bfgs matrix around and being updated 94 Must use gradient direction in this case */ 95 ierr = VecCopy(tao->gradient, bnk->D);CHKERRQ(ierr); 96 ++bnk->grad; 97 stepType = BNK_GRADIENT; 98 } else { 99 /* Attempt to use the BFGS direction */ 100 ierr = MatLMVMSolve(bnk->M, tao->gradient, bnk->D);CHKERRQ(ierr); 101 /* Check for success (descent direction) */ 102 ierr = VecDot(tao->solution, bnk->D, &gdx);CHKERRQ(ierr); 103 if ((gdx <= 0) || PetscIsInfOrNanReal(gdx)) { 104 /* BFGS direction is not descent or direction produced not a number 105 We can assert bfgsUpdates > 1 in this case 106 Use steepest descent direction (scaled) */ 107 108 if (bnk->f != 0.0) { 109 delta = 2.0 * PetscAbsScalar(bnk->f) / (bnk->gnorm*bnk->gnorm); 110 } else { 111 delta = 2.0 / (bnk->gnorm*bnk->gnorm); 112 } 113 ierr = MatLMVMSetDelta(bnk->M, delta);CHKERRQ(ierr); 114 ierr = MatLMVMReset(bnk->M);CHKERRQ(ierr); 115 ierr = MatLMVMUpdate(bnk->M, tao->solution, tao->gradient);CHKERRQ(ierr); 116 ierr = MatLMVMSolve(bnk->M, tao->gradient, bnk->D);CHKERRQ(ierr); 117 118 bfgsUpdates = 1; 119 ++bnk->sgrad; 120 stepType = BNK_SCALED_GRADIENT; 121 } else { 122 ierr = MatLMVMGetUpdates(bnk->M, &bfgsUpdates);CHKERRQ(ierr); 123 if (1 == bfgsUpdates) { 124 /* The first BFGS direction is always the scaled gradient */ 125 ++bnk->sgrad; 126 stepType = BNK_SCALED_GRADIENT; 127 } else { 128 ++bnk->bfgs; 129 stepType = BNK_BFGS; 130 } 131 } 132 } 133 break; 134 135 case BNK_BFGS: 136 /* Can only enter if pc_type == BNK_PC_BFGS 137 Failed to obtain acceptable iterate with BFGS step 138 Attempt to use the scaled gradient direction */ 139 140 if (bnk->f != 0.0) { 141 delta = 2.0 * PetscAbsScalar(bnk->f) / (bnk->gnorm*bnk->gnorm); 142 } else { 143 delta = 2.0 / (bnk->gnorm*bnk->gnorm); 144 } 145 ierr = MatLMVMSetDelta(bnk->M, delta);CHKERRQ(ierr); 146 ierr = MatLMVMReset(bnk->M);CHKERRQ(ierr); 147 ierr = MatLMVMUpdate(bnk->M, tao->solution, tao->gradient);CHKERRQ(ierr); 148 ierr = MatLMVMSolve(bnk->M, tao->gradient, bnk->D);CHKERRQ(ierr); 149 150 bfgsUpdates = 1; 151 ++bnk->sgrad; 152 stepType = BNK_SCALED_GRADIENT; 153 break; 154 155 case BNK_SCALED_GRADIENT: 156 /* Can only enter if pc_type == BNK_PC_BFGS 157 The scaled gradient step did not produce a new iterate; 158 attemp to use the gradient direction. 159 Need to make sure we are not using a different diagonal scaling */ 160 161 ierr = MatLMVMSetScale(bnk->M,0);CHKERRQ(ierr); 162 ierr = MatLMVMSetDelta(bnk->M,1.0);CHKERRQ(ierr); 163 ierr = MatLMVMReset(bnk->M);CHKERRQ(ierr); 164 ierr = MatLMVMUpdate(bnk->M, tao->solution, tao->gradient);CHKERRQ(ierr); 165 ierr = MatLMVMSolve(bnk->M, tao->gradient, bnk->D);CHKERRQ(ierr); 166 167 bfgsUpdates = 1; 168 ++bnk->grad; 169 stepType = BNK_GRADIENT; 170 break; 171 } 172 ierr = VecScale(bnk->D, -1.0);CHKERRQ(ierr); 173 174 ierr = TaoLineSearchApply(tao->linesearch, tao->solution, &bnk->f, tao->gradient, bnk->D, &step, &ls_reason);CHKERRQ(ierr); 175 ierr = TaoLineSearchGetFullStepObjective(tao->linesearch, &f_full);CHKERRQ(ierr); 176 ierr = TaoAddLineSearchCounts(tao);CHKERRQ(ierr); 177 } 178 179 if (ls_reason != TAOLINESEARCH_SUCCESS && ls_reason != TAOLINESEARCH_SUCCESS_USER) { 180 /* Failed to find an improving point */ 181 bnk->f = fold; 182 ierr = VecCopy(bnk->Xold, tao->solution);CHKERRQ(ierr); 183 ierr = VecCopy(bnk->Gold, tao->gradient);CHKERRQ(ierr); 184 step = 0.0; 185 tao->reason = TAO_DIVERGED_LS_FAILURE; 186 break; 187 } 188 189 /* Update trust region radius */ 190 if (bnk->is_nash || bnk->is_stcg || bnk->is_gltr) { 191 switch(bnk->update_type) { 192 case BNK_UPDATE_STEP: 193 if (stepType == BNK_NEWTON) { 194 if (step < bnk->nu1) { 195 /* Very bad step taken; reduce radius */ 196 tao->trust = bnk->omega1 * PetscMin(norm_d, tao->trust); 197 } else if (step < bnk->nu2) { 198 /* Reasonably bad step taken; reduce radius */ 199 tao->trust = bnk->omega2 * PetscMin(norm_d, tao->trust); 200 } else if (step < bnk->nu3) { 201 /* Reasonable step was taken; leave radius alone */ 202 if (bnk->omega3 < 1.0) { 203 tao->trust = bnk->omega3 * PetscMin(norm_d, tao->trust); 204 } else if (bnk->omega3 > 1.0) { 205 tao->trust = PetscMax(bnk->omega3 * norm_d, tao->trust); 206 } 207 } else if (step < bnk->nu4) { 208 /* Full step taken; increase the radius */ 209 tao->trust = PetscMax(bnk->omega4 * norm_d, tao->trust); 210 } else { 211 /* More than full step taken; increase the radius */ 212 tao->trust = PetscMax(bnk->omega5 * norm_d, tao->trust); 213 } 214 } else { 215 /* Newton step was not good; reduce the radius */ 216 tao->trust = bnk->omega1 * PetscMin(norm_d, tao->trust); 217 } 218 break; 219 220 case BNK_UPDATE_REDUCTION: 221 if (stepType == BNK_NEWTON) { 222 /* Get predicted reduction */ 223 ierr = KSPCGGetObjFcn(tao->ksp,&prered);CHKERRQ(ierr); 224 if (prered >= 0.0) { 225 /* The predicted reduction has the wrong sign. This cannot */ 226 /* happen in infinite precision arithmetic. Step should */ 227 /* be rejected! */ 228 tao->trust = bnk->alpha1 * PetscMin(tao->trust, norm_d); 229 } else { 230 if (PetscIsInfOrNanReal(f_full)) { 231 tao->trust = bnk->alpha1 * PetscMin(tao->trust, norm_d); 232 } else { 233 /* Compute and actual reduction */ 234 actred = fold - f_full; 235 prered = -prered; 236 if ((PetscAbsScalar(actred) <= bnk->epsilon) && 237 (PetscAbsScalar(prered) <= bnk->epsilon)) { 238 kappa = 1.0; 239 } else { 240 kappa = actred / prered; 241 } 242 243 /* Accept of reject the step and update radius */ 244 if (kappa < bnk->eta1) { 245 /* Very bad step */ 246 tao->trust = bnk->alpha1 * PetscMin(tao->trust, norm_d); 247 } else if (kappa < bnk->eta2) { 248 /* Marginal bad step */ 249 tao->trust = bnk->alpha2 * PetscMin(tao->trust, norm_d); 250 } else if (kappa < bnk->eta3) { 251 /* Reasonable step */ 252 if (bnk->alpha3 < 1.0) { 253 tao->trust = bnk->alpha3 * PetscMin(norm_d, tao->trust); 254 } else if (bnk->alpha3 > 1.0) { 255 tao->trust = PetscMax(bnk->alpha3 * norm_d, tao->trust); 256 } 257 } else if (kappa < bnk->eta4) { 258 /* Good step */ 259 tao->trust = PetscMax(bnk->alpha4 * norm_d, tao->trust); 260 } else { 261 /* Very good step */ 262 tao->trust = PetscMax(bnk->alpha5 * norm_d, tao->trust); 263 } 264 } 265 } 266 } else { 267 /* Newton step was not good; reduce the radius */ 268 tao->trust = bnk->alpha1 * PetscMin(norm_d, tao->trust); 269 } 270 break; 271 272 default: 273 if (stepType == BNK_NEWTON) { 274 ierr = KSPCGGetObjFcn(tao->ksp,&prered);CHKERRQ(ierr); 275 if (prered >= 0.0) { 276 /* The predicted reduction has the wrong sign. This cannot */ 277 /* happen in infinite precision arithmetic. Step should */ 278 /* be rejected! */ 279 tao->trust = bnk->gamma1 * PetscMin(tao->trust, norm_d); 280 } else { 281 if (PetscIsInfOrNanReal(f_full)) { 282 tao->trust = bnk->gamma1 * PetscMin(tao->trust, norm_d); 283 } else { 284 actred = fold - f_full; 285 prered = -prered; 286 if ((PetscAbsScalar(actred) <= bnk->epsilon) && (PetscAbsScalar(prered) <= bnk->epsilon)) { 287 kappa = 1.0; 288 } else { 289 kappa = actred / prered; 290 } 291 292 tau_1 = bnk->theta * gdx / (bnk->theta * gdx - (1.0 - bnk->theta) * prered + actred); 293 tau_2 = bnk->theta * gdx / (bnk->theta * gdx + (1.0 + bnk->theta) * prered - actred); 294 tau_min = PetscMin(tau_1, tau_2); 295 tau_max = PetscMax(tau_1, tau_2); 296 297 if (kappa >= 1.0 - bnk->mu1) { 298 /* Great agreement */ 299 if (tau_max < 1.0) { 300 tao->trust = PetscMax(tao->trust, bnk->gamma3 * norm_d); 301 } else if (tau_max > bnk->gamma4) { 302 tao->trust = PetscMax(tao->trust, bnk->gamma4 * norm_d); 303 } else { 304 tao->trust = PetscMax(tao->trust, tau_max * norm_d); 305 } 306 } else if (kappa >= 1.0 - bnk->mu2) { 307 /* Good agreement */ 308 309 if (tau_max < bnk->gamma2) { 310 tao->trust = bnk->gamma2 * PetscMin(tao->trust, norm_d); 311 } else if (tau_max > bnk->gamma3) { 312 tao->trust = PetscMax(tao->trust, bnk->gamma3 * norm_d); 313 } else if (tau_max < 1.0) { 314 tao->trust = tau_max * PetscMin(tao->trust, norm_d); 315 } else { 316 tao->trust = PetscMax(tao->trust, tau_max * norm_d); 317 } 318 } else { 319 /* Not good agreement */ 320 if (tau_min > 1.0) { 321 tao->trust = bnk->gamma2 * PetscMin(tao->trust, norm_d); 322 } else if (tau_max < bnk->gamma1) { 323 tao->trust = bnk->gamma1 * PetscMin(tao->trust, norm_d); 324 } else if ((tau_min < bnk->gamma1) && (tau_max >= 1.0)) { 325 tao->trust = bnk->gamma1 * PetscMin(tao->trust, norm_d); 326 } else if ((tau_1 >= bnk->gamma1) && (tau_1 < 1.0) && ((tau_2 < bnk->gamma1) || (tau_2 >= 1.0))) { 327 tao->trust = tau_1 * PetscMin(tao->trust, norm_d); 328 } else if ((tau_2 >= bnk->gamma1) && (tau_2 < 1.0) && ((tau_1 < bnk->gamma1) || (tau_2 >= 1.0))) { 329 tao->trust = tau_2 * PetscMin(tao->trust, norm_d); 330 } else { 331 tao->trust = tau_max * PetscMin(tao->trust, norm_d); 332 } 333 } 334 } 335 } 336 } else { 337 /* Newton step was not good; reduce the radius */ 338 tao->trust = bnk->gamma1 * PetscMin(norm_d, tao->trust); 339 } 340 break; 341 } 342 343 /* The radius may have been increased; modify if it is too large */ 344 tao->trust = PetscMin(tao->trust, bnk->max_radius); 345 } 346 347 /* Check for termination */ 348 ierr = TaoGradientNorm(tao, tao->gradient,NORM_2,&bnk->gnorm);CHKERRQ(ierr); 349 if (PetscIsInfOrNanReal(bnk->f) || PetscIsInfOrNanReal(bnk->gnorm)) SETERRQ(PETSC_COMM_SELF,1,"User provided compute function generated Not-a-Number"); 350 needH = 1; 351 ierr = TaoLogConvergenceHistory(tao,bnk->f,bnk->gnorm,0.0,tao->ksp_its);CHKERRQ(ierr); 352 ierr = TaoMonitor(tao,tao->niter,bnk->f,bnk->gnorm,0.0,step);CHKERRQ(ierr); 353 ierr = (*tao->ops->convergencetest)(tao,tao->cnvP);CHKERRQ(ierr); 354 } 355 PetscFunctionReturn(0); 356 } 357 358 PETSC_EXTERN PetscErrorCode TaoCreate_BNLS(Tao tao) 359 { 360 PetscErrorCode ierr; 361 362 PetscFunctionBegin; 363 ierr = TaoCreate_BNK(tao);CHKERRQ(ierr); 364 tao->ops->solve=TaoSolve_BNLS; 365 PetscFunctionReturn(0); 366 }