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