Eigen  3.2.8
IncompleteLUT.h
00001 // This file is part of Eigen, a lightweight C++ template library
00002 // for linear algebra.
00003 //
00004 // Copyright (C) 2012 Désiré Nuentsa-Wakam <desire.nuentsa_wakam@inria.fr>
00005 //
00006 // This Source Code Form is subject to the terms of the Mozilla
00007 // Public License v. 2.0. If a copy of the MPL was not distributed
00008 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
00009 
00010 #ifndef EIGEN_INCOMPLETE_LUT_H
00011 #define EIGEN_INCOMPLETE_LUT_H
00012 
00013 
00014 namespace Eigen { 
00015 
00016 namespace internal {
00017     
00027 template <typename VectorV, typename VectorI, typename Index>
00028 Index QuickSplit(VectorV &row, VectorI &ind, Index ncut)
00029 {
00030   typedef typename VectorV::RealScalar RealScalar;
00031   using std::swap;
00032   using std::abs;
00033   Index mid;
00034   Index n = row.size(); /* length of the vector */
00035   Index first, last ;
00036   
00037   ncut--; /* to fit the zero-based indices */
00038   first = 0; 
00039   last = n-1; 
00040   if (ncut < first || ncut > last ) return 0;
00041   
00042   do {
00043     mid = first; 
00044     RealScalar abskey = abs(row(mid)); 
00045     for (Index j = first + 1; j <= last; j++) {
00046       if ( abs(row(j)) > abskey) {
00047         ++mid;
00048         swap(row(mid), row(j));
00049         swap(ind(mid), ind(j));
00050       }
00051     }
00052     /* Interchange for the pivot element */
00053     swap(row(mid), row(first));
00054     swap(ind(mid), ind(first));
00055     
00056     if (mid > ncut) last = mid - 1;
00057     else if (mid < ncut ) first = mid + 1; 
00058   } while (mid != ncut );
00059   
00060   return 0; /* mid is equal to ncut */ 
00061 }
00062 
00063 }// end namespace internal
00064 
00095 template <typename _Scalar>
00096 class IncompleteLUT : internal::noncopyable
00097 {
00098     typedef _Scalar Scalar;
00099     typedef typename NumTraits<Scalar>::Real RealScalar;
00100     typedef Matrix<Scalar,Dynamic,1> Vector;
00101     typedef SparseMatrix<Scalar,RowMajor> FactorType;
00102     typedef SparseMatrix<Scalar,ColMajor> PermutType;
00103     typedef typename FactorType::Index Index;
00104 
00105   public:
00106     typedef Matrix<Scalar,Dynamic,Dynamic> MatrixType;
00107     
00108     IncompleteLUT()
00109       : m_droptol(NumTraits<Scalar>::dummy_precision()), m_fillfactor(10),
00110         m_analysisIsOk(false), m_factorizationIsOk(false), m_isInitialized(false)
00111     {}
00112     
00113     template<typename MatrixType>
00114     IncompleteLUT(const MatrixType& mat, const RealScalar& droptol=NumTraits<Scalar>::dummy_precision(), int fillfactor = 10)
00115       : m_droptol(droptol),m_fillfactor(fillfactor),
00116         m_analysisIsOk(false),m_factorizationIsOk(false),m_isInitialized(false)
00117     {
00118       eigen_assert(fillfactor != 0);
00119       compute(mat); 
00120     }
00121     
00122     Index rows() const { return m_lu.rows(); }
00123     
00124     Index cols() const { return m_lu.cols(); }
00125 
00131     ComputationInfo info() const
00132     {
00133       eigen_assert(m_isInitialized && "IncompleteLUT is not initialized.");
00134       return m_info;
00135     }
00136     
00137     template<typename MatrixType>
00138     void analyzePattern(const MatrixType& amat);
00139     
00140     template<typename MatrixType>
00141     void factorize(const MatrixType& amat);
00142     
00148     template<typename MatrixType>
00149     IncompleteLUT<Scalar>& compute(const MatrixType& amat)
00150     {
00151       analyzePattern(amat); 
00152       factorize(amat);
00153       return *this;
00154     }
00155 
00156     void setDroptol(const RealScalar& droptol); 
00157     void setFillfactor(int fillfactor); 
00158     
00159     template<typename Rhs, typename Dest>
00160     void _solve(const Rhs& b, Dest& x) const
00161     {
00162       x = m_Pinv * b;
00163       x = m_lu.template triangularView<UnitLower>().solve(x);
00164       x = m_lu.template triangularView<Upper>().solve(x);
00165       x = m_P * x; 
00166     }
00167 
00168     template<typename Rhs> inline const internal::solve_retval<IncompleteLUT, Rhs>
00169      solve(const MatrixBase<Rhs>& b) const
00170     {
00171       eigen_assert(m_isInitialized && "IncompleteLUT is not initialized.");
00172       eigen_assert(cols()==b.rows()
00173                 && "IncompleteLUT::solve(): invalid number of rows of the right hand side matrix b");
00174       return internal::solve_retval<IncompleteLUT, Rhs>(*this, b.derived());
00175     }
00176 
00177 protected:
00178 
00180     struct keep_diag {
00181       inline bool operator() (const Index& row, const Index& col, const Scalar&) const
00182       {
00183         return row!=col;
00184       }
00185     };
00186 
00187 protected:
00188 
00189     FactorType m_lu;
00190     RealScalar m_droptol;
00191     int m_fillfactor;
00192     bool m_analysisIsOk;
00193     bool m_factorizationIsOk;
00194     bool m_isInitialized;
00195     ComputationInfo m_info;
00196     PermutationMatrix<Dynamic,Dynamic,Index> m_P;     // Fill-reducing permutation
00197     PermutationMatrix<Dynamic,Dynamic,Index> m_Pinv;  // Inverse permutation
00198 };
00199 
00204 template<typename Scalar>
00205 void IncompleteLUT<Scalar>::setDroptol(const RealScalar& droptol)
00206 {
00207   this->m_droptol = droptol;   
00208 }
00209 
00214 template<typename Scalar>
00215 void IncompleteLUT<Scalar>::setFillfactor(int fillfactor)
00216 {
00217   this->m_fillfactor = fillfactor;   
00218 }
00219 
00220 template <typename Scalar>
00221 template<typename _MatrixType>
00222 void IncompleteLUT<Scalar>::analyzePattern(const _MatrixType& amat)
00223 {
00224   // Compute the Fill-reducing permutation
00225   // Since ILUT does not perform any numerical pivoting,
00226   // it is highly preferable to keep the diagonal through symmetric permutations.
00227 #ifndef EIGEN_MPL2_ONLY
00228   // To this end, let's symmetrize the pattern and perform AMD on it.
00229   SparseMatrix<Scalar,ColMajor, Index> mat1 = amat;
00230   SparseMatrix<Scalar,ColMajor, Index> mat2 = amat.transpose();
00231   // FIXME for a matrix with nearly symmetric pattern, mat2+mat1 is the appropriate choice.
00232   //       on the other hand for a really non-symmetric pattern, mat2*mat1 should be prefered...
00233   SparseMatrix<Scalar,ColMajor, Index> AtA = mat2 + mat1;
00234   AMDOrdering<Index> ordering;
00235   ordering(AtA,m_P);
00236   m_Pinv  = m_P.inverse(); // cache the inverse permutation
00237 #else
00238   // If AMD is not available, (MPL2-only), then let's use the slower COLAMD routine.
00239   SparseMatrix<Scalar,ColMajor, Index> mat1 = amat;
00240   COLAMDOrdering<Index> ordering;
00241   ordering(mat1,m_Pinv);
00242   m_P = m_Pinv.inverse();
00243 #endif
00244 
00245   m_analysisIsOk = true;
00246   m_factorizationIsOk = false;
00247   m_isInitialized = false;
00248 }
00249 
00250 template <typename Scalar>
00251 template<typename _MatrixType>
00252 void IncompleteLUT<Scalar>::factorize(const _MatrixType& amat)
00253 {
00254   using std::sqrt;
00255   using std::swap;
00256   using std::abs;
00257 
00258   eigen_assert((amat.rows() == amat.cols()) && "The factorization should be done on a square matrix");
00259   Index n = amat.cols();  // Size of the matrix
00260   m_lu.resize(n,n);
00261   // Declare Working vectors and variables
00262   Vector u(n) ;     // real values of the row -- maximum size is n --
00263   VectorXi ju(n);   // column position of the values in u -- maximum size  is n
00264   VectorXi jr(n);   // Indicate the position of the nonzero elements in the vector u -- A zero location is indicated by -1
00265 
00266   // Apply the fill-reducing permutation
00267   eigen_assert(m_analysisIsOk && "You must first call analyzePattern()");
00268   SparseMatrix<Scalar,RowMajor, Index> mat;
00269   mat = amat.twistedBy(m_Pinv);
00270 
00271   // Initialization
00272   jr.fill(-1);
00273   ju.fill(0);
00274   u.fill(0);
00275 
00276   // number of largest elements to keep in each row:
00277   Index fill_in =   static_cast<Index> (amat.nonZeros()*m_fillfactor)/n+1;
00278   if (fill_in > n) fill_in = n;
00279 
00280   // number of largest nonzero elements to keep in the L and the U part of the current row:
00281   Index nnzL = fill_in/2;
00282   Index nnzU = nnzL;
00283   m_lu.reserve(n * (nnzL + nnzU + 1));
00284 
00285   // global loop over the rows of the sparse matrix
00286   for (Index ii = 0; ii < n; ii++)
00287   {
00288     // 1 - copy the lower and the upper part of the row i of mat in the working vector u
00289 
00290     Index sizeu = 1; // number of nonzero elements in the upper part of the current row
00291     Index sizel = 0; // number of nonzero elements in the lower part of the current row
00292     ju(ii)    = ii;
00293     u(ii)     = 0;
00294     jr(ii)    = ii;
00295     RealScalar rownorm = 0;
00296 
00297     typename FactorType::InnerIterator j_it(mat, ii); // Iterate through the current row ii
00298     for (; j_it; ++j_it)
00299     {
00300       Index k = j_it.index();
00301       if (k < ii)
00302       {
00303         // copy the lower part
00304         ju(sizel) = k;
00305         u(sizel) = j_it.value();
00306         jr(k) = sizel;
00307         ++sizel;
00308       }
00309       else if (k == ii)
00310       {
00311         u(ii) = j_it.value();
00312       }
00313       else
00314       {
00315         // copy the upper part
00316         Index jpos = ii + sizeu;
00317         ju(jpos) = k;
00318         u(jpos) = j_it.value();
00319         jr(k) = jpos;
00320         ++sizeu;
00321       }
00322       rownorm += numext::abs2(j_it.value());
00323     }
00324 
00325     // 2 - detect possible zero row
00326     if(rownorm==0)
00327     {
00328       m_info = NumericalIssue;
00329       return;
00330     }
00331     // Take the 2-norm of the current row as a relative tolerance
00332     rownorm = sqrt(rownorm);
00333 
00334     // 3 - eliminate the previous nonzero rows
00335     Index jj = 0;
00336     Index len = 0;
00337     while (jj < sizel)
00338     {
00339       // In order to eliminate in the correct order,
00340       // we must select first the smallest column index among  ju(jj:sizel)
00341       Index k;
00342       Index minrow = ju.segment(jj,sizel-jj).minCoeff(&k); // k is relative to the segment
00343       k += jj;
00344       if (minrow != ju(jj))
00345       {
00346         // swap the two locations
00347         Index j = ju(jj);
00348         swap(ju(jj), ju(k));
00349         jr(minrow) = jj;   jr(j) = k;
00350         swap(u(jj), u(k));
00351       }
00352       // Reset this location
00353       jr(minrow) = -1;
00354 
00355       // Start elimination
00356       typename FactorType::InnerIterator ki_it(m_lu, minrow);
00357       while (ki_it && ki_it.index() < minrow) ++ki_it;
00358       eigen_internal_assert(ki_it && ki_it.col()==minrow);
00359       Scalar fact = u(jj) / ki_it.value();
00360 
00361       // drop too small elements
00362       if(abs(fact) <= m_droptol)
00363       {
00364         jj++;
00365         continue;
00366       }
00367 
00368       // linear combination of the current row ii and the row minrow
00369       ++ki_it;
00370       for (; ki_it; ++ki_it)
00371       {
00372         Scalar prod = fact * ki_it.value();
00373         Index j       = ki_it.index();
00374         Index jpos    = jr(j);
00375         if (jpos == -1) // fill-in element
00376         {
00377           Index newpos;
00378           if (j >= ii) // dealing with the upper part
00379           {
00380             newpos = ii + sizeu;
00381             sizeu++;
00382             eigen_internal_assert(sizeu<=n);
00383           }
00384           else // dealing with the lower part
00385           {
00386             newpos = sizel;
00387             sizel++;
00388             eigen_internal_assert(sizel<=ii);
00389           }
00390           ju(newpos) = j;
00391           u(newpos) = -prod;
00392           jr(j) = newpos;
00393         }
00394         else
00395           u(jpos) -= prod;
00396       }
00397       // store the pivot element
00398       u(len) = fact;
00399       ju(len) = minrow;
00400       ++len;
00401 
00402       jj++;
00403     } // end of the elimination on the row ii
00404 
00405     // reset the upper part of the pointer jr to zero
00406     for(Index k = 0; k <sizeu; k++) jr(ju(ii+k)) = -1;
00407 
00408     // 4 - partially sort and insert the elements in the m_lu matrix
00409 
00410     // sort the L-part of the row
00411     sizel = len;
00412     len = (std::min)(sizel, nnzL);
00413     typename Vector::SegmentReturnType ul(u.segment(0, sizel));
00414     typename VectorXi::SegmentReturnType jul(ju.segment(0, sizel));
00415     internal::QuickSplit(ul, jul, len);
00416 
00417     // store the largest m_fill elements of the L part
00418     m_lu.startVec(ii);
00419     for(Index k = 0; k < len; k++)
00420       m_lu.insertBackByOuterInnerUnordered(ii,ju(k)) = u(k);
00421 
00422     // store the diagonal element
00423     // apply a shifting rule to avoid zero pivots (we are doing an incomplete factorization)
00424     if (u(ii) == Scalar(0))
00425       u(ii) = sqrt(m_droptol) * rownorm;
00426     m_lu.insertBackByOuterInnerUnordered(ii, ii) = u(ii);
00427 
00428     // sort the U-part of the row
00429     // apply the dropping rule first
00430     len = 0;
00431     for(Index k = 1; k < sizeu; k++)
00432     {
00433       if(abs(u(ii+k)) > m_droptol * rownorm )
00434       {
00435         ++len;
00436         u(ii + len)  = u(ii + k);
00437         ju(ii + len) = ju(ii + k);
00438       }
00439     }
00440     sizeu = len + 1; // +1 to take into account the diagonal element
00441     len = (std::min)(sizeu, nnzU);
00442     typename Vector::SegmentReturnType uu(u.segment(ii+1, sizeu-1));
00443     typename VectorXi::SegmentReturnType juu(ju.segment(ii+1, sizeu-1));
00444     internal::QuickSplit(uu, juu, len);
00445 
00446     // store the largest elements of the U part
00447     for(Index k = ii + 1; k < ii + len; k++)
00448       m_lu.insertBackByOuterInnerUnordered(ii,ju(k)) = u(k);
00449   }
00450 
00451   m_lu.finalize();
00452   m_lu.makeCompressed();
00453 
00454   m_factorizationIsOk = true;
00455   m_isInitialized = m_factorizationIsOk;
00456   m_info = Success;
00457 }
00458 
00459 namespace internal {
00460 
00461 template<typename _MatrixType, typename Rhs>
00462 struct solve_retval<IncompleteLUT<_MatrixType>, Rhs>
00463   : solve_retval_base<IncompleteLUT<_MatrixType>, Rhs>
00464 {
00465   typedef IncompleteLUT<_MatrixType> Dec;
00466   EIGEN_MAKE_SOLVE_HELPERS(Dec,Rhs)
00467 
00468   template<typename Dest> void evalTo(Dest& dst) const
00469   {
00470     dec()._solve(rhs(),dst);
00471   }
00472 };
00473 
00474 } // end namespace internal
00475 
00476 } // end namespace Eigen
00477 
00478 #endif // EIGEN_INCOMPLETE_LUT_H
 All Classes Functions Variables Typedefs Enumerations Enumerator Friends