Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Algorithm for finding the longest path of a DAG #209

Merged
merged 12 commits into from
Mar 5, 2024
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 @@ -103,6 +103,7 @@ tests = [
"edit_distance",
"connectivity",
"persistence/persistence",
"shortestpaths/utils",
"shortestpaths/astar",
"shortestpaths/bellman-ford",
"shortestpaths/desopo-pape",
Expand All @@ -111,6 +112,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
Loading