Solver::Summary::FullReport() supports line search now.

Change-Id: Ib08d300198b85d9732cfb5785af4235ca4bd5226
diff --git a/examples/denoising.cc b/examples/denoising.cc
index 086be00..bfd8118 100644
--- a/examples/denoising.cc
+++ b/examples/denoising.cc
@@ -57,6 +57,8 @@
 DEFINE_string(output, "", "File to which the output image should be written");
 DEFINE_double(sigma, 20.0, "Standard deviation of noise");
 DEFINE_bool(verbose, false, "Prints information about the solver progress.");
+DEFINE_bool(line_search, false, "Use a line search instead of trust region "
+            "algorithm.");
 
 namespace ceres {
 namespace examples {
@@ -143,7 +145,11 @@
   if (FLAGS_verbose) {
     options.minimizer_progress_to_stdout = true;
   }
-  options.trust_region_strategy_type = ceres::LEVENBERG_MARQUARDT;
+
+  if (FLAGS_line_search) {
+    options.minimizer_type = ceres::LINE_SEARCH;
+  }
+
   options.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
   options.function_tolerance = 1e-3;  // Enough for denoising.
 
diff --git a/include/ceres/solver.h b/include/ceres/solver.h
index cb858ba..07ce0e4 100644
--- a/include/ceres/solver.h
+++ b/include/ceres/solver.h
@@ -59,7 +59,7 @@
     // Default constructor that sets up a generic sparse problem.
     Options() {
       minimizer_type = TRUST_REGION;
-      line_search_direction_type = STEEPEST_DESCENT;
+      line_search_direction_type = LBFGS;
       line_search_type = ARMIJO;
       nonlinear_conjugate_gradient_type = FLETCHER_REEVES;
       max_lbfgs_rank = 20;
@@ -578,6 +578,8 @@
     string FullReport() const;
 
     // Minimizer summary -------------------------------------------------
+    MinimizerType minimizer_type;
+
     SolverTerminationType termination_type;
 
     // If the solver did not run, or there was a failure, a
@@ -698,9 +700,14 @@
 
     TrustRegionStrategyType trust_region_strategy_type;
     DoglegType dogleg_type;
+    bool inner_iterations;
+
     SparseLinearAlgebraLibraryType sparse_linear_algebra_library;
 
-    bool inner_iterations;
+    LineSearchDirectionType line_search_direction_type;
+    LineSearchType line_search_type;
+    int max_lbfgs_rank;
+
     vector<int> inner_iteration_ordering_given;
     vector<int> inner_iteration_ordering_used;
   };
diff --git a/internal/ceres/solver.cc b/internal/ceres/solver.cc
index 3004717..e612c26 100644
--- a/internal/ceres/solver.cc
+++ b/internal/ceres/solver.cc
@@ -84,7 +84,8 @@
 Solver::Summary::Summary()
     // Invalid values for most fields, to ensure that we are not
     // accidentally reporting default values.
-    : termination_type(DID_NOT_RUN),
+    : minimizer_type(TRUST_REGION),
+      termination_type(DID_NOT_RUN),
       initial_cost(-1.0),
       final_cost(-1.0),
       fixed_cost(-1.0),
@@ -113,8 +114,10 @@
       linear_solver_type_used(SPARSE_NORMAL_CHOLESKY),
       preconditioner_type(IDENTITY),
       trust_region_strategy_type(LEVENBERG_MARQUARDT),
+      inner_iterations(false),
       sparse_linear_algebra_library(SUITE_SPARSE),
-      inner_iterations(false) {
+      line_search_direction_type(LBFGS),
+      line_search_type(ARMIJO) {
 }
 
 string Solver::Summary::BriefReport() const {
@@ -143,8 +146,10 @@
   return report;
 };
 
+using internal::StringAppendF;
+
 string Solver::Summary::FullReport() const {
-  using internal::StringAppendF;
+
 
   string report =
       "\n"
@@ -173,120 +178,185 @@
                   num_residuals, num_residuals_reduced);
   }
 
-  if (linear_solver_type_used == SPARSE_NORMAL_CHOLESKY ||
-      linear_solver_type_used == SPARSE_SCHUR ||
-      (linear_solver_type_used == ITERATIVE_SCHUR &&
-       (preconditioner_type == SCHUR_JACOBI ||
-        preconditioner_type == CLUSTER_JACOBI ||
-        preconditioner_type == CLUSTER_TRIDIAGONAL))) {
-    StringAppendF(&report, "\nSparse Linear Algebra Library %15s\n",
-                  SparseLinearAlgebraLibraryTypeToString(
-                                sparse_linear_algebra_library));
-  }
+  // TODO(sameeragarwal): Refactor this into separate functions.
 
-  StringAppendF(&report, "Trust Region Strategy     %19s",
-                TrustRegionStrategyTypeToString(
-                              trust_region_strategy_type));
-  if (trust_region_strategy_type == DOGLEG) {
-    if (dogleg_type == TRADITIONAL_DOGLEG) {
-      StringAppendF(&report, " (TRADITIONAL)");
-    } else {
-      StringAppendF(&report, " (SUBSPACE)");
+  if (minimizer_type == TRUST_REGION) {
+    StringAppendF(&report, "\nMinimizer                 %19s\n", "TRUST_REGION");
+    if (linear_solver_type_used == SPARSE_NORMAL_CHOLESKY ||
+        linear_solver_type_used == SPARSE_SCHUR ||
+        (linear_solver_type_used == ITERATIVE_SCHUR &&
+         (preconditioner_type == SCHUR_JACOBI ||
+          preconditioner_type == CLUSTER_JACOBI ||
+          preconditioner_type == CLUSTER_TRIDIAGONAL))) {
+      StringAppendF(&report, "\nSparse Linear Algebra Library %15s\n",
+                    SparseLinearAlgebraLibraryTypeToString(
+                                sparse_linear_algebra_library));
     }
-  }
-  StringAppendF(&report, "\n");
+
+    StringAppendF(&report, "Trust Region Strategy     %19s",
+                  TrustRegionStrategyTypeToString(
+                      trust_region_strategy_type));
+    if (trust_region_strategy_type == DOGLEG) {
+      if (dogleg_type == TRADITIONAL_DOGLEG) {
+        StringAppendF(&report, " (TRADITIONAL)");
+      } else {
+        StringAppendF(&report, " (SUBSPACE)");
+      }
+    }
+    StringAppendF(&report, "\n");
     StringAppendF(&report, "\n");
 
-  StringAppendF(&report,   "%45s    %21s\n", "Given",  "Used");
-  StringAppendF(&report, "Linear solver       %25s%25s\n",
-                LinearSolverTypeToString(linear_solver_type_given),
-                          LinearSolverTypeToString(linear_solver_type_used));
+    StringAppendF(&report,   "%45s    %21s\n", "Given",  "Used");
+    StringAppendF(&report, "Linear solver       %25s%25s\n",
+                  LinearSolverTypeToString(linear_solver_type_given),
+                  LinearSolverTypeToString(linear_solver_type_used));
 
-  if (linear_solver_type_given == CGNR ||
-      linear_solver_type_given == ITERATIVE_SCHUR) {
-    StringAppendF(&report, "Preconditioner      %25s%25s\n",
-                  PreconditionerTypeToString(preconditioner_type),
-                  PreconditionerTypeToString(preconditioner_type));
-  } else {
-    StringAppendF(&report, "Preconditioner      %25s%25s\n",
-                  "N/A", "N/A");
-  }
+    if (linear_solver_type_given == CGNR ||
+        linear_solver_type_given == ITERATIVE_SCHUR) {
+      StringAppendF(&report, "Preconditioner      %25s%25s\n",
+                    PreconditionerTypeToString(preconditioner_type),
+                    PreconditionerTypeToString(preconditioner_type));
+    } else {
+      StringAppendF(&report, "Preconditioner      %25s%25s\n",
+                    "N/A", "N/A");
+    }
 
-  StringAppendF(&report, "Threads:            % 25d% 25d\n",
-                num_threads_given, num_threads_used);
-  StringAppendF(&report, "Linear solver threads % 23d% 25d\n",
-                num_linear_solver_threads_given,
-                num_linear_solver_threads_used);
+    StringAppendF(&report, "Threads:            % 25d% 25d\n",
+                  num_threads_given, num_threads_used);
+    StringAppendF(&report, "Linear solver threads % 23d% 25d\n",
+                  num_linear_solver_threads_given,
+                  num_linear_solver_threads_used);
 
-  if (IsSchurType(linear_solver_type_used)) {
-    string given;
-    StringifyOrdering(linear_solver_ordering_given, &given);
-    string used;
-    StringifyOrdering(linear_solver_ordering_used, &used);
-    StringAppendF(&report,
-                  "Linear solver ordering %22s %24s\n",
-                  given.c_str(),
-                  used.c_str());
-  }
+    if (IsSchurType(linear_solver_type_used)) {
+      string given;
+      StringifyOrdering(linear_solver_ordering_given, &given);
+      string used;
+      StringifyOrdering(linear_solver_ordering_used, &used);
+      StringAppendF(&report,
+                    "Linear solver ordering %22s %24s\n",
+                    given.c_str(),
+                    used.c_str());
+    }
 
-  if (inner_iterations) {
-    string given;
-    StringifyOrdering(inner_iteration_ordering_given, &given);
-    string used;
-    StringifyOrdering(inner_iteration_ordering_used, &used);
+    if (inner_iterations) {
+      string given;
+      StringifyOrdering(inner_iteration_ordering_given, &given);
+      string used;
+      StringifyOrdering(inner_iteration_ordering_used, &used);
     StringAppendF(&report,
                   "Inner iteration ordering %20s %24s\n",
                   given.c_str(),
                   used.c_str());
+    }
+
+    if (termination_type == DID_NOT_RUN) {
+      CHECK(!error.empty())
+          << "Solver terminated with DID_NOT_RUN but the solver did not "
+          << "return a reason. This is a Ceres error. Please report this "
+          << "to the Ceres team";
+      StringAppendF(&report, "Termination:           %20s\n",
+                    "DID_NOT_RUN");
+      StringAppendF(&report, "Reason: %s\n", error.c_str());
+      return report;
+    }
+
+    StringAppendF(&report, "\nCost:\n");
+    StringAppendF(&report, "Initial        % 30e\n", initial_cost);
+    if (termination_type != NUMERICAL_FAILURE && termination_type != USER_ABORT) {
+      StringAppendF(&report, "Final          % 30e\n", final_cost);
+      StringAppendF(&report, "Change         % 30e\n",
+                    initial_cost - final_cost);
+    }
+
+    StringAppendF(&report, "\nNumber of iterations:\n");
+    StringAppendF(&report, "Successful               % 20d\n",
+                  num_successful_steps);
+    StringAppendF(&report, "Unsuccessful             % 20d\n",
+                  num_unsuccessful_steps);
+    StringAppendF(&report, "Total                    % 20d\n",
+                  num_successful_steps + num_unsuccessful_steps);
+
+    StringAppendF(&report, "\nTime (in seconds):\n");
+    StringAppendF(&report, "Preprocessor        %25.3f\n",
+                  preprocessor_time_in_seconds);
+    StringAppendF(&report, "\n  Residual Evaluations %22.3f\n",
+                  residual_evaluation_time_in_seconds);
+    StringAppendF(&report, "  Jacobian Evaluations %22.3f\n",
+                  jacobian_evaluation_time_in_seconds);
+    StringAppendF(&report, "  Linear Solver       %23.3f\n",
+                  linear_solver_time_in_seconds);
+    StringAppendF(&report, "Minimizer           %25.3f\n\n",
+                  minimizer_time_in_seconds);
+
+    StringAppendF(&report, "Postprocessor        %24.3f\n",
+                  postprocessor_time_in_seconds);
+
+    StringAppendF(&report, "Total               %25.3f\n\n",
+                  total_time_in_seconds);
+
+    StringAppendF(&report, "Termination:        %25s\n",
+                  SolverTerminationTypeToString(termination_type));
+  } else {
+    // LINE_SEARCH
+    StringAppendF(&report, "\nMinimizer                 %19s\n", "LINE_SEARCH");
+    if (line_search_direction_type == LBFGS) {
+      StringAppendF(&report,   "Line search direction     %19s(%d)\n",
+                    LineSearchDirectionTypeToString(line_search_direction_type),
+                    max_lbfgs_rank);
+    } else {
+      StringAppendF(&report,   "Line search direction     %19s\n",
+                    LineSearchDirectionTypeToString(line_search_direction_type));
+    }
+    StringAppendF(&report,   "Line search type          %19s\n",
+                  LineSearchTypeToString(line_search_type));
+
+    StringAppendF(&report, "\n");
+
+    StringAppendF(&report,   "%45s    %21s\n", "Given",  "Used");
+    StringAppendF(&report, "Threads:            % 25d% 25d\n",
+                  num_threads_given, num_threads_used);
+
+    if (termination_type == DID_NOT_RUN) {
+      CHECK(!error.empty())
+          << "Solver terminated with DID_NOT_RUN but the solver did not "
+          << "return a reason. This is a Ceres error. Please report this "
+          << "to the Ceres team";
+      StringAppendF(&report, "Termination:           %20s\n",
+                    "DID_NOT_RUN");
+      StringAppendF(&report, "Reason: %s\n", error.c_str());
+      return report;
+    }
+
+    StringAppendF(&report, "\nCost:\n");
+    StringAppendF(&report, "Initial        % 30e\n", initial_cost);
+    if (termination_type != NUMERICAL_FAILURE && termination_type != USER_ABORT) {
+      StringAppendF(&report, "Final          % 30e\n", final_cost);
+      StringAppendF(&report, "Change         % 30e\n",
+                    initial_cost - final_cost);
+    }
+
+    StringAppendF(&report, "\nNumber of iterations:    % 20ld\n", iterations.size() - 1);
+
+    StringAppendF(&report, "\nTime (in seconds):\n");
+    StringAppendF(&report, "Preprocessor        %25.3f\n",
+                  preprocessor_time_in_seconds);
+    StringAppendF(&report, "\n  Residual Evaluations %22.3f\n",
+                  residual_evaluation_time_in_seconds);
+    StringAppendF(&report, "  Jacobian Evaluations %22.3f\n",
+                  jacobian_evaluation_time_in_seconds);
+    StringAppendF(&report, "Minimizer           %25.3f\n\n",
+                  minimizer_time_in_seconds);
+
+    StringAppendF(&report, "Postprocessor        %24.3f\n",
+                  postprocessor_time_in_seconds);
+
+    StringAppendF(&report, "Total               %25.3f\n\n",
+                  total_time_in_seconds);
+
+    StringAppendF(&report, "Termination:        %25s\n",
+                  SolverTerminationTypeToString(termination_type));
   }
 
-  if (termination_type == DID_NOT_RUN) {
-    CHECK(!error.empty())
-        << "Solver terminated with DID_NOT_RUN but the solver did not "
-        << "return a reason. This is a Ceres error. Please report this "
-        << "to the Ceres team";
-    StringAppendF(&report, "Termination:           %20s\n",
-                  "DID_NOT_RUN");
-    StringAppendF(&report, "Reason: %s\n", error.c_str());
-    return report;
-  }
-
-  StringAppendF(&report, "\nCost:\n");
-  StringAppendF(&report, "Initial        % 30e\n", initial_cost);
-  if (termination_type != NUMERICAL_FAILURE && termination_type != USER_ABORT) {
-    StringAppendF(&report, "Final          % 30e\n", final_cost);
-    StringAppendF(&report, "Change         % 30e\n",
-                  initial_cost - final_cost);
-  }
-
-  StringAppendF(&report, "\nNumber of iterations:\n");
-  StringAppendF(&report, "Successful               % 20d\n",
-                num_successful_steps);
-  StringAppendF(&report, "Unsuccessful             % 20d\n",
-                num_unsuccessful_steps);
-  StringAppendF(&report, "Total                    % 20d\n",
-                num_successful_steps + num_unsuccessful_steps);
-
-  StringAppendF(&report, "\nTime (in seconds):\n");
-  StringAppendF(&report, "Preprocessor        %25.3f\n",
-                preprocessor_time_in_seconds);
-  StringAppendF(&report, "\n  Residual Evaluations %22.3f\n",
-                residual_evaluation_time_in_seconds);
-  StringAppendF(&report, "  Jacobian Evaluations %22.3f\n",
-                jacobian_evaluation_time_in_seconds);
-  StringAppendF(&report, "  Linear Solver       %23.3f\n",
-                linear_solver_time_in_seconds);
-  StringAppendF(&report, "Minimizer           %25.3f\n\n",
-                minimizer_time_in_seconds);
-
-  StringAppendF(&report, "Postprocessor        %24.3f\n",
-                postprocessor_time_in_seconds);
-
-  StringAppendF(&report, "Total               %25.3f\n\n",
-                total_time_in_seconds);
-
-  StringAppendF(&report, "Termination:        %25s\n",
-                SolverTerminationTypeToString(termination_type));
   return report;
 };
 
diff --git a/internal/ceres/solver_impl.cc b/internal/ceres/solver_impl.cc
index 475e71d..61275fc 100644
--- a/internal/ceres/solver_impl.cc
+++ b/internal/ceres/solver_impl.cc
@@ -344,6 +344,7 @@
   // Reset the summary object to its default values.
   *CHECK_NOTNULL(summary) = Solver::Summary();
 
+  summary->minimizer_type = TRUST_REGION;
   summary->num_parameter_blocks = problem_impl->NumParameterBlocks();
   summary->num_parameters = problem_impl->NumParameters();
   summary->num_residual_blocks = problem_impl->NumResidualBlocks();
@@ -676,6 +677,10 @@
   // Reset the summary object to its default values.
   *CHECK_NOTNULL(summary) = Solver::Summary();
 
+  summary->minimizer_type = LINE_SEARCH;
+  summary->line_search_direction_type = original_options.line_search_direction_type;
+  summary->max_lbfgs_rank = original_options.max_lbfgs_rank;
+  summary->line_search_type = original_options.line_search_type;
   summary->num_parameter_blocks = problem_impl->NumParameterBlocks();
   summary->num_parameters = problem_impl->NumParameters();
   summary->num_residual_blocks = problem_impl->NumResidualBlocks();
@@ -815,9 +820,6 @@
 
     // Ensure the program state is set to the user parameters on the way out.
     original_program->SetParameterBlockStatePtrsToUserStatePtrs();
-
-    summary->postprocessor_time_in_seconds =
-        WallTimeInSeconds() - post_process_start_time;
     return;
   }
 
@@ -891,6 +893,17 @@
   // Ensure the program state is set to the user parameters on the way out.
   original_program->SetParameterBlockStatePtrsToUserStatePtrs();
 
+  const map<string, double>& evaluator_time_statistics =
+      evaluator->TimeStatistics();
+
+  summary->residual_evaluation_time_in_seconds =
+      FindWithDefault(evaluator_time_statistics, "Evaluator::Residual", 0.0);
+  summary->jacobian_evaluation_time_in_seconds =
+      FindWithDefault(evaluator_time_statistics, "Evaluator::Jacobian", 0.0);
+
+  summary->postprocessor_time_in_seconds =
+      WallTimeInSeconds() - post_process_start_time;
+
   // Stick a fork in it, we're done.
   summary->postprocessor_time_in_seconds =
       WallTimeInSeconds() - post_process_start_time;