From 72fc97ea761a0477335d4a169fb962f744059d6e Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Mon, 29 Jul 2019 23:48:23 +0530 Subject: [PATCH 01/23] acyclic coloring --- src/coloring/acyclic_coloring.jl | 93 ++++++++++++++++++++++++++++++++ src/coloring/high_level.jl | 1 + 2 files changed, 94 insertions(+) create mode 100644 src/coloring/acyclic_coloring.jl diff --git a/src/coloring/acyclic_coloring.jl b/src/coloring/acyclic_coloring.jl new file mode 100644 index 00000000..dfe9ff8f --- /dev/null +++ b/src/coloring/acyclic_coloring.jl @@ -0,0 +1,93 @@ +using LightGraphs + +""" + color_graph(g::LightGraphs.AbstractGraphs, :: AcyclicColoring) + +Returns a coloring vector following the acyclic coloring rules (1) the coloring +corresponds to a distance-1 coloring, and (2) vertices in every cycle of the +graph are assigned at least three distinct colors. This variant of coloring is +called acyclic since every subgraph induced by vertices assigned any two colors +is a collection of trees—and hence is acyclic. +""" +function color_graph(g::LightGraphs.AbstractGraphs, ::AcyclicColoring) + + color = zeros(Int, nv(g)) + forbiddenColors = zeros(Int, nv(g)) + + for v in vertices(g) + #enforces the first condition of acyclic coloring + for w in outneighbors(g, v) + forbiddenColors[color[w]] = v + #enforces the second condition of acyclic coloring + for w in outneighbors(g, v) + if color[w] != 0 #colored neighbor + for x in outneighbors(w) + if color[x] != 0 #colored x + if forbiddenColors[color[x]] != v + prevent_cycle(v, w, x) + end + end + end + end + end + + color[v] = min_index(forbiddenColors, v) + + #grow star for every edge connecting colored vertices v and w + for w in outneighbors(g, v) + if color[w] != 0 + growStar(v, w) + end + end + + #merge the newly formed stars into existing trees if possible + for w in outneighbors(g, v) + if color[w] != 0 + for x in outneighbors(g, w) + if color[x] != 0 && x != v + if color[x] == color[v] + mergeTrees(v, w, x) + end + end + end + end + end + end +end + +""" + prevent_cycle() + +""" +function prevent_cycle(v::Integer, + w::Integer, + x::Integer, + forbiddenColors::AbstractVector{<:Integer}, + firstVisitToTree) + e = Find(w, x) + p, q = firstVisitToTree[e] + if p != v + firstVisitToTree[e] = (v, w) + else if q != w + forbiddenColors[color[x]] = v + end +end + +""" + min_index(forbiddenColors::AbstractVector{<:Integer}, v::Integer) + +Returns min{i > 0 such that forbiddenColors[i] != v} +""" +function min_index(forbiddenColors, v) + for i = 1:length(forbiddenColors) + if forbiddenColors[i] != v + return i + end + end +end + +function growStar(v:: Integer, w::Integer) +end + +function mergeTrees(v:: Integer, w::Integer, x::Integer) +end \ No newline at end of file diff --git a/src/coloring/high_level.jl b/src/coloring/high_level.jl index 08978e03..1ab0ad9a 100644 --- a/src/coloring/high_level.jl +++ b/src/coloring/high_level.jl @@ -4,6 +4,7 @@ struct BacktrackingColor <: ColoringAlgorithm end struct ContractionColor <: ColoringAlgorithm end struct GreedyStar1Color <: ColoringAlgorithm end struct GreedyStar2Color <: ColoringAlgorithm end +struct AcyclicColoring <: ColoringAlgorithm end """ matrix_colors(A,alg::ColoringAlgorithm = GreedyD1Color()) From 5f3e77d6294db6d11db7c9ff3c634dda9cf7e00e Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Wed, 7 Aug 2019 06:39:14 +0530 Subject: [PATCH 02/23] added acylic coloring --- Project.toml | 1 + src/coloring/acyclic_coloring.jl | 86 +++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 12 deletions(-) diff --git a/Project.toml b/Project.toml index b9c7add4..7a8f53d5 100644 --- a/Project.toml +++ b/Project.toml @@ -6,6 +6,7 @@ version = "0.6.0" [deps] BandedMatrices = "aae01518-5342-5314-be14-df237901396f" BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" +DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" DiffEqDiffTools = "01453d9d-ee7c-5054-8395-0335cb756afa" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" LightGraphs = "093fc24a-ae57-5d10-9952-331d41423f4d" diff --git a/src/coloring/acyclic_coloring.jl b/src/coloring/acyclic_coloring.jl index dfe9ff8f..d7e56a65 100644 --- a/src/coloring/acyclic_coloring.jl +++ b/src/coloring/acyclic_coloring.jl @@ -1,4 +1,5 @@ using LightGraphs +using DataStructures """ color_graph(g::LightGraphs.AbstractGraphs, :: AcyclicColoring) @@ -14,17 +15,25 @@ function color_graph(g::LightGraphs.AbstractGraphs, ::AcyclicColoring) color = zeros(Int, nv(g)) forbiddenColors = zeros(Int, nv(g)) + set = DisjointSets{LightGraphs.Edge}([]) + + firstVisitToTree = Array{Tuple{Int, Int}, 1}(undef, ne(g)) + firstNeighbor = Array{Tuple{Int, Int}, 1}(undef, nv(g)) + for v in vertices(g) #enforces the first condition of acyclic coloring for w in outneighbors(g, v) - forbiddenColors[color[w]] = v + if color[w] != 0 + forbiddenColors[color[w]] = v + end + end #enforces the second condition of acyclic coloring for w in outneighbors(g, v) if color[w] != 0 #colored neighbor - for x in outneighbors(w) + for x in outneighbors(g, w) if color[x] != 0 #colored x if forbiddenColors[color[x]] != v - prevent_cycle(v, w, x) + prevent_cycle(v, w, x, g, color, forbiddenColors, firstVisitToTree, set) end end end @@ -36,7 +45,7 @@ function color_graph(g::LightGraphs.AbstractGraphs, ::AcyclicColoring) #grow star for every edge connecting colored vertices v and w for w in outneighbors(g, v) if color[w] != 0 - growStar(v, w) + grow_star(v, w, g, firstNeighbor, set) end end @@ -46,13 +55,15 @@ function color_graph(g::LightGraphs.AbstractGraphs, ::AcyclicColoring) for x in outneighbors(g, w) if color[x] != 0 && x != v if color[x] == color[v] - mergeTrees(v, w, x) + mergeTrees(v, w, x, g, set) end end end end end end + + return color end """ @@ -61,13 +72,18 @@ end """ function prevent_cycle(v::Integer, w::Integer, - x::Integer, + x::Integer, + g, + color, forbiddenColors::AbstractVector{<:Integer}, - firstVisitToTree) - e = Find(w, x) - p, q = firstVisitToTree[e] + firstVisitToTree, + set) + + edge = find_edge(g, w, x) + e = find_root(set, edge) + p, q = firstVisitToTree[edge_index(g, e)] if p != v - firstVisitToTree[e] = (v, w) + firstVisitToTree[edge_index(g, e)] = (v, w) else if q != w forbiddenColors[color[x]] = v end @@ -86,8 +102,54 @@ function min_index(forbiddenColors, v) end end -function growStar(v:: Integer, w::Integer) +function grow_star(v::Integer, + w::Integer, + g, + firstNeighbor, + set) + edge = find_edge(g, v, w) + push!(set, edge) + p, q = firstNeighbor[color[w]] + if p != v + firstNeighbor[color[w]] = (v, w) + else + edge1 = find_edge(g, v, w) + edge2 = find_edge(g, p, q) + e1 = find_root(set, edge1) + e2 = find_root(set, edge2) + union!(set, e1, e2) + end +end + +function mergeTrees(v::Integer, + w::Integer, + x::Integer, + g, + set) + edge1 = find_edge(g, v, w) + edge2 = find_edge(g, w, x) + e1 = find_root(set, edge1) + e2 = find_root(set, edge2) + if (e1 != e2) + union!(set, e1, e2) + end +end + +function find_edge(g, v, w) + for e in edges(g) + if (src(e) == v && dst(e) == w) + return e + end + end + throw(error("$v and $w are not connected in graph g")) end -function mergeTrees(v:: Integer, w::Integer, x::Integer) +function edge_index(g, e) + edge_list = collect(edges(g)) + for i in 1:length(edge_list) + if edge_list[i] == e + return i + end + end + return -1 end \ No newline at end of file From a058acef969bccae0d1b1c64d6d102e8cf3adc49 Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Wed, 7 Aug 2019 06:59:38 +0530 Subject: [PATCH 03/23] added more documentation for functions --- src/coloring/acyclic_coloring.jl | 87 +++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 19 deletions(-) diff --git a/src/coloring/acyclic_coloring.jl b/src/coloring/acyclic_coloring.jl index d7e56a65..7cb5eacf 100644 --- a/src/coloring/acyclic_coloring.jl +++ b/src/coloring/acyclic_coloring.jl @@ -2,7 +2,7 @@ using LightGraphs using DataStructures """ - color_graph(g::LightGraphs.AbstractGraphs, :: AcyclicColoring) + color_graph(g::LightGraphs.AbstractGraphs, ::AcyclicColoring) Returns a coloring vector following the acyclic coloring rules (1) the coloring corresponds to a distance-1 coloring, and (2) vertices in every cycle of the @@ -10,7 +10,7 @@ graph are assigned at least three distinct colors. This variant of coloring is called acyclic since every subgraph induced by vertices assigned any two colors is a collection of trees—and hence is acyclic. """ -function color_graph(g::LightGraphs.AbstractGraphs, ::AcyclicColoring) +function color_graph(g::LightGraphs.AbstractGraph, ::AcyclicColoring) color = zeros(Int, nv(g)) forbiddenColors = zeros(Int, nv(g)) @@ -55,7 +55,7 @@ function color_graph(g::LightGraphs.AbstractGraphs, ::AcyclicColoring) for x in outneighbors(g, w) if color[x] != 0 && x != v if color[x] == color[v] - mergeTrees(v, w, x, g, set) + merge_trees(v, w, x, g, set) end end end @@ -67,17 +67,27 @@ function color_graph(g::LightGraphs.AbstractGraphs, ::AcyclicColoring) end """ - prevent_cycle() - + prevent_cycle(v::Integer, + w::Integer, + x::Integer, + g::LightGraphs.AbstractGraph, + color::AbstractVector{<:Integer}, + forbiddenColors::AbstractVector{<:Integer}, + firstVisitToTree::Array{Tuple{Integer, Integer}, 1}, + set::DisjointSets{LightGraphs.Edge}) + +Subroutine to avoid generation of 2-colored cycle due to coloring of vertex v, +which is adjacent to vertices w and x in graph g. Disjoint set is used to store +the induced 2-colored subgraphs/trees where the id of set is a key edge of g """ function prevent_cycle(v::Integer, w::Integer, x::Integer, - g, - color, + g::LightGraphs.AbstractGraph, + color::AbstractVector{<:Integer}, forbiddenColors::AbstractVector{<:Integer}, - firstVisitToTree, - set) + firstVisitToTree::Array{Tuple{Integer, Integer}, 1}, + set::DisjointSets{LightGraphs.Edge}) edge = find_edge(g, w, x) e = find_root(set, edge) @@ -94,7 +104,8 @@ end Returns min{i > 0 such that forbiddenColors[i] != v} """ -function min_index(forbiddenColors, v) +function min_index(forbiddenColors::AbstractVector{<:Integer}, + v::Integer) for i = 1:length(forbiddenColors) if forbiddenColors[i] != v return i @@ -102,11 +113,24 @@ function min_index(forbiddenColors, v) end end + +""" + grow_star(v::Integer, + w::Integer, + g::LightGraphs.AbstractGraph, + firstNeighbor::Array{Tuple{Integer, Integer}, 1}, + set::DisjointSets{LightGraphs.Edge}) + +Subroutine to grow a 2-colored star after assigning a new color to the +previously uncolored vertex v, by comparing it with the adjacent vertex w. +Disjoint set is used to store stars in sets, which are identified through key +edges present in g. +""" function grow_star(v::Integer, - w::Integer, - g, - firstNeighbor, - set) + w::Integer, + g::LightGraphs.AbstractGraph, + firstNeighbor::Array{Tuple{Integer, Integer}, 1}, + set::DisjointSets{LightGraphs.Edge}) edge = find_edge(g, v, w) push!(set, edge) p, q = firstNeighbor[color[w]] @@ -121,11 +145,22 @@ function grow_star(v::Integer, end end -function mergeTrees(v::Integer, + +""" + merge_trees(v::Integer, + w::Integer, + x::Integer, + g::LightGraphs.AbstractGraph, + set::DisjointSets{LightGraphs.Edge}) + +Subroutine to merge trees present in the disjoint set which have a +common edge. +""" +function merge_trees(v::Integer, w::Integer, x::Integer, - g, - set) + g::LightGraphs.AbstractGraph, + set::DisjointSets{LightGraphs.Edge}) edge1 = find_edge(g, v, w) edge2 = find_edge(g, w, x) e1 = find_root(set, edge1) @@ -135,7 +170,14 @@ function mergeTrees(v::Integer, end end -function find_edge(g, v, w) + +""" + find_edge(g::LightGraphs.AbstractGraph, v::Integer, w::Integer) + +Returns an edge object of the type LightGraphs.Edge which represents the +edge connecting vertices v and w of the undirected graph g +""" +function find_edge(g::LightGraphs.AbstractGraph, v::Integer, w::Integer) for e in edges(g) if (src(e) == v && dst(e) == w) return e @@ -144,7 +186,14 @@ function find_edge(g, v, w) throw(error("$v and $w are not connected in graph g")) end -function edge_index(g, e) +""" + edge_index(g::LightGraphs.AbstractGraph, e::LightGraphs.Edge) + +Returns an Integer value which uniquely identifies the edge e in graph +g. Used as an index in main function to avoid custom arrays with non- +numerical indices. +""" +function edge_index(g::LightGraphs.AbstractGraph, e::LightGraphs.Edge) edge_list = collect(edges(g)) for i in 1:length(edge_list) if edge_list[i] == e From 04e84d761538b7d786720a6251897e80e220aac4 Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Thu, 8 Aug 2019 01:42:06 +0530 Subject: [PATCH 04/23] added reference and modified find_edge function --- src/coloring/acyclic_coloring.jl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/coloring/acyclic_coloring.jl b/src/coloring/acyclic_coloring.jl index 7cb5eacf..8f5d5222 100644 --- a/src/coloring/acyclic_coloring.jl +++ b/src/coloring/acyclic_coloring.jl @@ -9,6 +9,8 @@ corresponds to a distance-1 coloring, and (2) vertices in every cycle of the graph are assigned at least three distinct colors. This variant of coloring is called acyclic since every subgraph induced by vertices assigned any two colors is a collection of trees—and hence is acyclic. + +Reference: Gebremedhin AH, Manne F, Pothen A. **New Acyclic and Star Coloring Algorithms with Application to Computing Hessians** """ function color_graph(g::LightGraphs.AbstractGraph, ::AcyclicColoring) @@ -178,8 +180,9 @@ Returns an edge object of the type LightGraphs.Edge which represents the edge connecting vertices v and w of the undirected graph g """ function find_edge(g::LightGraphs.AbstractGraph, v::Integer, w::Integer) - for e in edges(g) - if (src(e) == v && dst(e) == w) + for v1 in outneighbors(g, v) + if v1 == w + e = LightGraphs.SimpleGraphs.SimpleEdge(v, w) return e end end From d8859a3793a07114adba09b0a3c5dd53a26f59be Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Thu, 8 Aug 2019 19:10:23 +0530 Subject: [PATCH 05/23] added efficient edge-finding method --- src/coloring/acyclic_coloring.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/coloring/acyclic_coloring.jl b/src/coloring/acyclic_coloring.jl index 7cb5eacf..9e4b25a1 100644 --- a/src/coloring/acyclic_coloring.jl +++ b/src/coloring/acyclic_coloring.jl @@ -178,8 +178,9 @@ Returns an edge object of the type LightGraphs.Edge which represents the edge connecting vertices v and w of the undirected graph g """ function find_edge(g::LightGraphs.AbstractGraph, v::Integer, w::Integer) - for e in edges(g) - if (src(e) == v && dst(e) == w) + for v1 in outneighbors(g, v) + if v1 == w + e = LightGraphs.SimpleGraphs.SimpleEdge(v, w) return e end end @@ -201,4 +202,4 @@ function edge_index(g::LightGraphs.AbstractGraph, e::LightGraphs.Edge) end end return -1 -end \ No newline at end of file +end From d10f7227cfafead2d3d107b763fe2c4b49535723 Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Sun, 11 Aug 2019 22:24:57 +0530 Subject: [PATCH 06/23] fixed broken syntax The find_edge method has been reverted back to the old version with a worst case running time of O(size of E) where E is the edge set of graph g. The primary reason is that while the function can be optimized by returning a new edge, the ambiguity between which vertex is the source and which one is the destination breaks the disjoint set function as `Edge 1 => 2` is treated differently compared to `Edge 2=>1`. --- src/coloring/acyclic_coloring.jl | 52 +++++++++++++++++--------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/coloring/acyclic_coloring.jl b/src/coloring/acyclic_coloring.jl index 9e4b25a1..fcbef7e7 100644 --- a/src/coloring/acyclic_coloring.jl +++ b/src/coloring/acyclic_coloring.jl @@ -9,6 +9,8 @@ corresponds to a distance-1 coloring, and (2) vertices in every cycle of the graph are assigned at least three distinct colors. This variant of coloring is called acyclic since every subgraph induced by vertices assigned any two colors is a collection of trees—and hence is acyclic. + +Reference: Gebremedhin AH, Manne F, Pothen A. **New Acyclic and Star Coloring Algorithms with Application to Computing Hessians** """ function color_graph(g::LightGraphs.AbstractGraph, ::AcyclicColoring) @@ -45,7 +47,7 @@ function color_graph(g::LightGraphs.AbstractGraph, ::AcyclicColoring) #grow star for every edge connecting colored vertices v and w for w in outneighbors(g, v) if color[w] != 0 - grow_star(v, w, g, firstNeighbor, set) + grow_star(v, w, g, firstNeighbor, set, color) end end @@ -55,7 +57,7 @@ function color_graph(g::LightGraphs.AbstractGraph, ::AcyclicColoring) for x in outneighbors(g, w) if color[x] != 0 && x != v if color[x] == color[v] - merge_trees(v, w, x, g, set) + mergeTrees(v, w, x, g, set) end end end @@ -81,20 +83,20 @@ which is adjacent to vertices w and x in graph g. Disjoint set is used to store the induced 2-colored subgraphs/trees where the id of set is a key edge of g """ function prevent_cycle(v::Integer, - w::Integer, - x::Integer, - g::LightGraphs.AbstractGraph, - color::AbstractVector{<:Integer}, - forbiddenColors::AbstractVector{<:Integer}, - firstVisitToTree::Array{Tuple{Integer, Integer}, 1}, - set::DisjointSets{LightGraphs.Edge}) + w::Integer, + x::Integer, + g::LightGraphs.AbstractGraph, + color::AbstractVector{<:Integer}, + forbiddenColors::AbstractVector{<:Integer}, + firstVisitToTree::Array{Tuple{Int64, Int64}, 1}, + set::DisjointSets{LightGraphs.Edge}) edge = find_edge(g, w, x) e = find_root(set, edge) p, q = firstVisitToTree[edge_index(g, e)] if p != v firstVisitToTree[edge_index(g, e)] = (v, w) - else if q != w + elseif q != w forbiddenColors[color[x]] = v end end @@ -103,9 +105,8 @@ end min_index(forbiddenColors::AbstractVector{<:Integer}, v::Integer) Returns min{i > 0 such that forbiddenColors[i] != v} -""" -function min_index(forbiddenColors::AbstractVector{<:Integer}, - v::Integer) +""" +function min_index(forbiddenColors::AbstractVector{<:Integer}, v::Integer) for i = 1:length(forbiddenColors) if forbiddenColors[i] != v return i @@ -113,11 +114,10 @@ function min_index(forbiddenColors::AbstractVector{<:Integer}, end end - """ grow_star(v::Integer, w::Integer, - g::LightGraphs.AbstractGraph, + g::LightGraphs.AbstractGraph firstNeighbor::Array{Tuple{Integer, Integer}, 1}, set::DisjointSets{LightGraphs.Edge}) @@ -129,8 +129,9 @@ edges present in g. function grow_star(v::Integer, w::Integer, g::LightGraphs.AbstractGraph, - firstNeighbor::Array{Tuple{Integer, Integer}, 1}, - set::DisjointSets{LightGraphs.Edge}) + firstNeighbor::Array{Tuple{Int64, Int64}, 1}, + set::DisjointSets{LightGraphs.Edge}, + color::AbstractVector{<: Integer}) edge = find_edge(g, v, w) push!(set, edge) p, q = firstNeighbor[color[w]] @@ -156,7 +157,7 @@ end Subroutine to merge trees present in the disjoint set which have a common edge. """ -function merge_trees(v::Integer, +function mergeTrees(v::Integer, w::Integer, x::Integer, g::LightGraphs.AbstractGraph, @@ -177,10 +178,11 @@ end Returns an edge object of the type LightGraphs.Edge which represents the edge connecting vertices v and w of the undirected graph g """ -function find_edge(g::LightGraphs.AbstractGraph, v::Integer, w::Integer) - for v1 in outneighbors(g, v) - if v1 == w - e = LightGraphs.SimpleGraphs.SimpleEdge(v, w) +function find_edge(g::LightGraphs.AbstractGraph, + v::Integer, + w::Integer) + for e in edges(g) + if (src(e) == v && dst(e) == w) || (src(e) == w && dst(e) == v) return e end end @@ -194,12 +196,14 @@ Returns an Integer value which uniquely identifies the edge e in graph g. Used as an index in main function to avoid custom arrays with non- numerical indices. """ -function edge_index(g::LightGraphs.AbstractGraph, e::LightGraphs.Edge) +function edge_index(g::LightGraphs.AbstractGraph, + e::LightGraphs.Edge) edge_list = collect(edges(g)) for i in 1:length(edge_list) if edge_list[i] == e return i end end - return -1 + throw(error("Edge $e is not present in graph g")) + #return -1 end From 752c88873713080a4f70578236f5ed2ec5594532 Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Sun, 11 Aug 2019 22:26:34 +0530 Subject: [PATCH 07/23] exported acyclic_coloring function --- src/SparseDiffTools.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SparseDiffTools.jl b/src/SparseDiffTools.jl index c72b3053..bba0d8d0 100644 --- a/src/SparseDiffTools.jl +++ b/src/SparseDiffTools.jl @@ -39,6 +39,7 @@ include("coloring/high_level.jl") include("coloring/backtracking_coloring.jl") include("coloring/contraction_coloring.jl") include("coloring/greedy_d1_coloring.jl") +include("coloring/acyclic_coloring.jl") include("coloring/greedy_star1_coloring.jl") include("coloring/greedy_star2_coloring.jl") include("coloring/matrix2graph.jl") From 60670f75a3696c162e57cf4e2c2f4c3f2b6aa770 Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Sun, 11 Aug 2019 22:39:13 +0530 Subject: [PATCH 08/23] added test for acyclic coloring TODO: add tests for checking condition 2 of acyclic coloring (all cycles of size 3 or more should have atleast 3 colors) --- test/test_acyclic.jl | 98 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 test/test_acyclic.jl diff --git a/test/test_acyclic.jl b/test/test_acyclic.jl new file mode 100644 index 00000000..853ef384 --- /dev/null +++ b/test/test_acyclic.jl @@ -0,0 +1,98 @@ +using SparseDiffTools +using LightGraphs +using Test + +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. **New Acyclic and Star Coloring Algorithms with Application to Computing Hessians** + +#= + +(1) ----- (2) ----- (3) ---- (4) + | \ / | | + | \ / | | + | \/ | | + (5)---(6) -------- (7) ----(8) + | | / | | + | | (11) | | + | | / | | + (9) ---(10) ----- (12)-----(13) + \ / + \ / + ------------ +=# + +gx = SimpleGraph(13) + + +add_edge!(gx,1,2) +add_edge!(gx,1,5) +add_edge!(gx,1,6) +add_edge!(gx,2,3) +add_edge!(gx,2,6) +add_edge!(gx,3,4) +add_edge!(gx,3,7) +add_edge!(gx,4,8) +add_edge!(gx,5,6) +add_edge!(gx,5,9) +add_edge!(gx,6,7) +add_edge!(gx,6,10) +add_edge!(gx,7,8) +add_edge!(gx,7,11) +add_edge!(gx,7,12) +add_edge!(gx,8,13) +add_edge!(gx,9,10) +add_edge!(gx,9,12) +add_edge!(gx,10,11) +add_edge!(gx,10,12) +add_edge!(gx,12,13) + + +push!(test_graphs, gx) + +#begin testing +for i in 1:6 + g = test_graphs[i] + + out_colors = SparseDiffTools.color_graph(g,SparseDiffTools.AcyclicColoring()) + + #test condition 1 + for v = vertices(g) + color = out_colors[v] + for j in inneighbors(g, v) + @test out_colors[j] != color + end + end +end From 02e3e8ac8d2ae2aee999d86895daf3b8220d90f8 Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Sun, 11 Aug 2019 22:39:57 +0530 Subject: [PATCH 09/23] updated runtests to include acyclic coloring --- test/runtests.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/test/runtests.jl b/test/runtests.jl index 4b1aaab5..2d124e68 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,6 +8,7 @@ if GROUP == "All" @time @safetestset "Exact coloring via contraction" begin include("test_contraction.jl") end @time @safetestset "Greedy distance-1 coloring" begin include("test_greedy_d1.jl") end @time @safetestset "Greedy star coloring" begin include("test_greedy_star.jl") end + @time @safetestset "Acyclic coloring" begin include("test_acyclic.jl") end @time @safetestset "Matrix to graph conversion" begin include("test_matrix2graph.jl") end @time @safetestset "AD using colorvec vector" begin include("test_ad.jl") end @time @safetestset "Integration test" begin include("test_integration.jl") end From 7e376444f74a269f101bbd6fa7a65f76c16f4e04 Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Sun, 11 Aug 2019 22:46:46 +0530 Subject: [PATCH 10/23] cleaned test --- test/test_acyclic.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/test_acyclic.jl b/test/test_acyclic.jl index 853ef384..8b9a92a9 100644 --- a/test/test_acyclic.jl +++ b/test/test_acyclic.jl @@ -56,7 +56,6 @@ colors. gx = SimpleGraph(13) - add_edge!(gx,1,2) add_edge!(gx,1,5) add_edge!(gx,1,6) @@ -79,10 +78,8 @@ add_edge!(gx,10,11) add_edge!(gx,10,12) add_edge!(gx,12,13) - push!(test_graphs, gx) -#begin testing for i in 1:6 g = test_graphs[i] @@ -95,4 +92,6 @@ for i in 1:6 @test out_colors[j] != color end end + + #TODO: Add tests for condition 2 of acyclic coloring end From 1d1bdfbcc324100fbdc340bb73ed4756ccd07e64 Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Mon, 12 Aug 2019 23:03:08 +0530 Subject: [PATCH 11/23] fixed param type --- src/coloring/acyclic_coloring.jl | 11 ++--------- src/coloring/high_level.jl | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/coloring/acyclic_coloring.jl b/src/coloring/acyclic_coloring.jl index 01ad25fe..59fc0ee0 100644 --- a/src/coloring/acyclic_coloring.jl +++ b/src/coloring/acyclic_coloring.jl @@ -88,7 +88,7 @@ function prevent_cycle(v::Integer, g::LightGraphs.AbstractGraph, color::AbstractVector{<:Integer}, forbiddenColors::AbstractVector{<:Integer}, - firstVisitToTree::Array{Tuple{Int64, Int64}, 1}, + firstVisitToTree::AbstractArray{<: Tuple{Integer, Integer}, 1}, set::DisjointSets{LightGraphs.Edge}) edge = find_edge(g, w, x) @@ -129,7 +129,7 @@ edges present in g. function grow_star(v::Integer, w::Integer, g::LightGraphs.AbstractGraph, - firstNeighbor::Array{Tuple{Int64, Int64}, 1}, + firstNeighbor::AbstractArray{<: Tuple{Integer, Integer}, 1}, set::DisjointSets{LightGraphs.Edge}, color::AbstractVector{<: Integer}) edge = find_edge(g, v, w) @@ -178,18 +178,11 @@ end Returns an edge object of the type LightGraphs.Edge which represents the edge connecting vertices v and w of the undirected graph g """ -<<<<<<< HEAD -function find_edge(g::LightGraphs.AbstractGraph, v::Integer, w::Integer) - for v1 in outneighbors(g, v) - if v1 == w - e = LightGraphs.SimpleGraphs.SimpleEdge(v, w) -======= function find_edge(g::LightGraphs.AbstractGraph, v::Integer, w::Integer) for e in edges(g) if (src(e) == v && dst(e) == w) || (src(e) == w && dst(e) == v) ->>>>>>> 7e376444f74a269f101bbd6fa7a65f76c16f4e04 return e end end diff --git a/src/coloring/high_level.jl b/src/coloring/high_level.jl index 53b81466..13f8ccb9 100644 --- a/src/coloring/high_level.jl +++ b/src/coloring/high_level.jl @@ -4,7 +4,7 @@ struct BacktrackingColor <: SparseDiffToolsColoringAlgorithm end struct ContractionColor <: SparseDiffToolsColoringAlgorithm end struct GreedyStar1Color <: SparseDiffToolsColoringAlgorithm end struct GreedyStar2Color <: SparseDiffToolsColoringAlgorithm end -struct AcyclicColoring <: ColoringAlgorithm end +struct AcyclicColoring <: SparseDiffToolsColoringAlgorithm end """ matrix_colors(A,alg::ColoringAlgorithm = GreedyD1Color()) From 1b0387ac8255ab8b5f4c36109f2609732e2c5645 Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Thu, 15 Aug 2019 01:21:36 +0530 Subject: [PATCH 12/23] renamed function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Mathieu Besançon --- src/coloring/acyclic_coloring.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coloring/acyclic_coloring.jl b/src/coloring/acyclic_coloring.jl index 59fc0ee0..d9e6095b 100644 --- a/src/coloring/acyclic_coloring.jl +++ b/src/coloring/acyclic_coloring.jl @@ -157,7 +157,7 @@ end Subroutine to merge trees present in the disjoint set which have a common edge. """ -function mergeTrees(v::Integer, +function merge_trees(v::Integer, w::Integer, x::Integer, g::LightGraphs.AbstractGraph, From 56789136dea81b46ff1db1e7024b2b46c3889443 Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Thu, 15 Aug 2019 01:22:16 +0530 Subject: [PATCH 13/23] replaced 1:len with eachindex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Mathieu Besançon --- src/coloring/acyclic_coloring.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coloring/acyclic_coloring.jl b/src/coloring/acyclic_coloring.jl index d9e6095b..4d94b5f0 100644 --- a/src/coloring/acyclic_coloring.jl +++ b/src/coloring/acyclic_coloring.jl @@ -107,7 +107,7 @@ end Returns min{i > 0 such that forbiddenColors[i] != v} """ function min_index(forbiddenColors::AbstractVector{<:Integer}, v::Integer) - for i = 1:length(forbiddenColors) + for i in eachindex(forbiddenColors) if forbiddenColors[i] != v return i end From 78dca3ac5330d7b64a00360ced9a804f16ca96eb Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Thu, 15 Aug 2019 01:24:09 +0530 Subject: [PATCH 14/23] added return statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Mathieu Besançon --- src/coloring/high_level.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coloring/high_level.jl b/src/coloring/high_level.jl index 13f8ccb9..32cdc331 100644 --- a/src/coloring/high_level.jl +++ b/src/coloring/high_level.jl @@ -17,5 +17,5 @@ struct AcyclicColoring <: SparseDiffToolsColoringAlgorithm end function ArrayInterface.matrix_colors(A::AbstractMatrix,alg::SparseDiffToolsColoringAlgorithm = GreedyD1Color(); partition_by_rows::Bool = false) _A = A isa SparseMatrixCSC ? A : sparse(A) # Avoid the copy A_graph = matrix2graph(_A, partition_by_rows) - color_graph(A_graph,alg) + return color_graph(A_graph, alg) end From 1248e3b51e7ed93d4bc950ff5bea6eeb01bc5d8f Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Thu, 15 Aug 2019 01:24:42 +0530 Subject: [PATCH 15/23] mentioned specific error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Mathieu Besançon --- src/coloring/acyclic_coloring.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coloring/acyclic_coloring.jl b/src/coloring/acyclic_coloring.jl index 4d94b5f0..27dd0173 100644 --- a/src/coloring/acyclic_coloring.jl +++ b/src/coloring/acyclic_coloring.jl @@ -186,7 +186,7 @@ function find_edge(g::LightGraphs.AbstractGraph, return e end end - throw(error("$v and $w are not connected in graph g")) + throw(ArgumentError("$v and $w are not connected in graph g")) end """ From 3802606b6f3580ba057461b10e2946f1f1c5987b Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Thu, 15 Aug 2019 02:23:27 +0530 Subject: [PATCH 16/23] general fixes --- src/SparseDiffTools.jl | 1 + src/coloring/acyclic_coloring.jl | 79 ++++++++++++--------------- src/coloring/backtracking_coloring.jl | 2 - src/detect_cycles.jl | 76 ++++++++++++++++++++++++++ 4 files changed, 113 insertions(+), 45 deletions(-) create mode 100644 src/detect_cycles.jl diff --git a/src/SparseDiffTools.jl b/src/SparseDiffTools.jl index bba0d8d0..860745f8 100644 --- a/src/SparseDiffTools.jl +++ b/src/SparseDiffTools.jl @@ -14,6 +14,7 @@ using SparseArrays, ArrayInterface using BlockBandedMatrices: blocksize, nblocks using ForwardDiff: Dual, jacobian, partials, DEFAULT_CHUNK_THRESHOLD +using DataStructures: DisjointSets, find_root, union, merge using ArrayInterface: matrix_colors diff --git a/src/coloring/acyclic_coloring.jl b/src/coloring/acyclic_coloring.jl index 27dd0173..7463e95f 100644 --- a/src/coloring/acyclic_coloring.jl +++ b/src/coloring/acyclic_coloring.jl @@ -1,21 +1,18 @@ -using LightGraphs -using DataStructures - """ color_graph(g::LightGraphs.AbstractGraphs, ::AcyclicColoring) -Returns a coloring vector following the acyclic coloring rules (1) the coloring -corresponds to a distance-1 coloring, and (2) vertices in every cycle of the -graph are assigned at least three distinct colors. This variant of coloring is +Returns a coloring vector following the acyclic coloring rules (1) the coloring +corresponds to a distance-1 coloring, and (2) vertices in every cycle of the +graph are assigned at least three distinct colors. This variant of coloring is called acyclic since every subgraph induced by vertices assigned any two colors is a collection of trees—and hence is acyclic. Reference: Gebremedhin AH, Manne F, Pothen A. **New Acyclic and Star Coloring Algorithms with Application to Computing Hessians** """ function color_graph(g::LightGraphs.AbstractGraph, ::AcyclicColoring) - + color = zeros(Int, nv(g)) - forbiddenColors = zeros(Int, nv(g)) + forbidden_colors = zeros(Int, nv(g)) set = DisjointSets{LightGraphs.Edge}([]) @@ -26,7 +23,7 @@ function color_graph(g::LightGraphs.AbstractGraph, ::AcyclicColoring) #enforces the first condition of acyclic coloring for w in outneighbors(g, v) if color[w] != 0 - forbiddenColors[color[w]] = v + forbidden_colors[color[w]] = v end end #enforces the second condition of acyclic coloring @@ -34,15 +31,15 @@ function color_graph(g::LightGraphs.AbstractGraph, ::AcyclicColoring) if color[w] != 0 #colored neighbor for x in outneighbors(g, w) if color[x] != 0 #colored x - if forbiddenColors[color[x]] != v - prevent_cycle(v, w, x, g, color, forbiddenColors, firstVisitToTree, set) + if forbidden_colors[color[x]] != v + prevent_cycle(v, w, x, g, color, forbidden_colors, firstVisitToTree, set) end end end end end - color[v] = min_index(forbiddenColors, v) + color[v] = min_index(forbidden_colors, v) #grow star for every edge connecting colored vertices v and w for w in outneighbors(g, v) @@ -57,12 +54,12 @@ function color_graph(g::LightGraphs.AbstractGraph, ::AcyclicColoring) for x in outneighbors(g, w) if color[x] != 0 && x != v if color[x] == color[v] - mergeTrees(v, w, x, g, set) + merge_trees(v, w, x, g, set) end end end end - end + end end return color @@ -70,11 +67,11 @@ end """ prevent_cycle(v::Integer, - w::Integer, + w::Integer, x::Integer, g::LightGraphs.AbstractGraph, - color::AbstractVector{<:Integer}, - forbiddenColors::AbstractVector{<:Integer}, + color::AbstractVector{<:Integer}, + forbidden_colors::AbstractVector{<:Integer}, firstVisitToTree::Array{Tuple{Integer, Integer}, 1}, set::DisjointSets{LightGraphs.Edge}) @@ -83,35 +80,31 @@ which is adjacent to vertices w and x in graph g. Disjoint set is used to store the induced 2-colored subgraphs/trees where the id of set is a key edge of g """ function prevent_cycle(v::Integer, - w::Integer, + w::Integer, x::Integer, g::LightGraphs.AbstractGraph, - color::AbstractVector{<:Integer}, - forbiddenColors::AbstractVector{<:Integer}, + color::AbstractVector{<:Integer}, + forbidden_colors::AbstractVector{<:Integer}, firstVisitToTree::AbstractArray{<: Tuple{Integer, Integer}, 1}, set::DisjointSets{LightGraphs.Edge}) - + edge = find_edge(g, w, x) e = find_root(set, edge) p, q = firstVisitToTree[edge_index(g, e)] if p != v firstVisitToTree[edge_index(g, e)] = (v, w) elseif q != w - forbiddenColors[color[x]] = v + forbidden_colors[color[x]] = v end end """ - min_index(forbiddenColors::AbstractVector{<:Integer}, v::Integer) + min_index(forbidden_colors::AbstractVector{<:Integer}, v::Integer) -Returns min{i > 0 such that forbiddenColors[i] != v} -""" -function min_index(forbiddenColors::AbstractVector{<:Integer}, v::Integer) - for i in eachindex(forbiddenColors) - if forbiddenColors[i] != v - return i - end - end +Returns min{i > 0 such that forbidden_colors[i] != v} +""" +function min_index(forbidden_colors::AbstractVector{<:Integer}, v::Integer) + return findfirst(!isequal(v), forbidden_colors) end """ @@ -121,10 +114,10 @@ end firstNeighbor::Array{Tuple{Integer, Integer}, 1}, set::DisjointSets{LightGraphs.Edge}) -Subroutine to grow a 2-colored star after assigning a new color to the +Subroutine to grow a 2-colored star after assigning a new color to the previously uncolored vertex v, by comparing it with the adjacent vertex w. Disjoint set is used to store stars in sets, which are identified through key -edges present in g. +edges present in g. """ function grow_star(v::Integer, w::Integer, @@ -144,12 +137,13 @@ function grow_star(v::Integer, e2 = find_root(set, edge2) union!(set, e1, e2) end + return nothing end """ merge_trees(v::Integer, - w::Integer, + w::Integer, x::Integer, g::LightGraphs.AbstractGraph, set::DisjointSets{LightGraphs.Edge}) @@ -158,7 +152,7 @@ Subroutine to merge trees present in the disjoint set which have a common edge. """ function merge_trees(v::Integer, - w::Integer, + w::Integer, x::Integer, g::LightGraphs.AbstractGraph, set::DisjointSets{LightGraphs.Edge}) @@ -175,11 +169,11 @@ end """ find_edge(g::LightGraphs.AbstractGraph, v::Integer, w::Integer) -Returns an edge object of the type LightGraphs.Edge which represents the +Returns an edge object of the type LightGraphs.Edge which represents the edge connecting vertices v and w of the undirected graph g """ -function find_edge(g::LightGraphs.AbstractGraph, - v::Integer, +function find_edge(g::LightGraphs.AbstractGraph, + v::Integer, w::Integer) for e in edges(g) if (src(e) == v && dst(e) == w) || (src(e) == w && dst(e) == v) @@ -196,14 +190,13 @@ Returns an Integer value which uniquely identifies the edge e in graph g. Used as an index in main function to avoid custom arrays with non- numerical indices. """ -function edge_index(g::LightGraphs.AbstractGraph, +function edge_index(g::LightGraphs.AbstractGraph, e::LightGraphs.Edge) - edge_list = collect(edges(g)) - for i in 1:length(edge_list) - if edge_list[i] == e + for (i, edge) in enumerate(edges(g)) + if edge == e return i end end - throw(error("Edge $e is not present in graph g")) + throw(ArgumentError("Edge $e is not present in graph g")) #return -1 end diff --git a/src/coloring/backtracking_coloring.jl b/src/coloring/backtracking_coloring.jl index f1d60f6c..510a3d72 100644 --- a/src/coloring/backtracking_coloring.jl +++ b/src/coloring/backtracking_coloring.jl @@ -1,5 +1,3 @@ -using LightGraphs - """ color_graph(g::LightGraphs, ::BacktrackingColor) diff --git a/src/detect_cycles.jl b/src/detect_cycles.jl new file mode 100644 index 00000000..cb195157 --- /dev/null +++ b/src/detect_cycles.jl @@ -0,0 +1,76 @@ +using LightGraphs + +""" + detect_cycles(g::LightGraphs.AbstractGraph) + +Prints all the cycles present in undirected graph g +""" +function detect_cycles(g::LightGraphs.AbstractGraph) + color = zeros(Int64, 100) + par = zeros(Int64, 100) + + mark = zeros(Int64, 100) + + cycleNumber = 0 + edges = ne(g) + + cycleNumber = dfs_cycle(1, 1, color, mark, par, cycleNumber) + print_cycles(edges, mark, cycleNumber) + +end + +function dfs_cycle(u::Integer, + p::Integer, + color::AbstractArray{<:Integer, 1}, + mark::AbstractVector{<:Integer}, + par::AbstractVector{<:Integer}, + cycleNumber::Integer) + if color[u] == 2 + return cycleNumber + end + + if color[u] == 1 + cycleNumber += 1 + cur = p + mark[cur] = cycleNumber + + while (cur != u) + cur = par[cur] + mark[cur] = cycleNumber + end + return cycleNumber + end + + par[u] = p + color[u] = 1 + + for v in outneighbors(g, u) + if v == par[u] + continue + end + cycleNumber = dfs_cycle(v, u, color, mark, par, cycleNumber) + end + + color[u] = 2 +end + +function print_cycles(edges::Integer, + mark::AbstractVector{<:Integer}, + cycleNumber::Integer) + + cycles = Array{Array{Integer,1}}(undef, 5) + + for i in 1:edges + if mark[i] != 0 + push!(cycles[mark[i]], i) + end + end + + for i in 1:cycleNumber + println("Cycle number $i") + for i in cycles[i] + print("$i ") + end + end + +end \ No newline at end of file From 66cf64ee377c10c2e28f41ddbb869f80a3fc98db Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Thu, 15 Aug 2019 22:35:05 +0530 Subject: [PATCH 17/23] fixes --- src/SparseDiffTools.jl | 2 +- src/coloring/acyclic_coloring.jl | 41 ++++++++++++++++---------------- src/detect_cycles.jl | 4 ++-- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/SparseDiffTools.jl b/src/SparseDiffTools.jl index 860745f8..e99ace8b 100644 --- a/src/SparseDiffTools.jl +++ b/src/SparseDiffTools.jl @@ -14,7 +14,7 @@ using SparseArrays, ArrayInterface using BlockBandedMatrices: blocksize, nblocks using ForwardDiff: Dual, jacobian, partials, DEFAULT_CHUNK_THRESHOLD -using DataStructures: DisjointSets, find_root, union, merge +using DataStructures: DisjointSets, find_root, union! using ArrayInterface: matrix_colors diff --git a/src/coloring/acyclic_coloring.jl b/src/coloring/acyclic_coloring.jl index 7463e95f..856c281c 100644 --- a/src/coloring/acyclic_coloring.jl +++ b/src/coloring/acyclic_coloring.jl @@ -16,8 +16,8 @@ function color_graph(g::LightGraphs.AbstractGraph, ::AcyclicColoring) set = DisjointSets{LightGraphs.Edge}([]) - firstVisitToTree = Array{Tuple{Int, Int}, 1}(undef, ne(g)) - firstNeighbor = Array{Tuple{Int, Int}, 1}(undef, nv(g)) + first_visit_to_tree = Array{Tuple{Int, Int}, 1}(undef, ne(g)) + first_neighbor = Array{Tuple{Int, Int}, 1}(undef, nv(g)) for v in vertices(g) #enforces the first condition of acyclic coloring @@ -32,7 +32,7 @@ function color_graph(g::LightGraphs.AbstractGraph, ::AcyclicColoring) for x in outneighbors(g, w) if color[x] != 0 #colored x if forbidden_colors[color[x]] != v - prevent_cycle(v, w, x, g, color, forbidden_colors, firstVisitToTree, set) + prevent_cycle(v, w, x, g, color, forbidden_colors, first_visit_to_tree, set) end end end @@ -44,7 +44,7 @@ function color_graph(g::LightGraphs.AbstractGraph, ::AcyclicColoring) #grow star for every edge connecting colored vertices v and w for w in outneighbors(g, v) if color[w] != 0 - grow_star(v, w, g, firstNeighbor, set, color) + grow_star!(set, v, w, g, first_neighbor, color) end end @@ -54,7 +54,7 @@ function color_graph(g::LightGraphs.AbstractGraph, ::AcyclicColoring) for x in outneighbors(g, w) if color[x] != 0 && x != v if color[x] == color[v] - merge_trees(v, w, x, g, set) + merge_trees!(set, v, w, x, g) end end end @@ -72,7 +72,7 @@ end g::LightGraphs.AbstractGraph, color::AbstractVector{<:Integer}, forbidden_colors::AbstractVector{<:Integer}, - firstVisitToTree::Array{Tuple{Integer, Integer}, 1}, + first_visit_to_tree::Array{Tuple{Integer, Integer}, 1}, set::DisjointSets{LightGraphs.Edge}) Subroutine to avoid generation of 2-colored cycle due to coloring of vertex v, @@ -85,14 +85,14 @@ function prevent_cycle(v::Integer, g::LightGraphs.AbstractGraph, color::AbstractVector{<:Integer}, forbidden_colors::AbstractVector{<:Integer}, - firstVisitToTree::AbstractArray{<: Tuple{Integer, Integer}, 1}, + first_visit_to_tree::AbstractArray{<: Tuple{Integer, Integer}, 1}, set::DisjointSets{LightGraphs.Edge}) edge = find_edge(g, w, x) e = find_root(set, edge) - p, q = firstVisitToTree[edge_index(g, e)] + p, q = first_visit_to_tree[edge_index(g, e)] if p != v - firstVisitToTree[edge_index(g, e)] = (v, w) + first_visit_to_tree[edge_index(g, e)] = (v, w) elseif q != w forbidden_colors[color[x]] = v end @@ -108,10 +108,10 @@ function min_index(forbidden_colors::AbstractVector{<:Integer}, v::Integer) end """ - grow_star(v::Integer, + grow_star!(v::Integer, w::Integer, g::LightGraphs.AbstractGraph - firstNeighbor::Array{Tuple{Integer, Integer}, 1}, + first_neighbor::Array{Tuple{Integer, Integer}, 1}, set::DisjointSets{LightGraphs.Edge}) Subroutine to grow a 2-colored star after assigning a new color to the @@ -119,17 +119,17 @@ previously uncolored vertex v, by comparing it with the adjacent vertex w. Disjoint set is used to store stars in sets, which are identified through key edges present in g. """ -function grow_star(v::Integer, +function grow_star!(set::DisjointSets{LightGraphs.Edge}, + v::Integer, w::Integer, g::LightGraphs.AbstractGraph, - firstNeighbor::AbstractArray{<: Tuple{Integer, Integer}, 1}, - set::DisjointSets{LightGraphs.Edge}, + first_neighbor::AbstractArray{<: Tuple{Integer, Integer}, 1}, color::AbstractVector{<: Integer}) edge = find_edge(g, v, w) push!(set, edge) - p, q = firstNeighbor[color[w]] + p, q = first_neighbor[color[w]] if p != v - firstNeighbor[color[w]] = (v, w) + first_neighbor[color[w]] = (v, w) else edge1 = find_edge(g, v, w) edge2 = find_edge(g, p, q) @@ -142,7 +142,7 @@ end """ - merge_trees(v::Integer, + merge_trees!(v::Integer, w::Integer, x::Integer, g::LightGraphs.AbstractGraph, @@ -151,11 +151,11 @@ end Subroutine to merge trees present in the disjoint set which have a common edge. """ -function merge_trees(v::Integer, +function merge_trees!(set::DisjointSets{LightGraphs.Edge}, + v::Integer, w::Integer, x::Integer, - g::LightGraphs.AbstractGraph, - set::DisjointSets{LightGraphs.Edge}) + g::LightGraphs.AbstractGraph) edge1 = find_edge(g, v, w) edge2 = find_edge(g, w, x) e1 = find_root(set, edge1) @@ -198,5 +198,4 @@ function edge_index(g::LightGraphs.AbstractGraph, end end throw(ArgumentError("Edge $e is not present in graph g")) - #return -1 end diff --git a/src/detect_cycles.jl b/src/detect_cycles.jl index cb195157..72ee6be2 100644 --- a/src/detect_cycles.jl +++ b/src/detect_cycles.jl @@ -59,7 +59,7 @@ function print_cycles(edges::Integer, cycleNumber::Integer) cycles = Array{Array{Integer,1}}(undef, 5) - + for i in 1:edges if mark[i] != 0 push!(cycles[mark[i]], i) @@ -73,4 +73,4 @@ function print_cycles(edges::Integer, end end -end \ No newline at end of file +end From e0a920ac8fba27b675ad16b4e18e156fa7288f52 Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Sat, 17 Aug 2019 11:46:09 +0530 Subject: [PATCH 18/23] fixed tests --- test/test_acyclic.jl | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/test/test_acyclic.jl b/test/test_acyclic.jl index 8b9a92a9..169e2997 100644 --- a/test/test_acyclic.jl +++ b/test/test_acyclic.jl @@ -7,6 +7,7 @@ Random.seed!(123) #= Test data =# test_graphs = Array{SimpleGraph, 1}(undef, 0) +test_graphs_dir = Array{SimpleDiGraph, 1}(undef, 0) for _ in 1:5 nv = rand(5:20) @@ -39,7 +40,7 @@ colors. #Sample graph from Gebremedhin AH, Manne F, Pothen A. **New Acyclic and Star Coloring Algorithms with Application to Computing Hessians** #= - + (1) ----- (2) ----- (3) ---- (4) | \ / | | | \ / | | @@ -80,18 +81,48 @@ add_edge!(gx,12,13) push!(test_graphs, gx) +#create directed copies +for g in test_graphs + dg = SimpleDiGraph(nv(g)) + for e in edges(g) + src1 = src(e) + dst1 = dst(e) + add_edge!(dg, src1, dst1) + add_edge!(dg, dst1, src1) + end + push!(test_graphs_dir, dg) +end + + for i in 1:6 g = test_graphs[i] - - out_colors = SparseDiffTools.color_graph(g,SparseDiffTools.AcyclicColoring()) + dg = test_graphs_dir[i] + out_colors = SparseDiffTools.color_graph(g, SparseDiffTools.AcyclicColoring()) #test condition 1 - for v = vertices(g) + for v in vertices(g) color = out_colors[v] for j in inneighbors(g, v) @test out_colors[j] != color end end +end + +for i in 3:6 + g = test_graphs[i] + dg = test_graphs_dir[i] + out_colors = SparseDiffTools.color_graph(g, SparseDiffTools.AcyclicColoring()) + + #test condition 2 + cycles = simplecycles(dg) + for c in cycles + colors = zeros(Int, 0) + if length(c) > 2 + for v in c + push!(colors, out_colors[v]) + end + @test length(unique(colors)) >= 3 + end + end - #TODO: Add tests for condition 2 of acyclic coloring end From a7cf4f265d77825d223045eebb20b0f7fccf1699 Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Sat, 17 Aug 2019 16:04:31 +0530 Subject: [PATCH 19/23] added DataStructures version, fixed signature in docs --- Project.toml | 1 + src/coloring/acyclic_coloring.jl | 6 +-- src/detect_cycles.jl | 76 -------------------------------- 3 files changed, 4 insertions(+), 79 deletions(-) delete mode 100644 src/detect_cycles.jl diff --git a/Project.toml b/Project.toml index 076cf181..b239314b 100644 --- a/Project.toml +++ b/Project.toml @@ -20,6 +20,7 @@ VertexSafeGraphs = "19fa3120-7c27-5ec5-8db8-b0b0aa330d6f" [compat] ArrayInterface = "1.1" julia = "1" +DataStructures = "0.17" [extras] IterativeSolvers = "42fd0dbc-a981-5370-80f2-aaf504508153" diff --git a/src/coloring/acyclic_coloring.jl b/src/coloring/acyclic_coloring.jl index 856c281c..0f0c7f13 100644 --- a/src/coloring/acyclic_coloring.jl +++ b/src/coloring/acyclic_coloring.jl @@ -108,11 +108,11 @@ function min_index(forbidden_colors::AbstractVector{<:Integer}, v::Integer) end """ - grow_star!(v::Integer, + grow_star!(set::DisjointSets{LightGraphs.Edge}, + v::Integer, w::Integer, g::LightGraphs.AbstractGraph - first_neighbor::Array{Tuple{Integer, Integer}, 1}, - set::DisjointSets{LightGraphs.Edge}) + first_neighbor::Array{Tuple{Integer, Integer}, 1}) Subroutine to grow a 2-colored star after assigning a new color to the previously uncolored vertex v, by comparing it with the adjacent vertex w. diff --git a/src/detect_cycles.jl b/src/detect_cycles.jl deleted file mode 100644 index 72ee6be2..00000000 --- a/src/detect_cycles.jl +++ /dev/null @@ -1,76 +0,0 @@ -using LightGraphs - -""" - detect_cycles(g::LightGraphs.AbstractGraph) - -Prints all the cycles present in undirected graph g -""" -function detect_cycles(g::LightGraphs.AbstractGraph) - color = zeros(Int64, 100) - par = zeros(Int64, 100) - - mark = zeros(Int64, 100) - - cycleNumber = 0 - edges = ne(g) - - cycleNumber = dfs_cycle(1, 1, color, mark, par, cycleNumber) - print_cycles(edges, mark, cycleNumber) - -end - -function dfs_cycle(u::Integer, - p::Integer, - color::AbstractArray{<:Integer, 1}, - mark::AbstractVector{<:Integer}, - par::AbstractVector{<:Integer}, - cycleNumber::Integer) - if color[u] == 2 - return cycleNumber - end - - if color[u] == 1 - cycleNumber += 1 - cur = p - mark[cur] = cycleNumber - - while (cur != u) - cur = par[cur] - mark[cur] = cycleNumber - end - return cycleNumber - end - - par[u] = p - color[u] = 1 - - for v in outneighbors(g, u) - if v == par[u] - continue - end - cycleNumber = dfs_cycle(v, u, color, mark, par, cycleNumber) - end - - color[u] = 2 -end - -function print_cycles(edges::Integer, - mark::AbstractVector{<:Integer}, - cycleNumber::Integer) - - cycles = Array{Array{Integer,1}}(undef, 5) - - for i in 1:edges - if mark[i] != 0 - push!(cycles[mark[i]], i) - end - end - - for i in 1:cycleNumber - println("Cycle number $i") - for i in cycles[i] - print("$i ") - end - end - -end From ef53cc5f0f727aff6840df76080019b63f35ea5e Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Thu, 22 Aug 2019 23:48:23 +0530 Subject: [PATCH 20/23] changed AbstractArray to AbstractVector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Mathieu Besançon --- src/coloring/acyclic_coloring.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coloring/acyclic_coloring.jl b/src/coloring/acyclic_coloring.jl index 0f0c7f13..894d9fdb 100644 --- a/src/coloring/acyclic_coloring.jl +++ b/src/coloring/acyclic_coloring.jl @@ -85,7 +85,7 @@ function prevent_cycle(v::Integer, g::LightGraphs.AbstractGraph, color::AbstractVector{<:Integer}, forbidden_colors::AbstractVector{<:Integer}, - first_visit_to_tree::AbstractArray{<: Tuple{Integer, Integer}, 1}, + first_visit_to_tree::AbstractVector{<: Tuple{Integer, Integer}}, set::DisjointSets{LightGraphs.Edge}) edge = find_edge(g, w, x) From 02454fd011b8364ebcc7e96035325425df8fb2c0 Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Thu, 22 Aug 2019 23:49:03 +0530 Subject: [PATCH 21/23] added space between arguments in function definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Mathieu Besançon --- src/coloring/high_level.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coloring/high_level.jl b/src/coloring/high_level.jl index 32cdc331..70ff9fa0 100644 --- a/src/coloring/high_level.jl +++ b/src/coloring/high_level.jl @@ -14,7 +14,7 @@ struct AcyclicColoring <: SparseDiffToolsColoringAlgorithm end The coloring defaults to a greedy distance-1 coloring. """ -function ArrayInterface.matrix_colors(A::AbstractMatrix,alg::SparseDiffToolsColoringAlgorithm = GreedyD1Color(); partition_by_rows::Bool = false) +function ArrayInterface.matrix_colors(A::AbstractMatrix, alg::SparseDiffToolsColoringAlgorithm = GreedyD1Color(); partition_by_rows::Bool = false) _A = A isa SparseMatrixCSC ? A : sparse(A) # Avoid the copy A_graph = matrix2graph(_A, partition_by_rows) return color_graph(A_graph, alg) From c062fb394b0a1ff3d119669c788431df65acace4 Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Thu, 22 Aug 2019 23:49:23 +0530 Subject: [PATCH 22/23] changed `Array` to `Vector` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Mathieu Besançon --- test/test_acyclic.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_acyclic.jl b/test/test_acyclic.jl index 169e2997..ba00c625 100644 --- a/test/test_acyclic.jl +++ b/test/test_acyclic.jl @@ -6,7 +6,7 @@ using Random Random.seed!(123) #= Test data =# -test_graphs = Array{SimpleGraph, 1}(undef, 0) +test_graphs = Vector{SimpleGraph}(undef, 0) test_graphs_dir = Array{SimpleDiGraph, 1}(undef, 0) for _ in 1:5 From fe494d50b1d3734a00d5d8a9a0534b02fc0fb60d Mon Sep 17 00:00:00 2001 From: Pankaj Mishra Date: Thu, 22 Aug 2019 23:49:56 +0530 Subject: [PATCH 23/23] changed array to vector MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Mathieu Besançon --- test/test_acyclic.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_acyclic.jl b/test/test_acyclic.jl index ba00c625..3ee1ce8f 100644 --- a/test/test_acyclic.jl +++ b/test/test_acyclic.jl @@ -7,7 +7,7 @@ Random.seed!(123) #= Test data =# test_graphs = Vector{SimpleGraph}(undef, 0) -test_graphs_dir = Array{SimpleDiGraph, 1}(undef, 0) +test_graphs_dir = Vector{SimpleDiGraph}(undef, 0) for _ in 1:5 nv = rand(5:20)