PaStiX Handbook 6.4.0
Loading...
Searching...
No Matches
order_supernodes.c
Go to the documentation of this file.
1/**
2 *
3 * @file order_supernodes.c
4 *
5 * PaStiX order routines dedicated to split supernodes thanks to graph connectivity
6 *
7 * @copyright 2004-2025 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
8 * Univ. Bordeaux. All rights reserved.
9 *
10 * @version 6.4.0
11 * @author Gregoire Pichon
12 * @author Mathieu Faverge
13 * @date 2024-07-05
14 *
15 */
16#include "common.h"
17#include <string.h>
18#include "graph/graph.h"
19#include "order/order_internal.h"
20#include "blend/elimintree.h"
21#include "blend/extendVector.h"
22
23/**
24 *******************************************************************************
25 *
26 * @ingroup pastix_order
27 *
28 * @brief Order the supernodes with one of the clustering strategies.
29 *
30 *******************************************************************************
31 *
32 * @param[in] graph
33 * The graph that represents the sparse matrix.
34 *
35 * @param[inout] order
36 * The graph that represents the sparse matrix. On exit, this ordering
37 * is updated with the refined partition obtained thanks to one of the
38 * clustering strategies.
39 *
40 * @param[inout] etree
41 * The elimination tree. On exit it can be modified by
42 * eTreeComputeLevels().
43 *
44 * @param[in] iparm
45 * The integer array of parameters.
46 *
47 * @param[in] do_schur
48 * If do_schur is not zero, the last cblk represents the schur. Thus,
49 * neither k-way nor projection applied on it. If do_schur is zero,
50 * both k-way and projection can be performed everywhere.
51 *
52 *******************************************************************************
53 *
54 * @retval PASTIX_SUCCESS on successful exit,
55 * @retval PASTIX_ERR_BADPARAMETER if one parameter is incorrect,
56 *
57 *******************************************************************************/
59orderSupernodes( const pastix_graph_t *graph,
60 pastix_order_t *order,
61 EliminTree *etree,
62 pastix_int_t *iparm,
63 int do_schur )
64{
65 ExtendVectorINT sn_parts;
72 pastix_int_t *depth_size = NULL;
73 int *n_levels = NULL;
74 pastix_int_t i, sn_first, sn_id;
75 pastix_int_t new_cblknbr;
76 pastix_int_t *perm_treetab;
77 pastix_int_t *new_rangtab;
78 pastix_int_t *new_treetab;
79 int8_t *new_selevtx;
80
81 if ( strategy == PastixSplitNot ) {
82 return PASTIX_SUCCESS;
83 }
84
85 if ( order == NULL ) {
86 pastix_print_error( "orderSupernodes: invalid order pointer" );
88 }
89
90 lvl_kway = pastix_imax( lvl_proj, lvl_kway );
91
92 pastixOrderBase( order, 0 );
93
94 /* Make sure the node levels are computed in the etree */
95 eTreeComputeLevels( etree, eTreeRoot(etree), 0 );
96
97 /* Get the minimal index of a node at the max depth considered */
98 sn_first = eTreeGetLevelMinIdx( etree, eTreeRoot(etree), lvl_kway, order->cblknbr );
99
100 extendint_Init( &sn_parts, 16 );
101
102 /*
103 * Create an array with the depth of each vertex in the elimination tree
104 * used to compute the projection
105 */
106 if ( strategy == PastixSplitKwayProjections )
107 {
108 /* Allocate the size of each projection */
109 MALLOC_INTERN( depth_size, max_depth, pastix_int_t );
110
111 /* Store the level of each node */
112 MALLOC_INTERN( n_levels, graph->n, int );
113 for( sn_id=0; sn_id<order->cblknbr; sn_id++ ) {
114 int level = etree->nodetab[sn_id].ndlevel;
115 assert( level > 0 );
116
117 for(i=order->rangtab[sn_id]; i<order->rangtab[sn_id+1]; i++) {
118 n_levels[ order->peritab[i] ] = level;
119 }
120 }
121 }
122
123 /* Backup initial rangtab and allocated the permutation array for top elements of the treetab */
124 MALLOC_INTERN( perm_treetab, order->cblknbr - sn_first, pastix_int_t );
125 MALLOC_INTERN( new_rangtab, order->vertnbr+1, pastix_int_t );
126 MALLOC_INTERN( new_treetab, order->vertnbr, pastix_int_t );
127 MALLOC_INTERN( new_selevtx, order->vertnbr, int8_t );
128
129 memcpy( new_rangtab, order->rangtab, sizeof(pastix_int_t) * (sn_first+1) );
130 memcpy( new_treetab, order->treetab, sizeof(pastix_int_t) * sn_first );
131 memset( new_selevtx, 0, order->vertnbr * sizeof(int8_t) );
132
133 new_cblknbr = sn_first;
134
135 for (sn_id = sn_first; sn_id < order->cblknbr; sn_id++) {
136 pastix_graph_t sn_graph;
137 pastix_order_t sn_order;
138 pastix_int_t sn_level, sn_vertnbr;
139 pastix_int_t fnode, lnode, ret; /* , sorted; */
140 pastix_int_t sn_nbpart_proj, sn_nbparts, sn_nbparts_max;
141
142 perm_treetab[sn_id-sn_first] = new_cblknbr;
143
144 /* sorted = 0; */
145 fnode = order->rangtab[sn_id];
146 lnode = order->rangtab[sn_id+1];
147 sn_vertnbr = lnode - fnode;
148 sn_level = etree->nodetab[sn_id].ndlevel;
149 sn_nbparts_max = pastix_iceil( sn_vertnbr, iparm[IPARM_MAX_BLOCKSIZE] );
150
151 if ( (sn_level > (lvl_kway)) ||
152 (sn_nbparts_max == 1) ||
153 ((sn_id == order->cblknbr-1) && (do_schur)) )
154 {
155 new_treetab[ new_cblknbr ] = order->treetab[sn_id];
156 new_cblknbr++;
157 new_rangtab[ new_cblknbr ] = lnode;
158 continue;
159 }
160
161 if ( iparm[IPARM_VERBOSE] > 2 ) {
162 fprintf( stdout, " - Working on cblk %ld (level= %d, n= %ld):\n",
163 (long)sn_id, (int)sn_level, (long)(lnode-fnode) );
164 }
165
166 /* Reinitialize data structures */
167 extendint_Clear( &sn_parts );
168
169 pastixOrderAllocId( &sn_order, sn_vertnbr );
170 memset( &sn_graph, 0, sizeof(pastix_graph_t) );
171
172 /**
173 * Extract the subgraph with unknowns of the supernode sn_id
174 *
175 * 1 is sufficient for the max_distance in most cases, but set to 2 for
176 * corner cases that need extra connexions.
177 */
178 ret = graphIsolateRange( graph, order, &sn_graph,
179 fnode, lnode, 2 );
180 if ( ret != EXIT_SUCCESS ) {
181 fprintf(stderr, "Fatal error in graphIsolateSupernode()!\n");
182 exit(1);
183 }
184 assert( sn_vertnbr == sn_graph.n );
185
186 /**
187 * Compute sets of preselected unknowns based on projections
188 */
189 if ( ( sn_vertnbr > (16 * iparm[IPARM_MAX_BLOCKSIZE]) ) &&
190 ( sn_level <= lvl_proj ) &&
191 ( strategy == PastixSplitKwayProjections ) &&
192 ( etree->nodetab[ sn_id ].sonsnbr == 2 ) )
193 {
194 memset( depth_size, 0, max_depth * sizeof(pastix_int_t) );
195
196 pastix_int_t *permtab = malloc(graph->n * sizeof(pastix_int_t));
197 pastix_int_t *peritab = malloc(graph->n * sizeof(pastix_int_t));
198 memcpy(permtab, order->permtab, graph->n * sizeof(pastix_int_t));
199 memcpy(peritab, order->peritab, graph->n * sizeof(pastix_int_t));
200
201 /* Compute the projection of the sublevel separators onto the subgraph */
202 graphComputeProjection( graph, n_levels, order,
203 &sn_graph, &sn_order,
204 fnode, lnode, sn_level,
205 max_distance, max_depth, max_width,
206 depth_size );
207
208 /* Print statistics */
209 if ( iparm[IPARM_VERBOSE] > 2 )
210 {
211 fprintf(stdout, " - Results of the projection:\n" );
212 for( i=0; i<max_depth; i++ ) {
213 fprintf( stdout, " - At level %d: %8ld\n",
214 (int)(i+1), (long)depth_size[i] );
215 }
216 }
217
218 /* Partition the supernode if sets of preselected unknowns have correct sizes */
219 {
220 pastix_int_t selected, total, totalsel;
221 pastix_int_t selecmax = 20 * sqrt( sn_vertnbr );
222 selected = 0;
223 totalsel = 0;
224 total = sn_vertnbr;
225
226 for( i=0; i<max_depth; i++ ) {
227 totalsel += depth_size[i];
228
229 /* If we reach the maximum size of pre-selected unknowns, we exit */
230 if ( totalsel > selecmax ) {
231 break;
232 }
233
234 total -= depth_size[i];
235 selected += depth_size[i];
236
237 if ( selected > iparm[IPARM_MIN_BLOCKSIZE] ) {
238 extendint_Add( &sn_parts, selected );
239 selected = 0;
240 }
241 }
242
243 /* Add last partition of selected unknowns */
244 if ( selected > iparm[IPARM_MIN_BLOCKSIZE] ) {
245 extendint_Add( &sn_parts, selected );
246 selected = 0;
247 }
248 else {
249 /* Unselect the unknowns */
250 total += selected;
251 selected = 0;
252 }
253
254 if ( iparm[IPARM_VERBOSE] > 2 ) {
255 fprintf( stdout, " - %ld nodes selected, %ld remains for K-Way\n",
256 (long)(sn_vertnbr - total), (long)(total) );
257 }
258
259 /*
260 * The number of remaining unknowns is the non-selected unknowns
261 * + the selected unknows not extracted
262 */
263 if ( (sn_vertnbr - total) == 0 ) {
264 memcpy( order->permtab, permtab, graph->n * sizeof(pastix_int_t) );
265 memcpy( order->peritab, peritab, graph->n * sizeof(pastix_int_t) );
266 }
267
268 sn_vertnbr = total;
269 }
270 free(permtab);
271 free(peritab);
272 }
273
274 sn_nbparts = extendint_Size( &sn_parts );
275 sn_nbpart_proj = sn_nbparts;
276
277 /* Ordering based on K-way */
278 if ( ( strategy == PastixSplitKway ) ||
279 ( strategy == PastixSplitKwayProjections ) )
280 {
281 pastix_queue_t queue;
282 pastix_int_t *comp_vtx, *comp_sze;
283 pastix_int_t comp_nbr = 1;
284 pastix_int_t nbpart_kway;
285 pastix_int_t smallcp_id, smallcp_sz, cp_id, cp_sz;
286 nbpart_kway = pastix_iceil( sn_vertnbr, iparm[IPARM_MAX_BLOCKSIZE] );
287
288 /* Quick return */
289 if ( nbpart_kway < 2 ) {
290 goto cblk_end;
291 }
292
293 /* Update the subgraph by removing the selected vertices if any */
294 if ( sn_nbparts > 0 ) {
295 pastix_graph_t tmpgraph;
296 memset( &tmpgraph, 0, sizeof(pastix_graph_t) );
297
298 /*
299 * We isolate with a distance 0 here, as we already reconnected
300 * the graph at a given distance previously. In that case, we
301 * really want to disconnect components that are connected
302 * though selected vertices
303 */
304 graphIsolateRange( &sn_graph, &sn_order, &tmpgraph,
305 0, sn_vertnbr, 0 );
306 graphExit( &sn_graph );
307 memcpy( &sn_graph, &tmpgraph, sizeof(pastix_graph_t) );
308
309 /*
310 * Reduce the suborder structure.
311 * We use thefact that the main peritab as always been updated
312 * along with the subarrays.
313 */
314 pastixOrderExit( &sn_order );
315 pastixOrderAllocId( &sn_order, sn_vertnbr );
316 }
317
318 /* Isolate the connected components */
319 comp_vtx = malloc( sn_vertnbr * sizeof(pastix_int_t) );
320 comp_sze = malloc( sn_vertnbr * sizeof(pastix_int_t) );
321
322 comp_nbr = graphIsolateConnectedComponents( &sn_graph, comp_vtx, comp_sze );
323
324 if ( iparm[IPARM_VERBOSE] > 2 ) {
325 fprintf(stdout, " - Connected components: %ld\n", (long)comp_nbr );
326 }
327
328 pqueueInit( &queue, comp_nbr );
329 for( i=0; i<comp_nbr; i++) {
330 pqueuePush1( &queue, i, comp_sze[i] );
331 }
332
333 smallcp_id = -1;
334 smallcp_sz = 0;
335 while( pqueueSize( &queue ) > 0 ) {
336 cp_id = pqueuePop( &queue );
337 cp_sz = comp_sze[cp_id];
338
339 if ( cp_sz < iparm[IPARM_COMPRESS_MIN_WIDTH] ) {
340 /* Merge with other small components */
341 smallcp_sz += cp_sz;
342 if ( smallcp_id == -1 ) {
343 smallcp_id = cp_id;
344 }
345 else {
346 comp_sze[cp_id] = 0;
347 for( i=0; (i<sn_graph.n) && (cp_sz>0); i++) {
348 if ( comp_vtx[i] == cp_id ) {
349 comp_vtx[i] = smallcp_id;
350 cp_sz--;
351 }
352 }
353 }
354 comp_sze[ smallcp_id ] = smallcp_sz;
355 }
356 else {
357 /* Update the local number of K-Way */
358 nbpart_kway = pastix_iceil( cp_sz, iparm[IPARM_MAX_BLOCKSIZE] );
359 if ( nbpart_kway < 2 ) {
360 continue;
361 }
362
363 /* if (!sorted) { */
364 /* void *sortptr[3]; */
365 /* pastix_int_t *perm = sn_order.permtab; */
366 /* pastix_int_t *invp = sn_order.peritab; */
367
368 /* sortptr[0] = comp_vtx; */
369 /* sortptr[1] = order->peritab + fnode; */
370 /* sortptr[2] = invp; */
371
372 /* qsort3IntAsc( sortptr, sn_graph.n ); */
373
374 /* /\* Update the perm array *\/ */
375 /* for(i=0; i<sn_graph.n; i++) { */
376 /* perm[invp[i]] = i; */
377 /* } */
378
379 /* sorted = 1; */
380 /* } */
381
382 graphComputeKway( &sn_graph, &sn_order, order->peritab + fnode,
383 &comp_nbr, comp_sze, comp_vtx,
384 cp_id, nbpart_kway );
385 }
386 }
387 pqueueExit( &queue );
388
389 /* If multiple partitions, let's sort the unknowns */
390 if ( comp_nbr > 1 ) {
391 void *sortptr[2];
392 sortptr[0] = comp_vtx;
393 sortptr[1] = order->peritab + fnode;
394
395 qsort2IntAsc( sortptr, sn_graph.n );
396 }
397
398 for(i=comp_nbr-1; i>=0; i--) {
399 if (comp_sze[i] > 0) {
400 extendint_Add( &sn_parts, comp_sze[i] );
401 }
402 }
403 sn_vertnbr = 0;
404
405 free( comp_vtx );
406 free( comp_sze );
407
408 }
409
410 /* Let's add a first cblk with remaining nodes */
411 cblk_end:
412 if ( sn_vertnbr > 0 ) {
413 extendint_Add( &sn_parts, sn_vertnbr );
414 }
415
416 sn_nbparts = extendint_Size( &sn_parts );
417 assert( sn_nbparts >= 1 );
418 assert( new_rangtab[ new_cblknbr ] == fnode );
419
420 /* First cblk */
421 fnode += extendint_Read( &sn_parts, sn_nbparts-1 );
422 new_selevtx[ new_cblknbr ] = ( sn_nbparts-1 < sn_nbpart_proj ) ? SYMBCBLK_PROJ : SYMBCBLK_KWAY;
423 new_cblknbr++;
424 new_rangtab[ new_cblknbr ] = fnode;
425
426 assert( extendint_Read( &sn_parts, sn_nbparts-1 ) > 0 );
427 assert( fnode <= lnode );
428
429 /*
430 * Update rangtab and treetab
431 */
432 for(i=sn_nbparts-2; i>=0; i--)
433 {
434 /* Chain cblk together */
435 new_treetab[new_cblknbr-1] = -1 - new_cblknbr;
436 new_selevtx[ new_cblknbr ] = ( i < sn_nbpart_proj ) ? SYMBCBLK_PROJ : SYMBCBLK_KWAY;
437 fnode += extendint_Read( &sn_parts, i );
438 new_cblknbr++;
439 new_rangtab[new_cblknbr] = fnode;
440
441 assert( extendint_Read( &sn_parts, i ) > 0 );
442 assert( fnode <= lnode );
443 }
444
445 new_treetab[ new_cblknbr-1 ] = order->treetab[sn_id];
446
447 /* Update permtab for future extractions */
448 for (i=order->rangtab[sn_id]; i<order->rangtab[sn_id+1]; i++){
449 order->permtab[order->peritab[i]] = i;
450 }
451
452 graphExit( &sn_graph );
453 pastixOrderExit( &sn_order );
454 }
455 assert( new_rangtab[new_cblknbr] == order->vertnbr );
456
457 if ( n_levels != NULL ) {
458 free( n_levels );
459 }
460 if ( depth_size != NULL ) {
461 free( depth_size );
462 }
463 extendint_Exit( &sn_parts );
464
465 /* Update the treetab */
466 {
467 pastix_int_t *oldtree, *newtree;
468
469 memFree_null( order->treetab );
470 MALLOC_INTERN( order->treetab, new_cblknbr, pastix_int_t );
471
472 newtree = order->treetab;
473 oldtree = new_treetab;
474 for(i=0; i<new_cblknbr; i++, newtree++, oldtree++) {
475 if ( *oldtree >= sn_first ) {
476 *newtree = perm_treetab[ *oldtree - sn_first ];
477 }
478 else if ( *oldtree >= 0 ) {
479 *newtree = *oldtree;
480 }
481 else if ( *oldtree == -1 ) {
482 *newtree = -1;
483 }
484 else { /* < -1 */
485 /* Use the fact that 0 will never be a father to shift only by one to escape the root */
486 *newtree = - *oldtree - 1;
487 }
488 }
489 memFree_null( new_treetab );
490 memFree_null( perm_treetab );
491 }
492
493 /* Update the rangtab */
494 free(order->rangtab);
495 order->rangtab = realloc( new_rangtab, (new_cblknbr+1) * sizeof( pastix_int_t ) );
496
497 order->cblknbr = new_cblknbr;
498 order->selevtx = realloc( new_selevtx, new_cblknbr * sizeof(int8_t) );
499
500 if ( pastixOrderCheck( order ) != 0 ) {
501 printf("pastixOrderCheck() at the end of OrderSupernodes() failed !!!");
502 assert(0);
503 }
504
505 return PASTIX_SUCCESS;
506}
BEGIN_C_DECLS typedef int pastix_int_t
Definition datatypes.h:51
eTreeNode_t * nodetab
Definition elimintree.h:42
static pastix_int_t eTreeRoot(const EliminTree *etree)
Return the root of the elimination tree.
Definition elimintree.h:126
pastix_int_t eTreeGetLevelMinIdx(const EliminTree *, pastix_int_t, pastix_int_t, pastix_int_t)
Return the smallest index of the nodes belonging to the level lvl in the elimination tree.
Definition elimintree.c:429
pastix_int_t eTreeComputeLevels(EliminTree *, pastix_int_t, pastix_int_t)
Compute the number of level existing below a given node.
Definition elimintree.c:250
Elimination tree.
Definition elimintree.h:39
pastix_int_t extendint_Size(const ExtendVectorINT *)
Return the number of element stored in the vector.
void extendint_Clear(ExtendVectorINT *)
Cleanup the vector.
pastix_int_t extendint_Read(const ExtendVectorINT *, pastix_int_t)
Return the element of index eltnum.
pastix_int_t * extendint_Init(ExtendVectorINT *, pastix_int_t)
Initialize the extendVector structure with the initial size given.
void extendint_Add(ExtendVectorINT *, pastix_int_t)
Add an element elt to the end of the vector.
void extendint_Exit(ExtendVectorINT *)
Free the extendVector structure.
The extend integer array structure.
static void pqueuePush1(pastix_queue_t *q, pastix_int_t elt, double key1)
Push an element with a single key.
Definition queue.h:64
void pqueueExit(pastix_queue_t *)
Free the structure associated to the queue.
Definition queue.c:110
pastix_int_t pqueueSize(const pastix_queue_t *)
Return the size of the queue.
Definition queue.c:135
static pastix_int_t pqueuePop(pastix_queue_t *q)
Pop the head of the queue whithout returning the keys.
Definition queue.h:75
int pqueueInit(pastix_queue_t *, pastix_int_t)
Initialize the queue structure with an initial space to store the elements.
Definition queue.c:81
Queue structure.
Definition queue.h:38
enum pastix_split_e pastix_split_t
Splitting strategy available for IPARM_SPLITTING_STRATEGY parameter.
@ IPARM_COMPRESS_MIN_WIDTH
Definition api.h:129
@ IPARM_MIN_BLOCKSIZE
Definition api.h:88
@ IPARM_SPLITTING_PROJECTIONS_DISTANCE
Definition api.h:84
@ IPARM_VERBOSE
Definition api.h:36
@ IPARM_MAX_BLOCKSIZE
Definition api.h:89
@ IPARM_SPLITTING_LEVELS_PROJECTIONS
Definition api.h:81
@ IPARM_SPLITTING_STRATEGY
Definition api.h:80
@ IPARM_SPLITTING_LEVELS_KWAY
Definition api.h:82
@ IPARM_SPLITTING_PROJECTIONS_WIDTH
Definition api.h:85
@ IPARM_SPLITTING_PROJECTIONS_DEPTH
Definition api.h:83
@ PASTIX_SUCCESS
Definition api.h:367
@ PASTIX_ERR_BADPARAMETER
Definition api.h:374
@ PastixSplitNot
Definition api.h:416
@ PastixSplitKway
Definition api.h:417
@ PastixSplitKwayProjections
Definition api.h:418
void graphExit(pastix_graph_t *graph)
Free the content of the graph structure.
Definition graph.c:73
pastix_int_t graphIsolateConnectedComponents(const pastix_graph_t *graph, pastix_int_t *comp_vtx, pastix_int_t *comp_sze)
Isolate the connected components from the original graph.
void graphComputeProjection(const pastix_graph_t *graph, const int *vertlvl, pastix_order_t *order, const pastix_graph_t *subgraph, pastix_order_t *suborder, pastix_int_t fnode, pastix_int_t lnode, pastix_int_t sn_level, pastix_int_t distance, pastix_int_t maxdepth, pastix_int_t maxwidth, pastix_int_t *depthsze)
TODO.
int graphIsolateRange(const pastix_graph_t *graphIn, const pastix_order_t *order, pastix_graph_t *graphOut, pastix_int_t fnode, pastix_int_t lnode, pastix_int_t distance)
Isolate the subgraph associated to a range of unknowns in the permuted graph.
int graphComputeKway(const pastix_graph_t *graph, pastix_order_t *order, pastix_int_t *peritab, pastix_int_t *comp_nbr, pastix_int_t *comp_sze, pastix_int_t *comp_vtx, pastix_int_t comp_id, pastix_int_t nbpart)
Compute the K-way partition of a given set of unknowns.
pastix_int_t * treetab
Definition order.h:54
int8_t * selevtx
Definition order.h:55
pastix_int_t * permtab
Definition order.h:51
pastix_int_t * peritab
Definition order.h:52
pastix_int_t cblknbr
Definition order.h:50
pastix_int_t * rangtab
Definition order.h:53
pastix_int_t vertnbr
Definition order.h:49
void pastixOrderBase(pastix_order_t *ordeptr, pastix_int_t baseval)
This routine sets the base of the given ordering structure to the given base value.
Definition order.c:322
pastix_int_t orderSupernodes(const pastix_graph_t *graph, pastix_order_t *order, EliminTree *etree, pastix_int_t *iparm, int do_schur)
Order the supernodes with one of the clustering strategies.
int pastixOrderCheck(const pastix_order_t *ordeptr)
This routine checks the correctness of the ordering structure.
Definition order_check.c:39
int pastixOrderAllocId(pastix_order_t *ordeptr, pastix_int_t vertnbr)
Allocate the order structure for a given number of vertices with no cblk, and id permutation.
Definition order.c:123
void pastixOrderExit(pastix_order_t *ordeptr)
Free the arrays initialized in the order structure.
Definition order.c:273
Order structure.
Definition order.h:47