Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
""" Graph coloring
This module gathers all methods related to graph coloring. Here is what it can do :
**Proper vertex coloring**
.. csv-table:: :class: contentstable :widths: 30, 70 :delim: |
:meth:`all_graph_colorings` | Computes all `n`-colorings a graph :meth:`first_coloring` | Returns the first vertex coloring found :meth:`number_of_n_colorings` | Computes the number of `n`-colorings of a graph :meth:`numbers_of_colorings` | Computes the number of colorings of a graph :meth:`chromatic_number` | Returns the chromatic number of the graph :meth:`vertex_coloring` | Computes Vertex colorings and chromatic numbers
**Other colorings**
.. csv-table:: :class: contentstable :widths: 30, 70 :delim: |
:meth:`grundy_coloring` | Computes Grundy numbers and Grundy colorings :meth:`b_coloring` | Computes a b-chromatic numbers and b-colorings :meth:`edge_coloring` | Compute chromatic index and edge colorings :meth:`round_robin` | Computes a round-robin coloring of the complete graph on `n` vertices :meth:`linear_arboricity` | Computes the linear arboricity of the given graph :meth:`acyclic_edge_coloring` | Computes an acyclic edge coloring of the current graph
AUTHORS:
- Tom Boothby (2008-02-21): Initial version - Carlo Hamalainen (2009-03-28): minor change: switch to C++ DLX solver - Nathann Cohen (2009-10-24): Coloring methods using linear programming
Methods ------- """
#***************************************************************************** # Copyright (C) 2008 Tom Boothby <boothby@u.washington.edu> # # Distributed under the terms of the GNU General Public License (GPL) # http://www.gnu.org/licenses/ #*****************************************************************************
r""" Computes all `n`-colorings of the graph `G` by casting the graph coloring problem into an exact cover problem, and passing this into an implementation of the Dancing Links algorithm described by Knuth (who attributes the idea to Hitotumatu and Noshita).
INPUT:
* ``G`` - a graph
* ``n`` - a positive integer the number of colors
* `count_only` -- (default: ``False``) when set to ``True``, it returns 1 for each coloring
* `hex_colors` -- (default: ``False``) when set to ``False``, it labels the colors [0,1,.., ``n`` - 1], otherwise it uses the RGB Hex labeling
* `vertex_color_dict` -- (default: ``False``) when set to ``True``, it returns a dictionary {vertex:color}, otherwise it returns a dictionary {color:[list of vertices]}
The construction works as follows. Columns:
* The first `|V|` columns correspond to a vertex -- a `1` in this column indicates that that vertex has a color.
* After those `|V|` columns, we add `n*|E|` columns -- a `1` in these columns indicate that a particular edge is incident to a vertex with a certain color.
Rows:
* For each vertex, add `n` rows; one for each color `c`. Place a `1` in the column corresponding to the vertex, and a `1` in the appropriate column for each edge incident to the vertex, indicating that that edge is incident to the color `c`.
* If `n > 2`, the above construction cannot be exactly covered since each edge will be incident to only two vertices (and hence two colors) - so we add `n*|E|` rows, each one containing a `1` for each of the `n*|E|` columns. These get added to the cover solutions "for free" during the backtracking.
Note that this construction results in `n*|V| + 2*n*|E| + n*|E|` entries in the matrix. The Dancing Links algorithm uses a sparse representation, so if the graph is simple, `|E| \leq |V|^2` and `n <= |V|`, this construction runs in `O(|V|^3)` time. Back-conversion to a coloring solution is a simple scan of the solutions, which will contain `|V| + (n-2)*|E|` entries, so runs in `O(|V|^3)` time also. For most graphs, the conversion will be much faster -- for example, a planar graph will be transformed for `4`-coloring in linear time since `|E| = O(|V|)`.
REFERENCES:
http://www-cs-staff.stanford.edu/~uno/papers/dancing-color.ps.gz
EXAMPLES::
sage: from sage.graphs.graph_coloring import all_graph_colorings sage: G = Graph({0:[1,2,3],1:[2]}) sage: n = 0 sage: for C in all_graph_colorings(G,3,hex_colors=True): ....: parts = [C[k] for k in C] ....: for P in parts: ....: l = len(P) ....: for i in range(l): ....: for j in range(i+1,l): ....: if G.has_edge(P[i],P[j]): ....: raise RuntimeError("Coloring Failed.") ....: n+=1 sage: print("G has %s 3-colorings." % n) G has 12 3-colorings.
TESTS::
sage: G = Graph({0:[1,2,3],1:[2]}) sage: for C in all_graph_colorings(G,0): print(C) sage: for C in all_graph_colorings(G,-1): print(C) Traceback (most recent call last): ... ValueError: n must be non-negative. sage: G = Graph({0:[1],1:[2]}) sage: for c in all_graph_colorings(G,2, vertex_color_dict = True): print(c) {0: 0, 1: 1, 2: 0} {0: 1, 1: 0, 2: 1} sage: for c in all_graph_colorings(G,2,hex_colors = True): print(c) {'#00ffff': [1], '#ff0000': [0, 2]} {'#ff0000': [1], '#00ffff': [0, 2]} sage: for c in all_graph_colorings(G,2,hex_colors=True,vertex_color_dict = True): print(c) {0: '#ff0000', 1: '#00ffff', 2: '#ff0000'} {0: '#00ffff', 1: '#ff0000', 2: '#00ffff'} sage: for c in all_graph_colorings(G, 2, vertex_color_dict = True): print(c) {0: 0, 1: 1, 2: 0} {0: 1, 1: 0, 2: 1} sage: for c in all_graph_colorings(G, 2, count_only=True, vertex_color_dict = True): print(c) 1 1 """
else: else: else: else: else: raise RuntimeError("Too much recursion! Graph coloring failed.")
r""" Given a graph, and optionally a natural number `n`, returns the first coloring we find with at least `n` colors.
INPUT:
- ``hex_colors`` -- (default: ``False``) when set to ``True``, the partition returned is a dictionary whose keys are colors and whose values are the color classes (ideal for plotting).
- ``n`` -- The minimal number of colors to try.
EXAMPLES::
sage: from sage.graphs.graph_coloring import first_coloring sage: G = Graph({0: [1, 2, 3], 1: [2]}) sage: first_coloring(G, 3) [[1, 3], [0], [2]] """ else:
r""" Computes the number of `n`-colorings of a graph
EXAMPLES::
sage: from sage.graphs.graph_coloring import number_of_n_colorings sage: G = Graph({0:[1,2,3],1:[2]}) sage: number_of_n_colorings(G,3) 12 """ #Take care of the stupid stuff else: #negative colors?? what does that even mean? return 0
r""" Returns the number of `n`-colorings of the graph `G` for `n` from `0` to `|V|`.
EXAMPLES::
sage: from sage.graphs.graph_coloring import numbers_of_colorings sage: G = Graph({0:[1,2,3],1:[2]}) sage: numbers_of_colorings(G) [0, 0, 0, 12, 72] """
r""" Returns the minimal number of colors needed to color the vertices of the graph `G`.
EXAMPLES::
sage: from sage.graphs.graph_coloring import chromatic_number sage: G = Graph({0:[1,2,3],1:[2]}) sage: chromatic_number(G) 3
sage: G = graphs.PetersenGraph() sage: G.chromatic_number() 3 """ else: #counting cliques is faster than our brute-force method...
r""" Computes the chromatic number of the given graph or tests its `k`-colorability. See http://en.wikipedia.org/wiki/Graph_coloring for further details on graph coloring.
INPUT:
- ``g`` -- a graph.
- ``k`` -- (default: ``None``) tests whether the graph is `k`-colorable. The function returns a partition of the vertex set in `k` independent sets if possible and ``False`` otherwise.
- ``value_only`` -- (default: ``False``):
- When set to ``True``, only the chromatic number is returned.
- When set to ``False`` (default), a partition of the vertex set into independent sets is returned if possible.
- ``hex_colors`` -- (default: ``False``) when set to ``True``, the partition returned is a dictionary whose keys are colors and whose values are the color classes (ideal for plotting).
- ``solver`` -- (default: ``None``) Specify a Linear Program (LP) solver to be used. If set to ``None``, the default one is used. For more information on LP solvers and which default solver is used, see the method :meth:`solve <sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the class :class:`MixedIntegerLinearProgram <sage.numerical.mip.MixedIntegerLinearProgram>`.
- ``verbose`` -- integer (default: ``0``). Sets the level of verbosity. Set to 0 by default, which means quiet.
OUTPUT:
- If ``k=None`` and ``value_only=False``, then return a partition of the vertex set into the minimum possible of independent sets.
- If ``k=None`` and ``value_only=True``, return the chromatic number.
- If ``k`` is set and ``value_only=None``, return ``False`` if the graph is not `k`-colorable, and a partition of the vertex set into `k` independent sets otherwise.
- If ``k`` is set and ``value_only=True``, test whether the graph is `k`-colorable, and return ``True`` or ``False`` accordingly.
EXAMPLES::
sage: from sage.graphs.graph_coloring import vertex_coloring sage: g = graphs.PetersenGraph() sage: vertex_coloring(g, value_only=True) 3
TESTS:
Empty graph::
sage: from sage.graphs.graph_coloring import vertex_coloring sage: empty = Graph() sage: vertex_coloring(empty, value_only=True) 0 sage: vertex_coloring(empty, hex_colors=True) {} sage: vertex_coloring(empty) [] """
# If k is None, tries to find an optimal coloring # No need to start a linear program if the graph is an # independent set, is bipartite, or is empty. # - Empty graph else: # - Independent set if value_only: return 1 elif hex_colors: return {rainbow(1)[0]: g.vertices()} else: return [g.vertices()] # - Bipartite set if value_only: return 2 elif hex_colors: return dict(zip(rainbow(2), g.bipartite_sets())) else: return g.bipartite_sets()
# - No need to try any k smaller than the maximum clique in # - the graph No need to try k less than |G|/alpha(G), as each # color class is at most alpha(G) # - max, because we know it is not bipartite
# tries to color the graph, increasing k each time it fails. hex_colors=hex_colors, verbose=verbose) else: k += 1 else: # Is the graph empty? # If the graph is empty, something should be returned. # This is not so stupid, as the graph could be emptied # by the test of degeneracy. return dict([(color, []) for color in rainbow(k)]) else: # Is the graph connected? # This is not so stupid, as the graph could be disconnected # by the test of degeneracy (as previously). if value_only: for component in g.connected_components(): tmp = vertex_coloring(g.subgraph(component), k=k, value_only=value_only, hex_colors=hex_colors, verbose=verbose) if tmp is False: return False return True colorings = [] for component in g.connected_components(): tmp = vertex_coloring(g.subgraph(component), k=k, value_only=value_only, hex_colors=False, verbose=verbose) if tmp is False: return False colorings.append(tmp) value = [[] for color in range(k)] for color in range(k): for component in colorings: value[color].extend(component[color]) if hex_colors: return dict(zip(rainbow(k), value)) else: return value
# Degeneracy # Vertices whose degree is less than k are of no importance in # the coloring. value_only=value_only, hex_colors=hex_colors, verbose=verbose) value_only=value_only, hex_colors=False, verbose=verbose) return False else:
# a vertex has exactly one color
# adjacent vertices have different colors
# The first vertex is colored with 1. It costs nothing to say # it, and it can help.
else: chi = p.solve(log=verbose) except MIPSolverException: return False
color = p.get_values(color) # builds the color classes classes = [[] for i in range(k)]
for v in g.vertices(): for i in range(k): if color[v,i] == 1: classes[i].append(v) break
if hex_colors: return dict(zip(rainbow(len(classes)), classes)) else: return classes
r""" Computes the worst-case of a first-fit coloring with less than `k` colors.
Definition :
A first-fit coloring is obtained by sequentially coloring the vertices of a graph, assigning them the smallest color not already assigned to one of its neighbors. The result is clearly a proper coloring, which usually requires much more colors than an optimal vertex coloring of the graph, and heavily depends on the ordering of the vertices.
The number of colors required by the worst-case application of this algorithm on a graph `G` is called the Grundy number, written `\Gamma (G)`.
Equivalent formulation :
Equivalently, a Grundy coloring is a proper vertex coloring such that any vertex colored with `i` has, for every `j<i`, a neighbor colored with `j`. This can define a Linear Program, which is used here to compute the Grundy number of a graph.
.. NOTE::
This method computes a grundy coloring using at *MOST* `k` colors. If this method returns a value equal to `k`, it can not be assumed that `k` is equal to `\Gamma(G)`. Meanwhile, if it returns any value `k' < k`, this is a certificate that the Grundy number of the given graph is `k'`.
As `\Gamma(G)\leq \Delta(G)+1`, it can also be assumed that `\Gamma(G) = k` if ``grundy_coloring(g, k)`` returns `k` when `k = \Delta(G) +1`.
INPUT:
- ``k`` (integer) -- Maximum number of colors
- ``solver`` -- (default: ``None``) Specify a Linear Program (LP) solver to be used. If set to ``None``, the default one is used. For more information on LP solvers and which default solver is used, see the method :meth:`solve <sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the class :class:`MixedIntegerLinearProgram <sage.numerical.mip.MixedIntegerLinearProgram>`.
- ``value_only`` -- boolean (default: ``True``). When set to ``True``, only the number of colors is returned. Otherwise, the pair ``(nb_colors, coloring)`` is returned, where ``coloring`` is a dictionary associating its color (integer) to each vertex of the graph.
- ``verbose`` -- integer (default: ``0``). Sets the level of verbosity. Set to 0 by default, which means quiet.
ALGORITHM:
Integer Linear Program.
EXAMPLES:
The Grundy number of a `P_4` is equal to 3::
sage: from sage.graphs.graph_coloring import grundy_coloring sage: g = graphs.PathGraph(4) sage: grundy_coloring(g, 4) 3
The Grundy number of the PetersenGraph is equal to 4::
sage: g = graphs.PetersenGraph() sage: grundy_coloring(g, 5) 4
It would have been sufficient to set the value of ``k`` to 4 in this case, as `4 = \Delta(G)+1`. """
# List of colors
# b[v,i] is set to 1 if and only if v is colored with i
# is_used[i] is set to 1 if and only if color [i] is used by some # vertex
# Each vertex is in exactly one class
# Two adjacent vertices have different classes
# The following constraints ensure that if v is colored with i, # then it has a neighbor colored with j for every j<i
# If b[v,i] == 0, then the following constraint is # always satisfied, as a sum of binary variables is # always positive. If it is equal to 1, then at least # one of fthe other variables must be set to 1 too.
# is_used[i] can be set to 1 only if the color is used
# Trying to use as many colors as possible
except MIPSolverException: raise ValueError("This graph can not be colored with k colors")
# Building the dictionary associating its color to every vertex
b = p.get_values(b) coloring = {}
for v in g: for i in classes: if b[v,i] == 1: coloring[v] = i break
return obj, coloring
r""" Computes a b-coloring with at most k colors that maximizes the number of colors, if such a coloring exists
Definition :
Given a proper coloring of a graph `G` and a color class `C` such that none of its vertices have neighbors in all the other color classes, one can eliminate color class `C` assigning to each of its elements a missing color in its neighborhood.
Let a b-vertex be a vertex with neighbors in all other colorings. Then, one can repeat the above procedure until a coloring is obtained where every color class contains a b-vertex, in which case none of the color classes can be eliminated with the same ideia. So, one can define a b-coloring as a proper coloring where each color class has a b-vertex.
In the worst case, after successive applications of the above procedure, one get a proper coloring that uses a number of colors equal to the the b-chromatic number of `G` (denoted `\chi_b(G)`): the maximum `k` such that `G` admits a b-coloring with `k` colors.
An useful upper bound for calculating the b-chromatic number is the following. If G admits a b-coloring with k colors, then there are `k` vertices of degree at least `k - 1` (the b-vertices of each color class). So, if we set `m(G) = max` \{`k | `there are k vertices of degree at least `k - 1`\}, we have that `\chi_b(G) \leq m(G)`.
.. NOTE::
This method computes a b-coloring that uses at *MOST* `k` colors. If this method returns a value equal to `k`, it can not be assumed that `k` is equal to `\chi_b(G)`. Meanwhile, if it returns any value `k' < k`, this is a certificate that the Grundy number of the given graph is `k'`.
As `\chi_b(G)\leq m(G)`, it can be assumed that `\chi_b(G) = k` if ``b_coloring(g, k)`` returns `k` when `k = m(G)`.
INPUT:
- ``k`` (integer) -- Maximum number of colors
- ``solver`` -- (default: ``None``) Specify a Linear Program (LP) solver to be used. If set to ``None``, the default one is used. For more information on LP solvers and which default solver is used, see the method :meth:`solve <sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the class :class:`MixedIntegerLinearProgram <sage.numerical.mip.MixedIntegerLinearProgram>`.
- ``value_only`` -- boolean (default: ``True``). When set to ``True``, only the number of colors is returned. Otherwise, the pair ``(nb_colors, coloring)`` is returned, where ``coloring`` is a dictionary associating its color (integer) to each vertex of the graph.
- ``verbose`` -- integer (default: ``0``). Sets the level of verbosity. Set to 0 by default, which means quiet.
ALGORITHM:
Integer Linear Program.
EXAMPLES:
The b-chromatic number of a `P_5` is equal to 3::
sage: from sage.graphs.graph_coloring import b_coloring sage: g = graphs.PathGraph(5) sage: b_coloring(g, 5) 3
The b-chromatic number of the Petersen Graph is equal to 3::
sage: g = graphs.PetersenGraph() sage: b_coloring(g, 5) 3
It would have been sufficient to set the value of ``k`` to 4 in this case, as `4 = m(G)`. """
# Calculate the upper bound m(G) # To do so, it takes the list of degrees in # non-increasing order and computes the largest # i, such that the ith degree on the list is # at least i - 1 (note that in the code we need # to take in consideration that the indices # of the list starts with 0)
else: m = g.order()
# In case the k specified by the user is greater than m(G), make k = m(G)
# List of possible colors
#color[v,i] is set to 1 if and only if v is colored i
#b[v,i] is set to 1 if and only if v is a b-vertex from color class i
#is_used[i] is set to 1 if and only if color [i] is used by some vertex
# Each vertex is in exactly one class
# Adjacent vertices have distinct colors
# The following constraints ensure that if v is a b-vertex of color i # then it has a neighbor colored j for every j != i
# If v is not a b-vertex of color i, the constraint # is always satisfied, since the only possible # negative term in this case is -is_used[j] which is # cancelled by + 1. If v is a b-vertex of color i # then we MUST have sum(color[w,j] for w in g.neighbors(v)) # valued at least 1, which means that v has a neighbour in # color j, as desired. + 1 - is_used[j], min=0)
#if color i is used, there is a vertex colored i
#if there is a vertex colored with color i, then i is used
#a color class is used if and only if it has one b-vertex
#We want to maximize the number of used colors
except MIPSolverException: raise ValueError("This graph can not be colored with k colors")
# Building the dictionary associating its color to every vertex
c = p.get_values(color) coloring = {}
for v in g: for i in classes: if c[v,i] == 1: coloring[v] = i break
return obj, coloring
r""" Compute chromatic index and edge colorings.
INPUT:
- ``g`` -- a graph.
- ``value_only`` -- (default: ``False``):
- When set to ``True``, only the chromatic index is returned.
- When set to ``False``, a partition of the edge set into matchings is returned if possible.
- ``vizing`` -- (default: ``False``):
- When set to ``True``, tries to find a `\Delta + 1`-edge-coloring, where `\Delta` is equal to the maximum degree in the graph.
- When set to ``False``, tries to find a `\Delta`-edge-coloring, where `\Delta` is equal to the maximum degree in the graph. If impossible, tries to find and returns a `\Delta + 1`-edge-coloring. This implies that ``value_only=False``.
- ``hex_colors`` -- (default: ``False``) when set to ``True``, the partition returned is a dictionary whose keys are colors and whose values are the color classes (ideal for plotting).
- ``solver`` -- (default: ``None``) Specify a Linear Program (LP) solver to be used. If set to ``None``, the default one is used. For more information on LP solvers and which default solver is used, see the method :meth:`solve <sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the class :class:`MixedIntegerLinearProgram <sage.numerical.mip.MixedIntegerLinearProgram>`.
- ``verbose`` -- integer (default: ``0``). Sets the level of verbosity. Set to 0 by default, which means quiet.
OUTPUT:
In the following, `\Delta` is equal to the maximum degree in the graph ``g``.
- If ``vizing=True`` and ``value_only=False``, return a partition of the edge set into `\Delta + 1` matchings.
- If ``vizing=False`` and ``value_only=True``, return the chromatic index.
- If ``vizing=False`` and ``value_only=False``, return a partition of the edge set into the minimum number of matchings.
- If ``vizing=True`` and ``value_only=True``, should return something, but mainly you are just trying to compute the maximum degree of the graph, and this is not the easiest way. By Vizing's theorem, a graph has a chromatic index equal to `\Delta` or to `\Delta + 1`.
.. NOTE::
In a few cases, it is possible to find very quickly the chromatic index of a graph, while it remains a tedious job to compute a corresponding coloring. For this reason, ``value_only = True`` can sometimes be much faster, and it is a bad idea to compute the whole coloring if you do not need it !
.. SEEALSO::
- :wikipedia:`Edge_coloring` for further details on edge coloring - :meth:`~Graph.chromatic_index` - :meth:`~Graph.fractional_chromatic_index` - :meth:`~Graph.chromatic_number` - :meth:`sage.graphs.graph_coloring.vertex_coloring`
EXAMPLES:
The Petersen graph has chromatic index 4::
sage: from sage.graphs.graph_coloring import edge_coloring sage: g = graphs.PetersenGraph() sage: edge_coloring(g, value_only=True, solver='GLPK') 4 sage: edge_coloring(g, value_only=False, solver='GLPK') [[(0, 1), (2, 3), (4, 9), (5, 7)], [(0, 4), (1, 2), (3, 8), (6, 9)], [(1, 6), (5, 8), (7, 9)], [(0, 5), (2, 7), (3, 4), (6, 8)]] sage: edge_coloring(g, value_only=False, hex_colors=True, solver='GLPK') {'#00ffff': [(1, 6), (5, 8), (7, 9)], '#7f00ff': [(0, 5), (2, 7), (3, 4), (6, 8)], '#7fff00': [(0, 4), (1, 2), (3, 8), (6, 9)], '#ff0000': [(0, 1), (2, 3), (4, 9), (5, 7)]}
Complete graphs are colored using the linear-time round-robin coloring::
sage: from sage.graphs.graph_coloring import edge_coloring sage: len(edge_coloring(graphs.CompleteGraph(20))) 19
The chromatic index of a non connected graph is the maximum over its connected components::
sage: g = graphs.CompleteGraph(4) + graphs.CompleteGraph(10) sage: edge_coloring(g, value_only=True) 9
TESTS:
Graph without edge::
sage: g = Graph(2) sage: edge_coloring(g) [] sage: edge_coloring(g, value_only=True) 0 sage: edge_coloring(g, hex_colors=True) {} """
value_only = False
""" Helper method to maintain an ordering """
# The chromatic index of g is the maximum value over its connected # components, and the edge coloring is the union of the edge # coloring of its connected components
continue
# We get the vertex of maximum degree and its degree
# create missing color classes, if any # add edges to classes
# Vizing's coloring uses Delta + 1 colors. Otherwise, we try both.
# A vertex can not have two incident edges with the same color. # Nn edge must have a color # We color the edges of the vertex of maximum degree raise RuntimeError("Something is wrong! Certainly a problem in the" " algorithm... please contact sage-devel@googlegroups.com") # The coloring fails with Delta colors
else: # create missing color classes, if any # add edges to color classes
# if needed, builds a dictionary from the color classes adding colors else:
r""" Computes a round-robin coloring of the complete graph on `n` vertices.
A round-robin coloring of the complete graph `G` on `2n` vertices (`V = [0, \dots, 2n - 1]`) is a proper coloring of its edges such that the edges with color `i` are all the `(i + j, i - j)` plus the edge `(2n - 1, i)`.
If `n` is odd, one obtain a round-robin coloring of the complete graph through the round-robin coloring of the graph with `n + 1` vertices.
INPUT:
- ``n`` -- the number of vertices in the complete graph.
OUTPUT:
- A ``CompleteGraph`` with labelled edges such that the label of each edge is its color.
EXAMPLES::
sage: from sage.graphs.graph_coloring import round_robin sage: round_robin(3).edges() [(0, 1, 2), (0, 2, 1), (1, 2, 0)]
::
sage: round_robin(4).edges() [(0, 1, 2), (0, 2, 1), (0, 3, 0), (1, 2, 0), (1, 3, 1), (2, 3, 2)]
For higher orders, the coloring is still proper and uses the expected number of colors.
::
sage: g = round_robin(9) sage: sum([Set([e[2] for e in g.edges_incident(v)]).cardinality() for v in g]) == 2*g.size() True sage: Set([e[2] for e in g.edge_iterator()]).cardinality() 9
::
sage: g = round_robin(10) sage: sum([Set([e[2] for e in g.edges_incident(v)]).cardinality() for v in g]) == 2*g.size() True sage: Set([e[2] for e in g.edge_iterator()]).cardinality() 9 """ raise ValueError("There must be at least two vertices in the graph.") else:
r""" Computes the linear arboricity of the given graph.
The linear arboricity of a graph `G` is the least number `la(G)` such that the edges of `G` can be partitioned into linear forests (i.e. into forests of paths).
Obviously, `la(G)\geq \lceil \frac{\Delta(G)}{2} \rceil`.
It is conjectured in [Aki1980]_ that `la(G)\leq \lceil \frac{\Delta(G)+1}{2} \rceil`.
INPUT:
- ``hex_colors`` (boolean)
- If ``hex_colors = True``, the function returns a dictionary associating to each color a list of edges (meant as an argument to the ``edge_colors`` keyword of the ``plot`` method).
- If ``hex_colors = False`` (default value), returns a list of graphs corresponding to each color class.
- ``value_only`` (boolean)
- If ``value_only = True``, only returns the linear arboricity as an integer value.
- If ``value_only = False``, returns the color classes according to the value of ``hex_colors``
- ``plus_one`` (integer) -- whether to use `\lceil \frac{\Delta(G)}{2} \rceil` or `\lceil \frac{\Delta(G)+1}{2} \rceil` colors.
- If ``0``, computes a decomposition of `G` into `\lceil \frac{\Delta(G)}{2} \rceil` forests of paths
- If ``1``, computes a decomposition of `G` into `\lceil \frac{\Delta(G)+1}{2} \rceil` colors, which is the conjectured general bound.
- If ``plus_one = None`` (default), computes a decomposition using the least possible number of colors.
- ``solver`` -- (default: ``None``) Specify a Linear Program (LP) solver to be used. If set to ``None``, the default one is used. For more information on LP solvers and which default solver is used, see the method :meth:`~sage.numerical.mip.MixedIntegerLinearProgram.solve` of the class :class:`~sage.numerical.mip.MixedIntegerLinearProgram`.
- ``verbose`` -- integer (default: ``0``). Sets the level of verbosity of the LP solver. Set to 0 by default, which means quiet.
ALGORITHM:
Linear Programming
COMPLEXITY:
NP-Hard
EXAMPLES:
Obviously, a square grid has a linear arboricity of 2, as the set of horizontal lines and the set of vertical lines are an admissible partition::
sage: from sage.graphs.graph_coloring import linear_arboricity sage: g = graphs.GridGraph([4,4]) sage: g1,g2 = linear_arboricity(g)
Each graph is of course a forest::
sage: g1.is_forest() and g2.is_forest() True
Of maximum degree 2::
sage: max(g1.degree()) <= 2 and max(g2.degree()) <= 2 True
Which constitutes a partition of the whole edge set::
sage: all([g1.has_edge(e) or g2.has_edge(e) for e in g.edges(labels = None)]) True
TESTS:
Asking for the value of the linear arboricity only (:trac:`24991`)::
sage: from sage.graphs.graph_coloring import linear_arboricity sage: [linear_arboricity(G, value_only=True) for G in graphs(4)] [0, 1, 1, 2, 2, 1, 1, 2, 2, 2, 2] """
plus_one = 0, value_only = value_only, hex_colors = hex_colors, solver = solver, verbose = verbose) plus_one = 1, value_only = value_only, hex_colors = hex_colors, solver = solver, verbose = verbose) else: raise ValueError("plus_one must be equal to 0,1, or to None !")
# c is a boolean value such that c[i,(u,v)] = 1 if and only if (u,v) is colored with i
# relaxed value
# Partition of the edges
# r greater than c
# Maximum degree 2
# no cycles
raise RuntimeError("It looks like you have found a counterexample to a very old conjecture. Please do not loose it ! Please publish it, and send a post to sage-devel to warn us. I implore you ! Nathann Cohen ") else:
answer = [[] for i in range(k)] def add(uv, i): return answer[i].append(uv) else:
return dict(zip(rainbow(len(classes)),classes)) else:
r""" Computes an acyclic edge coloring of the current graph.
An edge coloring of a graph is a assignment of colors to the edges of a graph such that :
- the coloring is proper (no adjacent edges share a color) - For any two colors `i,j`, the union of the edges colored with `i` or `j` is a forest.
The least number of colors such that such a coloring exists for a graph `G` is written `\chi'_a(G)`, also called the acyclic chromatic index of `G`.
It is conjectured that this parameter can not be too different from the obvious lower bound `\Delta(G)\leq \chi'_a(G)`, `\Delta(G)` being the maximum degree of `G`, which is given by the first of the two constraints. Indeed, it is conjectured that `\Delta(G)\leq \chi'_a(G) \leq \Delta(G) + 2`.
INPUT:
- ``hex_colors`` (boolean)
- If ``hex_colors = True``, the function returns a dictionary associating to each color a list of edges (meant as an argument to the ``edge_colors`` keyword of the ``plot`` method).
- If ``hex_colors = False`` (default value), returns a list of graphs corresponding to each color class.
- ``value_only`` (boolean)
- If ``value_only = True``, only returns the acyclic chromatic index as an integer value
- If ``value_only = False``, returns the color classes according to the value of ``hex_colors``
- ``k`` (integer) -- the number of colors to use.
- If ``k>0``, computes an acyclic edge coloring using `k` colors.
- If ``k=0`` (default), computes a coloring of `G` into `\Delta(G) + 2` colors, which is the conjectured general bound.
- If ``k=None``, computes a decomposition using the least possible number of colors.
- ``solver`` -- (default: ``None``) Specify a Linear Program (LP) solver to be used. If set to ``None``, the default one is used. For more information on LP solvers and which default solver is used, see the method :meth:`~sage.numerical.mip.MixedIntegerLinearProgram.solve` of the class :class:`~sage.numerical.mip.MixedIntegerLinearProgram`.
- ``verbose`` -- integer (default: ``0``). Sets the level of verbosity of the LP solver. Set to 0 by default, which means quiet.
ALGORITHM:
Linear Programming
EXAMPLES:
The complete graph on 8 vertices can not be acyclically edge-colored with less `\Delta+1` colors, but it can be colored with `\Delta+2=9`::
sage: from sage.graphs.graph_coloring import acyclic_edge_coloring sage: g = graphs.CompleteGraph(8) sage: colors = acyclic_edge_coloring(g)
Each color class is of course a matching ::
sage: all([max(gg.degree())<=1 for gg in colors]) True
These matchings being a partition of the edge set::
sage: all([ any([gg.has_edge(e) for gg in colors]) for e in g.edges(labels = False)]) True
Besides, the union of any two of them is a forest ::
sage: all([g1.union(g2).is_forest() for g1 in colors for g2 in colors]) True
If one wants to acyclically color a cycle on `4` vertices, at least 3 colors will be necessary. The function raises an exception when asked to color it with only 2::
sage: g = graphs.CycleGraph(4) sage: acyclic_edge_coloring(g, k=2) Traceback (most recent call last): ... ValueError: This graph can not be colored with the given number of colors.
The optimal coloring give us `3` classes::
sage: colors = acyclic_edge_coloring(g, k=None) sage: len(colors) 3
TESTS:
Ticket :trac:`24991` is fixed::
sage: from sage.graphs.graph_coloring import acyclic_edge_coloring sage: [acyclic_edge_coloring(G, value_only=True) for G in graphs(4)] [2, 3, 4, 4, 5, 3, 4, 5, 4, 5, 5] """
value_only = value_only, hex_colors = hex_colors, k = k, solver = solver, verbose = verbose)
raise RuntimeError("This should not happen. Please report a bug !")
# c is a binary variable such that c[i,(u,v)] = 1 if and only if (u,v) is # colored with i
# relaxed value
# Partition of the edges: each edge is assigned a unique color
# Maximum degree 1
# r is greater than c
# r greater than c
raise RuntimeError("It looks like you have found a counterexample to a very old conjecture. Please do not loose it ! Please publish it, and send a post to sage-devel to warn us. I implore you ! Nathann Cohen ") else:
answer = [[] for i in range(k)] def add(uv, i): return answer[i].append(uv) else:
return dict(zip(rainbow(len(classes)),classes)) else:
r""" This class performs randomized testing for all_graph_colorings. Since everything else in this file is derived from all_graph_colorings, this is a pretty good randomized tester for the entire file. Note that for a graph `G`, ``G.chromatic_polynomial()`` uses an entirely different algorithm, so we provide a good, independent test. """
r""" Calls ``self.random_all_graph_colorings()``. In the future, if other methods are added, it should call them, too.
TESTS::
sage: from sage.graphs.graph_coloring import Test sage: Test().random(1) """
r""" Verifies the results of ``all_graph_colorings()`` in three ways:
#. all colorings are unique
#. number of m-colorings is `P(m)` (where `P` is the chromatic polynomial of the graph being tested)
#. colorings are valid -- that is, that no two vertices of the same color share an edge.
TESTS::
sage: from sage.graphs.graph_coloring import Test sage: Test().random_all_graph_colorings(1) """
raise RuntimeError("Coloring Failed.")
#make the dict into a set for quick uniqueness checking
raise RuntimeError("Incorrect number of unique colorings!") |