SpM Handbook 1.2.4
Loading...
Searching...
No Matches
spm_redistribute.c
Go to the documentation of this file.
1/**
2 *
3 * @file spm_redistribute.c
4 *
5 * SPM subroutines to redistribute a given SPM with a new distribution.
6 *
7 * @copyright 2016-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
8 * Univ. Bordeaux. All rights reserved.
9 *
10 * @version 1.2.4
11 * @author Pierre Ramet
12 * @author Mathieu Faverge
13 * @author Tony Delarue
14 * @author Alycia Lisito
15 * @date 2024-06-25
16 *
17 * @ingroup spm_dev_mpi
18 * @{
19 *
20 **/
21#include "common.h"
22
23/**
24 * @def COLTAG
25 * @brief Internal tag for colptr array communications
26 *
27 * @def ROWTAG
28 * @brief Internal tag for rowptr array communications
29 *
30 * @def VALTAG
31 * @brief Internal tag for values array communications
32 */
33#define COLTAG 97
34#define ROWTAG 98
35#define VALTAG 99
36
37/**
38 * @brief Data structure to manage the communication requests
39 */
40typedef struct spm_req_manager_s {
41 MPI_Request *requests; /**< The request array of size nbreq_max */
42 int nbreq; /**< The number of submitted requests */
43 int nbreq_max; /**< The size of the requests array */
45
46/**
47 *******************************************************************************
48 *
49 * @brief Make the communications progress in the request manager structure.
50 *
51 *******************************************************************************
52 *
53 * @param[inout] reqmanager
54 * The data structure that holds the requests to test for progress.
55 * On exit, the array if compacted in release mode.
56 *
57 *******************************************************************************/
58static inline void
60{
61 MPI_Status status;
62 int rc, idx, flag = 0;
63
64 if ( reqmanager->nbreq == 0 ) {
65 return;
66 }
67
68 do {
69 rc = MPI_Testany( reqmanager->nbreq, reqmanager->requests,
70 &idx, &flag, &status );
71 assert( rc == MPI_SUCCESS );
72
73 if ( flag && (idx != MPI_UNDEFINED) )
74 {
75 assert( reqmanager->requests[idx] == MPI_REQUEST_NULL );
76
77 reqmanager->nbreq--;
78 if ( idx < reqmanager->nbreq ) {
79 reqmanager->requests[idx] = reqmanager->requests[reqmanager->nbreq];
80 }
81 }
82 }
83 while ( (reqmanager->nbreq > 0) && flag && (idx != MPI_UNDEFINED) );
84
85 (void)rc;
86 return;
87}
88
89/**
90 *******************************************************************************
91 *
92 * @brief Wait for all the communications to be completed.
93 *
94 *******************************************************************************
95 *
96 * @param[inout] reqmanager
97 * The data structure that holds the requests to wait for completion.
98 *
99 *******************************************************************************/
100static inline void
102{
103 MPI_Status status;
104 int rc, idx;
105
106 if ( reqmanager->nbreq == 0 ) {
107 return;
108 }
109
110 do {
111 rc = MPI_Waitany( reqmanager->nbreq, reqmanager->requests,
112 &idx, &status );
113 assert( rc == MPI_SUCCESS );
114
115 if ( idx != MPI_UNDEFINED )
116 {
117 assert( reqmanager->requests[idx] == MPI_REQUEST_NULL );
118
119 reqmanager->nbreq--;
120 if ( idx < reqmanager->nbreq ) {
121 reqmanager->requests[idx] = reqmanager->requests[reqmanager->nbreq];
122 }
123 }
124 }
125 while ( (reqmanager->nbreq > 0) && (idx != MPI_UNDEFINED) );
126
127 (void)rc;
128 return;
129}
130
131/**
132 *******************************************************************************
133 *
134 * @brief Get the new glob2loc array.
135 *
136 *******************************************************************************
137 *
138 * @param[in] oldspm
139 * The sparse matrix to redistribute.
140 *
141 * @param[in] new_n
142 * The size of newl2g
143 *
144 * @param[in] newl2g
145 * The new loc2glob.
146 *
147 *******************************************************************************
148 *
149 * @return The new glob2loc array
150 *
151 *******************************************************************************/
152static inline spm_int_t *
154 spm_int_t new_n,
155 const spm_int_t *newl2g )
156{
157 spmatrix_t newspm;
158
159 spmInitDist( &newspm, oldspm->comm );
160
161 /* Set specific info that we can set */
162 newspm.baseval = oldspm->baseval;
163 newspm.n = new_n;
164 newspm.gN = oldspm->gN;
165 newspm.loc2glob = (spm_int_t *)newl2g;
166 newspm.glob2loc = NULL;
167 newspm.replicated = 0;
168 spm_getandset_glob2loc( &newspm );
169
170 return newspm.glob2loc;
171}
172
173/**
174 *******************************************************************************
175 *
176 * @brief Extract the local information and compute the volumes to exchange.
177 *
178 * This function create a copy of the matrix in which it extracts the local
179 * information, while it computes the number of data to exchange with
180 * everyone. On exit, the new spm is allocated to its final size in the IJV
181 * format to receive information from the other nodes.
182 *
183 *******************************************************************************
184 *
185 * @param[in] oldspm
186 * The original sparse matrix to redistribute.
187 *
188 * @param[in] newg2l
189 * The new spm glob2loc array of size oldspm->gN and 0-based.
190 * The array is generated with spm_redist_get_newg2l() and shifted to
191 * match the oldspm baseval.
192 *
193 * @param[in] distribution
194 * The distribution of thne original matrix as computed by
195 * spm_get_distribution().
196 *
197 * @param[inout] sendsizes
198 * On entry, array of size (2*clustnbr) that must be allocated.
199 * On exit, the array stores the couple (nbelts, nbvals) of the number
200 * of elements/values to send to each remote process.
201 *
202 * @param[inout] recvsizes
203 * On entry, array of size (2*clustnbr) that must be allocated.
204 * On exit, the array stores the couple (nbelts, nbvals) of the number
205 * of elements/values to reacv from each remote process.
206 *
207 * @param[inout] newspm
208 * Initialize the redistributed spm.
209 *
210 *******************************************************************************/
211static inline void
213 const spm_int_t *newg2l,
214 int distribution,
215 spm_int_t *sendsizes,
216 spm_int_t *recvsizes,
217 spmatrix_t *newspm )
218{
219 const spm_int_t *oldcol;
220 const spm_int_t *oldrow;
221 const char *oldval;
222 spm_int_t *newcol;
223 spm_int_t *newrow;
224 char *newval;
225 const spm_int_t *dofs;
226
227 spm_int_t i, ig, j, jg, dof;
228 spm_int_t nbrow, nbval;
229 size_t fltsize, valsize;
230 int owner;
231
232 dof = oldspm->dof;
233 dofs = oldspm->dofs - oldspm->baseval;
234 fltsize = spm_size_of( oldspm->flttype );
235
236 /*
237 * Initialize the new spm
238 *
239 * The new spm is initialized with the actual size that is enough to start
240 * with to store the local information.
241 * Note that the new spm is stored in IJV format to ease the data exchange
242 * and will be later compressed. Right now, only required fields are
243 * initialized.
244 */
245 spmInitDist( newspm, oldspm->comm );
246
247 newspm->replicated = 0;
248 newspm->mtxtype = oldspm->mtxtype;
249 newspm->flttype = oldspm->flttype;
250 newspm->fmttype = SpmIJV;
251 newspm->colptr = malloc( oldspm->nnz * sizeof( spm_int_t ) );
252 newspm->rowptr = malloc( oldspm->nnz * sizeof( spm_int_t ) );
253 if ( fltsize > 0 ) {
254 newspm->values = malloc( oldspm->nnzexp * fltsize );
255 }
256
257 /* Get the correct pointers according to the column/row distribution */
258 if ( distribution & SpmDistByColumn ) {
259 oldcol = oldspm->colptr;
260 oldrow = oldspm->rowptr;
261 newcol = newspm->colptr;
262 newrow = newspm->rowptr;
263 }
264 else {
265 oldcol = oldspm->rowptr;
266 oldrow = oldspm->colptr;
267 newcol = newspm->rowptr;
268 newrow = newspm->colptr;
269 }
270 oldval = oldspm->values;
271 newval = newspm->values;
272
273 /* Make sure the counters are set to 0 */
274 memset( sendsizes, 0, 2 * oldspm->clustnbr * sizeof( spm_int_t ) );
275 memset( recvsizes, 0, 2 * oldspm->clustnbr * sizeof( spm_int_t ) );
276
277 if ( oldspm->fmttype != SpmIJV ) {
278 spm_int_t *oldl2g = oldspm->loc2glob;
279
280 for ( j=0; j<oldspm->n;
281 j++, oldl2g++, oldcol++, oldrow += nbrow, oldval += valsize )
282 {
283 jg = *oldl2g;
284 nbrow = oldcol[1] - oldcol[0];
285 nbval = 0;
286
287 /* Compute the number of values in the expanded value array */
288 if ( fltsize != 0 ) {
289 spm_int_t dofj = ( dof > 0 ) ? dof : dofs[jg+1] - dofs[jg];
290 spm_int_t dofi = 0;
291 for ( i = 0; i < nbrow; i++ ) {
292 ig = oldrow[i];
293 dofi += ( dof > 0 ) ? dof : dofs[ig+1] - dofs[ig];
294 }
295 nbval = dofj * dofi;
296 }
297 valsize = nbval * fltsize;
298
299 /* This column remains local */
300 if ( newg2l[jg] >= 0 ) {
301 owner = oldspm->clustnum;
302
303 /* Copy the colptr (into IJV format) */
304 for ( i = 0; i < nbrow; i++, newcol++ ) {
305 *newcol = jg;
306 }
307
308 /* Copy the rowptr */
309 memcpy( newrow, oldrow, nbrow * sizeof( spm_int_t ) );
310 newrow += nbrow;
311
312 /* Copy the value array */
313 if ( fltsize > 0 ) {
314 memcpy( newval, oldval, valsize );
315 newval += valsize;
316 }
317 }
318 else {
319 owner = -newg2l[jg] - 1;
320 assert( owner != oldspm->clustnum );
321 }
322
323 sendsizes[2*owner] += nbrow;
324 sendsizes[2*owner+1] += nbval;
325 }
326 }
327 else {
328 for ( j=0; j < oldspm->nnz; j++, oldcol++, oldrow++, oldval+=valsize )
329 {
330 jg = *oldcol;
331 ig = *oldrow;
332 nbval = 0;
333
334 if ( fltsize != 0 ) {
335 spm_int_t dofj = ( dof > 0 ) ? dof : dofs[jg+1] - dofs[jg];
336 spm_int_t dofi = ( dof > 0 ) ? dof : dofs[ig+1] - dofs[ig];
337 nbval = dofj * dofi;
338 }
339 valsize = nbval * fltsize;
340
341 /* This element remainsx local */
342 if ( newg2l[jg] >= 0 ) {
343 owner = oldspm->clustnum;
344
345 /* Copy the element */
346 *newcol = jg;
347 newcol++;
348 *newrow = ig;
349 newrow++;
350
351 /* Copy the values associated to the element */
352 memcpy( newval, oldval, valsize );
353 newval += valsize;
354 }
355 else {
356 owner = -newg2l[jg] - 1;
357 assert( owner != oldspm->clustnum );
358 }
359
360 sendsizes[2*owner]++;
361 sendsizes[2*owner+1] += nbval;
362 }
363 }
364
365 /* Thanks to the sendsizes array, compute the recvsizes. */
366 MPI_Alltoall( sendsizes, 2, SPM_MPI_INT,
367 recvsizes, 2, SPM_MPI_INT, oldspm->comm );
368
369 /* Compute nnz and nnzexp -> realloc pointers */
370 {
371 newspm->nnz = 0;
372 newspm->nnzexp = 0;
373 for ( owner = 0; owner < newspm->clustnbr; owner++ )
374 {
375 newspm->nnz += recvsizes[ 2*owner ];
376 newspm->nnzexp += recvsizes[ 2*owner+1 ];
377 }
378 newspm->colptr = realloc( newspm->colptr, newspm->nnz * sizeof( spm_int_t ) );
379 newspm->rowptr = realloc( newspm->rowptr, newspm->nnz * sizeof( spm_int_t ) );
380 if ( fltsize > 0 ) {
381 newspm->values = realloc( newspm->values, newspm->nnzexp * fltsize );
382 }
383 }
384
385 return;
386}
387
388/**
389 *******************************************************************************
390 *
391 * @brief Allocate the reqtab array and post all the required receptions for
392 * each process.
393 *
394 *******************************************************************************
395 *
396 * @param[inout] newspm
397 * The new spm in which to store the reception. Must be in IJV format.
398 * On entry, it is allocated to store all the information that will be
399 * received, and on exit, it may be partially updated by the reception
400 * asynchronously.
401 *
402 * @param[in] recvsizes
403 * Amount of recvs for each process.
404 * {ptr_recvs, val_recvs}
405 *
406 * @param[in] distribution
407 * The distribution of the original sparse matrix.
408 *
409 * @param[inout] reqmanager
410 * On entry, an allocated request manager structure.
411 * On exit, the data structure is initialized and the first requests
412 * are associated to the submitted reception.
413 *
414 *******************************************************************************/
415static inline void
417 const spm_int_t *recvsizes,
418 int distribution,
419 spm_req_manager_t *reqmanager )
420{
421 MPI_Request *request;
422 spm_int_t *colptr, *rowptr;
423 char *values;
424 size_t fltsize, nbval;
425 spm_int_t nbelt;
426 int c;
427
428 fltsize = spm_size_of( newspm->flttype );
429
430 /* Initialize reqtab array */
431 {
432 int nbcomm_per_node = ( newspm->flttype == SpmPattern ) ? 2 : 3;
433 int nbcomm_max = ( newspm->clustnbr - 1 ) * nbcomm_per_node;
434
435 /* Let's double for send/recv */
436 nbcomm_max *= 2;
437
438 memset( reqmanager, 0, sizeof( spm_req_manager_t ) );
439 reqmanager->requests = malloc( nbcomm_max * sizeof( MPI_Request ) );
440 for ( int i = 0; i < nbcomm_max; i++ ) {
441 reqmanager->requests[i] = MPI_REQUEST_NULL;
442 }
443 reqmanager->nbreq_max = nbcomm_max;
444 }
445
446 /* Get the correct array pointers */
447 if ( distribution & SpmDistByColumn ) {
448 colptr = newspm->colptr;
449 rowptr = newspm->rowptr;
450 }
451 else {
452 colptr = newspm->rowptr;
453 rowptr = newspm->colptr;
454 }
455 values = newspm->values;
456
457 /* Let's shift after the local information */
458 nbelt = recvsizes[2 * newspm->clustnum];
459 nbval = recvsizes[2 * newspm->clustnum + 1] * fltsize;
460 colptr += nbelt;
461 rowptr += nbelt;
462 values += nbval;
463
464 request = reqmanager->requests;
465 for ( c = 0; c < newspm->clustnbr; c++ )
466 {
467 if ( c == newspm->clustnum ) {
468 continue;
469 }
470 nbelt = recvsizes[2 * c];
471 nbval = recvsizes[2 * c + 1] * fltsize;
472
473 if ( nbelt == 0 ) {
474 assert( nbval == 0 );
475 continue;
476 }
477
478 /* Post receptions */
479 MPI_Irecv( colptr, nbelt, SPM_MPI_INT, c,
480 COLTAG, newspm->comm, request );
481 request++;
482
483 MPI_Irecv( rowptr, nbelt, SPM_MPI_INT, c,
484 ROWTAG, newspm->comm, request );
485 request++;
486
487 if ( fltsize != 0 ) {
488 MPI_Irecv( values, nbval, MPI_CHAR, c,
489 VALTAG, newspm->comm, request );
490 request++;
491 }
492
493 colptr += nbelt;
494 rowptr += nbelt;
495 values += nbval;
496 }
497
498 reqmanager->nbreq = request - reqmanager->requests;
499 return;
500}
501
502/**
503 * @brief Structure to stire the information to send in spmRedistribute()
504 */
505struct spm_send_data_s {
506 spm_int_t *rowptr; /**< Pointer to the rowptr to send */
507 spm_int_t *colptr; /**< Pointer to the colptr to send */
508 char *values; /**< Pointer to the values to send */
509 spm_int_t nbelt, nbelt_to_send; /**< Nb elt to send */
510 size_t nbval, nbval_to_send; /**< Nb values (in byte) to send */
511 int sent; /**< Boolean to indicate if the send has been done */
512};
513
514/**
515 *******************************************************************************
516 *
517 * @brief Submit the send request if the buffers are filled.
518 *
519 *******************************************************************************
520 *
521 * @param[inout] reqmanager
522 * The data structure that holds the requests to wait for completion.
523 *
524 * @param[inout] sendproc
525 * The data structure that holds the data to send.
526 *
527 * @param[in] dest
528 * The destination rank of the communications.
529 *
530 * @param[in] comm
531 * The MPI communicator to use for the communication
532 *
533 *******************************************************************************/
534static inline void
536 struct spm_send_data_s *sendproc,
537 int dest,
538 MPI_Comm comm )
539{
540 assert( sendproc->sent == 0 );
541
542 if ( sendproc->nbelt != sendproc->nbelt_to_send ) {
543 return;
544 }
545
546 /* Send the colptr info */
547 assert( reqmanager->nbreq < reqmanager->nbreq_max );
548 MPI_Isend( sendproc->colptr, sendproc->nbelt_to_send, SPM_MPI_INT,
549 dest, COLTAG, comm, reqmanager->requests + reqmanager->nbreq );
550 reqmanager->nbreq++;
551
552 /* Send the rowptr info */
553 assert( reqmanager->nbreq < reqmanager->nbreq_max );
554 MPI_Isend( sendproc->rowptr, sendproc->nbelt_to_send, SPM_MPI_INT,
555 dest, ROWTAG, comm, reqmanager->requests + reqmanager->nbreq );
556 reqmanager->nbreq++;
557
558 /* Send the values info */
559 if ( sendproc->nbval_to_send > 0 ) {
560 assert( reqmanager->nbreq < reqmanager->nbreq_max );
561 MPI_Isend( sendproc->values, sendproc->nbval_to_send, MPI_CHAR,
562 dest, VALTAG, comm, reqmanager->requests + reqmanager->nbreq );
563 reqmanager->nbreq++;
564 }
565
566 sendproc->sent = 1;
567
568 /* Test the communications to make them progress */
569 spm_redist_reqmanager_test( reqmanager );
570
571 return;
572}
573
574/**
575 *******************************************************************************
576 *
577 * @brief Fill all the send arrays from CSC/CSR format to IJV buffers and submit
578 * the send.
579 *
580 *******************************************************************************
581 *
582 * @param[in] oldspm
583 * The original sparse matrix to redistribute.
584 *
585 * @param[in] newg2l
586 * The new spm glob2loc array [-baseval].
587 *
588 * @param[inout] senddata
589 * The allocated data structure to holds the information to send.
590 * On exit, the buffers are filled and associated send requests are
591 * submitted but may not be completed.
592 *
593 * @param[inout] reqmanager
594 * The data structure that holds the requests to wait for completion.
595 *
596 *******************************************************************************/
597static inline void
599 const spm_int_t *newg2l,
600 struct spm_send_data_s *senddata,
601 spm_req_manager_t *reqmanager )
602{
603 struct spm_send_data_s *sendproc;
604
605 const spm_int_t *oldcol = ( oldspm->fmttype == SpmCSC ) ? oldspm->colptr : oldspm->rowptr;
606 const spm_int_t *oldrow = ( oldspm->fmttype == SpmCSC ) ? oldspm->rowptr : oldspm->colptr;
607 const char *oldval = oldspm->values;
608 const spm_int_t *dofs = oldspm->dofs - oldspm->baseval;
609 const spm_int_t *oldl2g = oldspm->loc2glob;
610
611 spm_int_t dof = oldspm->dof;
612 spm_int_t fltsize = spm_size_of( oldspm->flttype );
613
614 spm_int_t i, ig, j, jg;
615 spm_int_t nbrow, nbval;
616 size_t valsize;
617 int owner;
618
619 for ( j=0; j<oldspm->n;
620 j++, oldl2g++, oldcol++, oldrow += nbrow, oldval += valsize )
621 {
622 jg = *oldl2g;
623 nbrow = oldcol[1] - oldcol[0];
624 nbval = 0;
625
626 /* Compute the number of values in the expanded value array */
627 if ( fltsize != 0 ) {
628 spm_int_t dofj = (dof > 0) ? dof : dofs[jg+1] - dofs[jg];
629 spm_int_t dofi = 0;
630 for ( i = 0; i < nbrow; i++ ) {
631 ig = oldrow[i];
632 dofi += (dof > 0) ? dof : dofs[ig+1] - dofs[ig];
633 }
634 nbval = dofj * dofi;
635 }
636 valsize = nbval * fltsize;
637
638 /* This column remains local or is empty, let's skip it */
639 if ( ( newg2l[jg] >= 0 ) ||
640 ( nbrow == 0 ) )
641 {
642 continue;
643 }
644
645 owner = -newg2l[jg] - 1;
646 assert( owner != oldspm->clustnum );
647
648 sendproc = senddata + owner;
649 assert( sendproc->sent == 0 );
650
651 for ( i=0; i<nbrow; i++ ) {
652 sendproc->colptr[ sendproc->nbelt ] = jg;
653 sendproc->rowptr[ sendproc->nbelt ] = oldrow[i];
654 sendproc->nbelt++;
655 }
656
657 memcpy( sendproc->values + sendproc->nbval, oldval, valsize );
658 sendproc->nbval += valsize;
659
660 spm_redist_reqmanager_try_sendone( reqmanager, sendproc, owner, oldspm->comm );
661 }
662
663 return;
664}
665
666/**
667 *******************************************************************************
668 *
669 * @brief Fill all the send arrays from IJV format to IJV buffers and submit
670 * the send.
671 *
672 *******************************************************************************
673 *
674 * @param[in] oldspm
675 * The original sparse matrix to redistribute.
676 *
677 * @param[in] newg2l
678 * The new spm glob2loc array [-baseval].
679 *
680 * @param[inout] senddata
681 * The allocated data structure to holds the information to send.
682 * On exit, the buffers are filled and associated send requests are
683 * submitted but may not be completed.
684 *
685 * @param[inout] reqmanager
686 * The data structure that holds the requests to wait for completion.
687 *
688 * @param[in] distribution
689 * The distribution of the original sparse matrix.
690 *
691 *******************************************************************************/
692static inline void
694 const spm_int_t *newg2l,
695 struct spm_send_data_s *senddata,
696 spm_req_manager_t *reqmanager,
697 int distribution )
698{
699 struct spm_send_data_s *sendproc;
700
701 const spm_int_t *oldcol = ( distribution & SpmDistByColumn ) ? oldspm->colptr : oldspm->rowptr;
702 const spm_int_t *oldrow = ( distribution & SpmDistByColumn ) ? oldspm->rowptr : oldspm->colptr;
703 const char *oldval = oldspm->values;
704 const spm_int_t *dofs = oldspm->dofs - oldspm->baseval;
705
706 spm_int_t dof = oldspm->dof;
707 spm_int_t fltsize = spm_size_of( oldspm->flttype );
708
709 spm_int_t ig, j, jg;
710 spm_int_t nbval;
711 size_t valsize;
712 int owner;
713
714 for ( j=0; j<oldspm->nnz; j++, oldcol++, oldrow++, oldval+=valsize )
715 {
716 jg = *oldcol;
717 ig = *oldrow;
718 nbval = 0;
719
720 if ( fltsize != 0 ) {
721 spm_int_t dofj = ( dof > 0 ) ? dof : dofs[jg+1] - dofs[jg];
722 spm_int_t dofi = ( dof > 0 ) ? dof : dofs[ig+1] - dofs[ig];
723 nbval = dofj * dofi;
724 }
725 valsize = nbval * fltsize;
726
727 /* This element remains local, let's skip it */
728 if ( newg2l[jg] >= 0 ) {
729 continue;
730 }
731
732 owner = -newg2l[jg] - 1;
733 assert( owner != oldspm->clustnum );
734
735 sendproc = senddata + owner;
736 assert( sendproc->sent == 0 );
737
738 sendproc->colptr[sendproc->nbelt] = jg;
739 sendproc->rowptr[sendproc->nbelt] = ig;
740 sendproc->nbelt++;
741
742 memcpy( sendproc->values + sendproc->nbval, oldval, valsize );
743 sendproc->nbval += valsize;
744
745 spm_redist_reqmanager_try_sendone( reqmanager, sendproc, owner, oldspm->comm );
746 }
747
748 return;
749}
750
751/**
752 *******************************************************************************
753 *
754 * @brief Fill all the send arrays and send them.
755 *
756 *******************************************************************************
757 *
758 * @param[in] oldspm
759 * The sparse matrix to redistribute.
760 *
761 * @param[in] newg2l
762 * The new glob2loc array.
763 *
764 * @param[in] sendsizes
765 * The array that store the number of elements to send
766 * for each process : {col_sends, row_sends, val_sends}
767 *
768 * @param[inout] reqmanager
769 * The data structure that holds the requests to wait for completion.
770 *
771 * @param[in] distribution
772 * The distribution of the original sparse matrix.
773 *
774 *******************************************************************************/
775static inline void
777 const spm_int_t *newg2l,
778 const spm_int_t *sendsizes,
779 spm_req_manager_t *reqmanager,
780 int distribution )
781{
782 struct spm_send_data_s *senddata = calloc( oldspm->clustnbr, sizeof( struct spm_send_data_s ) );
783
784 spm_int_t baseval = oldspm->baseval;
785 int c;
786
787 /* Allocate the data structure per remote process */
788 for ( c = 0; c < oldspm->clustnbr; c++ )
789 {
790 if ( c == oldspm->clustnum ) {
791 continue;
792 }
793
794 senddata[c].nbelt_to_send = sendsizes[2 * c];
795 senddata[c].nbval_to_send = sendsizes[2 * c + 1] * spm_size_of( oldspm->flttype );
796
797 senddata[c].rowptr = malloc( senddata[c].nbelt_to_send * sizeof( spm_int_t ) );
798 senddata[c].colptr = malloc( senddata[c].nbelt_to_send * sizeof( spm_int_t ) );
799 senddata[c].values = malloc( senddata[c].nbval_to_send );
800 }
801
802 if ( oldspm->fmttype != SpmIJV ) {
803 spm_redist_send_loop_csx( oldspm, newg2l - baseval, senddata, reqmanager );
804 }
805 else {
806 spm_redist_send_loop_ijv( oldspm, newg2l - baseval, senddata, reqmanager, distribution );
807 }
808
809 spm_redist_reqmanager_wait( reqmanager );
810
811 /* Free the data structure per remote process */
812 for ( c = 0; c < oldspm->clustnbr; c++ )
813 {
814 if ( c == oldspm->clustnum ) {
815 continue;
816 }
817
818 free( senddata[c].rowptr );
819 free( senddata[c].colptr );
820 free( senddata[c].values );
821 }
822 free( senddata );
823}
824
825/**
826 *******************************************************************************
827 *
828 * @brief Finalize the compuration of the newspm to correspond to
829 * oldspm redistributed thanks to newl2g.
830 *
831 *******************************************************************************
832 *
833 * @param[in] oldspm
834 * The sparse matrix to redistribute.
835 *
836 * @param[inout] newspm
837 * The new spm.
838 *
839 * @param[in] newg2l
840 * The new glob2loc array. Will be stored in the newspm.
841 *
842 * @param[in] newl2g
843 * The new loc2glob array. Will be copied in the newspm.
844 *
845 * @param[in] new_n
846 * Size of the new loc2glob.
847 *
848 *******************************************************************************/
849static inline void
851 spmatrix_t *newspm,
852 const spm_int_t *newg2l,
853 const spm_int_t *newl2g,
854 spm_int_t new_n )
855{
856 /* Copy the same datas */
857 newspm->baseval = oldspm->baseval;
858 newspm->mtxtype = oldspm->mtxtype;
859 newspm->flttype = oldspm->flttype;
860 newspm->layout = oldspm->layout;
861 newspm->dof = oldspm->dof;
862
863 /* Copy dofs if needed */
864 if ( oldspm->dofs != NULL ) {
865 newspm->dofs = malloc( (oldspm->gN + 1) * sizeof(spm_int_t) );
866 memcpy( newspm->dofs, oldspm->dofs, (oldspm->gN + 1) * sizeof(spm_int_t) );
867 }
868
869 /* Set specific datas */
870 newspm->n = new_n;
871 newspm->loc2glob = malloc( new_n * sizeof(spm_int_t) );
872 memcpy( newspm->loc2glob, newl2g, new_n * sizeof(spm_int_t) );
873 newspm->glob2loc = (spm_int_t *)newg2l;
874
875 /* Get gN, gnnz, nexp, gnnzexp */
876 spmUpdateComputedFields( newspm );
877
878 /* Check that global info are identical to the original one */
879 assert( (newspm->gN == oldspm->gN) &&
880 (newspm->gnnz == oldspm->gnnz) &&
881 (newspm->gNexp == oldspm->gNexp) &&
882 (newspm->gnnzexp == oldspm->gnnzexp) );
883
884 if ( oldspm->fmttype != SpmIJV ) {
885 spmConvert( oldspm->fmttype, newspm );
886 }
887 else {
888 spmSort( newspm );
889 }
890}
891/**
892 * @}
893 */
894
895/**
896 *******************************************************************************
897 *
898 * @ingroup spm
899 *
900 * @brief Create a copy of a given SPM with a new distribution corresponding to
901 * the given loc2glob array.
902 *
903 *******************************************************************************
904 *
905 * @param[in] spm
906 * The original sparse matrix to redistribute.
907 * If spm->loc2glob == NULL, the spm is just scattered based on
908 * loc2glob distribution to create the new one.
909 *
910 * @param[in] new_n
911 * Size of the newl2g array. Must be > 0. Not referenced if newl2g == NULL.
912 *
913 * @param[in] newl2g
914 * Array of size new_ with the same base value as the input spm.
915 * Contains the distribution array for the new distributed sparse matrix.
916 * If NULL, the spm will be gathered to create the new one.
917 *
918 * @param[inout] newspm
919 * On entry, the allocated spm structure.
920 * On exit, the structure contains the redistributed matrix based on
921 * the newl2g distribution.
922 *
923 *******************************************************************************
924 *
925 * @retval SPM_SUCCESS on success,
926 * @retval SPM_ERR_BADPARAMETER otherwise.
927 *
928 *******************************************************************************/
929int
931 spm_int_t new_n,
932 const spm_int_t *newl2g,
933 spmatrix_t *newspm )
934{
935 spm_req_manager_t reqmanager;
936 spm_int_t *newg2l;
937 spm_int_t *sendsizes, *recvsizes;
938 int distribution;
939
940 /* New loc2glob is NULL, gather the spm */
941 if ( newl2g == NULL ) {
942 return spmGather( spm, -1, newspm );
943 }
944
945 /*
946 * The spm wasn't distributed
947 * We scatter it on all nodes.
948 */
949 if ( spm->replicated ) {
950 int distByColumn = ( spm->fmttype == SpmCSR ) ? 0 : 1;
951 assert( spm->loc2glob == NULL );
952 return spmScatter( newspm, -1, spm, new_n, newl2g, distByColumn, spm->comm );
953 }
954
955 /* Get the global to local indices array for the new spm */
956 newg2l = spm_redist_get_newg2l( spm, new_n, newl2g );
957
958 /* ( nbelts, nbvals ) for each process */
959 sendsizes = malloc( 2 * spm->clustnbr * sizeof( spm_int_t ) );
960 /* ( nbelts, nbvals ) for each process */
961 recvsizes = malloc( 2 * spm->clustnbr * sizeof( spm_int_t ) );
962
963 distribution = spm_get_distribution( spm );
964
965 /* Extract the local info and compute sendsizes/recvsizes */
966 spm_redist_extract_local( spm, newg2l - spm->baseval, distribution,
967 sendsizes, recvsizes, newspm );
968
969 /*
970 * Allocate the request array
971 * Post the asynchronous receive
972 */
973 spm_redist_post_recv( newspm, recvsizes, distribution, &reqmanager );
974 free( recvsizes );
975
976 /*
977 * For each node, go throught the oldspm and find the remote datas
978 * Store them in the sendptr / sendval.
979 */
980 spm_redist_send( spm, newg2l, sendsizes, &reqmanager, distribution );
981 free( sendsizes );
982 free( reqmanager.requests );
983
984 /* Finalize redistribution computation */
985 spm_redist_finalize( spm, newspm, newg2l, newl2g, new_n );
986
987 return SPM_SUCCESS;
988}
static size_t spm_size_of(spm_coeftype_t type)
Double datatype that is not converted through precision generator functions.
Definition datatypes.h:209
#define SpmDistByColumn
Distribution of the matrix storage.
Definition const.h:42
@ SPM_SUCCESS
Definition const.h:82
@ SpmPattern
Definition const.h:62
@ SpmCSC
Definition const.h:73
@ SpmCSR
Definition const.h:74
@ SpmIJV
Definition const.h:75
spm_coeftype_t flttype
Definition spm.h:67
spm_int_t * dofs
Definition spm.h:85
void * values
Definition spm.h:92
int clustnbr
Definition spm.h:96
spm_int_t nnz
Definition spm.h:75
spm_int_t nnzexp
Definition spm.h:80
spm_int_t * rowptr
Definition spm.h:90
spm_int_t gnnz
Definition spm.h:74
spm_fmttype_t fmttype
Definition spm.h:69
spm_int_t dof
Definition spm.h:82
spm_int_t n
Definition spm.h:73
spm_mtxtype_t mtxtype
Definition spm.h:65
spm_int_t * loc2glob
Definition spm.h:91
spm_layout_t layout
Definition spm.h:87
SPM_Comm comm
Definition spm.h:97
spm_int_t baseval
Definition spm.h:71
spm_int_t * glob2loc
Definition spm.h:94
spm_int_t gnnzexp
Definition spm.h:79
spm_int_t * colptr
Definition spm.h:89
spm_int_t gNexp
Definition spm.h:77
int clustnum
Definition spm.h:95
spm_int_t gN
Definition spm.h:72
int replicated
Definition spm.h:98
int spmGather(const spmatrix_t *spm_scattered, int root, spmatrix_t *opt_spm_gathered)
Gather a distributed Sparse Matrix on a single node.
Definition spm_gather.c:436
#define SPM_MPI_INT
The MPI type associated to spm_int_t.
Definition datatypes.h:72
void spmUpdateComputedFields(spmatrix_t *spm)
Update all the computed fields based on the static values stored.
int spmScatter(spmatrix_t *spm_scattered, int root, const spmatrix_t *opt_spm_gathered, spm_int_t opt_n, const spm_int_t *opt_loc2glob, int opt_distByColumn, SPM_Comm opt_comm)
Scatter the SPM thanks to loc2glob.
int spm_int_t
The main integer datatype used in spm arrays.
Definition datatypes.h:70
void spmInitDist(spmatrix_t *spm, SPM_Comm comm)
Init the spm structure with a specific communicator.
Definition spm.c:101
int spmConvert(int ofmttype, spmatrix_t *ospm)
Convert the storage format of the spm.
Definition spm.c:418
int spmRedistribute(const spmatrix_t *spm, spm_int_t new_n, const spm_int_t *newl2g, spmatrix_t *newspm)
Create a copy of a given SPM with a new distribution corresponding to the given loc2glob array.
int spmSort(spmatrix_t *spm)
Sort the subarray of edges of each vertex.
Definition spm.c:746
The sparse matrix data structure.
Definition spm.h:64
spm_int_t * spm_getandset_glob2loc(spmatrix_t *spm)
Computes the glob2loc array if needed, and returns it.
Definition spm.c:2007
int spm_get_distribution(const spmatrix_t *spm)
Search the distribution pattern used in the spm structure.
Definition spm.c:2049
#define VALTAG
Internal tag for values array communications.
static void spm_redist_reqmanager_test(spm_req_manager_t *reqmanager)
Make the communications progress in the request manager structure.
#define COLTAG
Internal tag for colptr array communications.
static void spm_redist_extract_local(const spmatrix_t *oldspm, const spm_int_t *newg2l, int distribution, spm_int_t *sendsizes, spm_int_t *recvsizes, spmatrix_t *newspm)
Extract the local information and compute the volumes to exchange.
static void spm_redist_send_loop_csx(const spmatrix_t *oldspm, const spm_int_t *newg2l, struct spm_send_data_s *senddata, spm_req_manager_t *reqmanager)
Fill all the send arrays from CSC/CSR format to IJV buffers and submit the send.
static void spm_redist_reqmanager_wait(spm_req_manager_t *reqmanager)
Wait for all the communications to be completed.
static void spm_redist_finalize(const spmatrix_t *oldspm, spmatrix_t *newspm, const spm_int_t *newg2l, const spm_int_t *newl2g, spm_int_t new_n)
Finalize the compuration of the newspm to correspond to oldspm redistributed thanks to newl2g.
static spm_int_t * spm_redist_get_newg2l(const spmatrix_t *oldspm, spm_int_t new_n, const spm_int_t *newl2g)
Get the new glob2loc array.
#define ROWTAG
Internal tag for rowptr array communications.
static void spm_redist_post_recv(spmatrix_t *newspm, const spm_int_t *recvsizes, int distribution, spm_req_manager_t *reqmanager)
Allocate the reqtab array and post all the required receptions for each process.
static void spm_redist_send_loop_ijv(const spmatrix_t *oldspm, const spm_int_t *newg2l, struct spm_send_data_s *senddata, spm_req_manager_t *reqmanager, int distribution)
Fill all the send arrays from IJV format to IJV buffers and submit the send.
struct spm_req_manager_s spm_req_manager_t
Data structure to manage the communication requests.
static void spm_redist_send(const spmatrix_t *oldspm, const spm_int_t *newg2l, const spm_int_t *sendsizes, spm_req_manager_t *reqmanager, int distribution)
Fill all the send arrays and send them.
static void spm_redist_reqmanager_try_sendone(spm_req_manager_t *reqmanager, struct spm_send_data_s *sendproc, int dest, MPI_Comm comm)
Submit the send request if the buffers are filled.