git-svn-id: http://svn.sintef.no/trondheim/IFEM/trunk@865 e10b68d5-8a6e-419e-a041-bce267b0401d
1411 lines
43 KiB
C++
1411 lines
43 KiB
C++
// $Id: matrix.h,v 1.32 2011-02-05 17:39:56 kmo Exp $
|
|
//==============================================================================
|
|
//!
|
|
//! \file matrix.h
|
|
//!
|
|
//! \date Oct 1 2007
|
|
//!
|
|
//! \author Knut Morten Okstad / SINTEF
|
|
//!
|
|
//! \brief Simple template classes for dense rectangular matrices and vectors.
|
|
//! \details The classes have some algebraic operators defined, such that the
|
|
//! class type \a T has to be of a numerical type, i.e., \a float or \a double.
|
|
//! The multiplication methods are implemented based on the CBLAS library if
|
|
//! the macro symbol USE_CBLAS is defined. Otherwise, inlined methods are used.
|
|
//!
|
|
//==============================================================================
|
|
|
|
#ifndef UTL_MATRIX_H
|
|
#define UTL_MATRIX_H
|
|
|
|
#include <vector>
|
|
#include <iostream>
|
|
#include <algorithm>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#ifdef USE_MKL
|
|
#pragma push_macro("real")
|
|
#undef real
|
|
#include <mkl_cblas.h>
|
|
#pragma pop_macro("real")
|
|
#elif defined(USE_CBLAS)
|
|
#include <cblas.h>
|
|
#endif
|
|
|
|
#ifdef INDEX_CHECK
|
|
#if INDEX_CHECK > 1
|
|
#define ABORT_ON_INDEX_CHECK abort()
|
|
#else
|
|
#define ABORT_ON_INDEX_CHECK
|
|
#endif
|
|
#define CHECK_INDEX(label,i,n) if (i < 1 || i > n) { \
|
|
std::cerr << label << i <<" is out of range [1,"<< n <<"]"<< std::endl; \
|
|
ABORT_ON_INDEX_CHECK; }
|
|
#else
|
|
#define CHECK_INDEX(label,i,n)
|
|
#define ABORT_ON_INDEX_CHECK
|
|
#endif
|
|
|
|
#ifdef SING_CHECK
|
|
#define ABORT_ON_SINGULARITY abort()
|
|
#else
|
|
#define ABORT_ON_SINGULARITY
|
|
#endif
|
|
|
|
|
|
namespace utl //! General utility classes and functions.
|
|
{
|
|
/*!
|
|
\brief Sub-class of std::vector with some added algebraic operations.
|
|
\details The class type \a T has to be of a numerical type, i.e.,
|
|
\a float or \a double.
|
|
*/
|
|
|
|
template<class T> class vector : public std::vector<T>
|
|
{
|
|
public:
|
|
//! \brief Constructor creating an empty vector.
|
|
vector<T>() {}
|
|
//! \brief Constructor creating a vector of length \a n.
|
|
vector<T>(size_t n) { this->resize(n); }
|
|
//! \brief Constructor creating a vector from an array.
|
|
vector<T>(const T* values, size_t n) { this->fill(values,n); }
|
|
|
|
//! \brief Overloaded assignment operator.
|
|
vector<T>& operator=(const std::vector<T>& X)
|
|
{
|
|
this->std::vector<T>::operator=(X);
|
|
return *this;
|
|
}
|
|
|
|
//! \brief Access through pointer.
|
|
T* ptr() { return &this->front(); }
|
|
//! \brief Reference through pointer.
|
|
const T* ptr() const { return &this->front(); }
|
|
|
|
//! \brief Index-1 based element access.
|
|
T& operator()(size_t i)
|
|
{
|
|
CHECK_INDEX("vector::operator(): Index ",i,this->size());
|
|
return this->operator[](i-1);
|
|
}
|
|
|
|
//! \brief Index-1 based element reference.
|
|
const T& operator()(size_t i) const
|
|
{
|
|
CHECK_INDEX("vector::operator(): Index ",i,this->size());
|
|
return this->operator[](i-1);
|
|
}
|
|
|
|
//! \brief Fill the vector with a scalar value.
|
|
void fill(const T& s) { std::fill(this->begin(),this->end(),s); }
|
|
//! \brief Fill the vector with data from an array.
|
|
void fill(const T* values, size_t n = 0)
|
|
{
|
|
if (n > this->size()) this->std::vector<T>::resize(n);
|
|
memcpy(&this->front(),values,this->size()*sizeof(T));
|
|
}
|
|
|
|
//! \brief Multiplication with a scalar.
|
|
vector<T>& operator*=(const T& c);
|
|
//! \brief Division by a scalar.
|
|
vector<T>& operator/=(const T& d) { return this->operator*=(T(1)/d); }
|
|
|
|
//! \brief Add the given vector \b X to \a *this.
|
|
vector<T>& operator+=(const vector<T>& X) { return this->add(X); }
|
|
//! \brief Subtract the given vector \b X from \a *this.
|
|
vector<T>& operator-=(const vector<T>& X) { return this->add(X,T(-1)); }
|
|
//! \brief Add the given vector \b X scaled by \a alfa to \a *this.
|
|
vector<T>& add(const std::vector<T>& X, T alfa = T(1));
|
|
|
|
//! \brief Dot product between \a *this and another vector.
|
|
//! \param[in] v The vector to dot this vector with
|
|
//! \param[in] off1 Offset for this vector
|
|
//! \param[in] inc1 Increment for this vector
|
|
//! \param[in] off2 Offset for vector \b v
|
|
//! \param[in] inc2 Increment for vector \b v
|
|
T dot(const std::vector<T>& v,
|
|
size_t off1 = 0, int inc1 = 1,
|
|
size_t off2 = 0, int inc2 = 1) const;
|
|
|
|
//! \brief Return the Euclidean norm of the vector.
|
|
//! \param[in] off Index offset relative to the first vector component
|
|
//! \param[in] inc Increment in the vector component indices
|
|
T norm2(size_t off = 0, int inc = 1) const;
|
|
//! \brief Return the infinite norm of the vector.
|
|
//! \param off Index offset relative to the first vector component on input,
|
|
//! 1-based index of the largest vector component on output
|
|
//! \param[in] inc Increment in the vector component indices
|
|
T normInf(size_t& off, int inc = 1) const;
|
|
|
|
//! \brief Return the sum of the absolute value of the vector elements.
|
|
//! \param[in] off Index offset relative to the first vector component
|
|
//! \param[in] inc Increment in the vector component indices
|
|
T asum(size_t off = 0, int inc = 1) const;
|
|
|
|
//! \brief Return the sum of the vector elements.
|
|
//! \param[in] off Index offset relative to the first vector component
|
|
//! \param[in] inc Increment in the vector component indices
|
|
T sum(size_t off = 0, int inc = 1) const
|
|
{
|
|
T xsum = T(0);
|
|
if (inc < 1) return xsum;
|
|
|
|
const T* v = this->ptr();
|
|
for (size_t i = off; i < this->size(); i += inc)
|
|
xsum += v[i];
|
|
return xsum;
|
|
}
|
|
|
|
//! \brief Resize the vector to length \a n.
|
|
//! \details Will erase the previous content, but only if the size changed,
|
|
//! unless \a forceClear is \e true.
|
|
void resize(size_t n, bool forceClear = false)
|
|
{
|
|
if (forceClear)
|
|
{
|
|
// Erase previous content
|
|
if (n == this->size())
|
|
this->fill(T(0));
|
|
else
|
|
this->clear();
|
|
}
|
|
|
|
if (n == this->size()) return; // nothing to do
|
|
|
|
if (!forceClear) this->clear();
|
|
|
|
this->std::vector<T>::resize(n,T(0));
|
|
}
|
|
};
|
|
|
|
|
|
/*!
|
|
\brief Two-dimensional rectangular matrix with some algebraic operations.
|
|
\details This is a 2D equivalent to the \a vector class. The matrix elements
|
|
are stored column-wise in a one-dimensional array, such that its pointer
|
|
might be passed to Fortran subroutines requiring 2D arrays as arguments.
|
|
*/
|
|
|
|
template<class T> class matrix
|
|
{
|
|
public:
|
|
//! \brief Constructor creating an empty matrix.
|
|
matrix() : nrow(0), ncol(0) {}
|
|
//! \brief Constructor creating a matrix of dimension \f$r \times c\f$.
|
|
matrix(size_t r, size_t c) : nrow(r), ncol(c), elem(r*c) {}
|
|
|
|
//! \brief Resize the matrix to dimension \f$r \times c\f$.
|
|
//! \details Will erase the previous content, but only if both
|
|
//! the total matrix size and the number of rows in the matrix are changed.
|
|
//! It is therefore possible to add or remove a given number of columns to
|
|
//! the matrix without loosing the contents of the remaining columns.
|
|
//! If \a forceClear is \e true, the old matrix content is always erased.
|
|
void resize(size_t r, size_t c, bool forceClear = false)
|
|
{
|
|
if (forceClear)
|
|
{
|
|
// Erase previous content
|
|
if (nrow*ncol == r*c)
|
|
this->fill(T(0));
|
|
else
|
|
{
|
|
nrow = ncol = 0;
|
|
elem.clear();
|
|
}
|
|
}
|
|
|
|
if (r == nrow && c == ncol) return; // nothing to do
|
|
|
|
size_t oldNrow = nrow;
|
|
size_t oldSize = this->size();
|
|
nrow = r;
|
|
ncol = c;
|
|
if (this->size() == oldSize) return; // no more to do, size is unchanged
|
|
|
|
// If the number of rows is changed the previous content must be cleared
|
|
if (!forceClear && r != oldNrow) elem.clear();
|
|
|
|
elem.std::vector<T>::resize(r*c,T(0));
|
|
}
|
|
|
|
//! \brief Query number of matrix rows.
|
|
size_t rows() const { return nrow; }
|
|
//! \brief Query number of matrix columns.
|
|
size_t cols() const { return ncol; }
|
|
//! \brief Query total matrix size.
|
|
size_t size() const { return nrow*ncol; }
|
|
//! \brief Check if the matrix is empty.
|
|
bool empty() const { return elem.empty(); }
|
|
|
|
//! \brief Access through pointer.
|
|
T* ptr(size_t c = 0) { return &elem[nrow*c]; }
|
|
//! \brief Reference through pointer.
|
|
const T* ptr(size_t c = 0) const { return &elem[nrow*c]; }
|
|
//! \brief Type casting to a one-dimensional vector.
|
|
operator const vector<T>&() const { return elem; }
|
|
|
|
//! \brief Index-1 based element access.
|
|
//! \details Assuming column-wise storage as in Fortran.
|
|
T& operator()(size_t r, size_t c)
|
|
{
|
|
CHECK_INDEX("matrix::operator(): Row-index ",r,nrow);
|
|
CHECK_INDEX("matrix::operator(): Column-index ",c,ncol);
|
|
return elem[r-1+nrow*(c-1)];
|
|
}
|
|
|
|
//! \brief Index-1 based element reference.
|
|
//! \details Assuming column-wise storage as in Fortran.
|
|
const T& operator()(size_t r, size_t c) const
|
|
{
|
|
CHECK_INDEX("matrix::operator(): Row-index ",r,nrow);
|
|
CHECK_INDEX("matrix::operator(): Column-index ",c,ncol);
|
|
return elem[r-1+nrow*(c-1)];
|
|
}
|
|
|
|
//! \brief Extract a row from the matrix.
|
|
vector<T> getRow(size_t r) const
|
|
{
|
|
CHECK_INDEX("matrix::getRow: Row-index ",r,nrow);
|
|
vector<T> row(ncol);
|
|
for (size_t i = 0; i < ncol; i++)
|
|
row[i] = elem[r-1+nrow*i];
|
|
return row;
|
|
}
|
|
|
|
//! \brief Extract a column from the matrix.
|
|
vector<T> getColumn(size_t c) const
|
|
{
|
|
CHECK_INDEX("matrix::getColumn: Column-index ",c,ncol);
|
|
vector<T> col(nrow);
|
|
memcpy(col.ptr(),this->ptr(c-1),nrow*sizeof(T));
|
|
return col;
|
|
}
|
|
|
|
//! \brief Fill a column of the matrix.
|
|
void fillColumn(size_t c, const std::vector<T>& data)
|
|
{
|
|
CHECK_INDEX("matrix::getColumn: Column-index ",c,ncol);
|
|
size_t ndata = nrow > data.size() ? data.size() : nrow;
|
|
memcpy(this->ptr(c-1),&data.front(),ndata*sizeof(T));
|
|
}
|
|
|
|
//! \brief Fill a column of the matrix.
|
|
void fillColumn(size_t c, const T* data)
|
|
{
|
|
CHECK_INDEX("matrix::fillColumn: Column-index ",c,ncol);
|
|
memcpy(this->ptr(c-1),data,nrow*sizeof(T));
|
|
}
|
|
|
|
//! \brief Fill the matrix with a scalar value.
|
|
void fill(const T& s) { std::fill(elem.begin(),elem.end(),s); }
|
|
//! \brief Fill the matrix with data from an array.
|
|
void fill(const T* values, size_t n = 0) { elem.fill(values,n); }
|
|
|
|
//! \brief Create a diagonal matrix.
|
|
matrix<T>& diag(const T& d, size_t dim = 0)
|
|
{
|
|
if (dim > 0)
|
|
this->resize(dim,dim,true);
|
|
else
|
|
this->resize(nrow,ncol,true);
|
|
for (size_t r = 0; r < nrow && r < ncol; r++)
|
|
elem[r+nrow*r] = d;
|
|
return *this;
|
|
}
|
|
|
|
//! \brief Replace the current matrix by its transpose.
|
|
matrix<T>& transpose()
|
|
{
|
|
matrix<T> tmp(*this);
|
|
for (size_t r = 0; r < nrow; r++)
|
|
for (size_t c = 0; c < ncol; c++)
|
|
elem[c+ncol*r] = tmp.elem[r+nrow*c];
|
|
|
|
nrow = tmp.ncol;
|
|
ncol = tmp.nrow;
|
|
return *this;
|
|
}
|
|
|
|
#define THIS(i,j) this->operator()(i,j)
|
|
|
|
//! \brief Compute the determinant of a square matrix.
|
|
T det() const
|
|
{
|
|
if (ncol == 1 && nrow >= 1)
|
|
return THIS(1,1);
|
|
else if (ncol == 2 && nrow >= 2)
|
|
return THIS(1,1)*THIS(2,2) - THIS(2,1)*THIS(1,2);
|
|
else if (ncol == 3 && nrow >= 3)
|
|
return THIS(1,1)*(THIS(2,2)*THIS(3,3) - THIS(3,2)*THIS(2,3))
|
|
- THIS(1,2)*(THIS(2,1)*THIS(3,3) - THIS(3,1)*THIS(2,3))
|
|
+ THIS(1,3)*(THIS(2,1)*THIS(3,2) - THIS(3,1)*THIS(2,2));
|
|
else if (ncol > 0 && nrow > 0) {
|
|
std::cerr <<"matrix::det: Not available for "
|
|
<< nrow <<"x"<< ncol <<" matrices"<< std::endl;
|
|
ABORT_ON_SINGULARITY;
|
|
return T(-999);
|
|
}
|
|
else
|
|
return T(0);
|
|
}
|
|
|
|
//! \brief Compute the inverse of a square matrix.
|
|
//! \param[in] tol Division by zero tolerance
|
|
//! \return Determinant of the matrix
|
|
T inverse(const T& tol = T(0))
|
|
{
|
|
T det = this->det();
|
|
if (det == T(-999))
|
|
return det;
|
|
else if (det <= tol && det >= -tol) {
|
|
std::cerr <<"matrix::inverse: Singular matrix |A|="<< det << std::endl;
|
|
ABORT_ON_SINGULARITY;
|
|
return T(0);
|
|
}
|
|
|
|
if (ncol == 1)
|
|
THIS(1,1) = T(1) / det;
|
|
else if (ncol == 2) {
|
|
matrix<T> B(2,2);
|
|
B(1,1) = THIS(2,2) / det;
|
|
B(2,1) = -THIS(2,1) / det;
|
|
B(1,2) = -THIS(1,2) / det;
|
|
B(2,2) = THIS(1,1) / det;
|
|
*this = B;
|
|
}
|
|
else if (ncol == 3) {
|
|
matrix<T> B(3,3);
|
|
B(1,1) = (THIS(2,2)*THIS(3,3) - THIS(3,2)*THIS(2,3)) / det;
|
|
B(2,1) = -(THIS(2,1)*THIS(3,3) - THIS(3,1)*THIS(2,3)) / det;
|
|
B(3,1) = (THIS(2,1)*THIS(3,2) - THIS(3,1)*THIS(2,2)) / det;
|
|
B(1,2) = -(THIS(1,2)*THIS(3,3) - THIS(3,2)*THIS(1,3)) / det;
|
|
B(2,2) = (THIS(1,1)*THIS(3,3) - THIS(3,1)*THIS(1,3)) / det;
|
|
B(3,2) = -(THIS(1,1)*THIS(3,2) - THIS(3,1)*THIS(1,2)) / det;
|
|
B(1,3) = (THIS(1,2)*THIS(2,3) - THIS(2,2)*THIS(1,3)) / det;
|
|
B(2,3) = -(THIS(1,1)*THIS(2,3) - THIS(2,1)*THIS(1,3)) / det;
|
|
B(3,3) = (THIS(1,1)*THIS(2,2) - THIS(2,1)*THIS(1,2)) / det;
|
|
*this = B;
|
|
}
|
|
|
|
return det;
|
|
}
|
|
|
|
//! \brief Check for symmetry.
|
|
//! \param[in] tol Comparison tolerance
|
|
bool isSymmetric(const T& tol = T(0)) const
|
|
{
|
|
if (nrow != ncol) return false;
|
|
|
|
for (size_t r = 0; r < nrow; r++)
|
|
for (size_t c = 0; c < r; c++)
|
|
{
|
|
T diff = elem[r+nrow*c] - elem[c+nrow*r];
|
|
if (diff < -tol || diff > tol) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//! \brief Add the given matrix \b A to \a *this.
|
|
matrix<T>& operator+=(const matrix<T>& A) { return this->add(A); }
|
|
//! \brief Subtract the given matrix \b A from \a *this.
|
|
matrix<T>& operator-=(const matrix<T>& A) { return this->add(A,T(-1)); }
|
|
//! \brief Add the given matrix \b A scaled by \a alfa to \a *this.
|
|
matrix<T>& add(const matrix<T>& A, T alfa = T(1));
|
|
|
|
//! \brief Multiplication with a scalar.
|
|
matrix<T>& operator*=(const T& c) { return this->multiply(c); }
|
|
//! \brief Division by a scalar.
|
|
matrix<T>& operator/=(const T& d) { return this->multiply(T(1)/d); }
|
|
//! \brief Multiplication of this matrix by a scalar \a c.
|
|
matrix<T>& multiply(const T& c);
|
|
|
|
/*! \brief Matrix-matrix multiplication.
|
|
\details Performs one of the following operations (\b C = \a *this):
|
|
-# \f$ {\bf C} = {\bf A} {\bf B} \f$
|
|
-# \f$ {\bf C} = {\bf A}^T {\bf B} \f$
|
|
-# \f$ {\bf C} = {\bf A} {\bf B}^T \f$
|
|
-# \f$ {\bf C} = {\bf A}^T {\bf B}^T \f$
|
|
-# \f$ {\bf C} = {\bf C} + {\bf A} {\bf B} \f$
|
|
-# \f$ {\bf C} = {\bf C} + {\bf A}^T {\bf B} \f$
|
|
-# \f$ {\bf C} = {\bf C} + {\bf A} {\bf B}^T \f$
|
|
-# \f$ {\bf C} = {\bf C} + {\bf A}^T {\bf B}^T \f$
|
|
*/
|
|
matrix<T>& multiply(const matrix<T>& A, const matrix<T>& B,
|
|
bool transA = false, bool transB = false,
|
|
bool addTo = false);
|
|
|
|
/*! \brief Matrix-matrix multiplication.
|
|
\details Performs one of the following operations (\b C = \a *this):
|
|
-# \f$ {\bf C} = {\bf A} {\bf B} \f$
|
|
-# \f$ {\bf C} = {\bf A}^T {\bf B} \f$
|
|
-# \f$ {\bf C} = {\bf C} + {\bf A} {\bf B} \f$
|
|
-# \f$ {\bf C} = {\bf C} + {\bf A}^T {\bf B} \f$
|
|
|
|
The matrix \b B is here represented by a one-dimensional vector,
|
|
and its number of rows is assumed to match the number of columns in
|
|
\b A (or its transpose) and its number of columns is then the total
|
|
vector length divided by the number of rows.
|
|
*/
|
|
bool multiplyMat(const matrix<T>& A, const std::vector<T>& B,
|
|
bool transA = false, bool addTo = false);
|
|
|
|
/*! \brief Matrix-matrix multiplication.
|
|
\details Performs one of the following operations (\b C = \a *this):
|
|
-# \f$ {\bf C} = {\bf A} {\bf B} \f$
|
|
-# \f$ {\bf C} = {\bf A} {\bf B}^T \f$
|
|
-# \f$ {\bf C} = {\bf C} + {\bf A} {\bf B} \f$
|
|
-# \f$ {\bf C} = {\bf C} + {\bf A} {\bf B}^T \f$
|
|
|
|
The matrix \b A is here represented by a one-dimensional vector,
|
|
and its number of columns is assumed to match the number of rows in
|
|
\b B (or its transpose) and its number of rows is then the total
|
|
vector length divided by the number of columns.
|
|
*/
|
|
bool multiplyMat(const std::vector<T>& A, const matrix<T>& B,
|
|
bool transB = false, bool addTo = false);
|
|
|
|
/*! \brief Matrix-vector multiplication.
|
|
\details Performs one of the following operations (\b A = \a *this):
|
|
-# \f$ {\bf Y} = {\bf A} {\bf X} \f$
|
|
-# \f$ {\bf Y} = {\bf A}^T {\bf X} \f$
|
|
-# \f$ {\bf Y} = {\bf Y} + {\bf A} {\bf X} \f$
|
|
-# \f$ {\bf Y} = {\bf Y} + {\bf A}^T {\bf X} \f$
|
|
*/
|
|
bool multiply(const std::vector<T>& X, std::vector<T>& Y,
|
|
bool transA = false, bool addTo = false) const;
|
|
|
|
//! \brief Outer product between two vectors.
|
|
bool outer_product(const std::vector<T>& X, const std::vector<T>& Y,
|
|
bool addTo = false)
|
|
{
|
|
if (!addTo)
|
|
this->resize(X.size(),Y.size());
|
|
else if (X.size() != nrow || Y.size() != ncol)
|
|
{
|
|
std::cerr <<"matrix::outer_product: Incompatible matrix and vectors: A("
|
|
<< nrow <<','<< ncol <<"), X("
|
|
<< X.size() <<"), Y("<< Y.size() <<")\n"
|
|
<<" when computing A += X*Y^t"
|
|
<< std::endl;
|
|
ABORT_ON_INDEX_CHECK;
|
|
return false;
|
|
}
|
|
|
|
if (addTo)
|
|
for (size_t j = 0; j < ncol; j++)
|
|
for (size_t i = 0; i < nrow; i++)
|
|
elem[i+nrow*j] += X[i]*Y[j];
|
|
else
|
|
for (size_t j = 0; j < ncol; j++)
|
|
for (size_t i = 0; i < nrow; i++)
|
|
elem[i+nrow*j] = X[i]*Y[j];
|
|
|
|
return true;
|
|
}
|
|
|
|
//! \brief Return the infinite norm of the matrix.
|
|
T normInf() const
|
|
{
|
|
if (nrow == 0) return T(0);
|
|
|
|
// Compute row sums
|
|
vector<T> sums(nrow);
|
|
for (size_t i = 0; i < nrow; i++)
|
|
sums[i] = elem.asum(i,ncol);
|
|
return *std::max_element(sums.begin(),sums.end());
|
|
}
|
|
|
|
private:
|
|
//! \brief Check dimension compatibility for matrix-vector multiplication.
|
|
bool compatible(const std::vector<T>& X, const std::vector<T>& Y,
|
|
bool transA) const
|
|
{
|
|
if ((transA ? nrow : ncol) == X.size()) return true;
|
|
|
|
std::cerr <<"matrix::multiply: Incompatible matrices: A("
|
|
<< nrow <<','<< ncol <<"), X("<< X.size() <<")\n"
|
|
<<" when computing Y = "
|
|
<< (transA ? "A^t":"A") <<" * X"<< std::endl;
|
|
ABORT_ON_INDEX_CHECK;
|
|
return false;
|
|
}
|
|
|
|
//! \brief Check dimension compatibility for matrix-matrix multiplication.
|
|
bool compatible(const matrix<T>& A, const matrix<T>& B,
|
|
bool transA, bool transB, size_t& M, size_t& N, size_t& K)
|
|
{
|
|
M = transA ? A.ncol : A.nrow;
|
|
N = transB ? B.nrow : B.ncol;
|
|
K = transA ? A.nrow : A.ncol;
|
|
if (K == (transB ? B.ncol : B.nrow)) return true;
|
|
|
|
std::cerr <<"matrix::multiply: Incompatible matrices: A("
|
|
<< A.nrow <<','<< A.ncol <<"), B("
|
|
<< B.nrow <<','<< B.ncol <<")\n"
|
|
<<" when computing C = "
|
|
<< (transA ? "A^t":"A") <<" * "
|
|
<< (transB ? "B^t":"B") << std::endl;
|
|
ABORT_ON_INDEX_CHECK;
|
|
return false;
|
|
}
|
|
|
|
//! \brief Check dimension compatibility for matrix-matrix multiplication,
|
|
//! when the matrix B is represented by a one-dimensional vector.
|
|
bool compatible(const matrix<T>& A, const std::vector<T>& B,
|
|
bool transA, size_t& M, size_t& N, size_t& K)
|
|
{
|
|
M = transA ? A.ncol : A.nrow;
|
|
K = transA ? A.nrow : A.ncol;
|
|
N = K > 0 ? B.size()/K : 0;
|
|
if (N*K == B.size() && !B.empty()) return true;
|
|
|
|
std::cerr <<"matrix::multiply: Incompatible matrices: A("
|
|
<< A.nrow <<','<< A.ncol <<"), B(r*c="<< B.size() <<")\n"
|
|
<<" when computing C = "
|
|
<< (transA ? "A^t":"A") <<" * B"<< std::endl;
|
|
ABORT_ON_INDEX_CHECK;
|
|
return false;
|
|
}
|
|
|
|
//! \brief Check dimension compatibility for matrix-matrix multiplication,
|
|
//! when the matrix A is represented by a one-dimensional vector.
|
|
bool compatible(const std::vector<T>& A, const matrix<T>& B,
|
|
bool transB, size_t& M, size_t& N, size_t& K)
|
|
{
|
|
N = transB ? B.nrow : B.ncol;
|
|
K = transB ? B.ncol : B.nrow;
|
|
M = K > 0 ? A.size() / K : 0;
|
|
if (M*K == A.size() && !A.empty()) return true;
|
|
|
|
std::cerr <<"matrix::multiply: Incompatible matrices: A(r*c="<< A.size()
|
|
<<"), B("<< B.nrow <<","<< B.ncol <<")\n"
|
|
<<" when computing C = A * "
|
|
<< (transB ? "B^t":"B") << std::endl;
|
|
ABORT_ON_INDEX_CHECK;
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
size_t nrow; //!< Number of matrix rows
|
|
size_t ncol; //!< Number of matrix columns
|
|
vector<T> elem; //!< Actual matrix elements, stored column by column
|
|
};
|
|
|
|
|
|
/*!
|
|
\brief Three-dimensional rectangular matrix with some algebraic operations.
|
|
\details This is a 3D equivalent to the \a matrix class. The matrix elements
|
|
are stored column-wise in a one-dimensional array, such that its pointer
|
|
might be passed to Fortran subroutines requiring 3D arrays as arguments.
|
|
*/
|
|
|
|
template<class T> class matrix3d
|
|
{
|
|
public:
|
|
//! \brief Constructor creating an empty matrix.
|
|
matrix3d() { n[0] = n[1] = n[2] = 0; }
|
|
//! \brief Constructor creating a matrix of dimension
|
|
// \f$n_1 \times n_2 \times n_3\f$.
|
|
matrix3d(size_t n_1, size_t n_2, size_t n_3)
|
|
{
|
|
n[0] = n_1; n[1] = n_2; n[2] = n_3; elem.resize(n_1*n_2*n_3);
|
|
}
|
|
|
|
//! \brief Resize the matrix to dimension \f$n_1 \times n_2 \times n_3\f$.
|
|
//! \details Will erase the previous content, but only if both
|
|
//! the total matrix size, and n_1 or n_2 in the matrix are changed.
|
|
//! It is therefore possible to add or remove a given number of elements in
|
|
//! the third dimension of the matrix without loosing the contents of the
|
|
//! first and second dimensions.
|
|
//! If \a forceClear is \e true, the old matrix content is always erased.
|
|
void resize(size_t n_1, size_t n_2, size_t n_3, bool forceClear = false)
|
|
{
|
|
if (forceClear)
|
|
{
|
|
// Erase previous content
|
|
if (n[0]*n[1]*n[2] == n_1*n_2*n_3)
|
|
this->fill(T(0));
|
|
else
|
|
{
|
|
n[0] = n[1] = n[2] = 0;
|
|
elem.clear();
|
|
}
|
|
}
|
|
|
|
if (n[0] == n_1 && n[1] == n_2 && n[2] == n_3) return; // nothing to do
|
|
|
|
size_t oldn1 = n[0];
|
|
size_t oldn2 = n[1];
|
|
size_t oldSize = this->size();
|
|
n[0] = n_1;
|
|
n[1] = n_2;
|
|
n[2] = n_3;
|
|
if (this->size() == oldSize) return; // no more to do, size is unchanged
|
|
|
|
// If the size in any of the first two dimensions are changed
|
|
// the previous content must be cleared
|
|
if (!forceClear && (n[0] != oldn1 || n[1] != oldn2)) elem.clear();
|
|
|
|
elem.std::vector<T>::resize(n[0]*n[1]*n[2],T(0));
|
|
}
|
|
|
|
//! \brief Query dimensions.
|
|
size_t dim(short int d = 1) const { return d > 0 && d <= 3 ? n[d-1] : 0; }
|
|
//! \brief Query total matrix size.
|
|
size_t size() const { return n[0]*n[1]*n[2]; }
|
|
//! \brief Check if the matrix is empty.
|
|
bool empty() const { return elem.empty(); }
|
|
|
|
//! \brief Type casting to a one-dimensional vector.
|
|
operator const vector<T>&() const { return elem; }
|
|
|
|
//! \brief Index-1 based element access.
|
|
//! \details Assuming column-wise storage as in Fortran.
|
|
T& operator()(size_t i1, size_t i2, size_t i3)
|
|
{
|
|
CHECK_INDEX("matrix3d::operator(): First index " ,i1,n[0]);
|
|
CHECK_INDEX("matrix3d::operator(): Second index ",i2,n[1]);
|
|
CHECK_INDEX("matrix3d::operator(): Third index " ,i3,n[2]);
|
|
return elem[i1-1 + n[0]*((i2-1) + n[1]*(i3-1))];
|
|
}
|
|
|
|
//! \brief Index-1 based element access.
|
|
//! \details Assuming column-wise storage as in Fortran.
|
|
const T& operator()(size_t i1, size_t i2, size_t i3) const
|
|
{
|
|
CHECK_INDEX("matrix3d::operator(): First index " ,i1,n[0]);
|
|
CHECK_INDEX("matrix3d::operator(): Second index ",i2,n[1]);
|
|
CHECK_INDEX("matrix3d::operator(): Third index " ,i3,n[2]);
|
|
return elem[i1-1 + n[0]*((i2-1) + n[1]*(i3-1))];
|
|
}
|
|
|
|
//! \brief Access through pointer.
|
|
T* ptr() { return &elem.front(); }
|
|
//! \brief Reference through pointer.
|
|
const T* ptr() const { return &elem.front(); }
|
|
|
|
//! \brief Fill the matrix with a scalar value.
|
|
void fill(const T& s) { std::fill(elem.begin(),elem.end(),s); }
|
|
|
|
/*! \brief Matrix-matrix multiplication.
|
|
\details Performs one of the following operations (\b C = \a *this):
|
|
-# \f$ {\bf C} = {\bf A} {\bf B} \f$
|
|
-# \f$ {\bf C} = {\bf A}^T {\bf B} \f$
|
|
-# \f$ {\bf C} = {\bf C} + {\bf A} {\bf B} \f$
|
|
-# \f$ {\bf C} = {\bf C} + {\bf A}^T {\bf B} \f$
|
|
*/
|
|
bool multiply(const matrix<T>& A, const matrix3d<T>& B,
|
|
bool transA = false, bool addTo = false);
|
|
|
|
private:
|
|
//! \brief Check dimension compatibility for matrix-matrix multiplication.
|
|
bool compatible(const matrix<T>& A, const matrix3d<T>& B,
|
|
bool transA, size_t& M, size_t& N, size_t& K)
|
|
{
|
|
M = transA ? A.cols() : A.rows();
|
|
N = B.n[1]*B.n[2];
|
|
K = transA ? A.rows() : A.cols();
|
|
if (K == B.n[0]) return true;
|
|
|
|
std::cerr <<"matrix3d::multiply: Incompatible matrices: A("
|
|
<< A.rows() <<','<< A.cols() <<"), B("
|
|
<< B.n[0] <<','<< B.n[1] <<','<< B.n[2] <<")\n"
|
|
<<" when computing C = "
|
|
<< (transA ? "A^t":"A") <<" * B"<< std::endl;
|
|
ABORT_ON_INDEX_CHECK;
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
size_t n[3]; //!< Dimension of the 3D matrix
|
|
vector<T> elem; //!< Actual matrix elements, stored column by column
|
|
};
|
|
|
|
|
|
#ifdef USE_CBLAS
|
|
//============================================================================
|
|
//=== BLAS-implementation of the matrix/vector multiplication methods ====
|
|
//============================================================================
|
|
|
|
template<> inline
|
|
vector<float>& vector<float>::operator*=(const float& c)
|
|
{
|
|
cblas_sscal(this->size(),c,this->ptr(),1);
|
|
return *this;
|
|
}
|
|
|
|
template<> inline
|
|
vector<double>& vector<double>::operator*=(const double& c)
|
|
{
|
|
cblas_dscal(this->size(),c,this->ptr(),1);
|
|
return *this;
|
|
}
|
|
|
|
template<> inline
|
|
float vector<float>::dot(const std::vector<float>& v,
|
|
size_t o1, int i1, size_t o2, int i2) const
|
|
{
|
|
int n1 = i1 > 1 || i1 < -1 ? this->size()/abs(i1) : this->size()-o1;
|
|
int n2 = i2 > 1 || i2 < -1 ? v.size()/abs(i2) : v.size()-o2;
|
|
int n = n1 < n2 ? n1 : n2;
|
|
return cblas_sdot(n,this->ptr()+o1,i1,&v.front()+o2,i2);
|
|
}
|
|
|
|
template<> inline
|
|
double vector<double>::dot(const std::vector<double>& v,
|
|
size_t o1, int i1, size_t o2, int i2) const
|
|
{
|
|
int n1 = i1 > 1 || i1 < -1 ? this->size()/abs(i1) : this->size()-o1;
|
|
int n2 = i2 > 1 || i2 < -1 ? v.size()/abs(i2) : v.size()-o2;
|
|
int n = n1 < n2 ? n1 : n2;
|
|
return cblas_ddot(n,this->ptr()+o1,i1,&v.front()+o2,i2);
|
|
}
|
|
|
|
template<> inline
|
|
float vector<float>::norm2(size_t off, int inc) const
|
|
{
|
|
int n = inc > 1 || inc < -1 ? this->size()/abs(inc) : this->size()-off;
|
|
return cblas_snrm2(n,this->ptr()+off,inc);
|
|
}
|
|
|
|
template<> inline
|
|
double vector<double>::norm2(size_t off, int inc) const
|
|
{
|
|
int n = inc > 1 || inc < -1 ? this->size()/abs(inc) : this->size()-off;
|
|
return cblas_dnrm2(n,this->ptr()+off,inc);
|
|
}
|
|
|
|
template<> inline
|
|
float vector<float>::normInf(size_t& off, int inc) const
|
|
{
|
|
if (inc < 1) return 0.0f;
|
|
|
|
const float* v = this->ptr() + off;
|
|
off = 1 + cblas_isamax(this->size()/inc,v,inc);
|
|
return fabsf(v[(off-1)*inc]);
|
|
}
|
|
|
|
template<> inline
|
|
double vector<double>::normInf(size_t& off, int inc) const
|
|
{
|
|
if (inc < 1) return 0.0;
|
|
|
|
const double* v = this->ptr() + off;
|
|
off = 1 + cblas_idamax(this->size()/inc,v,inc);
|
|
return fabs(v[(off-1)*inc]);
|
|
}
|
|
|
|
template<> inline
|
|
float vector<float>::asum(size_t off, int inc) const
|
|
{
|
|
int n = inc > 1 || inc < -1 ? this->size()/abs(inc) : this->size()-off;
|
|
return cblas_sasum(n,this->ptr()+off,inc);
|
|
}
|
|
|
|
template<> inline
|
|
double vector<double>::asum(size_t off, int inc) const
|
|
{
|
|
int n = inc > 1 || inc < -1 ? this->size()/abs(inc) : this->size()-off;
|
|
return cblas_dasum(n,this->ptr()+off,inc);
|
|
}
|
|
|
|
template<> inline
|
|
vector<float>& vector<float>::add(const std::vector<float>& X, float alfa)
|
|
{
|
|
size_t n = this->size() < X.size() ? this->size() : X.size();
|
|
cblas_saxpy(n,alfa,&X.front(),1,this->ptr(),1);
|
|
return *this;
|
|
}
|
|
|
|
template<> inline
|
|
vector<double>& vector<double>::add(const std::vector<double>& X, double alfa)
|
|
{
|
|
size_t n = this->size() < X.size() ? this->size() : X.size();
|
|
cblas_daxpy(n,alfa,&X.front(),1,this->ptr(),1);
|
|
return *this;
|
|
}
|
|
|
|
template<> inline
|
|
matrix<float>& matrix<float>::add(const matrix<float>& X, float alfa)
|
|
{
|
|
size_t n = this->size() < X.size() ? this->size() : X.size();
|
|
cblas_saxpy(n,alfa,X.ptr(),1,this->ptr(),1);
|
|
return *this;
|
|
}
|
|
|
|
template<> inline
|
|
matrix<double>& matrix<double>::add(const matrix<double>& X, double alfa)
|
|
{
|
|
size_t n = this->size() < X.size() ? this->size() : X.size();
|
|
cblas_daxpy(n,alfa,X.ptr(),1,this->ptr(),1);
|
|
return *this;
|
|
}
|
|
|
|
template<> inline
|
|
matrix<float>& matrix<float>::multiply(const float& c)
|
|
{
|
|
cblas_sscal(this->size(),c,this->ptr(),1);
|
|
return *this;
|
|
}
|
|
|
|
template<> inline
|
|
matrix<double>& matrix<double>::multiply(const double& c)
|
|
{
|
|
cblas_dscal(this->size(),c,this->ptr(),1);
|
|
return *this;
|
|
}
|
|
|
|
template<> inline
|
|
bool matrix<float>::multiply(const std::vector<float>& X,
|
|
std::vector<float>& Y,
|
|
bool transA, bool addTo) const
|
|
{
|
|
if (!this->compatible(X,Y,transA)) return false;
|
|
if (!addTo) Y.resize(transA ? ncol : nrow);
|
|
|
|
cblas_sgemv(CblasColMajor,
|
|
transA ? CblasTrans : CblasNoTrans,
|
|
nrow, ncol, 1.0f,
|
|
this->ptr(), nrow,
|
|
&X.front(), 1, addTo ? 1.0f : 0.0f,
|
|
&Y.front(), 1);
|
|
|
|
return true;
|
|
}
|
|
|
|
template<> inline
|
|
bool matrix<double>::multiply(const std::vector<double>& X,
|
|
std::vector<double>& Y,
|
|
bool transA, bool addTo) const
|
|
{
|
|
if (!this->compatible(X,Y,transA)) return false;
|
|
if (!addTo) Y.resize(transA ? ncol : nrow);
|
|
|
|
cblas_dgemv(CblasColMajor,
|
|
transA ? CblasTrans : CblasNoTrans,
|
|
nrow, ncol, 1.0,
|
|
this->ptr(), nrow,
|
|
&X.front(), 1, addTo ? 1.0 : 0.0,
|
|
&Y.front(), 1);
|
|
|
|
return true;
|
|
}
|
|
|
|
template<> inline
|
|
matrix<float>& matrix<float>::multiply(const matrix<float>& A,
|
|
const matrix<float>& B,
|
|
bool transA, bool transB, bool addTo)
|
|
{
|
|
size_t M, N, K;
|
|
if (!this->compatible(A,B,transA,transB,M,N,K)) return *this;
|
|
if (!addTo) this->resize(M,N);
|
|
|
|
cblas_sgemm(CblasColMajor,
|
|
transA ? CblasTrans : CblasNoTrans,
|
|
transB ? CblasTrans : CblasNoTrans,
|
|
M, N, K, 1.0f,
|
|
A.ptr(), A.nrow,
|
|
B.ptr(), B.nrow,
|
|
addTo ? 1.0f : 0.0f,
|
|
this->ptr(), nrow);
|
|
|
|
return *this;
|
|
}
|
|
|
|
template<> inline
|
|
matrix<double>& matrix<double>::multiply(const matrix<double>& A,
|
|
const matrix<double>& B,
|
|
bool transA, bool transB, bool addTo)
|
|
{
|
|
size_t M, N, K;
|
|
if (!this->compatible(A,B,transA,transB,M,N,K)) return *this;
|
|
if (!addTo) this->resize(M,N);
|
|
|
|
cblas_dgemm(CblasColMajor,
|
|
transA ? CblasTrans : CblasNoTrans,
|
|
transB ? CblasTrans : CblasNoTrans,
|
|
M, N, K, 1.0,
|
|
A.ptr(), A.nrow,
|
|
B.ptr(), B.nrow,
|
|
addTo ? 1.0 : 0.0,
|
|
this->ptr(), nrow);
|
|
|
|
return *this;
|
|
}
|
|
|
|
template<> inline
|
|
bool matrix<float>::multiplyMat(const matrix<float>& A,
|
|
const std::vector<float>& B,
|
|
bool transA, bool addTo)
|
|
{
|
|
size_t M, N, K;
|
|
if (!this->compatible(A,B,transA,M,N,K)) return false;
|
|
if (!addTo) this->resize(M,N);
|
|
|
|
cblas_sgemm(CblasColMajor,
|
|
transA ? CblasTrans : CblasNoTrans, CblasNoTrans,
|
|
M, N, K, 1.0f,
|
|
A.ptr(), A.nrow,
|
|
&B.front(), K,
|
|
addTo ? 1.0f : 0.0f,
|
|
this->ptr(), nrow);
|
|
|
|
return true;
|
|
}
|
|
|
|
template<> inline
|
|
bool matrix<double>::multiplyMat(const matrix<double>& A,
|
|
const std::vector<double>& B,
|
|
bool transA, bool addTo)
|
|
{
|
|
size_t M, N, K;
|
|
if (!this->compatible(A,B,transA,M,N,K)) return false;
|
|
if (!addTo) this->resize(M,N);
|
|
|
|
cblas_dgemm(CblasColMajor,
|
|
transA ? CblasTrans : CblasNoTrans, CblasNoTrans,
|
|
M, N, K, 1.0,
|
|
A.ptr(), A.nrow,
|
|
&B.front(), K,
|
|
addTo ? 1.0 : 0.0,
|
|
this->ptr(), nrow);
|
|
|
|
return true;
|
|
}
|
|
|
|
template<> inline
|
|
bool matrix<float>::multiplyMat(const std::vector<float>& A,
|
|
const matrix<float>& B,
|
|
bool transB, bool addTo)
|
|
{
|
|
size_t M, N, K;
|
|
if (!this->compatible(A,B,transB,M,N,K)) return false;
|
|
if (!addTo) this->resize(M,N);
|
|
|
|
cblas_sgemm(CblasColMajor,
|
|
CblasNoTrans, transB ? CblasTrans : CblasNoTrans,
|
|
M, N, K, 1.0f,
|
|
&A.front(), M,
|
|
B.ptr(), B.nrow,
|
|
addTo ? 1.0f : 0.0f,
|
|
this->ptr(), nrow);
|
|
|
|
return true;
|
|
}
|
|
|
|
template<> inline
|
|
bool matrix<double>::multiplyMat(const std::vector<double>& A,
|
|
const matrix<double>& B,
|
|
bool transB, bool addTo)
|
|
{
|
|
size_t M, N, K;
|
|
if (!this->compatible(A,B,transB,M,N,K)) return false;
|
|
if (!addTo) this->resize(M,N);
|
|
|
|
cblas_dgemm(CblasColMajor,
|
|
CblasNoTrans, transB ? CblasTrans : CblasNoTrans,
|
|
M, N, K, 1.0,
|
|
&A.front(), M,
|
|
B.ptr(), B.nrow,
|
|
addTo ? 1.0 : 0.0,
|
|
this->ptr(), nrow);
|
|
|
|
return true;
|
|
}
|
|
|
|
template<> inline
|
|
bool matrix3d<float>::multiply(const matrix<float>& A,
|
|
const matrix3d<float>& B,
|
|
bool transA, bool addTo)
|
|
{
|
|
size_t M, N, K;
|
|
if (!this->compatible(A,B,transA,M,N,K)) return false;
|
|
if (!addTo) this->resize(M,B.n[1],B.n[2]);
|
|
|
|
cblas_sgemm(CblasColMajor,
|
|
transA ? CblasTrans : CblasNoTrans, CblasNoTrans,
|
|
M, N, K, 1.0f,
|
|
A.ptr(), A.rows(),
|
|
B.ptr(), B.n[0],
|
|
addTo ? 1.0f : 0.0f,
|
|
this->ptr(), n[0]);
|
|
|
|
return true;
|
|
}
|
|
|
|
template<> inline
|
|
bool matrix3d<double>::multiply(const matrix<double>& A,
|
|
const matrix3d<double>& B,
|
|
bool transA, bool addTo)
|
|
{
|
|
size_t M, N, K;
|
|
if (!this->compatible(A,B,transA,M,N,K)) return false;
|
|
if (!addTo) this->resize(M,B.n[1],B.n[2]);
|
|
|
|
cblas_dgemm(CblasColMajor,
|
|
transA ? CblasTrans : CblasNoTrans, CblasNoTrans,
|
|
M, N, K, 1.0,
|
|
A.ptr(), A.rows(),
|
|
B.ptr(), B.n[0],
|
|
addTo ? 1.0 : 0.0,
|
|
this->ptr(), n[0]);
|
|
|
|
return true;
|
|
}
|
|
|
|
#else
|
|
//============================================================================
|
|
//=== Non-BLAS inlined implementations (slow...) =========================
|
|
//============================================================================
|
|
|
|
template<class T> inline
|
|
vector<T>& vector<T>::operator*=(const T& c)
|
|
{
|
|
for (size_t i = 0; i < this->size(); i++)
|
|
std::vector<T>::operator[](i) *= c;
|
|
return *this;
|
|
}
|
|
|
|
template<class T> inline
|
|
T vector<T>::dot(const std::vector<T>& v,
|
|
size_t o1, int i1, size_t o2, int i2) const
|
|
{
|
|
size_t i, j;
|
|
T dotprod = T(0);
|
|
for (i = o1, j = o2; i < this->size() && j < v.size(); i += i1, j += i2)
|
|
dotprod += this->operator[](i) * v[j];
|
|
return dotprod;
|
|
}
|
|
|
|
template<class T> inline
|
|
T vector<T>::norm2(size_t off, int inc) const
|
|
{
|
|
double xsum = 0.0;
|
|
if (inc < 1) return xsum;
|
|
|
|
// Warning: This might overflow or underflow for large/small values
|
|
const T* v = this->ptr();
|
|
for (size_t i = off; i < this->size(); i += inc)
|
|
xsum += v[i]*v[i];
|
|
return sqrt(xsum);
|
|
}
|
|
|
|
template<class T> inline
|
|
T vector<T>::normInf(size_t& off, int inc) const
|
|
{
|
|
T xmax = T(0);
|
|
if (inc < 1) return xmax;
|
|
|
|
const T* v = this->ptr();
|
|
for (size_t i = off; i < this->size(); i += inc)
|
|
if (v[i] > xmax)
|
|
{
|
|
off = 1+i/inc;
|
|
xmax = v[i];
|
|
}
|
|
else if (v[i] < -xmax)
|
|
{
|
|
off = 1+i/inc;
|
|
xmax = -v[i];
|
|
}
|
|
|
|
return xmax;
|
|
}
|
|
|
|
template<class T> inline
|
|
T vector<T>::asum(size_t off, int inc) const
|
|
{
|
|
T xsum = T(0);
|
|
if (inc < 1) return xsum;
|
|
|
|
const T* v = this->ptr();
|
|
for (size_t i = off; i < this->size(); i += inc)
|
|
xsum += v[i] < T(0) ? -v[i] : v[i];
|
|
return xsum;
|
|
}
|
|
|
|
template<class T> inline
|
|
vector<T>& vector<T>::add(const std::vector<T>& X, T alfa)
|
|
{
|
|
T* p = this->ptr();
|
|
const T* q = &X.front();
|
|
for (size_t i = 0; i < this->size() && i < X.size(); i++, p++, q++)
|
|
*p += alfa*(*q);
|
|
return *this;
|
|
}
|
|
|
|
template<class T> inline
|
|
matrix<T>& matrix<T>::add(const matrix<T>& X, T alfa)
|
|
{
|
|
T* p = this->ptr();
|
|
const T* q = X.ptr();
|
|
for (size_t i = 0; i < this->size() && i < X.size(); i++, p++, q++)
|
|
*p += alfa*(*q);
|
|
return *this;
|
|
}
|
|
|
|
template<class T> inline
|
|
matrix<T>& matrix<T>::multiply(const T& c)
|
|
{
|
|
for (size_t i = 0; i < elem.size(); i++)
|
|
elem[i] *= c;
|
|
return *this;
|
|
}
|
|
|
|
template<class T> inline
|
|
bool matrix<T>::multiply(const std::vector<T>& X, std::vector<T>& Y,
|
|
bool transA, bool addTo) const
|
|
{
|
|
if (!this->compatible(X,Y,transA)) return false;
|
|
if (!addTo)
|
|
{
|
|
Y.clear();
|
|
Y.resize(transA ? ncol : nrow, T(0));
|
|
}
|
|
|
|
for (size_t i = 0; i < Y.size(); i++)
|
|
for (size_t j = 0; j < X.size(); j++)
|
|
if (transA)
|
|
Y[i] += THIS(j+1,i+1) * X[j];
|
|
else
|
|
Y[i] += THIS(i+1,j+1) * X[j];
|
|
|
|
return true;
|
|
}
|
|
|
|
template<class T> inline
|
|
matrix<T>& matrix<T>::multiply(const matrix<T>& A,
|
|
const matrix<T>& B,
|
|
bool transA, bool transB, bool addTo)
|
|
{
|
|
size_t M, N, K;
|
|
if (!this->compatible(A,B,transA,transB,M,N,K)) return *this;
|
|
if (!addTo) this->resize(M,N,true);
|
|
|
|
for (size_t i = 1; i <= M; i++)
|
|
for (size_t j = 1; j <= N; j++)
|
|
for (size_t k = 1; k <= K; k++)
|
|
if (transA && transB)
|
|
THIS(i,j) += A(k,i)*B(j,k);
|
|
else if (transA)
|
|
THIS(i,j) += A(k,i)*B(k,j);
|
|
else if (transB)
|
|
THIS(i,j) += A(i,k)*B(j,k);
|
|
else
|
|
THIS(i,j) += A(i,k)*B(k,j);
|
|
|
|
return *this;
|
|
}
|
|
|
|
template<class T> inline
|
|
bool matrix<T>::multiplyMat(const matrix<T>& A, const std::vector<T>& B,
|
|
bool transA, bool addTo)
|
|
{
|
|
size_t M, N, K;
|
|
if (!this->compatible(A,B,transA,M,N,K)) return false;
|
|
if (!addTo) this->resize(M,N,true);
|
|
|
|
for (size_t i = 1; i <= M; i++)
|
|
for (size_t j = 1; j <= N; j++)
|
|
for (size_t k = 1; k <= K; k++)
|
|
if (transA)
|
|
THIS(i,j) += A(k,i)*B[k-1+K*(j-1)];
|
|
else
|
|
THIS(i,j) += A(i,k)*B[k-1+K*(j-1)];
|
|
|
|
return true;
|
|
}
|
|
|
|
template<class T> inline
|
|
bool matrix<T>::multiplyMat(const std::vector<T>& A, const matrix<T>& B,
|
|
bool transB, bool addTo)
|
|
{
|
|
size_t M, N, K;
|
|
if (!this->compatible(A,B,transB,M,N,K)) return false;
|
|
if (!addTo) this->resize(M,N,true);
|
|
|
|
for (size_t i = 1; i <= M; i++)
|
|
for (size_t j = 1; j <= N; j++)
|
|
for (size_t k = 1; k <= K; k++)
|
|
if (transB)
|
|
THIS(i,j) += A[i-1+M*(k-1)]*B(j,k);
|
|
else
|
|
THIS(i,j) += A[i-1+M*(k-1)]*B(k,j);
|
|
|
|
return true;
|
|
}
|
|
|
|
template<class T> inline
|
|
bool matrix3d<T>::multiply(const matrix<T>& A, const matrix3d<T>& B,
|
|
bool transA, bool addTo)
|
|
{
|
|
size_t M, N, K;
|
|
if (!this->compatible(A,B,transA,M,N,K)) return false;
|
|
if (!addTo) this->resize(M,B.n[1],B.n[2],true);
|
|
|
|
for (size_t i = 1; i <= n[0]; i++)
|
|
for (size_t j = 1; j <= n[1]; j++)
|
|
for (size_t k = 1; k <= K; k++)
|
|
for (size_t l = 1; l <= n[2]; l++)
|
|
if (transA)
|
|
this->operator()(i,j,l) += A(k,i)*B(k,j,l);
|
|
else
|
|
this->operator()(i,j,l) += A(i,k)*B(k,j,l);
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
//============================================================================
|
|
//=== Global operators ===================================================
|
|
//============================================================================
|
|
|
|
//! \brief Multiplication of a vector and a scalar.
|
|
//! \return \f$ {\bf Y} = c {\bf X} \f$
|
|
template<class T> vector<T> operator*(const vector<T>& X, const T& c)
|
|
{
|
|
vector<T> Y(X.size());
|
|
for (size_t i = 0; i < X.size(); i++)
|
|
Y[i] = X[i]*c;
|
|
return Y;
|
|
}
|
|
|
|
//! \brief Division of a vector by a scalar.
|
|
//! \return \f$ {\bf Y} = \frac{1}{d} {\bf X} \f$
|
|
template<class T> vector<T> operator/(const vector<T>& X, const T& d)
|
|
{
|
|
vector<T> Y(X.size());
|
|
T c = T(1) / d;
|
|
for (size_t i = 0; i < X.size(); i++)
|
|
Y[i] = X[i]*c;
|
|
return Y;
|
|
}
|
|
|
|
//! \brief Addition of two vectors.
|
|
//! \return \f$ {\bf Z} = {\bf X} + {\bf Y} \f$
|
|
template<class T> vector<T> operator+(const vector<T>& X, const vector<T>& Y)
|
|
{
|
|
vector<T> Z(X.size());
|
|
for (size_t i = 0; i < X.size() && i < Y.size(); i++)
|
|
Z[i] = X[i] + Y[i];
|
|
return Z;
|
|
}
|
|
|
|
//! \brief Subtraction of two vectors.
|
|
//! \return \f$ {\bf Z} = {\bf X} - {\bf Y} \f$
|
|
template<class T> vector<T> operator-(const vector<T>& X, const vector<T>& Y)
|
|
{
|
|
vector<T> Z(X.size());
|
|
for (size_t i = 0; i < X.size() && i < Y.size(); i++)
|
|
Z[i] = X[i] - Y[i];
|
|
return Z;
|
|
}
|
|
|
|
//! \brief Multiplication of a matrix and a scalar.
|
|
//! \return \f$ {\bf B} = c {\bf A} \f$
|
|
template<class T> matrix<T> operator*(const matrix<T>& A, const T& c)
|
|
{
|
|
matrix<T> B(A);
|
|
return B.multiply(c);
|
|
}
|
|
|
|
//! \brief Multiplication of a matrix and a vector.
|
|
//! \return \f$ {\bf Y} = {\bf A} {\bf X} \f$
|
|
template<class T> vector<T> operator*(const matrix<T>& A, const vector<T>& X)
|
|
{
|
|
vector<T> Y;
|
|
A.multiply(X,Y);
|
|
return Y;
|
|
}
|
|
|
|
//! \brief Multiplication of two matrices.
|
|
//! \return \f$ {\bf C} = {\bf A} {\bf B} \f$
|
|
template<class T> matrix<T> operator*(const matrix<T>& A, const matrix<T>& B)
|
|
{
|
|
matrix<T> C;
|
|
return C.multiply(A,B);
|
|
}
|
|
|
|
extern double zero_print_tol; //!< Zero tolerance for printing numbers
|
|
extern int nval_per_line; //!< Number of values to print per line
|
|
|
|
//! \brief Truncate a value to zero when it is less than a given threshold.
|
|
//! \details Used when printing matrices for easy comparison with other
|
|
//! matrices when they contain terms that are numerically zero, except for
|
|
//! some round-off noice. The value of the global variable \a zero_print_tol
|
|
//! is used as a tolerance in this method.
|
|
template<class T> inline T trunc(const T& v)
|
|
{
|
|
return v > T(zero_print_tol) || v < T(-zero_print_tol) ? v : T(0);
|
|
}
|
|
|
|
//! \brief Print the vector \b X to the stream \a s.
|
|
template<class T> std::ostream& operator<<(std::ostream& s,
|
|
const vector<T>& X)
|
|
{
|
|
if (X.size() < 1) return s;
|
|
|
|
for (size_t i = 0; i < X.size(); i++)
|
|
s << (i%nval_per_line ? ' ':'\n') << trunc(X[i]);
|
|
|
|
return s << std::endl;
|
|
}
|
|
|
|
//! \brief Print the matrix \b A to the stream \a s.
|
|
//! \details If the matrix is symmetric, only the upper triangular part of
|
|
//! the matrix is printed, with the diagonal elements in the first column.
|
|
//! The global variable \a zero_print_tol is used as a tolerance when
|
|
//! checking whether the matrix is symmetric or not.
|
|
template<class T> std::ostream& operator<<(std::ostream& s,
|
|
const matrix<T>& A)
|
|
{
|
|
if (A.rows() < 1 || A.cols() < 1) return s;
|
|
|
|
bool symm = A.isSymmetric(zero_print_tol);
|
|
for (size_t i = 1; i <= A.rows(); i++)
|
|
{
|
|
size_t c1 = symm ? i : 1;
|
|
s <<"\nRow "<< i <<": "<< trunc(A(i,c1));
|
|
for (size_t j = c1+1; j <= A.cols(); j++)
|
|
s <<' '<< trunc(A(i,j));
|
|
}
|
|
|
|
return s << std::endl;
|
|
}
|
|
|
|
//! \brief Print the vector \b X to the stream \a s in matlab format.
|
|
template<class T> void writeMatlab(const char* label, const vector<T>& X,
|
|
std::ostream& s = std::cout)
|
|
{
|
|
s << label <<" = [";
|
|
for (size_t i = 1; i <= X.size(); i++)
|
|
s <<' '<< trunc(X(i));
|
|
s <<" ];"<< std::endl;
|
|
}
|
|
|
|
//! \brief Print the matrix \b A to the stream \a s in matlab format.
|
|
template<class T> void writeMatlab(const char* label, const matrix<T>& A,
|
|
std::ostream& s = std::cout)
|
|
{
|
|
s << label <<" = [";
|
|
size_t nsp = 4 + strlen(label);
|
|
for (size_t i = 1; i <= A.rows(); i++)
|
|
{
|
|
if (i > 1)
|
|
{
|
|
s <<";\n";
|
|
for (size_t k = 0; k < nsp; k++) s <<' ';
|
|
}
|
|
for (size_t j = 1; j <= A.cols(); j++)
|
|
s <<' '<< trunc(A(i,j));
|
|
}
|
|
s <<" ];"<< std::endl;
|
|
}
|
|
|
|
};
|
|
|
|
#undef THIS
|
|
#endif
|