Added a "matrix order" parameter to choleskyCareful so that it can be used to factor the frontal piece in choleskyPartial. choleskyCareful also now returns a bool indicating whether the input matrix is full-rank. Also added an additional Cholesky unit test.

release/4.3a0
Richard Roberts 2011-02-04 00:47:08 +00:00
parent 3d9f7294a9
commit 3dc36369d9
4 changed files with 67 additions and 70 deletions

View File

@ -31,6 +31,10 @@ using namespace std;
namespace gtsam { namespace gtsam {
static const double negativePivotThreshold = -1e-1;
static const double zeroPivotThreshold = 1e-3;
static const double underconstrainedPrior = 0.001;
/* ************************************************************************* */ /* ************************************************************************* */
void cholesky_inplace(MatrixColMajor& I) { void cholesky_inplace(MatrixColMajor& I) {
@ -116,40 +120,51 @@ size_t choleskyFactorUnderdetermined(MatrixColMajor& Ab, size_t nFrontal) {
} }
/* ************************************************************************* */ /* ************************************************************************* */
static inline bool choleskyStep(MatrixColMajor& ATA, size_t k) { static inline bool choleskyStep(MatrixColMajor& ATA, size_t k, size_t order) {
// Tolerance for being equal to zero
// static const double zeroTol = numeric_limits<double>::epsilon();
static const double zeroTol = 1.e-15;
const size_t n = ATA.size1(); const size_t n = ATA.size1();
const double alpha = ATA(k,k); 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;
const double beta = sqrt(alpha); const double beta = sqrt(alpha);
if(beta > zeroTol) { if(beta > zeroPivotThreshold) {
const double betainv = 1.0 / beta; const double betainv = 1.0 / beta;
// Update k,k // Update k,k
ATA(k,k) = beta; ATA(k,k) = beta;
if(k < (n-1)) { 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));
// Update A(k,k+1:end) <- A(k,k+1:end) / beta // Update A(k,k+1:end) <- A(k,k+1:end) / beta
cblas_dscal(n-k-1, betainv, &(ATA(k,k+1)), n); V *= betainv;
// Update A(k+1:end, k+1:end) <- A(k+1:end, k+1:end) - v*v' / alpha // Update A(k+1:end, k+1:end) <- A(k+1:end, k+1:end) - v*v' / alpha
cblas_dsyr(CblasColMajor, CblasUpper, n-k-1, -1.0, &(ATA(k,k+1)), n, &(ATA(k+1,k+1)), n); ublas::matrix_range<MatrixColMajor> L(ublas::subrange(ATA, k+1,order, k+1,order));
L -= ublas::outer_prod(V, V);
} }
return true; return true;
} else } else {
ATA(k,k) = underconstrainedPrior;
for(size_t j=k+1; j<order; ++j)
ATA(k,j) = 0.0;
return false; return false;
}
} }
/* ************************************************************************* */ /* ************************************************************************* */
size_t choleskyCareful(MatrixColMajor& ATA) { pair<size_t,bool> choleskyCareful(MatrixColMajor& ATA, int order) {
static const bool debug = false; const bool debug = ISDEBUG("choleskyCareful");
// // Tolerance for being equal to zero // // Tolerance for being equal to zero
//// static const double zeroTol = numeric_limits<double>::epsilon(); //// static const double zeroTol = numeric_limits<double>::epsilon();
@ -161,78 +176,34 @@ size_t choleskyCareful(MatrixColMajor& ATA) {
// Number of rows/columns // Number of rows/columns
const size_t n = ATA.size1(); const size_t n = ATA.size1();
if(order < 0)
order = n;
assert(order <= n);
// The index of the row after the last non-zero row of the square-root factor // The index of the row after the last non-zero row of the square-root factor
size_t maxrank = 0; size_t maxrank = 0;
bool fullRank = true;
for(size_t k = 0; k < n; ++k) { for(size_t k = 0; k < order; ++k) {
if(choleskyStep(ATA, k)) { if(choleskyStep(ATA, k, order)) {
if(debug) cout << "choleskyCareful: Factored through " << k << endl; if(debug) cout << "choleskyCareful: Factored through " << k << endl;
if(debug) print(ATA, "ATA: "); if(debug) print(ATA, "ATA: ");
maxrank = k+1; maxrank = k+1;
} else { } else {
fullRank = false;
if(debug) cout << "choleskyCareful: Skipping " << k << endl; if(debug) cout << "choleskyCareful: Skipping " << k << endl;
} }
// // If the diagonal element is not zero, run Cholesky as far as possible -
// // Cholesky will stop on the first zero diagonal element. Because ATA is
// // symmetric positive semi-definite, a zero diagonal element implies
// // a corresponding row and column of zeros, thus we need only check the
// // diagonal.
// if(ATA(k,k) > zeroTol || ATA(k,k) < -zeroTol) {
//
// // Try to do Cholesky on the remaining lower-right square submatrix.
// int info = lapack_dpotf2('U', n-k, &(ATA(k,k)), n);
//
// if(info > 0) {
// // The submatrix is rank-deficient, but Cholesky factored the first
// // subRank rows/columns, leaving a positive semi-definite matrix
// // starting at subRank. (we're speaking in zero-based indexices).
// size_t subRank = info - 1;
//
// // The row/column after the last nonzero one.
// maxrank = k + subRank;
//
// // We know that the row/column just after the factored part is zero, so
// // skip it. Note that after this statement k will be the next
// // row/column to process.
// k += subRank + 1;
//
// if(debug) cout << "choleskyCareful: Factored until " << maxrank << ", skipping next." << endl;
//
// } else if(info == 0) {
// // Cholesky successfully factored the rest of the matrix. Note that
// // after this statement k will be the last processed row/column, and
// // will be incremented by 1 by the 'for' loop.
// k += n - k;
//
// // The last row/column is nonzero.
// maxrank = n;
//
// if(debug) cout << "choleskyCareful: Factored the remainder" << endl;
//
// } else {
// throw std::domain_error(boost::str(boost::format(
// "Bad input to choleskyFactorUnderdetermined, dpotrf returned %d.\n")%info));
// }
// } else {
//
// if(debug) cout << "choleskyCareful: Skipping " << k << endl;
//
// // The diagonal element is numerically zero, so skip this row/column.
// ++ k;
// }
//
// if(debug) print(ATA, "ATA: ");
} }
return maxrank; return make_pair(maxrank, fullRank);
} }
/* ************************************************************************* */ /* ************************************************************************* */
void choleskyPartial(MatrixColMajor& ABC, size_t nFrontal) { void choleskyPartial(MatrixColMajor& ABC, size_t nFrontal) {
static const bool debug = false; const bool debug = ISDEBUG("choleskyPartial");
assert(ABC.size1() == ABC.size2()); assert(ABC.size1() == ABC.size2());
assert(nFrontal <= ABC.size1()); assert(nFrontal <= ABC.size1());

View File

@ -58,7 +58,7 @@ size_t choleskyFactorUnderdetermined(MatrixColMajor& Ab, size_t nFrontal);
* (always?) the case during elimination of a fully-constrained least-squares * (always?) the case during elimination of a fully-constrained least-squares
* problem. * problem.
*/ */
size_t choleskyCareful(MatrixColMajor& ATA); std::pair<size_t,bool> choleskyCareful(MatrixColMajor& ATA, int order = -1);
/** /**
* Partial Cholesky computes a factor [R S such that [R' 0 [R S = [A B * Partial Cholesky computes a factor [R S such that [R' 0 [R S = [A B

View File

@ -96,7 +96,33 @@ TEST(cholesky, choleskyPartial) {
Matrix actual(R1 * R2); Matrix actual(R1 * R2);
EXPECT(assert_equal(ublas::symmetric_adaptor<Matrix,ublas::upper>(ABC), actual, 1e-9)); EXPECT(assert_equal(ublas::symmetric_adaptor<Matrix,ublas::upper>(ABC), actual, 1e-9));
}
/* ************************************************************************* */
TEST(cholesky, choleskyCareful) {
Matrix A = Matrix_(7,7,
5184.9, -4381.3, 1137.3, -4448.6, 5127.2, 3264.2, 2.4894,
-4381.3, 1.1345e+05, -28036., -13136., -1.1277e+05, -76360., 19.118,
1137.3, -28036., 1.3193e+05, 3192.3, 27877., -1.0613e+05, -38.623,
-4448.6, -13136., 3192.3, 6417.7, 12294., 8530., -5.4028,
5127.2, -1.1277e+05, 27877., 12294., 1.1222e+05, 75952., -18.507,
3264.2, -76360., -1.0613e+05, 8530., 75952., 1.7642e+05, 21.269,
2.4894, 19.118, -38.623, -5.4028, -18.507, 21.269, 0.014511);
Matrix expected = Matrix_(7,7,
72.006, -60.846, 15.795, -61.781, 71.206, 45.332, 0.034572,
0.0, 331.28, -81.728, -50.999, -327.33, -222.17, 0.064059,
0.0, 0.0, 353.55, 3.6466e-06, 0.00014052, -353.55, -0.09598,
0.0, 0.0, 0.0, 0.024427, -2.3538, 0.33228, -0.0004039,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
MatrixColMajor actual = A;
choleskyCareful(actual);
actual = ublas::triangular_adaptor<MatrixColMajor,ublas::upper>(actual);
EXPECT(assert_equal(expected, Matrix(actual), 1e-3));
} }
/* ************************************************************************* */ /* ************************************************************************* */

View File

@ -273,7 +273,7 @@ SharedDiagonal Gaussian::Cholesky(MatrixColMajor& Ab, size_t nFrontals) const {
// Use Cholesky to factor Ab // Use Cholesky to factor Ab
tic("Cholesky: 3 careful"); tic("Cholesky: 3 careful");
size_t maxrank = choleskyCareful(Ab); size_t maxrank = choleskyCareful(Ab).first;
toc("Cholesky: 3 careful"); toc("Cholesky: 3 careful");
// Due to numerical error the rank could appear to be more than the number // Due to numerical error the rank could appear to be more than the number