Warning: this documentation for the development version is under construction.

/home/vivien/public_html/.src_seldon/computation/solver/SparseCholeskyFactorisation.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_SPARSE_CHOLESKY_FACTORISATION_CXX
00021 
00022 #include "Ordering.cxx"
00023 #include "SparseCholeskyFactorisation.hxx"
00024 
00025 namespace Seldon
00026 {
00027 
00029 
00034   template<class T, class Prop, class Allocator>
00035   void GetCholesky(Matrix<T, Prop, ArrayRowSymSparse, Allocator>& A,
00036                    int print_level = 0)
00037   {
00038     int n = A.GetN();
00039     T t, s, fact;
00040     int j_col, jrow, index_lu, jpos;
00041     Vector<T> Row_Val(n);
00042     IVect Index(n), Row_Ind(n);
00043     Row_Val.Fill(0);
00044     Row_Ind.Fill(-1);
00045 
00046     Index.Fill(-1);
00047 
00048     // Conversion to unsymmetric matrix.
00049     Matrix<T, General, ArrayRowSparse, Allocator> B;
00050     Copy(A, B);
00051 
00052     // A is cleared.
00053     A.Clear();
00054     A.Reallocate(n, n);
00055 
00056     // Main loop over rows.
00057     int new_percent = 0, old_percent = 0;
00058     for (int i_row = 0; i_row < n; i_row++)
00059       {
00060         // Progress bar if print level is high enough.
00061         if (print_level > 0)
00062           {
00063             new_percent = int(double(i_row) / double(n-1) * 78.);
00064             for (int percent = old_percent; percent < new_percent; percent++)
00065               {
00066                 cout << "#";
00067                 cout.flush();
00068               }
00069             old_percent = new_percent;
00070           }
00071 
00072         int size_row = B.GetRowSize(i_row);
00073 
00074         // we are separating lower from upper part
00075         int length_lower = 0, length_upper = 1;
00076         Row_Ind(i_row) = i_row;
00077         Row_Val(i_row) = 0.0;
00078         Index(i_row) = i_row;
00079 
00080         for (int j = 0; j < size_row; j++)
00081           {
00082             int k = B.Index(i_row, j);
00083             t = B.Value(i_row, j);
00084             if (k < i_row)
00085               {
00086                 Row_Ind(length_lower) = k;
00087                 Row_Val(length_lower) = t;
00088                 Index(k) = length_lower;
00089                 length_lower++;
00090               }
00091             else if (k == i_row)
00092               Row_Val(i_row) = t;
00093             else
00094               {
00095                 jpos = i_row + length_upper;
00096                 Row_Ind(jpos) = k;
00097                 Row_Val(jpos) = t;
00098                 Index(k) = jpos;
00099                 length_upper++;
00100               }
00101           }
00102 
00103         B.ClearRow(i_row);
00104 
00105         j_col = 0;
00106         int length = 0;
00107 
00108         // Previous rows are eliminated.
00109         while (j_col < length_lower)
00110           {
00111             jrow = Row_Ind(j_col);
00112 
00113             // We search first element in lower part.
00114             int k = j_col;
00115             for (int j = (j_col+1) ; j < length_lower; j++)
00116               {
00117                 if (Row_Ind(j) < jrow)
00118                   {
00119                     jrow = Row_Ind(j);
00120                     k = j;
00121                   }
00122               }
00123 
00124 
00125             if (k != j_col)
00126               {
00127                 // If k different from j_col, we are exchanging positions.
00128                 int j = Row_Ind(j_col);
00129                 Row_Ind(j_col) = Row_Ind(k);
00130                 Row_Ind(k) = j;
00131 
00132                 Index(jrow) = j_col;
00133                 Index(j) = k;
00134 
00135                 s = Row_Val(j_col);
00136                 Row_Val(j_col) = Row_Val(k);
00137                 Row_Val(k) = s;
00138               }
00139 
00140             // Zero out element in row.
00141             Index(jrow) = -1;
00142             fact = Row_Val(j_col) * A.Value(jrow, 0);
00143 
00144             // Combines current row and row jrow.
00145             for (int k = 1; k < A.GetRowSize(jrow); k++)
00146               {
00147                 s = fact * A.Value(jrow, k);
00148                 int j = A.Index(jrow, k);
00149 
00150                 jpos = Index(j);
00151                 if (j >= i_row)
00152                   {
00153                     // Dealing with upper part.
00154                     if (jpos == -1)
00155                       {
00156                         // This is a fill-in element.
00157                         int i = i_row + length_upper;
00158                         Row_Ind(i) = j;
00159                         Index(j) = i;
00160                         Row_Val(i) = -s;
00161                         length_upper++;
00162                       }
00163                     else
00164                       {
00165                         // This is not a fill-in element.
00166                         Row_Val(jpos) -= s;
00167                       }
00168                   }
00169                 else
00170                   {
00171                     // Dealing  with lower part.
00172                     if (jpos == -1)
00173                       {
00174                         // This is a fill-in element.
00175                         Row_Ind(length_lower) = j;
00176                         Index(j) = length_lower;
00177                         Row_Val(length_lower) = -s;
00178                         length_lower++;
00179                       }
00180                     else
00181                       {
00182                         // This is not a fill-in element.
00183                         Row_Val(jpos) -= s;
00184                       }
00185                   }
00186               }
00187 
00188             // Stores this pivot element
00189             // (from left to right -- no danger of overlap
00190             // with the working elements in L (pivots).
00191             Row_Val(length) = fact;
00192             Row_Ind(length) = jrow;
00193             ++length;
00194             j_col++;
00195           }
00196 
00197         for (int k = 0; k < length_upper; k++)
00198           Index(Row_Ind(i_row + k)) = -1;
00199 
00200         // Now we can store the uppert part of row.
00201         size_row = length_upper;
00202         A.ReallocateRow(i_row, length_upper);
00203 
00204         // We store inverse of square root of diagonal element of u.
00205         if (Row_Val(i_row) <= 0)
00206           {
00207             cout << "Error during Cholesky factorization " << endl;
00208             cout << "Matrix must be definite positive " << endl;
00209             cout << "but diagonal element of row " << i_row
00210                  << "is equal to " << Row_Val(i_row) << endl;
00211 
00212 #ifdef SELDON_WITH_ABORT
00213             abort();
00214 #endif
00215           }
00216 
00217         A.Value(i_row, 0) = 1.0 / Row_Val(i_row);
00218         A.Index(i_row, 0) = i_row;
00219         index_lu = 1;
00220 
00221         // and extra-diagonal terms
00222         for (int k = i_row + 1; k < i_row + length_upper; k++)
00223           {
00224             A.Index(i_row, index_lu) = Row_Ind(k);
00225             A.Value(i_row, index_lu) = Row_Val(k);
00226             index_lu++;
00227           }
00228       }
00229 
00230     if (print_level > 0)
00231       {
00232         cout << endl;
00233         cout << "The matrix takes " <<
00234           int((A.GetDataSize() * (sizeof(T)+4)) / (1024*1024)) << " MB" << endl;
00235       }
00236 
00237     // Diagonal of A is replaced by its square root.
00238     for (int i = 0; i < n; i++)
00239       {
00240         A.Value(i, 0) = sqrt(A.Value(i,0));
00241         // and other elements multiplied by this value.
00242         for (int k = 1; k < A.GetRowSize(i); k++)
00243           A.Value(i, k) *= A.Value(i, 0);
00244 
00245         A.Value(i, 0) = 1.0 / A.Value(i, 0);
00246       }
00247   }
00248 
00249 
00251 
00256   template<class classTrans,
00257            class T0, class T1, class Prop, class Storage,
00258            class Allocator1, class Allocator2>
00259   void SolveCholesky(const classTrans& TransA,
00260                      const Matrix<T0, Prop, ArrayRowSymSparse, Allocator1>& A,
00261                      Vector<T1, Storage, Allocator2>& x)
00262   {
00263     int n = A.GetM();
00264     if (n <= 0)
00265       return;
00266 
00267     if (TransA.Trans())
00268       {
00269         // We solve L^T x = x
00270         int j;
00271         for (int i = n - 1; i >= 0; i--)
00272           {
00273             T1 val = x(i);
00274             for (int k = 1; k < A.GetRowSize(i) ; k++)
00275               {
00276                 j = A.Index(i, k);
00277                 val -= A.Value(i, k) * x(j);
00278               }
00279 
00280             x(i) = val / A.Value(i, 0);
00281           }
00282       }
00283     else
00284       {
00285         // We solve L x = x
00286         int j;
00287         for (int i = 0; i < n; i++)
00288           {
00289             x(i) /= A.Value(i, 0);
00290             for (int k = 1; k < A.GetRowSize(i) ; k++)
00291               {
00292                 j = A.Index(i, k);
00293                 x(j) -= A.Value(i, k) * x(i);
00294               }
00295           }
00296       }
00297   }
00298 
00299 
00301 
00306   template<class classTrans,
00307            class T, class Prop, class Allocator>
00308   void SolveCholesky(const classTrans& TransA,
00309                      const Matrix<T, Prop, RowSymSparse>& A,
00310                      Vector<T, VectFull, Allocator>& X)
00311   {
00312     int n = A.GetM();
00313     if (n <= 0)
00314       return;
00315 
00316     int* ind = A.GetInd();
00317     int* ptr = A.GetPtr();
00318     T* data = A.GetData();
00319     T val = 0;
00320 
00321     if (TransA.Trans())
00322       // Resolution of L^T x = x.
00323       for (int i = n - 1; i >= 0; i--)
00324         {
00325           val = X(i);
00326           for (int j = ptr[i] + 1; j < ptr[i+1]; j++)
00327             val -= data[j] * X(ind[j]);
00328 
00329           X(i) = val / data[ptr[i]];
00330         }
00331     else
00332       // Resolution of L x = x.
00333       for (int i = 0; i < n; i++)
00334         {
00335           X(i) /= data[ptr[i]];
00336           for (int j = ptr[i] + 1; j < ptr[i+1]; j++)
00337             X(ind[j]) -= data[j] * X(i);
00338         }
00339   }
00340 
00341 
00343 
00348   template<class classTrans,
00349            class T0, class T1, class Prop, class Storage,
00350            class Allocator1, class Allocator2>
00351   void MltCholesky(const classTrans& TransA,
00352                    const Matrix<T0, Prop, ArrayRowSymSparse, Allocator1>& A,
00353                    Vector<T1, Storage, Allocator2>& x)
00354   {
00355     int n = A.GetM();
00356     if (n <= 0)
00357       return;
00358 
00359     if (TransA.Trans())
00360       {
00361         // We overwrite x by L^T x
00362         int j;
00363         for (int i = 0; i < n; i++)
00364           {
00365             T1 val = x(i) * A.Value(i, 0);
00366             for (int k = 1; k < A.GetRowSize(i) ; k++)
00367               {
00368                 j = A.Index(i, k);
00369                 val += A.Value(i, k)*x(j);
00370               }
00371 
00372             x(i) = val;
00373           }
00374       }
00375     else
00376       {
00377         // We overwrite x with L x
00378         int j;
00379         for (int i = n - 1; i >= 0; i--)
00380           {
00381             for (int k = 1; k < A.GetRowSize(i) ; k++)
00382               {
00383                 j = A.Index(i, k);
00384                 x(j) += A.Value(i, k)*x(i);
00385               }
00386 
00387             x(i) *= A.Value(i, 0);
00388           }
00389       }
00390   }
00391 
00392 
00394 
00399   template<class classTrans,
00400            class T0, class T1, class Prop, class Storage,
00401            class Allocator1, class Allocator2>
00402   void MltCholesky(const classTrans& TransA,
00403                    const Matrix<T0, Prop, RowSymSparse, Allocator1>& A,
00404                    Vector<T1, Storage, Allocator2>& x)
00405   {
00406     int n = A.GetM();
00407     if (n <= 0)
00408       return;
00409 
00410     int* ind = A.GetInd();
00411     int* ptr = A.GetPtr();
00412     T1* data = A.GetData();
00413     T1 val = 0;
00414 
00415     if (TransA.Trans())
00416       // We overwrite x by L^T x.
00417       for (int i = 0; i < n; i++)
00418         {
00419           val = x(i) * data[ptr[i]];
00420           for (int k = ptr[i] + 1; k < ptr[i+1]; k++)
00421             val += data[k] * x(ind[k]);
00422 
00423           x(i) = val;
00424         }
00425     else
00426       // We overwrite x by L x.
00427       for (int i = n - 1; i >= 0; i--)
00428         {
00429           for (int k = ptr[i] + 1; k < ptr[i+1]; k++)
00430             x(ind[k]) += data[k] * x(i);
00431 
00432           x(i) *= data[ptr[i]];
00433         }
00434   }
00435 
00436 
00438   // SPARSECHOLESKYSOLVER //
00440 
00441 
00443   template<class T>
00444   SparseCholeskySolver<T>::SparseCholeskySolver()
00445   {
00446     n = 0;
00447     print_level = -1;
00448     type_ordering = SparseMatrixOrdering::IDENTITY;
00449 
00450     type_solver = SELDON_SOLVER;
00451 #ifdef SELDON_WITH_CHOLMOD
00452     type_solver = CHOLMOD;
00453 #endif
00454   }
00455 
00456 
00458   template<class T>
00459   void SparseCholeskySolver<T>::HideMessages()
00460   {
00461     print_level = -1;
00462 
00463 #ifdef SELDON_WITH_CHOLMOD
00464     mat_chol.HideMessages();
00465 #endif
00466 
00467   }
00468 
00469 
00471   template<class T>
00472   void SparseCholeskySolver<T>::ShowMessages()
00473   {
00474     print_level = 1;
00475 
00476 #ifdef SELDON_WITH_CHOLMOD
00477     mat_chol.ShowMessages();
00478 #endif
00479 
00480   }
00481 
00482 
00484   template<class T>
00485   void SparseCholeskySolver<T>::ShowFullHistory()
00486   {
00487     print_level = 3;
00488 
00489 #ifdef SELDON_WITH_CHOLMOD
00490     mat_chol.ShowMessages();
00491 #endif
00492 
00493   }
00494 
00495 
00497   template<class T>
00498   void SparseCholeskySolver<T>::Clear()
00499   {
00500     if (n > 0)
00501       {
00502         n = 0;
00503 
00504 #ifdef SELDON_WITH_CHOLMOD
00505         mat_chol.Clear();
00506 #endif
00507 
00508         mat_sym.Clear();
00509       }
00510   }
00511 
00512 
00514   template<class T>
00515   int SparseCholeskySolver<T>::GetM() const
00516   {
00517     return n;
00518   }
00519 
00520 
00522   template<class T>
00523   int SparseCholeskySolver<T>::GetN() const
00524   {
00525     return n;
00526   }
00527 
00528 
00530   template<class T>
00531   int SparseCholeskySolver<T>::GetTypeOrdering() const
00532   {
00533     return type_ordering;
00534   }
00535 
00536 
00538   template<class T>
00539   void SparseCholeskySolver<T>::SetOrdering(const IVect& num)
00540   {
00541     type_ordering = SparseMatrixOrdering::USER;
00542     permutation = num;
00543   }
00544 
00545 
00547   template<class T>
00548   void SparseCholeskySolver<T>::SelectOrdering(int type)
00549   {
00550     type_ordering = type;
00551   }
00552 
00553 
00555   template<class T>
00556   void SparseCholeskySolver<T>::SelectDirectSolver(int type)
00557   {
00558     type_solver = type;
00559   }
00560 
00561 
00563   template<class T>
00564   int SparseCholeskySolver<T>::GetDirectSolver()
00565   {
00566     return type_solver;
00567   }
00568 
00569 
00571   template<class T> template<class MatrixSparse>
00572   void SparseCholeskySolver<T>::Factorize(MatrixSparse& A, bool keep_matrix)
00573   {
00574     n = A.GetM();
00575     if (type_solver == CHOLMOD)
00576       {
00577 #ifdef SELDON_WITH_CHOLMOD
00578         mat_chol.FactorizeMatrix(A, keep_matrix);
00579 #else
00580         throw Error("SparseCholeskySolver::Factorize",
00581                     "Recompile with Cholmod or change solver type.");
00582 #endif
00583       }
00584     else
00585       {
00586         FindSparseOrdering(A, permutation, type_ordering);
00587         Copy(A, mat_sym);
00588         if (!keep_matrix)
00589           A.Clear();
00590 
00591         ApplyInversePermutation(mat_sym, permutation, permutation);
00592 
00593         GetCholesky(mat_sym, print_level);
00594         xtmp.Reallocate(n);
00595       }
00596   }
00597 
00598 
00600   template<class T> template<class TransStatus, class Vector1>
00601   void SparseCholeskySolver<T>
00602   ::Solve(const TransStatus& TransA, Vector1& x_solution)
00603   {
00604     if (type_solver == CHOLMOD)
00605       {
00606 #ifdef SELDON_WITH_CHOLMOD
00607         mat_chol.Solve(TransA, x_solution);
00608 #else
00609         throw Error("SparseCholeskySolver::Factorize",
00610                     "Recompile with Cholmod or change solver type.");
00611 #endif
00612       }
00613     else
00614       if (TransA.NoTrans())
00615         {
00616           for (int i = 0; i < x_solution.GetM(); i++)
00617             xtmp(permutation(i)) = x_solution(i);
00618 
00619           SolveCholesky(TransA, mat_sym, xtmp);
00620           Copy(xtmp, x_solution);
00621         }
00622       else
00623         {
00624           Copy(x_solution, xtmp);
00625           SolveCholesky(TransA, mat_sym, xtmp);
00626 
00627           for (int i = 0; i < x_solution.GetM(); i++)
00628             x_solution(i) = xtmp(permutation(i));
00629         }
00630   }
00631 
00632 
00634   template<class T> template<class TransStatus, class Vector1>
00635   void SparseCholeskySolver<T>
00636   ::Mlt(const TransStatus& TransA, Vector1& x_solution)
00637   {
00638     if (type_solver == CHOLMOD)
00639       {
00640 #ifdef SELDON_WITH_CHOLMOD
00641         mat_chol.Mlt(TransA, x_solution);
00642 #else
00643         throw Error("SparseCholeskySolver::Factorize",
00644                     "Recompile with Cholmod or change solver type.");
00645 #endif
00646       }
00647     else
00648       if (TransA.NoTrans())
00649         {
00650           Copy(x_solution, xtmp);
00651           MltCholesky(TransA, mat_sym, xtmp);
00652 
00653           for (int i = 0; i < x_solution.GetM(); i++)
00654             x_solution(i) = xtmp(permutation(i));
00655         }
00656       else
00657         {
00658           for (int i = 0; i < x_solution.GetM(); i++)
00659             xtmp(permutation(i)) = x_solution(i);
00660 
00661           MltCholesky(TransA, mat_sym, xtmp);
00662           Copy(xtmp, x_solution);
00663         }
00664   }
00665 
00666 } // namespace Seldon.
00667 
00668 
00669 #define SELDON_FILE_SPARSE_CHOLESKY_FACTORISATION_CXX
00670 #endif