From d499c7e1f4b9be2c2983302ec7f15e78ca920390 Mon Sep 17 00:00:00 2001 From: Filippos Christou Date: Mon, 27 Jan 2025 13:24:55 +0100 Subject: [PATCH] Unitful support for shortestpath dist --- Project.toml | 3 ++- src/Parallel/distance.jl | 2 +- src/Parallel/shortestpaths/bellman-ford.jl | 6 +++--- src/Parallel/shortestpaths/dijkstra.jl | 4 ++-- src/Parallel/shortestpaths/floyd-warshall.jl | 4 ++-- src/Parallel/shortestpaths/johnson.jl | 2 +- src/cycles/karp.jl | 2 +- src/distance.jl | 2 +- src/graphcut/normalized_cut.jl | 2 +- src/operators.jl | 2 +- src/shortestpaths/bellman-ford.jl | 8 ++++---- src/shortestpaths/desopo-pape.jl | 4 ++-- src/shortestpaths/dijkstra.jl | 4 ++-- src/shortestpaths/floyd-warshall.jl | 2 +- src/shortestpaths/johnson.jl | 6 +++--- src/shortestpaths/spfa.jl | 4 ++-- src/shortestpaths/yen.jl | 2 +- src/spanningtrees/boruvka.jl | 2 +- src/spanningtrees/kruskal.jl | 2 +- src/spanningtrees/prim.jl | 2 +- src/traversals/maxadjvisit.jl | 4 ++-- test/runtests.jl | 2 ++ test/shortestpaths/yen.jl | 14 ++++++++++++++ 23 files changed, 51 insertions(+), 34 deletions(-) diff --git a/Project.toml b/Project.toml index 045fc8c45..1f44e54c6 100644 --- a/Project.toml +++ b/Project.toml @@ -42,6 +42,7 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" [targets] -test = ["Aqua", "Base64", "DelimitedFiles", "Documenter", "JET", "JuliaFormatter", "LinearAlgebra", "Pkg", "Random", "SparseArrays", "StableRNGs", "Statistics", "Test"] +test = ["Aqua", "Base64", "DelimitedFiles", "Documenter", "JET", "JuliaFormatter", "LinearAlgebra", "Pkg", "Random", "SparseArrays", "StableRNGs", "Statistics", "Test", "Unitful"] diff --git a/src/Parallel/distance.jl b/src/Parallel/distance.jl index e84ec1da4..3145339f3 100644 --- a/src/Parallel/distance.jl +++ b/src/Parallel/distance.jl @@ -2,7 +2,7 @@ function eccentricity( g::AbstractGraph, vs=vertices(g), distmx::AbstractMatrix{T}=weights(g) -) where {T<:Real} +) where {T<:Number} vlen = length(vs) eccs = SharedVector{T}(vlen) @sync @distributed for i in 1:vlen diff --git a/src/Parallel/shortestpaths/bellman-ford.jl b/src/Parallel/shortestpaths/bellman-ford.jl index c37ed5df3..80b38fbd2 100644 --- a/src/Parallel/shortestpaths/bellman-ford.jl +++ b/src/Parallel/shortestpaths/bellman-ford.jl @@ -2,7 +2,7 @@ function bellman_ford_shortest_paths( g::AbstractGraph{U}, sources::AbstractVector{<:Integer}, distmx::AbstractMatrix{T}=weights(g), -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} nvg = nv(g) active = Set{U}() sizehint!(active, nv(g)) @@ -30,7 +30,7 @@ function _loop_body!( dists::Vector{T}, parents::Vector{U}, active::Set{U}, -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} prev_dists = deepcopy(dists) tmp_active = collect(active) @@ -58,7 +58,7 @@ end function has_negative_edge_cycle( g::AbstractGraph{U}, distmx::AbstractMatrix{T} -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} try Parallel.bellman_ford_shortest_paths(g, vertices(g), distmx) catch e diff --git a/src/Parallel/shortestpaths/dijkstra.jl b/src/Parallel/shortestpaths/dijkstra.jl index 39f018b95..70363cf93 100644 --- a/src/Parallel/shortestpaths/dijkstra.jl +++ b/src/Parallel/shortestpaths/dijkstra.jl @@ -3,7 +3,7 @@ An [`AbstractPathState`](@ref) designed for Parallel.dijkstra_shortest_paths calculation. """ -struct MultipleDijkstraState{T<:Real,U<:Integer} <: AbstractPathState +struct MultipleDijkstraState{T<:Number,U<:Integer} <: AbstractPathState dists::Matrix{T} parents::Matrix{U} end @@ -18,7 +18,7 @@ traversal information. """ function dijkstra_shortest_paths( g::AbstractGraph{U}, sources=vertices(g), distmx::AbstractMatrix{T}=weights(g) -) where {T<:Real} where {U} +) where {T<:Number} where {U} n_v = nv(g) r_v = length(sources) diff --git a/src/Parallel/shortestpaths/floyd-warshall.jl b/src/Parallel/shortestpaths/floyd-warshall.jl index 6136006e9..3f7bb0d44 100644 --- a/src/Parallel/shortestpaths/floyd-warshall.jl +++ b/src/Parallel/shortestpaths/floyd-warshall.jl @@ -1,7 +1,7 @@ # Helper function used due to performance bug in @threads. function _loopbody!( pivot::U, nvg::U, dists::Matrix{T}, parents::Matrix{U} -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} # Relax dists[u, v] = min(dists[u, v], dists[u, pivot]+dists[pivot, v]) for all u, v @inbounds @threads for v in one(U):nvg d = dists[pivot, v] @@ -26,7 +26,7 @@ end function floyd_warshall_shortest_paths( g::AbstractGraph{U}, distmx::AbstractMatrix{T}=weights(g) -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} nvg = nv(g) dists = fill(typemax(T), (Int(nvg), Int(nvg))) parents = zeros(U, (Int(nvg), Int(nvg))) diff --git a/src/Parallel/shortestpaths/johnson.jl b/src/Parallel/shortestpaths/johnson.jl index f4fdff2ba..57c5c2511 100644 --- a/src/Parallel/shortestpaths/johnson.jl +++ b/src/Parallel/shortestpaths/johnson.jl @@ -4,7 +4,7 @@ using Compat function johnson_shortest_paths( g::AbstractGraph{U}, distmx::AbstractMatrix{T}=weights(g) -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} nvg = nv(g) type_distmx = typeof(distmx) # Change when parallel implementation of Bellman Ford available diff --git a/src/cycles/karp.jl b/src/cycles/karp.jl index 992e3f44e..66ef5f9bb 100644 --- a/src/cycles/karp.jl +++ b/src/cycles/karp.jl @@ -3,7 +3,7 @@ # Discrete Mathematics, 1978, 23, 309 - 311 function _karp_minimum_cycle_mean( g::AbstractGraph, distmx::AbstractMatrix{T}, component::Vector{U} -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} v2j = Dict{U,Int}() for (j, v) in enumerate(component) v2j[v] = j diff --git a/src/distance.jl b/src/distance.jl index 76afbe290..df2a0d3fe 100644 --- a/src/distance.jl +++ b/src/distance.jl @@ -73,7 +73,7 @@ julia> eccentricity(g, [1; 2], [0 2 0; 0.5 0 0.5; 0 2 0]) """ function eccentricity( g::AbstractGraph, v::Integer, distmx::AbstractMatrix{T}=weights(g) -) where {T<:Real} +) where {T<:Number} e = maximum(dijkstra_shortest_paths(g, v, distmx).dists) e == typemax(T) && @warn("Infinite path length detected for vertex $v") diff --git a/src/graphcut/normalized_cut.jl b/src/graphcut/normalized_cut.jl index 3092ab1a7..33dc496e2 100644 --- a/src/graphcut/normalized_cut.jl +++ b/src/graphcut/normalized_cut.jl @@ -201,6 +201,6 @@ function normalized_cut( thres::Real, W::AbstractMatrix{T}=adjacency_matrix(g), num_cuts::Int=10, -) where {T<:Real} +) where {T<:Number} return _recursive_normalized_cut(W, thres, num_cuts) end diff --git a/src/operators.jl b/src/operators.jl index 9bdd3c6d0..d8aeb2172 100644 --- a/src/operators.jl +++ b/src/operators.jl @@ -405,7 +405,7 @@ end # """Provides multiplication of a graph `g` by a vector `v` such that spectral # graph functions in [GraphMatrices.jl](https://github.com/jpfairbanks/GraphMatrices.jl) can utilize Graphs natively. # """ -function *(g::AbstractGraph, v::Vector{T}) where {T<:Real} +function *(g::AbstractGraph, v::Vector{T}) where {T<:Number} length(v) == nv(g) || throw(ArgumentError("Vector size must equal number of vertices")) y = zeros(T, nv(g)) for e in edges(g) diff --git a/src/shortestpaths/bellman-ford.jl b/src/shortestpaths/bellman-ford.jl index 05fbdcf1a..875d59377 100644 --- a/src/shortestpaths/bellman-ford.jl +++ b/src/shortestpaths/bellman-ford.jl @@ -22,7 +22,7 @@ An `AbstractPathState` designed for Bellman-Ford shortest-paths calculations. - `parents::Vector{U}`: `parents[v]` is the predecessor of vertex `v` on the shortest path from the source to `v` - `dists::Vector{T}`: `dists[v]` is the length of the shortest path from the source to `v` """ -struct BellmanFordState{T<:Real,U<:Integer} <: AbstractPathState +struct BellmanFordState{T<:Number,U<:Integer} <: AbstractPathState parents::Vector{U} dists::Vector{T} end @@ -40,7 +40,7 @@ function bellman_ford_shortest_paths( graph::AbstractGraph{U}, sources::AbstractVector{<:Integer}, distmx::AbstractMatrix{T}=weights(graph), -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} nvg = nv(graph) active = falses(nvg) active[sources] .= true @@ -76,7 +76,7 @@ end function bellman_ford_shortest_paths( graph::AbstractGraph{U}, v::Integer, distmx::AbstractMatrix{T}=weights(graph); -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} return bellman_ford_shortest_paths(graph, [v], distmx) end @@ -84,7 +84,7 @@ has_negative_edge_cycle(g::AbstractGraph) = false function has_negative_edge_cycle( g::AbstractGraph{U}, distmx::AbstractMatrix{T} -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} try bellman_ford_shortest_paths(g, collect_if_not_vector(vertices(g)), distmx) catch e diff --git a/src/shortestpaths/desopo-pape.jl b/src/shortestpaths/desopo-pape.jl index ccda36376..f488a5bc0 100644 --- a/src/shortestpaths/desopo-pape.jl +++ b/src/shortestpaths/desopo-pape.jl @@ -8,7 +8,7 @@ An [`AbstractPathState`](@ref) designed for D`Esopo-Pape shortest-path calculati - `parents::Vector{U}`: `parents[v]` is the predecessor of vertex `v` on the shortest path from the source to `v` - `dists::Vector{T}`: `dists[v]` is the length of the shortest path from the source to `v` """ -struct DEsopoPapeState{T<:Real,U<:Integer} <: AbstractPathState +struct DEsopoPapeState{T<:Number,U<:Integer} <: AbstractPathState parents::Vector{U} dists::Vector{T} end @@ -47,7 +47,7 @@ julia> ds.dists """ function desopo_pape_shortest_paths( g::AbstractGraph, src::Integer, distmx::AbstractMatrix{T}=weights(g) -) where {T<:Real} +) where {T<:Number} U = eltype(g) nvg = nv(g) (src in 1:nvg) || throw(DomainError(src, "src should be in between 1 and $nvg")) diff --git a/src/shortestpaths/dijkstra.jl b/src/shortestpaths/dijkstra.jl index d6b554929..9a8e8911c 100644 --- a/src/shortestpaths/dijkstra.jl +++ b/src/shortestpaths/dijkstra.jl @@ -11,7 +11,7 @@ An [`AbstractPathState`](@ref) designed for Dijkstra shortest-paths calculations - `pathcounts::Vector{Float64}`: a vector, indexed by vertex, of the number of shortest paths from the source to that vertex. The path count of a source vertex is always `1.0`. The path count of an unreached vertex is always `0.0`. - `closest_vertices::Vector{U}`: a vector of all vertices in the graph ordered from closest to farthest. """ -struct DijkstraState{T<:Real,U<:Integer} <: AbstractPathState +struct DijkstraState{T<:Number,U<:Integer} <: AbstractPathState parents::Vector{U} dists::Vector{T} predecessors::Vector{Vector{U}} @@ -73,7 +73,7 @@ function dijkstra_shortest_paths( allpaths=false, trackvertices=false, maxdist=typemax(T), -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} nvg = nv(g) dists = fill(typemax(T), nvg) parents = zeros(U, nvg) diff --git a/src/shortestpaths/floyd-warshall.jl b/src/shortestpaths/floyd-warshall.jl index ad513913e..cdff7347d 100644 --- a/src/shortestpaths/floyd-warshall.jl +++ b/src/shortestpaths/floyd-warshall.jl @@ -27,7 +27,7 @@ Space complexity is on the order of `O(|V|^2)`. """ function floyd_warshall_shortest_paths( g::AbstractGraph{U}, distmx::AbstractMatrix{T}=weights(g) -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} nvg = nv(g) # if we do checkbounds here, we can use @inbounds later checkbounds(distmx, Base.OneTo(nvg), Base.OneTo(nvg)) diff --git a/src/shortestpaths/johnson.jl b/src/shortestpaths/johnson.jl index 145d315ee..c02c3928b 100644 --- a/src/shortestpaths/johnson.jl +++ b/src/shortestpaths/johnson.jl @@ -9,7 +9,7 @@ An [`AbstractPathState`](@ref) designed for Johnson shortest-paths calculations. - `dists::Matrix{T}`: `dists[u, v]` is the length of the shortest path from `u` to `v` - `parents::Matrix{U}`: `parents[u, v]` is the predecessor of vertex `v` on the shortest path from `u` to `v` """ -struct JohnsonState{T<:Real,U<:Integer} <: AbstractPathState +struct JohnsonState{T<:Number,U<:Integer} <: AbstractPathState dists::Matrix{T} parents::Matrix{U} end @@ -29,7 +29,7 @@ Complexity: `O(|V|*|E|)` """ function johnson_shortest_paths( g::AbstractGraph{U}, distmx::AbstractMatrix{T}=weights(g) -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} nvg = nv(g) type_distmx = typeof(distmx) # Change when parallel implementation of Bellman Ford available @@ -71,7 +71,7 @@ end function enumerate_paths( s::JohnsonState{T,U}, v::Integer -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} pathinfo = s.parents[v, :] paths = Vector{Vector{U}}() for i in 1:length(pathinfo) diff --git a/src/shortestpaths/spfa.jl b/src/shortestpaths/spfa.jl index 80636a6dc..e295005f0 100644 --- a/src/shortestpaths/spfa.jl +++ b/src/shortestpaths/spfa.jl @@ -46,7 +46,7 @@ ERROR: Graphs.NegativeCycleError() """ function spfa_shortest_paths( graph::AbstractGraph{U}, source::Integer, distmx::AbstractMatrix{T}=weights(graph) -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} nvg = nv(graph) (source in 1:nvg) || @@ -115,7 +115,7 @@ false """ function has_negative_edge_cycle_spfa( g::AbstractGraph{U}, distmx::AbstractMatrix{T} -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} try spfa_shortest_paths(g, 1, distmx) catch e diff --git a/src/shortestpaths/yen.jl b/src/shortestpaths/yen.jl index 04f8e3e92..50625044b 100644 --- a/src/shortestpaths/yen.jl +++ b/src/shortestpaths/yen.jl @@ -30,7 +30,7 @@ function yen_k_shortest_paths( distmx::AbstractMatrix{T}=weights(g), K::Int=1; maxdist=typemax(T), -) where {T<:Real} where {U<:Integer} +) where {T<:Number} where {U<:Integer} source == target && return YenState{T,U}([U(0)], [[source]]) dj = dijkstra_shortest_paths(g, source, distmx; maxdist) diff --git a/src/spanningtrees/boruvka.jl b/src/spanningtrees/boruvka.jl index 0fa39e6d9..02b038a5c 100644 --- a/src/spanningtrees/boruvka.jl +++ b/src/spanningtrees/boruvka.jl @@ -14,7 +14,7 @@ function boruvka_mst end @traitfn function boruvka_mst( g::AG::(!IsDirected), distmx::AbstractMatrix{T}=weights(g); minimize=true -) where {T<:Real,U,AG<:AbstractGraph{U}} +) where {T<:Number,U,AG<:AbstractGraph{U}} djset = IntDisjointSets(nv(g)) # maximizing Z is the same as minimizing -Z # mode will indicate the need for the -1 multiplication diff --git a/src/spanningtrees/kruskal.jl b/src/spanningtrees/kruskal.jl index 883e39495..b9efba367 100644 --- a/src/spanningtrees/kruskal.jl +++ b/src/spanningtrees/kruskal.jl @@ -11,7 +11,7 @@ function kruskal_mst end # see https://github.com/mauro3/SimpleTraits.jl/issues/47#issuecomment-327880153 for syntax @traitfn function kruskal_mst( g::AG::(!IsDirected), distmx::AbstractMatrix{T}=weights(g); minimize=true -) where {T<:Real,U,AG<:AbstractGraph{U}} +) where {T<:Number,U,AG<:AbstractGraph{U}} connected_vs = IntDisjointSets(nv(g)) mst = Vector{edgetype(g)}() diff --git a/src/spanningtrees/prim.jl b/src/spanningtrees/prim.jl index 39a855405..baf963b1c 100644 --- a/src/spanningtrees/prim.jl +++ b/src/spanningtrees/prim.jl @@ -8,7 +8,7 @@ Return a vector of edges. function prim_mst end @traitfn function prim_mst( g::AG::(!IsDirected), distmx::AbstractMatrix{T}=weights(g) -) where {T<:Real,U,AG<:AbstractGraph{U}} +) where {T<:Number,U,AG<:AbstractGraph{U}} nvg = nv(g) pq = PriorityQueue{U,T}() diff --git a/src/traversals/maxadjvisit.jl b/src/traversals/maxadjvisit.jl index 7ebcd36f2..ab8bd6b52 100644 --- a/src/traversals/maxadjvisit.jl +++ b/src/traversals/maxadjvisit.jl @@ -18,7 +18,7 @@ assumed to be 1. """ @traitfn function mincut( g::::(!IsDirected), distmx::AbstractMatrix{T}=weights(g) -) where {T<:Real} +) where {T<:Number} nvg = nv(g) U = eltype(g) @@ -140,7 +140,7 @@ function maximum_adjacency_visit( log::Bool=false, io::IO=stdout, s::U=one(U), -) where {U,T<:Real} +) where {U,T<:Number} pq = PriorityQueue{U,T}(Base.Order.Reverse) vertices_order = Vector{U}() has_key = ones(Bool, nv(g)) diff --git a/test/runtests.jl b/test/runtests.jl index b85d4136d..bf54bc62b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -16,8 +16,10 @@ using Random using Statistics: mean, std using StableRNGs using Pkg +using Unitful const testdir = dirname(@__FILE__) +const KMf = typeof(u"1.0km") function get_pkg_version(name::AbstractString) for dep in values(Pkg.dependencies()) diff --git a/test/shortestpaths/yen.jl b/test/shortestpaths/yen.jl index af1dfb393..737489c76 100644 --- a/test/shortestpaths/yen.jl +++ b/test/shortestpaths/yen.jl @@ -51,6 +51,20 @@ 1.0 0.0 3.0 0.0 ] + w_km = KMf.(w) + for g in testgraphs(G) + ds = @inferred(yen_k_shortest_paths(g, 2, 4, w_km)) + @test ds.paths == [[2, 1, 4]] + ds = @inferred(yen_k_shortest_paths(g, 2, 1, w_km)) + @test ds.paths == [[2, 1]] + ds = @inferred(yen_k_shortest_paths(g, 2, 3, w_km)) + @test ds.paths == [[2, 3]] + + # Test with multiple paths + ds = @inferred(yen_k_shortest_paths(g, 2, 4, w_km, 2)) + @test ds.paths == [[2, 1, 4], [2, 3, 4]] + end + for g in testgraphs(G) ds = @inferred(yen_k_shortest_paths(g, 2, 4, w)) @test ds.paths == [[2, 1, 4]]