Add explicit no sparse linear algebra library available option. - Previously we had no defined default value for sparse_linear_algebra_library_type in Solver::Options if Ceres was compiled with no sparse library available. Thus in that case, the default value (dependent upon the compiler) would indicate that one was available. - Now we have an explicit option that means no sparse library is available, which is now the default value in Solver::Options in this case. - Add a warning in CMake when the user disables all sparse libraries. - Fix typos in trust_region_preprocessor_test: (SUITE/CX)_SPARSE -> (SUITE/CX)SPARSE that induced failures when no sparse libraries were available. Change-Id: I869c399a12d42bfc44220cbb25ce6d6dd80236bd
diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c0ed8c..6fe1e32 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt
@@ -370,6 +370,16 @@ CXSPARSE_LIBRARY) ENDIF (CXSPARSE) +# Ensure that the user understands they have disabled all sparse libraries. +IF (NOT SUITESPARSE AND NOT CXSPARSE AND NOT EIGENSPARSE) + MESSAGE(" ===============================================================") + MESSAGE(" Compiling without any sparse library: SuiteSparse, CXSparse ") + MESSAGE(" & Eigen (Sparse) are all disabled or unavailable. No sparse ") + MESSAGE(" linear solvers (SPARSE_NORMAL_CHOLESKY & SPARSE_SCHUR)") + MESSAGE(" will be available when Ceres is used.") + MESSAGE(" ===============================================================") +ENDIF(NOT SUITESPARSE AND NOT CXSPARSE AND NOT EIGENSPARSE) + # GFlags. IF (GFLAGS) HANDLE_LEGACY_INCLUDE_DEPENDENCY_HINT(GFLAGS_INCLUDE GFLAGS_INCLUDE_DIR_HINTS)
diff --git a/docs/source/nnls_solving.rst b/docs/source/nnls_solving.rst index a283f98..067e3b1 100644 --- a/docs/source/nnls_solving.rst +++ b/docs/source/nnls_solving.rst
@@ -1237,13 +1237,16 @@ .. member:: SparseLinearAlgebraLibrary Solver::Options::sparse_linear_algebra_library_type - Default:``SUITE_SPARSE`` + Default: The highest available according to: ``SUITE_SPARSE`` > + ``CX_SPARSE`` > ``EIGEN_SPARSE`` > ``NO_SPARSE`` Ceres supports the use of three sparse linear algebra libraries, ``SuiteSparse``, which is enabled by setting this parameter to ``SUITE_SPARSE``, ``CXSparse``, which can be selected by setting - this parameter to ```CX_SPARSE`` and ``Eigen`` which is enabled by - setting this parameter to ``EIGEN_SPARSE``. + this parameter to ``CX_SPARSE`` and ``Eigen`` which is enabled by + setting this parameter to ``EIGEN_SPARSE``. Lastly, ``NO_SPARSE`` + means that no sparse linear solver should be used; note that this is + irrespective of whether Ceres was compiled with support for one. ``SuiteSparse`` is a sophisticated and complex sparse linear algebra library and should be used in general.
diff --git a/include/ceres/solver.h b/include/ceres/solver.h index 791630d..b2fa3ae 100644 --- a/include/ceres/solver.h +++ b/include/ceres/solver.h
@@ -104,7 +104,8 @@ // Choose a default sparse linear algebra library in the order: // - // SUITE_SPARSE > CX_SPARSE > EIGEN_SPARSE + // SUITE_SPARSE > CX_SPARSE > EIGEN_SPARSE > NO_SPARSE + sparse_linear_algebra_library_type = NO_SPARSE; #if !defined(CERES_NO_SUITESPARSE) sparse_linear_algebra_library_type = SUITE_SPARSE; #else
diff --git a/include/ceres/types.h b/include/ceres/types.h index a07c893..8915188 100644 --- a/include/ceres/types.h +++ b/include/ceres/types.h
@@ -157,7 +157,12 @@ // Eigen's sparse linear algebra routines. In particular Ceres uses // the Simplicial LDLT routines. - EIGEN_SPARSE + EIGEN_SPARSE, + + // No sparse linear solver should be used. This does not necessarily + // imply that Ceres was built without any sparse library, although that + // is the likely use case, merely that one should not be used. + NO_SPARSE }; enum DenseLinearAlgebraLibraryType {
diff --git a/internal/ceres/solver.cc b/internal/ceres/solver.cc index de170c3..738828b 100644 --- a/internal/ceres/solver.cc +++ b/internal/ceres/solver.cc
@@ -228,6 +228,20 @@ } #endif + if (options.sparse_linear_algebra_library_type == NO_SPARSE) { + if (options.linear_solver_type == SPARSE_NORMAL_CHOLESKY) { + *error = "Can't use SPARSE_NORMAL_CHOLESKY as " + "sparse_linear_algebra_library_type is NO_SPARSE."; + return false; + } + + if (options.linear_solver_type == SPARSE_SCHUR) { + *error = "Can't use SPARSE_SCHUR as " + "sparse_linear_algebra_library_type is NO_SPARSE."; + return false; + } + } + if (options.trust_region_strategy_type == DOGLEG) { if (options.linear_solver_type == ITERATIVE_SCHUR || options.linear_solver_type == CGNR) {
diff --git a/internal/ceres/solver_test.cc b/internal/ceres/solver_test.cc index cd82f55..4069578 100644 --- a/internal/ceres/solver_test.cc +++ b/internal/ceres/solver_test.cc
@@ -293,6 +293,42 @@ } #endif +TEST(Solver, SparseNormalCholeskyNoSparseLibrary) { + Solver::Options options; + options.sparse_linear_algebra_library_type = NO_SPARSE; + options.linear_solver_type = SPARSE_NORMAL_CHOLESKY; + string message; + EXPECT_FALSE(options.IsValid(&message)); +} + +TEST(Solver, SparseSchurNoSparseLibrary) { + Solver::Options options; + options.sparse_linear_algebra_library_type = NO_SPARSE; + options.linear_solver_type = SPARSE_SCHUR; + string message; + EXPECT_FALSE(options.IsValid(&message)); +} + +TEST(Solver, IterativeSchurWithClusterJacobiPerconditionerNoSparseLibrary) { + Solver::Options options; + options.sparse_linear_algebra_library_type = NO_SPARSE; + options.linear_solver_type = ITERATIVE_SCHUR; + // Requires SuiteSparse. + options.preconditioner_type = CLUSTER_JACOBI; + string message; + EXPECT_FALSE(options.IsValid(&message)); +} + +TEST(Solver, IterativeSchurWithClusterTridiagonalPerconditionerNoSparseLibrary) { + Solver::Options options; + options.sparse_linear_algebra_library_type = NO_SPARSE; + options.linear_solver_type = ITERATIVE_SCHUR; + // Requires SuiteSparse. + options.preconditioner_type = CLUSTER_TRIDIAGONAL; + string message; + EXPECT_FALSE(options.IsValid(&message)); +} + TEST(Solver, IterativeLinearSolverForDogleg) { Solver::Options options; options.trust_region_strategy_type = DOGLEG;
diff --git a/internal/ceres/trust_region_preprocessor_test.cc b/internal/ceres/trust_region_preprocessor_test.cc index a6189ad..b895a72 100644 --- a/internal/ceres/trust_region_preprocessor_test.cc +++ b/internal/ceres/trust_region_preprocessor_test.cc
@@ -201,16 +201,16 @@ } #if defined(CERES_USE_EIGEN_SPARSE) || \ - !defined(CERES_NO_SUITE_SPARSE) || \ - !defined(CERES_NO_CX_SPARSE) + !defined(CERES_NO_SUITESPARSE) || \ + !defined(CERES_NO_CXSPARSE) TEST_F(LinearSolverAndEvaluatorCreationTest, SparseNormalCholesky) { PreprocessForGivenLinearSolverAndVerify(SPARSE_NORMAL_CHOLESKY); } #endif #if defined(CERES_USE_EIGEN_SPARSE) || \ - !defined(CERES_NO_SUITE_SPARSE) || \ - !defined(CERES_NO_CX_SPARSE) + !defined(CERES_NO_SUITESPARSE) || \ + !defined(CERES_NO_CXSPARSE) TEST_F(LinearSolverAndEvaluatorCreationTest, SparseSchur) { PreprocessForGivenLinearSolverAndVerify(SPARSE_SCHUR); }
diff --git a/internal/ceres/types.cc b/internal/ceres/types.cc index 4710261..52bd67d 100644 --- a/internal/ceres/types.cc +++ b/internal/ceres/types.cc
@@ -97,6 +97,7 @@ CASESTR(SUITE_SPARSE); CASESTR(CX_SPARSE); CASESTR(EIGEN_SPARSE); + CASESTR(NO_SPARSE); default: return "UNKNOWN"; } @@ -109,6 +110,7 @@ STRENUM(SUITE_SPARSE); STRENUM(CX_SPARSE); STRENUM(EIGEN_SPARSE); + STRENUM(NO_SPARSE); return false; }