File: graph_coarsen.c

package info (click to toggle)
scotch 5.0.1.dfsg-1
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 6,088 kB
  • ctags: 4,515
  • sloc: ansic: 46,851; makefile: 2,093; yacc: 588; lex: 263; fortran: 90
file content (811 lines) | stat: -rw-r--r-- 41,759 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
/* Copyright 2004,2007 ENSEIRB, INRIA & CNRS
**
** This file is part of the Scotch software package for static mapping,
** graph partitioning and sparse matrix ordering.
**
** This software is governed by the CeCILL-C license under French law
** and abiding by the rules of distribution of free software. You can
** use, modify and/or redistribute the software under the terms of the
** CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
** URL: "http://www.cecill.info".
** 
** As a counterpart to the access to the source code and rights to copy,
** modify and redistribute granted by the license, users are provided
** only with a limited warranty and the software's author, the holder of
** the economic rights, and the successive licensors have only limited
** liability.
** 
** In this respect, the user's attention is drawn to the risks associated
** with loading, using, modifying and/or developing or reproducing the
** software by the user in light of its specific status of free software,
** that may mean that it is complicated to manipulate, and that also
** therefore means that it is reserved for developers and experienced
** professionals having in-depth computer knowledge. Users are therefore
** encouraged to load and test the software's suitability as regards
** their requirements in conditions enabling the security of their
** systems and/or data to be ensured and, more generally, to use and
** operate it in the same conditions as regards security.
** 
** The fact that you are presently reading this means that you have had
** knowledge of the CeCILL-C license and that you accept its terms.
*/
/************************************************************/
/**                                                        **/
/**   NAME       : graph_coarsen.c                         **/
/**                                                        **/
/**   AUTHOR     : Francois PELLEGRINI                     **/
/**                                                        **/
/**   FUNCTION   : This module contains the source graph   **/
/**                coarsening functions.                   **/
/**                                                        **/
/**   DATES      : # Version 0.0  : from : 01 dec 1992     **/
/**                                 to     18 may 1993     **/
/**                # Version 1.3  : from : 30 apr 1994     **/
/**                                 to     18 may 1994     **/
/**                # Version 2.0  : from : 06 jun 1994     **/
/**                                 to     31 oct 1994     **/
/**                # Version 3.0  : from : 07 jul 1995     **/
/**                                 to     28 sep 1995     **/
/**                # Version 3.1  : from : 28 nov 1995     **/
/**                                 to     08 jun 1996     **/
/**                # Version 3.2  : from : 07 sep 1996     **/
/**                                 to     17 sep 1998     **/
/**                # Version 4.0  : from : 13 dec 2001     **/
/**                                 to     31 aug 2005     **/
/**                # Version 5.0  : from : 13 dec 2006     **/
/**                                 to     10 sep 2007     **/
/**                                                        **/
/************************************************************/

/*
**  The defines and includes.
*/

#define GRAPH_COARSEN

#include "module.h"
#include "common.h"
#include "graph.h"
#include "graph_coarsen.h"

/*
**  The static variables.
*/

static Gnum              (* graphCoarsenFuncTab[GRAPHCOARNBR]) () = { /* Tables of edge-matching routines */
                              graphCoarsenMatchHy,
                              graphCoarsenMatchSc,
                              graphCoarsenMatchCs,
                              graphCoarsenMatchCh };

/***************************/
/*                         */
/* The coarsening routine. */
/*                         */
/***************************/

/* This routine coarsens the given "finegraph" into
** "coargraph", as long as the coarsening ratio remains
** below some threshold value and the coarsened graph
** is not too small.
** It returns:
** - 0  : if the graph has been coarsened.
** - 1  : if the graph could not be coarsened.
** - 2  : on error.
*/

