Skip to content

Commit fe1d354

Browse files
Add ALL SHORTEST PATH 2RPQ support
This patch introduces ALL SHORTEST PATH semantics in the regular path query algorithm. The key insight is really similar to the reachability (i.e. ENPOINTS) semantics described in detail in [^1]. The idea of SINGLE SOURCE ALL SHORTEST PATH semantics is for a given query $Q$, a graph $G$, and a vertex $s$ is for all vertices $v$ to find all minimum length paths from $s$ to $v$. The implementation combines custom semirings for ALL PATHS along with filtering already-visited pairs of NFA states and graph vertices. [^1] https://arxiv.org/abs/2412.10287
1 parent a2b4fb7 commit fe1d354

3 files changed

Lines changed: 120 additions & 10 deletions

File tree

experimental/algorithm/LAGraph_2Rpq.c

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,7 @@ static int ensure_result_capacity
10801080
LAGraph_Free ((void **) &BT, NULL) ; \
10811081
LAGraph_Free ((void **) &X, NULL) ; \
10821082
LAGraph_Free ((void **) &I, NULL) ; \
1083+
LAGraph_Free ((void **) &J, NULL) ; \
10831084
temp_arena_destroy () ; \
10841085
}
10851086

@@ -1111,6 +1112,7 @@ static int LAGraph_2Rpq
11111112
const GrB_Index *S, // source vertices to start searching paths
11121113
size_t ns, // number of source vertices
11131114
bool inverse, // inverse the whole query
1115+
bool ignore_visited, // use mask to avoid processing the same (q, v)
11141116
uint64_t limit, // maximum path count
11151117
char *msg, // LAGraph output message
11161118
GrB_IndexUnaryOp op // index unary op for a specific semantic
@@ -1129,6 +1131,7 @@ static int LAGraph_2Rpq
11291131
// specific label
11301132
GrB_Matrix next_frontier = NULL ; // frontier value on the next
11311133
// traversal step
1134+
GrB_Matrix visited = NULL ; // visited pairs (state, vertex)
11321135
GrB_Vector final_reducer = NULL ; // auxiliary vector for reducing the
11331136
// visited matrix to an answer
11341137

@@ -1150,6 +1153,7 @@ static int LAGraph_2Rpq
11501153

11511154
MultiplePaths *X = NULL ;
11521155
GrB_Index *I = NULL ;
1156+
GrB_Index *J = NULL ;
11531157
size_t result_capacity = 0 ;
11541158

11551159
if (paths == NULL || path_count == NULL || G == NULL || R == NULL ||
@@ -1314,6 +1318,11 @@ static int LAGraph_2Rpq
13141318

13151319
GRB_TRY (GrB_Matrix_new (&next_frontier, multiple_paths, nr, ng)) ;
13161320

1321+
if (ignore_visited)
1322+
{
1323+
GRB_TRY (GrB_Matrix_new (&visited, GrB_BOOL, nr, ng)) ;
1324+
}
1325+
13171326
// Initialize frontier with the source nodes
13181327

13191328
for (size_t i = 0 ; i < ns ; i++)
@@ -1339,6 +1348,11 @@ static int LAGraph_2Rpq
13391348
}
13401349
}
13411350

1351+
if (ignore_visited)
1352+
{
1353+
GrB_assign (visited, NULL, NULL, true, QS, nqs, S, ns, NULL) ;
1354+
}
1355+
13421356
// Initialize a few utility matrices
13431357
GRB_TRY (GrB_Matrix_new (&frontier, multiple_paths, nr, ng)) ;
13441358
GRB_TRY (GrB_Matrix_new (&symbol_frontier, multiple_paths, nr, ng)) ;
@@ -1354,9 +1368,10 @@ static int LAGraph_2Rpq
13541368

13551369
LG_TRY (LAGraph_Calloc ((void **) &X, nvals, sizeof (MultiplePaths), msg)) ;
13561370
LG_TRY (LAGraph_Calloc ((void **) &I, nvals, sizeof (GrB_Index), msg)) ;
1371+
LG_TRY (LAGraph_Calloc ((void **) &J, nvals, sizeof (GrB_Index), msg)) ;
13571372

13581373
// TODO: Change to a generic call.
1359-
GRB_TRY (GrB_Matrix_extractTuples_UDT (I, GrB_NULL, (void**) X, &nvals, next_frontier)) ;
1374+
GRB_TRY (GrB_Matrix_extractTuples_UDT (I, J, (void**) X, &nvals, next_frontier)) ;
13601375
//printf("Next frontier with %d entries\n", nvals);
13611376

13621377
for (size_t i = 0 ; i < nvals ; i++)
@@ -1383,6 +1398,20 @@ static int LAGraph_2Rpq
13831398
}
13841399
//printf("Path at %ld final is %b", I[i], final) ;
13851400

