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;
}
Example #3
0
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;
}
Example #15
0
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;
}
Example #16
0
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;
}
Example #22
0
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;
}