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);