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

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

Generated by: LCOV version 1.14