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: 7b9d2ee. Current time (UTC): 2025-06-03T21:57:17Z Lines: 95 95 100.0 %
Date: 2025-06-03 22:02:40 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             : // global C arrays used in SelectOp
      38             : GrB_Index *I = NULL, *V = NULL, *F = NULL, *B = NULL, *M = NULL;
      39             : 
      40             : // edge_removal:
      41             : //  - remove the edges connected to newly identified SCCs (vertices u with M[u]==1)
      42             : //  - remove the edges (u, v) where u and v can never be in the same SCC.
      43             : //
      44             : // Here's a brief explanation of the second case. After the forward and backward
      45             : // propagation, each vertex u has two labels
      46             : //  - F[u]: the smallest vertex that can reach u
      47             : //  - B[u]: the smallest vertex that is reachable from u
      48             : // If two vertices u and v are in the same SCC, then F[u]==F[v] and B[u]==B[v] must
      49             : // hold. The converse is not true unless F[u]==B[u]. However, we can safely remove
      50             : // an edge (u, v) if either F[u]!=F[v] or B[u]!=B[v] holds, which can accelerate
      51             : // the SCC computation in the future rounds.
      52             : 
      53             : void edge_removal (bool *z, const void *x, GrB_Index i, GrB_Index j, const void *thunk) ;
      54     1515400 : void edge_removal (bool *z, const void *x, GrB_Index i, GrB_Index j, const void *thunk)
      55             : {
      56     1515400 :     (*z) = (!M[i] && !M[j] && F[i] == F[j] && B[i] == B[j]) ;
      57     1515400 : }
      58             : 
      59             : //****************************************************************************
      60             : // trim_one: remove the edges connected to trivial SCCs
      61             : //  - A vertex is a trivial SCC if it has no incoming or outgoing edges.
      62             : //  - M[i] = i   | if vertex i is a trivial SCC
      63             : //    M[i] = n   | otherwise
      64             : 
      65             : void trim_one (bool *z, const void *x, GrB_Index i, GrB_Index j, const void *thunk) ;
      66      127480 : void trim_one (bool *z, const void *x, GrB_Index i, GrB_Index j, const void *thunk)
      67             : {
      68      127480 :     (*z) = (M[i] == M[j]) ;
      69      127480 : }
      70             : 
      71             : //****************************************************************************
      72             : // label propagation
      73             : //  - label  : (input/output) labels
      74             : //  - mask   : (input) mask
      75             : //  - A      : (input) original matrix
      76             : //  - AT     : (input) transposed matrix
      77             : //  - n      : (input) number of vertices
      78             : 
      79             : #undef  LG_FREE_ALL
      80             : #define LG_FREE_ALL    \
      81             :     GrB_free (&s) ;    \
      82             :     GrB_free (&t) ;
      83             : 
      84         160 : static GrB_Info propagate (GrB_Vector label, GrB_Vector mask,
      85             :         GrB_Matrix A, GrB_Matrix AT, GrB_Index n, char *msg)
      86             : {
      87             :     GrB_Info info;
      88             :     // semirings
      89             : 
      90         160 :     GrB_Vector s = NULL, t = NULL;
      91         160 :     GRB_TRY (GrB_Vector_new (&s, GrB_UINT64, n));
      92         160 :     GRB_TRY (GrB_Vector_new (&t, GrB_UINT64, n));
      93         160 :     GRB_TRY (GrB_assign (s, mask, 0, label, GrB_ALL, 0, 0));
      94         160 :     GRB_TRY (GrB_assign (t, 0, 0, label, GrB_ALL, 0, 0));
      95             : 
      96             :     GrB_Index active;
      97             :     while (true)
      98             :     {
      99        7186 :         GRB_TRY (GrB_vxm (t, 0, GrB_MIN_UINT64,
     100             :                                  GrB_MIN_FIRST_SEMIRING_UINT64, s, A, 0));
     101        7186 :         GRB_TRY (GrB_eWiseMult (mask, 0, 0, GxB_ISNE_UINT64, t, label, 0));
     102        7186 :         GRB_TRY (GrB_assign (label, mask, 0, t, GrB_ALL, 0, 0));
     103        7186 :         GRB_TRY (GrB_reduce (&active, 0, GrB_PLUS_MONOID_UINT64, mask, 0));
     104        7186 :         if (active == 0) break;
     105        7026 :         GRB_TRY (GrB_Vector_clear (s));
     106        7026 :         GRB_TRY (GrB_assign (s, mask, 0, label, GrB_ALL, 0, 0));
     107             :     }
     108             : 
     109         160 :     LG_FREE_ALL ;
     110         160 :     return GrB_SUCCESS;
     111             : }
     112             : 
     113             : //****************************************************************************
     114             : 
     115             : #undef  LG_FREE_ALL
     116             : #define LG_FREE_ALL                         \
     117             :     LAGraph_Free ((void **) &I, msg);       \
     118             :     LAGraph_Free ((void **) &V, msg);       \
     119             :     LAGraph_Free ((void **) &F, msg);       \
     120             :     LAGraph_Free ((void **) &B, msg);       \
     121             :     LAGraph_Free ((void **) &M, msg);       \
     122             :     GrB_free (&ind);                        \
     123             :     GrB_free (&inf);                        \
     124             :     GrB_free (&f);                          \
     125             :     GrB_free (&b);                          \
     126             :     GrB_free (&mask);                       \
     127             :     GrB_free (&FW);                         \
     128             :     GrB_free (&BW);                         \
     129             :     GrB_free (&sel1);                       \
     130             :     GrB_free (&sel2);                       \
     131             :     GrB_free (&scc);
     132             : 
     133             : #endif
     134             : 
     135             : //****************************************************************************
     136          53 : int LAGraph_scc
     137             : (
     138             :     GrB_Vector *result,     // output: array of component identifiers
     139             :     GrB_Matrix A,           // input matrix
     140             :     char *msg
     141             : )
     142             : {
     143             : #if LAGRAPH_SUITESPARSE
     144             : 
     145          53 :     LG_CLEAR_MSG ;
     146             : 
     147             :     GrB_Info info;
     148          53 :     GrB_Vector scc = NULL ;
     149          53 :     GrB_Vector ind = NULL ;
     150          53 :     GrB_Vector inf = NULL ;
     151          53 :     GrB_Vector f = NULL, b = NULL, mask = NULL ;
     152          53 :     GrB_IndexUnaryOp sel1 = NULL, sel2 = NULL ;
     153          53 :     GrB_Monoid Add = NULL ;
     154          53 :     GrB_Matrix FW = NULL, BW = NULL;
     155             : 
     156          53 :     if (result == NULL || A == NULL) return (GrB_NULL_POINTER) ;
     157             : 
     158             :     GrB_Index n, ncols, nvals;
     159          52 :     GRB_TRY (GrB_Matrix_nrows (&n, A));
     160          52 :     GRB_TRY (GrB_Matrix_ncols (&ncols, A));
     161          52 :     if (n != ncols) return (GrB_DIMENSION_MISMATCH) ;
     162             : 
     163             :     // store the graph in both directions (forward / backward)
     164          51 :     GRB_TRY (GrB_Matrix_new (&FW, GrB_BOOL, n, n));
     165          51 :     GRB_TRY (GrB_Matrix_new (&BW, GrB_BOOL, n, n));
     166          51 :     GRB_TRY (GrB_transpose (FW, 0, 0, A, GrB_DESC_T0)); // FW = A
     167          51 :     GRB_TRY (GrB_transpose (BW, 0, 0, A, 0));     // BW = A'
     168             : 
     169             :     // check format
     170             :     int32_t A_format, AT_format;
     171          51 :     GRB_TRY (GrB_get (FW, &A_format , GrB_STORAGE_ORIENTATION_HINT));
     172          51 :     GRB_TRY (GrB_get (BW, &AT_format, GrB_STORAGE_ORIENTATION_HINT));
     173             : 
     174          51 :     bool is_csr = (A_format == GrB_ROWMAJOR && AT_format == GrB_ROWMAJOR);
     175          51 :     if (!is_csr) return (GrB_INVALID_VALUE) ;
     176             : 
     177          51 :     LG_TRY (LAGraph_Malloc ((void **) &I, n, sizeof (GrB_Index), msg)) ;
     178          51 :     LG_TRY (LAGraph_Malloc ((void **) &V, n, sizeof (GrB_Index), msg)) ;
     179          51 :     LG_TRY (LAGraph_Malloc ((void **) &F, n, sizeof (GrB_Index), msg)) ;
     180          51 :     LG_TRY (LAGraph_Malloc ((void **) &B, n, sizeof (GrB_Index), msg)) ;
     181          51 :     LG_TRY (LAGraph_Malloc ((void **) &M, n, sizeof (GrB_Index), msg)) ;
     182             : 
     183       19605 :     for (GrB_Index i = 0; i < n; i++)
     184       19554 :         I[i] = V[i] = i;
     185             : 
     186             :     // scc: the SCC identifier for each vertex
     187             :     // scc[u] == n: not assigned yet
     188          51 :     GRB_TRY (GrB_Vector_new (&scc, GrB_UINT64, n));
     189             :     // vector of indices: ind[i] == i
     190          51 :     GRB_TRY (GrB_Vector_new (&ind, GrB_UINT64, n));
     191          51 :     GRB_TRY (GrB_Vector_build (ind, I, V, n, GrB_PLUS_UINT64));
     192             :     // vector of infinite value: inf[i] == n
     193          51 :     GRB_TRY (GrB_Vector_new (&inf, GrB_UINT64, n));
     194          51 :     GRB_TRY (GrB_assign (inf, 0, 0, n, GrB_ALL, 0, 0));
     195             :     // other vectors
     196          51 :     GRB_TRY (GrB_Vector_new (&f, GrB_UINT64, n));
     197          51 :     GRB_TRY (GrB_Vector_new (&b, GrB_UINT64, n));
     198          51 :     GRB_TRY (GrB_Vector_new (&mask, GrB_UINT64, n));
     199          51 :     GRB_TRY (GrB_IndexUnaryOp_new (&sel1, (void *) trim_one, GrB_BOOL, GrB_UINT64, GrB_UINT64));
     200          51 :     GRB_TRY (GrB_IndexUnaryOp_new (&sel2, (void *) edge_removal, GrB_BOOL, GrB_UINT64, GrB_UINT64));
     201             : 
     202             :     // remove trivial SCCs
     203          51 :     GRB_TRY (GrB_reduce (f, 0, GrB_PLUS_UINT64, GrB_PLUS_UINT64, FW, 0));
     204          51 :     GRB_TRY (GrB_reduce (b, 0, GrB_PLUS_UINT64, GrB_PLUS_UINT64, BW, 0));
     205          51 :     GRB_TRY (GrB_eWiseMult (mask, 0, GxB_LAND_UINT64, GxB_LAND_UINT64, f, b, 0));
     206          51 :     GRB_TRY (GrB_Vector_nvals (&nvals, mask));
     207             : 
     208          51 :     GRB_TRY (GrB_assign (scc, 0, 0, ind, GrB_ALL, 0, 0));
     209          51 :     GRB_TRY (GrB_assign (scc, mask, 0, n, GrB_ALL, 0, 0));
     210          51 :     GRB_TRY (GrB_Vector_clear (mask));
     211             : 
     212          51 :     if (nvals < n)
     213             :     {
     214           9 :         GRB_TRY (GrB_Vector_extractTuples (I, M, &n, scc));
     215           9 :         GRB_TRY (GrB_select (FW, 0, 0, sel1, FW, 0, 0));
     216           9 :         GRB_TRY (GrB_select (BW, 0, 0, sel1, BW, 0, 0));
     217             :     }
     218             : 
     219          51 :     GRB_TRY (GrB_Matrix_nvals (&nvals, FW));
     220         131 :     while (nvals > 0)
     221             :     {
     222          80 :         GRB_TRY (GrB_eWiseMult (mask, 0, 0, GxB_ISEQ_UINT64, scc, inf, 0));
     223          80 :         GRB_TRY (GrB_assign (f, 0, 0, ind, GrB_ALL, 0, 0));
     224          80 :         LG_TRY (propagate (f, mask, FW, BW, n, msg));
     225             : 
     226          80 :         GRB_TRY (GrB_eWiseMult (mask, 0, 0, GxB_ISEQ_UINT64, f, ind, 0));
     227          80 :         GRB_TRY (GrB_assign (b, 0, 0, inf, GrB_ALL, 0, 0));
     228          80 :         GRB_TRY (GrB_assign (b, mask, 0, ind, GrB_ALL, 0, 0));
     229          80 :         LG_TRY (propagate (b, mask, BW, FW, n, msg));
     230             : 
     231          80 :         GRB_TRY (GrB_eWiseMult (mask, 0, 0, GxB_ISEQ_UINT64, f, b, 0));
     232          80 :         GRB_TRY (GrB_assign (scc, mask, GrB_MIN_UINT64, f, GrB_ALL, 0, 0));
     233             : 
     234          80 :         GRB_TRY (GrB_Vector_extractTuples (I, F, &n, f));
     235          80 :         GRB_TRY (GrB_Vector_extractTuples (I, B, &n, b));
     236          80 :         GRB_TRY (GrB_Vector_extractTuples (I, M, &n, mask));
     237             : 
     238          80 :         GRB_TRY (GrB_select (FW, 0, 0, sel2, FW, 0, 0));
     239          80 :         GRB_TRY (GrB_select (BW, 0, 0, sel2, BW, 0, 0));
     240             : 
     241          80 :         GRB_TRY (GrB_Matrix_nvals (&nvals, FW));
     242             :     }
     243          51 :     GRB_TRY (GrB_eWiseMult (mask, 0, 0, GxB_ISEQ_UINT64, scc, inf, 0));
     244          51 :     GRB_TRY (GrB_assign (scc, mask, 0, ind, GrB_ALL, 0, 0));
     245             : 
     246          51 :     GRB_TRY (GrB_eWiseMult (mask, 0, 0, GxB_ISEQ_UINT64, scc, ind, 0));
     247          51 :     GRB_TRY (GrB_reduce (&nvals, 0, GrB_PLUS_MONOID_UINT64, mask, 0));
     248             : 
     249          51 :     *result = scc;
     250          51 :     scc = NULL;
     251             : 
     252          51 :     LG_FREE_ALL ;
     253          51 :     return (GrB_SUCCESS) ;
     254             : #else
     255             :     return (GrB_NOT_IMPLEMENTED) ;
     256             : #endif
     257             : }

Generated by: LCOV version 1.14