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