Move alternate linear solver and preconditioner policy. Move functions that determine alternatives to Schur type linear solver and preconditioners into the LinearSolver and Preconditioner interfaces. Change-Id: Iae900afb7db17cdbeb7753497005a48c3144e2d7
diff --git a/internal/ceres/linear_solver.cc b/internal/ceres/linear_solver.cc index 08c3ba1..7339205 100644 --- a/internal/ceres/linear_solver.cc +++ b/internal/ceres/linear_solver.cc
@@ -45,30 +45,40 @@ LinearSolver::~LinearSolver() { } +LinearSolverType LinearSolver::LinearSolverForZeroEBlocks( + LinearSolverType linear_solver_type) { + if (!IsSchurType(linear_solver_type)) { + return linear_solver_type; + } + + if (linear_solver_type == SPARSE_SCHUR) { + return SPARSE_NORMAL_CHOLESKY; + } + + if (linear_solver_type == DENSE_SCHUR) { + // TODO(sameeragarwal): This is probably not a great choice. + // Ideally, we should have a DENSE_NORMAL_CHOLESKY, that can take + // a BlockSparseMatrix as input. + return DENSE_QR; + } + + if (linear_solver_type == ITERATIVE_SCHUR) { + return CGNR; + } + + return linear_solver_type; +} + LinearSolver* LinearSolver::Create(const LinearSolver::Options& options) { switch (options.type) { case CGNR: return new CgnrSolver(options); case SPARSE_NORMAL_CHOLESKY: -#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE) - LOG(WARNING) << "SPARSE_NORMAL_CHOLESKY is not available. Please " - << "build Ceres with SuiteSparse or CXSparse. " - << "Returning NULL."; - return NULL; -#else return new SparseNormalCholeskySolver(options); -#endif case SPARSE_SCHUR: -#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE) - LOG(WARNING) << "SPARSE_SCHUR is not available. Please " - << "build Ceres with SuiteSparse or CXSparse. " - << "Returning NULL."; - return NULL; -#else return new SparseSchurComplementSolver(options); -#endif case DENSE_SCHUR: return new DenseSchurComplementSolver(options);
diff --git a/internal/ceres/linear_solver.h b/internal/ceres/linear_solver.h index f091bc5..58b9044 100644 --- a/internal/ceres/linear_solver.h +++ b/internal/ceres/linear_solver.h
@@ -274,6 +274,14 @@ string message; }; + // If the optimization problem is such that there are no remaining + // e-blocks, a Schur type linear solver cannot be used. If the + // linear solver is of Schur type, this function implements a policy + // to select an alternate nearest linear solver to the one selected + // by the user. The input linear_solver_type is returned otherwise. + static LinearSolverType LinearSolverForZeroEBlocks( + LinearSolverType linear_solver_type); + virtual ~LinearSolver(); // Solve Ax = b.
diff --git a/internal/ceres/preconditioner.cc b/internal/ceres/preconditioner.cc index 505a47d..062347f 100644 --- a/internal/ceres/preconditioner.cc +++ b/internal/ceres/preconditioner.cc
@@ -37,6 +37,16 @@ Preconditioner::~Preconditioner() { } +PreconditionerType Preconditioner::PreconditionerForZeroEBlocks( + PreconditionerType preconditioner_type) { + if (preconditioner_type == SCHUR_JACOBI || + preconditioner_type == CLUSTER_JACOBI || + preconditioner_type == CLUSTER_TRIDIAGONAL) { + return JACOBI; + } + return preconditioner_type; +} + SparseMatrixPreconditionerWrapper::SparseMatrixPreconditionerWrapper( const SparseMatrix* matrix) : matrix_(CHECK_NOTNULL(matrix)) {
diff --git a/internal/ceres/preconditioner.h b/internal/ceres/preconditioner.h index 21cbc00..e8d5994 100644 --- a/internal/ceres/preconditioner.h +++ b/internal/ceres/preconditioner.h
@@ -36,6 +36,7 @@ #include "ceres/compressed_row_sparse_matrix.h" #include "ceres/linear_operator.h" #include "ceres/sparse_matrix.h" +#include "ceres/types.h" namespace ceres { namespace internal { @@ -95,6 +96,14 @@ int f_block_size; }; + // If the optimization problem is such that there are no remaining + // e-blocks, ITERATIVE_SCHUR with a Schur type preconditioner cannot + // be used. This function returns JACOBI if a preconditioner for + // ITERATIVE_SCHUR is used. The input preconditioner_type is + // returned otherwise. + static PreconditionerType PreconditionerForZeroEBlocks( + PreconditionerType preconditioner_type); + virtual ~Preconditioner(); // Update the numerical value of the preconditioner for the linear
diff --git a/internal/ceres/solver_impl.cc b/internal/ceres/solver_impl.cc index 421a4d9..24034be 100644 --- a/internal/ceres/solver_impl.cc +++ b/internal/ceres/solver_impl.cc
@@ -49,6 +49,7 @@ #include "ceres/ordered_groups.h" #include "ceres/parameter_block.h" #include "ceres/parameter_block_ordering.h" +#include "ceres/preconditioner.h" #include "ceres/problem.h" #include "ceres/problem_impl.h" #include "ceres/program.h" @@ -712,7 +713,15 @@ // as they assume there is at least one e_block. Thus, we // automatically switch to the closest solver to the one indicated // by the user. - AlternateLinearSolverForSchurTypeLinearSolver(options); + if (options->linear_solver_type == ITERATIVE_SCHUR) { + options->preconditioner_type = + Preconditioner::PreconditionerForZeroEBlocks( + options->preconditioner_type); + } + + options->linear_solver_type = + LinearSolver::LinearSolverForZeroEBlocks( + options->linear_solver_type); } if (IsSchurType(options->linear_solver_type)) { @@ -1059,39 +1068,6 @@ return inner_iteration_minimizer.release(); } -void SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver( - Solver::Options* options) { - if (!IsSchurType(options->linear_solver_type)) { - return; - } - - string msg = "No e_blocks remaining. Switching from "; - if (options->linear_solver_type == SPARSE_SCHUR) { - options->linear_solver_type = SPARSE_NORMAL_CHOLESKY; - msg += "SPARSE_SCHUR to SPARSE_NORMAL_CHOLESKY."; - } else if (options->linear_solver_type == DENSE_SCHUR) { - // TODO(sameeragarwal): This is probably not a great choice. - // Ideally, we should have a DENSE_NORMAL_CHOLESKY, that can - // take a BlockSparseMatrix as input. - options->linear_solver_type = DENSE_QR; - msg += "DENSE_SCHUR to DENSE_QR."; - } else if (options->linear_solver_type == ITERATIVE_SCHUR) { - options->linear_solver_type = CGNR; - if (options->preconditioner_type != IDENTITY) { - msg += StringPrintf("ITERATIVE_SCHUR with %s preconditioner " - "to CGNR with JACOBI preconditioner.", - PreconditionerTypeToString( - options->preconditioner_type)); - // CGNR currently only supports the JACOBI preconditioner. - options->preconditioner_type = JACOBI; - } else { - msg += "ITERATIVE_SCHUR with IDENTITY preconditioner" - "to CGNR with IDENTITY preconditioner."; - } - } - LOG(WARNING) << msg; -} - bool SolverImpl::ApplyUserOrdering( const ProblemImpl::ParameterMap& parameter_map, const ParameterBlockOrdering* parameter_block_ordering,
diff --git a/internal/ceres/solver_impl.h b/internal/ceres/solver_impl.h index e9a3bf5..53b87a7 100644 --- a/internal/ceres/solver_impl.h +++ b/internal/ceres/solver_impl.h
@@ -149,16 +149,6 @@ const ProblemImpl::ParameterMap& parameter_map, Solver::Summary* summary); - // If the linear solver is of Schur type, then replace it with the - // closest equivalent linear solver. This is done when the user - // requested a Schur type solver but the problem structure makes it - // impossible to use one. - // - // If the linear solver is not of Schur type, the function is a - // no-op. - static void AlternateLinearSolverForSchurTypeLinearSolver( - Solver::Options* options); - // Reorder the parameter blocks in program using the ordering static bool ApplyUserOrdering( const ProblemImpl::ParameterMap& parameter_map,
diff --git a/internal/ceres/solver_impl_test.cc b/internal/ceres/solver_impl_test.cc index b0005a7..51e8cbf 100644 --- a/internal/ceres/solver_impl_test.cc +++ b/internal/ceres/solver_impl_test.cc
@@ -378,63 +378,5 @@ EXPECT_TRUE(problem.program().IsValid()); } -TEST(SolverImpl, AlternateLinearSolverForSchurTypeLinearSolver) { - Solver::Options options; - - options.linear_solver_type = DENSE_QR; - SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options); - EXPECT_EQ(options.linear_solver_type, DENSE_QR); - - options.linear_solver_type = DENSE_NORMAL_CHOLESKY; - SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options); - EXPECT_EQ(options.linear_solver_type, DENSE_NORMAL_CHOLESKY); - - options.linear_solver_type = SPARSE_NORMAL_CHOLESKY; - SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options); - EXPECT_EQ(options.linear_solver_type, SPARSE_NORMAL_CHOLESKY); - - options.linear_solver_type = CGNR; - SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options); - EXPECT_EQ(options.linear_solver_type, CGNR); - - options.linear_solver_type = DENSE_SCHUR; - SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options); - EXPECT_EQ(options.linear_solver_type, DENSE_QR); - - options.linear_solver_type = SPARSE_SCHUR; - SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options); - EXPECT_EQ(options.linear_solver_type, SPARSE_NORMAL_CHOLESKY); - - options.linear_solver_type = ITERATIVE_SCHUR; - options.preconditioner_type = IDENTITY; - SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options); - EXPECT_EQ(options.linear_solver_type, CGNR); - EXPECT_EQ(options.preconditioner_type, IDENTITY); - - options.linear_solver_type = ITERATIVE_SCHUR; - options.preconditioner_type = JACOBI; - SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options); - EXPECT_EQ(options.linear_solver_type, CGNR); - EXPECT_EQ(options.preconditioner_type, JACOBI); - - options.linear_solver_type = ITERATIVE_SCHUR; - options.preconditioner_type = SCHUR_JACOBI; - SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options); - EXPECT_EQ(options.linear_solver_type, CGNR); - EXPECT_EQ(options.preconditioner_type, JACOBI); - - options.linear_solver_type = ITERATIVE_SCHUR; - options.preconditioner_type = CLUSTER_JACOBI; - SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options); - EXPECT_EQ(options.linear_solver_type, CGNR); - EXPECT_EQ(options.preconditioner_type, JACOBI); - - options.linear_solver_type = ITERATIVE_SCHUR; - options.preconditioner_type = CLUSTER_TRIDIAGONAL; - SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options); - EXPECT_EQ(options.linear_solver_type, CGNR); - EXPECT_EQ(options.preconditioner_type, JACOBI); -} - } // namespace internal } // namespace ceres