1 /* Copyright 2010,2014 IPB, Universite de Bordeaux, INRIA & CNRS
2 **
3 ** This file is part of the Scotch software package for static mapping,
4 ** graph partitioning and sparse matrix ordering.
5 **
6 ** This software is governed by the CeCILL-C license under French law
7 ** and abiding by the rules of distribution of free software. You can
8 ** use, modify and/or redistribute the software under the terms of the
9 ** CeCILL-C license as circulated by CEA, CNRS and INRIA at the following
10 ** URL: "http://www.cecill.info".
11 **
12 ** As a counterpart to the access to the source code and rights to copy,
13 ** modify and redistribute granted by the license, users are provided
14 ** only with a limited warranty and the software's author, the holder of
15 ** the economic rights, and the successive licensors have only limited
16 ** liability.
17 **
18 ** In this respect, the user's attention is drawn to the risks associated
19 ** with loading, using, modifying and/or developing or reproducing the
20 ** software by the user in light of its specific status of free software,
21 ** that may mean that it is complicated to manipulate, and that also
22 ** therefore means that it is reserved for developers and experienced
23 ** professionals having in-depth computer knowledge. Users are therefore
24 ** encouraged to load and test the software's suitability as regards
25 ** their requirements in conditions enabling the security of their
26 ** systems and/or data to be ensured and, more generally, to use and
27 ** operate it in the same conditions as regards security.
28 **
29 ** The fact that you are presently reading this means that you have had
30 ** knowledge of the CeCILL-C license and that you accept its terms.
31 */
32 /************************************************************/
33 /**                                                        **/
34 /**   NAME       : wgraph_part_rb.c                        **/
35 /**                                                        **/
36 /**   AUTHOR     : Jun-Ho HER (v6.0)                       **/
37 /**                Francois PELLEGRINI                     **/
38 /**                                                        **/
39 /**   FUNCTION   : This module performs the vertex overla- **/
40 /**                pped graph partitioning based on recur- **/
41 /**                sive bipartitioning approach.           **/
42 /**                                                        **/
43 /**   DATES      : # Version 6.0  : from : 16 mar 2010     **/
44 /**                                 to     12 aug 2014     **/
45 /**                                                        **/
46 /**   NOTES      : # This code derives from the code of    **/
47 /**                  kgraph_map_rb_part.c for the vertex   **/
48 /**                  overlapped graph partitioning.        **/
49 /**                                                        **/
50 /************************************************************/
51 
52 /*
53 **  The defines and includes.
54 */
55 
56 #define WGRAPH_PART_RB
57 
58 #include "module.h"
59 #include "common.h"
60 #include "parser.h"
61 #include "graph.h"
62 #include "arch.h"
63 #include "arch_cmplt.h"
64 #include "mapping.h"
65 #include "vgraph.h"
66 #include "vgraph_separate_st.h"
67 #include "vgraph_separate_zr.h"
68 #include "wgraph.h"
69 #include "wgraph_part_rb.h"
70 #include "scotch.h"
71 
72 /*
73 **  The static variables.
74 */
75 
76 static const Gnum           wgraphpartrbloadone = 1;
77 
78 /********************************************/
79 /*                                          */
80 /* This is the entry point for the vertex   */
81 /* overlapped graph partitioning based on   */
82 /* recursive bipartitioning approach.       */
83 /*                                          */
84 /********************************************/
85 
86 /* This routine runs recursive
87 ** bipartitioning approach.
88 ** It returns:
89 ** - 0   : on success.
90 ** - !0  : on error.
91 */
92 
93 static
94 int
wgraphPartRb3(const Graph * restrict const orggrafptr,const GraphPart * restrict const orgparttax,const GraphPart indpartval,const int domnnum,Mapping * restrict const mappptr)95 wgraphPartRb3 (
96 const Graph * restrict const     orggrafptr,    /* Graph to induce and bipartition         */
97 const GraphPart * restrict const orgparttax,    /* Part array of original graph            */
98 const GraphPart                  indpartval,    /* Part of graph to consider               */
99 const int                        domnnum,       /* Index of domain onto which map the part */
100 Mapping * restrict const         mappptr)       /* Final mapping                           */
101 {
102   Gnum               vertnum;
103 
104   if (orgparttax == NULL) {                       /* If graph is full graph */
105 #ifdef SCOTCH_DEBUG_WGRAPH2
106     if ((orggrafptr->vnumtax != NULL) || (domnnum != 0)) {
107       errorPrint ("wgraphPartRb3: internal error");
108       return     (1);
109     }
110 #endif /* SCOTCH_DEBUG_WGRAPH2 */
111     memSet (mappptr->parttax + mappptr->grafptr->baseval, 0, orggrafptr->vertnbr * sizeof (ArchDomNum));
112   }
113   else {                                          /* Graph to consider is a subgraph of the original graph       */
114     if (orggrafptr->vnumtax == NULL) {            /* If original graph is not itself a subgraph                  */
115       for (vertnum = orggrafptr->baseval; vertnum < orggrafptr->vertnnd; vertnum ++) { /* For all graph vertices */
116         if (orgparttax[vertnum] == indpartval)    /* If vertex belongs to the right part                         */
117           mappptr->parttax[vertnum] = domnnum;
118       }
119     }
120     else {
121       for (vertnum = orggrafptr->baseval; vertnum < orggrafptr->vertnnd; vertnum ++) { /* For all graph vertices */
122         if (orgparttax[vertnum] == indpartval)    /* If vertex belongs to the right part                         */
123           mappptr->parttax[orggrafptr->vnumtax[vertnum]] = domnnum;
124       }
125     }
126   }
127 
128   return (0);
129 }
130 
131 static
132 int
wgraphPartRb2(WgraphPartRbData * restrict const dataptr,Graph * restrict const orggrafptr,const GraphPart * restrict const orgparttax,const GraphPart indpartval,const int indvertnbr,const int domnnum)133 wgraphPartRb2 (
134 WgraphPartRbData * restrict const dataptr,        /* Top-level graph and partition data       */
135 Graph * restrict const            orggrafptr,     /* Graph to induce and bipartition          */
136 const GraphPart * restrict const  orgparttax,     /* Part array of original graph to consider */
137 const GraphPart                   indpartval,     /* Part of graph to consider                */
138 const int                         indvertnbr,     /* Number of vertices in part or in graph   */
139 const int                         domnnum)        /* Index of domain onto which map the part  */
140 {
141   Graph                  indgrafdat;
142   Graph *                indgrafptr;
143   Vgraph                 actgrafdat;
144   Anum                   domnsubidx;
145   Anum                   domnsubdlt;
146   ArchDom                domnsubtab[2];           /* Target subdomains              */
147   Anum                   domnsubnum[2];           /* Index of subdomains in mapping */
148   Gnum                   grafsubsiz[2];
149   Gnum                   vertnum;
150   Mapping * restrict     mappptr;
151   int                    i;
152   int                    o;
153 
154   mappptr = &dataptr->mappdat;
155   o = archDomBipart (mappptr->archptr, &mappptr->domntab[domnnum], &domnsubtab[0], &domnsubtab[1]);
156 
157   switch (o) {
158     case 1 :                                      /* If target domain is terminal */
159       return (wgraphPartRb3 (orggrafptr, orgparttax, indpartval, domnnum, mappptr)); /* Update mapping and return */
160     case 2 :                                      /* On error */
161       errorPrint ("wgraphPartRb2: cannot bipartition domain");
162       return     (1);
163   }
164 
165   indgrafptr = orggrafptr;                        /* Assume we will work on the original graph */
166   if (orgparttax != NULL) {                       /* If not the case, build induced subgraph   */
167     indgrafptr = &indgrafdat;
168     if (graphInducePart (orggrafptr, orgparttax, indvertnbr, indpartval, &indgrafdat) != 0) {
169       errorPrint ("wgraphPartRb2: cannot induce graph");
170       return     (1);
171     }
172   }
173 
174   actgrafdat.s = *indgrafptr;
175   actgrafdat.s.vlbltax = NULL;
176   if ((actgrafdat.frontab = (Gnum *) memAlloc (actgrafdat.s.vertnbr * sizeof (Gnum))) == NULL) {
177     errorPrint ("wgraphPartRb2: out of memory (1)");
178     return     (1);
179   }
180   if ((actgrafdat.parttax = (GraphPart *) memAlloc (actgrafdat.s.vertnbr * sizeof (GraphPart))) == NULL) {
181     errorPrint ("wgraphPartRb2: out of memory (2)");
182     memFree    (actgrafdat.frontab);
183     return     (1);
184   }
185   actgrafdat.parttax -= actgrafdat.s.baseval;
186   vgraphZero (&actgrafdat);                       /* Create active graph */
187   if (vgraphSeparateSt (&actgrafdat, dataptr->stratptr) != 0) { /* Perform bipartitioning */
188     errorPrint ("wgraphPartRb2: cannot bipartition graph");
189     vgraphExit (&actgrafdat);
190     return     (1);
191   }
192 
193   if (actgrafdat.s.vnumtax == NULL) {             /* If the active graph is not itself a subgraph                  */
194     for (vertnum = actgrafdat.s.baseval; vertnum < actgrafdat.s.vertnnd; vertnum ++) { /* For all graph vertices */
195       if (actgrafdat.parttax[vertnum] == 2) {     /* If vertex belongs to frontier */
196 	mappptr->parttax[vertnum]   = -1;
197 	actgrafdat.parttax[vertnum] = 3;
198       }
199     }
200   }
201   else {
202     for (vertnum = actgrafdat.s.baseval; vertnum < actgrafdat.s.vertnnd; vertnum ++) { /* For all graph vertices */
203       if (actgrafdat.parttax[vertnum] == 2) {     /* If vertex belongs to frontier */
204 	mappptr->parttax[actgrafdat.s.vnumtax[vertnum]]= -1;
205 	actgrafdat.parttax[vertnum] = 3;
206       }
207     }
208   }
209 
210 
211   domnsubdlt = mappptr->domnnbr - domnnum;        /* Increment in domain number */
212   domnsubidx = domnnum - domnsubdlt;              /* Place where to insert subdomain */
213   mappptr->domnnbr --;                            /* One less subdomain as for now   */
214   grafsubsiz[0] = actgrafdat.compsize[0];
215   grafsubsiz[1] = actgrafdat.compsize[1];
216 
217   o = 0;
218   for (i = 1; i >= 0; i --) {                     /* For all subparts             */
219     if (grafsubsiz[i] <= 0)                       /* If subpart is empty, skip it */
220       continue;
221     mappptr->domnnbr ++;                          /* One more subdomain to account for */
222     domnsubidx   += domnsubdlt;                   /* Compute location of subdomain */
223     domnsubnum[i] = domnsubidx;                   /* Record it before recursion    */
224     mappptr->domntab[domnsubidx] = domnsubtab[i]; /* Write it at this place        */
225   }
226 
227   if (o == 0) {
228     for (i = 1; i >= 0; i --) {                   /* For all subparts             */
229       if (grafsubsiz[i] <= 0)                     /* If subpart is empty, skip it */
230         continue;
231 
232       if ((o = wgraphPartRb2 (dataptr, indgrafptr, actgrafdat.parttax, (GraphPart) i, grafsubsiz[i], domnsubnum[i])) != 0)
233         return (1);                               /* If problem in recursion, stop */
234     }
235   }
236 
237   memFree (actgrafdat.frontab);                   /* Frontier array of bipartitioning graph is no longer necessary      */
238   memFree (actgrafdat.parttax + actgrafdat.s.baseval); /* Frontier array of bipartitioning graph is no longer necessary */
239   if (indgrafptr == &indgrafdat)                  /* If an induced subgraph had been created                            */
240     graphExit (indgrafptr);                       /* Free it                                                            */
241 
242   return (o);
243 }
244 
245 int
wgraphPartRb(Wgraph * restrict const grafptr,const WgraphPartRbParam * restrict const paraptr)246 wgraphPartRb (
247 Wgraph * restrict const                   grafptr,
248 const WgraphPartRbParam * restrict const  paraptr)
249 {
250   const Anum * restrict         parttax;
251   Anum				partval;
252   Gnum                          vertnum;
253   Gnum                          velomsk;
254   const Gnum * restrict         velobax;              /* Data for handling of optional arrays  */
255   Gnum * restrict               frontab;
256   Gnum                          fronnbr;
257   Gnum                          fronload;
258   Gnum * restrict               compload;
259   Gnum * restrict               compsize;
260   WgraphPartRbData              datadat;
261   Arch                          archdat;
262   WgraphPartList * restrict     listtab;
263 
264   const Gnum * restrict const   verttax = grafptr->s.verttax;
265   const Gnum * restrict const   vendtax = grafptr->s.vendtax;
266   const Gnum * restrict const   edgetax = grafptr->s.edgetax;
267 
268   if ((listtab = (WgraphPartList *) memAlloc ((grafptr->partnbr + 1) * sizeof (WgraphPartList))) == NULL) { /* TRICK: "+1" to create slot for a "-1" index */
269     errorPrint ("wgraphPartRb: out of memory (1)");
270     return     (1);
271   }
272   listtab ++;                                     /* TRICK: Trim array so that listtab[-1] is valid */
273   memSet (listtab, ~0, grafptr->partnbr * sizeof (WgraphPartList)); /* Set vertex indices to ~0     */
274 
275   datadat.grafptr  = &grafptr->s;
276   datadat.frontab  = grafptr->frontab;            /* Re-use frontier array */
277   datadat.fronnbr  = 0;
278   datadat.stratptr = paraptr->stratptr;
279   datadat.mappdat.grafptr = &grafptr->s;
280   datadat.mappdat.parttax = grafptr->parttax;     /* Re-use part array */
281   datadat.mappdat.domnmax = grafptr->partnbr + 1;
282   datadat.mappdat.domnnbr = 1;
283 
284   SCOTCH_archCmplt ((SCOTCH_Arch *) &archdat, grafptr->partnbr); /* Create a complete graph architecture */
285   datadat.mappdat.archptr = &archdat;
286 
287   archDomFrst (datadat.mappdat.archptr, &datadat.mappdat.domnorg); /* Get first domain of architecture */
288   if ((datadat.mappdat.domntab = (ArchDom *) memAlloc ((grafptr->partnbr + 2) * sizeof (ArchDom))) == NULL) {
289     errorPrint ("wgraphPartRb: out of memory (2)");
290     memFree    (listtab - 1);                     /* TRICK: free array using its real beginning */
291     return     (1);
292   }
293   datadat.mappdat.domntab[0] = datadat.mappdat.domnorg; /* Set first domain */
294 
295   if (wgraphPartRb2 (&datadat, &grafptr->s, NULL, 0, grafptr->s.vertnbr, 0) != 0) {
296     errorPrint ("wgraphPartRb: internal error (1)");
297     return     (1);
298   }
299 
300   if (grafptr->s.velotax == NULL) {               /* Set accesses to optional arrays             */
301     velobax = &wgraphpartrbloadone;               /* In case vertices not weighted (least often) */
302     velomsk = 0;
303   }
304   else {
305     velobax = grafptr->s.velotax;
306     velomsk = ~((Gnum) 0);
307   }
308 
309   compload = grafptr->compload;
310   compsize = grafptr->compsize;
311   memSet (compload, 0, grafptr->partnbr * sizeof (Gnum));
312   memSet (compsize, 0, grafptr->partnbr * sizeof (Gnum));
313 
314   parttax  = grafptr->parttax;
315   frontab  = grafptr->frontab;
316   fronnbr  =
317   fronload = 0;
318   for (vertnum = grafptr->s.baseval; vertnum < grafptr->s.vertnnd; vertnum ++) {
319     Gnum                partval;
320 
321     partval = parttax[vertnum];
322     if (partval >= 0) {
323       compload[partval] += velobax[vertnum & velomsk];
324       compsize[partval] ++;
325     }
326     else {                                        /* Vertex is in separator       */
327       Gnum                listidx;                /* Index of first neighbor part */
328       Gnum                edgenum;
329       Gnum                veloval;
330 
331       frontab[fronnbr ++] = vertnum;              /* Add vertex to frontier */
332       fronload           += velobax[vertnum & velomsk];
333 
334       listidx = -1;                               /* No neighboring parts recorded yet          */
335       listtab[-1].vertnum = vertnum;              /* Separator neighbors will not be considered */
336       for (edgenum = verttax[vertnum];
337            edgenum < vendtax[vertnum]; edgenum ++) { /* Compute gain */
338         Gnum                vertend;
339         Gnum                partend;
340 
341         vertend = edgetax[edgenum];
342         partend = parttax[vertend];
343         if (listtab[partend].vertnum != vertnum) { /* If part not yet considered  */
344           listtab[partend].vertnum = vertnum;     /* Link it in list of neighbors */
345           listtab[partend].nextidx = listidx;
346           listidx = partend;
347         }
348       }
349 
350       veloval = velobax[vertnum & velomsk];
351 
352       while (listidx != -1) {                     /* For all neighboring parts found      */
353         compload[listidx] += veloval;             /* Add load of separator vertex to part */
354         compsize[listidx] ++;
355         listidx = listtab[listidx].nextidx;
356       }
357     }
358   }
359   grafptr->fronnbr  = fronnbr;
360   grafptr->fronload = fronload;
361 
362 #if 0 /* TODO REMOVE */
363   for (partval = 0; partval < grafptr->partnbr; partval ++)
364     printf("\033[0;33mcompload[%d] %d %d\033[0m\n", partval, grafptr->compload[partval], grafptr->compsize[partval]);
365 #endif
366 
367   memFree (datadat.mappdat.domntab);              /* Free only newly allocated array of mapping */
368   memFree (listtab - 1);                          /* TRICK: free array using its real beginning */
369 
370 #ifdef SCOTCH_DEBUG_WGRAPH2
371   if (wgraphCheck (grafptr) != 0) {
372     errorPrint ("wgraphPartRb: inconsistent graph data");
373     return     (1);
374   }
375 #endif /* SCOTCH_DEBUG_WGRAPH2 */
376 
377   return (0);
378 }
379