|  | @@ -33,138 +33,44 @@
 | 
	
		
			
				|  |  |  #include <algorithm>
 | 
	
		
			
				|  |  |  #include <cstring>
 | 
	
		
			
				|  |  |  #include <ctime>
 | 
	
		
			
				|  |  | -#include <sstream>
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -#include "Eigen/SparseCore"
 | 
	
		
			
				|  |  |  #include "ceres/compressed_row_sparse_matrix.h"
 | 
	
		
			
				|  |  | -#include "ceres/cxsparse.h"
 | 
	
		
			
				|  |  |  #include "ceres/internal/eigen.h"
 | 
	
		
			
				|  |  |  #include "ceres/internal/scoped_ptr.h"
 | 
	
		
			
				|  |  |  #include "ceres/linear_solver.h"
 | 
	
		
			
				|  |  | -#include "ceres/suitesparse.h"
 | 
	
		
			
				|  |  | +#include "ceres/sparse_cholesky.h"
 | 
	
		
			
				|  |  |  #include "ceres/triplet_sparse_matrix.h"
 | 
	
		
			
				|  |  |  #include "ceres/types.h"
 | 
	
		
			
				|  |  |  #include "ceres/wall_time.h"
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -#ifdef CERES_USE_EIGEN_SPARSE
 | 
	
		
			
				|  |  | -#include "Eigen/SparseCholesky"
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  namespace ceres {
 | 
	
		
			
				|  |  |  namespace internal {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -// Different sparse linear algebra libraries prefer different storage
 | 
	
		
			
				|  |  | -// orders for the input matrix. This trait class helps choose the
 | 
	
		
			
				|  |  | -// ordering based on the sparse linear algebra backend being used.
 | 
	
		
			
				|  |  | -//
 | 
	
		
			
				|  |  | -// The storage order is lower-triangular by default. It is only
 | 
	
		
			
				|  |  | -// SuiteSparse which prefers an upper triangular matrix. Saves a whole
 | 
	
		
			
				|  |  | -// matrix copy in the process.
 | 
	
		
			
				|  |  | -//
 | 
	
		
			
				|  |  | -// Note that this is the storage order for a compressed row sparse
 | 
	
		
			
				|  |  | -// matrix. All the sparse linear algebra libraries take compressed
 | 
	
		
			
				|  |  | -// column sparse matrices as input. We map these matrices to into
 | 
	
		
			
				|  |  | -// compressed column sparse matrices before calling them and in the
 | 
	
		
			
				|  |  | -// process, transpose them.
 | 
	
		
			
				|  |  | -//
 | 
	
		
			
				|  |  | -// TODO(sameeragarwal): This does not account for post ordering, where
 | 
	
		
			
				|  |  | -// the optimal storage order maybe different. Either get rid of post
 | 
	
		
			
				|  |  | -// ordering support entirely, or investigate making this trait class
 | 
	
		
			
				|  |  | -// richer.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -CompressedRowSparseMatrix::StorageType StorageTypeForSparseLinearAlgebraLibrary(
 | 
	
		
			
				|  |  | -    SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type) {
 | 
	
		
			
				|  |  | -  if (sparse_linear_algebra_library_type == SUITE_SPARSE) {
 | 
	
		
			
				|  |  | -    return CompressedRowSparseMatrix::UPPER_TRIANGULAR;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  return CompressedRowSparseMatrix::LOWER_TRIANGULAR;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -namespace {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -#ifdef CERES_USE_EIGEN_SPARSE
 | 
	
		
			
				|  |  | -// A templated factorized and solve function, which allows us to use
 | 
	
		
			
				|  |  | -// the same code independent of whether a AMD or a Natural ordering is
 | 
	
		
			
				|  |  | -// used.
 | 
	
		
			
				|  |  | -template <typename SimplicialCholeskySolver, typename SparseMatrixType>
 | 
	
		
			
				|  |  | -LinearSolver::Summary SimplicialLDLTSolve(const SparseMatrixType& lhs,
 | 
	
		
			
				|  |  | -                                          const bool do_symbolic_analysis,
 | 
	
		
			
				|  |  | -                                          SimplicialCholeskySolver* solver,
 | 
	
		
			
				|  |  | -                                          double* rhs_and_solution,
 | 
	
		
			
				|  |  | -                                          EventLogger* event_logger) {
 | 
	
		
			
				|  |  | -  LinearSolver::Summary summary;
 | 
	
		
			
				|  |  | -  summary.num_iterations = 1;
 | 
	
		
			
				|  |  | -  summary.termination_type = LINEAR_SOLVER_SUCCESS;
 | 
	
		
			
				|  |  | -  summary.message = "Success.";
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  if (do_symbolic_analysis) {
 | 
	
		
			
				|  |  | -    solver->analyzePattern(lhs);
 | 
	
		
			
				|  |  | -    if (VLOG_IS_ON(2)) {
 | 
	
		
			
				|  |  | -      std::stringstream ss;
 | 
	
		
			
				|  |  | -      solver->dumpMemory(ss);
 | 
	
		
			
				|  |  | -      VLOG(2) << "Symbolic Analysis\n" << ss.str();
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -    event_logger->AddEvent("Analyze");
 | 
	
		
			
				|  |  | -    if (solver->info() != Eigen::Success) {
 | 
	
		
			
				|  |  | -      summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
 | 
	
		
			
				|  |  | -      summary.message = "Eigen failure. Unable to find symbolic factorization.";
 | 
	
		
			
				|  |  | -      return summary;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  solver->factorize(lhs);
 | 
	
		
			
				|  |  | -  event_logger->AddEvent("Factorize");
 | 
	
		
			
				|  |  | -  if (solver->info() != Eigen::Success) {
 | 
	
		
			
				|  |  | -    summary.termination_type = LINEAR_SOLVER_FAILURE;
 | 
	
		
			
				|  |  | -    summary.message = "Eigen failure. Unable to find numeric factorization.";
 | 
	
		
			
				|  |  | -    return summary;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  const Vector rhs = VectorRef(rhs_and_solution, lhs.cols());
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  VectorRef(rhs_and_solution, lhs.cols()) = solver->solve(rhs);
 | 
	
		
			
				|  |  | -  event_logger->AddEvent("Solve");
 | 
	
		
			
				|  |  | -  if (solver->info() != Eigen::Success) {
 | 
	
		
			
				|  |  | -    summary.termination_type = LINEAR_SOLVER_FAILURE;
 | 
	
		
			
				|  |  | -    summary.message = "Eigen failure. Unable to do triangular solve.";
 | 
	
		
			
				|  |  | -    return summary;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  return summary;
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -#endif  // CERES_USE_EIGEN_SPARSE
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -}  // namespace
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  SparseNormalCholeskySolver::SparseNormalCholeskySolver(
 | 
	
		
			
				|  |  |      const LinearSolver::Options& options)
 | 
	
		
			
				|  |  | -    : factor_(NULL), cxsparse_factor_(NULL), options_(options) {}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -void SparseNormalCholeskySolver::FreeFactorization() {
 | 
	
		
			
				|  |  | -  if (factor_ != NULL) {
 | 
	
		
			
				|  |  | -    ss_.Free(factor_);
 | 
	
		
			
				|  |  | -    factor_ = NULL;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  if (cxsparse_factor_ != NULL) {
 | 
	
		
			
				|  |  | -    cxsparse_.Free(cxsparse_factor_);
 | 
	
		
			
				|  |  | -    cxsparse_factor_ = NULL;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +    : options_(options) {
 | 
	
		
			
				|  |  | +  sparse_cholesky_.reset(
 | 
	
		
			
				|  |  | +      SparseCholesky::Create(options_.sparse_linear_algebra_library_type,
 | 
	
		
			
				|  |  | +                             options_.use_postordering ? AMD : NATURAL));
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -SparseNormalCholeskySolver::~SparseNormalCholeskySolver() {
 | 
	
		
			
				|  |  | -  FreeFactorization();
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | +SparseNormalCholeskySolver::~SparseNormalCholeskySolver() {}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  LinearSolver::Summary SparseNormalCholeskySolver::SolveImpl(
 | 
	
		
			
				|  |  |      CompressedRowSparseMatrix* A,
 | 
	
		
			
				|  |  |      const double* b,
 | 
	
		
			
				|  |  |      const LinearSolver::PerSolveOptions& per_solve_options,
 | 
	
		
			
				|  |  |      double* x) {
 | 
	
		
			
				|  |  | +  EventLogger event_logger("SparseNormalCholeskySolver::Solve");
 | 
	
		
			
				|  |  | +  LinearSolver::Summary summary;
 | 
	
		
			
				|  |  | +  summary.num_iterations = 1;
 | 
	
		
			
				|  |  | +  summary.termination_type = LINEAR_SOLVER_SUCCESS;
 | 
	
		
			
				|  |  | +  summary.message = "Success.";
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |    const int num_cols = A->num_cols();
 | 
	
		
			
				|  |  |    VectorRef(x, num_cols).setZero();
 | 
	
		
			
				|  |  |    A->LeftMultiply(b, x);
 | 
	
		
			
				|  |  | +  event_logger.AddEvent("Compute RHS");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    if (per_solve_options.D != NULL) {
 | 
	
		
			
				|  |  |      // Temporarily append a diagonal block to the A matrix, but undo
 | 
	
	
		
			
				|  | @@ -179,241 +85,28 @@ LinearSolver::Summary SparseNormalCholeskySolver::SolveImpl(
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |      A->AppendRows(*regularizer);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  | +  event_logger.AddEvent("Append Rows");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    if (outer_product_.get() == NULL) {
 | 
	
		
			
				|  |  |      outer_product_.reset(
 | 
	
		
			
				|  |  |          CompressedRowSparseMatrix::CreateOuterProductMatrixAndProgram(
 | 
	
		
			
				|  |  | -            *A,
 | 
	
		
			
				|  |  | -            StorageTypeForSparseLinearAlgebraLibrary(
 | 
	
		
			
				|  |  | -                options_.sparse_linear_algebra_library_type),
 | 
	
		
			
				|  |  | -            &pattern_));
 | 
	
		
			
				|  |  | +            *A, sparse_cholesky_->StorageType(), &pattern_));
 | 
	
		
			
				|  |  | +    event_logger.AddEvent("Outer Product Program");
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    CompressedRowSparseMatrix::ComputeOuterProduct(
 | 
	
		
			
				|  |  |        *A, pattern_, outer_product_.get());
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  LinearSolver::Summary summary;
 | 
	
		
			
				|  |  | -  switch (options_.sparse_linear_algebra_library_type) {
 | 
	
		
			
				|  |  | -    case SUITE_SPARSE:
 | 
	
		
			
				|  |  | -      summary = SolveImplUsingSuiteSparse(x);
 | 
	
		
			
				|  |  | -      break;
 | 
	
		
			
				|  |  | -    case CX_SPARSE:
 | 
	
		
			
				|  |  | -      summary = SolveImplUsingCXSparse(x);
 | 
	
		
			
				|  |  | -      break;
 | 
	
		
			
				|  |  | -    case EIGEN_SPARSE:
 | 
	
		
			
				|  |  | -      summary = SolveImplUsingEigen(x);
 | 
	
		
			
				|  |  | -      break;
 | 
	
		
			
				|  |  | -    default:
 | 
	
		
			
				|  |  | -      LOG(FATAL) << "Unknown sparse linear algebra library : "
 | 
	
		
			
				|  |  | -                 << options_.sparse_linear_algebra_library_type;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | +  event_logger.AddEvent("Outer Product");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    if (per_solve_options.D != NULL) {
 | 
	
		
			
				|  |  |      A->DeleteRows(num_cols);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +  summary.termination_type = sparse_cholesky_->FactorAndSolve(
 | 
	
		
			
				|  |  | +      outer_product_.get(), x, x, &summary.message);
 | 
	
		
			
				|  |  | +  event_logger.AddEvent("Factor & Solve");
 | 
	
		
			
				|  |  |    return summary;
 | 
	
		
			
				|  |  |  }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -LinearSolver::Summary SparseNormalCholeskySolver::SolveImplUsingEigen(
 | 
	
		
			
				|  |  | -    double* rhs_and_solution) {
 | 
	
		
			
				|  |  | -#ifndef CERES_USE_EIGEN_SPARSE
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  LinearSolver::Summary summary;
 | 
	
		
			
				|  |  | -  summary.num_iterations = 0;
 | 
	
		
			
				|  |  | -  summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
 | 
	
		
			
				|  |  | -  summary.message =
 | 
	
		
			
				|  |  | -      "SPARSE_NORMAL_CHOLESKY cannot be used with EIGEN_SPARSE "
 | 
	
		
			
				|  |  | -      "because Ceres was not built with support for "
 | 
	
		
			
				|  |  | -      "Eigen's SimplicialLDLT decomposition. "
 | 
	
		
			
				|  |  | -      "This requires enabling building with -DEIGENSPARSE=ON.";
 | 
	
		
			
				|  |  | -  return summary;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -#else
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  EventLogger event_logger("SparseNormalCholeskySolver::Eigen::Solve");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Map outer_product_ to an upper triangular column major matrix.
 | 
	
		
			
				|  |  | -  //
 | 
	
		
			
				|  |  | -  // outer_product_ is a compressed row sparse matrix and in lower
 | 
	
		
			
				|  |  | -  // triangular form, when mapped to a compressed column sparse
 | 
	
		
			
				|  |  | -  // matrix, it becomes an upper triangular matrix.
 | 
	
		
			
				|  |  | -  Eigen::MappedSparseMatrix<double, Eigen::ColMajor> lhs(
 | 
	
		
			
				|  |  | -      outer_product_->num_rows(),
 | 
	
		
			
				|  |  | -      outer_product_->num_rows(),
 | 
	
		
			
				|  |  | -      outer_product_->num_nonzeros(),
 | 
	
		
			
				|  |  | -      outer_product_->mutable_rows(),
 | 
	
		
			
				|  |  | -      outer_product_->mutable_cols(),
 | 
	
		
			
				|  |  | -      outer_product_->mutable_values());
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  bool do_symbolic_analysis = false;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // If using post ordering or an old version of Eigen, we cannot
 | 
	
		
			
				|  |  | -  // depend on a preordered jacobian, so we work with a SimplicialLDLT
 | 
	
		
			
				|  |  | -  // decomposition with AMD ordering.
 | 
	
		
			
				|  |  | -  if (options_.use_postordering || !EIGEN_VERSION_AT_LEAST(3, 2, 2)) {
 | 
	
		
			
				|  |  | -    if (amd_ldlt_.get() == NULL) {
 | 
	
		
			
				|  |  | -      amd_ldlt_.reset(new SimplicialLDLTWithAMDOrdering);
 | 
	
		
			
				|  |  | -      do_symbolic_analysis = true;
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    return SimplicialLDLTSolve(lhs,
 | 
	
		
			
				|  |  | -                               do_symbolic_analysis,
 | 
	
		
			
				|  |  | -                               amd_ldlt_.get(),
 | 
	
		
			
				|  |  | -                               rhs_and_solution,
 | 
	
		
			
				|  |  | -                               &event_logger);
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -#if EIGEN_VERSION_AT_LEAST(3, 2, 2)
 | 
	
		
			
				|  |  | -  // The common case
 | 
	
		
			
				|  |  | -  if (natural_ldlt_.get() == NULL) {
 | 
	
		
			
				|  |  | -    natural_ldlt_.reset(new SimplicialLDLTWithNaturalOrdering);
 | 
	
		
			
				|  |  | -    do_symbolic_analysis = true;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  return SimplicialLDLTSolve(lhs,
 | 
	
		
			
				|  |  | -                             do_symbolic_analysis,
 | 
	
		
			
				|  |  | -                             natural_ldlt_.get(),
 | 
	
		
			
				|  |  | -                             rhs_and_solution,
 | 
	
		
			
				|  |  | -                             &event_logger);
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -#endif  // EIGEN_USE_EIGEN_SPARSE
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -LinearSolver::Summary SparseNormalCholeskySolver::SolveImplUsingCXSparse(
 | 
	
		
			
				|  |  | -    double* rhs_and_solution) {
 | 
	
		
			
				|  |  | -#ifdef CERES_NO_CXSPARSE
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  LinearSolver::Summary summary;
 | 
	
		
			
				|  |  | -  summary.num_iterations = 0;
 | 
	
		
			
				|  |  | -  summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
 | 
	
		
			
				|  |  | -  summary.message =
 | 
	
		
			
				|  |  | -      "SPARSE_NORMAL_CHOLESKY cannot be used with CX_SPARSE "
 | 
	
		
			
				|  |  | -      "because Ceres was not built with support for CXSparse. "
 | 
	
		
			
				|  |  | -      "This requires enabling building with -DCXSPARSE=ON.";
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  return summary;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -#else
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  EventLogger event_logger("SparseNormalCholeskySolver::CXSparse::Solve");
 | 
	
		
			
				|  |  | -  LinearSolver::Summary summary;
 | 
	
		
			
				|  |  | -  summary.num_iterations = 1;
 | 
	
		
			
				|  |  | -  summary.termination_type = LINEAR_SOLVER_SUCCESS;
 | 
	
		
			
				|  |  | -  summary.message = "Success.";
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Map outer_product_ to an upper triangular column major matrix.
 | 
	
		
			
				|  |  | -  //
 | 
	
		
			
				|  |  | -  // outer_product_ is a compressed row sparse matrix and in lower
 | 
	
		
			
				|  |  | -  // triangular form, when mapped to a compressed column sparse
 | 
	
		
			
				|  |  | -  // matrix, it becomes an upper triangular matrix.
 | 
	
		
			
				|  |  | -  cs_di lhs = cxsparse_.CreateSparseMatrixTransposeView(outer_product_.get());
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  event_logger.AddEvent("Setup");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Compute symbolic factorization if not available.
 | 
	
		
			
				|  |  | -  if (cxsparse_factor_ == NULL) {
 | 
	
		
			
				|  |  | -    if (options_.use_postordering) {
 | 
	
		
			
				|  |  | -      cxsparse_factor_ = cxsparse_.BlockAnalyzeCholesky(
 | 
	
		
			
				|  |  | -          &lhs, outer_product_->col_blocks(), outer_product_->col_blocks());
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      cxsparse_factor_ = cxsparse_.AnalyzeCholeskyWithNaturalOrdering(&lhs);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  event_logger.AddEvent("Analysis");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  if (cxsparse_factor_ == NULL) {
 | 
	
		
			
				|  |  | -    summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
 | 
	
		
			
				|  |  | -    summary.message =
 | 
	
		
			
				|  |  | -        "CXSparse failure. Unable to find symbolic factorization.";
 | 
	
		
			
				|  |  | -  } else if (!cxsparse_.SolveCholesky(
 | 
	
		
			
				|  |  | -                 &lhs, cxsparse_factor_, rhs_and_solution)) {
 | 
	
		
			
				|  |  | -    summary.termination_type = LINEAR_SOLVER_FAILURE;
 | 
	
		
			
				|  |  | -    summary.message = "CXSparse::SolveCholesky failed.";
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -  event_logger.AddEvent("Solve");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  return summary;
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -LinearSolver::Summary SparseNormalCholeskySolver::SolveImplUsingSuiteSparse(
 | 
	
		
			
				|  |  | -    double* rhs_and_solution) {
 | 
	
		
			
				|  |  | -#ifdef CERES_NO_SUITESPARSE
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  LinearSolver::Summary summary;
 | 
	
		
			
				|  |  | -  summary.num_iterations = 0;
 | 
	
		
			
				|  |  | -  summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
 | 
	
		
			
				|  |  | -  summary.message =
 | 
	
		
			
				|  |  | -      "SPARSE_NORMAL_CHOLESKY cannot be used with SUITE_SPARSE "
 | 
	
		
			
				|  |  | -      "because Ceres was not built with support for SuiteSparse. "
 | 
	
		
			
				|  |  | -      "This requires enabling building with -DSUITESPARSE=ON.";
 | 
	
		
			
				|  |  | -  return summary;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -#else
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  EventLogger event_logger("SparseNormalCholeskySolver::SuiteSparse::Solve");
 | 
	
		
			
				|  |  | -  LinearSolver::Summary summary;
 | 
	
		
			
				|  |  | -  summary.termination_type = LINEAR_SOLVER_SUCCESS;
 | 
	
		
			
				|  |  | -  summary.num_iterations = 1;
 | 
	
		
			
				|  |  | -  summary.message = "Success.";
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  // Map outer_product_ to a lower triangular column major matrix.
 | 
	
		
			
				|  |  | -  //
 | 
	
		
			
				|  |  | -  // outer_product_ is a compressed row sparse matrix and in upper
 | 
	
		
			
				|  |  | -  // triangular form, when mapped to a compressed column sparse
 | 
	
		
			
				|  |  | -  // matrix, it becomes a lower triangular matrix.
 | 
	
		
			
				|  |  | -  const int num_cols = outer_product_->num_cols();
 | 
	
		
			
				|  |  | -  cholmod_sparse lhs =
 | 
	
		
			
				|  |  | -      ss_.CreateSparseMatrixTransposeView(outer_product_.get());
 | 
	
		
			
				|  |  | -  event_logger.AddEvent("Setup");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  if (factor_ == NULL) {
 | 
	
		
			
				|  |  | -    if (options_.use_postordering) {
 | 
	
		
			
				|  |  | -      factor_ = ss_.BlockAnalyzeCholesky(
 | 
	
		
			
				|  |  | -          &lhs,
 | 
	
		
			
				|  |  | -          outer_product_->col_blocks(),
 | 
	
		
			
				|  |  | -          outer_product_->col_blocks(),
 | 
	
		
			
				|  |  | -          &summary.message);
 | 
	
		
			
				|  |  | -    } else {
 | 
	
		
			
				|  |  | -      factor_ = ss_.AnalyzeCholeskyWithNaturalOrdering(&lhs, &summary.message);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  event_logger.AddEvent("Analysis");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  if (factor_ == NULL) {
 | 
	
		
			
				|  |  | -    summary.termination_type = LINEAR_SOLVER_FATAL_ERROR;
 | 
	
		
			
				|  |  | -    // No need to set message as it has already been set by the
 | 
	
		
			
				|  |  | -    // symbolic analysis routines above.
 | 
	
		
			
				|  |  | -    return summary;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  summary.termination_type = ss_.Cholesky(&lhs, factor_, &summary.message);
 | 
	
		
			
				|  |  | -  if (summary.termination_type != LINEAR_SOLVER_SUCCESS) {
 | 
	
		
			
				|  |  | -    return summary;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  cholmod_dense* rhs =
 | 
	
		
			
				|  |  | -      ss_.CreateDenseVector(rhs_and_solution, num_cols, num_cols);
 | 
	
		
			
				|  |  | -  cholmod_dense* solution = ss_.Solve(factor_, rhs, &summary.message);
 | 
	
		
			
				|  |  | -  event_logger.AddEvent("Solve");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  ss_.Free(rhs);
 | 
	
		
			
				|  |  | -  if (solution != NULL) {
 | 
	
		
			
				|  |  | -    memcpy(rhs_and_solution, solution->x, num_cols * sizeof(*rhs_and_solution));
 | 
	
		
			
				|  |  | -    ss_.Free(solution);
 | 
	
		
			
				|  |  | -  } else {
 | 
	
		
			
				|  |  | -    // No need to set message as it has already been set by the
 | 
	
		
			
				|  |  | -    // numeric factorization routine above.
 | 
	
		
			
				|  |  | -    summary.termination_type = LINEAR_SOLVER_FAILURE;
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  event_logger.AddEvent("Teardown");
 | 
	
		
			
				|  |  | -  return summary;
 | 
	
		
			
				|  |  | -#endif
 | 
	
		
			
				|  |  | -}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  }  // namespace internal
 | 
	
		
			
				|  |  |  }  // namespace ceres
 |