Added in-place insertion functions to Matrix, as well as a #define flag to enable or disable GSL code without a reconfigure.

release/4.3a0
Alex Cunningham 2010-01-31 17:21:07 +00:00
parent b5ca322d21
commit 48d2dabe43
4 changed files with 247 additions and 96 deletions

View File

@ -28,6 +28,9 @@
#include "Vector.h"
#include "svdcmp.h"
// use for switching quickly between GSL and nonGSL versions without reconfigure
//#define REVERTGSL
using namespace std;
namespace ublas = boost::numeric::ublas;
@ -199,13 +202,6 @@ Vector row_(const Matrix& A, size_t i) {
const double * Aptr = A.data().begin() + A.size2() * i;
return Vector_(A.size2(), Aptr);
// TODO: improve this
// size_t n = A.size2();
// Vector a(n);
// for (size_t j=0; j<n; ++j)
// a(j) = A(i,j);
// return a;
}
/* ************************************************************************* */
@ -235,6 +231,33 @@ Matrix sub(const Matrix& A, size_t i1, size_t i2, size_t j1, size_t j2) {
return B;
}
/* ************************************************************************* */
void insertSub(Matrix& big, const Matrix& small, size_t i, size_t j) {
// direct pointer method
size_t ib = big.size1(), jb = big.size2(),
is = small.size1(), js = small.size2();
// pointer to start of window in big
double * bigptr = big.data().begin() + i*jb + j;
const double * smallptr = small.data().begin();
for (size_t row=0; row<is; ++row)
memcpy(bigptr+row*jb, smallptr+row*js, js*sizeof(double));
}
/* ************************************************************************* */
void insertColumn(Matrix& A, const Vector& col, size_t j) {
ublas::matrix_column<Matrix> colproxy(A, j);
colproxy = col;
}
/* ************************************************************************* */
void insertColumn(Matrix& A, const Vector& col, size_t i, size_t j) {
ublas::matrix_column<Matrix> colproxy(A, j);
ublas::vector_range<ublas::matrix_column<Matrix> > colsubproxy(colproxy,
ublas::range (i, i+col.size()));
colsubproxy = col;
}
/* ************************************************************************* */
void solve(Matrix& A, Matrix& B)
{
@ -310,41 +333,45 @@ pair<Matrix,Matrix> qr(const Matrix& A) {
* on a number of different matrices for which all columns change.
*/
/* ************************************************************************* */
inline void householder_update_manual(Matrix &A, int j, double beta, const Vector& vjm) {
const size_t m = A.size1(), n = A.size2();
// w = beta*transpose(A(j:m,:))*v(j:m)
Vector w(n);
for( size_t c = 0; c < n; c++) {
w(c) = 0.0;
// dangerous as relies on row-major scheme
const double *a = &A(j,c), * const v = &vjm(0);
for( size_t r=j, s=0 ; r < m ; r++, s++, a+=n )
// w(c) += A(r,c) * vjm(r-j)
w(c) += (*a) * v[s];
w(c) *= beta;
}
// rank 1 update A(j:m,:) -= v(j:m)*w'
for( size_t c = 0 ; c < n; c++) {
double wc = w(c);
double *a = &A(j,c); const double * const v =&vjm(0);
for( size_t r=j, s=0 ; r < m ; r++, s++, a+=n )
// A(r,c) -= vjm(r-j) * wjn(c-j);
(*a) -= v[s] * wc;
}
}
void householder_update(Matrix &A, int j, double beta, const Vector& vjm) {
const size_t m = A.size1(), n = A.size2();
#ifdef GSL
// use GSL version
gsl_vector_const_view v = gsl_vector_const_view_array(vjm.data().begin(), m-j);
gsl_matrix_view Ag = gsl_matrix_view_array(A.data().begin(), m, n);
gsl_matrix_view Ag_view = gsl_matrix_submatrix (&(Ag.matrix), j, 0, m-j, n);
gsl_linalg_householder_hm (beta, &(v.vector), &(Ag_view.matrix));
#ifndef REVERTGSL
const size_t m = A.size1(), n = A.size2();
// use GSL version
gsl_vector_const_view v = gsl_vector_const_view_array(vjm.data().begin(), m-j);
gsl_matrix_view Ag = gsl_matrix_view_array(A.data().begin(), m, n);
gsl_matrix_view Ag_view = gsl_matrix_submatrix (&(Ag.matrix), j, 0, m-j, n);
gsl_linalg_householder_hm (beta, &(v.vector), &(Ag_view.matrix));
#else
householder_update_manual(A,j,beta,vjm);
#endif
#else
// elegant but slow: A -= Matrix(outer_prod(v,beta*trans(A)*v));
// faster code below
// w = beta*transpose(A(j:m,:))*v(j:m)
Vector w(n);
for( size_t c = 0; c < n; c++) {
w(c) = 0.0;
// dangerous as relies on row-major scheme
const double *a = &A(j,c), * const v = &vjm(0);
for( size_t r=j, s=0 ; r < m ; r++, s++, a+=n )
// w(c) += A(r,c) * vjm(r-j)
w(c) += (*a) * v[s];
w(c) *= beta;
}
// rank 1 update A(j:m,:) -= v(j:m)*w'
for( size_t c = 0 ; c < n; c++) {
double wc = w(c);
double *a = &A(j,c); const double * const v =&vjm(0);
for( size_t r=j, s=0 ; r < m ; r++, s++, a+=n )
// A(r,c) -= vjm(r-j) * wjn(c-j);
(*a) -= v[s] * wc;
}
householder_update_manual(A,j,beta,vjm);
#endif
}
@ -352,52 +379,51 @@ void householder_update(Matrix &A, int j, double beta, const Vector& vjm) {
// update A, b
// A' \define A_{S}-ar and b'\define b-ad
// __attribute__ ((noinline)) // uncomment to prevent inlining when profiling
static void updateAb(Matrix& A, Vector& b, int j, const Vector& a,
inline void updateAB_manual(Matrix& A, Vector& b, int j, const Vector& a,
const Vector& r, double d) {
const size_t m = A.size1(), n = A.size2();
#ifdef GSL
for (size_t i = 0; i < m; i++) { // update all rows
double ai = a(i);
b(i) -= ai * d;
double *Aij = A.data().begin() + i * n + j + 1;
const double *rptr = r.data().begin() + j + 1;
// A(i,j+1:end) -= ai*r(j+1:end)
for (size_t j2 = j + 1; j2 < n; j2++, Aij++, rptr++)
*Aij -= ai * (*rptr);
}
}
static void updateAb(Matrix& A, Vector& b, int j, const Vector& a,
const Vector& r, double d) {
#ifdef GSL
#ifndef REVERTGSL
const size_t m = A.size1(), n = A.size2();
// update A
// A(0:m,j+1:end) = A(0:m,j+1:end) - a(0:m)*r(j+1:end)'
// get a view for A
// gsl_matrix_view Ag = gsl_matrix_view_array(A.data().begin(), m, n);
// gsl_matrix_view Ag_view = gsl_matrix_submatrix (&(Ag.matrix), 0, j+1, m, n-j-1);
// // get a view for r
// gsl_vector_const_view rg = gsl_vector_const_view_array(r.data().begin()+j+1, n-j-1);
// // get a view for a
// gsl_vector_const_view ag = gsl_vector_const_view_array(a.data().begin(), m);
//
// // rank one update
// gsl_blas_dger (-1.0, &(ag.vector), &(rg.vector), &(Ag_view.matrix));
//
// // update b
// double * bptr = b.data().begin();
// const double * aptr = a.data().begin();
// for (size_t i = 0; i < m; i++) {
// *(bptr+i) -= d* *(aptr+i);
// }
gsl_matrix_view Ag = gsl_matrix_view_array(A.data().begin(), m, n);
gsl_matrix_view Ag_view = gsl_matrix_submatrix (&(Ag.matrix), 0, j+1, m, n-j-1);
// get a view for r
gsl_vector_const_view rg = gsl_vector_const_view_array(r.data().begin()+j+1, n-j-1);
// get a view for a
gsl_vector_const_view ag = gsl_vector_const_view_array(a.data().begin(), m);
// original
for (size_t i = 0; i < m; i++) { // update all rows
double ai = a(i);
b(i) -= ai * d;
double *Aij = A.data().begin() + i * n + j + 1;
const double *rptr = r.data().begin() + j + 1;
// A(i,j+1:end) -= ai*r(j+1:end)
for (size_t j2 = j + 1; j2 < n; j2++, Aij++, rptr++)
*Aij -= ai * (*rptr);
// rank one update
gsl_blas_dger (-1.0, &(ag.vector), &(rg.vector), &(Ag_view.matrix));
// update b
double * bptr = b.data().begin();
const double * aptr = a.data().begin();
for (size_t i = 0; i < m; i++) {
*(bptr+i) -= d* *(aptr+i);
}
#else
for (size_t i = 0; i < m; i++) { // update all rows
double ai = a(i);
b(i) -= ai * d;
double *Aij = A.data().begin() + i * n + j + 1;
const double *rptr = r.data().begin() + j + 1;
// A(i,j+1:end) -= ai*r(j+1:end)
for (size_t j2 = j + 1; j2 < n; j2++, Aij++, rptr++)
*Aij -= ai * (*rptr);
}
updateAB_manual(A, b,j,a,r,d);
#endif
#else
updateAb_manual(A,b,j,a,r,d);
#endif
}
@ -458,11 +484,36 @@ weighted_eliminate(Matrix& A, Vector& b, const Vector& sigmas) {
* version with Householder vectors below diagonal, as in GVL
*/
/* ************************************************************************* */
inline void householder_manual(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
}
void householder_(Matrix &A, size_t k)
{
const size_t m = A.size1(), n = A.size2(), kprime = min(k,min(m,n));
#ifdef GSL
#ifndef REVERTGSL
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
@ -510,27 +561,11 @@ void householder_(Matrix &A, size_t k)
} // column j
#else
// loop over the kprime first columns
for(size_t j=0; j < kprime; j++){
// below, the indices r,c always refer to original A
householder_manual(A, k);
#endif
// 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
#else
householder_manual(A, k);
#endif
}

View File

@ -133,6 +133,16 @@ void print(const Matrix& A, const std::string& s = "");
*/
Matrix sub(const Matrix& A, size_t i1, size_t i2, size_t j1, size_t j2);
/**
* insert a submatrix IN PLACE at a specified location in a larger matrix
* NOTE: there is no size checking
* @param large matrix to be updated
* @param small matrix to be inserted
* @param i is the row of the upper left corner insert location
* @param j is the column of the upper left corner insert location
*/
void insertSub(Matrix& big, const Matrix& small, size_t i, size_t j);
/**
* extracts a column from a matrix
* NOTE: using this without the underscore is the ublas version!
@ -142,6 +152,17 @@ Matrix sub(const Matrix& A, size_t i1, size_t i2, size_t j1, size_t j2);
*/
Vector column_(const Matrix& A, size_t j);
/**
* inserts a column into a matrix IN PLACE
* NOTE: there is no size checking
* Alternate form allows for vectors smaller than the whole column to be inserted
* @param A matrix to be modified in place
* @param col is the vector to be inserted
* @param j is the index to insert the column
*/
void insertColumn(Matrix& A, const Vector& col, size_t j);
void insertColumn(Matrix& A, const Vector& col, size_t i, size_t j);
/**
* extracts a row from a matrix
* @param matrix to extract row from
@ -164,6 +185,12 @@ void solve(Matrix& A, Matrix& B);
*/
Matrix inverse(const Matrix& A);
/**
* Perform updates of system matrices
*/
static void updateAb(Matrix& A, Vector& b, int j, const Vector& a,
const Vector& r, double d);
/**
* QR factorization, inefficient, best use imperative householder below
* m*n matrix -> m*m Q, m*n R

View File

@ -10,6 +10,7 @@
#include <boost/tuple/tuple.hpp>
#include <boost/foreach.hpp>
#include <boost/numeric/ublas/matrix_proxy.hpp>
#include <boost/numeric/ublas/io.hpp>
#include "Matrix.h"
using namespace std;
@ -165,6 +166,45 @@ TEST( matrix, column )
CHECK(assert_equal(a3, exp3));
}
/* ************************************************************************* */
TEST( matrix, insert_column )
{
Matrix big = zeros(5,6);
Vector col = ones(5);
size_t j = 3;
insertColumn(big, col, j);
Matrix expected = Matrix_(5,6,
0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0, 0.0);
CHECK(assert_equal(expected, big));
}
/* ************************************************************************* */
TEST( matrix, insert_subcolumn )
{
Matrix big = zeros(5,6);
Vector col = ones(2);
size_t i = 1;
size_t j = 3;
insertColumn(big, col, i, j);
Matrix expected = Matrix_(5,6,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.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);
CHECK(assert_equal(expected, big));
}
/* ************************************************************************* */
TEST( matrix, row )
{
@ -198,6 +238,24 @@ TEST( matrix, zeros )
EQUALITY(A , zero);
}
/* ************************************************************************* */
TEST( matrix, insert_sub )
{
Matrix big = zeros(5,6),
small = Matrix_(2,3, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0);
insertSub(big, small, 1, 2);
Matrix expected = Matrix_(5,6,
0.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 1.0, 1.0, 1.0, 0.0,
0.0, 0.0, 1.0, 1.0, 1.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);
CHECK(assert_equal(expected, big));
}
/* ************************************************************************* */
TEST( matrix, scale_columns )
{

View File

@ -198,6 +198,31 @@ double timeHouseholder(size_t reps) {
}
return elapsed;
}
/**
* Results: (Alex's machine)
* reps: 200000
*
* Initial (boost matrix proxies) - 12.08
* Direct pointer method - 4.62
*/
double timeMatrixInsert(size_t reps) {
// create a matrix
Matrix bigBase = zeros(100, 100);
Matrix small = eye(5,5);
// perform timing
double elapsed;
{
boost::timer t;
Matrix big = bigBase;
for (size_t rep=0; rep<reps; ++rep)
for (size_t i=0; i<100; i += 5)
for (size_t j=0; j<100; j += 5)
insertSub(big, small, i,j);
elapsed = t.elapsed();
}
return elapsed;
}
int main(int argc, char ** argv) {
@ -233,5 +258,11 @@ int main(int argc, char ** argv) {
double house_time = timeHouseholder(reps_house);
cout << "Elapsed time for householder_() : " << house_time << " sec" << endl;
// Time matrix insertion
cout << "Starting insertSub() Timing" << endl;
size_t reps_insert = 200000;
double insert_time = timeMatrixInsert(reps_insert);
cout << "Elapsed time for insertSub() : " << insert_time << " sec" << endl;
return 0;
}