Add the ability to specify the pivot threshold in Covariance::Options
https: //github.com/ceres-solver/ceres-solver/issues/777
Change-Id: I481612b7bc727d5cd0dc21a0e0dbaf356722ba22
diff --git a/docs/source/nnls_covariance.rst b/docs/source/nnls_covariance.rst
index 66afd44..c85bedd 100644
--- a/docs/source/nnls_covariance.rst
+++ b/docs/source/nnls_covariance.rst
@@ -187,6 +187,23 @@
well as rank deficient Jacobians.
+.. member:: double Covariance::Options::column_pivot_threshold
+
+ Default: :math:`-1`
+
+ During QR factorization, if a column with Euclidean norm less than
+ ``column_pivot_threshold`` is encountered it is treated as zero.
+
+ If ``column_pivot_threshold < 0``, then an automatic default value
+ of `20*(m+n)*eps*sqrt(max(diag(J’*J)))` is used. Here `m` and `n`
+ are the number of rows and columns of the Jacobian (`J`)
+ respectively.
+
+ This is an advanced option meant for users who know enough about
+ their Jacobian matrices that they can determine a value better
+ than the default.
+
+
.. member:: int Covariance::Options::min_reciprocal_condition_number
Default: :math:`10^{-14}`
diff --git a/include/ceres/covariance.h b/include/ceres/covariance.h
index 60bcc80..00d00bd 100644
--- a/include/ceres/covariance.h
+++ b/include/ceres/covariance.h
@@ -246,6 +246,20 @@
// used.
CovarianceAlgorithmType algorithm_type = SPARSE_QR;
+ // During QR factorization, if a column with Euclidean norm less
+ // than column_pivot_threshold is encountered it is treated as
+ // zero.
+ //
+ // If column_pivot_threshold < 0, then an automatic default value
+ // of 20*(m+n)*eps*sqrt(max(diag(J’*J))) is used. Here m and n are
+ // the number of rows and columns of the Jacobian (J)
+ // respectively.
+ //
+ // This is an advanced option meant for users who know enough
+ // about their Jacobian matrices that they can determine a value
+ // better than the default.
+ double column_pivot_threshold = -1;
+
// If the Jacobian matrix is near singular, then inverting J'J
// will result in unreliable results, e.g, if
//
diff --git a/internal/ceres/covariance_impl.cc b/internal/ceres/covariance_impl.cc
index 324b553..ef0aac0 100644
--- a/internal/ceres/covariance_impl.cc
+++ b/internal/ceres/covariance_impl.cc
@@ -628,13 +628,15 @@
// more efficient, both in runtime as well as the quality of
// ordering computed. So, it maybe worth doing that analysis
// separately.
- const SuiteSparse_long rank = SuiteSparseQR<double>(SPQR_ORDERING_BESTAMD,
- SPQR_DEFAULT_TOL,
- cholmod_jacobian.ncol,
- &cholmod_jacobian,
- &R,
- &permutation,
- &cc);
+ const SuiteSparse_long rank = SuiteSparseQR<double>(
+ SPQR_ORDERING_BESTAMD,
+ options_.column_pivot_threshold < 0 ? SPQR_DEFAULT_TOL
+ : options_.column_pivot_threshold,
+ cholmod_jacobian.ncol,
+ &cholmod_jacobian,
+ &R,
+ &permutation,
+ &cc);
event_logger.AddEvent("Numeric Factorization");
if (R == nullptr) {
LOG(ERROR) << "Something is wrong. SuiteSparseQR returned R = nullptr.";
@@ -830,19 +832,23 @@
jacobian.values.data());
event_logger.AddEvent("ConvertToSparseMatrix");
- Eigen::SparseQR<EigenSparseMatrix, Eigen::COLAMDOrdering<int>> qr_solver(
- sparse_jacobian);
+ Eigen::SparseQR<EigenSparseMatrix, Eigen::COLAMDOrdering<int>> qr;
+ if (options_.column_pivot_threshold > 0) {
+ qr.setPivotThreshold(options_.column_pivot_threshold);
+ }
+
+ qr.compute(sparse_jacobian);
event_logger.AddEvent("QRDecomposition");
- if (qr_solver.info() != Eigen::Success) {
+ if (qr.info() != Eigen::Success) {
LOG(ERROR) << "Eigen::SparseQR decomposition failed.";
return false;
}
- if (qr_solver.rank() < jacobian.num_cols) {
+ if (qr.rank() < jacobian.num_cols) {
LOG(ERROR) << "Jacobian matrix is rank deficient. "
<< "Number of columns: " << jacobian.num_cols
- << " rank: " << qr_solver.rank();
+ << " rank: " << qr.rank();
return false;
}
@@ -852,7 +858,7 @@
// Compute the inverse column permutation used by QR factorization.
Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> inverse_permutation =
- qr_solver.colsPermutation().inverse();
+ qr.colsPermutation().inverse();
// The following loop exploits the fact that the i^th column of A^{-1}
// is given by the solution to the linear system
@@ -875,9 +881,9 @@
if (row_end != row_begin) {
double* solution = workspace.get() + thread_id * num_cols;
SolveRTRWithSparseRHS<int>(num_cols,
- qr_solver.matrixR().innerIndexPtr(),
- qr_solver.matrixR().outerIndexPtr(),
- &qr_solver.matrixR().data().value(0),
+ qr.matrixR().innerIndexPtr(),
+ qr.matrixR().outerIndexPtr(),
+ &qr.matrixR().data().value(0),
inverse_permutation.indices().coeff(r),
solution);