Add Block AMD ordering for SPARSE_SCHUR + EIGEN_SPARSE. Ordering routines for the Schur complement when using EIGEN_SPARSE. Also integration into SchurComplementSolver. Part of this CL is also a refactoring of the block jacobian matrix construction. Change-Id: I11d665cc7d4867c64190e6fed1118f4d2e13d59b
diff --git a/internal/ceres/reorder_program.cc b/internal/ceres/reorder_program.cc index d05f9f9..5b9212f 100644 --- a/internal/ceres/reorder_program.cc +++ b/internal/ceres/reorder_program.cc
@@ -78,6 +78,28 @@ return min_parameter_block_position; } +#ifdef CERES_USE_EIGEN_SPARSE +Eigen::SparseMatrix<int> CreateBlockJacobian( + const TripletSparseMatrix& block_jacobian_transpose) { + typedef Eigen::SparseMatrix<int> SparseMatrix; + typedef Eigen::Triplet<int> Triplet; + + const int* rows = block_jacobian_transpose.rows(); + const int* cols = block_jacobian_transpose.cols(); + int num_nonzeros = block_jacobian_transpose.num_nonzeros(); + std::vector<Triplet> triplets; + triplets.reserve(num_nonzeros); + for (int i = 0; i < num_nonzeros; ++i) { + triplets.push_back(Triplet(cols[i], rows[i], 1)); + } + + SparseMatrix block_jacobian(block_jacobian_transpose.num_cols(), + block_jacobian_transpose.num_rows()); + block_jacobian.setFromTriplets(triplets.begin(), triplets.end()); + return block_jacobian; +} +#endif + void OrderingForSparseNormalCholeskyUsingSuiteSparse( const TripletSparseMatrix& tsm_block_jacobian_transpose, const vector<ParameterBlock*>& parameter_blocks, @@ -157,30 +179,17 @@ // 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(); + const SparseMatrix block_jacobian = + CreateBlockJacobian(tsm_block_jacobian_transpose); + const SparseMatrix block_hessian = + block_jacobian.transpose() * block_jacobian; 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) { + for (int i = 0; i < block_hessian.rows(); ++i) { ordering[i] = perm.indices()[i]; } #endif // CERES_USE_EIGEN_SPARSE @@ -360,6 +369,64 @@ #endif } +void MaybeReorderSchurComplementColumnsUsingEigen( + const int size_of_first_elimination_group, + const ProblemImpl::ParameterMap& parameter_map, + Program* program) { +#if !EIGEN_VERSION_AT_LEAST(3, 2, 2) || !defined(CERES_USE_EIGEN_SPARSE) + return; +#else + + scoped_ptr<TripletSparseMatrix> tsm_block_jacobian_transpose( + program->CreateJacobianBlockSparsityTranspose()); + + typedef Eigen::SparseMatrix<int> SparseMatrix; + const SparseMatrix block_jacobian = + CreateBlockJacobian(*tsm_block_jacobian_transpose); + const int num_rows = block_jacobian.rows(); + const int num_cols = block_jacobian.cols(); + + // Vertically partition the jacobian in parameter blocks of type E + // and F. + const SparseMatrix E = + block_jacobian.block(0, + 0, + num_rows, + size_of_first_elimination_group); + const SparseMatrix F = + block_jacobian.block(0, + size_of_first_elimination_group, + num_rows, + num_cols - size_of_first_elimination_group); + + // Block sparsity pattern of the schur complement. + const SparseMatrix block_schur_complement = + F.transpose() * F - F.transpose() * E * E.transpose() * F; + + Eigen::AMDOrdering<int> amd_ordering; + Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic, int> perm; + amd_ordering(block_schur_complement, perm); + + const vector<ParameterBlock*>& parameter_blocks = program->parameter_blocks(); + vector<ParameterBlock*> ordering(num_cols); + + // The ordering of the first size_of_first_elimination_group does + // not matter, so we preserve the existing ordering. + for (int i = 0; i < size_of_first_elimination_group; ++i) { + ordering[i] = parameter_blocks[i]; + } + + // For the rest of the blocks, use the ordering computed using AMD. + for (int i = 0; i < block_schur_complement.cols(); ++i) { + ordering[size_of_first_elimination_group + i] = + parameter_blocks[size_of_first_elimination_group + perm.indices()[i]]; + } + + swap(*program->mutable_parameter_blocks(), ordering); + program->SetParameterOffsetsAndIndex(); +#endif +} + bool ReorderProgramForSchurTypeLinearSolver( const LinearSolverType linear_solver_type, const SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type, @@ -430,19 +497,24 @@ program->SetParameterOffsetsAndIndex(); - if (linear_solver_type == SPARSE_SCHUR && - sparse_linear_algebra_library_type == SUITE_SPARSE) { - MaybeReorderSchurComplementColumnsUsingSuiteSparse( - *parameter_block_ordering, - program); + const int size_of_first_elimination_group = + parameter_block_ordering->group_to_elements().begin()->second.size(); + + if (linear_solver_type == SPARSE_SCHUR) { + if (sparse_linear_algebra_library_type == SUITE_SPARSE) { + MaybeReorderSchurComplementColumnsUsingSuiteSparse( + *parameter_block_ordering, + program); + } else if (sparse_linear_algebra_library_type == EIGEN_SPARSE) { + MaybeReorderSchurComplementColumnsUsingEigen( + size_of_first_elimination_group, + parameter_map, + program); + } } - - // Schur type solvers also require that their residual blocks be // lexicographically ordered. - const int size_of_first_elimination_group = - parameter_block_ordering->group_to_elements().begin()->second.size(); if (!LexicographicallyOrderResidualBlocks(size_of_first_elimination_group, program, error)) {
diff --git a/internal/ceres/schur_complement_solver.h b/internal/ceres/schur_complement_solver.h index 723b149..d1efd28 100644 --- a/internal/ceres/schur_complement_solver.h +++ b/internal/ceres/schur_complement_solver.h
@@ -49,6 +49,7 @@ #ifdef CERES_USE_EIGEN_SPARSE #include "Eigen/SparseCholesky" +#include "Eigen/OrderingMethods" #endif namespace ceres { @@ -189,7 +190,19 @@ cs_dis* cxsparse_factor_; #ifdef CERES_USE_EIGEN_SPARSE - typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<double> > SimplicialLDLT; + + // For Eigen versions less than 3.2.2, we cannot pre-order the + // Jacobian, so we must use an AMD ordering. +#if EIGEN_VERSION_AT_LEAST(3,2,2) + typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>, + Eigen::Lower, + Eigen::NaturalOrdering<int> > SimplicialLDLT; +#else + typedef Eigen::SimplicialLDLT<Eigen::SparseMatrix<double>, + Eigen::Lower, + Eigen::AMDOrdering<int> > SimplicialLDLT; +#endif + scoped_ptr<SimplicialLDLT> simplicial_ldlt_; #endif