diff --git a/src/LightGraphs.jl b/src/LightGraphs.jl index 5a6a23278..f3216b114 100644 --- a/src/LightGraphs.jl +++ b/src/LightGraphs.jl @@ -62,6 +62,9 @@ randomwalk, saw, non_backtracking_randomwalk, # diffusion diffusion, diffusion_rate, +# coloring +greedy_color, + # connectivity connected_components, strongly_connected_components, weakly_connected_components, is_connected, is_strongly_connected, is_weakly_connected, period, @@ -185,6 +188,7 @@ include("digraph/cycles/hadwick-james.jl") include("digraph/cycles/karp.jl") include("traversals/bfs.jl") include("traversals/bipartition.jl") +include("traversals/greedy_color.jl") include("traversals/parallel_bfs.jl") include("traversals/dfs.jl") include("traversals/maxadjvisit.jl") diff --git a/src/traversals/greedy_color.jl b/src/traversals/greedy_color.jl new file mode 100644 index 000000000..590018375 --- /dev/null +++ b/src/traversals/greedy_color.jl @@ -0,0 +1,155 @@ +""" + struct coloring{T} + +Store number of colors used and mapping from vertex to color +""" +struct coloring{T<:Integer} + num_colors::T + colors::Vector{T} +end + +best_color(c1::coloring, c2::coloring) = c1.num_colors < c2.num_colors ? c1 : c2 + +""" + perm_greedy_color(g, seq) + +Color graph `g` according to an order specified by `seq` using a greedy heuristic. +seq[i] = v imples that vertex v is the ith vertex to be colored. +""" +function perm_greedy_color( + g::AbstractGraph, + seq::Vector{T} + ) where T <: Integer + + nvg::T = nv(g) + cols = Vector{T}(nvg) + seen = zeros(Bool, nvg + 1) + + for v in seq + seen[v] = true + colors_used = zeros(Bool, nvg) + + for w in neighbors(g, v) + if seen[w] + colors_used[Int(cols[w])] = true + end + end + + + for i in one(T):nvg + if colors_used[i] == false + cols[v] = i + break; + end + end + end + + result = coloring{T}(maximum(cols), cols) + return result +end + +""" + degree_greedy_color(g) + +Color graph `g` iteratively in the descending order of the degree of the vertices. +""" +function degree_greedy_color(g::AbstractGraph{U}) where U<:Integer + seq::Vector{U} = sortperm(degree(g, vertices(g)) , rev=true) + result = perm_greedy_color(g, seq) + return result +end + +""" + parallel_random_greedy_color(g, reps) + +Color graph `g` iteratively in a random order using a greedy heuristic and +choose the best coloring out of `reps` number of colorings computed in parallel. +""" +function parallel_random_greedy_color( + g::AbstractGraph, + reps::Integer + ) + + best::coloring = @parallel (best_color) for i in 1:reps + seq = shuffle(vertices(g)) + perm_greedy_color(g, seq) + end + + return best +end + +""" + seq_random_greedy_color(g, reps) + +Color graph `g` iteratively in a random order using a greedy heuristic +and choose the best coloring out of `reps` such random coloring. +""" +function seq_random_greedy_color( + g::AbstractGraph, + reps::Integer + ) + + seq = shuffle(vertices(g)) + best::coloring = perm_greedy_color(g, seq) + + for i in 2:reps + shuffle!(seq) + best = best_color(best, perm_greedy_color(g, seq)) + end + return best +end + +""" + random_greedy_color(g, reps=1, parallel=false) + +Color graph `g` iteratively in a random order using a greedy heruistic +and choose the best coloring out of `reps` such random coloring. + +If parallel is true then the colorings are executed in parallel. +""" +function random_greedy_color( + g::AbstractGraph, + reps::Integer = 1, + parallel::Bool = false + ) + + if !parallel + return seq_random_greedy_color(g, reps) + else + return parallel_random_greedy_color(g, reps) + end + +end + +""" + greedy_color(g; sort_degree=false, parallel=false, reps = 1) + +Color graph `g` based on [Greedy Coloring Heuristics](https://en.wikipedia.org/wiki/Greedy_coloring) + +The heuristics can be described as choosing a permutation of the vertices and assigning the +lowest color index available iteratively in that order. + +If `sort_degree` is true then the permutation is chosen in reverse sorted order of the degree of the vertices. +`parallel` and `reps` are irrelevant in this case. + +If `sort_degree` is false then `reps` colorings are obtained based on random permutations and the one using least +colors is chosen. + +If `parallel` is true then this function executes coloring in parallel. +""" +function greedy_color( + g::AbstractGraph{U}; + sort_degree::Bool=false, + parallel::Bool = false, + reps::Integer=1, +)::coloring{U} where U <: Integer + + result = coloring(zero(U), zeros(U, nv(g))) + if sort_degree + result = degree_greedy_color(g) + else + result = random_greedy_color(g, reps, parallel) + end + return result +end + diff --git a/test/runtests.jl b/test/runtests.jl index a04239f92..1e419ce4b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -39,6 +39,7 @@ tests = [ "traversals/bfs", "traversals/parallel_bfs", "traversals/bipartition", + "traversals/greedy_color", "traversals/dfs", "traversals/maxadjvisit", "traversals/randomwalks", diff --git a/test/traversals/greedy_color.jl b/test/traversals/greedy_color.jl new file mode 100644 index 000000000..cca525a4e --- /dev/null +++ b/test/traversals/greedy_color.jl @@ -0,0 +1,32 @@ +@testset "Greedy Coloring" begin + + g3 = StarGraph(10) + + for g in testgraphs(g3) + for op1 in (true, false), op2 in (true, false) + C = @inferred(greedy_color(g, reps=50, sort_degree=op1, parallel=op2)) + @test C.num_colors == 2 + end + end + + g4 = PathGraph(20) + g5 = CompleteGraph(20) + + for graph in [g4, g5] + for g in testgraphs(graph) + for op1 in (true, false), op2 in (true, false) + C = @inferred(greedy_color(g, reps=50, sort_degree=op1, parallel=op2)) + + correct = true + C.num_colors > maximum(degree(g))+1 && (correct = false) + for e in edges(g) + C.colors[Int(src(e))] == C.colors[Int(dst(e))] && (correct = false) + end + + @test correct + end + end + end + +end +