int
graphCoarsen (
const Graph * restrict const          finegrafptr, /*+ Graph to coarsen                    +*/
Graph * restrict const                coargrafptr, /*+ Coarse graph to build               +*/
GraphCoarsenMulti * restrict * const  coarmultptr, /*+ Pointer to multinode table to build +*/
const Gnum                            coarnbr,    /*+ Minimum number of coarse vertices    +*/
const double                          coarrat,    /*+ Maximum contraction ratio            +*/
const GraphCoarsenType                coartype)   /*+ Edge matching type                   +*/
{
  Gnum                          coarhashnbr;      /* Size of the hash table                   */
  Gnum                          coarhashmsk;      /* Mask for access to hash table            */
  GraphCoarsenHash * restrict   coarhashtab;      /* Table of edges to other multinodes       */
  Gnum                          coarvertnbr;      /* Number of coarse vertices                */
  Gnum                          coarvertnum;      /* Number of current multinode vertex       */
  Gnum                          coarvertmax;      /* Maximum number of multinode vertices     */
  Gnum                          coarvelomax;      /* Maximum vertex weight allowed            */
  GraphCoarsenMulti * restrict  coarmulttax;      /* Multinode array                          */
  Gnum * restrict               finecoartax;      /* Based access to finecoartab              */
  Gnum                          finevertnum;      /* Number of currently selected fine vertex */
  size_t                        coarmultoftval;
  size_t                        coarvelooftval;
  size_t                        coaredgeoftval;
  size_t                        coaredlooftval;

#ifdef SCOTCH_DEBUG_GRAPH2
  if (coartype >= GRAPHCOARNBR) {
    errorPrint ("graphCoarsen: invalid parameter");
    return     (2);
  }
#endif /* SCOTCH_DEBUG_GRAPH2 */
#ifdef SCOTCH_DEBUG_GRAPH1
  if (coarrat < 0.5L)                             /* If impossible coarsening ratio wanted */
    return (1);                                   /* We will never succeed                 */
#endif /* SCOTCH_DEBUG_GRAPH1 */

  coarvertmax = (Gnum) ((double) finegrafptr->vertnbr * coarrat); /* Maximum number of coarse vertices */
  if (coarvertmax < coarnbr)                      /* If there will be too few vertices in graph        */
    return (1);                                   /* It is useless to go any further                   */

  if ((finecoartax = (Gnum *) memAlloc (finegrafptr->vertnbr * sizeof (Gnum))) == NULL) {
    errorPrint ("graphCoarsen: out of memory (1)"); /* Allocate coarse graph uncoarsening array */
    return     (2);
  }
  memSet (finecoartax, ~0, finegrafptr->vertnbr * sizeof (Gnum));
  finecoartax -= finegrafptr->baseval;            /* Set based access to finecoartab */

  coarvelomax = (3 * finegrafptr->velosum) / (2 * coarnbr) + 1;

  coarvertnbr = graphCoarsenFuncTab[coartype] (finegrafptr, finecoartax, coarvertmax, coarvelomax); /* Call proper matching function */

  if (coarvertnbr >= coarvertmax) {               /* If coarsened graph too large */
    memFree (finecoartax + finegrafptr->baseval); /* Do not proceed any further   */
    return  (1);
  }

#ifdef SCOTCH_DEBUG_GRAPH2
  for (finevertnum = finegrafptr->baseval; finevertnum < finegrafptr->vertnnd; finevertnum ++) {
    if (finecoartax[finevertnum] <= ~0) {         /* If coarsening not aborted, this should not happen */
      errorPrint ("graphCoarsen: internal error (1)");
      memFree    (finecoartax + finegrafptr->baseval);
      return     (2);
    }
  }
#endif /* SCOTCH_DEBUG_GRAPH2 */

  memSet (coargrafptr, 0, sizeof (Graph));        /* Initialize coarse graph */
  coargrafptr->flagval = GRAPHFREEVERT | GRAPHVERTGROUP | GRAPHEDGEGROUP;
  coargrafptr->baseval = finegrafptr->baseval;
  coargrafptr->vertnbr = coarvertnbr;
  coargrafptr->vertnnd = coarvertnbr + coargrafptr->baseval;
  coargrafptr->velosum = finegrafptr->velosum;    /* Keep load of finer graph */

  for (coarhashmsk = 31; coarhashmsk < finegrafptr->degrmax; coarhashmsk = coarhashmsk * 2 + 1) ;
  coarhashmsk = coarhashmsk * 4 + 3;
  coarhashnbr = coarhashmsk + 1;

  if (memAllocGroup ((void **) (void *)
                     &coargrafptr->verttax, (size_t) ((coarvertnbr + 1)    * sizeof (Gnum)),
                     &coargrafptr->velotax, (size_t) (coarvertnbr          * sizeof (Gnum)),
                     &coarmulttax,          (size_t) (coarvertnbr          * sizeof (GraphCoarsenMulti)),
                     &coargrafptr->edgetax, (size_t) (finegrafptr->edgenbr * sizeof (Gnum)), /* Pre-allocate space for edge arrays */ 
                     &coargrafptr->edlotax, (size_t) (finegrafptr->edgenbr * sizeof (Gnum)),
                     &coarhashtab,          (size_t) (coarhashnbr          * sizeof (GraphCoarsenHash)), NULL) == NULL) {
    errorPrint ("graphCoarsen: out of memory (2)"); /* Allocate coarser graph structure */
    memFree    (finecoartax + finegrafptr->baseval);
    return     (2);
  }
  coargrafptr->verttax -= coargrafptr->baseval;   /* Base coarse graph arrays */
  coargrafptr->velotax -= coargrafptr->baseval;
  coargrafptr->edgetax -= coargrafptr->baseval;
  coargrafptr->edlotax -= coargrafptr->baseval;
  coarmulttax          -= coargrafptr->baseval;

  for (finevertnum = finegrafptr->baseval, coarvertnum = coargrafptr->baseval; /* Finalize finecoartab array */
       finevertnum < finegrafptr->vertnnd; finevertnum ++) {
    Gnum                finematenum;              /* Number of current mate vertex */

    finematenum = finecoartax[finevertnum];       /* Get mate number                               */
    if (finematenum >= finevertnum) {             /* If mate has larger number                     */
      coarmulttax[coarvertnum].vertnum[0] = finevertnum; /* Build new multinode                    */
      coarmulttax[coarvertnum].vertnum[1] = finematenum; /* Second index always biggest            */
      finecoartax[finematenum] =                  /* Point to coarse vertex                        */
      finecoartax[finevertnum] = coarvertnum;     /* Always valid since coarvertnum <= finevertnum */
      coarvertnum ++;                             /* One more multinode created                    */
    }
  }
#ifdef SCOTCH_DEBUG_GRAPH2
  if ((coarvertnum - coargrafptr->baseval) != coarvertnbr) {
    errorPrint ("graphCoarsen: internal error (2)");
    graphFree  (coargrafptr);
    memFree    (finecoartax + finegrafptr->baseval);
    return     (2);
  }
#endif /* SCOTCH_DEBUG_GRAPH2 */

  if (finegrafptr->velotax != NULL) {             /* If fine graph is weighted */
    for (coarvertnum = coargrafptr->baseval; coarvertnum < coargrafptr->vertnnd; coarvertnum ++) {
      Gnum                coarveloval;

      coarveloval = finegrafptr->velotax[coarmulttax[coarvertnum].vertnum[0]];
      if (coarmulttax[coarvertnum].vertnum[0] != coarmulttax[coarvertnum].vertnum[1])
        coarveloval += finegrafptr->velotax[coarmulttax[coarvertnum].vertnum[1]];
      coargrafptr->velotax[coarvertnum] = coarveloval;
    }
  }
  else {                                          /* Fine graph is not weighted */
    for (coarvertnum = coargrafptr->baseval; coarvertnum < coargrafptr->vertnnd; coarvertnum ++)
      coargrafptr->velotax[coarvertnum] = (coarmulttax[coarvertnum].vertnum[0] != coarmulttax[coarvertnum].vertnum[1]) ? 2 : 1;
  }

  memSet (coarhashtab, ~0, coarhashnbr * sizeof (GraphCoarsenHash));
  if (finegrafptr->edlotax != NULL)               /* If edge loads available */
    graphCoarsenEdgeLl (finegrafptr, finecoartax, coarmulttax, coargrafptr, coarhashtab, coarhashmsk);
  else                                            /* Fine edges not weighted */
    graphCoarsenEdgeLu (finegrafptr, finecoartax, coarmulttax, coargrafptr, coarhashtab, coarhashmsk);
  coargrafptr->edgenbr = coargrafptr->verttax[coargrafptr->vertnnd] - coargrafptr->baseval; /* Set exact number of edges */

  memFree (finecoartax + finegrafptr->baseval);

  coarvelooftval = coargrafptr->velotax - coargrafptr->verttax;
  coarmultoftval = (Gnum *) coarmulttax - coargrafptr->verttax;
  coaredgeoftval = coargrafptr->edgetax - coargrafptr->verttax;
  coaredlooftval = coargrafptr->edlotax - coargrafptr->verttax;
  memReallocGroup ((void *) (coargrafptr->verttax + coargrafptr->baseval), /* Re-allocate data, wiping temporary arrays */
                   &coargrafptr->verttax, (size_t) ((coarvertnbr + 1)    * sizeof (Gnum)),
                   &coargrafptr->velotax, (size_t) (coarvertnbr          * sizeof (Gnum)),
                   &coarmulttax,          (size_t) (coarvertnbr          * sizeof (GraphCoarsenMulti)),
                   &coargrafptr->edgetax, (size_t) (finegrafptr->edgenbr * sizeof (Gnum)),
                   &coargrafptr->edlotax, (size_t) (coargrafptr->edgenbr * sizeof (Gnum)), NULL);
  coargrafptr->verttax -= coargrafptr->baseval;
  coargrafptr->vendtax  = coargrafptr->verttax + 1; /* Use compact representation of arrays */
  coargrafptr->velotax  = coargrafptr->verttax + coarvelooftval;
  coargrafptr->edgetax  = coargrafptr->verttax + coaredgeoftval;
  coargrafptr->edlotax  = coargrafptr->verttax + coaredlooftval;
  coarmulttax           = (GraphCoarsenMulti *) (coargrafptr->verttax + coarmultoftval);
  *coarmultptr          = coarmulttax;            /* Return pointer to multinode array */

#ifdef SCOTCH_DEBUG_GRAPH2
  if (graphCheck (coargrafptr) != 0) {            /* Check graph consistency */
    errorPrint ("graphCoarsen: inconsistent graph data");
    graphFree  (coargrafptr);
    return     (2);
  }
#endif /* SCOTCH_DEBUG_GRAPH2 */

  return (0);
}

