void NOX::MeritFunction::SumOfSquares:: computeGradient(const NOX::Abstract::Group& grp, NOX::Abstract::Vector& result) const { if ( !(grp.isF()) ) { utils->err() << "ERROR: NOX::MeritFunction::SumOfSquares::computeGradient() - " << "F has not been computed yet!. Please call " << "computeF() on the group passed into this function." << std::endl; throw "NOX Error"; } if ( !(grp.isJacobian()) ) { utils->err() << "ERROR: NOX::MeritFunction::SumOfSquares::computeGradient() - " << "Jacobian has not been computed yet!. Please call " << "computeJacobian() on the group passed into this function." << std::endl; throw "NOX Error"; } NOX::Abstract::Group::ReturnType status = grp.applyJacobianTranspose(grp.getF(), result); if (status != NOX::Abstract::Group::Ok) { utils->err() << "ERROR: NOX::MeritFunction::SumOfSquares::compute" << "Gradient - applyJacobianTranspose failed!" << std::endl; throw "NOX Error"; } return; }
double NOX::Solver::TensorBased::getNormModelResidual( const NOX::Abstract::Vector& dir, const NOX::Abstract::Group& soln, bool isTensorModel) const { // Compute residual of Newton model... Teuchos::RCP<NOX::Abstract::Vector> residualPtr = soln.getF().clone(ShapeCopy); soln.applyJacobian(dir, *residualPtr); numJvMults++; residualPtr->update(1.0, soln.getF(), 1.0); // Compute residual of Tensor model, if requested... if (isTensorModel) { double tmp = sVecPtr->innerProduct(dir); if (utilsPtr->isPrintType(NOX::Utils::Details)) utilsPtr->out() << " sc'*dt = " << utilsPtr->sciformat(tmp, 6) << std::endl; residualPtr->update(tmp*tmp, *aVecPtr, 1.0); } double modelNorm = residualPtr->norm(); return modelNorm; }
double NOX::StatusTest::NormF::computeNorm(const NOX::Abstract::Group& grp) { if (!grp.isF()) return -1.0; double norm; int n = grp.getX().length(); switch (normType) { case NOX::Abstract::Vector::TwoNorm: norm = grp.getNormF(); if (scaleType == Scaled) norm /= sqrt(1.0 * n); break; default: norm = grp.getF().norm(normType); if (scaleType == Scaled) norm /= n; break; } return norm; }
bool NOX::Direction::ModifiedNewton:: compute(NOX::Abstract::Vector& dir, NOX::Abstract::Group& soln, const NOX::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"); maxAgeOfJacobian = paramsPtr->sublist("Modified-Newton").get("Max Age of Jacobian", 10); if (Teuchos::is_null(oldJacobianGrpPtr)) { oldJacobianGrpPtr = soln.clone(DeepCopy); } NOX::Abstract::Group& oldJacobianGrp = *oldJacobianGrpPtr; status = NOX::Abstract::Group::Failed; while (status != NOX::Abstract::Group::Ok) { // Conditionally compute Jacobian at current solution. if ( (ageOfJacobian == -1) || (ageOfJacobian == maxAgeOfJacobian) ) { if (ageOfJacobian > 0) oldJacobianGrp = soln; status = oldJacobianGrp.computeJacobian(); if (status != NOX::Abstract::Group::Ok) throwError("compute", "Unable to compute Jacobian"); ageOfJacobian = 1; } else ageOfJacobian++; // Compute the Modified Newton direction status = oldJacobianGrp.applyJacobianInverse(paramsPtr->sublist("Modified-Newton").sublist("Linear Solver"), soln.getF(), dir); dir.scale(-1.0); // It didn't converge, but maybe we can recover. if ((status != NOX::Abstract::Group::Ok) && (doRescue == false)) { throwError("compute", "Unable to solve Newton system"); } else if ((status != NOX::Abstract::Group::Ok) && (doRescue == true)) { if (utils->isPrintType(NOX::Utils::Warning)) utils->out() << "WARNING: NOX::Direction::ModifiedNewton::compute() - " << "Linear solve failed to achieve convergence - " << "using the step anyway since \"Rescue Bad Newton Solve\" " << "is true. Also, flagging recompute of Jacobian." << std::endl; ageOfJacobian = maxAgeOfJacobian; status = NOX::Abstract::Group::Ok; } } return true; }
double NOX::Solver::TensorBased::getDirectionalDerivative( const NOX::Abstract::Vector& dir, const NOX::Abstract::Group& soln) const { Teuchos::RCP<NOX::Abstract::Vector> tmpPtr = soln.getF().clone(ShapeCopy); soln.applyJacobian(dir, *tmpPtr); numJvMults++; double fprime = tmpPtr->innerProduct(soln.getF()); return fprime; }
bool NOX::Direction::Newton::compute(NOX::Abstract::Vector& dir, NOX::Abstract::Group& soln, const NOX::Solver::Generic& solver) { NOX::Abstract::Group::ReturnType status; // Compute F at current solution. status = soln.computeF(); if (status != NOX::Abstract::Group::Ok) NOX::Direction::Newton::throwError("compute", "Unable to compute F"); // Reset the linear solver tolerance. if (useAdjustableForcingTerm) { resetForcingTerm(soln, solver.getPreviousSolutionGroup(), solver.getNumIterations(), solver); } else { if (utils->isPrintType(Utils::Details)) { utils->out() << " CALCULATING FORCING TERM" << endl; utils->out() << " Method: Constant" << endl; utils->out() << " Forcing Term: " << eta_k << endl; } } // Compute Jacobian at current solution. status = soln.computeJacobian(); if (status != NOX::Abstract::Group::Ok) NOX::Direction::Newton::throwError("compute", "Unable to compute Jacobian"); // Compute the Newton direction status = soln.computeNewton(paramsPtr->sublist("Newton").sublist("Linear Solver")); // It didn't converge, but maybe we can recover. if ((status != NOX::Abstract::Group::Ok) && (doRescue == false)) { NOX::Direction::Newton::throwError("compute", "Unable to solve Newton system"); } else if ((status != NOX::Abstract::Group::Ok) && (doRescue == true)) { if (utils->isPrintType(NOX::Utils::Warning)) utils->out() << "WARNING: NOX::Direction::Newton::compute() - Linear solve " << "failed to achieve convergence - using the step anyway " << "since \"Rescue Bad Newton Solve\" is true " << endl; } // Set search direction. dir = soln.getNewton(); return true; }
double NOX::MeritFunction::SumOfSquares:: computef(const NOX::Abstract::Group& grp) const { if ( !(grp.isF()) ) { utils->err() << "ERROR: NOX::MeritFunction::SumOfSquares::computef() - " << "F has not been computed yet!. Please call " << "computeF() on the group passed into this function." << std::endl; throw "NOX Error"; } return (0.5 * grp.getNormF() * grp.getNormF()); }
bool NOX::LineSearch::Polynomial:: updateGrp(NOX::Abstract::Group& newGrp, const NOX::Abstract::Group& oldGrp, const NOX::Abstract::Vector& dir, double step) const { newGrp.computeX(oldGrp, dir, step); NOX::Abstract::Group::ReturnType status = newGrp.computeF(); if (status != NOX::Abstract::Group::Ok) return false; return true; }
bool NOX::Direction::ModifiedNewton::rescueBadNewtonSolve(const NOX::Abstract::Group& grp) const { //! Check if the "rescue" option has been selected if (!doRescue) return false; //! See if the group has compute the accuracy double accuracy; NOX::Abstract::Group::ReturnType status = oldJacobianGrpPtr->getNormLastLinearSolveResidual(accuracy); // If this functionality is not supported in the group, return false /* NOTE FROM TAMMY: We could later modify this to acutally caluclate the error itself if it's just a matter of the status being NotDefined. */ if (status != NOX::Abstract::Group::Ok) return false; // Check if there is any improvement in the relative residual double normF = grp.getNormF(); // If we can't reduce the relative norm at all, we're not happy if (accuracy >= normF) return false; // Otherwise, we just print a warning and keep going if (utils->isPrintType(NOX::Utils::Warning)) utils->out() << "WARNING: NOX::Direction::ModifiedNewton::compute - Unable to achieve desired linear solve accuracy." << std::endl; return true; }
void NOX::Solver::TensorBased::printDirectionInfo(std::string dirName, const NOX::Abstract::Vector& dir, const NOX::Abstract::Group& soln, bool isTensorModel) const { double dirNorm = dir.norm(); double residual = getNormModelResidual(dir, soln, isTensorModel); double residualRel = residual / soln.getNormF(); double fprime = getDirectionalDerivative(dir, soln); double fprimeRel = fprime / dirNorm; if (utilsPtr->isPrintType(NOX::Utils::Details)) { utilsPtr->out() << " " << dirName << " norm of model residual = " << utilsPtr->sciformat(residual, 6) << " (abs) " << utilsPtr->sciformat(residualRel, 6) << " (rel)" << std::endl; utilsPtr->out() << " " << dirName << " directional derivative = " << utilsPtr->sciformat(fprime, 6) << " (abs) " << utilsPtr->sciformat(fprimeRel, 6) << " (rel)" << std::endl; utilsPtr->out() << " " << dirName << " norm = " << utilsPtr->sciformat(dirNorm, 6) << std::endl; } }
void NOX::MeritFunction::SumOfSquares:: computeQuadraticMinimizer(const NOX::Abstract::Group& grp, NOX::Abstract::Vector& result) const { // Clone a temporary vector if (Teuchos::is_null(tmpVecPtr)) tmpVecPtr = grp.getF().clone(NOX::ShapeCopy); // Make sure the function and Jacobian have been evaluated if ( !(grp.isF()) ) { utils->err() << "ERROR: NOX::MeritFunction::SumOfSquares::" << "computeQuadraticMinimizer() - " << "F has not been computed yet!. Please call " << "computeF() on the group passed into this function." << std::endl; throw "NOX Error"; } if ( !(grp.isJacobian()) ) { utils->err() << "ERROR: NOX::MeritFunction::SumOfSquares::" << "computeQuadraticMinimizer() - " << "Jacobian has not been computed yet!. Please call " << "computeJacobian() on the group passed into this function." << std::endl; throw "NOX Error"; } // Compute the steepest descent direction = J^T F this->computeGradient(grp, result); // Compute = J (J^T F) NOX::Abstract::Group::ReturnType status = grp.applyJacobian(result, *tmpVecPtr); if (status != NOX::Abstract::Group::Ok) { utils->err() << "ERROR: NOX::MeritFunction::SumOfSquares::" << "computeQuadraticMinimizer() - grp->applyJacobian() has failed!" << std::endl; throw "NOX Error"; } result.scale( -1.0 * result.innerProduct(result) / tmpVecPtr->innerProduct(*tmpVecPtr) ); }
double NOX::MeritFunction::SumOfSquares:: computeQuadraticModel(const NOX::Abstract::Vector& dir, const NOX::Abstract::Group& grp) const { if (Teuchos::is_null(tmpVecPtr)) tmpVecPtr = grp.getF().clone(); double m = 0.0; m = this->computef(grp); m += this->computeSlope(dir, grp); grp.applyJacobian(dir, *(tmpVecPtr.get())); m += 0.5 * tmpVecPtr->innerProduct(*(tmpVecPtr.get())); return m; }
double NOX::MeritFunction::SumOfSquares:: computeSlope(const NOX::Abstract::Vector& dir, const NOX::Abstract::Group& grp) const { if (Teuchos::is_null(tmpVecPtr)) tmpVecPtr = grp.getF().clone(); // If the Jacobian is not computed, approximate it with // directional derivatives. dir^T J^T F = F^T Jd if (!(grp.isJacobian())) return this->computeSlopeWithoutJacobian(dir, grp); // If the Jacobian is computed but doesn't support a gradient, // employ a different form for the inner product, eg // return <v, F> = F' * J * dir = <J'F, dir> = <g, dir> else if(!(grp.isGradient())) return this->computeSlopeWithoutJacobianTranspose(dir, grp); this->computeGradient(grp, *(tmpVecPtr.get())); return dir.innerProduct(*(tmpVecPtr.get())); }
double NOX::LineSearch::Polynomial:: computeValue(const NOX::Abstract::Group& grp, double phi) { double value = phi; if (suffDecrCond == AredPred) { value = grp.getNormF(); } return value; }
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; }
void NOX::StatusTest::NormF::relativeSetup(NOX::Abstract::Group& initialGuess) { NOX::Abstract::Group::ReturnType rtype; rtype = initialGuess.computeF(); if (rtype != NOX::Abstract::Group::Ok) { utils.err() << "NOX::StatusTest::NormF::NormF - Unable to compute F" << endl; throw "NOX Error"; } initialTolerance = computeNorm(initialGuess); trueTolerance = specifiedTolerance * initialTolerance; }
bool NOX::Solver::TensorBased::implementGlobalStrategy(NOX::Abstract::Group& newGrp, double& in_stepSize, const NOX::Solver::Generic& s) { bool ok; counter.incrementNumLineSearches(); isNewtonDirection = false; NOX::Abstract::Vector& searchDirection = *tensorVecPtr; if ((counter.getNumLineSearches() == 1) || (lsType == Newton)) { isNewtonDirection = true; searchDirection = *newtonVecPtr; } // Do line search and compute new soln. if ((lsType != Dual) || (isNewtonDirection)) ok = performLinesearch(newGrp, in_stepSize, searchDirection, s); else if (lsType == Dual) { double fTensor = 0.0; double fNew = 0.0; double tensorStep = 1.0; bool isTensorDescent = false; const Abstract::Group& oldGrp = s.getPreviousSolutionGroup(); double fprime = slopeObj.computeSlope(searchDirection, oldGrp); // Backtrack along tensor direction if it is descent direction. if (fprime < 0) { ok = performLinesearch(newGrp, in_stepSize, searchDirection, s); assert(ok); fTensor = 0.5 * newGrp.getNormF() * newGrp.getNormF(); tensorStep = in_stepSize; isTensorDescent = true; } // Backtrack along the Newton direction. ok = performLinesearch(newGrp, in_stepSize, *newtonVecPtr, s); fNew = 0.5 * newGrp.getNormF() * newGrp.getNormF(); // If backtracking on the tensor step produced a better step, then use it. if (isTensorDescent && (fTensor <= fNew)) { newGrp.computeX(oldGrp, *tensorVecPtr, tensorStep); newGrp.computeF(); } } return ok; }
bool NOX::Direction::Broyden::doRestart(NOX::Abstract::Group& soln, const NOX::Solver::LineSearchBased& solver) { // Test 1 - First iteration! if (solver.getNumIterations() == 0) return true; // Test 2 - Frequency if (cnt >= cntMax) return true; // Test 3 - Last step was zero! if (solver.getStepSize() == 0.0) return true; // Test 4 - Check for convergence rate convRate = soln.getNormF() / solver.getPreviousSolutionGroup().getNormF(); if (convRate > maxConvRate) return true; return false; }
double NOX::MeritFunction::SumOfSquares:: computeSlopeWithoutJacobian(const NOX::Abstract::Vector& dir, const NOX::Abstract::Group& grp) const { if (Teuchos::is_null(tmpVecPtr)) tmpVecPtr = grp.getF().clone(NOX::ShapeCopy); if (Teuchos::is_null(tmpGrpPtr)) tmpGrpPtr = grp.clone(NOX::ShapeCopy); // Compute the perturbation parameter double lambda = 1.0e-6; double denominator = dir.norm(); // Don't divide by zero if (denominator == 0.0) denominator = 1.0; double eta = lambda * (lambda + grp.getX().norm() / denominator); // Don't divide by zero if (eta == 0.0) eta = 1.0e-6; // Perturb the solution vector tmpVecPtr->update(eta, dir, 1.0, grp.getX(), 0.0); // Compute the new F --> F(x + eta * dir) tmpGrpPtr->setX(*(tmpVecPtr.get())); tmpGrpPtr->computeF(); // Compute Js = (F(x + eta * dir) - F(x))/eta tmpVecPtr->update(-1.0/eta, grp.getF(), 1.0/eta, tmpGrpPtr->getF(), 0.0); return(tmpVecPtr->innerProduct(grp.getF())); }
bool NOX::LineSearch::Backtrack:: compute(NOX::Abstract::Group& grp, double& step, const NOX::Abstract::Vector& dir, const NOX::Solver::Generic& s) { const Abstract::Group& oldGrp = s.getPreviousSolutionGroup(); double oldF = meritFunctionPtr->computef(oldGrp); double newF; bool isFailed = false; step = defaultStep; grp.computeX(oldGrp, dir, step); NOX::Abstract::Group::ReturnType rtype; rtype = grp.computeF(); if (rtype != NOX::Abstract::Group::Ok) { utils->err() << "NOX::LineSearch::BackTrack::compute - Unable to compute F" << std::endl; throw "NOX Error"; } newF = meritFunctionPtr->computef(grp); int nIters = 1; if (utils->isPrintType(Utils::InnerIteration)) { utils->out() << "\n" << Utils::fill(72) << "\n" << "-- Backtrack Line Search -- \n"; } NOX::StatusTest::FiniteValue checkNAN; while ( ((newF >= oldF) || (checkNAN.finiteNumberTest(newF) !=0)) && (!isFailed)) { if (utils->isPrintType(Utils::InnerIteration)) { utils->out() << std::setw(3) << nIters << ":"; utils->out() << " step = " << utils->sciformat(step); utils->out() << " old f = " << utils->sciformat(oldF); utils->out() << " new f = " << utils->sciformat(newF); utils->out() << std::endl; } nIters ++; step = step * reductionFactor; if ((step < minStep) || (nIters > maxIters)) { isFailed = true; step = recoveryStep; } grp.computeX(oldGrp, dir, step); rtype = grp.computeF(); if (rtype != NOX::Abstract::Group::Ok) { utils->err() << "NOX::LineSearch::BackTrack::compute - Unable to compute F" << std::endl; throw "NOX Error"; } newF = meritFunctionPtr->computef(grp); } if (utils->isPrintType(Utils::InnerIteration)) { utils->out() << std::setw(3) << nIters << ":"; utils->out() << " step = " << utils->sciformat(step); utils->out() << " old f = " << utils->sciformat(oldF); utils->out() << " new f = " << utils->sciformat(newF); if (isFailed) utils->out() << " (USING RECOVERY STEP!)" << std::endl; else utils->out() << " (STEP ACCEPTED!)" << std::endl; utils->out() << Utils::fill(72) << "\n" << std::endl; } return (!isFailed); }
// ************************************************************************** // *** computeForcingTerm // ************************************************************************** double NOX::Direction::Utils::InexactNewton:: computeForcingTerm(const NOX::Abstract::Group& soln, const NOX::Abstract::Group& oldsoln, int niter, const NOX::Solver::Generic& solver, double eta_last) { const std::string indent = " "; if (forcingTermMethod == Constant) { if (printing->isPrintType(NOX::Utils::Details)) { printing->out() << indent << "CALCULATING FORCING TERM" << std::endl; printing->out() << indent << "Method: Constant" << std::endl; printing->out() << indent << "Forcing Term: " << eta_k << std::endl; } if (setTolerance) paramsPtr->sublist(directionMethod).sublist("Linear Solver"). set("Tolerance", eta_k); return eta_k; } // Get linear solver current tolerance. // NOTE: These values are changing at each nonlinear iteration and // must either be updated from the parameter list each time a compute // is called or supplied during the function call! double eta_km1 = 0.0; if (eta_last < 0.0) eta_km1 = paramsPtr->sublist(directionMethod). sublist("Linear Solver").get("Tolerance", 0.0); else eta_km1 = eta_last; // Tolerance may have been adjusted in a line search algorithm so we // have to account for this. const NOX::Solver::LineSearchBased* solverPtr = 0; solverPtr = dynamic_cast<const NOX::Solver::LineSearchBased*>(&solver); if (solverPtr != 0) { eta_km1 = 1.0 - solverPtr->getStepSize() * (1.0 - eta_km1); } if (printing->isPrintType(NOX::Utils::Details)) { printing->out() << indent << "CALCULATING FORCING TERM" << std::endl; printing->out() << indent << "Method: " << method << std::endl; } if (forcingTermMethod == Type1) { if (niter == 0) { eta_k = eta_initial; } else { // Return norm of predicted F // do NOT use the following lines!! This does NOT account for // line search step length taken. // const double normpredf = 0.0; // oldsoln.getNormLastLinearSolveResidual(normpredf); // Create a new vector to be the predicted RHS if (Teuchos::is_null(predRhs)) { predRhs = oldsoln.getF().clone(ShapeCopy); } if (Teuchos::is_null(stepDir)) { stepDir = oldsoln.getF().clone(ShapeCopy); } // stepDir = X - oldX (i.e., the step times the direction) stepDir->update(1.0, soln.getX(), -1.0, oldsoln.getX(), 0); // Compute predRhs = Jacobian * step * dir if (!(oldsoln.isJacobian())) { if (printing->isPrintType(NOX::Utils::Details)) { printing->out() << "WARNING: NOX::InexactNewtonUtils::resetForcingTerm() - " << "Jacobian is out of date! Recomputing Jacobian." << std::endl; } const_cast<NOX::Abstract::Group&>(oldsoln).computeJacobian(); } oldsoln.applyJacobian(*stepDir, *predRhs); // Compute predRhs = RHSVector + predRhs (this is the predicted RHS) predRhs->update(1.0, oldsoln.getF(), 1.0); // Compute the norms double normpredf = predRhs->norm(); double normf = soln.getNormF(); double normoldf = oldsoln.getNormF(); if (printing->isPrintType(NOX::Utils::Details)) { printing->out() << indent << "Forcing Term Norm: Using L-2 Norm." << std::endl; } // Compute forcing term eta_k = fabs(normf - normpredf) / normoldf; // Some output if (printing->isPrintType(NOX::Utils::Details)) { printing->out() << indent << "Residual Norm k-1 = " << normoldf << "\n"; printing->out() << indent << "Residual Norm Linear Model k = " << normpredf << "\n"; printing->out() << indent << "Residual Norm k = " << normf << "\n"; printing->out() << indent << "Calculated eta_k (pre-bounds) = " << eta_k << std::endl; } // Impose safeguard and constraints ... const double tmp = (1.0 + sqrt(5.0)) / 2.0; const double eta_km1_alpha = pow(eta_km1, tmp); if (eta_km1_alpha > 0.1) eta_k = NOX_MAX(eta_k, eta_km1_alpha); eta_k = NOX_MAX(eta_k, eta_min); eta_k = NOX_MIN(eta_max, eta_k); } } else if (forcingTermMethod == Type2) { if (niter == 0) { eta_k = eta_initial; } else { double normf = soln.getNormF(); double normoldf = oldsoln.getNormF(); if (printing->isPrintType(NOX::Utils::Details)) { printing->out() << indent << "Forcing Term Norm: Using L-2 Norm." << std::endl; } const double residual_ratio = normf / normoldf; eta_k = gamma * pow(residual_ratio, alpha); // Some output if (printing->isPrintType(NOX::Utils::Details)) { printing->out() << indent << "Residual Norm k-1 = " << normoldf << "\n"; printing->out() << indent << "Residual Norm k = " << normf << "\n"; printing->out() << indent << "Calculated eta_k (pre-bounds) = " << eta_k << std::endl; } // Impose safeguard and constraints ... const double eta_k_alpha = gamma * pow(eta_km1, alpha); if (eta_k_alpha > 0.1) eta_k = NOX_MAX(eta_k, eta_k_alpha); eta_k = NOX_MAX(eta_k, eta_min); eta_k = NOX_MIN(eta_max, eta_k); } } // Set the new linear solver tolerance if (setTolerance) paramsPtr->sublist(directionMethod).sublist("Linear Solver"). set("Tolerance", eta_k); if (printing->isPrintType(NOX::Utils::Details)) printing->out() << indent << "Forcing Term: " << eta_k << std::endl; return eta_k; }
void MatrixFree::setGroupForComputeF(const NOX::Abstract::Group& group) { useGroupForComputeF = true; groupPtr = group.clone(); return; }
bool NOX::Solver::TensorBased::computeTensorDirection(NOX::Abstract::Group& soln, const NOX::Solver::Generic& solver) { NOX::Abstract::Group::ReturnType dir_status; Teuchos::ParameterList& linearParams = paramsPtr->sublist("Direction"). sublist(paramsPtr->sublist("Direction"). get("Method","Tensor")). sublist("Linear Solver"); // Compute F at current solution. dir_status = soln.computeF(); if (dir_status != NOX::Abstract::Group::Ok) throwError("computeTensorDirection", "Unable to compute F"); // Compute Jacobian at current solution. dir_status = soln.computeJacobian(); if (dir_status != NOX::Abstract::Group::Ok) throwError("computeTensorDirection", "Unable to compute Jacobian"); // Begin processing for the tensor step, if necessary. double sDotS = 0.0; int tempVal1 = 0; if ((nIter > 0) && (requestedBaseStep == TensorStep)) { // Compute the tensor term s = x_{k-1} - x_k *sVecPtr = soln.getX(); sVecPtr->update(1.0, solver.getPreviousSolutionGroup().getX(), -1.0); double normS = sVecPtr->norm(); sDotS = normS * normS; // Form the tensor term a = (F_{k-1} - F_k - J*s) / (s^T s)^2 soln.applyJacobian(*sVecPtr, *aVecPtr); numJvMults++; aVecPtr->update(1.0, solver.getPreviousSolutionGroup().getF(), -1.0); aVecPtr->update(-1.0, soln.getF(), 1.0); if (sDotS != 0) aVecPtr->scale(1.0 / (sDotS * sDotS)); // Save old Newton step as initial guess to second system *tmpVecPtr = *newtonVecPtr; tmpVecPtr->scale(-1.0); // Rewrite to avoid this? // Compute residual of linear system using initial guess... soln.applyJacobian(*tmpVecPtr, *residualVecPtr); numJvMults++; residualVecPtr->update(1.0, solver.getPreviousSolutionGroup().getF(),-1.0); double residualNorm = residualVecPtr->norm(); #if DEBUG_LEVEL > 0 double tmpVecNorm = tmpVecPtr->norm(); double residualNormRel = residualNorm / solver.getPreviousSolutionGroup().getNormF(); if (utilsPtr->isPrintType(NOX::Utils::Details)) { utilsPtr->out() << " Norm of initial guess: " << utilsPtr->sciformat(tmpVecNorm, 6) << std::endl; utilsPtr->out() << " initg norm of model residual = " << utilsPtr->sciformat(residualNorm, 6) << " (abs) " << utilsPtr->sciformat(residualNormRel, 6) << " (rel)" << std::endl; } #endif // Save some parameters and use them later... double tol = linearParams.get("Tolerance", 1e-4); double relativeResidual = residualNorm / solver.getPreviousSolutionGroup().getNormF(); // Decide whether to use initial guess... bool isInitialGuessGood = false; #ifdef USE_INITIAL_GUESS_LOGIC if (relativeResidual < 1.0) { if (utilsPtr->isPrintType(NOX::Utils::Details)) utilsPtr->out() << " Initial guess is good..." << std::endl; isInitialGuessGood = true; // RPP - Brett please make sure the line below is correct. *tensorVecPtr = *tmpVecPtr; double newTol = tol / relativeResidual; if (newTol > 0.99) newTol = 0.99; // force at least one iteration linearParams.set("Tolerance", newTol); if (utilsPtr->isPrintType(NOX::Utils::Details)) utilsPtr->out() << " Setting tolerance to " << utilsPtr->sciformat(newTol,6) << std::endl; } else #endif // USE_INITIAL_GUESS_LOGIC { //utilsPtr->out() << " Initial guess is BAD... do not use!\n"; isInitialGuessGood = false; *residualVecPtr = solver.getPreviousSolutionGroup().getF(); } // Compute the term inv(J)*Fp.... tmpVecPtr->init(0.0); dir_status = soln.applyJacobianInverse(linearParams, *residualVecPtr, *tmpVecPtr); // If it didn't converge, maybe we can recover. if (dir_status != NOX::Abstract::Group::Ok) { if (doRescue == false) throwError("computeTensorDirection", "Unable to apply Jacobian inverse"); else if ((doRescue == true) && (utilsPtr->isPrintType(NOX::Utils::Warning))) utilsPtr->out() << "WARNING: NOX::Solver::TensorBased::computeTensorDirection() - " << "Linear solve failed to achieve convergence - " << "using the step anyway " << "since \"Rescue Bad Newton Solve\" is true." << std::endl; } // Continue processing #ifdef USE_INITIAL_GUESS_LOGIC if (isInitialGuessGood) { tmpVecPtr->update(1.0, *tensorVecPtr, 1.0); linearParams.set("Tolerance", tol); } #endif // Save iteration count for comparison later if (linearParams.sublist("Output"). isParameter("Number of Linear Iterations")) tempVal1 = linearParams.sublist("Output"). get("Number of Linear Iterations",0); #if DEBUG_LEVEL > 0 // Compute residual of linear system with initial guess... soln.applyJacobian(*tmpVecPtr, *residualVecPtr); numJvMults++; residualVec.update(-1.0, solver.getPreviousSolutionGroup().getF(),1.0); double residualNorm2 = residualVec.norm(); double residualNorm2Rel = residualNorm2 / solver.getPreviousSolutionGroup().getNormF(); if (utilsPtr->isPrintType(NOX::Utils::Details)) utilsPtr->out() << " jifp norm of model residual = " << utilsPtr->sciformat(residualNorm2, 6) << " (abs) " << utilsPtr->sciformat(residualNorm2Rel, 6) << " (rel)" << std::endl; #endif } // Compute the Newton direction dir_status = soln.computeNewton(linearParams); // If it didn't converge, maybe we can recover. if (dir_status != NOX::Abstract::Group::Ok) { if (doRescue == false) throwError("computeTensorDirection", "Unable to apply Jacobian inverse"); else if ((doRescue == true) && (utilsPtr->isPrintType(NOX::Utils::Warning))) utilsPtr->out() << "WARNING: NOX::Solver::TensorBased::computeTensorDirection() - " << "Linear solve failed to achieve convergence - " << "using the step anyway " << "since \"Rescue Bad Newton Solve\" is true." << std::endl; } // Set Newton direction *newtonVecPtr = soln.getNewton(); // Update counter int tempVal2 = 0; if (linearParams.sublist("Output"). isParameter("Number of Linear Iterations")) tempVal2 = linearParams.sublist("Output"). get("Number of Linear Iterations",0); numJ2vMults += (tempVal1 > tempVal2) ? tempVal1 : tempVal2; #ifdef CHECK_RESIDUALS printDirectionInfo("newtonVec", *newtonVecPtr, soln, false); #endif // CHECK_RESIDUALS // Continue processing the tensor step, if necessary if ((nIter > 0) && (requestedBaseStep == TensorStep)) { // Form the term inv(J)*a... (note that a is not multiplied by 2) // The next line does not work in some implementations for some reason //tmpVec.update(1.0, newtonVec, -1.0, sVec, 1.0); tmpVecPtr->update(1.0, *newtonVecPtr, 1.0); tmpVecPtr->update(-1.0, *sVecPtr, 1.0); if (sDotS != 0.0) tmpVecPtr->scale( 1.0 / (sDotS * sDotS)); // Calculate value of beta sTinvJF = -sVecPtr->innerProduct(*newtonVecPtr); sTinvJa = sVecPtr->innerProduct(*tmpVecPtr); double qval = 0; double lambdaBar = 1; beta = calculateBeta(sTinvJa, 1.0, sTinvJF, qval, lambdaBar); double sVecNorm = sVecPtr->norm(); double aVecNorm = aVecPtr->norm(); if (utilsPtr->isPrintType(NOX::Utils::Details)) { utilsPtr->out() << " sTinvJF = " << utilsPtr->sciformat(sTinvJF, 6) << " sTinvJa = " << utilsPtr->sciformat(sTinvJa, 6) << std::endl; utilsPtr->out() << " norm(s) = " << utilsPtr->sciformat(sVecNorm, 6) << " norm(a) = " << utilsPtr->sciformat(aVecNorm, 6) << std::endl; } if (useModifiedMethod) { double alpha2 = lambdaBar; if (utilsPtr->isPrintType(NOX::Utils::Details)) utilsPtr->out() << " Beta = " << utilsPtr->sciformat(beta, 6) << " Alpha2 = " << utilsPtr->sciformat(alpha2, 6) << std::endl; if (alpha2 != 1.0) { if (utilsPtr->isPrintType(NOX::Utils::Details)) utilsPtr->out() << " *** Scaling tensor term a ***" << std::endl; aVecPtr->scale(alpha2); tmpVecPtr->scale(alpha2); sTinvJa *= alpha2; beta /= alpha2; lambdaBar = 1.0; qval = 0; } } // Form the tensor step tensorVecPtr->update(1.0, *newtonVecPtr, -beta*beta, *tmpVecPtr, 0.0); #ifdef CHECK_RESIDUALS printDirectionInfo("tensorVec", *tensorVecPtr, soln, true); #endif // CHECK_RESIDUALS #if DEBUG_LEVEL > 0 double sDotT = tensorVecPtr->innerProduct(sVec); if (utilsPtr->isPrintType(NOX::Utils::Details)) utilsPtr->out() << " Beta = " << utilsPtr->sciformat(beta, 6) << " std = " << utilsPtr->sciformat(sDotT, 6) << " qval = " << utilsPtr->sciformat(qval, 2) << " lambdaBar = " << lambdaBar << std::endl; #endif } else *tensorVecPtr = *newtonVecPtr; return true; }
bool NOX::Solver::TensorBased::performLinesearch(NOX::Abstract::Group& newSoln, double& in_stepSize, const NOX::Abstract::Vector& lsDir, const NOX::Solver::Generic& s) { if (print.isPrintType(NOX::Utils::InnerIteration)) { utilsPtr->out() << "\n" << NOX::Utils::fill(72) << "\n"; utilsPtr->out() << "-- Tensor Line Search ("; if (lsType == Curvilinear) utilsPtr->out() << "Curvilinear"; else if (lsType == Standard) utilsPtr->out() << "Standard"; else if (lsType == FullStep) utilsPtr->out() << "Full Step"; else if (lsType == Dual) utilsPtr->out() << "Dual"; utilsPtr->out() << ") -- " << std::endl; } // Local variables bool isFailed = false; bool isAcceptable = false; bool isFirstPass = true; std::string message = "(STEP ACCEPTED!)"; // Set counters int lsIterations = 1; // Get Old f const Abstract::Group& oldSoln = s.getPreviousSolutionGroup(); double fOld = 0.5 * oldSoln.getNormF() * oldSoln.getNormF(); // Compute first trial point and its function value in_stepSize = defaultStep; newSoln.computeX(oldSoln, lsDir, in_stepSize); newSoln.computeF(); double fNew = 0.5 * newSoln.getNormF() * newSoln.getNormF(); // Stop here if only using the full step if (lsType == FullStep) { print.printStep(lsIterations, in_stepSize, fOld, fNew, message); return (!isFailed); } // Compute directional derivative double fprime; if ((lsType == Curvilinear) && !(isNewtonDirection)) fprime = slopeObj.computeSlope(*newtonVecPtr, oldSoln); else fprime = slopeObj.computeSlope(lsDir, oldSoln); numJvMults++; // computeSlope() has J*v inside of it // Compute the convergence criteria for the line search double threshold = fOld + alpha*in_stepSize*fprime; isAcceptable = (fNew < threshold); // Update counter and temporarily hold direction if a linesearch is needed if (!isAcceptable) { counter.incrementNumNonTrivialLineSearches(); *tmpVecPtr = lsDir; } // Iterate until the trial point is accepted.... while (!isAcceptable) { // Check for linesearch failure if (lsIterations > maxIters) { isFailed = true; message = "(FAILED - Max Iters)"; break; } print.printStep(lsIterations, in_stepSize, fOld, fNew); // Is the full tensor step a descent direction? If not, switch to Newton if (isFirstPass && (!isNewtonDirection) && (fprime >= 0) && (lsType != Curvilinear) ) { *tmpVecPtr = *newtonVecPtr; fprime = slopeObj.computeSlope(*tmpVecPtr, oldSoln); numJvMults++; if (utilsPtr->isPrintType(NOX::Utils::Details)) utilsPtr->out() << " Switching to Newton step. New fprime = " << utilsPtr->sciformat(fprime, 6) << std::endl; } else { in_stepSize = selectLambda(fNew, fOld, fprime, in_stepSize); } isFirstPass = false; // Check for linesearch failure if (in_stepSize < minStep) { isFailed = true; message = "(FAILED - Min Step)"; break; } // Update the number of linesearch iterations counter.incrementNumIterations(); lsIterations ++; // Compute new trial point and its function value if ((lsType == Curvilinear) && !(isNewtonDirection)) { computeCurvilinearStep(*tmpVecPtr, oldSoln, s, in_stepSize); // Note: oldSoln is needed above to get correct preconditioner newSoln.computeX(oldSoln, *tmpVecPtr, 1.0); } else { newSoln.computeX(oldSoln, *tmpVecPtr, in_stepSize); } newSoln.computeF(); fNew = 0.5 * newSoln.getNormF() * newSoln.getNormF(); // Recompute convergence criteria based on new step threshold = fOld + alpha*in_stepSize*fprime; isAcceptable = (fNew < threshold); } if (isFailed) { counter.incrementNumFailedLineSearches(); if (recoveryStepType == Constant) { in_stepSize = recoveryStep; if (in_stepSize == 0.0) { newSoln = oldSoln; newSoln.computeF(); fNew = fOld; } else { // Update the group using recovery step if ((lsType == Curvilinear) && !(isNewtonDirection)) { computeCurvilinearStep(*tmpVecPtr, oldSoln, s, in_stepSize); // Note: oldSoln is needed above to get correct preconditioner newSoln.computeX(oldSoln, *tmpVecPtr, 1.0); } else { newSoln.computeX(oldSoln, *tmpVecPtr, in_stepSize); } //newSoln.computeX(oldSoln, lsDir, in_stepSize); newSoln.computeF(); fNew = 0.5 * newSoln.getNormF() * newSoln.getNormF(); message = "(USING RECOVERY STEP!)"; } } else message = "(USING LAST STEP!)"; } print.printStep(lsIterations, in_stepSize, fOld, fNew, message); counter.setValues(paramsPtr->sublist("Line Search")); return (!isFailed); }
NOX::Abstract::Group::ReturnType LOCA::Eigensolver::DGGEVStrategy::computeEigenvalues( NOX::Abstract::Group& group, Teuchos::RCP< std::vector<double> >& evals_r, Teuchos::RCP< std::vector<double> >& evals_i, Teuchos::RCP< NOX::Abstract::MultiVector >& evecs_r, Teuchos::RCP< NOX::Abstract::MultiVector >& evecs_i) { // Get LAPACK group LOCA::LAPACK::Group* grp = dynamic_cast<LOCA::LAPACK::Group*>(&group); bool hasMassMatrix = true; if (globalData->locaUtils->isPrintType(NOX::Utils::StepperIteration)) { globalData->locaUtils->out() << std::endl << globalData->locaUtils->fill(64,'=') << std::endl << "LAPACK "; if (hasMassMatrix) globalData->locaUtils->out() << "DGGEV "; else globalData->locaUtils->out() << "DGEEV "; globalData->locaUtils->out() << "Eigensolver starting." << std::endl << std::endl;; } // Make sure Jacobian & mass matrices are fresh grp->computeJacobian(); if (hasMassMatrix) grp->computeShiftedMatrix(0.0, 1.0); // Get data out of group NOX::LAPACK::Matrix<double>& jacobianMatrix = grp->getJacobianMatrix(); NOX::LAPACK::Matrix<double>& massMatrix = grp->getShiftedMatrix(); // Size of matrix int n = jacobianMatrix.numRows(); int lda = jacobianMatrix.numRows(); int ldb = massMatrix.numRows(); // Space to hold right eigenvectors double *vr = new double[n*n]; // Space to hold real and imaginary eigenvalues double *alphar = new double[n]; double *alphai = new double[n]; double *beta = new double[n]; // Size of work array, set to -1 to do a workspace query int lwork = -1; // Initial work "array" double work0; // Actual work array double *work; // Return code int info; // Copy Jacobian matrix since lapack routines overwrite it NOX::LAPACK::Matrix<double> J(jacobianMatrix); NOX::LAPACK::Matrix<double> M; // First do a workspace query if (hasMassMatrix) { // Copy mass matrix since lapack routines overwrite it M = massMatrix; DGGEV_F77("N", "V", &n, &J(0,0), &lda, &M(0,0), &ldb, alphar, alphai, beta, vr, &n, vr, &n, &work0, &lwork, &info); } else { DGEEV_F77("N", "V", &n, &J(0,0), &lda, alphar, alphai, vr, &n, vr, &n, &work0, &lwork, &info); } // Allocate work array lwork = (int) work0; work = new double[lwork]; // Calculate eigenvalues, eigenvectors if (hasMassMatrix) { DGGEV_F77("N", "V", &n, &J(0,0), &lda, &M(0,0), &ldb, alphar, alphai, beta, vr, &n, vr, &n, work, &lwork, &info); } else { DGEEV_F77("N", "V", &n, &J(0,0), &lda, alphar, alphai, vr, &n, vr, &n, work, &lwork, &info); } // Check for success if (info != 0) return NOX::Abstract::Group::Failed; // Compute all of the eigenvalues and eigenvectors before sorting std::vector<double> evals_r_tmp(n); std::vector<double> evals_i_tmp(n); Teuchos::RCP<NOX::Abstract::MultiVector> evecs_r_tmp = group.getX().createMultiVector(n, NOX::ShapeCopy); Teuchos::RCP<NOX::Abstract::MultiVector>evecs_i_tmp = group.getX().createMultiVector(n, NOX::ShapeCopy); NOX::LAPACK::Vector* tmpr; NOX::LAPACK::Vector* tmpi; double rnext; double inext; bool isComplexEval = false; bool isPrevComplexEval = false; for (int j=0; j<n; j++) { // Compute eigenvalues if (hasMassMatrix) { evals_r_tmp[j] = alphar[j]/beta[j]; evals_i_tmp[j] = alphai[j]/beta[j]; } else { evals_r_tmp[j] = alphar[j]; evals_i_tmp[j] = alphai[j]; } // Compute next eigenvalue if (!isPrevComplexEval && j < n-1) { if (hasMassMatrix) { rnext = alphar[j+1]/beta[j+1]; inext = alphai[j+1]/beta[j+1]; } else { rnext = alphar[j+1]; inext = alphai[j+1]; } // Determine if this eigenvalue is a complex conjugate pair if (fabs(evals_r_tmp[j] - rnext) < 1.0e-14*fabs(1.0+evals_r_tmp[j]) && fabs(evals_i_tmp[j] + inext) < 1.0e-14*fabs(1.0+evals_i_tmp[j])) isComplexEval = true; else isComplexEval = false; } else if (!isPrevComplexEval && j == n-1) isComplexEval = false; tmpr = dynamic_cast<NOX::LAPACK::Vector*>(&((*evecs_r_tmp)[j])); tmpi = dynamic_cast<NOX::LAPACK::Vector*>(&((*evecs_i_tmp)[j])); if (isComplexEval) for (int i=0; i<n; i++) { (*tmpr)(i) = vr[i+j*n]; (*tmpi)(i) = vr[i+(j+1)*n]; } else if (isPrevComplexEval) for (int i=0; i<n; i++) { (*tmpr)(i) = vr[i+(j-1)*n]; (*tmpi)(i) = -vr[i+j*n]; } else for (int i=0; i<n; i++) { (*tmpr)(i) = vr[i+j*n]; (*tmpi)(i) = 0.0;; } if (isPrevComplexEval) { isPrevComplexEval = false; isComplexEval = false; } if (isComplexEval) { isPrevComplexEval = true; isComplexEval = false; } } // Instantiate a sorting strategy Teuchos::RCP<LOCA::EigenvalueSort::AbstractStrategy> evalSort = globalData->locaFactory->createEigenvalueSortStrategy(topParams, eigenParams); // Create permutation array std::vector<int> perm(n); // Sort eigenvalues evalSort->sort(n, &evals_r_tmp[0], &evals_i_tmp[0], &perm); // Get first nev entries of perm std::vector<int> perm_short(perm.begin(), perm.begin()+nev); // Get sorted eigenvalues and eigenvectors evals_r = Teuchos::rcp(new std::vector<double>(evals_r_tmp.begin(), evals_r_tmp.begin()+nev)); evals_i = Teuchos::rcp(new std::vector<double>(evals_i_tmp.begin(), evals_i_tmp.begin()+nev)); evecs_r = evecs_r_tmp->subCopy(perm_short); evecs_i = evecs_i_tmp->subCopy(perm_short); // Print out eigenvalues if (globalData->locaUtils->isPrintType(NOX::Utils::StepperIteration)) { for (int i=0; i<nev; i++) globalData->locaUtils->out() << "Eigenvalue " << i << " : " << globalData->locaUtils->sciformat((*evals_r)[i]) << " " << globalData->locaUtils->sciformat((*evals_i)[i]) << " i" << std::endl; } if (globalData->locaUtils->isPrintType(NOX::Utils::StepperIteration)) globalData->locaUtils->out() << "\nLAPACK Eigensolver finished.\n" << globalData->locaUtils->fill(64,'=') << "\n" << std::endl; delete [] alphar; delete [] alphai; delete [] beta; delete [] vr; delete [] work; return NOX::Abstract::Group::Ok; }
bool NOX::Direction::Broyden::compute(NOX::Abstract::Vector& dir, NOX::Abstract::Group& soln, const NOX::Solver::LineSearchBased& solver) { // Return value for group operations (temp variable) 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"); // Check for restart if (doRestart(soln, solver)) { // Reset memory memory.reset(); // Update group if (Teuchos::is_null(oldJacobianGrpPtr)) oldJacobianGrpPtr = soln.clone(NOX::DeepCopy); else // RPP - update the entire group (this grabs state vectors in xyce). // Otherwise, xyce is forced to recalculate F at each iteration. //oldJacobianGrpPtr->setX(soln.getX()); *oldJacobianGrpPtr = soln; // Calcuate new Jacobian if (utils->isPrintType(NOX::Utils::Details)) utils->out() << " Recomputing Jacobian" << endl; status = oldJacobianGrpPtr->computeJacobian(); if (status != NOX::Abstract::Group::Ok) throwError("compute", "Unable to compute Jacobian"); // Reset counter cnt = 0; } // If necesary, scale the s-vector from the last iteration if (!memory.empty()) { double step = solver.getStepSize(); memory[memory.size() - 1].setStep(step); } // --- Calculate the Broyden direction --- // Compute inexact forcing term if requested. inexactNewtonUtils.computeForcingTerm(soln, solver.getPreviousSolutionGroup(), solver.getNumIterations(), solver); // dir = - J_old^{-1} * F cnt ++; status = oldJacobianGrpPtr->applyJacobianInverse(*lsParamsPtr, soln.getF(), dir); if (status != NOX::Abstract::Group::Ok) throwError("compute", "Unable to apply Jacobian inverse"); dir.scale(-1.0); // Apply the Broyden modifications to the old Jacobian (implicitly) if (!memory.empty()) { // Number of elements in the memory int m = memory.size(); // Information corresponding to index i double step; Teuchos::RCP<const NOX::Abstract::Vector> sPtr; // Information corresponding to index i + 1 // (initialized for i = -1) double stepNext = memory[0].step(); Teuchos::RCP<const NOX::Abstract::Vector> sPtrNext = memory[0].sPtr(); // Intermediate storage double a, b, c, denom; for (int i = 0; i < m-1; i ++) { step = stepNext; sPtr = sPtrNext; stepNext = memory[i+1].step(); sPtrNext = memory[i+1].sPtr(); a = step / stepNext; b = step - 1; c = sPtr->innerProduct(dir) / memory[i].sNormSqr(); dir.update(a * c, *sPtrNext, b * c, *sPtr, 1.0); } step = stepNext; sPtr = sPtrNext; a = sPtr->innerProduct(dir); // <s,z> b = memory[m-1].sNormSqr(); // ||s||^2 c = (step - 1) * a; // (\lambda-1) <s,z> denom = b - step * a; // ||s||^2 - \lambda <s,z> dir.update(c / denom, *sPtr, b / denom); } //! Add this direction to the memory memory.push(dir); return true; }