# Ticket #6679: coloring-1.patch

File coloring-1.patch, 14.5 KB (added by ncohen, 11 years ago)

First patch !

• ## sage/graphs/graph.py

```# HG changeset patch
# User Nathann Cohen <nathann.cohen@gmail.com>
# Date 1249571353 25200
# Node ID dd1aef342549705197e840f31ac74af03e29c281
# Parent  5543f92abe9095489f5b3b4204896cbf8556fa95
Functions vertex_coloring and edge_coloring in Graph

diff -r 5543f92abe90 -r dd1aef342549 sage/graphs/graph.py```
 a else: return C.values() def vertex_coloring(self,k=None,value_only=False,hex_colors=False,log=0): """ Computes the chromatic number of a graph, or tests its k-colorability ( cf. http://en.wikipedia.org/wiki/Graph_coloring ) INPUT: - ``value_only'' : When set to True, only the chromatic number is returned When set to False (default), a partition of the vertex set into independant sets is returned is possible - ``k'' ( an integer ) : 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 - ``hex_colors'' : When set to True ( it is False by default ), the partition returned is a dictionary whose keys are colors and whose values are the color classes ( ideal to be plotted ) - ``log'' ( integer ) : As vertex-coloring is a NP-complete problem, its solving may take some time depending on the graph. Use `log' to define the level of verbosity you want from the linear program solver. By default log=0, meaning that there will be no message printed by the solver. OUTPUT : - If k=None and value_only=None: Returns a partition of the vertex set into the minimum possible of independent sets - If k=None and value_only=True: Returns the chromatic number - If k is set and value_only=None: Returns 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 returns True or False accordingly EXAMPLE: sage: g=graphs.PetersenGraph() sage: g.vertex_coloring(value_only=True) 3 """ from sage.numerical.mip import MIP from sage.plot.colors import rainbow g=self if k==None: # no need to start a linear program if the graph is an independent set or bipartite if g.size()==0: return ([g.vertices] if value_only==False else g.order()) if g.is_bipartite(): if value_only==True: return 2 tmp=g.bipartite_sets() return [tmp[0],tmp[1]] # no need to try any k smaller than the maximum clique in the graph k=g.clique_number() while True: # tries to color the graph, increasing k each time it fails. tmp=g.vertex_coloring(k=k, value_only=value_only,hex_colors=hex_colors,log=log) if tmp!=False: if value_only: return k else: return tmp k=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 if g.order()==0: if value_only==True: return True if hex_colors==True: return dict([(color,[]) for color in rainbow(k)]) else: return [[] for i in range(k)] # Is the graph connected ? # This is not so stupid, as the graph could be disconnected # by the test of degeneracy ( as previously ) if g.is_connected()==False: if value_only==True: for component in g.connected_components(): if g.subgraph(component).vertex_coloring(k=k,value_only=value_only,hex_colors=hex_colors,log=log)==False: return False return True if hex_colors==True: colorings=[] for component in g.connected_components(): tmp=g.subgraph(component).vertex_coloring(k=k,value_only=value_only,hex_colors=True,log=log) if tmp==False: return False colorings.append(tmp) value=dict([(color,[]) for color in rainbow(k)]) for color in rainbow(k): for component in colorings: value[color].extend(component[color]) return value else: colorings=[] for component in g.connected_components(): tmp=g.subgraph(component).vertex_coloring(k=k,value_only=value_only,hex_colors=False,log=log) if tmp==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]) return value # Degeneracy # Vertices whose degree is less than k are of no importance in the coloring if min(g.degree())0: v=tmp.pop(0) neighbors=list(set(g.neighbors(v)) & vertices) if v in vertices and len(neighbors)0: for (color,classe) in value.items(): if len(list(set(classe) & set(g.neighbors(deg[-1]))))==0: value[color].append(deg[-1]) deg.pop(-1) break return value else: value=g.subgraph(list(vertices)).vertex_coloring(k=k,value_only=value_only,hex_colors=hex_colors,log=log) if value==False: return False while len(deg)>0: for classe in value: if len(list(set(classe) & set(g.neighbors(deg[-1]))))==0: classe.append(deg[-1]) deg.pop(-1) break return value p=MIP(sense=1) obj={} # a vertex has only one color for c in [[((v,i),1) for i in range(k)] for v in g.vertices()]: p.addconstraint(dict(c),max=1) for e in g.edges(): for i in range(k): obj[(e[0],i)]=1 obj[(e[1],i)]=1 p.setbinary((e[1],i)) p.setbinary((e[0],i)) p.addconstraint({(e[0],i):1,(e[1],i):1},max=1) p.addconstraint(obj,min=g.order(),max=g.order()) p.setobj(obj) try: if value_only==True: tmp=p.solve(objective_only=True,log=log) return True else: tmp=p.solve(log=log) except: return False # builds the color classes a=[0]*k for i in range(k): a[i]=[] for i in tmp[1].items(): if i[1]==1: i=i[0] a[i[1]].append(i[0]) # if needed, builds a dictionary from the color classes adding colors if hex_colors: b={} r=rainbow(len(a)) for l in range(len(a)): b[r[l]]=a[l] return b return a def edge_coloring(self,value_only=False,vizing=False,hex_colors=False, log=0): """ Properly colors the edges of a graph. ( cf. http://en.wikipedia.org/wiki/Edge_coloring ) INPUT: - ``value_only'' : When set to True, only the chromatic index is returned When set to False (default), a partition of the edge set into matchings is returned is possible - ``vizing'' ( an boolean ) : 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 --- Implies value_only = False --- - ``hex_colors'' : When set to True ( it is False by default ), the partition returned is a dictionary whose keys are colors and whose values are the color classes ( ideal to be plotted ) - ``log'' ( integer ) : As edge-coloring is a NP-complete problem, its solving may take some time depending on the graph. Use log to define the level of verbosity you want from the linear program solver. By default log=0, meaning that there will be no message printed by the solver. OUTPUT : In the following, Delta is equal to the maximum degree in the graph - If vizing=True and value_only=False: Returns a partition of the edge set into Delta+1 matchings - If vizing=False and value_only=True: Returns the chromatic index - If vizing=False and value_only=False Returns a partition of the edge set into the minimum number of matchings - If vizing=True is set 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 its maximum degree or its maximum degree+1 EXAMPLE: sage: g=graphs.PetersenGraph() sage: g.edge_coloring(value_only=True) 7 """ from sage.numerical.mip import MIP from sage.plot.colors import rainbow g=self # An edge (u,v) is sometimes returned as (v,u) and we want to avoid that. def reorder_edge((u,uu,uuu)): if u<=uu: return ((u,uu,uuu)) else: return ((uu,u,uuu)) p=MIP(sense=1) obj={} k=max(g.degree()) # Vizing's coloring uses Delta+1 colors if vizing: value_only=False k=k+1 for v in g.vertices(): for i in range(k): c={} # for any vertex v and for any color i, there is only one # edge of color i adjacent to v for e in g.edges_incident(v): c[(reorder_edge(e),i)]=1 obj[(reorder_edge(e),i)]=1 p.addconstraint(c,max=1) for e in g.edges(): c={} # an edge has at most one color for i in range(k): c[(reorder_edge(e),i)]=1 p.setbinary((reorder_edge(e),i)) p.addconstraint(c,max=1) # all the edges must have a color p.addconstraint(obj,max=g.size(),min=g.size()) p.setobj(obj) try: if value_only==True: p.solve(objective_only=True,log=log) else: tmp=p.solve(log=log) except: if value_only: return k+1 else: # if the coloring with Delta colors fails, tries Delta+1 return g.edge_coloring(vizing=True,hex_colors=hex_colors,log=log) if value_only: return k a=[[] for i in range(k)] # builds the color classes for i in range(k): a[i]=[] for i in tmp[1].items(): if i[1]==1: i=i[0] a[i[1]].append(i[0]) # if needed, builds a dictionary from the color classes adding colors if hex_colors: b={} r=rainbow(len(a)) for l in range(len(a)): b[r[l]]=a[l] return b else: return a ### Centrality def centrality_betweenness(self, normalized=True):