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