Add support for reporting linear solver and inner iteration
orderings.

Change-Id: I0588a4285e0925ce689e47bd48ddcc61ce596a1f
diff --git a/include/ceres/numeric_diff_functor.h b/include/ceres/numeric_diff_functor.h
index 14adbed..40c0f9c 100644
--- a/include/ceres/numeric_diff_functor.h
+++ b/include/ceres/numeric_diff_functor.h
@@ -133,7 +133,7 @@
   }
 
   bool operator()(const double* x0, double* residuals) const {
-    functor_(x0, residuals);
+    return functor_(x0, residuals);
   }
 
   bool operator()(const double* x0,
@@ -228,7 +228,7 @@
 
   template <typename T>
   bool operator()(const T* x0, T* residuals) const {
-    functor_(x0, residuals);
+    return functor_(x0, residuals);
   }
 
   template <typename T>
diff --git a/include/ceres/solver.h b/include/ceres/solver.h
index b8be006..9ad7e50 100644
--- a/include/ceres/solver.h
+++ b/include/ceres/solver.h
@@ -687,11 +687,18 @@
     LinearSolverType linear_solver_type_given;
     LinearSolverType linear_solver_type_used;
 
+    vector<int> linear_solver_ordering_given;
+    vector<int> linear_solver_ordering_used;
+
     PreconditionerType preconditioner_type;
 
     TrustRegionStrategyType trust_region_strategy_type;
     DoglegType dogleg_type;
     SparseLinearAlgebraLibraryType sparse_linear_algebra_library;
+
+    bool inner_iterations;
+    vector<int> inner_iteration_ordering_given;
+    vector<int> inner_iteration_ordering_used;
   };
 
   // Once a least squares problem has been built, this function takes
