Amd.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) 2010 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: this routine has been adapted from the CSparse library:
12 
13 Copyright (c) 2006, Timothy A. Davis.
14 http://www.suitesparse.com
15 
16 The author of CSparse, Timothy A. Davis., has executed a license with Google LLC
17 to permit distribution of this code and derivative works as part of Eigen under
18 the Mozilla Public License v. 2.0, as stated at the top of this file.
19 */
20 
21 #ifndef EIGEN_SPARSE_AMD_H
22 #define EIGEN_SPARSE_AMD_H
23 
24 #include "./InternalHeaderCheck.h"
25 
26 namespace Eigen {
27 
28 namespace internal {
29 
30 template<typename T> inline T amd_flip(const T& i) { return -i-2; }
31 template<typename T> inline T amd_unflip(const T& i) { return i<0 ? amd_flip(i) : i; }
32 template<typename T0, typename T1> inline bool amd_marked(const T0* w, const T1& j) { return w[j]<0; }
33 template<typename T0, typename T1> inline void amd_mark(const T0* w, const T1& j) { return w[j] = amd_flip(w[j]); }
34 
35 /* clear w */
36 template<typename StorageIndex>
37 static StorageIndex cs_wclear (StorageIndex mark, StorageIndex lemax, StorageIndex *w, StorageIndex n)
38 {
39  StorageIndex k;
40  if(mark < 2 || (mark + lemax < 0))
41  {
42  for(k = 0; k < n; k++)
43  if(w[k] != 0)
44  w[k] = 1;
45  mark = 2;
46  }
47  return (mark); /* at this point, w[0..n-1] < mark holds */
48 }
49 
50 /* depth-first search and postorder of a tree rooted at node j */
51 template<typename StorageIndex>
52 StorageIndex cs_tdfs(StorageIndex j, StorageIndex k, StorageIndex *head, const StorageIndex *next, StorageIndex *post, StorageIndex *stack)
53 {
54  StorageIndex i, p, top = 0;
55  if(!head || !next || !post || !stack) return (-1); /* check inputs */
56  stack[0] = j; /* place j on the stack */
57  while (top >= 0) /* while (stack is not empty) */
58  {
59  p = stack[top]; /* p = top of stack */
60  i = head[p]; /* i = youngest child of p */
61  if(i == -1)
62  {
63  top--; /* p has no unordered children left */
64  post[k++] = p; /* node p is the kth postordered node */
65  }
66  else
67  {
68  head[p] = next[i]; /* remove i from children of p */
69  stack[++top] = i; /* start dfs on child node i */
70  }
71  }
72  return k;
73 }
74 
75 
85 template<typename Scalar, typename StorageIndex>
87 {
88  using std::sqrt;
89 
90  StorageIndex d, dk, dext, lemax = 0, e, elenk, eln, i, j, k, k1,
91  k2, k3, jlast, ln, dense, nzmax, mindeg = 0, nvi, nvj, nvk, mark, wnvi,
92  ok, nel = 0, p, p1, p2, p3, p4, pj, pk, pk1, pk2, pn, q, t, h;
93 
94  StorageIndex n = StorageIndex(C.cols());
95  dense = std::max<StorageIndex> (16, StorageIndex(10 * sqrt(double(n)))); /* find dense threshold */
96  dense = (std::min)(n-2, dense);
97 
98  StorageIndex cnz = StorageIndex(C.nonZeros());
99  perm.resize(n+1);
100  t = cnz + cnz/5 + 2*n; /* add elbow room to C */
101  C.resizeNonZeros(t);
102 
103  // get workspace
104  ei_declare_aligned_stack_constructed_variable(StorageIndex,W,8*(n+1),0);
105  StorageIndex* len = W;
106  StorageIndex* nv = W + (n+1);
107  StorageIndex* next = W + 2*(n+1);
108  StorageIndex* head = W + 3*(n+1);
109  StorageIndex* elen = W + 4*(n+1);
110  StorageIndex* degree = W + 5*(n+1);
111  StorageIndex* w = W + 6*(n+1);
112  StorageIndex* hhead = W + 7*(n+1);
113  StorageIndex* last = perm.indices().data(); /* use P as workspace for last */
114 
115  /* --- Initialize quotient graph ---------------------------------------- */
116  StorageIndex* Cp = C.outerIndexPtr();
117  StorageIndex* Ci = C.innerIndexPtr();
118  for(k = 0; k < n; k++)
119  len[k] = Cp[k+1] - Cp[k];
120  len[n] = 0;
121  nzmax = t;
122 
123  for(i = 0; i <= n; i++)
124  {
125  head[i] = -1; // degree list i is empty
126  last[i] = -1;
127  next[i] = -1;
128  hhead[i] = -1; // hash list i is empty
129  nv[i] = 1; // node i is just one node
130  w[i] = 1; // node i is alive
131  elen[i] = 0; // Ek of node i is empty
132  degree[i] = len[i]; // degree of node i
133  }
134  mark = internal::cs_wclear<StorageIndex>(0, 0, w, n); /* clear w */
135 
136  /* --- Initialize degree lists ------------------------------------------ */
137  for(i = 0; i < n; i++)
138  {
139  bool has_diag = false;
140  for(p = Cp[i]; p<Cp[i+1]; ++p)
141  if(Ci[p]==i)
142  {
143  has_diag = true;
144  break;
145  }
146 
147  d = degree[i];
148  if(d == 1 && has_diag) /* node i is empty */
149  {
150  elen[i] = -2; /* element i is dead */
151  nel++;
152  Cp[i] = -1; /* i is a root of assembly tree */
153  w[i] = 0;
154  }
155  else if(d > dense || !has_diag) /* node i is dense or has no structural diagonal element */
156  {
157  nv[i] = 0; /* absorb i into element n */
158  elen[i] = -1; /* node i is dead */
159  nel++;
160  Cp[i] = amd_flip (n);
161  nv[n]++;
162  }
163  else
164  {
165  if(head[d] != -1) last[head[d]] = i;
166  next[i] = head[d]; /* put node i in degree list d */
167  head[d] = i;
168  }
169  }
170 
171  elen[n] = -2; /* n is a dead element */
172  Cp[n] = -1; /* n is a root of assembly tree */
173  w[n] = 0; /* n is a dead element */
174 
175  while (nel < n) /* while (selecting pivots) do */
176  {
177  /* --- Select node of minimum approximate degree -------------------- */
178  for(k = -1; mindeg < n && (k = head[mindeg]) == -1; mindeg++) {}
179  if(next[k] != -1) last[next[k]] = -1;
180  head[mindeg] = next[k]; /* remove k from degree list */
181  elenk = elen[k]; /* elenk = |Ek| */
182  nvk = nv[k]; /* # of nodes k represents */
183  nel += nvk; /* nv[k] nodes of A eliminated */
184 
185  /* --- Garbage collection ------------------------------------------- */
186  if(elenk > 0 && cnz + mindeg >= nzmax)
187  {
188  for(j = 0; j < n; j++)
189  {
190  if((p = Cp[j]) >= 0) /* j is a live node or element */
191  {
192  Cp[j] = Ci[p]; /* save first entry of object */
193  Ci[p] = amd_flip (j); /* first entry is now amd_flip(j) */
194  }
195  }
196  for(q = 0, p = 0; p < cnz; ) /* scan all of memory */
197  {
198  if((j = amd_flip (Ci[p++])) >= 0) /* found object j */
199  {
200  Ci[q] = Cp[j]; /* restore first entry of object */
201  Cp[j] = q++; /* new pointer to object j */
202  for(k3 = 0; k3 < len[j]-1; k3++) Ci[q++] = Ci[p++];
203  }
204  }
205  cnz = q; /* Ci[cnz...nzmax-1] now free */
206  }
207 
208  /* --- Construct new element ---------------------------------------- */
209  dk = 0;
210  nv[k] = -nvk; /* flag k as in Lk */
211  p = Cp[k];
212  pk1 = (elenk == 0) ? p : cnz; /* do in place if elen[k] == 0 */
213  pk2 = pk1;
214  for(k1 = 1; k1 <= elenk + 1; k1++)
215  {
216  if(k1 > elenk)
217  {
218  e = k; /* search the nodes in k */
219  pj = p; /* list of nodes starts at Ci[pj]*/
220  ln = len[k] - elenk; /* length of list of nodes in k */
221  }
222  else
223  {
224  e = Ci[p++]; /* search the nodes in e */
225  pj = Cp[e];
226  ln = len[e]; /* length of list of nodes in e */
227  }
228  for(k2 = 1; k2 <= ln; k2++)
229  {
230  i = Ci[pj++];
231  if((nvi = nv[i]) <= 0) continue; /* node i dead, or seen */
232  dk += nvi; /* degree[Lk] += size of node i */
233  nv[i] = -nvi; /* negate nv[i] to denote i in Lk*/
234  Ci[pk2++] = i; /* place i in Lk */
235  if(next[i] != -1) last[next[i]] = last[i];
236  if(last[i] != -1) /* remove i from degree list */
237  {
238  next[last[i]] = next[i];
239  }
240  else
241  {
242  head[degree[i]] = next[i];
243  }
244  }
245  if(e != k)
246  {
247  Cp[e] = amd_flip (k); /* absorb e into k */
248  w[e] = 0; /* e is now a dead element */
249  }
250  }
251  if(elenk != 0) cnz = pk2; /* Ci[cnz...nzmax] is free */
252  degree[k] = dk; /* external degree of k - |Lk\i| */
253  Cp[k] = pk1; /* element k is in Ci[pk1..pk2-1] */
254  len[k] = pk2 - pk1;
255  elen[k] = -2; /* k is now an element */
256 
257  /* --- Find set differences ----------------------------------------- */
258  mark = internal::cs_wclear<StorageIndex>(mark, lemax, w, n); /* clear w if necessary */
259  for(pk = pk1; pk < pk2; pk++) /* scan 1: find |Le\Lk| */
260  {
261  i = Ci[pk];
262  if((eln = elen[i]) <= 0) continue;/* skip if elen[i] empty */
263  nvi = -nv[i]; /* nv[i] was negated */
264  wnvi = mark - nvi;
265  for(p = Cp[i]; p <= Cp[i] + eln - 1; p++) /* scan Ei */
266  {
267  e = Ci[p];
268  if(w[e] >= mark)
269  {
270  w[e] -= nvi; /* decrement |Le\Lk| */
271  }
272  else if(w[e] != 0) /* ensure e is a live element */
273  {
274  w[e] = degree[e] + wnvi; /* 1st time e seen in scan 1 */
275  }
276  }
277  }
278 
279  /* --- Degree update ------------------------------------------------ */
280  for(pk = pk1; pk < pk2; pk++) /* scan2: degree update */
281  {
282  i = Ci[pk]; /* consider node i in Lk */
283  p1 = Cp[i];
284  p2 = p1 + elen[i] - 1;
285  pn = p1;
286  for(h = 0, d = 0, p = p1; p <= p2; p++) /* scan Ei */
287  {
288  e = Ci[p];
289  if(w[e] != 0) /* e is an unabsorbed element */
290  {
291  dext = w[e] - mark; /* dext = |Le\Lk| */
292  if(dext > 0)
293  {
294  d += dext; /* sum up the set differences */
295  Ci[pn++] = e; /* keep e in Ei */
296  h += e; /* compute the hash of node i */
297  }
298  else
299  {
300  Cp[e] = amd_flip (k); /* aggressive absorb. e->k */
301  w[e] = 0; /* e is a dead element */
302  }
303  }
304  }
305  elen[i] = pn - p1 + 1; /* elen[i] = |Ei| */
306  p3 = pn;
307  p4 = p1 + len[i];
308  for(p = p2 + 1; p < p4; p++) /* prune edges in Ai */
309  {
310  j = Ci[p];
311  if((nvj = nv[j]) <= 0) continue; /* node j dead or in Lk */
312  d += nvj; /* degree(i) += |j| */
313  Ci[pn++] = j; /* place j in node list of i */
314  h += j; /* compute hash for node i */
315  }
316  if(d == 0) /* check for mass elimination */
317  {
318  Cp[i] = amd_flip (k); /* absorb i into k */
319  nvi = -nv[i];
320  dk -= nvi; /* |Lk| -= |i| */
321  nvk += nvi; /* |k| += nv[i] */
322  nel += nvi;
323  nv[i] = 0;
324  elen[i] = -1; /* node i is dead */
325  }
326  else
327  {
328  degree[i] = std::min<StorageIndex> (degree[i], d); /* update degree(i) */
329  Ci[pn] = Ci[p3]; /* move first node to end */
330  Ci[p3] = Ci[p1]; /* move 1st el. to end of Ei */
331  Ci[p1] = k; /* add k as 1st element in of Ei */
332  len[i] = pn - p1 + 1; /* new len of adj. list of node i */
333  h %= n; /* finalize hash of i */
334  next[i] = hhead[h]; /* place i in hash bucket */
335  hhead[h] = i;
336  last[i] = h; /* save hash of i in last[i] */
337  }
338  } /* scan2 is done */
339  degree[k] = dk; /* finalize |Lk| */
340  lemax = std::max<StorageIndex>(lemax, dk);
341  mark = internal::cs_wclear<StorageIndex>(mark+lemax, lemax, w, n); /* clear w */
342 
343  /* --- Supernode detection ------------------------------------------ */
344  for(pk = pk1; pk < pk2; pk++)
345  {
346  i = Ci[pk];
347  if(nv[i] >= 0) continue; /* skip if i is dead */
348  h = last[i]; /* scan hash bucket of node i */
349  i = hhead[h];
350  hhead[h] = -1; /* hash bucket will be empty */
351  for(; i != -1 && next[i] != -1; i = next[i], mark++)
352  {
353  ln = len[i];
354  eln = elen[i];
355  for(p = Cp[i]+1; p <= Cp[i] + ln-1; p++) w[Ci[p]] = mark;
356  jlast = i;
357  for(j = next[i]; j != -1; ) /* compare i with all j */
358  {
359  ok = (len[j] == ln) && (elen[j] == eln);
360  for(p = Cp[j] + 1; ok && p <= Cp[j] + ln - 1; p++)
361  {
362  if(w[Ci[p]] != mark) ok = 0; /* compare i and j*/
363  }
364  if(ok) /* i and j are identical */
365  {
366  Cp[j] = amd_flip (i); /* absorb j into i */
367  nv[i] += nv[j];
368  nv[j] = 0;
369  elen[j] = -1; /* node j is dead */
370  j = next[j]; /* delete j from hash bucket */
371  next[jlast] = j;
372  }
373  else
374  {
375  jlast = j; /* j and i are different */
376  j = next[j];
377  }
378  }
379  }
380  }
381 
382  /* --- Finalize new element------------------------------------------ */
383  for(p = pk1, pk = pk1; pk < pk2; pk++) /* finalize Lk */
384  {
385  i = Ci[pk];
386  if((nvi = -nv[i]) <= 0) continue;/* skip if i is dead */
387  nv[i] = nvi; /* restore nv[i] */
388  d = degree[i] + dk - nvi; /* compute external degree(i) */
389  d = std::min<StorageIndex> (d, n - nel - nvi);
390  if(head[d] != -1) last[head[d]] = i;
391  next[i] = head[d]; /* put i back in degree list */
392  last[i] = -1;
393  head[d] = i;
394  mindeg = std::min<StorageIndex> (mindeg, d); /* find new minimum degree */
395  degree[i] = d;
396  Ci[p++] = i; /* place i in Lk */
397  }
398  nv[k] = nvk; /* # nodes absorbed into k */
399  if((len[k] = p-pk1) == 0) /* length of adj list of element k*/
400  {
401  Cp[k] = -1; /* k is a root of the tree */
402  w[k] = 0; /* k is now a dead element */
403  }
404  if(elenk != 0) cnz = p; /* free unused space in Lk */
405  }
406 
407  /* --- Postordering ----------------------------------------------------- */
408  for(i = 0; i < n; i++) Cp[i] = amd_flip (Cp[i]);/* fix assembly tree */
409  for(j = 0; j <= n; j++) head[j] = -1;
410  for(j = n; j >= 0; j--) /* place unordered nodes in lists */
411  {
412  if(nv[j] > 0) continue; /* skip if j is an element */
413  next[j] = head[Cp[j]]; /* place j in list of its parent */
414  head[Cp[j]] = j;
415  }
416  for(e = n; e >= 0; e--) /* place elements in lists */
417  {
418  if(nv[e] <= 0) continue; /* skip unless e is an element */
419  if(Cp[e] != -1)
420  {
421  next[e] = head[Cp[e]]; /* place e in list of its parent */
422  head[Cp[e]] = e;
423  }
424  }
425  for(k = 0, i = 0; i <= n; i++) /* postorder the assembly tree */
426  {
427  if(Cp[i] == -1) k = internal::cs_tdfs<StorageIndex>(i, k, head, next, perm.indices().data(), w);
428  }
429 
430  perm.indices().conservativeResize(n);
431 }
432 
433 } // namespace internal
434 
435 } // end namespace Eigen
436 
437 #endif // EIGEN_SPARSE_AMD_H
const SqrtReturnType sqrt() const
int n
FixedSegmentReturnType<... >::Type head(NType n)
Array< double, 1, 3 > e(1./3., 0.5, 2.)
Vector3f p1
RowVector3d w
#define ei_declare_aligned_stack_constructed_variable(TYPE, NAME, SIZE, BUFFER)
Definition: Memory.h:847
float * p
void resize(Index newSize)
const IndicesType & indices() const
Index cols() const
Definition: SparseMatrix.h:167
void resizeNonZeros(Index size)
Definition: SparseMatrix.h:758
const StorageIndex * outerIndexPtr() const
Definition: SparseMatrix.h:195
const StorageIndex * innerIndexPtr() const
Definition: SparseMatrix.h:186
static const last_t last
bfloat16() min(const bfloat16 &a, const bfloat16 &b)
Definition: BFloat16.h:684
StorageIndex cs_tdfs(StorageIndex j, StorageIndex k, StorageIndex *head, const StorageIndex *next, StorageIndex *post, StorageIndex *stack)
Definition: Amd.h:52
static StorageIndex cs_wclear(StorageIndex mark, StorageIndex lemax, StorageIndex *w, StorageIndex n)
Definition: Amd.h:37
T amd_flip(const T &i)
Definition: Amd.h:30
T amd_unflip(const T &i)
Definition: Amd.h:31
bool amd_marked(const T0 *w, const T1 &j)
Definition: Amd.h:32
void amd_mark(const T0 *w, const T1 &j)
Definition: Amd.h:33
void minimum_degree_ordering(SparseMatrix< Scalar, ColMajor, StorageIndex > &C, PermutationMatrix< Dynamic, Dynamic, StorageIndex > &perm)
Definition: Amd.h:86
: InteropHeaders
Definition: Core:139
const Eigen::CwiseUnaryOp< Eigen::internal::scalar_sqrt_op< typename Derived::Scalar >, const Derived > sqrt(const Eigen::ArrayBase< Derived > &x)
std::ptrdiff_t j