Blame internal/ceres/suitesparse.h

Packit ea1746
// Ceres Solver - A fast non-linear least squares minimizer
Packit ea1746
// Copyright 2017 Google Inc. All rights reserved.
Packit ea1746
// http://ceres-solver.org/
Packit ea1746
//
Packit ea1746
// Redistribution and use in source and binary forms, with or without
Packit ea1746
// modification, are permitted provided that the following conditions are met:
Packit ea1746
//
Packit ea1746
// * Redistributions of source code must retain the above copyright notice,
Packit ea1746
//   this list of conditions and the following disclaimer.
Packit ea1746
// * Redistributions in binary form must reproduce the above copyright notice,
Packit ea1746
//   this list of conditions and the following disclaimer in the documentation
Packit ea1746
//   and/or other materials provided with the distribution.
Packit ea1746
// * Neither the name of Google Inc. nor the names of its contributors may be
Packit ea1746
//   used to endorse or promote products derived from this software without
Packit ea1746
//   specific prior written permission.
Packit ea1746
//
Packit ea1746
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
Packit ea1746
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
Packit ea1746
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
Packit ea1746
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
Packit ea1746
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
Packit ea1746
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
Packit ea1746
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
Packit ea1746
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
Packit ea1746
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
Packit ea1746
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
Packit ea1746
// POSSIBILITY OF SUCH DAMAGE.
Packit ea1746
//
Packit ea1746
// Author: sameeragarwal@google.com (Sameer Agarwal)
Packit ea1746
//
Packit ea1746
// A simple C++ interface to the SuiteSparse and CHOLMOD libraries.
Packit ea1746
Packit ea1746
#ifndef CERES_INTERNAL_SUITESPARSE_H_
Packit ea1746
#define CERES_INTERNAL_SUITESPARSE_H_
Packit ea1746
Packit ea1746
// This include must come before any #ifndef check on Ceres compile options.
Packit ea1746
#include "ceres/internal/port.h"
Packit ea1746
Packit ea1746
#ifndef CERES_NO_SUITESPARSE
Packit ea1746
Packit ea1746
#include <cstring>
Packit ea1746
#include <string>
Packit ea1746
#include <vector>
Packit ea1746
#include "SuiteSparseQR.hpp"
Packit ea1746
#include "ceres/linear_solver.h"
Packit ea1746
#include "ceres/sparse_cholesky.h"
Packit ea1746
#include "cholmod.h"
Packit ea1746
#include "glog/logging.h"
Packit ea1746
Packit ea1746
// Before SuiteSparse version 4.2.0, cholmod_camd was only enabled
Packit ea1746
// if SuiteSparse was compiled with Metis support. This makes
Packit ea1746
// calling and linking into cholmod_camd problematic even though it
Packit ea1746
// has nothing to do with Metis. This has been fixed reliably in
Packit ea1746
// 4.2.0.
Packit ea1746
//
Packit ea1746
// The fix was actually committed in 4.1.0, but there is
Packit ea1746
// some confusion about a silent update to the tar ball, so we are
Packit ea1746
// being conservative and choosing the next minor version where
Packit ea1746
// things are stable.
Packit ea1746
#if (SUITESPARSE_VERSION < 4002)
Packit ea1746
#define CERES_NO_CAMD
Packit ea1746
#endif
Packit ea1746
Packit ea1746
// UF_long is deprecated but SuiteSparse_long is only available in
Packit ea1746
// newer versions of SuiteSparse. So for older versions of
Packit ea1746
// SuiteSparse, we define SuiteSparse_long to be the same as UF_long,
Packit ea1746
// which is what recent versions of SuiteSparse do anyways.
Packit ea1746
#ifndef SuiteSparse_long
Packit ea1746
#define SuiteSparse_long UF_long
Packit ea1746
#endif
Packit ea1746
Packit ea1746
namespace ceres {
Packit ea1746
namespace internal {
Packit ea1746
Packit ea1746
class CompressedRowSparseMatrix;
Packit ea1746
class TripletSparseMatrix;
Packit ea1746
Packit ea1746
// The raw CHOLMOD and SuiteSparseQR libraries have a slightly
Packit ea1746
// cumbersome c like calling format. This object abstracts it away and
Packit ea1746
// provides the user with a simpler interface. The methods here cannot
Packit ea1746
// be static as a cholmod_common object serves as a global variable
Packit ea1746
// for all cholmod function calls.
Packit ea1746
class SuiteSparse {
Packit ea1746
 public:
Packit ea1746
  SuiteSparse();
Packit ea1746
  ~SuiteSparse();
Packit ea1746
Packit ea1746
  // Functions for building cholmod_sparse objects from sparse
Packit ea1746
  // matrices stored in triplet form. The matrix A is not
Packit ea1746
  // modifed. Called owns the result.
Packit ea1746
  cholmod_sparse* CreateSparseMatrix(TripletSparseMatrix* A);
Packit ea1746
Packit ea1746
  // This function works like CreateSparseMatrix, except that the
Packit ea1746
  // return value corresponds to A' rather than A.
Packit ea1746
  cholmod_sparse* CreateSparseMatrixTranspose(TripletSparseMatrix* A);
Packit ea1746
Packit ea1746
  // Create a cholmod_sparse wrapper around the contents of A. This is
Packit ea1746
  // a shallow object, which refers to the contents of A and does not
Packit ea1746
  // use the SuiteSparse machinery to allocate memory.
Packit ea1746
  cholmod_sparse CreateSparseMatrixTransposeView(CompressedRowSparseMatrix* A);
Packit ea1746
Packit ea1746
  // Given a vector x, build a cholmod_dense vector of size out_size
Packit ea1746
  // with the first in_size entries copied from x. If x is NULL, then
Packit ea1746
  // an all zeros vector is returned. Caller owns the result.
Packit ea1746
  cholmod_dense* CreateDenseVector(const double* x, int in_size, int out_size);
Packit ea1746
Packit ea1746
  // The matrix A is scaled using the matrix whose diagonal is the
Packit ea1746
  // vector scale. mode describes how scaling is applied. Possible
Packit ea1746
  // values are CHOLMOD_ROW for row scaling - diag(scale) * A,
Packit ea1746
  // CHOLMOD_COL for column scaling - A * diag(scale) and CHOLMOD_SYM
Packit ea1746
  // for symmetric scaling which scales both the rows and the columns
Packit ea1746
  // - diag(scale) * A * diag(scale).
Packit ea1746
  void Scale(cholmod_dense* scale, int mode, cholmod_sparse* A) {
Packit ea1746
     cholmod_scale(scale, mode, A, &cc_;;
Packit ea1746
  }
Packit ea1746
Packit ea1746
  // Create and return a matrix m = A * A'. Caller owns the
Packit ea1746
  // result. The matrix A is not modified.
Packit ea1746
  cholmod_sparse* AATranspose(cholmod_sparse* A) {
Packit ea1746
    cholmod_sparse*m =  cholmod_aat(A, NULL, A->nrow, 1, &cc_;;
Packit ea1746
    m->stype = 1;  // Pay attention to the upper triangular part.
Packit ea1746
    return m;
Packit ea1746
  }
Packit ea1746
Packit ea1746
  // y = alpha * A * x + beta * y. Only y is modified.
Packit ea1746
  void SparseDenseMultiply(cholmod_sparse* A, double alpha, double beta,
Packit ea1746
                           cholmod_dense* x, cholmod_dense* y) {
Packit ea1746
    double alpha_[2] = {alpha, 0};
Packit ea1746
    double beta_[2] = {beta, 0};
Packit ea1746
    cholmod_sdmult(A, 0, alpha_, beta_, x, y, &cc_;;
Packit ea1746
  }
Packit ea1746
Packit ea1746
  // Find an ordering of A or AA' (if A is unsymmetric) that minimizes
Packit ea1746
  // the fill-in in the Cholesky factorization of the corresponding
Packit ea1746
  // matrix. This is done by using the AMD algorithm.
Packit ea1746
  //
Packit ea1746
  // Using this ordering, the symbolic Cholesky factorization of A (or
Packit ea1746
  // AA') is computed and returned.
Packit ea1746
  //
Packit ea1746
  // A is not modified, only the pattern of non-zeros of A is used,
Packit ea1746
  // the actual numerical values in A are of no consequence.
Packit ea1746
  //
Packit ea1746
  // message contains an explanation of the failures if any.
Packit ea1746
  //
Packit ea1746
  // Caller owns the result.
Packit ea1746
  cholmod_factor* AnalyzeCholesky(cholmod_sparse* A, std::string* message);
Packit ea1746
Packit ea1746
  cholmod_factor* BlockAnalyzeCholesky(cholmod_sparse* A,
Packit ea1746
                                       const std::vector<int>& row_blocks,
Packit ea1746
                                       const std::vector<int>& col_blocks,
Packit ea1746
                                       std::string* message);
Packit ea1746
Packit ea1746
  // If A is symmetric, then compute the symbolic Cholesky
Packit ea1746
  // factorization of A(ordering, ordering). If A is unsymmetric, then
Packit ea1746
  // compute the symbolic factorization of
Packit ea1746
  // A(ordering,:) A(ordering,:)'.
Packit ea1746
  //
Packit ea1746
  // A is not modified, only the pattern of non-zeros of A is used,
Packit ea1746
  // the actual numerical values in A are of no consequence.
Packit ea1746
  //
Packit ea1746
  // message contains an explanation of the failures if any.
Packit ea1746
  //
Packit ea1746
  // Caller owns the result.
Packit ea1746
  cholmod_factor* AnalyzeCholeskyWithUserOrdering(
Packit ea1746
      cholmod_sparse* A,
Packit ea1746
      const std::vector<int>& ordering,
Packit ea1746
      std::string* message);
Packit ea1746
Packit ea1746
  // Perform a symbolic factorization of A without re-ordering A. No
Packit ea1746
  // postordering of the elimination tree is performed. This ensures
Packit ea1746
  // that the symbolic factor does not introduce an extra permutation
Packit ea1746
  // on the matrix. See the documentation for CHOLMOD for more details.
Packit ea1746
  //
Packit ea1746
  // message contains an explanation of the failures if any.
Packit ea1746
  cholmod_factor* AnalyzeCholeskyWithNaturalOrdering(cholmod_sparse* A,
Packit ea1746
                                                     std::string* message);
Packit ea1746
Packit ea1746
  // Use the symbolic factorization in L, to find the numerical
Packit ea1746
  // factorization for the matrix A or AA^T. Return true if
Packit ea1746
  // successful, false otherwise. L contains the numeric factorization
Packit ea1746
  // on return.
Packit ea1746
  //
Packit ea1746
  // message contains an explanation of the failures if any.
Packit ea1746
  LinearSolverTerminationType Cholesky(cholmod_sparse* A,
Packit ea1746
                                       cholmod_factor* L,
Packit ea1746
                                       std::string* message);
Packit ea1746
Packit ea1746
  // Given a Cholesky factorization of a matrix A = LL^T, solve the
Packit ea1746
  // linear system Ax = b, and return the result. If the Solve fails
Packit ea1746
  // NULL is returned. Caller owns the result.
Packit ea1746
  //
Packit ea1746
  // message contains an explanation of the failures if any.
Packit ea1746
  cholmod_dense* Solve(cholmod_factor* L, cholmod_dense* b, std::string* message);
Packit ea1746
Packit ea1746
  // By virtue of the modeling layer in Ceres being block oriented,
Packit ea1746
  // all the matrices used by Ceres are also block oriented. When
Packit ea1746
  // doing sparse direct factorization of these matrices the
Packit ea1746
  // fill-reducing ordering algorithms (in particular AMD) can either
Packit ea1746
  // be run on the block or the scalar form of these matrices. The two
Packit ea1746
  // SuiteSparse::AnalyzeCholesky methods allows the the client to
Packit ea1746
  // compute the symbolic factorization of a matrix by either using
Packit ea1746
  // AMD on the matrix or a user provided ordering of the rows.
Packit ea1746
  //
Packit ea1746
  // But since the underlying matrices are block oriented, it is worth
Packit ea1746
  // running AMD on just the block structre of these matrices and then
Packit ea1746
  // lifting these block orderings to a full scalar ordering. This
Packit ea1746
  // preserves the block structure of the permuted matrix, and exposes
Packit ea1746
  // more of the super-nodal structure of the matrix to the numerical
Packit ea1746
  // factorization routines.
Packit ea1746
  //
Packit ea1746
  // Find the block oriented AMD ordering of a matrix A, whose row and
Packit ea1746
  // column blocks are given by row_blocks, and col_blocks
Packit ea1746
  // respectively. The matrix may or may not be symmetric. The entries
Packit ea1746
  // of col_blocks do not need to sum to the number of columns in
Packit ea1746
  // A. If this is the case, only the first sum(col_blocks) are used
Packit ea1746
  // to compute the ordering.
Packit ea1746
  bool BlockAMDOrdering(const cholmod_sparse* A,
Packit ea1746
                        const std::vector<int>& row_blocks,
Packit ea1746
                        const std::vector<int>& col_blocks,
Packit ea1746
                        std::vector<int>* ordering);
Packit ea1746
Packit ea1746
  // Find a fill reducing approximate minimum degree
Packit ea1746
  // ordering. ordering is expected to be large enough to hold the
Packit ea1746
  // ordering.
Packit ea1746
  bool ApproximateMinimumDegreeOrdering(cholmod_sparse* matrix, int* ordering);
Packit ea1746
Packit ea1746
Packit ea1746
  // Before SuiteSparse version 4.2.0, cholmod_camd was only enabled
Packit ea1746
  // if SuiteSparse was compiled with Metis support. This makes
Packit ea1746
  // calling and linking into cholmod_camd problematic even though it
Packit ea1746
  // has nothing to do with Metis. This has been fixed reliably in
Packit ea1746
  // 4.2.0.
Packit ea1746
  //
Packit ea1746
  // The fix was actually committed in 4.1.0, but there is
Packit ea1746
  // some confusion about a silent update to the tar ball, so we are
Packit ea1746
  // being conservative and choosing the next minor version where
Packit ea1746
  // things are stable.
Packit ea1746
  static bool IsConstrainedApproximateMinimumDegreeOrderingAvailable() {
Packit ea1746
    return (SUITESPARSE_VERSION > 4001);
Packit ea1746
  }
Packit ea1746
Packit ea1746
  // Find a fill reducing approximate minimum degree
Packit ea1746
  // ordering. constraints is an array which associates with each
Packit ea1746
  // column of the matrix an elimination group. i.e., all columns in
Packit ea1746
  // group 0 are eliminated first, all columns in group 1 are
Packit ea1746
  // eliminated next etc. This function finds a fill reducing ordering
Packit ea1746
  // that obeys these constraints.
Packit ea1746
  //
Packit ea1746
  // Calling ApproximateMinimumDegreeOrdering is equivalent to calling
Packit ea1746
  // ConstrainedApproximateMinimumDegreeOrdering with a constraint
Packit ea1746
  // array that puts all columns in the same elimination group.
Packit ea1746
  //
Packit ea1746
  // If CERES_NO_CAMD is defined then calling this function will
Packit ea1746
  // result in a crash.
Packit ea1746
  bool ConstrainedApproximateMinimumDegreeOrdering(cholmod_sparse* matrix,
Packit ea1746
                                                   int* constraints,
Packit ea1746
                                                   int* ordering);
Packit ea1746
Packit ea1746
  void Free(cholmod_sparse* m) { cholmod_free_sparse(&m, &cc_;; }
Packit ea1746
  void Free(cholmod_dense* m)  { cholmod_free_dense(&m, &cc_;;  }
Packit ea1746
  void Free(cholmod_factor* m) { cholmod_free_factor(&m, &cc_;; }
Packit ea1746
Packit ea1746
  void Print(cholmod_sparse* m, const std::string& name) {
Packit ea1746
    cholmod_print_sparse(m, const_cast<char*>(name.c_str()), &cc_;;
Packit ea1746
  }
Packit ea1746
Packit ea1746
  void Print(cholmod_dense* m, const std::string& name) {
Packit ea1746
    cholmod_print_dense(m, const_cast<char*>(name.c_str()), &cc_;;
Packit ea1746
  }
Packit ea1746
Packit ea1746
  void Print(cholmod_triplet* m, const std::string& name) {
Packit ea1746
    cholmod_print_triplet(m, const_cast<char*>(name.c_str()), &cc_;;
Packit ea1746
  }
Packit ea1746
Packit ea1746
  cholmod_common* mutable_cc() { return &cc_; }
Packit ea1746
Packit ea1746
 private:
Packit ea1746
  cholmod_common cc_;
Packit ea1746
};
Packit ea1746
Packit ea1746
class SuiteSparseCholesky : public SparseCholesky {
Packit ea1746
 public:
Packit ea1746
  static SuiteSparseCholesky* Create(const OrderingType ordering_type);
Packit ea1746
Packit ea1746
  // SparseCholesky interface.
Packit ea1746
  virtual ~SuiteSparseCholesky();
Packit ea1746
  virtual CompressedRowSparseMatrix::StorageType StorageType() const;
Packit ea1746
  virtual LinearSolverTerminationType Factorize(
Packit ea1746
      CompressedRowSparseMatrix* lhs, std::string* message);
Packit ea1746
  virtual LinearSolverTerminationType Solve(const double* rhs,
Packit ea1746
                                            double* solution,
Packit ea1746
                                            std::string* message);
Packit ea1746
 private:
Packit ea1746
  SuiteSparseCholesky(const OrderingType ordering_type);
Packit ea1746
Packit ea1746
  const OrderingType ordering_type_;
Packit ea1746
  SuiteSparse ss_;
Packit ea1746
  cholmod_factor* factor_;
Packit ea1746
};
Packit ea1746
Packit ea1746
}  // namespace internal
Packit ea1746
}  // namespace ceres
Packit ea1746
Packit ea1746
#else  // CERES_NO_SUITESPARSE
Packit ea1746
Packit ea1746
typedef void cholmod_factor;
Packit ea1746
Packit ea1746
namespace ceres {
Packit ea1746
namespace internal {
Packit ea1746
Packit ea1746
class SuiteSparse {
Packit ea1746
 public:
Packit ea1746
  // Defining this static function even when SuiteSparse is not
Packit ea1746
  // available, allows client code to check for the presence of CAMD
Packit ea1746
  // without checking for the absence of the CERES_NO_CAMD symbol.
Packit ea1746
  //
Packit ea1746
  // This is safer because the symbol maybe missing due to a user
Packit ea1746
  // accidently not including suitesparse.h in their code when
Packit ea1746
  // checking for the symbol.
Packit ea1746
  static bool IsConstrainedApproximateMinimumDegreeOrderingAvailable() {
Packit ea1746
    return false;
Packit ea1746
  }
Packit ea1746
Packit ea1746
  void Free(void* arg) {}
Packit ea1746
};
Packit ea1746
Packit ea1746
}  // namespace internal
Packit ea1746
}  // namespace ceres
Packit ea1746
Packit ea1746
#endif  // CERES_NO_SUITESPARSE
Packit ea1746
Packit ea1746
#endif  // CERES_INTERNAL_SUITESPARSE_H_