#define LG_FREE_ALL ;
#include "LG_internal.h"
#include <LAGraph.h>
#include <LAGraphX.h>
#if LAGRAPH_SUITESPARSE
GrB_Index *I = NULL, *V = NULL, *F = NULL, *B = NULL, *M = NULL;
void edge_removal (bool *z, const void *x, GrB_Index i, GrB_Index j, const void *thunk) ;
void edge_removal (bool *z, const void *x, GrB_Index i, GrB_Index j, const void *thunk)
{
(*z) = (!M[i] && !M[j] && F[i] == F[j] && B[i] == B[j]) ;
}
void trim_one (bool *z, const void *x, GrB_Index i, GrB_Index j, const void *thunk) ;
void trim_one (bool *z, const void *x, GrB_Index i, GrB_Index j, const void *thunk)
{
(*z) = (M[i] == M[j]) ;
}
#undef LG_FREE_ALL
#define LG_FREE_ALL \
GrB_free (&s) ; \
GrB_free (&t) ;
static GrB_Info propagate (GrB_Vector label, GrB_Vector mask,
GrB_Matrix A, GrB_Matrix AT, GrB_Index n, char *msg)
{
GrB_Info info;
GrB_Vector s = NULL, t = NULL;
GRB_TRY (GrB_Vector_new (&s, GrB_UINT64, n));
GRB_TRY (GrB_Vector_new (&t, GrB_UINT64, n));
GRB_TRY (GrB_assign (s, mask, 0, label, GrB_ALL, 0, 0));
GRB_TRY (GrB_assign (t, 0, 0, label, GrB_ALL, 0, 0));
GrB_Index active;
while (true)
{
GRB_TRY (GrB_vxm (t, 0, GrB_MIN_UINT64,
GrB_MIN_FIRST_SEMIRING_UINT64, s, A, 0));
GRB_TRY (GrB_eWiseMult (mask, 0, 0, GxB_ISNE_UINT64, t, label, 0));
GRB_TRY (GrB_assign (label, mask, 0, t, GrB_ALL, 0, 0));
GRB_TRY (GrB_reduce (&active, 0, GrB_PLUS_MONOID_UINT64, mask, 0));
if (active == 0) break;
GRB_TRY (GrB_Vector_clear (s));
GRB_TRY (GrB_assign (s, mask, 0, label, GrB_ALL, 0, 0));
}
LG_FREE_ALL ;
return GrB_SUCCESS;
}
#undef LG_FREE_ALL
#define LG_FREE_ALL \
LAGraph_Free ((void **) &I, msg); \
LAGraph_Free ((void **) &V, msg); \
LAGraph_Free ((void **) &F, msg); \
LAGraph_Free ((void **) &B, msg); \
LAGraph_Free ((void **) &M, msg); \
GrB_free (&ind); \
GrB_free (&inf); \
GrB_free (&f); \
GrB_free (&b); \
GrB_free (&mask); \
GrB_free (&FW); \
GrB_free (&BW); \
GrB_free (&sel1); \
GrB_free (&sel2); \
GrB_free (&scc);
#endif
int LAGraph_scc
(
GrB_Vector *result, GrB_Matrix A, char *msg
)
{
#if LAGRAPH_SUITESPARSE
LG_CLEAR_MSG ;
GrB_Info info;
GrB_Vector scc = NULL ;
GrB_Vector ind = NULL ;
GrB_Vector inf = NULL ;
GrB_Vector f = NULL, b = NULL, mask = NULL ;
GrB_IndexUnaryOp sel1 = NULL, sel2 = NULL ;
GrB_Monoid Add = NULL ;
GrB_Matrix FW = NULL, BW = NULL;
if (result == NULL || A == NULL) return (GrB_NULL_POINTER) ;
GrB_Index n, ncols, nvals;
GRB_TRY (GrB_Matrix_nrows (&n, A));
GRB_TRY (GrB_Matrix_ncols (&ncols, A));
if (n != ncols) return (GrB_DIMENSION_MISMATCH) ;
GRB_TRY (GrB_Matrix_new (&FW, GrB_BOOL, n, n));
GRB_TRY (GrB_Matrix_new (&BW, GrB_BOOL, n, n));
GRB_TRY (GrB_transpose (FW, 0, 0, A, GrB_DESC_T0)); GRB_TRY (GrB_transpose (BW, 0, 0, A, 0));
int32_t A_format, AT_format;
GRB_TRY (GrB_get (FW, &A_format , GrB_STORAGE_ORIENTATION_HINT));
GRB_TRY (GrB_get (BW, &AT_format, GrB_STORAGE_ORIENTATION_HINT));
bool is_csr = (A_format == GrB_ROWMAJOR && AT_format == GrB_ROWMAJOR);
if (!is_csr) return (GrB_INVALID_VALUE) ;
LG_TRY (LAGraph_Malloc ((void **) &I, n, sizeof (GrB_Index), msg)) ;
LG_TRY (LAGraph_Malloc ((void **) &V, n, sizeof (GrB_Index), msg)) ;
LG_TRY (LAGraph_Malloc ((void **) &F, n, sizeof (GrB_Index), msg)) ;
LG_TRY (LAGraph_Malloc ((void **) &B, n, sizeof (GrB_Index), msg)) ;
LG_TRY (LAGraph_Malloc ((void **) &M, n, sizeof (GrB_Index), msg)) ;
for (GrB_Index i = 0; i < n; i++)
I[i] = V[i] = i;
GRB_TRY (GrB_Vector_new (&scc, GrB_UINT64, n));
GRB_TRY (GrB_Vector_new (&ind, GrB_UINT64, n));
GRB_TRY (GrB_Vector_build (ind, I, V, n, GrB_PLUS_UINT64));
GRB_TRY (GrB_Vector_new (&inf, GrB_UINT64, n));
GRB_TRY (GrB_assign (inf, 0, 0, n, GrB_ALL, 0, 0));
GRB_TRY (GrB_Vector_new (&f, GrB_UINT64, n));
GRB_TRY (GrB_Vector_new (&b, GrB_UINT64, n));
GRB_TRY (GrB_Vector_new (&mask, GrB_UINT64, n));
GRB_TRY (GrB_IndexUnaryOp_new (&sel1, (void *) trim_one, GrB_BOOL, GrB_UINT64, GrB_UINT64));
GRB_TRY (GrB_IndexUnaryOp_new (&sel2, (void *) edge_removal, GrB_BOOL, GrB_UINT64, GrB_UINT64));
GRB_TRY (GrB_reduce (f, 0, GrB_PLUS_UINT64, GrB_PLUS_UINT64, FW, 0));
GRB_TRY (GrB_reduce (b, 0, GrB_PLUS_UINT64, GrB_PLUS_UINT64, BW, 0));
GRB_TRY (GrB_eWiseMult (mask, 0, GxB_LAND_UINT64, GxB_LAND_UINT64, f, b, 0));
GRB_TRY (GrB_Vector_nvals (&nvals, mask));
GRB_TRY (GrB_assign (scc, 0, 0, ind, GrB_ALL, 0, 0));
GRB_TRY (GrB_assign (scc, mask, 0, n, GrB_ALL, 0, 0));
GRB_TRY (GrB_Vector_clear (mask));
if (nvals < n)
{
GRB_TRY (GrB_Vector_extractTuples (I, M, &n, scc));
GRB_TRY (GrB_select (FW, 0, 0, sel1, FW, 0, 0));
GRB_TRY (GrB_select (BW, 0, 0, sel1, BW, 0, 0));
}
GRB_TRY (GrB_Matrix_nvals (&nvals, FW));
while (nvals > 0)
{
GRB_TRY (GrB_eWiseMult (mask, 0, 0, GxB_ISEQ_UINT64, scc, inf, 0));
GRB_TRY (GrB_assign (f, 0, 0, ind, GrB_ALL, 0, 0));
LG_TRY (propagate (f, mask, FW, BW, n, msg));
GRB_TRY (GrB_eWiseMult (mask, 0, 0, GxB_ISEQ_UINT64, f, ind, 0));
GRB_TRY (GrB_assign (b, 0, 0, inf, GrB_ALL, 0, 0));
GRB_TRY (GrB_assign (b, mask, 0, ind, GrB_ALL, 0, 0));
LG_TRY (propagate (b, mask, BW, FW, n, msg));
GRB_TRY (GrB_eWiseMult (mask, 0, 0, GxB_ISEQ_UINT64, f, b, 0));
GRB_TRY (GrB_assign (scc, mask, GrB_MIN_UINT64, f, GrB_ALL, 0, 0));
GRB_TRY (GrB_Vector_extractTuples (I, F, &n, f));
GRB_TRY (GrB_Vector_extractTuples (I, B, &n, b));
GRB_TRY (GrB_Vector_extractTuples (I, M, &n, mask));
GRB_TRY (GrB_select (FW, 0, 0, sel2, FW, 0, 0));
GRB_TRY (GrB_select (BW, 0, 0, sel2, BW, 0, 0));
GRB_TRY (GrB_Matrix_nvals (&nvals, FW));
}
GRB_TRY (GrB_eWiseMult (mask, 0, 0, GxB_ISEQ_UINT64, scc, inf, 0));
GRB_TRY (GrB_assign (scc, mask, 0, ind, GrB_ALL, 0, 0));
GRB_TRY (GrB_eWiseMult (mask, 0, 0, GxB_ISEQ_UINT64, scc, ind, 0));
GRB_TRY (GrB_reduce (&nvals, 0, GrB_PLUS_MONOID_UINT64, mask, 0));
*result = scc;
scc = NULL;
LG_FREE_ALL ;
return (GrB_SUCCESS) ;
#else
return (GrB_NOT_IMPLEMENTED) ;
#endif
}