diff --git a/common/fmatrix.hh b/common/fmatrix.hh index ff6041596370b431f3c6e1189738f550b640193c..0df0daf17b1e9e496d14c52cfa8f1a43779ed29e 100644 --- a/common/fmatrix.hh +++ b/common/fmatrix.hh @@ -505,8 +505,116 @@ namespace Dune { private: // the data, very simply a built in array with row-wise ordering row_type p[(n > 0) ? n : 1]; + + struct ElimPivot + { + ElimPivot(size_type pivot[n]); + + 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_; + }; + + template<class Func> + void luDecomposition(FieldMatrix<K,n,n>& A, Func func) const; }; + template<typename K, int n, int m> + FieldMatrix<K,n,m>::ElimPivot::ElimPivot(size_type pivot[n]) + : pivot_(pivot) + { + for(int i=0; i < n; ++i) pivot[i]=i; + } + + template<typename K, int n, int m> + void FieldMatrix<K,n,m>::ElimPivot::swap(int i, int j) + { + std::swap(pivot_[i], pivot_[j]); + } + + template<typename K, int n, int m> + template<typename V> + FieldMatrix<K,n,m>::Elim<V>::Elim(V& rhs) + : rhs_(&rhs) + {} + + template<typename K, int n, int m> + template<typename V> + void FieldMatrix<K,n,m>::Elim<V>::swap(int i, int j) + { + std::swap((*rhs_)[i], (*rhs_)[j]); + } + + template<typename K, int n, int m> + template<typename V> + void FieldMatrix<K,n,m>:: + Elim<V>::operator()(const typename V::field_type& factor, int k, int i) + { + (*rhs_)[k] -= factor*(*rhs_)[i]; + } + template<typename K, int n, int m> + template<typename Func> + inline void FieldMatrix<K,n,m>::luDecomposition(FieldMatrix<K,n,n>& A, Func func) const + { + 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()); + + // 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]); + func.swap(i, imax); // swap the pivot or rhs + } + } + + // singular ? + if (pivmax<singthres) + DUNE_THROW(FMatrixError,"matrix is singular"); + + // eliminate + for (int k=i+1; k<n; k++) + { + K factor = A[k][i]/A[i][i]; + A[k][i] = factor; + for (int j=i+1; j<n; j++) + A[k][j] -= factor*A[i][j]; + func(factor, k, i); + } + } + } + template <class K, int n, int m> template <class V> inline void FieldMatrix<K,n,m>::solve(V& x, const V& b) const @@ -533,65 +641,20 @@ namespace Dune { x[1] = detinv*(p[0][0]*b[1]-p[1][0]*b[0]); } else { - - // //////////////////////////////////////////////////// - // Gaussian elimination with maximum column pivot - // //////////////////////////////////////////////////// - - // make a copy of a to store factorization + V& rhs = x; // use x to store rhs + rhs = b; // copy data + Elim<V> elim(rhs); FieldMatrix<K,n,n> A(*this); - 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()); - 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"); - - // 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]; - } - } + luDecomposition(A, elim); // backsolve - for (int i=n-1; i>=0; i--) - { + 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]; } - } - } template <class K, int n, int m> @@ -622,69 +685,35 @@ namespace Dune { } else { FieldMatrix<K,n,n> A(*this); - 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()); - - // 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"); - - // 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]; - } - } + size_type pivot[n]; + luDecomposition(A, ElimPivot(pivot)); + FieldMatrix<K,n,m>& L=A; + FieldMatrix<K,n,m>& U=A; // initialize inverse - *this = 0; - for (int i=0; i<n; i++) p[i][i] = 1; + *this=0; + + for(int i=0; i<n; ++i) + p[pivot[i]][i]=1; // L Y = I; multiple right hand sides - for (int i=0; i<n; i++) + for (int i=0; i<n; i++) { + int row = pivot[i]; for (int j=0; j<i; j++) for (int k=0; k<n; k++) - p[i][k] -= L[i][j]*p[j][k]; + p[row][k] -= L[i][j]*p[j][k]; + } // U A^{-1} = Y - for (int i=n-1; i>=0; i--) - for (int k=0; k<n; k++) - { + for (int i=n-1; i>=0; i--) { + int row = pivot[i]; + for (int k=0; k<n; k++) { for (int j=i+1; j<n; j++) - p[i][k] -= U[i][j]*p[j][k]; - p[i][k] /= U[i][i]; + p[row][k] -= U[i][j]*p[j][k]; + p[row][k] /= U[i][i]; } - + } } - } // implementation of the determinant diff --git a/common/test/fmatrixtest.cc b/common/test/fmatrixtest.cc index 7be2dd34596007efd1014c781b79a7857a6ab74a..b97372681063fcf229dbf0a14e0a0f9ff6324d3c 100644 --- a/common/test/fmatrixtest.cc +++ b/common/test/fmatrixtest.cc @@ -8,6 +8,107 @@ using namespace Dune; +template<typename T, int n> +int test_invert_solve(T A_data[n*n], T inv_data[n*n], + T x_data[n], T b_data[n]) +{ + int ret=0; + + std::cout <<"Checking invertion of:"<<std::endl; + + FieldMatrix<T,n,n> A, inv, calced_inv; + FieldVector<T,n> x, b, calced_x; + + for(int i =0; i < n; ++i) { + x[i]=x_data[i]; + b[i]=b_data[i]; + for(int j=0; j <n; ++j) { + A[i][j] = A_data[i*n+j]; + inv[i][j] = inv_data[i*n+j]; + } + } + + std::cout<<A<<std::endl; + FieldMatrix<T,n,n> copy(A); + A.invert(); + + calced_inv = A; + A-=inv; + + + bool equal=true; + double singthres = FMatrixPrecision<>::singular_limit()*10; + for(int i =0; i < n; ++i) + for(int j=0; j <n; ++j) + if(std::abs(A[i][j])>singthres) { + std::cerr<<"calculated inverse wrong at ("<<i<<","<<j<<")"<<std::endl; + equal=false; + } + + if(!equal) { + ret++; + std::cerr<<"Calculated inverse was:"<<std::endl; + std::cerr <<calced_inv<<std::endl; + std::cerr<<"Should have been"<<std::endl; + std::cerr<<inv; + }else + std::cout<<"Result is"<<std::endl<<calced_inv<<std::endl; + + + std::cout<<"Checking solution for rhs="<<b<<std::endl; + + copy.solve(calced_x, b); + FieldVector<T,n> xcopy(calced_x); + xcopy-=x; + + equal=true; + + for(int i =0; i < n; ++i) + if(std::abs(xcopy[i])>singthres) { + std::cerr<<"calculated isolution wrong at ("<<i<<")"<<std::endl; + equal=false; + } + + if(!equal) { + ret++; + std::cerr<<"Calculated solution was:"<<std::endl; + std::cerr <<calced_x<<std::endl; + std::cerr<<"Should have been"<<std::endl; + std::cerr<<x; + std::cerr<<"difference is "<<xcopy<<std::endl; + }else + std::cout<<"Result is "<<calced_x<<std::endl; + + return ret; +} + + +int test_invert_solve() +{ + int ret=0; + + double A_data[9] = {1, 5, 7, 2, 14, 15, 4, 40, 39}; + double inv_data[9] = {-9.0/4, 85.0/24, -23.0/24, -3.0/4, 11.0/24, -1.0/24, 1, -5.0/6, 1.0/6}; + double b[3] = {32,75,201}; + double x[3] = {1,2,3}; + + + ret += test_invert_solve<double,3>(A_data, inv_data, x, b); + + double A_data1[9] = {0, 1, 0, 1, 0, 0, 0, 0, 1}; + double b1[3] = {0,1,2}; + double x1[3] = {1,0,2}; + + ret += test_invert_solve<double,3>(A_data1, A_data1, x1, b1); + + double A_data2[9] ={3, 1, 6, 2, 1, 3, 1, 1, 1}; + double inv_data2[9] ={-2, 5, -3, 1, -3, 3, 1, -2, 1}; + double b2[3] = {2, 7, 4}; + double x2[3] = {19,-7,-8}; + + return ret + test_invert_solve<double,3>(A_data2, inv_data2, x2, b2); +} + template<class K, int n, int m> void test_matrix() { @@ -82,6 +183,7 @@ int main() test_matrix<double, 1, 1>(); test_matrix<int, 10, 5>(); test_matrix<double, 5, 10>(); + return test_invert_solve(); } catch (Dune::Exception & e) {