diff --git a/CMakeLists.txt b/CMakeLists.txt index 46098c79fce213f7c4ed876a63389b6d7acb9dc1..66834ebfca0c93922eabd7a02a566af101523956 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -207,6 +207,7 @@ set(SOURCES src/z_spm_laplacian.c src/z_spm_matrixvector.c src/z_spm_print.c + src/z_spm_sort.c ) precisions_rules_py(generated_sources diff --git a/src/common.h b/src/common.h index 814be961faa2ff87026dd56ebe3be0b8f78ccb9d..1210be7be3f7811e23147be9f8d75ad9a4214028 100644 --- a/src/common.h +++ b/src/common.h @@ -55,6 +55,7 @@ spm_get_datatype( const spmatrix_t *spm ) spm_int_t *spm_get_glob2loc( spmatrix_t *spm, spm_int_t baseval ); int spm_get_distribution( const spmatrix_t *spm ); +spm_int_t *spm_create_asc_values( const spmatrix_t *spm ); /******************************************************************** * Conjuguate/Id functions diff --git a/src/spm.c b/src/spm.c index a27458ce4b4e37df9144f9fff994f24871c2e548..82a7fff45721e5b5dddfdf1e778f209874bd7337 100644 --- a/src/spm.c +++ b/src/spm.c @@ -558,44 +558,24 @@ spmNorm( spm_normtype_t ntype, int spmSort( spmatrix_t *spm ) { - if ( (spm->dof != 1) && (spm->flttype != SpmPattern) ) { - switch (spm->flttype) { - case SpmFloat: - s_spmSortMultidof( spm ); - break; - case SpmDouble: - d_spmSortMultidof( spm ); - break; - case SpmComplex32: - c_spmSortMultidof( spm ); - break; - case SpmComplex64: - z_spmSortMultidof( spm ); - break; - default: - return SPM_ERR_BADPARAMETER; - } - } - else { - switch (spm->flttype) { - case SpmPattern: - p_spmSort( spm ); - break; - case SpmFloat: - s_spmSort( spm ); - break; - case SpmDouble: - d_spmSort( spm ); - break; - case SpmComplex32: - c_spmSort( spm ); - break; - case SpmComplex64: - z_spmSort( spm ); - break; - default: - return SPM_ERR_BADPARAMETER; - } + switch (spm->flttype) { + case SpmPattern: + p_spmSort( spm ); + break; + case SpmFloat: + s_spmSort( spm ); + break; + case SpmDouble: + d_spmSort( spm ); + break; + case SpmComplex32: + c_spmSort( spm ); + break; + case SpmComplex64: + z_spmSort( spm ); + break; + default: + return SPM_ERR_BADPARAMETER; } return SPM_SUCCESS; } @@ -1699,3 +1679,82 @@ spm_get_distribution( const spmatrix_t *spm ) assert(distribution > 0); return distribution; } + +/** + ******************************************************************************* + * + * @ingroup spm_dev_check + * + * @brief Create an nnz array that represents the shift of the original + * multidof value array. + * + ******************************************************************************* + * + * @param[in] spm + * The sparse matrix structure. + * + ******************************************************************************** + * + * @return An nnz array which stores the multidof shift of the original + * values aray + * + *******************************************************************************/ +spm_int_t * +spm_create_asc_values( const spmatrix_t *spm ) +{ + spm_int_t i, j, ig, jg; + spm_int_t baseval, n; + spm_int_t dof, dofi, dofj; + spm_int_t *colptr = spm->colptr; + spm_int_t *rowptr = spm->rowptr; + spm_int_t *dofs = spm->dofs; + spm_int_t *loc2glob = spm->loc2glob; + spm_int_t *values = malloc( (spm->nnz + 1) * sizeof(spm_int_t)); + spm_int_t *valtmp = values; + + values[0] = 0; + baseval = spmFindBase(spm); + dof = spm->dof; + switch (spm->fmttype) + { + case SpmCSR: + colptr = spm->rowptr; + rowptr = spm->colptr; + + spm_attr_fallthrough; + + case SpmCSC: + n = spm->n; + loc2glob = spm->loc2glob; + for ( j = 0; j < n; j++, colptr++, loc2glob++ ) + { + jg = (spm->loc2glob == NULL) ? j : *loc2glob - baseval; + dofj = (dof > 0) ? dof : dofs[jg+1] - dofs[jg]; + for ( i = colptr[0]; i < colptr[1]; i++, rowptr++, valtmp++ ) + { + ig = *rowptr - baseval; + dofi = (dof > 0) ? dof : dofs[ig+1] - dofs[ig]; + + valtmp[1] = valtmp[0] + (dofj*dofi); + } + } + break; + + case SpmIJV: + n = spm->nnz; + for ( j = 0; j < n; j++, colptr++, rowptr++, valtmp++ ) + { + jg = *colptr - baseval; + dofj = (dof > 0) ? dof : dofs[jg+1] - dofs[jg]; + ig = *rowptr - baseval; + dofi = (dof > 0) ? dof : dofs[ig+1] - dofs[ig]; + + valtmp[1] = valtmp[0] + (dofj*dofi); + } + break; + } + assert((valtmp - values) == spm->nnz); + values = realloc( values, spm->nnz * sizeof(spm_int_t) ); + + return values; +} diff --git a/src/z_spm.c b/src/z_spm.c index 366a84c528d0e7d600c79cad7464a78abd86ff96..273593dbd204562b12dd4fd34496626f2b2b2fe6 100644 --- a/src/z_spm.c +++ b/src/z_spm.c @@ -18,372 +18,6 @@ #include "z_spm.h" #include <string.h> -/** - ******************************************************************************* - * - * @ingroup spm_dev_check - * - * @brief This routine sorts the spm matrix. - * - * For the CSC and CSR formats, the subarray of edges for each vertex are sorted. - * For the IJV format, the edges are storted first by column indexes, and then - * by row indexes. To perform a sort first by row, second by column, please swap - * the colptr and rowptr of the structure before calling the subroutine. - * - * @warning This function should NOT be called if dof is greater than 1. - * - ******************************************************************************* - * - * @param[inout] spm - * On entry, the pointer to the sparse matrix structure. - * On exit, the same sparse matrix with subarrays of edges sorted by - * ascending order. - * - *******************************************************************************/ -void -z_spmSort( spmatrix_t *spm ) -{ - spm_int_t *colptr = spm->colptr; - spm_int_t *rowptr = spm->rowptr; - spm_complex64_t *values = spm->values; - void *sortptr[3]; - spm_int_t n = spm->n; - spm_int_t i, size; - (void)sortptr; - -#if !defined(PRECISION_p) - assert( spm->dof == 1 ); -#endif - - /* Sort in place each subset */ - if ( spm->fmttype == SpmCSC ) { - for (i=0; i<n; i++, colptr++) - { - size = colptr[1] - colptr[0]; - -#if defined(PRECISION_p) - spmIntSort1Asc1( rowptr, size ); -#else - sortptr[0] = rowptr; - sortptr[1] = values; - z_spmIntFltSortAsc( sortptr, size ); -#endif - rowptr += size; - values += size; - } - } - else if ( spm->fmttype == SpmCSR ) { - for (i=0; i<n; i++, rowptr++) - { - size = rowptr[1] - rowptr[0]; - -#if defined(PRECISION_p) - spmIntSort1Asc1( colptr, size ); -#else - sortptr[0] = colptr; - sortptr[1] = values; - z_spmIntFltSortAsc( sortptr, size ); -#endif - colptr += size; - values += size; - } - } - else if ( spm->fmttype == SpmIJV ) { - size = spm->nnz; - - sortptr[0] = colptr; - sortptr[1] = rowptr; - -#if defined(PRECISION_p) - spmIntMSortIntAsc( sortptr, size ); -#else - sortptr[2] = values; - z_spmIntIntFltSortAsc( sortptr, size ); -#endif - } -} - -/** - * @brief Sort subarrays of rowptr and values - * - * @param[in] rowptr - * The original rowptr. - * - * @param[in] rowtmp - * The sorted copy of the rowptr. - * - * @param[in] values - * The original values array. - * - * @param[inout] valtmp - * The copy of the valptr to sort. - * - * @param[in] dofs - * The pointer to the dofs array. - * - * @param[in] dof - * SPM dof value. - * - * @param[in] baseval - * SPM baseval. - * - * @param[in] dofj - * Current colum dof. - * - * @param[in] size - * Size of the current subarray. - * - * @return number of value sorted in the value array - */ -static inline spm_int_t -z_spm_sort_multidof_csx_values( const spm_int_t *rowptr, - const spm_int_t *rowtmp, - const spm_complex64_t *values, - spm_complex64_t *valtmp, - const spm_int_t *dofs, - spm_int_t dof, - spm_int_t baseval, - spm_int_t dofj, - spm_int_t size ) -{ - spm_int_t i, ig, dofi; - spm_int_t k = 0; - spm_int_t memory, count, added = 0; - - while (k < size) - { - memory = 0; - while ( (k < (size - 1)) && (rowtmp[k] == rowtmp[k+1]) ) - { - memory++; - k++; - } - - count = 0; - for ( i = 0; i < size; i++) - { - ig = rowptr[i] - baseval; - dofi = (dof > 0) ? dof : dofs[ig+1] - dofs[ig]; - if ( rowtmp[k] != rowptr[i] ) { - count += dofj * dofi; - continue; - } - /* - * The matrix isn't merged. - * We have to make sure that we don't copy the same information. - */ - memcpy( valtmp + added, - values + count, - dofi * dofj * sizeof(spm_complex64_t) ); - added += dofi * dofj; - - if ( memory > 0 ) { - memory--; - continue; - } - - k++; - break; - } - } - return added; -} - -/** - * @brief Sort a IJV matrix. - * - * @param[in] spm - * Pointer to the spm structure. - * - * @param[inout] newcol - * The sorted copy of the colptr. - * - * @param[inout] newrow - * The sorted copy of the rowptr. - * - * @param[inout] newval - * The copy of the valptr to sort. - */ -static inline void -z_spm_sort_multidof_ijv_values( const spmatrix_t *spm, - spm_int_t *newcol, - spm_int_t *newrow, - spm_complex64_t *newval ) -{ - spm_int_t *colptr; - spm_int_t *rowptr; - spm_complex64_t *values; - spm_int_t *dofs; - spm_int_t i, ig, jg, dofi, dofj, dof2; - spm_int_t size, baseval; - spm_int_t k = 0; - spm_int_t count, memory = 0; - - values = spm->values; - dofs = spm->dofs; - size = spm->nnz; - baseval = spmFindBase(spm); - while (k < size) - { - while ( (newcol[0] == newcol[1]) && (newrow[0] == newrow[1]) ) - { - newcol++; - newrow++; - memory++; - k++; - } - - jg = *newcol - baseval; - dofj = (spm->dof > 0) ? spm->dof : dofs[jg+1] - dofs[jg]; - ig = *newrow - baseval; - dofi = (spm->dof > 0) ? spm->dof : dofs[ig+1] - dofs[ig]; - dof2 = dofi * dofj; - - count = 0; - colptr = spm->colptr; - rowptr = spm->rowptr; - for ( i = 0; i < size; i++, colptr++, rowptr++ ) - { - jg = *colptr - baseval; - dofj = (spm->dof > 0) ? spm->dof : dofs[jg+1] - dofs[jg]; - ig = *rowptr - baseval; - dofi = (spm->dof > 0) ? spm->dof : dofs[ig+1] - dofs[ig]; - - if ( ((*newcol) != (*colptr)) || ((*newrow) != (*rowptr)) ) { - count += dofj * dofi; - continue; - } - - memcpy( newval, - values + count, - dof2 * sizeof(spm_complex64_t) ); - newval += dof2; - - if( memory > 0 ) { - memory--; - continue; - } - - newcol++; - newrow++; - k++; - break; - } - assert(memory == 0); - } -} - -/** - ******************************************************************************* - * - * @ingroup spm_dev_check - * - * @brief This routine sorts the spm matrix. - * - * For the CSC and CSR formats, the subarray of edges for each vertex are sorted. - * For the IJV format, the edges are sorted first by column indexes, and then - * by row indexes. To perform a sort first by row, second by column, please swap - * the colptr and rowptr of the structure before calling the subroutine. - * This routine is used for multidof matrices. It's way less efficient than the - * single dof one. - * - ******************************************************************************* - * - * @param[inout] spm - * On entry, the pointer to the sparse matrix structure. - * On exit, the same sparse matrix with subarrays of edges sorted by - * ascending order. - * - *******************************************************************************/ -void -z_spmSortMultidof( spmatrix_t *spm ) -{ - spm_int_t *colptr, *newcol, *coltmp; - spm_int_t *rowptr, *newrow, *rowtmp; - spm_complex64_t *values, *newval, *valtmp; - spm_int_t size, n = spm->n; - - newrow = malloc( spm->nnz * sizeof(spm_int_t) ); - newval = malloc( spm->nnzexp * sizeof(spm_complex64_t) ); - values = spm->values; - - if ( spm->fmttype != SpmIJV ) { - spm_int_t *loc2glob = spm->loc2glob; - spm_int_t *dofs = spm->dofs; - spm_int_t j, jg, dofj, baseval; - spm_int_t added; - - baseval = spmFindBase(spm); - rowtmp = newrow; - valtmp = newval; - colptr = (spm->fmttype == SpmCSC) ? spm->colptr : spm->rowptr; - rowptr = (spm->fmttype == SpmCSC) ? spm->rowptr : spm->colptr; - - memcpy( newrow, rowptr, spm->nnz * sizeof(spm_int_t) ); - for (j=0; j<n; j++, colptr++, loc2glob++) - { - size = colptr[1] - colptr[0]; - jg = (spm->loc2glob == NULL) ? j : *loc2glob - baseval; - dofj = (spm->dof > 0) ? spm->dof : dofs[jg+1] - dofs[jg]; - - /* Sort rowptr */ - spmIntSort1Asc1( rowtmp, size ); - - /* Sort values */ - added = z_spm_sort_multidof_csx_values( rowptr, rowtmp, values, valtmp, dofs, - spm->dof, baseval, dofj, size ); - - rowptr += size; - rowtmp += size; - values += added; - valtmp += added; - } - - if(spm->fmttype == SpmCSC) { - memcpy( spm->rowptr, newrow, spm->nnz * sizeof( spm_int_t ) ); - } - else { - memcpy( spm->colptr, newrow, spm->nnz * sizeof( spm_int_t ) ); - } - } - - else { - void *sortptr[2]; - - colptr = spm->colptr; - rowptr = spm->rowptr; - size = spm->nnz; - newcol = malloc( size * sizeof(spm_int_t) ); - - memcpy( newcol, colptr, size * sizeof(spm_int_t) ); - memcpy( newrow, rowptr, size * sizeof(spm_int_t) ); - - sortptr[0] = newcol; - sortptr[1] = newrow; - - /* Sort the colptr and the rowptr */ - spmIntMSortIntAsc( sortptr, size ); - - coltmp = newcol; - rowtmp = newrow; - valtmp = newval; - - /* Sort values */ - z_spm_sort_multidof_ijv_values( spm, coltmp, rowtmp, valtmp ); - - memcpy(spm->colptr, newcol, spm->nnz * sizeof( spm_int_t )); - memcpy(spm->rowptr, newrow, spm->nnz * sizeof( spm_int_t )); - - free(newcol); - } - - memcpy( spm->values, newval, spm->nnzexp * sizeof( spm_complex64_t ) ); - - free(newrow); - free(newval); -} - - /** ******************************************************************************* * diff --git a/src/z_spm.h b/src/z_spm.h index f2758d4e90a218d803d5a60c0b2d9ee646309498..7cd4c301a664bf653e7ff2f300ed1b898143e94e 100644 --- a/src/z_spm.h +++ b/src/z_spm.h @@ -70,7 +70,6 @@ double z_spmNorm( spm_normtype_t ntype, const spmatrix_t *spm ); * Extra routines */ void z_spmSort( spmatrix_t *spm ); -void z_spmSortMultidof( spmatrix_t *spm ); spm_int_t z_spmMergeDuplicate( spmatrix_t *spm ); spm_int_t z_spmSymmetrize( spmatrix_t *spm ); diff --git a/src/z_spm_sort.c b/src/z_spm_sort.c new file mode 100644 index 0000000000000000000000000000000000000000..725a45acd181fe60b2d102f2b3ff37eafab8aeb7 --- /dev/null +++ b/src/z_spm_sort.c @@ -0,0 +1,300 @@ +/** + * + * @file z_spm_sort.c + * + * SParse Matrix package precision dependent sort routines. + * + * @copyright 2016-2020 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria, + * Univ. Bordeaux. All rights reserved. + * + * @version 1.0.0 + * @author Mathieu Faverge + * @author Tony Delarue + * @date 2020-09-24 + * + * @precisions normal z -> c d s p + * + **/ +#include "common.h" +#include "z_spm.h" + +/** + ******************************************************************************* + * + * @ingroup spm_dev_check + * + * @brief This routine sorts the single dof spm matrix. + * + * For the CSC and CSR formats, the subarray of edges for each vertex are sorted. + * For the IJV format, the edges are storted first by column indexes, and then + * by row indexes. To perform a sort first by row, second by column, please swap + * the colptr and rowptr of the structure before calling the subroutine. + * + * @warning This function should NOT be called if dof is greater than 1. + * + ******************************************************************************* + * + * @param[inout] spm + * On entry, the pointer to the sparse matrix structure. + * On exit, the same sparse matrix with subarrays of edges sorted by + * ascending order. + * + *******************************************************************************/ +static inline void +z_spmSortNoDof( spmatrix_t *spm ) +{ + spm_int_t *colptr = spm->colptr; + spm_int_t *rowptr = spm->rowptr; + spm_complex64_t *values = spm->values; + void *sortptr[3]; + spm_int_t n = spm->n; + spm_int_t i, size; + (void)sortptr; + + /* Sort in place each subset */ + if ( spm->fmttype == SpmCSC ) { + for (i=0; i<n; i++, colptr++) + { + size = colptr[1] - colptr[0]; + +#if defined(PRECISION_p) + spmIntSort1Asc1( rowptr, size ); +#else + sortptr[0] = rowptr; + sortptr[1] = values; + z_spmIntFltSortAsc( sortptr, size ); +#endif + rowptr += size; + values += size; + } + } + else if ( spm->fmttype == SpmCSR ) { + for (i=0; i<n; i++, rowptr++) + { + size = rowptr[1] - rowptr[0]; + +#if defined(PRECISION_p) + spmIntSort1Asc1( colptr, size ); +#else + sortptr[0] = colptr; + sortptr[1] = values; + z_spmIntFltSortAsc( sortptr, size ); +#endif + colptr += size; + values += size; + } + } + else if ( spm->fmttype == SpmIJV ) { + size = spm->nnz; + + sortptr[0] = colptr; + sortptr[1] = rowptr; + +#if defined(PRECISION_p) + spmIntMSortIntAsc( sortptr, size ); +#else + sortptr[2] = values; + z_spmIntIntFltSortAsc( sortptr, size ); +#endif + } +} + +/** + * @brief Apply the permutations on the values array for a CSX spm + * The values array holds the permutation indexes of the new values array. + * + * @param[in] spm + * The pointer to the spm. + * + * @param[in] values + * The original values array. + * + * @param[inout] newval + * The new values array with the correct permutations. + * Must be allocated before this routine. + */ +static inline void +z_spm_sort_multidof_csx_values( const spmatrix_t *spm, + const spm_complex64_t *values, + spm_complex64_t *newval ) +{ + spm_int_t i, j, ig, jg, index; + spm_int_t size, baseval, dof; + spm_int_t dofi, dofj, dof2; + + spm_int_t *colptr = (spm->fmttype == SpmCSC) ? spm->colptr: spm->rowptr; + spm_int_t *rowptr = (spm->fmttype == SpmCSC) ? spm->rowptr: spm->colptr; + spm_int_t *indexes = spm->values; + spm_int_t *dofs = spm->dofs; + spm_int_t *loc2glob = spm->loc2glob; + + spm_complex64_t *valtmp = newval; + + size = spm->n; + baseval = spmFindBase(spm); + dof = spm->dof; + for ( j = 0; j < size; j++, colptr++, loc2glob++ ) + { + jg = (spm->loc2glob == NULL) ? j : *loc2glob - baseval; + dofj = (dof > 0) ? dof : dofs[jg+1] - dofs[jg]; + + for ( i = colptr[0]; i < colptr[1]; i++, rowptr++, indexes++ ) + { + ig = *rowptr - baseval; + dofi = (dof > 0) ? dof : dofs[ig+1] - dofs[ig]; + dof2 = dofi * dofj; + + index = *indexes; + memcpy( valtmp, values + index, dof2 * sizeof(spm_complex64_t) ); + valtmp += dof2; + } + } +} + +/** + * @brief Apply the permutations on the values array fon an IJV spm. + * The values array holds the permutation indexes of the new values array. + * + * @param[in] spm + * The pointer to the spm. + * + * @param[in] values + * The original values array. + * + * @param[inout] newval + * The new values array with the correct permutations. + * Must be allocated before this routine. + */ +static inline void +z_spm_sort_multidof_ijv_values( const spmatrix_t *spm, + const spm_complex64_t *values, + spm_complex64_t *newval ) +{ + spm_int_t i, ig, jg, index; + spm_int_t size, baseval, dof; + spm_int_t dofi, dofj, dof2; + + spm_int_t *colptr = spm->colptr; + spm_int_t *rowptr = spm->rowptr; + spm_int_t *indexes = spm->values; + spm_int_t *dofs; + + spm_complex64_t *valtmp = newval; + + size = spm->nnz; + baseval = spmFindBase(spm); + dof = spm->dof; + dofs = spm->dofs - baseval; + for ( i = 0; i < size; i++, colptr++, rowptr++, indexes++ ) + { + jg = *colptr; + dofj = (dof > 0) ? dof : dofs[jg+1] - dofs[jg]; + ig = *rowptr; + dofi = (dof > 0) ? dof : dofs[ig+1] - dofs[ig]; + dof2 = dofi * dofj; + + index = *indexes; + + memcpy( valtmp, values + index, dof2 * sizeof(spm_complex64_t) ); + valtmp += dof2; + } +} + +/** + ******************************************************************************* + * + * @ingroup spm_dev_check + * + * @brief This routine sorts the multiple dof spm matrix. + * + * For the CSC and CSR formats, the subarray of edges for each vertex are sorted. + * For the IJV format, the edges are sorted first by column indexes, and then + * by row indexes. To perform a sort first by row, second by column, please swap + * the colptr and rowptr of the structure before calling the subroutine. + * This routine is used for multidof matrices. It's way less efficient than the + * single dof one. + * + ******************************************************************************* + * + * @param[inout] spm + * On entry, the pointer to the sparse matrix structure. + * On exit, the same sparse matrix with subarrays of edges sorted by + * ascending order. + * + *******************************************************************************/ +static inline void +z_spmSortMultidof( spmatrix_t *spm ) +{ + spm_int_t dof; + spm_coeftype_t flttype; + spm_complex64_t *values = spm->values; + /* This array will store the solution */ + spm_complex64_t *newval = malloc( spm->nnzexp * sizeof(spm_complex64_t) ); + + /* Create a tmp array composed by the multidof indexes of the valptr */ + spm_int_t *indexes = spm_create_asc_values(spm); + + /* + * Sort the spm as a single dof matrix. + * The value array will represent the permutations. + */ + dof = spm->dof; + flttype = spm->flttype; + + spm->values = indexes; + spm->dof = 1; + + if ( sizeof(spm_int_t) == sizeof(spm_fixdbl_t) ) { + spm->flttype = 3; // SpmDouble + } + else { + spm->flttype = 2; // SpmFloat + } + spmSort( spm ); + + spm->dof = dof; + spm->flttype = flttype; + + /* Apply the permutations and copy datas in the newval */ + if ( spm->fmttype != SpmIJV ) { + z_spm_sort_multidof_csx_values( spm, values, newval ); + } + else { + z_spm_sort_multidof_ijv_values( spm, values, newval ); + } + free(indexes); + free(values); + + spm->values = newval; +} + +/** + ******************************************************************************* + * + * @ingroup spm_dev_check + * + * @brief This routine sorts the spm matrix. + * + * For the CSC and CSR formats, the subarray of edges for each vertex are sorted. + * For the IJV format, the edges are storted first by column indexes, and then + * by row indexes. To perform a sort first by row, second by column, please swap + * the colptr and rowptr of the structure before calling the subroutine. + * + ******************************************************************************* + * + * @param[inout] spm + * On entry, the pointer to the sparse matrix structure. + * On exit, the same sparse matrix with subarrays of edges sorted by + * ascending order. + * + *******************************************************************************/ +void +z_spmSort( spmatrix_t *spm ) +{ + if ( (spm->dof != 1) && (spm->flttype != SpmPattern) ) { + z_spmSortMultidof( spm ); + } + else { + z_spmSortNoDof( spm ); + } +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7257f221c866401a13394bedf1b904a00130821a..09b01b87e8f5b5c21b2d71e95dea094ff107fe4a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -17,6 +17,7 @@ set(LIB_SOURCES z_spm_tests.c core_zgeadd.c core_zplrnt.c + z_spm_sort_tests.c ) ## reset variables @@ -55,6 +56,7 @@ if ( SPM_WITH_MPI ) spm_dist_norm_tests.c spm_dist_genrhs_tests.c spm_dist_matvec_tests.c + spm_dist_sort_tests.c ) endif() @@ -66,14 +68,22 @@ endforeach() ## CTest execution set( SPM_TESTS - spm_convert_tests spm_norm_tests spm_matvec_tests ) + spm_convert_tests + spm_norm_tests + spm_matvec_tests + ) set( SPM_DOF_TESTS - spm_dof_expand_tests spm_dof_norm_tests spm_dof_matvec_tests spm_dof_sort_tests) + spm_dof_expand_tests + spm_dof_norm_tests + spm_dof_matvec_tests + spm_dof_sort_tests + ) set( SPM_MPI_TESTS spm_scatter_gather_tests spm_dist_norm_tests spm_dist_genrhs_tests spm_dist_matvec_tests + spm_dist_sort_tests ) # List of run types diff --git a/tests/spm_dist_sort_tests.c b/tests/spm_dist_sort_tests.c new file mode 100644 index 0000000000000000000000000000000000000000..f6de343602ed19b525cd34aee38c5f2dd43b8ccb --- /dev/null +++ b/tests/spm_dist_sort_tests.c @@ -0,0 +1,192 @@ +/** + * + * @file spm_dist_sort_tests.c + * + * Tests and validate the spm_sort routines with a distributed spm. + * + * @copyright 2015-2020 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria, + * Univ. Bordeaux. All rights reserved. + * + * @version 1.0.0 + * @author Mathieu Faverge + * @author Delarue Tony + * @date 2020-09-07 + * + **/ +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <math.h> +#include <string.h> +#include <assert.h> +#include <time.h> +#include <spm_tests.h> + +static inline int +spm_dist_sort_check( spmatrix_t *spm, spmatrix_t *spmdist ) +{ + spmatrix_t expand1, expand2; + spmatrix_t *gathered; + int rc; + + spmSort( spm ); + spmSort( spmdist ); + + gathered = spmGather( spmdist, -1 ); + + spmExpand( spm, &expand1 ); + spmExpand( gathered, &expand2 ); + + rc = spmCompare( &expand1, &expand2 ); + + MPI_Allreduce( MPI_IN_PLACE, &rc, 1, MPI_INT, MPI_SUM, spmdist->comm ); + + spmExit( &expand1 ); + spmExit( &expand2 ); + spmExit( gathered ); + free( gathered ); + + return rc; +} + +int main (int argc, char **argv) +{ + char *filename; + spmatrix_t original, *spm, *spmdist; + spm_driver_t driver; + int clustnbr = 1; + int clustnum = 0; + spm_mtxtype_t mtxtype; + spm_fmttype_t fmttype; + int baseval, distbycol; + int rc = SPM_SUCCESS; + int err = 0; + int dof, dofmax = 4; + int to_free = 0; + + MPI_Init( &argc, &argv ); + + /** + * Get options from command line + */ + spmGetOptions( argc, argv, + &driver, &filename ); + + rc = spmReadDriver( driver, filename, &original ); + free(filename); + + if ( rc != SPM_SUCCESS ) { + fprintf(stderr, "ERROR: Could not read the file, stop the test !!!\n"); + return EXIT_FAILURE; + } + + MPI_Comm_size( MPI_COMM_WORLD, &clustnbr ); + MPI_Comm_rank( MPI_COMM_WORLD, &clustnum ); + + if ( original.flttype == SpmPattern ) { + spmGenFakeValues( &original ); + } + + spmPrintInfo( &original, stdout ); + + if ( clustnum == 0 ) { + printf(" -- SPM Sort Test --\n"); + } + + for( fmttype=SpmCSC; fmttype<=SpmIJV; fmttype++ ) { + + distbycol = (fmttype == SpmCSR) ? 0 : 1; + if ( spmConvert( fmttype, &original ) != SPM_SUCCESS ) { + fprintf( stderr, "Issue to convert to %s format\n", fmtnames[fmttype] ); + continue; + } + + for( dof=-1; dof<2; dof++ ) + { + if ( dof >= 0 ) { + spm = spmDofExtend( &original, dof, dofmax ); + to_free = 1; + } + else { + spm = malloc(sizeof(spmatrix_t)); + memcpy( spm, &original, sizeof(spmatrix_t) ); + to_free = 0; + } + + if ( spm == NULL ) { + fprintf( stderr, "Issue to extend the matrix\n" ); + continue; + } + + spmdist = spmScatter( spm, -1, NULL, distbycol, -1, MPI_COMM_WORLD ); + if ( spmdist == NULL ) { + fprintf( stderr, "Failed to scatter the spm\n" ); + err++; + continue; + } + + for( baseval=0; baseval<2; baseval++ ) + { + spmBase( spmdist, baseval ); + spmBase( spm, baseval ); + + for( mtxtype=SpmGeneral; mtxtype<=SpmHermitian; mtxtype++ ) + { + if ( (mtxtype == SpmHermitian) && + ( ((original.flttype != SpmComplex64) && + (original.flttype != SpmComplex32)) || + (original.mtxtype != SpmHermitian) ) ) + { + continue; + } + + if ( (mtxtype != SpmGeneral) && + (original.mtxtype == SpmGeneral) ) + { + continue; + } + + if ( spm ) { + spm->mtxtype = mtxtype; + } + spmdist->mtxtype = mtxtype; + + if ( clustnum == 0 ) { + printf( " Case: %s / %s / %d / %s / %d\n", + fltnames[spmdist->flttype], + fmtnames[spmdist->fmttype], baseval, + mtxnames[mtxtype - SpmGeneral], dof ); + } + + rc = spm_dist_sort_check( spm, spmdist ); + + err = (rc != 0) ? err+1 : err; + } + } + spmExit( spmdist ); + free( spmdist ); + + if ( spm != &original ) { + if( to_free ){ + spmExit( spm ); + } + free( spm ); + } + } + } + + spmExit( &original ); + MPI_Finalize(); + + if( err == 0 ) { + if(clustnum == 0) { + printf(" -- All tests PASSED --\n"); + } + return EXIT_SUCCESS; + } + else + { + printf(" -- %d tests FAILED --\n", err); + return EXIT_FAILURE; + } +} diff --git a/tests/spm_dof_sort_tests.c b/tests/spm_dof_sort_tests.c index 1921bae614cacdd1493b0880460b158600479ac7..146be200d0be9cb612421e0cfe95d156e5520427 100644 --- a/tests/spm_dof_sort_tests.c +++ b/tests/spm_dof_sort_tests.c @@ -2,12 +2,12 @@ * * @file spm_dof_sort_tests.c * - * Tests and validate the spm_sort routines when the spm_tests.hold constant and/or variadic dofs. + * Tests and validate the spm_sort routines when the spm contains constant and/or variadic dofs. * - * @copyright 2015-2017 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria, + * @copyright 2015-2020 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria, * Univ. Bordeaux. All rights reserved. * - * @version 6.0.0 + * @version 1.0.0 * @author Mathieu Faverge * @author Delarue Tony * @date 2020-09-07 @@ -25,31 +25,189 @@ #define PRINT_RES(_ret_) \ if(_ret_) { \ printf("FAILED(%d)\n", _ret_); \ - err++; \ } \ else { \ printf("SUCCESS\n"); \ } +/** + ******************************************************************************* + * + * @brief This routine unsorts the spm matrix to check our sort routine. + * It will only change the pattern, the value array doesn't follow it. + * + ******************************************************************************* + * + * @param[inout] spm + * On entry, the pointer to the sparse matrix structure. + * On exit, the same sparse matrix with subarrays of edges unsorted + * + *******************************************************************************/ +static inline void +spm_unsort( spmatrix_t *spm ) +{ + spm_int_t i, j, size; + spm_int_t index1, index2, count; + spm_int_t baseval; + spm_int_t coltmp, rowtmp; + spm_int_t *colptr = spm->colptr; + spm_int_t *rowptr = spm->rowptr; + + if( spm->dof < 0 ) { + return; + } + + baseval = spmFindBase(spm); + switch (spm->fmttype) + { + case SpmCSR: + /* Swap pointers to call CSC */ + colptr = spm->rowptr; + rowptr = spm->colptr; + + spm_attr_fallthrough; + + case SpmCSC: + size = spm->n; + for ( j = 0; j < size; j++, colptr++ ) + { + count = colptr[1] - colptr[0]; + for ( i = 0; i < count; i++ ) + { + index1 = ( rand() % count ) + colptr[0] - baseval; + index2 = ( rand() % count ) + colptr[0] - baseval; + + rowtmp = rowptr[index1]; + rowptr[index1] = rowptr[index2]; + rowptr[index2] = rowtmp; + break; + } + } + break; + + case SpmIJV: + size = spm->nnz; + for ( i = 0; i < size; i++ ) + { + index1 = ( rand() % size ); + index2 = ( rand() % size ); + + coltmp = colptr[index1]; + rowtmp = rowptr[index1]; + + colptr[index1] = colptr[index2]; + rowptr[index1] = rowptr[index2]; + + colptr[index2] = coltmp; + rowptr[index2] = rowtmp; + } + break; + } +} + static inline int -spm_sort_check( spmatrix_t *spm ) +spm_sort_check_csx( const spmatrix_t *spm ) { - spmatrix_t expand1, expand2; - int rc; + spm_int_t i, j, max; + spm_int_t n = spm->n; + spm_int_t *colptr = (spm->fmttype == SpmCSC) ? spm->colptr : spm->rowptr; + spm_int_t *rowptr = (spm->fmttype == SpmCSC) ? spm->rowptr : spm->colptr; - spmExpand( spm, &expand1 ); + for ( j = 0; j < n; j++, colptr++ ) + { + max = (colptr[1] - 1); + for ( i = colptr[0]; i < max; i++, rowptr++ ) + { + if( rowptr[0] > rowptr[1] ) { + return 1; + } + } + rowptr++; + } + + return 0; +} + +static inline int +spm_sort_check_ijv( const spmatrix_t *spm ) +{ + spm_int_t k; + spm_int_t nnz = spm->nnz - 1; + spm_int_t *colptr = spm->colptr; + spm_int_t *rowptr = spm->rowptr; + + k = 0; + while ( k < nnz ) + { + while ( colptr[0] == colptr[1] ) + { + if( rowptr[0] > rowptr[1] ) { + return 1; + } + k++; + colptr++; + rowptr++; + if (k == nnz) { + return 0; + } + } + if( colptr[0] > colptr[1] ) { + return 1; + } + k++; + colptr++; + rowptr++; + } + return 0; +} + +static inline int +spm_sort_check( spmatrix_t *spm) +{ + spmatrix_t *spmcpy; + int rc1, rc2; + + spm_unsort(spm); + + spmcpy = spmCopy(spm); + spmSort( spmcpy ); - spmSort( spm ); - spmSort( &expand1 ); + /* Check that the matrix pattern is well sorted */ + if ( spm->fmttype != SpmIJV ) { + rc1 = spm_sort_check_csx( spmcpy ); + } + else { + rc1 = spm_sort_check_ijv( spmcpy ); + } + + /* Check that the matrix values follows the original pattern */ + switch (spm->flttype) + { + case SpmFloat: + rc2 = s_spm_sort_check_values(spm, spmcpy); + break; - spmExpand( spm, &expand2 ); + case SpmDouble: + rc2 = d_spm_sort_check_values(spm, spmcpy); + break; - rc = spmCompare( &expand1, &expand2 ); + case SpmComplex32: + rc2 = c_spm_sort_check_values(spm, spmcpy); + break; + + case SpmComplex64: + rc2 = z_spm_sort_check_values(spm, spmcpy); + break; + + default: + rc2 = 0; + break; + } - spmExit( &expand1 ); - spmExit( &expand2 ); + spmExit(spmcpy); + free(spmcpy); - return rc; + return rc1 + rc2; } int main (int argc, char **argv) diff --git a/tests/spm_tests.h b/tests/spm_tests.h index e747b28013d5597aae97571eff05efb22602f971..4a63693ffe9de1af1c92feb017b9a8b63dd32992 100644 --- a/tests/spm_tests.h +++ b/tests/spm_tests.h @@ -17,6 +17,7 @@ #define _spm_tests_h_ #include <stdlib.h> +#include <string.h> #include <assert.h> #include <stdio.h> #include <spm.h> @@ -86,6 +87,7 @@ int z_spm_dist_norm_check( const spmatrix_t *spm, const spmatrix_t *spmdist ); int z_spm_dist_genrhs_check( const spmatrix_t *spm, spm_int_t nrhs, const spm_complex64_t *bloc, const spm_complex64_t *bdst, int root ); int z_spm_dist_matvec_check( spm_int_t baseval, spm_trans_t trans, const spmatrix_t *spm ); +int z_spm_sort_check_values( const spmatrix_t *spm1, const spmatrix_t *spm2 ); void c_spm_print_check( char *filename, const spmatrix_t *spm ); int c_spm_matvec_check( spm_trans_t trans, const spmatrix_t *spm ); @@ -94,6 +96,7 @@ int c_spm_dist_norm_check( const spmatrix_t *spm, const spmatrix_t *spmdist ); int c_spm_dist_genrhs_check( const spmatrix_t *spm, spm_int_t nrhs, const spm_complex32_t *bloc, const spm_complex32_t *bdst, int root ); int c_spm_dist_matvec_check( spm_int_t baseval, spm_trans_t trans, const spmatrix_t *spm ); +int c_spm_sort_check_values( const spmatrix_t *spm1, const spmatrix_t *spm2 ); void d_spm_print_check( char *filename, const spmatrix_t *spm ); int d_spm_matvec_check( spm_trans_t trans, const spmatrix_t *spm ); @@ -102,6 +105,7 @@ int d_spm_dist_norm_check( const spmatrix_t *spm, const spmatrix_t *spmdist ); int d_spm_dist_genrhs_check( const spmatrix_t *spm, spm_int_t nrhs, const double *bloc, const double *bdst, int root ); int d_spm_dist_matvec_check( spm_int_t baseval, spm_trans_t trans, const spmatrix_t *spm ); +int d_spm_sort_check_values( const spmatrix_t *spm1, const spmatrix_t *spm2 ); void s_spm_print_check( char *filename, const spmatrix_t *spm ); int s_spm_matvec_check( spm_trans_t trans, const spmatrix_t *spm ); @@ -110,6 +114,7 @@ int s_spm_dist_norm_check( const spmatrix_t *spm, const spmatrix_t *spmdist ); int s_spm_dist_genrhs_check( const spmatrix_t *spm, spm_int_t nrhs, const float *bloc, const float *bdst, int root ); int s_spm_dist_matvec_check( spm_int_t baseval, spm_trans_t trans, const spmatrix_t *spm ); +int s_spm_sort_check_values( const spmatrix_t *spm1, const spmatrix_t *spm2 ); void p_spm_print_check( char *filename, const spmatrix_t *spm ); diff --git a/tests/z_spm_sort_tests.c b/tests/z_spm_sort_tests.c new file mode 100644 index 0000000000000000000000000000000000000000..8f8e399763b8577c722e553b372d0776850ea734 --- /dev/null +++ b/tests/z_spm_sort_tests.c @@ -0,0 +1,292 @@ +/** + * + * @file z_spm_sort_tests.c + * + * Tests and validate the spm_sort routines. + * + * @copyright 2015-2020 Bordeaux INP, CNRS (LaBRI UMR 5800), Inria, + * Univ. Bordeaux. All rights reserved. + * + * @version 1.0.0 + * @author Mathieu Faverge + * @author Tony Delarue + * @date 2020-09-14 + * + * @precisions normal z -> c d s + * + **/ +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <spm_tests.h> +#include <z_spm.h> + +/** + * @brief Indicates if the col/row couple is already in the memory array + * + * @param[inout] memory + * The array that contains the already inspect col/row. + * If a value has already been seen, it's value is set to -1. + * + * @param[in] col + * The column index. + * + * @param[in] row + * The row index. + * + * @param[in] size + * The size of the memory array. + * + * @param[in] ijv + * Indicates spm format type, ijv or csx. + */ +static inline spm_int_t +z_spm_sort_is_in( spm_int_t *memory, + spm_int_t col, + spm_int_t row, + spm_int_t size, + int ijv ) +{ + spm_int_t i; + + if( !ijv ) { + for ( i = 0; i < size; i++) + { + if( memory[i] == row ) { + memory[i] = -1; + return 1; + } + } + } + else + { + for ( i = 0; i < size; i++) + { + if( (memory[2*i] == col) && (memory[2*i+1] == row) ) { + memory[2*i] = -1; + memory[2*i+1] = -1; + return 1; + } + } + } + + return 0; +} + +/** + * @brief Check that the value array of this two spm match + * for csx formatted matrices. + * + * We have to be aware that doublons may exists in the spm, so a memory will be + * set up in this routine to store the already seen col/row index. It's + * necessary to be sure we've checked the same information. + * + * @param[in] spm1 + * The unsorted spm. + * + * @param[in] spm2 + * The sorted spm. + */ +static inline int +z_spm_sort_check_values_csx( const spmatrix_t *spm1, + const spmatrix_t *spm2 ) +{ + /* The spm2 is the one which is sorted */ + spm_int_t i, j, krow, kval, n; + spm_int_t count, size; + spm_int_t dofi, dofj, dof2, baseval; + spm_int_t *col1, *col2; + spm_int_t *row1, *row2; + spm_int_t *dofs; + spm_complex64_t *val1, *val2, *valtmp; + + col1 = (spm1->fmttype == SpmCSC) ? spm1->colptr : spm1->rowptr; + col2 = (spm2->fmttype == SpmCSC) ? spm2->colptr : spm2->rowptr; + row1 = (spm1->fmttype == SpmCSC) ? spm1->rowptr : spm1->colptr; + row2 = (spm2->fmttype == SpmCSC) ? spm2->rowptr : spm2->colptr; + val1 = spm1->values; + val2 = spm2->values; + + n = spm1->n; + dofs = spm1->dofs; + baseval = spmFindBase(spm1); + for ( i = 0; i < n; i++, col1++, col2++) + { + /* Make sure we're on the same col */ + if( *col1 != *col2 ) { + return 1; + } + + size = col1[1] - col1[0]; + count = 0; + dofj = (spm1->dof > 0) ? spm1->dof : dofs[i+1] - dofs[i]; + valtmp = val1; + while( count < size ) + { + spm_int_t row_stored[size]; + memset( row_stored, 0xff, size * sizeof(spm_int_t) ); + krow = 0; + kval = 0; + /* + * Get the same row to do the comparison + * row2 is sorted. + */ + while( *row1 > row2[krow] ) + { + j = row2[krow] - baseval; + dofi = (spm2->dof > 0) ? spm2->dof : dofs[j+1] - dofs[j]; + kval += dofj * dofi; + krow++; + } + + j = *row1 - baseval; + dofi = (spm1->dof > 0) ? spm1->dof : dofs[j+1] - dofs[j]; + dof2 = dofj * dofi; + + while( z_spm_sort_is_in( row_stored, -1, *row1, size, 0 ) ) + { + krow++; + kval += dof2; + } + /* Make sure we're on the same row */ + assert( *row1 == row2[krow] ); + + row_stored[count] = *row1; + + /* Check the values array with multidof */ + for ( j = 0; j < dof2; j++) + { + if ( val1[j] != val2[kval + j] ) { + return 1; + } + } + count++; + row1++; + val1 += dof2; + } + row2 += size; + val2 += (val1 - valtmp); + } + + return 0; +} + +/** + * @brief Check that the value array of this two spm match + * for ijv formatted matrices. + * + * We have to be aware that doublons may exists in the spm, so a memory will be + * set up in this routine to store the already seen col/row index. It's + * necessary to be sure we've checked the same information. + * + * @param[in] spm1 + * The unsorted spm. + * + * @param[in] spm2 + * The sorted spm. + */ +static inline int +z_spm_sort_check_values_ijv( const spmatrix_t *spm1, + const spmatrix_t *spm2 ) +{ + spm_int_t i, j, k, kptr, kval, n; + spm_int_t dofi, dofj, dof2, baseval; + spm_int_t *col1, *col2; + spm_int_t *row1, *row2; + spm_int_t *dofs; + spm_int_t *memory; + spm_complex64_t *val1, *val2; + + col1 = spm1->colptr; + col2 = spm2->colptr; + row1 = spm1->rowptr; + row2 = spm2->rowptr; + val1 = spm1->values; + val2 = spm2->values; + + n = spm1->nnz; + dofs = spm1->dofs; + baseval = spmFindBase(spm1); + memory = malloc( 2* n * sizeof(spm_int_t) ); + memset( memory, 0xff, 2 * n * sizeof(spm_int_t) ); + for ( k = 0; k < n; k++, col1++, row1++ ) + { + kptr = 0; + kval = 0; + while( (*col1 != col2[kptr]) || (*row1 != row2[kptr]) ) + { + i = row2[kptr] - baseval; + j = col2[kptr] - baseval; + dofi = (spm2->dof > 0) ? spm2->dof : dofs[i+1] - dofs[i]; + dofj = (spm2->dof > 0) ? spm2->dof : dofs[j+1] - dofs[j]; + kval += dofj * dofi; + + kptr++; + assert(kptr < n); + } + + i = row2[kptr] - baseval; + j = col2[kptr] - baseval; + dofi = (spm2->dof > 0) ? spm2->dof : dofs[i+1] - dofs[i]; + dofj = (spm2->dof > 0) ? spm2->dof : dofs[j+1] - dofs[j]; + dof2 = dofj * dofi; + + /* Check for doublons */ + while( z_spm_sort_is_in( memory, *col1, *row1, k, 1 ) ) + { + kptr++; + kval += dof2; + } + assert( (*col1 == col2[kptr]) && (*row1 == row2[kptr]) ); + + memory[2*k] = *col1; + memory[2*k+1] = *row1; + + for ( i = 0; i < dof2; i++) + { + if ( val1[i] != val2[kval + i] ) { + free(memory); + return 1; + } + } + + val1 += dof2; + } + free(memory); + + return 0; +} + +/** + ******************************************************************************* + * + * @brief Check that the value array corresponds after the sort routine. + * These routines should not be called with multiple MPI processes. + * + ******************************************************************************* + * + * @param[in] spm1 + * A pointer to the unsorted spm. + * + * @param[in] spm2 + * A pointer to the sorted spm. + * + *******************************************************************************/ +int +z_spm_sort_check_values( const spmatrix_t *spm1, + const spmatrix_t *spm2 ) +{ + int rc; + + assert(spm1->fmttype == spm2->fmttype); + + if( spm1->fmttype != SpmIJV ) { + rc = z_spm_sort_check_values_csx(spm1, spm2); + } + else + { + rc = z_spm_sort_check_values_ijv(spm1, spm2); + } + return rc; +}