Skip to content

Commit cbda59e

Browse files
committed
Add missing parallel=:threads implementations
These implementations are extremely basic, but they try to follow the patterns in the other parts of the Parallel module. My real motivation is to be able to move the Distributed implementations into an extension, so that Graphs.jl does not depend on Distributed.
1 parent 6130332 commit cbda59e

File tree

6 files changed

+214
-97
lines changed

6 files changed

+214
-97
lines changed

src/Parallel/distance.jl

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,51 @@
11
# used in shortest path calculations
22

33
function eccentricity(
4+
g::AbstractGraph,
5+
vs=vertices(g),
6+
distmx::AbstractMatrix{T}=weights(g);
7+
parallel=:distributed,
8+
) where {T<:Number}
9+
return if parallel === :threads
10+
threaded_eccentricity(g, vs, distmx)
11+
elseif parallel === :distributed
12+
distr_eccentricity(g, vs, distmx)
13+
else
14+
error(
15+
"Unsupported parallel argument '$(repr(parallel))' (supported: ':threads' or ':distributed')",
16+
)
17+
end
18+
end
19+
20+
function distr_eccentricity(
421
g::AbstractGraph, vs=vertices(g), distmx::AbstractMatrix{T}=weights(g)
522
) where {T<:Number}
623
vlen = length(vs)
724
eccs = SharedVector{T}(vlen)
825
@sync @distributed for i in 1:vlen
9-
eccs[i] = maximum(Graphs.dijkstra_shortest_paths(g, vs[i], distmx).dists)
26+
d′ = Graphs.dijkstra_shortest_paths(g, vs[i], distmx)
27+
eccs[i] = maximum(d′.dists)
1028
end
1129
d = sdata(eccs)
1230
maximum(d) == typemax(T) && @warn("Infinite path length detected")
1331
return d
1432
end
1533

16-
function eccentricity(g::AbstractGraph, distmx::AbstractMatrix)
17-
return eccentricity(g, vertices(g), distmx)
34+
function threaded_eccentricity(
35+
g::AbstractGraph, vs=vertices(g), distmx::AbstractMatrix{T}=weights(g)
36+
) where {T<:Number}
37+
vlen = length(vs)
38+
eccs = Vector{T}(undef, vlen)
39+
Base.Threads.@threads for i in 1:vlen
40+
d = Graphs.dijkstra_shortest_paths(g, vs[i], distmx)
41+
eccs[i] = maximum(d.dists)
42+
end
43+
maximum(eccs) == typemax(T) && @warn("Infinite path length detected")
44+
return eccs
45+
end
46+
47+
function eccentricity(g::AbstractGraph, distmx::AbstractMatrix; parallel=:distributed)
48+
return eccentricity(g, vertices(g), distmx; parallel)
1849
end
1950

2051
function diameter(g::AbstractGraph, distmx::AbstractMatrix=weights(g))

