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

          Line data    Source code
       1             : //------------------------------------------------------------------------------
       2             : // LAGr_SingleSourceShortestPath: single-source shortest path
       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, Scott Kolodziej and Tim Davis, Texas A&M
      15             : // University.  Adapted from GraphBLAS Template Library (GBTL) by Scott
      16             : // McMillan and Tze Meng Low.
      17             : 
      18             : //------------------------------------------------------------------------------
      19             : 
      20             : // This is an Advanced algorithm (G->emin is required).
      21             : 
      22             : // Single source shortest path with delta stepping.
      23             : 
      24             : // U. Sridhar, M. Blanco, R. Mayuranath, D. G. Spampinato, T. M. Low, and
      25             : // S. McMillan, "Delta-Stepping SSSP: From Vertices and Edges to GraphBLAS
      26             : // Implementations," in 2019 IEEE International Parallel and Distributed
      27             : // Processing Symposium Workshops (IPDPSW), 2019, pp. 241–250.
      28             : // https://ieeexplore.ieee.org/document/8778222/references
      29             : // https://arxiv.org/abs/1911.06895
      30             : 
      31             : // LAGr_SingleSourceShortestPath computes the shortest path lengths from the
      32             : // specified source vertex to all other vertices in the graph.
      33             : 
      34             : // The parent vector is not computed; see LAGraph_BF_* instead.
      35             : 
      36             : // NOTE: this method gets stuck in an infinite loop when there are negative-
      37             : // weight cycles in the graph.
      38             : 
      39             : // FUTURE: a Basic algorithm that picks Delta automatically
      40             : 
      41             : #define LG_FREE_WORK        \
      42             : {                           \
      43             :     GrB_free (&AL) ;        \
      44             :     GrB_free (&AH) ;        \
      45             :     GrB_free (&lBound) ;    \
      46             :     GrB_free (&uBound) ;    \
      47             :     GrB_free (&tmasked) ;   \
      48             :     GrB_free (&tReq) ;      \
      49             :     GrB_free (&tless) ;     \
      50             :     GrB_free (&s) ;         \
      51             :     GrB_free (&reach) ;     \
      52             :     GrB_free (&Empty) ;     \
      53             : }
      54             : 
      55             : #define LG_FREE_ALL         \
      56             : {                           \
      57             :     LG_FREE_WORK ;          \
      58             :     GrB_free (&t) ;         \
      59             : }
      60             : 
      61             : #include "LG_internal.h"
      62             : 
      63             : #define setelement(s, k)                                                      \
      64             : {                                                                             \
      65             :     switch (tcode)                                                            \
      66             :     {                                                                         \
      67             :         default:                                                              \
      68             :         case 0 : GrB_Scalar_setElement_INT32  (s, k * delta_int32 ) ; break ; \
      69             :         case 1 : GrB_Scalar_setElement_INT64  (s, k * delta_int64 ) ; break ; \
      70             :         case 2 : GrB_Scalar_setElement_UINT32 (s, k * delta_uint32) ; break ; \
      71             :         case 3 : GrB_Scalar_setElement_UINT64 (s, k * delta_uint64) ; break ; \
      72             :         case 4 : GrB_Scalar_setElement_FP32   (s, k * delta_fp32  ) ; break ; \
      73             :         case 5 : GrB_Scalar_setElement_FP64   (s, k * delta_fp64  ) ; break ; \
      74             :     }                                                                         \
      75             : }
      76             : 
      77        3955 : int LAGr_SingleSourceShortestPath
      78             : (
      79             :     // output:
      80             :     GrB_Vector *path_length,    // path_length (i) is the length of the shortest
      81             :                                 // path from the source vertex to vertex i
      82             :     // input:
      83             :     const LAGraph_Graph G,      // input graph, not modified
      84             :     GrB_Index source,           // source vertex
      85             :     GrB_Scalar Delta,           // delta value for delta stepping
      86             :     char *msg
      87             : )
      88             : {
      89             : 
      90             :     //--------------------------------------------------------------------------
      91             :     // check inputs
      92             :     //--------------------------------------------------------------------------
      93             : 
      94        3955 :     LG_CLEAR_MSG ;
      95        3955 :     GrB_Scalar lBound = NULL ;  // the threshold for GrB_select
      96        3955 :     GrB_Scalar uBound = NULL ;  // the threshold for GrB_select
      97        3955 :     GrB_Matrix AL = NULL ;      // graph containing the light weight edges
      98        3955 :     GrB_Matrix AH = NULL ;      // graph containing the heavy weight edges
      99        3955 :     GrB_Vector t = NULL ;       // tentative shortest path length
     100        3955 :     GrB_Vector tmasked = NULL ;
     101        3955 :     GrB_Vector tReq = NULL ;
     102        3955 :     GrB_Vector tless = NULL ;
     103        3955 :     GrB_Vector s = NULL ;
     104        3955 :     GrB_Vector reach = NULL ;
     105        3955 :     GrB_Vector Empty = NULL ;
     106             : 
     107        3955 :     LG_TRY (LAGraph_CheckGraph (G, msg)) ;
     108        3955 :     LG_ASSERT (path_length != NULL && Delta != NULL, GrB_NULL_POINTER) ;
     109        3955 :     (*path_length) = NULL ;
     110             : 
     111             :     GrB_Index nvals ;
     112        3955 :     LG_TRY (GrB_Scalar_nvals (&nvals, Delta)) ;
     113        3955 :     LG_ASSERT_MSG (nvals == 1, GrB_EMPTY_OBJECT, "Delta is missing") ;
     114             : 
     115        3954 :     GrB_Matrix A = G->A ;
     116             :     GrB_Index n ;
     117        3954 :     GRB_TRY (GrB_Matrix_nrows (&n, A)) ;
     118        3954 :     LG_ASSERT_MSG (source < n, GrB_INVALID_INDEX, "invalid source node") ;
     119             : 
     120             :     //--------------------------------------------------------------------------
     121             :     // initializations
     122             :     //--------------------------------------------------------------------------
     123             : 
     124             :     // get the type of the A matrix
     125             :     GrB_Type etype ;
     126             :     char typename [LAGRAPH_MAX_NAME_LEN] ;
     127        3954 :     LG_TRY (LAGraph_Matrix_TypeName (typename, A, msg)) ;
     128        3954 :     LG_TRY (LAGraph_TypeFromName (&etype, typename, msg)) ;
     129             : 
     130        3954 :     GRB_TRY (GrB_Scalar_new (&lBound, etype)) ;
     131        3926 :     GRB_TRY (GrB_Scalar_new (&uBound, etype)) ;
     132        3898 :     GRB_TRY (GrB_Vector_new (&t, etype, n)) ;
     133        3870 :     GRB_TRY (GrB_Vector_new (&tmasked, etype, n)) ;
     134        3842 :     GRB_TRY (GrB_Vector_new (&tReq, etype, n)) ;
     135        3814 :     GRB_TRY (GrB_Vector_new (&Empty, GrB_BOOL, n)) ;
     136        3786 :     GRB_TRY (GrB_Vector_new (&tless, GrB_BOOL, n)) ;
     137        3758 :     GRB_TRY (GrB_Vector_new (&s, GrB_BOOL, n)) ;
     138        3730 :     GRB_TRY (GrB_Vector_new (&reach, GrB_BOOL, n)) ;
     139             : 
     140             : #if LAGRAPH_SUITESPARSE
     141             :     // optional hints for SuiteSparse:GraphBLAS
     142        3702 :     GRB_TRY (GxB_set (t, GxB_SPARSITY_CONTROL, GxB_BITMAP)) ;
     143        3674 :     GRB_TRY (GxB_set (tmasked, GxB_SPARSITY_CONTROL, GxB_SPARSE)) ;
     144        3674 :     GRB_TRY (GxB_set (tReq, GxB_SPARSITY_CONTROL, GxB_SPARSE)) ;
     145        3674 :     GRB_TRY (GxB_set (tless, GxB_SPARSITY_CONTROL, GxB_SPARSE)) ;
     146        3674 :     GRB_TRY (GxB_set (s, GxB_SPARSITY_CONTROL, GxB_SPARSE)) ;
     147        3674 :     GRB_TRY (GxB_set (reach, GxB_SPARSITY_CONTROL, GxB_BITMAP)) ;
     148             : #endif
     149             : 
     150             :     // select the operators, and set t (:) = infinity
     151             :     GrB_IndexUnaryOp ne, le, ge, lt, gt ;
     152             :     GrB_BinaryOp less_than ;
     153             :     GrB_Semiring min_plus ;
     154             :     int tcode ;
     155             :     int32_t  delta_int32  ;
     156             :     int64_t  delta_int64  ;
     157             :     uint32_t delta_uint32 ;
     158             :     uint64_t delta_uint64 ;
     159             :     float    delta_fp32   ;
     160             :     double   delta_fp64   ;
     161             : 
     162        3646 :     bool negative_edge_weights = true ;
     163             : 
     164        3646 :     if (etype == GrB_INT32)
     165             :     {
     166        3419 :         GRB_TRY (GrB_Scalar_extractElement (&delta_int32, Delta)) ;
     167        3419 :         GRB_TRY (GrB_assign (t, NULL, NULL, (int32_t) INT32_MAX,
     168             :             GrB_ALL, n, NULL)) ;
     169        3391 :         ne = GrB_VALUENE_INT32 ;
     170        3391 :         le = GrB_VALUELE_INT32 ;
     171        3391 :         ge = GrB_VALUEGE_INT32 ;
     172        3391 :         lt = GrB_VALUELT_INT32 ;
     173        3391 :         gt = GrB_VALUEGT_INT32 ;
     174        3391 :         less_than = GrB_LT_INT32 ;
     175        3391 :         min_plus = GrB_MIN_PLUS_SEMIRING_INT32 ;
     176        3391 :         tcode = 0 ;
     177             :     }
     178         227 :     else if (etype == GrB_INT64)
     179             :     {
     180          48 :         GRB_TRY (GrB_Scalar_extractElement (&delta_int64, Delta)) ;
     181          48 :         GRB_TRY (GrB_assign (t, NULL, NULL, (int64_t) INT64_MAX,
     182             :             GrB_ALL, n, NULL)) ;
     183          48 :         ne = GrB_VALUENE_INT64 ;
     184          48 :         le = GrB_VALUELE_INT64 ;
     185          48 :         ge = GrB_VALUEGE_INT64 ;
     186          48 :         lt = GrB_VALUELT_INT64 ;
     187          48 :         gt = GrB_VALUEGT_INT64 ;
     188          48 :         less_than = GrB_LT_INT64 ;
     189          48 :         min_plus = GrB_MIN_PLUS_SEMIRING_INT64 ;
     190          48 :         tcode = 1 ;
     191             :     }
     192         179 :     else if (etype == GrB_UINT32)
     193             :     {
     194          12 :         GRB_TRY (GrB_Scalar_extractElement (&delta_uint32, Delta)) ;
     195          12 :         GRB_TRY (GrB_assign (t, NULL, NULL, (uint32_t) UINT32_MAX,
     196             :             GrB_ALL, n, NULL)) ;
     197          12 :         ne = GrB_VALUENE_UINT32 ;
     198          12 :         le = GrB_VALUELE_UINT32 ;
     199          12 :         ge = GrB_VALUEGE_UINT32 ;
     200          12 :         lt = GrB_VALUELT_UINT32 ;
     201          12 :         gt = GrB_VALUEGT_UINT32 ;
     202          12 :         less_than = GrB_LT_UINT32 ;
     203          12 :         min_plus = GrB_MIN_PLUS_SEMIRING_UINT32 ;
     204          12 :         tcode = 2 ;
     205          12 :         negative_edge_weights = false ;
     206             :     }
     207         167 :     else if (etype == GrB_UINT64)
     208             :     {
     209          12 :         GRB_TRY (GrB_Scalar_extractElement (&delta_uint64, Delta)) ;
     210          12 :         GRB_TRY (GrB_assign (t, NULL, NULL, (uint64_t) UINT64_MAX,
     211             :             GrB_ALL, n, NULL)) ;
     212          12 :         ne = GrB_VALUENE_UINT64 ;
     213          12 :         le = GrB_VALUELE_UINT64 ;
     214          12 :         ge = GrB_VALUEGE_UINT64 ;
     215          12 :         lt = GrB_VALUELT_UINT64 ;
     216          12 :         gt = GrB_VALUEGT_UINT64 ;
     217          12 :         less_than = GrB_LT_UINT64 ;
     218          12 :         min_plus = GrB_MIN_PLUS_SEMIRING_UINT64 ;
     219          12 :         tcode = 3 ;
     220          12 :         negative_edge_weights = false ;
     221             :     }
     222         155 :     else if (etype == GrB_FP32)
     223             :     {
     224           9 :         GRB_TRY (GrB_Scalar_extractElement (&delta_fp32, Delta)) ;
     225           9 :         GRB_TRY (GrB_assign (t, NULL, NULL, (float) INFINITY,
     226             :             GrB_ALL, n, NULL)) ;
     227           9 :         ne = GrB_VALUENE_FP32 ;
     228           9 :         le = GrB_VALUELE_FP32 ;
     229           9 :         ge = GrB_VALUEGE_FP32 ;
     230           9 :         lt = GrB_VALUELT_FP32 ;
     231           9 :         gt = GrB_VALUEGT_FP32 ;
     232           9 :         less_than = GrB_LT_FP32 ;
     233           9 :         min_plus = GrB_MIN_PLUS_SEMIRING_FP32 ;
     234           9 :         tcode = 4 ;
     235             :     }
     236         146 :     else if (etype == GrB_FP64)
     237             :     {
     238         145 :         GRB_TRY (GrB_Scalar_extractElement (&delta_fp64, Delta)) ;
     239         145 :         GRB_TRY (GrB_assign (t, NULL, NULL, (double) INFINITY,
     240             :             GrB_ALL, n, NULL)) ;
     241         145 :         ne = GrB_VALUENE_FP64 ;
     242         145 :         le = GrB_VALUELE_FP64 ;
     243         145 :         ge = GrB_VALUEGE_FP64 ;
     244         145 :         lt = GrB_VALUELT_FP64 ;
     245         145 :         gt = GrB_VALUEGT_FP64 ;
     246         145 :         less_than = GrB_LT_FP64 ;
     247         145 :         min_plus = GrB_MIN_PLUS_SEMIRING_FP64 ;
     248         145 :         tcode = 5 ;
     249             :     }
     250             :     else
     251             :     {
     252           1 :         LG_ASSERT_MSG (false, GrB_NOT_IMPLEMENTED, "type not supported") ;
     253             :     }
     254             : 
     255             :     // check if the graph might have negative edge weights
     256        3617 :     if (negative_edge_weights)
     257             :     {
     258        3593 :         double emin = -1 ;
     259        3593 :         if (G->emin != NULL &&
     260         452 :             (G->emin_state == LAGraph_VALUE ||
     261         238 :              G->emin_state == LAGraph_BOUND))
     262             :         {
     263         214 :             GRB_TRY (GrB_Scalar_extractElement_FP64 (&emin, G->emin)) ;
     264             :         }
     265             : //      else
     266             : //      {
     267             : //          // a future Basic algorithm should compute G->emin and perhaps
     268             : //          // G->emax, then compute Delta automatically.
     269             : //      }
     270        3593 :         negative_edge_weights = (emin < 0) ;
     271             :     }
     272             : 
     273             :     // t (src) = 0
     274        3617 :     GRB_TRY (GrB_Vector_setElement (t, 0, source)) ;
     275             : 
     276             :     // reach (src) = true
     277        3603 :     GRB_TRY (GrB_Vector_setElement (reach, true, source)) ;
     278             : 
     279             :     // s (src) = true
     280        3589 :     GRB_TRY (GrB_Vector_setElement (s, true, source)) ;
     281             : 
     282             :     // AL = A .* (A <= Delta)
     283        3547 :     GRB_TRY (GrB_Matrix_new (&AL, etype, n, n)) ;
     284        3505 :     GRB_TRY (GrB_select (AL, NULL, NULL, le, A, Delta, NULL)) ;
     285        3454 :     GRB_TRY (GrB_wait (AL, GrB_MATERIALIZE)) ;
     286             : 
     287             :     // FUTURE: costly for some problems, taking up to 50% of the total time:
     288             :     // AH = A .* (A > Delta)
     289        3454 :     GRB_TRY (GrB_Matrix_new (&AH, etype, n, n)) ;
     290        3412 :     GRB_TRY (GrB_select (AH, NULL, NULL, gt, A, Delta, NULL)) ;
     291        3336 :     GRB_TRY (GrB_wait (AH, GrB_MATERIALIZE)) ;
     292             : 
     293             :     //--------------------------------------------------------------------------
     294             :     // while (t >= step*Delta) not empty
     295             :     //--------------------------------------------------------------------------
     296             : 
     297        3336 :     for (int64_t step = 0 ; ; step++)
     298        9573 :     {
     299             : 
     300             :         //----------------------------------------------------------------------
     301             :         // tmasked = all entries in t<reach> that are less than (step+1)*Delta
     302             :         //----------------------------------------------------------------------
     303             : 
     304       12909 :         setelement (uBound, (step+1)) ;        // uBound = (step+1) * Delta
     305       15596 :         GRB_TRY (GrB_Vector_clear (tmasked)) ;
     306             : 
     307             :         // tmasked<reach> = t
     308             :         // FUTURE: this is costly, typically using Method 06s in SuiteSparse,
     309             :         // which is a very general-purpose one.  Write a specialized kernel to
     310             :         // exploit the fact that reach and t are bitmap and tmasked starts
     311             :         // empty, or fuse this assignment with the GrB_select below.
     312       12779 :         GRB_TRY (GrB_assign (tmasked, reach, NULL, t, GrB_ALL, n, NULL)) ;
     313             :         // tmasked = select (tmasked < (step+1)*Delta)
     314       12511 :         GRB_TRY (GrB_select (tmasked, NULL, NULL, lt, tmasked, uBound, NULL)) ;
     315             :         // --- alternative:
     316             :         // FUTURE this is slower than the above but should be much faster.
     317             :         // GrB_select is computing a bitmap result then converting it to
     318             :         // sparse.  t and reach are both bitmap and tmasked finally sparse.
     319             :         // tmasked<reach> = select (t < (step+1)*Delta)
     320             :         // GRB_TRY (GrB_select (tmasked, reach, NULL, lt, t, uBound, NULL)) ;
     321             : 
     322             :         GrB_Index tmasked_nvals ;
     323       12343 :         GRB_TRY (GrB_Vector_nvals (&tmasked_nvals, tmasked)) ;
     324             : 
     325             :         //----------------------------------------------------------------------
     326             :         // continue while the current bucket (tmasked) is not empty
     327             :         //----------------------------------------------------------------------
     328             : 
     329       25660 :         while (tmasked_nvals > 0)
     330             :         {
     331             :             // tReq = AL'*tmasked using the min_plus semiring
     332       20033 :             GRB_TRY (GrB_vxm (tReq, NULL, NULL, min_plus, tmasked, AL, NULL)) ;
     333             : 
     334             :             // s<struct(tmasked)> = true
     335       18360 :             GRB_TRY (GrB_assign (s, tmasked, NULL, (bool) true, GrB_ALL, n,
     336             :                 GrB_DESC_S)) ;
     337             : 
     338             :             // if nvals (tReq) is 0, no need to continue the rest of this loop
     339             :             GrB_Index tReq_nvals ;
     340       18153 :             GRB_TRY (GrB_Vector_nvals (&tReq_nvals, tReq)) ;
     341       20223 :             if (tReq_nvals == 0) break ;
     342             : 
     343             :             // tless = (tReq .< t) using set intersection
     344       16523 :             GRB_TRY (GrB_eWiseMult (tless, NULL, NULL, less_than, tReq, t,
     345             :                 NULL)) ;
     346             : 
     347             :             // remove explicit zeros from tless so it can be used as a
     348             :             // structural mask
     349             :             GrB_Index tless_nvals ;
     350       16310 :             GRB_TRY (GrB_select (tless, NULL, NULL, ne, tless, 0, NULL)) ;
     351       16103 :             GRB_TRY (GrB_Vector_nvals (&tless_nvals, tless)) ;
     352       16103 :             if (tless_nvals == 0) break ;
     353             : 
     354             :             // update reachable node list/mask
     355             :             // reach<struct(tless)> = true
     356       14033 :             GRB_TRY (GrB_assign (reach, tless, NULL, (bool) true, GrB_ALL, n,
     357             :                 GrB_DESC_S)) ;
     358             : 
     359             :             // tmasked<struct(tless)> = select (tReq < (step+1)*Delta)
     360       14033 :             GRB_TRY (GrB_Vector_clear (tmasked)) ;
     361       13985 :             GRB_TRY (GrB_select (tmasked, tless, NULL, lt, tReq, uBound,
     362             :                 GrB_DESC_S)) ;
     363             : 
     364             :             // For general graph with some negative weights:
     365       13581 :             if (negative_edge_weights)
     366             :             {
     367             :                 // If all entries of the graph are known to be positive, and
     368             :                 // the entries of tmasked are at least step*Delta, tReq =
     369             :                 // tmasked min.+ AL must be >= step*Delta.  Therefore, there is
     370             :                 // no need to perform this GrB_select with ge to find tmasked
     371             :                 // >= step*Delta from tReq.
     372       10710 :                 setelement (lBound, (step)) ;  // lBound = step*Delta
     373             :                 // tmasked = select entries in tmasked that are >= step*Delta
     374       10710 :                 GRB_TRY (GrB_select (tmasked, NULL, NULL, ge, tmasked, lBound,
     375             :                     NULL)) ;
     376             :             }
     377             : 
     378             :             // t<struct(tless)> = tReq
     379       13317 :             GRB_TRY (GrB_assign (t, tless, NULL, tReq, GrB_ALL, n, GrB_DESC_S));
     380       13317 :             GRB_TRY (GrB_Vector_nvals (&tmasked_nvals, tmasked)) ;
     381             :         }
     382             : 
     383             :         // tmasked<s> = t
     384       10670 :         GRB_TRY (GrB_Vector_clear (tmasked)) ;
     385       10638 :         GRB_TRY (GrB_assign (tmasked, s, NULL, t, GrB_ALL, n, GrB_DESC_S)) ;
     386             : 
     387             :         // tReq = AH'*tmasked using the min_plus semiring
     388       10404 :         GRB_TRY (GrB_vxm (tReq, NULL, NULL, min_plus, tmasked, AH, NULL)) ;
     389             : 
     390             :         // tless = (tReq .< t) using set intersection
     391       10212 :         GRB_TRY (GrB_eWiseMult (tless, NULL, NULL, less_than, tReq, t, NULL)) ;
     392             : 
     393             :         // t<tless> = tReq, which computes t = min (t, tReq)
     394       10116 :         GRB_TRY (GrB_assign (t, tless, NULL, tReq, GrB_ALL, n, NULL)) ;
     395             : 
     396             :         //----------------------------------------------------------------------
     397             :         // find out how many left to be computed
     398             :         //----------------------------------------------------------------------
     399             : 
     400             :         // update reachable node list
     401             :         // reach<tless> = true
     402       10116 :         GRB_TRY (GrB_assign (reach, tless, NULL, (bool) true, GrB_ALL, n,
     403             :             NULL)) ;
     404             : 
     405             :         // remove previous buckets
     406             :         // reach<struct(s)> = Empty
     407       10116 :         GRB_TRY (GrB_assign (reach, s, NULL, Empty, GrB_ALL, n, GrB_DESC_S)) ;
     408             :         GrB_Index nreach ;
     409       10110 :         GRB_TRY (GrB_Vector_nvals (&nreach, reach)) ;
     410       10110 :         if (nreach == 0) break ;
     411             : 
     412        9591 :         GRB_TRY (GrB_Vector_clear (s)) ; // clear s for the next iteration
     413             :     }
     414             : 
     415             :     //--------------------------------------------------------------------------
     416             :     // free workspace and return result
     417             :     //--------------------------------------------------------------------------
     418             : 
     419         519 :     (*path_length) = t ;
     420         519 :     LG_FREE_WORK ;
     421         519 :     return (GrB_SUCCESS) ;
     422             : }

Generated by: LCOV version 1.14