IncompleteCholesky.h
Go to the documentation of this file.
1 // This file is part of Eigen, a lightweight C++ template library
2 // for linear algebra.
3 //
4 // Copyright (C) 2012 Désiré Nuentsa-Wakam <desire.nuentsa_wakam@inria.fr>
5 // Copyright (C) 2015 Gael Guennebaud <gael.guennebaud@inria.fr>
6 //
7 // This Source Code Form is subject to the terms of the Mozilla
8 // Public License v. 2.0. If a copy of the MPL was not distributed
9 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 
11 #ifndef EIGEN_INCOMPLETE_CHOlESKY_H
12 #define EIGEN_INCOMPLETE_CHOlESKY_H
13 
14 #include <vector>
15 #include <list>
16 
17 #include "./InternalHeaderCheck.h"
18 
19 namespace Eigen {
45 template <typename Scalar, int UpLo_ = Lower, typename OrderingType_ = AMDOrdering<int> >
46 class IncompleteCholesky : public SparseSolverBase<IncompleteCholesky<Scalar,UpLo_,OrderingType_> >
47 {
48  protected:
51  public:
53  typedef OrderingType_ OrderingType;
54  typedef typename OrderingType::PermutationType PermutationType;
55  typedef typename PermutationType::StorageIndex StorageIndex;
60  typedef std::vector<std::list<StorageIndex> > VectorList;
61  enum { UpLo = UpLo_ };
62  enum {
65  };
66  public:
67 
75 
78  template<typename MatrixType>
80  {
81  compute(matrix);
82  }
83 
86 
89 
90 
100  {
101  eigen_assert(m_isInitialized && "IncompleteCholesky is not initialized.");
102  return m_info;
103  }
104 
107  void setInitialShift(RealScalar shift) { m_initialShift = shift; }
108 
111  template<typename MatrixType>
113  {
114  OrderingType ord;
115  PermutationType pinv;
116  ord(mat.template selfadjointView<UpLo>(), pinv);
117  if(pinv.size()>0) m_perm = pinv.inverse();
118  else m_perm.resize(0);
119  m_L.resize(mat.rows(), mat.cols());
120  m_analysisIsOk = true;
121  m_isInitialized = true;
122  m_info = Success;
123  }
124 
132  template<typename MatrixType>
133  void factorize(const MatrixType& mat);
134 
141  template<typename MatrixType>
142  void compute(const MatrixType& mat)
143  {
145  factorize(mat);
146  }
147 
148  // internal
149  template<typename Rhs, typename Dest>
150  void _solve_impl(const Rhs& b, Dest& x) const
151  {
152  eigen_assert(m_factorizationIsOk && "factorize() should be called first");
153  if (m_perm.rows() == b.rows()) x = m_perm * b;
154  else x = b;
155  x = m_scale.asDiagonal() * x;
156  x = m_L.template triangularView<Lower>().solve(x);
157  x = m_L.adjoint().template triangularView<Upper>().solve(x);
158  x = m_scale.asDiagonal() * x;
159  if (m_perm.rows() == b.rows())
160  x = m_perm.inverse() * x;
161  }
162 
164  const FactorType& matrixL() const { eigen_assert(m_factorizationIsOk && "factorize() should be called first"); return m_L; }
165 
167  const VectorRx& scalingS() const { eigen_assert(m_factorizationIsOk && "factorize() should be called first"); return m_scale; }
168 
170  const PermutationType& permutationP() const { eigen_assert(m_analysisIsOk && "analyzePattern() should be called first"); return m_perm; }
171 
172  protected:
173  FactorType m_L; // The lower part stored in CSC
174  VectorRx m_scale; // The vector for scaling the matrix
175  RealScalar m_initialShift; // The initial shift parameter
180 
181  private:
182  inline void updateList(Ref<const VectorIx> colPtr, Ref<VectorIx> rowIdx, Ref<VectorSx> vals, const Index& col, const Index& jk, VectorIx& firstElt, VectorList& listCol);
183 };
184 
185 // Based on the following paper:
186 // C-J. Lin and J. J. Moré, Incomplete Cholesky Factorizations with
187 // Limited memory, SIAM J. Sci. Comput. 21(1), pp. 24-45, 1999
188 // http://ftp.mcs.anl.gov/pub/tech_reports/reports/P682.pdf
189 template<typename Scalar, int UpLo_, typename OrderingType>
190 template<typename MatrixType_>
192 {
193  using std::sqrt;
194  eigen_assert(m_analysisIsOk && "analyzePattern() should be called first");
195 
196  // Dropping strategy : Keep only the p largest elements per column, where p is the number of elements in the column of the original matrix. Other strategies will be added
197 
198  // Apply the fill-reducing permutation computed in analyzePattern()
199  if (m_perm.rows() == mat.rows() ) // To detect the null permutation
200  {
201  // The temporary is needed to make sure that the diagonal entry is properly sorted
202  FactorType tmp(mat.rows(), mat.cols());
203  tmp = mat.template selfadjointView<UpLo_>().twistedBy(m_perm);
204  m_L.template selfadjointView<Lower>() = tmp.template selfadjointView<Lower>();
205  }
206  else
207  {
208  m_L.template selfadjointView<Lower>() = mat.template selfadjointView<UpLo_>();
209  }
210 
211  Index n = m_L.cols();
212  Index nnz = m_L.nonZeros();
213  Map<VectorSx> vals(m_L.valuePtr(), nnz); //values
214  Map<VectorIx> rowIdx(m_L.innerIndexPtr(), nnz); //Row indices
215  Map<VectorIx> colPtr( m_L.outerIndexPtr(), n+1); // Pointer to the beginning of each row
216  VectorIx firstElt(n-1); // for each j, points to the next entry in vals that will be used in the factorization
217  VectorList listCol(n); // listCol(j) is a linked list of columns to update column j
218  VectorSx col_vals(n); // Store a nonzero values in each column
219  VectorIx col_irow(n); // Row indices of nonzero elements in each column
220  VectorIx col_pattern(n);
221  col_pattern.fill(-1);
222  StorageIndex col_nnz;
223 
224 
225  // Computes the scaling factors
226  m_scale.resize(n);
227  m_scale.setZero();
228  for (Index j = 0; j < n; j++)
229  for (Index k = colPtr[j]; k < colPtr[j+1]; k++)
230  {
231  m_scale(j) += numext::abs2(vals(k));
232  if(rowIdx[k]!=j)
233  m_scale(rowIdx[k]) += numext::abs2(vals(k));
234  }
235 
236  m_scale = m_scale.cwiseSqrt().cwiseSqrt();
237 
238  for (Index j = 0; j < n; ++j)
239  if(m_scale(j)>(std::numeric_limits<RealScalar>::min)())
240  m_scale(j) = RealScalar(1)/m_scale(j);
241  else
242  m_scale(j) = 1;
243 
244  // TODO disable scaling if not needed, i.e., if it is roughly uniform? (this will make solve() faster)
245 
246  // Scale and compute the shift for the matrix
248  for (Index j = 0; j < n; j++)
249  {
250  for (Index k = colPtr[j]; k < colPtr[j+1]; k++)
251  vals[k] *= (m_scale(j)*m_scale(rowIdx[k]));
252  eigen_internal_assert(rowIdx[colPtr[j]]==j && "IncompleteCholesky: only the lower triangular part must be stored");
253  mindiag = numext::mini(numext::real(vals[colPtr[j]]), mindiag);
254  }
255 
256  FactorType L_save = m_L;
257 
258  RealScalar shift = 0;
259  if(mindiag <= RealScalar(0.))
260  shift = m_initialShift - mindiag;
261 
262  m_info = NumericalIssue;
263 
264  // Try to perform the incomplete factorization using the current shift
265  int iter = 0;
266  do
267  {
268  // Apply the shift to the diagonal elements of the matrix
269  for (Index j = 0; j < n; j++)
270  vals[colPtr[j]] += shift;
271 
272  // jki version of the Cholesky factorization
273  Index j=0;
274  for (; j < n; ++j)
275  {
276  // Left-looking factorization of the j-th column
277  // First, load the j-th column into col_vals
278  Scalar diag = vals[colPtr[j]]; // It is assumed that only the lower part is stored
279  col_nnz = 0;
280  for (Index i = colPtr[j] + 1; i < colPtr[j+1]; i++)
281  {
282  StorageIndex l = rowIdx[i];
283  col_vals(col_nnz) = vals[i];
284  col_irow(col_nnz) = l;
285  col_pattern(l) = col_nnz;
286  col_nnz++;
287  }
288  {
289  typename std::list<StorageIndex>::iterator k;
290  // Browse all previous columns that will update column j
291  for(k = listCol[j].begin(); k != listCol[j].end(); k++)
292  {
293  Index jk = firstElt(*k); // First element to use in the column
294  eigen_internal_assert(rowIdx[jk]==j);
295  Scalar v_j_jk = numext::conj(vals[jk]);
296 
297  jk += 1;
298  for (Index i = jk; i < colPtr[*k+1]; i++)
299  {
300  StorageIndex l = rowIdx[i];
301  if(col_pattern[l]<0)
302  {
303  col_vals(col_nnz) = vals[i] * v_j_jk;
304  col_irow[col_nnz] = l;
305  col_pattern(l) = col_nnz;
306  col_nnz++;
307  }
308  else
309  col_vals(col_pattern[l]) -= vals[i] * v_j_jk;
310  }
311  updateList(colPtr,rowIdx,vals, *k, jk, firstElt, listCol);
312  }
313  }
314 
315  // Scale the current column
316  if(numext::real(diag) <= 0)
317  {
318  if(++iter>=10)
319  return;
320 
321  // increase shift
322  shift = numext::maxi(m_initialShift,RealScalar(2)*shift);
323  // restore m_L, col_pattern, and listCol
324  vals = Map<const VectorSx>(L_save.valuePtr(), nnz);
325  rowIdx = Map<const VectorIx>(L_save.innerIndexPtr(), nnz);
326  colPtr = Map<const VectorIx>(L_save.outerIndexPtr(), n+1);
327  col_pattern.fill(-1);
328  for(Index i=0; i<n; ++i)
329  listCol[i].clear();
330 
331  break;
332  }
333 
334  RealScalar rdiag = sqrt(numext::real(diag));
335  vals[colPtr[j]] = rdiag;
336  for (Index k = 0; k<col_nnz; ++k)
337  {
338  Index i = col_irow[k];
339  //Scale
340  col_vals(k) /= rdiag;
341  //Update the remaining diagonals with col_vals
342  vals[colPtr[i]] -= numext::abs2(col_vals(k));
343  }
344  // Select the largest p elements
345  // p is the original number of elements in the column (without the diagonal)
346  Index p = colPtr[j+1] - colPtr[j] - 1 ;
347  Ref<VectorSx> cvals = col_vals.head(col_nnz);
348  Ref<VectorIx> cirow = col_irow.head(col_nnz);
349  internal::QuickSplit(cvals,cirow, p);
350  // Insert the largest p elements in the matrix
351  Index cpt = 0;
352  for (Index i = colPtr[j]+1; i < colPtr[j+1]; i++)
353  {
354  vals[i] = col_vals(cpt);
355  rowIdx[i] = col_irow(cpt);
356  // restore col_pattern:
357  col_pattern(col_irow(cpt)) = -1;
358  cpt++;
359  }
360  // Get the first smallest row index and put it after the diagonal element
361  Index jk = colPtr(j)+1;
362  updateList(colPtr,rowIdx,vals,j,jk,firstElt,listCol);
363  }
364 
365  if(j==n)
366  {
367  m_factorizationIsOk = true;
368  m_info = Success;
369  }
370  } while(m_info!=Success);
371 }
372 
373 template<typename Scalar, int UpLo_, typename OrderingType>
375 {
376  if (jk < colPtr(col+1) )
377  {
378  Index p = colPtr(col+1) - jk;
379  Index minpos;
380  rowIdx.segment(jk,p).minCoeff(&minpos);
381  minpos += jk;
382  if (rowIdx(minpos) != rowIdx(jk))
383  {
384  //Swap
385  std::swap(rowIdx(jk),rowIdx(minpos));
386  std::swap(vals(jk),vals(minpos));
387  }
388  firstElt(col) = internal::convert_index<StorageIndex,Index>(jk);
389  listCol[rowIdx(jk)].push_back(internal::convert_index<StorageIndex,Index>(col));
390  }
391 }
392 
393 } // end namespace Eigen
394 
395 #endif
const SqrtReturnType sqrt() const
Array< int, 3, 1 > b
int n
ColXpr col(Index i)
This is the const version of col().
RealReturnType real() const
Array< double, 1, 3 > e(1./3., 0.5, 2.)
#define eigen_internal_assert(x)
Definition: Macros.h:908
#define EIGEN_NOEXCEPT
Definition: Macros.h:1260
#define EIGEN_CONSTEXPR
Definition: Macros.h:747
#define eigen_assert(x)
Definition: Macros.h:902
float * p
Matrix< float, 1, Dynamic > MatrixType
Modified Incomplete Cholesky with dual threshold.
ComputationInfo info() const
Reports whether previous computation was successful.
void updateList(Ref< const VectorIx > colPtr, Ref< VectorIx > rowIdx, Ref< VectorSx > vals, const Index &col, const Index &jk, VectorIx &firstElt, VectorList &listCol)
IncompleteCholesky(const MatrixType &matrix)
std::vector< std::list< StorageIndex > > VectorList
SparseMatrix< Scalar, ColMajor, StorageIndex > FactorType
EIGEN_CONSTEXPR Index rows() const EIGEN_NOEXCEPT
Matrix< RealScalar, Dynamic, 1 > VectorRx
void factorize(const MatrixType &mat)
Performs the numerical factorization of the input matrix mat.
const VectorRx & scalingS() const
const PermutationType & permutationP() const
void _solve_impl(const Rhs &b, Dest &x) const
Matrix< StorageIndex, Dynamic, 1 > VectorIx
OrderingType::PermutationType PermutationType
SparseSolverBase< IncompleteCholesky< Scalar, UpLo_, OrderingType_ > > Base
void compute(const MatrixType &mat)
EIGEN_CONSTEXPR Index cols() const EIGEN_NOEXCEPT
const FactorType & matrixL() const
void analyzePattern(const MatrixType &mat)
Computes the fill reducing permutation vector using the sparsity pattern of mat.
Matrix< Scalar, Dynamic, 1 > VectorSx
PermutationType::StorageIndex StorageIndex
NumTraits< Scalar >::Real RealScalar
void setInitialShift(RealScalar shift)
Set the initial shift parameter .
A matrix or vector expression mapping an existing array of data.
Definition: Map.h:98
EIGEN_CONSTEXPR Index cols() const EIGEN_NOEXCEPT
EIGEN_CONSTEXPR Index rows() const EIGEN_NOEXCEPT
constexpr void resize(Index rows, Index cols)
A matrix or vector expression mapping an existing expression.
Definition: Ref.h:285
const AdjointReturnType adjoint() const
Index cols() const
Definition: SparseMatrix.h:167
const Scalar * valuePtr() const
Definition: SparseMatrix.h:177
void resize(Index rows, Index cols)
Definition: SparseMatrix.h:739
Index rows() const
Definition: SparseMatrix.h:165
const StorageIndex * outerIndexPtr() const
Definition: SparseMatrix.h:195
const StorageIndex * innerIndexPtr() const
Definition: SparseMatrix.h:186
A base class for sparse solvers.
ComputationInfo
Definition: Constants.h:444
@ NumericalIssue
Definition: Constants.h:448
@ Success
Definition: Constants.h:446
bfloat16() min(const bfloat16 &a, const bfloat16 &b)
Definition: BFloat16.h:684
Index QuickSplit(VectorV &row, VectorI &ind, Index ncut)
Definition: IncompleteLUT.h:31
void swap(scoped_array< T > &a, scoped_array< T > &b)
Definition: Memory.h:788
bool abs2(bool x)
EIGEN_ALWAYS_INLINE T maxi(const T &x, const T &y)
EIGEN_ALWAYS_INLINE T mini(const T &x, const T &y)
: InteropHeaders
Definition: Core:139
EIGEN_DEFAULT_DENSE_INDEX_TYPE Index
The Index type as used for the API.
Definition: Meta.h:82
const Eigen::CwiseUnaryOp< Eigen::internal::scalar_conjugate_op< typename Derived::Scalar >, const Derived > conj(const Eigen::ArrayBase< Derived > &x)
const int Dynamic
Definition: Constants.h:24
const Eigen::CwiseUnaryOp< Eigen::internal::scalar_sqrt_op< typename Derived::Scalar >, const Derived > sqrt(const Eigen::ArrayBase< Derived > &x)
Holds information about the various numeric (i.e. scalar) types allowed by Eigen.
Definition: NumTraits.h:231
std::ptrdiff_t j