• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Ceres Solver - A fast non-linear least squares minimizer
2 // Copyright 2013 Google Inc. All rights reserved.
3 // http://code.google.com/p/ceres-solver/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are met:
7 //
8 // * Redistributions of source code must retain the above copyright notice,
9 //   this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright notice,
11 //   this list of conditions and the following disclaimer in the documentation
12 //   and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors may be
14 //   used to endorse or promote products derived from this software without
15 //   specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 // POSSIBILITY OF SUCH DAMAGE.
28 //
29 // Author: sameeragarwal@google.com (Sameer Agarwal)
30 
31 #include "ceres/covariance_impl.h"
32 
33 #ifdef CERES_USE_OPENMP
34 #include <omp.h>
35 #endif
36 
37 #include <algorithm>
38 #include <cstdlib>
39 #include <utility>
40 #include <vector>
41 #include "Eigen/SparseCore"
42 
43 // Suppress unused local variable warning from Eigen Ordering.h #included by
44 // SparseQR in Eigen 3.2.0. This was fixed in Eigen 3.2.1, but 3.2.0 is still
45 // widely used (Ubuntu 14.04), and Ceres won't compile otherwise due to -Werror.
46 #if defined(_MSC_VER)
47 #pragma warning( push )
48 #pragma warning( disable : 4189 )
49 #else
50 #pragma GCC diagnostic push
51 #pragma GCC diagnostic ignored "-Wunused-but-set-variable"
52 #endif
53 #include "Eigen/SparseQR"
54 #if defined(_MSC_VER)
55 #pragma warning( pop )
56 #else
57 #pragma GCC diagnostic pop
58 #endif
59 
60 #include "Eigen/SVD"
61 #include "ceres/compressed_col_sparse_matrix_utils.h"
62 #include "ceres/compressed_row_sparse_matrix.h"
63 #include "ceres/covariance.h"
64 #include "ceres/crs_matrix.h"
65 #include "ceres/internal/eigen.h"
66 #include "ceres/map_util.h"
67 #include "ceres/parameter_block.h"
68 #include "ceres/problem_impl.h"
69 #include "ceres/suitesparse.h"
70 #include "ceres/wall_time.h"
71 #include "glog/logging.h"
72 
73 namespace ceres {
74 namespace internal {
75 
76 typedef vector<pair<const double*, const double*> > CovarianceBlocks;
77 
CovarianceImpl(const Covariance::Options & options)78 CovarianceImpl::CovarianceImpl(const Covariance::Options& options)
79     : options_(options),
80       is_computed_(false),
81       is_valid_(false) {
82   evaluate_options_.num_threads = options.num_threads;
83   evaluate_options_.apply_loss_function = options.apply_loss_function;
84 }
85 
~CovarianceImpl()86 CovarianceImpl::~CovarianceImpl() {
87 }
88 
Compute(const CovarianceBlocks & covariance_blocks,ProblemImpl * problem)89 bool CovarianceImpl::Compute(const CovarianceBlocks& covariance_blocks,
90                              ProblemImpl* problem) {
91   problem_ = problem;
92   parameter_block_to_row_index_.clear();
93   covariance_matrix_.reset(NULL);
94   is_valid_ = (ComputeCovarianceSparsity(covariance_blocks, problem) &&
95                ComputeCovarianceValues());
96   is_computed_ = true;
97   return is_valid_;
98 }
99 
GetCovarianceBlock(const double * original_parameter_block1,const double * original_parameter_block2,double * covariance_block) const100 bool CovarianceImpl::GetCovarianceBlock(const double* original_parameter_block1,
101                                         const double* original_parameter_block2,
102                                         double* covariance_block) const {
103   CHECK(is_computed_)
104       << "Covariance::GetCovarianceBlock called before Covariance::Compute";
105   CHECK(is_valid_)
106       << "Covariance::GetCovarianceBlock called when Covariance::Compute "
107       << "returned false.";
108 
109   // If either of the two parameter blocks is constant, then the
110   // covariance block is also zero.
111   if (constant_parameter_blocks_.count(original_parameter_block1) > 0 ||
112       constant_parameter_blocks_.count(original_parameter_block2) > 0) {
113     const ProblemImpl::ParameterMap& parameter_map = problem_->parameter_map();
114     ParameterBlock* block1 =
115         FindOrDie(parameter_map,
116                   const_cast<double*>(original_parameter_block1));
117 
118     ParameterBlock* block2 =
119         FindOrDie(parameter_map,
120                   const_cast<double*>(original_parameter_block2));
121     const int block1_size = block1->Size();
122     const int block2_size = block2->Size();
123     MatrixRef(covariance_block, block1_size, block2_size).setZero();
124     return true;
125   }
126 
127   const double* parameter_block1 = original_parameter_block1;
128   const double* parameter_block2 = original_parameter_block2;
129   const bool transpose = parameter_block1 > parameter_block2;
130   if (transpose) {
131     std::swap(parameter_block1, parameter_block2);
132   }
133 
134   // Find where in the covariance matrix the block is located.
135   const int row_begin =
136       FindOrDie(parameter_block_to_row_index_, parameter_block1);
137   const int col_begin =
138       FindOrDie(parameter_block_to_row_index_, parameter_block2);
139   const int* rows = covariance_matrix_->rows();
140   const int* cols = covariance_matrix_->cols();
141   const int row_size = rows[row_begin + 1] - rows[row_begin];
142   const int* cols_begin = cols + rows[row_begin];
143 
144   // The only part that requires work is walking the compressed column
145   // vector to determine where the set of columns correspnding to the
146   // covariance block begin.
147   int offset = 0;
148   while (cols_begin[offset] != col_begin && offset < row_size) {
149     ++offset;
150   }
151 
152   if (offset == row_size) {
153     LOG(ERROR) << "Unable to find covariance block for "
154                << original_parameter_block1 << " "
155                << original_parameter_block2;
156     return false;
157   }
158 
159   const ProblemImpl::ParameterMap& parameter_map = problem_->parameter_map();
160   ParameterBlock* block1 =
161       FindOrDie(parameter_map, const_cast<double*>(parameter_block1));
162   ParameterBlock* block2 =
163       FindOrDie(parameter_map, const_cast<double*>(parameter_block2));
164   const LocalParameterization* local_param1 = block1->local_parameterization();
165   const LocalParameterization* local_param2 = block2->local_parameterization();
166   const int block1_size = block1->Size();
167   const int block1_local_size = block1->LocalSize();
168   const int block2_size = block2->Size();
169   const int block2_local_size = block2->LocalSize();
170 
171   ConstMatrixRef cov(covariance_matrix_->values() + rows[row_begin],
172                      block1_size,
173                      row_size);
174 
175   // Fast path when there are no local parameterizations.
176   if (local_param1 == NULL && local_param2 == NULL) {
177     if (transpose) {
178       MatrixRef(covariance_block, block2_size, block1_size) =
179           cov.block(0, offset, block1_size, block2_size).transpose();
180     } else {
181       MatrixRef(covariance_block, block1_size, block2_size) =
182           cov.block(0, offset, block1_size, block2_size);
183     }
184     return true;
185   }
186 
187   // If local parameterizations are used then the covariance that has
188   // been computed is in the tangent space and it needs to be lifted
189   // back to the ambient space.
190   //
191   // This is given by the formula
192   //
193   //  C'_12 = J_1 C_12 J_2'
194   //
195   // Where C_12 is the local tangent space covariance for parameter
196   // blocks 1 and 2. J_1 and J_2 are respectively the local to global
197   // jacobians for parameter blocks 1 and 2.
198   //
199   // See Result 5.11 on page 142 of Hartley & Zisserman (2nd Edition)
200   // for a proof.
201   //
202   // TODO(sameeragarwal): Add caching of local parameterization, so
203   // that they are computed just once per parameter block.
204   Matrix block1_jacobian(block1_size, block1_local_size);
205   if (local_param1 == NULL) {
206     block1_jacobian.setIdentity();
207   } else {
208     local_param1->ComputeJacobian(parameter_block1, block1_jacobian.data());
209   }
210 
211   Matrix block2_jacobian(block2_size, block2_local_size);
212   // Fast path if the user is requesting a diagonal block.
213   if (parameter_block1 == parameter_block2) {
214     block2_jacobian = block1_jacobian;
215   } else {
216     if (local_param2 == NULL) {
217       block2_jacobian.setIdentity();
218     } else {
219       local_param2->ComputeJacobian(parameter_block2, block2_jacobian.data());
220     }
221   }
222 
223   if (transpose) {
224     MatrixRef(covariance_block, block2_size, block1_size) =
225         block2_jacobian *
226         cov.block(0, offset, block1_local_size, block2_local_size).transpose() *
227         block1_jacobian.transpose();
228   } else {
229     MatrixRef(covariance_block, block1_size, block2_size) =
230         block1_jacobian *
231         cov.block(0, offset, block1_local_size, block2_local_size) *
232         block2_jacobian.transpose();
233   }
234 
235   return true;
236 }
237 
238 // Determine the sparsity pattern of the covariance matrix based on
239 // the block pairs requested by the user.
ComputeCovarianceSparsity(const CovarianceBlocks & original_covariance_blocks,ProblemImpl * problem)240 bool CovarianceImpl::ComputeCovarianceSparsity(
241     const CovarianceBlocks&  original_covariance_blocks,
242     ProblemImpl* problem) {
243   EventLogger event_logger("CovarianceImpl::ComputeCovarianceSparsity");
244 
245   // Determine an ordering for the parameter block, by sorting the
246   // parameter blocks by their pointers.
247   vector<double*> all_parameter_blocks;
248   problem->GetParameterBlocks(&all_parameter_blocks);
249   const ProblemImpl::ParameterMap& parameter_map = problem->parameter_map();
250   constant_parameter_blocks_.clear();
251   vector<double*>& active_parameter_blocks = evaluate_options_.parameter_blocks;
252   active_parameter_blocks.clear();
253   for (int i = 0; i < all_parameter_blocks.size(); ++i) {
254     double* parameter_block = all_parameter_blocks[i];
255 
256     ParameterBlock* block = FindOrDie(parameter_map, parameter_block);
257     if (block->IsConstant()) {
258       constant_parameter_blocks_.insert(parameter_block);
259     } else {
260       active_parameter_blocks.push_back(parameter_block);
261     }
262   }
263 
264   sort(active_parameter_blocks.begin(), active_parameter_blocks.end());
265 
266   // Compute the number of rows.  Map each parameter block to the
267   // first row corresponding to it in the covariance matrix using the
268   // ordering of parameter blocks just constructed.
269   int num_rows = 0;
270   parameter_block_to_row_index_.clear();
271   for (int i = 0; i < active_parameter_blocks.size(); ++i) {
272     double* parameter_block = active_parameter_blocks[i];
273     const int parameter_block_size =
274         problem->ParameterBlockLocalSize(parameter_block);
275     parameter_block_to_row_index_[parameter_block] = num_rows;
276     num_rows += parameter_block_size;
277   }
278 
279   // Compute the number of non-zeros in the covariance matrix.  Along
280   // the way flip any covariance blocks which are in the lower
281   // triangular part of the matrix.
282   int num_nonzeros = 0;
283   CovarianceBlocks covariance_blocks;
284   for (int i = 0; i <  original_covariance_blocks.size(); ++i) {
285     const pair<const double*, const double*>& block_pair =
286         original_covariance_blocks[i];
287     if (constant_parameter_blocks_.count(block_pair.first) > 0 ||
288         constant_parameter_blocks_.count(block_pair.second) > 0) {
289       continue;
290     }
291 
292     int index1 = FindOrDie(parameter_block_to_row_index_, block_pair.first);
293     int index2 = FindOrDie(parameter_block_to_row_index_, block_pair.second);
294     const int size1 = problem->ParameterBlockLocalSize(block_pair.first);
295     const int size2 = problem->ParameterBlockLocalSize(block_pair.second);
296     num_nonzeros += size1 * size2;
297 
298     // Make sure we are constructing a block upper triangular matrix.
299     if (index1 > index2) {
300       covariance_blocks.push_back(make_pair(block_pair.second,
301                                             block_pair.first));
302     } else {
303       covariance_blocks.push_back(block_pair);
304     }
305   }
306 
307   if (covariance_blocks.size() == 0) {
308     VLOG(2) << "No non-zero covariance blocks found";
309     covariance_matrix_.reset(NULL);
310     return true;
311   }
312 
313   // Sort the block pairs. As a consequence we get the covariance
314   // blocks as they will occur in the CompressedRowSparseMatrix that
315   // will store the covariance.
316   sort(covariance_blocks.begin(), covariance_blocks.end());
317 
318   // Fill the sparsity pattern of the covariance matrix.
319   covariance_matrix_.reset(
320       new CompressedRowSparseMatrix(num_rows, num_rows, num_nonzeros));
321 
322   int* rows = covariance_matrix_->mutable_rows();
323   int* cols = covariance_matrix_->mutable_cols();
324 
325   // Iterate over parameter blocks and in turn over the rows of the
326   // covariance matrix. For each parameter block, look in the upper
327   // triangular part of the covariance matrix to see if there are any
328   // blocks requested by the user. If this is the case then fill out a
329   // set of compressed rows corresponding to this parameter block.
330   //
331   // The key thing that makes this loop work is the fact that the
332   // row/columns of the covariance matrix are ordered by the pointer
333   // values of the parameter blocks. Thus iterating over the keys of
334   // parameter_block_to_row_index_ corresponds to iterating over the
335   // rows of the covariance matrix in order.
336   int i = 0;  // index into covariance_blocks.
337   int cursor = 0;  // index into the covariance matrix.
338   for (map<const double*, int>::const_iterator it =
339            parameter_block_to_row_index_.begin();
340        it != parameter_block_to_row_index_.end();
341        ++it) {
342     const double* row_block =  it->first;
343     const int row_block_size = problem->ParameterBlockLocalSize(row_block);
344     int row_begin = it->second;
345 
346     // Iterate over the covariance blocks contained in this row block
347     // and count the number of columns in this row block.
348     int num_col_blocks = 0;
349     int num_columns = 0;
350     for (int j = i; j < covariance_blocks.size(); ++j, ++num_col_blocks) {
351       const pair<const double*, const double*>& block_pair =
352           covariance_blocks[j];
353       if (block_pair.first != row_block) {
354         break;
355       }
356       num_columns += problem->ParameterBlockLocalSize(block_pair.second);
357     }
358 
359     // Fill out all the compressed rows for this parameter block.
360     for (int r = 0; r < row_block_size; ++r) {
361       rows[row_begin + r] = cursor;
362       for (int c = 0; c < num_col_blocks; ++c) {
363         const double* col_block = covariance_blocks[i + c].second;
364         const int col_block_size = problem->ParameterBlockLocalSize(col_block);
365         int col_begin = FindOrDie(parameter_block_to_row_index_, col_block);
366         for (int k = 0; k < col_block_size; ++k) {
367           cols[cursor++] = col_begin++;
368         }
369       }
370     }
371 
372     i+= num_col_blocks;
373   }
374 
375   rows[num_rows] = cursor;
376   return true;
377 }
378 
ComputeCovarianceValues()379 bool CovarianceImpl::ComputeCovarianceValues() {
380   switch (options_.algorithm_type) {
381     case DENSE_SVD:
382       return ComputeCovarianceValuesUsingDenseSVD();
383 #ifndef CERES_NO_SUITESPARSE
384     case SUITE_SPARSE_QR:
385       return ComputeCovarianceValuesUsingSuiteSparseQR();
386 #else
387       LOG(ERROR) << "SuiteSparse is required to use the "
388                  << "SUITE_SPARSE_QR algorithm.";
389       return false;
390 #endif
391     case EIGEN_SPARSE_QR:
392       return ComputeCovarianceValuesUsingEigenSparseQR();
393     default:
394       LOG(ERROR) << "Unsupported covariance estimation algorithm type: "
395                  << CovarianceAlgorithmTypeToString(options_.algorithm_type);
396       return false;
397   }
398   return false;
399 }
400 
ComputeCovarianceValuesUsingSuiteSparseQR()401 bool CovarianceImpl::ComputeCovarianceValuesUsingSuiteSparseQR() {
402   EventLogger event_logger(
403       "CovarianceImpl::ComputeCovarianceValuesUsingSparseQR");
404 
405 #ifndef CERES_NO_SUITESPARSE
406   if (covariance_matrix_.get() == NULL) {
407     // Nothing to do, all zeros covariance matrix.
408     return true;
409   }
410 
411   CRSMatrix jacobian;
412   problem_->Evaluate(evaluate_options_, NULL, NULL, NULL, &jacobian);
413   event_logger.AddEvent("Evaluate");
414 
415   // Construct a compressed column form of the Jacobian.
416   const int num_rows = jacobian.num_rows;
417   const int num_cols = jacobian.num_cols;
418   const int num_nonzeros = jacobian.values.size();
419 
420   vector<SuiteSparse_long> transpose_rows(num_cols + 1, 0);
421   vector<SuiteSparse_long> transpose_cols(num_nonzeros, 0);
422   vector<double> transpose_values(num_nonzeros, 0);
423 
424   for (int idx = 0; idx < num_nonzeros; ++idx) {
425     transpose_rows[jacobian.cols[idx] + 1] += 1;
426   }
427 
428   for (int i = 1; i < transpose_rows.size(); ++i) {
429     transpose_rows[i] += transpose_rows[i - 1];
430   }
431 
432   for (int r = 0; r < num_rows; ++r) {
433     for (int idx = jacobian.rows[r]; idx < jacobian.rows[r + 1]; ++idx) {
434       const int c = jacobian.cols[idx];
435       const int transpose_idx = transpose_rows[c];
436       transpose_cols[transpose_idx] = r;
437       transpose_values[transpose_idx] = jacobian.values[idx];
438       ++transpose_rows[c];
439     }
440   }
441 
442   for (int i = transpose_rows.size() - 1; i > 0 ; --i) {
443     transpose_rows[i] = transpose_rows[i - 1];
444   }
445   transpose_rows[0] = 0;
446 
447   cholmod_sparse cholmod_jacobian;
448   cholmod_jacobian.nrow = num_rows;
449   cholmod_jacobian.ncol = num_cols;
450   cholmod_jacobian.nzmax = num_nonzeros;
451   cholmod_jacobian.nz = NULL;
452   cholmod_jacobian.p = reinterpret_cast<void*>(&transpose_rows[0]);
453   cholmod_jacobian.i = reinterpret_cast<void*>(&transpose_cols[0]);
454   cholmod_jacobian.x = reinterpret_cast<void*>(&transpose_values[0]);
455   cholmod_jacobian.z = NULL;
456   cholmod_jacobian.stype = 0;  // Matrix is not symmetric.
457   cholmod_jacobian.itype = CHOLMOD_LONG;
458   cholmod_jacobian.xtype = CHOLMOD_REAL;
459   cholmod_jacobian.dtype = CHOLMOD_DOUBLE;
460   cholmod_jacobian.sorted = 1;
461   cholmod_jacobian.packed = 1;
462 
463   cholmod_common cc;
464   cholmod_l_start(&cc);
465 
466   cholmod_sparse* R = NULL;
467   SuiteSparse_long* permutation = NULL;
468 
469   // Compute a Q-less QR factorization of the Jacobian. Since we are
470   // only interested in inverting J'J = R'R, we do not need Q. This
471   // saves memory and gives us R as a permuted compressed column
472   // sparse matrix.
473   //
474   // TODO(sameeragarwal): Currently the symbolic factorization and the
475   // numeric factorization is done at the same time, and this does not
476   // explicitly account for the block column and row structure in the
477   // matrix. When using AMD, we have observed in the past that
478   // computing the ordering with the block matrix is significantly
479   // more efficient, both in runtime as well as the quality of
480   // ordering computed. So, it maybe worth doing that analysis
481   // separately.
482   const SuiteSparse_long rank =
483       SuiteSparseQR<double>(SPQR_ORDERING_BESTAMD,
484                             SPQR_DEFAULT_TOL,
485                             cholmod_jacobian.ncol,
486                             &cholmod_jacobian,
487                             &R,
488                             &permutation,
489                             &cc);
490   event_logger.AddEvent("Numeric Factorization");
491   CHECK_NOTNULL(permutation);
492   CHECK_NOTNULL(R);
493 
494   if (rank < cholmod_jacobian.ncol) {
495     LOG(ERROR) << "Jacobian matrix is rank deficient. "
496                << "Number of columns: " << cholmod_jacobian.ncol
497                << " rank: " << rank;
498     free(permutation);
499     cholmod_l_free_sparse(&R, &cc);
500     cholmod_l_finish(&cc);
501     return false;
502   }
503 
504   vector<int> inverse_permutation(num_cols);
505   for (SuiteSparse_long i = 0; i < num_cols; ++i) {
506     inverse_permutation[permutation[i]] = i;
507   }
508 
509   const int* rows = covariance_matrix_->rows();
510   const int* cols = covariance_matrix_->cols();
511   double* values = covariance_matrix_->mutable_values();
512 
513   // The following loop exploits the fact that the i^th column of A^{-1}
514   // is given by the solution to the linear system
515   //
516   //  A x = e_i
517   //
518   // where e_i is a vector with e(i) = 1 and all other entries zero.
519   //
520   // Since the covariance matrix is symmetric, the i^th row and column
521   // are equal.
522   const int num_threads = options_.num_threads;
523   scoped_array<double> workspace(new double[num_threads * num_cols]);
524 
525 #pragma omp parallel for num_threads(num_threads) schedule(dynamic)
526   for (int r = 0; r < num_cols; ++r) {
527     const int row_begin = rows[r];
528     const int row_end = rows[r + 1];
529     if (row_end == row_begin) {
530       continue;
531     }
532 
533 #  ifdef CERES_USE_OPENMP
534     int thread_id = omp_get_thread_num();
535 #  else
536     int thread_id = 0;
537 #  endif
538 
539     double* solution = workspace.get() + thread_id * num_cols;
540     SolveRTRWithSparseRHS<SuiteSparse_long>(
541         num_cols,
542         static_cast<SuiteSparse_long*>(R->i),
543         static_cast<SuiteSparse_long*>(R->p),
544         static_cast<double*>(R->x),
545         inverse_permutation[r],
546         solution);
547     for (int idx = row_begin; idx < row_end; ++idx) {
548      const int c = cols[idx];
549      values[idx] = solution[inverse_permutation[c]];
550     }
551   }
552 
553   free(permutation);
554   cholmod_l_free_sparse(&R, &cc);
555   cholmod_l_finish(&cc);
556   event_logger.AddEvent("Inversion");
557   return true;
558 
559 #else  // CERES_NO_SUITESPARSE
560 
561   return false;
562 
563 #endif  // CERES_NO_SUITESPARSE
564 }
565 
ComputeCovarianceValuesUsingDenseSVD()566 bool CovarianceImpl::ComputeCovarianceValuesUsingDenseSVD() {
567   EventLogger event_logger(
568       "CovarianceImpl::ComputeCovarianceValuesUsingDenseSVD");
569   if (covariance_matrix_.get() == NULL) {
570     // Nothing to do, all zeros covariance matrix.
571     return true;
572   }
573 
574   CRSMatrix jacobian;
575   problem_->Evaluate(evaluate_options_, NULL, NULL, NULL, &jacobian);
576   event_logger.AddEvent("Evaluate");
577 
578   Matrix dense_jacobian(jacobian.num_rows, jacobian.num_cols);
579   dense_jacobian.setZero();
580   for (int r = 0; r < jacobian.num_rows; ++r) {
581     for (int idx = jacobian.rows[r]; idx < jacobian.rows[r + 1]; ++idx) {
582       const int c = jacobian.cols[idx];
583       dense_jacobian(r, c) = jacobian.values[idx];
584     }
585   }
586   event_logger.AddEvent("ConvertToDenseMatrix");
587 
588   Eigen::JacobiSVD<Matrix> svd(dense_jacobian,
589                                Eigen::ComputeThinU | Eigen::ComputeThinV);
590 
591   event_logger.AddEvent("SingularValueDecomposition");
592 
593   const Vector singular_values = svd.singularValues();
594   const int num_singular_values = singular_values.rows();
595   Vector inverse_squared_singular_values(num_singular_values);
596   inverse_squared_singular_values.setZero();
597 
598   const double max_singular_value = singular_values[0];
599   const double min_singular_value_ratio =
600       sqrt(options_.min_reciprocal_condition_number);
601 
602   const bool automatic_truncation = (options_.null_space_rank < 0);
603   const int max_rank = min(num_singular_values,
604                            num_singular_values - options_.null_space_rank);
605 
606   // Compute the squared inverse of the singular values. Truncate the
607   // computation based on min_singular_value_ratio and
608   // null_space_rank. When either of these two quantities are active,
609   // the resulting covariance matrix is a Moore-Penrose inverse
610   // instead of a regular inverse.
611   for (int i = 0; i < max_rank; ++i) {
612     const double singular_value_ratio = singular_values[i] / max_singular_value;
613     if (singular_value_ratio < min_singular_value_ratio) {
614       // Since the singular values are in decreasing order, if
615       // automatic truncation is enabled, then from this point on
616       // all values will fail the ratio test and there is nothing to
617       // do in this loop.
618       if (automatic_truncation) {
619         break;
620       } else {
621         LOG(ERROR) << "Cholesky factorization of J'J is not reliable. "
622                    << "Reciprocal condition number: "
623                    << singular_value_ratio * singular_value_ratio << " "
624                    << "min_reciprocal_condition_number: "
625                    << options_.min_reciprocal_condition_number;
626         return false;
627       }
628     }
629 
630     inverse_squared_singular_values[i] =
631         1.0 / (singular_values[i] * singular_values[i]);
632   }
633 
634   Matrix dense_covariance =
635       svd.matrixV() *
636       inverse_squared_singular_values.asDiagonal() *
637       svd.matrixV().transpose();
638   event_logger.AddEvent("PseudoInverse");
639 
640   const int num_rows = covariance_matrix_->num_rows();
641   const int* rows = covariance_matrix_->rows();
642   const int* cols = covariance_matrix_->cols();
643   double* values = covariance_matrix_->mutable_values();
644 
645   for (int r = 0; r < num_rows; ++r) {
646     for (int idx = rows[r]; idx < rows[r + 1]; ++idx) {
647       const int c = cols[idx];
648       values[idx] = dense_covariance(r, c);
649     }
650   }
651   event_logger.AddEvent("CopyToCovarianceMatrix");
652   return true;
653 }
654 
ComputeCovarianceValuesUsingEigenSparseQR()655 bool CovarianceImpl::ComputeCovarianceValuesUsingEigenSparseQR() {
656   EventLogger event_logger(
657       "CovarianceImpl::ComputeCovarianceValuesUsingEigenSparseQR");
658   if (covariance_matrix_.get() == NULL) {
659     // Nothing to do, all zeros covariance matrix.
660     return true;
661   }
662 
663   CRSMatrix jacobian;
664   problem_->Evaluate(evaluate_options_, NULL, NULL, NULL, &jacobian);
665   event_logger.AddEvent("Evaluate");
666 
667   typedef Eigen::SparseMatrix<double, Eigen::ColMajor> EigenSparseMatrix;
668 
669   // Convert the matrix to column major order as required by SparseQR.
670   EigenSparseMatrix sparse_jacobian =
671       Eigen::MappedSparseMatrix<double, Eigen::RowMajor>(
672           jacobian.num_rows, jacobian.num_cols,
673           static_cast<int>(jacobian.values.size()),
674           jacobian.rows.data(), jacobian.cols.data(), jacobian.values.data());
675   event_logger.AddEvent("ConvertToSparseMatrix");
676 
677   Eigen::SparseQR<EigenSparseMatrix, Eigen::COLAMDOrdering<int> >
678       qr_solver(sparse_jacobian);
679   event_logger.AddEvent("QRDecomposition");
680 
681   if(qr_solver.info() != Eigen::Success) {
682     LOG(ERROR) << "Eigen::SparseQR decomposition failed.";
683     return false;
684   }
685 
686   if (qr_solver.rank() < jacobian.num_cols) {
687     LOG(ERROR) << "Jacobian matrix is rank deficient. "
688                << "Number of columns: " << jacobian.num_cols
689                << " rank: " << qr_solver.rank();
690     return false;
691   }
692 
693   const int* rows = covariance_matrix_->rows();
694   const int* cols = covariance_matrix_->cols();
695   double* values = covariance_matrix_->mutable_values();
696 
697   // Compute the inverse column permutation used by QR factorization.
698   Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> inverse_permutation =
699       qr_solver.colsPermutation().inverse();
700 
701   // The following loop exploits the fact that the i^th column of A^{-1}
702   // is given by the solution to the linear system
703   //
704   //  A x = e_i
705   //
706   // where e_i is a vector with e(i) = 1 and all other entries zero.
707   //
708   // Since the covariance matrix is symmetric, the i^th row and column
709   // are equal.
710   const int num_cols = jacobian.num_cols;
711   const int num_threads = options_.num_threads;
712   scoped_array<double> workspace(new double[num_threads * num_cols]);
713 
714 #pragma omp parallel for num_threads(num_threads) schedule(dynamic)
715   for (int r = 0; r < num_cols; ++r) {
716     const int row_begin = rows[r];
717     const int row_end = rows[r + 1];
718     if (row_end == row_begin) {
719       continue;
720     }
721 
722 #  ifdef CERES_USE_OPENMP
723     int thread_id = omp_get_thread_num();
724 #  else
725     int thread_id = 0;
726 #  endif
727 
728     double* solution = workspace.get() + thread_id * num_cols;
729     SolveRTRWithSparseRHS<int>(
730         num_cols,
731         qr_solver.matrixR().innerIndexPtr(),
732         qr_solver.matrixR().outerIndexPtr(),
733         &qr_solver.matrixR().data().value(0),
734         inverse_permutation.indices().coeff(r),
735         solution);
736 
737     // Assign the values of the computed covariance using the
738     // inverse permutation used in the QR factorization.
739     for (int idx = row_begin; idx < row_end; ++idx) {
740      const int c = cols[idx];
741      values[idx] = solution[inverse_permutation.indices().coeff(c)];
742     }
743   }
744 
745   event_logger.AddEvent("Inverse");
746 
747   return true;
748 }
749 
750 }  // namespace internal
751 }  // namespace ceres
752