diff --git a/Project.toml b/Project.toml index bd73a99c6..bb6cb8fcb 100644 --- a/Project.toml +++ b/Project.toml @@ -6,7 +6,6 @@ version = "1.12.1" ArnoldiMethod = "ec485272-7323-5ecc-a04f-4719b315124d" Compat = "34da2185-b29b-5c13-b0c7-acf172513d20" DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" Inflate = "d25df0c9-e2be-5dd7-82c8-3ad0b3e990b9" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" @@ -15,6 +14,12 @@ SimpleTraits = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" +[weakdeps] +SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" + +[extensions] +GraphsSharedArraysExt = "SharedArrays" + [compat] Aqua = "0.6" ArnoldiMethod = "0.4" @@ -32,12 +37,14 @@ julia = "1.6" Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab" +Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b" JuliaFormatter = "98e50ef6-434e-11e9-1051-2b60c6c9e899" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" +SharedArrays = "1a1011a3-84de-559e-8e89-a11a2f7dc383" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" @@ -45,4 +52,4 @@ 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", "Unitful"] +test = ["Aqua", "Base64", "DelimitedFiles", "Distributed", "Documenter", "JET", "JuliaFormatter", "LinearAlgebra", "Pkg", "Random", "SharedArrays", "SparseArrays", "StableRNGs", "Statistics", "Test", "Unitful"] diff --git a/ext/GraphsSharedArraysExt.jl b/ext/GraphsSharedArraysExt.jl new file mode 100644 index 000000000..96576f81c --- /dev/null +++ b/ext/GraphsSharedArraysExt.jl @@ -0,0 +1,170 @@ +module GraphsSharedArraysExt + +using Graphs +using SharedArrays: SharedArrays, SharedMatrix, SharedVector, sdata +using SharedArrays.Distributed: @distributed +using Random: shuffle + +# betweenness +function Graphs.Parallel.distr_betweenness_centrality( + g::AbstractGraph, + vs=vertices(g), + distmx::AbstractMatrix=weights(g); + normalize=true, + endpoints=false, +)::Vector{Float64} + n_v = nv(g) + k = length(vs) + isdir = is_directed(g) + + # Parallel reduction + + betweenness = @distributed (+) for s in vs + temp_betweenness = zeros(n_v) + if degree(g, s) > 0 # this might be 1? + state = Graphs.dijkstra_shortest_paths( + g, s, distmx; allpaths=true, trackvertices=true + ) + if endpoints + Graphs._accumulate_endpoints!(temp_betweenness, state, g, s) + else + Graphs._accumulate_basic!(temp_betweenness, state, g, s) + end + end + temp_betweenness + end + + Graphs._rescale!(betweenness, n_v, normalize, isdir, k) + + return betweenness +end + +# closeness +function Graphs.Parallel.distr_closeness_centrality( + g::AbstractGraph, distmx::AbstractMatrix=weights(g); normalize=true +)::Vector{Float64} + n_v = Int(nv(g)) + closeness = SharedVector{Float64}(n_v) + fill!(closeness, 0.0) + + @sync @distributed for u in vertices(g) + if degree(g, u) == 0 # no need to do Dijkstra here + closeness[u] = 0.0 + else + d = Graphs.dijkstra_shortest_paths(g, u, distmx).dists + δ = filter(x -> x != typemax(x), d) + σ = sum(δ) + l = length(δ) - 1 + if σ > 0 + closeness[u] = l / σ + if normalize + n = l * 1.0 / (n_v - 1) + closeness[u] *= n + end + else + closeness[u] = 0.0 + end + end + end + return sdata(closeness) +end + +# radiality +function Graphs.Parallel.distr_radiality_centrality(g::AbstractGraph)::Vector{Float64} + n_v = nv(g) + vs = vertices(g) + n = ne(g) + meandists = SharedVector{Float64}(Int(n_v)) + maxdists = SharedVector{Float64}(Int(n_v)) + + @sync @distributed for i in 1:n_v + d = Graphs.dijkstra_shortest_paths(g, vs[i]) + maxdists[i] = maximum(d.dists) + meandists[i] = sum(d.dists) / (n_v - 1) + nothing + end + dmtr = maximum(maxdists) + radialities = collect(meandists) + return ((dmtr + 1) .- radialities) ./ dmtr +end + +# stress +function Graphs.Parallel.distr_stress_centrality( + g::AbstractGraph, vs=vertices(g) +)::Vector{Int64} + n_v = nv(g) + k = length(vs) + isdir = is_directed(g) + + # Parallel reduction + stress = @distributed (+) for s in vs + temp_stress = zeros(Int64, n_v) + if degree(g, s) > 0 # this might be 1? + state = Graphs.dijkstra_shortest_paths(g, s; allpaths=true, trackvertices=true) + Graphs._stress_accumulate_basic!(temp_stress, state, g, s) + end + temp_stress + end + return stress +end + +# generate_reduce +function Graphs.Parallel.distr_generate_reduce( + g::AbstractGraph{T}, gen_func::Function, comp::Comp, reps::Integer +) where {T<:Integer,Comp} + # Type assert required for type stability + min_set::Vector{T} = @distributed ((x, y) -> comp(x, y) ? x : y) for _ in 1:reps + gen_func(g) + end + return min_set +end + +# eccentricity +function Graphs.Parallel.distr_eccentricity( + g::AbstractGraph, vs=vertices(g), distmx::AbstractMatrix{T}=weights(g) +) where {T<:Number} + vlen = length(vs) + eccs = SharedVector{T}(vlen) + @sync @distributed for i in 1:vlen + d′ = Graphs.dijkstra_shortest_paths(g, vs[i], distmx) + eccs[i] = maximum(d′.dists) + end + d = sdata(eccs) + maximum(d) == typemax(T) && @warn("Infinite path length detected") + return d +end + +# dijkstra shortest paths +function Graphs.Parallel.distr_dijkstra_shortest_paths( + g::AbstractGraph{U}, sources=vertices(g), distmx::AbstractMatrix{T}=weights(g) +) where {T<:Number} where {U} + n_v = nv(g) + r_v = length(sources) + + # TODO: remove `Int` once julialang/#23029 / #23032 are resolved + dists = SharedMatrix{T}(Int(r_v), Int(n_v)) + parents = SharedMatrix{U}(Int(r_v), Int(n_v)) + + @sync @distributed for i in 1:r_v + state = Graphs.dijkstra_shortest_paths(g, sources[i], distmx) + dists[i, :] = state.dists + parents[i, :] = state.parents + end + + result = Graphs.Parallel.MultipleDijkstraState(sdata(dists), sdata(parents)) + return result +end + +# random greedy color +function Graphs.Parallel.distr_random_greedy_color( + g::AbstractGraph{T}, reps::Integer +) where {T<:Integer} + best = @distributed (Graphs.best_color) for i in 1:reps + seq = shuffle(vertices(g)) + Graphs.perm_greedy_color(g, seq) + end + + return convert(Graphs.Coloring{T}, best) +end + +end diff --git a/src/Graphs.jl b/src/Graphs.jl index 86bc0946a..76d0cc21f 100644 --- a/src/Graphs.jl +++ b/src/Graphs.jl @@ -560,5 +560,9 @@ include("Experimental/Experimental.jl") include("Parallel/Parallel.jl") include("Test/Test.jl") +if !isdefined(Base, :get_extension) + include("../ext/GraphsSharedArraysExt.jl") +end + using .LinAlg end # module diff --git a/src/Parallel/Parallel.jl b/src/Parallel/Parallel.jl index 6dc931231..3917dde96 100644 --- a/src/Parallel/Parallel.jl +++ b/src/Parallel/Parallel.jl @@ -2,9 +2,7 @@ module Parallel using Graphs using Graphs: sample, AbstractPathState, JohnsonState, BellmanFordState, FloydWarshallState -using Distributed: @distributed using Base.Threads: @threads, nthreads, Atomic, atomic_add!, atomic_cas! -using SharedArrays: SharedMatrix, SharedVector, sdata using ArnoldiMethod: LM, SR, LR, partialschur, partialeigen using Random: AbstractRNG, shuffle import SparseArrays: sparse diff --git a/src/Parallel/centrality/betweenness.jl b/src/Parallel/centrality/betweenness.jl index 14a7a5a8a..77bd739dc 100644 --- a/src/Parallel/centrality/betweenness.jl +++ b/src/Parallel/centrality/betweenness.jl @@ -4,7 +4,7 @@ function betweenness_centrality( distmx::AbstractMatrix=weights(g); normalize=true, endpoints=false, - parallel=:distributed, + parallel=:threads, ) return if parallel == :distributed distr_betweenness_centrality( @@ -23,7 +23,7 @@ function betweenness_centrality( distmx::AbstractMatrix=weights(g); normalize=true, endpoints=false, - parallel=:distributed, + parallel=:threads, rng::Union{Nothing,AbstractRNG}=nothing, seed::Union{Nothing,Integer}=nothing, ) @@ -39,37 +39,10 @@ function betweenness_centrality( end end -function distr_betweenness_centrality( - g::AbstractGraph, - vs=vertices(g), - distmx::AbstractMatrix=weights(g); - normalize=true, - endpoints=false, -)::Vector{Float64} - n_v = nv(g) - k = length(vs) - isdir = is_directed(g) - - # Parallel reduction - - betweenness = @distributed (+) for s in vs - temp_betweenness = zeros(n_v) - if degree(g, s) > 0 # this might be 1? - state = Graphs.dijkstra_shortest_paths( - g, s, distmx; allpaths=true, trackvertices=true - ) - if endpoints - Graphs._accumulate_endpoints!(temp_betweenness, state, g, s) - else - Graphs._accumulate_basic!(temp_betweenness, state, g, s) - end - end - temp_betweenness - end - - Graphs._rescale!(betweenness, n_v, normalize, isdir, k) - - return betweenness +function distr_betweenness_centrality(args...; kwargs...) + return error( + "`parallel = :distributed` requested, but SharedArrays or Distributed is not loaded" + ) end function distr_betweenness_centrality( diff --git a/src/Parallel/centrality/closeness.jl b/src/Parallel/centrality/closeness.jl index 920421211..2259ea0aa 100644 --- a/src/Parallel/centrality/closeness.jl +++ b/src/Parallel/centrality/closeness.jl @@ -1,8 +1,5 @@ function closeness_centrality( - g::AbstractGraph, - distmx::AbstractMatrix=weights(g); - normalize=true, - parallel=:distributed, + g::AbstractGraph, distmx::AbstractMatrix=weights(g); normalize=true, parallel=:threads ) return if parallel == :distributed distr_closeness_centrality(g, distmx; normalize=normalize) @@ -11,33 +8,10 @@ function closeness_centrality( end end -function distr_closeness_centrality( - g::AbstractGraph, distmx::AbstractMatrix=weights(g); normalize=true -)::Vector{Float64} - n_v = Int(nv(g)) - closeness = SharedVector{Float64}(n_v) - fill!(closeness, 0.0) - - @sync @distributed for u in vertices(g) - if degree(g, u) == 0 # no need to do Dijkstra here - closeness[u] = 0.0 - else - d = Graphs.dijkstra_shortest_paths(g, u, distmx).dists - δ = filter(x -> x != typemax(x), d) - σ = sum(δ) - l = length(δ) - 1 - if σ > 0 - closeness[u] = l / σ - if normalize - n = l * 1.0 / (n_v - 1) - closeness[u] *= n - end - else - closeness[u] = 0.0 - end - end - end - return sdata(closeness) +function distr_closeness_centrality(args...; kwargs...) + return error( + "`parallel = :distributed` requested, but SharedArrays or Distributed is not loaded" + ) end function threaded_closeness_centrality( diff --git a/src/Parallel/centrality/radiality.jl b/src/Parallel/centrality/radiality.jl index e10fbe81b..ace98d454 100644 --- a/src/Parallel/centrality/radiality.jl +++ b/src/Parallel/centrality/radiality.jl @@ -1,4 +1,4 @@ -function radiality_centrality(g::AbstractGraph; parallel=:distributed) +function radiality_centrality(g::AbstractGraph; parallel=:threads) return if parallel == :distributed distr_radiality_centrality(g) else @@ -6,22 +6,10 @@ function radiality_centrality(g::AbstractGraph; parallel=:distributed) end end -function distr_radiality_centrality(g::AbstractGraph)::Vector{Float64} - n_v = nv(g) - vs = vertices(g) - n = ne(g) - meandists = SharedVector{Float64}(Int(n_v)) - maxdists = SharedVector{Float64}(Int(n_v)) - - @sync @distributed for i in 1:n_v - d = Graphs.dijkstra_shortest_paths(g, vs[i]) - maxdists[i] = maximum(d.dists) - meandists[i] = sum(d.dists) / (n_v - 1) - nothing - end - dmtr = maximum(maxdists) - radialities = collect(meandists) - return ((dmtr + 1) .- radialities) ./ dmtr +function distr_radiality_centrality(args...; kwargs...) + return error( + "`parallel = :distributed` requested, but SharedArrays or Distributed is not loaded" + ) end function threaded_radiality_centrality(g::AbstractGraph)::Vector{Float64} diff --git a/src/Parallel/centrality/stress.jl b/src/Parallel/centrality/stress.jl index 58844af8d..7d717aa11 100644 --- a/src/Parallel/centrality/stress.jl +++ b/src/Parallel/centrality/stress.jl @@ -1,4 +1,4 @@ -function stress_centrality(g::AbstractGraph, vs=vertices(g); parallel=:distributed) +function stress_centrality(g::AbstractGraph, vs=vertices(g); parallel=:threads) return if parallel == :distributed distr_stress_centrality(g, vs) else @@ -9,7 +9,7 @@ end function stress_centrality( g::AbstractGraph, k::Integer; - parallel=:distributed, + parallel=:threads, rng::Union{Nothing,AbstractRNG}=nothing, seed::Union{Nothing,Integer}=nothing, ) @@ -21,21 +21,10 @@ function stress_centrality( end end -function distr_stress_centrality(g::AbstractGraph, vs=vertices(g))::Vector{Int64} - n_v = nv(g) - k = length(vs) - isdir = is_directed(g) - - # Parallel reduction - stress = @distributed (+) for s in vs - temp_stress = zeros(Int64, n_v) - if degree(g, s) > 0 # this might be 1? - state = Graphs.dijkstra_shortest_paths(g, s; allpaths=true, trackvertices=true) - Graphs._stress_accumulate_basic!(temp_stress, state, g, s) - end - temp_stress - end - return stress +function distr_stress_centrality(args...; kwargs...) + return error( + "`parallel = :distributed` requested, but SharedArrays or Distributed is not loaded" + ) end function threaded_stress_centrality(g::AbstractGraph, vs=vertices(g))::Vector{Int64} diff --git a/src/Parallel/distance.jl b/src/Parallel/distance.jl index 3145339f3..9d33f97c0 100644 --- a/src/Parallel/distance.jl +++ b/src/Parallel/distance.jl @@ -1,20 +1,43 @@ # used in shortest path calculations function eccentricity( + g::AbstractGraph, + vs=vertices(g), + distmx::AbstractMatrix{T}=weights(g); + parallel=:threads, +) where {T<:Number} + return if parallel === :threads + threaded_eccentricity(g, vs, distmx) + elseif parallel === :distributed + distr_eccentricity(g, vs, distmx) + else + error( + "Unsupported parallel argument '$(repr(parallel))' (supported: ':threads' or ':distributed')", + ) + end +end + +function distr_eccentricity(args...; kwargs...) + return error( + "`parallel = :distributed` requested, but SharedArrays or Distributed is not loaded" + ) +end + +function threaded_eccentricity( g::AbstractGraph, vs=vertices(g), distmx::AbstractMatrix{T}=weights(g) ) where {T<:Number} vlen = length(vs) - eccs = SharedVector{T}(vlen) - @sync @distributed for i in 1:vlen - eccs[i] = maximum(Graphs.dijkstra_shortest_paths(g, vs[i], distmx).dists) + eccs = Vector{T}(undef, vlen) + Base.Threads.@threads for i in 1:vlen + d = Graphs.dijkstra_shortest_paths(g, vs[i], distmx) + eccs[i] = maximum(d.dists) end - d = sdata(eccs) - maximum(d) == typemax(T) && @warn("Infinite path length detected") - return d + maximum(eccs) == typemax(T) && @warn("Infinite path length detected") + return eccs end -function eccentricity(g::AbstractGraph, distmx::AbstractMatrix) - return eccentricity(g, vertices(g), distmx) +function eccentricity(g::AbstractGraph, distmx::AbstractMatrix; parallel=:threads) + return eccentricity(g, vertices(g), distmx; parallel) end function diameter(g::AbstractGraph, distmx::AbstractMatrix=weights(g)) diff --git a/src/Parallel/shortestpaths/dijkstra.jl b/src/Parallel/shortestpaths/dijkstra.jl index 70363cf93..ceb80a791 100644 --- a/src/Parallel/shortestpaths/dijkstra.jl +++ b/src/Parallel/shortestpaths/dijkstra.jl @@ -17,21 +17,44 @@ an optional distance matrix `distmx`. Return a [`Parallel.MultipleDijkstraState` traversal information. """ function dijkstra_shortest_paths( + g::AbstractGraph{U}, + sources=vertices(g), + distmx::AbstractMatrix{T}=weights(g); + parallel=:threads, +) where {T<:Number} where {U} + return if parallel === :threads + threaded_dijkstra_shortest_paths(g, sources, distmx) + elseif parallel === :distributed + distr_dijkstra_shortest_paths(g, sources, distmx) + else + error( + "Unsupported parallel argument '$(repr(parallel))' (supported: ':threads' or ':distributed')", + ) + end +end + +function threaded_dijkstra_shortest_paths( g::AbstractGraph{U}, sources=vertices(g), distmx::AbstractMatrix{T}=weights(g) ) where {T<:Number} where {U} n_v = nv(g) r_v = length(sources) # TODO: remove `Int` once julialang/#23029 / #23032 are resolved - dists = SharedMatrix{T}(Int(r_v), Int(n_v)) - parents = SharedMatrix{U}(Int(r_v), Int(n_v)) + dists = Matrix{T}(undef, Int(r_v), Int(n_v)) + parents = Matrix{U}(undef, Int(r_v), Int(n_v)) - @sync @distributed for i in 1:r_v + Base.Threads.@threads for i in 1:r_v state = Graphs.dijkstra_shortest_paths(g, sources[i], distmx) dists[i, :] = state.dists parents[i, :] = state.parents end - result = MultipleDijkstraState(sdata(dists), sdata(parents)) + result = MultipleDijkstraState(dists, parents) return result end + +function distr_dijkstra_shortest_paths(args...; kwargs...) + return error( + "`parallel = :distributed` requested, but SharedArrays or Distributed is not loaded" + ) +end diff --git a/src/Parallel/traversals/greedy_color.jl b/src/Parallel/traversals/greedy_color.jl index 6eb0c1e8e..a1eb03c13 100644 --- a/src/Parallel/traversals/greedy_color.jl +++ b/src/Parallel/traversals/greedy_color.jl @@ -1,18 +1,40 @@ -function random_greedy_color(g::AbstractGraph{T}, reps::Integer) where {T<:Integer} - best = @distributed (Graphs.best_color) for i in 1:reps +function random_greedy_color( + g::AbstractGraph{T}, reps::Integer; parallel=:threads +) where {T<:Integer} + return if parallel === :threads + threaded_random_greedy_color(g, reps) + elseif parallel === :distributed + distr_random_greedy_color(g, reps) + else + error( + "Unsupported parallel argument '$(repr(parallel))' (supported: ':threads' or ':distributed')", + ) + end +end + +function threaded_random_greedy_color(g::AbstractGraph{T}, reps::Integer) where {T<:Integer} + local_best = Any[nothing for _ in 1:reps] + Base.Threads.@threads for i in 1:reps seq = shuffle(vertices(g)) - Graphs.perm_greedy_color(g, seq) + local_best[i] = Graphs.perm_greedy_color(g, seq) end + best = reduce(Graphs.best_color, local_best) return convert(Graphs.Coloring{T}, best) end +function distr_random_greedy_color(args...; kwargs...) + return error( + "`parallel = :distributed` requested, but SharedArrays or Distributed is not loaded" + ) +end + function greedy_color( - g::AbstractGraph{U}; sort_degree::Bool=false, reps::Integer=1 + g::AbstractGraph{U}; sort_degree::Bool=false, reps::Integer=1, parallel=:threads ) where {U<:Integer} return if sort_degree Graphs.degree_greedy_color(g) else - Parallel.random_greedy_color(g, reps) + Parallel.random_greedy_color(g, reps; parallel) end end diff --git a/src/Parallel/utils.jl b/src/Parallel/utils.jl index fe763ecdc..85847db33 100644 --- a/src/Parallel/utils.jl +++ b/src/Parallel/utils.jl @@ -22,14 +22,10 @@ end Distributed implementation of [`generate_reduce`](@ref). """ -function distr_generate_reduce( - g::AbstractGraph{T}, gen_func::Function, comp::Comp, reps::Integer -) where {T<:Integer,Comp} - # Type assert required for type stability - min_set::Vector{T} = @distributed ((x, y) -> comp(x, y) ? x : y) for _ in 1:reps - gen_func(g) - end - return min_set +function distr_generate_reduce(args...; kwargs...) + return error( + "`parallel = :distributed` requested, but SharedArrays or Distributed is not loaded" + ) end """ diff --git a/test/parallel/distance.jl b/test/parallel/distance.jl index 16db54e06..0ba9076f9 100644 --- a/test/parallel/distance.jl +++ b/test/parallel/distance.jl @@ -7,31 +7,43 @@ distmx1 = [Inf 2.0 Inf; 2.0 Inf 4.2; Inf 4.2 Inf] distmx2 = [Inf 2.0 Inf; 3.2 Inf 4.2; 5.5 6.1 Inf] - for g in testgraphs(a1) - z = @inferred(Graphs.eccentricity(g, distmx1)) - y = @inferred(Parallel.eccentricity(g, distmx1)) - @test isapprox(y, z) - @test @inferred(Graphs.diameter(y)) == - @inferred(Parallel.diameter(g, distmx1)) == - 6.2 - @test @inferred(Graphs.periphery(y)) == - @inferred(Parallel.periphery(g, distmx1)) == - [1, 3] - @test @inferred(Graphs.radius(y)) == @inferred(Parallel.radius(g, distmx1)) == 4.2 - @test @inferred(Graphs.center(y)) == @inferred(Parallel.center(g, distmx1)) == [2] + for parallel in [:threads, :distributed] + for g in testgraphs(a1) + z = @inferred(Graphs.eccentricity(g, distmx1)) + y = @inferred(Parallel.eccentricity(g, distmx1; parallel)) + @test isapprox(y, z) + @test @inferred(Graphs.diameter(y)) == + @inferred(Parallel.diameter(g, distmx1)) == + 6.2 + @test @inferred(Graphs.periphery(y)) == + @inferred(Parallel.periphery(g, distmx1)) == + [1, 3] + @test @inferred(Graphs.radius(y)) == + @inferred(Parallel.radius(g, distmx1)) == + 4.2 + @test @inferred(Graphs.center(y)) == + @inferred(Parallel.center(g, distmx1)) == + [2] + end end - for g in testdigraphs(a2) - z = @inferred(Graphs.eccentricity(g, distmx2)) - y = @inferred(Parallel.eccentricity(g, distmx2)) - @test isapprox(y, z) - @test @inferred(Graphs.diameter(y)) == - @inferred(Parallel.diameter(g, distmx2)) == - 6.2 - @test @inferred(Graphs.periphery(y)) == - @inferred(Parallel.periphery(g, distmx2)) == - [1] - @test @inferred(Graphs.radius(y)) == @inferred(Parallel.radius(g, distmx2)) == 4.2 - @test @inferred(Graphs.center(y)) == @inferred(Parallel.center(g, distmx2)) == [2] + for parallel in [:threads, :distributed] + for g in testdigraphs(a2) + z = @inferred(Graphs.eccentricity(g, distmx2)) + y = @inferred(Parallel.eccentricity(g, distmx2; parallel)) + @test isapprox(y, z) + @test @inferred(Graphs.diameter(y)) == + @inferred(Parallel.diameter(g, distmx2)) == + 6.2 + @test @inferred(Graphs.periphery(y)) == + @inferred(Parallel.periphery(g, distmx2)) == + [1] + @test @inferred(Graphs.radius(y)) == + @inferred(Parallel.radius(g, distmx2)) == + 4.2 + @test @inferred(Graphs.center(y)) == + @inferred(Parallel.center(g, distmx2)) == + [2] + end end end diff --git a/test/parallel/runtests.jl b/test/parallel/runtests.jl index 76805ac5b..5e9dd1877 100644 --- a/test/parallel/runtests.jl +++ b/test/parallel/runtests.jl @@ -1,5 +1,6 @@ using Graphs using Graphs.Parallel +using SharedArrays using Base.Threads: @threads, Atomic @test length(description()) > 1 diff --git a/test/parallel/shortestpaths/dijkstra.jl b/test/parallel/shortestpaths/dijkstra.jl index 34f948232..dc4961ae7 100644 --- a/test/parallel/shortestpaths/dijkstra.jl +++ b/test/parallel/shortestpaths/dijkstra.jl @@ -6,42 +6,44 @@ g3 = path_graph(5) d = [0 1 2 3 4; 1 0 1 0 1; 2 1 0 11 12; 3 0 11 0 5; 4 1 19 5 0] - for g in testgraphs(g3) - z = floyd_warshall_shortest_paths(g, d) - zp = @inferred(Parallel.dijkstra_shortest_paths(g, collect(1:5), d)) - @test all(isapprox(z.dists, zp.dists)) + for parallel in [:threads, :distributed] + for g in testgraphs(g3) + z = floyd_warshall_shortest_paths(g, d) + zp = @inferred(Parallel.dijkstra_shortest_paths(g, collect(1:5), d; parallel)) + @test all(isapprox(z.dists, zp.dists)) - for i in 1:5 - state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) - for j in 1:5 - if zp.parents[i, j] != 0 - @test zp.parents[i, j] in state.predecessors[j] + for i in 1:5 + state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) + for j in 1:5 + if zp.parents[i, j] != 0 + @test zp.parents[i, j] in state.predecessors[j] + end end end - end - z = floyd_warshall_shortest_paths(g) - zp = @inferred(Parallel.dijkstra_shortest_paths(g)) - @test all(isapprox(z.dists, zp.dists)) + z = floyd_warshall_shortest_paths(g) + zp = @inferred(Parallel.dijkstra_shortest_paths(g; parallel)) + @test all(isapprox(z.dists, zp.dists)) - for i in 1:5 - state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) - for j in 1:5 - if zp.parents[i, j] != 0 - @test zp.parents[i, j] in state.predecessors[j] + for i in 1:5 + state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) + for j in 1:5 + if zp.parents[i, j] != 0 + @test zp.parents[i, j] in state.predecessors[j] + end end end - end - z = floyd_warshall_shortest_paths(g) - zp = @inferred(Parallel.dijkstra_shortest_paths(g, [1, 2])) - @test all(isapprox(z.dists[1:2, :], zp.dists)) + z = floyd_warshall_shortest_paths(g) + zp = @inferred(Parallel.dijkstra_shortest_paths(g, [1, 2]; parallel)) + @test all(isapprox(z.dists[1:2, :], zp.dists)) - for i in 1:2 - state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) - for j in 1:5 - if zp.parents[i, j] != 0 - @test zp.parents[i, j] in state.predecessors[j] + for i in 1:2 + state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) + for j in 1:5 + if zp.parents[i, j] != 0 + @test zp.parents[i, j] in state.predecessors[j] + end end end end @@ -51,42 +53,44 @@ g3 = path_digraph(5) d = float([0 1 2 3 4; 5 0 6 7 8; 9 10 0 11 12; 13 14 15 0 16; 17 18 19 20 0]) - for g in testdigraphs(g3) - z = floyd_warshall_shortest_paths(g, d) - zp = @inferred(Parallel.dijkstra_shortest_paths(g, collect(1:5), d)) - @test all(isapprox(z.dists, zp.dists)) + for parallel in [:threads, :distributed] + for g in testdigraphs(g3) + z = floyd_warshall_shortest_paths(g, d) + zp = @inferred(Parallel.dijkstra_shortest_paths(g, collect(1:5), d; parallel)) + @test all(isapprox(z.dists, zp.dists)) - for i in 1:5 - state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) - for j in 1:5 - if z.parents[i, j] != 0 - @test zp.parents[i, j] in state.predecessors[j] + for i in 1:5 + state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) + for j in 1:5 + if z.parents[i, j] != 0 + @test zp.parents[i, j] in state.predecessors[j] + end end end - end - z = floyd_warshall_shortest_paths(g) - zp = @inferred(Parallel.dijkstra_shortest_paths(g)) - @test all(isapprox(z.dists, zp.dists)) + z = floyd_warshall_shortest_paths(g) + zp = @inferred(Parallel.dijkstra_shortest_paths(g; parallel)) + @test all(isapprox(z.dists, zp.dists)) - for i in 1:5 - state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) - for j in 1:5 - if zp.parents[i, j] != 0 - @test zp.parents[i, j] in state.predecessors[j] + for i in 1:5 + state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) + for j in 1:5 + if zp.parents[i, j] != 0 + @test zp.parents[i, j] in state.predecessors[j] + end end end - end - z = floyd_warshall_shortest_paths(g) - zp = @inferred(Parallel.dijkstra_shortest_paths(g, [1, 2])) - @test all(isapprox(z.dists[1:2, :], zp.dists)) + z = floyd_warshall_shortest_paths(g) + zp = @inferred(Parallel.dijkstra_shortest_paths(g, [1, 2]; parallel)) + @test all(isapprox(z.dists[1:2, :], zp.dists)) - for i in 1:2 - state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) - for j in 1:5 - if zp.parents[i, j] != 0 - @test zp.parents[i, j] in state.predecessors[j] + for i in 1:2 + state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true) + for j in 1:5 + if zp.parents[i, j] != 0 + @test zp.parents[i, j] in state.predecessors[j] + end end end end diff --git a/test/parallel/traversals/greedy_color.jl b/test/parallel/traversals/greedy_color.jl index 65a43a514..36a25abdf 100644 --- a/test/parallel/traversals/greedy_color.jl +++ b/test/parallel/traversals/greedy_color.jl @@ -1,26 +1,34 @@ @testset "Parallel.Greedy Coloring" begin g3 = star_graph(10) - for g in testgraphs(g3) - for op_sort in (true, false) - C = @inferred(Parallel.greedy_color(g, reps=5, sort_degree=op_sort)) - @test C.num_colors == 2 + for parallel in [:threads, :distributed] + for g in testgraphs(g3) + for op_sort in (true, false) + C = @inferred( + Parallel.greedy_color(g; reps=5, sort_degree=op_sort, parallel) + ) + @test C.num_colors == 2 + end end end g4 = path_graph(20) g5 = complete_graph(20) - for graph in [g4, g5] - for g in testgraphs(graph) - for op_sort in (true, false) - C = @inferred(Parallel.greedy_color(g, reps=5, sort_degree=op_sort)) + for parallel in [:threads, :distributed] + for graph in [g4, g5] + for g in testgraphs(graph) + for op_sort in (true, false) + C = @inferred( + Parallel.greedy_color(g; reps=5, sort_degree=op_sort, parallel) + ) - @test C.num_colors <= maximum(degree(g)) + 1 - correct = true - for e in edges(g) - C.colors[src(e)] == C.colors[dst(e)] && (correct = false) + @test C.num_colors <= maximum(degree(g)) + 1 + correct = true + for e in edges(g) + C.colors[src(e)] == C.colors[dst(e)] && (correct = false) + end + @test correct end - @test correct end end end