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