#include <../src/tao/bound/impls/bnk/bnk.h> #include /* Implements Newton's Method with a trust region approach for solving bound constrained minimization problems. This version includes a line search fall-back in the event of a trust region failure. The linear system solve has to be done with a conjugate gradient method. */ static PetscErrorCode TaoSolve_BNTL(Tao tao) { PetscErrorCode ierr; TAO_BNK *bnk = (TAO_BNK *)tao->data; TaoLineSearchConvergedReason ls_reason; PetscReal oldTrust, prered, actred, stepNorm, gdx, delta, steplen; PetscBool stepAccepted = PETSC_TRUE; PetscInt stepType, bfgsUpdates, updateType; PetscFunctionBegin; /* Project the current point onto the feasible set */ ierr = TaoComputeVariableBounds(tao);CHKERRQ(ierr); ierr = TaoLineSearchSetVariableBounds(tao->linesearch,tao->XL,tao->XU);CHKERRQ(ierr); /* Project the initial point onto the feasible region */ ierr = VecMedian(tao->XL,tao->solution,tao->XU,tao->solution);CHKERRQ(ierr); /* Check convergence criteria */ ierr = TaoComputeObjectiveAndGradient(tao, tao->solution, &bnk->f, bnk->unprojected_gradient);CHKERRQ(ierr); ierr = VecBoundGradientProjection(bnk->unprojected_gradient,tao->solution,tao->XL,tao->XU,tao->gradient);CHKERRQ(ierr); ierr = TaoGradientNorm(tao, tao->gradient,NORM_2,&bnk->gnorm);CHKERRQ(ierr); if (PetscIsInfOrNanReal(bnk->f) || PetscIsInfOrNanReal(bnk->gnorm)) SETERRQ(PETSC_COMM_SELF,1, "User provided compute function generated Inf or NaN"); tao->reason = TAO_CONTINUE_ITERATING; ierr = TaoLogConvergenceHistory(tao,bnk->f,bnk->gnorm,0.0,tao->ksp_its);CHKERRQ(ierr); ierr = TaoMonitor(tao,tao->niter,bnk->f,bnk->gnorm,0.0,tao->trust);CHKERRQ(ierr); ierr = (*tao->ops->convergencetest)(tao,tao->cnvP);CHKERRQ(ierr); if (tao->reason != TAO_CONTINUE_ITERATING) PetscFunctionReturn(0); /* Initialize the preconditioner and trust radius */ ierr = TaoBNKInitialize(tao);CHKERRQ(ierr); /* Have not converged; continue with Newton method */ while (tao->reason == TAO_CONTINUE_ITERATING) { tao->niter++; tao->ksp_its=0; /* Compute the Hessian */ ierr = TaoComputeHessian(tao,tao->solution,tao->hessian,tao->hessian_pre);CHKERRQ(ierr); /* Update the BFGS preconditioner */ if (BNK_PC_BFGS == bnk->pc_type) { if (BFGS_SCALE_PHESS == bnk->bfgs_scale_type) { /* Obtain diagonal for the bfgs preconditioner */ ierr = MatGetDiagonal(tao->hessian, bnk->Diag);CHKERRQ(ierr); ierr = VecAbs(bnk->Diag);CHKERRQ(ierr); ierr = VecReciprocal(bnk->Diag);CHKERRQ(ierr); ierr = MatLMVMSetScale(bnk->M,bnk->Diag);CHKERRQ(ierr); } /* Update the limited memory preconditioner and get existing # of updates */ ierr = MatLMVMUpdate(bnk->M, tao->solution, bnk->unprojected_gradient);CHKERRQ(ierr); } /* Use the common BNK kernel to compute the Newton step (for inactive variables only) */ ierr = TaoBNKComputeStep(tao, PETSC_FALSE, &stepType);CHKERRQ(ierr); /* Store current solution before it changes */ oldTrust = tao->trust; bnk->fold = bnk->f; ierr = VecCopy(tao->solution, bnk->Xold);CHKERRQ(ierr); ierr = VecCopy(tao->gradient, bnk->Gold);CHKERRQ(ierr); ierr = VecCopy(bnk->unprojected_gradient, bnk->unprojected_gradient_old);CHKERRQ(ierr); /* Temporarily accept the step and project it into the bounds */ ierr = VecAXPY(tao->solution, 1.0, tao->stepdirection);CHKERRQ(ierr); ierr = VecMedian(tao->XL, tao->solution, tao->XU, tao->solution);CHKERRQ(ierr); /* Check if the projection changed the step direction */ ierr = VecCopy(tao->solution, tao->stepdirection);CHKERRQ(ierr); ierr = VecAXPY(tao->stepdirection, -1.0, bnk->Xold);CHKERRQ(ierr); ierr = VecNorm(tao->stepdirection, NORM_2, &stepNorm);CHKERRQ(ierr); if (stepNorm != bnk->dnorm) { /* Projection changed the step, so we have to recompute predicted reduction. However, we deliberately do not change the step norm and the trust radius in order for the safeguard to more closely mimic a piece-wise linesearch along the bounds. */ ierr = MatMult(tao->hessian, tao->stepdirection, bnk->Xwork);CHKERRQ(ierr); ierr = VecAYPX(bnk->Xwork, -0.5, tao->gradient);CHKERRQ(ierr); ierr = VecDot(bnk->Xwork, tao->stepdirection, &prered); } else { /* Step did not change, so we can just recover the pre-computed prediction */ ierr = KSPCGGetObjFcn(tao->ksp, &prered);CHKERRQ(ierr); } prered = -prered; /* Compute the actual reduction and update the trust radius */ ierr = TaoComputeObjective(tao, tao->solution, &bnk->f);CHKERRQ(ierr); actred = bnk->fold - bnk->f; ierr = TaoBNKUpdateTrustRadius(tao, prered, actred, stepType, &stepAccepted);CHKERRQ(ierr); if (stepAccepted) { /* Step is good, evaluate the gradient and the hessian */ steplen = 1.0; ierr = TaoComputeGradient(tao, tao->solution, bnk->unprojected_gradient);CHKERRQ(ierr); ierr = VecBoundGradientProjection(bnk->unprojected_gradient,tao->solution,tao->XL,tao->XU,tao->gradient);CHKERRQ(ierr); } else { /* Trust-region rejected the step. Revert the solution. */ bnk->f = bnk->fold; ierr = VecCopy(bnk->Xold, tao->solution);CHKERRQ(ierr); /* Now check to make sure the Newton step is a descent direction... */ ierr = VecDot(tao->stepdirection, tao->gradient, &gdx);CHKERRQ(ierr); if ((gdx >= 0.0) || PetscIsInfOrNanReal(gdx)) { /* Newton step is not descent or direction produced Inf or NaN */ --bnk->newt; if (BNK_PC_BFGS != bnk->pc_type) { /* We don't have the BFGS matrix around and updated Must use gradient direction in this case */ ierr = VecCopy(tao->gradient, tao->stepdirection);CHKERRQ(ierr); ++bnk->grad; stepType = BNK_GRADIENT; } else { /* We have the BFGS matrix, so attempt to use the BFGS direction */ ierr = MatLMVMSolve(bnk->M, bnk->unprojected_gradient, tao->stepdirection);CHKERRQ(ierr); /* Check for success (descent direction) NOTE: Negative gdx here means not a descent direction because the fall-back step is missing a negative sign. */ ierr = VecDot(tao->stepdirection, tao->gradient, &gdx);CHKERRQ(ierr); if ((gdx <= 0) || PetscIsInfOrNanReal(gdx)) { /* BFGS direction is not descent or direction produced not a number We can assert bfgsUpdates > 1 in this case because the first solve produces the scaled gradient direction, which is guaranteed to be descent */ /* Use steepest descent direction (scaled) */ if (bnk->f != 0.0) { delta = 2.0 * PetscAbsScalar(bnk->f) / (bnk->gnorm*bnk->gnorm); } else { delta = 2.0 / (bnk->gnorm*bnk->gnorm); } ierr = MatLMVMSetDelta(bnk->M, delta);CHKERRQ(ierr); ierr = MatLMVMReset(bnk->M);CHKERRQ(ierr); ierr = MatLMVMUpdate(bnk->M, tao->solution, bnk->unprojected_gradient);CHKERRQ(ierr); ierr = MatLMVMSolve(bnk->M, bnk->unprojected_gradient, tao->stepdirection);CHKERRQ(ierr); ++bnk->sgrad; stepType = BNK_SCALED_GRADIENT; } else { ierr = MatLMVMGetUpdates(bnk->M, &bfgsUpdates);CHKERRQ(ierr); if (1 == bfgsUpdates) { /* The first BFGS direction is always the scaled gradient */ ++bnk->sgrad; stepType = BNK_SCALED_GRADIENT; } else { ++bnk->bfgs; stepType = BNK_BFGS; } } } } /* Make sure the safeguarded fall-back step is zero for actively bounded variables */ ierr = VecScale(tao->stepdirection, -1.0);CHKERRQ(ierr); ierr = TaoBNKBoundStep(tao, tao->stepdirection);CHKERRQ(ierr); /* Trigger the line search */ ierr = TaoBNKPerformLineSearch(tao, stepType, &steplen, &ls_reason);CHKERRQ(ierr); if (ls_reason != TAOLINESEARCH_SUCCESS && ls_reason != TAOLINESEARCH_SUCCESS_USER) { /* Line search failed, revert solution and terminate */ bnk->f = bnk->fold; ierr = VecCopy(bnk->Xold, tao->solution);CHKERRQ(ierr); ierr = VecCopy(bnk->Gold, tao->gradient);CHKERRQ(ierr); ierr = VecCopy(bnk->unprojected_gradient_old, bnk->unprojected_gradient);CHKERRQ(ierr); tao->trust = 0.0; tao->reason = TAO_DIVERGED_LS_FAILURE; } else { /* Line search succeeded so we should update the trust radius based on the LS step length */ updateType = bnk->update_type; bnk->update_type = BNK_UPDATE_STEP; tao->trust = oldTrust; ierr = TaoBNKUpdateTrustRadius(tao, prered, actred, stepType, &stepAccepted);CHKERRQ(ierr); bnk->update_type = updateType; } } /* Check for termination */ ierr = TaoGradientNorm(tao, tao->gradient, NORM_2, &bnk->gnorm);CHKERRQ(ierr); if (PetscIsInfOrNanReal(bnk->gnorm)) SETERRQ(PETSC_COMM_SELF,1,"User provided compute function generated Not-a-Number"); ierr = TaoLogConvergenceHistory(tao,bnk->f,bnk->gnorm,0.0,tao->ksp_its);CHKERRQ(ierr); ierr = TaoMonitor(tao,tao->niter,bnk->f,bnk->gnorm,0.0,steplen);CHKERRQ(ierr); ierr = (*tao->ops->convergencetest)(tao,tao->cnvP);CHKERRQ(ierr); } PetscFunctionReturn(0); } /*------------------------------------------------------------*/ PETSC_EXTERN PetscErrorCode TaoCreate_BNTL(Tao tao) { TAO_BNK *bnk; PetscErrorCode ierr; PetscFunctionBegin; ierr = TaoCreate_BNK(tao);CHKERRQ(ierr); tao->ops->solve=TaoSolve_BNTL; bnk = (TAO_BNK *)tao->data; bnk->update_type = BNK_UPDATE_REDUCTION; /* trust region updates based on predicted/actual reduction */ bnk->sval = 0.0; /* disable Hessian shifting */ PetscFunctionReturn(0); }