|  | // Ceres Solver - A fast non-linear least squares minimizer | 
|  | // Copyright 2010, 2011, 2012 Google Inc. All rights reserved. | 
|  | // http://code.google.com/p/ceres-solver/ | 
|  | // | 
|  | // Redistribution and use in source and binary forms, with or without | 
|  | // modification, are permitted provided that the following conditions are met: | 
|  | // | 
|  | // * Redistributions of source code must retain the above copyright notice, | 
|  | //   this list of conditions and the following disclaimer. | 
|  | // * Redistributions in binary form must reproduce the above copyright notice, | 
|  | //   this list of conditions and the following disclaimer in the documentation | 
|  | //   and/or other materials provided with the distribution. | 
|  | // * Neither the name of Google Inc. nor the names of its contributors may be | 
|  | //   used to endorse or promote products derived from this software without | 
|  | //   specific prior written permission. | 
|  | // | 
|  | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
|  | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
|  | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | 
|  | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | 
|  | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | 
|  | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | 
|  | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | 
|  | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 
|  | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 
|  | // POSSIBILITY OF SUCH DAMAGE. | 
|  | // | 
|  | // Author: keir@google.com (Keir Mierle) | 
|  | //         sameeragarwal@google.com (Sameer Agarwal) | 
|  |  | 
|  | #include "ceres/solver.h" | 
|  |  | 
|  | #include <vector> | 
|  | #include "ceres/problem.h" | 
|  | #include "ceres/problem_impl.h" | 
|  | #include "ceres/program.h" | 
|  | #include "ceres/solver_impl.h" | 
|  | #include "ceres/stringprintf.h" | 
|  | #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::~Solver() {} | 
|  |  | 
|  | void Solver::Solve(const Solver::Options& options, | 
|  | Problem* problem, | 
|  | Solver::Summary* summary) { | 
|  | double start_time_seconds = internal::WallTimeInSeconds(); | 
|  | internal::ProblemImpl* problem_impl = | 
|  | CHECK_NOTNULL(problem)->problem_impl_.get(); | 
|  | internal::SolverImpl::Solve(options, problem_impl, summary); | 
|  | summary->total_time_in_seconds = | 
|  | internal::WallTimeInSeconds() - start_time_seconds; | 
|  | } | 
|  |  | 
|  | void Solve(const Solver::Options& options, | 
|  | Problem* problem, | 
|  | Solver::Summary* summary) { | 
|  | Solver solver; | 
|  | solver.Solve(options, problem, summary); | 
|  | } | 
|  |  | 
|  | Solver::Summary::Summary() | 
|  | // Invalid values for most fields, to ensure that we are not | 
|  | // accidentally reporting default values. | 
|  | : minimizer_type(TRUST_REGION), | 
|  | termination_type(FAILURE), | 
|  | message("ceres::Solve was not called."), | 
|  | initial_cost(-1.0), | 
|  | final_cost(-1.0), | 
|  | fixed_cost(-1.0), | 
|  | num_successful_steps(-1), | 
|  | num_unsuccessful_steps(-1), | 
|  | num_inner_iteration_steps(-1), | 
|  | preprocessor_time_in_seconds(-1.0), | 
|  | minimizer_time_in_seconds(-1.0), | 
|  | postprocessor_time_in_seconds(-1.0), | 
|  | total_time_in_seconds(-1.0), | 
|  | linear_solver_time_in_seconds(-1.0), | 
|  | residual_evaluation_time_in_seconds(-1.0), | 
|  | jacobian_evaluation_time_in_seconds(-1.0), | 
|  | inner_iteration_time_in_seconds(-1.0), | 
|  | num_parameter_blocks(-1), | 
|  | num_parameters(-1), | 
|  | num_effective_parameters(-1), | 
|  | num_residual_blocks(-1), | 
|  | num_residuals(-1), | 
|  | num_parameter_blocks_reduced(-1), | 
|  | num_parameters_reduced(-1), | 
|  | num_effective_parameters_reduced(-1), | 
|  | num_residual_blocks_reduced(-1), | 
|  | num_residuals_reduced(-1), | 
|  | num_threads_given(-1), | 
|  | num_threads_used(-1), | 
|  | num_linear_solver_threads_given(-1), | 
|  | num_linear_solver_threads_used(-1), | 
|  | linear_solver_type_given(SPARSE_NORMAL_CHOLESKY), | 
|  | linear_solver_type_used(SPARSE_NORMAL_CHOLESKY), | 
|  | inner_iterations_given(false), | 
|  | inner_iterations_used(false), | 
|  | preconditioner_type(IDENTITY), | 
|  | visibility_clustering_type(CANONICAL_VIEWS), | 
|  | trust_region_strategy_type(LEVENBERG_MARQUARDT), | 
|  | dense_linear_algebra_library_type(EIGEN), | 
|  | sparse_linear_algebra_library_type(SUITE_SPARSE), | 
|  | line_search_direction_type(LBFGS), | 
|  | line_search_type(ARMIJO), | 
|  | line_search_interpolation_type(BISECTION), | 
|  | nonlinear_conjugate_gradient_type(FLETCHER_REEVES), | 
|  | max_lbfgs_rank(-1) { | 
|  | } | 
|  |  | 
|  | using internal::StringAppendF; | 
|  | using internal::StringPrintf; | 
|  |  | 
|  | string Solver::Summary::BriefReport() const { | 
|  | return StringPrintf("Ceres Solver Report: " | 
|  | "Iterations: %d, " | 
|  | "Initial cost: %e, " | 
|  | "Final cost: %e, " | 
|  | "Termination: %s", | 
|  | num_successful_steps + num_unsuccessful_steps, | 
|  | initial_cost, | 
|  | final_cost, | 
|  | TerminationTypeToString(termination_type)); | 
|  | }; | 
|  |  | 
|  | string Solver::Summary::FullReport() const { | 
|  | string report = | 
|  | "\n" | 
|  | "Ceres Solver Report\n" | 
|  | "-------------------\n"; | 
|  |  | 
|  | 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); | 
|  | if (num_effective_parameters_reduced != num_parameters_reduced) { | 
|  | StringAppendF(&report, "Effective parameters% 25d% 25d\n", | 
|  | num_effective_parameters, num_effective_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); | 
|  |  | 
|  | if (minimizer_type == TRUST_REGION) { | 
|  | // TRUST_SEARCH HEADER | 
|  | StringAppendF(&report, "\nMinimizer                 %19s\n", | 
|  | "TRUST_REGION"); | 
|  |  | 
|  | if (linear_solver_type_used == DENSE_NORMAL_CHOLESKY || | 
|  | linear_solver_type_used == DENSE_SCHUR || | 
|  | linear_solver_type_used == DENSE_QR) { | 
|  | StringAppendF(&report, "\nDense linear algebra library  %15s\n", | 
|  | DenseLinearAlgebraLibraryTypeToString( | 
|  | dense_linear_algebra_library_type)); | 
|  | } | 
|  |  | 
|  | if (linear_solver_type_used == SPARSE_NORMAL_CHOLESKY || | 
|  | linear_solver_type_used == SPARSE_SCHUR || | 
|  | (linear_solver_type_used == ITERATIVE_SCHUR && | 
|  | (preconditioner_type == CLUSTER_JACOBI || | 
|  | preconditioner_type == CLUSTER_TRIDIAGONAL))) { | 
|  | StringAppendF(&report, "\nSparse linear algebra library %15s\n", | 
|  | SparseLinearAlgebraLibraryTypeToString( | 
|  | sparse_linear_algebra_library_type)); | 
|  | } | 
|  |  | 
|  | 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)); | 
|  |  | 
|  | if (linear_solver_type_given == CGNR || | 
|  | linear_solver_type_given == ITERATIVE_SCHUR) { | 
|  | StringAppendF(&report, "Preconditioner      %25s%25s\n", | 
|  | PreconditionerTypeToString(preconditioner_type), | 
|  | PreconditionerTypeToString(preconditioner_type)); | 
|  | } | 
|  |  | 
|  | if (preconditioner_type == CLUSTER_JACOBI || | 
|  | preconditioner_type == CLUSTER_TRIDIAGONAL) { | 
|  | StringAppendF(&report, "Visibility clustering%24s%25s\n", | 
|  | VisibilityClusteringTypeToString( | 
|  | visibility_clustering_type), | 
|  | VisibilityClusteringTypeToString( | 
|  | visibility_clustering_type)); | 
|  | } | 
|  | 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_given) { | 
|  | StringAppendF(&report, | 
|  | "Use inner iterations     %20s     %20s\n", | 
|  | inner_iterations_given ? "True" : "False", | 
|  | inner_iterations_used ? "True" : "False"); | 
|  | } | 
|  |  | 
|  | if (inner_iterations_used) { | 
|  | 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()); | 
|  | } | 
|  | } else { | 
|  | // LINE_SEARCH HEADER | 
|  | StringAppendF(&report, "\nMinimizer                 %19s\n", "LINE_SEARCH"); | 
|  |  | 
|  |  | 
|  | string line_search_direction_string; | 
|  | if (line_search_direction_type == LBFGS) { | 
|  | line_search_direction_string = StringPrintf("LBFGS (%d)", max_lbfgs_rank); | 
|  | } else if (line_search_direction_type == NONLINEAR_CONJUGATE_GRADIENT) { | 
|  | line_search_direction_string = | 
|  | NonlinearConjugateGradientTypeToString( | 
|  | nonlinear_conjugate_gradient_type); | 
|  | } else { | 
|  | line_search_direction_string = | 
|  | LineSearchDirectionTypeToString(line_search_direction_type); | 
|  | } | 
|  |  | 
|  | StringAppendF(&report, "Line search direction     %19s\n", | 
|  | line_search_direction_string.c_str()); | 
|  |  | 
|  | const string line_search_type_string = | 
|  | StringPrintf("%s %s", | 
|  | LineSearchInterpolationTypeToString( | 
|  | line_search_interpolation_type), | 
|  | LineSearchTypeToString(line_search_type)); | 
|  | StringAppendF(&report, "Line search type          %19s\n", | 
|  | line_search_type_string.c_str()); | 
|  | StringAppendF(&report, "\n"); | 
|  |  | 
|  | StringAppendF(&report, "%45s    %21s\n", "Given",  "Used"); | 
|  | StringAppendF(&report, "Threads             % 25d% 25d\n", | 
|  | num_threads_given, num_threads_used); | 
|  | } | 
|  |  | 
|  | StringAppendF(&report, "\nCost:\n"); | 
|  | StringAppendF(&report, "Initial        % 30e\n", initial_cost); | 
|  | if (termination_type != FAILURE && | 
|  | termination_type != USER_FAILURE) { | 
|  | StringAppendF(&report, "Final          % 30e\n", final_cost); | 
|  | StringAppendF(&report, "Change         % 30e\n", | 
|  | initial_cost - final_cost); | 
|  | } | 
|  |  | 
|  | StringAppendF(&report, "\nMinimizer iterations         % 16d\n", | 
|  | num_successful_steps + num_unsuccessful_steps); | 
|  |  | 
|  | // Successful/Unsuccessful steps only matter in the case of the | 
|  | // trust region solver. Line search terminates when it encounters | 
|  | // the first unsuccessful step. | 
|  | if (minimizer_type == TRUST_REGION) { | 
|  | StringAppendF(&report, "Successful steps               % 14d\n", | 
|  | num_successful_steps); | 
|  | StringAppendF(&report, "Unsuccessful steps             % 14d\n", | 
|  | num_unsuccessful_steps); | 
|  | } | 
|  | if (inner_iterations_used) { | 
|  | StringAppendF(&report, "Steps with inner iterations    % 14d\n", | 
|  | num_inner_iteration_steps); | 
|  | } | 
|  |  | 
|  | StringAppendF(&report, "\nTime (in seconds):\n"); | 
|  | StringAppendF(&report, "Preprocessor        %25.3f\n", | 
|  | preprocessor_time_in_seconds); | 
|  |  | 
|  | StringAppendF(&report, "\n  Residual evaluation %23.3f\n", | 
|  | residual_evaluation_time_in_seconds); | 
|  | StringAppendF(&report, "  Jacobian evaluation %23.3f\n", | 
|  | jacobian_evaluation_time_in_seconds); | 
|  |  | 
|  | if (minimizer_type == TRUST_REGION) { | 
|  | StringAppendF(&report, "  Linear solver       %23.3f\n", | 
|  | linear_solver_time_in_seconds); | 
|  | } | 
|  |  | 
|  | if (inner_iterations_used) { | 
|  | StringAppendF(&report, "  Inner iterations    %23.3f\n", | 
|  | inner_iteration_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 (%s)\n", | 
|  | TerminationTypeToString(termination_type), message.c_str()); | 
|  | return report; | 
|  | }; | 
|  |  | 
|  | bool Solver::Summary::IsSolutionUsable() const { | 
|  | return (termination_type == CONVERGENCE || | 
|  | termination_type == NO_CONVERGENCE || | 
|  | termination_type == USER_SUCCESS); | 
|  | } | 
|  |  | 
|  | }  // namespace ceres |