|  | @@ -44,6 +44,7 @@
 | 
	
		
			
				|  |  |  #include "ceres/file.h"
 | 
	
		
			
				|  |  |  #include "ceres/internal/eigen.h"
 | 
	
		
			
				|  |  |  #include "ceres/internal/scoped_ptr.h"
 | 
	
		
			
				|  |  | +#include "ceres/line_search.h"
 | 
	
		
			
				|  |  |  #include "ceres/linear_least_squares_problems.h"
 | 
	
		
			
				|  |  |  #include "ceres/sparse_matrix.h"
 | 
	
		
			
				|  |  |  #include "ceres/stringprintf.h"
 | 
	
	
		
			
				|  | @@ -57,6 +58,45 @@ namespace internal {
 | 
	
		
			
				|  |  |  namespace {
 | 
	
		
			
				|  |  |  // Small constant for various floating point issues.
 | 
	
		
			
				|  |  |  const double kEpsilon = 1e-12;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +LineSearch::Summary DoLineSearch(const Minimizer::Options& options,
 | 
	
		
			
				|  |  | +                                 const Vector& x,
 | 
	
		
			
				|  |  | +                                 const Vector& gradient,
 | 
	
		
			
				|  |  | +                                 const double cost,
 | 
	
		
			
				|  |  | +                                 const Vector& delta,
 | 
	
		
			
				|  |  | +                                 Evaluator* evaluator) {
 | 
	
		
			
				|  |  | +  LineSearchFunction line_search_function(evaluator);
 | 
	
		
			
				|  |  | +  line_search_function.Init(x, delta);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  LineSearch::Options line_search_options;
 | 
	
		
			
				|  |  | +  line_search_options.interpolation_type =
 | 
	
		
			
				|  |  | +      options.line_search_interpolation_type;
 | 
	
		
			
				|  |  | +  line_search_options.min_step_size = options.min_line_search_step_size;
 | 
	
		
			
				|  |  | +  line_search_options.sufficient_decrease =
 | 
	
		
			
				|  |  | +      options.line_search_sufficient_function_decrease;
 | 
	
		
			
				|  |  | +  line_search_options.max_step_contraction =
 | 
	
		
			
				|  |  | +      options.max_line_search_step_contraction;
 | 
	
		
			
				|  |  | +  line_search_options.min_step_contraction =
 | 
	
		
			
				|  |  | +      options.min_line_search_step_contraction;
 | 
	
		
			
				|  |  | +  line_search_options.max_num_iterations =
 | 
	
		
			
				|  |  | +      options.max_num_line_search_step_size_iterations;
 | 
	
		
			
				|  |  | +  line_search_options.sufficient_curvature_decrease =
 | 
	
		
			
				|  |  | +      options.line_search_sufficient_curvature_decrease;
 | 
	
		
			
				|  |  | +  line_search_options.max_step_expansion =
 | 
	
		
			
				|  |  | +      options.max_line_search_step_expansion;
 | 
	
		
			
				|  |  | +  line_search_options.function = &line_search_function;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  string message;
 | 
	
		
			
				|  |  | +  scoped_ptr<LineSearch>
 | 
	
		
			
				|  |  | +      line_search(CHECK_NOTNULL(
 | 
	
		
			
				|  |  | +                      LineSearch::Create(ceres::ARMIJO,
 | 
	
		
			
				|  |  | +                                         line_search_options,
 | 
	
		
			
				|  |  | +                                         &message)));
 | 
	
		
			
				|  |  | +  LineSearch::Summary summary;
 | 
	
		
			
				|  |  | +  line_search->Search(1.0, cost, gradient.dot(delta), &summary);
 | 
	
		
			
				|  |  | +  return summary;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  }  // namespace
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  // Compute a scaling vector that is used to improve the conditioning
 | 
	
	
		
			
				|  | @@ -81,24 +121,30 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
 | 
	
		
			
				|  |  |    double start_time = WallTimeInSeconds();
 | 
	
		
			
				|  |  |    double iteration_start_time =  start_time;
 | 
	
		
			
				|  |  |    Init(options);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  Evaluator* evaluator = CHECK_NOTNULL(options_.evaluator);
 | 
	
		
			
				|  |  | +  SparseMatrix* jacobian = CHECK_NOTNULL(options_.jacobian);
 | 
	
		
			
				|  |  | +  TrustRegionStrategy* strategy = CHECK_NOTNULL(options_.trust_region_strategy);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    const bool is_not_silent = !options.is_silent;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  // If the problem is bounds constrained, then enable the use of a
 | 
	
		
			
				|  |  | +  // line search after the trust region step has been computed. This
 | 
	
		
			
				|  |  | +  // line search will automatically use a projected the test point
 | 
	
		
			
				|  |  | +  // onto the feasible set, there by guaranteeing the feasibility of
 | 
	
		
			
				|  |  | +  // the final output.
 | 
	
		
			
				|  |  | +  //
 | 
	
		
			
				|  |  | +  // TODO(sameeragarwal): Make line search available more generally.
 | 
	
		
			
				|  |  | +  const bool use_line_search = options.is_constrained;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    summary->termination_type = NO_CONVERGENCE;
 | 
	
		
			
				|  |  |    summary->num_successful_steps = 0;
 | 
	
		
			
				|  |  |    summary->num_unsuccessful_steps = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  Evaluator* evaluator = CHECK_NOTNULL(options_.evaluator);
 | 
	
		
			
				|  |  | -  SparseMatrix* jacobian = CHECK_NOTNULL(options_.jacobian);
 | 
	
		
			
				|  |  | -  TrustRegionStrategy* strategy = CHECK_NOTNULL(options_.trust_region_strategy);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |    const int num_parameters = evaluator->NumParameters();
 | 
	
		
			
				|  |  |    const int num_effective_parameters = evaluator->NumEffectiveParameters();
 | 
	
		
			
				|  |  |    const int num_residuals = evaluator->NumResiduals();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  VectorRef x_min(parameters, num_parameters);
 | 
	
		
			
				|  |  | -  Vector x = x_min;
 | 
	
		
			
				|  |  | -  double x_norm = x.norm();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |    Vector residuals(num_residuals);
 | 
	
		
			
				|  |  |    Vector trust_region_step(num_effective_parameters);
 | 
	
		
			
				|  |  |    Vector delta(num_effective_parameters);
 | 
	
	
		
			
				|  | @@ -121,6 +167,23 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
 | 
	
		
			
				|  |  |    iteration_summary.linear_solver_iterations = 0;
 | 
	
		
			
				|  |  |    iteration_summary.step_solver_time_in_seconds = 0;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  VectorRef x_min(parameters, num_parameters);
 | 
	
		
			
				|  |  | +  Vector x = x_min;
 | 
	
		
			
				|  |  | +  // Project onto the feasible set.
 | 
	
		
			
				|  |  | +  if (options.is_constrained) {
 | 
	
		
			
				|  |  | +    delta.setZero();
 | 
	
		
			
				|  |  | +    if (!evaluator->Plus(x.data(), delta.data(), x_plus_delta.data())) {
 | 
	
		
			
				|  |  | +      summary->message = "Unable to project initial point onto the feasible set.";
 | 
	
		
			
				|  |  | +      summary->termination_type = FAILURE;
 | 
	
		
			
				|  |  | +      LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message;
 | 
	
		
			
				|  |  | +      return;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    x_min = x_plus_delta;
 | 
	
		
			
				|  |  | +    x = x_plus_delta;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  double x_norm = x.norm();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    // Do initial cost and Jacobian evaluation.
 | 
	
		
			
				|  |  |    double cost = 0.0;
 | 
	
		
			
				|  |  |    if (!evaluator->Evaluate(x.data(),
 | 
	
	
		
			
				|  | @@ -128,9 +191,9 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
 | 
	
		
			
				|  |  |                             residuals.data(),
 | 
	
		
			
				|  |  |                             gradient.data(),
 | 
	
		
			
				|  |  |                             jacobian)) {
 | 
	
		
			
				|  |  | -    summary->message = "Terminating: Residual and Jacobian evaluation failed.";
 | 
	
		
			
				|  |  | +    summary->message = "Residual and Jacobian evaluation failed.";
 | 
	
		
			
				|  |  |      summary->termination_type = FAILURE;
 | 
	
		
			
				|  |  | -    LOG_IF(WARNING, is_not_silent) << summary->message;
 | 
	
		
			
				|  |  | +    LOG_IF(WARNING, is_not_silent) << "Terminating: " << summary->message;
 | 
	
		
			
				|  |  |      return;
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -305,19 +368,36 @@ void TrustRegionMinimizer::Minimize(const Minimizer::Options& options,
 | 
	
		
			
				|  |  |        // Undo the Jacobian column scaling.
 | 
	
		
			
				|  |  |        delta = (trust_region_step.array() * scale.array()).matrix();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -      double new_cost = numeric_limits<double>::max();
 | 
	
		
			
				|  |  | -      if (!evaluator->Plus(x.data(), delta.data(), x_plus_delta.data())) {
 | 
	
		
			
				|  |  | +      // Try improving the step further by using an ARMIJO line
 | 
	
		
			
				|  |  | +      // search.
 | 
	
		
			
				|  |  | +      //
 | 
	
		
			
				|  |  | +      // TODO(sameeragarwal): What happens to trust region sizing as
 | 
	
		
			
				|  |  | +      // it interacts with the line search ?
 | 
	
		
			
				|  |  | +      if (use_line_search) {
 | 
	
		
			
				|  |  | +        const LineSearch::Summary line_search_summary =
 | 
	
		
			
				|  |  | +            DoLineSearch(options, x, gradient, cost, delta, evaluator);
 | 
	
		
			
				|  |  | +        if (line_search_summary.success) {
 | 
	
		
			
				|  |  | +          delta *= line_search_summary.optimal_step_size;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      double new_cost = std::numeric_limits<double>::max();
 | 
	
		
			
				|  |  | +      if (evaluator->Plus(x.data(), delta.data(), x_plus_delta.data())) {
 | 
	
		
			
				|  |  | +        if (!evaluator->Evaluate(x_plus_delta.data(),
 | 
	
		
			
				|  |  | +                                &new_cost,
 | 
	
		
			
				|  |  | +                                NULL,
 | 
	
		
			
				|  |  | +                                NULL,
 | 
	
		
			
				|  |  | +                                NULL)) {
 | 
	
		
			
				|  |  | +          LOG(WARNING) << "Step failed to evaluate. "
 | 
	
		
			
				|  |  | +                       << "Treating it as a step with infinite cost";
 | 
	
		
			
				|  |  | +          new_cost = numeric_limits<double>::max();
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  |          LOG(WARNING) << "x_plus_delta = Plus(x, delta) failed. "
 | 
	
		
			
				|  |  |                       << "Treating it as a step with infinite cost";
 | 
	
		
			
				|  |  | -      } else if (!evaluator->Evaluate(x_plus_delta.data(),
 | 
	
		
			
				|  |  | -                                      &new_cost,
 | 
	
		
			
				|  |  | -                                      NULL,
 | 
	
		
			
				|  |  | -                                      NULL,
 | 
	
		
			
				|  |  | -                                      NULL)) {
 | 
	
		
			
				|  |  | -        LOG(WARNING) << "Step failed to evaluate. "
 | 
	
		
			
				|  |  | -                     << "Treating it as a step with infinite cost";
 | 
	
		
			
				|  |  | -        new_cost = numeric_limits<double>::max();
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +      if (new_cost < std::numeric_limits<double>::max()) {
 | 
	
		
			
				|  |  |          // Check if performing an inner iteration will make it better.
 | 
	
		
			
				|  |  |          if (inner_iterations_are_enabled) {
 | 
	
		
			
				|  |  |            ++summary->num_inner_iteration_steps;
 |