File indexing completed on 2025-04-19 09:06:20
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #ifndef EIGEN_INCOMPLETE_CHOlESKY_H
0012 #define EIGEN_INCOMPLETE_CHOlESKY_H
0013
0014 #include <vector>
0015 #include <list>
0016
0017 namespace RivetEigen {
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044 template <typename Scalar, int _UpLo = Lower, typename _OrderingType = AMDOrdering<int> >
0045 class IncompleteCholesky : public SparseSolverBase<IncompleteCholesky<Scalar,_UpLo,_OrderingType> >
0046 {
0047 protected:
0048 typedef SparseSolverBase<IncompleteCholesky<Scalar,_UpLo,_OrderingType> > Base;
0049 using Base::m_isInitialized;
0050 public:
0051 typedef typename NumTraits<Scalar>::Real RealScalar;
0052 typedef _OrderingType OrderingType;
0053 typedef typename OrderingType::PermutationType PermutationType;
0054 typedef typename PermutationType::StorageIndex StorageIndex;
0055 typedef SparseMatrix<Scalar,ColMajor,StorageIndex> FactorType;
0056 typedef Matrix<Scalar,Dynamic,1> VectorSx;
0057 typedef Matrix<RealScalar,Dynamic,1> VectorRx;
0058 typedef Matrix<StorageIndex,Dynamic, 1> VectorIx;
0059 typedef std::vector<std::list<StorageIndex> > VectorList;
0060 enum { UpLo = _UpLo };
0061 enum {
0062 ColsAtCompileTime = Dynamic,
0063 MaxColsAtCompileTime = Dynamic
0064 };
0065 public:
0066
0067
0068
0069
0070
0071
0072
0073 IncompleteCholesky() : m_initialShift(1e-3),m_analysisIsOk(false),m_factorizationIsOk(false) {}
0074
0075
0076
0077 template<typename MatrixType>
0078 IncompleteCholesky(const MatrixType& matrix) : m_initialShift(1e-3),m_analysisIsOk(false),m_factorizationIsOk(false)
0079 {
0080 compute(matrix);
0081 }
0082
0083
0084 EIGEN_CONSTEXPR Index rows() const EIGEN_NOEXCEPT { return m_L.rows(); }
0085
0086
0087 EIGEN_CONSTEXPR Index cols() const EIGEN_NOEXCEPT { return m_L.cols(); }
0088
0089
0090
0091
0092
0093
0094
0095
0096
0097
0098 ComputationInfo info() const
0099 {
0100 eigen_assert(m_isInitialized && "IncompleteCholesky is not initialized.");
0101 return m_info;
0102 }
0103
0104
0105
0106 void setInitialShift(RealScalar shift) { m_initialShift = shift; }
0107
0108
0109
0110 template<typename MatrixType>
0111 void analyzePattern(const MatrixType& mat)
0112 {
0113 OrderingType ord;
0114 PermutationType pinv;
0115 ord(mat.template selfadjointView<UpLo>(), pinv);
0116 if(pinv.size()>0) m_perm = pinv.inverse();
0117 else m_perm.resize(0);
0118 m_L.resize(mat.rows(), mat.cols());
0119 m_analysisIsOk = true;
0120 m_isInitialized = true;
0121 m_info = Success;
0122 }
0123
0124
0125
0126
0127
0128
0129
0130
0131 template<typename MatrixType>
0132 void factorize(const MatrixType& mat);
0133
0134
0135
0136
0137
0138
0139
0140 template<typename MatrixType>
0141 void compute(const MatrixType& mat)
0142 {
0143 analyzePattern(mat);
0144 factorize(mat);
0145 }
0146
0147
0148 template<typename Rhs, typename Dest>
0149 void _solve_impl(const Rhs& b, Dest& x) const
0150 {
0151 eigen_assert(m_factorizationIsOk && "factorize() should be called first");
0152 if (m_perm.rows() == b.rows()) x = m_perm * b;
0153 else x = b;
0154 x = m_scale.asDiagonal() * x;
0155 x = m_L.template triangularView<Lower>().solve(x);
0156 x = m_L.adjoint().template triangularView<Upper>().solve(x);
0157 x = m_scale.asDiagonal() * x;
0158 if (m_perm.rows() == b.rows())
0159 x = m_perm.inverse() * x;
0160 }
0161
0162
0163 const FactorType& matrixL() const { eigen_assert("m_factorizationIsOk"); return m_L; }
0164
0165
0166 const VectorRx& scalingS() const { eigen_assert("m_factorizationIsOk"); return m_scale; }
0167
0168
0169 const PermutationType& permutationP() const { eigen_assert("m_analysisIsOk"); return m_perm; }
0170
0171 protected:
0172 FactorType m_L;
0173 VectorRx m_scale;
0174 RealScalar m_initialShift;
0175 bool m_analysisIsOk;
0176 bool m_factorizationIsOk;
0177 ComputationInfo m_info;
0178 PermutationType m_perm;
0179
0180 private:
0181 inline void updateList(Ref<const VectorIx> colPtr, Ref<VectorIx> rowIdx, Ref<VectorSx> vals, const Index& col, const Index& jk, VectorIx& firstElt, VectorList& listCol);
0182 };
0183
0184
0185
0186
0187
0188 template<typename Scalar, int _UpLo, typename OrderingType>
0189 template<typename _MatrixType>
0190 void IncompleteCholesky<Scalar,_UpLo, OrderingType>::factorize(const _MatrixType& mat)
0191 {
0192 using std::sqrt;
0193 eigen_assert(m_analysisIsOk && "analyzePattern() should be called first");
0194
0195
0196
0197
0198 if (m_perm.rows() == mat.rows() )
0199 {
0200
0201 FactorType tmp(mat.rows(), mat.cols());
0202 tmp = mat.template selfadjointView<_UpLo>().twistedBy(m_perm);
0203 m_L.template selfadjointView<Lower>() = tmp.template selfadjointView<Lower>();
0204 }
0205 else
0206 {
0207 m_L.template selfadjointView<Lower>() = mat.template selfadjointView<_UpLo>();
0208 }
0209
0210 Index n = m_L.cols();
0211 Index nnz = m_L.nonZeros();
0212 Map<VectorSx> vals(m_L.valuePtr(), nnz);
0213 Map<VectorIx> rowIdx(m_L.innerIndexPtr(), nnz);
0214 Map<VectorIx> colPtr( m_L.outerIndexPtr(), n+1);
0215 VectorIx firstElt(n-1);
0216 VectorList listCol(n);
0217 VectorSx col_vals(n);
0218 VectorIx col_irow(n);
0219 VectorIx col_pattern(n);
0220 col_pattern.fill(-1);
0221 StorageIndex col_nnz;
0222
0223
0224
0225 m_scale.resize(n);
0226 m_scale.setZero();
0227 for (Index j = 0; j < n; j++)
0228 for (Index k = colPtr[j]; k < colPtr[j+1]; k++)
0229 {
0230 m_scale(j) += numext::abs2(vals(k));
0231 if(rowIdx[k]!=j)
0232 m_scale(rowIdx[k]) += numext::abs2(vals(k));
0233 }
0234
0235 m_scale = m_scale.cwiseSqrt().cwiseSqrt();
0236
0237 for (Index j = 0; j < n; ++j)
0238 if(m_scale(j)>(std::numeric_limits<RealScalar>::min)())
0239 m_scale(j) = RealScalar(1)/m_scale(j);
0240 else
0241 m_scale(j) = 1;
0242
0243
0244
0245
0246 RealScalar mindiag = NumTraits<RealScalar>::highest();
0247 for (Index j = 0; j < n; j++)
0248 {
0249 for (Index k = colPtr[j]; k < colPtr[j+1]; k++)
0250 vals[k] *= (m_scale(j)*m_scale(rowIdx[k]));
0251 eigen_internal_assert(rowIdx[colPtr[j]]==j && "IncompleteCholesky: only the lower triangular part must be stored");
0252 mindiag = numext::mini(numext::real(vals[colPtr[j]]), mindiag);
0253 }
0254
0255 FactorType L_save = m_L;
0256
0257 RealScalar shift = 0;
0258 if(mindiag <= RealScalar(0.))
0259 shift = m_initialShift - mindiag;
0260
0261 m_info = NumericalIssue;
0262
0263
0264 int iter = 0;
0265 do
0266 {
0267
0268 for (Index j = 0; j < n; j++)
0269 vals[colPtr[j]] += shift;
0270
0271
0272 Index j=0;
0273 for (; j < n; ++j)
0274 {
0275
0276
0277 Scalar diag = vals[colPtr[j]];
0278 col_nnz = 0;
0279 for (Index i = colPtr[j] + 1; i < colPtr[j+1]; i++)
0280 {
0281 StorageIndex l = rowIdx[i];
0282 col_vals(col_nnz) = vals[i];
0283 col_irow(col_nnz) = l;
0284 col_pattern(l) = col_nnz;
0285 col_nnz++;
0286 }
0287 {
0288 typename std::list<StorageIndex>::iterator k;
0289
0290 for(k = listCol[j].begin(); k != listCol[j].end(); k++)
0291 {
0292 Index jk = firstElt(*k);
0293 eigen_internal_assert(rowIdx[jk]==j);
0294 Scalar v_j_jk = numext::conj(vals[jk]);
0295
0296 jk += 1;
0297 for (Index i = jk; i < colPtr[*k+1]; i++)
0298 {
0299 StorageIndex l = rowIdx[i];
0300 if(col_pattern[l]<0)
0301 {
0302 col_vals(col_nnz) = vals[i] * v_j_jk;
0303 col_irow[col_nnz] = l;
0304 col_pattern(l) = col_nnz;
0305 col_nnz++;
0306 }
0307 else
0308 col_vals(col_pattern[l]) -= vals[i] * v_j_jk;
0309 }
0310 updateList(colPtr,rowIdx,vals, *k, jk, firstElt, listCol);
0311 }
0312 }
0313
0314
0315 if(numext::real(diag) <= 0)
0316 {
0317 if(++iter>=10)
0318 return;
0319
0320
0321 shift = numext::maxi(m_initialShift,RealScalar(2)*shift);
0322
0323 vals = Map<const VectorSx>(L_save.valuePtr(), nnz);
0324 rowIdx = Map<const VectorIx>(L_save.innerIndexPtr(), nnz);
0325 colPtr = Map<const VectorIx>(L_save.outerIndexPtr(), n+1);
0326 col_pattern.fill(-1);
0327 for(Index i=0; i<n; ++i)
0328 listCol[i].clear();
0329
0330 break;
0331 }
0332
0333 RealScalar rdiag = sqrt(numext::real(diag));
0334 vals[colPtr[j]] = rdiag;
0335 for (Index k = 0; k<col_nnz; ++k)
0336 {
0337 Index i = col_irow[k];
0338
0339 col_vals(k) /= rdiag;
0340
0341 vals[colPtr[i]] -= numext::abs2(col_vals(k));
0342 }
0343
0344
0345 Index p = colPtr[j+1] - colPtr[j] - 1 ;
0346 Ref<VectorSx> cvals = col_vals.head(col_nnz);
0347 Ref<VectorIx> cirow = col_irow.head(col_nnz);
0348 internal::QuickSplit(cvals,cirow, p);
0349
0350 Index cpt = 0;
0351 for (Index i = colPtr[j]+1; i < colPtr[j+1]; i++)
0352 {
0353 vals[i] = col_vals(cpt);
0354 rowIdx[i] = col_irow(cpt);
0355
0356 col_pattern(col_irow(cpt)) = -1;
0357 cpt++;
0358 }
0359
0360 Index jk = colPtr(j)+1;
0361 updateList(colPtr,rowIdx,vals,j,jk,firstElt,listCol);
0362 }
0363
0364 if(j==n)
0365 {
0366 m_factorizationIsOk = true;
0367 m_info = Success;
0368 }
0369 } while(m_info!=Success);
0370 }
0371
0372 template<typename Scalar, int _UpLo, typename OrderingType>
0373 inline void IncompleteCholesky<Scalar,_UpLo, OrderingType>::updateList(Ref<const VectorIx> colPtr, Ref<VectorIx> rowIdx, Ref<VectorSx> vals, const Index& col, const Index& jk, VectorIx& firstElt, VectorList& listCol)
0374 {
0375 if (jk < colPtr(col+1) )
0376 {
0377 Index p = colPtr(col+1) - jk;
0378 Index minpos;
0379 rowIdx.segment(jk,p).minCoeff(&minpos);
0380 minpos += jk;
0381 if (rowIdx(minpos) != rowIdx(jk))
0382 {
0383
0384 std::swap(rowIdx(jk),rowIdx(minpos));
0385 std::swap(vals(jk),vals(minpos));
0386 }
0387 firstElt(col) = internal::convert_index<StorageIndex,Index>(jk);
0388 listCol[rowIdx(jk)].push_back(internal::convert_index<StorageIndex,Index>(col));
0389 }
0390 }
0391
0392 }
0393
0394 #endif