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;
 }