Files
mlpack/fastlib/u/nvasil/tpie/matrix.h
T

923 lines
21 KiB
C++

// Copyright (c) 1994 Darren Vengroff
//
// File: matrix.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 11/4/94
//
// $Id: matrix.h,v 1.11 2005/01/14 18:35:00 tavi Exp $
//
#ifndef MATRIX_H
#define MATRIX_H
// Get definitions for working with Unix and Windows
#include "u/nvasil/tpie/portability.h"
#include <iostream>
#include "u/nvasil/tpie/tpie_assert.h"
// Enable exceptions if the compiler supports them.
#ifndef HANDLE_EXCEPTIONS
#define HANDLE_EXCEPTIONS 0
#endif
// References to rows and colums and submatrices.
template<class T> class rowref;
template<class T> class colref;
// Matrices and submatrices.
template<class T> class matrix_base;
template<class T> class matrix;
template<class T> class submatrix;
// A base class for matrices and submatrices.
template<class T> class matrix_base
{
protected:
TPIE_OS_SIZE_T r,c;
public:
#if HANDLE_EXCEPTIONS
// Exception class.
class range { };
#endif
matrix_base(TPIE_OS_SIZE_T rows, TPIE_OS_SIZE_T cols);
virtual ~matrix_base(void);
// What is the size of the matrix?
TPIE_OS_SIZE_T rows(void) const;
TPIE_OS_SIZE_T cols(void) const;
// Access to the contents of the matrix.
virtual T &elt(TPIE_OS_SIZE_T row, TPIE_OS_SIZE_T col) const = 0;
rowref<T> row(TPIE_OS_SIZE_T row) ;
colref<T> col(TPIE_OS_SIZE_T col) ;
rowref<T> operator[](TPIE_OS_SIZE_T row) ;
// Assignement.
matrix_base<T> &operator=(const matrix_base<T> &rhs);
matrix_base<T> &operator=(const rowref<T> &rhs);
matrix_base<T> &operator=(const colref<T> &rhs);
// Addition in place.
matrix_base<T> &operator+=(const matrix_base<T> &rhs);
};
// References to rows and columns.
template<class T>
class rowref
{
private:
matrix_base<T> &m;
TPIE_OS_SIZE_T r;
public:
rowref(matrix_base<T> &amatrix, TPIE_OS_SIZE_T row);
~rowref(void);
T &operator[](const TPIE_OS_SIZE_T col) const;
friend class matrix_base<T>;
friend class matrix<T>;
};
template<class T>
class colref
{
private:
matrix_base<T> &m;
TPIE_OS_SIZE_T c;
public:
colref(matrix_base<T> &amatrix, TPIE_OS_SIZE_T col);
~colref(void);
T &operator[](const TPIE_OS_SIZE_T col) const;
friend class matrix_base<T>;
friend class matrix<T>;
};
template<class T>
matrix_base<T>::matrix_base(TPIE_OS_SIZE_T rows, TPIE_OS_SIZE_T cols) :
r(rows),
c(cols)
{
}
template<class T>
matrix_base<T>::~matrix_base(void)
{
}
template<class T>
TPIE_OS_SIZE_T matrix_base<T>::rows(void) const
{
return r;
}
template<class T>
TPIE_OS_SIZE_T matrix_base<T>::cols(void) const
{
return c;
}
template<class T>
rowref<T> matrix_base<T>::row(TPIE_OS_SIZE_T row)
{
if (row >= r) {
#if HANDLE_EXCEPTIONS
throw range();
#else
tp_assert(0, "Range error.");
#endif
}
return rowref<T>(*this, row);
}
template<class T>
colref<T> matrix_base<T>::col(TPIE_OS_SIZE_T col)
{
if (col >= c) {
#if HANDLE_EXCEPTIONS
throw range();
#else
tp_assert(0, "Range error.");
#endif
}
return colref<T>(*this, col);
}
template<class T>
rowref<T> matrix_base<T>::operator[](TPIE_OS_SIZE_T row)
{
return this->row(row);
}
template<class T>
matrix_base<T> &matrix_base<T>::operator=(const matrix_base<T> &rhs)
{
if ((rows() != rhs.rows()) || (cols() != rhs.cols())) {
#if HANDLE_EXCEPTIONS
throw range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii,jj;
for (ii = rows(); ii--; ) {
for (jj = cols(); jj--; ) {
elt(ii,jj) = rhs.elt(ii,jj);
}
}
return *this;
}
template<class T>
matrix_base<T> &matrix_base<T>::operator=(const rowref<T> &rhs)
{
if ((rows() != 1) || (cols() != rhs.m.cols())) {
#if HANDLE_EXCEPTIONS
throw range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii;
for (ii = cols(); ii--; ) {
elt(0,ii) = rhs[ii];
}
return *this;
}
template<class T>
matrix_base<T> &matrix_base<T>::operator=(const colref<T> &rhs)
{
if ((cols() != 1) || (rows() != rhs.m.rows())) {
#if HANDLE_EXCEPTIONS
throw range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii;
T t;
for (ii = rows(); ii--; ) {
t = rhs[ii];
elt(ii,0) = t;
}
return *this;
}
template<class T>
matrix_base<T> &matrix_base<T>::
operator+=(const matrix_base<T> &rhs)
{
if ((rows() != rhs.rows()) || (cols() != rhs.cols())) {
#if HANDLE_EXCEPTIONS
throw range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii,jj;
for (ii = rows(); ii--; ) {
for (jj = cols(); jj--; ) {
elt(ii,jj) = elt(ii,jj) + rhs.elt(ii,jj);
}
}
return *this;
}
template<class T>
matrix<T> operator+(const matrix_base<T> &op1,
const matrix_base<T> &op2)
{
if ((op1.rows() != op2.rows()) || (op1.cols() != op2.cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
matrix<T> temp(op1);
return temp += op2;
}
template<class T>
void perform_mult_in_place(const matrix_base<T> &op1,
const matrix_base<T> &op2,
matrix_base<T> &res)
{
if ((op1.cols() != op2.rows()) ||
(op1.rows() != res.rows()) ||
(op2.cols() != res.cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii,jj,kk;
T t;
// Iterate over rows of op1.
for (ii = op1.rows(); ii--; ) {
// Iterate over colums of op2.
for (jj = op2.cols(); jj--; ) {
// Iterate through the row of r1 and the column of r2.
t = op1.elt(ii,op1.cols()-1) * op2.elt(op2.rows()-1,jj);
for (kk = op2.rows() - 1; kk--; ) {
t += op1.elt(ii,kk) * op2.elt(kk,jj);
}
// Assign into the result.
res.elt(ii,jj) = t;
}
}
}
template<class T>
void perform_mult_add_in_place(matrix_base<T> &op1,
matrix_base<T> &op2,
matrix_base<T> &res)
{
if ((op1.cols() != op2.rows()) ||
(op1.rows() != res.rows()) ||
(op2.cols() != res.cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii,jj,kk;
T t;
// Iterate over rows of op1.
for (ii = op1.rows(); ii--; ) {
// Iterate over colums of op2.
for (jj = op2.cols(); jj--; ) {
// Iterate through the row of r1 and the column of r2.
t = op1.elt(ii,op1.cols()-1) * op2.elt(op2.rows()-1,jj);
for (kk = op2.rows() - 1; kk--; ) {
t += op1.elt(ii,kk) * op2.elt(kk,jj);
}
// Add into the result.
res.elt(ii,jj) += t;
}
}
}
template<class T>
matrix<T> operator*(const matrix_base<T> &op1,
const matrix_base<T> &op2)
{
if (op1.cols() != op2.rows()) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
matrix<T> temp(op1.rows(),op2.cols());
perform_mult_in_place(op1, op2, (matrix_base<T> &)temp);
return temp;
}
template<class T>
ostream &operator<<(ostream &s, matrix_base<T> &m)
{
TPIE_OS_SIZE_T ii,jj;
// Iterate over rows
for (ii = 0; ii < m.rows(); ii++) {
// Iterate over cols
s << m.elt(ii,0);
for (jj = 1; jj < m.cols(); jj++) {
if (jj) (s << ' ');
s << m.elt(ii,jj);
}
s << '\n';
}
return s;
}
// Member functions for row and column reference classes.
template<class T>
rowref<T>::rowref(matrix_base<T> &amatrix, TPIE_OS_SIZE_T row) :
m(amatrix),
r(row)
{
}
template<class T>
rowref<T>::~rowref(void)
{
}
template<class T>
T &rowref<T>::operator[](const TPIE_OS_SIZE_T col) const
{
return m.elt(r,col);
}
template<class T>
colref<T>::colref(matrix_base<T> &amatrix, TPIE_OS_SIZE_T col) :
m(amatrix),
c(col)
{
}
template<class T>
colref<T>::~colref(void)
{
}
template<class T>
T &colref<T>::operator[](const TPIE_OS_SIZE_T row) const
{
return m.elt(row,c);
}
// A submatrix class.
template<class T>
class submatrix : public matrix_base<T>
{
private:
matrix_base<T> &m;
TPIE_OS_SIZE_T r1,r2,c1,c2;
public:
using matrix_base<T>::rows;
using matrix_base<T>::cols;
// Construction/destruction.
submatrix(matrix_base<T> &amatrix,
TPIE_OS_SIZE_T row1, TPIE_OS_SIZE_T row2,
TPIE_OS_SIZE_T col1, TPIE_OS_SIZE_T col2);
virtual ~submatrix(void);
// We need an assignement operator that copies data by explicitly
// calling the base class's assignment operator to do elementwise
// copying. Otherwise, m, r1, r2, c1, and c2 are just copied.
submatrix<T> &operator=(const submatrix<T> &rhs);
// We also want to be able to assign from matrices.
submatrix<T> &operator=(const matrix<T> &rhs);
// Access to elements.
T& elt(TPIE_OS_SIZE_T row, TPIE_OS_SIZE_T col) const;
};
template<class T>
submatrix<T>::submatrix(matrix_base<T> &amatrix,
TPIE_OS_SIZE_T row1, TPIE_OS_SIZE_T row2,
TPIE_OS_SIZE_T col1, TPIE_OS_SIZE_T col2) :
matrix_base<T>(row2 - row1 + 1,
col2 - col1 + 1),
m(amatrix),
r1(row1), r2(row2),
c1(col1), c2(col2)
{
}
template<class T>
submatrix<T>::~submatrix(void)
{
}
template<class T>
submatrix<T> &submatrix<T>::operator=(const submatrix<T> &rhs)
{
// Call the assignement operator from the base class to do range
// checking and elementwise assignment.
(matrix_base<T> &)(*this) = (matrix_base<T> &)rhs;
return *this;
}
template<class T>
submatrix<T> &submatrix<T>::operator=(const matrix<T> &rhs)
{
// Call the assignement operator from the base class to do range
// checking and elementwise assignment.
(matrix_base<T> &)(*this) = (matrix_base<T> &)rhs;
return *this;
}
template<class T>
T& submatrix<T>::elt(TPIE_OS_SIZE_T row, TPIE_OS_SIZE_T col) const
{
if ((row >= rows()) || (col >= cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
return m.elt(row + r1, col + c1);
}
// The matrix class itself.
template<class T>
class matrix : public matrix_base<T> {
private:
using matrix_base<T>::r;
using matrix_base<T>::c;
T *data;
public:
using matrix_base<T>::rows;
using matrix_base<T>::cols;
// Construction/destruction.
matrix(TPIE_OS_SIZE_T arows, TPIE_OS_SIZE_T acols);
matrix(const matrix<T> &rhs);
matrix(const matrix_base<T> &rhs);
matrix(const submatrix<T> &rhs);
matrix(const rowref<T> umrr);
matrix(const colref<T> umcr);
virtual ~matrix(void);
// We need an assignement operator that copies data by explicitly
// calling the base class's assignment operator to do elementwise
// copying. Otherwise, the data pointer is just copied.
matrix<T> &operator=(const matrix<T> &rhs);
// We also want to be able to assign from submatrices.
matrix<T> &operator=(const submatrix<T> &rhs);
// Access to elements.
T &elt(TPIE_OS_SIZE_T row, TPIE_OS_SIZE_T col) const;
// Friends that need direct access to data for fast multiplication.
// friend void quick_matrix_mult_in_place(const matrix<T> &op1,
// const matrix<T> &op2,
// matrix<T> &res);
// friend void quick_matrix_mult_add_in_place(const matrix<T> &op1,
// const matrix<T> &op2,
// matrix<T> &res);
// friend void aggarwal_matrix_mult_in_place(const matrix<T> &op1,
// const matrix<T> &op2,
// matrix<T> &res);
// friend void aggarwal_matrix_mult_add_in_place(const matrix<T> &op1,
// const matrix<T> &op2,
// matrix<T> &res);
};
template<class T>
matrix<T>::matrix(TPIE_OS_SIZE_T arows, TPIE_OS_SIZE_T acols) :
matrix_base<T>(arows, acols)
{
data = new T[arows * acols];
// Initialize the contents of the matrix.
memset(data, 0, arows * acols * sizeof(T));
}
template<class T>
matrix<T>::matrix(const matrix<T> &rhs) :
matrix_base<T>(rhs.rows(), rhs.cols())
{
TPIE_OS_SIZE_T ii;
data = new T[r*c];
for (ii = r*c; ii--; ) {
data[ii] = rhs.data[ii];
}
}
template<class T>
matrix<T>::matrix(const matrix_base<T> &rhs) :
matrix_base<T>(rhs.rows(), rhs.cols())
{
TPIE_OS_SIZE_T ii,jj;
data = new T[r*c];
for (ii = r; ii--; ) {
for (jj = c; jj--; ) {
data[c*ii+jj] = ((matrix_base<T> &)rhs).elt(ii,jj);
}
}
}
template<class T>
matrix<T>::matrix(const submatrix<T> &rhs) :
matrix_base<T>(rhs.rows(), rhs.cols())
{
TPIE_OS_SIZE_T ii,jj;
data = new T[r*c];
for (ii = r; ii--; ) {
for (jj = c; jj--; ) {
data[c*ii+jj] = ((submatrix<T> &)rhs).elt(ii,jj);
}
}
}
template<class T>
matrix<T>::matrix(const rowref<T> umrr) :
matrix_base<T>(1, umrr.m.cols())
{
data = new T[c];
matrix_base<T>::operator=(umrr);
}
template<class T>
matrix<T>::matrix(const colref<T> umcr) :
matrix_base<T>(umcr.m.rows(),1)
{
data = new T[r];
matrix_base<T>::operator=(umcr);
}
template<class T>
matrix<T>::~matrix(void) {
delete[] data;
}
template<class T>
matrix<T> &matrix<T>::operator=(const matrix<T> &rhs)
{
// Call the assignement operator from the base class to do range
// checking and elementwise assignment.
(matrix_base<T> &)(*this) = (matrix_base<T> &)rhs;
return *this;
}
template<class T>
matrix<T> &matrix<T>::operator=(const submatrix<T> &rhs)
{
// Call the assignement operator from the base class to do range
// checking and elementwise assignment.
(matrix_base<T> &)(*this) = (matrix_base<T> &)rhs;
return *this;
}
template<class T>
T& matrix<T>::elt(TPIE_OS_SIZE_T row, TPIE_OS_SIZE_T col) const
{
if ((row >= rows()) || (col >= cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
return data[row*cols()+col];
}
// These are needed since template functions accept only exact argument
// type matches. Base class promotion is not done as it is for
// ordinary functions.
#define MAT_DUMMY_OP(TM1,TM2,OP) \
template<class T> \
matrix<T> operator OP (const TM1 &op1, \
const TM2 &op2) \
{ \
return ((matrix_base<T> &)op1) OP \
((matrix_base<T> &)op2); \
}
MAT_DUMMY_OP(matrix<T>,matrix<T>,+)
MAT_DUMMY_OP(matrix<T>,submatrix<T>,+)
MAT_DUMMY_OP(submatrix<T>,matrix<T>,+)
MAT_DUMMY_OP(submatrix<T>,submatrix<T>,+)
MAT_DUMMY_OP(matrix<T>,matrix<T>,*)
MAT_DUMMY_OP(matrix<T>,submatrix<T>,*)
MAT_DUMMY_OP(submatrix<T>,matrix<T>,*)
MAT_DUMMY_OP(submatrix<T>,submatrix<T>,*)
template<class T>
ostream &operator<<(ostream &s, const matrix<T> &m)
{
return s << (matrix_base<T> &)m;
}
template<class T>
ostream &operator<<(ostream &s, const submatrix<T> &m)
{
return s << (matrix_base<T> &)m;
}
// Speedups for multiplying matrices. This is only for use with the
// specific implementation of matrices above. General purpose
// multiplication still has to be done with perform_mult_in_place or
// perform_mult_add_in_place.
template<class T>
void quick_matrix_mult_in_place(const matrix<T> &op1,
const matrix<T> &op2,
matrix<T> &res)
{
if ((op1.cols() != op2.rows()) ||
(op1.rows() != res.rows()) ||
(op2.cols() != res.cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii,jj,kk;
TPIE_OS_SIZE_T r1,r2,c1,c2,cres;
T t;
r1 = op1.rows();
r2 = op2.rows();
c1 = op1.cols();
c2 = op2.cols();
cres = res.cols();
// Iterate over rows of op1.
for (ii = r1; ii--; ) {
// Iterate over colums of op2.
for (jj = c2; jj--; ) {
// Iterate through the row of r1 and the column of r2.
// t = op1.data[ii*c1+c1-1] * op2.data[(r2-1)*c2+jj];
// // op1.elt(ii,op1.cols()-1) * op2.elt(op2.rows()-1,jj);
t = op1.elt(ii,c1-1) * op2.elt(r2-1,jj);
for (kk = r2 - 1; kk--; ) {
// t += op1.data[ii*c1+kk] * op2.data[kk*c2+jj];
// // op1.elt(ii,kk) * op2.elt(kk,jj);
t += op1.elt(ii,kk) * op2.elt(kk,jj);
}
// Assign into the result.
// res.data[ii*cres+jj] = t;
res.elt(ii,jj) = t;
}
}
}
template<class T>
void quick_matrix_mult_add_in_place(const matrix<T> &op1,
const matrix<T> &op2,
matrix<T> &res)
{
if ((op1.cols() != op2.rows()) ||
(op1.rows() != res.rows()) ||
(op2.cols() != res.cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii,jj,kk;
TPIE_OS_SIZE_T r1,r2,c1,c2,cres;
T t;
r1 = op1.rows();
r2 = op2.rows();
c1 = op1.cols();
c2 = op2.cols();
cres = res.cols();
// Iterate over rows of op1.
for (ii = r1; ii--; ) {
// Iterate over colums of op2.
for (jj = c2; jj--; ) {
// Iterate through the row of r1 and the column of r2.
// t = op1.data[ii*c1+c1-1] * op2.data[(r2-1)*c2+jj];
// // op1.elt(ii,op1.cols()-1) * op2.elt(op2.rows()-1,jj);
t = op1.elt(ii,c1-1) * op2.elt(r2-1,jj);
for (kk = r2 - 1; kk--; ) {
// t += op1.data[ii*c1+kk] * op2.data[kk*c2+jj];
t += op1.elt(ii,kk) * op2.elt(kk,jj);
}
// Assign into the result.
// res.data[ii*cres+jj] += t;
res.elt(ii,jj) += t;
}
}
}
// Aggarwal et. al.'s algorithm.
template<class T>
void aggarwal_matrix_mult_in_place(const matrix<T> &op1,
const matrix<T> &op2,
matrix<T> &res)
{
if ((op1.cols() != op2.rows()) ||
(op1.rows() != res.rows()) ||
(op2.cols() != res.cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii,jj,kk;
TPIE_OS_SIZE_T r1,r2,c1,c2,cres;
r1 = op1.rows();
r2 = op2.rows();
c1 = op1.cols();
c2 = op2.cols();
cres = res.cols();
// Temporary results.
T *temp = new T[c2];
T op1elt;
// Iterate over rows of op1.
for (ii = r1; ii--; ) {
// Clear out the temporary sums.
for (jj = c2; jj--; ) {
temp[jj] = 0;
}
// Iterate through the row of r1 and the column of r2.
for (kk = r2; kk--; ) {
// Iterate over columns of op2.
// op1elt = op1.data[ii*c1+kk];
op1elt = op1.elt(ii,kk);
for (jj = c2; jj--; ) {
// temp[jj] += op1elt * op2.data[kk*c2+jj];
temp[jj] += op1elt * op2.elt(kk,jj);
}
}
// Set the results.
for (jj = c2; jj--; ) {
// res.data[ii*cres+jj] = temp[jj];
res.elt(ii,jj) = temp[jj];
}
}
delete [] temp;
}
template<class T>
void aggarwal_matrix_mult_add_in_place(const matrix<T> &op1,
const matrix<T> &op2,
matrix<T> &res)
{
if ((op1.cols() != op2.rows()) ||
(op1.rows() != res.rows()) ||
(op2.cols() != res.cols())) {
#if HANDLE_EXCEPTIONS
throw matrix_base<T>::range();
#else
tp_assert(0, "Range error.");
#endif
}
TPIE_OS_SIZE_T ii,jj,kk;
TPIE_OS_SIZE_T r1,r2,c1,c2,cres;
r1 = op1.rows();
r2 = op2.rows();
c1 = op1.cols();
c2 = op2.cols();
cres = res.cols();
// Temporary results.
T *temp = new T[c2];
T op1elt;
// Iterate over rows of op1.
for (ii = r1; ii--; ) {
// Clear out the temporary sums.
for (jj = c2; jj--; ) {
temp[jj] = 0;
}
// Iterate through the row of r1 and the column of r2.
for (kk = r2; kk--; ) {
// Iterate over columns of op2.
// op1elt = op1.data[ii*c1+kk];
op1elt = op1.elt(ii,kk);
for (jj = c2; jj--; ) {
// temp[jj] += op1elt * op2.data[kk*c2+jj];
temp[jj] += op1elt * op2.elt(kk,jj);
}
}
// Set the results.
for (jj = c2; jj--; ) {
// res.data[ii*cres+jj] += temp[jj];
res.elt(ii,jj) += temp[jj];
}
}
delete [] temp;
}
#endif // MATRIX_H