Esempio n. 1
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;
}
StatusType NormUpdate::checkStatus(const Solver::Generic& problem, 
				   NOX::StatusTest::CheckType checkType)
{
  if (checkType == None)
  {
    status = Unevaluated;
    normUpdate = -1.0;
    return status;
  }

  // On the first iteration, the old and current solution are the same so
  // we should return the test as unconverged until there is a valid 
  // old solution (i.e. the number of iterations is greater than zero).
  int niters = problem.getNumIterations();
  if (niters == 0) 
  {
    status = Unconverged;
    normUpdate = -1.0;
    return status;
  } 

  // Check that F exists!
  if (!problem.getSolutionGroup().isF())
  {
    status = Unconverged;
    normUpdate = -1.0;
    return status;
  } 

  const Abstract::Vector& oldSoln = problem.getPreviousSolutionGroup().getX();
  const Abstract::Vector& curSoln = problem.getSolutionGroup().getX();

  if (Teuchos::is_null(updateVectorPtr)) 
    updateVectorPtr = curSoln.clone();

  updateVectorPtr->update(1.0, curSoln, -1.0, oldSoln, 0.0); 

  int n = (scaleType == Scaled) ? updateVectorPtr->length() : 0;

  switch (normType) {
    
  case NOX::Abstract::Vector::TwoNorm:
    normUpdate = updateVectorPtr->norm();
    if (scaleType == Scaled)
      normUpdate /= sqrt(1.0 * n);
    break;

  default:
    normUpdate = updateVectorPtr->norm(normType);
    if (scaleType == Scaled)
      normUpdate /= n;
    break;

  }

  status = (normUpdate < tolerance) ? Converged : Unconverged;
  return status;
}
NOX::StatusTest::StatusType 
NOX::StatusTest::Stagnation::
checkStatus(const Solver::Generic& problem,
	    NOX::StatusTest::CheckType checkType)
{
  status = Unconverged;

  // This test should ignore the checkType!  This test must be run
  // each iteration because it triggers after a set number of
  // iterations.

  // First time through we don't do anything
  int niters = problem.getNumIterations(); 
  if (niters == 0) {
    lastIteration = 0;
    numSteps = 0;
    return Unconverged;
  }

  // Make sure we have not already counted the last nonlinear iteration.
  // This protects against multiple calls to checkStatus() in between 
  // nonlinear iterations.
  bool isCounted = false;
  if (niters == lastIteration) {
    isCounted = true;
  }
  else
    lastIteration = niters;

  // Compute the convergence rate and set counter appropriately
  if (!isCounted) {

    convRate = problem.getSolutionGroup().getNormF() / 
               problem.getPreviousSolutionGroup().getNormF();
    
    if (convRate >= tolerance)
      numSteps ++;
    else
      numSteps = 0;
   
  }

  if (numSteps >= maxSteps)
    status = Failed;

  return status;
}
NOX::StatusTest::StatusType NOX::StatusTest::MaxIters::
checkStatus(const Solver::Generic& problem, 
	    NOX::StatusTest::CheckType checkType)
{
  switch (checkType)
  {
  case NOX::StatusTest::Complete:
  case NOX::StatusTest::Minimal:
    niters = problem.getNumIterations();
    status = (niters >= maxiters) ? Failed : Unconverged;
    break;

  case NOX::StatusTest::None:
  default:
    niters = -1;
    status = Unevaluated;
    break;
  }

  return status;
}
bool NonlinearCG::compute(Abstract::Vector& dir, Abstract::Group& soln,
                          const Solver::Generic& solver)
{
  Abstract::Group::ReturnType ok;

  // Initialize vector memory if haven't already
  if(Teuchos::is_null(oldDirPtr))
    oldDirPtr = soln.getX().clone(NOX::ShapeCopy);
  if(Teuchos::is_null(oldDescentDirPtr))
    oldDescentDirPtr = soln.getX().clone(NOX::ShapeCopy);
  // These are conditionally created
  if(Teuchos::is_null(diffVecPtr) && usePRbeta)
    diffVecPtr = soln.getX().clone(NOX::ShapeCopy);
  if(Teuchos::is_null(tmpVecPtr) && doPrecondition)
    tmpVecPtr = soln.getX().clone(NOX::ShapeCopy);

  // Get a reference to the old solution group (const)
  oldSolnPtr = &solver.getPreviousSolutionGroup();
  const Abstract::Group& oldSoln(*oldSolnPtr);

  niter = solver.getNumIterations();

  // Construct Residual and precondition (if desired) as first step in 
  // getting new search direction

  ok = soln.computeF();
  if (ok != Abstract::Group::Ok) 
  {
    if (utils->isPrintType(Utils::Warning))
      utils->out() << "NOX::Direction::NonlinearCG::compute - Unable to compute F." << std::endl;
    return false;
  }

  dir = soln.getF();  

  if(doPrecondition) 
  {
    if(!soln.isJacobian())
      ok = soln.computeJacobian();
    if (ok != Abstract::Group::Ok) 
    {
      if (utils->isPrintType(Utils::Warning))
        utils->out() << "NOX::Direction::NonlinearCG::compute - Unable to compute Jacobian." << std::endl;
      return false;
    }

    *tmpVecPtr = dir;

    ok = soln.applyRightPreconditioning(false, paramsPtr->sublist("Nonlinear CG").sublist("Linear Solver"), *tmpVecPtr, dir);
    if( ok != Abstract::Group::Ok ) 
    {
      if (utils->isPrintType(Utils::Warning))
        utils->out() << "NOX::Direction::NonlinearCG::compute - Unable to apply Right Preconditioner." << std::endl;
      return false;
    }
  }

  dir.scale(-1.0);

  // Orthogonalize using previous search direction

  beta = 0.0;

  if( niter!=0 )
  {  
    // Two choices (for now) for orthogonalizing descent direction with previous:
    if( usePRbeta )
    {
      // Polak-Ribiere beta
      *diffVecPtr = dir;
      diffVecPtr->update(-1.0, *oldDescentDirPtr, 1.0); 

      double denominator = oldDescentDirPtr->innerProduct(oldSoln.getF());

      beta = diffVecPtr->innerProduct(soln.getF()) / denominator;

      // Constrain beta >= 0
      if( beta < 0.0 ) 
      {
        if (utils->isPrintType(Utils::OuterIteration))
          utils->out() << "BETA < 0, (" << beta << ") --> Resetting to zero" << std::endl;
        beta = 0.0;
      }
    } 
    else
    {
      // Fletcher-Reeves beta
      double denominator = oldDescentDirPtr->innerProduct(oldSoln.getF());

      beta = dir.innerProduct(soln.getF()) / denominator;

    } 

    //  Allow for restart after specified number of nonlinear iterations
    if( (niter % restartFrequency) == 0 )
    {
      if( utils->isPrintType(Utils::OuterIteration) )
        utils->out() << "Resetting beta --> 0" << std::endl;

      beta = 0 ;  // Restart with Steepest Descent direction
    }
  } // niter != 0

  *oldDescentDirPtr = dir;

  dir.update(beta, *oldDirPtr, 1.0);

  *oldDirPtr = dir;

  return (ok == Abstract::Group::Ok);
}
bool NOX::LineSearch::Polynomial::compute(Abstract::Group& newGrp,
                      double& step,
                      const Abstract::Vector& dir,
                      const Solver::Generic& s)
{
  printOpeningRemarks();

  int nNonlinearIters = s.getNumIterations();

  if (useCounter)
    counter.incrementNumLineSearches();

  // Get the linear solve tolerance if doing ared/pred for conv criteria
  std::string direction = const_cast<Teuchos::ParameterList&>(s.getList()).
    sublist("Direction").get("Method", "Newton");
  double eta = (suffDecrCond == AredPred) ?
    const_cast<Teuchos::ParameterList&>(s.getList()).
    sublist("Direction").sublist(direction).sublist("Linear Solver").
    get("Tolerance", -1.0) : 0.0;

  // Computations with old group
  const Abstract::Group& oldGrp = s.getPreviousSolutionGroup();
  double oldPhi = meritFuncPtr->computef(oldGrp);    // \phi(0)
  double oldValue = computeValue(oldGrp, oldPhi);
  double oldSlope = meritFuncPtr->computeSlope(dir, oldGrp);

  // Computations with new group
  step = defaultStep;
  updateGrp(newGrp, oldGrp, dir, step);
  double newPhi = meritFuncPtr->computef(newGrp);
  double newValue = computeValue(newGrp, newPhi);

  bool isConverged = false;
  bool isFailed = false;
  int nIters = 1;

  if (oldSlope >= 0.0)
  {
    printBadSlopeWarning(oldSlope);
    isFailed = true;
  }
  else
    isConverged = checkConvergence(newValue, oldValue, oldSlope, step,
                   eta, nIters, nNonlinearIters);

  // Increment the number of newton steps requiring a line search
  if ((useCounter) && (!isConverged))
    counter.incrementNumNonTrivialLineSearches();

  double prevPhi = 0.0;        // \phi(\lambda_{k-1})
  double prevPrevPhi = 0.0;    // \phi(\lambda_{k-2})
  double prevStep = 0.0;    // \lambda_{k-1}
  double prevPrevStep = 0.0;    // \lambda_{k-2}

  while ((!isConverged) && (!isFailed))
  {
    print.printStep(nIters, step, oldValue, newValue,
            "", (suffDecrCond != AredPred));

    if (nIters > maxIters)
    {
      isFailed = true;
      break;
    }

    if (interpolationType == Quadratic3)
    {
      /* 3-Point Quadratic Interpolation */

      prevPrevPhi = prevPhi;
      prevPhi = newPhi;
      prevPrevStep = prevStep;
      prevStep = step;

      if (nIters == 1)
      {
    step = 0.5 * step;
      }
      else
      {
    double c1 = prevStep * prevStep * (prevPrevPhi - oldPhi) -
      prevPrevStep * prevPrevStep * (prevPhi - oldPhi);
    double c2 = prevPrevStep * (prevPhi - oldPhi) -
      prevStep * (prevPrevPhi - oldPhi);

    if (c1 < 0)
      step = -0.5 * c1 / c2;
      }
    }

    else if ((nIters == 1) || (interpolationType == Quadratic))
    {
      /* Quadratic Interpolation */

      prevPhi = newPhi;
      prevStep = step;

      step = - (oldSlope * prevStep * prevStep) /
    (2.0 * (prevPhi - oldPhi - prevStep * oldSlope)) ;

    }

    else
    {
      /*   Cubic Interpolation */

      prevPrevPhi = prevPhi;
      prevPhi = newPhi;
      prevPrevStep = prevStep;
      prevStep = step;

      double term1 = prevPhi - oldPhi - prevStep * oldSlope ;
      double term2 = prevPrevPhi - oldPhi - prevPrevStep * oldSlope ;

      double a = 1.0 / (prevStep - prevPrevStep) *
    (term1 / (prevStep * prevStep) - term2 /
     (prevPrevStep * prevPrevStep)) ;

      double b = 1.0 / (prevStep - prevPrevStep) *
    (-1.0 * term1 * prevPrevStep / (prevStep * prevStep) +
     term2 * prevStep / (prevPrevStep * prevPrevStep)) ;

      double disc = b * b - 3.0 * a * oldSlope;

      if (disc < 0)
      {
    isFailed = true;
    break;
      }

      if (b > 0.0) // Check to prevent round off error (H. Walker)
      {
    step = -oldSlope / (b + sqrt(disc));
      }
      else
      {
    if (fabs(a) < 1.e-12) // check for when a is small
    {
      step = -oldSlope / (2.0 * b);
    }
    else
    {
      step = (-b + sqrt(disc))/ (3.0 * a);
    }
      }
    }

    // Apply bounds
    if (step < minBoundFactor * prevStep)
      step = minBoundFactor * prevStep;
    else if (step > maxBoundFactor * prevStep)
      step = maxBoundFactor * prevStep;

    // Check that step isn't too small
    if (step < minStep)
    {
      isFailed = true;
      break;
    }

    // Update the new group and compute new measures
    updateGrp(newGrp, oldGrp, dir, step);
    newPhi = meritFuncPtr->computef(newGrp);
    newValue = computeValue(newGrp, newPhi);

    nIters ++;

    if (useCounter)
      counter.incrementNumIterations();

    isConverged = checkConvergence(newValue, oldValue, oldSlope, step,
                   eta, nIters, nNonlinearIters);

  } // End while loop


  if (isFailed)
  {
    if (useCounter)
      counter.incrementNumFailedLineSearches();

    if (recoveryStepType == Constant)
      step = recoveryStep;

    if (step == 0.0)
    {
      newGrp = oldGrp;
      newPhi = oldPhi;
      newValue = oldValue;
    }
    else
    {
      updateGrp(newGrp, oldGrp, dir, step);
      newPhi = meritFuncPtr->computef(newGrp);
      newValue = computeValue(newGrp, newPhi);
    }
  }

  std::string message = (isFailed) ? "(USING RECOVERY STEP!)" : "(STEP ACCEPTED!)";
  print.printStep(nIters, step, oldValue, newValue, message, (suffDecrCond != AredPred));

  paramsPtr->set("Adjusted Tolerance", 1.0 - step * (1.0 - eta));

  if (useCounter)
    counter.setValues(*paramsPtr);

  return (!isFailed);
}