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

          Line data    Source code
       1             : //------------------------------------------------------------------------------
       2             : // LAGraph_EstimateDiameter: Graph Diameter Estimation
       3             : //------------------------------------------------------------------------------
       4             : 
       5             : // LAGraph, (c) 2021 by The LAGraph Contributors, All Rights Reserved.
       6             : // SPDX-License-Identifier: BSD-2-Clause
       7             : // See additional acknowledgments in the LICENSE file,
       8             : // or contact permission@sei.cmu.edu for the full terms.
       9             : 
      10             : // Contributed by Alexandra Goff
      11             : 
      12             : //------------------------------------------------------------------------------
      13             : 
      14             : // TODO: almost ready for src; need to handle GxB
      15             : 
      16             : // Takes in a graph and estimates the diameter 
      17             : // and optionally also finds pseudo-peripheral nodes of the graph
      18             : 
      19             : // Outputs: 
      20             : // Diameter returns the estimated diameter of the graph
      21             : // If not set to NULL, peripheral will be a vector with n elements
      22             : // index i of peripheral is the estimated diameter if it's a pseudo-peripheral node or nothing if not
      23             : 
      24             : // Inputs:
      25             : // G is the graph to be analyzed
      26             : // maxSrcs limits the number of sources used each cycle
      27             : // maxLoops limits the number of times the core loop will run if a stable diameter isn't found
      28             : // msg is a buffer for error messages
      29             : 
      30             : #define LG_FREE_WORK        \
      31             : {                           \
      32             :     GrB_free (&ecc) ;       \
      33             :     GrB_free (&candidateSrcs) ;       \
      34             :     GrB_free (&srcs) ;      \
      35             :     GrB_free (&level) ;     \
      36             :     GrB_free (&Mod) ;       \
      37             :     LAGraph_Free((void**) &sourceIndicies, NULL);   \
      38             :     LAGraph_Free((void**) &sourceValues, NULL);     \
      39             : }
      40             : 
      41             : #define LG_FREE_ALL         \
      42             : {                           \
      43             :     LG_FREE_WORK ;          \
      44             :     GrB_free (&peri) ;      \
      45             : }
      46             : 
      47             : #include "LG_internal.h"
      48             : #include "LAGraphX.h"
      49             : 
      50             : void mod32 (int32_t *z, const int32_t *x, const int32_t *y) ;
      51          64 : void mod32 (int32_t *z, const int32_t *x, const int32_t *y)
      52             : {
      53             :     /* make sure x is positive */
      54          64 :     int32_t t = ((*x) > 0) ? (*x) : -(*x) ;
      55          64 :     (*z) = t % (*y) ;
      56          64 : }
      57             : 
      58             : #define MOD32_DEFN                                                  \
      59             : "void mod32 (int32_t *z, const int32_t *x, const int32_t *y)    \n" \
      60             : "{                                                              \n" \
      61             : "    /* make sure x is positive */                              \n" \
      62             : "    int32_t t = ((*x) > 0) ? (*x) : -(*x) ;                    \n" \
      63             : "    (*z) = t % (*y) ;                                          \n" \
      64             : "}"
      65             : 
      66             : void mod64 (int64_t *z, const int64_t *x, const int64_t *y) ;
      67           8 : void mod64 (int64_t *z, const int64_t *x, const int64_t *y)
      68             : {
      69             :     /* make sure x is positive */
      70           8 :     int64_t t = ((*x) > 0) ? (*x) : -(*x) ;
      71           8 :     (*z) = t % (*y) ;
      72           8 : }
      73             : 
      74             : #define MOD64_DEFN                                                  \
      75             : "void mod64 (int64_t *z, const int64_t *x, const int64_t *y)    \n" \
      76             : "{                                                              \n" \
      77             : "    /* make sure x is positive */                              \n" \
      78             : "    int64_t t = ((*x) > 0) ? (*x) : -(*x) ;                    \n" \
      79             : "    (*z) = t % (*y) ;                                          \n" \
      80             : "}"
      81             : 
      82          21 : int LAGraph_EstimateDiameter
      83             : (
      84             :     // outputs:
      85             :     GrB_Index    *diameter,
      86             :     GrB_Vector    *peripheral,
      87             :     // inputs:
      88             :     const LAGraph_Graph G,
      89             :     GrB_Index    maxSrcs,
      90             :     GrB_Index    maxLoops,
      91             :     uint64_t     seed,          // seed for randomization
      92             :     char          *msg
      93             : )
      94             : {
      95             : #if LAGRAPH_SUITESPARSE
      96             : 
      97             :     //--------------------------------------------------------------------------
      98             :     // check inputs
      99             :     //--------------------------------------------------------------------------
     100             : 
     101          21 :     LG_CLEAR_MSG ;
     102          21 :     GrB_Vector ecc = NULL ;           // the eccentricity of the nodes
     103          21 :     GrB_Vector peri = NULL ;          // vector to store peripheral node status in
     104          21 :     GrB_Index d = 0 ;              // current diameter
     105          21 :     GrB_Index lastd = 0 ;          // previous diameter
     106          21 :     GrB_Vector srcs = NULL ;        // list of current sources
     107             :     GrB_Index nsrcs ;               // number of current sources
     108          21 :     GrB_Matrix level = NULL ;       // matrix for msbfs to put level info in
     109          21 :     GrB_Vector candidateSrcs = NULL ; // work vector for getting sources for the next iteration of the loop
     110          21 :     GrB_BinaryOp Mod = NULL ;
     111             : 
     112          21 :     GrB_Index *sourceIndicies = NULL ;
     113          21 :     int64_t *sourceValues = NULL ; // just need this so extractTuples will run
     114             : 
     115          21 :     bool compute_periphery  = (peripheral != NULL) ;
     116          21 :     if (compute_periphery ) (*peripheral) = NULL ;
     117          21 :     bool compute_diameter  = (diameter != NULL) ;
     118          21 :     LG_ASSERT_MSG (compute_diameter, GrB_NULL_POINTER,
     119             :         "Diameter destination must be non-NULL") ;
     120             : 
     121          21 :     LG_TRY (LAGraph_CheckGraph (G, msg)) ;
     122             : 
     123             :     //--------------------------------------------------------------------------
     124             :     // get the problem size and cached properties
     125             :     //--------------------------------------------------------------------------
     126             : 
     127          21 :     GrB_Matrix A = G->A ;
     128             :     
     129             :     GrB_Index n;        // number of nodes in the graph
     130          21 :     GRB_TRY (GrB_Matrix_nrows (&n, A)) ;
     131             : 
     132          21 :     GrB_Type int_type = (n > INT32_MAX) ? GrB_INT64 : GrB_INT32 ;
     133             : 
     134             :     //--------------------------------------------------------------------------
     135             :     // set up the first maxSrcs random nodes
     136             :     //--------------------------------------------------------------------------
     137             : 
     138             :     // currently just doing the first maxSrcs, consider different randomization
     139             :     // check maxSrcs < n
     140          21 :     if (maxSrcs > n)
     141             :     {
     142           2 :         nsrcs = n;
     143             :     }
     144             :     else
     145             :     {
     146          19 :         nsrcs = maxSrcs;
     147             :     }
     148          21 :     GRB_TRY (GrB_Vector_new (&srcs, int_type, nsrcs)) ;
     149             :     // srcs (0:nsrcs-1) = 0
     150          21 :     GRB_TRY (GrB_assign (srcs, NULL, NULL, 0, GrB_ALL, nsrcs, NULL)) ;
     151          21 :     if (nsrcs == n)
     152             :     {
     153             :         // srcs = 0:n-1
     154           4 :         GrB_IndexUnaryOp op =
     155           4 :             (n > INT32_MAX) ?  GrB_ROWINDEX_INT64 : GrB_ROWINDEX_INT32 ;
     156           4 :         GRB_TRY (GrB_apply (srcs, NULL, NULL, op, srcs, 0, NULL)) ;
     157             :     }
     158             :     else
     159             :     {
     160             :         // srcs = randomized, still of size nsrcs-1, values in range 0 to UINT64_MAX
     161             :         // TODO: if the graph is very sparse, select nodes at random with at
     162             :         // least one out-going edge.
     163          17 :         LAGRAPH_TRY (LAGraph_Random_Seed (srcs, seed, msg)) ;
     164          17 :         GRB_TRY (GxB_BinaryOp_new (&Mod,
     165             :             (n > INT32_MAX) ? ((GxB_binary_function)mod64) :
     166             :                               ((GxB_binary_function)mod32),
     167             :             int_type, int_type, int_type,
     168             :             (n > INT32_MAX) ? "mod64" : "mod32",
     169             :             (n > INT32_MAX) ? MOD64_DEFN : MOD32_DEFN)) ;
     170          17 :         GRB_TRY (GrB_apply (srcs, NULL, NULL, Mod, srcs, n, NULL)) ;
     171          17 :         GrB_free (&Mod) ;
     172             :     }
     173             : 
     174             :     //--------------------------------------------------------------------------
     175             :     // core loop, run until current and previous diameters match or reach given limit
     176             :     //--------------------------------------------------------------------------
     177             : 
     178          21 :     GrB_Monoid max =
     179          21 :         (n > INT32_MAX) ?  GrB_MAX_MONOID_INT64 : GrB_MAX_MONOID_INT32 ;
     180          21 :     GrB_IndexUnaryOp eqOp =
     181          21 :         (n > INT32_MAX) ?  GrB_VALUEEQ_INT64 : GrB_VALUEEQ_INT32 ;
     182          21 :     bool incSrcs = false;
     183          45 :     for (int64_t i = 0; i < maxLoops; i++)
     184             :     {
     185             :         // save previous diameter
     186          45 :         lastd = d;
     187             : 
     188             :         // get new diameter 
     189          45 :         GrB_free (&level) ;
     190          45 :         LG_TRY (LAGraph_MultiSourceBFS(&level, NULL, G, srcs, msg)) ;
     191          45 :         GrB_free (&ecc) ;
     192          45 :         GRB_TRY (GrB_Vector_new (&ecc, int_type, n)) ;
     193          45 :         GRB_TRY (GrB_reduce(ecc, NULL, NULL, max, level, GrB_DESC_T0)) ;
     194          45 :         GRB_TRY (GrB_reduce(&d, NULL, max, ecc, GrB_NULL)) ;
     195          45 :         GrB_free (&level) ;
     196             : 
     197             :         // check if done
     198          45 :         if (d == lastd){
     199          21 :             incSrcs = true;
     200          21 :             break;
     201             :         }
     202             : 
     203             :         // now with fewer for loops
     204             :         // said in last discussion: remaining for loop fine because of batch processing?
     205             : 
     206             :         // set up source list for next round
     207             :         // get the number of peripheral nodes 
     208          24 :         GrB_Index nperi = 0;
     209          24 :         GRB_TRY (GrB_Vector_new (&candidateSrcs, int_type, n)) ;
     210          24 :         GRB_TRY (GrB_select(candidateSrcs, NULL, NULL, eqOp, ecc, d, NULL)) ;
     211          24 :         GRB_TRY (GrB_Vector_nvals(&nperi, candidateSrcs)) ;
     212             :         
     213             : 
     214             :         // select the number of sources
     215          24 :         if (nperi > maxSrcs) {
     216           4 :             nsrcs = maxSrcs;
     217             :         } else {
     218          20 :             nsrcs = nperi;
     219             :         }
     220             : 
     221             :         // choose sources
     222          24 :         GrB_free (&srcs) ;
     223          24 :         GRB_TRY (GrB_Vector_new (&srcs, int_type, nsrcs)) ;
     224             : 
     225          24 :         GRB_TRY (LAGraph_Calloc((void **) &sourceIndicies, nperi, sizeof(GrB_Index), msg)) ;
     226          24 :         GRB_TRY (LAGraph_Calloc((void **) &sourceValues, nperi, sizeof(int64_t), msg)) ;
     227          24 :         GRB_TRY (GrB_Vector_extractTuples(sourceIndicies, sourceValues, &nperi, candidateSrcs)) ;
     228         126 :         for (int64_t j = 0; j < nsrcs; j++) {
     229         102 :             GRB_TRY (GrB_Vector_setElement (srcs, sourceIndicies[j], j)) ;
     230             :         }
     231          24 :         LAGraph_Free((void**) &sourceIndicies, NULL);
     232          24 :         LAGraph_Free((void**) &sourceValues, NULL);
     233          24 :         GrB_free(&candidateSrcs) ;
     234             :     }
     235             : 
     236             :     //--------------------------------------------------------------------------
     237             :     // after loop, set up peripheral nodes if needed
     238             :     //--------------------------------------------------------------------------
     239          21 :     if (compute_periphery) {
     240          21 :         GRB_TRY (GrB_Vector_new (&peri, int_type, n)) ;
     241             : 
     242          21 :         GRB_TRY (GrB_select(peri, NULL, NULL, eqOp, ecc, d, NULL)) ;
     243             : 
     244          21 :         if (incSrcs) {
     245         111 :             for (int64_t i = 0; i < nsrcs; i++) {
     246             :                 GrB_Index currsrc;
     247          90 :                 GRB_TRY(GrB_Vector_extractElement(&currsrc, srcs, i));
     248          90 :                 GRB_TRY (GrB_Vector_setElement (peri, d, currsrc)) ;  
     249             :             }
     250             :         }
     251             :        
     252             : 
     253             :     }
     254             : 
     255             :     //--------------------------------------------------------------------------
     256             :     // free workspace and return result
     257             :     //--------------------------------------------------------------------------
     258             : 
     259          21 :     if (compute_periphery) (*peripheral) = peri ;
     260          21 :     (*diameter ) = d ;
     261          21 :     LG_FREE_WORK ;
     262          21 :     return (GrB_SUCCESS) ;
     263             : #else
     264             :     return (GrB_NOT_IMPLEMENTED) ;
     265             : #endif
     266             : }

Generated by: LCOV version 1.14