/****************************************/
/*                                      */
/* The edge array building subroutines. */
/*                                      */
/****************************************/

#define GRAPHCOARSENEDGENAME        graphCoarsenEdgeLl
#define GRAPHCOARSENEDGEEDLOINIT    coargrafptr->edlotax[coaredgenum] = finegrafptr->edlotax[fineedgenum]
#define GRAPHCOARSENEDGEEDLOADD     coargrafptr->edlotax[coarhashtab[h].edgenum] += finegrafptr->edlotax[fineedgenum]
#define GRAPHCOARSENEDGEEDLOSUB     coaredlosum -= finegrafptr->edlotax[fineedgenum]
#include "graph_coarsen_edge.c"
#undef GRAPHCOARSENEDGENAME
#undef GRAPHCOARSENEDGEEDLOINIT
#undef GRAPHCOARSENEDGEEDLOADD
#undef GRAPHCOARSENEDGEEDLOSUB

#define GRAPHCOARSENEDGENAME        graphCoarsenEdgeLu
#define GRAPHCOARSENEDGEEDLOINIT    coargrafptr->edlotax[coaredgenum] = 1
#define GRAPHCOARSENEDGEEDLOADD     coargrafptr->edlotax[coarhashtab[h].edgenum] ++
#define GRAPHCOARSENEDGEEDLOSUB     coaredlosum --
#include "graph_coarsen_edge.c"
#undef GRAPHCOARSENEDGENAME
#undef GRAPHCOARSENEDGEEDLOINIT
#undef GRAPHCOARSENEDGEEDLOADD
#undef GRAPHCOARSENEDGEEDLOSUB

