Amesos Package Browser (Single Doxygen Collection)  Development
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
amesos_cholmod_metis.c
Go to the documentation of this file.
1 /* ========================================================================== */
2 /* === Partition/cholmod_metis ============================================== */
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 interface to the METIS package (Version 4.0.1):
15  *
16  * cholmod_metis_bisector:
17  *
18  * Wrapper for METIS_NodeComputeSeparator. Finds a set of nodes that
19  * partitions the graph into two parts.
20  *
21  * cholmod_metis:
22  *
23  * Wrapper for METIS_NodeND, METIS's own nested dissection algorithm.
24  * Typically faster than cholmod_nested_dissection, mostly because it
25  * uses minimum degree on just the leaves of the separator tree, rather
26  * than the whole matrix.
27  *
28  * Note that METIS does not return an error if it runs out of memory. Instead,
29  * it terminates the program. This interface attempts to avoid that problem
30  * by preallocating space that should be large enough for any memory allocations
31  * within METIS, and then freeing that space, just before the call to METIS.
32  * While this is not guaranteed to work, it is very unlikely to fail. If you
33  * encounter this problem, increase Common->metis_memory. If you don't mind
34  * having your program terminated, set Common->metis_memory to zero (a value of
35  * 2.0 is usually safe). Several other METIS workarounds are made in the
36  * routines in this file. See the description of metis_memory_ok, just below,
37  * for more details.
38  *
39  * FUTURE WORK: interfaces to other partitioners (CHACO, SCOTCH, JOSTLE, ... )
40  *
41  * workspace: several size-nz and size-n temporary arrays. Uses no workspace
42  * in Common.
43  *
44  * Supports any xtype (pattern, real, complex, or zomplex).
45  */
46 
47 #ifndef NPARTITION
48 
50 #undef ASSERT
51 
52 #include "metis.h"
53 /* METIS has its own ASSERT that it reveals to the user, so remove it here: */
54 #undef ASSERT
55 
56 /* and redefine it back again */
57 #ifndef NDEBUG
58 #define ASSERT(expression) (assert (expression))
59 #else
60 #define ASSERT(expression)
61 #endif
62 
65 
66 
67 /* ========================================================================== */
68 /* === dumpgraph ============================================================ */
69 /* ========================================================================== */
70 
71 /* For dumping the input graph to METIS_NodeND, to check with METIS's onmetis
72  * and graphchk programs. For debugging only. To use, uncomment this #define:
73 #define DUMP_GRAPH
74  */
75 
76 #ifdef DUMP_GRAPH
77 #include <stdio.h>
78 /* After dumping the graph with this routine, run "onmetis metisgraph" */
79 static void dumpgraph (idxtype *Mp, idxtype *Mi, UF_long n,
80  cholmod_common *Common)
81 {
82  UF_long i, j, p, nz ;
83  FILE *f ;
84  nz = Mp [n] ;
85  printf ("Dumping METIS graph n %ld nz %ld\n", n, nz) ; /* DUMP_GRAPH */
86  f = fopen ("metisgraph", "w") ;
87  if (f == NULL)
88  {
89  ERROR (-99, "cannot open metisgraph") ;
90  return ;
91  }
92  fprintf (f, "%ld %ld\n", n, nz/2) ; /* DUMP_GRAPH */
93  for (j = 0 ; j < n ; j++)
94  {
95  for (p = Mp [j] ; p < Mp [j+1] ; p++)
96  {
97  i = Mi [p] ;
98  fprintf (f, " %ld", i+1) ; /* DUMP_GRAPH */
99  }
100  fprintf (f, "\n") ; /* DUMP_GRAPH */
101  }
102  fclose (f) ;
103 }
104 #endif
105 
106 
107 /* ========================================================================== */
108 /* === metis_memory_ok ====================================================== */
109 /* ========================================================================== */
110 
111 /* METIS_NodeND and METIS_NodeComputeSeparator will terminate your program it
112  * they run out of memory. In an attempt to workaround METIS' behavior, this
113  * routine allocates a single block of memory of size equal to an observed
114  * upper bound on METIS' memory usage. It then immediately deallocates the
115  * block. If the allocation fails, METIS is not called.
116  *
117  * Median memory usage for a graph with n nodes and nz edges (counting each
118  * edge twice, or both upper and lower triangular parts of a matrix) is
119  * 4*nz + 40*n + 4096 integers. A "typical" upper bound is 10*nz + 50*n + 4096
120  * integers. Nearly all matrices tested fit within that upper bound, with the
121  * exception two in the UF sparse matrix collection: Schenk_IBMNA/c-64 and
122  * Gupta/gupta2. The latter exceeds the "upper bound" by a factor of just less
123  * than 2.
124  *
125  * If you do not mind having your program terminated if it runs out of memory,
126  * set Common->metis_memory to zero. Its default value is 2, which allows for
127  * some memory fragmentation, and also accounts for the Gupta/gupta2 matrix.
128  *
129  * An alternative, if CHOLMOD is used in MATLAB, is to use a version of METIS
130  * (4.0.2, perhaps) proposed to George Karypis. This version uses function
131  * pointer for malloc and free. They can be set to mxMalloc and mxFree
132  * (see sputil_config in MATLAB/sputil.c). On Linux, with gcc, you must also
133  * compile CHOLMOD, METIS, AMD, COLAMD, and CCOLAMD with the -fexceptions
134  * compiler flag. With this configuration, mxMalloc safely aborts the
135  * mexFunction, frees all memory allocted by METIS, and safely returns to
136  * MATLAB. You may then set Common->metis_memory = 0.
137  */
138 
139 #define GUESS(nz,n) (10 * (nz) + 50 * (n) + 4096)
140 
141 static int metis_memory_ok
142 (
143  Int n,
144  Int nz,
145  cholmod_common *Common
146 )
147 {
148  double s ;
149  void *p ;
150  size_t metis_guard ;
151 
152  if (Common->metis_memory <= 0)
153  {
154  /* do not prevent METIS from running out of memory */
155  return (TRUE) ;
156  }
157 
158  n = MAX (1, n) ;
159  nz = MAX (0, nz) ;
160 
161  /* compute in double, to avoid integer overflow */
162  s = GUESS ((double) nz, (double) n) ;
163  s *= Common->metis_memory ;
164 
165  if (s * sizeof (idxtype) >= ((double) Size_max))
166  {
167  /* don't even attempt to malloc such a large block */
168  return (FALSE) ;
169  }
170 
171  /* recompute in size_t */
172  metis_guard = GUESS ((size_t) nz, (size_t) n) ;
173  metis_guard *= Common->metis_memory ;
174 
175  /* attempt to malloc the block */
176  p = CHOLMOD(malloc) (metis_guard, sizeof (idxtype), Common) ;
177  if (p == NULL)
178  {
179  /* failure - return out-of-memory condition */
180  return (FALSE) ;
181  }
182 
183  /* success - free the block */
184  CHOLMOD(free) (metis_guard, sizeof (idxtype), p, Common) ;
185  return (TRUE) ;
186 }
187 
188 
189 /* ========================================================================== */
190 /* === cholmod_metis_bisector =============================================== */
191 /* ========================================================================== */
192 
193 /* Finds a set of nodes that bisects the graph of A or AA' (direct interface
194  * to METIS_NodeComputeSeparator).
195  *
196  * The input matrix A must be square, symmetric (with both upper and lower
197  * parts present) and with no diagonal entries. These conditions are NOT
198  * checked.
199  */
200 
201 UF_long CHOLMOD(metis_bisector) /* returns separator size */
202 (
203  /* ---- input ---- */
204  cholmod_sparse *A, /* matrix to bisect */
205  Int *Anw, /* size A->nrow, node weights */
206  Int *Aew, /* size nz, edge weights */
207  /* ---- output --- */
208  Int *Partition, /* size A->nrow */
209  /* --------------- */
210  cholmod_common *Common
211 )
212 {
213  Int *Ap, *Ai ;
214  idxtype *Mp, *Mi, *Mnw, *Mew, *Mpart ;
215  Int n, nleft, nright, j, p, csep, total_weight, lightest, nz ;
216  int Opt [8], nn, csp ;
217  size_t n1 ;
218  DEBUG (Int nsep) ;
219 
220  /* ---------------------------------------------------------------------- */
221  /* check inputs */
222  /* ---------------------------------------------------------------------- */
223 
225  RETURN_IF_NULL (A, EMPTY) ;
226  RETURN_IF_NULL (Anw, EMPTY) ;
227  RETURN_IF_NULL (Aew, EMPTY) ;
228  RETURN_IF_NULL (Partition, EMPTY) ;
230  if (A->stype || A->nrow != A->ncol)
231  {
232  /* A must be square, with both upper and lower parts present */
233  ERROR (CHOLMOD_INVALID, "matrix must be square, symmetric,"
234  " and with both upper/lower parts present") ;
235  return (EMPTY) ;
236  }
237  Common->status = CHOLMOD_OK ;
238 
239  /* ---------------------------------------------------------------------- */
240  /* quick return */
241  /* ---------------------------------------------------------------------- */
242 
243  n = A->nrow ;
244  if (n == 0)
245  {
246  return (0) ;
247  }
248  n1 = ((size_t) n) + 1 ;
249 
250  /* ---------------------------------------------------------------------- */
251  /* get inputs */
252  /* ---------------------------------------------------------------------- */
253 
254  Ap = A->p ;
255  Ai = A->i ;
256  nz = Ap [n] ;
257 
258  /* ---------------------------------------------------------------------- */
259  /* METIS does not have a 64-bit integer version */
260  /* ---------------------------------------------------------------------- */
261 
262 #ifdef LONG
263  if (sizeof (Int) > sizeof (idxtype) && MAX (n,nz) > INT_MAX / sizeof (int))
264  {
265  /* CHOLMOD's matrix is too large for METIS */
266  return (EMPTY) ;
267  }
268 #endif
269 
270  /* ---------------------------------------------------------------------- */
271  /* set default options */
272  /* ---------------------------------------------------------------------- */
273 
274  Opt [0] = 0 ; /* use defaults */
275  Opt [1] = 3 ; /* matching type */
276  Opt [2] = 1 ; /* init. partitioning algo*/
277  Opt [3] = 2 ; /* refinement algorithm */
278  Opt [4] = 0 ; /* no debug */
279  Opt [5] = 0 ; /* unused */
280  Opt [6] = 0 ; /* unused */
281  Opt [7] = -1 ; /* random seed */
282 
283  DEBUG (for (j = 0 ; j < n ; j++) ASSERT (Anw [j] > 0)) ;
284 
285  /* ---------------------------------------------------------------------- */
286  /* copy Int to METIS idxtype, if necessary */
287  /* ---------------------------------------------------------------------- */
288 
289  DEBUG (for (j = 0 ; j < nz ; j++) ASSERT (Aew [j] > 0)) ;
290  if (sizeof (Int) == sizeof (idxtype))
291  {
292  /* this is the typical case */
293  Mi = (idxtype *) Ai ;
294  Mew = (idxtype *) Aew ;
295  Mp = (idxtype *) Ap ;
296  Mnw = (idxtype *) Anw ;
297  Mpart = (idxtype *) Partition ;
298  }
299  else
300  {
301  /* idxtype and Int differ; copy the graph into the METIS idxtype */
302  Mi = CHOLMOD(malloc) (nz, sizeof (idxtype), Common) ;
303  Mew = CHOLMOD(malloc) (nz, sizeof (idxtype), Common) ;
304  Mp = CHOLMOD(malloc) (n1, sizeof (idxtype), Common) ;
305  Mnw = CHOLMOD(malloc) (n, sizeof (idxtype), Common) ;
306  Mpart = CHOLMOD(malloc) (n, sizeof (idxtype), Common) ;
307  if (Common->status < CHOLMOD_OK)
308  {
309  CHOLMOD(free) (nz, sizeof (idxtype), Mi, Common) ;
310  CHOLMOD(free) (nz, sizeof (idxtype), Mew, Common) ;
311  CHOLMOD(free) (n1, sizeof (idxtype), Mp, Common) ;
312  CHOLMOD(free) (n, sizeof (idxtype), Mnw, Common) ;
313  CHOLMOD(free) (n, sizeof (idxtype), Mpart, Common) ;
314  return (EMPTY) ;
315  }
316  for (p = 0 ; p < nz ; p++)
317  {
318  Mi [p] = Ai [p] ;
319  }
320  for (p = 0 ; p < nz ; p++)
321  {
322  Mew [p] = Aew [p] ;
323  }
324  for (j = 0 ; j <= n ; j++)
325  {
326  Mp [j] = Ap [j] ;
327  }
328  for (j = 0 ; j < n ; j++)
329  {
330  Mnw [j] = Anw [j] ;
331  }
332  }
333 
334  /* ---------------------------------------------------------------------- */
335  /* METIS workaround: try to ensure METIS doesn't run out of memory */
336  /* ---------------------------------------------------------------------- */
337 
338  if (!metis_memory_ok (n, nz, Common))
339  {
340  /* METIS might ask for too much memory and thus terminate the program */
341  if (sizeof (Int) != sizeof (idxtype))
342  {
343  CHOLMOD(free) (nz, sizeof (idxtype), Mi, Common) ;
344  CHOLMOD(free) (nz, sizeof (idxtype), Mew, Common) ;
345  CHOLMOD(free) (n1, sizeof (idxtype), Mp, Common) ;
346  CHOLMOD(free) (n, sizeof (idxtype), Mnw, Common) ;
347  CHOLMOD(free) (n, sizeof (idxtype), Mpart, Common) ;
348  }
349  return (EMPTY) ;
350  }
351 
352  /* ---------------------------------------------------------------------- */
353  /* partition the graph */
354  /* ---------------------------------------------------------------------- */
355 
356 #ifndef NDEBUG
357  PRINT1 (("Metis graph, n = "ID"\n", n)) ;
358  for (j = 0 ; j < n ; j++)
359  {
360  Int ppp ;
361  PRINT2 (("M(:,"ID") node weight "ID"\n", j, (Int) Mnw [j])) ;
362  ASSERT (Mnw [j] > 0) ;
363  for (ppp = Mp [j] ; ppp < Mp [j+1] ; ppp++)
364  {
365  PRINT3 ((" "ID" : "ID"\n", (Int) Mi [ppp], (Int) Mew [ppp])) ;
366  ASSERT (Mi [ppp] != j) ;
367  ASSERT (Mew [ppp] > 0) ;
368  }
369  }
370 #endif
371 
372  nn = n ;
373  METIS_NodeComputeSeparator (&nn, Mp, Mi, Mnw, Mew, Opt, &csp, Mpart) ;
374  n = nn ;
375  csep = csp ;
376 
377  PRINT1 (("METIS csep "ID"\n", csep)) ;
378 
379  /* ---------------------------------------------------------------------- */
380  /* copy the results back from idxtype, if required */
381  /* ---------------------------------------------------------------------- */
382 
383  if (sizeof (Int) != sizeof (idxtype))
384  {
385  for (j = 0 ; j < n ; j++)
386  {
387  Partition [j] = Mpart [j] ;
388  }
389  CHOLMOD(free) (nz, sizeof (idxtype), Mi, Common) ;
390  CHOLMOD(free) (nz, sizeof (idxtype), Mew, Common) ;
391  CHOLMOD(free) (n1, sizeof (idxtype), Mp, Common) ;
392  CHOLMOD(free) (n, sizeof (idxtype), Mnw, Common) ;
393  CHOLMOD(free) (n, sizeof (idxtype), Mpart, Common) ;
394  }
395 
396  /* ---------------------------------------------------------------------- */
397  /* ensure a reasonable separator */
398  /* ---------------------------------------------------------------------- */
399 
400  /* METIS can return a valid separator with no nodes in (for example) the
401  * left part. In this case, there really is no separator. CHOLMOD
402  * prefers, in this case, for all nodes to be in the separator (and both
403  * left and right parts to be empty). Also, if the graph is unconnected,
404  * METIS can return a valid empty separator. CHOLMOD prefers at least one
405  * node in the separator. Note that cholmod_nested_dissection only calls
406  * this routine on connected components, but cholmod_bisect can call this
407  * routine for any graph. */
408 
409  if (csep == 0)
410  {
411  /* The separator is empty, select lightest node as separator. If
412  * ties, select the highest numbered node. */
413  lightest = 0 ;
414  for (j = 0 ; j < n ; j++)
415  {
416  if (Anw [j] <= Anw [lightest])
417  {
418  lightest = j ;
419  }
420  }
421  PRINT1 (("Force "ID" as sep\n", lightest)) ;
422  Partition [lightest] = 2 ;
423  csep = Anw [lightest] ;
424  }
425 
426  /* determine the node weights in the left and right part of the graph */
427  nleft = 0 ;
428  nright = 0 ;
429  DEBUG (nsep = 0) ;
430  for (j = 0 ; j < n ; j++)
431  {
432  PRINT1 (("Partition ["ID"] = "ID"\n", j, Partition [j])) ;
433  if (Partition [j] == 0)
434  {
435  nleft += Anw [j] ;
436  }
437  else if (Partition [j] == 1)
438  {
439  nright += Anw [j] ;
440  }
441 #ifndef NDEBUG
442  else
443  {
444  ASSERT (Partition [j] == 2) ;
445  nsep += Anw [j] ;
446  }
447 #endif
448  }
449  ASSERT (csep == nsep) ;
450 
451  total_weight = nleft + nright + csep ;
452 
453  if (csep < total_weight)
454  {
455  /* The separator is less than the whole graph. Make sure the left and
456  * right parts are either both empty or both non-empty. */
457  PRINT1 (("nleft "ID" nright "ID" csep "ID" tot "ID"\n",
458  nleft, nright, csep, total_weight)) ;
459  ASSERT (nleft + nright + csep == total_weight) ;
460  ASSERT (nleft > 0 || nright > 0) ;
461  if ((nleft == 0 && nright > 0) || (nleft > 0 && nright == 0))
462  {
463  /* left or right is empty; put all nodes in the separator */
464  PRINT1 (("Force all in sep\n")) ;
465  csep = total_weight ;
466  for (j = 0 ; j < n ; j++)
467  {
468  Partition [j] = 2 ;
469  }
470  }
471  }
472 
473  ASSERT (CHOLMOD(dump_partition) (n, Ap, Ai, Anw, Partition, csep, Common)) ;
474 
475  /* ---------------------------------------------------------------------- */
476  /* return the sum of the weights of nodes in the separator */
477  /* ---------------------------------------------------------------------- */
478 
479  return (csep) ;
480 }
481 
482 
483 /* ========================================================================== */
484 /* === cholmod_metis ======================================================== */
485 /* ========================================================================== */
486 
487 /* CHOLMOD wrapper for the METIS_NodeND ordering routine. Creates A+A',
488  * A*A' or A(:,f)*A(:,f)' and then calls METIS_NodeND on the resulting graph.
489  * This routine is comparable to cholmod_nested_dissection, except that it
490  * calls METIS_NodeND directly, and it does not return the separator tree.
491  *
492  * workspace: Flag (nrow), Iwork (4*n+uncol)
493  * Allocates a temporary matrix B=A*A' or B=A.
494  */
495 
496 int CHOLMOD(metis)
497 (
498  /* ---- input ---- */
499  cholmod_sparse *A, /* matrix to order */
500  Int *fset, /* subset of 0:(A->ncol)-1 */
501  size_t fsize, /* size of fset */
502  int postorder, /* if TRUE, follow with etree or coletree postorder */
503  /* ---- output --- */
504  Int *Perm, /* size A->nrow, output permutation */
505  /* --------------- */
506  cholmod_common *Common
507 )
508 {
509  double d ;
510  Int *Iperm, *Iwork, *Bp, *Bi ;
511  idxtype *Mp, *Mi, *Mperm, *Miperm ;
512  cholmod_sparse *B ;
513  Int i, j, n, nz, p, identity, uncol ;
514  int Opt [8], nn, zero = 0 ;
515  size_t n1, s ;
516  int ok = TRUE ;
517 
518  /* ---------------------------------------------------------------------- */
519  /* get inputs */
520  /* ---------------------------------------------------------------------- */
521 
523  RETURN_IF_NULL (A, FALSE) ;
524  RETURN_IF_NULL (Perm, FALSE) ;
526  Common->status = CHOLMOD_OK ;
527 
528  /* ---------------------------------------------------------------------- */
529  /* quick return */
530  /* ---------------------------------------------------------------------- */
531 
532  n = A->nrow ;
533  if (n == 0)
534  {
535  return (TRUE) ;
536  }
537  n1 = ((size_t) n) + 1 ;
538 
539  /* ---------------------------------------------------------------------- */
540  /* allocate workspace */
541  /* ---------------------------------------------------------------------- */
542 
543  /* s = 4*n + uncol */
544  uncol = (A->stype == 0) ? A->ncol : 0 ;
545  s = CHOLMOD(mult_size_t) (n, 4, &ok) ;
546  s = CHOLMOD(add_size_t) (s, uncol, &ok) ;
547  if (!ok)
548  {
549  ERROR (CHOLMOD_TOO_LARGE, "problem too large") ;
550  return (FALSE) ;
551  }
552 
553  CHOLMOD(allocate_work) (n, s, 0, Common) ;
554  if (Common->status < CHOLMOD_OK)
555  {
556  return (FALSE) ;
557  }
558  ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ;
559 
560  /* ---------------------------------------------------------------------- */
561  /* convert the matrix to adjacency list form */
562  /* ---------------------------------------------------------------------- */
563 
564  /* The input graph for METIS must be symmetric, with both upper and lower
565  * parts present, and with no diagonal entries. The columns need not be
566  * sorted.
567  * B = A+A', A*A', or A(:,f)*A(:,f)', upper and lower parts present */
568  if (A->stype)
569  {
570  /* Add the upper/lower part to a symmetric lower/upper matrix by
571  * converting to unsymmetric mode */
572  /* workspace: Iwork (nrow) */
573  B = CHOLMOD(copy) (A, 0, -1, Common) ;
574  }
575  else
576  {
577  /* B = A*A' or A(:,f)*A(:,f)', no diagonal */
578  /* workspace: Flag (nrow), Iwork (max (nrow,ncol)) */
579  B = CHOLMOD(aat) (A, fset, fsize, -1, Common) ;
580  }
581  ASSERT (CHOLMOD(dump_sparse) (B, "B for NodeND", Common) >= 0) ;
582  if (Common->status < CHOLMOD_OK)
583  {
584  return (FALSE) ;
585  }
586  ASSERT (B->nrow == A->nrow) ;
587 
588  /* ---------------------------------------------------------------------- */
589  /* get inputs */
590  /* ---------------------------------------------------------------------- */
591 
592  Iwork = Common->Iwork ;
593  Iperm = Iwork ; /* size n (i/i/l) */
594 
595  Bp = B->p ;
596  Bi = B->i ;
597  nz = Bp [n] ;
598 
599  /* ---------------------------------------------------------------------- */
600  /* METIS does not have a UF_long integer version */
601  /* ---------------------------------------------------------------------- */
602 
603 #ifdef LONG
604  if (sizeof (Int) > sizeof (idxtype) && MAX (n,nz) > INT_MAX / sizeof (int))
605  {
606  /* CHOLMOD's matrix is too large for METIS */
607  CHOLMOD(free_sparse) (&B, Common) ;
608  return (FALSE) ;
609  }
610 #endif
611 
612  /* B does not include the diagonal, and both upper and lower parts.
613  * Common->anz includes the diagonal, and just the lower part of B */
614  Common->anz = nz / 2 + n ;
615 
616  /* ---------------------------------------------------------------------- */
617  /* set control parameters for METIS_NodeND */
618  /* ---------------------------------------------------------------------- */
619 
620  Opt [0] = 0 ; /* use defaults */
621  Opt [1] = 3 ; /* matching type */
622  Opt [2] = 1 ; /* init. partitioning algo*/
623  Opt [3] = 2 ; /* refinement algorithm */
624  Opt [4] = 0 ; /* no debug */
625  Opt [5] = 1 ; /* initial compression */
626  Opt [6] = 0 ; /* no dense node removal */
627  Opt [7] = 1 ; /* number of separators @ each step */
628 
629  /* ---------------------------------------------------------------------- */
630  /* allocate the METIS input arrays, if needed */
631  /* ---------------------------------------------------------------------- */
632 
633  if (sizeof (Int) == sizeof (idxtype))
634  {
635  /* This is the typical case. */
636  Miperm = (idxtype *) Iperm ;
637  Mperm = (idxtype *) Perm ;
638  Mp = (idxtype *) Bp ;
639  Mi = (idxtype *) Bi ;
640  }
641  else
642  {
643  /* allocate graph for METIS only if Int and idxtype differ */
644  Miperm = CHOLMOD(malloc) (n, sizeof (idxtype), Common) ;
645  Mperm = CHOLMOD(malloc) (n, sizeof (idxtype), Common) ;
646  Mp = CHOLMOD(malloc) (n1, sizeof (idxtype), Common) ;
647  Mi = CHOLMOD(malloc) (nz, sizeof (idxtype), Common) ;
648  if (Common->status < CHOLMOD_OK)
649  {
650  /* out of memory */
651  CHOLMOD(free_sparse) (&B, Common) ;
652  CHOLMOD(free) (n, sizeof (idxtype), Miperm, Common) ;
653  CHOLMOD(free) (n, sizeof (idxtype), Mperm, Common) ;
654  CHOLMOD(free) (n1, sizeof (idxtype), Mp, Common) ;
655  CHOLMOD(free) (nz, sizeof (idxtype), Mi, Common) ;
656  return (FALSE) ;
657  }
658  for (j = 0 ; j <= n ; j++)
659  {
660  Mp [j] = Bp [j] ;
661  }
662  for (p = 0 ; p < nz ; p++)
663  {
664  Mi [p] = Bi [p] ;
665  }
666  }
667 
668  /* ---------------------------------------------------------------------- */
669  /* METIS workarounds */
670  /* ---------------------------------------------------------------------- */
671 
672  identity = FALSE ;
673  if (nz == 0)
674  {
675  /* The matrix has no off-diagonal entries. METIS_NodeND fails in this
676  * case, so avoid using it. The best permutation is identity anyway,
677  * so this is an easy fix. */
678  identity = TRUE ;
679  PRINT1 (("METIS:: no nz\n")) ;
680  }
681  else if (Common->metis_nswitch > 0)
682  {
683  /* METIS_NodeND in METIS 4.0.1 gives a seg fault with one matrix of
684  * order n = 3005 and nz = 6,036,025, including the diagonal entries.
685  * The workaround is to return the identity permutation instead of using
686  * METIS for matrices of dimension 3000 or more and with density of 66%
687  * or more - admittedly an uncertain fix, but such matrices are so dense
688  * that any reasonable ordering will do, even identity (n^2 is only 50%
689  * higher than nz in this case). CHOLMOD's nested dissection method
690  * (cholmod_nested_dissection) has no problems with the same matrix,
691  * even though it too uses METIS_NodeComputeSeparator. The matrix is
692  * derived from LPnetlib/lpi_cplex1 in the UF sparse matrix collection.
693  * If C is the lpi_cplex matrix (of order 3005-by-5224), A = (C*C')^2
694  * results in the seg fault. The seg fault also occurs in the stand-
695  * alone onmetis program that comes with METIS. If a future version of
696  * METIS fixes this problem, then set Common->metis_nswitch to zero.
697  */
698  d = ((double) nz) / (((double) n) * ((double) n)) ;
699  if (n > (Int) (Common->metis_nswitch) && d > Common->metis_dswitch)
700  {
701  identity = TRUE ;
702  PRINT1 (("METIS:: nswitch/dswitch activated\n")) ;
703  }
704  }
705 
706  if (!identity && !metis_memory_ok (n, nz, Common))
707  {
708  /* METIS might ask for too much memory and thus terminate the program */
709  identity = TRUE ;
710  }
711 
712  /* ---------------------------------------------------------------------- */
713  /* find the permutation */
714  /* ---------------------------------------------------------------------- */
715 
716  if (identity)
717  {
718  /* no need to do the postorder */
719  postorder = FALSE ;
720  for (i = 0 ; i < n ; i++)
721  {
722  Mperm [i] = i ;
723  }
724  }
725  else
726  {
727 #ifdef DUMP_GRAPH
728  /* DUMP_GRAPH */ printf ("Calling METIS_NodeND n "ID" nz "ID""
729  "density %g\n", n, nz, ((double) nz) / (((double) n) * ((double) n)));
730  dumpgraph (Mp, Mi, n, Common) ;
731 #endif
732 
733  nn = n ;
734  METIS_NodeND (&nn, Mp, Mi, &zero, Opt, Mperm, Miperm) ;
735  n = nn ;
736 
737  PRINT0 (("METIS_NodeND done\n")) ;
738  }
739 
740  /* ---------------------------------------------------------------------- */
741  /* free the METIS input arrays */
742  /* ---------------------------------------------------------------------- */
743 
744  if (sizeof (Int) != sizeof (idxtype))
745  {
746  for (i = 0 ; i < n ; i++)
747  {
748  Perm [i] = (Int) (Mperm [i]) ;
749  }
750  CHOLMOD(free) (n, sizeof (idxtype), Miperm, Common) ;
751  CHOLMOD(free) (n, sizeof (idxtype), Mperm, Common) ;
752  CHOLMOD(free) (n+1, sizeof (idxtype), Mp, Common) ;
753  CHOLMOD(free) (nz, sizeof (idxtype), Mi, Common) ;
754  }
755 
756  CHOLMOD(free_sparse) (&B, Common) ;
757 
758  /* ---------------------------------------------------------------------- */
759  /* etree or column-etree postordering, using the Cholesky Module */
760  /* ---------------------------------------------------------------------- */
761 
762  if (postorder)
763  {
764  Int *Parent, *Post, *NewPerm ;
765  Int k ;
766 
767  Parent = Iwork + 2*((size_t) n) + uncol ; /* size n = nrow */
768  Post = Parent + n ; /* size n */
769 
770  /* workspace: Iwork (2*nrow+uncol), Flag (nrow), Head (nrow+1) */
771  CHOLMOD(analyze_ordering) (A, CHOLMOD_METIS, Perm, fset, fsize,
772  Parent, Post, NULL, NULL, NULL, Common) ;
773  if (Common->status == CHOLMOD_OK)
774  {
775  /* combine the METIS permutation with its postordering */
776  NewPerm = Parent ; /* use Parent as workspace */
777  for (k = 0 ; k < n ; k++)
778  {
779  NewPerm [k] = Perm [Post [k]] ;
780  }
781  for (k = 0 ; k < n ; k++)
782  {
783  Perm [k] = NewPerm [k] ;
784  }
785  }
786  }
787 
788  ASSERT (CHOLMOD(dump_work) (TRUE, TRUE, 0, Common)) ;
789  PRINT1 (("cholmod_metis done\n")) ;
790  return (Common->status == CHOLMOD_OK) ;
791 }
792 #endif
#define CHOLMOD_TOO_LARGE
void f()
#define EMPTY
#define Int
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)
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() dump_partition(UF_long n, Int *Cp, Int *Ci, Int *Cnw, Int *Part, UF_long sepsize, cholmod_common *Common)
int CHOLMOD() free_sparse(cholmod_sparse **AHandle, cholmod_common *Common)
#define NULL
#define PRINT2(params)
#define ID
#define CHOLMOD_METIS
int CHOLMOD() metis(cholmod_sparse *A, Int *fset, size_t fsize, int postorder, Int *Perm, cholmod_common *Common)
#define CHOLMOD_INVALID
#define CHOLMOD_OK
#define PRINT0(params)
static int metis_memory_ok(Int n, Int nz, cholmod_common *Common)
int CHOLMOD() allocate_work(size_t nrow, size_t iworksize, size_t xworksize, cholmod_common *Common)
#define Size_max
int CHOLMOD() analyze_ordering(cholmod_sparse *A, int ordering, Int *Perm, Int *fset, size_t fsize, Int *Parent, Int *Post, Int *ColCount, Int *First, Int *Level, cholmod_common *Common)
#define RETURN_IF_NULL(A, result)
#define DEBUG(statement)
#define UF_long
UF_long CHOLMOD(metis_bisector)
#define ERROR(status, msg)
#define TRUE
#define GUESS(nz, n)
#define RETURN_IF_XTYPE_INVALID(A, xtype1, xtype2, result)
#define CHOLMOD_ZOMPLEX
#define ASSERT(expression)
cholmod_sparse *CHOLMOD() copy(cholmod_sparse *A, int stype, int mode, cholmod_common *Common)