OrderingMethods_Module

Classes

class  Eigen::AMDOrdering< StorageIndex >
 
class  Eigen::NaturalOrdering< StorageIndex >
 
class  Eigen::COLAMDOrdering< StorageIndex >
 

Functions

template<typename Scalar , typename StorageIndex >
void Eigen::internal::minimum_degree_ordering (SparseMatrix< Scalar, ColMajor, StorageIndex > &C, PermutationMatrix< Dynamic, Dynamic, StorageIndex > &perm)
 
template<typename MatrixType >
void Eigen::internal::ordering_helper_at_plus_a (const MatrixType &A, MatrixType &symmat)
 

Detailed Description

Function Documentation

◆ minimum_degree_ordering()

template<typename Scalar , typename StorageIndex >
void Eigen::internal::minimum_degree_ordering ( SparseMatrix< Scalar, ColMajor, StorageIndex > &  C,
PermutationMatrix< Dynamic, Dynamic, StorageIndex > &  perm 
)

Approximate minimum degree ordering algorithm.

Parameters
[in]Cthe input selfadjoint matrix stored in compressed column major format.
[out]permthe permutation P reducing the fill-in of the input matrix C

Note that the input matrix C must be complete, that is both the upper and lower parts have to be stored, as well as the diagonal entries. On exit the values of C are destroyed