/*****************************/
/*                           */
/* The matching subroutines. */
/*                           */
/*****************************/

static
Gnum
graphCoarsenMatchHy (
const Graph * restrict const  finegrafptr,        /* Fine graph to perform matching on */
Gnum * restrict               finecoartax,        /* Fine to coarse vertex index array */
const Gnum                    coarvertmax,        /* Maximum number of vertices to get */
const Gnum                    coarvelomax)        /* Maximum vertex weight allowed     */
{
  Gnum                  coarvertnum;              /* Number of current multinode vertex       */
  Gnum                  finepertbas;              /* Index of base of perturbation area       */
  Gnum                  finepertnbr;              /* Size of perturbation area                */
  const Gnum * restrict fineverttax;              /* Based access to vertex array             */
  const Gnum * restrict finevendtax;              /* Based access to end vertex array         */
  const Gnum * restrict finevelotax;              /* Based access to end vertex array         */
  const Gnum * restrict fineedgetax;              /* Based access to end vertex array         */
  const Gnum * restrict fineedlotax;              /* Based access to end vertex array         */
  Gnum                  finevertnnd;              /* Current end of vertex array              */
  Gnum                  finevertnum;              /* Number of currently selected fine vertex */

  if (finegrafptr->edlotax == NULL)               /* If no edge loads, perform scan matching instead */
    return (graphCoarsenMatchSc (finegrafptr, finecoartax, coarvertmax, coarvelomax));

  fineverttax = finegrafptr->verttax;
  finevendtax = finegrafptr->vendtax;
  finevelotax = finegrafptr->velotax;
  fineedgetax = finegrafptr->edgetax;
  fineedlotax = finegrafptr->edlotax;
  finevertnnd = finegrafptr->vertnnd;
  coarvertnum = 0;

  if (finegrafptr->velotax != NULL) {
    Gnum                finevelodlt;              /* Minimum load of neighbor */

    finevelodlt = (3 * finegrafptr->velosum) / (5 * (finevertnnd - finegrafptr->baseval));

    for (finevertnum = finegrafptr->baseval;      /* Pre-selection loop for isolated and lightest vertices */
         finevertnum < finevertnnd; finevertnum ++) {
      if (fineverttax[finevertnum] == finevendtax[finevertnum]) { /* If isolated vertex      */
        while (finecoartax[-- finevertnnd] != ~0) ; /* Search for first matchable "neighbor" */

        finecoartax[finevertnum] = finevertnnd;   /* At worst we will stop at finevertnum */
        finecoartax[finevertnnd] = finevertnum;
        coarvertnum ++;                           /* One more coarse vertex created */
      }
      else {                                      /* Vertex has neighbors */
        if ((finevelotax[finevertnum] < finevelodlt) &&
            (finecoartax[finevertnum] == ~0)) {   /* If vertex is too light on average  */
          Gnum                finevertbst;        /* Number of current best neighbor    */
          Gnum                fineedlobst;        /* Edge load of current best neighbor */
          Gnum                fineedgenum;

          if (coarvertnum >= coarvertmax)         /* If coarse graph is too large       */
            return (coarvertmax);                 /* Return that we cannot coarsen more */

          finevertbst = finevertnum;              /* No matching neighbor found yet */
          fineedlobst = 0;
          for (fineedgenum = fineverttax[finevertnum]; /* For all adjacent vertices */
               fineedgenum < finevendtax[finevertnum]; fineedgenum ++) {
            if ((finecoartax[fineedgetax[fineedgenum]] == ~0) && /* If unmatched vertex */
                (fineedlotax[fineedgenum] > fineedlobst)) { /* And is better candidate  */
              fineedlobst = fineedlotax[fineedgenum];
              finevertbst = fineedgetax[fineedgenum];
            }
          }

          finecoartax[finevertnum] = finevertbst;
          finecoartax[finevertbst] = finevertnum;
          coarvertnum ++;                         /* One more coarse vertex created */
        }
      }
    }
  }

  finepertnbr = 2 + intRandVal (GRAPHCOARPERTPRIME - 2); /* Compute perturbation area size (avoid DIV0 in random) */
  for (finepertbas = finegrafptr->baseval; finepertbas < finevertnnd; /* Run cache-friendly perturbation          */
       finepertbas += finepertnbr) {
    Gnum                finepertval;              /* Current index in perturbation area */

    if (finepertbas + finepertnbr > finevertnnd)
      finepertnbr = finevertnnd - finepertbas;

    finepertval = 0;                              /* Start from first perturbation vertex */
    do {                                          /* Loop on perturbation vertices        */
      finevertnum = finepertbas + finepertval;    /* Compute corresponding vertex number  */

      if (finecoartax[finevertnum] == ~0) {       /* If vertex has not been picked already */
        Gnum                finevertbst;          /* Number of current best neighbor       */
        Gnum                fineedlobst;          /* Edge load of current best neighbor    */
        Gnum                finevelodlt;          /* Maximum load of neighbor              */
        Gnum                fineedgenum;

        if (coarvertnum >= coarvertmax)           /* If coarse graph too large       */
          return (coarvertmax);                   /* Return that cannot coarsen more */

        finevertbst = finevertnum;                /* No matching vertex found yet */
        fineedlobst = 0;
        finevelodlt = coarvelomax - ((finevelotax != NULL) ? finevelotax[finevertnum] : 1);

        for (fineedgenum = fineverttax[finevertnum]; /* For all adjacent vertices */
             fineedgenum < finevendtax[finevertnum]; fineedgenum ++) {
          if ((finecoartax[fineedgetax[fineedgenum]] == ~0) && /* If unmatched vertex */
              (fineedlotax[fineedgenum] > fineedlobst) && /* And better candidate     */
              ((finevelotax == NULL) ||  /* And does not create overloads             */
               (finevelodlt >= finevelotax[fineedgetax[fineedgenum]]))) {
            fineedlobst = fineedlotax[fineedgenum];
            finevertbst = fineedgetax[fineedgenum];
          }
        }

        finecoartax[finevertnum] = finevertbst;
        finecoartax[finevertbst] = finevertnum;
        coarvertnum ++;                           /* One more coarse vertex created */
      }

      finepertval = (finepertval + GRAPHCOARPERTPRIME) % finepertnbr; /* Compute next perturbation index */
    } while (finepertval != 0);
  }

  return (coarvertnum);                           /* Return number of coarse vertices */
}

