diff --git a/Project.toml b/Project.toml index 44adc690..057d5355 100644 --- a/Project.toml +++ b/Project.toml @@ -11,6 +11,7 @@ DiffEqDiffTools = "01453d9d-ee7c-5054-8395-0335cb756afa" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" VertexSafeGraphs = "19fa3120-7c27-5ec5-8db8-b0b0aa330d6f" diff --git a/src/SparseDiffTools.jl b/src/SparseDiffTools.jl index f60af7cd..192f0065 100644 --- a/src/SparseDiffTools.jl +++ b/src/SparseDiffTools.jl @@ -13,6 +13,8 @@ import Core: SSAValue export contract_color, greedy_d1, + greedy_star1_coloring, + greedy_star2_coloring, matrix2graph, matrix_colors, forwarddiff_color_jacobian!, @@ -33,6 +35,8 @@ export contract_color, include("coloring/high_level.jl") include("coloring/contraction_coloring.jl") include("coloring/greedy_d1_coloring.jl") +include("coloring/greedy_star1_coloring.jl") +include("coloring/greedy_star2_coloring.jl") include("coloring/matrix2graph.jl") include("differentiation/compute_jacobian_ad.jl") include("differentiation/jaches_products.jl") diff --git a/src/coloring/greedy_d1_coloring.jl b/src/coloring/greedy_d1_coloring.jl index d85acbfa..41719b74 100644 --- a/src/coloring/greedy_d1_coloring.jl +++ b/src/coloring/greedy_d1_coloring.jl @@ -1,5 +1,5 @@ """ - GreedyD1 Coloring + greedy_d1_coloring Find a coloring of a given input graph such that no two vertices connected by an edge have the same @@ -7,24 +7,24 @@ color using greedy approach. The number of colors used may be equal or greater than the chromatic number χ(G) of the graph. """ -function color_graph(G::VSafeGraph,alg::GreedyD1Color) - V = nv(G) - result = zeros(Int64, V) +function color_graph(g::VSafeGraph, alg::GreedyD1Color) + v = nv(g) + result = zeros(Int64, v) result[1] = 1 - available = zeros(Int64, V) - for i = 2:V - for j in inneighbors(G, i) + available = BitArray(undef, v) + for i = 2:v + for j in inneighbors(g, i) if result[j] != 0 - available[result[j]] = 1 + available[result[j]] = true end end - for cr = 1:V - if available[cr] == 0 + for cr = 1:v + if available[cr] == false result[i] = cr break end end - available = zeros(Int64, V) + fill!(available, false) end return result end diff --git a/src/coloring/greedy_star1_coloring.jl b/src/coloring/greedy_star1_coloring.jl new file mode 100644 index 00000000..fa545501 --- /dev/null +++ b/src/coloring/greedy_star1_coloring.jl @@ -0,0 +1,67 @@ +""" + greedy_star1_coloring + + Find a coloring of a given input graph such that + no two vertices connected by an edge have the same + color using greedy approach. The number of colors + used may be equal or greater than the chromatic + number `χ(G)` of the graph. + + A star coloring is a special type of distance - 1 coloring, + For a coloring to be called a star coloring, it must satisfy + two conditions: + + 1. every pair of adjacent vertices receives distinct colors + (a distance-1 coloring) + + 2. For any vertex v, any color that leads to a two-colored path + involving v and three other vertices is impermissible for v. + In other words, every path on four vertices uses at least three + colors. + + Reference: Gebremedhin AH, Manne F, Pothen A. **What color is your Jacobian? Graph coloring for computing derivatives.** SIAM review. 2005;47(4):629-705. +""" +function color_graph(g::LightGraphs.AbstractGraph, ::GreedyStar1Color) + v = nv(g) + color = zeros(Int64, v) + + forbidden_colors = zeros(Int64, v+1) + + for vertex_i = vertices(g) + + for w in inneighbors(g, vertex_i) + if color[w] != 0 + forbidden_colors[color[w]] = vertex_i + end + + for x in inneighbors(g, w) + if color[x] != 0 + if color[w] == 0 + forbidden_colors[color[x]] = vertex_i + else + for y in inneighbors(g, x) + if color[y] != 0 + if y != w && color[y] == color[w] + forbidden_colors[color[x]] = vertex_i + break + end + end + end + end + end + end + end + + color[vertex_i] = find_min_color(forbidden_colors, vertex_i) + end + + color +end + +function find_min_color(forbidden_colors::AbstractVector, vertex_i::Integer) + c = 1 + while (forbidden_colors[c] == vertex_i) + c+=1 + end + c +end diff --git a/src/coloring/greedy_star2_coloring.jl b/src/coloring/greedy_star2_coloring.jl new file mode 100644 index 00000000..1820e21a --- /dev/null +++ b/src/coloring/greedy_star2_coloring.jl @@ -0,0 +1,57 @@ +""" + greedy_star2_coloring + + Find a coloring of a given input graph such that + no two vertices connected by an edge have the same + color using greedy approach. The number of colors + used may be equal or greater than the chromatic + number `χ(G)` of the graph. + + A star coloring is a special type of distance - 1 coloring, + For a coloring to be called a star coloring, it must satisfy + two conditions: + + 1. every pair of adjacent vertices receives distinct colors + (a distance-1 coloring) + + 2. For any vertex v, any color that leads to a two-colored path + involving v and three other vertices is impermissible for v. + In other words, every path on four vertices uses at least three + colors. + + Reference: Gebremedhin AH, Manne F, Pothen A. **What color is your Jacobian? Graph coloring for computing derivatives.** SIAM review. 2005;47(4):629-705. + + TODO: add text explaining the difference between star1 and + star2 +""" +function color_graph(g::LightGraphs.AbstractGraph, :: GreedyStar2Color) + v = nv(g) + color = zeros(Int64, v) + + forbidden_colors = zeros(Int64, v+1) + + for vertex_i = vertices(g) + + for w in inneighbors(g, vertex_i) + if color[w] != 0 + forbidden_colors[color[w]] = vertex_i + end + + for x in inneighbors(g, w) + if color[x] != 0 + if color[w] == 0 + forbidden_colors[color[x]] = vertex_i + else + if color[x] < color[w] + forbidden_colors[color[x]] = vertex_i + end + end + end + end + end + + color[vertex_i] = find_min_color(forbidden_colors, vertex_i) + end + + color +end diff --git a/src/coloring/high_level.jl b/src/coloring/high_level.jl index 5d53a268..ed6c4536 100644 --- a/src/coloring/high_level.jl +++ b/src/coloring/high_level.jl @@ -2,6 +2,8 @@ abstract type ColoringAlgorithm end struct GreedyD1Color <: ColoringAlgorithm end struct BSCColor <: ColoringAlgorithm end struct ContractionColor <: ColoringAlgorithm end +struct GreedyStar1Color <: ColoringAlgorithm end +struct GreedyStar2Color <: ColoringAlgorithm end """ matrix_colors(A,alg::ColoringAlgorithm = GreedyD1Color()) diff --git a/test/runtests.jl b/test/runtests.jl index fd0df5d3..1aeb06dd 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,9 +4,11 @@ using Test @testset "Exact coloring via contraction" begin include("test_contraction.jl") end @testset "Greedy distance-1 coloring" begin include("test_greedy_d1.jl") end +@testset "Greedy star coloring" begin include("test_greedy_star.jl") end @testset "Matrix to graph conversion" begin include("test_matrix2graph.jl") end @testset "AD using color vector" begin include("test_ad.jl") end @testset "Integration test" begin include("test_integration.jl") end @testset "Special matrices" begin include("test_specialmatrices.jl") end @testset "Jac Vecs and Hes Vecs" begin include("test_jaches_products.jl") end @testset "Program sparsity computation" begin include("program_sparsity/testall.jl") end + diff --git a/test/test_greedy_star.jl b/test/test_greedy_star.jl new file mode 100644 index 00000000..807d5bea --- /dev/null +++ b/test/test_greedy_star.jl @@ -0,0 +1,104 @@ +using SparseDiffTools +using LightGraphs +using Random + +Random.seed!(123) + +#= Test data =# +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 + +#= + Coloring needs to satisfy two conditions: + +1. every pair of adjacent vertices receives distinct colors +(a distance-1 coloring) + +2. For any vertex v, any color that leads to a two-colored path +involving v and three other vertices is impermissible for v. +In other words, every path on four vertices uses at least three +colors. +=# + + +#Sample graph from Gebremedhin AH, Manne F, Pothen A. **What color is your Jacobian? Graph coloring for computing derivatives.** + +#= + (2) + / \ + / \ + (1)----(3)----(4) + +=# + +gx = SimpleGraph(4) + +add_edge!(gx,1,2) +add_edge!(gx,1,3) +add_edge!(gx,2,3) +add_edge!(gx,3,4) + +push!(test_graphs, gx) + +#begin testing +for i in 1:6 + g = test_graphs[i] + + out_colors1 = SparseDiffTools.color_graph(g,SparseDiffTools.GreedyStar1Color()) + out_colors2 = SparseDiffTools.color_graph(g,SparseDiffTools.GreedyStar2Color()) + + #test condition 1 + for v = vertices(g) + color = out_colors1[v] + for j in inneighbors(g, v) + @test out_colors1[j] != color + end + end + + #test condition 2 + for j = vertices(g) + walk = LightGraphs.saw(g, j, 4) + walk_colors = zeros(Int64, 0) + if length(walk) >= 4 + for t in walk + push!(walk_colors, out_colors1[t]) + end + @test length(unique(walk_colors)) >= 3 + end + end + + #test condition 1 + for v = vertices(g) + color = out_colors2[v] + for j in inneighbors(g, v) + @test out_colors2[j] != color + end + end + + #test condition 2 + for j = vertices(g) + walk = LightGraphs.saw(g, j, 4) + walk_colors = zeros(Int64, 0) + if length(walk) >= 4 + for t in walk + push!(walk_colors, out_colors2[t]) + end + @test length(unique(walk_colors)) >= 3 + end + end + +end