From e98adda012be316e2021b3988a0949fca382912b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Di=C3=B3genes=20Andrade?= Date: Wed, 4 Jan 2023 00:48:49 +0100 Subject: [PATCH 1/9] Algorithm for finding the longest path of a DAG --- docs/src/algorithms/longestpaths.md | 20 ++++++ src/Graphs.jl | 6 +- src/longestpaths/longest_path.jl | 95 +++++++++++++++++++++++++++++ test/longestpaths/longest_path.jl | 24 ++++++++ 4 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 docs/src/algorithms/longestpaths.md create mode 100644 src/longestpaths/longest_path.jl create mode 100644 test/longestpaths/longest_path.jl diff --git a/docs/src/algorithms/longestpaths.md b/docs/src/algorithms/longestpaths.md new file mode 100644 index 000000000..88ab4611e --- /dev/null +++ b/docs/src/algorithms/longestpaths.md @@ -0,0 +1,20 @@ +# Shortest paths + +*Graphs.jl* includes algorithm for [longest paths](https://en.wikipedia.org/wiki/Longest_path_problem#Acyclic_graphs) in DAGs. + +## Index + +```@index +Pages = ["longestpaths.md"] +``` + +## Full docs + +```@autodocs +Modules = [Graphs] +Pages = [ + "longestpaths/longest_path.jl", +] + +``` + diff --git a/src/Graphs.jl b/src/Graphs.jl index 171204112..7571aef61 100644 --- a/src/Graphs.jl +++ b/src/Graphs.jl @@ -413,7 +413,10 @@ export independent_set, # vertexcover - vertex_cover + vertex_cover, + + # longestpaths + dag_longest_path """ Graphs @@ -526,6 +529,7 @@ include("independentset/degree_ind_set.jl") include("independentset/maximal_ind_set.jl") include("vertexcover/degree_vertex_cover.jl") include("vertexcover/random_vertex_cover.jl") +include("longestpaths/longest_path.jl") include("Experimental/Experimental.jl") include("Parallel/Parallel.jl") diff --git a/src/longestpaths/longest_path.jl b/src/longestpaths/longest_path.jl new file mode 100644 index 000000000..d09928ff9 --- /dev/null +++ b/src/longestpaths/longest_path.jl @@ -0,0 +1,95 @@ +""" + dag_longest_path(g; distmx=weights(g), topological_order=topological_sort_by_dfs(g)) + +DAG longest path algorithm. +Return a longest path of `g`. +The parameter `distmx` defines the distance matrix, while +the parameter `topological_order` defines a topological order of the `vertices(g)`. + +# Examples +```jldoctest +julia> using Graphs + +julia> g = random_orientation_dag(complete_graph(10)) +julia> dag_longest_path(g) +10-element Vector{Int64}: + 9 + 1 + 5 + 2 + 4 + 6 + 3 + 10 + 8 + 7 + +julia> dag_longest_path(g, topological_order = topological_sort_by_dfs(g)) +10-element Vector{Int64}: + 9 + 1 + 5 + 2 + 4 + 6 + 3 + 10 + 8 + 7 + +julia> dag_longest_path(g, distmx = weights(g), topological_order = topological_sort_by_dfs(g)) +10-element Vector{Int64}: + 9 + 1 + 5 + 2 + 4 + 6 + 3 + 10 + 8 + 7 +``` +""" +function dag_longest_path end + +function dag_longest_path( + g::SimpleDiGraph{U}; + distmx::AbstractMatrix{T} = weights(g), + topological_order::Vector{U} = topological_sort_by_dfs(g) + ) where {T<:Real} where {U<:Integer} + n::Int = nv(g) + path::Vector{U} = Vector{U}() + + # edge case + n == 0 && return path + + dist::Vector{U} = Vector{U}(zeros(n)) + pred::Vector{U} = Vector{U}(1:n) + + # get distances + for v in topological_order + for u in inneighbors(g, v) + + newdist::U = dist[u] + distmx[u, v] + + if newdist > dist[v] + dist[v] = newdist + pred[v] = u + end + + end + end + + # retrieve path + v = argmax(x -> dist[x], vertices(g)) + push!(path, v) + + while pred[v] != v + v = pred[v] + push!(path, v) + end + + # return + return reverse(path) +end diff --git a/test/longestpaths/longest_path.jl b/test/longestpaths/longest_path.jl new file mode 100644 index 000000000..ab49e4c77 --- /dev/null +++ b/test/longestpaths/longest_path.jl @@ -0,0 +1,24 @@ +@testset "Longest path" begin + # empty DAG + g::DiGraph = DiGraph() + @test dag_longest_path(g) == Vector{Int}() + + # unweighted DAG + g = DiGraph() + V::Vector{Int} = Vector{Int}(1:7) + A = Vector{Tuple{Int, Int}}([(1, 2), (2, 3), (2, 4), (3, 5), (5, 6), (3, 7)]) + [add_vertex!(g) for _ in V] + [add_edge!(g, edge) for edge in A] + @test dag_longest_path(g) == Vector{Int}([1, 2, 3, 5, 6]) + + # weighted DAG + g = DiGraph() + V = Vector{Int}(1:6) + A = Vector{Tuple{Int, Int, Float64}}([(1, 2, -5), (2, 3, 1), (3, 4, 1), (4, 5, 0), (3, 5, 4), (1, 6, 2)]) + n::Int = length(V) + distmx::Matrix{Float64} = Matrix{Float64}(undef, n, n) + [(distmx[i, j] = dist) for (i, j, dist) in A] + [add_vertex!(g) for _ in V] + [add_edge!(g, (i, j)) for (i, j, _) in A] + @test dag_longest_path(g; distmx = distmx) == Vector{Int}([2, 3, 5]) +end From af7cd2a11bbda38bcd33bcf009cef9353bd0c950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Di=C3=B3genes=20Andrade?= Date: Wed, 4 Jan 2023 20:56:18 +0100 Subject: [PATCH 2/9] Automated test issue --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 786bdb949..4cc782cb3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -21,7 +21,7 @@ const testdir = dirname(@__FILE__) end @testset verbose = true "Code formatting (JuliaFormatter.jl)" begin - @test format(Graphs; verbose=false, overwrite=false, ignore=["vf2.jl"]) # TODO: remove ignore kwarg once the file is formatted correctly + @test format(Graphs; verbose=false, overwrite=false, ignore="vf2.jl") # TODO: remove ignore kwarg once the file is formatted correctly end @testset verbose = true "Doctests (Documenter.jl)" begin From e2f128c8575550c2c3f06c907499d1b14ec92ea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Di=C3=B3genes=20Andrade?= Date: Thu, 5 Jan 2023 11:00:21 +0100 Subject: [PATCH 3/9] Including longest path test --- test/runtests.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/runtests.jl b/test/runtests.jl index 4cc782cb3..e54eb564b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -116,6 +116,7 @@ tests = [ "independentset/maximal_ind_set", "vertexcover/degree_vertex_cover", "vertexcover/random_vertex_cover", + "longestpaths/longest_path.jl", "experimental/experimental", ] From 9b13d0a2468435861e175a94bc93e9e1af419065 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Di=C3=B3genes=20Andrade?= Date: Thu, 5 Jan 2023 11:41:17 +0100 Subject: [PATCH 4/9] Test fix --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index e54eb564b..0522e2d21 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -116,7 +116,7 @@ tests = [ "independentset/maximal_ind_set", "vertexcover/degree_vertex_cover", "vertexcover/random_vertex_cover", - "longestpaths/longest_path.jl", + "longestpaths/longest_path", "experimental/experimental", ] From 4b782a010c2810ab95df36dca63988e89a456076 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Thu, 21 Dec 2023 09:44:07 +0100 Subject: [PATCH 5/9] Simplify --- src/longestpaths/longest_path.jl | 62 ++++++++----------------------- test/longestpaths/longest_path.jl | 34 ++++++++--------- 2 files changed, 33 insertions(+), 63 deletions(-) diff --git a/src/longestpaths/longest_path.jl b/src/longestpaths/longest_path.jl index d09928ff9..b73f01a1e 100644 --- a/src/longestpaths/longest_path.jl +++ b/src/longestpaths/longest_path.jl @@ -1,43 +1,17 @@ """ dag_longest_path(g; distmx=weights(g), topological_order=topological_sort_by_dfs(g)) -DAG longest path algorithm. -Return a longest path of `g`. -The parameter `distmx` defines the distance matrix, while -the parameter `topological_order` defines a topological order of the `vertices(g)`. +Find a longest path in a directed acyclic graph `g`. + +The parameter `distmx` defines the distance matrix, while the parameter `topological_order` defines a topological order of the `vertices(g)`. # Examples ```jldoctest julia> using Graphs -julia> g = random_orientation_dag(complete_graph(10)) -julia> dag_longest_path(g) -10-element Vector{Int64}: - 9 - 1 - 5 - 2 - 4 - 6 - 3 - 10 - 8 - 7 - -julia> dag_longest_path(g, topological_order = topological_sort_by_dfs(g)) -10-element Vector{Int64}: - 9 - 1 - 5 - 2 - 4 - 6 - 3 - 10 - 8 - 7 +julia> g = random_orientation_dag(complete_graph(10)); -julia> dag_longest_path(g, distmx = weights(g), topological_order = topological_sort_by_dfs(g)) +julia> dag_longest_path(g) 10-element Vector{Int64}: 9 1 @@ -53,38 +27,34 @@ julia> dag_longest_path(g, distmx = weights(g), topological_order = topological_ """ function dag_longest_path end -function dag_longest_path( - g::SimpleDiGraph{U}; - distmx::AbstractMatrix{T} = weights(g), - topological_order::Vector{U} = topological_sort_by_dfs(g) - ) where {T<:Real} where {U<:Integer} - n::Int = nv(g) - path::Vector{U} = Vector{U}() +@traitfn function dag_longest_path( + g::AG::IsDirected; + distmx::AbstractMatrix{T}=weights(g), + topological_order::Vector{U}=topological_sort_by_dfs(g), +) where {T<:Real,U<:Integer,AG<:AbstractGraph{U}} + n = nv(g) + path = U[] # edge case n == 0 && return path - dist::Vector{U} = Vector{U}(zeros(n)) - pred::Vector{U} = Vector{U}(1:n) + dist = zeros(T, n) + pred = collect(vertices(g)) # get distances for v in topological_order for u in inneighbors(g, v) - - newdist::U = dist[u] + distmx[u, v] - + newdist = dist[u] + distmx[u, v] if newdist > dist[v] dist[v] = newdist pred[v] = u end - end end # retrieve path - v = argmax(x -> dist[x], vertices(g)) + v = argmax(dist) push!(path, v) - while pred[v] != v v = pred[v] push!(path, v) diff --git a/test/longestpaths/longest_path.jl b/test/longestpaths/longest_path.jl index ab49e4c77..c2bc0d464 100644 --- a/test/longestpaths/longest_path.jl +++ b/test/longestpaths/longest_path.jl @@ -1,24 +1,24 @@ @testset "Longest path" begin # empty DAG - g::DiGraph = DiGraph() - @test dag_longest_path(g) == Vector{Int}() + g = DiGraph() + @test dag_longest_path(g) == Int[] # unweighted DAG - g = DiGraph() - V::Vector{Int} = Vector{Int}(1:7) - A = Vector{Tuple{Int, Int}}([(1, 2), (2, 3), (2, 4), (3, 5), (5, 6), (3, 7)]) - [add_vertex!(g) for _ in V] - [add_edge!(g, edge) for edge in A] - @test dag_longest_path(g) == Vector{Int}([1, 2, 3, 5, 6]) + g = DiGraph(7) + A = [(1, 2), (2, 3), (2, 4), (3, 5), (5, 6), (3, 7)] + for (i, j) in A + add_edge!(g, i, j) + end + @test dag_longest_path(g) == [1, 2, 3, 5, 6] # weighted DAG - g = DiGraph() - V = Vector{Int}(1:6) - A = Vector{Tuple{Int, Int, Float64}}([(1, 2, -5), (2, 3, 1), (3, 4, 1), (4, 5, 0), (3, 5, 4), (1, 6, 2)]) - n::Int = length(V) - distmx::Matrix{Float64} = Matrix{Float64}(undef, n, n) - [(distmx[i, j] = dist) for (i, j, dist) in A] - [add_vertex!(g) for _ in V] - [add_edge!(g, (i, j)) for (i, j, _) in A] - @test dag_longest_path(g; distmx = distmx) == Vector{Int}([2, 3, 5]) + 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=distmx) == [2, 3, 5] end From 18f2ed21a37372dc091a9998487889e8c0b886a7 Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Thu, 21 Dec 2023 09:53:26 +0100 Subject: [PATCH 6/9] Fix JET warning --- src/longestpaths/longest_path.jl | 9 +++++---- test/longestpaths/longest_path.jl | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/longestpaths/longest_path.jl b/src/longestpaths/longest_path.jl index b73f01a1e..b7ee1137e 100644 --- a/src/longestpaths/longest_path.jl +++ b/src/longestpaths/longest_path.jl @@ -28,10 +28,11 @@ julia> dag_longest_path(g) function dag_longest_path end @traitfn function dag_longest_path( - g::AG::IsDirected; - distmx::AbstractMatrix{T}=weights(g), - topological_order::Vector{U}=topological_sort_by_dfs(g), -) where {T<:Real,U<:Integer,AG<:AbstractGraph{U}} + g::::IsDirected, + distmx::AbstractMatrix{T}=weights(g); + topological_order=topological_sort_by_dfs(g), +) where {T<:Real} + U = eltype(g) n = nv(g) path = U[] diff --git a/test/longestpaths/longest_path.jl b/test/longestpaths/longest_path.jl index c2bc0d464..df33925d9 100644 --- a/test/longestpaths/longest_path.jl +++ b/test/longestpaths/longest_path.jl @@ -20,5 +20,5 @@ add_edge!(g, (i, j)) distmx[i, j] = dist end - @test dag_longest_path(g; distmx=distmx) == [2, 3, 5] + @test dag_longest_path(g, distmx) == [2, 3, 5] end From bc67886ec2d9767fc59c608048164edd9df5c5a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Di=C3=B3genes=20Andrade?= Date: Thu, 21 Dec 2023 11:19:36 +0100 Subject: [PATCH 7/9] Fixing doctest --- src/longestpaths/longest_path.jl | 71 ++++++++++++++++++++++--------- test/longestpaths/longest_path.jl | 2 +- 2 files changed, 52 insertions(+), 21 deletions(-) diff --git a/src/longestpaths/longest_path.jl b/src/longestpaths/longest_path.jl index b7ee1137e..625552aea 100644 --- a/src/longestpaths/longest_path.jl +++ b/src/longestpaths/longest_path.jl @@ -1,61 +1,92 @@ """ dag_longest_path(g; distmx=weights(g), topological_order=topological_sort_by_dfs(g)) -Find a longest path in a directed acyclic graph `g`. - -The parameter `distmx` defines the distance matrix, while the parameter `topological_order` defines a topological order of the `vertices(g)`. +DAG longest path algorithm. +Return a longest path of `g`. +The parameter `distmx` defines the distance matrix, while +the parameter `topological_order` defines a topological order of the `vertices(g)`. # Examples ```jldoctest julia> using Graphs -julia> g = random_orientation_dag(complete_graph(10)); +julia> g = random_orientation_dag(complete_graph(10); seed = 1) +{10, 45} directed simple Int64 graph julia> dag_longest_path(g) 10-element Vector{Int64}: - 9 - 1 - 5 + 10 + 6 2 + 9 + 7 + 8 + 3 4 + 5 + 1 + +julia> dag_longest_path(g, topological_order = topological_sort_by_dfs(g)) +10-element Vector{Int64}: + 10 6 + 2 + 9 + 7 + 8 3 + 4 + 5 + 1 + +julia> dag_longest_path(g, distmx = weights(g), topological_order = topological_sort_by_dfs(g)) +10-element Vector{Int64}: 10 - 8 + 6 + 2 + 9 7 + 8 + 3 + 4 + 5 + 1 ``` """ function dag_longest_path end -@traitfn function dag_longest_path( - g::::IsDirected, - distmx::AbstractMatrix{T}=weights(g); - topological_order=topological_sort_by_dfs(g), -) where {T<:Real} - U = eltype(g) - n = nv(g) - path = U[] +function dag_longest_path( + g::SimpleDiGraph{U}; + distmx::AbstractMatrix{T} = weights(g), + topological_order::Vector{U} = topological_sort_by_dfs(g) + ) where {T<:Real} where {U<:Integer} + n::Int = nv(g) + path::Vector{U} = Vector{U}() # edge case n == 0 && return path - dist = zeros(T, n) - pred = collect(vertices(g)) + dist::Vector{U} = Vector{U}(zeros(n)) + pred::Vector{U} = Vector{U}(1:n) # get distances for v in topological_order for u in inneighbors(g, v) - newdist = dist[u] + distmx[u, v] + + newdist::U = dist[u] + distmx[u, v] + if newdist > dist[v] dist[v] = newdist pred[v] = u end + end end # retrieve path - v = argmax(dist) + v = argmax(x -> dist[x], vertices(g)) push!(path, v) + while pred[v] != v v = pred[v] push!(path, v) diff --git a/test/longestpaths/longest_path.jl b/test/longestpaths/longest_path.jl index df33925d9..d5dfc6a49 100644 --- a/test/longestpaths/longest_path.jl +++ b/test/longestpaths/longest_path.jl @@ -20,5 +20,5 @@ add_edge!(g, (i, j)) distmx[i, j] = dist end - @test dag_longest_path(g, distmx) == [2, 3, 5] + @test dag_longest_path(g, distmx = distmx) == [2, 3, 5] end From 432b163eb16bedca747ae10a0ca726cf06d4a925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Di=C3=B3genes=20Andrade?= Date: Thu, 21 Dec 2023 14:02:36 +0100 Subject: [PATCH 8/9] Adjustments --- src/longestpaths/longest_path.jl | 16 +++++++++------- test/longestpaths/longest_path.jl | 7 ++++++- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/longestpaths/longest_path.jl b/src/longestpaths/longest_path.jl index 625552aea..d1f7019da 100644 --- a/src/longestpaths/longest_path.jl +++ b/src/longestpaths/longest_path.jl @@ -1,5 +1,5 @@ """ - dag_longest_path(g; distmx=weights(g), topological_order=topological_sort_by_dfs(g)) + dag_longest_path(g, distmx=weights(g); topological_order=topological_sort_by_dfs(g)) DAG longest path algorithm. Return a longest path of `g`. @@ -39,7 +39,7 @@ julia> dag_longest_path(g, topological_order = topological_sort_by_dfs(g)) 5 1 -julia> dag_longest_path(g, distmx = weights(g), topological_order = topological_sort_by_dfs(g)) +julia> dag_longest_path(g, weights(g); topological_order = topological_sort_by_dfs(g)) 10-element Vector{Int64}: 10 6 @@ -51,15 +51,17 @@ julia> dag_longest_path(g, distmx = weights(g), topological_order = topological_ 4 5 1 + ``` """ function dag_longest_path end -function dag_longest_path( - g::SimpleDiGraph{U}; - distmx::AbstractMatrix{T} = weights(g), - topological_order::Vector{U} = topological_sort_by_dfs(g) - ) where {T<:Real} where {U<:Integer} +@traitfn function dag_longest_path( + g::::IsDirected, + distmx::AbstractMatrix{T}=weights(g); + topological_order=topological_sort_by_dfs(g), + ) where {T<:Real} + U = eltype(g) n::Int = nv(g) path::Vector{U} = Vector{U}() diff --git a/test/longestpaths/longest_path.jl b/test/longestpaths/longest_path.jl index d5dfc6a49..914d88f64 100644 --- a/test/longestpaths/longest_path.jl +++ b/test/longestpaths/longest_path.jl @@ -11,6 +11,8 @@ end @test dag_longest_path(g) == [1, 2, 3, 5, 6] + @test dag_longest_path(g; topological_order = topological_sort_by_dfs(g)) == [1, 2, 3, 5, 6] + # weighted DAG n = 6 g = DiGraph(n) @@ -20,5 +22,8 @@ add_edge!(g, (i, j)) distmx[i, j] = dist end - @test dag_longest_path(g, distmx = distmx) == [2, 3, 5] + @test dag_longest_path(g, distmx) == [2, 3, 5] + + @test dag_longest_path(g, distmx; topological_order = topological_sort_by_dfs(g)) == [2, 3, 5] + end From 989b819d1c109040b2602b16456433e21382f6fe Mon Sep 17 00:00:00 2001 From: Guillaume Dalle <22795598+gdalle@users.noreply.github.com> Date: Tue, 5 Mar 2024 10:54:07 +0100 Subject: [PATCH 9/9] Simplify --- docs/src/algorithms/longestpaths.md | 20 ---- docs/src/algorithms/shortestpaths.md | 3 +- src/Graphs.jl | 3 +- src/longestpaths/longest_path.jl | 99 ------------------- src/shortestpaths/longest_path.jl | 35 +++++++ src/shortestpaths/utils.jl | 9 ++ test/runtests.jl | 3 +- .../longest_path.jl | 11 +-- test/shortestpaths/utils.jl | 9 ++ 9 files changed, 60 insertions(+), 132 deletions(-) delete mode 100644 docs/src/algorithms/longestpaths.md delete mode 100644 src/longestpaths/longest_path.jl create mode 100644 src/shortestpaths/longest_path.jl create mode 100644 src/shortestpaths/utils.jl rename test/{longestpaths => shortestpaths}/longest_path.jl (58%) create mode 100644 test/shortestpaths/utils.jl diff --git a/docs/src/algorithms/longestpaths.md b/docs/src/algorithms/longestpaths.md deleted file mode 100644 index 88ab4611e..000000000 --- a/docs/src/algorithms/longestpaths.md +++ /dev/null @@ -1,20 +0,0 @@ -# Shortest paths - -*Graphs.jl* includes algorithm for [longest paths](https://en.wikipedia.org/wiki/Longest_path_problem#Acyclic_graphs) in DAGs. - -## Index - -```@index -Pages = ["longestpaths.md"] -``` - -## Full docs - -```@autodocs -Modules = [Graphs] -Pages = [ - "longestpaths/longest_path.jl", -] - -``` - diff --git a/docs/src/algorithms/shortestpaths.md b/docs/src/algorithms/shortestpaths.md index 8db4ad3d9..4c5ef5692 100644 --- a/docs/src/algorithms/shortestpaths.md +++ b/docs/src/algorithms/shortestpaths.md @@ -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 @@ -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", ] diff --git a/src/Graphs.jl b/src/Graphs.jl index 34def755a..675db9eef 100644 --- a/src/Graphs.jl +++ b/src/Graphs.jl @@ -499,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") @@ -507,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") @@ -543,7 +545,6 @@ include("independentset/degree_ind_set.jl") include("independentset/maximal_ind_set.jl") include("vertexcover/degree_vertex_cover.jl") include("vertexcover/random_vertex_cover.jl") -include("longestpaths/longest_path.jl") include("Experimental/Experimental.jl") include("Parallel/Parallel.jl") include("Test/Test.jl") diff --git a/src/longestpaths/longest_path.jl b/src/longestpaths/longest_path.jl deleted file mode 100644 index d1f7019da..000000000 --- a/src/longestpaths/longest_path.jl +++ /dev/null @@ -1,99 +0,0 @@ -""" - dag_longest_path(g, distmx=weights(g); topological_order=topological_sort_by_dfs(g)) - -DAG longest path algorithm. -Return a longest path of `g`. -The parameter `distmx` defines the distance matrix, while -the parameter `topological_order` defines a topological order of the `vertices(g)`. - -# Examples -```jldoctest -julia> using Graphs - -julia> g = random_orientation_dag(complete_graph(10); seed = 1) -{10, 45} directed simple Int64 graph - -julia> dag_longest_path(g) -10-element Vector{Int64}: - 10 - 6 - 2 - 9 - 7 - 8 - 3 - 4 - 5 - 1 - -julia> dag_longest_path(g, topological_order = topological_sort_by_dfs(g)) -10-element Vector{Int64}: - 10 - 6 - 2 - 9 - 7 - 8 - 3 - 4 - 5 - 1 - -julia> dag_longest_path(g, weights(g); topological_order = topological_sort_by_dfs(g)) -10-element Vector{Int64}: - 10 - 6 - 2 - 9 - 7 - 8 - 3 - 4 - 5 - 1 - -``` -""" -function dag_longest_path end - -@traitfn function dag_longest_path( - g::::IsDirected, - distmx::AbstractMatrix{T}=weights(g); - topological_order=topological_sort_by_dfs(g), - ) where {T<:Real} - U = eltype(g) - n::Int = nv(g) - path::Vector{U} = Vector{U}() - - # edge case - n == 0 && return path - - dist::Vector{U} = Vector{U}(zeros(n)) - pred::Vector{U} = Vector{U}(1:n) - - # get distances - for v in topological_order - for u in inneighbors(g, v) - - newdist::U = dist[u] + distmx[u, v] - - if newdist > dist[v] - dist[v] = newdist - pred[v] = u - end - - end - end - - # retrieve path - v = argmax(x -> dist[x], vertices(g)) - push!(path, v) - - while pred[v] != v - v = pred[v] - push!(path, v) - end - - # return - return reverse(path) -end diff --git a/src/shortestpaths/longest_path.jl b/src/shortestpaths/longest_path.jl new file mode 100644 index 000000000..b610102ab --- /dev/null +++ b/src/shortestpaths/longest_path.jl @@ -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 diff --git a/src/shortestpaths/utils.jl b/src/shortestpaths/utils.jl new file mode 100644 index 000000000..585e4ad96 --- /dev/null +++ b/src/shortestpaths/utils.jl @@ -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 diff --git a/test/runtests.jl b/test/runtests.jl index 7d663bed8..659ef9940 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -103,6 +103,7 @@ tests = [ "edit_distance", "connectivity", "persistence/persistence", + "shortestpaths/utils", "shortestpaths/astar", "shortestpaths/bellman-ford", "shortestpaths/desopo-pape", @@ -111,6 +112,7 @@ tests = [ "shortestpaths/floyd-warshall", "shortestpaths/yen", "shortestpaths/spfa", + "shortestpaths/longest_path", "traversals/bfs", "traversals/bipartition", "traversals/greedy_color", @@ -152,7 +154,6 @@ tests = [ "independentset/maximal_ind_set", "vertexcover/degree_vertex_cover", "vertexcover/random_vertex_cover", - "longestpaths/longest_path", "trees/prufer", "experimental/experimental", ] diff --git a/test/longestpaths/longest_path.jl b/test/shortestpaths/longest_path.jl similarity index 58% rename from test/longestpaths/longest_path.jl rename to test/shortestpaths/longest_path.jl index 914d88f64..29a99c933 100644 --- a/test/longestpaths/longest_path.jl +++ b/test/shortestpaths/longest_path.jl @@ -4,15 +4,9 @@ @test dag_longest_path(g) == Int[] # unweighted DAG - g = DiGraph(7) - A = [(1, 2), (2, 3), (2, 4), (3, 5), (5, 6), (3, 7)] - for (i, j) in A - add_edge!(g, i, j) - end + 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] - @test dag_longest_path(g; topological_order = topological_sort_by_dfs(g)) == [1, 2, 3, 5, 6] - # weighted DAG n = 6 g = DiGraph(n) @@ -23,7 +17,4 @@ distmx[i, j] = dist end @test dag_longest_path(g, distmx) == [2, 3, 5] - - @test dag_longest_path(g, distmx; topological_order = topological_sort_by_dfs(g)) == [2, 3, 5] - end diff --git a/test/shortestpaths/utils.jl b/test/shortestpaths/utils.jl new file mode 100644 index 000000000..adfe54ea4 --- /dev/null +++ b/test/shortestpaths/utils.jl @@ -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