static
Gnum
graphCoarsenMatchSc (
const Graph * restrict const  finegrafptr,        /* Fine graph to perform matching on */
Gnum * restrict               finecoartax,        /* Fine to coarse vertex index array */
const Gnum                    coarvertmax,        /* Maximum number of vertices to get */
const Gnum                    coarvelomax)        /* Maximum vertex weight allowed     */
{
  Gnum                  coarvertnum;              /* Number of current multinode vertex       */
  Gnum                  finepertbas;              /* Index of base of perturbation area       */
  const Gnum * restrict fineverttax;              /* Based access to vertex array             */
  Gnum                  finepertnbr;              /* Size of perturbation area                */
  Gnum                  finevertnnd;              /* Current end of vertex array              */
  Gnum                  finevertnum;              /* Number of currently selected fine vertex */

  fineverttax = finegrafptr->verttax;

  coarvertnum = 0;
  for (finepertbas = finegrafptr->baseval, finevertnnd = finegrafptr->vertnnd;
       finepertbas < finevertnnd; finepertbas += finepertnbr) { /* Run cache-friendly perturbation */
    Gnum                finepertval;              /* Current index in perturbation area            */

    finepertnbr = finegrafptr->degrmax * 2 + intRandVal (finegrafptr->degrmax + 1) + 1; /* Compute perturbation area size (avoid DIV0 in random) */
    if (finepertnbr >= GRAPHCOARPERTPRIME)
      finepertnbr = 32 + intRandVal (GRAPHCOARPERTPRIME - 34);

    if (finepertbas + finepertnbr > finevertnnd)
      finepertnbr = finevertnnd - finepertbas;

    finepertval = 0;                              /* Start from first perturbation vertex */
    do {                                          /* Loop on perturbation vertices        */
      finevertnum = finepertbas + finepertval;    /* Compute corresponding vertex number  */

      if (finecoartax[finevertnum] == ~0) {       /* If vertex has not been picked already  */
        Gnum                finevertbst;          /* Number of current best matching vertex */

        if (coarvertnum >= coarvertmax)           /* If coarse graph is too large          */
          return (coarvertmax);                   /* Return that we cannot coarsen more    */

        if (fineverttax[finevertnum] == finegrafptr->vendtax[finevertnum]) { /* If isolated vertex */
          while (finecoartax[-- finevertnnd] != ~0) ; /* Search for first matchable "neighbor"     */
          finevertbst = finevertnnd;              /* Unmatched vertex will act as neighbor         */
        }
        else {                                    /* Vertex has neighbors */
          Gnum                finevelodlt;        /* Overload limit       */
          Gnum                fineedgenum;        /* Current edge number  */

          finevertbst = finevertnum;              /* No matching vertex found yet */
          finevelodlt = coarvelomax - ((finegrafptr->velotax != NULL) ? finegrafptr->velotax[finevertnum] : 1);

          for (fineedgenum = fineverttax[finevertnum]; /* For all adjacent vertices */
               fineedgenum < finegrafptr->vendtax[finevertnum]; fineedgenum ++) {
            if ((finecoartax[finegrafptr->edgetax[fineedgenum]] == ~0) && /* If unmatched vertex */
                ((finegrafptr->velotax == NULL) || /* And does not create overloads              */
                 (finevelodlt >= finegrafptr->velotax[finegrafptr->edgetax[fineedgenum]]))) {
              finevertbst = finegrafptr->edgetax[fineedgenum];
              break;
            }
          }
        }

        finecoartax[finevertnum] = finevertbst;
        finecoartax[finevertbst] = finevertnum;
        coarvertnum ++;                           /* One more coarse vertex created */
      }

      finepertval = (finepertval + GRAPHCOARPERTPRIME) % finepertnbr; /* Compute next perturbation index */
    } while (finepertval != 0);
  }

  return (coarvertnum);                           /* Return number of coarse vertices */
}

