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