1401+
// HACK: only for all_shortest_paths
1402+
if (ignore_visited) {
1403+
GrB_Vector w;
1404+
GRB_TRY (GrB_Vector_new (&w, GrB_BOOL, nr)) ;
1405+
GrB_Col_extract(w, GrB_NULL, GrB_NULL, visited, QF, nqf, J[i], GrB_NULL);
1406+
GrB_Index col_nvals = 0 ;
1407+
GrB_Vector_nvals (&col_nvals, w) ;
1408+
GrB_free (&w) ;
1409+
1410+
if (col_nvals > 0) {
1411+
continue ;
1412+
}
1413+
}
1414+
13861415
if (!final)
13871416
{
13881417
continue ;
@@ -1414,8 +1443,16 @@ static int LAGraph_2Rpq
14141443
}
14151444
}
14161445

1446+
if (ignore_visited)
1447+
{
1448+
//GRB_TRY (GrB_assign (visited, visited, GrB_NULL, next_frontier,
1449+
// GrB_ALL, nr, GrB_ALL, ng, GrB_DESC_SC)) ;
1450+
GrB_assign (visited, next_frontier, GrB_NULL, true, GrB_ALL, nr, GrB_ALL, ng, GrB_DESC_S) ;
1451+
}
1452+
14171453
LAGraph_Free ((void **) &X, NULL) ;
14181454
LAGraph_Free ((void **) &I, NULL) ;
1455+
LAGraph_Free ((void **) &J, NULL) ;
14191456

14201457
if (!had_non_empty_path || (*path_count) == limit)
14211458
{
@@ -1456,22 +1493,25 @@ static int LAGraph_2Rpq
14561493
GRB_TRY (GrB_Matrix_nvals (&symbol_nvals, symbol_frontier)) ;
14571494
if (symbol_nvals == 0) continue ;
14581495

1496+
GrB_Descriptor desc_forward = ignore_visited ? GrB_DESC_SC : GrB_NULL ;
1497+
GrB_Descriptor desc_backward = ignore_visited ? GrB_DESC_SCT1 : GrB_DESC_T1 ;
1498+
14591499
// Traverse the graph
14601500
if (!inverse_labels[i]) {
14611501
if (!inverse) {
1462-
GRB_TRY (GrB_mxm (next_frontier, GrB_NULL, acc, sr1, symbol_frontier, A[i], GrB_NULL)) ;
1502+
GRB_TRY (GrB_mxm (next_frontier, visited, acc, sr1, symbol_frontier, A[i], desc_forward)) ;
14631503
} else if (AT[i]) {
1464-
GRB_TRY (GrB_mxm (next_frontier, GrB_NULL, acc, sr1, symbol_frontier, AT[i], GrB_NULL)) ;
1504+
GRB_TRY (GrB_mxm (next_frontier, visited, acc, sr1, symbol_frontier, AT[i], desc_forward)) ;
14651505
} else {
1466-
GRB_TRY (GrB_mxm (next_frontier, GrB_NULL, acc, sr1, symbol_frontier, A[i], GrB_DESC_T1)) ;
1506+
GRB_TRY (GrB_mxm (next_frontier, visited, acc, sr1, symbol_frontier, A[i], desc_backward)) ;
14671507
}
14681508
} else {
14691509
if (!inverse && AT[i]) {
1470-
GRB_TRY (GrB_mxm (next_frontier, GrB_NULL, acc, sr1, symbol_frontier, AT[i], GrB_NULL)) ;
1510+
GRB_TRY (GrB_mxm (next_frontier, visited, acc, sr1, symbol_frontier, AT[i], desc_forward)) ;
14711511
} else if (!inverse) {
1472-
GRB_TRY (GrB_mxm (next_frontier, GrB_NULL, acc, sr1, symbol_frontier, A[i], GrB_DESC_T1)) ;
1512+
GRB_TRY (GrB_mxm (next_frontier, visited, acc, sr1, symbol_frontier, A[i], desc_backward)) ;
14731513
} else {
1474-
GRB_TRY (GrB_mxm (next_frontier, GrB_NULL, acc, sr1, symbol_frontier, A[i], GrB_NULL)) ;
1514+
GRB_TRY (GrB_mxm (next_frontier, visited, acc, sr1, symbol_frontier, A[i], desc_forward)) ;
14751515
}
14761516
}
14771517

@@ -1524,7 +1564,7 @@ int LAGraph_2Rpq_AllSimple // All simple paths satisfying regular
15241564
char *msg // LAGraph output message
15251565
)
15261566
{
1527-
return LAGraph_2Rpq(paths, path_count, R, inverse_labels, nl, QS, nqs, QF, nqf, G, S, ns, inverse, ULLONG_MAX, msg, extend_multiple_simple) ;
1567+
return LAGraph_2Rpq(paths, path_count, R, inverse_labels, nl, QS, nqs, QF, nqf, G, S, ns, inverse, false, ULLONG_MAX, msg, extend_multiple_simple) ;
15281568
}
15291569

15301570
LAGRAPHX_PUBLIC
@@ -1552,7 +1592,7 @@ int LAGraph_2Rpq_AllTrails // All trails satisfying regular expression.
15521592
char *msg // LAGraph output message
15531593
)
15541594
{
1555-
return LAGraph_2Rpq(paths, path_count, R, inverse_labels, nl, QS, nqs, QF, nqf, G, S, ns, inverse, ULLONG_MAX, msg, extend_multiple_trails) ;
1595+
return LAGraph_2Rpq(paths, path_count, R, inverse_labels, nl, QS, nqs, QF, nqf, G, S, ns, inverse, false, ULLONG_MAX, msg, extend_multiple_trails) ;
15561596
}
15571597