static
Gnum
graphCoarsenMatchCs (                             /* Crystallographic scan             */
const Graph * restrict const  finegrafptr,        /* Fine graph to perform matching on */
Gnum * restrict               finecoartax,        /* Fine to coarse vertex index array */
const Gnum                    coarvertmax,        /* Maximum number of vertices to get */
const Gnum                    coarvelomax)        /* Maximum vertex weight allowed     */
{
  Gnum                  coarvertnum;              /* Number of current multinode vertex       */
  const Gnum * restrict fineverttax;              /* Based access to vertex array             */
  const Gnum * restrict finevendtax;              /* Based access to end vertex array         */
  const Gnum * restrict finevelotax;              /* Based access to vertex load array        */
  const Gnum * restrict fineedgetax;              /* Based access to edge array               */
  Gnum                  finevertnum;              /* Number of currently selected fine vertex */
  Gnum * restrict       finequeutab;
  Gnum                  finequeuheadval;
  Gnum                  finequeutailval;
  Gnum                  finepermnum;              /* Permutation number for finding connected components */

  fineverttax = finegrafptr->verttax;
  finevendtax = finegrafptr->vendtax;
  finevelotax = finegrafptr->velotax;
  fineedgetax = finegrafptr->edgetax;
  if ((finequeutab = memAlloc (finegrafptr->vertnbr * sizeof (Gnum))) == NULL) {
    errorPrint ("graphCoarsenMatchCs: out of memory");
    return     (graphCoarsenMatchSc (finegrafptr, finecoartax, coarvertmax, coarvelomax)); /* Fallback strategy */
  }

  coarvertnum     = 0;
  finequeuheadval = 1;
  finequeutailval = 0;
  finequeutab[0] = finegrafptr->baseval + intRandVal (finegrafptr->vertnbr); /* Start from random vertex */
  finecoartax[finequeutab[0]] = -2;               /* Set vertex as enqueued */
  
  for (finepermnum = finegrafptr->baseval; finequeutailval < finegrafptr->vertnbr; ) {
    if (finequeutailval < finequeuheadval) {      /* If vertices remain in queue */
      Gnum                finevertbst;            /* Best vertex found till now  */
      Gnum                finevelodlt;            /* Overload limit              */
      Gnum                fineedgenum;            /* Current edge number         */

      finevertnum = finequeutab[finequeutailval ++]; /* Select a vertex from the queue */

      if (finecoartax[finevertnum] >= 0) {        /* If selected vertex already matched */
        for (fineedgenum = fineverttax[finevertnum]; /* For all adjacent vertices       */
             fineedgenum < finevendtax[finevertnum]; fineedgenum ++) {
          Gnum                finevertend;

          finevertend = fineedgetax[fineedgenum];
          if (finecoartax[finevertend] == ~0) {
            finequeutab[finequeuheadval ++] = finevertend;
            finecoartax[finevertend] = -2;
          }
        }
        continue;                                 /* Skip to next vertex */
      }

      if (coarvertnum >= coarvertmax)             /* If coarse graph is too large       */
        break;                                    /* Return that we cannot coarsen more */

      finevertbst = finevertnum;                  /* No matching vertex found yet */
      finevelodlt = coarvelomax - ((finegrafptr->velotax != NULL) ? finegrafptr->velotax[finevertnum] : 1);

      for (fineedgenum = fineverttax[finevertnum]; /* For all adjacent vertices */
           fineedgenum < finevendtax[finevertnum]; fineedgenum ++) {
        Gnum                finevertend;
        Gnum                finecoarval;

        finevertend = fineedgetax[fineedgenum];
        finecoarval = finecoartax[finevertend];

        if (finecoarval < 0) {                    /* If vertex not matched  */
          if (finecoartax[finevertend] == ~0) {   /* If vertex not enqueued */
            finequeutab[finequeuheadval ++] = finevertend; /* Enqueue it    */
            finecoartax[finevertend] = -2;
          }
          if ((finevelotax == NULL) ||            /* And does not create overloads */
              (finevelodlt >= finevelotax[finevertend])) {
            finevertbst = finevertend;            /* Get matching vertex */

            while (++ fineedgenum < finevendtax[finevertnum]) { /* Scan and enqueue remaining neighbors */
              finevertend = fineedgetax[fineedgenum];
              if (finecoartax[finevertend] == ~0) {
                finequeutab[finequeuheadval ++] = finevertend;
                finecoartax[finevertend] = -2;
              }
            }
          }
        }
      }

      finecoartax[finevertnum] = finevertbst;     /* Match both vertices */
      finecoartax[finevertbst] = finevertnum;
      coarvertnum ++;                             /* One more coarse vertex created */
    }
    else {                                        /* Search for other connected component */
      Gnum                finevertbst;

      for ( ; finecoartax[finepermnum] >= 0; finepermnum ++) { /* Scan vertices in ascending order */
#ifdef SCOTCH_DEBUG_GRAPH2
        if (finepermnum >= finegrafptr->vertnnd) {
          errorPrint ("graphCoarsenMatchCs: internal error (1)");
          memFree    (finequeutab);
          return     (finegrafptr->vertnbr);      /* Coarsening aborted */
        }
#endif /* SCOTCH_DEBUG_GRAPH2 */
      }
#ifdef SCOTCH_DEBUG_GRAPH2
      if (finecoartax[finepermnum] != ~0) {
        errorPrint ("graphCoarsenMatchCs: internal error (2)");
        memFree    (finequeutab);
        return     (finegrafptr->vertnbr);        /* Coarsening aborted */
      }
#endif /* SCOTCH_DEBUG_GRAPH2 */
      finevertnum = finepermnum ++;               /* Start from found vertex */

      if (fineverttax[finevertnum] != finevendtax[finevertnum]) { /* If vertex not isolated */
        finequeutab[finequeuheadval ++] = finevertnum; /* Enqueue it for normal processing  */
        continue;                                 /* Skip to main loop to process it        */
      }

      finequeuheadval = ++ finequeutailval;       /* One more vertex enqueued-edqueued */

      if (coarvertnum >= coarvertmax)             /* If coarse graph is too large       */
        break;                                    /* Return that we cannot coarsen more */

      if (finequeutailval >= finegrafptr->vertnbr) /* If isolated vertex is last available vertex */
        finevertbst = finevertnum;
      else {
        for ( ; finecoartax[finepermnum] >= 0; finepermnum ++) {
#ifdef SCOTCH_DEBUG_GRAPH2
          if (finepermnum >= finegrafptr->vertnnd) {
            errorPrint ("graphCoarsenMatchCs: internal error (3)");
            memFree    (finequeutab);
            return     (finegrafptr->vertnbr);    /* Coarsening aborted */
          }
#endif /* SCOTCH_DEBUG_GRAPH2 */
        }
#ifdef SCOTCH_DEBUG_GRAPH2
        if (finecoartax[finepermnum] != ~0) {
          errorPrint ("graphCoarsenMatchCs: internal error (4)");
          memFree    (finequeutab);
          return     (finegrafptr->vertnbr);      /* Coarsening aborted */
        }
#endif /* SCOTCH_DEBUG_GRAPH2 */
        finevertbst     = finepermnum ++;         /* Get found vertex                  */
        finequeuheadval = ++ finequeutailval;     /* One more vertex enqueued-edqueued */
      }

      finecoartax[finevertnum] = finevertbst;     /* Match both vertices */
      finecoartax[finevertbst] = finevertnum;
      coarvertnum ++;                             /* One more coarse vertex created */
    }
  }

  memFree (finequeutab);

  return (coarvertnum);                           /* Return number of coarse vertices */
}

