computation/solver/preconditioner/SymmetricIlutPreconditioning.cxx

00001 // Copyright (C) 2010 Marc Duruflé
00002 //
00003 // This file is part of the linear-algebra library Seldon,
00004 // http://seldon.sourceforge.net/.
00005 //
00006 // Seldon is free software; you can redistribute it and/or modify it under the
00007 // terms of the GNU Lesser General Public License as published by the Free
00008 // Software Foundation; either version 2.1 of the License, or (at your option)
00009 // any later version.
00010 //
00011 // Seldon is distributed in the hope that it will be useful, but WITHOUT ANY
00012 // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00013 // FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
00014 // more details.
00015 //
00016 // You should have received a copy of the GNU Lesser General Public License
00017 // along with Seldon. If not, see http://www.gnu.org/licenses/.
00018 
00019 
00020 #ifndef SELDON_FILE_SYMMETRIC_ILUT_PRECONDITIONING_CXX
00021 
00022 namespace Seldon
00023 {
00024 
00026   template<class real, class cplx, class Allocator1, class Allocator2>
00027   void GetIlut(const IlutPreconditioning<real, cplx, Allocator1>& param,
00028                Matrix<cplx, Symmetric, ArrayRowSymSparse, Allocator2>& A)
00029   {
00030     int size_row;
00031     int n = A.GetN();
00032     int lfil = param.GetFillLevel();
00033     real zero = 0.0;
00034     real droptol = param.GetDroppingThreshold();
00035     real alpha = param.GetDiagonalCoefficient();
00036     bool variable_fill = false;
00037     bool standard_dropping = true;
00038     int type_factorisation = param.GetFactorisationType();
00039     int additional_fill = param.GetAdditionalFillNumber();
00040     int print_level = param.GetPrintLevel();
00041     if (type_factorisation == param.ILUT)
00042       standard_dropping = false;
00043     else if (type_factorisation == param.ILU_D)
00044       standard_dropping = true;
00045     else if (type_factorisation == param.ILUT_K)
00046       {
00047         variable_fill = true;
00048         standard_dropping = false;
00049       }
00050     else if (type_factorisation == param.ILU_0)
00051       {
00052         GetIlu0(A);
00053         return;
00054       }
00055     else if (type_factorisation == param.MILU_0)
00056       {
00057         GetMilu0(A);
00058         return;
00059       }
00060     else if (type_factorisation == param.ILU_K)
00061       {
00062         GetIluk(lfil, A);
00063         return;
00064       }
00065 
00066     cplx fact, s, t; real tnorm;
00067     int length_lower, length_upper, jpos, jrow;
00068     int i_row, j_col, index_lu, length;
00069     int i, j, k;
00070 
00071     if (lfil < 0)
00072       {
00073         cout << "Incorrect fill level." << endl;
00074         abort();
00075       }
00076 
00077     typedef Vector<cplx, VectFull, Allocator2> VectCplx;
00078     VectCplx Row_Val(n);
00079     IVect Index(n), Row_Ind(n);
00080     Row_Val.Zero(); Row_Ind.Fill(-1);
00081 
00082     Index.Fill(-1);
00083 
00084     bool element_dropped; cplx dropsum;
00085 
00086     // We convert A into an unsymmetric matrix.
00087     Matrix<cplx, General, ArrayRowSparse, Allocator2> B;
00088     Seldon::Copy(A, B);
00089 
00090     A.Clear();
00091     A.Reallocate(n, n);
00092 
00093     // Main loop.
00094     int new_percent = 0, old_percent = 0;
00095     for (i_row = 0; i_row < n; i_row++)
00096       {
00097         // Progress bar if print level is high enough.
00098         if (print_level > 0)
00099           {
00100             new_percent = int(real(i_row)/(n-1)*80);
00101             for (int percent = old_percent; percent < new_percent; percent++)
00102               {
00103                 cout << "#"; cout.flush();
00104               }
00105 
00106             old_percent = new_percent;
00107           }
00108 
00109         // 1-norm of the row of initial matrix.
00110         size_row = B.GetRowSize(i_row);
00111         tnorm = zero;
00112         dropsum = zero;
00113         for (k = 0 ; k < size_row; k++)
00114           tnorm += abs(B.Value(i_row, k));
00115 
00116         if (tnorm == zero)
00117           {
00118             cout << "Structurally singular matrix." << endl;
00119             cout << "Norm of row " << i_row << " is equal to 0." << endl;
00120             abort();
00121           }
00122 
00123         // tnorm is the sum of absolute value of coefficients of row i_row
00124         tnorm /= real(size_row);
00125         if (variable_fill)
00126           lfil = size_row + additional_fill;
00127 
00128 
00129         // Separating lower part from upper part for this row.
00130         length_upper = 1;
00131         length_lower = 0;
00132         Row_Ind(i_row) = i_row;
00133         Row_Val(i_row) = 0.0;
00134         Index(i_row) = i_row;
00135 
00136         for (j = 0; j < size_row; j++)
00137           {
00138             k = B.Index(i_row,j);
00139             t = B.Value(i_row,j);
00140             if (k < i_row)
00141               {
00142                 Row_Ind(length_lower) = k;
00143                 Row_Val(length_lower) = t;
00144                 Index(k) = length_lower;
00145                 ++length_lower;
00146               }
00147             else if (k == i_row)
00148               {
00149                 Row_Val(i_row) = t;
00150               }
00151             else
00152               {
00153                 jpos = i_row + length_upper;
00154                 Row_Ind(jpos) = k;
00155                 Row_Val(jpos) = t;
00156                 Index(k) = jpos;
00157                 length_upper++;
00158               }
00159           }
00160 
00161         // This row of B is cleared.
00162         B.ClearRow(i_row);
00163 
00164         j_col = 0;
00165         length = 0;
00166 
00167 
00168         // We eliminate previous rows.
00169         while (j_col <length_lower)
00170           {
00171             // In order to do the elimination in the correct order, we must
00172             // select the smallest column index.
00173             jrow = Row_Ind(j_col);
00174             k = j_col;
00175 
00176             // We determine smallest column index.
00177             for (j = (j_col+1) ; j < length_lower; j++)
00178               {
00179                 if (Row_Ind(j) < jrow)
00180                   {
00181                     jrow = Row_Ind(j);
00182                     k = j;
00183                   }
00184               }
00185 
00186             // If needed, we exchange positions of this element in
00187             // Row_Ind/Row_Val so that it appears first.
00188             if (k != j_col)
00189               {
00190 
00191                 j = Row_Ind(j_col);
00192                 Row_Ind(j_col) = Row_Ind(k);
00193                 Row_Ind(k) = j;
00194 
00195                 Index(jrow) = j_col;
00196                 Index(j) = k;
00197 
00198                 s = Row_Val(j_col);
00199                 Row_Val(j_col) = Row_Val(k);
00200                 Row_Val(k) = s;
00201               }
00202 
00203             // Zero out element in row by setting Index to -1.
00204             Index(jrow) = -1;
00205 
00206             element_dropped = false;
00207             if (standard_dropping)
00208               if (abs(Row_Val(j_col)) <= droptol*tnorm)
00209                 {
00210                   dropsum += Row_Val(j_col);
00211                   element_dropped = true;
00212                 }
00213 
00214             // Gets the multiplier for row to be eliminated.
00215             if (!element_dropped)
00216               {
00217                 fact = Row_Val(j_col) * A.Value(jrow, 0);
00218 
00219                 if (!standard_dropping)
00220                   {
00221                     if (abs(fact) <= droptol)
00222                       element_dropped = true;
00223                   }
00224               }
00225 
00226             if (!element_dropped)
00227               {
00228                 // Combines current row and row jrow.
00229                 for (k = 1; k < A.GetRowSize(jrow); k++)
00230                   {
00231                     s = fact * A.Value(jrow,k);
00232                     j = A.Index(jrow,k);
00233 
00234                     jpos = Index(j);
00235                     if (j >= i_row)
00236                       {
00237 
00238                         // Dealing with upper part.
00239                         if (jpos == -1)
00240                           {
00241 
00242                             // This is a fill-in element.
00243                             i = i_row + length_upper;
00244                             Row_Ind(i) = j;
00245                             Index(j) = i;
00246                             Row_Val(i) = -s;
00247                             ++length_upper;
00248                           }
00249                         else
00250                           {
00251                             // This is not a fill-in element.
00252                             Row_Val(jpos) -= s;
00253                           }
00254                       }
00255                     else
00256                       {
00257                         // Dealing  with lower part.
00258                         if (jpos == -1)
00259                           {
00260                             // This is a fill-in element.
00261                             Row_Ind(length_lower) = j;
00262                             Index(j) = length_lower;
00263                             Row_Val(length_lower) = -s;
00264                             ++length_lower;
00265                           }
00266                         else
00267                           {
00268                             // This is not a fill-in element.
00269                             Row_Val(jpos) -= s;
00270                           }
00271                       }
00272                   }
00273 
00274                 // We store this pivot element (from left to right -- no
00275                 // danger of overlap with the working elements in L (pivots).
00276                 Row_Val(length) = fact;
00277                 Row_Ind(length) = jrow;
00278                 ++length;
00279               }
00280 
00281             j_col++;
00282           }
00283 
00284         // Resets double-pointer to zero (U-part).
00285         for (k = 0; k < length_upper; k++)
00286           Index(Row_Ind(i_row+k )) = -1;
00287 
00288         // Updating U-matrix -- first apply dropping strategy.
00289 
00290         length = 0;
00291         for (k = 1; k <= (length_upper-1); k++)
00292           {
00293             if (abs(Row_Val(i_row+k)) > droptol * tnorm)
00294               {
00295                 ++length;
00296                 Row_Val(i_row+length) = Row_Val(i_row+k);
00297                 Row_Ind(i_row+length) = Row_Ind(i_row+k);
00298               }
00299             else
00300               dropsum += Row_Val(i_row+k);
00301           }
00302 
00303 
00304         if (!standard_dropping)
00305           {
00306             length_upper = length + 1;
00307             length = min(length_upper, lfil);
00308 
00309             qsplit_ilut(Row_Val, Row_Ind, i_row+1,
00310                         i_row+length_upper, i_row+length+1, tnorm);
00311           }
00312         else
00313           length++;
00314 
00315         // Copies U-part in matrix A.
00316         A.ReallocateRow(i_row, length);
00317         index_lu = 1;
00318         for (k = (i_row+1) ; k <= (i_row+length-1) ; k++)
00319           {
00320             A.Index(i_row,index_lu) = Row_Ind(k);
00321             A.Value(i_row,index_lu) = Row_Val(k);
00322             ++index_lu;
00323           }
00324 
00325         // Stores the inverse of the diagonal element of u.
00326         if (standard_dropping)
00327           Row_Val(i_row) += alpha*dropsum;
00328 
00329         if (Row_Val(i_row) == zero)
00330           Row_Val(i_row) = (droptol + 1e-4) * tnorm;
00331 
00332         A.Value(i_row,0) = 1.0 / Row_Val(i_row);
00333 
00334       } // end main loop.
00335 
00336     if (print_level > 0)
00337       cout<<endl;
00338 
00339     // for each row of A, we divide by diagonal value
00340     for (int i = 0; i < n; i++)
00341       for (int j = 1; j < A.GetRowSize(i); j++)
00342         A.Value(i,j) *= A.Value(i,0);
00343 
00344     if (print_level > 0)
00345       cout << "The matrix takes " <<
00346         int((A.GetDataSize()*(sizeof(cplx)+4))/(1024*1024)) << " MB" << endl;
00347 
00348   }
00349 
00350 
00351   template<class cplx, class Allocator>
00352   void GetIluk(int lfil,
00353                Matrix<cplx, Symmetric, ArrayRowSymSparse, Allocator>& A)
00354   {
00355     int n = A.GetM();
00356     Vector<cplx, VectFull, CallocAlloc<cplx> > w;
00357     w.Reallocate(n+1);
00358     IVect jw(3*n);
00359     Vector<IVect, VectFull, NewAlloc<IVect> > levs(n);
00360 
00361     // Local variables.
00362     cplx fact, s, t;
00363     int length_lower, length_upper, jpos, jrow, i_row, j_col;
00364     int i, j, k, index_lu, length;
00365     bool element_dropped;
00366 
00367     // Initializes nonzero indicator array.
00368     int n2 = 2*n, jlev, k_, size_upper;
00369     jw.Fill(-1);
00370 
00371     // We convert A into an unsymmetric matrix.
00372     Matrix<cplx, General, ArrayRowSparse, Allocator> B;
00373     Seldon::Copy(A,B);
00374 
00375     // Main loop.
00376     for (i_row = 0; i_row < n; i_row++)
00377       {
00378         int size_row = A.GetRowSize(i_row);
00379 
00380         // Unpacks L-part and U-part of row of A in arrays w, jw.
00381         length_upper = 1;
00382         length_lower = 0;
00383         jw(i_row) = i_row;
00384         w(i_row) = 0.0;
00385         jw(n + i_row) = i_row;
00386 
00387         for (j = 0; j < size_row; j++)
00388           {
00389             k = B.Index(i_row,j);
00390             t = B.Value(i_row,j);
00391             if (k < i_row)
00392               {
00393                 jw(length_lower) = k;
00394                 w(length_lower) = t;
00395                 jw(n + k) = length_lower;
00396                 jw(n2+length_lower) = -1;
00397                 ++length_lower;
00398               }
00399             else if (k == i_row)
00400               {
00401                 w(i_row) = t;
00402                 jw(n2+length_lower) = -1;
00403               }
00404             else
00405               {
00406                 jpos = i_row + length_upper;
00407                 jw(jpos) = k;
00408                 w(jpos) = t;
00409                 jw(n + k) = jpos;
00410                 length_upper++;
00411               }
00412           }
00413 
00414         B.ClearRow(i_row);
00415 
00416         j_col = 0;
00417         length = 0;
00418 
00419         // Eliminates previous rows.
00420         while (j_col <length_lower)
00421           {
00422 
00423             // In order to do the elimination in the correct order, we must
00424             // select the smallest column index among jw(k); k = j_col + 1,
00425             // ..., length_lower.
00426             jrow = jw(j_col);
00427             k = j_col;
00428 
00429             // Determines smallest column index.
00430             for (j = (j_col+1) ; j < length_lower; j++)
00431               {
00432                 if (jw(j) < jrow)
00433                   {
00434                     jrow = jw(j);
00435                     k = j;
00436                   }
00437               }
00438 
00439             if (k!=j_col)
00440               {
00441                 // Exchanges in jw.
00442                 j = jw(j_col);
00443                 jw(j_col) = jw(k);
00444                 jw(k) = j;
00445 
00446                 // Exchanges in jw(n+  (pointers/ nonzero indicator).
00447                 jw(n+jrow) = j_col;
00448                 jw(n+j) = k;
00449 
00450                 // Exchanges in jw(n2+  (levels).
00451                 j = jw(n2+j_col);
00452                 jw(n2+j_col)  = jw(n2+k);
00453                 jw(n2+k) = j;
00454 
00455                 // Exchanges in w.
00456                 s = w(j_col);
00457                 w(j_col) = w(k);
00458                 w(k) = s;
00459               }
00460 
00461             // Zero out element in row by setting jw(n+jrow) to zero.
00462 
00463             jw(n + jrow) = -1;
00464 
00465             element_dropped = false;
00466 
00467             // Gets the multiplier for row to be eliminated (jrow).
00468             fact = w(j_col) * A.Value(jrow,0);
00469 
00470             jlev = jw(n2+j_col) + 1;
00471             if (jlev > lfil)
00472               element_dropped = true;
00473 
00474             if (!element_dropped)
00475               {
00476                 // Combines current row and row jrow.
00477                 k_ = 0;
00478                 for (k = 1; k < A.GetRowSize(jrow) ; k++)
00479                   {
00480                     s = fact * A.Value(jrow,k);
00481                     j = A.Index(jrow,k);
00482 
00483                     jpos = jw(n + j);
00484 
00485                     if (j >= i_row)
00486                       {
00487                         // Dealing with upper part.
00488                         if (jpos == -1)
00489                           {
00490                             // This is a fill-in element.
00491                             i = i_row + length_upper;
00492                             jw(i) = j;
00493                             jw(n + j) = i;
00494                             w(i) = -s;
00495 
00496                             jw(n2+i) = jlev + levs(jrow)(k_) + 1;
00497                             ++length_upper;
00498                           }
00499                         else
00500                           {
00501                             // This is not a fill-in element.
00502                             w(jpos) -= s;
00503                             jw(n2+jpos) = min(jw(n2+jpos),
00504                                               jlev+levs(jrow)(k_)+1);
00505                           }
00506                       }
00507                     else
00508                       {
00509                         // Dealing  with lower part.
00510                         if (jpos == -1)
00511                           {
00512                             // This is a fill-in element.
00513                             jw(length_lower) = j;
00514                             jw(n + j) = length_lower;
00515                             w(length_lower) = -s;
00516                             jw(n2+length_lower) = jlev + levs(jrow)(k_) + 1;
00517                             ++length_lower;
00518                           }
00519                         else
00520                           {
00521                             // This is not a fill-in element.
00522                             w(jpos) -= s;
00523                             jw(n2+jpos) = min(jw(n2+jpos),
00524                                               jlev+levs(jrow)(k_)+1);
00525                           }
00526                       }
00527                     k_++;
00528                   }
00529 
00530               }
00531 
00532             // Stores this pivot element from left to right : no danger of
00533             // overlap with the working elements in L (pivots).
00534             w(j_col) = fact;
00535             jw(j_col) = jrow;
00536 
00537             j_col++;
00538           }
00539 
00540         // Resets double-pointer to zero (U-part).
00541         for (k = 0; k < length_upper; k++)
00542           jw(n + jw(i_row + k )) = -1;
00543 
00544         // Updates L-matrix.
00545         size_row = 1; // we have the diagonal value.
00546 
00547         // Size of U-matrix.
00548         size_upper = 0;
00549         for (k = (i_row+1) ; k <= (i_row+length_upper-1) ; k++)
00550           if (jw(n2+k) < lfil)
00551             size_upper++;
00552 
00553         size_row += size_upper;
00554         A.ReallocateRow(i_row, size_row);
00555         levs(i_row).Reallocate(size_upper);
00556 
00557         index_lu = 0;
00558 
00559         A.Value(i_row,index_lu) = 1.0 / w(i_row);
00560         A.Index(i_row,index_lu++) = i_row;
00561 
00562         // Updates U-matrix -- first apply dropping strategy.
00563         for (k = (i_row+1) ; k <= (i_row+length_upper-1) ; k++)
00564           {
00565             if (jw(n2+k) < lfil)
00566               {
00567                 A.Index(i_row,index_lu) = jw(k);
00568                 A.Value(i_row,index_lu) = w(k);
00569                 levs(i_row)(index_lu-1) = jw(n2+k);
00570                 ++index_lu;
00571               }
00572           }
00573       } // End main loop.
00574 
00575     // For each row of A, we divide by diagonal value.
00576     for (int i = 0; i < n; i++)
00577       for (int j = 1; j < A.GetRowSize(i); j++)
00578         A.Value(i,j) *= A.Value(i,0);
00579 
00580   }
00581 
00582 
00583   template<class cplx, class Allocator>
00584   void GetIlu0(Matrix<cplx, Symmetric, ArrayRowSymSparse, Allocator>& A)
00585   {
00586     cout << "Not implemented." << endl;
00587     abort();
00588   }
00589 
00590 
00591   template<class cplx, class Allocator>
00592   void GetMilu0(Matrix<cplx, Symmetric, ArrayRowSymSparse, Allocator>& A)
00593   {
00594     cout << "Not implemented." << endl;
00595     abort();
00596   }
00597 
00598 
00600 
00603   template<class real, class cplx, class Allocator,
00604            class Storage2, class Allocator2>
00605   void SolveLU(const Matrix<real, Symmetric, ArrayRowSymSparse, Allocator>& A,
00606                Vector<cplx, Storage2, Allocator2>& x)
00607   {
00608     int n = A.GetM(), j_row;
00609     cplx tmp;
00610 
00611     // We solve first L y = b.
00612     for (int i_col = 0; i_col < n ; i_col++)
00613       {
00614         for (int k = 1; k < A.GetRowSize(i_col) ; k++)
00615           {
00616             j_row = A.Index(i_col, k);
00617             x(j_row) -= A.Value(i_col, k)*x(i_col);
00618           }
00619       }
00620 
00621     // Inverting by diagonal D.
00622     for (int i_col = 0; i_col < n ; i_col++)
00623       x(i_col) *= A.Value(i_col,0);
00624 
00625     // Then we solve L^t x = y.
00626     for (int i_col = n-1; i_col >=0 ; i_col--)
00627       {
00628         tmp = x(i_col);
00629         for (int k = 1; k < A.GetRowSize(i_col) ; k++)
00630           {
00631             j_row = A.Index(i_col,k);
00632             tmp -= A.Value(i_col,k)*x(j_row);
00633           }
00634         x(i_col) = tmp;
00635       }
00636   }
00637 
00638 } // end namespace
00639 
00640 #define SELDON_SYMMETRIC_ILUT_PRECONDITIONING_CXX
00641 #endif