diff --git a/src/SparseDiffTools.jl b/src/SparseDiffTools.jl index c56ea3c9..7e3a8b42 100644 --- a/src/SparseDiffTools.jl +++ b/src/SparseDiffTools.jl @@ -34,6 +34,7 @@ export contract_color, include("coloring/high_level.jl") +include("coloring/backtracking_coloring.jl") include("coloring/contraction_coloring.jl") include("coloring/greedy_d1_coloring.jl") include("coloring/greedy_star1_coloring.jl") diff --git a/src/coloring/backtracking_coloring.jl b/src/coloring/backtracking_coloring.jl new file mode 100644 index 00000000..4f4e8cb3 --- /dev/null +++ b/src/coloring/backtracking_coloring.jl @@ -0,0 +1,235 @@ +using LightGraphs + +""" + color_graph(g::LightGraphs, ::BacktrackingColor) + +Returns a tight, distance-1 coloring of graph g +using the minimum number of colors possible (i.e. +the chromatic number of graph, χ(g)) +""" +function color_graph(g::LightGraphs.AbstractGraph, ::BacktrackingColor) + v = nv(g) + + #A is list of vertices in non-increasing order of degree + A = sort_by_degree(g) + + #F is the coloring of vertices, 0 means uncolored + #Fopt is the optimal coloring of the graph + F = zeros(Int, v) + Fopt= zeros(Int, v) + + start = 1 + + #optimal color number + opt = v + 1 + + #current vertex to be colored + x = A[1] + + #colors[j] = number of colors in A[0]...A[j] + #assume colors[0] = 1 + colors = zeros(Int, v) + + #set of free colors + U = zeros(Int, 0) + push!(U, 1) + + #set of free colors of x + freeColors = [Vector{Int}() for _ in 1:v] + freeColors[x] = copy(U) + + while (start >= 1) + + back = false + for i = start:v + if i > start + x = uncolored_vertex_of_maximal_degree(A,F) + U = free_colors(x, A, colors, F, g, opt) + sort!(U) + end + if length(U) > 0 + k = U[1] + F[x] = k + cp = F[x] + deleteat!(U, 1) + freeColors[x] = copy(U) + if i==1 + l = 0 + else + l = colors[i-1] + end + colors[i] = max(k, l) + else + start = i-1 + back = true + break + end + end + + if back + if start >= 1 + x = A[start] + F[x] = 0 + U = freeColors[x] + end + else + Fopt = copy(F) + opt = colors[v-1] + i = least_index(F,A,opt) + start = i-1 + if start < 1 + break + end + + #uncolor all vertices A[i] with i >= start + uncolor_all!(F, A, start) + + for i = 1:start+1 + x = A[i] + U = freeColors[x] + + #remove colors >= opt from U + U = remove_higher_colors(U, opt) + freeColors[x] = copy(U) + end + end + end + return Fopt +end + +""" + sort_by_degree(g::LightGraphs.AbstractGraph) + +Returns a list of the vertices of graph g sorted +in non-increasing order of their degrees +""" +function sort_by_degree(g::LightGraphs.AbstractGraph) + vs = vertices(g) + degrees = (LightGraphs.degree(g, v) for v in vs) + vertex_pairs = collect(zip(vs, degrees)) + sort!(vertex_pairs, by = p -> p[2], rev = true) + return [v[1] for v in vertex_pairs] +end + +""" + uncolored_vertex_of_maximal_degree(A::AbstractVector{<:Integer},F::AbstractVector{<:Integer}) + +Returns an uncolored vertex from the partially +colored graph which has the highest degree +""" +function uncolored_vertex_of_maximal_degree(A::AbstractVector{<:Integer},F::AbstractVector{<:Integer}) + for v in A + if F[v] == 0 + return v + end + end +end + + +""" + free_colors(x::Integer, + A::AbstractVector{<:Integer}, + colors::AbstractVector{<:Integer}, + F::Array{Integer,1}, + g::LightGraphs.AbstractGraph, + opt::Integer) + +Returns set of free colors of x which are less +than optimal color number (opt) + +Arguments: + +x: Vertex who's set of free colors is to be calculated +A: List of vertices of graph g sorted in non-increasing order of degree +colors: colors[i] stores the number of distinct colors used in the + coloring of vertices A[0], A[1]... A[i-1] +F: F[i] stores the color of vertex i +g: Graph to be colored +opt: Current optimal number of colors to be used in the coloring of graph g +""" +function free_colors(x::Integer, + A::AbstractVector{<:Integer}, + colors::AbstractVector{<:Integer}, + F::Array{Integer,1}, + g::LightGraphs.AbstractGraph, + opt::Integer) + index = -1 + + freecolors = zeros(Int, 0) + + for i in eachindex(A) + if A[i] == x + index = i + break + end + end + + if index == 1 + colors_used = 0 + else + colors_used = colors[index-1] + end + + colors_used += 1 + for c = 1:colors_used + c_allowed = true + for w in inneighbors(g, x) + if F[w] == c + c_allowed = false + break + end + end + if c_allowed && c < opt + push!(freecolors, c) + end + end + + return freecolors + +end + +""" + least_index(F::AbstractVector{<:Integer}, A::AbstractVector{<:Integer}, opt::Integer) + +Returns least index i such that color of vertex +A[i] is equal to `opt` (optimal color number) +""" +function least_index(F::AbstractVector{<:Integer}, A::AbstractVector{<:Integer}, opt::Integer) + for i in eachindex(A) + if F[A[i]] == opt + return i + end + end +end + +""" + uncolor_all(F::AbstractVector{<:Integer}, A::AbstractVector{<:Integer}, start::Integer) + +Uncolors all vertices A[i] where i is +greater than or equal to start +""" +function uncolor_all!(F::AbstractVector{<:Integer}, A::AbstractVector{<:Integer}, start::Integer) + for i = start:length(A) + F[A[i]] = 0 + end +end + +""" + remove_higher_colors(U::AbstractVector{<:Integer}, opt::Integer) + +Remove all the colors which are greater than or +equal to the `opt` (optimal color number) from +the set of colors U +""" +function remove_higher_colors(U::AbstractVector{<:Integer}, opt::Integer) + if length(U) == 0 + return U + end + u = zeros(Int, 0) + for color in U + if color < opt + push!(u, color) + end + end + return u +end diff --git a/src/coloring/bsc_algo.jl b/src/coloring/bsc_algo.jl deleted file mode 100644 index 20b0e3ca..00000000 --- a/src/coloring/bsc_algo.jl +++ /dev/null @@ -1,172 +0,0 @@ -""" - BSCColor - - Backtracking Sequential Coloring algorithm -""" -function color_graph(G::VSafeGraph,::BSCColor) - V = nv(G) - F = zeros(Int, V) - freeColors = [Vector{Int}() for _ in 1:V] #set of free colors for each vertex - U = zeros(Int, 0) #stores set of free colors - #F = zeros(Int, 0) #stores final coloring of vertices - - sorted_vertices = order_by_degree(G) - start = 1 - optColorNumber = V + 1 - x = sorted_vertices[1] - colors[0] = 0 - push!(U, 1) - freeColors[x] = U - - while (start >= 1) - back = false - for i = 1:V - if i > start - x = find_uncolored_vertex(sorted_vertices, F) - U = free_colors(x,F,G,optColorNumber) - sort(U) - end - if length(U) > 0 - k = U[1] - F[x] = k - deleteat!(U,1) - freeColors[x] = copy(U) - l = colors[i-1] - colors[i] = max(k,l) - else - start = i - 1 - back = true - break - end - end - if back - if start >= 1 - x = A[start] - F[x] = 0 #uncolor x - U = freeColors[x] - end - else - F_opt = F - optColorNumber = colors[V-1] - i = least_index(sorted_vertices,optColorNumber,G) - start = i - 1 - if start < 1 - break #leave the while loop - end - uncolor_all(F, sorted_vertices, start, G) - for i = 0:start - x = A[i] - U = freeColors[x] - U = remove_colors(U, optColorNumber) - freeColors[x] = copy(U) - end - end - end - return F_opt -end - - -""" - vertex_degree(G,z) - -Find the degree of the vertex z which belongs to the graph G. -""" -function degree(G::VSafeGraph,z::Int) - return length(inneighbors(G,z)) -end - - -function sorted_vertices(G::VSafeGraph) - V = nv(G) - marked = zeros(Int,V) - sv = zeros(Int,0) - max_degree = -1 - max_degree_vertex = -1 - for i = 1:V - max_degree = -1 - max_degree_vertex = -1 - for j = 1:V - if j != i - if degree(G,j) > max_degree && marked[j] == 0 - max_degree = degree(G,j) - max_degree_vertex = j - end - end - end - push!(sv,max_degree_vertex) - marked[max_degree_vertex] = 1 - end - return sv -end - -#find uncolored vertex of maximal degree of saturation -function find_uncolored_vertex(sv::Array{Int,1}, G::VSafeGraph) - colors = zeros(Int,0) - max_colors = -1 - max_color_index = -1 - for i = 1:nv(G) - if F[i] != 0 - for j in inneighbors(G,i) - if F[j] != 0 && F[j] in colors == false - push!(colors, F[j]) - end - end - if length(colors) > max_colors - max_colors = length(colors) - max_color_index = i - end - end - colors = zeros(Int,0) - end - for i = 1:nv(G) - if A[i] == max_color_index - return i - end - end - -end - -#set of free colors of x, which are < optColorNumber -function free_colors(x::Int, F::Array{Int,1}, G::VSafeGraph, max_color::Int) - colors = zeros(Int,0) - for color in 1:max_color - present = true - for y in inneighbors(G,x) - if F[y] == color - present = false - break - end - end - if present - push!(colors,color) - end - end - return colors -end - -#least index with F(A[i]) = optColorNumber -function least_index(A::Array{Int, 1}, F::Array{Int,1}, optColorNumber::Int, G::VSafeGraph) - for i = 1:nv(G) - if F[A[i]] == optColorNumber - return i - end - end -end - -#uncolor all vertices A[i] with i >= start -function uncolor_all(F::Array{Int,1}, A::Array{Int,1}, start::Int, G::VSafeGraph) - for i = start:nv(G) - F[A[i]] = 0 - end -end - -#remove from U all colors >= optColorNumber -function remove_colors(U::Array{Int,1}, optColorNumber::Int) - modified_U = zeros(Int,0) - for i = 1:length(U) - if U[i] < optColorNumber - push!(mmodified_U, U[i]) - end - end - return modified_U -end diff --git a/src/coloring/high_level.jl b/src/coloring/high_level.jl index ed6c4536..08978e03 100644 --- a/src/coloring/high_level.jl +++ b/src/coloring/high_level.jl @@ -1,6 +1,6 @@ abstract type ColoringAlgorithm end struct GreedyD1Color <: ColoringAlgorithm end -struct BSCColor <: ColoringAlgorithm end +struct BacktrackingColor <: ColoringAlgorithm end struct ContractionColor <: ColoringAlgorithm end struct GreedyStar1Color <: ColoringAlgorithm end struct GreedyStar2Color <: ColoringAlgorithm end diff --git a/test/test_bsc.jl b/test/test_bsc.jl new file mode 100644 index 00000000..08305398 --- /dev/null +++ b/test/test_bsc.jl @@ -0,0 +1,131 @@ +using SparseDiffTools +using LightGraphs +using Random +#= + Graph g0 +vertex nuumber followed by color in parentheses + + 6(3) + / \ + / \ + / \ + 5(1)--------3(2) + | \ | + | \ | + | \ | + | 1(2)----2(1) + | / + | / + 4(3) +=# + +g0 = SimpleGraph(6) +add_edge!(g0, 1,2) +add_edge!(g0, 1,4) +add_edge!(g0, 1,5) +add_edge!(g0, 3,2) +add_edge!(g0, 3,5) +add_edge!(g0, 3,6) +add_edge!(g0, 4,5) +add_edge!(g0, 5,6) + + +#= + Graph g1 + + 1 + | + | + 5----2----3 + / \ + / \ + 6 4 + +=# +g1 = SimpleGraph(6) +add_edge!(g1, 2,1) +add_edge!(g1, 3,2) +add_edge!(g1, 4,2) +add_edge!(g1, 5,2) +add_edge!(g1, 6,2) + + +#= + Graph g2 + + 1------2 + / | / | + / | / | + / | / | + 3----4------5 + +=# +g2 = SimpleGraph(5) +add_edge!(g2, 1,2) +add_edge!(g2, 1,3) +add_edge!(g2, 1,4) +add_edge!(g2, 4,2) +add_edge!(g2, 5,2) +add_edge!(g2, 3,4) +add_edge!(g2, 4,5) + +#test custom graphs first +coloring0 = SparseDiffTools.color_graph(g0, SparseDiffTools.BacktrackingColor()) +coloring1 = SparseDiffTools.color_graph(g1, SparseDiffTools.BacktrackingColor()) +coloring2 = SparseDiffTools.color_graph(g2, SparseDiffTools.BacktrackingColor()) + +for v = 1:nv(g0) + color = coloring0[v] + for j in inneighbors(g0, v) + if coloring0[j] == color + @test false + end + end +end + +for v = 1:nv(g1) + color = coloring1[v] + for j in inneighbors(g1, v) + if coloring1[j] == color + @test false + end + end +end + +for v = 1:nv(g2) + color = coloring2[v] + for j in inneighbors(g2, v) + if coloring2[j] == color + @test false + end + end +end + +test_graphs = Array{SimpleGraph, 1}(undef, 0) +for _ in 1:5 + nv = rand(5:20) + ne = rand(1:100) + graph = SimpleGraph(nv) + for e in 1:ne + v1 = rand(1:nv) + v2 = rand(1:nv) + while v1 == v2 + v2 = rand(1:nv) + end + add_edge!(graph, v1, v2) + end + push!(test_graphs, copy(graph)) +end + + +for i in 1:5 + g = test_graphs[i] + out_colors = SparseDiffTools.color_graph(g,SparseDiffTools.BacktrackingColor()) + + for v = vertices(g) + color = out_colors[v] + for j in inneighbors(g, v) + @test out_colors[j] != color + end + end +end