Compute & report timing information for line searches.

- We now compute & report the cumulative time spent performing the
  following tasks as part of a line search:
  - Evaluation of the univariate cost function value & gradient.
  - Minimization of the interpolating polynomial.
  - Total time spent performing line searches.
- This information is now reported for all minimizers, although only in
  the case of a constrained problem for the TR minimizer.
- Remove LineSearch::Function abstraction in place of using
  LineSearchFunction implementation directly, and remove virtual
  functions from LineSearchFunction.
-- LineSearch::Function added an unnecessary level of abstraction since
   the user always had to create a LineSearchFunction anyway to use a
   Ceres Evaluator, and it added an unncessary virtual function call.

Change-Id: Ia4e1921d78f351ae119875aa97a3ea5e8b5d9877
diff --git a/internal/ceres/gradient_problem_solver.cc b/internal/ceres/gradient_problem_solver.cc
index 4024f4c..4fda929 100644
--- a/internal/ceres/gradient_problem_solver.cc
+++ b/internal/ceres/gradient_problem_solver.cc
@@ -138,6 +138,7 @@
   solver_summary.fixed_cost = 0.0;
   solver_summary.preprocessor_time_in_seconds = 0.0;
   solver_summary.postprocessor_time_in_seconds = 0.0;
+  solver_summary.line_search_polynomial_minimization_time_in_seconds = 0.0;
 
   minimizer->Minimize(minimizer_options, solution.data(), &solver_summary);
 
@@ -146,6 +147,8 @@
   summary->initial_cost     = solver_summary.initial_cost;
   summary->final_cost       = solver_summary.final_cost;
   summary->iterations       = solver_summary.iterations;
