LCOV - code coverage report
Current view: top level - experimental/algorithm - LAGraph_BF_full_mxv.c (source / functions) Hit Total Coverage
Test: LAGraph code coverage report. Commit id: cc56ed4. Current time (UTC): 2024-08-30T17:14:30Z Lines: 94 94 100.0 %
Date: 2024-08-30 17:16:41 Functions: 4 4 100.0 %

          Line data    Source code
       1             : //------------------------------------------------------------------------------
       2             : // LAGraph_BF_full.c: Bellman-Ford single-source shortest paths, returns tree
       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 Jinhao Chen and Timothy A. Davis, Texas A&M University
      15             : 
      16             : //------------------------------------------------------------------------------
      17             : 
      18             : // LAGraph_BF_full_mxv: Bellman-Ford single source shortest paths, returning
      19             : // both the path lengths and the shortest-path tree.
      20             : 
      21             : // LAGraph_BF_full_mxv performs a Bellman-Ford to find out shortest path
      22             : // length, parent nodes along the path and the hops (number of edges) in the
      23             : // path from given source vertex s in the range of [0, n) on graph with n nodes.
      24             : // It works almost the same as LAGraph_BF_full except that it performs update
      25             : // using GrB_mxv instead of GrB_vxm, therefore, it require the input matrix as
      26             : // the transpose of adjacency matrix A with size n by n. That is, the input
      27             : // sparse matrix has entry AT(i, j) if there is edge from vertex j to vertex i
      28             : // with weight w, then AT(i, j) = w. While same as LAGraph_BF_full, it requires
      29             : // AT(i, i) = 0 for all 0 <= i < n.
      30             : 
      31             : // LAGraph_BF_full_mxv returns GrB_SUCCESS if it succeeds.  In this case, there
      32             : // are no negative-weight cycles in the graph, and d, pi, and h are returned.
      33             : // The vector d has d(k) as the shortest distance from s to k. pi(k) = p+1,
      34             : // where p is the parent node of k-th node in the shortest path. In particular,
      35             : // pi(s) = 0. h(k) = hop(s, k), the number of edges from s to k in the shortest
      36             : // path.
      37             : 
      38             : // If the graph has a negative-weight cycle, GrB_NO_VALUE is returned, and the
      39             : // GrB_Vectors d(k), pi(k) and h(k)  (i.e., *pd_output, *ppi_output and
      40             : // *ph_output respectively) will be NULL when negative-weight cycle detected.
      41             : 
      42             : // Otherwise, other errors such as GrB_OUT_OF_MEMORY, GrB_INVALID_OBJECT, and
      43             : // so on, can be returned, if these errors are found by the underlying
      44             : // GrB_* functions.
      45             : 
      46             : 
      47             : //------------------------------------------------------------------------------
      48             : 
      49             : #define LG_FREE_WORK                   \
      50             : {                                      \
      51             :     GrB_free(&d);                      \
      52             :     GrB_free(&dtmp);                   \
      53             :     GrB_free(&Atmp);                   \
      54             :     GrB_free(&BF_Tuple3);              \
      55             :     GrB_free(&BF_lMIN_Tuple3);         \
      56             :     GrB_free(&BF_PLUSrhs_Tuple3);      \
      57             :     GrB_free(&BF_EQ_Tuple3);           \
      58             :     GrB_free(&BF_lMIN_Tuple3_Monoid);  \
      59             :     GrB_free(&BF_lMIN_PLUSrhs_Tuple3); \
      60             :     LAGraph_Free ((void**)&I, NULL);   \
      61             :     LAGraph_Free ((void**)&J, NULL);   \
      62             :     LAGraph_Free ((void**)&w, NULL);   \
      63             :     LAGraph_Free ((void**)&W, NULL);   \
      64             :     LAGraph_Free ((void**)&h, NULL);   \
      65             :     LAGraph_Free ((void**)&pi, NULL);  \
      66             : }
      67             : 
      68             : #define LG_FREE_ALL                    \
      69             : {                                      \
      70             :     LG_FREE_WORK ;                     \
      71             :     GrB_free (pd_output);              \
      72             :     GrB_free (ppi_output);             \
      73             :     GrB_free (ph_output);              \
      74             : }
      75             : 
      76             : #include <LAGraph.h>
      77             : #include <LAGraphX.h>
      78             : #include <LG_internal.h>  // from src/utility
      79             : 
      80             : typedef void (*LAGraph_binary_function) (void *, const void *, const void *) ;
      81             : 
      82             : //------------------------------------------------------------------------------
      83             : // data type for each entry of the adjacent matrix A and "distance" vector d;
      84             : // <INFINITY,INFINITY,INFINITY> corresponds to nonexistence of a path, and
      85             : // the value  <0, 0, NULL> corresponds to a path from a vertex to itself
      86             : //------------------------------------------------------------------------------
      87             : 
      88             : typedef struct
      89             : {
      90             :     double w;    // w  corresponds to a path weight.
      91             :     GrB_Index h; // h  corresponds to a path size or number of hops.
      92             :     GrB_Index pi;// pi corresponds to the penultimate vertex along a path.
      93             :                  // vertex indexed as 1, 2, 3, ... , V, and pi = 0 (as nil)
      94             :                  // for u=v, and pi = UINT64_MAX (as inf) for (u,v) not in E
      95             : }
      96             : BF_Tuple3_struct;
      97             : 
      98             : //------------------------------------------------------------------------------
      99             : // binary functions, z=f(x,y), where Tuple3xTuple3 -> Tuple3
     100             : //------------------------------------------------------------------------------
     101             : 
     102       20297 : void BF_lMIN_mxv
     103             : (
     104             :     BF_Tuple3_struct *z,
     105             :     const BF_Tuple3_struct *y,
     106             :     const BF_Tuple3_struct *x
     107             : )
     108             : {
     109       20297 :     if (x->w < y->w
     110       15239 :         || (x->w == y->w && x->h < y->h)
     111       15239 :         || (x->w == y->w && x->h == y->h && x->pi < y->pi))
     112             :     {
     113        5058 :         if (z != x) { *z = *x; }
     114             :     }
     115             :     else
     116             :     {
     117       15239 :         *z = *y;
     118             :     }
     119       20297 : }
     120             : 
     121       25194 : void BF_PLUSrhs_mxv
     122             : (
     123             :     BF_Tuple3_struct *z,
     124             :     const BF_Tuple3_struct *y,
     125             :     const BF_Tuple3_struct *x
     126             : )
     127             : {
     128       25194 :     z->w = x->w + y->w;
     129       25194 :     z->h = x->h + y->h;
     130       25194 :     z->pi = (x->pi != UINT64_MAX && y->pi != 0) ?  y->pi : x->pi ;
     131       25194 : }
     132             : 
     133        4424 : void BF_EQ_mxv
     134             : (
     135             :     bool *z,
     136             :     const BF_Tuple3_struct *y,
     137             :     const BF_Tuple3_struct *x
     138             : )
     139             : {
     140        4424 :     (*z) = (x->w == y->w && x->h == y->h && x->pi == y->pi) ;
     141        4424 : }
     142             : 
     143             : // Given the transpose of a n-by-n adjacency matrix A and a source vertex s.
     144             : // If there is no negative-weight cycle reachable from s, return the distances
     145             : // of shortest paths from s and parents along the paths as vector d. Otherwise,
     146             : // returns d=NULL if there is a negtive-weight cycle.
     147             : // pd_output is pointer to a GrB_Vector, where the i-th entry is d(s,i), the
     148             : //   sum of edges length in the shortest path
     149             : // ppi_output is pointer to a GrB_Vector, where the i-th entry is pi(i), the
     150             : //   parent of i-th vertex in the shortest path
     151             : // ph_output is pointer to a GrB_Vector, where the i-th entry is h(s,i), the
     152             : //   number of edges from s to i in the shortest path
     153             : // AT has zeros on diagonal and weights on corresponding entries of edges
     154             : // s is given index for source vertex
     155             : 
     156           5 : GrB_Info LAGraph_BF_full_mxv
     157             : (
     158             :     GrB_Vector *pd_output,      //the pointer to the vector of distance
     159             :     GrB_Vector *ppi_output,     //the pointer to the vector of parent
     160             :     GrB_Vector *ph_output,      //the pointer to the vector of hops
     161             :     const GrB_Matrix AT,         //matrix for the graph
     162             :     const GrB_Index s           //given index of the source
     163             : )
     164             : {
     165             :     GrB_Info info;
     166           5 :     char *msg = NULL ;
     167             :     // tmp vector to store distance vector after n (i.e., V) loops
     168           5 :     GrB_Vector d = NULL, dtmp = NULL;
     169           5 :     GrB_Matrix Atmp = NULL;
     170             :     GrB_Type BF_Tuple3;
     171             : 
     172             :     GrB_BinaryOp BF_lMIN_Tuple3;
     173             :     GrB_BinaryOp BF_PLUSrhs_Tuple3;
     174             :     GrB_BinaryOp BF_EQ_Tuple3;
     175             : 
     176             :     GrB_Monoid BF_lMIN_Tuple3_Monoid;
     177             :     GrB_Semiring BF_lMIN_PLUSrhs_Tuple3;
     178             : 
     179             :     GrB_Index nrows, ncols, n, nz;  // n = # of row/col, nz = # of nnz in graph
     180           5 :     GrB_Index *I = NULL, *J = NULL; // for col/row indices of entries from AT
     181           5 :     GrB_Index *h = NULL, *pi = NULL;
     182           5 :     double *w = NULL;
     183           5 :     BF_Tuple3_struct *W = NULL;
     184             : 
     185           5 :     LG_ASSERT (AT != NULL && pd_output != NULL &&
     186             :         ppi_output != NULL && ph_output != NULL, GrB_NULL_POINTER) ;
     187             : 
     188           5 :     *pd_output  = NULL;
     189           5 :     *ppi_output = NULL;
     190           5 :     *ph_output  = NULL;
     191           5 :     GRB_TRY (GrB_Matrix_nrows (&nrows, AT)) ;
     192           5 :     GRB_TRY (GrB_Matrix_ncols (&ncols, AT)) ;
     193           5 :     GRB_TRY (GrB_Matrix_nvals (&nz, AT));
     194           5 :     LG_ASSERT_MSG (nrows == ncols, -1002, "A must be square") ;
     195           5 :     n = nrows;
     196           5 :     LG_ASSERT_MSG (s < n, GrB_INVALID_INDEX, "invalid source node") ;
     197             : 
     198             :     //--------------------------------------------------------------------------
     199             :     // create all GrB_Type GrB_BinaryOp GrB_Monoid and GrB_Semiring
     200             :     //--------------------------------------------------------------------------
     201             :     // GrB_Type
     202           5 :     GRB_TRY (GrB_Type_new(&BF_Tuple3, sizeof(BF_Tuple3_struct)));
     203             : 
     204             :     // GrB_BinaryOp
     205           5 :     GRB_TRY (GrB_BinaryOp_new(&BF_EQ_Tuple3,
     206             :         (LAGraph_binary_function) (&BF_EQ_mxv),
     207             :         GrB_BOOL, BF_Tuple3, BF_Tuple3));
     208           5 :     GRB_TRY (GrB_BinaryOp_new(&BF_lMIN_Tuple3,
     209             :         (LAGraph_binary_function) (&BF_lMIN_mxv),
     210             :         BF_Tuple3, BF_Tuple3, BF_Tuple3));
     211           5 :     GRB_TRY (GrB_BinaryOp_new(&BF_PLUSrhs_Tuple3,
     212             :         (LAGraph_binary_function)(&BF_PLUSrhs_mxv),
     213             :         BF_Tuple3, BF_Tuple3, BF_Tuple3));
     214             : 
     215             :     // GrB_Monoid
     216           5 :     BF_Tuple3_struct BF_identity = (BF_Tuple3_struct) { .w = INFINITY,
     217             :         .h = UINT64_MAX, .pi = UINT64_MAX };
     218           5 :     GRB_TRY (GrB_Monoid_new_UDT(&BF_lMIN_Tuple3_Monoid, BF_lMIN_Tuple3,
     219             :         &BF_identity));
     220             : 
     221             :     //GrB_Semiring
     222           5 :     GRB_TRY (GrB_Semiring_new(&BF_lMIN_PLUSrhs_Tuple3,
     223             :         BF_lMIN_Tuple3_Monoid, BF_PLUSrhs_Tuple3));
     224             : 
     225             :     //--------------------------------------------------------------------------
     226             :     // allocate arrays used for tuplets
     227             :     //--------------------------------------------------------------------------
     228             : 
     229           5 :     LAGRAPH_TRY (LAGraph_Malloc ((void **) &I, nz, sizeof(GrB_Index), msg)) ;
     230           5 :     LAGRAPH_TRY (LAGraph_Malloc ((void **) &J, nz, sizeof(GrB_Index), msg)) ;
     231           5 :     LAGRAPH_TRY (LAGraph_Malloc ((void **) &w, nz, sizeof(double), msg)) ;
     232           5 :     LAGRAPH_TRY (LAGraph_Malloc ((void **) &W, nz, sizeof(BF_Tuple3_struct),
     233             :         msg)) ;
     234             : 
     235             :     //--------------------------------------------------------------------------
     236             :     // create matrix Atmp based on AT, while its entries become BF_Tuple3 type
     237             :     //--------------------------------------------------------------------------
     238           5 :     GRB_TRY (GrB_Matrix_extractTuples_FP64(I, J, w, &nz, AT));
     239         951 :     for (GrB_Index k = 0; k < nz; k++)
     240             :     {
     241         946 :         if (w[k] == 0)             //diagonal entries
     242             :         {
     243         184 :             W[k] = (BF_Tuple3_struct) { .w = 0, .h = 0, .pi = 0 };
     244             :         }
     245             :         else
     246             :         {
     247             :             // w(i, j) is the weight of edge from j to i, so the parent here
     248             :             // should be j
     249         762 :             W[k] = (BF_Tuple3_struct) { .w = w[k], .h = 1, .pi = J[k] + 1 };
     250             :         }
     251             :     }
     252           5 :     GRB_TRY (GrB_Matrix_new(&Atmp, BF_Tuple3, n, n));
     253           5 :     GRB_TRY (GrB_Matrix_build_UDT(Atmp, I, J, W, nz, BF_lMIN_Tuple3));
     254           5 :     LAGraph_Free ((void**)&I, NULL);
     255           5 :     LAGraph_Free ((void**)&J, NULL);
     256           5 :     LAGraph_Free ((void**)&W, NULL);
     257           5 :     LAGraph_Free ((void**)&w, NULL);
     258             : 
     259             :     //--------------------------------------------------------------------------
     260             :     // create and initialize "distance" vector d
     261             :     //--------------------------------------------------------------------------
     262           5 :     GRB_TRY (GrB_Vector_new(&d, BF_Tuple3, n));
     263             :     // initial distance from s to itself
     264           5 :     BF_Tuple3_struct d0 = (BF_Tuple3_struct) { .w = 0, .h = 0, .pi = 0 };
     265           5 :     GRB_TRY (GrB_Vector_setElement_UDT(d, &d0, s));
     266             : 
     267             :     //--------------------------------------------------------------------------
     268             :     // start the Bellman Ford process
     269             :     //--------------------------------------------------------------------------
     270             :     // copy d to dtmp in order to create a same size of vector
     271           5 :     GRB_TRY (GrB_Vector_dup(&dtmp, d));
     272           5 :     bool same= false;          // variable indicating if d == dtmp
     273           5 :     int64_t iter = 0;          // number of iterations
     274             : 
     275             :     // terminate when no new path is found or more than V-1 loops
     276          93 :     while (!same && iter < n - 1)
     277             :     {
     278             :         // execute semiring on d and AT, and save the result to dtmp
     279          88 :         GRB_TRY (GrB_mxv(dtmp, GrB_NULL, GrB_NULL, BF_lMIN_PLUSrhs_Tuple3,
     280             :             Atmp, d, GrB_NULL));
     281          88 :         LG_TRY (LAGraph_Vector_IsEqualOp (&same, dtmp, d, BF_EQ_Tuple3, NULL));
     282          88 :         if (!same)
     283             :         {
     284          85 :             GrB_Vector ttmp = dtmp;
     285          85 :             dtmp = d;
     286          85 :             d = ttmp;
     287             :         }
     288          88 :         iter ++;
     289             :     }
     290             : 
     291             :     // check for negative-weight cycle only when there was a new path in the
     292             :     // last loop, otherwise, there can't be a negative-weight cycle.
     293           5 :     if (!same)
     294             :     {
     295             :         // execute semiring again to check for negative-weight cycle
     296           2 :         GRB_TRY (GrB_mxv(dtmp, GrB_NULL, GrB_NULL, BF_lMIN_PLUSrhs_Tuple3,
     297             :             Atmp, d, GrB_NULL));
     298           2 :         LG_TRY (LAGraph_Vector_IsEqualOp (&same, dtmp, d, BF_EQ_Tuple3, NULL));
     299             : 
     300             :         // if d != dtmp, then there is a negative-weight cycle in the graph
     301           2 :         if (!same)
     302             :         {
     303             :             // printf("A negative-weight cycle found. \n");
     304           2 :             LG_FREE_ALL;
     305           2 :             return (GrB_NO_VALUE) ;
     306             :         }
     307             :     }
     308             : 
     309             :     //--------------------------------------------------------------------------
     310             :     // extract tuple from "distance" vector d and create GrB_Vectors for output
     311             :     //--------------------------------------------------------------------------
     312             : 
     313           3 :     LAGRAPH_TRY (LAGraph_Malloc ((void **) &I, n, sizeof(GrB_Index), msg)) ;
     314           3 :     LAGRAPH_TRY (LAGraph_Malloc ((void **) &W, n, sizeof(BF_Tuple3_struct),
     315             :         msg)) ;
     316           3 :     LAGRAPH_TRY (LAGraph_Malloc ((void **) &w, n, sizeof(double), msg)) ;
     317           3 :     LAGRAPH_TRY (LAGraph_Malloc ((void **) &h, n, sizeof(GrB_Index), msg)) ;
     318           3 :     LAGRAPH_TRY (LAGraph_Malloc ((void **) &pi, n, sizeof(GrB_Index), msg)) ;
     319             : 
     320           3 :     nz = n ;
     321           3 :     GRB_TRY (GrB_Vector_extractTuples_UDT (I, (void *) W, &nz, d));
     322             : 
     323         111 :     for (GrB_Index k = 0; k < n; k++)
     324             :     {
     325         108 :         w [k] = W[k].w ;
     326         108 :         h [k] = W[k].h ;
     327         108 :         pi[k] = W[k].pi;
     328             :     }
     329             : 
     330           3 :     GRB_TRY (GrB_Vector_new(pd_output,  GrB_FP64,   n));
     331           3 :     GRB_TRY (GrB_Vector_new(ppi_output, GrB_UINT64, n));
     332           3 :     GRB_TRY (GrB_Vector_new(ph_output,  GrB_UINT64, n));
     333           3 :     GRB_TRY (GrB_Vector_build (*pd_output , I, w , nz, GrB_MIN_FP64  ));
     334           3 :     GRB_TRY (GrB_Vector_build (*ppi_output, I, pi, nz, GrB_MIN_UINT64));
     335           3 :     GRB_TRY (GrB_Vector_build (*ph_output , I, h , nz, GrB_MIN_UINT64));
     336           3 :     LG_FREE_WORK;
     337           3 :     return (GrB_SUCCESS) ;
     338             : }

Generated by: LCOV version 1.14