Add a single precision variant of EigenSparseCholesky. Given a double precision linear system, solve it using a single precision Cholesky factorization. Change-Id: I8a6e8b7a451e961a8a23a62dd7b5d8159c4db8ce
diff --git a/internal/ceres/eigensparse.cc b/internal/ceres/eigensparse.cc index 4945036..8ca1d17 100644 --- a/internal/ceres/eigensparse.cc +++ b/internal/ceres/eigensparse.cc
@@ -42,7 +42,7 @@ namespace internal { template <typename Solver> -class EigenSparseCholeskyTemplate : public EigenSparseCholesky { +class EigenSparseCholeskyTemplate : public SparseCholesky { public: EigenSparseCholeskyTemplate() : analyzed_(false) {} virtual ~EigenSparseCholeskyTemplate() {} @@ -51,7 +51,8 @@ } virtual LinearSolverTerminationType Factorize( - const Eigen::SparseMatrix<double>& lhs, std::string* message) { + const Eigen::SparseMatrix<typename Solver::Scalar>& lhs, + std::string* message) { if (!analyzed_) { solver_.analyzePattern(lhs); @@ -77,13 +78,24 @@ return LINEAR_SOLVER_SUCCESS; } - virtual LinearSolverTerminationType Solve(const double* rhs, - double* solution, + virtual LinearSolverTerminationType Solve(const double* rhs_ptr, + double* solution_ptr, std::string* message) { CHECK(analyzed_) << "Solve called without a call to Factorize first."; - VectorRef(solution, solver_.cols()) = - solver_.solve(ConstVectorRef(rhs, solver_.cols())); + ConstVectorRef rhs(rhs_ptr, solver_.cols()); + VectorRef solution(solution_ptr, solver_.cols()); + + // The two casts are needed if the Scalar in this class is not + // double. For code simplicitly we are going to assume that Eigen + // is smart enough to figure out that casting a double Vector to a + // double Vector is a straight copy. If this turns into a + // performance bottleneck (unlikely), we can revisit this. + solution = + solver_ + .solve(rhs.template cast<typename Solver::Scalar>()) + .template cast<double>(); + if (solver_.info() != Eigen::Success) { *message = "Eigen failure. Unable to do triangular solve."; return LINEAR_SOLVER_FAILURE; @@ -94,23 +106,37 @@ virtual LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs, std::string* message) { CHECK_EQ(lhs->storage_type(), StorageType()); - Eigen::MappedSparseMatrix<double, Eigen::ColMajor> eigen_lhs( - lhs->num_rows(), - lhs->num_rows(), - lhs->num_nonzeros(), - lhs->mutable_rows(), - lhs->mutable_cols(), - lhs->mutable_values()); + + typename Solver::Scalar* values_ptr = NULL; + if (std::is_same<typename Solver::Scalar, double>::value) { + values_ptr = + reinterpret_cast<typename Solver::Scalar*>(lhs->mutable_values()); + } else { + // In the case where the scalar used in this class is not + // double. In that case, make a copy of the values array in the + // CompressedRowSparseMatrix and cast it to Scalar along the way. + values_ = ConstVectorRef(lhs->values(), lhs->num_nonzeros()) + .cast<typename Solver::Scalar>(); + values_ptr = values_.data(); + } + + Eigen::MappedSparseMatrix<typename Solver::Scalar, Eigen::ColMajor> + eigen_lhs(lhs->num_rows(), + lhs->num_rows(), + lhs->num_nonzeros(), + lhs->mutable_rows(), + lhs->mutable_cols(), + values_ptr); return Factorize(eigen_lhs, message); } private: + Eigen::Matrix<typename Solver::Scalar, Eigen::Dynamic, 1> values_; bool analyzed_; Solver solver_; }; -EigenSparseCholesky* EigenSparseCholesky::Create( - const OrderingType ordering_type) { +SparseCholesky* EigenSparseCholesky::Create(const OrderingType ordering_type) { // The preprocessor gymnastics here are dealing with the fact that // before version 3.2.2, Eigen did not support a third template // parameter to specify the ordering and it always defaults to AMD. @@ -137,6 +163,35 @@ EigenSparseCholesky::~EigenSparseCholesky() {} +SparseCholesky* FloatEigenSparseCholesky::Create( + const OrderingType ordering_type) { + // The preprocessor gymnastics here are dealing with the fact that + // before version 3.2.2, Eigen did not support a third template + // parameter to specify the ordering and it always defaults to AMD. +#if EIGEN_VERSION_AT_LEAST(3, 2, 2) + typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<float>, + Eigen::Upper, + Eigen::AMDOrdering<int> > + WithAMDOrdering; + typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<float>, + Eigen::Upper, + Eigen::NaturalOrdering<int> > + WithNaturalOrdering; + if (ordering_type == AMD) { + LOG(FATAL) << "We should not be doing this"; + return new EigenSparseCholeskyTemplate<WithAMDOrdering>(); + } else { + return new EigenSparseCholeskyTemplate<WithNaturalOrdering>(); + } +#else + typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<float>, Eigen::Upper> + WithAMDOrdering; + return new EigenSparseCholeskyTemplate<WithAMDOrdering>(); +#endif +} + +FloatEigenSparseCholesky::~FloatEigenSparseCholesky() {} + } // namespace internal } // namespace ceres
diff --git a/internal/ceres/eigensparse.h b/internal/ceres/eigensparse.h index c73a811..ce8ef34 100644 --- a/internal/ceres/eigensparse.h +++ b/internal/ceres/eigensparse.h
@@ -49,10 +49,29 @@ class EigenSparseCholesky : public SparseCholesky { public: // Factory - static EigenSparseCholesky* Create(const OrderingType ordering_type); + static SparseCholesky* Create(const OrderingType ordering_type); // SparseCholesky interface. virtual ~EigenSparseCholesky(); + virtual LinearSolverTerminationType Factorize( + CompressedRowSparseMatrix* lhs, std::string* message) = 0; + virtual CompressedRowSparseMatrix::StorageType StorageType() const = 0; + virtual LinearSolverTerminationType Solve(const double* rhs, + double* solution, + std::string* message) = 0; +}; + +// Even though the input is double precision linear system, this class +// solves it by computing a single precision Cholesky factorization. +class FloatEigenSparseCholesky : public SparseCholesky { + public: + // Factory + static SparseCholesky* Create(const OrderingType ordering_type); + + // SparseCholesky interface. + virtual ~FloatEigenSparseCholesky(); + virtual LinearSolverTerminationType Factorize( + CompressedRowSparseMatrix* lhs, std::string* message) = 0; virtual CompressedRowSparseMatrix::StorageType StorageType() const = 0; virtual LinearSolverTerminationType Solve(const double* rhs, double* solution,
diff --git a/internal/ceres/sparse_cholesky_test.cc b/internal/ceres/sparse_cholesky_test.cc index 79db689..b75e3aa 100644 --- a/internal/ceres/sparse_cholesky_test.cc +++ b/internal/ceres/sparse_cholesky_test.cc
@@ -205,6 +205,13 @@ ::testing::Values(AMD, NATURAL), ::testing::Values(true, false)), ParamInfoToString); + +INSTANTIATE_TEST_CASE_P(EigenSparseCholeskySingle, + SparseCholeskyTest, + ::testing::Combine(::testing::Values(EIGEN_SPARSE), + ::testing::Values(AMD, NATURAL), + ::testing::Values(true, false)), + ParamInfoToString); #endif } // namespace internal