#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
#define LG_FREE_ALL ;
#include "LG_internal.h"
#if LAGRAPH_SUITESPARSE
static inline GrB_Info fastsv
(
GrB_Matrix A, GrB_Vector parent, GrB_Vector mngp, GrB_Vector *gp, GrB_Vector *gp_new, GrB_Vector t, GrB_BinaryOp eq, GrB_BinaryOp min, GrB_Semiring min_2nd, GrB_Matrix C, GrB_Index **Cp, GrB_Index **Px, void **Cx, char *msg
)
{
GrB_Index n ;
GRB_TRY (GrB_Vector_size (&n, parent)) ;
GrB_Index Cp_size = (n+1) * sizeof (GrB_Index) ;
GrB_Index Ci_size = n * sizeof (GrB_Index) ;
GrB_Index Cx_size = sizeof (bool) ;
bool iso = true, jumbled = false, done = false ;
while (true)
{
GRB_TRY (GrB_mxv (mngp, NULL, min, min_2nd, A, *gp, NULL)) ;
GRB_TRY (GxB_Matrix_pack_CSC (C, Cp, Px, Cx,
Cp_size, Ci_size, Cx_size, iso, jumbled, NULL)) ;
GRB_TRY (GrB_mxv (parent, NULL, min, min_2nd, C, mngp, NULL)) ;
GRB_TRY (GxB_Matrix_unpack_CSC (C, Cp, Px, Cx,
&Cp_size, &Ci_size, &Cx_size, &iso, &jumbled, NULL)) ;
GRB_TRY (GrB_eWiseAdd (parent, NULL, min, min, mngp, *gp, NULL)) ;
GRB_TRY (GrB_Vector_extractTuples (NULL, *Px, &n, parent)) ;
GRB_TRY (GrB_extract (*gp_new, NULL, NULL, parent, *Px, n, NULL)) ;
GRB_TRY (GrB_eWiseMult (t, NULL, NULL, eq, *gp_new, *gp, NULL)) ;
GRB_TRY (GrB_reduce (&done, NULL, GrB_LAND_MONOID_BOOL, t, NULL)) ;
if (done) break ;
GrB_Vector s = (*gp) ; (*gp) = (*gp_new) ; (*gp_new) = s ;
}
return (GrB_SUCCESS) ;
}
#undef LG_FREE_WORK
#define LG_FREE_WORK \
{ \
LAGraph_Free ((void **) &Tp, NULL) ; \
LAGraph_Free ((void **) &Tj, NULL) ; \
LAGraph_Free ((void **) &Tx, NULL) ; \
LAGraph_Free ((void **) &Cp, NULL) ; \
LAGraph_Free ((void **) &Px, NULL) ; \
LAGraph_Free ((void **) &Cx, NULL) ; \
LAGraph_Free ((void **) &ht_key, NULL) ; \
LAGraph_Free ((void **) &ht_count, NULL) ; \
LAGraph_Free ((void **) &count, NULL) ; \
LAGraph_Free ((void **) &range, NULL) ; \
GrB_free (&C) ; \
GrB_free (&T) ; \
GrB_free (&t) ; \
GrB_free (&y) ; \
GrB_free (&gp) ; \
GrB_free (&mngp) ; \
GrB_free (&gp_new) ; \
}
#undef LG_FREE_ALL
#define LG_FREE_ALL \
{ \
LG_FREE_WORK ; \
GrB_free (&parent) ; \
}
#endif
#ifdef TIMINGS
static void print_timings (double timings [16])
{
double total = timings [0] + timings [1] + timings [2] ;
printf ("SV6 %12.6f (%4.1f%%) init\n", timings [0], 100. * timings [0] / total) ;
printf ("SV6 %12.6f (%4.1f%%) total sampling:\n", timings [1], 100. * timings [1] / total) ;
printf ("SV6 %12.6f (%4.1f%%) setup T\n", timings [3], 100. * timings [3] / total) ;
printf ("SV6 %12.6f (%4.1f%%) create T\n", timings [4], 100. * timings [4] / total) ;
printf ("SV6 %12.6f (%4.1f%%) fastsv sample\n", timings [5], 100 * timings [5] / total) ;
printf ("SV6 %12.6f (%4.1f%%) hash\n", timings [6], 100. * timings [6] / total) ;
printf ("SV6 %12.6f (%4.1f%%) prune\n", timings [7], 100. * timings [7] / total) ;
printf ("SV6 %12.6f (%4.1f%%) total final\n", timings [2], 100. * timings [2] / total) ;
}
#endif
int LG_CC_FastSV6 (
GrB_Vector *component, LAGraph_Graph G, char *msg
)
{
#if LAGRAPH_SUITESPARSE
LG_CLEAR_MSG ;
#ifdef TIMINGS
double timings [16] ;
for (int kk = 0 ; kk < 16 ; kk++) timings [kk] = 0 ;
double tic = LAGraph_WallClockTime ( ) ;
LG_SET_BURBLE (false) ;
#endif
int64_t *range = NULL ;
GrB_Index n, nvals, Cp_size = 0, *ht_key = NULL, *Px = NULL, *Cp = NULL,
*count = NULL, *Tp = NULL, *Tj = NULL ;
GrB_Vector parent = NULL, gp_new = NULL, mngp = NULL, gp = NULL, t = NULL,
y = NULL ;
GrB_Matrix T = NULL, C = NULL ;
void *Tx = NULL, *Cx = NULL ;
int *ht_count = NULL ;
LG_TRY (LAGraph_CheckGraph (G, msg)) ;
LG_ASSERT (component != NULL, GrB_NULL_POINTER) ;
(*component) = NULL ;
LG_ASSERT_MSG ((G->kind == LAGraph_ADJACENCY_UNDIRECTED ||
(G->kind == LAGraph_ADJACENCY_DIRECTED &&
G->is_symmetric_structure == LAGraph_TRUE)),
LAGRAPH_SYMMETRIC_STRUCTURE_REQUIRED,
"G->A must be known to be symmetric") ;
GrB_Matrix A = G->A ;
GRB_TRY (GrB_Matrix_nrows (&n, A)) ;
GRB_TRY (GrB_Matrix_nvals (&nvals, A)) ;
GrB_Type Uint, Int ;
GrB_IndexUnaryOp ramp ;
GrB_Semiring min_2nd, min_2ndi ;
GrB_BinaryOp min, eq, imin ;
#ifdef COVERAGE
#define NBIG 100
#else
#define NBIG INT32_MAX
#endif
if (n > NBIG)
{
Uint = GrB_UINT64 ;
Int = GrB_INT64 ;
ramp = GrB_ROWINDEX_INT64 ;
min = GrB_MIN_UINT64 ;
imin = GrB_MIN_INT64 ;
eq = GrB_EQ_UINT64 ;
min_2nd = GrB_MIN_SECOND_SEMIRING_UINT64 ;
min_2ndi = GxB_MIN_SECONDI_INT64 ;
}
else
{
Uint = GrB_UINT32 ;
Int = GrB_INT32 ;
ramp = GrB_ROWINDEX_INT32 ;
min = GrB_MIN_UINT32 ;
imin = GrB_MIN_INT32 ;
eq = GrB_EQ_UINT32 ;
min_2nd = GrB_MIN_SECOND_SEMIRING_UINT32 ;
min_2ndi = GxB_MIN_SECONDI_INT32 ;
}
#define FASTSV_SAMPLES 4
bool sampling = (nvals > n * FASTSV_SAMPLES * 2 && n > 1024) ;
int nthreads, nthreads_outer, nthreads_inner ;
LG_TRY (LAGraph_GetNumThreads (&nthreads_outer, &nthreads_inner, msg)) ;
nthreads = nthreads_outer * nthreads_inner ;
nthreads = LAGRAPH_MIN (nthreads, n / 16) ;
nthreads = LAGRAPH_MAX (nthreads, 1) ;
LG_TRY (LAGraph_Calloc ((void **) &Cx, 1, sizeof (bool), msg)) ;
LG_TRY (LAGraph_Malloc ((void **) &Px, n, sizeof (GrB_Index), msg)) ;
GRB_TRY (GrB_Matrix_new (&C, GrB_BOOL, n, n)) ;
GRB_TRY (GrB_Vector_new (&t, GrB_INT64, n+1)) ;
GRB_TRY (GrB_assign (t, NULL, NULL, 0, GrB_ALL, n+1, NULL)) ;
GRB_TRY (GrB_apply (t, NULL, NULL, GrB_ROWINDEX_INT64, t, 0, NULL)) ;
GRB_TRY (GxB_Vector_unpack_Full (t, (void **) &Cp, &Cp_size, NULL, NULL)) ;
GRB_TRY (GrB_free (&t)) ;
GRB_TRY (GrB_Vector_new (&t, Int, n)) ;
GRB_TRY (GrB_Vector_new (&y, Int, n)) ;
GRB_TRY (GrB_assign (t, NULL, NULL, 0, GrB_ALL, n, NULL)) ;
GRB_TRY (GrB_assign (y, NULL, NULL, 0, GrB_ALL, n, NULL)) ;
GRB_TRY (GrB_apply (y, NULL, NULL, ramp, y, 0, NULL)) ;
GRB_TRY (GrB_mxv (y, NULL, imin, min_2ndi, A, t, NULL)) ;
GRB_TRY (GrB_free (&t)) ;
GRB_TRY (GrB_Vector_new (&parent, Uint, n)) ;
GRB_TRY (GrB_assign (parent, NULL, NULL, y, GrB_ALL, n, NULL)) ;
GRB_TRY (GrB_free (&y)) ;
GRB_TRY (GrB_Vector_extractTuples (NULL, Px, &n, parent)) ;
GRB_TRY (GrB_Vector_dup (&gp, parent)) ;
GRB_TRY (GrB_Vector_dup (&mngp, parent)) ;
GRB_TRY (GrB_Vector_new (&gp_new, Uint, n)) ;
GRB_TRY (GrB_Vector_new (&t, GrB_BOOL, n)) ;
#ifdef TIMINGS
double toc = LAGraph_WallClockTime ( ) ;
timings [0] = toc - tic ; tic = toc ;
#endif
if (sampling)
{
#ifdef TIMINGS
double tic2 = LAGraph_WallClockTime ( ) ;
#endif
void *Ax ;
GrB_Index *Ap, *Aj, Ap_size, Aj_size, Ax_size ;
bool A_jumbled, A_iso ;
GRB_TRY (GxB_Matrix_unpack_CSR (A, &Ap, &Aj, &Ax,
&Ap_size, &Aj_size, &Ax_size, &A_iso, &A_jumbled, NULL)) ;
GrB_Index Tp_size = (n+1) * sizeof (GrB_Index) ;
GrB_Index Tj_size = nvals * sizeof (GrB_Index) ;
GrB_Index Tx_size = sizeof (bool) ;
LG_TRY (LAGraph_Malloc ((void **) &Tp, n+1, sizeof (GrB_Index), msg)) ;
LG_TRY (LAGraph_Malloc ((void **) &Tj, nvals, sizeof (GrB_Index),
msg)) ;
LG_TRY (LAGraph_Calloc ((void **) &Tx, 1, sizeof (bool), msg)) ;
LG_TRY (LAGraph_Malloc ((void **) &range, nthreads + 1,
sizeof (int64_t), msg)) ;
LG_TRY (LAGraph_Calloc ((void **) &count, nthreads + 1,
sizeof (GrB_Index), msg)) ;
int tid;
for (tid = 0 ; tid <= nthreads ; tid++)
{
range [tid] = (n * tid + nthreads - 1) / nthreads ;
}
#pragma omp parallel for num_threads(nthreads) schedule(static)
for (tid = 0 ; tid < nthreads ; tid++)
{
for (int64_t i = range [tid] ; i < range [tid+1] ; i++)
{
int64_t deg = Ap [i + 1] - Ap [i] ;
count [tid + 1] += LAGRAPH_MIN (FASTSV_SAMPLES, deg) ;
}
}
for (tid = 0 ; tid < nthreads ; tid++)
{
count [tid + 1] += count [tid] ;
}
#ifdef TIMINGS
double toc2 = LAGraph_WallClockTime ( ) ;
timings [3] = toc2 - tic2 ; tic2 = toc2 ;
#endif
#pragma omp parallel for num_threads(nthreads) schedule(static)
for (tid = 0 ; tid < nthreads ; tid++)
{
GrB_Index p = count [tid] ;
Tp [range [tid]] = p ;
for (int64_t i = range [tid] ; i < range [tid+1] ; i++)
{
for (int64_t j = 0 ;
j < FASTSV_SAMPLES && Ap [i] + j < Ap [i + 1] ; j++)
{
Tj [p++] = Aj [Ap [i] + j] ;
}
Tp [i + 1] = p ;
}
}
GRB_TRY (GrB_Matrix_new (&T, GrB_BOOL, n, n)) ;
GRB_TRY (GxB_Matrix_pack_CSR (T, &Tp, &Tj, &Tx, Tp_size, Tj_size,
Tx_size, true, A_jumbled, NULL)) ;
#ifdef TIMINGS
toc2 = LAGraph_WallClockTime ( ) ;
timings [4] = toc2 - tic2 ; tic2 = toc2 ;
#endif
LG_TRY (fastsv (T, parent, mngp, &gp, &gp_new, t, eq, min, min_2nd,
C, &Cp, &Px, &Cx, msg)) ;
#ifdef TIMINGS
toc2 = LAGraph_WallClockTime ( ) ;
timings [5] = toc2 - tic2 ; tic2 = toc2 ;
#endif
#define HASH_SIZE 1024
#define HASH_SAMPLES 864
#define HASH(x) (((x << 4) + x) & (HASH_SIZE-1))
#define NEXT(x) ((x + 23) & (HASH_SIZE-1))
LG_TRY (LAGraph_Malloc ((void **) &ht_key, HASH_SIZE,
sizeof (GrB_Index), msg)) ;
LG_TRY (LAGraph_Calloc ((void **) &ht_count, HASH_SIZE,
sizeof (int), msg)) ;
for (int k = 0 ; k < HASH_SIZE ; k++)
{
ht_key [k] = UINT64_MAX ;
}
uint64_t seed = n ; int64_t key = -1 ; int max_count = 0 ; for (int64_t k = 0 ; k < HASH_SAMPLES ; k++)
{
GrB_Index x = Px [LG_Random64 (&seed) % n] ;
GrB_Index h = HASH (x) ;
while (ht_key [h] != UINT64_MAX && ht_key [h] != x) h = NEXT (h) ;
ht_key [h] = x ;
ht_count [h]++ ;
if (ht_count [h] > max_count)
{
key = ht_key [h] ;
max_count = ht_count [h] ;
}
}
#ifdef TIMINGS
toc2 = LAGraph_WallClockTime ( ) ;
timings [6] = toc2 - tic2 ; tic2 = toc2 ;
#endif
bool T_jumbled, T_iso ;
GRB_TRY (GxB_Matrix_unpack_CSR (T, &Tp, &Tj, &Tx, &Tp_size, &Tj_size,
&Tx_size, &T_iso, &T_jumbled, NULL)) ;
#pragma omp parallel for num_threads(nthreads) schedule(static)
for (tid = 0 ; tid < nthreads ; tid++)
{
GrB_Index p = Ap [range [tid]] ;
for (int64_t i = range [tid] ; i < range [tid+1] ; i++)
{
int64_t pi = Px [i] ; Tp [i] = p ; if (pi != key)
{
for (GrB_Index pS = Ap [i] ; pS < Ap [i+1] ; pS++)
{
int64_t j = Aj [pS] ;
if (Px [j] != key)
{
Tj [p++] = j ;
}
}
if (p - Tp [i] < Ap [i+1] - Ap [i])
{
Tj [p++] = key ;
}
}
}
count [tid] = p - Tp [range [tid]] ;
}
nvals = 0 ;
for (tid = 0 ; tid < nthreads ; tid++)
{
memmove (Tj + nvals,
Tj + Tp [range [tid]], sizeof (GrB_Index) * count [tid]) ;
nvals += count [tid] ;
count [tid] = nvals - count [tid] ;
}
#pragma omp parallel for num_threads(nthreads) schedule(static)
for (tid = 0 ; tid < nthreads ; tid++)
{
GrB_Index p = Tp [range [tid]] ;
for (int64_t i = range [tid] ; i < range [tid+1] ; i++)
{
Tp [i] -= p - count [tid] ;
}
}
Tp [n] = nvals ;
GRB_TRY (GxB_Matrix_pack_CSR (T, &Tp, &Tj, &Tx, Tp_size, Tj_size,
Tx_size, T_iso, true, NULL)) ;
GRB_TRY (GxB_Matrix_pack_CSR (A, &Ap, &Aj, &Ax, Ap_size, Aj_size,
Ax_size, A_iso, A_jumbled, NULL)) ;
A = T ;
#ifdef TIMINGS
toc2 = LAGraph_WallClockTime ( ) ;
timings [7] = toc2 - tic2 ; tic2 = toc2 ;
#endif
}
#ifdef TIMINGS
toc = LAGraph_WallClockTime ( ) ;
timings [1] = toc - tic ; tic = toc ;
#endif
if (nvals == 0)
{
(*component) = parent ;
LG_FREE_WORK ;
#ifdef TIMINGS
print_timings (timings) ;
LG_SET_BURBLE (false) ;
#endif
return (GrB_SUCCESS) ;
}
LG_TRY (fastsv (A, parent, mngp, &gp, &gp_new, t, eq, min, min_2nd,
C, &Cp, &Px, &Cx, msg)) ;
(*component) = parent ;
LG_FREE_WORK ;
#ifdef TIMINGS
toc = LAGraph_WallClockTime ( ) ;
timings [2] = toc - tic ; print_timings (timings) ;
LG_SET_BURBLE (false) ;
#endif
return (GrB_SUCCESS) ;
#else
return (GrB_NOT_IMPLEMENTED) ;
#endif
}