Skip to content
Snippets Groups Projects
fmatrix.hh 38.7 KiB
Newer Older
Oliver Sander's avatar
Oliver Sander committed
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set et ts=4 sw=2 sts=2:
Oliver Sander's avatar
Oliver Sander committed
#ifndef DUNE_FMATRIX_HH
#define DUNE_FMATRIX_HH

#include <math.h>
#include <complex>
#include <iostream>
#include "exceptions.hh"
#include "fvector.hh"
#include "precision.hh"
Oliver Sander's avatar
Oliver Sander committed

namespace Dune {

  /**
Markus Blatt's avatar
Markus Blatt committed
  /*! \file

     \brief  This file implements a matrix constructed from a given type
     representing a field and compile-time given number of rows and columns.
   */

Oliver Sander's avatar
Oliver Sander committed
  template<class K, int n, int m> class FieldMatrix;

  /** @brief Error thrown if operations of a FieldMatrix fail. */
  class FMatrixError : public Exception {};

Oliver Sander's avatar
Oliver Sander committed
  // template meta program for assignment from scalar
  template<int I>
  struct fmmeta_assignscalar {
    template<class T, class K>
    static void assignscalar (T* x, const K& k)
    {
      fmmeta_assignscalar<I-1>::assignscalar(x,k);
      x[I] = k;
    }
  };
  template<>
  struct fmmeta_assignscalar<0> {
    template<class T, class K>
    static void assignscalar (T* x, const K& k)
    {
      x[0] = k;
    }
  };

  // template meta program for operator+=
  template<int I>
  struct fmmeta_plusequal {
    template<class T>
    static void plusequal (T& x, const T& y)
    {
      x[I] += y[I];
      fmmeta_plusequal<I-1>::plusequal(x,y);
    }
  };
  template<>
  struct fmmeta_plusequal<0> {
    template<class T>
    static void plusequal (T& x, const T& y)
    {
      x[0] += y[0];
    }
  };

  // template meta program for operator-=
  template<int I>
  struct fmmeta_minusequal {
    template<class T>
    static void minusequal (T& x, const T& y)
    {
      x[I] -= y[I];
      fmmeta_minusequal<I-1>::minusequal(x,y);
    }
  };
  template<>
  struct fmmeta_minusequal<0> {
    template<class T>
    static void minusequal (T& x, const T& y)
    {
      x[0] -= y[0];
    }
  };

  // template meta program for operator*=
  template<int I>
  struct fmmeta_multequal {
    template<class T, class K>
    static void multequal (T& x, const K& k)
    {
      x[I] *= k;
      fmmeta_multequal<I-1>::multequal(x,k);
    }
  };
  template<>
  struct fmmeta_multequal<0> {
    template<class T, class K>
    static void multequal (T& x, const K& k)
    {
      x[0] *= k;
    }
  };

  // template meta program for operator/=
  template<int I>
  struct fmmeta_divequal {
    template<class T, class K>
    static void divequal (T& x, const K& k)
    {
      x[I] /= k;
      fmmeta_divequal<I-1>::divequal(x,k);
    }
  };
  template<>
  struct fmmeta_divequal<0> {
    template<class T, class K>
    static void divequal (T& x, const K& k)
    {
      x[0] /= k;
    }
  };

  // template meta program for dot
  template<int I>
  struct fmmeta_dot {
    template<class X, class Y, class K>
    static K dot (const X& x, const Y& y)
    {
      return x[I]*y[I] + fmmeta_dot<I-1>::template dot<X,Y,K>(x,y);
    }
  };
  template<>
  struct fmmeta_dot<0> {
    template<class X, class Y, class K>
    static K dot (const X& x, const Y& y)
    {
      return x[0]*y[0];
    }
  };

  // template meta program for umv(x,y)
  template<int I>
  struct fmmeta_umv {
    template<class Mat, class X, class Y, int c>
    static void umv (const Mat& A, const X& x, Y& y)
    {
      typedef typename Mat::row_type R;
      typedef typename Mat::field_type K;
      y[I] += fmmeta_dot<c>::template dot<R,X,K>(A[I],x);
      fmmeta_umv<I-1>::template umv<Mat,X,Y,c>(A,x,y);
    }
  };
  template<>
  struct fmmeta_umv<0> {
    template<class Mat, class X, class Y, int c>
    static void umv (const Mat& A, const X& x, Y& y)
    {
      typedef typename Mat::row_type R;
      typedef typename Mat::field_type K;
      y[0] += fmmeta_dot<c>::template dot<R,X,K>(A[0],x);
    }
  };

  // template meta program for mmv(x,y)
  template<int I>
  struct fmmeta_mmv {
    template<class Mat, class X, class Y, int c>
    static void mmv (const Mat& A, const X& x, Y& y)
    {
      typedef typename Mat::row_type R;
      typedef typename Mat::field_type K;
      y[I] -= fmmeta_dot<c>::template dot<R,X,K>(A[I],x);
      fmmeta_mmv<I-1>::template mmv<Mat,X,Y,c>(A,x,y);
    }
  };
  template<>
  struct fmmeta_mmv<0> {
    template<class Mat, class X, class Y, int c>
    static void mmv (const Mat& A, const X& x, Y& y)
    {
      typedef typename Mat::row_type R;
      typedef typename Mat::field_type K;
      y[0] -= fmmeta_dot<c>::template dot<R,X,K>(A[0],x);
    }
  };

