Address some of the comments on CGNR patch
- Rename BlockDiagonalPreconditioner to BlockJacobiPreconditioner
- Include the diagonal in the block jacobi preconditioner.
- Better flag help for eta.
- Enable test for CGNR
- Rename CONJUGATE_GRADIENTS to CGNR.
- etc.
diff --git a/examples/bundle_adjuster.cc b/examples/bundle_adjuster.cc
index 885dbe2..0331f32 100644
--- a/examples/bundle_adjuster.cc
+++ b/examples/bundle_adjuster.cc
@@ -97,8 +97,8 @@
options->linear_solver_type = ceres::ITERATIVE_SCHUR;
} else if (FLAGS_solver_type == "cholesky") {
options->linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
- } else if (FLAGS_solver_type == "conjugate_gradients") {
- options->linear_solver_type = ceres::CONJUGATE_GRADIENTS;
+ } else if (FLAGS_solver_type == "cgnr") {
+ options->linear_solver_type = ceres::CGNR;
} else if (FLAGS_solver_type == "dense_qr") {
// DENSE_QR is included here for completeness, but actually using
// this option is a bad idea due to the amount of memory needed
@@ -110,14 +110,14 @@
<< FLAGS_solver_type;
}
- if (options->linear_solver_type == ceres::CONJUGATE_GRADIENTS) {
+ if (options->linear_solver_type == ceres::CGNR) {
options->linear_solver_min_num_iterations = 5;
if (FLAGS_preconditioner_type == "identity") {
options->preconditioner_type = ceres::IDENTITY;
} else if (FLAGS_preconditioner_type == "jacobi") {
options->preconditioner_type = ceres::JACOBI;
} else {
- LOG(FATAL) << "For CONJUGATE_GRADIENTS, only identity and jacobian "
+ LOG(FATAL) << "For CGNR, only identity and jacobian "
<< "preconditioners are supported. Got: "
<< FLAGS_preconditioner_type;
}
diff --git a/include/ceres/types.h b/include/ceres/types.h
index ea41b4d..b83a266 100644
--- a/include/ceres/types.h
+++ b/include/ceres/types.h
@@ -83,12 +83,8 @@
// problems.
ITERATIVE_SCHUR,
- // Symmetric positive definite solvers
-
- // This is not meant for direct client use; it is used under the
- // hood while using ITERATIVE_SCHUR. Once there is a decent
- // preconditioner, this will make sense for general sparse problems.
- CONJUGATE_GRADIENTS
+ // Conjugate gradients on the normal equations.
+ CGNR
};
enum PreconditionerType {
diff --git a/internal/ceres/CMakeLists.txt b/internal/ceres/CMakeLists.txt
index 03d650a..f105fcd 100644
--- a/internal/ceres/CMakeLists.txt
+++ b/internal/ceres/CMakeLists.txt
@@ -29,8 +29,8 @@
# Author: keir@google.com (Keir Mierle)
SET(CERES_INTERNAL_SRC
- block_diagonal_preconditioner.cc
block_evaluate_preparer.cc
+ block_jacobi_preconditioner.cc
block_jacobian_writer.cc
block_random_access_dense_matrix.cc
block_random_access_matrix.cc
diff --git a/internal/ceres/block_diagonal_preconditioner.h b/internal/ceres/block_diagonal_preconditioner.h
deleted file mode 100644
index 561b0db..0000000
--- a/internal/ceres/block_diagonal_preconditioner.h
+++ /dev/null
@@ -1,68 +0,0 @@
-// Ceres Solver - A fast non-linear least squares minimizer
-// Copyright 2012 Google Inc. All rights reserved.
-// http://code.google.com/p/ceres-solver/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are met:
-//
-// * Redistributions of source code must retain the above copyright notice,
-// this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above copyright notice,
-// this list of conditions and the following disclaimer in the documentation
-// and/or other materials provided with the distribution.
-// * Neither the name of Google Inc. nor the names of its contributors may be
-// used to endorse or promote products derived from this software without
-// specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-// POSSIBILITY OF SUCH DAMAGE.
-//
-// Author: keir@google.com (Keir Mierle)
-
-#ifndef CERES_INTERNAL_BLOCK_DIAGONAL_PRECONDITIONER_H_
-#define CERES_INTERNAL_BLOCK_DIAGONAL_PRECONDITIONER_H_
-
-#include <vector>
-#include "ceres/linear_operator.h"
-
-namespace ceres {
-namespace internal {
-
-class CompressedRowBlockStructure;
-class LinearOperator;
-class SparseMatrix;
-
-// A block diagonal preconditioner; also known as block-Jacobi.
-class BlockDiagonalPreconditioner : public LinearOperator {
- public:
- BlockDiagonalPreconditioner(const LinearOperator& A);
- virtual ~BlockDiagonalPreconditioner();
-
- void Update(const LinearOperator& matrix);
-
- virtual void RightMultiply(const double* x, double* y) const;
- virtual void LeftMultiply(const double* x, double* y) const;
-
- virtual int num_rows() const { return size_; }
- virtual int num_cols() const { return size_; }
-
- private:
- std::vector<double*> blocks_;
- std::vector<double> block_storage_;
- int size_;
- const CompressedRowBlockStructure& block_structure_;
-};
-
-} // namespace internal
-} // namespace ceres
-
-#endif // CERES_INTERNAL_LINEAR_OPERATOR_H_
diff --git a/internal/ceres/block_diagonal_preconditioner.cc b/internal/ceres/block_jacobi_preconditioner.cc
similarity index 74%
rename from internal/ceres/block_diagonal_preconditioner.cc
rename to internal/ceres/block_jacobi_preconditioner.cc
index 0779a91..f043200 100644
--- a/internal/ceres/block_diagonal_preconditioner.cc
+++ b/internal/ceres/block_jacobi_preconditioner.cc
@@ -28,22 +28,23 @@
//
// Author: keir@google.com (Keir Mierle)
-#include "ceres/block_diagonal_preconditioner.h"
+#include "ceres/block_jacobi_preconditioner.h"
#include "Eigen/Cholesky"
#include "ceres/block_sparse_matrix.h"
#include "ceres/block_structure.h"
#include "ceres/casts.h"
+#include "ceres/integral_types.h"
#include "ceres/internal/eigen.h"
namespace ceres {
namespace internal {
-BlockDiagonalPreconditioner::BlockDiagonalPreconditioner(
+BlockJacobiPreconditioner::BlockJacobiPreconditioner(
const LinearOperator& A)
: block_structure_(
- *(down_cast<const BlockSparseMatrix*>(&A)->block_structure())) {
-
+ *(down_cast<const BlockSparseMatrix*>(&A)->block_structure())),
+ num_rows_(A.num_rows()) {
// Calculate the amount of storage needed.
int storage_needed = 0;
for (int c = 0; c < block_structure_.cols.size(); ++c) {
@@ -56,7 +57,7 @@
block_storage_.resize(storage_needed);
// Put pointers to the storage in the offsets.
- double *block_cursor = &block_storage_[0];
+ double* block_cursor = &block_storage_[0];
for (int c = 0; c < block_structure_.cols.size(); ++c) {
int size = block_structure_.cols[c].size;
blocks_[c] = block_cursor;
@@ -64,10 +65,10 @@
}
}
-BlockDiagonalPreconditioner::~BlockDiagonalPreconditioner() {
+BlockJacobiPreconditioner::~BlockJacobiPreconditioner() {
}
-void BlockDiagonalPreconditioner::Update(const LinearOperator& matrix) {
+void BlockJacobiPreconditioner::Update(const LinearOperator& matrix, const double* D) {
const BlockSparseMatrix& A = *(down_cast<const BlockSparseMatrix*>(&matrix));
const CompressedRowBlockStructure* bs = A.block_structure();
@@ -86,20 +87,35 @@
MatrixRef(blocks_[cells[c].block_id],
col_block_size,
col_block_size).noalias() += m.transpose() * m;
+
+ // TODO(keir): Figure out when the below expression is actually faster
+ // than doing the full rank update. The issue is that for smaller sizes,
+ // the rankUpdate() function is slower than the full product done above.
+ //
+ // On the typical bundling problems, the above product is ~5% faster.
+ //
+ // MatrixRef(blocks_[cells[c].block_id],
+ // col_block_size,
+ // col_block_size).selfadjointView<Eigen::Upper>().rankUpdate(m);
+ //
}
}
- // Invert each block.
+ // Add the diagonal and invert each block.
for (int c = 0; c < bs->cols.size(); ++c) {
const int size = block_structure_.cols[c].size;
- MatrixRef D(blocks_[c], size, size);
- D = D.selfadjointView<Eigen::Upper>()
- .ldlt()
- .solve(Matrix::Identity(size, size));
+ const int position = block_structure_.cols[c].position;
+ MatrixRef DD(blocks_[c], size, size);
+
+ DD.diagonal() += ConstVectorRef(D + position, size).array().square().matrix();
+
+ DD = DD.selfadjointView<Eigen::Upper>()
+ .ldlt()
+ .solve(Matrix::Identity(size, size));
}
}
-void BlockDiagonalPreconditioner::RightMultiply(const double* x, double* y) const {
+void BlockJacobiPreconditioner::RightMultiply(const double* x, double* y) const {
for (int c = 0; c < block_structure_.cols.size(); ++c) {
const int size = block_structure_.cols[c].size;
const int position = block_structure_.cols[c].position;
@@ -110,7 +126,7 @@
}
}
-void BlockDiagonalPreconditioner::LeftMultiply(const double* x, double* y) const {
+void BlockJacobiPreconditioner::LeftMultiply(const double* x, double* y) const {
RightMultiply(x, y);
}
diff --git a/internal/ceres/block_jacobi_preconditioner.h b/internal/ceres/block_jacobi_preconditioner.h
new file mode 100644
index 0000000..91cfedd
--- /dev/null
+++ b/internal/ceres/block_jacobi_preconditioner.h
@@ -0,0 +1,84 @@
+// Ceres Solver - A fast non-linear least squares minimizer
+// Copyright 2012 Google Inc. All rights reserved.
+// http://code.google.com/p/ceres-solver/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+// Author: keir@google.com (Keir Mierle)
+
+#ifndef CERES_INTERNAL_BLOCK_JACOBI_PRECONDITIONER_H_
+#define CERES_INTERNAL_BLOCK_JACOBI_PRECONDITIONER_H_
+
+#include <vector>
+#include "ceres/linear_operator.h"
+
+namespace ceres {
+namespace internal {
+
+class CompressedRowBlockStructure;
+class LinearOperator;
+class SparseMatrix;
+
+// A block Jacobi preconditioner. This is intended for use with conjugate
+// gradients, or other iterative symmetric solvers. To use the preconditioner,
+// create one by passing a BlockSparseMatrix as the linear operator "A" to the
+// constructor. This fixes the sparsity pattern to the pattern of the matrix
+// A^TA.
+//
+// Before each use of the preconditioner in a solve with conjugate gradients,
+// update the matrix by running Update(A, D). The values of the matrix A are
+// inspected to construct the preconditioner. The vector D is applied as the
+// D^TD diagonal term.
+class BlockJacobiPreconditioner : public LinearOperator {
+ public:
+ // A must remain valid while the BlockJacobiPreconditioner is.
+ BlockJacobiPreconditioner(const LinearOperator& A);
+ virtual ~BlockJacobiPreconditioner();
+
+ // Update the preconditioner with the values found in A. The sparsity pattern
+ // must match that of the A passed to the constructor. D is a vector that
+ // must have the same number of rows as A, and is applied as a diagonal in
+ // addition to the block diagonals of A.
+ void Update(const LinearOperator& A, const double* D);
+
+ // LinearOperator interface.
+ virtual void RightMultiply(const double* x, double* y) const;
+ virtual void LeftMultiply(const double* x, double* y) const;
+ virtual int num_rows() const { return num_rows_; }
+ virtual int num_cols() const { return num_rows_; }
+
+ private:
+ std::vector<double*> blocks_;
+ std::vector<double> block_storage_;
+ int num_rows_;
+
+ // The block structure of the matrix this preconditioner is for (e.g. J).
+ const CompressedRowBlockStructure& block_structure_;
+};
+
+} // namespace internal
+} // namespace ceres
+
+#endif // CERES_INTERNAL_BLOCK_JACOBI_PRECONDITIONER_H_
diff --git a/internal/ceres/cgnr_linear_operator.h b/internal/ceres/cgnr_linear_operator.h
index 94767fb..f32d8d9 100644
--- a/internal/ceres/cgnr_linear_operator.h
+++ b/internal/ceres/cgnr_linear_operator.h
@@ -61,12 +61,11 @@
//
// Then multiply out to get:
//
-// = xA^TAx - 2xA^T b + b^Tb + xD^TDx
// = xA^TAx - 2b^T Ax + b^Tb + xD^TDx
//
// Take the derivative:
//
-// 0 = 2A^TAx - 2b^T A + b^Tb + 2 D^TDx
+// 0 = 2A^TAx - 2A^T b + 2 D^TDx
// 0 = A^TAx - A^T b + D^TDx
// 0 = (A^TA + D^TD)x - A^T b
//
@@ -80,26 +79,25 @@
// Note: This class is not thread safe, since it uses some temporary storage.
class CgnrLinearOperator : public LinearOperator {
public:
- CgnrLinearOperator(LinearOperator* A, double *D)
- : A_(A), D_(D), z_(new double[A->num_rows()]) {
+ CgnrLinearOperator(const LinearOperator& A, const double *D)
+ : A_(A), D_(D), z_(new double[A.num_rows()]) {
}
virtual ~CgnrLinearOperator() {}
virtual void RightMultiply(const double* x, double* y) const {
- std::fill(z_.get(), z_.get() + A_->num_rows(), 0.0);
+ std::fill(z_.get(), z_.get() + A_.num_rows(), 0.0);
// z = Ax
- A_->RightMultiply(x, z_.get());
+ A_.RightMultiply(x, z_.get());
// y = y + Atz
- A_->LeftMultiply(z_.get(), y);
+ A_.LeftMultiply(z_.get(), y);
// y = y + DtDx
if (D_ != NULL) {
- int n = A_->num_cols();
- for (int i = 0; i < n; ++i) {
- y[i] += D_[i] * D_[i] * x[i];
- }
+ int n = A_.num_cols();
+ VectorRef(y, n).array() += ConstVectorRef(D_, n).array().square() *
+ ConstVectorRef(x, n).array();
}
}
@@ -107,12 +105,12 @@
RightMultiply(x, y);
}
- virtual int num_rows() const { return A_->num_cols(); }
- virtual int num_cols() const { return A_->num_cols(); }
+ virtual int num_rows() const { return A_.num_cols(); }
+ virtual int num_cols() const { return A_.num_cols(); }
private:
- LinearOperator* A_;
- double* D_;
+ const LinearOperator& A_;
+ const double* D_;
scoped_array<double> z_;
};
diff --git a/internal/ceres/cgnr_solver.cc b/internal/ceres/cgnr_solver.cc
index 2c88e33..ccc8026 100644
--- a/internal/ceres/cgnr_solver.cc
+++ b/internal/ceres/cgnr_solver.cc
@@ -30,10 +30,11 @@
#include "ceres/cgnr_solver.h"
+#include "glog/logging.h"
#include "ceres/linear_solver.h"
#include "ceres/cgnr_linear_operator.h"
#include "ceres/conjugate_gradients_solver.h"
-#include "ceres/block_diagonal_preconditioner.h"
+#include "ceres/block_jacobi_preconditioner.h"
namespace ceres {
namespace internal {
@@ -57,19 +58,19 @@
LinearSolver::PerSolveOptions cg_per_solve_options = per_solve_options;
if (options_.preconditioner_type == JACOBI) {
if (jacobi_preconditioner_.get() == NULL) {
- jacobi_preconditioner_.reset(new BlockDiagonalPreconditioner(*A));
+ jacobi_preconditioner_.reset(new BlockJacobiPreconditioner(*A));
}
- jacobi_preconditioner_->Update(*A);
+ jacobi_preconditioner_->Update(*A, per_solve_options.D);
cg_per_solve_options.preconditioner = jacobi_preconditioner_.get();
} else if (options_.preconditioner_type != IDENTITY) {
- // TODO(keir): This should die somehow.
+ LOG(FATAL) << "CGNR only supports IDENTITY and JACOBI preconditioners.";
}
// Solve (AtA + DtD)x = z (= Atb).
std::fill(x, x + A->num_cols(), 0.0);
- CgnrLinearOperator AtApDtD(A, per_solve_options.D);
+ CgnrLinearOperator lhs(*A, per_solve_options.D);
ConjugateGradientsSolver conjugate_gradient_solver(options_);
- return conjugate_gradient_solver.Solve(&AtApDtD,
+ return conjugate_gradient_solver.Solve(&lhs,
z.get(),
cg_per_solve_options,
x);
diff --git a/internal/ceres/cgnr_solver.h b/internal/ceres/cgnr_solver.h
index db49b07..dd36f99 100644
--- a/internal/ceres/cgnr_solver.h
+++ b/internal/ceres/cgnr_solver.h
@@ -37,7 +37,7 @@
namespace ceres {
namespace internal {
-class BlockDiagonalPreconditioner;
+class BlockJacobiPreconditioner;
// A conjugate gradients on the normal equations solver. This directly solves
// for the solution to
@@ -56,7 +56,7 @@
private:
const LinearSolver::Options options_;
- scoped_ptr<BlockDiagonalPreconditioner> jacobi_preconditioner_;
+ scoped_ptr<BlockJacobiPreconditioner> jacobi_preconditioner_;
DISALLOW_COPY_AND_ASSIGN(CgnrSolver);
};
diff --git a/internal/ceres/evaluator.cc b/internal/ceres/evaluator.cc
index 7ceb7ac..ea05aef 100644
--- a/internal/ceres/evaluator.cc
+++ b/internal/ceres/evaluator.cc
@@ -53,7 +53,7 @@
case DENSE_SCHUR:
case SPARSE_SCHUR:
case ITERATIVE_SCHUR:
- case CONJUGATE_GRADIENTS:
+ case CGNR:
return new ProgramEvaluator<BlockEvaluatePreparer,
BlockJacobianWriter>(options,
program);
diff --git a/internal/ceres/iterative_schur_complement_solver.cc b/internal/ceres/iterative_schur_complement_solver.cc
index 4379ebd..42d7e86 100644
--- a/internal/ceres/iterative_schur_complement_solver.cc
+++ b/internal/ceres/iterative_schur_complement_solver.cc
@@ -84,7 +84,6 @@
// Instantiate a conjugate gradient solver that runs on the Schur complement
// matrix with the block diagonal of the matrix F'F as the preconditioner.
LinearSolver::Options cg_options;
- cg_options.type = CONJUGATE_GRADIENTS;
cg_options.max_num_iterations = options_.max_num_iterations;
ConjugateGradientsSolver cg_solver(cg_options);
LinearSolver::PerSolveOptions cg_per_solve_options;
diff --git a/internal/ceres/linear_solver.cc b/internal/ceres/linear_solver.cc
index fd40252..7221a74 100644
--- a/internal/ceres/linear_solver.cc
+++ b/internal/ceres/linear_solver.cc
@@ -46,7 +46,7 @@
LinearSolver* LinearSolver::Create(const LinearSolver::Options& options) {
switch (options.type) {
- case CONJUGATE_GRADIENTS:
+ case CGNR:
return new CgnrSolver(options);
case SPARSE_NORMAL_CHOLESKY:
diff --git a/internal/ceres/solver.cc b/internal/ceres/solver.cc
index c45c55f..77f04d1 100644
--- a/internal/ceres/solver.cc
+++ b/internal/ceres/solver.cc
@@ -155,7 +155,7 @@
LinearSolverTypeToString(linear_solver_type_given),
LinearSolverTypeToString(linear_solver_type_used));
- if (linear_solver_type_given == CONJUGATE_GRADIENTS ||
+ if (linear_solver_type_given == CGNR ||
linear_solver_type_given == ITERATIVE_SCHUR) {
internal::StringAppendF(&report, "Preconditioner %25s%25s\n",
PreconditionerTypeToString(preconditioner_type),
diff --git a/internal/ceres/system_test.cc b/internal/ceres/system_test.cc
index 01573ac..a9a6747 100644
--- a/internal/ceres/system_test.cc
+++ b/internal/ceres/system_test.cc
@@ -476,6 +476,8 @@
CONFIGURE(DENSE_SCHUR, USER, IDENTITY, 1);
CONFIGURE(DENSE_SCHUR, SCHUR, IDENTITY, 1);
+ CONFIGURE(CGNR, USER, JACOBI, 1);
+
CONFIGURE(ITERATIVE_SCHUR, USER, JACOBI, 1);
#ifndef CERES_NO_SUITESPARSE
diff --git a/internal/ceres/types.cc b/internal/ceres/types.cc
index 55910b0..860f8a4 100644
--- a/internal/ceres/types.cc
+++ b/internal/ceres/types.cc
@@ -42,7 +42,7 @@
CASESTR(DENSE_SCHUR);
CASESTR(SPARSE_SCHUR);
CASESTR(ITERATIVE_SCHUR);
- CASESTR(CONJUGATE_GRADIENTS);
+ CASESTR(CGNR);
default:
return "UNKNOWN";
}