diff --git a/internal/ceres/solver.cc b/internal/ceres/solver.cc
index 450a710..af32629 100644
--- a/internal/ceres/solver.cc
+++ b/internal/ceres/solver.cc
@@ -40,6 +40,21 @@
 #include "ceres/wall_time.h"
 
 namespace ceres {
+namespace {
+
+void StringifyOrdering(const vector<int>& ordering, string* report) {
+  if (ordering.size() == 0) {
+    internal::StringAppendF(report, "AUTOMATIC");
+    return;
+  }
+
+  for (int i = 0; i < ordering.size() - 1; ++i) {
+    internal::StringAppendF(report, "%d, ", ordering[i]);
+  }
+  internal::StringAppendF(report, "%d", ordering.back());
+}
+
+}  // namespace
 
 Solver::Options::~Options() {
   delete linear_solver_ordering;
@@ -95,7 +110,8 @@
       linear_solver_type_used(SPARSE_NORMAL_CHOLESKY),
       preconditioner_type(IDENTITY),
       trust_region_strategy_type(LEVENBERG_MARQUARDT),
-      sparse_linear_algebra_library(SUITE_SPARSE) {
+      sparse_linear_algebra_library(SUITE_SPARSE),
+      inner_iterations(false) {
 }
 
 string Solver::Summary::BriefReport() const {
@@ -125,114 +141,137 @@
 };
 
 string Solver::Summary::FullReport() const {
+  using internal::StringAppendF;
+
   string report =
       "\n"
       "Ceres Solver Report\n"
       "-------------------\n";
 
   if (termination_type == DID_NOT_RUN) {
-    internal::StringAppendF(&report, "                      Original\n");
-    internal::StringAppendF(&report, "Parameter blocks    % 10d\n",
-                            num_parameter_blocks);
-    internal::StringAppendF(&report, "Parameters          % 10d\n",
-                            num_parameters);
-    internal::StringAppendF(&report, "Residual blocks     % 10d\n",
-                            num_residual_blocks);
-    internal::StringAppendF(&report, "Residuals           % 10d\n\n",
-                            num_residuals);
+    StringAppendF(&report, "                      Original\n");
+    StringAppendF(&report, "Parameter blocks    % 10d\n",
+                  num_parameter_blocks);
+    StringAppendF(&report, "Parameters          % 10d\n",
+                  num_parameters);
+    StringAppendF(&report, "Residual blocks     % 10d\n",
+                  num_residual_blocks);
+    StringAppendF(&report, "Residuals           % 10d\n\n",
+                  num_residuals);
   } else {
-    internal::StringAppendF(&report, "%45s    %21s\n", "Original", "Reduced");
-    internal::StringAppendF(&report, "Parameter blocks    % 25d% 25d\n",
-                            num_parameter_blocks, num_parameter_blocks_reduced);
-    internal::StringAppendF(&report, "Parameters          % 25d% 25d\n",
-                            num_parameters, num_parameters_reduced);
-    internal::StringAppendF(&report, "Residual blocks     % 25d% 25d\n",
-                            num_residual_blocks, num_residual_blocks_reduced);
-    internal::StringAppendF(&report, "Residual            % 25d% 25d\n\n",
-                          num_residuals, num_residuals_reduced);
+    StringAppendF(&report, "%45s    %21s\n", "Original", "Reduced");
+    StringAppendF(&report, "Parameter blocks    % 25d% 25d\n",
+                  num_parameter_blocks, num_parameter_blocks_reduced);
+    StringAppendF(&report, "Parameters          % 25d% 25d\n",
+                  num_parameters, num_parameters_reduced);
+    StringAppendF(&report, "Residual blocks     % 25d% 25d\n",
+                  num_residual_blocks, num_residual_blocks_reduced);
+    StringAppendF(&report, "Residual            % 25d% 25d\n",
+                  num_residuals, num_residuals_reduced);
   }
 
-  internal::StringAppendF(&report,   "%45s    %21s\n", "Given",  "Used");
-  internal::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) {
-    internal::StringAppendF(&report, "Preconditioner      %25s%25s\n",
-                            PreconditionerTypeToString(preconditioner_type),
-                            PreconditionerTypeToString(preconditioner_type));
-  } else {
-    internal::StringAppendF(&report, "Preconditioner      %25s%25s\n",
-                            "N/A", "N/A");
-  }
-
-  // TODO(sameeragarwal): Add support for logging the ordering object.
-  internal::StringAppendF(&report, "Threads:            % 25d% 25d\n",
-                          num_threads_given, num_threads_used);
-  internal::StringAppendF(&report, "Linear solver threads % 23d% 25d\n",
-                          num_linear_solver_threads_given,
-                          num_linear_solver_threads_used);
-
   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))) {
-    internal::StringAppendF(&report, "\nSparse Linear Algebra Library %15s\n",
-                            SparseLinearAlgebraLibraryTypeToString(
+    StringAppendF(&report, "\nSparse Linear Algebra Library %15s\n",
+                  SparseLinearAlgebraLibraryTypeToString(
                                 sparse_linear_algebra_library));
   }
 
-  internal::StringAppendF(&report, "Trust Region Strategy     %19s",
-                          TrustRegionStrategyTypeToString(
+  StringAppendF(&report, "Trust Region Strategy     %19s",
+                TrustRegionStrategyTypeToString(
                               trust_region_strategy_type));
   if (trust_region_strategy_type == DOGLEG) {
     if (dogleg_type == TRADITIONAL_DOGLEG) {
-      internal::StringAppendF(&report, " (TRADITIONAL)");
+      StringAppendF(&report, " (TRADITIONAL)");
     } else {
-      internal::StringAppendF(&report, " (SUBSPACE)");
+      StringAppendF(&report, " (SUBSPACE)");
     }
   }
-  internal::StringAppendF(&report, "\n");
+  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));
+
+  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);
+
+  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);
+    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";
-    internal::StringAppendF(&report, "Termination:           %20s\n",
-                            "DID_NOT_RUN");
-    internal::StringAppendF(&report, "Reason: %s\n", error.c_str());
+    StringAppendF(&report, "Termination:           %20s\n",
+                  "DID_NOT_RUN");
+    StringAppendF(&report, "Reason: %s\n", error.c_str());
     return report;
   }
 
-  internal::StringAppendF(&report, "\nCost:\n");
-  internal::StringAppendF(&report, "Initial        % 30e\n", initial_cost);
+  StringAppendF(&report, "\nCost:\n");
+  StringAppendF(&report, "Initial        % 30e\n", initial_cost);
   if (termination_type != NUMERICAL_FAILURE && termination_type != USER_ABORT) {
-    internal::StringAppendF(&report, "Final          % 30e\n", final_cost);
-    internal::StringAppendF(&report, "Change         % 30e\n",
-                            initial_cost - final_cost);
+    StringAppendF(&report, "Final          % 30e\n", final_cost);
+    StringAppendF(&report, "Change         % 30e\n",
+                  initial_cost - final_cost);
   }
 