15581598
int LAGraph_2Rpq_AllPaths // All paths satisfying regular expression
@@ -1579,7 +1619,33 @@ int LAGraph_2Rpq_AllPaths // All paths satisfying regular expression
15791619
char *msg // LAGraph output message
15801620
)
15811621
{
1582-
return LAGraph_2Rpq(paths, path_count, R, inverse_labels, nl, QS, nqs, QF, nqf, G, S, ns, inverse, limit, msg, extend_multiple_paths) ;
1622+
return LAGraph_2Rpq(paths, path_count, R, inverse_labels, nl, QS, nqs, QF, nqf, G, S, ns, inverse, false, limit, msg, extend_multiple_paths) ;
1623+
}
1624+
1625+
int LAGraph_2Rpq_AllShortestPaths // All shortest paths satisfying regular expression
1626+
(
1627+
// output:
1628+
Path **paths, // paths from one of the starting nodes
1629+
// satisfying regular constraints
1630+
size_t *path_count, // resulting path count
1631+
// input:
1632+
LAGraph_Graph *R, // input non-deterministic finite automaton
1633+
// adjacency matrix decomposition
1634+
bool *inverse_labels, // inversed labels
1635+
size_t nl, // total label count, # of matrices graph and
1636+
// NFA adjacency matrix decomposition
1637+
const GrB_Index *QS, // starting states in NFA
1638+
size_t nqs, // number of starting states in NFA
1639+
const GrB_Index *QF, // final states in NFA
1640+
size_t nqf, // number of final states in NFA
1641+
LAGraph_Graph *G, // input graph adjacency matrix decomposition
1642+
const GrB_Index *S, // source vertices to start searching paths
1643+
size_t ns, // number of source vertices
1644+
bool inverse, // inverse the whole query
1645+
char *msg // LAGraph output message
1646+
)
1647+
{
1648+
return LAGraph_2Rpq(paths, path_count, R, inverse_labels, nl, QS, nqs, QF, nqf, G, S, ns, inverse, true, ULLONG_MAX, msg, extend_multiple_paths) ;
15831649
}
15841650

15851651
// Required because returned Path objects may own heap-allocated PathExtra.

experimental/test/test_2Rpq.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,26 @@ void test_Rpq_Simple (void)
225225
}
226226
printf("\n");
227227

228+
// Cleanup
229+
OK (LAGraph_Free ((void **) &paths, NULL)) ;
230+
231+
res = LAGraph_2Rpq_AllShortestPaths (&paths, &path_count, R, inverse_labels,
232+
MAX_LABELS, QS, nqs, QF, nqf, G, S, ns,
233+
inverse, msg) ;
234+
235+
// Compare the results with expected values
236+
//TEST_CHECK (nvals == files[k].expected_count) ;
237+
//for (uint64_t i = 0 ; i < nvals ; i++)
238+
// TEST_CHECK (reachable[i] + 1 == files[k].expected[i]) ;
239+
240+
printf("ALL SHORTEST PATHS:\n");
241+
for (size_t i = 0 ; i < path_count ; i++)
242+
{
243+
Path_print (&paths[i]);
244+
}
245+
printf("\n");
246+
247+
// Cleanup
228248
OK (LAGraph_Free ((void **) &paths, NULL)) ;
229249

230250
for (uint64_t i = 0 ; i < MAX_LABELS ; i++)

include/LAGraphX.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,30 @@ int LAGraph_2Rpq_AllPaths // All paths satisfying regular expression
903903
uint64_t limit, // maximum path count
904904
char *msg // LAGraph output message
905905
);
906+
907+
LAGRAPHX_PUBLIC
908+
int LAGraph_2Rpq_AllShortestPaths // All shortest paths satisfying regular expression
909+
(
910+
// output:
911+
Path **paths, // paths from one of the starting nodes
912+
// satisfying regular constraints
913+
size_t *path_count, // resulting path count
914+
// input:
915+
LAGraph_Graph *R, // input non-deterministic finite automaton
916+
// adjacency matrix decomposition
917+
bool *inverse_labels, // inversed labels
918+
size_t nl, // total label count, # of matrices graph and
919+
// NFA adjacency matrix decomposition
920+
const GrB_Index *QS, // starting states in NFA
921+
size_t nqs, // number of starting states in NFA
922+
const GrB_Index *QF, // final states in NFA
923+
size_t nqf, // number of final states in NFA
924+
LAGraph_Graph *G, // input graph adjacency matrix decomposition
925+
const GrB_Index *S, // source vertices to start searching paths
926+
size_t ns, // number of source vertices
927+
bool inverse, // inverse the whole query
928+
char *msg // LAGraph output message
929+
);
906930
//****************************************************************************
907931
LAGRAPHX_PUBLIC
908932
int LAGraph_VertexCentrality_Triangle // vertex triangle-centrality

0 commit comments

Comments
 (0)