Skip to content

Commit

Permalink
Algorithm for finding the longest path of a DAG (#209)
Browse files Browse the repository at this point in the history
* Algorithm for finding the longest path of a DAG

* Automated test issue

* Including longest path test

* Test fix

* Simplify

* Fix JET warning

* Fixing doctest

* Adjustments

* Simplify

---------

Co-authored-by: Matheus Diógenes Andrade <[email protected]>
Co-authored-by: Guillaume Dalle <[email protected]>
  • Loading branch information
3 people authored Mar 5, 2024
1 parent 7133460 commit d1081b0
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 2 deletions.
3 changes: 2 additions & 1 deletion docs/src/algorithms/shortestpaths.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Shortest paths

*Graphs.jl* includes standard algorithms for [shortest paths](https://en.wikipedia.org/wiki/Shortest_path_problem).
*Graphs.jl* includes standard algorithms for [shortest paths](https://en.wikipedia.org/wiki/Shortest_path_problem) and longest paths.

## Index

Expand All @@ -19,6 +19,7 @@ Pages = [
"shortestpaths/dijkstra.jl",
"shortestpaths/floyd-warshall.jl",
"shortestpaths/johnson.jl",
"shortestpaths/longest_path.jl",
"shortestpaths/spfa.jl",
"shortestpaths/yen.jl",
]
Expand Down
7 changes: 6 additions & 1 deletion src/Graphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,10 @@ export
independent_set,

# vertexcover
vertex_cover
vertex_cover,

# longestpaths
dag_longest_path

"""
Graphs
Expand Down Expand Up @@ -496,6 +499,7 @@ include("traversals/eulerian.jl")
include("connectivity.jl")
include("distance.jl")
include("editdist.jl")
include("shortestpaths/utils.jl")
include("shortestpaths/astar.jl")
include("shortestpaths/bellman-ford.jl")
include("shortestpaths/dijkstra.jl")
Expand All @@ -504,6 +508,7 @@ include("shortestpaths/desopo-pape.jl")
include("shortestpaths/floyd-warshall.jl")
include("shortestpaths/yen.jl")
include("shortestpaths/spfa.jl")
include("shortestpaths/longest_path.jl")
include("linalg/LinAlg.jl")
include("operators.jl")
include("persistence/common.jl")
Expand Down
35 changes: 35 additions & 0 deletions src/shortestpaths/longest_path.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""
dag_longest_path(g, distmx=weights(g); topological_order=topological_sort_by_dfs(g))
Return a longest path within the directed acyclic graph `g`, with distance matrix `distmx` and using `topological_order` to iterate on vertices.
"""
function dag_longest_path end

@traitfn function dag_longest_path(
g::::IsDirected,
distmx::AbstractMatrix=weights(g);
topological_order=topological_sort_by_dfs(g),
)
U = eltype(g)
T = eltype(distmx)

dists = zeros(T, nv(g))
parents = zeros(U, nv(g))

for v in topological_order
for u in inneighbors(g, v)
newdist = dists[u] + distmx[u, v]
if newdist > dists[v]
dists[v] = newdist
parents[v] = u
end
end
end

if isempty(dists)
return U[]
else
v = argmax(dists)
return path_from_parents(v, parents)
end
end
9 changes: 9 additions & 0 deletions src/shortestpaths/utils.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function path_from_parents(target::Integer, parents::AbstractVector)
v = target
path = [v]
while parents[v] != v && parents[v] != zero(v)
v = parents[v]
pushfirst!(path, v)
end
return path
end
2 changes: 2 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ tests = [
"edit_distance",
"connectivity",
"persistence/persistence",
"shortestpaths/utils",
"shortestpaths/astar",
"shortestpaths/bellman-ford",
"shortestpaths/desopo-pape",
Expand All @@ -99,6 +100,7 @@ tests = [
"shortestpaths/floyd-warshall",
"shortestpaths/yen",
"shortestpaths/spfa",
"shortestpaths/longest_path",
"traversals/bfs",
"traversals/bipartition",
"traversals/greedy_color",
Expand Down
20 changes: 20 additions & 0 deletions test/shortestpaths/longest_path.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@testset "Longest path" begin
# empty DAG
g = DiGraph()
@test dag_longest_path(g) == Int[]

# unweighted DAG
g = SimpleDiGraphFromIterator(Edge.([(1, 2), (2, 3), (2, 4), (3, 5), (5, 6), (3, 7)]))
@test dag_longest_path(g) == [1, 2, 3, 5, 6]

# weighted DAG
n = 6
g = DiGraph(n)
A = [(1, 2, -5), (2, 3, 1), (3, 4, 1), (4, 5, 0), (3, 5, 4), (1, 6, 2)]
distmx = fill(NaN, n, n)
for (i, j, dist) in A
add_edge!(g, (i, j))
distmx[i, j] = dist
end
@test dag_longest_path(g, distmx) == [2, 3, 5]
end
9 changes: 9 additions & 0 deletions test/shortestpaths/utils.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@testset "Path from parents" begin
using Graphs: path_from_parents
parents = [3, 0, 2, 5, 5]
@test path_from_parents(1, parents) == [2, 3, 1]
@test path_from_parents(2, parents) == [2]
@test path_from_parents(3, parents) == [2, 3]
@test path_from_parents(4, parents) == [5, 4]
@test path_from_parents(5, parents) == [5]
end

0 comments on commit d1081b0

Please sign in to comment.