src/Parallel/shortestpaths/dijkstra.jl

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,43 @@ an optional distance matrix `distmx`. Return a [`Parallel.MultipleDijkstraState`
1717
traversal information.
1818
"""
1919
function dijkstra_shortest_paths(
20+
g::AbstractGraph{U},
21+
sources=vertices(g),
22+
distmx::AbstractMatrix{T}=weights(g);
23+
parallel=:distributed,
24+
) where {T<:Number} where {U}
25+
return if parallel === :threads
26+
threaded_dijkstra_shortest_paths(g, sources, distmx)
27+
elseif parallel === :distributed
28+
distr_dijkstra_shortest_paths(g, sources, distmx)
29+
else
30+
error(
31+
"Unsupported parallel argument '$(repr(parallel))' (supported: ':threads' or ':distributed')",
32+
)
33+
end
34+
end
35+
36+
function threaded_dijkstra_shortest_paths(
37+
g::AbstractGraph{U}, sources=vertices(g), distmx::AbstractMatrix{T}=weights(g)
38+
) where {T<:Number} where {U}
39+
n_v = nv(g)
40+
r_v = length(sources)
41+
42+
# TODO: remove `Int` once julialang/#23029 / #23032 are resolved
43+
dists = Matrix{T}(undef, Int(r_v), Int(n_v))
44+
parents = Matrix{U}(undef, Int(r_v), Int(n_v))
45+
46+
Base.Threads.@threads for i in 1:r_v
47+
state = Graphs.dijkstra_shortest_paths(g, sources[i], distmx)
48+
dists[i, :] = state.dists
49+
parents[i, :] = state.parents
50+
end
51+
52+
result = MultipleDijkstraState(dists, parents)
53+
return result
54+
end
55+
56+
function distr_dijkstra_shortest_paths(
2057
g::AbstractGraph{U}, sources=vertices(g), distmx::AbstractMatrix{T}=weights(g)
2158
) where {T<:Number} where {U}
2259
n_v = nv(g)
Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,29 @@
1-
function random_greedy_color(g::AbstractGraph{T}, reps::Integer) where {T<:Integer}
1+
function random_greedy_color(
2+
g::AbstractGraph{T}, reps::Integer; parallel=:distributed
3+
) where {T<:Integer}
4+
return if parallel === :threads
5+
threaded_random_greedy_color(g, reps)
6+
elseif parallel === :distributed
7+
distr_random_greedy_color(g, reps)
8+
else
9+
error(
10+
"Unsupported parallel argument '$(repr(parallel))' (supported: ':threads' or ':distributed')",
11+
)
12+
end
13+
end
14+
15+
function threaded_random_greedy_color(g::AbstractGraph{T}, reps::Integer) where {T<:Integer}
16+
local_best = Any[nothing for _ in 1:reps]
17+
Base.Threads.@threads for i in 1:reps
18+
seq = shuffle(vertices(g))
19+
local_best[i] = Graphs.perm_greedy_color(g, seq)
20+
end
21+
best = reduce(Graphs.best_color, local_best)
22+
23+
return convert(Graphs.Coloring{T}, best)
24+
end
25+
26+
function distr_random_greedy_color(g::AbstractGraph{T}, reps::Integer) where {T<:Integer}
227
best = @distributed (Graphs.best_color) for i in 1:reps
328
seq = shuffle(vertices(g))
429
Graphs.perm_greedy_color(g, seq)
@@ -8,11 +33,11 @@ function random_greedy_color(g::AbstractGraph{T}, reps::Integer) where {T<:Integ
833
end
934

1035
function greedy_color(
11-
g::AbstractGraph{U}; sort_degree::Bool=false, reps::Integer=1
36+
g::AbstractGraph{U}; sort_degree::Bool=false, reps::Integer=1, parallel=:distributed
1237
) where {U<:Integer}
1338
return if sort_degree
1439
Graphs.degree_greedy_color(g)
1540
else
16-
Parallel.random_greedy_color(g, reps)
41+
Parallel.random_greedy_color(g, reps; parallel)
1742
end
1843
end

test/parallel/distance.jl

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,43 @@
77
distmx1 = [Inf 2.0 Inf; 2.0 Inf 4.2; Inf 4.2 Inf]
88
distmx2 = [Inf 2.0 Inf; 3.2 Inf 4.2; 5.5 6.1 Inf]
99

10-
for g in testgraphs(a1)
11-
z = @inferred(Graphs.eccentricity(g, distmx1))
12-
y = @inferred(Parallel.eccentricity(g, distmx1))
13-
@test isapprox(y, z)
14-
@test @inferred(Graphs.diameter(y)) ==
15-
@inferred(Parallel.diameter(g, distmx1)) ==
16-
6.2
17-
@test @inferred(Graphs.periphery(y)) ==
18-
@inferred(Parallel.periphery(g, distmx1)) ==
19-
[1, 3]
20-
@test @inferred(Graphs.radius(y)) == @inferred(Parallel.radius(g, distmx1)) == 4.2
21-
@test @inferred(Graphs.center(y)) == @inferred(Parallel.center(g, distmx1)) == [2]
10+
for parallel in [:threads, :distributed]
11+
for g in testgraphs(a1)
12+
z = @inferred(Graphs.eccentricity(g, distmx1))
13+
y = @inferred(Parallel.eccentricity(g, distmx1; parallel))
14+
@test isapprox(y, z)
15+
@test @inferred(Graphs.diameter(y)) ==
16+
@inferred(Parallel.diameter(g, distmx1)) ==
17+
6.2
18+
@test @inferred(Graphs.periphery(y)) ==
19+
@inferred(Parallel.periphery(g, distmx1)) ==
20+
[1, 3]
21+
@test @inferred(Graphs.radius(y)) ==
22+
@inferred(Parallel.radius(g, distmx1)) ==
23+
4.2
24+
@test @inferred(Graphs.center(y)) ==
25+
@inferred(Parallel.center(g, distmx1)) ==
26+
[2]
27+
end
2228
end
2329

24-
for g in testdigraphs(a2)
25-
z = @inferred(Graphs.eccentricity(g, distmx2))
26-
y = @inferred(Parallel.eccentricity(g, distmx2))
27-
@test isapprox(y, z)
28-
@test @inferred(Graphs.diameter(y)) ==
29-
@inferred(Parallel.diameter(g, distmx2)) ==
30-
6.2
31-
@test @inferred(Graphs.periphery(y)) ==
32-
@inferred(Parallel.periphery(g, distmx2)) ==
33-
[1]
34-
@test @inferred(Graphs.radius(y)) == @inferred(Parallel.radius(g, distmx2)) == 4.2
35-
@test @inferred(Graphs.center(y)) == @inferred(Parallel.center(g, distmx2)) == [2]
30+
for parallel in [:threads, :distributed]
31+
for g in testdigraphs(a2)
32+
z = @inferred(Graphs.eccentricity(g, distmx2))
33+
y = @inferred(Parallel.eccentricity(g, distmx2; parallel))
34+
@test isapprox(y, z)
35+
@test @inferred(Graphs.diameter(y)) ==
36+
@inferred(Parallel.diameter(g, distmx2)) ==
37+
6.2
38+
@test @inferred(Graphs.periphery(y)) ==
39+
@inferred(Parallel.periphery(g, distmx2)) ==
40+
[1]
41+
@test @inferred(Graphs.radius(y)) ==
42+
@inferred(Parallel.radius(g, distmx2)) ==
43+
4.2
44+
@test @inferred(Graphs.center(y)) ==
45+
@inferred(Parallel.center(g, distmx2)) ==
46+
[2]
47+
end
3648
end
3749
end

test/parallel/shortestpaths/dijkstra.jl

Lines changed: 58 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,44 @@
66
g3 = path_graph(5)
77
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]
88

9-
for g in testgraphs(g3)
10-
z = floyd_warshall_shortest_paths(g, d)
11-
zp = @inferred(Parallel.dijkstra_shortest_paths(g, collect(1:5), d))
12-
@test all(isapprox(z.dists, zp.dists))
9+
for parallel in [:threads, :distributed]
10+
for g in testgraphs(g3)
11+
z = floyd_warshall_shortest_paths(g, d)
12+
zp = @inferred(Parallel.dijkstra_shortest_paths(g, collect(1:5), d; parallel))
13+
@test all(isapprox(z.dists, zp.dists))
1314

14-
for i in 1:5
15-
state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true)
16-
for j in 1:5
17-
if zp.parents[i, j] != 0
18-
@test zp.parents[i, j] in state.predecessors[j]
15+
for i in 1:5
16+
state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true)
17+
for j in 1:5
18+
if zp.parents[i, j] != 0
19+
@test zp.parents[i, j] in state.predecessors[j]
20+
end
1921
end
2022
end
21-
end
2223

23-
z = floyd_warshall_shortest_paths(g)
24-
zp = @inferred(Parallel.dijkstra_shortest_paths(g))
25-
@test all(isapprox(z.dists, zp.dists))
24+
z = floyd_warshall_shortest_paths(g)
25+
zp = @inferred(Parallel.dijkstra_shortest_paths(g; parallel))
26+
@test all(isapprox(z.dists, zp.dists))
2627

27-
for i in 1:5
28-
state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true)
29-
for j in 1:5
30-
if zp.parents[i, j] != 0
31-
@test zp.parents[i, j] in state.predecessors[j]
28+
for i in 1:5
29+
state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true)
30+
for j in 1:5
31+
if zp.parents[i, j] != 0
32+
@test zp.parents[i, j] in state.predecessors[j]
33+
end
3234
end
3335
end
34-
end
3536

36-
z = floyd_warshall_shortest_paths(g)
37-
zp = @inferred(Parallel.dijkstra_shortest_paths(g, [1, 2]))
38-
@test all(isapprox(z.dists[1:2, :], zp.dists))
37+
z = floyd_warshall_shortest_paths(g)
38+
zp = @inferred(Parallel.dijkstra_shortest_paths(g, [1, 2]; parallel))
39+
@test all(isapprox(z.dists[1:2, :], zp.dists))
3940

40-
for i in 1:2
41-
state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true)
42-
for j in 1:5
43-
if zp.parents[i, j] != 0
44-
@test zp.parents[i, j] in state.predecessors[j]
41+
for i in 1:2
42+
state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true)
43+
for j in 1:5
44+
if zp.parents[i, j] != 0
45+
@test zp.parents[i, j] in state.predecessors[j]
46+
end
4547
end
4648
end
4749
end
@@ -51,42 +53,44 @@
5153
g3 = path_digraph(5)
5254
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])
5355

54-
for g in testdigraphs(g3)
55-
z = floyd_warshall_shortest_paths(g, d)
56-
zp = @inferred(Parallel.dijkstra_shortest_paths(g, collect(1:5), d))
57-
@test all(isapprox(z.dists, zp.dists))
56+
for parallel in [:threads, :distributed]
57+
for g in testdigraphs(g3)
58+
z = floyd_warshall_shortest_paths(g, d)
59+
zp = @inferred(Parallel.dijkstra_shortest_paths(g, collect(1:5), d; parallel))
60+
@test all(isapprox(z.dists, zp.dists))
5861

59-
for i in 1:5
60-
state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true)
61-
for j in 1:5
62-
if z.parents[i, j] != 0
63-
@test zp.parents[i, j] in state.predecessors[j]
62+
for i in 1:5
63+
state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true)
64+
for j in 1:5
65+
if z.parents[i, j] != 0
66+
@test zp.parents[i, j] in state.predecessors[j]
67+
end
6468
end
6569
end
66-
end
6770

68-
z = floyd_warshall_shortest_paths(g)
69-
zp = @inferred(Parallel.dijkstra_shortest_paths(g))
70-
@test all(isapprox(z.dists, zp.dists))
71+
z = floyd_warshall_shortest_paths(g)
72+
zp = @inferred(Parallel.dijkstra_shortest_paths(g; parallel))
73+
@test all(isapprox(z.dists, zp.dists))
7174

72-
for i in 1:5
73-
state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true)
74-
for j in 1:5
75-
if zp.parents[i, j] != 0
76-
@test zp.parents[i, j] in state.predecessors[j]
75+
for i in 1:5
76+
state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true)
77+
for j in 1:5
78+
if zp.parents[i, j] != 0
79+
@test zp.parents[i, j] in state.predecessors[j]
80+
end
7781
end
7882
end
79-
end
8083

81-
z = floyd_warshall_shortest_paths(g)
82-
zp = @inferred(Parallel.dijkstra_shortest_paths(g, [1, 2]))
83-
@test all(isapprox(z.dists[1:2, :], zp.dists))
84+
z = floyd_warshall_shortest_paths(g)
85+
zp = @inferred(Parallel.dijkstra_shortest_paths(g, [1, 2]; parallel))
86+
@test all(isapprox(z.dists[1:2, :], zp.dists))
8487

85-
for i in 1:2
86-
state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true)
87-
for j in 1:5
88-
if zp.parents[i, j] != 0
89-
@test zp.parents[i, j] in state.predecessors[j]
88+
for i in 1:2
89+
state = Graphs.dijkstra_shortest_paths(g, i; allpaths=true)
90+
for j in 1:5
91+
if zp.parents[i, j] != 0
92+
@test zp.parents[i, j] in state.predecessors[j]
93+
end
9094
end
9195
end
9296
end

0 commit comments

Comments
 (0)