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

          Line data    Source code
       1             : //------------------------------------------------------------------------------
       2             : // LAGraph_MultiSourceBFS: BFS from several source nodes in parallel
       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 a vanilla method
      15             : 
      16             : // Takes in a vector of source nodes and finds level and/or parent vectors for each,
      17             : // stored together in a matrix
      18             : 
      19             : #define LG_FREE_WORK        \
      20             : {                           \
      21             :     GrB_free (&q) ;         \
      22             : }
      23             : 
      24             : #define LG_FREE_ALL         \
      25             : {                           \
      26             :     LG_FREE_WORK ;          \
      27             :     GrB_free (&pi) ;        \
      28             :     GrB_free (&v) ;         \
      29             : }
      30             : 
      31             : #include "LG_internal.h"
      32             : #include "LAGraphX.h"
      33             : 
      34         474 : int LAGraph_MultiSourceBFS
      35             : (
      36             :     // outputs:
      37             :     GrB_Matrix    *level,
      38             :     GrB_Matrix    *parent,
      39             :     // inputs:
      40             :     const LAGraph_Graph G,
      41             :     GrB_Vector      src,
      42             :     char          *msg
      43             : )
      44             : {
      45             : #if LAGRAPH_SUITESPARSE
      46             : 
      47             :     //--------------------------------------------------------------------------
      48             :     // check inputs
      49             :     //--------------------------------------------------------------------------
      50             : 
      51         474 :     LG_CLEAR_MSG ;
      52         474 :     GrB_Matrix q = NULL ;           // the current frontier 
      53             :     // GrB_Vector w = NULL ;     to compute work remaining, removed since not doing push-pull
      54         474 :     GrB_Matrix pi = NULL ;          // parent matrix
      55         474 :     GrB_Matrix v = NULL ;           // level matrix
      56             : 
      57         474 :     bool compute_level  = (level != NULL) ;
      58         474 :     bool compute_parent = (parent != NULL) ;
      59         474 :     if (compute_level ) (*level ) = NULL ;
      60         474 :     if (compute_parent) (*parent) = NULL ;
      61         474 :     LG_ASSERT_MSG (compute_level || compute_parent, GrB_NULL_POINTER,
      62             :         "either level or parent must be non-NULL") ;
      63             : 
      64         474 :     LG_TRY (LAGraph_CheckGraph (G, msg)) ;
      65             : 
      66             :     //--------------------------------------------------------------------------
      67             :     // get the problem size and cached properties
      68             :     //--------------------------------------------------------------------------
      69             : 
      70         474 :     GrB_Matrix A = G->A ;
      71             :     
      72             :     GrB_Index nsrc; // holds the number of source nodes
      73             :     GrB_Index n, nvals ;
      74         474 :     GRB_TRY (GrB_Matrix_nrows (&n, A)) ;
      75         474 :     GRB_TRY (GrB_Matrix_nvals (&nvals, A)) ;
      76         474 :     GRB_TRY (GrB_Vector_size (&nsrc, src)) ;
      77        4080 :     for (int64_t s = 0; s < nsrc; s++) 
      78             :     {
      79             :         GrB_Index currsrc;
      80        3606 :         GRB_TRY (GrB_Vector_extractElement (&currsrc, src, s)) ;
      81        3606 :         LG_ASSERT_MSG (currsrc < n, GrB_INVALID_INDEX, "invalid source node") ;
      82             :     }
      83         474 :     bool very_sparse = (nvals <= n/16) ;
      84             : 
      85             :     // determine the semiring type 
      86         474 :     GrB_Type int_type = (n > INT32_MAX) ? GrB_INT64 : GrB_INT32 ;
      87             :     GrB_Semiring semiring ;
      88             : 
      89         474 :     if (compute_parent)
      90             :     {
      91             :         // use the ANY_SECONDI_INT* semiring: either 32 or 64-bit depending on
      92             :         // the # of nodes in the graph.
      93          22 :         semiring = (n > INT32_MAX) ?
      94          11 :             GxB_ANY_SECONDI_INT64 : GxB_ANY_SECONDI_INT32 ;
      95             : 
      96             :         // create the parent matrix.  pi(i, j) is the parent id of node j in source i's BFS
      97          11 :         GRB_TRY (GrB_Matrix_new (&pi, int_type, nsrc, n)) ;
      98          11 :         if (!very_sparse)
      99             :         {
     100          10 :             GRB_TRY (LG_SET_FORMAT_HINT (pi, LG_BITMAP + LG_FULL)) ;
     101             :         }
     102             : 
     103             :         // pi (i, src) = src denotes the root of that row's BFS tree
     104          55 :         for (int64_t s = 0; s < nsrc; s++) 
     105             :         {
     106             :             GrB_Index currsrc;
     107          44 :             GRB_TRY (GrB_Vector_extractElement (&currsrc, src, s)) ;
     108             :             // now s contains the row associated with the current source node
     109             :             // and currsrc contains the column that should index itself
     110          44 :             GRB_TRY (GrB_Matrix_setElement (pi, currsrc, s, currsrc)) ;
     111             :         }
     112             : 
     113             :         // create a sparse integer matrix q, and set q(s, src) = src for each row's source
     114          11 :         GRB_TRY (GrB_Matrix_new (&q, int_type, nsrc, n)) ;
     115          55 :         for (int64_t s = 0; s < nsrc; s++) 
     116             :         {
     117             :             GrB_Index currsrc;
     118          44 :             GRB_TRY (GrB_Vector_extractElement (&currsrc, src, s)) ;
     119             :             // now s contains the row associated with the current source node
     120             :             // and currsrc contains the column that should index itself
     121          44 :             GRB_TRY (GrB_Matrix_setElement (q, currsrc, s, currsrc)) ;
     122             :         }
     123             :     }
     124             :     else
     125             :     {
     126             :         // only the level is needed, use the LAGraph_any_one_bool semiring
     127         463 :         semiring = LAGraph_any_one_bool ;
     128             : 
     129             :         // create a sparse boolean matrix q, and set q(s, src) = true for the source in each row
     130         463 :         GRB_TRY (GrB_Matrix_new (&q, GrB_BOOL, nsrc, n)) ;
     131        4025 :         for (int64_t s = 0; s < nsrc; s++) 
     132             :         {
     133             :             GrB_Index currsrc;
     134        3562 :             GRB_TRY (GrB_Vector_extractElement (&currsrc, src, s)) ;
     135             :             // now s contains the row associated with the current source node
     136             :             // and currsrc contains the column that should be true
     137        3562 :             GRB_TRY (GrB_Matrix_setElement (q, true, s, currsrc)) ;
     138             :         }
     139             :     }
     140             : 
     141         474 :     if (compute_level)
     142             :     {
     143             :         // create the level matrix. v(i,j) is the level of node j in source i's BFS
     144             :         // v (s, src) = 0 denotes the source node of that row
     145         474 :         GRB_TRY (GrB_Matrix_new (&v, int_type, nsrc, n)) ;
     146         474 :         if (!very_sparse)
     147             :         {
     148         472 :             GRB_TRY (LG_SET_FORMAT_HINT (v, LG_BITMAP + LG_FULL)) ;
     149             :         }
     150        4080 :         for (int64_t s = 0; s < nsrc; s++) 
     151             :         {
     152             :             GrB_Index currsrc;
     153        3606 :             GRB_TRY (GrB_Vector_extractElement (&currsrc, src, s)) ;
     154             :             // now s contains the row associated with the current source node
     155             :             // and currsrc contains the column that should be 0
     156        3606 :             GRB_TRY (GrB_Matrix_setElement (v, 0, s, currsrc)) ;
     157             :         }
     158             :     }
     159             : 
     160         474 :     GrB_Index nq = nsrc ;      // number of nodes in the current level
     161             :     // skipping work remaining computation set-up since we're not doing push-pull
     162             : 
     163             :     //--------------------------------------------------------------------------
     164             :     // BFS traversal and label the nodes
     165             :     //--------------------------------------------------------------------------
     166             : 
     167             :     // {!mask} is the set of unvisited nodes
     168         474 :     GrB_Matrix mask = (compute_parent) ? pi : v ;
     169             : 
     170       10805 :     for (int64_t nvisited = nsrc, k = 1 ; nvisited < n*nsrc ; nvisited += nq, k++)
     171             :     {
     172             : 
     173             :         //----------------------------------------------------------------------
     174             :         // q = frontier at the kth level of the BFS
     175             :         //----------------------------------------------------------------------
     176             : 
     177             :         // mask is pi if computing parent, v if computing just level
     178             :         
     179             :         // push (saxpy-based mxm):  q'{!mask} = q'*A 
     180       10333 :         GRB_TRY (GrB_mxm (q, mask, NULL, semiring, q, A, GrB_DESC_RSC)) ;
     181             :         
     182             :         //----------------------------------------------------------------------
     183             :         // done if q is empty
     184             :         //----------------------------------------------------------------------
     185             : 
     186       10333 :         GRB_TRY (GrB_Matrix_nvals (&nq, q)) ;
     187       10333 :         if (nq == 0)
     188             :         {
     189           2 :             break ;
     190             :         }
     191             : 
     192             :         //----------------------------------------------------------------------
     193             :         // assign parents/levels
     194             :         //----------------------------------------------------------------------
     195             : 
     196       10331 :         if (compute_parent)
     197             :         {
     198             :             // q(s, i) currently contains the parent id of node i in tree s.
     199             :             // pi{q} = q
     200         100 :             GRB_TRY (GrB_assign (pi, q, NULL, q, GrB_ALL, nsrc, GrB_ALL, n, GrB_DESC_S)) ;
     201             :         }
     202       10331 :         if (compute_level)
     203             :         {
     204             :             // v{q} = k, the kth level of the BFS
     205       10331 :             GRB_TRY (GrB_assign (v, q, NULL, k, GrB_ALL, nsrc, GrB_ALL, n, GrB_DESC_S)) ;
     206             :         }
     207             :     }
     208             : 
     209             :     //--------------------------------------------------------------------------
     210             :     // free workspace and return result
     211             :     //--------------------------------------------------------------------------
     212             : 
     213         474 :     if (compute_parent) (*parent) = pi ;
     214         474 :     if (compute_level ) (*level ) = v ;
     215         474 :     LG_FREE_WORK ;
     216         474 :     return (GrB_SUCCESS) ;
     217             : #else
     218             :     return (GrB_NOT_IMPLEMENTED) ;
     219             : #endif
     220             : }

Generated by: LCOV version 1.14