SimplicialCholesky_impl.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) 2008-2012 Gael Guennebaud <gael.guennebaud@inria.fr>
5 //
6 // This Source Code Form is subject to the terms of the Mozilla
7 // Public License v. 2.0. If a copy of the MPL was not distributed
8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 
10 /*
11 NOTE: these functions have been adapted from the LDL library:
12 
13 LDL Copyright (c) 2005 by Timothy A. Davis. All Rights Reserved.
14 
15 The author of LDL, Timothy A. Davis., has executed a license with Google LLC
16 to permit distribution of this code and derivative works as part of Eigen under
17 the Mozilla Public License v. 2.0, as stated at the top of this file.
18  */
19 
20 #ifndef EIGEN_SIMPLICIAL_CHOLESKY_IMPL_H
21 #define EIGEN_SIMPLICIAL_CHOLESKY_IMPL_H
22 
23 // IWYU pragma: private
24 #include "./InternalHeaderCheck.h"
25 
26 namespace Eigen {
27 
28 namespace internal {
29 
30 template <typename Scalar, typename StorageIndex>
35  static constexpr StorageIndex kEmpty = -1;
36 
37  // Implementation of a stack or last-in first-out structure with some debugging machinery.
38  struct Stack {
39  StorageIndex* m_data;
41 #ifndef EIGEN_NO_DEBUG
43  Stack(StorageIndex* data, StorageIndex size, StorageIndex maxSize)
44  : m_data(data), m_size(size), m_maxSize(maxSize) {
45  eigen_assert(size >= 0);
46  eigen_assert(maxSize >= size);
47  }
48 #else
49  Stack(StorageIndex* data, StorageIndex size, StorageIndex /*maxSize*/) : m_data(data), m_size(size) {}
50 #endif
51  bool empty() const { return m_size == 0; }
52  Index size() const { return m_size; }
53  StorageIndex back() const {
54  eigen_assert(m_size > 0);
55  return m_data[m_size - 1];
56  }
57  void push(const StorageIndex& value) {
58 #ifndef EIGEN_NO_DEBUG
60 #endif
61  m_data[m_size] = value;
62  m_size++;
63  }
64  void pop() {
65  eigen_assert(m_size > 0);
66  m_size--;
67  }
68  };
69 
70  // Implementation of a disjoint-set or union-find structure with path compression.
71  struct DisjointSet {
72  StorageIndex* m_set;
73  DisjointSet(StorageIndex* set, StorageIndex size) : m_set(set) { std::iota(set, set + size, 0); }
74  // Find the set representative or root of `u`.
75  StorageIndex find(StorageIndex u) const {
76  eigen_assert(u != kEmpty);
77  while (m_set[u] != u) {
78  // manually unroll the loop by a factor of 2 to improve performance
79  u = m_set[m_set[u]];
80  }
81  return u;
82  }
83  // Perform full path compression such that each node from `u` to `v` points to `v`.
84  void compress(StorageIndex u, StorageIndex v) {
85  eigen_assert(u != kEmpty);
86  eigen_assert(v != kEmpty);
87  while (m_set[u] != v) {
88  StorageIndex next = m_set[u];
89  m_set[u] = v;
90  u = next;
91  }
92  };
93  };
94 
95  // Computes the higher adjacency pattern by transposing the input lower adjacency matrix.
96  // Only the index arrays are calculated, as the values are not needed for the symbolic factorization.
97  // The outer index array provides the size requirements of the inner index array.
98 
99  // Computes the outer index array of the higher adjacency matrix.
100  static void calc_hadj_outer(const StorageIndex size, const CholMatrixType& ap, StorageIndex* outerIndex) {
101  for (StorageIndex j = 1; j < size; j++) {
102  for (InnerIterator it(ap, j); it; ++it) {
103  StorageIndex i = it.index();
104  if (i < j) outerIndex[i + 1]++;
105  }
106  }
107  std::partial_sum(outerIndex, outerIndex + size + 1, outerIndex);
108  }
109 
110  // inner index array
111  static void calc_hadj_inner(const StorageIndex size, const CholMatrixType& ap, const StorageIndex* outerIndex,
112  StorageIndex* innerIndex, StorageIndex* tmp) {
113  std::fill_n(tmp, size, 0);
114 
115  for (StorageIndex j = 1; j < size; j++) {
116  for (InnerIterator it(ap, j); it; ++it) {
117  StorageIndex i = it.index();
118  if (i < j) {
119  StorageIndex b = outerIndex[i] + tmp[i];
120  innerIndex[b] = j;
121  tmp[i]++;
122  }
123  }
124  }
125  }
126 
127  // Adapted from:
128  // Joseph W. Liu. (1986).
129  // A compact row storage scheme for Cholesky factors using elimination trees.
130  // ACM Trans. Math. Softw. 12, 2 (June 1986), 127–148. https://doi.org/10.1145/6497.6499
131 
132  // Computes the elimination forest of the lower adjacency matrix, a compact representation of the sparse L factor.
133  // The L factor may contain multiple elimination trees if a column contains only its diagonal element.
134  // Each elimination tree is an n-ary tree in which each node points to its parent.
135  static void calc_etree(const StorageIndex size, const CholMatrixType& ap, StorageIndex* parent, StorageIndex* tmp) {
136  std::fill_n(parent, size, kEmpty);
137 
138  DisjointSet ancestor(tmp, size);
139 
140  for (StorageIndex j = 1; j < size; j++) {
141  for (InnerIterator it(ap, j); it; ++it) {
142  StorageIndex i = it.index();
143  if (i < j) {
144  StorageIndex r = ancestor.find(i);
145  if (r != j) parent[r] = j;
146  ancestor.compress(i, j);
147  }
148  }
149  }
150  }
151 
152  // Computes the child pointers of the parent tree to facilitate a depth-first search traversal.
153  static void calc_lineage(const StorageIndex size, const StorageIndex* parent, StorageIndex* firstChild,
154  StorageIndex* firstSibling) {
155  std::fill_n(firstChild, size, kEmpty);
156  std::fill_n(firstSibling, size, kEmpty);
157 
158  for (StorageIndex j = 0; j < size; j++) {
159  StorageIndex p = parent[j];
160  if (p == kEmpty) continue;
161  StorageIndex c = firstChild[p];
162  if (c == kEmpty)
163  firstChild[p] = j;
164  else {
165  while (firstSibling[c] != kEmpty) c = firstSibling[c];
166  firstSibling[c] = j;
167  }
168  }
169  }
170 
171  // Computes a post-ordered traversal of the elimination tree.
172  static void calc_post(const StorageIndex size, const StorageIndex* parent, StorageIndex* firstChild,
173  const StorageIndex* firstSibling, StorageIndex* post, StorageIndex* dfs) {
174  Stack post_stack(post, 0, size);
175  for (StorageIndex j = 0; j < size; j++) {
176  if (parent[j] != kEmpty) continue;
177  // Begin at a root
178  Stack dfs_stack(dfs, 0, size);
179  dfs_stack.push(j);
180  while (!dfs_stack.empty()) {
181  StorageIndex i = dfs_stack.back();
182  StorageIndex c = firstChild[i];
183  if (c == kEmpty) {
184  post_stack.push(i);
185  dfs_stack.pop();
186  } else {
187  dfs_stack.push(c);
188  // Remove the path from `i` to `c` for future traversals.
189  firstChild[i] = firstSibling[c];
190  }
191  }
192  }
193  eigen_assert(post_stack.size() == size);
194  eigen_assert(std::all_of(firstChild, firstChild + size, [](StorageIndex a) { return a == kEmpty; }));
195  }
196 
197  // Adapted from:
198  // Gilbert, J. R., Ng, E., & Peyton, B. W. (1994).
199  // An efficient algorithm to compute row and column counts for sparse Cholesky factorization.
200  // SIAM Journal on Matrix Analysis and Applications, 15(4), 1075–1091.
201 
202  // Computes the non-zero pattern of the L factor.
203  static void calc_colcount(const StorageIndex size, const StorageIndex* hadjOuter, const StorageIndex* hadjInner,
204  const StorageIndex* parent, StorageIndex* prevLeaf, StorageIndex* tmp,
205  const StorageIndex* post, StorageIndex* nonZerosPerCol, bool doLDLT) {
206  // initialize nonZerosPerCol with 1 for leaves, 0 for non-leaves
207  std::fill_n(nonZerosPerCol, size, 1);
208  for (StorageIndex j = 0; j < size; j++) {
209  StorageIndex p = parent[j];
210  // p is not a leaf
211  if (p != kEmpty) nonZerosPerCol[p] = 0;
212  }
213 
214  DisjointSet parentSet(tmp, size);
215  // prevLeaf is already initialized
216  eigen_assert(std::all_of(prevLeaf, prevLeaf + size, [](StorageIndex a) { return a == kEmpty; }));
217 
218  for (StorageIndex j_ = 0; j_ < size; j_++) {
219  StorageIndex j = post[j_];
220  nonZerosPerCol[j] += hadjOuter[j + 1] - hadjOuter[j];
221  for (StorageIndex k = hadjOuter[j]; k < hadjOuter[j + 1]; k++) {
222  StorageIndex i = hadjInner[k];
224  StorageIndex prev = prevLeaf[i];
225  if (prev != kEmpty) {
226  StorageIndex q = parentSet.find(prev);
227  parentSet.compress(prev, q);
228  nonZerosPerCol[q]--;
229  }
230  prevLeaf[i] = j;
231  }
232  StorageIndex p = parent[j];
233  if (p != kEmpty) parentSet.compress(j, p);
234  }
235 
236  for (StorageIndex j = 0; j < size; j++) {
237  StorageIndex p = parent[j];
238  if (p != kEmpty) nonZerosPerCol[p] += nonZerosPerCol[j] - 1;
239  if (doLDLT) nonZerosPerCol[j]--;
240  }
241  }
242 
243  // Finalizes the non zero pattern of the L factor and allocates the memory for the factorization.
244  static void init_matrix(const StorageIndex size, const StorageIndex* nonZerosPerCol, CholMatrixType& L) {
245  eigen_assert(L.outerIndexPtr()[0] == 0);
246  std::partial_sum(nonZerosPerCol, nonZerosPerCol + size, L.outerIndexPtr() + 1);
247  L.resizeNonZeros(L.outerIndexPtr()[size]);
248  }
249 
250  // Driver routine for the symbolic sparse Cholesky factorization.
251  static void run(const StorageIndex size, const CholMatrixType& ap, CholMatrixType& L, VectorI& parent,
252  VectorI& workSpace, bool doLDLT) {
253  parent.resize(size);
254  workSpace.resize(4 * size);
255  L.resize(size, size);
256 
257  StorageIndex* tmp1 = workSpace.data();
258  StorageIndex* tmp2 = workSpace.data() + size;
259  StorageIndex* tmp3 = workSpace.data() + 2 * size;
260  StorageIndex* tmp4 = workSpace.data() + 3 * size;
261 
262  // Borrow L's outer index array for the higher adjacency pattern.
263  StorageIndex* hadj_outer = L.outerIndexPtr();
264  calc_hadj_outer(size, ap, hadj_outer);
265  // Request additional temporary storage for the inner indices of the higher adjacency pattern.
266  ei_declare_aligned_stack_constructed_variable(StorageIndex, hadj_inner, hadj_outer[size], nullptr);
267  calc_hadj_inner(size, ap, hadj_outer, hadj_inner, tmp1);
268 
269  calc_etree(size, ap, parent.data(), tmp1);
270  calc_lineage(size, parent.data(), tmp1, tmp2);
271  calc_post(size, parent.data(), tmp1, tmp2, tmp3, tmp4);
272  calc_colcount(size, hadj_outer, hadj_inner, parent.data(), tmp1, tmp2, tmp3, tmp4, doLDLT);
273  init_matrix(size, tmp4, L);
274  }
275 };
276 
277 } // namespace internal
278 
279 template <typename Derived>
282 
283  eigen_assert(ap.innerSize() == ap.outerSize());
284  const StorageIndex size = internal::convert_index<StorageIndex>(ap.outerSize());
285 
286  Helper::run(size, ap, m_matrix, m_parent, m_workSpace, doLDLT);
287 
288  m_isInitialized = true;
289  m_info = Success;
290  m_analysisIsOk = true;
291  m_factorizationIsOk = false;
292 }
293 
294 template <typename Derived>
295 template <bool DoLDLT, bool NonHermitian>
297  using std::sqrt;
298  const StorageIndex size = StorageIndex(ap.rows());
299 
300  eigen_assert(m_analysisIsOk && "You must first call analyzePattern()");
301  eigen_assert(ap.rows() == ap.cols());
302  eigen_assert(m_parent.size() == size);
303  eigen_assert(m_workSpace.size() >= 3 * size);
304 
305  const StorageIndex* Lp = m_matrix.outerIndexPtr();
306  StorageIndex* Li = m_matrix.innerIndexPtr();
307  Scalar* Lx = m_matrix.valuePtr();
308 
310  StorageIndex* nonZerosPerCol = m_workSpace.data();
311  StorageIndex* pattern = m_workSpace.data() + size;
312  StorageIndex* tags = m_workSpace.data() + 2 * size;
313 
314  bool ok = true;
315  m_diag.resize(DoLDLT ? size : 0);
316 
317  for (StorageIndex k = 0; k < size; ++k) {
318  // compute nonzero pattern of kth row of L, in topological order
319  y[k] = Scalar(0); // Y(0:k) is now all zero
320  StorageIndex top = size; // stack for pattern is empty
321  tags[k] = k; // mark node k as visited
322  nonZerosPerCol[k] = 0; // count of nonzeros in column k of L
323  for (typename CholMatrixType::InnerIterator it(ap, k); it; ++it) {
324  StorageIndex i = it.index();
325  if (i <= k) {
326  y[i] += getSymm(it.value()); /* scatter A(i,k) into Y (sum duplicates) */
327  Index len;
328  for (len = 0; tags[i] != k; i = m_parent[i]) {
329  pattern[len++] = i; /* L(k,i) is nonzero */
330  tags[i] = k; /* mark i as visited */
331  }
332  while (len > 0) pattern[--top] = pattern[--len];
333  }
334  }
335 
336  /* compute numerical values kth row of L (a sparse triangular solve) */
337 
338  DiagonalScalar d =
339  getDiag(y[k]) * m_shiftScale + m_shiftOffset; // get D(k,k), apply the shift function, and clear Y(k)
340  y[k] = Scalar(0);
341  for (; top < size; ++top) {
342  Index i = pattern[top]; /* pattern[top:n-1] is pattern of L(:,k) */
343  Scalar yi = y[i]; /* get and clear Y(i) */
344  y[i] = Scalar(0);
345 
346  /* the nonzero entry L(k,i) */
347  Scalar l_ki;
348  if (DoLDLT)
349  l_ki = yi / getDiag(m_diag[i]);
350  else
351  yi = l_ki = yi / Lx[Lp[i]];
352 
353  Index p2 = Lp[i] + nonZerosPerCol[i];
354  Index p;
355  for (p = Lp[i] + (DoLDLT ? 0 : 1); p < p2; ++p) y[Li[p]] -= getSymm(Lx[p]) * yi;
356  d -= getDiag(l_ki * getSymm(yi));
357  Li[p] = k; /* store L(k,i) in column form of L */
358  Lx[p] = l_ki;
359  ++nonZerosPerCol[i]; /* increment count of nonzeros in col i */
360  }
361  if (DoLDLT) {
362  m_diag[k] = d;
363  if (d == RealScalar(0)) {
364  ok = false; /* failure, D(k,k) is zero */
365  break;
366  }
367  } else {
368  Index p = Lp[k] + nonZerosPerCol[k]++;
369  Li[p] = k; /* store L(k,k) = sqrt (d) in column k */
370  if (NonHermitian ? d == RealScalar(0) : numext::real(d) <= RealScalar(0)) {
371  ok = false; /* failure, matrix is not positive definite */
372  break;
373  }
374  Lx[p] = sqrt(d);
375  }
376  }
377 
378  m_info = ok ? Success : NumericalIssue;
379  m_factorizationIsOk = true;
380 }
381 
382 } // end namespace Eigen
383 
384 #endif // EIGEN_SIMPLICIAL_CHOLESKY_IMPL_H
AnnoyingScalar sqrt(const AnnoyingScalar &x)
Definition: AnnoyingScalar.h:134
Array< int, Dynamic, 1 > v
Definition: Array_initializer_list_vector_cxx11.cpp:1
int i
Definition: BiCGSTAB_step_by_step.cpp:9
MatrixXd L
Definition: LLT_example.cpp:6
#define eigen_assert(x)
Definition: Macros.h:910
int data[]
Definition: Map_placement_new.cpp:1
#define ei_declare_aligned_stack_constructed_variable(TYPE, NAME, SIZE, BUFFER)
Definition: Memory.h:806
float * p
Definition: Tutorial_Map_using.cpp:9
Scalar Scalar int size
Definition: benchVecAdd.cpp:17
Scalar * b
Definition: benchVecAdd.cpp:17
SCALAR Scalar
Definition: bench_gemm.cpp:45
NumTraits< Scalar >::Real RealScalar
Definition: bench_gemm.cpp:46
constexpr EIGEN_DEVICE_FUNC const Scalar * data() const
Definition: PlainObjectBase.h:273
EIGEN_DEVICE_FUNC constexpr EIGEN_STRONG_INLINE void resize(Index rows, Index cols)
Definition: PlainObjectBase.h:294
MatrixType::Scalar Scalar
Definition: SimplicialCholesky.h:59
MatrixType::StorageIndex StorageIndex
Definition: SimplicialCholesky.h:62
internal::traits< Derived >::DiagonalScalar DiagonalScalar
Definition: SimplicialCholesky.h:61
void analyzePattern_preordered(const CholMatrixType &a, bool doLDLT)
Definition: SimplicialCholesky_impl.h:280
void factorize_preordered(const CholMatrixType &a)
Definition: SimplicialCholesky_impl.h:296
Base::InnerIterator InnerIterator
Definition: SparseMatrix.h:138
float real
Definition: datatypes.h:10
@ NumericalIssue
Definition: Constants.h:442
@ Success
Definition: Constants.h:440
Scalar * y
Definition: level1_cplx_impl.h:128
const Scalar * a
Definition: level2_cplx_impl.h:32
Scalar * ap
Definition: level2_cplx_impl.h:161
char char char int int * k
Definition: level2_impl.h:374
Eigen::Matrix< Scalar, Dynamic, Dynamic, ColMajor > tmp
Definition: level3_impl.h:365
EIGEN_DEVICE_FUNC const Scalar & q
Definition: SpecialFunctionsImpl.h:2019
Namespace containing all symbols from the Eigen library.
Definition: bench_norm.cpp:70
squared absolute value
Definition: GlobalFunctions.h:87
EIGEN_DEFAULT_DENSE_INDEX_TYPE Index
The Index type as used for the API.
Definition: Meta.h:83
double Lx
Length of domain in x direction.
Definition: periodic_load.cc:55
void dfs(Node *node_pt, Node *parent_node_pt, std::map< Node *, Vector< std::pair< NodeNodeConstraintElementType *, Node * >>> &tree, std::map< Node *, Colour > &colours, Vector< NodeNodeConstraintElementType * > &pruned_branches)
Definition: mortaring_helpers.h:320
r
Definition: UniformPSDSelfTest.py:20
int c
Definition: calibrate.py:100
Definition: Eigen_Colamd.h:49
void set(Container &c, Position position, const Value &value)
Definition: stdlist_overload.cpp:36
Definition: SimplicialCholesky_impl.h:71
DisjointSet(StorageIndex *set, StorageIndex size)
Definition: SimplicialCholesky_impl.h:73
void compress(StorageIndex u, StorageIndex v)
Definition: SimplicialCholesky_impl.h:84
StorageIndex find(StorageIndex u) const
Definition: SimplicialCholesky_impl.h:75
StorageIndex * m_set
Definition: SimplicialCholesky_impl.h:72
Definition: SimplicialCholesky_impl.h:38
Index size() const
Definition: SimplicialCholesky_impl.h:52
Index m_size
Definition: SimplicialCholesky_impl.h:40
Stack(StorageIndex *data, StorageIndex size, StorageIndex maxSize)
Definition: SimplicialCholesky_impl.h:43
StorageIndex * m_data
Definition: SimplicialCholesky_impl.h:39
bool empty() const
Definition: SimplicialCholesky_impl.h:51
void push(const StorageIndex &value)
Definition: SimplicialCholesky_impl.h:57
void pop()
Definition: SimplicialCholesky_impl.h:64
const Index m_maxSize
Definition: SimplicialCholesky_impl.h:42
StorageIndex back() const
Definition: SimplicialCholesky_impl.h:53
Definition: SimplicialCholesky_impl.h:31
static void calc_hadj_outer(const StorageIndex size, const CholMatrixType &ap, StorageIndex *outerIndex)
Definition: SimplicialCholesky_impl.h:100
static constexpr StorageIndex kEmpty
Definition: SimplicialCholesky_impl.h:35
static void calc_etree(const StorageIndex size, const CholMatrixType &ap, StorageIndex *parent, StorageIndex *tmp)
Definition: SimplicialCholesky_impl.h:135
static void run(const StorageIndex size, const CholMatrixType &ap, CholMatrixType &L, VectorI &parent, VectorI &workSpace, bool doLDLT)
Definition: SimplicialCholesky_impl.h:251
static void calc_colcount(const StorageIndex size, const StorageIndex *hadjOuter, const StorageIndex *hadjInner, const StorageIndex *parent, StorageIndex *prevLeaf, StorageIndex *tmp, const StorageIndex *post, StorageIndex *nonZerosPerCol, bool doLDLT)
Definition: SimplicialCholesky_impl.h:203
static void init_matrix(const StorageIndex size, const StorageIndex *nonZerosPerCol, CholMatrixType &L)
Definition: SimplicialCholesky_impl.h:244
static void calc_hadj_inner(const StorageIndex size, const CholMatrixType &ap, const StorageIndex *outerIndex, StorageIndex *innerIndex, StorageIndex *tmp)
Definition: SimplicialCholesky_impl.h:111
typename CholMatrixType::InnerIterator InnerIterator
Definition: SimplicialCholesky_impl.h:33
static void calc_post(const StorageIndex size, const StorageIndex *parent, StorageIndex *firstChild, const StorageIndex *firstSibling, StorageIndex *post, StorageIndex *dfs)
Definition: SimplicialCholesky_impl.h:172
static void calc_lineage(const StorageIndex size, const StorageIndex *parent, StorageIndex *firstChild, StorageIndex *firstSibling)
Definition: SimplicialCholesky_impl.h:153
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2
void run(const string &dir_name, LinearSolver *linear_solver_pt, const unsigned nel_1d, bool mess_up_order)
Definition: two_d_poisson_compare_solvers.cc:317