diff --git a/experimental/algorithm/LAGr_EdgeBetweennessCentrality.c b/experimental/algorithm/LAGr_EdgeBetweennessCentrality.c index 9fea4ae129..551dc21401 100644 --- a/experimental/algorithm/LAGr_EdgeBetweennessCentrality.c +++ b/experimental/algorithm/LAGr_EdgeBetweennessCentrality.c @@ -19,7 +19,7 @@ // LAGr_EdgeBetweennessCentrality: Exact algorithm for computing // betweeness centrality. -// This is an Advanced algorithm (G->AT is required). +// This is an Advanced algorithm (no self edges allowed) //------------------------------------------------------------------------------ @@ -82,19 +82,19 @@ // (1+x)/y function for double: z = (1 + x) / y //------------------------------------------------------------------------------ -void add_one_divide_function (void *z, const void *x, const void *y) +void add_one_divide_function (double *z, const double *x, const double *y) { - double a = (*((double *) x)) ; - double b = (*((double *) y)) ; - (*((double *) z)) = (1 + a) / b ; + double a = (*(x)) ; + double b = (*(y)) ; + (*(z)) = (1 + a) / b ; } #define ADD_ONE_DIVIDE_FUNCTION_DEFN \ -"void add_one_divide_function (void *z, const void *x, const void *y) \n" \ +"void add_one_divide_function (double *z, const double *x, const double *y)\n" \ "{ \n" \ -" double a = (*((double *) x)) ; \n" \ -" double b = (*((double *) y)) ; \n" \ -" (*((double *) z)) = (1 + a) / b ; \n" \ +" double a = (*(x)) ; \n" \ +" double b = (*(y)) ; \n" \ +" (*(z)) = (1 + a) / b ; \n" \ "}" //------------------------------------------------------------------------------ @@ -154,6 +154,7 @@ int LAGr_EdgeBetweennessCentrality LG_TRY (LAGraph_CheckGraph (G, msg)) ; GrB_Matrix A = G->A ; + #if 0 GrB_Matrix AT ; if (G->kind == LAGraph_ADJACENCY_UNDIRECTED || G->is_symmetric_structure == LAGraph_TRUE) @@ -167,12 +168,14 @@ int LAGr_EdgeBetweennessCentrality AT = G->AT ; LG_ASSERT_MSG (AT != NULL, LAGRAPH_NOT_CACHED, "G->AT is required") ; } + #endif // ========================================================================= // === initialization ===================================================== // ========================================================================= - GRB_TRY (GxB_BinaryOp_new (&Add_One_Divide, add_one_divide_function, + GRB_TRY (GxB_BinaryOp_new (&Add_One_Divide, + (GxB_binary_function) add_one_divide_function, GrB_FP64, GrB_FP64, GrB_FP64, "add_one_divide_function", ADD_ONE_DIVIDE_FUNCTION_DEFN)) ; @@ -214,10 +217,12 @@ int LAGr_EdgeBetweennessCentrality GRB_TRY (GrB_Matrix_clear (Update)) ; - // Extract row root from A into frontier vector: frontier = AT(root,:) - GRB_TRY (GrB_Col_extract (frontier, NULL, NULL, AT, GrB_ALL, n, root, - NULL)) ; + // Extract row root from A into frontier vector: frontier = A(root,:) + GRB_TRY (GrB_Col_extract (frontier, NULL, NULL, A, GrB_ALL, n, root, + GrB_DESC_T0)) ; + GRB_TRY (GrB_Vector_nvals (&frontier_size, frontier)) ; + GRB_TRY (GrB_assign (frontier, frontier, NULL, 1.0, GrB_ALL, n, GrB_DESC_S)) ; while (frontier_size != 0) { @@ -242,7 +247,8 @@ int LAGr_EdgeBetweennessCentrality //---------------------------------------------------------------------- GRB_TRY (LG_SET_FORMAT_HINT (frontier, LG_SPARSE)) ; - GRB_TRY (GrB_vxm (frontier, paths, NULL, GxB_PLUS_FIRST_FP64, frontier, + GRB_TRY (GrB_vxm (frontier, paths, NULL, /* LAGraph_plus_first_fp64 */ + GxB_PLUS_FIRST_FP64, frontier, A, GrB_DESC_RSC )) ; //---------------------------------------------------------------------- @@ -269,8 +275,12 @@ int LAGr_EdgeBetweennessCentrality // Backtrack through the BFS and compute centrality updates for each vertex // GrB_Index fd1_size; + + // printf ("\n----------------------------- backtrack:\n") ; + while (depth >= 1) { + // printf ("\n----------------------------- backtrack depth : %" PRId64 "\n", depth) ; GrB_Vector f_d = Search [depth] ; GrB_Vector f_d1 = Search [depth - 1] ; @@ -291,20 +301,20 @@ int LAGr_EdgeBetweennessCentrality // combine // intermediate matrix for Fd1 * A - // GRB_TRY (GrB_eWiseMult(Fd1A, NULL, NULL, GrB_TIMES_FP64, I_matrix, AT, NULL)) ; - GRB_TRY(GrB_mxm(Fd1A, NULL, NULL, GrB_PLUS_TIMES_SEMIRING_FP64, + GRB_TRY(GrB_mxm(Fd1A, NULL, NULL, LAGraph_plus_first_fp64, I_matrix, A, NULL)) ; - // GRB_TRY (GrB_eWiseMult(U, NULL, NULL, GrB_TIMES_FP64, Fd1A, J_matrix, NULL)) ; GRB_TRY(GrB_mxm(Update, NULL, NULL, GrB_PLUS_TIMES_SEMIRING_FP64, Fd1A, J_matrix, NULL)) ; - - // 22 centrality = centrality + Update - // GRB_TRY (GrB_assign(centrality, centrality, GrB_PLUS_FP64, Update, GrB_ALL, n, GrB_ALL, n, - // GrB_DESC_S)) ; + #if 1 + // 22 centrality{A} += Update, using assign + GRB_TRY (GrB_assign(*centrality, A, GrB_PLUS_FP64, Update, GrB_ALL, n, GrB_ALL, n, + GrB_DESC_S)) ; + #else + // 22 centrality = centrality + Update using eWiseAdd GRB_TRY (GrB_eWiseAdd (*centrality, NULL, NULL, GrB_PLUS_FP64, *centrality, Update, NULL)) ; - + #endif // 23 v = Update +. diff --git a/experimental/benchmark/edgeBetweennessCentrality_demo.c b/experimental/benchmark/edgeBetweennessCentrality_demo.c new file mode 100644 index 0000000000..46dfae134c --- /dev/null +++ b/experimental/benchmark/edgeBetweennessCentrality_demo.c @@ -0,0 +1,124 @@ + +#define LG_FREE_ALL \ + printf ("done here: %d\n", __LINE__) ; \ + printf ("msg: [%s]\n", msg) ; \ + GrB_free (¢rality) ; \ + GrB_free (&A) ; \ + LAGraph_Delete (&G, msg) ; \ + +#include "LAGraphX.h" +#include "LG_internal.h" +#include + +double difference(GrB_Matrix bc, GrB_Matrix reference_bc) +{ + GrB_Matrix diff = NULL ; + + uint64_t n ; + GrB_Matrix_nrows (&n, bc) ; + + // Compute diff = max(abs(reference_bc - bc)) + GrB_Matrix_new(&diff, GrB_FP64, n, n) ; + GrB_eWiseAdd(diff, NULL, NULL, GrB_MINUS_FP64, reference_bc, bc, NULL) ; + GrB_apply(diff, NULL, NULL, GrB_ABS_FP64, diff, NULL) ; + + double err = 1 ; + GrB_reduce(&err, NULL, GrB_MAX_MONOID_FP64, diff, NULL) ; + + GrB_free(&diff) ; + + return err ; +} ; + +int main (int argc, char **argv) +{ + + //-------------------------------------------------------------------------- + // startup LAGraph and GraphBLAS + //-------------------------------------------------------------------------- + + char msg [LAGRAPH_MSG_LEN] ; // for error messages from LAGraph + LAGraph_Graph G = NULL ; + GrB_Matrix centrality = NULL, A = NULL ; + GrB_Info info ; + + // start GraphBLAS and LAGraph + LAGRAPH_TRY (LAGraph_Init (msg)) ; + + //-------------------------------------------------------------------------- + // read in the graph via a Matrix Market file from stdin + //-------------------------------------------------------------------------- + + if (argc < 2) + { + printf("Usage: %s \n", argv[0]); + return (GrB_INVALID_VALUE) ; + } + + FILE *f = fopen(argv[1], "r"); + if (f == NULL) + { + printf("Error: unable to open file %s\n", argv[1]); + return (GrB_INVALID_VALUE) ; + } + + double t = LAGraph_WallClockTime ( ) ; + LAGRAPH_TRY (LAGraph_MMRead (&A, f, msg)) ; + fclose(f); + uint64_t n ; + GRB_TRY (GrB_Matrix_nrows (&n, A)) ; + + LAGRAPH_TRY (LAGraph_New (&G, &A, LAGraph_ADJACENCY_DIRECTED, msg)) ; + LAGRAPH_TRY (LAGraph_DeleteSelfEdges (G, msg)) ; + LAGRAPH_TRY (LAGraph_Cached_AT (G, msg)) ; + t = LAGraph_WallClockTime ( ) - t ; + printf ("Time to read the graph: %g sec\n", t) ; + + printf ("\n==========================The input graph matrix G:\n") ; + LAGRAPH_TRY (LAGraph_Graph_Print (G, LAGraph_SHORT, stdout, msg)) ; + + //-------------------------------------------------------------------------- + // compute edge betweenness centrality + //-------------------------------------------------------------------------- + + // LG_SET_BURBLE (true) ; + + t = LAGraph_WallClockTime ( ) ; + LAGRAPH_TRY (LAGr_EdgeBetweennessCentrality (¢rality, G, msg)) ; + t = LAGraph_WallClockTime ( ) - t ; + printf ("Time for LAGr_EdgeBetweennessCentrality: %g sec\n", t) ; + + // LG_SET_BURBLE (false) ; + + //-------------------------------------------------------------------------- + // check the results using LG_check_edgeBetweennessCentrality + //-------------------------------------------------------------------------- + + GrB_Matrix reference_centrality = NULL; + t = LAGraph_WallClockTime ( ) ; + LAGRAPH_TRY (LG_check_edgeBetweennessCentrality(&reference_centrality, G, msg)) ; + t = LAGraph_WallClockTime ( ) - t ; + printf ("Time for LG_check_edgeBetweennessCentrality: %g sec\n", t) ; + + + double err = difference(centrality, reference_centrality) ; + printf ("Error between computed and reference centrality: %e\n", err) ; + if (err < 1e-4) + { + printf ("Test passed.\n") ; + } + else + { + printf ("Test failure!\n") ; + } + + //-------------------------------------------------------------------------- + // free everything and finish + //-------------------------------------------------------------------------- + + GrB_free (¢rality) ; + GrB_free (&reference_centrality) ; + LAGraph_Delete (&G, msg) ; + LAGRAPH_TRY (LAGraph_Finalize (msg)) ; + return (GrB_SUCCESS) ; +} diff --git a/experimental/test/test_edgeBetweennessCentrality.c b/experimental/test/test_edgeBetweennessCentrality.c index acec3cced8..ee85206599 100644 --- a/experimental/test/test_edgeBetweennessCentrality.c +++ b/experimental/test/test_edgeBetweennessCentrality.c @@ -31,40 +31,56 @@ LAGraph_Graph G = NULL ; // difference: compare the LAGraph and GAP results //------------------------------------------------------------------------------ -double difference(GrB_Matrix bc, double* gap_result, GrB_Index rows, GrB_Index cols) ; +double difference(GrB_Matrix bc, double* reference_bc, GrB_Index rows, GrB_Index cols) ; -double difference(GrB_Matrix bc, double* gap_result, GrB_Index rows, GrB_Index cols) +double difference(GrB_Matrix bc, double* reference_bc, GrB_Index rows, GrB_Index cols) { // GrB_Matrix diff = NULL; - GrB_Matrix diff = NULL, gap_bc = NULL; - OK(GrB_Matrix_new(&gap_bc, GrB_FP64, rows, cols)); + GrB_Matrix diff = NULL, reference_bc_matrix = NULL ; + OK(GrB_Matrix_new(&reference_bc_matrix, GrB_FP64, rows, cols)) ; // Populate gap_bc with values from gap_result for (GrB_Index i = 0; i < rows; i++) { for (GrB_Index j = 0; j < cols; j++) { - // if (*(gap_result + i * cols + j) != 0) printf(" (%ld, %ld) %g\n", i, j, *(gap_result + i * cols + j)); - OK(GrB_Matrix_setElement_FP64(gap_bc, *(gap_result + i * cols + j), i, j)); + OK(GrB_Matrix_setElement_FP64(reference_bc_matrix, *(reference_bc + i * cols + j), i, j)) ; } } - // GxB_print (bc, 5) ; - // GxB_print (gap_bc, 5) ; + // Compute diff = max(abs(reference_bc_matrix - bc)) + OK(GrB_Matrix_new(&diff, GrB_FP64, rows, cols)) ; + OK(GrB_eWiseAdd(diff, NULL, NULL, GrB_MINUS_FP64, reference_bc_matrix, bc, NULL)) ; + OK(GrB_apply(diff, NULL, NULL, GrB_ABS_FP64, diff, NULL)) ; - // Compute diff = max(abs(gap_bc - bc)) - OK(GrB_Matrix_new(&diff, GrB_FP64, rows, cols)); - OK(GrB_eWiseAdd(diff, NULL, NULL, GrB_MINUS_FP64, gap_bc, bc, NULL)); - // GxB_print (diff, 5) ; - OK(GrB_apply(diff, NULL, NULL, GrB_ABS_FP64, diff, NULL)); + double err = 0 ; + OK(GrB_reduce(&err, NULL, GrB_MAX_MONOID_FP64, diff, NULL)) ; - double err = 0; - OK(GrB_reduce(&err, NULL, GrB_MAX_MONOID_FP64, diff, NULL)); + OK(GrB_free(&diff)) ; + OK(GrB_free(&reference_bc_matrix)) ; - OK(GrB_free(&diff)); - OK(GrB_free(&gap_bc)); - - return err; + return err ; } +double matrix_difference(GrB_Matrix bc, GrB_Matrix reference_bc) ; + +double matrix_difference(GrB_Matrix bc, GrB_Matrix reference_bc) +{ + GrB_Matrix diff = NULL ; + + uint64_t n ; + GrB_Matrix_nrows (&n, bc) ; + + // Compute diff = max(abs(reference_bc - bc)) + GrB_Matrix_new(&diff, GrB_FP64, n, n) ; + GrB_eWiseAdd(diff, NULL, NULL, GrB_MINUS_FP64, reference_bc, bc, NULL) ; + GrB_apply(diff, NULL, NULL, GrB_ABS_FP64, diff, NULL) ; + + double err = 1 ; + GrB_reduce(&err, NULL, GrB_MAX_MONOID_FP64, diff, NULL) ; + + GrB_free(&diff) ; + + return err ; +} //------------------------------------------------------------------------------ // results for book graph @@ -162,14 +178,14 @@ void test_diamonds_ebc (void) // compute its betweenness centrality with C version OK (LG_check_edgeBetweennessCentrality (¢rality, G, msg)) ; double err = difference(centrality, &diamonds_ebc[0][0], 8, 8) ; - printf ("diamonds: err: %e (C version)\n", err) ; + printf ("\n diamonds: err: %e (C version)", err) ; TEST_CHECK (err < 1e-4) ; OK (GrB_free (¢rality)) ; // compute its betweenness centrality with GraphBLAS version OK (LAGr_EdgeBetweennessCentrality (¢rality, G, msg)) ; err = difference(centrality, &diamonds_ebc[0][0], 8, 8) ; - printf ("diamonds: err: %e (pure GraphBLAS)\n", err) ; + printf ("\n diamonds: err: %e (pure GraphBLAS)\n", err) ; TEST_CHECK (err < 1e-4) ; OK (GrB_free (¢rality)) ; @@ -200,14 +216,14 @@ void test_karate_ebc (void) // compute its betweenness centrality (C version) OK (LG_check_edgeBetweennessCentrality (¢rality, G, msg)) ; double err = difference(centrality, &karate_ebc[0][0], 34, 34) ; - printf ("karate: err: %e (C version)\n", err) ; + printf ("\n karate: err: %e (C version)", err) ; TEST_CHECK (err < 1e-4) ; OK (GrB_free (¢rality)) ; // compute its betweenness centrality (GraphBLAS version) OK (LAGr_EdgeBetweennessCentrality (¢rality, G, msg)) ; err = difference(centrality, &karate_ebc[0][0], 34, 34) ; - printf ("karate: err: %e (GraphBLAS version)\n", err) ; + printf ("\n karate: err: %e (GraphBLAS version)\n", err) ; TEST_CHECK (err < 1e-4) ; OK (GrB_free (¢rality)) ; @@ -216,15 +232,64 @@ void test_karate_ebc (void) } +// Function to test multiple matrix market files +void test_many(void) +{ + LAGraph_Init(msg); + + const char *files[] = { + "random_unweighted_bipartite1.mtx", + "random_unweighted_bipartite2.mtx", + "random_unweighted_general1.mtx", + "random_unweighted_general2.mtx", + "dnn_data/n1024-l1.mtx", + NULL + }; + + for (int i = 0; files[i] != NULL; i++) + { + GrB_Matrix A = NULL; + GrB_Matrix centrality = NULL; + GrB_Matrix reference_centrality = NULL; + + snprintf(filename, LEN, LG_DATA_DIR "%s", files[i]); + FILE *f = fopen(filename, "r"); + TEST_CHECK(f != NULL); + OK(LAGraph_MMRead(&A, f, msg)); + OK(fclose(f)); + OK(LAGraph_New(&G, &A, LAGraph_ADJACENCY_DIRECTED, msg)); + OK(LAGraph_DeleteSelfEdges (G, msg)) ; + OK(LAGraph_Cached_AT (G, msg)) ; + TEST_CHECK(A == NULL); // A has been moved into G->A + + // compute its betweenness centrality (GraphBLAS version) + OK(LAGr_EdgeBetweennessCentrality(¢rality, G, msg)); + + // compute its betweenness centrality (C version) + OK(LG_check_edgeBetweennessCentrality(&reference_centrality, G, msg)); + + // Compare the results + double err = matrix_difference(centrality, reference_centrality); + printf("\n %s: err: %e", files[i], err); + TEST_CHECK(err < 1e-4); + + OK(GrB_free(¢rality)); + OK(GrB_free(&reference_centrality)); + OK(LAGraph_Delete(&G, msg)); + } + printf("\n") ; + + LAGraph_Finalize(msg); +} + //------------------------------------------------------------------------------ // list of tests //------------------------------------------------------------------------------ -// FIXME: add more matrices TEST_LIST = { {"test_diamonds_ebc", test_diamonds_ebc}, {"test_karate_ebc", test_karate_ebc}, -// {"test_many", test_many}, FIXME ADD THIS + {"test_many", test_many}, {NULL, NULL} };