#define LG_FREE_WORK \
{ \
GrB_free (&w) ; \
GrB_free (&q) ; \
}
#define LG_FREE_ALL \
{ \
LG_FREE_WORK ; \
GrB_free (&pi) ; \
GrB_free (&v) ; \
}
#include "LG_alg_internal.h"
#ifdef LG_BFS_EXTENDED
int LG_BreadthFirstSearch_SSGrB_Extended
(
GrB_Vector *level,
GrB_Vector *parent,
const LAGraph_Graph G,
GrB_Index src,
int64_t max_level, int64_t dest, bool many_expected, char *msg
)
#else
int LG_BreadthFirstSearch_SSGrB
(
GrB_Vector *level,
GrB_Vector *parent,
const LAGraph_Graph G,
GrB_Index src,
char *msg
)
#endif
{
#if LAGRAPH_SUITESPARSE
LG_CLEAR_MSG ;
GrB_Vector q = NULL ; GrB_Vector w = NULL ; GrB_Vector pi = NULL ; GrB_Vector v = NULL ;
bool compute_level = (level != NULL) ;
bool compute_parent = (parent != NULL) ;
if (compute_level ) (*level ) = NULL ;
if (compute_parent) (*parent) = NULL ;
LG_ASSERT_MSG (compute_level || compute_parent, GrB_NULL_POINTER,
"either level or parent must be non-NULL") ;
LG_TRY (LAGraph_CheckGraph (G, msg)) ;
GrB_Matrix A = G->A ;
GrB_Index n, nvals ;
GRB_TRY (GrB_Matrix_nrows (&n, A)) ;
LG_ASSERT_MSG (src < n, GrB_INVALID_INDEX, "invalid source node") ;
GRB_TRY (GrB_Matrix_nvals (&nvals, A)) ;
GrB_Matrix AT = NULL ;
GrB_Vector Degree = G->out_degree ;
if (G->kind == LAGraph_ADJACENCY_UNDIRECTED ||
(G->kind == LAGraph_ADJACENCY_DIRECTED &&
G->is_symmetric_structure == LAGraph_TRUE))
{
AT = G->A ;
}
else
{
AT = G->AT ;
}
bool push_pull = (Degree != NULL && AT != NULL) ;
GrB_Type int_type = (n > INT32_MAX) ? GrB_INT64 : GrB_INT32 ;
GrB_Semiring semiring ;
#ifndef LG_BFS_EXTENDED
bool many_expected = (nvals >= n) ;
#endif
if (compute_parent)
{
semiring = (n > INT32_MAX) ?
GxB_ANY_SECONDI_INT64 : GxB_ANY_SECONDI_INT32 ;
GRB_TRY (GrB_Vector_new (&pi, int_type, n)) ;
if (many_expected)
{
GRB_TRY (LG_SET_FORMAT_HINT (pi, LG_BITMAP + LG_FULL)) ;
}
GRB_TRY (GrB_Vector_setElement (pi, src, src)) ;
GRB_TRY (GrB_Vector_new (&q, int_type, n)) ;
GRB_TRY (GrB_Vector_setElement (q, src, src)) ;
}
else
{
semiring = LAGraph_any_one_bool ;
GRB_TRY (GrB_Vector_new (&q, GrB_BOOL, n)) ;
GRB_TRY (GrB_Vector_setElement (q, true, src)) ;
}
if (compute_level)
{
GRB_TRY (GrB_Vector_new (&v, int_type, n)) ;
if (many_expected)
{
GRB_TRY (LG_SET_FORMAT_HINT (v, LG_BITMAP + LG_FULL)) ;
}
GRB_TRY (GrB_Vector_setElement (v, 0, src)) ;
}
GRB_TRY (GrB_Vector_new (&w, GrB_INT64, n)) ;
GrB_Index nq = 1 ; double alpha = 8.0 ;
double beta1 = 8.0 ;
double beta2 = 512.0 ;
int64_t n_over_beta1 = (int64_t) (((double) n) / beta1) ;
int64_t n_over_beta2 = (int64_t) (((double) n) / beta2) ;
bool do_push = true ; GrB_Index last_nq = 0 ;
int64_t edges_unexplored = nvals ;
bool any_pull = false ;
GrB_Vector mask = (compute_parent) ? pi : v ;
for (int64_t nvisited = 1, k = 1 ; nvisited < n ; nvisited += nq, k++)
{
#ifdef LG_BFS_EXTENDED
{
if (max_level >= 0 && k > max_level)
{
break ;
}
if (dest >= 0)
{
GrB_Info info = GxB_Vector_isStoredElement (mask, dest) ;
if (info == GrB_SUCCESS)
{
break ;
}
GRB_TRY (info) ;
}
}
#endif
if (push_pull)
{
if (do_push)
{
bool growing = nq > last_nq ;
bool switch_to_pull = false ;
if (edges_unexplored < n)
{
push_pull = false ;
}
else if (any_pull)
{
switch_to_pull = (growing && nq > n_over_beta1) ;
}
else
{
GRB_TRY (GrB_assign (w, q, NULL, Degree, GrB_ALL, n,
GrB_DESC_RS)) ;
int64_t edges_in_frontier = 0 ;
GRB_TRY (GrB_reduce (&edges_in_frontier, NULL,
GrB_PLUS_MONOID_INT64, w, NULL)) ;
edges_unexplored -= edges_in_frontier ;
switch_to_pull = growing &&
(edges_in_frontier > (edges_unexplored / alpha)) ;
}
if (switch_to_pull)
{
do_push = false ;
}
}
else
{
bool shrinking = nq < last_nq ;
if (shrinking && (nq <= n_over_beta2))
{
do_push = true ;
}
}
any_pull = any_pull || (!do_push) ;
}
if (do_push)
{
GRB_TRY (LG_SET_FORMAT_HINT (q, LG_SPARSE)) ;
GRB_TRY (GrB_vxm (q, mask, NULL, semiring, q, A, GrB_DESC_RSC)) ;
}
else
{
GRB_TRY (LG_SET_FORMAT_HINT (q, LG_BITMAP)) ;
GRB_TRY (GrB_mxv (q, mask, NULL, semiring, AT, q, GrB_DESC_RSC)) ;
}
last_nq = nq ;
GRB_TRY (GrB_Vector_nvals (&nq, q)) ;
if (nq == 0)
{
break ;
}
if (compute_parent)
{
GRB_TRY (GrB_assign (pi, q, NULL, q, GrB_ALL, n, GrB_DESC_S)) ;
}
if (compute_level)
{
GRB_TRY (GrB_assign (v, q, NULL, k, GrB_ALL, n, GrB_DESC_S)) ;
}
}
if (compute_parent) (*parent) = pi ;
if (compute_level ) (*level ) = v ;
LG_FREE_WORK ;
return (GrB_SUCCESS) ;
#else
return (GrB_NOT_IMPLEMENTED) ;
#endif
}