93  {
94  using std::sqrt;
95 
96  StorageIndex d, dk, dext, lemax = 0, e, elenk, eln, i, j, k, k1, k2, k3, jlast, ln, dense, nzmax, mindeg = 0, nvi,
97  nvj, nvk, mark, wnvi, ok, nel = 0, p, p1, p2, p3, p4, pj, pk, pk1, pk2, pn, q, t, h;
98 
99  StorageIndex n = StorageIndex(C.cols());
100  dense = std::max<StorageIndex>(16, StorageIndex(10 * sqrt(double(n)))); /* find dense threshold */
101  dense = (std::min)(n - 2, dense);
102 
103  StorageIndex cnz = StorageIndex(C.nonZeros());
104  perm.resize(n + 1);
105  t = cnz + cnz / 5 + 2 * n; /* add elbow room to C */
106  C.resizeNonZeros(t);
107 
108  // get workspace
109  ei_declare_aligned_stack_constructed_variable(StorageIndex, W, 8 * (n + 1), 0);
110  StorageIndex* len = W;
111  StorageIndex* nv = W + (n + 1);
112  StorageIndex* next = W + 2 * (n + 1);
113  StorageIndex* head = W + 3 * (n + 1);
114  StorageIndex* elen = W + 4 * (n + 1);
115  StorageIndex* degree = W + 5 * (n + 1);
116  StorageIndex* w = W + 6 * (n + 1);
117  StorageIndex* hhead = W + 7 * (n + 1);
118  StorageIndex* last = perm.indices().data(); /* use P as workspace for last */
119 
120  /* --- Initialize quotient graph ---------------------------------------- */
121  StorageIndex* Cp = C.outerIndexPtr();
122  StorageIndex* Ci = C.innerIndexPtr();
123  for (k = 0; k < n; k++) len[k] = Cp[k + 1] - Cp[k];
124  len[n] = 0;
125  nzmax = t;
126 
127  for (i = 0; i <= n; i++) {
128  head[i] = -1; // degree list i is empty
129  last[i] = -1;
130  next[i] = -1;
131  hhead[i] = -1; // hash list i is empty
132  nv[i] = 1; // node i is just one node
133  w[i] = 1; // node i is alive
134  elen[i] = 0; // Ek of node i is empty
135  degree[i] = len[i]; // degree of node i
136  }
137  mark = internal::cs_wclear<StorageIndex>(0, 0, w, n); /* clear w */
138 
139  /* --- Initialize degree lists ------------------------------------------ */
140  for (i = 0; i < n; i++) {
141  bool has_diag = false;
142  for (p = Cp[i]; p < Cp[i + 1]; ++p)
143  if (Ci[p] == i) {
144  has_diag = true;
145  break;
146  }
147 
148  d = degree[i];
149  if (d == 1 && has_diag) /* node i is empty */
150  {
151  elen[i] = -2; /* element i is dead */
152  nel++;
153  Cp[i] = -1; /* i is a root of assembly tree */
154  w[i] = 0;
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  } else {
163  if (head[d] != -1) last[head[d]] = i;
164  next[i] = head[d]; /* put node i in degree list d */
165  head[d] = i;
166  }
167  }
168 
169  elen[n] = -2; /* n is a dead element */
170  Cp[n] = -1; /* n is a root of assembly tree */
171  w[n] = 0; /* n is a dead element */
172 
173  while (nel < n) /* while (selecting pivots) do */
174  {
175  /* --- Select node of minimum approximate degree -------------------- */
176  for (k = -1; mindeg < n && (k = head[mindeg]) == -1; mindeg++) {
177  }
178  if (next[k] != -1) last[next[k]] = -1;
179  head[mindeg] = next[k]; /* remove k from degree list */
180  elenk = elen[k]; /* elenk = |Ek| */
181  nvk = nv[k]; /* # of nodes k represents */
182  nel += nvk; /* nv[k] nodes of A eliminated */
183 
184  /* --- Garbage collection ------------------------------------------- */
185  if (elenk > 0 && cnz + mindeg >= nzmax) {
186  for (j = 0; j < n; j++) {
187  if ((p = Cp[j]) >= 0) /* j is a live node or element */
188  {
189  Cp[j] = Ci[p]; /* save first entry of object */
190  Ci[p] = amd_flip(j); /* first entry is now amd_flip(j) */
191  }
192  }
193  for (q = 0, p = 0; p < cnz;) /* scan all of memory */
194  {
195  if ((j = amd_flip(Ci[p++])) >= 0) /* found object j */
196  {
197  Ci[q] = Cp[j]; /* restore first entry of object */
198  Cp[j] = q++; /* new pointer to object j */
199  for (k3 = 0; k3 < len[j] - 1; k3++) Ci[q++] = Ci[p++];
200  }
201  }
202  cnz = q; /* Ci[cnz...nzmax-1] now free */
203  }
204 
205  /* --- Construct new element ---------------------------------------- */
206  dk = 0;
207  nv[k] = -nvk; /* flag k as in Lk */
208  p = Cp[k];
209  pk1 = (elenk == 0) ? p : cnz; /* do in place if elen[k] == 0 */
210  pk2 = pk1;
211  for (k1 = 1; k1 <= elenk + 1; k1++) {
212  if (k1 > elenk) {
213  e = k; /* search the nodes in k */
214  pj = p; /* list of nodes starts at Ci[pj]*/
215  ln = len[k] - elenk; /* length of list of nodes in k */
216  } else {
217  e = Ci[p++]; /* search the nodes in e */
218  pj = Cp[e];
219  ln = len[e]; /* length of list of nodes in e */
220  }
221  for (k2 = 1; k2 <= ln; k2++) {
222  i = Ci[pj++];
223  if ((nvi = nv[i]) <= 0) continue; /* node i dead, or seen */
224  dk += nvi; /* degree[Lk] += size of node i */
225  nv[i] = -nvi; /* negate nv[i] to denote i in Lk*/
226  Ci[pk2++] = i; /* place i in Lk */
227  if (next[i] != -1) last[next[i]] = last[i];
228  if (last[i] != -1) /* remove i from degree list */
229  {
230  next[last[i]] = next[i];
231  } else {
232  head[degree[i]] = next[i];
233  }
234  }
235  if (e != k) {
236  Cp[e] = amd_flip(k); /* absorb e into k */
237  w[e] = 0; /* e is now a dead element */
238  }
239  }
240  if (elenk != 0) cnz = pk2; /* Ci[cnz...nzmax] is free */
241  degree[k] = dk; /* external degree of k - |Lk\i| */
242  Cp[k] = pk1; /* element k is in Ci[pk1..pk2-1] */
243  len[k] = pk2 - pk1;
244  elen[k] = -2; /* k is now an element */
245 
246  /* --- Find set differences ----------------------------------------- */
247  mark = internal::cs_wclear<StorageIndex>(mark, lemax, w, n); /* clear w if necessary */
248  for (pk = pk1; pk < pk2; pk++) /* scan 1: find |Le\Lk| */
249  {
250  i = Ci[pk];
251  if ((eln = elen[i]) <= 0) continue; /* skip if elen[i] empty */
252  nvi = -nv[i]; /* nv[i] was negated */
253  wnvi = mark - nvi;
254  for (p = Cp[i]; p <= Cp[i] + eln - 1; p++) /* scan Ei */
255  {
256  e = Ci[p];
257  if (w[e] >= mark) {
258  w[e] -= nvi; /* decrement |Le\Lk| */
259  } else if (w[e] != 0) /* ensure e is a live element */
260  {
261  w[e] = degree[e] + wnvi; /* 1st time e seen in scan 1 */
262  }
263  }
264  }
265 
266  /* --- Degree update ------------------------------------------------ */
267  for (pk = pk1; pk < pk2; pk++) /* scan2: degree update */
268  {
269  i = Ci[pk]; /* consider node i in Lk */
270  p1 = Cp[i];
271  p2 = p1 + elen[i] - 1;
272  pn = p1;
273  for (h = 0, d = 0, p = p1; p <= p2; p++) /* scan Ei */
274  {
275  e = Ci[p];
276  if (w[e] != 0) /* e is an unabsorbed element */
277  {
278  dext = w[e] - mark; /* dext = |Le\Lk| */
279  if (dext > 0) {
280  d += dext; /* sum up the set differences */
281  Ci[pn++] = e; /* keep e in Ei */
282  h += e; /* compute the hash of node i */
283  } else {
284  Cp[e] = amd_flip(k); /* aggressive absorb. e->k */
285  w[e] = 0; /* e is a dead element */
286  }
287  }
288  }
289  elen[i] = pn - p1 + 1; /* elen[i] = |Ei| */
290  p3 = pn;
291  p4 = p1 + len[i];
292  for (p = p2 + 1; p < p4; p++) /* prune edges in Ai */
293  {
294  j = Ci[p];
295  if ((nvj = nv[j]) <= 0) continue; /* node j dead or in Lk */
296  d += nvj; /* degree(i) += |j| */
297  Ci[pn++] = j; /* place j in node list of i */
298  h += j; /* compute hash for node i */
299  }
300  if (d == 0) /* check for mass elimination */
301  {
302  Cp[i] = amd_flip(k); /* absorb i into k */
303  nvi = -nv[i];
304  dk -= nvi; /* |Lk| -= |i| */
305  nvk += nvi; /* |k| += nv[i] */
306  nel += nvi;
307  nv[i] = 0;
308  elen[i] = -1; /* node i is dead */
309  } else {
310  degree[i] = std::min<StorageIndex>(degree[i], d); /* update degree(i) */
311  Ci[pn] = Ci[p3]; /* move first node to end */
312  Ci[p3] = Ci[p1]; /* move 1st el. to end of Ei */
313  Ci[p1] = k; /* add k as 1st element in of Ei */
314  len[i] = pn - p1 + 1; /* new len of adj. list of node i */
315  h %= n; /* finalize hash of i */
316  next[i] = hhead[h]; /* place i in hash bucket */
317  hhead[h] = i;
318  last[i] = h; /* save hash of i in last[i] */
319  }
320  } /* scan2 is done */
321  degree[k] = dk; /* finalize |Lk| */
322  lemax = std::max<StorageIndex>(lemax, dk);
323  mark = internal::cs_wclear<StorageIndex>(mark + lemax, lemax, w, n); /* clear w */
324 
325  /* --- Supernode detection ------------------------------------------ */
326  for (pk = pk1; pk < pk2; pk++) {
327  i = Ci[pk];
328  if (nv[i] >= 0) continue; /* skip if i is dead */
329  h = last[i]; /* scan hash bucket of node i */
330  i = hhead[h];
331  hhead[h] = -1; /* hash bucket will be empty */
332  for (; i != -1 && next[i] != -1; i = next[i], mark++) {
333  ln = len[i];
334  eln = elen[i];
335  for (p = Cp[i] + 1; p <= Cp[i] + ln - 1; p++) w[Ci[p]] = mark;
336  jlast = i;
337  for (j = next[i]; j != -1;) /* compare i with all j */
338  {
339  ok = (len[j] == ln) && (elen[j] == eln);
340  for (p = Cp[j] + 1; ok && p <= Cp[j] + ln - 1; p++) {
341  if (w[Ci[p]] != mark) ok = 0; /* compare i and j*/
342  }
343  if (ok) /* i and j are identical */
344  {
345  Cp[j] = amd_flip(i); /* absorb j into i */
346  nv[i] += nv[j];
347  nv[j] = 0;
348  elen[j] = -1; /* node j is dead */
349  j = next[j]; /* delete j from hash bucket */
350  next[jlast] = j;
351  } else {
352  jlast = j; /* j and i are different */
353  j = next[j];
354  }
355  }
356  }
357  }
358 
359  /* --- Finalize new element------------------------------------------ */
360  for (p = pk1, pk = pk1; pk < pk2; pk++) /* finalize Lk */
361  {
362  i = Ci[pk];
363  if ((nvi = -nv[i]) <= 0) continue; /* skip if i is dead */
364  nv[i] = nvi; /* restore nv[i] */
365  d = degree[i] + dk - nvi; /* compute external degree(i) */
366  d = std::min<StorageIndex>(d, n - nel - nvi);
367  if (head[d] != -1) last[head[d]] = i;
368  next[i] = head[d]; /* put i back in degree list */
369  last[i] = -1;
370  head[d] = i;
371  mindeg = std::min<StorageIndex>(mindeg, d); /* find new minimum degree */
372  degree[i] = d;
373  Ci[p++] = i; /* place i in Lk */
374  }
375  nv[k] = nvk; /* # nodes absorbed into k */
376  if ((len[k] = p - pk1) == 0) /* length of adj list of element k*/
377  {
378  Cp[k] = -1; /* k is a root of the tree */
379  w[k] = 0; /* k is now a dead element */
380  }
381  if (elenk != 0) cnz = p; /* free unused space in Lk */
382  }
383 
384  /* --- Postordering ----------------------------------------------------- */
385  for (i = 0; i < n; i++) Cp[i] = amd_flip(Cp[i]); /* fix assembly tree */
386  for (j = 0; j <= n; j++) head[j] = -1;
387  for (j = n; j >= 0; j--) /* place unordered nodes in lists */
388  {
389  if (nv[j] > 0) continue; /* skip if j is an element */
390  next[j] = head[Cp[j]]; /* place j in list of its parent */
391  head[Cp[j]] = j;
392  }
393  for (e = n; e >= 0; e--) /* place elements in lists */
394  {
395  if (nv[e] <= 0) continue; /* skip unless e is an element */
396  if (Cp[e] != -1) {
397  next[e] = head[Cp[e]]; /* place e in list of its parent */
398  head[Cp[e]] = e;
399  }
400  }
401  for (k = 0, i = 0; i <= n; i++) /* postorder the assembly tree */
402  {
403  if (Cp[i] == -1) k = internal::cs_tdfs<StorageIndex>(i, k, head, next, perm.indices().data(), w);
404  }
405 
406  perm.indices().conservativeResize(n);
407 }
AnnoyingScalar sqrt(const AnnoyingScalar &x)
Definition: AnnoyingScalar.h:134
int i
Definition: BiCGSTAB_step_by_step.cpp:9
const unsigned n
Definition: CG3DPackingUnitTest.cpp:11
Array< double, 1, 3 > e(1./3., 0.5, 2.)
Vector3f p1
Definition: MatrixBase_all.cpp:2
RowVector3d w
Definition: Matrix_resize_int.cpp:3
#define ei_declare_aligned_stack_constructed_variable(TYPE, NAME, SIZE, BUFFER)
Definition: Memory.h:806
float * p
Definition: Tutorial_Map_using.cpp:9
void resize(Index newSize)
Definition: PermutationMatrix.h:119
const IndicesType & indices() const
Definition: PermutationMatrix.h:334
Definition: matrices.h:74
#define min(a, b)
Definition: datatypes.h:22
static constexpr const last_t last
Definition: IndexedViewHelper.h:48
char char char int int * k
Definition: level2_impl.h:374
T amd_flip(const T &i)
Definition: Amd.h:32
EIGEN_DEVICE_FUNC const Scalar & q
Definition: SpecialFunctionsImpl.h:2019
const Mdouble degree
Definition: ExtendedMath.h:32
@ W
Definition: quadtree.h:63
t
Definition: plotPSD.py:36
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2

