Skip to content

Commit

Permalink
Add Zykov and Christofides Algorithms for Chromatic Number
Browse files Browse the repository at this point in the history
Style changes from review


Duplicate tests already included


Add outline for Byskov algorithm

Add function to find three colourable subgraphs


Moved three colourable processing to before Byskov


Added declaration for Byskovs


Handled Special case for three vertices and no maximal independent sets


Wasn't actually getting the complement of the independent set


Create category isDigraphAlgorithm and sub-category isDigraphColouringAlgorithm

Will be used to distinguish different colouring algorithms that are available.
Add Bound Global objects for Digraph Colouring Algorithms


Update original functions to use new method selection


Update test for new method selection


Forgot to subtract set before getting the induced subgraph


Remove from extreme test as it is far too large for these tests


Move tests that are too slow


Special case was not needed


Add loop checks to Lawler and Byskov Algorithms


Remove Extreme Tests


Add standard tests that will run in a reasonable amount of time

Not all chromatic number tests were duplicated, as some would use too much memory or take too long
Fix formatting


More formatting issues


Should be the last one


Added Documentation


Misc Comments


Make label match docs


Optimise away the subdigraph use


Remove another copy


Fix lawler issues


Optimise Byskov


clarify todo


Revert to using induced subdigraph


Fix some linting


Missed a few


Fix Indent


Add method selection objects for Zykov


Initial algorithm skeleton


Fill in the rest


Forgot about edge direction


Documentation


Start adaptation for pruned trees


Simplify logic


Add tests for Zykov


Add filters and method objects for Christofides


Initialise variable for Christofides


Update comments


Initial version of Christofides implemented.


Fixed a few syntax errors


Fix calculation of MIS


Remove debug print


Cleanup christofides


fix typo


Convert to using Blists

Need to benchmark to check improvement
Fix formatting


Missed some trailing whitespace


Restart attempt at in place zykov


Revert to just copying

In place is more hassle than it's worth
Remove redundant extra check


Use new function for Christofides


Fix merge issues


Add vertex ordering to Zykov


Add Christofides test


Optimise clique finder used


Fix lint


remove duplicate bib


Update for new method selection


Fix test output


Update Docs


Remove unused reference


Fix spelling mistake
  • Loading branch information
EwanGilligan committed Jun 2, 2021
1 parent 5c6bea0 commit 4c77fee
Show file tree
Hide file tree
Showing 4 changed files with 331 additions and 0 deletions.
4 changes: 4 additions & 0 deletions doc/attr.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1427,6 +1427,10 @@ gap> DigraphLoops(D);
<Cite Key="Law1976"/></Item>
<Item><C>byskov</C> - Byskov's Algorithm
<Cite Key="Bys2002"/></Item>
<Item><C>zykov</C> - Zykov's Algorithm
<Cite Key="Corneil1973"/></Item>
<Item><C>christofides</C> - Christofides's Algorithm
<Cite Key="Wang1974"/></Item>
</List>

