#define LG_FREE_ALL \
{ \
GrB_free (L) ; \
GrB_free (U) ; \
}
#include "LG_internal.h"
static int tricount_prep
(
GrB_Matrix *L, GrB_Matrix *U, GrB_Matrix A, char *msg
)
{
GrB_Index n ;
GRB_TRY (GrB_Matrix_nrows (&n, A)) ;
if (L != NULL)
{
GRB_TRY (GrB_Matrix_new (L, GrB_BOOL, n, n)) ;
GRB_TRY (GrB_select (*L, NULL, NULL, GrB_TRIL, A, (int64_t) (-1),
NULL)) ;
GRB_TRY (GrB_Matrix_wait (*L, GrB_MATERIALIZE)) ;
}
if (U != NULL)
{
GRB_TRY (GrB_Matrix_new (U, GrB_BOOL, n, n)) ;
GRB_TRY (GrB_select (*U, NULL, NULL, GrB_TRIU, A, (int64_t) 1, NULL)) ;
GRB_TRY (GrB_Matrix_wait (*U, GrB_MATERIALIZE)) ;
}
return (GrB_SUCCESS) ;
}
#undef LG_FREE_ALL
#define LG_FREE_ALL \
{ \
GrB_free (&C) ; \
GrB_free (&L) ; \
GrB_free (&T) ; \
GrB_free (&U) ; \
LAGraph_Free ((void **) &P, NULL) ; \
}
int LAGr_TriangleCount
(
uint64_t *ntriangles,
const LAGraph_Graph G,
LAGr_TriangleCount_Method *p_method,
LAGr_TriangleCount_Presort *p_presort,
char *msg
)
{
LG_CLEAR_MSG ;
GrB_Matrix C = NULL, L = NULL, U = NULL, T = NULL ;
int64_t *P = NULL ;
LAGr_TriangleCount_Method method ;
method = (p_method == NULL) ? LAGr_TriangleCount_AutoMethod : (*p_method) ;
LG_ASSERT_MSG (
method == LAGr_TriangleCount_AutoMethod || method == LAGr_TriangleCount_Burkhardt || method == LAGr_TriangleCount_Cohen || method == LAGr_TriangleCount_Sandia_LL || method == LAGr_TriangleCount_Sandia_UU || method == LAGr_TriangleCount_Sandia_LUT || method == LAGr_TriangleCount_Sandia_ULT, GrB_INVALID_VALUE, "method is invalid") ;
LAGr_TriangleCount_Presort presort ;
presort = (p_presort == NULL) ? LAGr_TriangleCount_AutoSort : (*p_presort) ;
LG_ASSERT_MSG (
presort == LAGr_TriangleCount_NoSort ||
presort == LAGr_TriangleCount_Ascending ||
presort == LAGr_TriangleCount_Descending ||
presort == LAGr_TriangleCount_AutoSort,
GrB_INVALID_VALUE, "presort is invalid") ;
LG_TRY (LAGraph_CheckGraph (G, msg)) ;
LG_ASSERT (ntriangles != NULL, GrB_NULL_POINTER) ;
LG_ASSERT (G->nself_edges == 0, LAGRAPH_NO_SELF_EDGES_ALLOWED) ;
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") ;
if (method == LAGr_TriangleCount_AutoMethod)
{
method = LAGr_TriangleCount_Sandia_LUT ;
}
bool method_can_use_presort =
method == LAGr_TriangleCount_Sandia_LL || method == LAGr_TriangleCount_Sandia_UU || method == LAGr_TriangleCount_Sandia_LUT || method == LAGr_TriangleCount_Sandia_ULT ;
GrB_Matrix A = G->A ;
GrB_Vector Degree = G->out_degree ;
bool auto_sort = (presort == LAGr_TriangleCount_AutoSort) ;
if (auto_sort && method_can_use_presort)
{
LG_ASSERT_MSG (Degree != NULL,
LAGRAPH_NOT_CACHED, "G->out_degree is required") ;
}
GrB_Index n ;
GRB_TRY (GrB_Matrix_nrows (&n, A)) ;
GRB_TRY (GrB_Matrix_new (&C, GrB_INT64, n, n)) ;
GrB_Semiring semiring = LAGraph_plus_one_int64 ;
GrB_Monoid monoid = GrB_PLUS_MONOID_INT64 ;
if (!method_can_use_presort)
{
presort = LAGr_TriangleCount_NoSort ;
}
else if (auto_sort)
{
presort = LAGr_TriangleCount_NoSort ;
if (method_can_use_presort)
{
#define NSAMPLES 2000
GrB_Index nvals ;
GRB_TRY (GrB_Matrix_nvals (&nvals, A)) ;
if (n > NSAMPLES && ((double) nvals / ((double) n)) >= 10)
{
double mean, median ;
LG_TRY (LAGr_SampleDegree (&mean, &median,
G, true, NSAMPLES, n, msg)) ;
if (mean > 3 * median)
{
switch (method)
{
case LAGr_TriangleCount_Sandia_LL:
presort = LAGr_TriangleCount_Ascending ;
break ;
case LAGr_TriangleCount_Sandia_UU:
presort = LAGr_TriangleCount_Descending ;
break ;
default:
case LAGr_TriangleCount_Sandia_LUT:
presort = LAGr_TriangleCount_Ascending ;
break ;
case LAGr_TriangleCount_Sandia_ULT:
presort = LAGr_TriangleCount_Descending ;
break ;
}
}
}
}
}
if (presort != LAGr_TriangleCount_NoSort)
{
LG_TRY (LAGr_SortByDegree (&P, G, true,
presort == LAGr_TriangleCount_Ascending, msg)) ;
GRB_TRY (GrB_Matrix_new (&T, GrB_BOOL, n, n)) ;
GRB_TRY (GrB_extract (T, NULL, NULL, A, (GrB_Index *) P, n,
(GrB_Index *) P, n, NULL)) ;
A = T ;
LG_TRY (LAGraph_Free ((void **) &P, NULL)) ;
}
int64_t ntri ;
switch (method)
{
case LAGr_TriangleCount_Burkhardt:
GRB_TRY (GrB_mxm (C, A, NULL, semiring, A, A, GrB_DESC_S)) ;
GRB_TRY (GrB_reduce (&ntri, NULL, monoid, C, NULL)) ;
ntri /= 6 ;
break ;
case LAGr_TriangleCount_Cohen:
LG_TRY (tricount_prep (&L, &U, A, msg)) ;
GRB_TRY (GrB_mxm (C, A, NULL, semiring, L, U, GrB_DESC_S)) ;
GRB_TRY (GrB_reduce (&ntri, NULL, monoid, C, NULL)) ;
ntri /= 2 ;
break ;
case LAGr_TriangleCount_Sandia_LL:
LG_TRY (tricount_prep (&L, NULL, A, msg)) ;
GRB_TRY (GrB_mxm (C, L, NULL, semiring, L, L, GrB_DESC_S)) ;
GRB_TRY (GrB_reduce (&ntri, NULL, monoid, C, NULL)) ;
break ;
case LAGr_TriangleCount_Sandia_UU:
LG_TRY (tricount_prep (NULL, &U, A, msg)) ;
GRB_TRY (GrB_mxm (C, U, NULL, semiring, U, U, GrB_DESC_S)) ;
GRB_TRY (GrB_reduce (&ntri, NULL, monoid, C, NULL)) ;
break ;
default:
case LAGr_TriangleCount_Sandia_LUT:
LG_TRY (tricount_prep (&L, &U, A, msg)) ;
GRB_TRY (GrB_mxm (C, L, NULL, semiring, L, U, GrB_DESC_ST1)) ;
GRB_TRY (GrB_reduce (&ntri, NULL, monoid, C, NULL)) ;
break ;
case LAGr_TriangleCount_Sandia_ULT:
LG_TRY (tricount_prep (&L, &U, A, msg)) ;
GRB_TRY (GrB_mxm (C, U, NULL, semiring, U, L, GrB_DESC_ST1)) ;
GRB_TRY (GrB_reduce (&ntri, NULL, monoid, C, NULL)) ;
break ;
}
LG_FREE_ALL ;
if (p_method != NULL) (*p_method) = method ;
if (p_presort != NULL) (*p_presort) = presort ;
(*ntriangles) = (uint64_t) ntri ;
return (GrB_SUCCESS) ;
}