Relax the limitation that SchurEliminator::Eliminate requires a rhs.
When using the SchurEliminator to compute a preconditioner, there
is no rhs. This CL removes that limitation and simplifies the
call sites in the two preconditioners.
https://github.com/ceres-solver/ceres-solver/issues/271
Change-Id: I05b8518fdc9f0d1a6d88ae76d3a7e8e838e7204a
diff --git a/internal/ceres/schur_eliminator.h b/internal/ceres/schur_eliminator.h
index d93bcb0..deae5a5 100644
--- a/internal/ceres/schur_eliminator.h
+++ b/internal/ceres/schur_eliminator.h
@@ -218,9 +218,6 @@
// parameters as template arguments so that they are visible to the
// compiler and can be used for compile time optimization of the low
// level linear algebra routines.
-//
-// This implementation is mulithreaded using OpenMP. The level of
-// parallelism is controlled by LinearSolver::Options::num_threads.
template <int kRowBlockSize = Eigen::Dynamic,
int kEBlockSize = Eigen::Dynamic,
int kFBlockSize = Eigen::Dynamic >
diff --git a/internal/ceres/schur_eliminator_impl.h b/internal/ceres/schur_eliminator_impl.h
index 06d0983..bc74015 100644
--- a/internal/ceres/schur_eliminator_impl.h
+++ b/internal/ceres/schur_eliminator_impl.h
@@ -176,7 +176,9 @@
double* rhs) {
if (lhs->num_rows() > 0) {
lhs->SetZero();
- VectorRef(rhs, lhs->num_rows()).setZero();
+ if (rhs) {
+ VectorRef(rhs, lhs->num_rows()).setZero();
+ }
}
const CompressedRowBlockStructure* bs = A->block_structure();
@@ -276,15 +278,16 @@
//
// rhs = F'b - F'E(E'E)^(-1) E'b
- FixedArray<double, 8> inverse_ete_g(e_block_size);
- MatrixVectorMultiply<kEBlockSize, kEBlockSize, 0>(
- inverse_ete.data(),
- e_block_size,
- e_block_size,
- g.get(),
- inverse_ete_g.get());
-
- UpdateRhs(chunk, A, b, chunk.start, inverse_ete_g.get(), rhs);
+ if (rhs) {
+ FixedArray<double, 8> inverse_ete_g(e_block_size);
+ MatrixVectorMultiply<kEBlockSize, kEBlockSize, 0>(
+ inverse_ete.data(),
+ e_block_size,
+ e_block_size,
+ g.get(),
+ inverse_ete_g.get());
+ UpdateRhs(chunk, A, b, chunk.start, inverse_ete_g.get(), rhs);
+ }
// S -= F'E(E'E)^{-1}E'F
ChunkOuterProduct(
@@ -469,12 +472,13 @@
values + e_cell.position, row.block.size, e_block_size,
ete->data(), 0, 0, e_block_size, e_block_size);
- // g += E_i' b_i
- MatrixTransposeVectorMultiply<kRowBlockSize, kEBlockSize, 1>(
- values + e_cell.position, row.block.size, e_block_size,
- b + b_pos,
- g);
-
+ if (b) {
+ // g += E_i' b_i
+ MatrixTransposeVectorMultiply<kRowBlockSize, kEBlockSize, 1>(
+ values + e_cell.position, row.block.size, e_block_size,
+ b + b_pos,
+ g);
+ }
// buffer = E'F. This computation is done by iterating over the
// f_blocks for each row in the chunk.
@@ -561,6 +565,10 @@
const CompressedRowBlockStructure* bs = A->block_structure();
const double* values = A->values();
for (; row_block_counter < bs->rows.size(); ++row_block_counter) {
+ NoEBlockRowOuterProduct(A, row_block_counter, lhs);
+ if (!rhs) {
+ continue;
+ }
const CompressedRow& row = bs->rows[row_block_counter];
for (int c = 0; c < row.cells.size(); ++c) {
const int block_id = row.cells[c].block_id;
@@ -571,7 +579,6 @@
b + row.block.position,
rhs + lhs_row_layout_[block]);
}
- NoEBlockRowOuterProduct(A, row_block_counter, lhs);
}
}
diff --git a/internal/ceres/schur_jacobi_preconditioner.cc b/internal/ceres/schur_jacobi_preconditioner.cc
index 3ad5dd7..3a3dffd 100644
--- a/internal/ceres/schur_jacobi_preconditioner.cc
+++ b/internal/ceres/schur_jacobi_preconditioner.cc
@@ -88,19 +88,8 @@
const int num_rows = m_->num_rows();
CHECK_GT(num_rows, 0);
- // We need a dummy rhs vector and a dummy b vector since the Schur
- // eliminator combines the computation of the reduced camera matrix
- // with the computation of the right hand side of that linear
- // system.
- //
- // TODO(sameeragarwal): Perhaps its worth refactoring the
- // SchurEliminator::Eliminate function to allow NULL for the rhs. As
- // of now it does not seem to be worth the effort.
- Vector rhs = Vector::Zero(m_->num_rows());
- Vector b = Vector::Zero(A.num_rows());
-
// Compute a subset of the entries of the Schur complement.
- eliminator_->Eliminate(&A, b.data(), D, m_.get(), rhs.data());
+ eliminator_->Eliminate(&A, nullptr, D, m_.get(), nullptr);
m_->Invert();
return true;
}
diff --git a/internal/ceres/visibility_based_preconditioner.cc b/internal/ceres/visibility_based_preconditioner.cc
index 7cd9bd4..5eebb69 100644
--- a/internal/ceres/visibility_based_preconditioner.cc
+++ b/internal/ceres/visibility_based_preconditioner.cc
@@ -338,19 +338,8 @@
const int num_rows = m_->num_rows();
CHECK_GT(num_rows, 0);
- // We need a dummy rhs vector and a dummy b vector since the Schur
- // eliminator combines the computation of the reduced camera matrix
- // with the computation of the right hand side of that linear
- // system.
- //
- // TODO(sameeragarwal): Perhaps its worth refactoring the
- // SchurEliminator::Eliminate function to allow NULL for the rhs. As
- // of now it does not seem to be worth the effort.
- Vector rhs = Vector::Zero(m_->num_rows());
- Vector b = Vector::Zero(A.num_rows());
-
// Compute a subset of the entries of the Schur complement.
- eliminator_->Eliminate(&A, b.data(), D, m_.get(), rhs.data());
+ eliminator_->Eliminate(&A, nullptr, D, m_.get(), nullptr);
// Try factorizing the matrix. For CLUSTER_JACOBI, this should
// always succeed modulo some numerical/conditioning problems. For