1 /*****************************************************************************
2 /
3 / SPACE (SPArse Cholesky Elimination) Library: nestdiss.c
4 /
5 / author        J"urgen Schulze, University of Paderborn
6 / created       00dec29
7 /
8 / This file contains functions dealing with the rec. nested dissection object
9 /
10 ******************************************************************************
11 
12 Data type:  struct nestdiss
13               graph_t *G;               pointer to original graph
14               int     *map;             maps nodes of G to constructed subgraph
15               int     depth;            depth in nested dissection tree
16               int     nvint;            number of vertices in subgraph
17               int     *intvertex;       internal vertices of subgraph
18               int     *intcolor;        color of vertices in intvertex
19               int     cwght[3];         weights of bisection
20               struct nestdiss *parent;  pointer to parent nd node
21               struct nestdiss *childB;  pointer to black descendant nd node
22               struct nestdiss *childW;  pointer to white descendand nd node
23 Comments:
24   o Structure used to build the nested dissection tree. Vector intvertex
25     holds the vertices of the subgraph to be partitioned. Once a separator
26     has been computed, the coloring of vertex u = intvertex[i] is stored in
27     vector intcolor[i] and the partition weights are stored in cwght[GRAY],
28     cwght[BLACK], and cwght[WHITE].
29   o Structure does not own graph object G => it will not be freed
30     Note: G is the original graph
31   o Structure does not own map array => it will not be freed
32     Note: map is a "global" array that is used when constructing the subgraph
33           induced by the vertices in intvertex. The array maps the vertices
34           of the original graph G to the vertices of the subgraph.
35 Methods in lib/nestdiss.c:
36 - nd = newNDnode(graph_t *G, int *map, int nvint);
37     o Initial: depth = 0, cwght[GRAY] = cwght[BLACK] = cwght[WHITE] = 0,
38                and parent = childB = childW = NULL;
39 - void freeNDnode(nestdiss_t *nd);
40 - ndroot = setupNDroot(graph_t *G, int *map);
41     o sets up the root of the nested dissection tree; the function first
42       calls newNDnode to allocate memory for ndroot and, then, sets
43       intvertex[i] = i for all 0 <= i < G->nvtx
44 - void splitNDnode(nestdiss_t *nd, options_t *options, timings_t *cpus);
45     o constructs the subgraph induced by nd->intvertex and computes a
46       bisection for it by calling constructSeparator and smoothSeparator.
47       Then, the nd object is splitted in a black one that holds the black
48       partition and a white one that holds the white partition.
49     o used options: (see constructSeparator and smoothSeparator)
50        OPTION_MSGLVL, OPTION_NODE_SELECTION3
51     o returned timings: (also see constructSeparator)
52        TIME_INITDOMDEC, TIME_COARSEDOMDEC, TIME_INITSEP, TIME_REFINESEP
53        TIME_MULTILEVEL, TIME_SMOOTH
54 - void buildNDtree(nestdiss_t *ndroot, options_t *options, timings_t *cpus);
55     o builds the nested dissection tree under root ndroot, i.e. it applies
56       the nested dissection process to the (sub)graph induced by
57       ndroot->intvertex by iteratively calling function splitNDnode.
58     o used options: (also see splitNDnode)
59        OPTION_DOMAIN_SIZE, OPTION_MSGLVL, OPTION_NODE_SELECTION3
60     o returned timings: (see splitNDnode)
61        TIME_INITDOMDEC, TIME_COARSEDOMDEC, TIME_INITSEP, TIME_REFINESEP
62        TIME_MULTILEVEL, TIME_SMOOTH
63 - void freeNDtree(nestdiss_t *ndroot);
64     o removes the nested dissection tree under root ndroot
65       Note: ndroot is not freed
66 
67 ******************************************************************************/
68 
69 #include <space.h>
70 
71 
72 /*****************************************************************************
73 ******************************************************************************/
74 nestdiss_t*
newNDnode(graph_t * G,PORD_INT * map,PORD_INT nvint)75 newNDnode(graph_t *G, PORD_INT *map, PORD_INT nvint)
76 { nestdiss_t *nd;
77 
78   mymalloc(nd, 1, nestdiss_t);
79   mymalloc(nd->intvertex, nvint, PORD_INT);
80   mymalloc(nd->intcolor, nvint, PORD_INT);
81 
82   nd->G = G;
83   nd->map = map;
84   nd->depth = 0;
85   nd->nvint = nvint;
86   nd->cwght[GRAY] = nd->cwght[BLACK] = nd->cwght[WHITE] = 0;
87   nd->parent = nd->childB = nd->childW = NULL;
88 
89   return(nd);
90 }
91 
92 
93 /*****************************************************************************
94 ******************************************************************************/
95 void
freeNDnode(nestdiss_t * nd)96 freeNDnode(nestdiss_t *nd)
97 {
98   free(nd->intvertex);
99   free(nd->intcolor);
100   free(nd);
101 }
102 
103 
104 /*****************************************************************************
105 ******************************************************************************/
106 nestdiss_t*
setupNDroot(graph_t * G,PORD_INT * map)107 setupNDroot(graph_t *G, PORD_INT *map)
108 { nestdiss_t *ndroot;
109   PORD_INT        *intvertex, nvtx, i;
110 
111   nvtx = G->nvtx;
112   ndroot = newNDnode(G, map, nvtx);
113   intvertex = ndroot->intvertex;
114 
115   for (i = 0; i < nvtx; i++)
116     intvertex[i] = i;
117 
118   return(ndroot);
119 }
120 
121 
122 /*****************************************************************************
123 ******************************************************************************/
124 void
splitNDnode(nestdiss_t * nd,options_t * options,timings_t * cpus)125 splitNDnode(nestdiss_t *nd, options_t *options, timings_t *cpus)
126 { nestdiss_t *b_nd, *w_nd;
127   graph_t    *Gsub;
128   gbisect_t  *Gbisect;
129   PORD_INT        *map, *intvertex, *intcolor, *b_intvertex, *w_intvertex;
130   PORD_INT        nvint, b_nvint, w_nvint, u, i;
131 
132   map = nd->map;
133   nvint = nd->nvint;
134   intvertex = nd->intvertex;
135   intcolor = nd->intcolor;
136 
137   /* -------------------------------------------------------------
138      extract the subgraph for which a bisection has to be computed
139      ------------------------------------------------------------- */
140   if (nd->G->nvtx == nd->nvint)
141    { Gsub = nd->G;                    /* a hack to save time and space */
142      for (u = 0; u < nd->nvint; u++)  /* but do not forget the map vector */
143        map[u] = u;
144    }
145   else
146     Gsub = setupSubgraph(nd->G, intvertex, nvint, map);
147   Gbisect = newGbisect(Gsub);
148 
149   /* ---------------------------------
150      compute the bisection for Gbisect
151      --------------------------------- */
152   pord_starttimer(cpus[TIME_MULTILEVEL]);
153   constructSeparator(Gbisect, options, cpus);
154   pord_stoptimer(cpus[TIME_MULTILEVEL]);
155 
156   pord_starttimer(cpus[TIME_SMOOTH]);
157   if (Gbisect->cwght[GRAY] > 0)
158     smoothSeparator(Gbisect, options);
159   pord_stoptimer(cpus[TIME_SMOOTH]);
160 
161   /* ----------------------------------------
162      copy the bisection back to the nd object
163      ---------------------------------------- */
164   b_nvint = w_nvint = 0;
165   nd->cwght[GRAY] = Gbisect->cwght[GRAY];
166   nd->cwght[BLACK] = Gbisect->cwght[BLACK];
167   nd->cwght[WHITE] = Gbisect->cwght[WHITE];
168   for (i = 0; i < nvint; i++)
169    { u = intvertex[i];
170      intcolor[i] = Gbisect->color[map[u]];
171      switch(intcolor[i])
172       { case GRAY: break;
173         case BLACK: b_nvint++; break;
174         case WHITE: w_nvint++; break;
175         default:
176           fprintf(stderr, "\nError in function splitNDnode\n"
177                "  node %d has unrecognized color %d\n", u, intcolor[i]);
178           quit();
179       }
180    }
181 
182   /* ------------------------------------------------------
183      and now split the nd object according to the bisection
184      ------------------------------------------------------ */
185   b_nd = newNDnode(nd->G, map, b_nvint);
186   b_intvertex = b_nd->intvertex;
187   w_nd = newNDnode(nd->G, map, w_nvint);
188   w_intvertex = w_nd->intvertex;
189 
190   b_nvint = w_nvint = 0;
191   for (i = 0; i < nvint; i++)
192    { u = intvertex[i];
193      if (intcolor[i] == BLACK) b_intvertex[b_nvint++] = u;
194      if (intcolor[i] == WHITE) w_intvertex[w_nvint++] = u;
195    }
196   nd->childB = b_nd; b_nd->parent = nd;
197   nd->childW = w_nd; w_nd->parent = nd;
198   b_nd->depth = nd->depth + 1;
199   w_nd->depth = nd->depth + 1;
200 
201   /* -----------------
202      free the subgraph
203      ----------------- */
204   if (Gsub != nd->G)
205     freeGraph(Gsub);
206   freeGbisect(Gbisect);
207 }
208 
209 
210 /*****************************************************************************
211 ******************************************************************************/
212 void
buildNDtree(nestdiss_t * ndroot,options_t * options,timings_t * cpus)213 buildNDtree(nestdiss_t *ndroot, options_t *options, timings_t *cpus)
214 { nestdiss_t *nd;
215   nestdiss_t *queue[2*MAX_SEPS+1];
216   PORD_INT        maxseps, seps, domainsize, qhead, qtail;
217 
218   maxseps = MAX_SEPS;
219   domainsize = options[OPTION_DOMAIN_SIZE];
220   if (domainsize == 1) maxseps = DEFAULT_SEPS;   /* secret switch */
221 
222   /* --------------------------------------------------
223      build the nested dissection tree under root ndroot
224      -------------------------------------------------- */
225   queue[0] = ndroot;
226   qhead = 0; qtail = 1; seps = 0;
227   while ((qhead != qtail) && (seps < maxseps))
228    { seps++;
229      nd = queue[qhead++];
230 
231      splitNDnode(nd, options, cpus);
232      if ((nd->childB == NULL) || (nd->childW == NULL))
233       { fprintf(stderr, "\nError in function buildNDtree\n"
234              "  recursive nested dissection process failed\n");
235         quit();
236       }
237 
238      if (options[OPTION_MSGLVL] > 1)
239        printf("%4d. S %6d, B %6d, W %6d [bal %4.2f, rel %6.4f, cost %7.2f]\n",
240              seps, nd->cwght[GRAY], nd->cwght[BLACK], nd->cwght[WHITE],
241              (FLOAT)min(nd->cwght[BLACK], nd->cwght[WHITE])
242                      / max(nd->cwght[BLACK], nd->cwght[WHITE]),
243              (FLOAT)nd->cwght[GRAY]
244                      / (nd->cwght[GRAY] + nd->cwght[BLACK] + nd->cwght[WHITE]),
245              F(nd->cwght[GRAY], nd->cwght[BLACK], nd->cwght[WHITE]));
246 
247      if ((nd->childB->nvint > MIN_NODES)
248         && ((nd->cwght[BLACK] > domainsize) || (qtail < DEFAULT_SEPS)))
249        queue[qtail++] = nd->childB;
250      if ((nd->childW->nvint > MIN_NODES)
251         && ((nd->cwght[WHITE] > domainsize) || (qtail < DEFAULT_SEPS)))
252        queue[qtail++] = nd->childW;
253    }
254 }
255 
256 
257 /*****************************************************************************
258 ******************************************************************************/
259 void
freeNDtree(nestdiss_t * ndroot)260 freeNDtree(nestdiss_t *ndroot)
261 { nestdiss_t *nd, *parent;
262 
263   /* ------------------------------------------------------
264      to remove the nested dissection tree under root ndroot
265      visit the nodes in post-order
266      ------------------------------------------------------ */
267   for (nd = ndroot; nd->childB != NULL; nd = nd->childB);
268   while (nd != ndroot)
269    { parent = nd->parent;
270      if ((parent == NULL) || (parent->childB == NULL)
271         || (parent->childW == NULL))
272        { fprintf(stderr, "\nError in function removeNDtree\n"
273               "  nested dissection tree corrupted\n");
274          quit();
275        }
276      if (parent->childB == nd)  /* left subtree of parent visited */
277       { freeNDnode(nd);         /* free root of left subtree and goto right */
278         for (nd = parent->childW; nd->childB != NULL; nd = nd->childB);
279       }
280      else                       /* right subtree of parent visited */
281       { freeNDnode(nd);         /* free root of right subtree and goto parent */
282         nd = parent;
283       }
284    }
285 }
286