  template<class K, int n, int m, class X, class Y>
  inline void fm_mmv (const FieldMatrix<K,n,m>& A,  const X& x, Y& y)
  {
    for (int i=0; i<n; i++)
      for (int j=0; j<m; j++)
        y[i] -= A[i][j]*x[j];
  }

  template<class K>
  inline void fm_mmv (const FieldMatrix<K,1,1>& A, const FieldVector<K,1>& x, FieldVector<K,1>& y)
Oliver Sander's avatar
Oliver Sander committed
  {
    y[0] -= A[0][0]*x[0];
  }

  // template meta program for usmv(x,y)
  template<int I>
  struct fmmeta_usmv {
    template<class Mat, class K, class X, class Y, int c>
    static void usmv (const Mat& A, const K& alpha, const X& x, Y& y)
    {
      typedef typename Mat::row_type R;
      y[I] += alpha*fmmeta_dot<c>::template dot<R,X,K>(A[I],x);
      fmmeta_usmv<I-1>::template usmv<Mat,K,X,Y,c>(A,alpha,x,y);
    }
  };
  template<>
  struct fmmeta_usmv<0> {
    template<class Mat, class K,  class X, class Y, int c>
    static void usmv (const Mat& A, const K& alpha, const X& x, Y& y)
    {
      typedef typename Mat::row_type R;
      y[0] += alpha*fmmeta_dot<c>::template dot<R,X,K>(A[0],x);
    }
  };

  // conjugate komplex does nothing for non-complex types
  template<class K>
  inline K fm_ck (const K& k)
  {
    return k;
  }

  // conjugate komplex
  template<class K>
  inline std::complex<K> fm_ck (const std::complex<K>& c)
  {
    return std::complex<K>(c.real(),-c.imag());
  }


  //! solve small system
  template<class K, int n, class V>
  void fm_solve (const FieldMatrix<K,n,n>& Ain,  V& x, const V& b)
  {
    // make a copy of a to store factorization
    FieldMatrix<K,n,n> A(Ain);

    // Gaussian elimination with maximum column pivot
    double norm=A.infinity_norm_real();     // for relative thresholds
    double pivthres = std::max(FMatrixPrecision<>::absolute_limit(),norm*FMatrixPrecision<>::pivoting_limit());
    double singthres = std::max(FMatrixPrecision<>::absolute_limit(),norm*FMatrixPrecision<>::singular_limit());
Oliver Sander's avatar
Oliver Sander committed
    V& rhs = x;          // use x to store rhs
    rhs = b;             // copy data

    // elimination phase
    for (int i=0; i<n; i++)      // loop over all rows
    {
      double pivmax=fvmeta_absreal(A[i][i]);

      // pivoting ?
      if (pivmax<pivthres)
      {
        // compute maximum of row
        int imax=i; double abs;
        for (int k=i+1; k<n; k++)
          if ((abs=fvmeta_absreal(A[k][i]))>pivmax)
          {
            pivmax = abs; imax = k;
          }
        // swap rows
        if (imax!=i)
          for (int j=i; j<n; j++)
            std::swap(A[i][j],A[imax][j]);
      }

      // singular ?
      if (pivmax<singthres)
        DUNE_THROW(FMatrixError,"matrix is singular");
Oliver Sander's avatar
Oliver Sander committed

      // eliminate
      for (int k=i+1; k<n; k++)
      {
        K factor = -A[k][i]/A[i][i];
        for (int j=i+1; j<n; j++)
          A[k][j] += factor*A[i][j];
        rhs[k] += factor*rhs[i];
      }
    }

    // backsolve
    for (int i=n-1; i>=0; i--)
    {
      for (int j=i+1; j<n; j++)
        rhs[i] -= A[i][j]*x[j];
      x[i] = rhs[i]/A[i][i];
    }
  }

  //! special case for 1x1 matrix, x and b may be identical
  template<class K, class V>
  inline void fm_solve (const FieldMatrix<K,1,1>& A,  V& x, const V& b)
  {
#ifdef DUNE_FMatrix_WITH_CHECKING
    if (fvmeta_absreal(A[0][0])<FMatrixPrecision<>::absolute_limit())
      DUNE_THROW(FMatrixError,"matrix is singular");
Oliver Sander's avatar
Oliver Sander committed
#endif
    x[0] = b[0]/A[0][0];
  }

  //! special case for 2x2 matrix, x and b may be identical
  template<class K, class V>
  inline void fm_solve (const FieldMatrix<K,2,2>& A,  V& x, const V& b)
  {
#ifdef DUNE_FMatrix_WITH_CHECKING
Oliver Sander's avatar
Oliver Sander committed
    K detinv = A[0][0]*A[1][1]-A[0][1]*A[1][0];
    if (fvmeta_absreal(detinv)<FMatrixPrecision<>::absolute_limit())
      DUNE_THROW(FMatrixError,"matrix is singular");
Oliver Sander's avatar
Oliver Sander committed
    detinv = 1/detinv;
#else
    K detinv = 1.0/(A[0][0]*A[1][1]-A[0][1]*A[1][0]);
#endif

    K temp = b[0];
    x[0] = detinv*(A[1][1]*b[0]-A[0][1]*b[1]);
    x[1] = detinv*(A[0][0]*b[1]-A[1][0]*temp);
  }



