2010-11-07 05:06:52 +08:00
/* ----------------------------------------------------------------------------
* GTSAM Copyright 2010 , Georgia Tech Research Corporation ,
* Atlanta , Georgia 30332 - 0415
* All Rights Reserved
* Authors : Frank Dellaert , et al . ( see THANKS for the full author list )
* See LICENSE for the license information
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/**
* @ file cholesky . cpp
* @ brief Efficient incomplete Cholesky on rank - deficient matrices , todo : constrained Cholesky
* @ author Richard Roberts
* @ created Nov 5 , 2010
*/
2011-02-04 08:53:00 +08:00
# include <gtsam/base/debug.h>
2010-11-07 05:06:52 +08:00
# include <gtsam/base/cholesky.h>
# include <gtsam/base/lapack.h>
2011-01-21 06:22:00 +08:00
# include <gtsam/base/timing.h>
2010-11-07 05:06:52 +08:00
# include <boost/numeric/ublas/matrix_proxy.hpp>
2011-02-04 08:00:31 +08:00
# include <boost/numeric/ublas/symmetric.hpp>
2010-11-07 05:06:52 +08:00
# include <boost/numeric/ublas/blas.hpp>
# include <boost/format.hpp>
namespace ublas = boost : : numeric : : ublas ;
2010-11-29 09:31:51 +08:00
using namespace std ;
2010-11-07 05:06:52 +08:00
namespace gtsam {
2011-02-04 08:47:08 +08:00
static const double negativePivotThreshold = - 1e-1 ;
static const double zeroPivotThreshold = 1e-3 ;
static const double underconstrainedPrior = 0.001 ;
2010-11-07 05:06:52 +08:00
/* ************************************************************************* */
void cholesky_inplace ( MatrixColMajor & I ) {
// We do not check for symmetry but we do check for squareness
assert ( I . size1 ( ) = = I . size2 ( ) ) ;
// Do Cholesky, return value info as follows (from dpotrf.f):
// 00054 * INFO (output) INTEGER
// 00055 * = 0: successful exit
// 00056 * < 0: if INFO = -i, the i-th argument had an illegal value
// 00057 * > 0: if INFO = i, the leading minor of order i is not
// 00058 * positive definite, and the factorization could not be
// 00059 * completed.
int info = lapack_dpotrf ( ' U ' , I . size1 ( ) , & I ( 0 , 0 ) , I . size1 ( ) ) ;
2010-11-09 03:31:40 +08:00
if ( info ! = 0 ) {
2010-11-07 05:06:52 +08:00
if ( info < 0 )
throw std : : domain_error ( boost : : str ( boost : : format (
" Bad input to cholesky_inplace, dpotrf returned %d. \n " ) % info ) ) ;
else
throw std : : domain_error ( " The matrix passed into cholesky_inplace is rank-deficient " ) ;
2010-11-09 03:31:40 +08:00
}
2010-11-07 05:06:52 +08:00
}
/* ************************************************************************* */
2010-11-29 09:31:51 +08:00
size_t choleskyFactorUnderdetermined ( MatrixColMajor & Ab , size_t nFrontal ) {
2010-11-07 05:06:52 +08:00
2010-11-29 09:31:51 +08:00
static const bool debug = false ;
2010-11-20 00:27:08 +08:00
2010-11-07 05:06:52 +08:00
size_t m = Ab . size1 ( ) ;
size_t n = Ab . size2 ( ) ;
// If m >= n, this function will just compute the plain Cholesky
// factorization of A'A. If m < n, A'A is rank-deficient this function
// instead computes the upper-trapazoidal factor [ R S ], as described in the
// header file comment.
2010-11-29 09:31:51 +08:00
// size_t rank = std::min(m,n-1);
size_t rank = nFrontal ;
2010-11-07 05:06:52 +08:00
2010-11-09 03:31:40 +08:00
if ( rank > 0 ) {
2010-11-07 05:06:52 +08:00
2010-11-09 03:31:40 +08:00
// F is the first 'rank' columns of Ab, G is the remaining columns
ublas : : matrix_range < MatrixColMajor > F ( ublas : : project ( Ab , ublas : : range ( 0 , m ) , ublas : : range ( 0 , rank ) ) ) ;
ublas : : matrix_range < MatrixColMajor > G ( ublas : : project ( Ab , ublas : : range ( 0 , m ) , ublas : : range ( rank , n ) ) ) ;
2010-11-07 05:06:52 +08:00
2010-11-20 00:27:08 +08:00
if ( debug ) {
print ( F , " F: " ) ;
print ( G , " G: " ) ;
}
2010-11-09 03:31:40 +08:00
ublas : : matrix_range < MatrixColMajor > R ( ublas : : project ( Ab , ublas : : range ( 0 , rank ) , ublas : : range ( 0 , rank ) ) ) ;
ublas : : matrix_range < MatrixColMajor > S ( ublas : : project ( Ab , ublas : : range ( 0 , rank ) , ublas : : range ( rank , n ) ) ) ;
2010-11-07 05:06:52 +08:00
2010-11-09 03:31:40 +08:00
// First compute F' * G (ublas makes a copy here to avoid aliasing)
if ( S . size2 ( ) > 0 )
S = ublas : : prod ( ublas : : trans ( F ) , G ) ;
2010-11-07 05:06:52 +08:00
2010-11-09 03:31:40 +08:00
// ublas makes a copy to avoid aliasing on this assignment
R = ublas : : prod ( ublas : : trans ( F ) , F ) ;
// Compute the values of R from F'F
int info = lapack_dpotrf ( ' U ' , rank , & R ( 0 , 0 ) , Ab . size1 ( ) ) ;
if ( info ! = 0 ) {
if ( info < 0 )
throw std : : domain_error ( boost : : str ( boost : : format (
" Bad input to choleskyFactorUnderdetermined, dpotrf returned %d. \n " ) % info ) ) ;
else
2010-11-09 04:34:12 +08:00
throw std : : domain_error ( boost : : str ( boost : : format (
2010-11-20 00:27:08 +08:00
" The matrix passed into choleskyFactorUnderdetermined is numerically rank-deficient, dpotrf returned rank=%d, expected rank was %d. \n " ) % ( info - 1 ) % rank ) ) ;
2010-11-09 03:31:40 +08:00
}
// Compute S = inv(R') * F' * G, i.e. solve S when R'S = F'G
if ( S . size2 ( ) > 0 )
cblas_dtrsm ( CblasColMajor , CblasLeft , CblasUpper , CblasTrans , CblasNonUnit , S . size1 ( ) , S . size2 ( ) , 1.0 , & R ( 0 , 0 ) , m , & S ( 0 , 0 ) , m ) ;
2010-11-07 05:06:52 +08:00
2010-11-20 00:27:08 +08:00
if ( debug ) {
print ( R , " R: " ) ;
print ( S , " S: " ) ;
}
2010-11-29 09:31:51 +08:00
return m ;
2010-11-09 03:31:40 +08:00
} else
return 0 ;
2010-11-07 05:06:52 +08:00
}
2010-11-29 09:31:51 +08:00
/* ************************************************************************* */
2011-02-04 08:47:08 +08:00
static inline bool choleskyStep ( MatrixColMajor & ATA , size_t k , size_t order ) {
2010-11-29 09:31:51 +08:00
const size_t n = ATA . size1 ( ) ;
2011-02-04 08:47:08 +08:00
double alpha = ATA ( k , k ) ;
if ( alpha < negativePivotThreshold ) {
cout < < " pivot = " < < alpha < < endl ;
print ( ATA , " Partially-factorized matrix: " ) ;
throw ( invalid_argument ( " The matrix was found to be non-positive-semidefinite when factoring with careful Cholesky. " ) ) ;
} else if ( alpha < 0.0 )
alpha = 0.0 ;
2010-11-29 09:31:51 +08:00
const double beta = sqrt ( alpha ) ;
2011-02-04 08:47:08 +08:00
if ( beta > zeroPivotThreshold ) {
2010-11-29 09:31:51 +08:00
const double betainv = 1.0 / beta ;
// Update k,k
ATA ( k , k ) = beta ;
2011-02-04 08:47:08 +08:00
if ( k < ( order - 1 ) ) {
ublas : : matrix_row < MatrixColMajor > Vf ( ublas : : row ( ATA , k ) ) ;
ublas : : vector_range < typeof ( Vf ) > V ( ublas : : subrange ( Vf , k + 1 , order ) ) ;
2010-11-29 09:31:51 +08:00
// Update A(k,k+1:end) <- A(k,k+1:end) / beta
2011-02-04 08:47:08 +08:00
V * = betainv ;
2010-11-29 09:31:51 +08:00
// Update A(k+1:end, k+1:end) <- A(k+1:end, k+1:end) - v*v' / alpha
2011-02-04 08:47:08 +08:00
ublas : : matrix_range < MatrixColMajor > L ( ublas : : subrange ( ATA , k + 1 , order , k + 1 , order ) ) ;
L - = ublas : : outer_prod ( V , V ) ;
2010-11-29 09:31:51 +08:00
}
return true ;
2011-02-04 08:47:08 +08:00
} else {
ATA ( k , k ) = underconstrainedPrior ;
for ( size_t j = k + 1 ; j < order ; + + j )
ATA ( k , j ) = 0.0 ;
2010-11-29 09:31:51 +08:00
return false ;
2011-02-04 08:47:08 +08:00
}
2010-11-29 09:31:51 +08:00
}
/* ************************************************************************* */
2011-02-04 08:47:08 +08:00
pair < size_t , bool > choleskyCareful ( MatrixColMajor & ATA , int order ) {
2010-11-29 09:31:51 +08:00
2011-02-04 08:47:08 +08:00
const bool debug = ISDEBUG ( " choleskyCareful " ) ;
2010-11-29 09:31:51 +08:00
// // Tolerance for being equal to zero
//// static const double zeroTol = numeric_limits<double>::epsilon();
// static const double zeroTol = 1.e-15;
// Check that the matrix is square (we do not check for symmetry)
assert ( ATA . size1 ( ) = = ATA . size2 ( ) ) ;
// Number of rows/columns
const size_t n = ATA . size1 ( ) ;
2011-02-04 08:47:08 +08:00
if ( order < 0 )
order = n ;
assert ( order < = n ) ;
2010-11-29 09:31:51 +08:00
// The index of the row after the last non-zero row of the square-root factor
size_t maxrank = 0 ;
2011-02-04 08:47:08 +08:00
bool fullRank = true ;
2010-11-29 09:31:51 +08:00
2011-02-04 08:47:08 +08:00
for ( size_t k = 0 ; k < order ; + + k ) {
2010-11-29 09:31:51 +08:00
2011-02-04 08:47:08 +08:00
if ( choleskyStep ( ATA , k , order ) ) {
2010-11-29 09:31:51 +08:00
if ( debug ) cout < < " choleskyCareful: Factored through " < < k < < endl ;
if ( debug ) print ( ATA , " ATA: " ) ;
maxrank = k + 1 ;
} else {
2011-02-04 08:47:08 +08:00
fullRank = false ;
2010-11-29 09:31:51 +08:00
if ( debug ) cout < < " choleskyCareful: Skipping " < < k < < endl ;
}
}
2011-02-04 08:47:08 +08:00
return make_pair ( maxrank , fullRank ) ;
2011-01-21 06:22:00 +08:00
}
/* ************************************************************************* */
void choleskyPartial ( MatrixColMajor & ABC , size_t nFrontal ) {
2011-02-04 08:47:08 +08:00
const bool debug = ISDEBUG ( " choleskyPartial " ) ;
2011-01-21 06:22:00 +08:00
assert ( ABC . size1 ( ) = = ABC . size2 ( ) ) ;
assert ( nFrontal < = ABC . size1 ( ) ) ;
const size_t n = ABC . size1 ( ) ;
2010-11-29 09:31:51 +08:00
2011-01-21 06:22:00 +08:00
// Compute Cholesky factorization of A, overwrites A.
2011-02-04 09:05:52 +08:00
if ( true ) {
tic ( 1 , " dpotrf " ) ;
int info = lapack_dpotrf ( ' U ' , nFrontal , & ABC ( 0 , 0 ) , n ) ;
if ( info ! = 0 ) {
if ( info < 0 )
throw std : : domain_error ( boost : : str ( boost : : format (
" Bad input to choleskyPartial, dpotrf returned %d. \n " ) % info ) ) ;
else
throw std : : domain_error ( boost : : str ( boost : : format (
" The matrix passed into choleskyPartial is numerically rank-deficient, dpotrf returned rank=%d, expected rank was %d. \n " ) % ( info - 1 ) % nFrontal ) ) ;
}
toc ( 1 , " dpotrf " ) ;
} else {
bool fullRank = choleskyCareful ( ABC , nFrontal ) . second ;
if ( ! fullRank )
throw invalid_argument ( " Rank-deficient " ) ;
2011-01-21 06:22:00 +08:00
}
toc ( 1 , " dpotrf " ) ;
2011-02-04 09:05:52 +08:00
# ifndef NDEBUG
// Check for non-finite values
for ( size_t i = 0 ; i < ABC . size1 ( ) ; + + i )
for ( size_t j = 0 ; j < ABC . size2 ( ) ; + + j )
if ( ! isfinite ( ABC ( i , j ) ) ) {
cout < < nFrontal < < " frontal variables " < < endl ;
print ( ABC , " Partially-factored matrix: " ) ;
throw invalid_argument ( " After dpotrf, matrix contains non-finite matrix entries. " ) ;
}
# endif
2011-01-21 06:22:00 +08:00
// Views of R, S, and L submatrices.
2011-02-04 09:05:52 +08:00
ublas : : matrix_range < MatrixColMajor > Rf ( ublas : : project ( ABC , ublas : : range ( 0 , nFrontal ) , ublas : : range ( 0 , nFrontal ) ) ) ;
ublas : : triangular_adaptor < ublas : : matrix_range < MatrixColMajor > , ublas : : upper > R ( Rf ) ;
2011-01-21 06:22:00 +08:00
ublas : : matrix_range < MatrixColMajor > S ( ublas : : project ( ABC , ublas : : range ( 0 , nFrontal ) , ublas : : range ( nFrontal , n ) ) ) ;
2011-02-04 08:00:31 +08:00
ublas : : matrix_range < MatrixColMajor > Lf ( ublas : : project ( ABC , ublas : : range ( nFrontal , n ) , ublas : : range ( nFrontal , n ) ) ) ;
ublas : : symmetric_adaptor < typeof ( Lf ) , ublas : : upper > L ( Lf ) ;
2011-01-21 06:22:00 +08:00
// Compute S = inv(R') * B
tic ( 2 , " compute S " ) ;
2011-02-04 09:05:52 +08:00
if ( S . size2 ( ) > 0 ) {
typeof ( ublas : : trans ( R ) ) RT ( ublas : : trans ( R ) ) ;
ublas : : inplace_solve ( RT , S , ublas : : lower_tag ( ) ) ;
//cblas_dtrsm(CblasColMajor, CblasLeft, CblasUpper, CblasTrans, CblasNonUnit, S.size1(), S.size2(), 1.0, &R(0,0), n, &S(0,0), n);
}
2011-01-21 06:22:00 +08:00
if ( debug ) gtsam : : print ( S , " S: " ) ;
toc ( 2 , " compute S " ) ;
2011-02-04 09:05:52 +08:00
# ifndef NDEBUG
// Check for non-finite values
for ( size_t i = 0 ; i < ABC . size1 ( ) ; + + i )
for ( size_t j = 0 ; j < ABC . size2 ( ) ; + + j )
if ( ! isfinite ( ABC ( i , j ) ) ) {
cout < < nFrontal < < " frontal variables " < < endl ;
print ( ABC , " Partially-factored matrix: " ) ;
throw invalid_argument ( " After computing S, matrix contains non-finite matrix entries. " ) ;
}
# endif
2011-01-21 06:22:00 +08:00
// Compute L = C - S' * S
tic ( 3 , " compute L " ) ;
if ( debug ) gtsam : : print ( L , " C: " ) ;
if ( L . size2 ( ) > 0 )
2011-02-04 08:00:31 +08:00
L - = ublas : : prod ( ublas : : trans ( S ) , S ) ;
2011-01-21 06:22:00 +08:00
if ( debug ) gtsam : : print ( L , " L: " ) ;
toc ( 3 , " compute L " ) ;
2011-02-04 09:05:52 +08:00
# ifndef NDEBUG
// Check for positive semi-definiteness of L
try {
MatrixColMajor Lf ( L ) ;
choleskyCareful ( Lf ) ;
} catch ( const invalid_argument & e ) {
cout < < " Remaining Hessian L is not positive semi-definite: " < < e . what ( ) < < endl ;
throw runtime_error ( " Remaining Hessian L is not positive semi-definite " ) ;
}
// Check for non-finite values
for ( size_t i = 0 ; i < ABC . size1 ( ) ; + + i )
for ( size_t j = 0 ; j < ABC . size2 ( ) ; + + j )
if ( ! isfinite ( ABC ( i , j ) ) ) {
cout < < nFrontal < < " frontal variables " < < endl ;
print ( ABC , " Partially-factored matrix: " ) ;
throw invalid_argument ( " After Cholesky, matrix contains non-finite matrix entries. " ) ;
}
# endif
2010-11-29 09:31:51 +08:00
}
2010-11-07 05:06:52 +08:00
}