LCOV - code coverage report
Current view: top level - experimental/algorithm - LAGraph_scc.c (source / functions) Hit Total Coverage
Test: LAGraph code coverage report. Commit id: 50cd0c8. Current time (UTC): 2025-07-25T16:32:50Z Lines: 105 105 100.0 %
Date: 2025-07-25 16:38:41 Functions: 4 4 100.0 %

          Line data    Source code
       1             : //------------------------------------------------------------------------------
       2             : // LAGraph_scc.c
       3             : //------------------------------------------------------------------------------
       4             : 
       5             : // LAGraph, (c) 2019-2022 by The LAGraph Contributors, All Rights Reserved.
       6             : // SPDX-License-Identifier: BSD-2-Clause
       7             : //
       8             : // For additional details (including references to third party source code and
       9             : // other files) see the LICENSE file or contact permission@sei.cmu.edu. See
      10             : // Contributors.txt for a full list of contributors. Created, in part, with
      11             : // funding and support from the U.S. Government (see Acknowledgments.txt file).
      12             : // DM22-0790
      13             : 
      14             : // Contributed by Yongzhe Zhang (zyz915@gmail.com)
      15             : 
      16             : //------------------------------------------------------------------------------
      17             : 
      18             : // TODO: not ready for src; uses global variables
      19             : 
      20             : /**
      21             :  * Code is based on the Min-Label algorithm described in the following paper:
      22             :  * D. Yan, J. Cheng, K. Xin, Y. Lu, W. Ng, Y. Bu, "Pregel Algorithms for Graph
      23             :  * Connectivity Problems with Performance Guarantees"
      24             :  * Proc. VLDB Endow. 7, 14 (October 2014), 1821–1832.
      25             :  * DOI: https://doi.org/10.14778/2733085.2733089
      26             :  **/
      27             : 
      28             : #define LG_FREE_ALL ;
      29             : 
      30             : #include "LG_internal.h"
      31             : #include <LAGraph.h>
      32             : #include <LAGraphX.h>
      33             : 
      34             : #if LAGRAPH_SUITESPARSE
      35             : 
      36             : //****************************************************************************
      37             : //arrays used in SelectOp
      38             : typedef struct 
      39             : {
      40             :     uint64_t *F, *B;
      41             :     bool *M;
      42             : } LG_SCC_Context;
      43             : #define SCCCONTEXT \
      44             : "typedef struct \n"             \
      45             : "{\n"                           \
      46             : "    uint64_t *F, *B;\n"    \
      47             : "    bool *M;\n"                \
      48             : "} LG_SCC_Context;\n"               
      49             : 
      50             : 
      51             : // LG_SCC_edge_removal:
      52             : //  - remove the edges connected to newly identified SCCs (vertices u with M[u]==1)
      53             : //  - remove the edges (u, v) where u and v can never be in the same SCC.
      54             : //
      55             : // Here's a brief explanation of the second case. After the forward and backward
      56             : // propagation, each vertex u has two labels
      57             : //  - F[u]: the smallest vertex that can reach u
      58             : //  - B[u]: the smallest vertex that is reachable from u
      59             : // If two vertices u and v are in the same SCC, then F[u]==F[v] and B[u]==B[v] must
      60             : // hold. The converse is not true unless F[u]==B[u]. However, we can safely remove
      61             : // an edge (u, v) if either F[u]!=F[v] or B[u]!=B[v] holds, which can accelerate
      62             : // the SCC computation in the future rounds.
      63             : 
      64             : void LG_SCC_edge_removal (bool *z, const void *x, GrB_Index i, GrB_Index j, const LG_SCC_Context *thunk) ;
      65     1474596 : void LG_SCC_edge_removal (bool *z, const void *x, GrB_Index i, GrB_Index j, const LG_SCC_Context *thunk)
      66             : {
      67        1194 :     (*z) = (!thunk->M[i] && !thunk->M[j] 
      68         974 :         && thunk->F[i] == thunk->F[j] 
      69     1475790 :         && thunk->B[i] == thunk->B[j]) ;
      70     1474596 : }
      71             : #define EDGE_REMOVAL \
      72             : "void LG_SCC_edge_removal \n"                                                          \
      73             : "(bool *z, const void *x, GrB_Index i, GrB_Index j, const LG_SCC_Context *thunk)\n" \
      74             : "{\n"                                                                           \
      75             : "    (*z) = (!thunk->M[i] && !thunk->M[j] \n"                                   \
      76             : "        && thunk->F[i] == thunk->F[j] \n"                                      \
      77             : "        && thunk->B[i] == thunk->B[j]) ;\n"                                    \
      78             : "}\n"                                                                           
      79             : 
      80             : //****************************************************************************
      81             : // LG_SCC_trim_one: remove the edges connected to trivial SCCs
      82             : //  - A vertex is a trivial SCC if it has no incoming or outgoing edges.
      83             : //  - M[i] = i   | if vertex i is a trivial SCC
      84             : //    M[i] = n   | otherwise
      85             : 
      86             : void LG_SCC_trim_one (bool *z, const void *x, GrB_Index i, GrB_Index j, const LG_SCC_Context *thunk) ;
      87      127480 : void LG_SCC_trim_one (bool *z, const void *x, GrB_Index i, GrB_Index j, const LG_SCC_Context *thunk)
      88             : {
      89      127480 :     (*z) = (thunk->F[i] == thunk->F[j]) ;
      90      127480 : }
      91             : #define TRIM_ONE \
      92             : "void LG_SCC_trim_one\n"                                                               \
      93             : "(bool *z, const void *x, GrB_Index i, GrB_Index j, const LG_SCC_Context *thunk)\n" \
      94             : "{\n"                                                                           \
      95             : "    (*z) = (thunk->F[i] == thunk->F[j]) ;\n"                                   \
      96             : "}\n"
      97             : 
      98             : //****************************************************************************
      99             : // label propagation
     100             : //  - label  : (input/output) labels
     101             : //  - mask   : (input) mask
     102             : //  - A      : (input) original matrix
     103             : //  - AT     : (input) transposed matrix
     104             : //  - n      : (input) number of vertices
     105             : 
     106             : #undef  LG_FREE_ALL
     107             : #define LG_FREE_ALL    \
     108             : {                      \
     109             :     GrB_free (&s) ;    \
     110             :     GrB_free (&t) ;    \
     111             : }
     112             : 
     113         296 : static GrB_Info propagate (GrB_Vector label, GrB_Vector mask,
     114             :         const GrB_Matrix A, const GrB_Matrix AT, GrB_Index n, char *msg)
     115             : {
     116             :     GrB_Info info;
     117             :     // semirings
     118         296 :     GrB_Vector s = NULL, t = NULL;
     119         296 :     GRB_TRY (GrB_Vector_new (&s, GrB_UINT64, n));
     120         296 :     GRB_TRY (GrB_Vector_new (&t, GrB_UINT64, n));
     121         296 :     GRB_TRY (GrB_assign (s, mask, 0, label, GrB_ALL, 0, 0));
     122             :     // GxB_fprint(s, GxB_SHORT, stdout);
     123         296 :     GRB_TRY (GrB_assign (t, 0, 0, label, GrB_ALL, 0, 0));
     124         296 :     GRB_TRY (GrB_wait(A, GrB_MATERIALIZE));
     125             : 
     126             :     bool active;
     127             :     while (true)
     128             :     {
     129             :         // GRB_TRY (GrB_mxv 
     130             :         //     (t, 0, GrB_MIN_UINT64, GrB_MIN_SECOND_SEMIRING_UINT64, AT, s, 0));
     131       14184 :         GRB_TRY (GrB_vxm (t, 0, GrB_MIN_UINT64,
     132             :                                  GrB_MIN_FIRST_SEMIRING_UINT64, s, A, 0));
     133       14184 :         GRB_TRY (GrB_eWiseMult (mask, 0, 0, GrB_NE_UINT64, t, label, 0));
     134       14184 :         GRB_TRY (GrB_assign (label, NULL, NULL, t, GrB_ALL, n, NULL));
     135       14184 :         GRB_TRY (GrB_reduce (&active, 0, GrB_LOR_MONOID_BOOL, mask, 0));
     136       14184 :         if (!active) break;
     137       13888 :         GRB_TRY (GrB_Vector_clear(s));
     138       13888 :         GRB_TRY (GrB_assign (s, mask, 0, label, GrB_ALL, 0, 0));
     139       13888 :         GRB_TRY (GrB_wait(s, GrB_MATERIALIZE));
     140             :     }
     141             : 
     142         296 :     LG_FREE_ALL ;
     143         296 :     return GrB_SUCCESS;
     144             : }
     145             : //****************************************************************************
     146             : 
     147             : #undef  LG_FREE_ALL
     148             : #define LG_FREE_ALL                         \
     149             :     LAGraph_Free ((void **) &contx.F, msg); \
     150             :     LAGraph_Free ((void **) &contx.B, msg); \
     151             :     LAGraph_Free ((void **) &contx.M, msg); \
     152             :     GrB_free (&ind);                        \
     153             :     GrB_free (&inf);                        \
     154             :     GrB_free (&f);                          \
     155             :     GrB_free (&b);                          \
     156             :     GrB_free (&D);                          \
     157             :     GrB_free (&x);                          \
     158             :     GrB_free (&mask);                       \
     159             :     GrB_free (&m2);                         \
     160             :     GrB_free (&FW);                         \
     161             :     GrB_free (&BW);                         \
     162             :     GrB_free (&sel1);                       \
     163             :     GrB_free (&sel2);                       \
     164             :     GrB_free (&contx_type);                 \
     165             :     GrB_free (&scc);
     166             : 
     167             : #endif
     168             : 
     169             : //****************************************************************************
     170         104 : int LAGraph_scc
     171             : (
     172             :     GrB_Vector *result,     // output: array of component identifiers
     173             :     GrB_Matrix A,           // input matrix
     174             :     char *msg
     175             : )
     176             : {
     177             : #if LAGRAPH_SUITESPARSE
     178             : 
     179         104 :     LG_CLEAR_MSG ;
     180         104 :     LG_SCC_Context contx = {NULL, NULL, NULL};
     181         104 :     GrB_Info info = GrB_SUCCESS;
     182         104 :     GrB_Type contx_type = NULL;
     183         104 :     GrB_Type type_F = NULL, type_B = NULL, type_M = NULL;
     184         104 :     int hand_F = GrB_DEFAULT, hand_B = GrB_DEFAULT, hand_M = GrB_DEFAULT;
     185         104 :     uint64_t n_F = 0, n_B = 0, n_M = 0, size_F = 0, size_B = 0, size_M = 0;
     186         104 :     GrB_Vector scc = NULL ;
     187         104 :     GrB_Vector ind = NULL ;
     188         104 :     GrB_Vector inf = NULL ;
     189         104 :     GrB_Vector x = NULL ;
     190         104 :     GrB_Vector f = NULL, b = NULL, mask = NULL, m2 = NULL;
     191         104 :     GrB_IndexUnaryOp sel1 = NULL, sel2 = NULL ;
     192         104 :     GrB_Monoid Add = NULL ;
     193         104 :     GrB_Matrix FW = NULL, BW = NULL, D = NULL;
     194         104 :     LG_ASSERT(result != NULL, GrB_NULL_POINTER);
     195         103 :     LG_ASSERT(A != NULL, GrB_NULL_POINTER);
     196             : 
     197             :     GrB_Index n, ncols, nvals;
     198         103 :     GRB_TRY (GrB_Matrix_nrows (&n, A));
     199         103 :     GRB_TRY (GrB_Matrix_ncols (&ncols, A));
     200         103 :     LG_ASSERT(n == ncols, GrB_DIMENSION_MISMATCH);
     201             :     
     202             :     #if !LG_SUITESPARSE_GRAPHBLAS_V10
     203             :     LG_TRY (LAGraph_Malloc ((void **) &contx.F, n, sizeof (uint64_t), msg)) ;
     204             :     LG_TRY (LAGraph_Malloc ((void **) &contx.B, n, sizeof (uint64_t), msg)) ;
     205             :     LG_TRY (LAGraph_Malloc ((void **) &contx.M, n, sizeof (bool), msg)) ;
     206             :     #endif
     207             :     // scc: the SCC identifier for each vertex
     208             :     // scc[u] == n: not assigned yet
     209         102 :     GRB_TRY (GrB_Vector_new (&scc, GrB_UINT64, n));
     210             :     // vector of indices: ind[i] == i
     211         102 :     GRB_TRY (GrB_Vector_new (&ind, GrB_UINT64, n));
     212         102 :     GRB_TRY (GrB_Vector_assign_UINT64 (
     213             :         ind, NULL, NULL, (uint64_t) 0, GrB_ALL, n, NULL)) ;
     214         102 :     GRB_TRY (GrB_Vector_apply_IndexOp_UINT64 (
     215             :         ind, NULL, NULL, GrB_ROWINDEX_INT64, ind, 0, NULL)) ;
     216             :     // vector of infinite value: inf[i] == n
     217         102 :     GRB_TRY (GrB_Vector_new (&inf, GrB_UINT64, n));
     218         102 :     GRB_TRY (GrB_assign (inf, NULL, NULL, n, GrB_ALL, 0, NULL));
     219             :     // other vectors
     220         102 :     GRB_TRY (GrB_Vector_new (&f, GrB_UINT64, n));
     221         102 :     GRB_TRY (GrB_Vector_new (&b, GrB_UINT64, n));
     222         102 :     GRB_TRY (GrB_Vector_new (&mask, GrB_BOOL, n));
     223         102 :     GRB_TRY (GrB_Vector_new (&m2, GrB_BOOL, n));
     224         102 :     GRB_TRY (GrB_Vector_new (&x, GrB_BOOL, n));
     225         102 :     GRB_TRY (GxB_Type_new (
     226             :         &contx_type, sizeof(LG_SCC_Context), "LG_SCC_Context", SCCCONTEXT)) ;
     227             : 
     228         102 :     GRB_TRY (GxB_IndexUnaryOp_new (
     229             :         &sel1, (GxB_index_unary_function) LG_SCC_trim_one, 
     230             :         GrB_BOOL, GrB_UINT64, contx_type, 
     231             :         // NULL, NULL
     232             :         "LG_SCC_trim_one", TRIM_ONE
     233             :     ));
     234         102 :     GRB_TRY (GxB_IndexUnaryOp_new (
     235             :         &sel2, (GxB_index_unary_function) LG_SCC_edge_removal, 
     236             :         GrB_BOOL, GrB_UINT64, contx_type,
     237             :         // NULL, NULL
     238             :         "LG_SCC_edge_removal", EDGE_REMOVAL
     239             :     ));
     240             : 
     241             :     // store the graph in both directions (forward / backward)
     242         102 :     GRB_TRY (GrB_Matrix_new (&FW, GrB_BOOL, n, n));
     243         102 :     GRB_TRY (GrB_Matrix_new (&BW, GrB_BOOL, n, n));
     244         102 :     GRB_TRY (GrB_Matrix_assign_BOOL(
     245             :         FW, A, NULL, true, GrB_ALL, n, GrB_ALL, n, GrB_DESC_S)) ;
     246         102 :     GRB_TRY (GrB_transpose (BW, NULL, NULL, FW, NULL));     // BW = FW'
     247             :        
     248             :     // check format
     249             :     int32_t A_format, AT_format;
     250         102 :     GRB_TRY (GrB_get (FW, &A_format , GrB_STORAGE_ORIENTATION_HINT));
     251         102 :     GRB_TRY (GrB_get (BW, &AT_format, GrB_STORAGE_ORIENTATION_HINT));
     252             : 
     253         102 :     bool is_csr = (A_format == GrB_ROWMAJOR && AT_format == GrB_ROWMAJOR);
     254         102 :     LG_ASSERT (is_csr, GrB_INVALID_VALUE) ;
     255             : 
     256             :     // remove trivial SCCs
     257         102 :     GRB_TRY (GrB_Vector_assign_BOOL (x, NULL, NULL, 0, GrB_ALL, n, NULL)) ;
     258         102 :     GRB_TRY (GrB_mxv (m2, NULL, NULL, GxB_ANY_PAIR_BOOL, FW, x, NULL)) ;
     259         102 :     GRB_TRY (GrB_mxv (mask, m2, NULL, GxB_ANY_PAIR_BOOL, BW, x, GrB_DESC_S)) ;
     260         102 :     GRB_TRY (GrB_Vector_nvals (&nvals, mask));
     261             : 
     262         102 :     GRB_TRY (GrB_assign (scc, NULL, NULL, ind, GrB_ALL, 0, NULL));
     263         102 :     GRB_TRY (GrB_assign (scc, mask, NULL, n, GrB_ALL, 0, NULL));
     264             : 
     265         102 :     if (nvals < n)
     266             :     {
     267             :         // No reason for context. 
     268             :         #if LG_SUITESPARSE_GRAPHBLAS_V10
     269          18 :         GRB_TRY(GxB_Vector_unload(
     270             :             scc, (void **) &contx.F, &type_F, &n_F, &size_F, &hand_F, NULL)) ;
     271             :         #else
     272             :         GRB_TRY (GrB_Vector_extractTuples_UINT64 (NULL, contx.F, &n, scc));
     273             :         #endif
     274          18 :         GRB_TRY (GrB_Matrix_select_UDT (
     275             :             FW, NULL, NULL, sel1, FW, &contx, NULL)) ;
     276          18 :         GRB_TRY (GrB_Matrix_select_UDT (
     277             :             BW, NULL, NULL, sel1, BW, &contx, NULL)) ;
     278             :         #if LG_SUITESPARSE_GRAPHBLAS_V10
     279          18 :         GRB_TRY(GxB_Vector_load(
     280             :             scc, (void **) &contx.F, type_F, n_F, size_F, hand_F, NULL)) ;
     281             :         #endif
     282             :     }
     283             : 
     284         102 :     GRB_TRY (GrB_Matrix_nvals (&nvals, FW));
     285         250 :     while (nvals > 0)
     286             :     {
     287         148 :         GRB_TRY (GrB_Vector_apply_BinaryOp2nd_UINT64 (
     288             :             mask, NULL, NULL, GrB_EQ_UINT64, scc, n, NULL));
     289         148 :         GRB_TRY (GrB_assign (f, NULL, NULL, ind, GrB_ALL, 0, NULL));
     290         148 :         LG_TRY (propagate (f, mask, FW, BW, n, msg));
     291             : 
     292         148 :         GRB_TRY (GrB_eWiseMult (mask, NULL, NULL, GrB_EQ_UINT64, f, ind, NULL));
     293         148 :         GRB_TRY (GrB_Vector_assign_UINT64 (
     294             :             b, NULL, NULL, n, GrB_ALL, 0, NULL)) ;
     295         148 :         GRB_TRY (GrB_assign (b, mask, NULL, ind, GrB_ALL, 0, NULL));
     296         148 :         LG_TRY (propagate (b, mask, BW, FW, n, msg));
     297             : 
     298         148 :         GRB_TRY (GrB_eWiseMult (mask, NULL, NULL, GrB_EQ_UINT64, f, b, NULL));
     299         148 :         GRB_TRY (GrB_assign (scc, mask, GrB_MIN_UINT64, f, GrB_ALL, 0, NULL));
     300             : 
     301             :         #if LG_SUITESPARSE_GRAPHBLAS_V10
     302         148 :             GRB_TRY(GxB_Vector_unload(
     303             :                 f, (void **) &contx.F, &type_F, &n_F, &size_F, &hand_F, NULL)) ;
     304         148 :             GRB_TRY(GxB_Vector_unload(
     305             :                 b, (void **) &contx.B, &type_B, &n_B, &size_B, &hand_B, NULL)) ;
     306         148 :             GRB_TRY(GxB_Vector_unload(
     307             :                 mask, (void **) &contx.M, &type_M, &n_M, &size_M, &hand_M, NULL
     308             :             )) ;
     309             :         #else
     310             :             GRB_TRY (GrB_Vector_extractTuples_UINT64 (NULL, contx.F, &n, f));
     311             :             GRB_TRY (GrB_Vector_extractTuples_UINT64 (NULL, contx.B, &n, b));
     312             :             GRB_TRY (GrB_Vector_extractTuples_BOOL (NULL, contx.M, &n, mask));
     313             :         #endif
     314             : 
     315         148 :         GRB_TRY (GrB_Matrix_select_UDT (
     316             :             FW, NULL, NULL, sel2, FW, &contx, NULL)) ;
     317         148 :         GRB_TRY (GrB_Matrix_select_UDT (
     318             :             BW, NULL, NULL, sel2, BW, &contx, NULL)) ;
     319             :         #if LG_SUITESPARSE_GRAPHBLAS_V10
     320         148 :             GRB_TRY(GxB_Vector_load(
     321             :                 f, (void **) &contx.F, type_F, n_F, size_F, hand_F, NULL)) ;
     322         148 :             GRB_TRY(GxB_Vector_load(
     323             :                 b, (void **) &contx.B, type_B, n_B, size_B, hand_B, NULL)) ;
     324         148 :             GRB_TRY(GxB_Vector_load(
     325             :                 mask, (void **) &contx.M, type_M, n_M, size_M, hand_M, NULL
     326             :             )) ;
     327             :         #endif
     328         148 :         GRB_TRY (GrB_Matrix_nvals (&nvals, FW));
     329             :     }
     330         102 :     GRB_TRY (GrB_Vector_apply_BinaryOp2nd_UINT64 (
     331             :             mask, NULL, NULL, GrB_EQ_UINT64, scc, n, NULL));
     332         102 :     GRB_TRY (GrB_assign (scc, mask, NULL, ind, GrB_ALL, 0, NULL));
     333             : 
     334         102 :     *result = scc;
     335         102 :     scc = NULL;
     336             : 
     337         102 :     LG_FREE_ALL ;
     338         102 :     return (GrB_SUCCESS) ;
     339             : #else
     340             :     return (GrB_NOT_IMPLEMENTED) ;
     341             : #endif
     342             : }

Generated by: LCOV version 1.14