  //! compute inverse
  template<class K, int n>
  void fm_invert (FieldMatrix<K,n,n>& B)
  {
    FieldMatrix<K,n,n> A(B);
    FieldMatrix<K,n,n>& L=A;
    FieldMatrix<K,n,n>& U=A;

    double norm=A.infinity_norm_real();     // for relative thresholds
    double pivthres = std::max(FMatrixPrecision<>::absolute_limit(),norm*FMatrixPrecision<>::pivoting_limit());
    double singthres = std::max(FMatrixPrecision<>::absolute_limit(),norm*FMatrixPrecision<>::singular_limit());
Oliver Sander's avatar
Oliver Sander committed

    // LU decomposition of A in A
    for (int i=0; i<n; i++)      // loop over all rows
    {
      double pivmax=fvmeta_absreal(A[i][i]);

      // pivoting ?
      if (pivmax<pivthres)
      {
        // compute maximum of column
        int imax=i; double abs;
        for (int k=i+1; k<n; k++)
          if ((abs=fvmeta_absreal(A[k][i]))>pivmax)
          {
            pivmax = abs; imax = k;
          }
        // swap rows
        if (imax!=i)
          for (int j=i; j<n; j++)
            std::swap(A[i][j],A[imax][j]);
      }

      // singular ?
      if (pivmax<singthres)
        DUNE_THROW(FMatrixError,"matrix is singular");
Oliver Sander's avatar
Oliver Sander committed

      // eliminate
      for (int k=i+1; k<n; k++)
      {
        K factor = A[k][i]/A[i][i];
        L[k][i] = factor;
        for (int j=i+1; j<n; j++)
          A[k][j] -= factor*A[i][j];
      }
    }

    // initialize inverse
    B = 0;
    for (int i=0; i<n; i++) B[i][i] = 1;

    // L Y = I; multiple right hand sides
    for (int i=0; i<n; i++)
      for (int j=0; j<i; j++)
        for (int k=0; k<n; k++)
          B[i][k] -= L[i][j]*B[j][k];

    // U A^{-1} = Y
    for (int i=n-1; i>=0; i--)
      for (int k=0; k<n; k++)
      {
        for (int j=i+1; j<n; j++)
          B[i][k] -= U[i][j]*B[j][k];
        B[i][k] /= U[i][i];
      }
  }

  //! compute inverse n=1
  template<class K>
  void fm_invert (FieldMatrix<K,1,1>& A)
  {
#ifdef DUNE_FMatrix_WITH_CHECKING
    if (fvmeta_absreal(A[0][0])<FMatrixPrecision<>::absolute_limit())
      DUNE_THROW(FMatrixError,"matrix is singular");
Oliver Sander's avatar
Oliver Sander committed
#endif
    A[0][0] = 1/A[0][0];
  }

  //! compute inverse n=2
  template<class K>
  void fm_invert (FieldMatrix<K,2,2>& A)
  {
    K detinv = A[0][0]*A[1][1]-A[0][1]*A[1][0];
#ifdef DUNE_FMatrix_WITH_CHECKING
    if (fvmeta_absreal(detinv)<FMatrixPrecision<>::absolute_limit())
      DUNE_THROW(FMatrixError,"matrix is singular");
Oliver Sander's avatar
Oliver Sander committed
#endif
    detinv = 1/detinv;

    K temp=A[0][0];
    A[0][0] =  A[1][1]*detinv;
    A[0][1] = -A[0][1]*detinv;
    A[1][0] = -A[1][0]*detinv;
    A[1][1] =  temp*detinv;
  }

  //! left multiplication with matrix
  template<class K, int n, int m>
  void fm_leftmultiply (const FieldMatrix<K,n,n>& M, FieldMatrix<K,n,m>& A)
  {
    FieldMatrix<K,n,m> C(A);

    for (int i=0; i<n; i++)
      for (int j=0; j<m; j++)
      {
        A[i][j] = 0;
        for (int k=0; k<n; k++)
          A[i][j] += M[i][k]*C[k][j];
      }
  }

  //! left multiplication with matrix, n=1
  template<class K>
  void fm_leftmultiply (const FieldMatrix<K,1,1>& M, FieldMatrix<K,1,1>& A)
  {
    A[0][0] *= M[0][0];
  }

  //! left multiplication with matrix, n=2
  template<class K>
  void fm_leftmultiply (const FieldMatrix<K,2,2>& M, FieldMatrix<K,2,2>& A)
  {
    FieldMatrix<K,2,2> C(A);

    A[0][0] = M[0][0]*C[0][0] + M[0][1]*C[1][0];
    A[0][1] = M[0][0]*C[0][1] + M[0][1]*C[1][1];
    A[1][0] = M[1][0]*C[0][0] + M[1][1]*C[1][0];
    A[1][1] = M[1][0]*C[0][1] + M[1][1]*C[1][1];
  }

