Multiple sparse linear algebra backends.
1. Added support for CXSparse - SparseNormalCholesky and
SchurComplementSolver support SuiteSparse and CXSparse now.
I am not sure I will add suport for visibility based
preconditioning using CXSparse. Its not a high priority.
2. New enum SparseLinearAlgebraLibraryType which allows the user
to indicate which sparse linear algebra library should be used.
3. Updated tests for SolverImpl and system_test.
4. Build system changes to automatically detect CXSparse and
link to it by default -- just like SuiteSparse.
5. Minor bug fixes dealing in the cmake files and VBP.
6. Changed the order of the system test.
7. Deduped the unsymmetric linear solver test.
Change-Id: I33252a103c87b722ecb7ed7b5f0ae7fd91249244
diff --git a/internal/ceres/schur_complement_solver.cc b/internal/ceres/schur_complement_solver.cc
index 4db7c31..b30d6ed 100644
--- a/internal/ceres/schur_complement_solver.cc
+++ b/internal/ceres/schur_complement_solver.cc
@@ -32,6 +32,11 @@
#include <ctime>
#include <set>
#include <vector>
+
+#ifndef CERES_NO_CXSPARSE
+#include "cs.h"
+#endif // CERES_NO_CXSPARSE
+
#include "Eigen/Dense"
#include "ceres/block_random_access_dense_matrix.h"
#include "ceres/block_random_access_matrix.h"
@@ -48,6 +53,7 @@
#include "ceres/internal/scoped_ptr.h"
#include "ceres/types.h"
+
namespace ceres {
namespace internal {
@@ -139,18 +145,22 @@
return true;
}
-#ifndef CERES_NO_SUITESPARSE
+
SparseSchurComplementSolver::SparseSchurComplementSolver(
const LinearSolver::Options& options)
- : SchurComplementSolver(options),
- symbolic_factor_(NULL) {
+ : SchurComplementSolver(options) {
+#ifndef CERES_NO_SUITESPARSE
+ symbolic_factor_ = NULL;
+#endif // CERES_NO_SUITESPARSE
}
SparseSchurComplementSolver::~SparseSchurComplementSolver() {
+#ifndef CERES_NO_SUITESPARSE
if (symbolic_factor_ != NULL) {
ss_.Free(symbolic_factor_);
symbolic_factor_ = NULL;
}
+#endif // CERES_NO_SUITESPARSE
}
// Determine the non-zero blocks in the Schur Complement matrix, and
@@ -224,10 +234,28 @@
set_rhs(new double[lhs()->num_rows()]);
}
+bool SparseSchurComplementSolver::SolveReducedLinearSystem(double* solution) {
+ switch (options().sparse_linear_algebra_library) {
+ case SUITE_SPARSE:
+ return SolveReducedLinearSystemUsingSuiteSparse(solution);
+ case CX_SPARSE:
+ return SolveReducedLinearSystemUsingCXSparse(solution);
+ default:
+ LOG(FATAL) << "Unknown sparse linear algebra library : "
+ << options().sparse_linear_algebra_library;
+ }
+
+ LOG(FATAL) << "Unknown sparse linear algebra library : "
+ << options().sparse_linear_algebra_library;
+ return false;
+}
+
+#ifndef CERES_NO_SUITESPARSE
// Solve the system Sx = r, assuming that the matrix S is stored in a
// BlockRandomAccessSparseMatrix. The linear system is solved using
// CHOLMOD's sparse cholesky factorization routines.
-bool SparseSchurComplementSolver::SolveReducedLinearSystem(double* solution) {
+bool SparseSchurComplementSolver::SolveReducedLinearSystemUsingSuiteSparse(
+ double* solution) {
// Extract the TripletSparseMatrix that is used for actually storing S.
TripletSparseMatrix* tsm =
const_cast<TripletSparseMatrix*>(
@@ -272,7 +300,58 @@
ss_.Free(cholmod_solution);
return true;
}
+#else
+bool SparseSchurComplementSolver::SolveReducedLinearSystemUsingSuiteSparse(
+ double* solution) {
+ LOG(FATAL) << "No SuiteSparse support in Ceres.";
+ return false;
+}
#endif // CERES_NO_SUITESPARSE
+#ifndef CERES_NO_CXSPARSE
+// Solve the system Sx = r, assuming that the matrix S is stored in a
+// BlockRandomAccessSparseMatrix. The linear system is solved using
+// CXSparse's sparse cholesky factorization routines.
+bool SparseSchurComplementSolver::SolveReducedLinearSystemUsingCXSparse(
+ double* solution) {
+ // Extract the TripletSparseMatrix that is used for actually storing S.
+ TripletSparseMatrix* tsm =
+ const_cast<TripletSparseMatrix*>(
+ down_cast<const BlockRandomAccessSparseMatrix*>(lhs())->matrix());
+
+ const int num_rows = tsm->num_rows();
+
+ // The case where there are no f blocks, and the system is block
+ // diagonal.
+ if (num_rows == 0) {
+ return true;
+ }
+
+ cs_di_sparse tsm_wrapper;
+ tsm_wrapper.nzmax = tsm->num_nonzeros();
+ tsm_wrapper.m = num_rows;
+ tsm_wrapper.n = num_rows;
+ tsm_wrapper.p = tsm->mutable_cols();
+ tsm_wrapper.i = tsm->mutable_rows();
+ tsm_wrapper.x = tsm->mutable_values();
+ tsm_wrapper.nz = tsm->num_nonzeros();
+
+ cs_di_sparse* lhs = cs_compress(&tsm_wrapper);
+ VectorRef(solution, num_rows) = ConstVectorRef(rhs(), num_rows);
+
+ // It maybe worth caching the ordering here, but for now we are
+ // going to go with the simple cholsol based implementation.
+ int ok = cs_di_cholsol(1, lhs, solution);
+ cs_free(lhs);
+ return ok;
+}
+#else
+bool SparseSchurComplementSolver::SolveReducedLinearSystemUsingCXSparse(
+ double* solution) {
+ LOG(FATAL) << "No CXSparse support in Ceres.";
+ return false;
+}
+#endif // CERES_NO_CXPARSE
+
} // namespace internal
} // namespace ceres