diff --git a/doc/attr.xml b/doc/attr.xml
index d525a100d..00483733c 100644
--- a/doc/attr.xml
+++ b/doc/attr.xml
@@ -1427,6 +1427,10 @@ gap> DigraphLoops(D);
- byskov - Byskov's Algorithm
+ - zykov - Zykov's Algorithm
+
+ - christofides - Christofides's Algorithm
+
must be a digraph with no loops,");
+ elif nr = 0 then
+ return 0; # chromatic number = 0 iff has 0 verts
+ elif IsNullDigraph(D) then
+ return 1; # chromatic number = 1 iff has >= 1 verts & no edges
+ elif IsBipartiteDigraph(D) then
+ return 2; # chromatic number = 2 iff has >= 2 verts & is bipartite
+ # has at least 2 vertices at this stage
+ fi;
+ # 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);
+ if DigraphHasLoops(D) then
+ ErrorNoReturn("the argument must be a digraph with no loops,");
+ elif nr = 0 then
+ return 0; # chromatic number = 0 iff has 0 verts
+ elif IsNullDigraph(D) then
+ return 1; # chromatic number = 1 iff has >= 1 verts & no edges
+ elif IsBipartiteDigraph(D) then
+ return 2; # chromatic number = 2 iff has >= 2 verts & is bipartite
+ # has at least 2 vertices at this stage
+ fi;
+ 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)
@@ -373,6 +568,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 is at least 3 and at most nr
diff --git a/tst/standard/attr.tst b/tst/standard/attr.tst
index a9f73b3a6..6ad9144b9 100644
--- a/tst/standard/attr.tst
+++ b/tst/standard/attr.tst
@@ -1654,6 +1654,122 @@ Error, the argument must be a digraph with no loops,
gap> DIGRAPHS_UnderThreeColourable(EmptyDigraph(0));
0
+# Test ChromaticNumber Zykov
+gap> ChromaticNumber(Digraph([[1]]) : zykov);
+Error, the argument 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], [], []]);
+
+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));
+
+gap> ChromaticNumber(gr : zykov);
+2
+gap> gr := DigraphFromGraph6String("KmKk~K??G@_@");
+
+gap> ChromaticNumber(gr : zykov);
+4
+gap> gr := CycleDigraph(7);
+
+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 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], [], []]);
+
+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));
+
+gap> ChromaticNumber(gr : christofides);
+2
+gap> gr := DigraphFromGraph6String("KmKk~K??G@_@");
+
+gap> ChromaticNumber(gr : christofides);
+4
+gap> gr := CycleDigraph(7);
+
+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);