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;