-  internal::StringAppendF(&report, "\nNumber of iterations:\n");
-  internal::StringAppendF(&report, "Successful               % 20d\n",
-                          num_successful_steps);
-  internal::StringAppendF(&report, "Unsuccessful             % 20d\n",
-                          num_unsuccessful_steps);
-  internal::StringAppendF(&report, "Total                    % 20d\n",
-                          num_successful_steps + num_unsuccessful_steps);
-  internal::StringAppendF(&report, "\nTime (in seconds):\n");
-  internal::StringAppendF(&report, "Preprocessor        % 25e\n",
-                          preprocessor_time_in_seconds);
-  internal::StringAppendF(&report, "Minimizer           % 25e\n",
-                          minimizer_time_in_seconds);
-  internal::StringAppendF(&report, "Total               % 25e\n",
-                          total_time_in_seconds);
+  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        % 25e\n",
+                preprocessor_time_in_seconds);
+  StringAppendF(&report, "Minimizer           % 25e\n",
+                minimizer_time_in_seconds);
+  StringAppendF(&report, "Total               % 25e\n",
+                total_time_in_seconds);
 
-  internal::StringAppendF(&report, "Termination:        %25s\n",
+  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 4e0f133..50c96e5 100644
--- a/internal/ceres/solver_impl.cc
+++ b/internal/ceres/solver_impl.cc
@@ -177,6 +177,22 @@
     FILE* fptr_;
 };
 
+void SummarizeOrdering(ParameterBlockOrdering* ordering,
+                       vector<int>* summary) {
+  CHECK_NOTNULL(summary)->clear();
+  if (ordering == NULL) {
+    return;
+  }
+
+  const map<int, set<double*> >& group_to_elements =
+      ordering->group_to_elements();
+  for (map<int, set<double*> >::const_iterator it = group_to_elements.begin();
+       it != group_to_elements.end();
+       ++it) {
+    summary->push_back(it->second.size());
+  }
+}
+
 }  // namespace
 
 void SolverImpl::TrustRegionMinimize(
@@ -318,6 +334,12 @@
     return;
   }
 
+  SummarizeOrdering(original_options.linear_solver_ordering,
+                    &(summary->linear_solver_ordering_given));
+
+  SummarizeOrdering(original_options.inner_iteration_ordering,
+                    &(summary->inner_iteration_ordering_given));
+
   Solver::Options options(original_options);
   options.linear_solver_ordering = NULL;
   options.inner_iteration_ordering = NULL;
@@ -418,6 +440,9 @@
     return;
   }
 
+  SummarizeOrdering(options.linear_solver_ordering,
+                    &(summary->linear_solver_ordering_used));
+
   summary->num_parameter_blocks_reduced = reduced_program->NumParameterBlocks();
   summary->num_parameters_reduced = reduced_program->NumParameters();
   summary->num_residual_blocks_reduced = reduced_program->NumResidualBlocks();
@@ -516,7 +541,7 @@
           CreateInnerIterationMinimizer(original_options,
                                         *reduced_program,
                                         problem_impl->parameter_map(),
-                                        &summary->error));
+                                        summary));
       if (inner_iteration_minimizer == NULL) {
         LOG(ERROR) << summary->error;
         return;
@@ -1390,7 +1415,7 @@
     const Solver::Options& options,
     const Program& program,
     const ProblemImpl::ParameterMap& parameter_map,
-    string* error) {
+    Solver::Summary* summary) {
   scoped_ptr<CoordinateDescentMinimizer> inner_iteration_minimizer(
       new CoordinateDescentMinimizer);
   scoped_ptr<ParameterBlockOrdering> inner_iteration_ordering;
@@ -1416,7 +1441,7 @@
     for ( ;it != group_to_elements.end(); ++it) {
       if (!IsParameterBlockSetIndependent(it->second,
                                           program.residual_blocks())) {
-        *error =
+        summary->error =
             StringPrintf("The user-provided "
                          "parameter_blocks_for_inner_iterations does not "
                          "form an independent set. Group Id: %d", it->first);
@@ -1429,10 +1454,13 @@
   if (!inner_iteration_minimizer->Init(program,
                                        parameter_map,
                                        *ordering_ptr,
-                                       error)) {
+                                       &summary->error)) {
     return NULL;
   }
 
+  summary->inner_iterations = true;
+  SummarizeOrdering(ordering_ptr, &(summary->inner_iteration_ordering_used));
+
   return inner_iteration_minimizer.release();
 }
 
diff --git a/internal/ceres/solver_impl.h b/internal/ceres/solver_impl.h
index 92c37fb..c9848fb 100644
--- a/internal/ceres/solver_impl.h
+++ b/internal/ceres/solver_impl.h
@@ -148,7 +148,7 @@
       const Solver::Options& options,
       const Program& program,
       const ProblemImpl::ParameterMap& parameter_map,
-      string* error);
+      Solver::Summary* summary);
 };
 
 }  // namespace internal