SpM Handbook 1.2.4
Loading...
Searching...
No Matches
spm_symmetrize.c
Go to the documentation of this file.
1/**
2 *
3 * @file spm_symmetrize.c
4 *
5 * SParse Matrix package symmetrize routines.
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 * @date 2024-06-25
15 *
16 * @remark All routines in this files consider the order (j, i) as we usually
17 * store the matrix in CSC format.
18 *
19 **/
20#include "common.h"
21
22/**
23 * @brief Arbitrary tag for the symmetry exchange communications
24 * @remark We may need to use a tag per spm when solving multiple problem in parallel
25 */
26#define TAG_SPM_SYMM 3483
27
28/**
29 *******************************************************************************
30 *
31 * @ingroup spm_dev
32 *
33 * @brief Add a couple (ig, jg) in the list of missing entries of the owner.
34 *
35 *******************************************************************************
36 *
37 * @param[inout] miss_sze
38 * Array of size spm->clustnbr
39 * Contains the allocated sizes of the miss_buf arrays.
40 *
41 * @param[inout] miss_buf
42 * Array of size spm->clustnbr.
43 * Contains the pointer to the allocated array of looked for elements.
44 *
45 * @param[inout] miss_nbr
46 * Array of size spm->clustnbr.
47 * Contains the number of looked for entries per process.
48 *
49 * @param[in] jg
50 * Global column index of the looked for element.
51 *
52 * @param[in] ig
53 * Global row index of the looked for element.
54 *
55 * @param[in] owner
56 * Index of the owner of the looked for element.
57 *
58 ********************************************************************************/
59static inline void
61 spm_int_t **miss_buf,
62 spm_int_t *miss_nbr,
63 spm_int_t jg,
64 spm_int_t ig,
65 int owner )
66{
67 spm_int_t *newelem;
68
69 /* The buffer is full, we need to extend it */
70 if ( miss_nbr[ owner ] >= miss_sze[ owner ] ) {
71 miss_sze[ owner ] *= 2;
72 miss_buf[ owner ] = (spm_int_t*)realloc( miss_buf[ owner ],
73 miss_sze[ owner ] * 2 * sizeof(spm_int_t) );
74 }
75 newelem = miss_buf[ owner ] + 2 * miss_nbr[ owner ];
76
77 /* Store column first (CSC) */
78 newelem[0] = jg;
79 newelem[1] = ig;
80
81 miss_nbr[ owner ]++;
82}
83
84/**
85 *******************************************************************************
86 *
87 * @ingroup spm_dev
88 *
89 * @brief Search locally if an element exists.
90 *
91 *******************************************************************************
92 *
93 * @param[in] colptr
94 * Colptr of the local spm.
95 *
96 * @param[in] rowptr
97 * Rowptr of the local spm.
98 *
99 * @param[in] jl
100 * Local column index of the looked for element. 0-based.
101 *
102 * @param[in] ig
103 * Global row index of the looked for element. O-based.
104 *
105 * @param[in] baseval
106 * Baseval of the spm.
107 *
108 *******************************************************************************
109 *
110 * @retval 1 if a symmetric element is found at (jl, ig);
111 * @retval 0 otherwise.
112 *
113 ********************************************************************************/
114static inline int
116 const spm_int_t *rowptr,
117 spm_int_t jl,
118 spm_int_t ig,
119 spm_int_t baseval )
120{
121 spm_int_t k;
122 spm_int_t frow = colptr[jl] - baseval;
123 spm_int_t lrow = colptr[jl+1] - baseval;
124 int found = 0;
125
126 for ( k=frow; k < lrow; k++ )
127 {
128 spm_int_t row = rowptr[k] - baseval;
129 if ( ig == row )
130 {
131 return 1;
132 }
133 else if ( ig < row )
134 {
135 /* The spm is sorted so we will not find it later */
136 return 0;
137 }
138 }
139
140 return found;
141}
142
143/**
144 *******************************************************************************
145 *
146 * @ingroup spm_dev
147 *
148 * @brief Check the local symmetry of the pattern.
149 *
150 * This function checks the local symmetry of the pattern, and for all missing
151 * elements stores them in the miss_xxx arrays for later checks.
152 * Local miss will need to be added.
153 * Remote miss will need to be first check for existence, and eventually be
154 * added to the remote structure.
155 *
156 *******************************************************************************
157 *
158 * @param[inout] spm
159 * The spm for which the pattern symmetry must be checked.
160 * On exit, the glob2loc array is allocated if need for later checks.
161 *
162 * @param[inout] miss_sze
163 * Array of size spm->clustnbr
164 * Contains the allocated sizes of the miss_buf arrays.
165 *
166 * @param[inout] miss_buf
167 * Array of size spm->clustnbr.
168 * Contains the pointer to the allocated array of looked for elements.
169 *
170 * @param[inout] miss_nbr
171 * Array of size spm->clustnbr.
172 * Contains the number of looked for entries per process.
173 *
174 ********************************************************************************/
175static inline void
177 spm_int_t *miss_sze,
178 spm_int_t **miss_buf,
179 spm_int_t *miss_nbr )
180{
181 const spm_int_t *colptr = (spm->fmttype == SpmCSC) ? spm->colptr : spm->rowptr;
182 const spm_int_t *rowptr = (spm->fmttype == SpmCSC) ? spm->rowptr : spm->colptr;
183 const spm_int_t *glob2loc = spm_getandset_glob2loc( spm );
184 const spm_int_t *loc2glob = spm->loc2glob;
185 const spm_int_t *coltmp = colptr;
186 const spm_int_t *rowtmp = rowptr;
187 spm_int_t baseval = spm->baseval;
188 spm_int_t il, ig, jl, jg, k;
189
190 for (jl=0; jl<spm->n; jl++, coltmp++, loc2glob++)
191 {
192 jg = spm->replicated ? jl : *loc2glob - baseval;
193
194 for ( k=coltmp[0]; k<coltmp[1]; k++, rowtmp++ )
195 {
196 ig = *rowtmp - baseval;
197
198 /* If diagonal element */
199 if ( ig == jg ) {
200 continue;
201 }
202
203 il = (glob2loc == NULL) ? ig : glob2loc[ig];
204
205#if defined(SPM_WITH_MPI)
206 /*
207 * ( jg, ig ) is a remote element
208 * Accumulate ( jg, ig ) on the owner buffer and continue
209 */
210 if( il < 0 ) {
211 int owner = (int)(- il - 1);
212 assert(owner != spm->clustnum);
213 spm_symm_add_missing_elt( miss_sze, miss_buf, miss_nbr,
214 ig, jg, owner );
215 continue;
216 }
217#endif
218 assert( il >= 0 );
219
220 /* Look for the local symmetric element ( jg, ig ) */
221 if ( !spm_symm_local_search( colptr, rowptr, il, jg, baseval ) ) {
222 /* For local missing element we store the local column index */
223 spm_symm_add_missing_elt( miss_sze, miss_buf, miss_nbr,
224 il, jg, spm->clustnum );
225 }
226 }
227 }
228}
229
230#if defined(SPM_WITH_MPI)
231/**
232 *******************************************************************************
233 *
234 * @ingroup spm_dev_mpi
235 *
236 * @brief Check remote entries that should be stored locally. If they are not
237 * available, let's add them to the local missing array.
238 *
239 *******************************************************************************
240 *
241 * @param[in] spm
242 * The spm for which the pattern symmetry must be checked.
243 *
244 * @param[inout] miss_sze
245 * Array of size spm->clustnbr
246 * Contains the allocated sizes of the miss_buf arrays.
247 *
248 * @param[inout] miss_buf
249 * Array of size spm->clustnbr.
250 * Contains the pointer to the allocated array of looked for elements.
251 *
252 * @param[inout] miss_nbr
253 * Array of size spm->clustnbr.
254 * Contains the number of looked for entries per process.
255 *
256 * @param[in] nbrecv
257 * Number of elements received from a remote node.
258 *
259 * @param[in] buffer
260 * Buffer with the remote element to look for and add if not present.
261 *
262 ********************************************************************************/
263static inline void
264spm_symm_check_remote( spmatrix_t *spm,
265 spm_int_t *miss_sze,
266 spm_int_t **miss_buf,
267 spm_int_t *miss_nbr,
268 spm_int_t nbrecv,
269 const spm_int_t *buffer )
270{
271 const spm_int_t *colptr = (spm->fmttype == SpmCSC) ? spm->colptr : spm->rowptr;
272 const spm_int_t *rowptr = (spm->fmttype == SpmCSC) ? spm->rowptr : spm->colptr;
273 const spm_int_t *glob2loc = spm->glob2loc;
274 spm_int_t k, ig, jl, jg;
275
276 /*
277 * Glob2loc must have been initialized earlier in a function called by all
278 * processes to avoid communication dead locks
279 */
280 assert( glob2loc != NULL );
281
282 for ( k=0; k<nbrecv; k++, buffer+=2 )
283 {
284 jg = buffer[0];
285 ig = buffer[1];
286
287 /* Get the local index */
288 jl = glob2loc[ jg ];
289 assert( (jl >= 0) && (jl < spm->n) );
290
291 /* If the ( ig, jg ) couple is in the SPM, continue. */
292 if( !spm_symm_local_search( colptr, rowptr, jl, ig, spm->baseval ) ) {
293 /* For local missing element we store the local column index */
294 spm_symm_add_missing_elt( miss_sze, miss_buf, miss_nbr,
295 jl, ig, spm->clustnum );
296 }
297 }
298}
299
300/**
301 *******************************************************************************
302 *
303 * @ingroup spm_dev_mpi
304 *
305 * @brief Gather the remote data from all nodes
306 *
307 * This routines exchanges the missing entries between the nodes, check if they
308 * are owned locally, and if not, insert them in the local missing entries
309 * array.
310 *
311 *******************************************************************************
312 *
313 * @param[in] spm
314 * Pointer to the spm matrix
315 *
316 * @param[inout] miss_sze
317 * Array of size spm->clustnbr
318 * Contains the allocated sizes of the miss_buf arrays.
319 *
320 * @param[inout] miss_buf
321 * Array of size spm->clustnbr.
322 * Contains the pointer to the allocated array of looked for elements.
323 *
324 * @param[inout] miss_nbr
325 * Array of size spm->clustnbr.
326 * Contains the number of looked for entries per process.
327 *
328 ********************************************************************************/
329static inline void
330spm_symm_remote_exchange( spmatrix_t *spm,
331 spm_int_t *miss_sze,
332 spm_int_t **miss_buf,
333 spm_int_t *miss_nbr )
334{
335 int clustnum = spm->clustnum;
336 int clustnbr = spm->clustnbr;
337 spm_int_t maxrecv = 0;
338 spm_int_t maxsend = 0;
339 MPI_Request *requests = malloc( clustnbr * sizeof(MPI_Request) );
340 MPI_Status status;
341 spm_int_t *recvcnts = malloc( clustnbr * sizeof(spm_int_t) );
342 spm_int_t *buffer = NULL;
343 int c, rc;
344
345 /* Exchange the number of data to send/recv from every nodes */
346 MPI_Alltoall( miss_nbr, 1, SPM_MPI_INT,
347 recvcnts, 1, SPM_MPI_INT, spm->comm );
348
349 /* Start the send communications and check if we need to receive something */
350 for ( c=0; c<clustnbr; c++ )
351 {
352 requests[c] = MPI_REQUEST_NULL;
353 if ( c == clustnum ) {
354 continue;
355 }
356
357 /* Store info that we will have some reception to do */
358 maxrecv = spm_imax( maxrecv, recvcnts[c] );
359
360 /* Start send if needed */
361 if ( miss_nbr[c] > 0 ) {
362 maxsend = spm_imax( maxsend, miss_nbr[c] );
363
364 MPI_Isend( miss_buf[c], 2 * miss_nbr[c], SPM_MPI_INT,
365 c, TAG_SPM_SYMM, spm->comm, requests + c );
366 }
367 }
368
369 /* Quick return */
370 if ( (maxrecv == 0) && (maxsend == 0) ) {
371 free( requests );
372 free( recvcnts );
373 return;
374 }
375
376 if ( maxrecv > 0 ) {
377 buffer = malloc( 2 * maxrecv * sizeof(spm_int_t) );
378 }
379
380 /* Glob2loc is required to check the symmetry. Let's initialize it here
381 while we are sure everyon enters the functions */
383
384 /* Gather the data on our local buffer */
385 for ( c=0; c<clustnbr; c++ )
386 {
387 if ( (c == clustnum) || (recvcnts[c] == 0) ) {
388 continue;
389 }
390 rc = MPI_Recv( buffer, 2 * recvcnts[c], SPM_MPI_INT,
391 c, TAG_SPM_SYMM, spm->comm, &status );
392 if ( rc != MPI_SUCCESS ) {
393 char errmsg[MPI_MAX_ERROR_STRING];
394 int len;
395
396 MPI_Error_string( status.MPI_ERROR, errmsg, &len );
397 fprintf( stderr, "Recv: %s\n", errmsg );
398 }
399 assert( rc == MPI_SUCCESS );
400
401 /*
402 * Check locally if we have the requested symmetry, if not let's add
403 * them to our local missing array
404 */
405 spm_symm_check_remote( spm, miss_sze, miss_buf, miss_nbr,
406 recvcnts[c], buffer );
407 }
408 free( recvcnts );
409 free( buffer );
410
411 /* Wait for ongoing communications */
412 for ( c=0; c<clustnbr; c++ )
413 {
414 if ( requests[c] != MPI_REQUEST_NULL )
415 {
416 assert( miss_nbr[c] > 0 );
417 rc = MPI_Wait( requests + c, &status );
418 if ( rc != MPI_SUCCESS ) {
419 char errmsg[MPI_MAX_ERROR_STRING];
420 int len;
421
422 MPI_Error_string( status.MPI_ERROR, errmsg, &len );
423 fprintf( stderr, "Wait(send): %s\n", errmsg );
424 }
425 assert( rc == MPI_SUCCESS );
426 }
427
428 if ( c != clustnum ) {
429 free( miss_buf[c] );
430 miss_buf[c] = NULL;
431 }
432 }
433 free( requests );
434
435 return;
436}
437#endif
438
439/**
440 *******************************************************************************
441 *
442 * @ingroup spm_dev_mpi
443 *
444 * @brief Compute the new size of the values array
445 *
446 *******************************************************************************
447 *
448 * @param[in] spm
449 * Pointer to the spm matrix
450 *
451 * @param[in] miss_nbr
452 * Number of local missing entries.
453 *
454 * @param[in] miss_buf
455 * Array of missing entries.
456 *
457 *******************************************************************************
458 *
459 * @return The new size of the values array in number of entries.
460 *
461 ********************************************************************************/
462static inline spm_int_t
464 spm_int_t miss_nbr,
465 const spm_int_t *miss_buf )
466{
467 const spm_int_t *dofs = spm->dofs;
468 const spm_int_t *loc2glob = spm->loc2glob;
469 spm_int_t newsize = spm->nnzexp;
470 spm_int_t baseval = spm->baseval;
471 spm_int_t k, il, ig, jg;
472
473 /* Constant dof */
474 if( spm->dof > 0 ) {
475 return newsize + (miss_nbr * spm->dof * spm->dof);
476 }
477
478 /* Variadic dof */
479 for ( k=0; k<miss_nbr; k++, miss_buf+=2 )
480 {
481 il = miss_buf[0];
482 jg = miss_buf[1] ;
483 ig = spm->replicated ? il : loc2glob[ il ] - baseval;
484
485 newsize += (dofs[ig+1] - dofs[ig]) * (dofs[jg+1] - dofs[jg]);
486 }
487
488 return newsize;
489}
490
491/**
492 *******************************************************************************
493 *
494 * @ingroup spm_dev
495 *
496 * @brief Copy the former column into the new one
497 *
498 *******************************************************************************
499 *
500 * @param[inout] spm
501 * On entry, the non symmetric local spm.
502 * On exit, the spm contains the symmetric elements with a value of 0.
503 * @warning The computed fields are not updated. Only nnz and nnzexp are.
504 *
505 * @param[in] eltsize
506 * The element size in byte
507 *
508 * @param[in] jg
509 * The global column index.
510 *
511 * @param[in] colptr
512 * Number of local missing entries.
513 *
514 * @param[inout] oldrow
515 * Pointer to the current position in the former rowptr array.
516 *
517 * @param[inout] newrow
518 * Pointer to the current position in the new rowptr array.
519 *
520 * @param[inout] oldval
521 * Pointer to the current position in the former values array.
522 *
523 * @param[inout] newval
524 * Pointer to the current position in the new values array.
525 *
526 *******************************************************************************
527 *
528 * @return The number of added elements to symmetrize the pattern.
529 *
530 ********************************************************************************/
531static inline spm_int_t
533 size_t eltsize,
534 spm_int_t jg,
535 const spm_int_t *colptr,
536 const spm_int_t **oldrow,
537 spm_int_t **newrow,
538 const char **oldval,
539 char **newval )
540{
541 const spm_int_t *dofs = spm->dofs;
542 spm_int_t baseval = spm->baseval;
543 spm_int_t ig, k;
544 spm_int_t frow, lrow;
545 spm_int_t nbelt, nbval, dofi, dofj;
546
547 frow = colptr[0];
548 lrow = colptr[1];
549 nbelt = lrow - frow;
550
551 dofj = (spm->dof > 0) ? spm->dof : dofs[jg+1] - dofs[jg];
552
553 /* Copy current column */
554 if ( spm->flttype != SpmPattern ) {
555 nbval = 0;
556 for ( k=0; k<nbelt; k++ )
557 {
558 ig = (*oldrow)[k] - baseval;
559 dofi = (spm->dof > 0) ? spm->dof : dofs[ig+1] - dofs[ig];
560 nbval += dofi;
561 }
562 nbval *= dofj;
563 nbval *= eltsize;
564
565 memcpy( *newval, *oldval, nbval );
566 *oldval += nbval;
567 *newval += nbval;
568 }
569
570 /* Copy current column */
571 memcpy( *newrow, *oldrow, nbelt * sizeof(spm_int_t) );
572 *newrow += nbelt;
573 *oldrow += nbelt;
574
575 return nbelt;
576}
577
578/**
579 *******************************************************************************
580 *
581 * @ingroup spm_dev
582 *
583 * @brief Add the missing entries to the local spm.
584 *
585 * This function adds the entries that have been discovered missing in the
586 * previous checks. The spm arrays are realllocated to include these entries.
587 *
588 *******************************************************************************
589 *
590 * @param[inout] spm
591 * On entry, the non symmetric local spm.
592 * On exit, the spm contains the symmetric elements with a value of 0.
593 * @warning The computed fields are not updated. Only nnz and nnzexp are.
594 *
595 * @param[in] eltsize
596 * The element size in byte
597 *
598 * @param[in] jl
599 * The local column index.
600 *
601 * @param[in] jg
602 * The global column index.
603 *
604 * @param[in] colptr
605 * Number of local missing entries.
606 *
607 * @param[inout] oldrowptr
608 * Pointer to the current position in the former rowptr array.
609 *
610 * @param[inout] newrowptr
611 * Pointer to the current position in the new rowptr array.
612 *
613 * @param[inout] oldvalptr
614 * Pointer to the current position in the former values array.
615 *
616 * @param[inout] newvalptr
617 * Pointer to the current position in the new values array.
618 *
619 * @param[inout] miss_nbr
620 * The current remaining number of unnknowns to add.
621 *
622 * @param[inout] miss_buf
623 * The point to the current missing entry to add.
624 *
625 *******************************************************************************
626 *
627 * @return The number of added elements to symmetrize the pattern.
628 *
629 ********************************************************************************/
630static inline spm_int_t
632 size_t eltsize,
633 spm_int_t jl,
634 spm_int_t jg,
635 const spm_int_t *colptr,
636 const spm_int_t **oldrowptr,
637 spm_int_t **newrowptr,
638 const char **oldvalptr,
639 char **newvalptr,
640 spm_int_t *miss_nbr,
641 spm_int_t **miss_buf )
642{
643 const spm_int_t *oldrow = *oldrowptr;
644 spm_int_t *newrow = *newrowptr;
645 const char *oldval = *oldvalptr;
646 char *newval = *newvalptr;
647 const spm_int_t *dofs = spm->dofs;
648 spm_int_t baseval = spm->baseval;
649
650 spm_int_t ig, k;
651 spm_int_t frow, lrow;
652 spm_int_t nbelt, nbval, dofi, dofj;
653
654 frow = colptr[0];
655 lrow = colptr[1];
656
657 dofj = (spm->dof > 0) ? spm->dof : dofs[jg+1] - dofs[jg];
658
659 /* Let's insert the new elements directly in order */
660 nbelt = 0;
661 for ( k=frow; k<lrow; k++, oldrow++, newrow++ )
662 {
663 ig = *oldrow - baseval;
664
665 /* Insert new elements before the current one */
666 while( ((*miss_nbr) > 0) &&
667 ((*miss_buf)[0] == jl) &&
668 ((*miss_buf)[1] < ig) )
669 {
670 spm_int_t miss_ig = (*miss_buf)[1];
671
672 /* Add row */
673 newrow[0] = miss_ig + baseval;
674 newrow++;
675
676 /* Add null values */
677 if ( spm->flttype != SpmPattern ) {
678 dofi = (spm->dof > 0) ? spm->dof : dofs[miss_ig+1] - dofs[miss_ig];
679 nbval = dofi * dofj * eltsize;
680 memset( newval, 0x00, nbval );
681 newval += nbval;
682 }
683
684 nbelt++;
685 (*miss_buf) += 2;
686 (*miss_nbr) -= 1;
687 }
688
689 /* Copy row */
690 newrow[0] = *oldrow;
691
692 /* Copy values */
693 if ( spm->flttype != SpmPattern ) {
694 dofi = (spm->dof > 0) ? spm->dof : dofs[ig+1] - dofs[ig];
695 nbval = dofi * dofj * eltsize;
696 memcpy( newval, oldval, nbval );
697 newval += nbval;
698 oldval += nbval;
699 }
700
701 nbelt++;
702 }
703
704 /* Insert reamining elements in the column */
705 while( ((*miss_nbr) > 0) &&
706 ((*miss_buf)[0] == jl) )
707 {
708 spm_int_t miss_ig = (*miss_buf)[1];
709
710 /* Add row */
711 newrow[0] = miss_ig + baseval;
712 newrow++;
713
714 /* Add null values */
715 if ( spm->flttype != SpmPattern ) {
716 dofi = (spm->dof > 0) ? spm->dof : dofs[miss_ig+1] - dofs[miss_ig];
717 nbval = dofi * dofj * eltsize;
718 memset( newval, 0x00, nbval );
719 newval += nbval;
720 }
721
722 nbelt++;
723 (*miss_buf) += 2;
724 (*miss_nbr) -= 1;
725 }
726
727 *oldrowptr = oldrow;
728 *newrowptr = newrow;
729 *oldvalptr = oldval;
730 *newvalptr = newval;
731 return nbelt;
732}
733
734/**
735 *******************************************************************************
736 *
737 * @ingroup spm_dev
738 *
739 * @brief Add the missing entries to the local spm.
740 *
741 * This function adds the entries that have been discovered missing in the
742 * previous checks. The spm arrays are realllocated to include these entries.
743 *
744 *******************************************************************************
745 *
746 * @param[inout] spm
747 * On entry, the non symmetric local spm.
748 * On exit, the spm contains the symmetric elements with a value of 0.
749 * @warning The computed fields are not updated. Only nnz and nnzexp are.
750 *
751 * @param[in] miss_nbr
752 * Number of local missing entries.
753 *
754 * @param[in] miss_buf
755 * Array of missing entries.
756 *
757 ********************************************************************************/
758static inline void
760 spm_int_t miss_nbr,
761 spm_int_t *miss_buf )
762{
763 spm_int_t *colptr = (spm->fmttype == SpmCSC) ? spm->colptr : spm->rowptr;
764 const spm_int_t *oldrow = (spm->fmttype == SpmCSC) ? spm->rowptr : spm->colptr;
765 const char *oldval = spm->values;
766 const spm_int_t *loc2glob = spm->loc2glob;
767 spm_int_t *newrow = NULL;
768 char *newval = NULL;
769 spm_int_t baseval = spm->baseval;
770 spm_int_t *rowtmp;
771 char *valtmp;
772
773 spm_int_t jl, jg, l;
774 spm_int_t nnz, nnzexp;
775 spm_int_t nbelt;
776 size_t eltsize;
777
778 /* Let's sort the array per column */
779 spmIntSort2Asc1( miss_buf, miss_nbr );
780
781 /* Allocate the new rowptr and values */
782 nnz = spm->nnz + miss_nbr;
783 newrow = malloc( nnz * sizeof(spm_int_t) );
784
785 if ( spm->flttype != SpmPattern ) {
786 eltsize = spm_size_of( spm->flttype );
787 nnzexp = spm_symm_values_newsize( spm, miss_nbr, miss_buf );
788 newval = malloc( nnzexp * eltsize );
789 }
790 else {
791 eltsize = 0;
792 nnzexp = nnz;
793 }
794
795 l = 0; /* Counter of the already seen elements */
796 rowtmp = newrow;
797 valtmp = newval;
798
799 for ( jl=0; jl<spm->n; jl++, colptr++, loc2glob++ )
800 {
801 assert( (l+baseval) >= colptr[0] );
802 assert( l < nnz );
803
804 jg = spm->replicated ? jl : *loc2glob - baseval;
805
806 if ( (miss_nbr == 0) || (miss_buf[0] > jl) )
807 {
808 /*
809 * No element need to be added into this column
810 * Let's copy everything as before
811 */
813 spm, eltsize, jg, colptr,
814 &oldrow, &rowtmp, &oldval, &valtmp );
815 }
816 else {
818 spm, eltsize, jl, jg, colptr,
819 &oldrow, &rowtmp, &oldval, &valtmp,
820 &miss_nbr, &miss_buf );
821 }
822
823 colptr[0] = l + baseval;
824 l += nbelt;
825 }
826 colptr[0] = l + baseval;
827
828 assert( miss_nbr == 0 );
829 assert( (colptr[0] - baseval) == nnz );
830 assert( (rowtmp - newrow) == nnz );
831 assert( (spm->flttype == SpmPattern) ||
832 (((valtmp - newval)/eltsize) == ((size_t)nnzexp)) );
833
834 if ( spm->fmttype == SpmCSC ) {
835 free( spm->rowptr );
836 spm->rowptr = newrow;
837 }
838 else {
839 free( spm->colptr );
840 spm->colptr = newrow;
841 }
842 free( spm->values );
843 spm->values = newval;
844 spm->nnz = nnz;
845 spm->nnzexp = nnzexp;
846}
847
848/**
849 *******************************************************************************
850 *
851 * @brief Symmetrize the pattern of the spm.
852 *
853 * This routine corrects the sparse matrix structure if it's pattern is not
854 * symmetric. It returns the new symmetric pattern with zeroes on the new
855 * entries.
856 *
857 * First, we look for local structure and store missing entries,and/or entries
858 * that should be verified on other nodes if any.
859 * Second, if we are in a distributed case, we exhange the entries to check and
860 * verifies if they are available locally or not. The missing entries are added
861 * to the local buffer of missing entries.
862 * Third, we extend the matrix structure with the missing entries, as well as
863 * the values array in which 0 values are added.
864 *
865 *******************************************************************************
866 *
867 * @param[inout] spm
868 * On entry, the pointer to the sparse matrix structure.
869 * On exit, the same sparse matrix with extra entries that makes it
870 * pattern symmetric.
871 *
872 ********************************************************************************
873 *
874 * @retval >=0 the number of entries added to the matrix,
875 * @retval SPM_ERR_BADPARAMETER if the given spm was incorrect.
876 *
877 *******************************************************************************/
880{
881 spm_int_t *miss_sze;
882 spm_int_t *miss_nbr;
883 spm_int_t **miss_buf;
884 int clustnbr = spm->clustnbr;
885 int clustnum = spm->clustnum;
886 spm_int_t gnb, nb;
887 int i;
888
889 /*
890 * Allocate the data structure that will store the missing entries per node
891 * We initialize the allocation with a maximum of 5% of extra elements
892 * balanced among all processes.
893 * The size of these arrays will double when allocated size is reached.
894 */
895 {
896 spm_int_t buffsize = spm_imax( (0.05 / clustnbr ) * (double)spm->nnz, 1 );
897 miss_sze = malloc( clustnbr * sizeof(spm_int_t) );
898 miss_buf = malloc( clustnbr * sizeof(spm_int_t*) );
899 miss_nbr = malloc( clustnbr * sizeof(spm_int_t) );
900
901 for ( i=0; i<clustnbr; i++ )
902 {
903 miss_sze[i] = buffsize;
904 miss_buf[i] = malloc( 2 * buffsize * sizeof(spm_int_t) );
905 miss_nbr[i] = 0;
906 }
907 }
908
909 /* Check local symmetry and store missing entries */
910 spm_symm_check_local_pattern( spm, miss_sze, miss_buf, miss_nbr );
911
912#if defined(SPM_WITH_MPI)
913 /* Exchange information with remote nodes if necessary */
914 if ( !spm->replicated ) {
915 spm_symm_remote_exchange( spm, miss_sze, miss_buf, miss_nbr );
916 }
917#endif
918
919 /* Add local missing entries */
920 nb = miss_nbr[ clustnum ];
921 if ( nb > 0 ) {
923 miss_nbr[ clustnum ],
924 miss_buf[ clustnum ] );
925 }
926
927#if defined(SPM_WITH_MPI)
928 if ( !spm->replicated && (spm->clustnbr > 1) )
929 {
930 MPI_Allreduce( &nb, &gnb, 1, SPM_MPI_INT, MPI_SUM, spm->comm );
931 if ( gnb > 0 ) {
932 MPI_Allreduce( &(spm->nnz), &(spm->gnnz), 1, SPM_MPI_INT, MPI_SUM, spm->comm );
933 MPI_Allreduce( &(spm->nnzexp), &(spm->gnnzexp), 1, SPM_MPI_INT, MPI_SUM, spm->comm );
934 }
935 }
936 else
937#endif
938 {
939 gnb = nb;
940 if( gnb > 0 ) {
941 spm->gnnz = spm->nnz;
942 spm->gnnzexp = spm->nnzexp;
943 }
944 }
945
946 /* Free the stored entries */
947 for ( i=0; i < clustnbr; i++)
948 {
949 free( miss_buf[ i ] );
950 }
951 free( miss_sze );
952 free( miss_buf );
953 free( miss_nbr );
954
955 return gnb;
956}
static size_t spm_size_of(spm_coeftype_t type)
Double datatype that is not converted through precision generator functions.
Definition datatypes.h:209
static spm_int_t spm_imax(spm_int_t a, spm_int_t b)
Internal function to compute max(a,b)
Definition datatypes.h:119
@ SpmPattern
Definition const.h:62
@ SpmCSC
Definition const.h:73
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_int_t * loc2glob
Definition spm.h:91
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
int clustnum
Definition spm.h:95
int replicated
Definition spm.h:98
spm_int_t spmSymmetrize(spmatrix_t *spm)
Symmetrize the pattern of the spm.
#define SPM_MPI_INT
The MPI type associated to spm_int_t.
Definition datatypes.h:72
int spm_int_t
The main integer datatype used in spm arrays.
Definition datatypes.h:70
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
#define TAG_SPM_SYMM
Arbitrary tag for the symmetry exchange communications.
static spm_int_t spm_symm_local_copy_column(const spmatrix_t *spm, size_t eltsize, spm_int_t jg, const spm_int_t *colptr, const spm_int_t **oldrow, spm_int_t **newrow, const char **oldval, char **newval)
Copy the former column into the new one.
static int spm_symm_local_search(const spm_int_t *colptr, const spm_int_t *rowptr, spm_int_t jl, spm_int_t ig, spm_int_t baseval)
Search locally if an element exists.
static void spm_symm_check_local_pattern(spmatrix_t *spm, spm_int_t *miss_sze, spm_int_t **miss_buf, spm_int_t *miss_nbr)
Check the local symmetry of the pattern.
static void spm_symm_local_add(spmatrix_t *spm, spm_int_t miss_nbr, spm_int_t *miss_buf)
Add the missing entries to the local spm.
static spm_int_t spm_symm_values_newsize(const spmatrix_t *spm, spm_int_t miss_nbr, const spm_int_t *miss_buf)
Compute the new size of the values array.
static spm_int_t spm_symm_local_extend_column(spmatrix_t *spm, size_t eltsize, spm_int_t jl, spm_int_t jg, const spm_int_t *colptr, const spm_int_t **oldrowptr, spm_int_t **newrowptr, const char **oldvalptr, char **newvalptr, spm_int_t *miss_nbr, spm_int_t **miss_buf)
Add the missing entries to the local spm.
static void spm_symm_add_missing_elt(spm_int_t *miss_sze, spm_int_t **miss_buf, spm_int_t *miss_nbr, spm_int_t jg, spm_int_t ig, int owner)
Add a couple (ig, jg) in the list of missing entries of the owner.