+  summary->line_search_polynomial_minimization_time_in_seconds =
+      solver_summary.line_search_polynomial_minimization_time_in_seconds;
 
   if (summary->IsSolutionUsable()) {
     parameters = solution;
@@ -172,6 +175,7 @@
       total_time_in_seconds(-1.0),
       cost_evaluation_time_in_seconds(-1.0),
       gradient_evaluation_time_in_seconds(-1.0),
+      line_search_polynomial_minimization_time_in_seconds(-1.0),
       num_parameters(-1),
       num_local_parameters(-1),
       line_search_direction_type(LBFGS),
@@ -246,12 +250,14 @@
 
   StringAppendF(&report, "\nTime (in seconds):\n");
 
-  StringAppendF(&report, "\n  Cost evaluation     %23.3f\n",
+  StringAppendF(&report, "\n  Cost evaluation     %23.4f\n",
                 cost_evaluation_time_in_seconds);
-  StringAppendF(&report, "  Gradient evaluation %23.3f\n",
+  StringAppendF(&report, "  Gradient evaluation %23.4f\n",
                 gradient_evaluation_time_in_seconds);
+  StringAppendF(&report, "  Polynomial minimization   %17.4f\n",
+                line_search_polynomial_minimization_time_in_seconds);
 
-  StringAppendF(&report, "Total               %25.3f\n\n",
+  StringAppendF(&report, "Total               %25.4f\n\n",
                 total_time_in_seconds);
 
   StringAppendF(&report, "Termination:        %25s (%s)\n",
diff --git a/internal/ceres/line_search.cc b/internal/ceres/line_search.cc
index 0d645ac..8c2624e 100644
--- a/internal/ceres/line_search.cc
+++ b/internal/ceres/line_search.cc
@@ -28,17 +28,19 @@
 //
 // Author: sameeragarwal@google.com (Sameer Agarwal)
 
+#include "ceres/line_search.h"
+
 #include <iomanip>
 #include <iostream>  // NOLINT
 
-#include "ceres/line_search.h"
-
-#include "ceres/fpclassify.h"
+#include "glog/logging.h"
 #include "ceres/evaluator.h"
 #include "ceres/internal/eigen.h"
+#include "ceres/fpclassify.h"
+#include "ceres/map_util.h"
 #include "ceres/polynomial.h"
 #include "ceres/stringprintf.h"
-#include "glog/logging.h"
+#include "ceres/wall_time.h"
 
 namespace ceres {
 namespace internal {
@@ -106,8 +108,9 @@
       direction_(evaluator->NumEffectiveParameters()),
       evaluation_point_(evaluator->NumParameters()),
       scaled_direction_(evaluator->NumEffectiveParameters()),
-      gradient_(evaluator->NumEffectiveParameters()) {
-}
+      gradient_(evaluator->NumEffectiveParameters()),
+      initial_evaluator_residual_time_in_seconds(0.0),
+      initial_evaluator_jacobian_time_in_seconds(0.0) {}
 
 void LineSearchFunction::Init(const Vector& position,
                               const Vector& direction) {
@@ -142,6 +145,54 @@
   return direction_.lpNorm<Eigen::Infinity>();
 }
 
+void LineSearchFunction::ResetTimeStatistics() {
+  const map<string, double> evaluator_time_statistics =
+      evaluator_->TimeStatistics();
+  initial_evaluator_residual_time_in_seconds =
+      FindWithDefault(evaluator_time_statistics, "Evaluator::Residual", 0.0);
+  initial_evaluator_jacobian_time_in_seconds =
+      FindWithDefault(evaluator_time_statistics, "Evaluator::Jacobian", 0.0);
+}
+
+void LineSearchFunction::TimeStatistics(
+    double* cost_evaluation_time_in_seconds,
+    double* gradient_evaluation_time_in_seconds) const {
+  const map<string, double> evaluator_time_statistics =
+      evaluator_->TimeStatistics();
+  *cost_evaluation_time_in_seconds =
+      FindWithDefault(evaluator_time_statistics, "Evaluator::Residual", 0.0) -
+      initial_evaluator_residual_time_in_seconds;
+  // Strictly speaking this will slightly underestimate the time spent
+  // evaluating the gradient of the line search univariate cost function as it
+  // does not count the time spent performing the dot product with the direction
+  // vector.  However, this will typically be small by comparison, and also
+  // allows direct subtraction of the timing information from the totals for
+  // the evaluator returned in the solver summary.
+  *gradient_evaluation_time_in_seconds =
+      FindWithDefault(evaluator_time_statistics, "Evaluator::Jacobian", 0.0) -
+      initial_evaluator_jacobian_time_in_seconds;
+}
+
+void LineSearch::Search(double step_size_estimate,
+                        double initial_cost,
+                        double initial_gradient,
+                        Summary* summary) const {
+  const double start_time = WallTimeInSeconds();
+  *CHECK_NOTNULL(summary) = LineSearch::Summary();
+
+  summary->cost_evaluation_time_in_seconds = 0.0;
+  summary->gradient_evaluation_time_in_seconds = 0.0;
+  summary->polynomial_minimization_time_in_seconds = 0.0;
+
+  options().function->ResetTimeStatistics();
+  this->DoSearch(step_size_estimate, initial_cost, initial_gradient, summary);
+  options().function->
+      TimeStatistics(&summary->cost_evaluation_time_in_seconds,
+                     &summary->gradient_evaluation_time_in_seconds);
+
+  summary->total_time_in_seconds = WallTimeInSeconds() - start_time;
+}
+
 // Returns step_size \in [min_step_size, max_step_size] which minimizes the
 // polynomial of degree defined by interpolation_type which interpolates all
 // of the provided samples with valid values.
@@ -217,16 +268,15 @@
 ArmijoLineSearch::ArmijoLineSearch(const LineSearch::Options& options)
     : LineSearch(options) {}
 
-void ArmijoLineSearch::Search(const double step_size_estimate,
-                              const double initial_cost,
-                              const double initial_gradient,
-                              Summary* summary) {
-  *CHECK_NOTNULL(summary) = LineSearch::Summary();
+void ArmijoLineSearch::DoSearch(const double step_size_estimate,
+                                const double initial_cost,
+                                const double initial_gradient,
+                                Summary* summary) const {
   CHECK_GE(step_size_estimate, 0.0);
   CHECK_GT(options().sufficient_decrease, 0.0);
   CHECK_LT(options().sufficient_decrease, 1.0);
   CHECK_GT(options().max_num_iterations, 0);
-  Function* function = options().function;
+  LineSearchFunction* function = options().function;
 
   // Note initial_cost & initial_gradient are evaluated at step_size = 0,
   // not step_size_estimate, which is our starting guess.
@@ -245,8 +295,7 @@
   // gradient at the current query point.
   const bool interpolation_uses_gradient_at_current_sample =
       options().interpolation_type == CUBIC;
-  const double descent_direction_max_norm =
-      static_cast<const LineSearchFunction*>(function)->DirectionInfinityNorm();
+  const double descent_direction_max_norm = function->DirectionInfinityNorm();
 
   ++summary->num_function_evaluations;
   if (interpolation_uses_gradient_at_current_sample) {
@@ -277,6 +326,7 @@
       return;
     }
 
+    const double polynomial_minimization_start_time = WallTimeInSeconds();
     const double step_size =
         this->InterpolatingPolynomialMinimizingStepSize(
             options().interpolation_type,
@@ -285,6 +335,8 @@
             current,
             (options().max_step_contraction * current.x),
             (options().min_step_contraction * current.x));
+    summary->polynomial_minimization_time_in_seconds +=
+        (WallTimeInSeconds() - polynomial_minimization_start_time);
 
     if (step_size * descent_direction_max_norm < options().min_step_size) {
       summary->error =
@@ -318,11 +370,10 @@
 WolfeLineSearch::WolfeLineSearch(const LineSearch::Options& options)
     : LineSearch(options) {}
 
-void WolfeLineSearch::Search(const double step_size_estimate,
-                             const double initial_cost,
-                             const double initial_gradient,
-                             Summary* summary) {
-  *CHECK_NOTNULL(summary) = LineSearch::Summary();
+void WolfeLineSearch::DoSearch(const double step_size_estimate,
+                               const double initial_cost,
+                               const double initial_gradient,
+                               Summary* summary) const {
   // All parameters should have been validated by the Solver, but as
   // invalid values would produce crazy nonsense, hard check them here.
   CHECK_GE(step_size_estimate, 0.0);
@@ -454,15 +505,15 @@
     FunctionSample* bracket_low,
     FunctionSample* bracket_high,
     bool* do_zoom_search,
-    Summary* summary) {
-  Function* function = options().function;
+    Summary* summary) const {
+  LineSearchFunction* function = options().function;
 
   FunctionSample previous = initial_position;
   FunctionSample current = ValueAndGradientSample(step_size_estimate, 0.0, 0.0);
   current.value_is_valid = false;
 
   const double descent_direction_max_norm =
-      static_cast<const LineSearchFunction*>(function)->DirectionInfinityNorm();
+      function->DirectionInfinityNorm();
 
   *do_zoom_search = false;
   *bracket_low = initial_position;
@@ -592,6 +643,7 @@
     const FunctionSample unused_previous;
     DCHECK(!unused_previous.value_is_valid);
     // Contracts step size if f(current) is not valid.
+    const double polynomial_minimization_start_time = WallTimeInSeconds();
     const double step_size =
         this->InterpolatingPolynomialMinimizingStepSize(
             options().interpolation_type,
@@ -600,6 +652,8 @@
             current,
             previous.x,
             max_step_size);
+    summary->polynomial_minimization_time_in_seconds +=
+        (WallTimeInSeconds() - polynomial_minimization_start_time);
     if (step_size * descent_direction_max_norm < options().min_step_size) {
       summary->error =
           StringPrintf("Line search failed: step_size too small: %.5e "
@@ -640,8 +694,8 @@
                                 FunctionSample bracket_low,
                                 FunctionSample bracket_high,
                                 FunctionSample* solution,
-                                Summary* summary) {
-  Function* function = options().function;
+                                Summary* summary) const {
+  LineSearchFunction* function = options().function;
 
   CHECK(bracket_low.value_is_valid && bracket_low.gradient_is_valid)
       << std::scientific << std::setprecision(kErrorMessageNumericPrecision)
@@ -695,8 +749,7 @@
   }
 
   const int num_bracketing_iterations = summary->num_iterations;
-  const double descent_direction_max_norm =
-      static_cast<const LineSearchFunction*>(function)->DirectionInfinityNorm();
+  const double descent_direction_max_norm = function->DirectionInfinityNorm();
 
   while (true) {
     // Set solution to bracket_low, as it is our best step size (smallest f())
@@ -739,6 +792,7 @@
     // value that will therefore be ignored.
     const FunctionSample unused_previous;
     DCHECK(!unused_previous.value_is_valid);
+    const double polynomial_minimization_start_time = WallTimeInSeconds();
     solution->x =
         this->InterpolatingPolynomialMinimizingStepSize(
             options().interpolation_type,
@@ -747,6 +801,8 @@
             upper_bound_step,
             lower_bound_step.x,
             upper_bound_step.x);
+    summary->polynomial_minimization_time_in_seconds +=
+        (WallTimeInSeconds() - polynomial_minimization_start_time);
     // No check on magnitude of step size being too small here as it is
     // lower-bounded by the initial bracket start point, which was valid.
     //
diff --git a/internal/ceres/line_search.h b/internal/ceres/line_search.h
index 97b9bc6..ea72f74 100644
--- a/internal/ceres/line_search.h
+++ b/internal/ceres/line_search.h
@@ -44,6 +44,7 @@
 
 class Evaluator;
 struct FunctionSample;
+class LineSearchFunction;
 
 // Line search is another name for a one dimensional optimization
 // algorithm. The name "line search" comes from the fact one
@@ -57,7 +58,7 @@
 // algorithms, e.g., Armijo, Wolfe etc.
 class LineSearch {
  public:
-  class Function;
+  struct Summary;
 
   struct Options {
     Options()
@@ -147,31 +148,7 @@
 
     // The one dimensional function that the line search algorithm
     // minimizes.
-    Function* function;
-  };
-
-  // An object used by the line search to access the function values
-  // and gradient of the one dimensional function being optimized.
-  //
-  // In practice, this object will provide access to the objective
-  // function value and the directional derivative of the underlying
-  // optimization problem along a specific search direction.
-  //
-  // See LineSearchFunction for an example implementation.
-  class Function {
-   public:
-    virtual ~Function() {}
-    // Evaluate the line search objective
-    //
-    //   f(x) = p(position + x * direction)
-    //
-    // Where, p is the objective function of the general optimization
-    // problem.
-    //
-    // g is the gradient f'(x) at x.
-    //
-    // f must not be null. The gradient is computed only if g is not null.
-    virtual bool Evaluate(double x, double* f, double* g) = 0;
+    LineSearchFunction* function;
   };
 
   // Result of the line search.
@@ -181,13 +158,27 @@
           optimal_step_size(0.0),
           num_function_evaluations(0),
           num_gradient_evaluations(0),
-          num_iterations(0) {}
+          num_iterations(0),
+          cost_evaluation_time_in_seconds(-1.0),
+          gradient_evaluation_time_in_seconds(-1.0),
+          polynomial_minimization_time_in_seconds(-1.0),
+          total_time_in_seconds(-1.0) {}
 
     bool success;
     double optimal_step_size;
     int num_function_evaluations;
     int num_gradient_evaluations;
     int num_iterations;
+    // Cumulative time spent evaluating the value of the cost function across
+    // all iterations.
+    double cost_evaluation_time_in_seconds;
+    // Cumulative time spent evaluating the gradient of the cost function across
+    // all iterations.
+    double gradient_evaluation_time_in_seconds;
+    // Cumulative time spent minimizing the interpolating polynomial to compute
+    // the next candidate step size across all iterations.
+    double polynomial_minimization_time_in_seconds;
+    double total_time_in_seconds;
     string error;
   };
 
@@ -208,10 +199,10 @@
   // search.
   //
   // Summary::success is true if a non-zero step size is found.
-  virtual void Search(double step_size_estimate,
-                      double initial_cost,
-                      double initial_gradient,
-                      Summary* summary) = 0;
+  void Search(double step_size_estimate,
+              double initial_cost,
+              double initial_gradient,
+              Summary* summary) const;
   double InterpolatingPolynomialMinimizingStepSize(
       const LineSearchInterpolationType& interpolation_type,
       const FunctionSample& lowerbound_sample,
@@ -224,16 +215,41 @@
   const LineSearch::Options& options() const { return options_; }
 
  private:
+  virtual void DoSearch(double step_size_estimate,
+                        double initial_cost,
+                        double initial_gradient,
+                        Summary* summary) const = 0;
+
+ private:
   LineSearch::Options options_;
 };
 
-class LineSearchFunction : public LineSearch::Function {
+// An object used by the line search to access the function values
+// and gradient of the one dimensional function being optimized.
+//
+// In practice, this object provides access to the objective
+// function value and the directional derivative of the underlying
+// optimization problem along a specific search direction.
+class LineSearchFunction {
  public:
   explicit LineSearchFunction(Evaluator* evaluator);
-  virtual ~LineSearchFunction() {}
   void Init(const Vector& position, const Vector& direction);
-  virtual bool Evaluate(double x, double* f, double* g);
+  // Evaluate the line search objective
+  //
+  //   f(x) = p(position + x * direction)
+  //
+  // Where, p is the objective function of the general optimization
+  // problem.
+  //
+  // g is the gradient f'(x) at x.
+  //
+  // f must not be null. The gradient is computed only if g is not null.
+  bool Evaluate(double x, double* f, double* g);
   double DirectionInfinityNorm() const;
+  // Resets to now, the start point for the results from TimeStatistics().
+  void ResetTimeStatistics();
+  void TimeStatistics(double* cost_evaluation_time_in_seconds,
+                      double* gradient_evaluation_time_in_seconds) const;
 
  private:
   Evaluator* evaluator_;
@@ -246,6 +262,13 @@
   // scaled_direction = x * direction_;
   Vector scaled_direction_;
   Vector gradient_;
+
+  // We may not exclusively own the evaluator (e.g. in the Trust Region
+  // minimizer), hence we need to save the initial evaluation durations for the
+  // value & gradient to accurately determine the duration of the evaluations
+  // we invoked.  These are reset by a call to ResetTimeStatistics().
+  double initial_evaluator_residual_time_in_seconds;
+  double initial_evaluator_jacobian_time_in_seconds;
 };
 
 // Backtracking and interpolation based Armijo line search. This
@@ -257,10 +280,12 @@
  public:
   explicit ArmijoLineSearch(const LineSearch::Options& options);
   virtual ~ArmijoLineSearch() {}
-  virtual void Search(double step_size_estimate,
-                      double initial_cost,
-                      double initial_gradient,
-                      Summary* summary);
+
+ private:
+  virtual void DoSearch(double step_size_estimate,
+                        double initial_cost,
+                        double initial_gradient,
+                        Summary* summary) const;
 };
 
 // Bracketing / Zoom Strong Wolfe condition line search.  This implementation
@@ -274,23 +299,26 @@
  public:
   explicit WolfeLineSearch(const LineSearch::Options& options);
   virtual ~WolfeLineSearch() {}
-  virtual void Search(double step_size_estimate,
-                      double initial_cost,
-                      double initial_gradient,
-                      Summary* summary);
+
   // Returns true iff either a valid point, or valid bracket are found.
   bool BracketingPhase(const FunctionSample& initial_position,
                        const double step_size_estimate,
                        FunctionSample* bracket_low,
                        FunctionSample* bracket_high,
                        bool* perform_zoom_search,
-                       Summary* summary);
+                       Summary* summary) const;
   // Returns true iff final_line_sample satisfies strong Wolfe conditions.
   bool ZoomPhase(const FunctionSample& initial_position,
                  FunctionSample bracket_low,
                  FunctionSample bracket_high,
                  FunctionSample* solution,
-                 Summary* summary);
+                 Summary* summary) const;
+
+ private:
+  virtual void DoSearch(double step_size_estimate,
+                        double initial_cost,
+                        double initial_gradient,
+                        Summary* summary) const;
 };
 
 }  // namespace internal
diff --git a/internal/ceres/line_search_minimizer.cc b/internal/ceres/line_search_minimizer.cc
index ad28ffb..d36545d 100644
--- a/internal/ceres/line_search_minimizer.cc
+++ b/internal/ceres/line_search_minimizer.cc
@@ -375,6 +375,14 @@
         WallTimeInSeconds() - start_time
         + summary->preprocessor_time_in_seconds;
 
+    summary->line_search_cost_evaluation_time_in_seconds +=
+        line_search_summary.cost_evaluation_time_in_seconds;
+    summary->line_search_gradient_evaluation_time_in_seconds +=
+        line_search_summary.gradient_evaluation_time_in_seconds;
+    summary->line_search_polynomial_minimization_time_in_seconds +=
+        line_search_summary.polynomial_minimization_time_in_seconds;
+    summary->line_search_total_time_in_seconds +=
+        line_search_summary.total_time_in_seconds;
     ++summary->num_successful_steps;
 
     if (iteration_summary.gradient_max_norm <= options.gradient_tolerance) {
diff --git a/internal/ceres/solver.cc b/internal/ceres/solver.cc
index c21927c..de170c3 100644
--- a/internal/ceres/solver.cc
+++ b/internal/ceres/solver.cc
@@ -349,6 +349,10 @@
   summary->dense_linear_algebra_library_type  = options.dense_linear_algebra_library_type;  //  NOLINT
   summary->dogleg_type                        = options.dogleg_type;
   summary->inner_iteration_time_in_seconds    = 0.0;
+  summary->line_search_cost_evaluation_time_in_seconds = 0.0;
+  summary->line_search_gradient_evaluation_time_in_seconds = 0.0;
+  summary->line_search_polynomial_minimization_time_in_seconds = 0.0;
+  summary->line_search_total_time_in_seconds  = 0.0;
   summary->inner_iterations_given             = options.use_inner_iterations;
   summary->line_search_direction_type         = options.line_search_direction_type;         //  NOLINT
   summary->line_search_interpolation_type     = options.line_search_interpolation_type;     //  NOLINT
@@ -558,6 +562,10 @@
       residual_evaluation_time_in_seconds(-1.0),
       jacobian_evaluation_time_in_seconds(-1.0),
       inner_iteration_time_in_seconds(-1.0),
+      line_search_cost_evaluation_time_in_seconds(-1.0),
+      line_search_gradient_evaluation_time_in_seconds(-1.0),
+      line_search_polynomial_minimization_time_in_seconds(-1.0),
+      line_search_total_time_in_seconds(-1.0),
       num_parameter_blocks(-1),
       num_parameters(-1),
       num_effective_parameters(-1),
@@ -568,6 +576,7 @@
       num_effective_parameters_reduced(-1),
       num_residual_blocks_reduced(-1),
       num_residuals_reduced(-1),
+      is_constrained(false),
       num_threads_given(-1),
       num_threads_used(-1),
       num_linear_solver_threads_given(-1),
@@ -773,14 +782,26 @@
                   num_inner_iteration_steps);
   }
 
+  const bool print_line_search_timing_information =
+      minimizer_type == LINE_SEARCH ||
+      (minimizer_type == TRUST_REGION && is_constrained);
+
   StringAppendF(&report, "\nTime (in seconds):\n");
   StringAppendF(&report, "Preprocessor        %25.4f\n",
                 preprocessor_time_in_seconds);
 
   StringAppendF(&report, "\n  Residual evaluation %23.4f\n",
                 residual_evaluation_time_in_seconds);
+  if (print_line_search_timing_information) {
+    StringAppendF(&report, "    Line search cost evaluation    %10.4f\n",
+                  line_search_cost_evaluation_time_in_seconds);
+  }
   StringAppendF(&report, "  Jacobian evaluation %23.4f\n",
                 jacobian_evaluation_time_in_seconds);
+  if (print_line_search_timing_information) {
+    StringAppendF(&report, "    Line search gradient evaluation    %6.4f\n",
+                  line_search_gradient_evaluation_time_in_seconds);
+  }
 
   if (minimizer_type == TRUST_REGION) {
     StringAppendF(&report, "  Linear solver       %23.4f\n",
@@ -792,6 +813,11 @@
                   inner_iteration_time_in_seconds);
   }
 
+  if (print_line_search_timing_information) {
+    StringAppendF(&report, "  Line search polynomial minimization  %.4f\n",
+                  line_search_polynomial_minimization_time_in_seconds);
+  }
+
   StringAppendF(&report, "Minimizer           %25.4f\n\n",
                 minimizer_time_in_seconds);
 
diff --git a/internal/ceres/trust_region_minimizer.cc b/internal/ceres/trust_region_minimizer.cc
index c41b189..2f81fcb 100644
--- a/internal/ceres/trust_region_minimizer.cc
+++ b/internal/ceres/trust_region_minimizer.cc
@@ -147,6 +147,7 @@
   summary->termination_type = NO_CONVERGENCE;
   summary->num_successful_steps = 0;
   summary->num_unsuccessful_steps = 0;
+  summary->is_constrained = options.is_constrained;
 
   const int num_parameters = evaluator->NumParameters();
   const int num_effective_parameters = evaluator->NumEffectiveParameters();
@@ -398,6 +399,16 @@
       if (use_line_search) {
         const LineSearch::Summary line_search_summary =
             DoLineSearch(options, x, gradient, cost, delta, evaluator);
+
+        summary->line_search_cost_evaluation_time_in_seconds +=
+            line_search_summary.cost_evaluation_time_in_seconds;
+        summary->line_search_gradient_evaluation_time_in_seconds +=
+            line_search_summary.gradient_evaluation_time_in_seconds;
+        summary->line_search_polynomial_minimization_time_in_seconds +=
+            line_search_summary.polynomial_minimization_time_in_seconds;
+        summary->line_search_total_time_in_seconds +=
+            line_search_summary.total_time_in_seconds;
+
         if (line_search_summary.success) {
           delta *= line_search_summary.optimal_step_size;
         }