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