  //! right multiplication with matrix
  template<class K, int n, int m>
  void fm_rightmultiply (const FieldMatrix<K,m,m>& M, FieldMatrix<K,n,m>& A)
  {
    FieldMatrix<K,n,m> C(A);

    for (int i=0; i<n; i++)
      for (int j=0; j<m; j++)
      {
        A[i][j] = 0;
        for (int k=0; k<m; k++)
          A[i][j] += C[i][k]*M[k][j];
      }
  }

  //! right multiplication with matrix, n=1
  template<class K>
  void fm_rightmultiply (const FieldMatrix<K,1,1>& M, FieldMatrix<K,1,1>& A)
  {
    A[0][0] *= M[0][0];
  }

  //! right multiplication with matrix, n=2
  template<class K>
  void fm_rightmultiply (const FieldMatrix<K,2,2>& M, FieldMatrix<K,2,2>& A)
  {
    FieldMatrix<K,2,2> C(A);

    A[0][0] = C[0][0]*M[0][0] + C[0][1]*M[1][0];
    A[0][1] = C[0][0]*M[0][1] + C[0][1]*M[1][1];
    A[1][0] = C[1][0]*M[0][0] + C[1][1]*M[1][0];
    A[1][1] = C[1][0]*M[0][1] + C[1][1]*M[1][1];
  }

Markus Blatt's avatar
Markus Blatt committed
  /**
      @brief A dense n x m matrix.

     Matrices represent linear maps from a vector space V to a vector space W.
Oliver Sander's avatar
Oliver Sander committed
       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
       columns is given at compile time.

           Implementation of all members uses template meta programs where appropriate
   */
  template<class K, int n, int m>
  class FieldMatrix
  {
  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;

    //! We are at the leaf of the block recursion
    enum {
      //! The number of block levels we contain. This is 1.
      blocklevel = 1
    };
Oliver Sander's avatar
Oliver Sander committed

    //! Each row is implemented by a field vector
    typedef FieldVector<K,m> row_type;

    //! export size
    enum {
      //! The number of rows.
      rows = n,
      //! The number of columns.
      cols = m
    };
Oliver Sander's avatar
Oliver Sander committed

    //===== constructors
    /** \brief Default constructor
     */
    FieldMatrix () {}

    /** \brief Constructor initializing the whole matrix with a scalar
     */
    FieldMatrix (const K& k)
    {
      for (int i=0; i<n; i++) p[i] = k;
    }

    //===== random access interface to rows of the matrix

    //! random access to the rows
    row_type& operator[] (int i)
    {
#ifdef DUNE_FMatrix_WITH_CHECKING
      if (i<0 || i>=n) DUNE_THROW(FMatrixError,"index out of range");
Oliver Sander's avatar
Oliver Sander committed
#endif
      return p[i];
    }

    //! same for read only access
    const row_type& operator[] (int i) const
    {
#ifdef DUNE_FMatrix_WITH_CHECKING
      if (i<0 || i>=n) DUNE_THROW(FMatrixError,"index out of range");
Oliver Sander's avatar
Oliver Sander committed
#endif
      return p[i];
    }


    //===== iterator interface to rows of the matrix
    //! Iterator class for sequential access
    typedef Dune::GenericIterator<FieldMatrix<K,n,m>,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;
Oliver Sander's avatar
Oliver Sander committed

    //! begin iterator
    Iterator begin ()
    {
      return Iterator(*this,0);
Oliver Sander's avatar
Oliver Sander committed
    }

    //! end iterator
    Iterator end ()
    {
      return Iterator(*this,n);
Oliver Sander's avatar
Oliver Sander committed
    }

    //! begin iterator
    Iterator rbegin ()
    {
      return Iterator(*this,n-1);
Oliver Sander's avatar
Oliver Sander committed
    }

    //! end iterator
    Iterator rend ()
    {
      return Iterator(*this,-1);
    //! Iterator class for sequential access
    typedef Dune::GenericIterator<const FieldMatrix<K,n,m>,const row_type> ConstIterator;
    //! typedef for stl compliant access
    typedef ConstIterator const_iterator;
Oliver Sander's avatar
Oliver Sander committed
    //! rename the iterators for easier access
    typedef ConstIterator ConstRowIterator;
    //! rename the iterators for easier access
    typedef typename row_type::ConstIterator ConstColIterator;
Oliver Sander's avatar
Oliver Sander committed

    //! begin iterator
    ConstIterator begin () const
    {
      return ConstIterator(*this,0);
Oliver Sander's avatar
Oliver Sander committed
    }

    //! end iterator
    ConstIterator end () const
    {
      return ConstIterator(*this,n);
Oliver Sander's avatar
Oliver Sander committed
    }

    //! begin iterator
    ConstIterator rbegin () const
    {
      return ConstIterator(*this,n-1);
Oliver Sander's avatar
Oliver Sander committed
    }

    //! end iterator
    ConstIterator rend () const
    {
      return ConstIterator(*this,-1);
Oliver Sander's avatar
Oliver Sander committed
    }

    //===== assignment from scalar
    FieldMatrix& operator= (const K& k)
    {
      fmmeta_assignscalar<n-1>::assignscalar(p,k);
      return *this;
    }

    //===== vector space arithmetic

    //! vector space addition
    FieldMatrix& operator+= (const FieldMatrix& y)
    {
      fmmeta_plusequal<n-1>::plusequal(*this,y);
      return *this;
    }

    //! vector space subtraction
    FieldMatrix& operator-= (const FieldMatrix& y)
    {
      fmmeta_minusequal<n-1>::minusequal(*this,y);
      return *this;
    }

    //! vector space multiplication with scalar
    FieldMatrix& operator*= (const K& k)
    {
      fmmeta_multequal<n-1>::multequal(*this,k);
      return *this;
    }

    //! vector space division by scalar
    FieldMatrix& operator/= (const K& k)
    {
      fmmeta_divequal<n-1>::divequal(*this,k);
      return *this;
    }

    //===== linear maps

    //! 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");
Oliver Sander's avatar
Oliver Sander committed
#endif
      fmmeta_umv<n-1>::template umv<FieldMatrix,X,Y,m-1>(*this,x,y);
    }