static
Gnum
graphCoarsenMatchCh (                             /* Crystallographic heavy edge       */
const Graph * restrict const  finegrafptr,        /* Fine graph to perform matching on */
Gnum * restrict               finecoartax,        /* Fine to coarse vertex index array */
const Gnum                    coarvertmax,        /* Maximum number of vertices to get */
const Gnum                    coarvelomax)        /* Maximum vertex weight allowed     */
{
  Gnum                  coarvertnum;              /* Number of current multinode vertex       */
  const Gnum * restrict fineverttax;              /* Based access to vertex array             */
  const Gnum * restrict finevendtax;              /* Based access to end vertex array         */
  const Gnum * restrict finevelotax;              /* Based access to vertex load array        */
  const Gnum * restrict fineedgetax;              /* Based access to edge array               */
  const Gnum * restrict fineedlotax;              /* Based access to edge load array          */
  Gnum                  finevertnum;              /* Number of currently selected fine vertex */
  Gnum * restrict       finequeutab;
  Gnum                  finequeuheadval;
  Gnum                  finequeutailval;
  Gnum                  finepermnum;              /* Permutation number for finding connected components */

  if (finegrafptr->edlotax == NULL)               /* If no edge loads, perform scan matching instead */
    return (graphCoarsenMatchCs (finegrafptr, finecoartax, coarvertmax, coarvelomax));

  fineverttax = finegrafptr->verttax;
  finevendtax = finegrafptr->vendtax;
  finevelotax = finegrafptr->velotax;
  fineedgetax = finegrafptr->edgetax;
  fineedlotax = finegrafptr->edlotax;
  if ((finequeutab = memAlloc (finegrafptr->vertnbr * sizeof (Gnum))) == NULL) {
    errorPrint ("graphCoarsenMatchCh: out of memory");
    return     (graphCoarsenMatchSc (finegrafptr, finecoartax, coarvertmax, coarvelomax)); /* Fallback strategy */
  }

  coarvertnum     = 0;
  finequeuheadval = 1;
  finequeutailval = 0;
  finequeutab[0] = finegrafptr->baseval + intRandVal (finegrafptr->vertnbr); /* Start from random vertex */
  finecoartax[finequeutab[0]] = -2;               /* Set vertex as enqueued */
  
  for (finepermnum = finegrafptr->baseval; finequeutailval < finegrafptr->vertnbr; ) {
    if (finequeutailval < finequeuheadval) {      /* If vertices remain in queue        */
      Gnum                finevertbst;            /* Best vertex found till now         */
      Gnum                fineedlobst;            /* Edge load of current best neighbor */
      Gnum                finevelodlt;            /* Overload limit                     */
      Gnum                fineedgenum;            /* Current edge number                */

      finevertnum = finequeutab[finequeutailval ++]; /* Select a vertex from the queue */

      if (finecoartax[finevertnum] >= 0) {        /* If selected vertex already matched */
        for (fineedgenum = fineverttax[finevertnum]; /* For all adjacent vertices       */
             fineedgenum < finevendtax[finevertnum]; fineedgenum ++) {
          Gnum                finevertend;

          finevertend = fineedgetax[fineedgenum];
          if (finecoartax[finevertend] == ~0) {
            finequeutab[finequeuheadval ++] = finevertend;
            finecoartax[finevertend] = -2;
          }
        }
        continue;                                 /* Skip to next vertex */
      }

      if (coarvertnum >= coarvertmax)             /* If coarse graph is too large       */
        break;                                    /* Return that we cannot coarsen more */

      finevertbst = finevertnum;                  /* No matching vertex found yet */
      fineedlobst = 0;
      finevelodlt = coarvelomax - ((finegrafptr->velotax != NULL) ? finegrafptr->velotax[finevertnum] : 1);

      for (fineedgenum = fineverttax[finevertnum]; /* For all adjacent vertices */
           fineedgenum < finevendtax[finevertnum]; fineedgenum ++) {
        Gnum                finevertend;
        Gnum                finecoarval;

        finevertend = fineedgetax[fineedgenum];
        finecoarval = finecoartax[finevertend];

        if (finecoarval < 0) {                    /* If vertex not matched */
          Gnum                fineedloval;

          fineedloval = fineedlotax[fineedgenum];
          if (finecoartax[finevertend] == ~0) {   /* If vertex not enqueued */
            finequeutab[finequeuheadval ++] = finevertend; /* Enqueue it    */
            finecoartax[finevertend] = -2;
          }
          if (((finevelotax == NULL) ||            /* And does not create overloads */
               (finevelodlt >= finevelotax[finevertend])) &&
              (fineedloval > fineedlobst)) {
            finevertbst = finevertend;            /* Get matching vertex */
            fineedlobst = fineedloval;
          }
        }
      }

      finecoartax[finevertnum] = finevertbst;     /* Match both vertices */
      finecoartax[finevertbst] = finevertnum;
      coarvertnum ++;                             /* One more coarse vertex created */
    }
    else {                                        /* Search for other connected component */
      Gnum                finevertbst;

      for ( ; finecoartax[finepermnum] >= 0; finepermnum ++) { /* Scan vertices in ascending order */
#ifdef SCOTCH_DEBUG_GRAPH2
        if (finepermnum >= finegrafptr->vertnnd) {
          errorPrint ("graphCoarsenMatchCh: internal error (1)");
          memFree    (finequeutab);
          return     (finegrafptr->vertnbr);      /* Coarsening aborted */
        }
#endif /* SCOTCH_DEBUG_GRAPH2 */
      }
#ifdef SCOTCH_DEBUG_GRAPH2
      if (finecoartax[finepermnum] != ~0) {
        errorPrint ("graphCoarsenMatchCh: internal error (2)");
        memFree    (finequeutab);
        return     (finegrafptr->vertnbr);        /* Coarsening aborted */
      }
#endif /* SCOTCH_DEBUG_GRAPH2 */
      finevertnum = finepermnum ++;               /* Start from found vertex */

      if (fineverttax[finevertnum] != finevendtax[finevertnum]) { /* If vertex not isolated */
        finequeutab[finequeuheadval ++] = finevertnum; /* Enqueue it for normal processing  */
        continue;                                 /* Skip to main loop to process it        */
      }

      finequeuheadval = ++ finequeutailval;       /* One more vertex enqueued-edqueued */

      if (coarvertnum >= coarvertmax)             /* If coarse graph is too large       */
        break;                                    /* Return that we cannot coarsen more */

      if (finequeutailval >= finegrafptr->vertnbr) /* If isolated vertex is last available vertex */
        finevertbst = finevertnum;
      else {
        for ( ; finecoartax[finepermnum] >= 0; finepermnum ++) {
#ifdef SCOTCH_DEBUG_GRAPH2
          if (finepermnum >= finegrafptr->vertnnd) {
            errorPrint ("graphCoarsenMatchCh: internal error (3)");
            memFree    (finequeutab);
            return     (finegrafptr->vertnbr);    /* Coarsening aborted */
          }
#endif /* SCOTCH_DEBUG_GRAPH2 */
        }
#ifdef SCOTCH_DEBUG_GRAPH2
        if (finecoartax[finepermnum] != ~0) {
          errorPrint ("graphCoarsenMatchCh: internal error (4)");
          memFree    (finequeutab);
          return     (finegrafptr->vertnbr);      /* Coarsening aborted */
        }
#endif /* SCOTCH_DEBUG_GRAPH2 */
        finevertbst     = finepermnum ++;         /* Get found vertex                  */
        finequeuheadval = ++ finequeutailval;     /* One more vertex enqueued-edqueued */
      }

      finecoartax[finevertnum] = finevertbst;     /* Match both vertices */
      finecoartax[finevertbst] = finevertnum;
      coarvertnum ++;                             /* One more coarse vertex created */
    }
  }

  memFree (finequeutab);

  return (coarvertnum);                           /* Return number of coarse vertices */
}