/** * @file Matrix.cpp * @brief matrix class * @author Christian Potthast */ #include #include #include #include #include #include #include #include #include #include "Matrix.h" #include "Vector.h" #include "svdcmp.h" using namespace std; namespace ublas = boost::numeric::ublas; namespace gtsam { /* ************************************************************************* */ Matrix Matrix_( size_t m, size_t n, const double* const data) { Matrix A(m,n); copy(data, data+m*n, A.data().begin()); return A; } /* ************************************************************************* */ Matrix Matrix_( size_t m, size_t n, const Vector& v) { Matrix A(m,n); // column-wise copy for( size_t j = 0, k=0 ; j < n ; j++) for( size_t i = 0; i < m ; i++,k++) A(i,j) = v(k); return A; } /* ************************************************************************* */ Matrix Matrix_(size_t m, size_t n, ...) { Matrix A(m,n); va_list ap; va_start(ap, n); for( size_t i = 0 ; i < m ; i++) for( size_t j = 0 ; j < n ; j++) { double value = va_arg(ap, double); A(i,j) = value; } va_end(ap); return A; } /* ************************************************************************* */ /** create a matrix with value zero */ /* ************************************************************************* */ Matrix zeros( size_t m, size_t n ) { Matrix A(m,n, 0.0); return A; } /** * Identity matrix */ Matrix eye( size_t m, size_t n){ Matrix A = zeros(m,n); for(size_t i = 0; i tol) return false; } return true; } /* ************************************************************************* */ bool assert_equal(const Matrix& expected, const Matrix& actual, double tol) { if (equal_with_abs_tol(expected,actual,tol)) return true; size_t n1 = expected.size2(), m1 = expected.size1(); size_t n2 = actual.size2(), m2 = actual.size1(); cout << "not equal:" << endl; print(expected,"expected = "); print(actual,"actual = "); if(m1!=m2 || n1!=n2) cout << m1 << "," << n1 << " != " << m2 << "," << n2 << endl; else print(actual-expected, "actual - expected = "); return false; } /* ************************************************************************ */ /** negation */ /* ************************************************************************ */ /* Matrix operator-() const { size_t m = size1(),n=size2(); Matrix M(m,n); for(size_t i = 0; i < m; i++) for(size_t j = 0; j < n; j++) M(i,j) = -matrix_(i,j); return M; } */ /* ************************************************************************* */ Vector Vector_(const Matrix& A) { size_t m = A.size1(), n = A.size2(); Vector v(m*n); for( size_t j = 0, k=0 ; j < n ; j++) for( size_t i = 0; i < m ; i++,k++) v(k) = A(i,j); return v; } /* ************************************************************************* */ Vector column_(const Matrix& A, size_t j) { // if (j>=A.size2()) // throw invalid_argument("Column index out of bounds!"); return column(A,j); // real boost version // TODO: improve this // size_t m = A.size1(); // Vector a(m); // for (size_t i=0; i=A.size1()) throw invalid_argument("Row index out of bounds!"); // TODO: improve this size_t n = A.size2(); Vector a(n); for (size_t j=0; j(A, B); } /* ************************************************************************* */ Matrix inverse(const Matrix& originalA) { Matrix A(originalA); Matrix B = eye(A.size2()); solve(A,B); return B; } /* ************************************************************************* */ /** Householder QR factorization, Golub & Van Loan p 224, explicit version */ /* ************************************************************************* */ pair qr(const Matrix& A) { const size_t m = A.size1(), n = A.size2(), kprime = min(m,n); Matrix Q=eye(m,m),R(A); Vector v(m); // loop over the kprime first columns for(size_t j=0; j < kprime; j++){ // we now work on the matrix (m-j)*(n-j) matrix A(j:end,j:end) const size_t mm=m-j; // copy column from matrix to xjm, i.e. x(j:m) = A(j:m,j) Vector xjm(mm); for(size_t k = 0 ; k < mm; k++) xjm(k) = R(j+k, j); // calculate the Householder vector v double beta; Vector vjm; boost::tie(beta,vjm) = house(xjm); // pad with zeros to get m-dimensional vector v for(size_t k = 0 ; k < m; k++) v(k) = k > weighted_eliminate(Matrix& A, Vector& b, const Vector& sigmas) { size_t m = A.size1(), n = A.size2(); // get size(A) size_t maxRank = min(m,n); // create list list > results; Vector pseudo(m); // allocate storage for pseudo-inverse Vector weights = reciprocal(emul(sigmas,sigmas)); // calculate weights once // We loop over all columns, because the columns that can be eliminated // are not necessarily contiguous. For each one, estimate the corresponding // scalar variable x as d-rS, with S the separator (remaining columns). // Then update A and b by substituting x with d-rS, zero-ing out x's column. for (size_t j=0; j(A, j2)); // TODO: don't use ublas // create the rhs double d = inner_prod(pseudo, b); // construct solution (r, d, sigma) // TODO: avoid sqrt, store precision or at least variance results.push_back(boost::make_tuple(r, d, 1./sqrt(precision))); // exit after rank exhausted if (results.size()>=maxRank) break; // update A, b, expensive, using outer product // A' \define A_{S}-a*r and b'\define b-d*a updateAb(A, b, j, a, r, d); } return results; } /* ************************************************************************* */ /** Imperative version of Householder QR factorization, Golub & Van Loan p 224 * version with Householder vectors below diagonal, as in GVL */ /* ************************************************************************* */ void householder_(Matrix &A, size_t k) { const size_t m = A.size1(), n = A.size2(), kprime = min(k,min(m,n)); // loop over the kprime first columns for(size_t j=0; j < kprime; j++){ // below, the indices r,c always refer to original A // copy column from matrix to xjm, i.e. x(j:m) = A(j:m,j) Vector xjm(m-j); for(size_t r = j ; r < m; r++) xjm(r-j) = A(r,j); // calculate the Householder vector double beta; Vector vjm; boost::tie(beta,vjm) = house(xjm); // do outer product update A = (I-beta vv')*A = A - v*(beta*A'*v)' = A - v*w' householder_update(A, j, beta, vjm) ; // the Householder vector is copied in the zeroed out part for( size_t r = j+1 ; r < m ; r++ ) A(r,j) = vjm(r-j); } // column j } /* ************************************************************************* */ /** version with zeros below diagonal */ /* ************************************************************************* */ void householder(Matrix &A, size_t k) { householder_(A,k); const size_t m = A.size1(), n = A.size2(), kprime = min(k,min(m,n)); for(size_t j=0; j < kprime; j++) for( size_t i = j+1 ; i < m ; i++ ) A(i,j) = 0.0; } /* ************************************************************************* */ Vector backSubstituteUpper(const Matrix& U, const Vector& b, bool unit) { size_t m = U.size1(), n = U.size2(); #ifndef NDEBUG if (m!=n) throw invalid_argument("backSubstituteUpper: U must be square"); #endif Vector result(n); for (size_t i = n; i > 0; i--) { double zi = b(i-1); for (size_t j = i+1; j <= n; j++) zi -= U(i-1,j-1) * result(j-1); if (!unit) zi /= U(i-1,i-1); result(i-1) = zi; } return result; } /* ************************************************************************* */ Vector backSubstituteUpper(const Vector& b, const Matrix& U, bool unit) { size_t m = U.size1(), n = U.size2(); #ifndef NDEBUG if (m!=n) throw invalid_argument("backSubstituteUpper: U must be square"); #endif Vector result(n); for (size_t i = 1; i <= n; i++) { double zi = b(i-1); for (size_t j = 1; j < i; j++) zi -= U(j-1,i-1) * result(j-1); if (!unit) zi /= U(i-1,i-1); result(i-1) = zi; } return result; } /* ************************************************************************* */ Vector backSubstituteLower(const Matrix& L, const Vector& b, bool unit) { size_t m = L.size1(), n = L.size2(); #ifndef NDEBUG if (m!=n) throw invalid_argument("backSubstituteLower: L must be square"); #endif Vector result(n); for (size_t i = 1; i <= n; i++) { double zi = b(i-1); for (size_t j = 1; j < i; j++) zi -= L(i-1,j-1) * result(j-1); if (!unit) zi /= L(i-1,i-1); result(i-1) = zi; } return result; } /* ************************************************************************* */ Matrix stack(size_t nrMatrices, ...) { size_t dimA1 = 0; size_t dimA2 = 0; va_list ap; va_start(ap, nrMatrices); for(size_t i = 0 ; i < nrMatrices ; i++) { Matrix *M = va_arg(ap, Matrix *); dimA1 += M->size1(); dimA2 = M->size2(); // TODO: should check if all the same ! } va_end(ap); va_start(ap, nrMatrices); Matrix A(dimA1, dimA2); size_t vindex = 0; for( size_t i = 0 ; i < nrMatrices ; i++) { Matrix *M = va_arg(ap, Matrix *); for(size_t d1 = 0; d1 < M->size1(); d1++) for(size_t d2 = 0; d2 < M->size2(); d2++) A(vindex+d1, d2) = (*M)(d1, d2); vindex += M->size1(); } return A; } /* ************************************************************************* */ Matrix collect(const std::vector& matrices, size_t m, size_t n) { // if we have known and constant dimensions, use them size_t dimA1 = m; size_t dimA2 = n*matrices.size(); if (!m && !n) BOOST_FOREACH(const Matrix* M, matrices) { dimA1 = M->size1(); // TODO: should check if all the same ! dimA2 += M->size2(); } // original version // Matrix A(dimA1, dimA2); // size_t hindex = 0; // BOOST_FOREACH(const Matrix* M, matrices) { // for(size_t d1 = 0; d1 < M->size1(); d1++) // for(size_t d2 = 0; d2 < M->size2(); d2++) // A(d1, d2+hindex) = (*M)(d1, d2); // hindex += M->size2(); // } // matrix_range version // Result: slower // Matrix A(dimA1, dimA2); // size_t hindex = 0; // BOOST_FOREACH(const Matrix* M, matrices) { // ublas::matrix_range mr(A, ublas::range(0, dimA1), // ublas::range(hindex, hindex+M->size2())); // noalias(mr) = *M; // hindex += M->size2(); // } // memcpy version Matrix A(dimA1, dimA2); double * Aptr = A.data().begin(); size_t hindex = 0; BOOST_FOREACH(const Matrix* M, matrices) { size_t row_len = M->size2(); // find the size of the row to copy size_t row_size = sizeof(double) * row_len; // loop over rows for(size_t d1 = 0; d1 < M->size1(); ++d1) { // rows // get a pointer to the start of the row in each matrix double * Arow = Aptr + d1*dimA2 + hindex; double * Mrow = const_cast (M->data().begin() + d1*row_len); // do direct memory copy to move the row over memcpy(Arow, Mrow, row_size); } hindex += row_len; } return A; } /* ************************************************************************* */ Matrix collect(size_t nrMatrices, ...) { vector matrices; va_list ap; va_start(ap, nrMatrices); for( size_t i = 0 ; i < nrMatrices ; i++) { Matrix *M = va_arg(ap, Matrix *); matrices.push_back(M); } return collect(matrices); } /* ************************************************************************* */ // row scaling Matrix vector_scale(const Vector& v, const Matrix& A) { Matrix M(A); size_t m = A.size1(); size_t n = A.size2(); for (size_t i=0; i