<Example><![CDATA[
Expand Down
32 changes: 32 additions & 0 deletions doc/digraphs.bib
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,35 @@ @article{Bys2002
Journal = {BRICS Report Series},
Doi = {10.7146/brics.v9i45.21760}
}

@article{Corneil1973,
author = {Corneil, D. G. and Graham, B.},
title = {An Algorithm for Determining the Chromatic Number of a Graph},
journal = {SIAM Journal on Computing},
volume = {2},
number = {4},
pages = {311-318},
year = {1973},
doi = {10.1137/0202026},
URL = {https://doi.org/10.1137/0202026},
eprint = {https://doi.org/10.1137/0202026}
}

@article{Wang1974,
author= {Wang, Chung C.},
title = {An Algorithm for the Chromatic Number of a Graph},
year = {1974},
issue_date = {July 1974},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
volume = {21},
number = {3},
issn = {0004-5411},
url = {https://doi.org/10.1145/321832.321837},
doi = {10.1145/321832.321837},
abstract = {Christofides' algorithm for finding the chromatic number of a graph is improved both in speed and memory space by using a depth-first search rule to search for a shortest path in a reduced subgraph tree.},
journal = {J. ACM},
month = jul,
pages = {385–391},
numpages = {7}
}
179 changes: 179 additions & 0 deletions gap/attr.gi
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,181 @@ function(D)
end
);

BindGlobal("DIGRAPHS_ChromaticNumberZykov",
function(D)
local nr, ZykovReduce, chrom;
nr := DigraphNrVertices(D);
# Recursive function call
ZykovReduce := function(D)
local nr, D_contract, adjacent, vertices, v, x, y, x_i, y_i, found, deg;
nr := DigraphNrVertices(D);
# Update upper bound if possible.
chrom := Minimum(nr, chrom);
# Leaf nodes are either complete graphs or q-cliques. The chromatic number
# is then the smallest q-clique found.
if not IsCompleteDigraph(D) and IsEmpty(CliquesFinder(D, fail, [], 1, [],
[], false, chrom,
true)) then
# Get adjacency function
adjacent := DigraphAdjacencyFunction(D);
# Sort vertices by degree, so that higher degree vertices are picked first
vertices := [1 .. nr];
deg := ShallowCopy(OutDegrees(D));
SortParallel(deg, vertices, {x, y} -> x > y);
# Choose two non-adjacent vertices x, y
# This is just done by ascending ordering.
found := false;
for x_i in [1 .. nr] do
x := vertices[x_i];
for y_i in [x_i + 1 .. nr] do
y := vertices[y_i];
if not adjacent(x, y) then
found := true;
break;
fi;
od;
if found then
break;
fi;
od;
Assert(1, x <> y, "x and y must be different");
Assert(1, found, "No adjacent vertices");
# Colour the vertex contraction.
# A contraction of a graph effectively merges two non adjacent vertices
# into a single new vertex with the edges merged.
# We merge y into x, keeping x.
D_contract := DigraphMutableCopy(D);
for v in vertices do
# Iterate over all vertices that
if v = x or v = y then
continue;
fi;
# Add any edge that involves y, but not already x to avoid duplication.
if adjacent(v, y) and not adjacent(v, x) then
DigraphAddEdge(D_contract, x, v);
DigraphAddEdge(D_contract, v, x);
fi;
od;
DigraphRemoveVertex(D_contract, y);
ZykovReduce(D_contract);
# Colour the edge addition
# This just adds symmetric edges between x and y;
DigraphAddEdge(D, [x, y]);
DigraphAddEdge(D, [y, x]);
ZykovReduce(D);
# Undo changes to the graph
DigraphRemoveEdge(D, [x, y]);
DigraphRemoveEdge(D, [y, x]);
fi;
end;
# Algorithm requires an undirected graph.
D := DigraphSymmetricClosure(DigraphMutableCopy(D));
# Use greedy colouring as an upper bound
chrom := RankOfTransformation(DigraphGreedyColouring(D), nr);
ZykovReduce(D);
return chrom;
end
);

BindGlobal("DIGRAPHS_ChromaticNumberChristofides",
function(D)
local nr, I, n, T, b, unprocessed, i, v_without_t, j, u, min_occurences,
cur_occurences, chrom, colouring, stack, vertices;

nr := DigraphNrVertices(D);
vertices := List(DigraphVertices(D));
# Initialise the required variables.
# Calculate all maximal independent sets of D.
I := DigraphMaximalIndependentSets(D);
# Convert each MIS into a BList
I := List(I, i -> BlistList(vertices, i));
# Upper bound for chromatic number.
chrom := nr;
# Set of vertices of D not in the current subgraph at level n.
T := ListWithIdenticalEntries(nr, false);
# Current search level of the subgraph tree.
n := 0;
# The maximal independent sets of V \ T at level n.
b := [ListWithIdenticalEntries(nr, false)];
# Number of unprocessed MIS's of V \ T from level 1 to n
unprocessed := ListWithIdenticalEntries(nr, 0);
# Would be jth colour class of the chromatic colouring of G.
colouring := List([1 .. nr], i -> BlistList(vertices, [i]));
# Stores current unprocessed MIS's of V \ T at level 1 to level n
stack := [];
# Now perform the search.
repeat
# Step 2
if n < chrom then
# Step 3
# If V = T then we've reached a null subgraph
if SizeBlist(T) = nr then
chrom := n;
SubtractBlist(T, b[n + 1]);
for i in [1 .. chrom] do
colouring[i] := b[i];
# TODO set colouring attribute
od;
else
# Step 4
# Compute the maximal independent sets of V \ T
v_without_t := DIGRAPHS_MaximalIndependentSetsSubtractedSet(I, T,
infinity);
# Step 5
# Pick u in V \ T such that u is in the fewest maximal independent sets.
u := -1;
min_occurences := infinity;
for i in vertices do
# Skip elements of T.
if T[i] then
continue;
fi;
cur_occurences := 0;
for j in v_without_t do
if j[i] then
cur_occurences := cur_occurences + 1;
fi;
od;
if cur_occurences < min_occurences then
min_occurences := cur_occurences;
u := i;
fi;
od;
Assert(1, u <> -1, "Vertex must be picked");
# Remove maximal independent sets not containing u.
v_without_t := Filtered(v_without_t, x -> x[u]);
# Add these MISs to the stack
Append(stack, v_without_t);
# Search has moved one level deeper
n := n + 1;
unprocessed[n] := Length(v_without_t);
fi;
else
# if n >= g then T = T \ b[n]
# This exceeds the current best bound, so stop search.
SubtractBlist(T, b[n + 1]);
fi;
# Step 6
while n <> 0 do
# step 7
if unprocessed[n] = 0 then
n := n - 1;
SubtractBlist(T, b[n + 1]);
else
# Step 8
# take an element from the top of the stack
i := Remove(stack);
unprocessed[n] := unprocessed[n] - 1;
b[n + 1] := i;
UniteBlist(T, i);
break;
fi;
od;
until n = 0;
return chrom;
end
);

InstallMethod(ChromaticNumber, "for a digraph by out-neighbours",
[IsDigraphByOutNeighboursRep],
function(D)
Expand All @@ -373,6 +548,10 @@ function(D)
return DIGRAPHS_ChromaticNumberLawler(D);
elif ValueOption("byskov") <> fail then
return DIGRAPHS_ChromaticNumberByskov(D);
elif ValueOption("zykov") <> fail then
return DIGRAPHS_ChromaticNumberZykov(D);
elif ValueOption("christofides") <> fail then
return DIGRAPHS_ChromaticNumberChristofides(D);
fi;

# The chromatic number of <D> is at least 3 and at most nr
Expand Down
116 changes: 116 additions & 0 deletions tst/standard/attr.tst
Original file line number Diff line number Diff line change
Expand Up @@ -1654,6 +1654,122 @@ Error, the argument <D> must be a digraph with no loops,
gap> DIGRAPHS_UnderThreeColourable(EmptyDigraph(0));
0

# Test ChromaticNumber Zykov
gap> ChromaticNumber(Digraph([[1]]) : zykov);
Error, the argument <D> must be a digraph with no loops,
gap> ChromaticNumber(NullDigraph(10) : zykov);
1
gap> ChromaticNumber(CompleteDigraph(10) : zykov);
10
gap> ChromaticNumber(CompleteBipartiteDigraph(5, 5) : zykov);
2
gap> ChromaticNumber(DigraphRemoveEdge(CompleteDigraph(10), [1, 2]) : zykov);
10
gap> ChromaticNumber(Digraph([[4, 8], [6, 10], [9], [2, 3, 9], [],
> [3], [4], [6], [], [5, 7]]) : zykov);
3
gap> ChromaticNumber(DigraphDisjointUnion(CompleteDigraph(1),
> Digraph([[2], [4], [1, 2], [3]])) : zykov);
3
gap> ChromaticNumber(DigraphDisjointUnion(CompleteDigraph(1),
> Digraph([[2], [4], [1, 2], [3], [1, 2, 3]])) : zykov);
4
gap> gr := Digraph([[2, 3, 4], [3], [], []]);
<immutable digraph with 4 vertices, 4 edges>
gap> ChromaticNumber(gr : zykov);
3
gap> ChromaticNumber(EmptyDigraph(0) : zykov);
0
gap> gr := CompleteDigraph(4);;
gap> gr := DigraphAddVertex(gr);;
gap> ChromaticNumber(gr : zykov);
4
gap> gr := Digraph([[2, 4, 7, 3], [3, 5, 8, 1], [1, 6, 9, 2],
> [5, 7, 1, 6], [6, 8, 2, 4], [4, 9, 3, 5], [8, 1, 4, 9], [9, 2, 5, 7],
> [7, 3, 6, 8]]);;
gap> ChromaticNumber(gr : zykov);
3
gap> gr := DigraphSymmetricClosure(ChainDigraph(5));
<immutable symmetric digraph with 5 vertices, 8 edges>
gap> ChromaticNumber(gr : zykov);
2
gap> gr := DigraphFromGraph6String("KmKk~K??G@_@");
<immutable symmetric digraph with 12 vertices, 42 edges>
gap> ChromaticNumber(gr : zykov);
4
gap> gr := CycleDigraph(7);
<immutable cycle digraph with 7 vertices>
gap> ChromaticNumber(gr : zykov);
3
gap> ChromaticNumber(gr : zykov);
3
gap> ChromaticNumber(gr : zykov);
3
gap> a := DigraphRemoveEdges(CompleteDigraph(50), [[1, 2], [2, 1]]);;
gap> b := DigraphAddVertex(a);;
gap> ChromaticNumber(a : zykov);
49
gap> ChromaticNumber(b : zykov);
49

# Test ChromaticNumber Christofides
gap> ChromaticNumber(Digraph([[1]]) : christofides);
Error, the argument <D> must be a digraph with no loops,
gap> ChromaticNumber(NullDigraph(10) : christofides);
1
gap> ChromaticNumber(CompleteDigraph(10) : christofides);
10
gap> ChromaticNumber(CompleteBipartiteDigraph(5, 5) : christofides);
2
gap> ChromaticNumber(DigraphRemoveEdge(CompleteDigraph(10), [1, 2]) : christofides);
10
gap> ChromaticNumber(Digraph([[4, 8], [6, 10], [9], [2, 3, 9], [],
> [3], [4], [6], [], [5, 7]]) : christofides);
3
gap> ChromaticNumber(DigraphDisjointUnion(CompleteDigraph(1),
> Digraph([[2], [4], [1, 2], [3]])) : christofides);
3
gap> ChromaticNumber(DigraphDisjointUnion(CompleteDigraph(1),
> Digraph([[2], [4], [1, 2], [3], [1, 2, 3]])) : christofides);
4
gap> gr := Digraph([[2, 3, 4], [3], [], []]);
<immutable digraph with 4 vertices, 4 edges>
gap> ChromaticNumber(gr : christofides);
3
gap> ChromaticNumber(EmptyDigraph(0) : christofides);
0
gap> gr := CompleteDigraph(4);;
gap> gr := DigraphAddVertex(gr);;
gap> ChromaticNumber(gr : christofides);
4
gap> gr := Digraph([[2, 4, 7, 3], [3, 5, 8, 1], [1, 6, 9, 2],
> [5, 7, 1, 6], [6, 8, 2, 4], [4, 9, 3, 5], [8, 1, 4, 9], [9, 2, 5, 7],
> [7, 3, 6, 8]]);;
gap> ChromaticNumber(gr : christofides);
3
gap> gr := DigraphSymmetricClosure(ChainDigraph(5));
<immutable symmetric digraph with 5 vertices, 8 edges>
gap> ChromaticNumber(gr : christofides);
2
gap> gr := DigraphFromGraph6String("KmKk~K??G@_@");
<immutable symmetric digraph with 12 vertices, 42 edges>
gap> ChromaticNumber(gr : christofides);
4
gap> gr := CycleDigraph(7);
<immutable cycle digraph with 7 vertices>
gap> ChromaticNumber(gr : christofides);
3
gap> ChromaticNumber(gr : christofides);
3
gap> ChromaticNumber(gr : christofides);
3
gap> a := DigraphRemoveEdges(CompleteDigraph(50), [[1, 2], [2, 1]]);;
gap> b := DigraphAddVertex(a);;
gap> ChromaticNumber(a : christofides);
49
gap> ChromaticNumber(b : christofides);
49

# DegreeMatrix
gap> gr := Digraph([[2, 3, 4], [2, 5], [1, 5, 4], [1], [1, 1, 2, 4]]);;
gap> DegreeMatrix(gr);
Expand Down

0 comments on commit 4c77fee

Please sign in to comment.