    //! 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");
Oliver Sander's avatar
Oliver Sander committed
#endif

      for (int i=0; i<n; i++)
        for (int j=0; j<m; 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");
Oliver Sander's avatar
Oliver Sander committed
#endif

      for (int i=0; i<n; i++)
        for (int j=0; j<m; j++)
          y[j] += fm_ck(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");
Oliver Sander's avatar
Oliver Sander committed
#endif
      fmmeta_mmv<n-1>::template mmv<FieldMatrix,X,Y,m-1>(*this,x,y);
      //fm_mmv(*this,x,y);
    }

    //! 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");
Oliver Sander's avatar
Oliver Sander committed
#endif

      for (int i=0; i<n; i++)
        for (int j=0; j<m; 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");
Oliver Sander's avatar
Oliver Sander committed
#endif

      for (int i=0; i<n; i++)
        for (int j=0; j<m; j++)
          y[j] -= fm_ck(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");
Oliver Sander's avatar
Oliver Sander committed
#endif
      fmmeta_usmv<n-1>::template usmv<FieldMatrix,K,X,Y,m-1>(*this,alpha,x,y);
    }

    //! 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");
Oliver Sander's avatar
Oliver Sander committed
#endif

      for (int i=0; i<n; i++)
        for (int j=0; j<m; 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");
Oliver Sander's avatar
Oliver Sander committed
#endif

      for (int i=0; i<n; i++)
        for (int j=0; j<m; j++)
          y[j] += alpha*fm_ck(p[i][j])*x[i];
    }

    //===== norms

    //! frobenius norm: sqrt(sum over squared values of entries)
    double frobenius_norm () const
    {
      double sum=0;
      for (int i=0; i<n; ++i) sum += p[i].two_norm2();
      return sqrt(sum);
    }

    //! square of frobenius norm, need for block recursion
    double frobenius_norm2 () const
    {
      double sum=0;
      for (int i=0; i<n; ++i) sum += p[i].two_norm2();
      return sum;
    }

    //! infinity norm (row sum norm, how to generalize for blocks?)
    double infinity_norm () const
    {
      double max=0;
      for (int i=0; i<n; ++i) max = std::max(max,p[i].one_norm());
      return max;
    }

    //! simplified infinity norm (uses Manhattan norm for complex values)
    double infinity_norm_real () const
    {
      double max=0;
      for (int i=0; i<n; ++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's avatar
Oliver Sander committed
     */
    template<class V>
    void solve (V& x, const V& b) const
    {
      fm_solve(*this,x,b);
    }

    /** \brief Compute inverse
     *
     * \exception FMatrixError if the matrix is singular
Oliver Sander's avatar
Oliver Sander committed
     */
    void invert ()
    {
      fm_invert(*this);
    }

    //! calculates the determinant of this matrix
    K determinant () const;

    //! Multiplies M from the left to this matrix
    FieldMatrix& leftmultiply (const FieldMatrix<K,n,n>& M)
    {
      fm_leftmultiply(M,*this);
      return *this;
    }

    //! Multiplies M from the right to this matrix
    FieldMatrix& rightmultiply (const FieldMatrix<K,n,n>& M)
    {
      fm_rightmultiply(M,*this);
      return *this;
    }


    //===== sizes

    //! number of blocks in row direction
    int N () const
    {
      return n;
    }

    //! number of blocks in column direction
    int M () const
    {
      return m;
    }

    //! row dimension of block r
    int rowdim (int r) const
    {
      return 1;
    }

    //! col dimension of block c
    int coldim (int c) const
    {
      return 1;
    }

    //! dimension of the destination vector space
    int rowdim () const
    {
      return n;
    }

    //! dimension of the source vector space
    int coldim () const
    {
      return m;
    }

    //===== query

    //! return true when (i,j) is in pattern
    bool exists (int i, int j) const
    {
#ifdef DUNE_FMatrix_WITH_CHECKING
      if (i<0 || i>=n) DUNE_THROW(FMatrixError,"index out of range");
      if (j<0 || i>=m) DUNE_THROW(FMatrixError,"index out of range");
Oliver Sander's avatar
Oliver Sander committed
#endif
      return true;
    }

    //===== conversion operator

    /** \brief Sends the matrix to an output stream */
    void print (std::ostream& s) const
    {
      for (int i=0; i<n; i++)
        s << p[i] << std::endl;
    }

    /** \brief Sends the matrix to an output stream */
    friend std::ostream& operator<< (std::ostream& s, const FieldMatrix<K,n,m>& a)
    {
      a.print(s);
      return s;
    }

  private:
    // the data, very simply a built in array with rowwise ordering
    row_type p[n];
  };


  namespace HelpMat {

    // calculation of determinat of matrix
    template <class K, int row,int col>
    static inline K determinantMatrix (const FieldMatrix<K,row,col> &matrix)
    {
      if (row!=col)
        DUNE_THROW(FMatrixError, "There is no determinant for a " << row << "x" << col << " matrix!");
      DUNE_THROW(FMatrixError, "No implementation of determinantMatrix "
Oliver Sander's avatar
Oliver Sander committed
                 << "for FieldMatrix<" << row << "," << col << "> !");

      return 0.0;
    }

    template <typename K>
    static inline K determinantMatrix (const FieldMatrix<K,1,1> &matrix)
    {
      return matrix[0][0];
    }

    template <typename K>
    static inline K determinantMatrix (const FieldMatrix<K,2,2> &matrix)
    {
      return matrix[0][0]*matrix[1][1] - matrix[0][1]*matrix[1][0];
    }

    template <typename K>
    static inline K determinantMatrix (const FieldMatrix<K,3,3> &matrix)
    {
      // code generated by maple
      K t4  = matrix[0][0] * matrix[1][1];
      K t6  = matrix[0][0] * matrix[1][2];
      K t8  = matrix[0][1] * matrix[1][0];
      K t10 = matrix[0][2] * matrix[1][0];
      K t12 = matrix[0][1] * matrix[2][0];
      K t14 = matrix[0][2] * matrix[2][0];

      K det = (t4*matrix[2][2]-t6*matrix[2][1]-t8*matrix[2][2]+
               t10*matrix[2][1]+t12*matrix[1][2]-t14*matrix[1][1]);
      return det;
    }

  } // end namespace HelpMat

  // implementation of the determinant
  template <class K, int n, int m>
  inline K FieldMatrix<K,n,m>::determinant () const
  {
    return HelpMat::determinantMatrix(*this);
  }

#ifdef USE_DEPRECATED_K1
Oliver Sander's avatar
Oliver Sander committed
  /** \brief Special type for 1x1 matrices
   */
  template<class K>
  class K11Matrix
  {
  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;

    //! 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
    };
Oliver Sander's avatar
Oliver Sander committed

    //! export size
    enum {
      //! \brief The number of rows.
      //! This is always one for this type.
      rows = 1,
      //! \brief The number of columns.
      //! This is always one for this type.
      cols = 1
    };
Oliver Sander's avatar
Oliver Sander committed

    //===== assignment from scalar

    K11Matrix& operator= (const K& k)
    {
      a = k;
      return *this;
    }

    //===== vector space arithmetic

    //! vector space addition
    K11Matrix& operator+= (const K& y)
    {
      a += y;
      return *this;
    }

    //! vector space subtraction
    K11Matrix& operator-= (const K& y)
    {
      a -= y;
      return *this;
    }

    //! vector space multiplication with scalar
    K11Matrix& operator*= (const K& k)
    {
      a *= k;
      return *this;
    }

    //! vector space division by scalar
    K11Matrix& operator/= (const K& k)
    {
      a /= k;
      return *this;
    }

    //===== linear maps

    //! y += A x
    void umv (const K1Vector<K>& x, K1Vector<K>& y) const
    {
      y.p += a * x.p;
    }

    //! y += A^T x
    void umtv (const K1Vector<K>& x, K1Vector<K>& y) const
    {
      y.p += a * x.p;
    }

    //! y += A^H x
    void umhv (const K1Vector<K>& x, K1Vector<K>& y) const
    {
      y.p += fm_ck(a) * x.p;
    }

    //! y -= A x
    void mmv (const K1Vector<K>& x, K1Vector<K>& y) const
    {
      y.p -= a * x.p;
    }

    //! y -= A^T x
    void mmtv (const K1Vector<K>& x, K1Vector<K>& y) const
    {
      y.p -= a * x.p;
    }

    //! y -= A^H x
    void mmhv (const K1Vector<K>& x, K1Vector<K>& y) const
    {
      y.p -= fm_ck(a) * x.p;
    }

    //! y += alpha A x
    void usmv (const K& alpha, const K1Vector<K>& x, K1Vector<K>& y) const
    {
      y.p += alpha * a * x.p;
    }

    //! y += alpha A^T x
    void usmtv (const K& alpha, const K1Vector<K>& x, K1Vector<K>& y) const
    {
      y.p += alpha * a * x.p;
    }

    //! y += alpha A^H x
    void usmhv (const K& alpha, const K1Vector<K>& x, K1Vector<K>& y) const
    {
      y.p += alpha * fm_ck(a) * x.p;
    }

    //===== norms

    //! frobenius norm: sqrt(sum over squared values of entries)
    double frobenius_norm () const
    {
      return sqrt(fvmeta_abs2(a));
    }

    //! square of frobenius norm, need for block recursion
    double frobenius_norm2 () const
    {
      return fvmeta_abs2(a);
    }

    //! infinity norm (row sum norm, how to generalize for blocks?)
    double infinity_norm () const
    {
      return fvmeta_abs(a);
    }

    //! simplified infinity norm (uses Manhattan norm for complex values)
    double infinity_norm_real () const
    {
      return fvmeta_abs_real(a);
    }

    //===== solve

    //! Solve system A x = b
    void solve (K1Vector<K>& x, const K1Vector<K>& b) const
    {
      x.p = b.p/a;
    }

    //! compute inverse
    void invert ()
    {
      a = 1/a;
    }

    //! left multiplication
    K11Matrix& leftmultiply (const K11Matrix<K>& M)
    {
      a *= M.a;
      return *this;
    }

    //! left multiplication
    K11Matrix& rightmultiply (const K11Matrix<K>& M)
    {
      a *= M.a;
      return *this;
    }


    //===== sizes

    //! number of blocks in row direction
    int N () const
    {
      return 1;
    }

    //! number of blocks in column direction
    int M () const
    {
      return 1;
    }

    //! row dimension of block r
    int rowdim (int r) const
    {
      return 1;
    }

    //! col dimension of block c
    int coldim (int c) const
    {
      return 1;
    }

    //! dimension of the destination vector space
    int rowdim () const
    {
      return 1;
    }

    //! dimension of the source vector space
    int coldim () const
    {
      return 1;
    }

    //===== query

    //! return true when (i,j) is in pattern
    bool exists (int i, int j) const
    {
      return i==0 && j==0;
    }

    //===== conversion operator

    operator K () const {return a;}

  private:
    // the data, just a single scalar
    K a;
  };
#endif // USE_DEPRECATED_K1

  /** \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;

    //! 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
    };

    //===== random access interface to rows of the matrix

    //! random access to the rows
    row_type& operator[] (int 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[] (int 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 Dune::GenericIterator<FieldMatrix<K,n,m>,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;

    //! 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 ()
    {
      return Iterator(*this,-1);
    }

    //! Iterator class for sequential access
    typedef Dune::GenericIterator<const FieldMatrix<K,n,m>,const row_type> ConstIterator;
    //! typedef for stl compliant access
    typedef ConstIterator const_iterator;
    //! rename the iterators for easier access
    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
    {
      return ConstIterator(*this,n);
    }

    //! begin iterator
    ConstIterator rbegin () const
    {
      return ConstIterator(*this,n-1);
    }

    //! end iterator
    ConstIterator rend () const
    {
      return ConstIterator(*this,-1);
    }

    //===== assignment from scalar

    FieldMatrix& operator= (const K& k)
    {
      a[0] = k;
      return *this;
    }

    //===== vector space arithmetic

    //! vector space addition
    FieldMatrix& operator+= (const K& y)
    {
      a[0] += y;
      return *this;
    }

    //! vector space subtraction
    FieldMatrix& operator-= (const K& y)
    {
      a[0] -= y;
      return *this;
    }

    //! vector space multiplication with scalar
    FieldMatrix& operator*= (const K& k)
    {
      a[0] *= k;
      return *this;
    }

    //! vector space division by scalar
    FieldMatrix& operator/= (const K& k)
    {
      a[0] /= k;
      return *this;
    }

    //===== linear maps

    //! y += A x
    void umv (const FieldVector<K,1>& x, FieldVector<K,1>& y) const
    {
      y.p += a[0] * x.p;
    }

    //! y += A^T x
    void umtv (const FieldVector<K,1>& x, FieldVector<K,1>& y) const
    {
      y.p += a[0] * x.p;
    }

    //! y += A^H x
    void umhv (const FieldVector<K,1>& x, FieldVector<K,1>& y) const
    {
      y.p += fm_ck(a[0]) * x.p;
    }

    //! y -= A x
    void mmv (const FieldVector<K,1>& x, FieldVector<K,1>& y) const
    {
      y.p -= a[0] * x.p;
    }

    //! y -= A^T x
    void mmtv (const FieldVector<K,1>& x, FieldVector<K,1>& y) const
    {
      y.p -= a[0] * x.p;
    }

    //! y -= A^H x
    void mmhv (const FieldVector<K,1>& x, FieldVector<K,1>& y) const
    {
      y.p -= fm_ck(a[0]) * x.p;
    }

    //! y += alpha A x
    void usmv (const K& alpha, const FieldVector<K,1>& x, FieldVector<K,1>& y) const
    {
      y.p += alpha * a[0] * x.p;
    }

    //! y += alpha A^T x
    void usmtv (const K& alpha, const FieldVector<K,1>& x, FieldVector<K,1>& y) const
    {
      y.p += alpha * a[0] * x.p;
    }

    //! y += alpha A^H x
    void usmhv (const K& alpha, const FieldVector<K,1>& x, FieldVector<K,1>& y) const
    {
      y.p += alpha * fm_ck(a[0]) * x.p;
    }

    //===== norms

    //! frobenius norm: sqrt(sum over squared values of entries)
    double frobenius_norm () const
    {
      return sqrt(fvmeta_abs2(a[0]));
    }

    //! square of frobenius norm, need for block recursion
    double frobenius_norm2 () const
    {
      return fvmeta_abs2(a[0]);
    }

    //! infinity norm (row sum norm, how to generalize for blocks?)
    double infinity_norm () const
    {
      return fvmeta_abs(a[0]);
    }

    //! simplified infinity norm (uses Manhattan norm for complex values)
    double infinity_norm_real () const
    {
      return fvmeta_abs_real(a[0]);
    }

    //===== solve

    //! Solve system A x = b
    void solve (FieldVector<K,1>& x, const FieldVector<K,1>& b) const
    {
      x.p = b.p/a[0];
    }

    //! compute inverse
    void invert ()
    {
      a[0] = 1/a[0];
    }

    //! left multiplication
    FieldMatrix& leftmultiply (const FieldMatrix& M)
    {
      a[0] *= M.a[0];
      return *this;
    }

    //! left multiplication
    FieldMatrix& rightmultiply (const FieldMatrix& M)
    {
      a[0] *= M.a[0];
      return *this;
    }


    //===== sizes

    //! number of blocks in row direction
    int N () const
    {
      return 1;
    }

    //! number of blocks in column direction
    int M () const
    {
      return 1;
    }

    //! row dimension of block r
    int rowdim (int r) const
    {
      return 1;
    }

    //! col dimension of block c
    int coldim (int c) const
    {
      return 1;
    }

    //! dimension of the destination vector space
    int rowdim () const
    {
      return 1;
    }

    //! dimension of the source vector space
    int coldim () const
    {
      return 1;
    }

    //===== query

    //! return true when (i,j) is in pattern
    bool exists (int i, int j) const
    {
      return i==0 && j==0;
    }

    //===== conversion operator

    operator K () const {return a[0];}

  private:
    // the data, just a single row with a single scalar
    row_type a;
  };

  namespace FMatrixHelp {


    //! invert scalar without changing the original matrix
    template <typename K>
    static inline K invertMatrix (const FieldMatrix<K,1,1> &matrix, FieldMatrix<K,1,1> &inverse)
    {
Oliver Sander's avatar
Oliver Sander committed
      inverse[0][0] = 1.0/matrix[0][0];
      return matrix[0][0];
    }


    //! invert 2x2 Matrix without changing the original matrix
    template <typename K>
    static inline K invertMatrix (const FieldMatrix<K,2,2> &matrix, FieldMatrix<K,2,2> &inverse)
    {
      // code generated by maple
      K det = (matrix[0][0]*matrix[1][1] - matrix[0][1]*matrix[1][0]);
      K det_1 = 1.0/det;
      inverse[0][0] =   matrix[1][1] * det_1;
      inverse[0][1] = - matrix[0][1] * det_1;
      inverse[1][0] = - matrix[1][0] * det_1;
      inverse[1][1] =   matrix[0][0] * det_1;
      return det;
    }

    //! invert 3x3 Matrix without changing the original matrix
    template <typename K>
    static inline K invertMatrix (const FieldMatrix<K,3,3> &matrix, FieldMatrix<K,3,3> &inverse)
    {
      // code generated by maple
      K t4  = matrix[0][0] * matrix[1][1];
      K t6  = matrix[0][0] * matrix[1][2];
      K t8  = matrix[0][1] * matrix[1][0];
      K t10 = matrix[0][2] * matrix[1][0];
      K t12 = matrix[0][1] * matrix[2][0];
      K t14 = matrix[0][2] * matrix[2][0];

      K det = (t4*matrix[2][2]-t6*matrix[2][1]-t8*matrix[2][2]+
               t10*matrix[2][1]+t12*matrix[1][2]-t14*matrix[1][1]);
      K t17 = 1.0/det;

      inverse[0][0] =  (matrix[1][1] * matrix[2][2] - matrix[1][2] * matrix[2][1])*t17;
      inverse[0][1] = -(matrix[0][1] * matrix[2][2] - matrix[0][2] * matrix[2][1])*t17;
      inverse[0][2] =  (matrix[0][1] * matrix[1][2] - matrix[0][2] * matrix[1][1])*t17;
      inverse[1][0] = -(matrix[1][0] * matrix[2][2] - matrix[1][2] * matrix[2][0])*t17;
      inverse[1][1] =  (matrix[0][0] * matrix[2][2] - t14) * t17;
      inverse[1][2] = -(t6-t10) * t17;
      inverse[2][0] =  (matrix[1][0] * matrix[2][1] - matrix[1][1] * matrix[2][0]) * t17;
      inverse[2][1] = -(matrix[0][0] * matrix[2][1] - t12) * t17;
      inverse[2][2] =  (t4-t8) * t17;

      return det;
    }

    //! calculates ret = matrix * x
    template <typename K, int dim>
    static void multAssign(const FieldMatrix<K,dim,dim> &matrix, const FieldVector<K,dim> & x, FieldVector<K,dim> & ret)
    {
      for(int i=0; i<dim; i++)
      {
        ret[i] = 0.0;
        for(int j=0; j<dim; j++)
        {
          ret[i] += matrix[i][j]*x[j];
        }
      }
    }

    //! calculates ret = matrix * x
    template <typename K, int dim>
    static FieldVector<K,dim> mult(const FieldMatrix<K,dim,dim> &matrix, const FieldVector<K,dim> & x)
    {
      FieldVector<K,dim> ret;
      multAssign(matrix,x,ret);
      return ret;
    }

Oliver Sander's avatar
Oliver Sander committed
  /** @} end documentation */

} // end namespace

#endif