Block AMD for SparseNormalCholesky + EIGEN_SPARSE. This is just the reordering routine. The integration with SparseNormalCholesky shall happen in a subsequent CL. Change-Id: I39ddc32aa66b11c368faf75404850fa0ae0d2b3a
diff --git a/internal/ceres/reorder_program.cc b/internal/ceres/reorder_program.cc index 4caa01f..d05f9f9 100644 --- a/internal/ceres/reorder_program.cc +++ b/internal/ceres/reorder_program.cc
@@ -46,6 +46,12 @@ #include "ceres/suitesparse.h" #include "ceres/triplet_sparse_matrix.h" #include "ceres/types.h" +#include "Eigen/SparseCore" + +#ifdef CERES_USE_EIGEN_SPARSE +#include "Eigen/OrderingMethods" +#endif + #include "glog/logging.h" namespace ceres { @@ -133,6 +139,53 @@ #endif // CERES_NO_CXSPARSE } +void OrderingForSparseNormalCholeskyUsingEigenSparse( + const TripletSparseMatrix& tsm_block_jacobian_transpose, + int* ordering) { +#ifndef CERES_USE_EIGEN_SPARSE + LOG(FATAL) << + "SPARSE_NORMAL_CHOLESKY cannot be used with EIGEN_SPARSE " + "because Ceres was not built with support for " + "Eigen's SimplicialLDLT decomposition. " + "This requires enabling building with -DEIGENSPARSE=ON."; +#else + + // This conversion from a TripletSparseMatrix to a Eigen::Triplet + // matrix is unfortunate, but unavoidable for now. It is not a + // significant performance penalty in the grand scheme of + // things. The right thing to do here would be to get a compressed + // row sparse matrix representation of the jacobian and go from + // there. But that is a project for another day. + + const int* rows = tsm_block_jacobian_transpose.rows(); + const int* cols = tsm_block_jacobian_transpose.cols(); + + typedef Eigen::SparseMatrix<int> SparseMatrix; + typedef Eigen::Triplet<int> Triplet; + std::vector<Triplet> triplets; + int num_nonzeros = tsm_block_jacobian_transpose.num_nonzeros(); + triplets.reserve(num_nonzeros); + for (int i = 0; i < num_nonzeros; ++i) { + triplets.push_back(Triplet(rows[i], cols[i], 1)); + } + + SparseMatrix block_jacobian_transpose( + tsm_block_jacobian_transpose.num_rows(), + tsm_block_jacobian_transpose.num_cols()); + block_jacobian_transpose.setFromTriplets(triplets.begin(), + triplets.end()); + SparseMatrix block_hessian = + block_jacobian_transpose * block_jacobian_transpose.transpose(); + + Eigen::AMDOrdering<int> amd_ordering; + Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic, int> perm; + amd_ordering(block_hessian, perm); + for (int i = 0; i < tsm_block_jacobian_transpose.num_rows(); ++i) { + ordering[i] = perm.indices()[i]; + } +#endif // CERES_USE_EIGEN_SPARSE +} + } // namespace bool ApplyOrdering(const ProblemImpl::ParameterMap& parameter_map, @@ -423,11 +476,18 @@ *tsm_block_jacobian_transpose, &ordering[0]); } else if (sparse_linear_algebra_library_type == EIGEN_SPARSE) { - // Starting with v3.2.2 Eigen has support for symbolic analysis on - // pre-ordered matrices. - // - // TODO(sameeragarwal): Apply block amd for eigen. +#if EIGEN_VERSION_AT_LEAST(3, 2, 2) + OrderingForSparseNormalCholeskyUsingEigenSparse( + *tsm_block_jacobian_transpose, + &ordering[0]); +#else + // For Eigen versions less than 3.2.2, there is nothing to do as + // older versions of Eigen do not expose a method for doing + // symbolic analysis on pre-ordered matrices, so a block + // pre-ordering is a bit pointless. + return true; +#endif } // Apply ordering.