1 | /* |
2 | * metis_util.hpp |
3 | * |
4 | * Created on: Nov 21, 2014 |
5 | * Author: Pietro Incardona |
6 | */ |
7 | |
8 | #ifndef METIS_UTIL_HPP |
9 | #define METIS_UTIL_HPP |
10 | |
11 | #include <iostream> |
12 | #include "metis.h" |
13 | #include "SubdomainGraphNodes.hpp" |
14 | #include "VTKWriter/VTKWriter.hpp" |
15 | |
16 | /*! \brief Metis graph structure |
17 | * |
18 | * Metis graph structure |
19 | * |
20 | */ |
21 | struct Metis_graph |
22 | { |
23 | //! The number of vertices in the graph |
24 | idx_t * nvtxs; |
25 | |
26 | //! number of balancing constrains |
27 | //! more practical, are the number of weights for each vertex |
28 | //! PS even we you specify vwgt == NULL ncon must be set at leat to |
29 | //! one |
30 | idx_t * ncon; |
31 | |
32 | //! For each vertex it store the adjacency lost start for the vertex i |
33 | idx_t * xadj; |
34 | |
35 | //! For each vertex it store a list of all neighborhood vertex |
36 | idx_t * adjncy; |
37 | |
38 | //! Array that store the weight for each vertex |
39 | idx_t * vwgt; |
40 | |
41 | //! Array of the vertex size, basically is the total communication amount |
42 | idx_t * vsize; |
43 | //! The weight of the edge |
44 | idx_t * adjwgt; |
45 | //! number of part to partition the graph |
46 | idx_t * nparts; |
47 | //! Desired weight for each partition (one for each constrain) |
48 | real_t * tpwgts; |
49 | //! For each partition load imbalance tollerated |
50 | real_t * ubvec; |
51 | //! Additional option for the graph partitioning |
52 | idx_t * options; |
53 | //! return the total comunication cost for each partition |
54 | idx_t * objval; |
55 | //! Is a output vector containing the partition for each vertex |
56 | idx_t * part; |
57 | }; |
58 | |
59 | //! Balance communication and computation |
60 | #define BALANCE_CC 1 |
61 | //! Balance communication computation and memory |
62 | #define BALANCE_CCM 2 |
63 | //! Balance computation and comunication and others |
64 | #define BALANCE_CC_O(c) c+1 |
65 | |
66 | /*! \brief Helper class to define Metis graph |
67 | * |
68 | * \tparam graph structure that store the graph |
69 | * |
70 | */ |
71 | |
72 | template<typename Graph> |
73 | class Metis |
74 | { |
75 | //! Graph in metis reppresentation |
76 | Metis_graph Mg; |
77 | |
78 | //! Original graph |
79 | Graph & g; |
80 | |
81 | //! indicate how many time decompose/refine/re-decompose has been called |
82 | size_t n_dec; |
83 | |
84 | //! Distribution tolerance |
85 | real_t dist_tol = 1.05; |
86 | |
87 | /*! \brief Construct Adjacency list |
88 | * |
89 | * \param g Graph |
90 | * |
91 | */ |
92 | void constructAdjList(Graph & g) |
93 | { |
94 | // create xadj and adjlist |
95 | |
96 | // create xadj, adjlist, vwgt, adjwgt and vsize |
97 | Mg.xadj = new idx_t[g.getNVertex() + 1]; |
98 | Mg.adjncy = new idx_t[g.getNEdge()]; |
99 | |
100 | //! starting point in the adjacency list |
101 | size_t prev = 0; |
102 | |
103 | // actual position |
104 | size_t id = 0; |
105 | |
106 | // for each vertex calculate the position of the starting point in the adjacency list |
107 | for (size_t i = 0; i < g.getNVertex(); i++) |
108 | { |
109 | // Calculate the starting point in the adjacency list |
110 | Mg.xadj[id] = prev; |
111 | |
112 | // Create the adjacency list |
113 | for (size_t s = 0; s < g.getNChilds(i); s++) |
114 | { |
115 | Mg.adjncy[prev + s] = g.getChild(i, s); |
116 | } |
117 | |
118 | // update the position for the next vertex |
119 | prev += g.getNChilds(i); |
120 | |
121 | id++; |
122 | } |
123 | |
124 | // Fill the last point |
125 | Mg.xadj[id] = prev; |
126 | } |
127 | |
128 | /*! \brief Construct Adjacency list |
129 | * |
130 | * \param g Graph |
131 | * |
132 | */ |
133 | void constructAdjListWithWeights(Graph & g) |
134 | { |
135 | // create xadj, adjlist, vwgt, adjwgt and vsize |
136 | if (Mg.xadj != NULL) |
137 | {delete [] Mg.xadj;} |
138 | if (Mg.adjncy != NULL) |
139 | {delete [] Mg.adjncy;} |
140 | if (Mg.vwgt != NULL) |
141 | {delete [] Mg.vwgt;} |
142 | if (Mg.adjwgt != NULL) |
143 | {delete [] Mg.adjwgt;} |
144 | if (Mg.vsize != NULL) |
145 | {delete [] Mg.vsize;} |
146 | Mg.xadj = new idx_t[g.getNVertex() + 1]; |
147 | Mg.adjncy = new idx_t[g.getNEdge()]; |
148 | Mg.vwgt = new idx_t[g.getNVertex()]; |
149 | Mg.adjwgt = new idx_t[g.getNEdge()]; |
150 | Mg.vsize = new idx_t[g.getNVertex()]; |
151 | |
152 | //! starting point in the adjacency list |
153 | size_t prev = 0; |
154 | |
155 | // actual position |
156 | size_t id = 0; |
157 | |
158 | // for each vertex calculate the position of the starting point in the adjacency list |
159 | for (size_t i = 0; i < g.getNVertex(); i++) |
160 | { |
161 | // Add weight to vertex and migration cost |
162 | Mg.vwgt[i] = g.vertex(i).template get<nm_v_computation>(); |
163 | Mg.vwgt[i] = (Mg.vwgt[i] == 0)?1:Mg.vwgt[i]; |
164 | Mg.vsize[i] = g.vertex(i).template get<nm_v_migration>(); |
165 | Mg.vsize[i] = (Mg.vsize[i] == 0)?1:Mg.vsize[i]; |
166 | |
167 | // Calculate the starting point in the adjacency list |
168 | Mg.xadj[id] = prev; |
169 | |
170 | // Create the adjacency list |
171 | for (size_t s = 0; s < g.getNChilds(i); s++) |
172 | { |
173 | Mg.adjncy[prev + s] = g.getChild(i, s); |
174 | |
175 | // zero values on Metis are dangerous |
176 | Mg.adjwgt[prev + s] = g.getChildEdge(i, s).template get<nm_e::communication>(); |
177 | Mg.adjwgt[prev + s] = (Mg.adjwgt[prev + s] == 0)?1:Mg.adjwgt[prev + s]; |
178 | } |
179 | |
180 | // update the position for the next vertex |
181 | prev += g.getNChilds(i); |
182 | |
183 | id++; |
184 | } |
185 | |
186 | // Fill the last point |
187 | Mg.xadj[id] = prev; |
188 | } |
189 | |
190 | |
191 | void reset_pointer() |
192 | { |
193 | Mg.nvtxs = NULL; |
194 | Mg.ncon = NULL; |
195 | Mg.xadj = NULL; |
196 | Mg.adjncy = NULL; |
197 | Mg.vwgt = NULL; |
198 | Mg.adjwgt = NULL; |
199 | Mg.nparts = NULL; |
200 | Mg.tpwgts = NULL; |
201 | Mg.ubvec = NULL; |
202 | Mg.options = NULL; |
203 | Mg.objval = NULL; |
204 | Mg.part = NULL; |
205 | Mg.vsize = NULL; |
206 | } |
207 | |
208 | public: |
209 | |
210 | /*! \brief Constructor |
211 | * |
212 | * Construct a metis graph from Graph_CSR |
213 | * |
214 | * \param g Graph we want to convert to decompose |
215 | * \param nc number of partitions |
216 | * \param useWeights tells if weights are used or not |
217 | * |
218 | */ |
219 | Metis(Graph & g, size_t nc, bool useWeights) |
220 | :g(g),n_dec(0) |
221 | { |
222 | reset_pointer(); |
223 | initMetisGraph(nc,useWeights); |
224 | } |
225 | |
226 | /*! \brief Constructor |
227 | * |
228 | * Construct a metis graph from Graph_CSR |
229 | * |
230 | * \param g Graph we want to convert to decompose |
231 | * \param nc number of partitions |
232 | * |
233 | */ |
234 | Metis(Graph & g, size_t nc) |
235 | :g(g),n_dec(0) |
236 | { |
237 | reset_pointer(); |
238 | initMetisGraph(nc,false); |
239 | } |
240 | |
241 | /*! \brief Constructor |
242 | * |
243 | * This constructor does not initialize the internal metis graph |
244 | * you have to use initMetisGraph to initialize |
245 | * |
246 | * \param g Graph we want to convert to decompose |
247 | * |
248 | */ |
249 | Metis(Graph & g) |
250 | :g(g),n_dec(0) |
251 | { |
252 | reset_pointer(); |
253 | } |
254 | |
255 | |
256 | /*! \brief Initialize the METIS graph |
257 | * |
258 | * \param nc number of partitions |
259 | * \param useWeights use the weights on the graph |
260 | * |
261 | */ |
262 | void initMetisGraph(int nc, bool useWeights) |
263 | { |
264 | |
265 | // Get the number of vertex |
266 | |
267 | if (Mg.nvtxs != NULL) |
268 | {delete[] Mg.nvtxs;} |
269 | Mg.nvtxs = new idx_t[1]; |
270 | Mg.nvtxs[0] = g.getNVertex(); |
271 | |
272 | // Set the number of constrains |
273 | |
274 | if (Mg.ncon != NULL) |
275 | {delete[] Mg.ncon;} |
276 | Mg.ncon = new idx_t[1]; |
277 | Mg.ncon[0] = 1; |
278 | |
279 | // Set to null the weight of the vertex |
280 | |
281 | if (Mg.vwgt != NULL) |
282 | {delete[] Mg.vwgt;} |
283 | Mg.vwgt = NULL; |
284 | |
285 | // Put the total communication size to NULL |
286 | |
287 | if (Mg.vsize != NULL) |
288 | {delete[] Mg.vsize;} |
289 | Mg.vsize = NULL; |
290 | |
291 | // Set to null the weight of the edge |
292 | |
293 | if (Mg.adjwgt != NULL) |
294 | {delete[] Mg.adjwgt;} |
295 | Mg.adjwgt = NULL; |
296 | |
297 | // construct the adjacency list |
298 | if (useWeights) |
299 | constructAdjListWithWeights(g); |
300 | else |
301 | constructAdjList(g); |
302 | |
303 | // Set the total number of partitions |
304 | |
305 | if (Mg.nparts != NULL) |
306 | {delete[] Mg.nparts;} |
307 | Mg.nparts = new idx_t[1]; |
308 | Mg.nparts[0] = nc; |
309 | |
310 | // Set to null the desired weight for each partition (one for each constrain) |
311 | |
312 | if (Mg.tpwgts != NULL) |
313 | {delete[] Mg.tpwgts;} |
314 | Mg.tpwgts = NULL; |
315 | |
316 | //! Set to null the partition load imbalance tolerance |
317 | if (Mg.ubvec != NULL) |
318 | {delete[] Mg.ubvec;} |
319 | Mg.ubvec = NULL; |
320 | |
321 | //! Set tp null additional option for the graph partitioning |
322 | |
323 | if (Mg.options != NULL) |
324 | {delete[] Mg.options;} |
325 | Mg.options = NULL; |
326 | |
327 | //! set the objective value |
328 | if (Mg.objval != NULL) |
329 | {delete[] Mg.objval;} |
330 | Mg.objval = new idx_t[1]; |
331 | |
332 | //! Is an output vector containing the partition for each vertex |
333 | if (Mg.part != NULL) |
334 | {delete[] Mg.part;} |
335 | Mg.part = new idx_t[g.getNVertex()]; |
336 | |
337 | for (size_t i = 0; i < g.getNVertex(); i++) |
338 | Mg.part[i] = 0; |
339 | } |
340 | |
341 | /*! \brief destructor |
342 | * |
343 | * Destructor, It destroy all the memory allocated |
344 | * |
345 | */ |
346 | ~Metis() |
347 | { |
348 | // Deallocate the Mg structure |
349 | if (Mg.nvtxs != NULL) |
350 | { |
351 | delete[] Mg.nvtxs; |
352 | } |
353 | |
354 | if (Mg.ncon != NULL) |
355 | { |
356 | delete[] Mg.ncon; |
357 | } |
358 | |
359 | if (Mg.xadj != NULL) |
360 | { |
361 | delete[] Mg.xadj; |
362 | } |
363 | |
364 | if (Mg.adjncy != NULL) |
365 | { |
366 | delete[] Mg.adjncy; |
367 | } |
368 | if (Mg.vwgt != NULL) |
369 | { |
370 | delete[] Mg.vwgt; |
371 | } |
372 | if (Mg.adjwgt != NULL) |
373 | { |
374 | delete[] Mg.adjwgt; |
375 | } |
376 | if (Mg.nparts != NULL) |
377 | { |
378 | delete[] Mg.nparts; |
379 | } |
380 | if (Mg.tpwgts != NULL) |
381 | { |
382 | delete[] Mg.tpwgts; |
383 | } |
384 | if (Mg.ubvec != NULL) |
385 | { |
386 | delete[] Mg.ubvec; |
387 | } |
388 | if (Mg.options != NULL) |
389 | { |
390 | delete[] Mg.options; |
391 | } |
392 | if (Mg.objval != NULL) |
393 | { |
394 | delete[] Mg.objval; |
395 | } |
396 | if (Mg.part != NULL) |
397 | { |
398 | delete[] Mg.part; |
399 | } |
400 | if (Mg.vsize != NULL) |
401 | { |
402 | delete[] Mg.vsize; |
403 | } |
404 | } |
405 | |
406 | /*! \brief Decompose the graph |
407 | * |
408 | * \tparam i which property store the decomposition |
409 | * |
410 | */ |
411 | |
412 | template<unsigned int i> |
413 | void decompose() |
414 | { |
415 | if (Mg.nparts[0] != 1) |
416 | { |
417 | // Decompose |
418 | METIS_PartGraphRecursive(Mg.nvtxs, Mg.ncon, Mg.xadj, Mg.adjncy, Mg.vwgt, Mg.vsize, Mg.adjwgt, Mg.nparts, Mg.tpwgts, Mg.ubvec, Mg.options, Mg.objval, Mg.part); |
419 | |
420 | // vertex id |
421 | |
422 | size_t id = 0; |
423 | |
424 | // For each vertex store the processor that contain the data |
425 | |
426 | auto it = g.getVertexIterator(); |
427 | |
428 | while (it.isNext()) |
429 | { |
430 | g.vertex(it).template get<i>() = Mg.part[id]; |
431 | |
432 | ++id; |
433 | ++it; |
434 | } |
435 | } |
436 | else |
437 | { |
438 | // Trivially assign all the domains to the processor 0 |
439 | |
440 | auto it = g.getVertexIterator(); |
441 | |
442 | while (it.isNext()) |
443 | { |
444 | g.vertex(it).template get<i>() = 0; |
445 | |
446 | ++it; |
447 | } |
448 | } |
449 | |
450 | n_dec++; |
451 | } |
452 | |
453 | /*! \brief Decompose the graph |
454 | * |
455 | * \tparam i which property store the decomposition |
456 | * |
457 | */ |
458 | |
459 | template<unsigned int i, typename Graph_part> |
460 | void decompose(Graph_part & gp) |
461 | { |
462 | // Decompose |
463 | METIS_PartGraphRecursive(Mg.nvtxs, Mg.ncon, Mg.xadj, Mg.adjncy, Mg.vwgt, Mg.vsize, Mg.adjwgt, Mg.nparts, Mg.tpwgts, Mg.ubvec, Mg.options, Mg.objval, Mg.part); |
464 | |
465 | // vertex id |
466 | |
467 | size_t id = 0; |
468 | |
469 | // For each vertex store the processor that contain the data |
470 | |
471 | auto it = gp.getVertexIterator(); |
472 | |
473 | while (it.isNext()) |
474 | { |
475 | gp.vertex(it).template get<i>() = Mg.part[id]; |
476 | |
477 | ++id; |
478 | ++it; |
479 | } |
480 | |
481 | n_dec++; |
482 | } |
483 | |
484 | /*! \brief It set Metis on test |
485 | * |
486 | * \param testing set to true to disable the testing |
487 | * |
488 | * At the moment disable the seed randomness to keep the result |
489 | * reproducible |
490 | * |
491 | */ |
492 | void onTest(bool testing) |
493 | { |
494 | if (testing == false) |
495 | return; |
496 | |
497 | if (Mg.options == NULL) |
498 | { |
499 | // allocate |
500 | Mg.options = new idx_t[METIS_NOPTIONS]; |
501 | |
502 | // set default options |
503 | METIS_SetDefaultOptions(Mg.options); |
504 | } |
505 | |
506 | Mg.options[METIS_OPTION_SEED] = 0; |
507 | } |
508 | |
509 | /*! \brief Distribution tolerance |
510 | * |
511 | * \param tol tolerance |
512 | * |
513 | */ |
514 | void setDistTol(real_t tol) |
515 | { |
516 | dist_tol = tol; |
517 | } |
518 | |
519 | /*! \brief Get the decomposition counter |
520 | * |
521 | * \return the decomposition counter |
522 | * |
523 | */ |
524 | size_t get_ndec() |
525 | { |
526 | return n_dec; |
527 | } |
528 | |
529 | /*! \brief Increment the decomposition counter |
530 | * |
531 | * |
532 | */ |
533 | void inc_dec() |
534 | { |
535 | n_dec++; |
536 | } |
537 | }; |
538 | |
539 | #endif |
540 | |