This repository was archived by the owner on Aug 22, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 45
Added acyclic coloring #60
Merged
Merged
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
72fc97e
acyclic coloring
pkj-m 5f3e77d
added acylic coloring
pkj-m a058ace
added more documentation for functions
pkj-m 0445102
Merge branch 'master' into acyclic
pkj-m 04e84d7
added reference and modified find_edge function
pkj-m d8859a3
added efficient edge-finding method
pkj-m d10f722
fixed broken syntax
pkj-m 752c888
exported acyclic_coloring function
pkj-m 60670f7
added test for acyclic coloring
pkj-m 02e3e8a
updated runtests to include acyclic coloring
pkj-m 7e37644
cleaned test
pkj-m 3fe154d
update
pkj-m 1d1bdfb
fixed param type
pkj-m 1b0387a
renamed function
pkj-m 5678913
replaced 1:len with eachindex
pkj-m 78dca3a
added return statement
pkj-m 1248e3b
mentioned specific error
pkj-m 3802606
general fixes
pkj-m 66cf64e
fixes
pkj-m e0a920a
fixed tests
pkj-m a7cf4f2
added DataStructures version, fixed signature in docs
pkj-m ef53cc5
changed AbstractArray to AbstractVector
pkj-m 02454fd
added space between arguments in function definition
pkj-m c062fb3
changed `Array` to `Vector`
pkj-m fe494d5
changed array to vector
pkj-m File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
""" | ||
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. | ||
|
||
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)) | ||
forbidden_colors = zeros(Int, nv(g)) | ||
|
||
set = DisjointSets{LightGraphs.Edge}([]) | ||
|
||
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 | ||
for w in outneighbors(g, v) | ||
if color[w] != 0 | ||
forbidden_colors[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(g, w) | ||
if color[x] != 0 #colored x | ||
if forbidden_colors[color[x]] != v | ||
prevent_cycle(v, w, x, g, color, forbidden_colors, first_visit_to_tree, set) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
color[v] = min_index(forbidden_colors, v) | ||
|
||
#grow star for every edge connecting colored vertices v and w | ||
for w in outneighbors(g, v) | ||
if color[w] != 0 | ||
grow_star!(set, v, w, g, first_neighbor, color) | ||
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] | ||
merge_trees!(set, v, w, x, g) | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
return color | ||
end | ||
|
||
""" | ||
prevent_cycle(v::Integer, | ||
w::Integer, | ||
x::Integer, | ||
g::LightGraphs.AbstractGraph, | ||
color::AbstractVector{<:Integer}, | ||
forbidden_colors::AbstractVector{<:Integer}, | ||
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, | ||
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}, | ||
forbidden_colors::AbstractVector{<:Integer}, | ||
first_visit_to_tree::AbstractVector{<: Tuple{Integer, Integer}}, | ||
set::DisjointSets{LightGraphs.Edge}) | ||
|
||
edge = find_edge(g, w, x) | ||
e = find_root(set, edge) | ||
p, q = first_visit_to_tree[edge_index(g, e)] | ||
if p != v | ||
first_visit_to_tree[edge_index(g, e)] = (v, w) | ||
elseif q != w | ||
forbidden_colors[color[x]] = v | ||
end | ||
end | ||
|
||
""" | ||
min_index(forbidden_colors::AbstractVector{<:Integer}, v::Integer) | ||
|
||
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 | ||
|
||
""" | ||
grow_star!(set::DisjointSets{LightGraphs.Edge}, | ||
v::Integer, | ||
w::Integer, | ||
g::LightGraphs.AbstractGraph | ||
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. | ||
Disjoint set is used to store stars in sets, which are identified through key | ||
edges present in g. | ||
""" | ||
function grow_star!(set::DisjointSets{LightGraphs.Edge}, | ||
v::Integer, | ||
w::Integer, | ||
g::LightGraphs.AbstractGraph, | ||
first_neighbor::AbstractArray{<: Tuple{Integer, Integer}, 1}, | ||
color::AbstractVector{<: Integer}) | ||
edge = find_edge(g, v, w) | ||
push!(set, edge) | ||
p, q = first_neighbor[color[w]] | ||
if p != v | ||
first_neighbor[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 | ||
return nothing | ||
end | ||
|
||
|
||
""" | ||
merge_trees!(v::Integer, | ||
w::Integer, | ||
x::Integer, | ||
g::LightGraphs.AbstractGraph, | ||
set::DisjointSets{LightGraphs.Edge}) | ||
matbesancon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Subroutine to merge trees present in the disjoint set which have a | ||
common edge. | ||
""" | ||
function merge_trees!(set::DisjointSets{LightGraphs.Edge}, | ||
v::Integer, | ||
w::Integer, | ||
x::Integer, | ||
g::LightGraphs.AbstractGraph) | ||
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 | ||
pkj-m marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
""" | ||
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) || (src(e) == w && dst(e) == v) | ||
return e | ||
end | ||
end | ||
throw(ArgumentError("$v and $w are not connected in graph g")) | ||
end | ||
|
||
""" | ||
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) | ||
for (i, edge) in enumerate(edges(g)) | ||
if edge == e | ||
return i | ||
end | ||
end | ||
throw(ArgumentError("Edge $e is not present in graph g")) | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,3 @@ | ||
using LightGraphs | ||
|
||
""" | ||
color_graph(g::LightGraphs, ::BacktrackingColor) | ||
|
||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,21 @@ | ||
abstract type SparseDiffToolsColoringAlgorithm <: ArrayInterface.ColoringAlgorithm end | ||
struct GreedyD1Color <: SparseDiffToolsColoringAlgorithm end | ||
struct BacktrackingColor <: SparseDiffToolsColoringAlgorithm end | ||
struct ContractionColor <: SparseDiffToolsColoringAlgorithm end | ||
struct GreedyStar1Color <: SparseDiffToolsColoringAlgorithm end | ||
struct GreedyStar2Color <: SparseDiffToolsColoringAlgorithm end | ||
|
||
""" | ||
matrix_colors(A,alg::ColoringAlgorithm = GreedyD1Color()) | ||
|
||
Returns the colorvec vector for the matrix A using the chosen coloring | ||
algorithm. If a known analytical solution exists, that is used instead. | ||
The coloring defaults to a greedy distance-1 coloring. | ||
|
||
""" | ||
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) | ||
end | ||
abstract type SparseDiffToolsColoringAlgorithm <: ArrayInterface.ColoringAlgorithm end | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this abstract type needed? Maybe the supertype from ArrayInterface is enough There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure why we implemented it this way. Maybe @ChrisRackauckas can throw some light over it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ping @ChrisRackauckas There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Makes it not type piracy |
||
struct GreedyD1Color <: SparseDiffToolsColoringAlgorithm end | ||
struct BacktrackingColor <: SparseDiffToolsColoringAlgorithm end | ||
struct ContractionColor <: SparseDiffToolsColoringAlgorithm end | ||
struct GreedyStar1Color <: SparseDiffToolsColoringAlgorithm end | ||
struct GreedyStar2Color <: SparseDiffToolsColoringAlgorithm end | ||
struct AcyclicColoring <: SparseDiffToolsColoringAlgorithm end | ||
|
||
""" | ||
matrix_colors(A,alg::ColoringAlgorithm = GreedyD1Color()) | ||
|
||
Returns the colorvec vector for the matrix A using the chosen coloring | ||
algorithm. If a known analytical solution exists, that is used instead. | ||
The coloring defaults to a greedy distance-1 coloring. | ||
|
||
""" | ||
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) | ||
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.