Warning: this documentation for the development version is under construction.
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