Newer
Older
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set et ts=4 sw=2 sts=2:
// $Id$
#include <cmath>
#include <cstddef>
#include "exceptions.hh"
#include "fvector.hh"
#include "precision.hh"
#include "static_assert.hh"
template<class K, int ROWS, int COLS> class FieldMatrix;
template<class K, int ROWS, int COLS>
struct FieldTraits< FieldMatrix<K,ROWS,COLS> >
{
typedef const typename FieldTraits<K>::field_type field_type;
typedef const typename FieldTraits<K>::real_type real_type;
};
/*! \file
\brief This file implements a matrix constructed from a given type
representing a field and compile-time given number of rows and columns.
*/
/**
\brief you have to specialize this function for any type T that should be assignable to a FieldMatrix
*/
template<class K, int ROWS, int COLS, typename T>
void istl_assign_to_fmatrix(FieldMatrix<K,ROWS,COLS>& f, const T& t)
{
DUNE_THROW(NotImplemented, "You need to specialise this function for type T!");
}
namespace
{
template<bool b>
struct Assigner
{
template<class K, int ROWS, int COLS, class T>
static void assign(FieldMatrix<K,ROWS,COLS>& fm, const T& t)
{
istl_assign_to_fmatrix(fm, t);
}
};
template<>
struct Assigner<true>
{
template<class K, int ROWS, int COLS, class T>
static void assign(FieldMatrix<K,ROWS,COLS>& fm, const T& t)
{
fm = static_cast<const K>(t);
}
};
}
/** @brief Error thrown if operations of a FieldMatrix fail. */
class FMatrixError : public Exception {};
/**
@brief A dense n x m matrix.
Matrices represent linear maps from a vector space V to a vector space W.
This class represents such a linear map by storing a two-dimensional
%array of numbers of a given field type K. The number of rows and
template<class K, int ROWS, int COLS>
class FieldMatrix : ExprTmpl::Matrix< FieldMatrix<K,ROWS,COLS> >
{
public:
// standard constructor and everything is sufficient ...
//===== type definitions and constants
//! export the type representing the field
typedef K field_type;
//! export the type representing the components
typedef K block_type;
//! The type used for the index access and size operations.
typedef std::size_t size_type;
enum {
//! The number of block levels we contain. This is 1.
blocklevel = 1
};
enum {
//! The number of rows.
//! The number of columns.
//! Each row is implemented by a field vector
typedef FieldVector<K,cols> row_type;
//===== constructors
/** \brief Default constructor
*/
FieldMatrix () {}
/** \brief Constructor initializing the whole matrix with a scalar
*/
explicit FieldMatrix (const K& k)
template<typename T>
explicit FieldMatrix( const T& t)
{
Assigner<Conversion<T,K>::exists>::assign(*this, t);
}
//===== random access interface to rows of the matrix
//! random access to the rows
row_type& operator[] (size_type i)
#ifdef DUNE_FMatrix_WITH_CHECKING
if (i<0 || i>=n) DUNE_THROW(FMatrixError,"index out of range");
#endif
return p[i];
}
//! same for read only access
const row_type& operator[] (size_type i) const
#ifdef DUNE_FMatrix_WITH_CHECKING
if (i<0 || i>=n) DUNE_THROW(FMatrixError,"index out of range");
#endif
return p[i];
}
//===== iterator interface to rows of the matrix
//! Iterator class for sequential access
typedef FieldIterator<FieldMatrix<K,rows,cols>,row_type> Iterator;
//! typedef for stl compliant access
typedef Iterator iterator;
//! rename the iterators for easier access
typedef Iterator RowIterator;
//! rename the iterators for easier access
typedef typename row_type::Iterator ColIterator;
return Iterator(*this,0);
}
//! end iterator
Iterator end ()
{
}
//! begin iterator
Iterator rbegin ()
{
}
//! end iterator
Iterator rend ()
{
return Iterator(*this,-1);
//! Iterator class for sequential access
typedef FieldIterator<const FieldMatrix<K,rows,cols>,const row_type> ConstIterator;
//! typedef for stl compliant access
typedef ConstIterator const_iterator;
typedef ConstIterator ConstRowIterator;
//! rename the iterators for easier access
typedef typename row_type::ConstIterator ConstColIterator;
//! begin iterator
ConstIterator begin () const
{
return ConstIterator(*this,0);
}
//! end iterator
ConstIterator end () const
{
}
//! begin iterator
ConstIterator rbegin () const
{
}
//! end iterator
ConstIterator rend () const
{
return ConstIterator(*this,-1);
}
//===== assignment from scalar
FieldMatrix& operator= (const K& k)
{
Oliver Sander
committed
p[i] = k;
template<typename T>
FieldMatrix& operator= ( const T& t)
{
Assigner<Conversion<T,K>::exists>::assign(*this, t);
return *this;
}
//===== vector space arithmetic
//! vector space addition
FieldMatrix& operator+= (const FieldMatrix& y)
{
Oliver Sander
committed
p[i] += y.p[i];
return *this;
}
//! vector space subtraction
FieldMatrix& operator-= (const FieldMatrix& y)
{
Oliver Sander
committed
p[i] -= y.p[i];
return *this;
}
//! vector space multiplication with scalar
FieldMatrix& operator*= (const K& k)
{
Oliver Sander
committed
p[i] *= k;
return *this;
}
//! vector space division by scalar
FieldMatrix& operator/= (const K& k)
{
Oliver Sander
committed
p[i] /= k;
//! vector space axpy operation (*this += k y)
FieldMatrix &axpy ( const K &k, const FieldMatrix &y )
{
p[ i ].axpy( k, y[ i ] );
return *this;
}
//! y = A x
template<class X, class Y>
void mv (const X& x, Y& y) const
{
#ifdef DUNE_FMatrix_WITH_CHECKING
assert(&x != &y);
if (x.N()!=M()) DUNE_THROW(FMatrixError,"index out of range");
if (y.N()!=N()) DUNE_THROW(FMatrixError,"index out of range");
#endif
//! y = A^T x
template< class X, class Y >
void mtv ( const X &x, Y &y ) const
{
#ifdef DUNE_FMatrix_WITH_CHECKING
assert( &x != &y );
if( x.N() != N() )
DUNE_THROW( FMatrixError, "Index out of range." );
if( y.N() != M() )
DUNE_THROW( FMatrixError, "Index out of range." );
#endif
{
y[ i ] = 0;
y[ i ] += (*this)[ j ][ i ] * x[ j ];
}
}
//! y += A x
template<class X, class Y>
void umv (const X& x, Y& y) const
{
#ifdef DUNE_FMatrix_WITH_CHECKING
if (x.N()!=M()) DUNE_THROW(FMatrixError,"index out of range");
if (y.N()!=N()) DUNE_THROW(FMatrixError,"index out of range");
for (size_type i=0; i<rows; i++)
for (size_type j=0; j<cols; j++)
Oliver Sander
committed
y[i] += (*this)[i][j] * x[j];
}
//! y += A^T x
template<class X, class Y>
void umtv (const X& x, Y& y) const
{
#ifdef DUNE_FMatrix_WITH_CHECKING
if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range");
if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range");
for (size_type i=0; i<rows; i++)
for (size_type j=0; j<cols; j++)
y[j] += p[i][j]*x[i];
}
//! y += A^H x
template<class X, class Y>
void umhv (const X& x, Y& y) const
{
#ifdef DUNE_FMatrix_WITH_CHECKING
if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range");
if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range");
for (size_type i=0; i<rows; i++)
for (size_type j=0; j<cols; j++)
y[j] += conjugateComplex(p[i][j])*x[i];
}
//! y -= A x
template<class X, class Y>
void mmv (const X& x, Y& y) const
{
#ifdef DUNE_FMatrix_WITH_CHECKING
if (x.N()!=M()) DUNE_THROW(FMatrixError,"index out of range");
if (y.N()!=N()) DUNE_THROW(FMatrixError,"index out of range");
for (size_type i=0; i<rows; i++)
for (size_type j=0; j<cols; j++)
Oliver Sander
committed
y[i] -= (*this)[i][j] * x[j];
}
//! y -= A^T x
template<class X, class Y>
void mmtv (const X& x, Y& y) const
{
#ifdef DUNE_FMatrix_WITH_CHECKING
if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range");
if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range");
for (size_type i=0; i<rows; i++)
for (size_type j=0; j<cols; j++)
y[j] -= p[i][j]*x[i];
}
//! y -= A^H x
template<class X, class Y>
void mmhv (const X& x, Y& y) const
{
#ifdef DUNE_FMatrix_WITH_CHECKING
if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range");
if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range");
for (size_type i=0; i<rows; i++)
for (size_type j=0; j<cols; j++)
y[j] -= conjugateComplex(p[i][j])*x[i];
}
//! y += alpha A x
template<class X, class Y>
void usmv (const K& alpha, const X& x, Y& y) const
{
#ifdef DUNE_FMatrix_WITH_CHECKING
if (x.N()!=M()) DUNE_THROW(FMatrixError,"index out of range");
if (y.N()!=N()) DUNE_THROW(FMatrixError,"index out of range");
for (size_type i=0; i<rows; i++)
for (size_type j=0; j<cols; j++)
Oliver Sander
committed
y[i] += alpha * (*this)[i][j] * x[j];
}
//! y += alpha A^T x
template<class X, class Y>
void usmtv (const K& alpha, const X& x, Y& y) const
{
#ifdef DUNE_FMatrix_WITH_CHECKING
if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range");
if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range");
for (size_type i=0; i<rows; i++)
for (size_type j=0; j<cols; j++)
y[j] += alpha*p[i][j]*x[i];
}
//! y += alpha A^H x
template<class X, class Y>
void usmhv (const K& alpha, const X& x, Y& y) const
{
#ifdef DUNE_FMatrix_WITH_CHECKING
if (x.N()!=N()) DUNE_THROW(FMatrixError,"index out of range");
if (y.N()!=M()) DUNE_THROW(FMatrixError,"index out of range");
for (size_type i=0; i<rows; i++)
for (size_type j=0; j<cols; j++)
y[j] += alpha*conjugateComplex(p[i][j])*x[i];
}
//===== norms
//! frobenius norm: sqrt(sum over squared values of entries)
typename FieldTraits<K>::real_type frobenius_norm () const
typename FieldTraits<K>::real_type sum=0;
for (size_type i=0; i<rows; ++i) sum += p[i].two_norm2();
return sqrt(sum);
}
//! square of frobenius norm, need for block recursion
typename FieldTraits<K>::real_type frobenius_norm2 () const
typename FieldTraits<K>::real_type sum=0;
for (size_type i=0; i<rows; ++i) sum += p[i].two_norm2();
return sum;
}
//! infinity norm (row sum norm, how to generalize for blocks?)
typename FieldTraits<K>::real_type infinity_norm () const
typename FieldTraits<K>::real_type max=0;
for (size_type i=0; i<rows; ++i) max = std::max(max,p[i].one_norm());
return max;
}
//! simplified infinity norm (uses Manhattan norm for complex values)
typename FieldTraits<K>::real_type infinity_norm_real () const
typename FieldTraits<K>::real_type max=0;
for (size_type i=0; i<rows; ++i) max = std::max(max,p[i].one_norm_real());
return max;
}
//===== solve
/** \brief Solve system A x = b
*
* \exception FMatrixError if the matrix is singular
Oliver Sander
committed
void solve (V& x, const V& b) const;
* \exception FMatrixError if the matrix is singular
Oliver Sander
committed
void invert();
//! calculates the determinant of this matrix
K determinant () const;
//! Multiplies M from the left to this matrix
FieldMatrix& leftmultiply (const FieldMatrix<K,rows,rows>& M)
Oliver Sander
committed
for (size_type i=0; i<rows; i++)
for (size_type j=0; j<cols; j++) {
Oliver Sander
committed
(*this)[i][j] = 0;
Oliver Sander
committed
(*this)[i][j] += M[i][k]*C[k][j];
}
//! Multiplies M from the left to this matrix, this matrix is not modified
template<int l>
FieldMatrix<K,l,cols> leftmultiplyany (const FieldMatrix<K,l,rows>& M)
for (size_type i=0; i<l; i++) {
C[i][j] += M[i][k]*(*this)[k][j];
}
}
return C;
}
FieldMatrix& rightmultiply (const FieldMatrix<K,cols,cols>& M)
Oliver Sander
committed
for (size_type i=0; i<rows; i++)
for (size_type j=0; j<cols; j++) {
Oliver Sander
committed
(*this)[i][j] = 0;
Oliver Sander
committed
(*this)[i][j] += C[i][k]*M[k][j];
}
//! Multiplies M from the right to this matrix, this matrix is not modified
template<int l>
FieldMatrix<K,rows,l> rightmultiplyany (const FieldMatrix<K,cols,l>& M)
for (size_type j=0; j<l; j++) {
C[i][j] = 0;
C[i][j] += (*this)[i][k]*M[k][j];
}
}
return C;
}
//===== sizes
//! number of blocks in row direction
size_type N () const
size_type M () const
}
//===== query
//! return true when (i,j) is in pattern
bool exists (size_type i, size_type j) const
#ifdef DUNE_FMatrix_WITH_CHECKING
if (i<0 || i>=n) DUNE_THROW(FMatrixError,"row index out of range");
if (j<0 || j>=m) DUNE_THROW(FMatrixError,"column index out of range");
#endif
return true;
}
//===== conversion operator
/** \brief Sends the matrix to an output stream */
friend std::ostream& operator<< (std::ostream& s, const FieldMatrix<K,rows,cols>& a)
s << a.p[i] << std::endl;
Oliver Sander
committed
// the data, very simply a built in array with row-wise ordering
void swap(int i, int j);
template<typename T>
void operator()(const T&, int k, int i)
{}
size_type* pivot_;
};
template<typename V>
struct Elim
{
Elim(V& rhs);
void swap(int i, int j);
void operator()(const typename V::field_type& factor, int k, int i);
V* rhs_;
};
struct ElimDet
{
ElimDet(K& sign) : sign_(sign)
{ sign_ = 1; }
void swap(int i, int j)
{ sign_ *= -1; }
void operator()(const K&, int k, int i)
{}
K& sign_;
};
void luDecomposition(FieldMatrix<K,ROWS,ROWS>& A, Func func) const;
#ifndef DOXYGEN
template<typename K, int ROWS, int COLS>
FieldMatrix<K,ROWS,COLS>::ElimPivot::ElimPivot(size_type pivot[ROWS])
template<typename K, int ROWS, int COLS>
void FieldMatrix<K,ROWS,COLS>::ElimPivot::swap(int i, int j)
FieldMatrix<K,ROWS,COLS>::Elim<V>::Elim(V& rhs)
void FieldMatrix<K,ROWS,COLS>::Elim<V>::swap(int i, int j)
{
std::swap((*rhs_)[i], (*rhs_)[j]);
}
Elim<V>::operator()(const typename V::field_type& factor, int k, int i)
{
(*rhs_)[k] -= factor*(*rhs_)[i];
}
inline void FieldMatrix<K,ROWS,COLS>::luDecomposition(FieldMatrix<K,ROWS,ROWS>& A, Func func) const
typename FieldTraits<K>::real_type norm=A.infinity_norm_real(); // for relative thresholds
typename FieldTraits<K>::real_type pivthres = std::max(FMatrixPrecision<>::absolute_limit(),norm*FMatrixPrecision<>::pivoting_limit());
typename FieldTraits<K>::real_type singthres = std::max(FMatrixPrecision<>::absolute_limit(),norm*FMatrixPrecision<>::singular_limit());
// LU decomposition of A in A
for (int i=0; i<rows; i++) // loop over all rows
typename FieldTraits<K>::real_type pivmax=fvmeta_absreal(A[i][i]);
// pivoting ?
if (pivmax<pivthres)
{
// compute maximum of column
int imax=i; typename FieldTraits<K>::real_type abs;
if ((abs=fvmeta_absreal(A[k][i]))>pivmax)
{
pivmax = abs; imax = k;
}
// swap rows
if (imax!=i) {
}
}
// singular ?
if (pivmax<singthres)
DUNE_THROW(FMatrixError,"matrix is singular");
// eliminate
{
K factor = A[k][i]/A[i][i];
A[k][i] = factor;
A[k][j] -= factor*A[i][j];
func(factor, k, i);
}
}
}
Oliver Sander
committed
template <class V>
inline void FieldMatrix<K,ROWS,COLS>::solve(V& x, const V& b) const
Oliver Sander
committed
{
// never mind those ifs, because they get optimized away
if (rows!=cols)
DUNE_THROW(FMatrixError, "Can't solve for a " << rows << "x" << cols << " matrix!");
Oliver Sander
committed
// no need to implement the case 1x1, because the whole matrix class is
// specialized for this
Oliver Sander
committed
#ifdef DUNE_FMatrix_WITH_CHECKING
K detinv = p[0][0]*p[1][1]-p[0][1]*p[1][0];
if (fvmeta_absreal(detinv)<FMatrixPrecision<>::absolute_limit())
DUNE_THROW(FMatrixError,"matrix is singular");
detinv = 1/detinv;
#else
K detinv = 1.0/(p[0][0]*p[1][1]-p[0][1]*p[1][0]);
#endif
Oliver Sander
committed
x[0] = detinv*(p[1][1]*b[0]-p[0][1]*b[1]);
x[1] = detinv*(p[0][0]*b[1]-p[1][0]*b[0]);
K d = determinant();
#ifdef DUNE_FMatrix_WITH_CHECKING
if (fvmeta_absreal(d)<FMatrixPrecision<>::absolute_limit())
DUNE_THROW(FMatrixError,"matrix is singular");
#endif
x[0] = (b[0]*p[1][1]*p[2][2] - b[0]*p[2][1]*p[1][2]
- b[1] *p[0][1]*p[2][2] + b[1]*p[2][1]*p[0][2]
+ b[2] *p[0][1]*p[1][2] - b[2]*p[1][1]*p[0][2]) / d;
x[1] = (p[0][0]*b[1]*p[2][2] - p[0][0]*b[2]*p[1][2]
- p[1][0] *b[0]*p[2][2] + p[1][0]*b[2]*p[0][2]
+ p[2][0] *b[0]*p[1][2] - p[2][0]*b[1]*p[0][2]) / d;
x[2] = (p[0][0]*p[1][1]*b[2] - p[0][0]*p[2][1]*b[1]
- p[1][0] *p[0][1]*b[2] + p[1][0]*p[2][1]*b[0]
+ p[2][0] *p[0][1]*b[1] - p[2][0]*p[1][1]*b[0]) / d;
Oliver Sander
committed
} else {
V& rhs = x; // use x to store rhs
rhs = b; // copy data
Oliver Sander
committed
Oliver Sander
committed
// backsolve
for(int i=rows-1; i>=0; i--) {
for (int j=i+1; j<rows; j++)
Oliver Sander
committed
rhs[i] -= A[i][j]*x[j];
x[i] = rhs[i]/A[i][i];
}
Oliver Sander
committed
}
template <class K, int ROWS, int COLS>
inline void FieldMatrix<K,ROWS,COLS>::invert()
Oliver Sander
committed
{
// never mind those ifs, because they get optimized away
if (rows!=cols)
DUNE_THROW(FMatrixError, "Can't invert a " << rows << "x" << cols << " matrix!");
Oliver Sander
committed
// no need to implement the case 1x1, because the whole matrix class is
// specialized for this
Oliver Sander
committed
K detinv = p[0][0]*p[1][1]-p[0][1]*p[1][0];
#ifdef DUNE_FMatrix_WITH_CHECKING
if (fvmeta_absreal(detinv)<FMatrixPrecision<>::absolute_limit())
DUNE_THROW(FMatrixError,"matrix is singular");
#endif
detinv = 1/detinv;
K temp=p[0][0];
p[0][0] = p[1][1]*detinv;
p[0][1] = -p[0][1]*detinv;
p[1][0] = -p[1][0]*detinv;
p[1][1] = temp*detinv;
} else {
FieldMatrix<K,rows,rows> A(*this);
size_type pivot[rows];
luDecomposition(A, ElimPivot(pivot));
FieldMatrix<K,rows,cols>& L=A;
FieldMatrix<K,rows,cols>& U=A;
Oliver Sander
committed
// initialize inverse
Oliver Sander
committed
// L Y = I; multiple right hand sides
Sreejith Pulloor Kuttanikkad
committed
for (size_type j=0; j<i; j++)
Oliver Sander
committed
// U A^{-1} = Y
for (size_type k=0; k<rows; k++) {
for (size_type j=i+1; j<rows; j++)
Oliver Sander
committed
}
Oliver Sander
committed
}
template <class K, int ROWS, int COLS>
inline K FieldMatrix<K,ROWS,COLS>::determinant() const
Oliver Sander
committed
// never mind those ifs, because they get optimized away
if (rows!=cols)
DUNE_THROW(FMatrixError, "There is no determinant for a " << rows << "x" << cols << " matrix!");
Oliver Sander
committed
// no need to implement the case 1x1, because the whole matrix class is
// specialized for this
Oliver Sander
committed
return p[0][0]*p[1][1] - p[0][1]*p[1][0];
Oliver Sander
committed
// code generated by maple
K t4 = p[0][0] * p[1][1];
K t6 = p[0][0] * p[1][2];
K t8 = p[0][1] * p[1][0];
K t10 = p[0][2] * p[1][0];
K t12 = p[0][1] * p[2][0];
K t14 = p[0][2] * p[2][0];
return (t4*p[2][2]-t6*p[2][1]-t8*p[2][2]+
t10*p[2][1]+t12*p[1][2]-t14*p[1][1]);
}
try
{
luDecomposition(A, ElimDet(det));
}
catch (FMatrixError&)
{
return 0;
}
det *= A[i][i];
return det;
/** \brief Special type for 1x1 matrices
*/
template<class K>
class FieldMatrix<K,1,1>
{
public:
// standard constructor and everything is sufficient ...
//===== type definitions and constants
//! export the type representing the field
typedef K field_type;
//! export the type representing the components
typedef K block_type;
//! The type used for index access and size operations
//! We are at the leaf of the block recursion
enum {
//! The number of block levels we contain.
//! This is always one for this type.
blocklevel = 1
};
//! Each row is implemented by a field vector
typedef FieldVector<K,1> row_type;
//! export size
enum {
//! \brief The number of rows.
//! This is always one for this type.
rows = 1,
n = 1,
//! \brief The number of columns.
//! This is always one for this type.
cols = 1,
m = 1
};
Oliver Sander
committed
//===== constructors
/** \brief Default constructor
*/
FieldMatrix () {}
/** \brief Constructor initializing the whole matrix with a scalar
*/
Oliver Sander
committed
{
a = k;
}
template<typename T>
explicit FieldMatrix( const T& t)
{
Assigner<Conversion<T,K>::exists>::assign(*this, t);
}
//===== random access interface to rows of the matrix
//! random access to the rows
row_type& operator[] (size_type i)
{
#ifdef DUNE_FMatrix_WITH_CHECKING
if (i<0 || i>=n) DUNE_THROW(FMatrixError,"index out of range");
#endif
return a;
}
//! same for read only access
const row_type& operator[] (size_type i) const
{
#ifdef DUNE_FMatrix_WITH_CHECKING
if (i<0 || i>=n) DUNE_THROW(FMatrixError,"index out of range");
#endif
return a;
}
//===== iterator interface to rows of the matrix
//! Iterator class for sequential access
typedef FieldIterator<FieldMatrix<K,rows,cols>,row_type> Iterator;
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//! typedef for stl compliant access
typedef Iterator iterator;
//! rename the iterators for easier access
typedef Iterator RowIterator;
//! rename the iterators for easier access
typedef typename row_type::Iterator ColIterator;
//! begin iterator
Iterator begin ()
{
return Iterator(*this,0);
}
//! end iterator
Iterator end ()
{
return Iterator(*this,n);
}
//! begin iterator
Iterator rbegin ()
{
return Iterator(*this,n-1);
}
//! end iterator
Iterator rend ()