References Eigen::internal::amd_flip(), constants::degree, e(), ei_declare_aligned_stack_constructed_variable, i, Eigen::PermutationMatrix< SizeAtCompileTime, MaxSizeAtCompileTime, StorageIndex_ >::indices(), j, k, Eigen::placeholders::last, min, n, p, p1, Eigen::numext::q, Eigen::PermutationBase< Derived >::resize(), sqrt(), plotPSD::t, w, and oomph::QuadTreeNames::W.

Referenced by Eigen::AMDOrdering< StorageIndex >::operator()().

◆ ordering_helper_at_plus_a()

template<typename MatrixType >
void Eigen::internal::ordering_helper_at_plus_a ( const MatrixType A,
MatrixType symmat 
)
Parameters
[in]Athe input non-symmetric matrix
[out]symmatthe symmetric pattern A^T+A from the input matrix A. FIXME: The values should not be considered here
30  {
31  MatrixType C;
32  C = A.transpose(); // NOTE: Could be costly
33  for (int i = 0; i < C.rows(); i++) {
34  for (typename MatrixType::InnerIterator it(C, i); it; ++it) it.valueRef() = typename MatrixType::Scalar(0);
35  }
36  symmat = C + A;
37 }
SCALAR Scalar
Definition: bench_gemm.cpp:45
Matrix< SCALARA, Dynamic, Dynamic, opt_A > A
Definition: bench_gemm.cpp:47
Matrix< Scalar, Dynamic, Dynamic > C
Definition: bench_gemm.cpp:49
MatrixXf MatrixType
Definition: benchmark-blocking-sizes.cpp:52

References i.

Referenced by Eigen::AMDOrdering< StorageIndex >::operator()().