21#ifndef EIGEN_SIMPLICIAL_CHOLESKY_IMPL_H
22#define EIGEN_SIMPLICIAL_CHOLESKY_IMPL_H
25#include "./InternalHeaderCheck.h"
31template <
typename Scalar,
typename StorageIndex>
32struct simpl_chol_helper {
33 using CholMatrixType = SparseMatrix<Scalar, ColMajor, StorageIndex>;
34 using InnerIterator =
typename CholMatrixType::InnerIterator;
35 using VectorI = Matrix<StorageIndex, Dynamic, 1>;
36 static constexpr StorageIndex kEmpty = -1;
43 const Index m_maxSize;
44 Stack(StorageIndex* data, StorageIndex size, StorageIndex maxSize)
45 : m_data(data), m_size(size), m_maxSize(maxSize) {
46 eigen_assert(size >= 0);
47 eigen_assert(maxSize >= size);
50 Stack(StorageIndex* data, StorageIndex size, StorageIndex ) : m_data(data), m_size(size) {}
52 bool empty()
const {
return m_size == 0; }
53 Index size()
const {
return m_size; }
54 StorageIndex back()
const {
55 eigen_assert(m_size > 0);
56 return m_data[m_size - 1];
58 void push(
const StorageIndex& value) {
60 eigen_assert(m_size < m_maxSize);
62 m_data[m_size] = value;
66 eigen_assert(m_size > 0);
74 DisjointSet(StorageIndex* set, StorageIndex size) : m_set(set) { std::iota(set, set + size, 0); }
76 StorageIndex find(StorageIndex u)
const {
77 eigen_assert(u != kEmpty);
78 while (m_set[u] != u) {
85 void compress(StorageIndex u, StorageIndex v) {
86 eigen_assert(u != kEmpty);
87 eigen_assert(v != kEmpty);
88 while (m_set[u] != v) {
89 StorageIndex next = m_set[u];
101 static void calc_hadj_outer(
const StorageIndex size,
const CholMatrixType& ap, StorageIndex* outerIndex) {
102 for (StorageIndex j = 1; j < size; j++) {
103 for (InnerIterator it(ap, j); it; ++it) {
104 StorageIndex i = it.index();
105 if (i < j) outerIndex[i + 1]++;
108 std::partial_sum(outerIndex, outerIndex + size + 1, outerIndex);
112 static void calc_hadj_inner(
const StorageIndex size,
const CholMatrixType& ap,
const StorageIndex* outerIndex,
113 StorageIndex* innerIndex, StorageIndex* tmp) {
114 std::fill_n(tmp, size, 0);
116 for (StorageIndex j = 1; j < size; j++) {
117 for (InnerIterator it(ap, j); it; ++it) {
118 StorageIndex i = it.index();
120 StorageIndex b = outerIndex[i] + tmp[i];
136 static void calc_etree(
const StorageIndex size,
const CholMatrixType& ap, StorageIndex* parent, StorageIndex* tmp) {
137 std::fill_n(parent, size, kEmpty);
139 DisjointSet ancestor(tmp, size);
141 for (StorageIndex j = 1; j < size; j++) {
142 for (InnerIterator it(ap, j); it; ++it) {
143 StorageIndex i = it.index();
145 StorageIndex r = ancestor.find(i);
146 if (r != j) parent[r] = j;
147 ancestor.compress(i, j);
154 static void calc_lineage(
const StorageIndex size,
const StorageIndex* parent, StorageIndex* firstChild,
155 StorageIndex* firstSibling) {
156 std::fill_n(firstChild, size, kEmpty);
157 std::fill_n(firstSibling, size, kEmpty);
159 for (StorageIndex j = 0; j < size; j++) {
160 StorageIndex p = parent[j];
161 if (p == kEmpty)
continue;
162 StorageIndex c = firstChild[p];
166 while (firstSibling[c] != kEmpty) c = firstSibling[c];
173 static void calc_post(
const StorageIndex size,
const StorageIndex* parent, StorageIndex* firstChild,
174 const StorageIndex* firstSibling, StorageIndex* post, StorageIndex* dfs) {
175 Stack post_stack(post, 0, size);
176 for (StorageIndex j = 0; j < size; j++) {
177 if (parent[j] != kEmpty)
continue;
179 Stack dfs_stack(dfs, 0, size);
181 while (!dfs_stack.empty()) {
182 StorageIndex i = dfs_stack.back();
183 StorageIndex c = firstChild[i];
190 firstChild[i] = firstSibling[c];
194 eigen_assert(post_stack.size() == size);
195 eigen_assert(std::all_of(firstChild, firstChild + size, [](StorageIndex a) {
return a == kEmpty; }));
204 static void calc_colcount(
const StorageIndex size,
const StorageIndex* hadjOuter,
const StorageIndex* hadjInner,
205 const StorageIndex* parent, StorageIndex* prevLeaf, StorageIndex* tmp,
206 const StorageIndex* post, StorageIndex* nonZerosPerCol,
bool doLDLT) {
208 std::fill_n(nonZerosPerCol, size, 1);
209 for (StorageIndex j = 0; j < size; j++) {
210 StorageIndex p = parent[j];
212 if (p != kEmpty) nonZerosPerCol[p] = 0;
215 DisjointSet parentSet(tmp, size);
217 eigen_assert(std::all_of(prevLeaf, prevLeaf + size, [](StorageIndex a) {
return a == kEmpty; }));
219 for (StorageIndex j_ = 0; j_ < size; j_++) {
220 StorageIndex j = post[j_];
221 nonZerosPerCol[j] += hadjOuter[j + 1] - hadjOuter[j];
222 for (StorageIndex k = hadjOuter[j]; k < hadjOuter[j + 1]; k++) {
223 StorageIndex i = hadjInner[k];
225 StorageIndex prev = prevLeaf[i];
226 if (prev != kEmpty) {
227 StorageIndex q = parentSet.find(prev);
228 parentSet.compress(prev, q);
233 StorageIndex p = parent[j];
234 if (p != kEmpty) parentSet.compress(j, p);
237 for (StorageIndex j = 0; j < size; j++) {
238 StorageIndex p = parent[j];
239 if (p != kEmpty) nonZerosPerCol[p] += nonZerosPerCol[j] - 1;
240 if (doLDLT) nonZerosPerCol[j]--;
245 static void init_matrix(
const StorageIndex size,
const StorageIndex* nonZerosPerCol, CholMatrixType& L) {
246 eigen_assert(L.outerIndexPtr()[0] == 0);
247 std::partial_sum(nonZerosPerCol, nonZerosPerCol + size, L.outerIndexPtr() + 1);
248 L.resizeNonZeros(L.outerIndexPtr()[size]);
252 static void run(
const StorageIndex size,
const CholMatrixType& ap, CholMatrixType& L, VectorI& parent,
253 VectorI& workSpace,
bool doLDLT) {
255 workSpace.resize(4 * size);
256 L.resize(size, size);
258 StorageIndex* tmp1 = workSpace.data();
259 StorageIndex* tmp2 = workSpace.data() + size;
260 StorageIndex* tmp3 = workSpace.data() + 2 * size;
261 StorageIndex* tmp4 = workSpace.data() + 3 * size;
264 StorageIndex* hadj_outer = L.outerIndexPtr();
265 calc_hadj_outer(size, ap, hadj_outer);
267 ei_declare_aligned_stack_constructed_variable(StorageIndex, hadj_inner, hadj_outer[size],
nullptr);
268 calc_hadj_inner(size, ap, hadj_outer, hadj_inner, tmp1);
270 calc_etree(size, ap, parent.data(), tmp1);
271 calc_lineage(size, parent.data(), tmp1, tmp2);
272 calc_post(size, parent.data(), tmp1, tmp2, tmp3, tmp4);
273 calc_colcount(size, hadj_outer, hadj_inner, parent.data(), tmp1, tmp2, tmp3, tmp4, doLDLT);
274 init_matrix(size, tmp4, L);
280#if EIGEN_COMP_CXXVER < 17
281template <
typename Scalar,
typename StorageIndex>
282constexpr StorageIndex simpl_chol_helper<Scalar, StorageIndex>::kEmpty;
287template <
typename Derived>
288void SimplicialCholeskyBase<Derived>::analyzePattern_preordered(
const CholMatrixType& ap,
bool doLDLT) {
289 using Helper = internal::simpl_chol_helper<Scalar, StorageIndex>;
291 eigen_assert(ap.innerSize() == ap.outerSize());
292 const StorageIndex size = internal::convert_index<StorageIndex>(ap.outerSize());
294 Helper::run(size, ap, m_matrix, m_parent, m_workSpace, doLDLT);
296 m_isInitialized =
true;
298 m_analysisIsOk =
true;
299 m_factorizationIsOk =
false;
302template <
typename Derived>
303template <
bool DoLDLT,
bool NonHermitian>
304void SimplicialCholeskyBase<Derived>::factorize_preordered(
const CholMatrixType& ap) {
305 EIGEN_IF_CONSTEXPR (internal::packet_traits<Scalar>::Vectorizable && internal::packet_traits<Scalar>::HasMul &&
306 internal::packet_traits<Scalar>::HasSub && !NumTraits<Scalar>::IsComplex) {
307 const StorageIndex* outer = m_matrix.outerIndexPtr();
308 for (Index i = 0; i < m_matrix.cols(); ++i) {
310 if (outer[i + 1] - outer[i] > internal::kSparseScatterPacketMinSize + (DoLDLT ? 0 : 1)) {
311 factorize_preordered_impl<DoLDLT, NonHermitian, true>(ap);
316 factorize_preordered_impl<DoLDLT, NonHermitian, false>(ap);
319template <
typename Derived>
320template <
bool DoLDLT,
bool NonHermitian,
bool UsePackets>
321void SimplicialCholeskyBase<Derived>::factorize_preordered_impl(
const CholMatrixType& ap) {
323 const StorageIndex size = StorageIndex(ap.rows());
325 eigen_assert(m_analysisIsOk &&
"You must first call analyzePattern()");
326 eigen_assert(ap.rows() == ap.cols());
327 eigen_assert(m_parent.size() == size);
328 eigen_assert(m_workSpace.size() >= 3 * size);
330 const StorageIndex* Lp = m_matrix.outerIndexPtr();
331 StorageIndex* Li = m_matrix.innerIndexPtr();
332 Scalar* Lx = m_matrix.valuePtr();
334 ei_declare_aligned_stack_constructed_variable(Scalar, y, size, 0);
335 StorageIndex* nonZerosPerCol = m_workSpace.data();
336 StorageIndex* pattern = m_workSpace.data() + size;
337 StorageIndex* tags = m_workSpace.data() + 2 * size;
340 m_diag.resize(DoLDLT ? size : 0);
342 for (StorageIndex k = 0; k < size; ++k) {
345 StorageIndex top = size;
347 nonZerosPerCol[k] = 0;
348 for (
typename CholMatrixType::InnerIterator it(ap, k); it; ++it) {
349 StorageIndex i = it.index();
351 y[i] += getSymm(it.value());
353 for (len = 0; tags[i] != k; i = m_parent[i]) {
357 while (len > 0) pattern[--top] = pattern[--len];
364 getDiag(y[k]) * m_shiftScale + m_shiftOffset;
366 for (; top < size; ++top) {
367 Index i = pattern[top];
373 EIGEN_IF_CONSTEXPR (DoLDLT)
374 l_ki = yi / getDiag(m_diag[i]);
376 yi = l_ki = yi / Lx[Lp[i]];
378 Index p2 = Lp[i] + nonZerosPerCol[i];
379 Index p = Lp[i] + (DoLDLT ? 0 : 1);
380 EIGEN_IF_CONSTEXPR (UsePackets)
381 p += internal::sparse_scatter_sub_packets<!NonHermitian>(y, Li + p, Lx + p, p2 - p, yi);
382 for (; p < p2; ++p) y[Li[p]] -= getSymm(Lx[p]) * yi;
383 d -= getDiag(l_ki * getSymm(yi));
388 EIGEN_IF_CONSTEXPR (DoLDLT) {
390 if (d == RealScalar(0)) {
395 Index p = Lp[k] + nonZerosPerCol[k]++;
398 EIGEN_IF_CONSTEXPR (NonHermitian) {
399 failed = d == RealScalar(0);
401 failed = numext::real(d) <= RealScalar(0);
412 m_factorizationIsOk =
true;
@ NumericalIssue
Definition Constants.h:459
@ Success
Definition Constants.h:457