LCOV - code coverage report
Current view: top level - experimental/algorithm - LAGraph_RichClubCoefficient.c (source / functions) Hit Total Coverage
Test: LAGraph code coverage report. Commit id: 7b9d2ee. Current time (UTC): 2025-06-03T21:57:17Z Lines: 81 81 100.0 %
Date: 2025-06-03 22:02:40 Functions: 3 3 100.0 %

          Line data    Source code
       1             : //------------------------------------------------------------------------------
       2             : // LAGraph_RichClubCoefficient: rich club coefficient of a graph
       3             : //------------------------------------------------------------------------------
       4             : 
       5             : // LAGraph, (c) 2019-2022 by The LAGraph Contributors, All Rights Reserved.
       6             : // SPDX-License-Identifier: BSD-2-Clause
       7             : //
       8             : // For additional details (including references to third party source code and
       9             : // other files) see the LICENSE file or contact permission@sei.cmu.edu. See
      10             : // Contributors.txt for a full list of contributors. Created, in part, with
      11             : // funding and support from the U.S. Government (see Acknowledgments.txt file).
      12             : // DM22-0790
      13             : 
      14             : // Contributed by Gabriel Gomez, Texas A&M University
      15             : 
      16             : //------------------------------------------------------------------------------
      17             : 
      18             : // Get the rich club coefficient of a graph.
      19             : 
      20             : // Given a Symetric Graph with no self edges, LAGraph_RichClubCoefficient will
      21             : // calculate the rich club coefficients of the graph. 
      22             : 
      23             : // The values will be output as a sparse GrB_Vector, the rich club coefficient 
      24             : // of k will be found at the closest entry at or above k.
      25             : 
      26             : // The G->out_degree cached property must be defined for this method.
      27             : 
      28             : // References:
      29             : 
      30             : // Julian J. McAuley, Luciano da Fontoura Costa, and Tibério S. Caetano, “The 
      31             : // rich-club phenomenon across complex network hierarchies”, Applied Physics 
      32             : // Letters Vol 91 Issue 8, August 2007. https://arxiv.org/abs/physics/0701290
      33             : 
      34             : #define LG_FREE_WORK                                    \
      35             : {                                                       \
      36             :     /* free any workspace used here */                  \
      37             :     GrB_free(&D) ;                                      \
      38             :     GrB_free(&P) ;                                      \
      39             :     GrB_free(&A_deg) ;                                  \
      40             :     GrB_free(&degrees) ;                                \
      41             :     GrB_free(&deg_x) ;                                  \
      42             :     GrB_free(&node_edges) ;                             \
      43             :     GrB_free(&node_edges_x) ;                           \
      44             :     GrB_free(&ones_v) ;                                 \
      45             :     GrB_free(&edges_per_deg) ;                          \
      46             :     GrB_free(&verts_per_deg) ;                          \
      47             :     GrB_free(&iseq_2lt) ;                               \
      48             :     GrB_free(&plus_2le) ;                               \
      49             :     GrB_free(&rcCalculation) ;                          \
      50             :     GrB_free(&ramp_v) ;                                 \
      51             :     LAGraph_Free(&a_space, NULL) ;                      \
      52             : }
      53             : 
      54             : 
      55             : #define LG_FREE_ALL                         \
      56             : {                                           \
      57             :     /* free any workspace used here */      \
      58             :     LG_FREE_WORK ;                          \
      59             :     /* free all the output variable(s) */   \
      60             :     GrB_free (rccs) ;      \
      61             : }
      62             : 
      63             : #include "LG_internal.h"
      64             : #include "LAGraphX.h"
      65             : 
      66             : typedef void (*LAGraph_binary_function) (void *, const void *, const void *) ;
      67             : 
      68             : #define ISEQ_2ISLT                                                          \
      69             :     "void iseq_2islt(int64_t *z, const int64_t *x, const int64_t *y)            \n"\
      70             :     "{                                                                          \n"\
      71             :         "(*z) = (int64_t)((*x < *y) + (*x <= *y)) ;                             \n"\
      72             :     "}"
      73     5816980 : void iseq_2islt(int64_t *z, const int64_t *x, const int64_t *y)
      74             : {
      75     5816980 :     (*z) = (int64_t)((*x < *y) + (*x <= *y)) ;
      76     5816980 : }
      77             : 
      78             : #define RICH_CLUB_FORMULA                                                      \
      79             :     "void rich_club_formula(double *z, const int64_t *x, const int64_t *y)      \n"\
      80             :     "{                                                                          \n"\
      81             :     "   (*z) = ((double)(*x)) / (((double)(*y)) * (((double)(*y)) - 1.0)) ;     \n"\
      82             :     "}"
      83         126 : void rich_club_formula(double *z, const int64_t *x, const int64_t *y)
      84             : {
      85         126 :     (*z) = ((double)(*x)) / (((double)(*y)) * (((double)(*y)) - 1.0));
      86         126 : } 
      87         402 : int LAGraph_RichClubCoefficient
      88             : (
      89             :     // output:
      90             :     //rccs(i): rich club coefficent of i
      91             :     GrB_Vector *rccs,    
      92             : 
      93             :     // input: 
      94             :     LAGraph_Graph G, //input graph
      95             :     char *msg
      96             : )
      97             : {
      98             :     //--------------------------------------------------------------------------
      99             :     // Declorations
     100             :     //--------------------------------------------------------------------------
     101         402 :     LG_CLEAR_MSG ;
     102             : 
     103             :     // n x n Adjacency Matrix
     104             :     // With values cooresponding to the degree of its column
     105         402 :     GrB_Matrix A_deg = NULL;
     106             : 
     107             :     // n x n Diagonal Matrix
     108             :     // entries corresponding to degrees.
     109         402 :     GrB_Matrix D = NULL;
     110             : 
     111             :     // n degrees vector
     112         402 :     GrB_Vector degrees = NULL, deg_x = NULL;
     113             : 
     114             :     // n x 1
     115             :     // contains the number of edges for which the ith node is
     116             :     // the smallest degree node * 2 + # edges w/ same degree as the other node
     117             :     // to account for double counting of edges w/ same degree as the other node.
     118         402 :     GrB_Vector node_edges = NULL, node_edges_x = NULL;
     119             : 
     120             :     // max_degree x 1
     121             :     // the ith entry contains the number of edges whose lowest degree is i.
     122         402 :     GrB_Vector edges_per_deg = NULL;
     123             : 
     124             :     // max_degree x 1
     125             :     // the ith entry contains the number of verticies whose degree is i.
     126         402 :     GrB_Vector verts_per_deg = NULL;
     127             : 
     128             :     // edge_vec_nvals x 1
     129             :     // Vector of ones
     130         402 :     GrB_Vector ones_v = NULL;
     131             : 
     132             :     // Ramp vector
     133         402 :     GrB_Vector ramp_v = NULL;
     134             : 
     135             :     // 2 * (x < y) + (x == y)
     136         402 :     GrB_BinaryOp iseq_2lt = NULL;
     137             : 
     138             :     // [+].[iseq_2lt]
     139         402 :     GrB_Semiring plus_2le = NULL;
     140             : 
     141             :     // 2E_K / (N_k (N_k -1))
     142         402 :     GrB_BinaryOp rcCalculation = NULL;
     143             : 
     144         402 :     GrB_Matrix A = NULL; // G->A, the adjacency matrix
     145             : 
     146             :     // Matrix used for row reduction
     147         402 :     GrB_Matrix P = NULL;
     148             : 
     149             :     GrB_Index n ;
     150             :     
     151             :     GrB_Index edge_vec_nvals;
     152             :     int64_t max_deg;
     153         402 :     bool iso = false;
     154             : 
     155         402 :     void *a_space = NULL;
     156             :     
     157         402 :     int64_t *node_edges_arr = NULL, *deg_arr = NULL, 
     158         402 :         *epd_arr = NULL, *ones = NULL, 
     159         402 :         *vpd_arr = NULL;
     160         402 :     GrB_Type epd_type = NULL, vpd_type = NULL;
     161         402 :     uint64_t epd_n = 0, vpd_n = 0, epd_size = 0, vpd_size = 0;
     162         402 :     int epd_h = 0, vpd_h = 0;
     163         402 :     GrB_Index *epd_index = NULL,  *vpd_index = NULL;
     164             : 
     165             :     //--------------------------------------------------------------------------
     166             :     // Check inputs
     167             :     //--------------------------------------------------------------------------
     168         402 :     LG_TRY (LAGraph_CheckGraph (G, msg)) ;
     169         402 :     LG_ASSERT (rccs != NULL, GrB_NULL_POINTER);
     170             : 
     171         402 :     LG_ASSERT_MSG(
     172             :         G->kind == LAGraph_ADJACENCY_UNDIRECTED, GrB_INVALID_VALUE, 
     173             :         "G->A must be symmetric") ;
     174         402 :     LG_ASSERT_MSG (G->out_degree != NULL, GrB_EMPTY_OBJECT,
     175             :         "G->out_degree must be defined") ;
     176         402 :     LG_ASSERT_MSG (G->nself_edges == 0, GrB_INVALID_VALUE, 
     177             :         "G->nself_edges must be zero") ; 
     178             : 
     179             :     //--------------------------------------------------------------------------
     180             :     // Initializations
     181             :     //--------------------------------------------------------------------------
     182         402 :     A = G->A ;
     183         402 :     GRB_TRY (GrB_Matrix_nrows (&n, A)) ;
     184         402 :     GRB_TRY (GrB_Matrix_new(&A_deg, GrB_INT64,n,n)) ;
     185             : 
     186         390 :     GRB_TRY (GrB_Vector_new(&degrees, GrB_INT64, n)) ;
     187         382 :     GRB_TRY (GrB_Vector_new(&node_edges, GrB_INT64, n)) ;
     188             :     #if LAGRAPH_SUITESPARSE
     189         374 :     GRB_TRY (GxB_BinaryOp_new(
     190             :         &iseq_2lt, (LAGraph_binary_function) (&iseq_2islt), 
     191             :         GrB_INT64, GrB_INT64, GrB_INT64, "iseq_2islt", ISEQ_2ISLT)) ;
     192         366 :     GRB_TRY (GxB_BinaryOp_new(
     193             :         &rcCalculation, (LAGraph_binary_function) (&rich_club_formula), 
     194             :         GrB_FP64, GrB_INT64, GrB_INT64, 
     195             :         "rich_club_formula", RICH_CLUB_FORMULA)) ;
     196             :     #else
     197             :     GRB_TRY (GrB_BinaryOp_new(
     198             :         &iseq_2lt, (LAGraph_binary_function) (&iseq_2islt), 
     199             :         GrB_INT64, GrB_INT64, GrB_INT64)) ;
     200             :     GRB_TRY (GrB_BinaryOp_new(
     201             :         &rcCalculation, (LAGraph_binary_function) (&rich_club_formula), 
     202             :         GrB_FP64, GrB_INT64, GrB_INT64 )) ;
     203             :     #endif
     204             : 
     205         358 :     GRB_TRY (GrB_Semiring_new(&plus_2le, GrB_PLUS_MONOID_INT64, iseq_2lt)) ;
     206             :     
     207         350 :     GRB_TRY (GrB_Vector_reduce_INT64(
     208             :         &max_deg, NULL, GrB_MAX_MONOID_INT64, G->out_degree, NULL)) ;
     209         350 :     GRB_TRY (GrB_Vector_new(&edges_per_deg, GrB_INT64, max_deg)) ;
     210         342 :     GRB_TRY (GrB_Vector_new(&verts_per_deg, GrB_INT64, max_deg)) ;
     211         334 :     GRB_TRY (GrB_Vector_new(rccs, GrB_FP64, max_deg)) ;
     212             : 
     213             :     //--------------------------------------------------------------------------
     214             :     // Calculations
     215             :     //--------------------------------------------------------------------------
     216             : 
     217             :     // degrees = G->out_degree - 1
     218             :     // Fill out degree vector, to target col_scale mxm on graphs 
     219             :     // with singletons, scalar value irrelevant.
     220         326 :     GRB_TRY (GrB_Vector_assign_INT64(
     221             :         degrees, NULL, NULL, (int64_t) -1, GrB_ALL, 0, NULL)) ;
     222         322 :     GRB_TRY (GrB_Vector_assign(
     223             :         degrees, NULL, GrB_PLUS_INT64, G->out_degree, GrB_ALL, 0, NULL)) ;
     224         318 :     GRB_TRY (GrB_Matrix_diag(&D, degrees, 0)) ;
     225             : 
     226             :     // Each edge in the graph gets the value of the degree of its row node
     227             :     #if LAGRAPH_SUITESPARSE
     228         294 :     GRB_TRY (GrB_mxm(
     229             :         A_deg, NULL, NULL, GxB_ANY_FIRST_INT64, D, A, NULL)) ;
     230             :     #else
     231             :     GRB_TRY (GrB_mxm(
     232             :         A_deg, NULL, NULL, GrB_PLUS_TIMES_SEMIRING_INT64, D, A, NULL)) ;
     233             :     #endif
     234             :     // Sum the number of edges each node is "responsible" for.
     235         282 :     GRB_TRY (GrB_mxv(
     236             :         node_edges, NULL, GrB_PLUS_INT64, plus_2le, A_deg, degrees, NULL)) ;
     237             : 
     238             :     // The rest of this is indexing the number of edges and number of nodes at 
     239             :     // each degree and then doing a cummulative sum to know the amount of edges 
     240             :     // and nodes at degree geq k.
     241         276 :     GRB_TRY (GrB_Vector_nvals (&edge_vec_nvals, node_edges)) ;
     242             :     #if LG_SUITESPARSE_GRAPHBLAS_V10
     243         276 :         if(n == edge_vec_nvals)
     244             :         {
     245         199 :             deg_x = degrees;
     246         199 :             degrees = NULL;
     247         199 :             node_edges_x = node_edges;
     248         199 :             node_edges = NULL;
     249             :         }
     250             :         else
     251             :         {
     252          77 :             GRB_TRY (GrB_Vector_assign(
     253             :                 degrees, G->out_degree, NULL, degrees, GrB_ALL, 0, GrB_DESC_RS
     254             :             )) ;
     255          75 :             GRB_TRY (GrB_Vector_new(&deg_x, GrB_BOOL, 0)) ;  
     256          73 :             GRB_TRY (GrB_Vector_new(&node_edges_x, GrB_BOOL, 0)) ;  
     257          71 :             GRB_TRY (GxB_Vector_extractTuples_Vector(
     258             :                 NULL, deg_x, degrees, NULL
     259             :             )) ;
     260          69 :             GRB_TRY (GxB_Vector_extractTuples_Vector(
     261             :                 NULL, node_edges_x, node_edges, NULL
     262             :             )) ;
     263             :         }
     264         266 :         GRB_TRY (GrB_Vector_nvals(&edge_vec_nvals, node_edges_x))
     265         266 :         GRB_TRY (GrB_Vector_new(&ones_v, GrB_INT64, edge_vec_nvals)) ;
     266             : 
     267             :         
     268         259 :         GRB_TRY (GrB_Vector_assign_INT64(
     269             :             edges_per_deg, NULL, NULL, (int64_t) 0, GrB_ALL, 0, NULL)) ;
     270         255 :         GRB_TRY (GrB_Vector_assign_INT64(
     271             :             verts_per_deg, NULL, NULL, (int64_t) 0, GrB_ALL, 0, NULL)) ;
     272         251 :         GRB_TRY (GrB_Vector_assign_INT64(
     273             :             ones_v, NULL, NULL, (int64_t) 0, GrB_ALL, 0, NULL)) ;
     274             :             
     275             :         #ifndef COVERAGE
     276             :         GRB_TRY (GrB_Vector_new(&ramp_v, GrB_INT64, edge_vec_nvals + 1)) ;  
     277             :         GRB_TRY (GrB_Vector_assign_INT64(
     278             :             ramp_v, NULL, NULL, (int64_t) 0, GrB_ALL, 0, NULL)) ;
     279             :         GRB_TRY (GrB_apply (
     280             :             ramp_v, NULL, NULL, GrB_ROWINDEX_INT64, ramp_v, 0, NULL)) ;
     281             :         #endif
     282             : 
     283         247 :         LG_TRY (LAGraph_FastAssign_Semiring (
     284             :             edges_per_deg, NULL, GrB_PLUS_INT64, deg_x, node_edges_x, ramp_v,
     285             :             GxB_PLUS_SECOND_INT64, NULL, msg
     286             :         )) ;
     287         135 :         LG_TRY (LAGraph_FastAssign_Semiring (
     288             :             verts_per_deg, NULL, GrB_PLUS_INT64, deg_x, ones_v, ramp_v,
     289             :             GxB_PLUS_PAIR_INT64, NULL, msg
     290             :         )) ;
     291             : 
     292          23 :         GRB_TRY (GxB_Vector_unload(
     293             :             edges_per_deg, (void **) &epd_arr, &epd_type,
     294             :             &epd_n, &epd_size, &epd_h, NULL)) ;
     295          23 :         GRB_TRY (GxB_Vector_unload(
     296             :             verts_per_deg, (void **) &vpd_arr, &vpd_type,
     297             :             &vpd_n, &vpd_size, &vpd_h, NULL)) ;
     298             :         
     299          23 :         LG_ASSERT (max_deg == vpd_n && max_deg == epd_n, GrB_INVALID_VALUE) ;
     300             :         //run a cummulative sum (backwards) on vpd_arr
     301        2501 :         for(int64_t i = max_deg - 1; i > 0; --i)
     302             :         {
     303        2478 :             vpd_arr[i-1] += vpd_arr[i] ;
     304        2478 :             epd_arr[i-1] += epd_arr[i] ;
     305             :         }
     306          23 :         GRB_TRY(GxB_Vector_load(
     307             :             edges_per_deg, (void **) &epd_arr, epd_type,
     308             :             epd_n, epd_size, epd_h, NULL)) ;
     309          23 :         GRB_TRY(GxB_Vector_load(
     310             :             verts_per_deg, (void **) &vpd_arr, vpd_type,
     311             :             vpd_n, vpd_size, vpd_h, NULL)) ;
     312             :     #else
     313             :         LG_TRY (LAGraph_Malloc(
     314             :             &a_space, edge_vec_nvals * 3 + max_deg * 4, sizeof(int64_t), NULL
     315             :         )) ;
     316             :         int64_t *T = a_space;
     317             :         deg_arr = T;            T += edge_vec_nvals;
     318             :         node_edges_arr = T;     T += edge_vec_nvals;
     319             :         ones = T;               T += edge_vec_nvals;
     320             :         epd_arr = T;            T += max_deg;
     321             :         vpd_arr = T;            T += max_deg;
     322             :         epd_index = T;          T += max_deg;
     323             :         vpd_index = T;          T += max_deg;
     324             : 
     325             :         #pragma omp parallel for schedule(static)
     326             :         for(uint64_t i = 0; i < edge_vec_nvals; ++i)
     327             :         {
     328             :             ones[i] = 1ll;
     329             :         }
     330             :         GRB_TRY (GrB_Vector_apply_BinaryOp2nd_INT64(
     331             :             degrees, NULL, NULL, GrB_MINUS_INT64, G->out_degree, 1, NULL)) ;
     332             :         //TODO: remove NULL for Vanilla GB
     333             :         GRB_TRY (GrB_Vector_extractTuples_INT64(
     334             :             NULL, deg_arr, &edge_vec_nvals, degrees
     335             :         )) ;
     336             :         GRB_TRY (GrB_Vector_extractTuples_INT64(
     337             :             NULL, node_edges_arr, &edge_vec_nvals, node_edges
     338             :         )) ;
     339             : 
     340             :         // Build with degrees as indecies and handle duplicates via adition
     341             :         GRB_TRY (GrB_Vector_build_INT64 (
     342             :             edges_per_deg, deg_arr, node_edges_arr, edge_vec_nvals, 
     343             :             GrB_PLUS_INT64)) ;
     344             :         GRB_TRY (GrB_Vector_build_INT64 (
     345             :             verts_per_deg, deg_arr, ones, edge_vec_nvals, GrB_PLUS_INT64)) ;
     346             :         GRB_TRY (GrB_Vector_assign_INT64(
     347             :             edges_per_deg, edges_per_deg, NULL, (int64_t) 0, 
     348             :             GrB_ALL, 0, GrB_DESC_SC)) ;
     349             :         GRB_TRY (GrB_Vector_assign_INT64(
     350             :             verts_per_deg, verts_per_deg, NULL, (int64_t) 0, 
     351             :             GrB_ALL, 0, GrB_DESC_SC)) ;
     352             :         
     353             :         // Extract into arrays
     354             :         GRB_TRY (GrB_Vector_extractTuples_INT64(
     355             :             epd_index, epd_arr, &max_deg, edges_per_deg
     356             :         )) ;
     357             :         GRB_TRY (GrB_Vector_extractTuples_INT64(
     358             :             vpd_index, vpd_arr, &max_deg, verts_per_deg
     359             :         )) ;
     360             :         //run a cummulative sum (backwards) on vpd_arr
     361             :         for(int64_t i = max_deg - 1; i > 0; --i)
     362             :         {
     363             :             vpd_arr[i-1] += vpd_arr[i] ;
     364             :             epd_arr[i-1] += epd_arr[i] ;
     365             :         }
     366             :         GRB_TRY (GrB_Vector_clear(edges_per_deg)) ;
     367             :         GRB_TRY (GrB_Vector_clear(verts_per_deg)) ;
     368             :         GRB_TRY (GrB_Vector_build_INT64(
     369             :             edges_per_deg, epd_index, epd_arr, max_deg, NULL
     370             :         )) ;
     371             :         GRB_TRY (GrB_Vector_build_INT64(
     372             :             verts_per_deg, vpd_index, vpd_arr, max_deg, NULL
     373             :         )) ;
     374             :         T = deg_arr = node_edges_arr = ones = NULL ;
     375             :         epd_index = vpd_index = epd_arr = vpd_arr = NULL ;
     376             :     #endif
     377             : 
     378             :     /**
     379             :      * Cumulative sum (TODO: should be a GBLAS method!)
     380             :      * 
     381             :      * GrB_cumsum(GrB_Matrix C, const GrB_Matrix mask, const GrB_BinaryOp accum,
     382             :      *      const GrB_BinaryOp plus, GrB_Matrix A, const GrB_Descriptor desc)
     383             :      * 
     384             :      * By default sums rows. Returns a nearly full matrix:
     385             :      * [., ., 1, 1, 1, 1, ., ., 1] --> [., ., 1, 2, 3, 4, 4, 4, 5]
     386             :      * Mask can be A, then returns a matrix with the same pattern.
     387             :      * [., ., 1, 1, 1, 1, ., ., 1] --> [., ., 1, 2, 3, 4, ., ., 5]
     388             :      * 
     389             :      * Should we be able to sum in the opposite direction? 
     390             :      *  Yes since not all monoids have inverse operations. 
     391             :      * 
     392             :      * If plus biop is not a monoid, this method should still work?
     393             :      */
     394             :     
     395             :     //Computes the RCC of a matrix
     396          23 :     GRB_TRY(GrB_eWiseMult(
     397             :         *rccs, NULL, NULL, rcCalculation, 
     398             :         edges_per_deg, verts_per_deg, NULL
     399             :     )) ;
     400             : 
     401          15 :     LG_FREE_WORK ;
     402          15 :     return (GrB_SUCCESS) ;
     403             : }

Generated by: LCOV version 1.14