SpM Handbook 1.2.4
Loading...
Searching...
No Matches
spm_gen_fake_values.c
Go to the documentation of this file.
1/**
2 * @file spm_gen_fake_values.c
3 *
4 * SParse Matrix generic laplacian value generator routines.
5 *
6 * @copyright 2016-2024 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria,
7 * Univ. Bordeaux. All rights reserved.
8 *
9 * @version 1.2.4
10 * @author Mathieu Faverge
11 * @author Tony Delarue
12 * @author Alycia Lisito
13 * @date 2024-06-25
14 *
15 * @ingroup spm_dev_driver
16 * @{
17 **/
18#include "common.h"
19
20/**
21 *******************************************************************************
22 *
23 * @brief Compute the local degree of each vertex of an SPM in CSR/CSC format.
24 *
25 *******************************************************************************
26 *
27 * @param[in] spm
28 * The spm to study in CSC/CSR format.
29 *
30 * @param[inout] degrees
31 * Array of size spm->gN allocated and set to 0 on entry. On exit,
32 * contains the degree of each vertex in the spm matrix for the local
33 * node.
34 *
35 *******************************************************************************
36 *
37 * @return the number of diagonal elements found during the computation.
38 *
39 *******************************************************************************/
40static inline spm_int_t
42 spm_int_t *degrees )
43{
44 const spm_int_t *colptr = spm->colptr;
45 const spm_int_t *rowptr = spm->rowptr;
46 const spm_int_t *loc2glob = spm->loc2glob;
47 spm_int_t baseval = spm->baseval;
48 spm_int_t diagval = 0;
49 spm_int_t j, jg, k, ig, dofi, dofj;
50
51 /* Swap pointers to call CSC */
52 if ( spm->fmttype == SpmCSR )
53 {
54 colptr = spm->rowptr;
55 rowptr = spm->colptr;
56 }
57
58 for(j=0; j<spm->n; j++, colptr++, loc2glob++) {
59 jg = spm->replicated ? j : *loc2glob - baseval;
60 dofj = spm->dof > 0 ? spm->dof : spm->dofs[jg+1] - spm->dofs[jg];
61
62 for(k=colptr[0]; k<colptr[1]; k++, rowptr++) {
63 ig = *rowptr - baseval;
64 dofi = spm->dof > 0 ? spm->dof : spm->dofs[ig+1] - spm->dofs[ig];
65
66 if ( ig != jg ) {
67 degrees[jg] += dofi;
68
69 if ( spm->mtxtype != SpmGeneral ) {
70 degrees[ig] += dofj;
71 }
72 }
73 else {
74 degrees[jg] += (dofi - 1);
75 diagval++;
76 }
77 }
78 }
79
80 return diagval;
81}
82
83/**
84 *******************************************************************************
85 *
86 * @brief Compute the degree of each vertex of an IJV matrix.
87 *
88 *******************************************************************************
89 *
90 * @param[in] spm
91 * The spm to study in IJV format.
92 *
93 * @param[inout] degrees
94 * Array of size spm->gN allocated and set to 0 on entry. On exit,
95 * contains the degree of each vertex in the spm matrix for the local
96 * node.
97 *
98 *******************************************************************************
99 *
100 * @return the number of diagonal elements found during the computation.
101 *
102 *******************************************************************************/
103static inline spm_int_t
105 spm_int_t *degrees )
106{
107 const spm_int_t *colptr = spm->colptr;
108 const spm_int_t *rowptr = spm->rowptr;
109 spm_int_t baseval = spm->baseval;
110 spm_int_t diagval = 0;
111 spm_int_t k, ig, jg, dofi, dofj;
112
113 for(k=0; k<spm->nnz; k++, rowptr++, colptr++)
114 {
115 ig = *rowptr - baseval;
116 jg = *colptr - baseval;
117 dofi = spm->dof > 0 ? spm->dof : spm->dofs[ig+1] - spm->dofs[ig];
118 dofj = spm->dof > 0 ? spm->dof : spm->dofs[jg+1] - spm->dofs[jg];
119
120 if ( ig != jg ) {
121 degrees[jg] += dofi;
122
123 if ( spm->mtxtype != SpmGeneral ) {
124 degrees[ig] += dofj;
125 }
126 }
127 else {
128 degrees[jg] += (dofi - 1);
129 diagval++;
130 }
131 }
132
133 return diagval;
134}
135
136/**
137 *******************************************************************************
138 *
139 * @brief Compute the degree of each vertex.
140 *
141 *******************************************************************************
142 *
143 * @param[in] spm
144 * The spm to study.
145 *
146 * @param[inout] degrees
147 * Array of size spm->n allocated on entry. On exit, contains the
148 * degree of each vertex in the spm matrix.
149 *
150 *******************************************************************************
151 *
152 * @return the number of diagonal elements found during the computation.
153 *
154 *******************************************************************************/
155static inline spm_int_t
157 spm_int_t *degrees )
158{
159 spm_int_t diagval;
160
161 memset( degrees, 0, spm->gN * sizeof(spm_int_t) );
162
163 if ( spm->fmttype == SpmIJV ) {
164 diagval = spm_compute_degrees_ijv( spm, degrees );
165 }
166 else {
167 diagval = spm_compute_degrees_csx( spm, degrees );
168 }
169
170 /*
171 * Use the simplest solution here, despite its cost.
172 * In fact, this function is used only for testing and should not be used
173 * with very large graph, so it should not be a problem.
174 */
175#if defined(SPM_WITH_MPI)
176 if ( !spm->replicated && (spm->clustnbr > 1) ) {
177 MPI_Allreduce( MPI_IN_PLACE, degrees, spm->gN, SPM_MPI_INT,
178 MPI_SUM, spm->comm );
179 }
180#else
181 assert( spm->replicated );
182 assert( spm->loc2glob == NULL );
183#endif
184
185 return diagval;
186}
187
188/**
189 *******************************************************************************
190 *
191 * @brief Insert diagonal elements to the graph of a CSC/CSR matrix to have a
192 * full Laplacian generated
193 *
194 *******************************************************************************
195 *
196 * @param[inout] spm
197 * At start, the initial spm structure with missing diagonal elements.
198 * At exit, contains the same sparse matrix with diagonal elements added.
199 *
200 * @param[in] diagval
201 * The number of diagonal elements already present in the matrix.
202 *
203 *******************************************************************************/
204static inline void
206 spm_int_t diagval )
207{
208 spmatrix_t oldspm;
209 spm_int_t ig, j, jg, k, d;
210 spm_int_t *oldcol;
211 const spm_int_t *oldrow;
212 spm_int_t *newrow;
213 spm_int_t *newcol;
214 const spm_int_t *loc2glob;
215 spm_int_t baseval = spm->baseval;
216
217 memcpy( &oldspm, spm, sizeof(spmatrix_t) );
218
219 spm->nnz = oldspm.nnz + (spm->n - diagval);
220 newrow = malloc( spm->nnz * sizeof(spm_int_t) );
221
222 /* Swap pointers to call CSC */
223 if ( spm->fmttype == SpmCSC )
224 {
225 oldcol = spm->colptr;
226 oldrow = spm->rowptr;
227 newcol = oldcol;
228 spm->rowptr = newrow;
229 }
230 else
231 {
232 oldcol = spm->rowptr;
233 oldrow = spm->colptr;
234 newcol = oldcol;
235 spm->colptr = newrow;
236 }
237 loc2glob = spm->loc2glob;
238
239 d = 0; /* Number of diagonal element added */
240 for(j=0; j<spm->n; j++, newcol++, loc2glob++) {
241 spm_int_t nbelt = newcol[1] - newcol[0];
242 int hasdiag = 0;
243
244 memcpy( newrow, oldrow, nbelt * sizeof(spm_int_t) );
245 newrow += nbelt;
246
247 /* Check if the diagonal element is present or not */
248 jg = spm->replicated ? j + baseval : *loc2glob;
249 for(k=0; k<nbelt; k++, oldrow++) {
250 ig = *oldrow;
251
252 if ( ig == jg ) {
253 hasdiag = 1;
254 }
255 }
256
257 newcol[0] += d;
258 if ( !hasdiag ) {
259 *newrow = jg;
260 newrow++;
261 d++;
262 }
263 }
264 newcol[0] += d;
265
266 if ( spm->fmttype == SpmCSC ) {
267 free( oldspm.rowptr );
268 }
269 else {
270 free( oldspm.colptr );
271 }
272 assert( d == spm->n );
273}
274
275/**
276 *******************************************************************************
277 *
278 * @brief Insert diagonal elements to the graph of an IJV matrix to have a
279 * full Laplacian generated
280 *
281 *******************************************************************************
282 *
283 * @param[inout] spm
284 * At start, the initial spm structure with missing diagonal elements.
285 * At exit, contains the same sparse matrix with diagonal elements added.
286 *
287 * @param[in] diagval
288 * The number of diagonal elements already present in the matrix.
289 *
290 *******************************************************************************/
291static inline void
293 spm_int_t diagval )
294{
295 spmatrix_t oldspm;
296 spm_int_t k;
297 const spm_int_t *oldcol = spm->colptr;
298 const spm_int_t *oldrow = spm->rowptr;
299 spm_int_t *newrow;
300 spm_int_t *newcol;
301 const spm_int_t *loc2glob;
302 spm_int_t baseval = spm->baseval;
303
304 memcpy( &oldspm, spm, sizeof(spmatrix_t));
305
306 spm->nnz = oldspm.nnz + (spm->n - diagval);
307 newrow = malloc( spm->nnz * sizeof(spm_int_t) );
308 newcol = malloc( spm->nnz * sizeof(spm_int_t) );
309 spm->colptr = newcol;
310 spm->rowptr = newrow;
311 loc2glob = spm->loc2glob;
312
313 /* Let's insert all diagonal elements first */
314 if ( !spm->replicated ) {
315 assert( loc2glob );
316 for(k=0; k<spm->n; k++, newrow++, newcol++, loc2glob++)
317 {
318 *newrow = *loc2glob;
319 *newcol = *loc2glob;
320 }
321 }
322 else {
323 assert( loc2glob == NULL );
324 for(k=0; k<spm->n; k++, newrow++, newcol++)
325 {
326 *newrow = k + baseval;
327 *newcol = k + baseval;
328 }
329 }
330
331 /* Now let's copy everything else but the diagonal elements */
332 for(k=0; k<spm->nnz; k++, oldrow++, oldcol++)
333 {
334 if ( *oldrow == *oldcol ) {
335 continue;
336 }
337
338 *newrow = *oldrow;
339 *newcol = *oldcol;
340 newrow++;
341 newcol++;
342 }
343
344 free( oldspm.colptr );
345 free( oldspm.rowptr );
346}
347
348/**
349 *******************************************************************************
350 *
351 * @brief Insert diagonal elements to the graph of a matrix to have a full
352 * Laplacian generated
353 *
354 *******************************************************************************
355 *
356 * @param[inout] spm
357 * At start, the initial spm structure with missing diagonal elements.
358 * At exit, contains the same sparse matrix with diagonal elements added.
359 *
360 * @param[in] diagval
361 * The number of diagonal elements already present in the matrix.
362 *
363 *******************************************************************************/
364static inline void
366 spm_int_t diagval )
367{
368 assert( diagval < spm->n );
369 if ( spm->fmttype == SpmIJV ) {
370 spm_add_diag_ijv( spm, diagval );
371 }
372 else {
373 spm_add_diag_csx( spm, diagval );
374 }
375}
376
377/**
378 *******************************************************************************
379 *
380 * @brief Generate the fake values array such that \f$ M = \alpha * D - \beta * A \f$
381 *
382 * D is the degree matrix, and A the adjacency matrix.
383 *
384 *******************************************************************************
385 *
386 * @param[inout] spm
387 * The spm structure for which the values array must be generated.
388 *
389 * @param[in] degrees
390 * Array of size spm->n that contains the degree of each vertex in the
391 * spm structure.
392 *
393 * @param[in] alpha
394 * The scalar alpha.
395 *
396 * @param[in] beta
397 * The scalar beta.
398 *
399 *******************************************************************************/
400static inline void
402 const spm_int_t *degrees,
403 double alpha,
404 double beta )
405{
406 double *values;
407 const spm_int_t *colptr = spm->colptr;
408 const spm_int_t *rowptr = spm->rowptr;
409 const spm_int_t *dofs = spm->dofs;
410 const spm_int_t *loc2glob = spm->loc2glob;
411 spm_int_t baseval = spm->baseval;
412 spm_int_t dof = spm->dof;
413 spm_int_t ig, j, jg, k, ige, jge;
414 spm_int_t jj, ii, dofj, dofi;
415
416 spm->values = malloc( spm->nnzexp * sizeof(double) );
417 values = spm->values;
418
419 switch(spm->fmttype)
420 {
421 case SpmCSR:
422 /* Swap pointers to call CSC */
423 colptr = spm->rowptr;
424 rowptr = spm->colptr;
425
426 spm_attr_fallthrough;
427
428 case SpmCSC:
429 for(j=0; j<spm->n; j++, colptr++, loc2glob++)
430 {
431 jg = spm->replicated ? j : *loc2glob - baseval;
432 jge = (dof > 0) ? jg * dof : dofs[jg] - baseval;
433 dofj = (dof > 0) ? dof : dofs[jg+1] - dofs[jg];
434 for(k=colptr[0]; k<colptr[1]; k++, rowptr++)
435 {
436 ig = *rowptr - baseval;
437 ige = (dof > 0) ? ig * dof : dofs[ig] - baseval;
438 dofi = (dof > 0) ? dof : dofs[ig+1] - dofs[ig];
439
440 for ( jj=0; jj<dofj; jj++ )
441 {
442 for ( ii=0; ii<dofi; ii++, values++ )
443 {
444 /* Diagonal element */
445 if ( (jge+jj) == (ige+ii) ) {
446 *values = alpha * degrees[jg];
447 }
448 else {
449 *values = - beta;
450 }
451 }
452 }
453 }
454 }
455 break;
456 case SpmIJV:
457 for(k=0; k<spm->nnz; k++, rowptr++, colptr++)
458 {
459 ig = *rowptr - baseval;
460 jg = *colptr - baseval;
461 ige = (dof > 0) ? ig * dof : dofs[ig] - baseval;
462 jge = (dof > 0) ? jg * dof : dofs[jg] - baseval;
463 dofi = (dof > 0) ? dof : dofs[ig+1] - dofs[ig];
464 dofj = (dof > 0) ? dof : dofs[jg+1] - dofs[jg];
465
466 for ( jj=0; jj<dofj; jj++ )
467 {
468 for ( ii=0; ii<dofi; ii++, values++ )
469 {
470 /* Diagonal element */
471 if ( (jge+jj) == (ige+ii) ) {
472 *values = alpha * degrees[jg];
473 }
474 else {
475 *values = - beta;
476 }
477 }
478 }
479 }
480 }
481 assert( (values - (double*)(spm->values)) == spm->nnzexp );
482
483 spm->flttype = SpmDouble;
484 if ( spm->mtxtype == SpmHermitian ) {
485 spm->mtxtype = SpmSymmetric;
486 }
487}
488
489/**
490 * @}
491 */
492
493/**
494 *******************************************************************************
495 *
496 * @ingroup spm
497 *
498 * @brief Generate the fake values array such that \f$ M = \alpha * D - \beta * A \f$
499 *
500 * D is the degree matrix, and A the adjacency matrix. The resulting matrix uses
501 * real double.
502 *
503 *******************************************************************************
504 *
505 * @param[inout] spm
506 * The spm structure for which the values array must be generated.
507 *
508 *******************************************************************************/
509void
511{
512 spm_int_t *degrees, diagval, gdiagval;
513 double alpha = 10.;
514 double beta = 1.;
515
516 if ( spm->flttype != SpmPattern ) {
517 spm_print_error( "spmGenFakeValues: Cannot generate fake values for non SpmPattern matrices\n" );
518 return;
519 }
520
521 if ( spm->values != NULL ) {
522 spm_print_error( "spmGenFakeValues: values field should be NULL on entry\n" );
523 return;
524 }
525
526 /*
527 * Read environment values for alpha/beta
528 */
529 {
530 char *str = spm_getenv( "SPM_FAKE_ALPHA" );
531 double value;
532
533 if ( str != NULL ) {
534 value = strtod( str, NULL );
535 if ( (value != HUGE_VAL) && (value != 0.) &&
536 !isnan(value) && !isinf(value) )
537 {
538 alpha = value;
539 }
540 spm_cleanenv( str );
541 }
542
543 str = spm_getenv( "SPM_FAKE_BETA" );
544 if ( str != NULL ) {
545 value = strtod( str, NULL );
546 if ( (value != HUGE_VAL) && (value != 0.) &&
547 !isnan(value) && !isinf(value) )
548 {
549 beta = value;
550 }
551 spm_cleanenv( str );
552 }
553 }
554
555 degrees = malloc( spm->gN * sizeof(spm_int_t));
556 diagval = spm_compute_degrees( spm, degrees );
557
558#if defined(SPM_WITH_MPI)
559 if ( !spm->replicated && (spm->clustnbr > 1) ) {
560 MPI_Allreduce( &diagval, &gdiagval, 1, SPM_MPI_INT,
561 MPI_SUM, spm->comm );
562 }
563 else
564#endif
565 {
566 gdiagval = diagval;
567 }
568
569 if ( gdiagval != spm->gN ) {
570 /* Diagonal elements must be added to the sparse matrix */
571 if ( spm->n != diagval ) {
572 spm_add_diag( spm, diagval );
573 }
575 }
576 spm_generate_fake_values( spm, degrees, alpha, beta );
577 free( degrees );
578
579 return;
580}
@ SpmDouble
Definition const.h:64
@ SpmPattern
Definition const.h:62
@ SpmGeneral
Definition const.h:165
@ SpmSymmetric
Definition const.h:166
@ SpmHermitian
Definition const.h:167
@ 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_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_Comm comm
Definition spm.h:97
spm_int_t baseval
Definition spm.h:71
spm_int_t * colptr
Definition spm.h:89
spm_int_t gN
Definition spm.h:72
int replicated
Definition spm.h:98
#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 spm_int_t
The main integer datatype used in spm arrays.
Definition datatypes.h:70
void spmGenFakeValues(spmatrix_t *spm)
Generate the fake values array such that .
The sparse matrix data structure.
Definition spm.h:64
static spm_int_t spm_compute_degrees_ijv(const spmatrix_t *spm, spm_int_t *degrees)
Compute the degree of each vertex of an IJV matrix.
static spm_int_t spm_compute_degrees_csx(const spmatrix_t *spm, spm_int_t *degrees)
Compute the local degree of each vertex of an SPM in CSR/CSC format.
static void spm_add_diag_ijv(spmatrix_t *spm, spm_int_t diagval)
Insert diagonal elements to the graph of an IJV matrix to have a full Laplacian generated.
static void spm_add_diag(spmatrix_t *spm, spm_int_t diagval)
Insert diagonal elements to the graph of a matrix to have a full Laplacian generated.
static void spm_add_diag_csx(spmatrix_t *spm, spm_int_t diagval)
Insert diagonal elements to the graph of a CSC/CSR matrix to have a full Laplacian generated.
static void spm_generate_fake_values(spmatrix_t *spm, const spm_int_t *degrees, double alpha, double beta)
Generate the fake values array such that .
static spm_int_t spm_compute_degrees(const spmatrix_t *spm, spm_int_t *degrees)
Compute the degree of each vertex.