Gowdy solver
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
numMethods/bandSol.h
Go to the documentation of this file.
00001 /**
00002  *  @file      bandSol.h
00003  *  @brief     Solving a linear equation `A * x = b` for a band-diagonal matrix `A`.
00004  *  @authors   Mikica Kocic, Wilkinson & Reinsch, Press et al
00005  *  @copyright GNU General Public License (GPLv3).
00006  */
00007 
00008 /////////////////////////////////////////////////////////////////////////////////////////
00009 /** @defgroup g11 Numerical Methods                                                    */
00010 /////////////////////////////////////////////////////////////////////////////////////////
00011                                                                                    /*@{*/
00012 /** BandLUDecomposition implements a method for solving linear equations
00013  *  `A * x = b` for a band-diagonal matrix `A` using LU decomposition.
00014  *
00015  *  `A` is band-diagonal with `m1` rows below the diagonal and `m2` rows above.
00016  *  The input vector is `x[0..n-1]` and the output vector is `b[0..n-1]`.
00017  *
00018  *  The LU factorization refers to the factorization of `A`, with proper row and/or
00019  *  column orderings or permutations, into two factors -- a lower triangular matrix
00020  *  `L` and an upper triangular matrix `U` such that `A = L U`.
00021  *
00022  *  The array `A[0..n-1][0..m1+m2]` stores `A` as follows: The diagonal elements are in
00023  *  `A[0..n-1][m1]`. Subdiagonal elements are in `A[j ..n-1][0..m1-1]` with `j > 0`
00024  *  appropriate to the number of  elements on each subdiagonal. Superdiagonal elements
00025  *  are in `A[0..j ][m1+1..m1+m2]` with `j < n-1` appropriate to the number of elements
00026  *  on each superdiagonal.
00027  *
00028  *  The implementation is based on the routines `bandet1` and `bansol1` in
00029  *  Wilkinson, J.H., and Reinsch, C. 1971, Linear Algebra, vol. II of Handbook for
00030  *  Automatic Computation (New York: Springer), Chapter I/6. The C++ code is based on
00031  *  `bandec.h` @cite Press:2007nrec, Section 2.4.2 Band-Diagonal Systems (pp. 58--61).
00032  */
00033 class BandLUDecomposition
00034 {
00035     Int nn;        //!< The full matrix size.
00036     Int m1;        //!< The size of subdiagonal.
00037     Int m2;        //!< The size of superdiagonal.
00038     MatReal L;     //!< The lower triangular matrix (stored compactly).
00039     MatReal U;     //!< The upper triangular matrix (stored compactly).
00040     VecInt indx;   //!< The row permutation index effected by the partial pivoting
00041     Real d;        //!< `+/-1` whether the number of row interchanges was even or odd
00042 
00043     static inline void SWAP( Real& x, Real& y ) {
00044         Real temp = x;  x = y;  y = temp;
00045     }
00046 
00047 public:
00048 
00049     /** The constructor which does the LU decomposition. It takes as arguments the
00050      *  compactly stored matrix `A`, and the integers `m1 >= 0` (subdiagonal size)
00051      *  and `m2 >= 0` (superdiagonal size). The upper and lower triangular matrices
00052      *  are stored in `U` and `L`, respectively.
00053      */
00054     BandLUDecomposition( MatReal_I& A, const Int mm1, const Int mm2 );
00055 
00056     /** Gets the lower triangular matrix.
00057      */
00058     MatReal& getL () {
00059         return L;
00060     }
00061 
00062     /** Gets the upper triangular matrix.
00063      */
00064     MatReal& getU () {
00065         return U;
00066     }
00067 
00068     /** Given a right-hand side vector `b[0..n-1]`, solves the band-diagonal
00069      *  linear equation `A * x = b`.
00070      */
00071     void solve( VecReal_I& b, VecReal_O& x );
00072 
00073     /** Performs a sanity check test with sample data.
00074      */
00075     static void sanityCheck ();
00076 
00077     /** Returns the determinant of the matrix A.
00078      */
00079     Real det () const
00080     {
00081         Real dd = d;
00082         for( Int i = 0; i < nn; ++i ) {
00083             dd *= U[i][0];
00084         }
00085         return dd;
00086     }
00087 };
00088 
00089 /** Given an `n*n` band-diagonal matrix `A` with `m1` subdiagonal rows and `m2`
00090  *  superdiagonal rows, compactly stored in the array `A[0..n-1][0..m1+m2]`,
00091  *  an LU decomposition of a rowwise permutation of A is constructed. The  upper
00092  *  and lower triangular matrices are stored in `U` and `L`, respectively.
00093  *  The stored vector `indx[0..n-1]` records the row permutation effected by the partial
00094  *  pivoting; `d` is `+/-1` depending on whether the number of row interchanges was even
00095  *  or odd, respectively.
00096  *
00097  *  Some pivoting is possible within the storage limitations, and the routine
00098  *  does take advantage of the opportunity. Also, when `TINY` is returned as
00099  *  a diagonal element of `U`, then the original matrix (perhaps as modified by roundoff
00100  *  error) is in fact singular.
00101  */
00102 BandLUDecomposition::BandLUDecomposition(
00103     MatReal_I& A,   //!< Compactly stored diagonal-band matrix
00104     const Int mm1,  //!< Size of subdiagonal
00105     const Int mm2   //!< Size of superdiagonal
00106 )
00107     : nn(A.nrows()), m1(mm1), m2(mm2), L(nn,m1), U(A), indx(nn)
00108 {
00109     const Real TINY = 1e-40;
00110 
00111     Int mm = m1 + m2 + 1;
00112     Int l = m1;
00113 
00114     for( Int i = 0; i < m1; ++i ) {
00115         for( Int j = m1 - i; j < mm; ++j ) {
00116             U[i][j-l] = U[i][j];
00117         }
00118         l--;
00119         for( Int j = mm - l - 1; j < mm; ++j ) {
00120             U[i][j] = 0;
00121         }
00122     }
00123 
00124     Real d = 1;
00125     l = m1;
00126 
00127     for( Int k = 0; k < nn; ++k )
00128     {
00129         Real dum = U[k][0];
00130         Int i = k;
00131         if( l < nn ) {
00132             l++;
00133         }
00134         for( Int j = k + 1; j < l; ++j ) {
00135             if( abs( U[j][0] ) > abs( dum ) ) {
00136                 dum = U[j][0];
00137                 i = j;
00138             }
00139         }
00140         indx[k] = i + 1;
00141         if( dum == 0 ) {
00142             U[k][0]=TINY;
00143         }
00144         if (i != k) {
00145             d = -d;
00146             for( Int j = 0; j < mm; ++j ) {
00147                 SWAP( U[k][j], U[i][j] );
00148             }
00149         }
00150         for( i = k + 1; i < l; ++i )
00151         {
00152             dum = U[i][0] / U[k][0];
00153             L[k][i-k-1] = dum;
00154             for( Int j = 1; j < mm; ++j ) {
00155                 U[i][j-1] = U[i][j] - dum * U[k][j];
00156             }
00157             U[i][mm-1] = 0;
00158         }
00159     }
00160 }
00161 
00162 /** Given a right-hand side vector `b[0..n-1]`, solves the band-diagonal
00163  *  linear equation `A * x = b`.
00164  */
00165 void BandLUDecomposition::solve( VecReal_I& b, VecReal_O& x )
00166 {
00167     Int mm = m1 + m2 + 1;
00168     Int l  = m1;
00169 
00170     for( Int k = 0; k < nn; ++k ) {
00171         x[k] = b[k];
00172     }
00173 
00174     for( Int k = 0; k < nn; ++k )
00175     {
00176         Int j = indx[k] - 1;
00177         if( j != k ) {
00178             SWAP( x[k], x[j] );
00179         }
00180         if( l < nn ) {
00181             l++;
00182         }
00183         for( j = k + 1; j < l; ++j ) {
00184             x[j] -= L[k][j-k-1] * x[k];
00185         }
00186     }
00187 
00188     l = 1;
00189 
00190     for( Int i = nn - 1; i >= 0; --i )
00191     {
00192         Real dum = x[i];
00193         for( Int k = 1; k < l; ++k ) {
00194             dum -= U[i][k]*x[k+i];
00195         }
00196 
00197         x[i] = dum / U[i][0];
00198         if( l < mm ) {
00199             l++;
00200         }
00201     }
00202 }
00203 
00204 /** Sanity check for BandLUDecomposition.
00205  */
00206 void BandLUDecomposition::sanityCheck ()
00207 {
00208     const Real A_values[] = {
00209     // -2  -1   0  +1  <--- the matrix diagonal lies at [0]
00210         0,  0,  3,  1,
00211         0,  4,  1,  5,
00212         9,  2,  6,  5,
00213         3,  5,  8,  9,
00214         7,  9,  3,  2,
00215         3,  8,  4,  6,
00216         2,  4,  4,  0
00217     };
00218     const Real b_values[] = {
00219         7, 6, 5, 4, 3, 2, 1
00220     };
00221 
00222     MatReal_I A( 7, 4, A_values );
00223     VecReal_I b( 7, b_values );
00224     VecReal_O x( 7 );
00225 
00226     BandLUDecomposition( A, 2, 1 ).solve( b, x );
00227 
00228     for( Int i = 0; i < 7; ++i ) {
00229         std::cout << x[i] << ", ";
00230     }
00231 
00232     std::cout << std::endl << std::endl << "Compare to:" << std::endl << std::endl
00233          << "4.66912, -7.00737, -1.13382, -3.24088, 6.29092, 10.616, -13.5114"
00234          << std::endl << std::endl;
00235 }
00236                                                                                    /*@}*/
00237 /////////////////////////////////////////////////////////////////////////////////////////