bool NOX::Direction::QuasiNewton::compute(NOX::Abstract::Vector& dir, NOX::Abstract::Group& soln, const Solver::Generic& solver) { NOX::Abstract::Group::ReturnType status; // Compute F at current solution status = soln.computeF(); if (status != NOX::Abstract::Group::Ok) throwError("compute", "Unable to compute F"); // Compute Jacobian at current solution. status = soln.computeJacobian(); if (status != NOX::Abstract::Group::Ok) throwError("compute", "Unable to compute Jacobian"); // Compute the gradient at the current solution status = soln.computeGradient(); if (status != NOX::Abstract::Group::Ok) throwError("compute", "Unable to compute gradient"); // Push the old information onto the memory, but only after at least one previous iteration if (solver.getNumIterations() > 0) { const NOX::Abstract::Group& oldSoln = solver.getPreviousSolutionGroup(); if (oldSoln.isGradient()) memory.add(soln.getX(), oldSoln.getX(), soln.getGradient(), oldSoln.getGradient()); } // *** Calculate the QN direction *** // d = -g dir = soln.getGradient(); dir.scale(-1.0); if (!memory.empty()) { int m = memory.size(); vector<double> alpha(m); double beta; for (int i = m-1; i >= 0; i --) { alpha[i] = memory[i].rho() * dir.innerProduct( memory[i].s() ); dir.update(-1.0 * alpha[i], memory[i].y(), 1.0); } dir.scale( memory[m-1].sdoty() / memory[m-1].ydoty() ); for (int i = 0; i < m; i ++) { beta = memory[i].rho() * dir.innerProduct( memory[i].y() ); dir.update(alpha[i] - beta, memory[i].s(), 1.0); } } return true; }
StatusType NormUpdate::checkStatus(const Solver::Generic& problem, NOX::StatusTest::CheckType checkType) { if (checkType == None) { status = Unevaluated; normUpdate = -1.0; return status; } // On the first iteration, the old and current solution are the same so // we should return the test as unconverged until there is a valid // old solution (i.e. the number of iterations is greater than zero). int niters = problem.getNumIterations(); if (niters == 0) { status = Unconverged; normUpdate = -1.0; return status; } // Check that F exists! if (!problem.getSolutionGroup().isF()) { status = Unconverged; normUpdate = -1.0; return status; } const Abstract::Vector& oldSoln = problem.getPreviousSolutionGroup().getX(); const Abstract::Vector& curSoln = problem.getSolutionGroup().getX(); if (Teuchos::is_null(updateVectorPtr)) updateVectorPtr = curSoln.clone(); updateVectorPtr->update(1.0, curSoln, -1.0, oldSoln, 0.0); int n = (scaleType == Scaled) ? updateVectorPtr->length() : 0; switch (normType) { case NOX::Abstract::Vector::TwoNorm: normUpdate = updateVectorPtr->norm(); if (scaleType == Scaled) normUpdate /= sqrt(1.0 * n); break; default: normUpdate = updateVectorPtr->norm(normType); if (scaleType == Scaled) normUpdate /= n; break; } status = (normUpdate < tolerance) ? Converged : Unconverged; return status; }
NOX::StatusTest::StatusType NOX::StatusTest::Stagnation:: checkStatus(const Solver::Generic& problem, NOX::StatusTest::CheckType checkType) { status = Unconverged; // This test should ignore the checkType! This test must be run // each iteration because it triggers after a set number of // iterations. // First time through we don't do anything int niters = problem.getNumIterations(); if (niters == 0) { lastIteration = 0; numSteps = 0; return Unconverged; } // Make sure we have not already counted the last nonlinear iteration. // This protects against multiple calls to checkStatus() in between // nonlinear iterations. bool isCounted = false; if (niters == lastIteration) { isCounted = true; } else lastIteration = niters; // Compute the convergence rate and set counter appropriately if (!isCounted) { convRate = problem.getSolutionGroup().getNormF() / problem.getPreviousSolutionGroup().getNormF(); if (convRate >= tolerance) numSteps ++; else numSteps = 0; } if (numSteps >= maxSteps) status = Failed; return status; }
NOX::StatusTest::StatusType NOX::StatusTest::MaxIters:: checkStatus(const Solver::Generic& problem, NOX::StatusTest::CheckType checkType) { switch (checkType) { case NOX::StatusTest::Complete: case NOX::StatusTest::Minimal: niters = problem.getNumIterations(); status = (niters >= maxiters) ? Failed : Unconverged; break; case NOX::StatusTest::None: default: niters = -1; status = Unevaluated; break; } return status; }
bool NonlinearCG::compute(Abstract::Vector& dir, Abstract::Group& soln, const Solver::Generic& solver) { Abstract::Group::ReturnType ok; // Initialize vector memory if haven't already if(Teuchos::is_null(oldDirPtr)) oldDirPtr = soln.getX().clone(NOX::ShapeCopy); if(Teuchos::is_null(oldDescentDirPtr)) oldDescentDirPtr = soln.getX().clone(NOX::ShapeCopy); // These are conditionally created if(Teuchos::is_null(diffVecPtr) && usePRbeta) diffVecPtr = soln.getX().clone(NOX::ShapeCopy); if(Teuchos::is_null(tmpVecPtr) && doPrecondition) tmpVecPtr = soln.getX().clone(NOX::ShapeCopy); // Get a reference to the old solution group (const) oldSolnPtr = &solver.getPreviousSolutionGroup(); const Abstract::Group& oldSoln(*oldSolnPtr); niter = solver.getNumIterations(); // Construct Residual and precondition (if desired) as first step in // getting new search direction ok = soln.computeF(); if (ok != Abstract::Group::Ok) { if (utils->isPrintType(Utils::Warning)) utils->out() << "NOX::Direction::NonlinearCG::compute - Unable to compute F." << std::endl; return false; } dir = soln.getF(); if(doPrecondition) { if(!soln.isJacobian()) ok = soln.computeJacobian(); if (ok != Abstract::Group::Ok) { if (utils->isPrintType(Utils::Warning)) utils->out() << "NOX::Direction::NonlinearCG::compute - Unable to compute Jacobian." << std::endl; return false; } *tmpVecPtr = dir; ok = soln.applyRightPreconditioning(false, paramsPtr->sublist("Nonlinear CG").sublist("Linear Solver"), *tmpVecPtr, dir); if( ok != Abstract::Group::Ok ) { if (utils->isPrintType(Utils::Warning)) utils->out() << "NOX::Direction::NonlinearCG::compute - Unable to apply Right Preconditioner." << std::endl; return false; } } dir.scale(-1.0); // Orthogonalize using previous search direction beta = 0.0; if( niter!=0 ) { // Two choices (for now) for orthogonalizing descent direction with previous: if( usePRbeta ) { // Polak-Ribiere beta *diffVecPtr = dir; diffVecPtr->update(-1.0, *oldDescentDirPtr, 1.0); double denominator = oldDescentDirPtr->innerProduct(oldSoln.getF()); beta = diffVecPtr->innerProduct(soln.getF()) / denominator; // Constrain beta >= 0 if( beta < 0.0 ) { if (utils->isPrintType(Utils::OuterIteration)) utils->out() << "BETA < 0, (" << beta << ") --> Resetting to zero" << std::endl; beta = 0.0; } } else { // Fletcher-Reeves beta double denominator = oldDescentDirPtr->innerProduct(oldSoln.getF()); beta = dir.innerProduct(soln.getF()) / denominator; } // Allow for restart after specified number of nonlinear iterations if( (niter % restartFrequency) == 0 ) { if( utils->isPrintType(Utils::OuterIteration) ) utils->out() << "Resetting beta --> 0" << std::endl; beta = 0 ; // Restart with Steepest Descent direction } } // niter != 0 *oldDescentDirPtr = dir; dir.update(beta, *oldDirPtr, 1.0); *oldDirPtr = dir; return (ok == Abstract::Group::Ok); }
bool NOX::LineSearch::Polynomial::compute(Abstract::Group& newGrp, double& step, const Abstract::Vector& dir, const Solver::Generic& s) { printOpeningRemarks(); int nNonlinearIters = s.getNumIterations(); if (useCounter) counter.incrementNumLineSearches(); // Get the linear solve tolerance if doing ared/pred for conv criteria std::string direction = const_cast<Teuchos::ParameterList&>(s.getList()). sublist("Direction").get("Method", "Newton"); double eta = (suffDecrCond == AredPred) ? const_cast<Teuchos::ParameterList&>(s.getList()). sublist("Direction").sublist(direction).sublist("Linear Solver"). get("Tolerance", -1.0) : 0.0; // Computations with old group const Abstract::Group& oldGrp = s.getPreviousSolutionGroup(); double oldPhi = meritFuncPtr->computef(oldGrp); // \phi(0) double oldValue = computeValue(oldGrp, oldPhi); double oldSlope = meritFuncPtr->computeSlope(dir, oldGrp); // Computations with new group step = defaultStep; updateGrp(newGrp, oldGrp, dir, step); double newPhi = meritFuncPtr->computef(newGrp); double newValue = computeValue(newGrp, newPhi); bool isConverged = false; bool isFailed = false; int nIters = 1; if (oldSlope >= 0.0) { printBadSlopeWarning(oldSlope); isFailed = true; } else isConverged = checkConvergence(newValue, oldValue, oldSlope, step, eta, nIters, nNonlinearIters); // Increment the number of newton steps requiring a line search if ((useCounter) && (!isConverged)) counter.incrementNumNonTrivialLineSearches(); double prevPhi = 0.0; // \phi(\lambda_{k-1}) double prevPrevPhi = 0.0; // \phi(\lambda_{k-2}) double prevStep = 0.0; // \lambda_{k-1} double prevPrevStep = 0.0; // \lambda_{k-2} while ((!isConverged) && (!isFailed)) { print.printStep(nIters, step, oldValue, newValue, "", (suffDecrCond != AredPred)); if (nIters > maxIters) { isFailed = true; break; } if (interpolationType == Quadratic3) { /* 3-Point Quadratic Interpolation */ prevPrevPhi = prevPhi; prevPhi = newPhi; prevPrevStep = prevStep; prevStep = step; if (nIters == 1) { step = 0.5 * step; } else { double c1 = prevStep * prevStep * (prevPrevPhi - oldPhi) - prevPrevStep * prevPrevStep * (prevPhi - oldPhi); double c2 = prevPrevStep * (prevPhi - oldPhi) - prevStep * (prevPrevPhi - oldPhi); if (c1 < 0) step = -0.5 * c1 / c2; } } else if ((nIters == 1) || (interpolationType == Quadratic)) { /* Quadratic Interpolation */ prevPhi = newPhi; prevStep = step; step = - (oldSlope * prevStep * prevStep) / (2.0 * (prevPhi - oldPhi - prevStep * oldSlope)) ; } else { /* Cubic Interpolation */ prevPrevPhi = prevPhi; prevPhi = newPhi; prevPrevStep = prevStep; prevStep = step; double term1 = prevPhi - oldPhi - prevStep * oldSlope ; double term2 = prevPrevPhi - oldPhi - prevPrevStep * oldSlope ; double a = 1.0 / (prevStep - prevPrevStep) * (term1 / (prevStep * prevStep) - term2 / (prevPrevStep * prevPrevStep)) ; double b = 1.0 / (prevStep - prevPrevStep) * (-1.0 * term1 * prevPrevStep / (prevStep * prevStep) + term2 * prevStep / (prevPrevStep * prevPrevStep)) ; double disc = b * b - 3.0 * a * oldSlope; if (disc < 0) { isFailed = true; break; } if (b > 0.0) // Check to prevent round off error (H. Walker) { step = -oldSlope / (b + sqrt(disc)); } else { if (fabs(a) < 1.e-12) // check for when a is small { step = -oldSlope / (2.0 * b); } else { step = (-b + sqrt(disc))/ (3.0 * a); } } } // Apply bounds if (step < minBoundFactor * prevStep) step = minBoundFactor * prevStep; else if (step > maxBoundFactor * prevStep) step = maxBoundFactor * prevStep; // Check that step isn't too small if (step < minStep) { isFailed = true; break; } // Update the new group and compute new measures updateGrp(newGrp, oldGrp, dir, step); newPhi = meritFuncPtr->computef(newGrp); newValue = computeValue(newGrp, newPhi); nIters ++; if (useCounter) counter.incrementNumIterations(); isConverged = checkConvergence(newValue, oldValue, oldSlope, step, eta, nIters, nNonlinearIters); } // End while loop if (isFailed) { if (useCounter) counter.incrementNumFailedLineSearches(); if (recoveryStepType == Constant) step = recoveryStep; if (step == 0.0) { newGrp = oldGrp; newPhi = oldPhi; newValue = oldValue; } else { updateGrp(newGrp, oldGrp, dir, step); newPhi = meritFuncPtr->computef(newGrp); newValue = computeValue(newGrp, newPhi); } } std::string message = (isFailed) ? "(USING RECOVERY STEP!)" : "(STEP ACCEPTED!)"; print.printStep(nIters, step, oldValue, newValue, message, (suffDecrCond != AredPred)); paramsPtr->set("Adjusted Tolerance", 1.0 - step * (1.0 - eta)); if (useCounter) counter.setValues(*paramsPtr); return (!isFailed); }