Amesos Package Browser (Single Doxygen Collection)  Development
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
amesos_cholmod_l_nesdis.c
Go to the documentation of this file.
1 /* ========================================================================== */
2 /* === Partition/cholmod_nesdis ============================================= */
3 /* ========================================================================== */
4 
5 /* -----------------------------------------------------------------------------
6  * CHOLMOD/Partition Module.
7  * Copyright (C) 2005-2006, Univ. of Florida. Author: Timothy A. Davis
8  * The CHOLMOD/Partition Module is licensed under Version 2.1 of the GNU
9  * Lesser General Public License. See lesser.txt for a text of the license.
10  * CHOLMOD is also available under other licenses; contact authors for details.
11  * http://www.cise.ufl.edu/research/sparse
12  * -------------------------------------------------------------------------- */
13 
14 /* CHOLMOD nested dissection and graph partitioning.
15  *
16  * cholmod_bisect:
17  *
18  * Finds a set of nodes that partitions the graph into two parts.
19  * Compresses the graph first. Requires METIS.
20  *
21  * cholmod_nested_dissection:
22  *
23  * Nested dissection, using its own compression and connected-commponents
24  * algorithms, an external graph partitioner (METIS), and a constrained
25  * minimum degree ordering algorithm (CCOLAMD or CSYMAMD). Typically
26  * gives better orderings than METIS_NodeND (about 5% to 10% fewer
27  * nonzeros in L).
28  *
29  * cholmod_collapse_septree:
30  *
31  * Prune the separator tree returned by cholmod_nested_dissection.
32  *
33  * This file contains several routines private to this file:
34  *
35  * partition compress and partition a graph
36  * clear_flag clear Common->Flag, but do not modify negative entries
37  * find_components find the connected components of a graph
38  *
39  * Supports any xtype (pattern, real, complex, or zomplex).
40  */
41 
42 #ifndef NPARTITION
43 
44 /* This file should make the long int version of CHOLMOD */
45 #define DLONG 1
46 
50 
51 /* ========================================================================== */
52 /* === partition ============================================================ */
53 /* ========================================================================== */
54 
55 /* Find a set of nodes that partition a graph. The graph must be symmetric
56  * with no diagonal entries. To compress the graph first, compress is TRUE
57  * and on input Hash [j] holds the hash key for node j, which must be in the
58  * range 0 to csize-1. The input graph (Cp, Ci) is destroyed. Cew is all 1's
59  * on input and output. Cnw [j] > 0 is the initial weight of node j. On
60  * output, Cnw [i] = 0 if node i is absorbed into j and the original weight
61  * Cnw [i] is added to Cnw [j]. If compress is FALSE, the graph is not
62  * compressed and Cnw and Hash are unmodified. The partition itself is held in
63  * the output array Part of size n. Part [j] is 0, 1, or 2, depending on
64  * whether node j is in the left part of the graph, the right part, or the
65  * separator, respectively. Note that the input graph need not be connected,
66  * and the output subgraphs (the three parts) may also be unconnected.
67  *
68  * Returns the size of the separator, in terms of the sum of the weights of
69  * the nodes. It is guaranteed to be between 1 and the total weight of all
70  * the nodes. If it is of size less than the total weight, then both the left
71  * and right parts are guaranteed to be non-empty (this guarantee depends on
72  * cholmod_metis_bisector).
73  */
74 
75 static UF_long partition /* size of separator or -1 if failure */
76 (
77  /* inputs, not modified on output */
78 #ifndef NDEBUG
79  Int csize, /* upper bound on # of edges in the graph;
80  * csize >= MAX (n, nnz(C)) must hold. */
81 #endif
82  int compress, /* if TRUE the compress the graph first */
83 
84  /* input/output */
85  Int Hash [ ], /* Hash [i] = hash >= 0 is the hash function for node
86  * i on input. On output, Hash [i] = FLIP (j) if node
87  * i is absorbed into j. Hash [i] >= 0 if i has not
88  * been absorbed. */
89 
90  /* input graph, compressed graph of cn nodes on output */
92 
93  /* input/output */
94  Int Cnw [ ], /* size n. Cnw [j] > 0 is the weight of node j on
95  * input. On output, if node i is absorbed into
96  * node j, then Cnw [i] = 0 and the original weight of
97  * node i is added to Cnw [j]. The sum of Cnw [0..n-1]
98  * is not modified. */
99 
100  /* workspace */
101  Int Cew [ ], /* size csize, all 1's on input and output */
102 
103  /* more workspace, undefined on input and output */
104  Int Cmap [ ], /* size n (i/i/l) */
105 
106  /* output */
107  Int Part [ ], /* size n, Part [j] = 0, 1, or 2. */
108 
109  cholmod_common *Common
110 )
111 {
112  Int n, hash, head, i, j, k, p, pend, ilen, ilast, pi, piend,
113  jlen, ok, cn, csep, pdest, nodes_pruned, nz, total_weight, jscattered ;
114  Int *Cp, *Ci, *Next, *Hhead ;
115 
116 #ifndef NDEBUG
117  Int cnt, pruned ;
118  double work = 0, goodwork = 0 ;
119 #endif
120 
121  /* ---------------------------------------------------------------------- */
122  /* quick return for small or empty graphs */
123  /* ---------------------------------------------------------------------- */
124 
125  n = C->nrow ;
126  Cp = C->p ;
127  Ci = C->i ;
128  nz = Cp [n] ;
129 
130  PRINT2 (("Partition start, n "ID" nz "ID"\n", n, nz)) ;
131 
132  total_weight = 0 ;
133  for (j = 0 ; j < n ; j++)
134  {
135  ASSERT (Cnw [j] > 0) ;
136  total_weight += Cnw [j] ;
137  }
138 
139  if (n <= 2)
140  {
141  /* very small graph */
142  for (j = 0 ; j < n ; j++)
143  {
144  Part [j] = 2 ;
145  }
146  return (total_weight) ;
147  }
148  else if (nz <= 0)
149  {
150  /* no edges, this is easy */
151  PRINT2 (("diagonal matrix\n")) ;
152  k = n/2 ;
153  for (j = 0 ; j < k ; j++)
154  {
155  Part [j] = 0 ;
156  }
157  for ( ; j < n ; j++)
158  {
159  Part [j] = 1 ;
160  }
161  /* ensure the separator is not empty (required by nested dissection) */
162  Part [n-1] = 2 ;
163  return (Cnw [n-1]) ;
164  }
165 
166 #ifndef NDEBUG
167  ASSERT (n > 1 && nz > 0) ;
168  PRINT2 (("original graph:\n")) ;
169  for (j = 0 ; j < n ; j++)
170  {
171  PRINT2 ((""ID": ", j)) ;
172  for (p = Cp [j] ; p < Cp [j+1] ; p++)
173  {
174  i = Ci [p] ;
175  PRINT3 ((""ID" ", i)) ;
176  ASSERT (i >= 0 && i < n && i != j) ;
177  }
178  PRINT2 (("hash: "ID"\n", Hash [j])) ;
179  }
180  DEBUG (for (p = 0 ; p < csize ; p++) ASSERT (Cew [p] == 1)) ;
181 #endif
182 
183  nodes_pruned = 0 ;
184 
185  if (compress)
186  {
187 
188  /* ------------------------------------------------------------------ */
189  /* get workspace */
190  /* ------------------------------------------------------------------ */
191 
192  Next = Part ; /* use Part as workspace for Next [ */
193  Hhead = Cew ; /* use Cew as workspace for Hhead [ */
194 
195  /* ------------------------------------------------------------------ */
196  /* create the hash buckets */
197  /* ------------------------------------------------------------------ */
198 
199  for (j = 0 ; j < n ; j++)
200  {
201  /* get the hash key for node j */
202  hash = Hash [j] ;
203  ASSERT (hash >= 0 && hash < csize) ;
204  head = Hhead [hash] ;
205  if (head > EMPTY)
206  {
207  /* hash bucket for this hash key is empty. */
208  head = EMPTY ;
209  }
210  else
211  {
212  /* hash bucket for this hash key is not empty. get old head */
213  head = FLIP (head) ;
214  ASSERT (head >= 0 && head < n) ;
215  }
216  /* node j becomes the new head of the hash bucket. FLIP it so that
217  * we can tell the difference between an empty or non-empty hash
218  * bucket. */
219  Hhead [hash] = FLIP (j) ;
220  Next [j] = head ;
221  ASSERT (head >= EMPTY && head < n) ;
222  }
223 
224 #ifndef NDEBUG
225  for (cnt = 0, k = 0 ; k < n ; k++)
226  {
227  ASSERT (Hash [k] >= 0 && Hash [k] < csize) ; /* k is alive */
228  hash = Hash [k] ;
229  ASSERT (hash >= 0 && hash < csize) ;
230  head = Hhead [hash] ;
231  ASSERT (head < EMPTY) ; /* hash bucket not empty */
232  j = FLIP (head) ;
233  ASSERT (j >= 0 && j < n) ;
234  if (j == k)
235  {
236  PRINT2 (("hash "ID": ", hash)) ;
237  for ( ; j != EMPTY ; j = Next [j])
238  {
239  PRINT3 ((" "ID"", j)) ;
240  ASSERT (j >= 0 && j < n) ;
241  ASSERT (Hash [j] == hash) ;
242  cnt++ ;
243  ASSERT (cnt <= n) ;
244  }
245  PRINT2 (("\n")) ;
246  }
247  }
248  ASSERT (cnt == n) ;
249 #endif
250 
251  /* ------------------------------------------------------------------ */
252  /* scan the non-empty hash buckets for indistinguishable nodes */
253  /* ------------------------------------------------------------------ */
254 
255  /* If there are no hash collisions and no compression occurs, this takes
256  * O(n) time. If no hash collisions, but some nodes are removed, this
257  * takes time O(n+e) where e is the sum of the degress of the nodes
258  * that are removed. Even with many hash collisions (a rare case),
259  * this algorithm has never been observed to perform more than nnz(A)
260  * useless work.
261  *
262  * Cmap is used as workspace to mark nodes of the graph, [
263  * for comparing the nonzero patterns of two nodes i and j.
264  */
265 
266 #define Cmap_MARK(i) Cmap [i] = j
267 #define Cmap_MARKED(i) (Cmap [i] == j)
268 
269  for (i = 0 ; i < n ; i++)
270  {
271  Cmap [i] = EMPTY ;
272  }
273 
274  for (k = 0 ; k < n ; k++)
275  {
276  hash = Hash [k] ;
277  ASSERT (hash >= FLIP (n-1) && hash < csize) ;
278  if (hash < 0)
279  {
280  /* node k has already been absorbed into some other node */
281  ASSERT (FLIP (Hash [k]) >= 0 && FLIP (Hash [k] < n)) ;
282  continue ;
283  }
284  head = Hhead [hash] ;
285  ASSERT (head < EMPTY || head == 1) ;
286  if (head == 1)
287  {
288  /* hash bucket is already empty */
289  continue ;
290  }
291  PRINT2 (("\n--------------------hash "ID":\n", hash)) ;
292  for (j = FLIP (head) ; j != EMPTY && Next[j] > EMPTY ; j = Next [j])
293  {
294  /* compare j with all nodes i following it in hash bucket */
295  ASSERT (j >= 0 && j < n && Hash [j] == hash) ;
296  p = Cp [j] ;
297  pend = Cp [j+1] ;
298  jlen = pend - p ;
299  jscattered = FALSE ;
300  DEBUG (for (i = 0 ; i < n ; i++) ASSERT (!Cmap_MARKED (i))) ;
301  DEBUG (pruned = FALSE) ;
302  ilast = j ;
303  for (i = Next [j] ; i != EMPTY ; i = Next [i])
304  {
305  ASSERT (i >= 0 && i < n && Hash [i] == hash && i != j) ;
306  pi = Cp [i] ;
307  piend = Cp [i+1] ;
308  ilen = piend - pi ;
309  DEBUG (work++) ;
310  if (ilen != jlen)
311  {
312  /* i and j have different degrees */
313  ilast = i ;
314  continue ;
315  }
316  /* scatter the pattern of node j, if not already */
317  if (!jscattered)
318  {
319  Cmap_MARK (j) ;
320  for ( ; p < pend ; p++)
321  {
322  Cmap_MARK (Ci [p]) ;
323  }
324  jscattered = TRUE ;
325  DEBUG (work += jlen) ;
326  }
327  for (ok = Cmap_MARKED (i) ; ok && pi < piend ; pi++)
328  {
329  ok = Cmap_MARKED (Ci [pi]) ;
330  DEBUG (work++) ;
331  }
332  if (ok)
333  {
334  /* found it. kill node i and merge it into j */
335  PRINT2 (("found "ID" absorbed into "ID"\n", i, j)) ;
336  Hash [i] = FLIP (j) ;
337  Cnw [j] += Cnw [i] ;
338  Cnw [i] = 0 ;
339  ASSERT (ilast != i && ilast >= 0 && ilast < n) ;
340  Next [ilast] = Next [i] ; /* delete i from bucket */
341  nodes_pruned++ ;
342  DEBUG (goodwork += (ilen+1)) ;
343  DEBUG (pruned = TRUE) ;
344  }
345  else
346  {
347  /* i and j are different */
348  ilast = i ;
349  }
350  }
351  DEBUG (if (pruned) goodwork += jlen) ;
352  }
353  /* empty the hash bucket, restoring Cew */
354  Hhead [hash] = 1 ;
355  }
356 
357  DEBUG (if (((work - goodwork) / (double) nz) > 0.20) PRINT0 ((
358  "work %12g good %12g nz %12g (wasted work/nz: %6.2f )\n",
359  work, goodwork, (double) nz, (work - goodwork) / ((double) nz)))) ;
360 
361  /* All hash buckets now empty. Cmap no longer needed as workspace. ]
362  * Cew no longer needed as Hhead; Cew is now restored to all ones. ]
363  * Part no longer needed as workspace for Next. ] */
364  }
365 
366  /* Edge weights are all one, node weights reflect node absorption */
367  DEBUG (for (p = 0 ; p < csize ; p++) ASSERT (Cew [p] == 1)) ;
368  DEBUG (for (cnt = 0, j = 0 ; j < n ; j++) cnt += Cnw [j]) ;
369  ASSERT (cnt == total_weight) ;
370 
371  /* ---------------------------------------------------------------------- */
372  /* compress and partition the graph */
373  /* ---------------------------------------------------------------------- */
374 
375  if (nodes_pruned == 0)
376  {
377 
378  /* ------------------------------------------------------------------ */
379  /* no pruning done at all. Do not create the compressed graph */
380  /* ------------------------------------------------------------------ */
381 
382  /* FUTURE WORK: could call CHACO, SCOTCH, ... here too */
383  csep = CHOLMOD(metis_bisector) (C, Cnw, Cew, Part, Common) ;
384 
385  }
386  else if (nodes_pruned == n-1)
387  {
388 
389  /* ------------------------------------------------------------------ */
390  /* only one node left. This is a dense graph */
391  /* ------------------------------------------------------------------ */
392 
393  PRINT2 (("completely dense graph\n")) ;
394  csep = total_weight ;
395  for (j = 0 ; j < n ; j++)
396  {
397  Part [j] = 2 ;
398  }
399 
400  }
401  else
402  {
403 
404  /* ------------------------------------------------------------------ */
405  /* compress the graph and partition the compressed graph */
406  /* ------------------------------------------------------------------ */
407 
408  /* ------------------------------------------------------------------ */
409  /* create the map from the uncompressed graph to the compressed graph */
410  /* ------------------------------------------------------------------ */
411 
412  /* Cmap [j] = k if node j is alive and the kth node of compressed graph.
413  * The mapping is done monotonically (that is, k <= j) to simplify the
414  * uncompression later on. Cmap [j] = EMPTY if node j is dead. */
415 
416  for (j = 0 ; j < n ; j++)
417  {
418  Cmap [j] = EMPTY ;
419  }
420  k = 0 ;
421  for (j = 0 ; j < n ; j++)
422  {
423  if (Cnw [j] > 0)
424  {
425  ASSERT (k <= j) ;
426  Cmap [j] = k++ ;
427  }
428  }
429  cn = k ; /* # of nodes in compressed graph */
430  PRINT2 (("compressed graph from "ID" to "ID" nodes\n", n, cn)) ;
431  ASSERT (cn > 1 && cn == n - nodes_pruned) ;
432 
433  /* ------------------------------------------------------------------ */
434  /* create the compressed graph */
435  /* ------------------------------------------------------------------ */
436 
437  k = 0 ;
438  pdest = 0 ;
439  for (j = 0 ; j < n ; j++)
440  {
441  if (Cnw [j] > 0)
442  {
443  /* node j in the full graph is node k in the compressed graph */
444  ASSERT (k <= j && Cmap [j] == k) ;
445  p = Cp [j] ;
446  pend = Cp [j+1] ;
447  Cp [k] = pdest ;
448  Cnw [k] = Cnw [j] ;
449  for ( ; p < pend ; p++)
450  {
451  /* prune dead nodes, and remap to new node numbering */
452  i = Ci [p] ;
453  ASSERT (i >= 0 && i < n && i != j) ;
454  i = Cmap [i] ;
455  ASSERT (i >= EMPTY && i < cn && i != k) ;
456  if (i > EMPTY)
457  {
458  ASSERT (pdest <= p) ;
459  Ci [pdest++] = i ;
460  }
461  }
462  k++ ;
463  }
464  }
465  Cp [cn] = pdest ;
466  C->nrow = cn ;
467  C->ncol = cn ; /* affects mem stats unless restored when C free'd */
468 
469 #ifndef NDEBUG
470  PRINT2 (("pruned graph ("ID"/"ID") nodes, ("ID"/"ID") edges\n",
471  cn, n, pdest, nz)) ;
472  PRINT2 (("compressed graph:\n")) ;
473  for (cnt = 0, j = 0 ; j < cn ; j++)
474  {
475  PRINT2 ((""ID": ", j)) ;
476  for (p = Cp [j] ; p < Cp [j+1] ; p++)
477  {
478  i = Ci [p] ;
479  PRINT3 ((""ID" ", i)) ;
480  ASSERT (i >= 0 && i < cn && i != j) ;
481  }
482  PRINT2 (("weight: "ID"\n", Cnw [j])) ;
483  ASSERT (Cnw [j] > 0) ;
484  cnt += Cnw [j] ;
485  }
486  ASSERT (cnt == total_weight) ;
487  for (j = 0 ; j < n ; j++) PRINT2 (("Cmap ["ID"] = "ID"\n", j, Cmap[j]));
488  ASSERT (k == cn) ;
489 #endif
490 
491  /* ------------------------------------------------------------------ */
492  /* find the separator of the compressed graph */
493  /* ------------------------------------------------------------------ */
494 
495  /* FUTURE WORK: could call CHACO, SCOTCH, ... here too */
496  csep = CHOLMOD(metis_bisector) (C, Cnw, Cew, Part, Common) ;
497 
498  if (csep < 0)
499  {
500  /* failed */
501  return (-1) ;
502  }
503 
504  PRINT2 (("Part: ")) ;
505  DEBUG (for (j = 0 ; j < cn ; j++) PRINT2 ((""ID" ", Part [j]))) ;
506  PRINT2 (("\n")) ;
507 
508  /* Cp and Ci no longer needed */
509 
510  /* ------------------------------------------------------------------ */
511  /* find the separator of the uncompressed graph */
512  /* ------------------------------------------------------------------ */
513 
514  /* expand the separator to live nodes in the uncompressed graph */
515  for (j = n-1 ; j >= 0 ; j--)
516  {
517  /* do this in reverse order so that Cnw can be expanded in place */
518  k = Cmap [j] ;
519  ASSERT (k >= EMPTY && k < n) ;
520  if (k > EMPTY)
521  {
522  /* node k in compressed graph and is node j in full graph */
523  ASSERT (k <= j) ;
524  ASSERT (Hash [j] >= EMPTY) ;
525  Part [j] = Part [k] ;
526  Cnw [j] = Cnw [k] ;
527  }
528  else
529  {
530  /* node j is a dead node */
531  Cnw [j] = 0 ;
532  DEBUG (Part [j] = EMPTY) ;
533  ASSERT (Hash [j] < EMPTY) ;
534  }
535  }
536 
537  /* find the components for the dead nodes */
538  for (i = 0 ; i < n ; i++)
539  {
540  if (Hash [i] < EMPTY)
541  {
542  /* node i has been absorbed into node j */
543  j = FLIP (Hash [i]) ;
544  ASSERT (Part [i] == EMPTY && j >= 0 && j < n && Cnw [i] == 0) ;
545  Part [i] = Part [j] ;
546  }
547  ASSERT (Part [i] >= 0 && Part [i] <= 2) ;
548  }
549 
550 #ifndef NDEBUG
551  PRINT2 (("Part: ")) ;
552  for (cnt = 0, j = 0 ; j < n ; j++)
553  {
554  ASSERT (Part [j] != EMPTY) ;
555  PRINT2 ((""ID" ", Part [j])) ;
556  if (Part [j] == 2) cnt += Cnw [j] ;
557  }
558  PRINT2 (("\n")) ;
559  PRINT2 (("csep "ID" "ID"\n", cnt, csep)) ;
560  ASSERT (cnt == csep) ;
561  for (cnt = 0, j = 0 ; j < n ; j++) cnt += Cnw [j] ;
562  ASSERT (cnt == total_weight) ;
563 #endif
564 
565  }
566 
567  /* ---------------------------------------------------------------------- */
568  /* return the separator (or -1 if error) */
569  /* ---------------------------------------------------------------------- */
570 
571  PRINT2 (("Partition done, n "ID" csep "ID"\n", n, csep)) ;
572  return (csep) ;
573 }
574 
575 
576 /* ========================================================================== */
577 /* === clear_flag =========================================================== */
578 /* ========================================================================== */
579 
580 /* A node j has been removed from the graph if Flag [j] < EMPTY.
581  * If Flag [j] >= EMPTY && Flag [j] < mark, then node j is alive but unmarked.
582  * Flag [j] == mark means that node j is alive and marked. Incrementing mark
583  * means that all nodes are either (still) dead, or live but unmarked.
584  *
585  * On output, Common->mark < Common->Flag [i] for all i from 0 to Common->nrow.
586  * This is the same output condition as cholmod_clear_flag, except that this
587  * routine maintains the Flag [i] < EMPTY condition as well, if that condition
588  * was true on input.
589  *
590  * workspace: Flag (nrow)
591  */
592 
594 {
595  Int nrow, i ;
596  Int *Flag ;
597  PRINT2 (("old mark %ld\n", Common->mark)) ;
598  Common->mark++ ;
599  PRINT2 (("new mark %ld\n", Common->mark)) ;
600  if (Common->mark <= 0)
601  {
602  nrow = Common->nrow ;
603  Flag = Common->Flag ;
604  for (i = 0 ; i < nrow ; i++)
605  {
606  /* if Flag [i] < EMPTY, leave it alone */
607  if (Flag [i] >= EMPTY)
608  {
609  Flag [i] = EMPTY ;
610  }
611  }
612  /* now Flag [i] <= EMPTY for all i */
613  Common->mark = 0 ;
614  }
615  return (Common->mark) ;
616 }
617 
618 
619 /* ========================================================================== */
620 /* === find_components ====================================================== */
621 /* ========================================================================== */
622 
623 /* Find all connected components of the current subgraph C. The subgraph C
624  * consists of the nodes of B that appear in the set Map [0..cn-1]. If Map
625  * is NULL, then it is assumed to be the identity mapping
626  * (Map [0..cn-1] = 0..cn-1).
627  *
628  * A node j does not appear in B if it has been ordered (Flag [j] < EMPTY,
629  * which means that j has been ordered and is "deleted" from B).
630  *
631  * If the size of a component is large, it is placed on the component stack,
632  * Cstack. Otherwise, its nodes are ordered and it is not placed on the Cstack.
633  *
634  * A component S is defined by a "representative node" (repnode for short)
635  * called the snode, which is one of the nodes in the subgraph. Likewise, the
636  * subgraph C is defined by its repnode, called cnode.
637  *
638  * If Part is not NULL on input, then Part [i] determines how the components
639  * are placed on the stack. Components containing nodes i with Part [i] == 0
640  * are placed first, followed by components with Part [i] == 1.
641  *
642  * The first node placed in each of the two parts is flipped when placed in
643  * the Cstack. This allows the components of the two parts to be found simply
644  * by traversing the Cstack.
645  *
646  * workspace: Flag (nrow)
647  */
648 
649 static void find_components
650 (
651  /* inputs, not modified on output */
652  cholmod_sparse *B,
653  Int Map [ ], /* size n, only Map [0..cn-1] used */
654  Int cn, /* # of nodes in C */
655  Int cnode, /* root node of component C, or EMPTY if C is the
656  * entire graph B */
657 
658  Int Part [ ], /* size cn, optional */
659 
660  /* input/output */
661  Int Bnz [ ], /* size n. Bnz [j] = # nonzeros in column j of B.
662  * Reduce since B is pruned of dead nodes. */
663 
664  Int CParent [ ], /* CParent [i] = j if component with repnode j is
665  * the parent of the component with repnode i.
666  * CParent [i] = EMPTY if the component with
667  * repnode i is a root of the separator tree.
668  * CParent [i] is -2 if i is not a repnode. */
669  Int Cstack [ ], /* component stack for nested dissection */
670  Int *top, /* Cstack [0..top] contains root nodes of the
671  * the components currently in the stack */
672 
673  /* workspace, undefined on input and output: */
674  Int Queue [ ], /* size n, for breadth-first search */
675 
676  cholmod_common *Common
677 )
678 {
679  Int n, mark, cj, j, sj, sn, p, i, snode, pstart, pdest, pend, nd_components,
680  part, first ;
681  Int *Bp, *Bi, *Flag ;
682 
683  /* ---------------------------------------------------------------------- */
684  /* get workspace */
685  /* ---------------------------------------------------------------------- */
686 
687  PRINT2 (("find components: cn %d\n", cn)) ;
688  Flag = Common->Flag ; /* size n */
689  Common->mark = EMPTY ; /* force initialization of Flag array */
690  mark = clear_flag (Common) ; /* clear Flag but preserve Flag [i]<EMPTY */
691  Bp = B->p ;
692  Bi = B->i ;
693  n = B->nrow ;
694  ASSERT (cnode >= EMPTY && cnode < n) ;
695  ASSERT (IMPLIES (cnode >= 0, Flag [cnode] < EMPTY)) ;
696 
697  /* get ordering parameters */
698  nd_components = Common->method [Common->current].nd_components ;
699 
700  /* ---------------------------------------------------------------------- */
701  /* find the connected components of C via a breadth-first search */
702  /* ---------------------------------------------------------------------- */
703 
704  part = (Part == NULL) ? 0 : 1 ;
705 
706  /* examine each part (part 1 and then part 0) */
707  for (part = (Part == NULL) ? 0 : 1 ; part >= 0 ; part--)
708  {
709 
710  /* first is TRUE for the first connected component in each part */
711  first = TRUE ;
712 
713  /* find all connected components in the current part */
714  for (cj = 0 ; cj < cn ; cj++)
715  {
716  /* get node snode, which is node cj of C. It might already be in
717  * the separator of C (and thus ordered, with Flag [snode] < EMPTY)
718  */
719  snode = (Map == NULL) ? (cj) : (Map [cj]) ;
720  ASSERT (snode >= 0 && snode < n) ;
721 
722  if (Flag [snode] >= EMPTY && Flag [snode] < mark
723  && ((Part == NULL) || Part [cj] == part))
724  {
725 
726  /* ---------------------------------------------------------- */
727  /* find new connected component S */
728  /* ---------------------------------------------------------- */
729 
730  /* node snode is the repnode of a connected component S, the
731  * parent of which is cnode, the repnode of C. If cnode is
732  * EMPTY then C is the original graph B. */
733  PRINT2 (("----------:::snode "ID" cnode "ID"\n", snode, cnode));
734 
735  ASSERT (CParent [snode] == -2) ;
736  if (first || nd_components)
737  {
738  /* If this is the first node in this part, then it becomes
739  * the repnode of all components in this part, and all
740  * components in this part form a single node in the
741  * separator tree. If nd_components is TRUE, then all
742  * connected components form their own node in the
743  * separator tree.
744  */
745  CParent [snode] = cnode ;
746  }
747 
748  /* place j in the queue and mark it */
749  sj = 0 ;
750  Queue [0] = snode ;
751  Flag [snode] = mark ;
752  sn = 1 ;
753 
754  /* breadth-first traversal, starting at node j */
755  for (sj = 0 ; sj < sn ; sj++)
756  {
757  /* get node j from head of Queue and traverse its edges */
758  j = Queue [sj] ;
759  PRINT2 ((" j: "ID"\n", j)) ;
760  ASSERT (j >= 0 && j < n) ;
761  ASSERT (Flag [j] == mark) ;
762  pstart = Bp [j] ;
763  pdest = pstart ;
764  pend = pstart + Bnz [j] ;
765  for (p = pstart ; p < pend ; p++)
766  {
767  i = Bi [p] ;
768  if (i != j && Flag [i] >= EMPTY)
769  {
770  /* node is still in the graph */
771  Bi [pdest++] = i ;
772  if (Flag [i] < mark)
773  {
774  /* node i is in this component S, and unflagged
775  * (first time node i has been seen in this BFS).
776  * place node i in the queue and mark it */
777  Queue [sn++] = i ;
778  Flag [i] = mark ;
779  }
780  }
781  }
782  /* edges to dead nodes have been removed */
783  Bnz [j] = pdest - pstart ;
784  }
785 
786  /* ---------------------------------------------------------- */
787  /* order S if it is small; place it on Cstack otherwise */
788  /* ---------------------------------------------------------- */
789 
790  PRINT2 (("sn "ID"\n", sn)) ;
791 
792  /* place the new component on the Cstack. Flip the node if
793  * is the first connected component of the current part,
794  * or if all components are treated as their own node in
795  * the separator tree. */
796  Cstack [++(*top)] =
797  (first || nd_components) ? FLIP (snode) : snode ;
798  first = FALSE ;
799  }
800  }
801  }
802 
803  /* clear Flag array, but preserve Flag [i] < EMPTY */
804  clear_flag (Common) ;
805 }
806 
807 
808 /* ========================================================================== */
809 /* === cholmod_bisect ======================================================= */
810 /* ========================================================================== */
811 
812 /* Finds a node bisector of A, A*A', A(:,f)*A(:,f)'.
813  *
814  * workspace: Flag (nrow),
815  * Iwork (nrow if symmetric, max (nrow,ncol) if unsymmetric).
816  * Allocates a temporary matrix B=A*A' or B=A,
817  * and O(nnz(A)) temporary memory space.
818  */
819 
820 UF_long CHOLMOD(bisect) /* returns # of nodes in separator */
821 (
822  /* ---- input ---- */
823  cholmod_sparse *A, /* matrix to bisect */
824  Int *fset, /* subset of 0:(A->ncol)-1 */
825  size_t fsize, /* size of fset */
826  int compress, /* if TRUE, compress the graph first */
827  /* ---- output --- */
828  Int *Partition, /* size A->nrow. Node i is in the left graph if
829  * Partition [i] = 0, the right graph if 1, and in the
830  * separator if 2. */
831  /* --------------- */
832  cholmod_common *Common
833 )
834 {
835  Int *Bp, *Bi, *Hash, *Cmap, *Bnw, *Bew, *Iwork ;
836  cholmod_sparse *B ;
837  unsigned Int hash ;
838  Int j, n, bnz, sepsize, p, pend ;
839  size_t csize, s ;
840  int ok = TRUE ;
841 
842  /* ---------------------------------------------------------------------- */
843  /* check inputs */
844  /* ---------------------------------------------------------------------- */
845 
847  RETURN_IF_NULL (A, EMPTY) ;
848  RETURN_IF_NULL (Partition, EMPTY) ;
850  Common->status = CHOLMOD_OK ;
851 
852  /* ---------------------------------------------------------------------- */
853  /* quick return */
854  /* ---------------------------------------------------------------------- */
855 
856  n = A->nrow ;
857  if (n == 0)
858  {
859  return (0) ;
860  }
861 
862  /* ---------------------------------------------------------------------- */
863  /* allocate workspace */
864  /* ---------------------------------------------------------------------- */
865 
866  /* s = n + MAX (n, A->ncol) */
867  s = CHOLMOD(add_size_t) (A->nrow, MAX (A->nrow, A->ncol), &ok) ;
868  if (!ok)
869  {
870  ERROR (CHOLMOD_TOO_LARGE, "problem too large") ;
871  return (EMPTY) ;
872  }
873 
874  CHOLMOD(allocate_work) (n, s, 0, Common) ;
875  if (Common->status < CHOLMOD_OK)
876  {
877  return (EMPTY) ;
878  }
879  ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ;
880 
881  Iwork = Common->Iwork ;
882  Hash = Iwork ; /* size n, (i/l/l) */
883  Cmap = Iwork + n ; /* size n, (i/i/l) */
884 
885  /* ---------------------------------------------------------------------- */
886  /* convert the matrix to adjacency list form */
887  /* ---------------------------------------------------------------------- */
888 
889  /* The input graph to must be symmetric, with no diagonal entries
890  * present. The columns need not be sorted. */
891 
892  /* B = A, A*A', or A(:,f)*A(:,f)', upper and lower parts present */
893 
894  if (A->stype)
895  {
896  /* Add the upper/lower part to a symmetric lower/upper matrix by
897  * converting to unsymmetric mode */
898  /* workspace: Iwork (nrow) */
899  B = CHOLMOD(copy) (A, 0, -1, Common) ;
900  }
901  else
902  {
903  /* B = A*A' or A(:,f)*A(:,f)', no diagonal */
904  /* workspace: Flag (nrow), Iwork (max (nrow,ncol)) */
905  B = CHOLMOD(aat) (A, fset, fsize, -1, Common) ;
906  }
907 
908  if (Common->status < CHOLMOD_OK)
909  {
910  return (EMPTY) ;
911  }
912  Bp = B->p ;
913  Bi = B->i ;
914  bnz = Bp [n] ;
915  ASSERT ((Int) (B->nrow) == n && (Int) (B->ncol) == n) ;
916 
917  /* B does not include the diagonal, and both upper and lower parts.
918  * Common->anz includes the diagonal, and just the lower part of B */
919  Common->anz = bnz / 2 + ((double) n) ;
920 
921  /* Bew should be at least size n for the hash function to work well */
922  /* this cannot cause overflow, because the matrix is already created */
923  csize = MAX (((size_t) n) + 1, (size_t) bnz) ;
924 
925  /* create the graph using Flag as workspace for node weights [ */
926  Bnw = Common->Flag ; /* size n workspace */
927 
928  /* compute hash for each node if compression requested */
929  if (compress)
930  {
931  for (j = 0 ; j < n ; j++)
932  {
933  hash = j ;
934  pend = Bp [j+1] ;
935  for (p = Bp [j] ; p < pend ; p++)
936  {
937  hash += Bi [p] ;
938  ASSERT (Bi [p] != j) ;
939  }
940  /* finalize the hash key for node j */
941  hash %= csize ;
942  Hash [j] = (Int) hash ;
943  ASSERT (Hash [j] >= 0 && Hash [j] < csize) ;
944  }
945  }
946 
947  /* allocate edge weights */
948  Bew = CHOLMOD(malloc) (csize, sizeof (Int), Common) ;
949  if (Common->status < CHOLMOD_OK)
950  {
951  /* out of memory */
952  CHOLMOD(free_sparse) (&B, Common) ;
953  CHOLMOD(free) (csize, sizeof (Int), Bew, Common) ;
954  return (EMPTY) ;
955  }
956 
957  /* graph has unit node and edge weights */
958  for (j = 0 ; j < n ; j++)
959  {
960  Bnw [j] = 1 ;
961  }
962  for (s = 0 ; s < csize ; s++)
963  {
964  Bew [s] = 1 ;
965  }
966 
967  /* ---------------------------------------------------------------------- */
968  /* compress and partition the graph */
969  /* ---------------------------------------------------------------------- */
970 
971  sepsize = partition (
972 #ifndef NDEBUG
973  csize,
974 #endif
975  compress, Hash, B, Bnw, Bew, Cmap, Partition, Common) ;
976 
977  /* contents of Bp, Bi, Bnw, and Bew no longer needed ] */
978 
979  /* If partition fails, free the workspace below and return sepsize < 0 */
980 
981  /* ---------------------------------------------------------------------- */
982  /* free workspace */
983  /* ---------------------------------------------------------------------- */
984 
985  B->ncol = n ; /* restore size for memory usage statistics */
986  CHOLMOD(free_sparse) (&B, Common) ;
987  Common->mark = EMPTY ;
988  CHOLMOD(clear_flag) (Common) ;
989  CHOLMOD(free) (csize, sizeof (Int), Bew, Common) ;
990  return (sepsize) ;
991 }
992 
993 
994 /* ========================================================================== */
995 /* === cholmod_nested_dissection ============================================ */
996 /* ========================================================================== */
997 
998 /* This method uses a node bisector, applied recursively (but using a
999  * non-recursive algorithm). Once the graph is partitioned, it calls a
1000  * constrained min degree code (CAMD or CSYMAMD for A+A', and CCOLAMD for A*A')
1001  * to order all the nodes in the graph - but obeying the constraints determined
1002  * by the separators. This routine is similar to METIS_NodeND, except for how
1003  * it treats the leaf nodes. METIS_NodeND orders the leaves of the separator
1004  * tree with MMD, ignoring the rest of the matrix when ordering a single leaf.
1005  * This routine orders the whole matrix with CSYMAMD or CCOLAMD, all at once,
1006  * when the graph partitioning is done.
1007  *
1008  * This function also returns a postorderd separator tree (CParent), and a
1009  * mapping of nodes in the graph to nodes in the separator tree (Cmember).
1010  *
1011  * workspace: Flag (nrow), Head (nrow+1), Iwork (4*nrow + (ncol if unsymmetric))
1012  * Allocates a temporary matrix B=A*A' or B=A,
1013  * and O(nnz(A)) temporary memory space.
1014  * Allocates an additional 3*n*sizeof(Int) temporary workspace
1015  */
1016 
1017 UF_long CHOLMOD(nested_dissection) /* returns # of components, or -1 if error */
1018 (
1019  /* ---- input ---- */
1020  cholmod_sparse *A, /* matrix to order */
1021  Int *fset, /* subset of 0:(A->ncol)-1 */
1022  size_t fsize, /* size of fset */
1023  /* ---- output --- */
1024  Int *Perm, /* size A->nrow, output permutation */
1025  Int *CParent, /* size A->nrow. On output, CParent [c] is the parent
1026  * of component c, or EMPTY if c is a root, and where
1027  * c is in the range 0 to # of components minus 1 */
1028  Int *Cmember, /* size A->nrow. Cmember [j] = c if node j of A is
1029  * in component c */
1030  /* --------------- */
1031  cholmod_common *Common
1032 )
1033 {
1034  double prune_dense, nd_oksep ;
1035  Int *Bp, *Bi, *Bnz, *Cstack, *Imap, *Map, *Flag, *Head, *Next, *Bnw, *Iwork,
1036  *Ipost, *NewParent, *Hash, *Cmap, *Cp, *Ci, *Cew, *Cnw, *Part, *Post,
1037  *Work3n ;
1038  unsigned Int hash ;
1039  Int n, bnz, top, i, j, k, cnode, cdense, p, cj, cn, ci, cnz, mark, c, uncol,
1040  sepsize, parent, ncomponents, threshold, ndense, pstart, pdest, pend,
1041  nd_compress, nd_camd, csize, jnext, nd_small, total_weight,
1042  nchild, child = EMPTY ;
1043  cholmod_sparse *B, *C ;
1044  size_t s ;
1045  int ok = TRUE ;
1046  DEBUG (Int cnt) ;
1047 
1048  /* ---------------------------------------------------------------------- */
1049  /* get inputs */
1050  /* ---------------------------------------------------------------------- */
1051 
1053  RETURN_IF_NULL (A, EMPTY) ;
1054  RETURN_IF_NULL (Perm, EMPTY) ;
1055  RETURN_IF_NULL (CParent, EMPTY) ;
1056  RETURN_IF_NULL (Cmember, EMPTY) ;
1058  Common->status = CHOLMOD_OK ;
1059 
1060  /* ---------------------------------------------------------------------- */
1061  /* quick return */
1062  /* ---------------------------------------------------------------------- */
1063 
1064  n = A->nrow ;
1065  if (n == 0)
1066  {
1067  return (1) ;
1068  }
1069 
1070  /* ---------------------------------------------------------------------- */
1071  /* get inputs */
1072  /* ---------------------------------------------------------------------- */
1073 
1074  ASSERT (CHOLMOD(dump_sparse) (A, "A for NESDIS:", Common) >= 0) ;
1075 
1076  /* get ordering parameters */
1077  prune_dense = Common->method [Common->current].prune_dense ;
1078  nd_compress = Common->method [Common->current].nd_compress ;
1079  nd_oksep = Common->method [Common->current].nd_oksep ;
1080  nd_oksep = MAX (0, nd_oksep) ;
1081  nd_oksep = MIN (1, nd_oksep) ;
1082  nd_camd = Common->method [Common->current].nd_camd ;
1083  nd_small = Common->method [Common->current].nd_small ;
1084  nd_small = MAX (4, nd_small) ;
1085 
1086  PRINT0 (("nd_components %d nd_small %d nd_oksep %g\n",
1087  Common->method [Common->current].nd_components,
1088  nd_small, nd_oksep)) ;
1089 
1090  /* ---------------------------------------------------------------------- */
1091  /* allocate workspace */
1092  /* ---------------------------------------------------------------------- */
1093 
1094  /* s = 4*n + uncol */
1095  uncol = (A->stype == 0) ? A->ncol : 0 ;
1096  s = CHOLMOD(mult_size_t) (n, 4, &ok) ;
1097  s = CHOLMOD(add_size_t) (s, uncol, &ok) ;
1098  if (!ok)
1099  {
1100  ERROR (CHOLMOD_TOO_LARGE, "problem too large") ;
1101  return (EMPTY) ;
1102  }
1103 
1104  CHOLMOD(allocate_work) (n, s, 0, Common) ;
1105  if (Common->status < CHOLMOD_OK)
1106  {
1107  return (EMPTY) ;
1108  }
1109  ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ;
1110 
1111  /* ---------------------------------------------------------------------- */
1112  /* get workspace */
1113  /* ---------------------------------------------------------------------- */
1114 
1115  Flag = Common->Flag ; /* size n */
1116  Head = Common->Head ; /* size n+1, all equal to -1 */
1117 
1118  Iwork = Common->Iwork ;
1119  Imap = Iwork ; /* size n, same as Queue in find_components */
1120  Map = Iwork + n ; /* size n */
1121  Bnz = Iwork + 2*((size_t) n) ; /* size n */
1122  Hash = Iwork + 3*((size_t) n) ; /* size n */
1123 
1124  Work3n = CHOLMOD(malloc) (n, 3*sizeof (Int), Common) ;
1125  Part = Work3n ; /* size n */
1126  Bnw = Part + n ; /* size n */
1127  Cnw = Bnw + n ; /* size n */
1128 
1129  Cstack = Perm ; /* size n, use Perm as workspace for Cstack [ */
1130  Cmap = Cmember ; /* size n, use Cmember as workspace [ */
1131 
1132  if (Common->status < CHOLMOD_OK)
1133  {
1134  return (EMPTY) ;
1135  }
1136 
1137  /* ---------------------------------------------------------------------- */
1138  /* convert B to symmetric form with both upper/lower parts present */
1139  /* ---------------------------------------------------------------------- */
1140 
1141  /* B = A+A', A*A', or A(:,f)*A(:,f)', upper and lower parts present */
1142 
1143  if (A->stype)
1144  {
1145  /* Add the upper/lower part to a symmetric lower/upper matrix by
1146  * converting to unsymmetric mode */
1147  /* workspace: Iwork (nrow) */
1148  B = CHOLMOD(copy) (A, 0, -1, Common) ;
1149  }
1150  else
1151  {
1152  /* B = A*A' or A(:,f)*A(:,f)', no diagonal */
1153  /* workspace: Flag (nrow), Iwork (max (nrow,ncol)) */
1154  B = CHOLMOD(aat) (A, fset, fsize, -1, Common) ;
1155  }
1156 
1157  if (Common->status < CHOLMOD_OK)
1158  {
1159  CHOLMOD(free) (3*n, sizeof (Int), Work3n, Common) ;
1160  return (EMPTY) ;
1161  }
1162  Bp = B->p ;
1163  Bi = B->i ;
1164  bnz = CHOLMOD(nnz) (B, Common) ;
1165  ASSERT ((Int) (B->nrow) == n && (Int) (B->ncol) == n) ;
1166  csize = MAX (n, bnz) ;
1167  ASSERT (CHOLMOD(dump_sparse) (B, "B for nd:", Common) >= 0) ;
1168 
1169  /* ---------------------------------------------------------------------- */
1170  /* initializations */
1171  /* ---------------------------------------------------------------------- */
1172 
1173  /* all nodes start out unmarked and unordered (Type 4, see below) */
1174  Common->mark = EMPTY ;
1175  CHOLMOD(clear_flag) (Common) ;
1176  ASSERT (Flag == Common->Flag) ;
1177  ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ;
1178 
1179  for (j = 0 ; j < n ; j++)
1180  {
1181  CParent [j] = -2 ;
1182  }
1183 
1184  /* prune dense nodes from B */
1185  if (IS_NAN (prune_dense) || prune_dense < 0)
1186  {
1187  /* only remove completely dense nodes */
1188  threshold = n-2 ;
1189  }
1190  else
1191  {
1192  /* remove nodes with degree more than threshold */
1193  threshold = (Int) (MAX (16, prune_dense * sqrt ((double) (n)))) ;
1194  threshold = MIN (n, threshold) ;
1195  }
1196  ndense = 0 ;
1197  cnode = EMPTY ;
1198  cdense = EMPTY ;
1199 
1200  for (j = 0 ; j < n ; j++)
1201  {
1202  Bnz [j] = Bp [j+1] - Bp [j] ;
1203  if (Bnz [j] > threshold)
1204  {
1205  /* node j is dense, prune it from B */
1206  PRINT2 (("j is dense %d\n", j)) ;
1207  ndense++ ;
1208  if (cnode == EMPTY)
1209  {
1210  /* first dense node found becomes root of this component,
1211  * which contains all of the dense nodes found here */
1212  cdense = j ;
1213  cnode = j ;
1214  CParent [cnode] = EMPTY ;
1215  }
1216  Flag [j] = FLIP (cnode) ;
1217  }
1218  }
1219  B->packed = FALSE ;
1220  ASSERT (B->nz == NULL) ;
1221 
1222  if (ndense == n)
1223  {
1224  /* all nodes removed: Perm is identity, all nodes in component zero,
1225  * and the separator tree has just one node. */
1226  PRINT2 (("all nodes are dense\n")) ;
1227  for (k = 0 ; k < n ; k++)
1228  {
1229  Perm [k] = k ;
1230  Cmember [k] = 0 ;
1231  }
1232  CParent [0] = EMPTY ;
1233  CHOLMOD(free_sparse) (&B, Common) ;
1234  CHOLMOD(free) (3*n, sizeof (Int), Work3n, Common) ;
1235  Common->mark = EMPTY ;
1236  CHOLMOD(clear_flag) (Common) ;
1237  return (1) ;
1238  }
1239 
1240  /* Cp and Ci are workspace to construct the subgraphs to partition */
1241  C = CHOLMOD(allocate_sparse) (n, n, csize, FALSE, TRUE, 0, CHOLMOD_PATTERN,
1242  Common) ;
1243  Cew = CHOLMOD(malloc) (csize, sizeof (Int), Common) ;
1244 
1245  if (Common->status < CHOLMOD_OK)
1246  {
1247  /* out of memory */
1248  CHOLMOD(free_sparse) (&C, Common) ;
1249  CHOLMOD(free_sparse) (&B, Common) ;
1250  CHOLMOD(free) (csize, sizeof (Int), Cew, Common) ;
1251  CHOLMOD(free) (3*n, sizeof (Int), Work3n, Common) ;
1252  Common->mark = EMPTY ;
1253  CHOLMOD(clear_flag) (Common) ;
1254  PRINT2 (("out of memory for C, etc\n")) ;
1255  return (EMPTY) ;
1256  }
1257 
1258  Cp = C->p ;
1259  Ci = C->i ;
1260 
1261  /* create initial unit node and edge weights */
1262  for (j = 0 ; j < n ; j++)
1263  {
1264  Bnw [j] = 1 ;
1265  }
1266  for (p = 0 ; p < csize ; p++)
1267  {
1268  Cew [p] = 1 ;
1269  }
1270 
1271  /* push the initial connnected components of B onto the Cstack */
1272  top = EMPTY ; /* Cstack is empty */
1273  /* workspace: Flag (nrow), Iwork (nrow); use Imap as workspace for Queue [*/
1274  find_components (B, NULL, n, cnode, NULL,
1275  Bnz, CParent, Cstack, &top, Imap, Common) ;
1276  /* done using Imap as workspace for Queue ] */
1277 
1278  /* Nodes can now be of Type 0, 1, 2, or 4 (see definition below) */
1279 
1280  /* ---------------------------------------------------------------------- */
1281  /* while Cstack is not empty, do: */
1282  /* ---------------------------------------------------------------------- */
1283 
1284  while (top >= 0)
1285  {
1286 
1287  /* clear the Flag array, but do not modify negative entries in Flag */
1288  mark = clear_flag (Common) ;
1289 
1290  DEBUG (for (i = 0 ; i < n ; i++) Imap [i] = EMPTY) ;
1291 
1292  /* ------------------------------------------------------------------ */
1293  /* get node(s) from the top of the Cstack */
1294  /* ------------------------------------------------------------------ */
1295 
1296  /* i is the repnode of its (unordered) connected component. Get
1297  * all repnodes for all connected components of a single part. If
1298  * each connected component is to be ordered separately (nd_components
1299  * is TRUE), then this while loop iterates just once. */
1300 
1301  cnode = EMPTY ;
1302  cn = 0 ;
1303  while (cnode == EMPTY)
1304  {
1305  i = Cstack [top--] ;
1306 
1307  if (i < 0)
1308  {
1309  /* this is the last node in this component */
1310  i = FLIP (i) ;
1311  cnode = i ;
1312  }
1313 
1314  ASSERT (i >= 0 && i < n && Flag [i] >= EMPTY) ;
1315 
1316  /* place i in the queue and mark it */
1317  Map [cn] = i ;
1318  Flag [i] = mark ;
1319  Imap [i] = cn ;
1320  cn++ ;
1321  }
1322 
1323  ASSERT (cnode != EMPTY) ;
1324 
1325  /* During ordering, there are five kinds of nodes in the graph of B,
1326  * based on Flag [j] and CParent [j] for nodes j = 0 to n-1:
1327  *
1328  * Type 0: If cnode is a repnode of an unordered component, then
1329  * CParent [cnode] is in the range EMPTY to n-1 and
1330  * Flag [cnode] >= EMPTY. This is a "live" node.
1331  *
1332  * Type 1: If cnode is a repnode of an ordered separator component,
1333  * then Flag [cnode] < EMPTY and FLAG [cnode] = FLIP (cnode).
1334  * CParent [cnode] is in the range EMPTY to n-1. cnode is a root of
1335  * the separator tree if CParent [cnode] == EMPTY. This node is dead.
1336  *
1337  * Type 2: If node j isn't a repnode, has not been absorbed via
1338  * graph compression into another node, but is in an ordered separator
1339  * component, then cnode = FLIP (Flag [j]) gives the repnode of the
1340  * component that contains j and CParent [j] is -2. This node is dead.
1341  * Note that Flag [j] < EMPTY.
1342  *
1343  * Type 3: If node i has been absorbed via graph compression into some
1344  * other node j = FLIP (Flag [i]) where j is not a repnode.
1345  * CParent [j] is -2. Node i may or may not be in an ordered
1346  * component. This node is dead. Note that Flag [j] < EMPTY.
1347  *
1348  * Type 4: If node j is "live" (not in an ordered component, and not
1349  * absorbed into any other node), then Flag [j] >= EMPTY.
1350  *
1351  * Only "live" nodes (of type 0 or 4) are placed in a subgraph to be
1352  * partitioned. Node j is alive if Flag [j] >= EMPTY, and dead if
1353  * Flag [j] < EMPTY.
1354  */
1355 
1356  /* ------------------------------------------------------------------ */
1357  /* create the subgraph for this connected component C */
1358  /* ------------------------------------------------------------------ */
1359 
1360  /* Do a breadth-first search of the graph starting at cnode.
1361  * use Map [0..cn-1] for nodes in the component C [
1362  * use Cnw and Cew for node and edge weights of the resulting subgraph [
1363  * use Cp and Ci for the resulting subgraph [
1364  * use Imap [i] for all nodes i in B that are in the component C [
1365  */
1366 
1367  cnz = 0 ;
1368  total_weight = 0 ;
1369  for (cj = 0 ; cj < cn ; cj++)
1370  {
1371  /* get node j from the head of the queue; it is node cj of C */
1372  j = Map [cj] ;
1373  ASSERT (Flag [j] == mark) ;
1374  Cp [cj] = cnz ;
1375  Cnw [cj] = Bnw [j] ;
1376  ASSERT (Cnw [cj] >= 0) ;
1377  total_weight += Cnw [cj] ;
1378  pstart = Bp [j] ;
1379  pdest = pstart ;
1380  pend = pstart + Bnz [j] ;
1381  hash = cj ;
1382  for (p = pstart ; p < pend ; p++)
1383  {
1384  i = Bi [p] ;
1385  /* prune diagonal entries and dead edges from B */
1386  if (i != j && Flag [i] >= EMPTY)
1387  {
1388  /* live node i is in the current component */
1389  Bi [pdest++] = i ;
1390  if (Flag [i] != mark)
1391  {
1392  /* First time node i has been seen, it is a new node
1393  * of C. place node i in the queue and mark it */
1394  Map [cn] = i ;
1395  Flag [i] = mark ;
1396  Imap [i] = cn ;
1397  cn++ ;
1398  }
1399  /* place the edge (cj,ci) in the adjacency list of cj */
1400  ci = Imap [i] ;
1401  ASSERT (ci >= 0 && ci < cn && ci != cj && cnz < csize) ;
1402  Ci [cnz++] = ci ;
1403  hash += ci ;
1404  }
1405  }
1406  /* edges to dead nodes have been removed */
1407  Bnz [j] = pdest - pstart ;
1408  /* finalize the hash key for column j */
1409  hash %= csize ;
1410  Hash [cj] = (Int) hash ;
1411  ASSERT (Hash [cj] >= 0 && Hash [cj] < csize) ;
1412  }
1413  Cp [cn] = cnz ;
1414  C->nrow = cn ;
1415  C->ncol = cn ; /* affects mem stats unless restored when C free'd */
1416 
1417  /* contents of Imap no longer needed ] */
1418 
1419 #ifndef NDEBUG
1420  for (cj = 0 ; cj < cn ; cj++)
1421  {
1422  j = Map [cj] ;
1423  PRINT2 (("----------------------------C column cj: "ID" j: "ID"\n",
1424  cj, j)) ;
1425  ASSERT (j >= 0 && j < n) ;
1426  ASSERT (Flag [j] >= EMPTY) ;
1427  for (p = Cp [cj] ; p < Cp [cj+1] ; p++)
1428  {
1429  ci = Ci [p] ;
1430  i = Map [ci] ;
1431  PRINT3 (("ci: "ID" i: "ID"\n", ci, i)) ;
1432  ASSERT (ci != cj && ci >= 0 && ci < cn) ;
1433  ASSERT (i != j && i >= 0 && i < n) ;
1434  ASSERT (Flag [i] >= EMPTY) ;
1435  }
1436  }
1437 #endif
1438 
1439  PRINT0 (("consider cn %d nd_small %d ", cn, nd_small)) ;
1440  if (cn < nd_small) /* could be 'total_weight < nd_small' instead */
1441  {
1442  /* place all nodes in the separator */
1443  PRINT0 ((" too small\n")) ;
1444  sepsize = total_weight ;
1445  }
1446  else
1447  {
1448 
1449  /* Cp and Ci now contain the component, with cn nodes and cnz
1450  * nonzeros. The mapping of a node cj into node j the main graph
1451  * B is given by Map [cj] = j */
1452  PRINT0 ((" cut\n")) ;
1453 
1454  /* -------------------------------------------------------------- */
1455  /* compress and partition the graph C */
1456  /* -------------------------------------------------------------- */
1457 
1458  /* The edge weights Cew [0..csize-1] are all 1's on input to and
1459  * output from the partition routine. */
1460 
1461  sepsize = partition (
1462 #ifndef NDEBUG
1463  csize,
1464 #endif
1465  nd_compress, Hash, C, Cnw, Cew,
1466  Cmap, Part, Common) ;
1467 
1468  /* contents of Cp and Ci no longer needed ] */
1469 
1470  if (sepsize < 0)
1471  {
1472  /* failed */
1473  C->ncol = n ; /* restore size for memory usage statistics */
1474  CHOLMOD(free_sparse) (&C, Common) ;
1475  CHOLMOD(free_sparse) (&B, Common) ;
1476  CHOLMOD(free) (csize, sizeof (Int), Cew, Common) ;
1477  CHOLMOD(free) (3*n, sizeof (Int), Work3n, Common) ;
1478  Common->mark = EMPTY ;
1479  CHOLMOD(clear_flag) (Common) ;
1480  return (EMPTY) ;
1481  }
1482 
1483  /* -------------------------------------------------------------- */
1484  /* compress B based on how C was compressed */
1485  /* -------------------------------------------------------------- */
1486 
1487  for (ci = 0 ; ci < cn ; ci++)
1488  {
1489  if (Hash [ci] < EMPTY)
1490  {
1491  /* ci is dead in C, having been absorbed into cj */
1492  cj = FLIP (Hash [ci]) ;
1493  PRINT2 (("In C, "ID" absorbed into "ID" (wgt now "ID")\n",
1494  ci, cj, Cnw [cj])) ;
1495  /* i is dead in B, having been absorbed into j */
1496  i = Map [ci] ;
1497  j = Map [cj] ;
1498  PRINT2 (("In B, "ID" (wgt "ID") => "ID" (wgt "ID")\n",
1499  i, Bnw [i], j, Bnw [j], Cnw [cj])) ;
1500  /* more than one node may be absorbed into j. This is
1501  * accounted for in Cnw [cj]. Assign it here rather
1502  * than += Bnw [i] */
1503  Bnw [i] = 0 ;
1504  Bnw [j] = Cnw [cj] ;
1505  Flag [i] = FLIP (j) ;
1506  }
1507  }
1508 
1509  DEBUG (for (cnt = 0, j = 0 ; j < n ; j++) cnt += Bnw [j]) ;
1510  ASSERT (cnt == n) ;
1511  }
1512 
1513  /* contents of Cnw [0..cn-1] no longer needed ] */
1514 
1515  /* ------------------------------------------------------------------ */
1516  /* order the separator, and stack the components when C is split */
1517  /* ------------------------------------------------------------------ */
1518 
1519  /* one more component has been found: either the separator of C,
1520  * or all of C */
1521 
1522  ASSERT (sepsize >= 0 && sepsize <= total_weight) ;
1523 
1524  PRINT0 (("sepsize %d tot %d : %8.4f ", sepsize, total_weight,
1525  ((double) sepsize) / ((double) total_weight))) ;
1526 
1527  if (sepsize == total_weight || sepsize == 0 ||
1528  sepsize > nd_oksep * total_weight)
1529  {
1530  /* Order the nodes in the component. The separator is too large,
1531  * or empty. Note that the partition routine cannot return a
1532  * sepsize of zero, but it can return a separator consisting of the
1533  * whole graph. The "sepsize == 0" test is kept, above, in case the
1534  * partition routine changes. In either case, this component
1535  * remains unsplit, and becomes a leaf of the separator tree. */
1536  PRINT2 (("cnode %d sepsize zero or all of graph: "ID"\n",
1537  cnode, sepsize)) ;
1538  for (cj = 0 ; cj < cn ; cj++)
1539  {
1540  j = Map [cj] ;
1541  Flag [j] = FLIP (cnode) ;
1542  PRINT2 ((" node cj: "ID" j: "ID" ordered\n", cj, j)) ;
1543  }
1544  ASSERT (Flag [cnode] == FLIP (cnode)) ;
1545  ASSERT (cnode != EMPTY && Flag [cnode] < EMPTY) ;
1546  PRINT0 (("discarded\n")) ;
1547 
1548  }
1549  else
1550  {
1551 
1552  /* Order the nodes in the separator of C and find a new repnode
1553  * cnode that is in the separator of C. This requires the separator
1554  * to be non-empty. */
1555  PRINT0 (("sepsize not tiny: "ID"\n", sepsize)) ;
1556  parent = CParent [cnode] ;
1557  ASSERT (parent >= EMPTY && parent < n) ;
1558  CParent [cnode] = -2 ;
1559  cnode = EMPTY ;
1560  for (cj = 0 ; cj < cn ; cj++)
1561  {
1562  j = Map [cj] ;
1563  if (Part [cj] == 2)
1564  {
1565  /* All nodes in the separator become part of a component
1566  * whose repnode is cnode */
1567  PRINT2 (("node cj: "ID" j: "ID" ordered\n", cj, j)) ;
1568  if (cnode == EMPTY)
1569  {
1570  PRINT2(("------------new cnode: cj "ID" j "ID"\n",
1571  cj, j)) ;
1572  cnode = j ;
1573  }
1574  Flag [j] = FLIP (cnode) ;
1575  }
1576  else
1577  {
1578  PRINT2 ((" node cj: "ID" j: "ID" not ordered\n",
1579  cj, j)) ;
1580  }
1581  }
1582  ASSERT (cnode != EMPTY && Flag [cnode] < EMPTY) ;
1583  ASSERT (CParent [cnode] == -2) ;
1584  CParent [cnode] = parent ;
1585 
1586  /* find the connected components when C is split, and push
1587  * them on the Cstack. Use Imap as workspace for Queue. [ */
1588  /* workspace: Flag (nrow) */
1589  find_components (B, Map, cn, cnode, Part, Bnz,
1590  CParent, Cstack, &top, Imap, Common) ;
1591  /* done using Imap as workspace for Queue ] */
1592  }
1593  /* contents of Map [0..cn-1] no longer needed ] */
1594  }
1595 
1596  /* done using Cmember as workspace for Cmap ] */
1597  /* done using Perm as workspace for Cstack ] */
1598 
1599  /* ---------------------------------------------------------------------- */
1600  /* place nodes removed via compression into their proper component */
1601  /* ---------------------------------------------------------------------- */
1602 
1603  /* At this point, all nodes are of Type 1, 2, or 3, as defined above. */
1604 
1605  for (i = 0 ; i < n ; i++)
1606  {
1607  /* find the repnode cnode that contains node i */
1608  j = FLIP (Flag [i]) ;
1609  PRINT2 (("\nfind component for "ID", in: "ID"\n", i, j)) ;
1610  ASSERT (j >= 0 && j < n) ;
1611  DEBUG (cnt = 0) ;
1612  while (CParent [j] == -2)
1613  {
1614  j = FLIP (Flag [j]) ;
1615  PRINT2 ((" walk up to "ID" ", j)) ;
1616  ASSERT (j >= 0 && j < n) ;
1617  PRINT2 ((" CParent "ID"\n", CParent [j])) ;
1618  ASSERT (cnt < n) ;
1619  DEBUG (cnt++) ;
1620  }
1621  cnode = j ;
1622  ASSERT (cnode >= 0 && cnode < n) ;
1623  ASSERT (CParent [cnode] >= EMPTY && CParent [cnode] < n) ;
1624  PRINT2 (("i "ID" is in component with cnode "ID"\n", i, cnode)) ;
1625  ASSERT (Flag [cnode] == FLIP (cnode)) ;
1626 
1627  /* Mark all nodes along the path from i to cnode as being in the
1628  * component whos repnode is cnode. Perform path compression. */
1629  j = FLIP (Flag [i]) ;
1630  Flag [i] = FLIP (cnode) ;
1631  DEBUG (cnt = 0) ;
1632  while (CParent [j] == -2)
1633  {
1634  ASSERT (j >= 0 && j < n) ;
1635  jnext = FLIP (Flag [j]) ;
1636  PRINT2 ((" "ID" walk "ID" set cnode to "ID"\n", i, j, cnode)) ;
1637  ASSERT (cnt < n) ;
1638  DEBUG (cnt++) ;
1639  Flag [j] = FLIP (cnode) ;
1640  j = jnext ;
1641  }
1642  }
1643 
1644  /* At this point, all nodes fall into Types 1 or 2, as defined above. */
1645 
1646 #ifndef NDEBUG
1647  for (j = 0 ; j < n ; j++)
1648  {
1649  PRINT2 (("j %d CParent %d ", j, CParent [j])) ;
1650  if (CParent [j] >= EMPTY && CParent [j] < n)
1651  {
1652  /* case 1: j is a repnode of a component */
1653  cnode = j ;
1654  PRINT2 ((" a repnode\n")) ;
1655  }
1656  else
1657  {
1658  /* case 2: j is not a repnode of a component */
1659  cnode = FLIP (Flag [j]) ;
1660  PRINT2 ((" repnode is %d\n", cnode)) ;
1661  ASSERT (cnode >= 0 && cnode < n) ;
1662  ASSERT (CParent [cnode] >= EMPTY && CParent [cnode] < n) ;
1663  }
1664  ASSERT (Flag [cnode] == FLIP (cnode)) ;
1665  /* case 3 no longer holds */
1666  }
1667 #endif
1668 
1669  /* ---------------------------------------------------------------------- */
1670  /* free workspace */
1671  /* ---------------------------------------------------------------------- */
1672 
1673  C->ncol = n ; /* restore size for memory usage statistics */
1674  CHOLMOD(free_sparse) (&C, Common) ;
1675  CHOLMOD(free_sparse) (&B, Common) ;
1676  CHOLMOD(free) (csize, sizeof (Int), Cew, Common) ;
1677  CHOLMOD(free) (3*n, sizeof (Int), Work3n, Common) ;
1678 
1679  /* ---------------------------------------------------------------------- */
1680  /* handle dense nodes */
1681  /* ---------------------------------------------------------------------- */
1682 
1683  /* The separator tree has nodes with either no children or two or more
1684  * children - with one exception. There may exist a single root node with
1685  * exactly one child, which holds the dense rows/columns of the matrix.
1686  * Delete this node if it exists. */
1687 
1688  if (ndense > 0)
1689  {
1690  ASSERT (CParent [cdense] == EMPTY) ; /* cdense has no parent */
1691  /* find the children of cdense */
1692  nchild = 0 ;
1693  for (j = 0 ; j < n ; j++)
1694  {
1695  if (CParent [j] == cdense)
1696  {
1697  nchild++ ;
1698  child = j ;
1699  }
1700  }
1701  if (nchild == 1)
1702  {
1703  /* the cdense node has just one child; merge the two nodes */
1704  PRINT1 (("root has one child\n")) ;
1705  CParent [cdense] = -2 ; /* cdense is deleted */
1706  CParent [child] = EMPTY ; /* child becomes a root */
1707  for (j = 0 ; j < n ; j++)
1708  {
1709  if (Flag [j] == FLIP (cdense))
1710  {
1711  /* j is a dense node */
1712  PRINT1 (("dense %d\n", j)) ;
1713  Flag [j] = FLIP (child) ;
1714  }
1715  }
1716  }
1717  }
1718 
1719  /* ---------------------------------------------------------------------- */
1720  /* postorder the components */
1721  /* ---------------------------------------------------------------------- */
1722 
1723  DEBUG (for (cnt = 0, j = 0 ; j < n ; j++) if (CParent [j] != -2) cnt++) ;
1724 
1725  /* use Cmember as workspace for Post [ */
1726  Post = Cmember ;
1727 
1728  /* cholmod_postorder uses Head and Iwork [0..2n]. It does not use Flag,
1729  * which here holds the mapping of nodes to repnodes. It ignores all nodes
1730  * for which CParent [j] < -1, so it operates just on the repnodes. */
1731  /* workspace: Head (n), Iwork (2*n) */
1732  ncomponents = CHOLMOD(postorder) (CParent, n, NULL, Post, Common) ;
1733  ASSERT (cnt == ncomponents) ;
1734 
1735  /* use Iwork [0..n-1] as workspace for Ipost ( */
1736  Ipost = Iwork ;
1737  DEBUG (for (j = 0 ; j < n ; j++) Ipost [j] = EMPTY) ;
1738 
1739  /* compute inverse postorder */
1740  for (c = 0 ; c < ncomponents ; c++)
1741  {
1742  cnode = Post [c] ;
1743  ASSERT (cnode >= 0 && cnode < n) ;
1744  Ipost [cnode] = c ;
1745  ASSERT (Head [c] == EMPTY) ;
1746  }
1747 
1748  /* adjust the parent array */
1749  /* Iwork [n..2n-1] used for NewParent [ */
1750  NewParent = Iwork + n ;
1751  for (c = 0 ; c < ncomponents ; c++)
1752  {
1753  parent = CParent [Post [c]] ;
1754  NewParent [c] = (parent == EMPTY) ? EMPTY : (Ipost [parent]) ;
1755  }
1756  for (c = 0 ; c < ncomponents ; c++)
1757  {
1758  CParent [c] = NewParent [c] ;
1759  }
1760  ASSERT (CHOLMOD(dump_parent) (CParent, ncomponents, "CParent", Common)) ;
1761 
1762  /* Iwork [n..2n-1] no longer needed for NewParent ] */
1763  /* Cmember no longer needed for Post ] */
1764 
1765 #ifndef NDEBUG
1766  /* count the number of children of each node */
1767  for (c = 0 ; c < ncomponents ; c++)
1768  {
1769  Cmember [c] = 0 ;
1770  }
1771  for (c = 0 ; c < ncomponents ; c++)
1772  {
1773  if (CParent [c] != EMPTY) Cmember [CParent [c]]++ ;
1774  }
1775  for (c = 0 ; c < ncomponents ; c++)
1776  {
1777  /* a node is either a leaf, or has 2 or more children */
1778  ASSERT (Cmember [c] == 0 || Cmember [c] >= 2) ;
1779  }
1780 #endif
1781 
1782  /* ---------------------------------------------------------------------- */
1783  /* place each node in its component */
1784  /* ---------------------------------------------------------------------- */
1785 
1786  for (j = 0 ; j < n ; j++)
1787  {
1788  /* node j is in the cth component, whose repnode is cnode */
1789  cnode = FLIP (Flag [j]) ;
1790  PRINT2 (("j "ID" flag "ID" cnode "ID"\n",
1791  j, Flag [j], FLIP (Flag [j]))) ;
1792  ASSERT (cnode >= 0 && cnode < n) ;
1793  c = Ipost [cnode] ;
1794  ASSERT (c >= 0 && c < ncomponents) ;
1795  Cmember [j] = c ;
1796  }
1797 
1798  /* Flag no longer needed for the node-to-component mapping */
1799 
1800  /* done using Iwork [0..n-1] as workspace for Ipost ) */
1801 
1802  /* ---------------------------------------------------------------------- */
1803  /* clear the Flag array */
1804  /* ---------------------------------------------------------------------- */
1805 
1806  Common->mark = EMPTY ;
1807  CHOLMOD(clear_flag) (Common) ;
1808  ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ;
1809 
1810  /* ---------------------------------------------------------------------- */
1811  /* find the permutation */
1812  /* ---------------------------------------------------------------------- */
1813 
1814  PRINT1 (("nd_camd: %d A->stype %d\n", nd_camd, A->stype)) ;
1815 
1816  if (nd_camd)
1817  {
1818 
1819  /* ------------------------------------------------------------------ */
1820  /* apply camd, csymamd, or ccolamd using the Cmember constraints */
1821  /* ------------------------------------------------------------------ */
1822 
1823  if (A->stype != 0)
1824  {
1825  /* ordering A+A', so fset and fsize are ignored.
1826  * Add the upper/lower part to a symmetric lower/upper matrix by
1827  * converting to unsymmetric mode
1828  * workspace: Iwork (nrow) */
1829  B = CHOLMOD(copy) (A, 0, -1, Common) ;
1830  if (Common->status < CHOLMOD_OK)
1831  {
1832  PRINT0 (("make symmetric failed\n")) ;
1833  return (EMPTY) ;
1834  }
1835  ASSERT ((Int) (B->nrow) == n && (Int) (B->ncol) == n) ;
1836  PRINT2 (("nested dissection (2)\n")) ;
1837  B->stype = -1 ;
1838  if (nd_camd == 2)
1839  {
1840  /* workspace: Head (nrow+1), Iwork (nrow) if symmetric-upper */
1841  ok = CHOLMOD(csymamd) (B, Cmember, Perm, Common) ;
1842  }
1843  else
1844  {
1845  /* workspace: Head (nrow), Iwork (4*nrow) */
1846  ok = CHOLMOD(camd) (B, NULL, 0, Cmember, Perm, Common) ;
1847  }
1848  CHOLMOD(free_sparse) (&B, Common) ;
1849  if (!ok)
1850  {
1851  /* failed */
1852  PRINT0 (("camd/csymamd failed\n")) ;
1853  return (EMPTY) ;
1854  }
1855  }
1856  else
1857  {
1858  /* ordering A*A' or A(:,f)*A(:,f)' */
1859  /* workspace: Iwork (nrow if no fset; MAX(nrow,ncol) if fset) */
1860  if (!CHOLMOD(ccolamd) (A, fset, fsize, Cmember, Perm, Common))
1861  {
1862  /* ccolamd failed */
1863  PRINT2 (("ccolamd failed\n")) ;
1864  return (EMPTY) ;
1865  }
1866  }
1867 
1868  }
1869  else
1870  {
1871 
1872  /* ------------------------------------------------------------------ */
1873  /* natural ordering of each component */
1874  /* ------------------------------------------------------------------ */
1875 
1876  /* use Iwork [0..n-1] for Next [ */
1877  Next = Iwork ;
1878 
1879  /* ------------------------------------------------------------------ */
1880  /* place the nodes in link lists, one list per component */
1881  /* ------------------------------------------------------------------ */
1882 
1883  /* do so in reverse order, to preserve original ordering */
1884  for (j = n-1 ; j >= 0 ; j--)
1885  {
1886  /* node j is in the cth component */
1887  c = Cmember [j] ;
1888  ASSERT (c >= 0 && c < ncomponents) ;
1889  /* place node j in link list for component c */
1890  Next [j] = Head [c] ;
1891  Head [c] = j ;
1892  }
1893 
1894  /* ------------------------------------------------------------------ */
1895  /* order each node in each component */
1896  /* ------------------------------------------------------------------ */
1897 
1898  k = 0 ;
1899  for (c = 0 ; c < ncomponents ; c++)
1900  {
1901  for (j = Head [c] ; j != EMPTY ; j = Next [j])
1902  {
1903  Perm [k++] = j ;
1904  }
1905  Head [c] = EMPTY ;
1906  }
1907  ASSERT (k == n) ;
1908 
1909  /* done using Iwork [0..n-1] for Next ] */
1910  }
1911 
1912  /* ---------------------------------------------------------------------- */
1913  /* clear workspace and return number of components */
1914  /* ---------------------------------------------------------------------- */
1915 
1916  ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ;
1917  return (ncomponents) ;
1918 }
1919 
1920 /* ========================================================================== */
1921 /* === cholmod_collapse_septree ============================================= */
1922 /* ========================================================================== */
1923 
1924 /* cholmod_nested_dissection returns the separator tree that was used in the
1925  * constrained minimum degree algorithm. Parameter settings (nd_small,
1926  * nd_oksep, etc) that give a good fill-reducing ordering may give too fine of
1927  * a separator tree for other uses (parallelism, multi-level LPDASA, etc). This
1928  * function takes as input the separator tree computed by
1929  * cholmod_nested_dissection, and collapses selected subtrees into single
1930  * nodes. A subtree is collapsed if its root node (the separator) is large
1931  * compared to the total number of nodes in the subtree, or if the subtree is
1932  * small. Note that the separator tree may actually be a forest.
1933  *
1934  * nd_oksep and nd_small act just like the ordering parameters in Common.
1935  * Returns the new number of nodes in the separator tree.
1936  */
1937 
1940  /* ---- input ---- */
1941  size_t n, /* # of nodes in the graph */
1942  size_t ncomponents, /* # of nodes in the separator tree (must be <= n) */
1943  double nd_oksep, /* collapse if #sep >= nd_oksep * #nodes in subtree */
1944  size_t nd_small, /* collapse if #nodes in subtree < nd_small */
1945  /* ---- in/out --- */
1946  Int *CParent, /* size ncomponents; from cholmod_nested_dissection */
1947  Int *Cmember, /* size n; from cholmod_nested_dissection */
1948  /* --------------- */
1949  cholmod_common *Common
1950 )
1951 {
1952  Int *First, *Count, *Csubtree, *W, *Map ;
1953  Int c, j, k, nc, sepsize, total_weight, parent, nc_new, first ;
1954  int collapse = FALSE, ok = TRUE ;
1955  size_t s ;
1956 
1957  /* ---------------------------------------------------------------------- */
1958  /* get inputs */
1959  /* ---------------------------------------------------------------------- */
1960 
1962  RETURN_IF_NULL (CParent, EMPTY) ;
1963  RETURN_IF_NULL (Cmember, EMPTY) ;
1964  if (n < ncomponents)
1965  {
1966  ERROR (CHOLMOD_INVALID, "invalid separator tree") ;
1967  return (EMPTY) ;
1968  }
1969  Common->status = CHOLMOD_OK ;
1970  nc = ncomponents ;
1971  if (n <= 1 || ncomponents <= 1)
1972  {
1973  /* no change; tree is one node already */
1974  return (nc) ;
1975  }
1976 
1977  nd_oksep = MAX (0, nd_oksep) ;
1978  nd_oksep = MIN (1, nd_oksep) ;
1979  nd_small = MAX (4, nd_small) ;
1980 
1981  /* ---------------------------------------------------------------------- */
1982  /* allocate workspace */
1983  /* ---------------------------------------------------------------------- */
1984 
1985  /* s = 3*ncomponents */
1986  s = CHOLMOD(mult_size_t) (ncomponents, 3, &ok) ;
1987  if (!ok)
1988  {
1989  ERROR (CHOLMOD_TOO_LARGE, "problem too large") ;
1990  return (EMPTY) ;
1991  }
1992  CHOLMOD(allocate_work) (0, s, 0, Common) ;
1993  if (Common->status < CHOLMOD_OK)
1994  {
1995  return (EMPTY) ;
1996  }
1997  W = Common->Iwork ;
1998  Count = W ; W += ncomponents ; /* size ncomponents */
1999  Csubtree = W ; W += ncomponents ; /* size ncomponents */
2000  First = W ; W += ncomponents ; /* size ncomponents */
2001 
2002  /* ---------------------------------------------------------------------- */
2003  /* find the first descendant of each node of the separator tree */
2004  /* ---------------------------------------------------------------------- */
2005 
2006  for (c = 0 ; c < nc ; c++)
2007  {
2008  First [c] = EMPTY ;
2009  }
2010  for (k = 0 ; k < nc ; k++)
2011  {
2012  for (c = k ; c != EMPTY && First [c] == -1 ; c = CParent [c])
2013  {
2014  ASSERT (c >= 0 && c < nc) ;
2015  First [c] = k ;
2016  }
2017  }
2018 
2019  /* ---------------------------------------------------------------------- */
2020  /* find the number of nodes of the graph in each node of the tree */
2021  /* ---------------------------------------------------------------------- */
2022 
2023  for (c = 0 ; c < nc ; c++)
2024  {
2025  Count [c] = 0 ;
2026  }
2027  for (j = 0 ; j < (Int) n ; j++)
2028  {
2029  ASSERT (Cmember [j] >= 0 && Cmember [j] < nc) ;
2030  Count [Cmember [j]]++ ;
2031  }
2032 
2033  /* ---------------------------------------------------------------------- */
2034  /* find the number of nodes in each subtree */
2035  /* ---------------------------------------------------------------------- */
2036 
2037  for (c = 0 ; c < nc ; c++)
2038  {
2039  /* each subtree includes its root */
2040  Csubtree [c] = Count [c] ;
2041  PRINT1 ((ID" size "ID" parent "ID" first "ID"\n",
2042  c, Count [c], CParent [c], First [c])) ;
2043  }
2044 
2045  for (c = 0 ; c < nc ; c++)
2046  {
2047  /* add the subtree of the child, c, into the count of its parent */
2048  parent = CParent [c] ;
2049  ASSERT (parent >= EMPTY && parent < nc) ;
2050  if (parent != EMPTY)
2051  {
2052  Csubtree [parent] += Csubtree [c] ;
2053  }
2054  }
2055 
2056 #ifndef NDEBUG
2057  /* the sum of the roots should be n */
2058  j = 0 ;
2059  for (c = 0 ; c < nc ; c++) if (CParent [c] == EMPTY) j += Csubtree [c] ;
2060  ASSERT (j == (Int) n) ;
2061 #endif
2062 
2063  /* ---------------------------------------------------------------------- */
2064  /* find subtrees to collapse */
2065  /* ---------------------------------------------------------------------- */
2066 
2067  /* consider all nodes in reverse post-order */
2068  for (c = nc-1 ; c >= 0 ; c--)
2069  {
2070  /* consider the subtree rooted at node c */
2071  sepsize = Count [c] ;
2072  total_weight = Csubtree [c] ;
2073  PRINT1 (("Node "ID" sepsize "ID" subtree "ID" ratio %g\n", c, sepsize,
2074  total_weight, ((double) sepsize)/((double) total_weight))) ;
2075  first = First [c] ;
2076  if (first < c && /* c must not be a leaf */
2077  (sepsize > nd_oksep * total_weight || total_weight < (int) nd_small))
2078  {
2079  /* this separator is too large, or the subtree is too small.
2080  * collapse the tree, by converting the entire subtree rooted at
2081  * c into a single node. The subtree consists of all nodes from
2082  * First[c] to the root c. Flag all nodes from First[c] to c-1
2083  * as dead.
2084  */
2085  collapse = TRUE ;
2086  for (k = first ; k < c ; k++)
2087  {
2088  CParent [k] = -2 ;
2089  PRINT1 ((" collapse node "ID"\n", k)) ;
2090  }
2091  /* continue at the next node, first-1 */
2092  c = first ;
2093  }
2094  }
2095 
2096  PRINT1 (("collapse: %d\n", collapse)) ;
2097 
2098  /* ---------------------------------------------------------------------- */
2099  /* compress the tree */
2100  /* ---------------------------------------------------------------------- */
2101 
2102  Map = Count ; /* Count no longer needed */
2103 
2104  nc_new = nc ;
2105  if (collapse)
2106  {
2107  nc_new = 0 ;
2108  for (c = 0 ; c < nc ; c++)
2109  {
2110  Map [c] = nc_new ;
2111  if (CParent [c] >= EMPTY)
2112  {
2113  /* node c is alive, and becomes node Map[c] in the new tree.
2114  * Increment nc_new for the next node c. */
2115  nc_new++ ;
2116  }
2117  }
2118  PRINT1 (("Collapse the tree from "ID" to "ID" nodes\n", nc, nc_new)) ;
2119  ASSERT (nc_new > 0) ;
2120  for (c = 0 ; c < nc ; c++)
2121  {
2122  parent = CParent [c] ;
2123  if (parent >= EMPTY)
2124  {
2125  /* node c is alive */
2126  CParent [Map [c]] = (parent == EMPTY) ? EMPTY : Map [parent] ;
2127  }
2128  }
2129  for (j = 0 ; j < (Int) n ; j++)
2130  {
2131  PRINT1 (("j "ID" Cmember[j] "ID" Map[Cmember[j]] "ID"\n",
2132  j, Cmember [j], Map [Cmember [j]])) ;
2133  Cmember [j] = Map [Cmember [j]] ;
2134  }
2135  }
2136 
2137  /* ---------------------------------------------------------------------- */
2138  /* return new size of separator tree */
2139  /* ---------------------------------------------------------------------- */
2140 
2141  return (nc_new) ;
2142 }
2143 #endif
#define Cmap_MARKED(i)
#define CHOLMOD_TOO_LARGE
int CHOLMOD() camd(cholmod_sparse *A, Int *fset, size_t fsize, Int *Cmember, Int *Perm, cholmod_common *Common)
#define EMPTY
UF_long CHOLMOD() nnz(cholmod_sparse *A, cholmod_common *Common)
#define Int
size_t CHOLMOD() add_size_t(size_t a, size_t b, int *ok)
#define FALSE
#define PRINT1(params)
static void find_components(cholmod_sparse *B, Int Map[], Int cn, Int cnode, Int Part[], Int Bnz[], Int CParent[], Int Cstack[], Int *top, Int Queue[], cholmod_common *Common)
#define PRINT3(params)
#define RETURN_IF_NULL_COMMON(result)
struct cholmod_common_struct::cholmod_method_struct method[CHOLMOD_MAXMETHODS+1]
size_t CHOLMOD() mult_size_t(size_t a, size_t k, int *ok)
cholmod_sparse *CHOLMOD() aat(cholmod_sparse *A, Int *fset, size_t fsize, int mode, cholmod_common *Common)
int CHOLMOD() dump_work(int flag, int head, UF_long wsize, cholmod_common *Common)
#define CHOLMOD_PATTERN
#define MAX(a, b)
int CHOLMOD() free_sparse(cholmod_sparse **AHandle, cholmod_common *Common)
UF_long CHOLMOD(bisect)
#define NULL
#define ASSERT(expression)
#define PRINT2(params)
int CHOLMOD() ccolamd(cholmod_sparse *A, Int *fset, size_t fsize, Int *Cmember, Int *Perm, cholmod_common *Common)
UF_long CHOLMOD() collapse_septree(size_t n, size_t ncomponents, double nd_oksep, size_t nd_small, Int *CParent, Int *Cmember, cholmod_common *Common)
#define FLIP(i)
#define NDEBUG
#define ID
#define CHOLMOD_INVALID
#define CHOLMOD_OK
#define PRINT0(params)
#define Cmap_MARK(i)
cholmod_sparse *CHOLMOD() allocate_sparse(size_t nrow, size_t ncol, size_t nzmax, int sorted, int packed, int stype, int xtype, cholmod_common *Common)
int CHOLMOD() allocate_work(size_t nrow, size_t iworksize, size_t xworksize, cholmod_common *Common)
static UF_long partition(Int csize, int compress, Int Hash[], cholmod_sparse *C, Int Cnw[], Int Cew[], Int Cmap[], Int Part[], cholmod_common *Common)
static UF_long clear_flag(cholmod_common *Common)
#define RETURN_IF_NULL(A, result)
#define IS_NAN(x)
#define DEBUG(statement)
int CHOLMOD() dump_parent(Int *Parent, size_t n, char *name, cholmod_common *Common)
#define MIN(a, b)
#define UF_long
#define IMPLIES(p, q)
int n
#define ERROR(status, msg)
#define TRUE
#define RETURN_IF_XTYPE_INVALID(A, xtype1, xtype2, result)
#define CHOLMOD_ZOMPLEX
int CHOLMOD() csymamd(cholmod_sparse *A, Int *Cmember, Int *Perm, cholmod_common *Common)
cholmod_sparse *CHOLMOD() copy(cholmod_sparse *A, int stype, int mode, cholmod_common *Common)