sym-ildl  1.2
Incomplete LDL' factorizations of indefinite symmetric and skew-symmetric matrices.
lilc_matrix_sym_amd.h
1 //-*- mode: c++ -*-
2 #ifndef _LIL_MATRIX_SYM_AMD_H_
3 #define _LIL_MATRIX_SYM_AMD_H_
4 
5 //adapted from EIGEN
6 //need to include license file later
7 
8 namespace amd {
9  inline int amd_flip(const int& i) { return -i-2; }
10 
11  /* clear w */
12  inline int wclear (int mark, int lemax, int *w, int n)
13  {
14  int k;
15  if(mark < 2 || (mark + lemax < 0))
16  {
17  for(k = 0; k < n; k++)
18  if(w[k] != 0)
19  w[k] = 1;
20  mark = 2;
21  }
22  return (mark); /* at this point, w[0..n-1] < mark holds */
23  }
24 
25  /* depth-first search and postorder of a tree rooted at node j */
26  inline int tdfs(int j, int k, int *head, const int *next, int *post, int *stack)
27  {
28  int i, p, top = 0;
29  if(!head || !next || !post || !stack) return (-1); /* check inputs */
30  stack[0] = j; /* place j on the stack */
31  while (top >= 0) /* while (stack is not empty) */
32  {
33  p = stack[top]; /* p = top of stack */
34  i = head[p]; /* i = youngest child of p */
35  if(i == -1)
36  {
37  top--; /* p has no unordered children left */
38  post[k++] = p; /* node p is the kth postordered node */
39  }
40  else
41  {
42  head[p] = next[i]; /* remove i from children of p */
43  stack[++top] = i; /* start dfs on child node i */
44  }
45  }
46  return k;
47  }
48 }
49 
55 template<class el_type>
56 inline void lilc_matrix<el_type> :: sym_amd(vector<int>& perm) {
57  using std::sqrt;
58  using std::min;
59  using std::max;
60 
61  int d, dk, dext, lemax = 0, e, elenk, eln, i, j, k, k1,
62  k2, k3, jlast, ln, dense, nzmax, mindeg = 0, nvi, nvj, nvk, mark, wnvi,
63  ok, nel = 0, p, p1, p2, p3, p4, pj, pk, pk1, pk2, pn, q, t;
64  unsigned int h;
65 
66  int n = this->n_rows();
67  dense = max(16, int(10 * sqrt(double(n)))); /* find dense threshold */
68  dense = min(n-2, dense);
69 
70  perm.resize(n);
71 
72  int* W = new int[8*(n+1)]; /* get workspace */
73  int* len = W;
74  int* nv = W + (n+1);
75  int* next = W + 2*(n+1);
76  int* head = W + 3*(n+1);
77  int* elen = W + 4*(n+1);
78  int* degree = W + 5*(n+1);
79  int* w = W + 6*(n+1);
80  int* hhead = W + 7*(n+1);
81 
82  int* temp_perm = new int[n+1];
83  int* last = temp_perm; //using temp_perms storage space as workspace for last
84 
85  /* --- Initialize quotient graph ---------------------------------------- */
86  int cnz = this->nnz();
87  t = 3*cnz + 3*cnz/5 + 2*n; /* add elbow room to C */
88  int* Cp = new int[n+1];
89  int* Ci = new int[t];
90 
91  int cnt = 0;
92  Cp[0] = 0;
93  for (int i = 0; i < n; i++) {
94  for (int j = 0; j < (int) list[i].size(); j++) {
95  Ci[cnt++] = list[i][j];
96  }
97  for (int j = 0; j < (int) m_idx[i].size(); j++) {
98  Ci[cnt++] = m_idx[i][j];
99  }
100  Cp[i+1] = cnt;
101  }
102  cnz = cnt;
103 
104  for(k = 0; k < n; k++)
105  len[k] = Cp[k+1] - Cp[k];
106  len[n] = 0;
107  nzmax = t;
108 
109  for(i = 0; i <= n; i++)
110  {
111  head[i] = -1; // degree list i is empty
112  last[i] = -1;
113  next[i] = -1;
114  hhead[i] = -1; // hash list i is empty
115  nv[i] = 1; // node i is just one node
116  w[i] = 1; // node i is alive
117  elen[i] = 0; // Ek of node i is empty
118  degree[i] = len[i]; // degree of node i
119  }
120  mark = amd::wclear(0, 0, w, n); /* clear w */
121  elen[n] = -2; /* n is a dead element */
122  Cp[n] = -1; /* n is a root of assembly tree */
123  w[n] = 0; /* n is a dead element */
124 
125  /* --- Initialize degree lists ------------------------------------------ */
126  for(i = 0; i < n; i++)
127  {
128  d = degree[i];
129  if(d == 0) /* node i is empty */
130  {
131  elen[i] = -2; /* element i is dead */
132  nel++;
133  Cp[i] = -1; /* i is a root of assembly tree */
134  w[i] = 0;
135  }
136  else if(d > dense) /* node i is dense */
137  {
138  nv[i] = 0; /* absorb i into element n */
139  elen[i] = -1; /* node i is dead */
140  nel++;
141  Cp[i] = amd::amd_flip (n);
142  nv[n]++;
143  }
144  else
145  {
146  if(head[d] != -1) last[head[d]] = i;
147  next[i] = head[d]; /* put node i in degree list d */
148  head[d] = i;
149  }
150  }
151 
152  while (nel < n) /* while (selecting pivots) do */
153  {
154  /* --- Select node of minimum approximate degree -------------------- */
155  for(k = -1; mindeg < n && (k = head[mindeg]) == -1; mindeg++) {}
156  if(next[k] != -1) last[next[k]] = -1;
157  head[mindeg] = next[k]; /* remove k from degree list */
158  elenk = elen[k]; /* elenk = |Ek| */
159  nvk = nv[k]; /* # of nodes k represents */
160  nel += nvk; /* nv[k] nodes of A eliminated */
161 
162  /* --- Garbage collection ------------------------------------------- */
163  if(elenk > 0 && cnz + mindeg >= nzmax)
164  {
165  for(j = 0; j < n; j++)
166  {
167  if((p = Cp[j]) >= 0) /* j is a live node or element */
168  {
169  Cp[j] = Ci[p]; /* save first entry of object */
170  Ci[p] = amd::amd_flip (j); /* first entry is now amd::amd_flip(j) */
171  }
172  }
173  for(q = 0, p = 0; p < cnz; ) /* scan all of memory */
174  {
175  if((j = amd::amd_flip (Ci[p++])) >= 0) /* found object j */
176  {
177  Ci[q] = Cp[j]; /* restore first entry of object */
178  Cp[j] = q++; /* new pointer to object j */
179  for(k3 = 0; k3 < len[j]-1; k3++) Ci[q++] = Ci[p++];
180  }
181  }
182  cnz = q; /* Ci[cnz...nzmax-1] now free */
183  }
184 
185  /* --- Construct new element ---------------------------------------- */
186  dk = 0;
187  nv[k] = -nvk; /* flag k as in Lk */
188  p = Cp[k];
189  pk1 = (elenk == 0) ? p : cnz; /* do in place if elen[k] == 0 */
190  pk2 = pk1;
191 
192  for(k1 = 1; k1 <= elenk + 1; k1++)
193  {
194  if(k1 > elenk)
195  {
196  e = k; /* search the nodes in k */
197  pj = p; /* list of nodes starts at Ci[pj]*/
198  ln = len[k] - elenk; /* length of list of nodes in k */
199  }
200  else
201  {
202  e = Ci[p++]; /* search the nodes in e */
203  pj = Cp[e];
204  ln = len[e]; /* length of list of nodes in e */
205  }
206  for(k2 = 1; k2 <= ln; k2++)
207  {
208  i = Ci[pj++];
209  if((nvi = nv[i]) <= 0) continue; /* node i dead, or seen */
210  dk += nvi; /* degree[Lk] += size of node i */
211  nv[i] = -nvi; /* negate nv[i] to denote i in Lk*/
212  Ci[pk2++] = i; /* place i in Lk */
213  if(next[i] != -1) last[next[i]] = last[i];
214  if(last[i] != -1) /* remove i from degree list */
215  {
216  next[last[i]] = next[i];
217  }
218  else
219  {
220  head[degree[i]] = next[i];
221  }
222  }
223  if(e != k)
224  {
225  Cp[e] = amd::amd_flip (k); /* absorb e into k */
226  w[e] = 0; /* e is now a dead element */
227  }
228  }
229  if(elenk != 0) cnz = pk2; /* Ci[cnz...nzmax] is free */
230  degree[k] = dk; /* external degree of k - |Lk\i| */
231  Cp[k] = pk1; /* element k is in Ci[pk1..pk2-1] */
232  len[k] = pk2 - pk1;
233  elen[k] = -2; /* k is now an element */
234 
235  /* --- Find set differences ----------------------------------------- */
236  mark = amd::wclear(mark, lemax, w, n); /* clear w if necessary */
237  for(pk = pk1; pk < pk2; pk++) /* scan 1: find |Le\Lk| */
238  {
239  i = Ci[pk];
240  if((eln = elen[i]) <= 0) continue;/* skip if elen[i] empty */
241  nvi = -nv[i]; /* nv[i] was negated */
242  wnvi = mark - nvi;
243  for(p = Cp[i]; p <= Cp[i] + eln - 1; p++) /* scan Ei */
244  {
245  e = Ci[p];
246  if(w[e] >= mark)
247  {
248  w[e] -= nvi; /* decrement |Le\Lk| */
249  }
250  else if(w[e] != 0) /* ensure e is a live element */
251  {
252  w[e] = degree[e] + wnvi; /* 1st time e seen in scan 1 */
253  }
254  }
255  }
256 
257  /* --- Degree update ------------------------------------------------ */
258  for(pk = pk1; pk < pk2; pk++) /* scan2: degree update */
259  {
260  i = Ci[pk]; /* consider node i in Lk */
261  p1 = Cp[i];
262  p2 = p1 + elen[i] - 1;
263  pn = p1;
264  for(h = 0, d = 0, p = p1; p <= p2; p++) /* scan Ei */
265  {
266  e = Ci[p];
267  if(w[e] != 0) /* e is an unabsorbed element */
268  {
269  dext = w[e] - mark; /* dext = |Le\Lk| */
270  if(dext > 0)
271  {
272  d += dext; /* sum up the set differences */
273  Ci[pn++] = e; /* keep e in Ei */
274  h += e; /* compute the hash of node i */
275  }
276  else
277  {
278  Cp[e] = amd::amd_flip (k); /* aggressive absorb. e->k */
279  w[e] = 0; /* e is a dead element */
280  }
281  }
282  }
283  elen[i] = pn - p1 + 1; /* elen[i] = |Ei| */
284  p3 = pn;
285  p4 = p1 + len[i];
286  for(p = p2 + 1; p < p4; p++) /* prune edges in Ai */
287  {
288  j = Ci[p];
289  if((nvj = nv[j]) <= 0) continue; /* node j dead or in Lk */
290  d += nvj; /* degree(i) += |j| */
291  Ci[pn++] = j; /* place j in node list of i */
292  h += j; /* compute hash for node i */
293  }
294  if(d == 0) /* check for mass elimination */
295  {
296  Cp[i] = amd::amd_flip (k); /* absorb i into k */
297  nvi = -nv[i];
298  dk -= nvi; /* |Lk| -= |i| */
299  nvk += nvi; /* |k| += nv[i] */
300  nel += nvi;
301  nv[i] = 0;
302  elen[i] = -1; /* node i is dead */
303  }
304  else
305  {
306  degree[i] = std::min(degree[i], d); /* update degree(i) */
307  Ci[pn] = Ci[p3]; /* move first node to end */
308  Ci[p3] = Ci[p1]; /* move 1st el. to end of Ei */
309  Ci[p1] = k; /* add k as 1st element in of Ei */
310  len[i] = pn - p1 + 1; /* new len of adj. list of node i */
311  h %= n; /* finalize hash of i */
312  next[i] = hhead[h]; /* place i in hash bucket */
313  hhead[h] = i;
314  last[i] = h; /* save hash of i in last[i] */
315  }
316  } /* scan2 is done */
317  degree[k] = dk; /* finalize |Lk| */
318  lemax = std::max(lemax, dk);
319  mark = amd::wclear(mark+lemax, lemax, w, n); /* clear w */
320 
321  /* --- Supernode detection ------------------------------------------ */
322  for(pk = pk1; pk < pk2; pk++)
323  {
324  i = Ci[pk];
325  if(nv[i] >= 0) continue; /* skip if i is dead */
326  h = last[i]; /* scan hash bucket of node i */
327  i = hhead[h];
328  hhead[h] = -1; /* hash bucket will be empty */
329  for(; i != -1 && next[i] != -1; i = next[i], mark++)
330  {
331  ln = len[i];
332  eln = elen[i];
333  for(p = Cp[i]+1; p <= Cp[i] + ln-1; p++) w[Ci[p]] = mark;
334  jlast = i;
335  for(j = next[i]; j != -1; ) /* compare i with all j */
336  {
337  ok = (len[j] == ln) && (elen[j] == eln);
338  for(p = Cp[j] + 1; ok && p <= Cp[j] + ln - 1; p++)
339  {
340  if(w[Ci[p]] != mark) ok = 0; /* compare i and j*/
341  }
342  if(ok) /* i and j are identical */
343  {
344  Cp[j] = amd::amd_flip (i); /* absorb j into i */
345  nv[i] += nv[j];
346  nv[j] = 0;
347  elen[j] = -1; /* node j is dead */
348  j = next[j]; /* delete j from hash bucket */
349  next[jlast] = j;
350  }
351  else
352  {
353  jlast = j; /* j and i are different */
354  j = next[j];
355  }
356  }
357  }
358  }
359 
360  /* --- Finalize new element------------------------------------------ */
361  for(p = pk1, pk = pk1; pk < pk2; pk++) /* finalize Lk */
362  {
363  i = Ci[pk];
364  if((nvi = -nv[i]) <= 0) continue;/* skip if i is dead */
365  nv[i] = nvi; /* restore nv[i] */
366  d = degree[i] + dk - nvi; /* compute external degree(i) */
367  d = std::min(d, n - nel - nvi);
368  if(head[d] != -1) last[head[d]] = i;
369  next[i] = head[d]; /* put i back in degree list */
370  last[i] = -1;
371  head[d] = i;
372  mindeg = std::min(mindeg, d); /* find new minimum degree */
373  degree[i] = d;
374  Ci[p++] = i; /* place i in Lk */
375  }
376  nv[k] = nvk; /* # nodes absorbed into k */
377  if((len[k] = p-pk1) == 0) /* length of adj list of element k*/
378  {
379  Cp[k] = -1; /* k is a root of the tree */
380  w[k] = 0; /* k is now a dead element */
381  }
382  if(elenk != 0) cnz = p; /* free unused space in Lk */
383  }
384 
385 
386  /* --- Postordering ----------------------------------------------------- */
387  for(i = 0; i < n; i++) Cp[i] = amd::amd_flip (Cp[i]);/* fix assembly tree */
388  for(j = 0; j <= n; j++) head[j] = -1;
389  for(j = n; j >= 0; j--) /* place unordered nodes in lists */
390  {
391  if(nv[j] > 0) continue; /* skip if j is an element */
392  next[j] = head[Cp[j]]; /* place j in list of its parent */
393  head[Cp[j]] = j;
394  }
395  for(e = n; e >= 0; e--) /* place elements in lists */
396  {
397  if(nv[e] <= 0) continue; /* skip unless e is an element */
398  if(Cp[e] != -1)
399  {
400  next[e] = head[Cp[e]]; /* place e in list of its parent */
401  head[Cp[e]] = e;
402  }
403  }
404 
405  for(k = 0, i = 0; i <= n; i++) /* postorder the assembly tree */
406  {
407  if(Cp[i] == -1) k = amd::tdfs(i, k, head, next, temp_perm, w);
408  }
409 
410  for (int i = 0; i < n; i++) {
411  perm[i] = temp_perm[i];
412  }
413 
414  delete[] W;
415  delete[] temp_perm;
416  delete[] Cp;
417  delete[] Ci;
418 
419 }
420 
421 #endif
void sym_amd(vector< int > &perm)
Returns a Approximate Minimum Degree ordering of the matrix A (stored in perm).
Definition: lilc_matrix_sym_amd.h:56
Definition: lilc_matrix_sym_amd.h:8