| 9841 | |
| 9842 | def vertex_coloring(self,k=None,value_only=False,hex_colors=False,log=0): |
| 9843 | """ |
| 9844 | Computes the chromatic number of a graph, or tests its k-colorability |
| 9845 | ( cf. http://en.wikipedia.org/wiki/Graph_coloring ) |
| 9846 | |
| 9847 | INPUT: |
| 9848 | |
| 9849 | - ``value_only'' : When set to True, only the chromatic number is returned |
| 9850 | When set to False (default), a partition of the vertex set into |
| 9851 | independant sets is returned is possible |
| 9852 | |
| 9853 | - ``k'' ( an integer ) : |
| 9854 | Tests whether the graph is k-colorable. The function returns |
| 9855 | a partition of the vertex set in k independent sets if possible |
| 9856 | and False otherwise |
| 9857 | |
| 9858 | - ``hex_colors'' : |
| 9859 | When set to True ( it is False by default ), the partition returned |
| 9860 | is a dictionary whose keys are colors and whose values are the color classes |
| 9861 | ( ideal to be plotted ) |
| 9862 | |
| 9863 | - ``log'' ( integer ) : |
| 9864 | As vertex-coloring is a NP-complete problem, its solving may take some time |
| 9865 | depending on the graph. Use `log' to define the level of verbosity you want |
| 9866 | from the linear program solver. |
| 9867 | By default log=0, meaning that there will be no message printed by the solver. |
| 9868 | |
| 9869 | OUTPUT : |
| 9870 | |
| 9871 | - If k=None and value_only=None: |
| 9872 | Returns a partition of the vertex set into the minimum possible of independent sets |
| 9873 | |
| 9874 | - If k=None and value_only=True: |
| 9875 | Returns the chromatic number |
| 9876 | |
| 9877 | - If k is set and value_only=None: |
| 9878 | Returns False if the graph is not k-colorable, and a partition of the |
| 9879 | vertex set into k independent sets otherwise |
| 9880 | |
| 9881 | - If k is set and value_only=True: |
| 9882 | Test whether the graph is k-colorable and returns True or False accordingly |
| 9883 | |
| 9884 | |
| 9885 | EXAMPLE: |
| 9886 | |
| 9887 | sage: g=graphs.PetersenGraph() |
| 9888 | sage: g.vertex_coloring(value_only=True) |
| 9889 | 3 |
| 9890 | |
| 9891 | """ |
| 9892 | from sage.numerical.mip import MIP |
| 9893 | from sage.plot.colors import rainbow |
| 9894 | g=self |
| 9895 | if k==None: |
| 9896 | # no need to start a linear program if the graph is an independent set or bipartite |
| 9897 | if g.size()==0: |
| 9898 | return ([g.vertices] if value_only==False else g.order()) |
| 9899 | if g.is_bipartite(): |
| 9900 | if value_only==True: |
| 9901 | return 2 |
| 9902 | tmp=g.bipartite_sets() |
| 9903 | return [tmp[0],tmp[1]] |
| 9904 | # no need to try any k smaller than the maximum clique in the graph |
| 9905 | k=g.clique_number() |
| 9906 | |
| 9907 | while True: |
| 9908 | # tries to color the graph, increasing k each time it fails. |
| 9909 | tmp=g.vertex_coloring(k=k, value_only=value_only,hex_colors=hex_colors,log=log) |
| 9910 | if tmp!=False: |
| 9911 | if value_only: |
| 9912 | return k |
| 9913 | else: |
| 9914 | return tmp |
| 9915 | k=k+1 |
| 9916 | else: |
| 9917 | # Is the graph empty ? |
| 9918 | |
| 9919 | # If the graph is empty, something should be returned.. |
| 9920 | # This is not so stupid, as the graph could be emptied |
| 9921 | # by the test of degeneracy |
| 9922 | if g.order()==0: |
| 9923 | if value_only==True: |
| 9924 | return True |
| 9925 | if hex_colors==True: |
| 9926 | return dict([(color,[]) for color in rainbow(k)]) |
| 9927 | else: |
| 9928 | return [[] for i in range(k)] |
| 9929 | |
| 9930 | # Is the graph connected ? |
| 9931 | |
| 9932 | # This is not so stupid, as the graph could be disconnected |
| 9933 | # by the test of degeneracy ( as previously ) |
| 9934 | |
| 9935 | if g.is_connected()==False: |
| 9936 | if value_only==True: |
| 9937 | for component in g.connected_components(): |
| 9938 | if g.subgraph(component).vertex_coloring(k=k,value_only=value_only,hex_colors=hex_colors,log=log)==False: |
| 9939 | return False |
| 9940 | return True |
| 9941 | if hex_colors==True: |
| 9942 | colorings=[] |
| 9943 | for component in g.connected_components(): |
| 9944 | tmp=g.subgraph(component).vertex_coloring(k=k,value_only=value_only,hex_colors=True,log=log) |
| 9945 | if tmp==False: |
| 9946 | return False |
| 9947 | colorings.append(tmp) |
| 9948 | value=dict([(color,[]) for color in rainbow(k)]) |
| 9949 | for color in rainbow(k): |
| 9950 | for component in colorings: |
| 9951 | value[color].extend(component[color]) |
| 9952 | return value |
| 9953 | else: |
| 9954 | colorings=[] |
| 9955 | for component in g.connected_components(): |
| 9956 | tmp=g.subgraph(component).vertex_coloring(k=k,value_only=value_only,hex_colors=False,log=log) |
| 9957 | if tmp==False: |
| 9958 | return False |
| 9959 | colorings.append(tmp) |
| 9960 | value=[[] for color in range(k)] |
| 9961 | for color in range(k): |
| 9962 | for component in colorings: |
| 9963 | value[color].extend(component[color]) |
| 9964 | return value |
| 9965 | |
| 9966 | |
| 9967 | # Degeneracy |
| 9968 | |
| 9969 | # Vertices whose degree is less than k are of no importance in the coloring |
| 9970 | if min(g.degree())<k: |
| 9971 | vertices=set(g.vertices()) |
| 9972 | deg=[] |
| 9973 | tmp=[v for v in vertices if g.degree(v)<k] |
| 9974 | while len(tmp)>0: |
| 9975 | v=tmp.pop(0) |
| 9976 | neighbors=list(set(g.neighbors(v)) & vertices) |
| 9977 | if v in vertices and len(neighbors)<k: |
| 9978 | vertices.remove(v) |
| 9979 | tmp.extend(neighbors) |
| 9980 | deg.append(v) |
| 9981 | |
| 9982 | if value_only==True: |
| 9983 | return g.subgraph(list(vertices)).vertex_coloring(k=k,value_only=value_only,hex_colors=hex_colors,log=log) |
| 9984 | if hex_colors==True: |
| 9985 | value=g.subgraph(list(vertices)).vertex_coloring(k=k,value_only=value_only,hex_colors=hex_colors,log=log) |
| 9986 | if value==False: |
| 9987 | return False |
| 9988 | while len(deg)>0: |
| 9989 | for (color,classe) in value.items(): |
| 9990 | if len(list(set(classe) & set(g.neighbors(deg[-1]))))==0: |
| 9991 | value[color].append(deg[-1]) |
| 9992 | deg.pop(-1) |
| 9993 | break |
| 9994 | return value |
| 9995 | else: |
| 9996 | value=g.subgraph(list(vertices)).vertex_coloring(k=k,value_only=value_only,hex_colors=hex_colors,log=log) |
| 9997 | if value==False: |
| 9998 | return False |
| 9999 | while len(deg)>0: |
| 10000 | for classe in value: |
| 10001 | if len(list(set(classe) & set(g.neighbors(deg[-1]))))==0: |
| 10002 | classe.append(deg[-1]) |
| 10003 | deg.pop(-1) |
| 10004 | break |
| 10005 | return value |
| 10006 | |
| 10007 | |
| 10008 | |
| 10009 | |
| 10010 | p=MIP(sense=1) |
| 10011 | obj={} |
| 10012 | |
| 10013 | # a vertex has only one color |
| 10014 | for c in [[((v,i),1) for i in range(k)] for v in g.vertices()]: |
| 10015 | p.addconstraint(dict(c),max=1) |
| 10016 | |
| 10017 | |
| 10018 | for e in g.edges(): |
| 10019 | for i in range(k): |
| 10020 | obj[(e[0],i)]=1 |
| 10021 | obj[(e[1],i)]=1 |
| 10022 | p.setbinary((e[1],i)) |
| 10023 | p.setbinary((e[0],i)) |
| 10024 | p.addconstraint({(e[0],i):1,(e[1],i):1},max=1) |
| 10025 | p.addconstraint(obj,min=g.order(),max=g.order()) |
| 10026 | p.setobj(obj) |
| 10027 | try: |
| 10028 | if value_only==True: |
| 10029 | tmp=p.solve(objective_only=True,log=log) |
| 10030 | return True |
| 10031 | else: |
| 10032 | tmp=p.solve(log=log) |
| 10033 | except: |
| 10034 | return False |
| 10035 | # builds the color classes |
| 10036 | a=[0]*k |
| 10037 | for i in range(k): |
| 10038 | a[i]=[] |
| 10039 | for i in tmp[1].items(): |
| 10040 | if i[1]==1: |
| 10041 | i=i[0] |
| 10042 | a[i[1]].append(i[0]) |
| 10043 | # if needed, builds a dictionary from the color classes adding colors |
| 10044 | if hex_colors: |
| 10045 | b={} |
| 10046 | r=rainbow(len(a)) |
| 10047 | for l in range(len(a)): |
| 10048 | b[r[l]]=a[l] |
| 10049 | return b |
| 10050 | return a |
| 10051 | |
| 10052 | def edge_coloring(self,value_only=False,vizing=False,hex_colors=False, log=0): |
| 10053 | """ |
| 10054 | Properly colors the edges of a graph. |
| 10055 | ( cf. http://en.wikipedia.org/wiki/Edge_coloring ) |
| 10056 | |
| 10057 | INPUT: |
| 10058 | |
| 10059 | - ``value_only'' : When set to True, only the chromatic index is returned |
| 10060 | When set to False (default), a partition of the edge set into |
| 10061 | matchings is returned is possible |
| 10062 | |
| 10063 | - ``vizing'' ( an boolean ) : |
| 10064 | When set to True, tries to find a Delta+1-edge-coloring ( where |
| 10065 | Delta is equal to the maximum degree in the graph ) |
| 10066 | When set to False, tries to find a Delta-edge-coloring ( where |
| 10067 | Delta is equal to the maximum degree in the graph ). if impossible |
| 10068 | tries to find and returns a Delta+1-edge-coloring |
| 10069 | |
| 10070 | --- Implies value_only = False --- |
| 10071 | |
| 10072 | - ``hex_colors'' : |
| 10073 | When set to True ( it is False by default ), the partition returned |
| 10074 | is a dictionary whose keys are colors and whose values are the color classes |
| 10075 | ( ideal to be plotted ) |
| 10076 | |
| 10077 | - ``log'' ( integer ) : |
| 10078 | As edge-coloring is a NP-complete problem, its solving may take some time |
| 10079 | depending on the graph. Use log to define the level of verbosity you want |
| 10080 | from the linear program solver. |
| 10081 | By default log=0, meaning that there will be no message printed by the solver. |
| 10082 | |
| 10083 | OUTPUT : |
| 10084 | |
| 10085 | In the following, Delta is equal to the maximum degree in the graph |
| 10086 | |
| 10087 | - If vizing=True and value_only=False: |
| 10088 | Returns a partition of the edge set into Delta+1 matchings |
| 10089 | |
| 10090 | - If vizing=False and value_only=True: |
| 10091 | Returns the chromatic index |
| 10092 | |
| 10093 | - If vizing=False and value_only=False |
| 10094 | Returns a partition of the edge set into the minimum number of matchings |
| 10095 | |
| 10096 | - If vizing=True is set and value_only=True: |
| 10097 | Should return something, but mainly you are just trying to compute the maximum |
| 10098 | degree of the graph, and this is not the easiest way ;-) |
| 10099 | By Vizing's theorem, a graph has a chromatic index equal to its maximum degree |
| 10100 | or its maximum degree+1 |
| 10101 | |
| 10102 | EXAMPLE: |
| 10103 | |
| 10104 | sage: g=graphs.PetersenGraph() |
| 10105 | sage: g.edge_coloring(value_only=True) |
| 10106 | 7 |
| 10107 | |
| 10108 | """ |
| 10109 | from sage.numerical.mip import MIP |
| 10110 | from sage.plot.colors import rainbow |
| 10111 | g=self |
| 10112 | # An edge (u,v) is sometimes returned as (v,u) and we want to avoid that. |
| 10113 | def reorder_edge((u,uu,uuu)): |
| 10114 | if u<=uu: |
| 10115 | return ((u,uu,uuu)) |
| 10116 | else: |
| 10117 | return ((uu,u,uuu)) |
| 10118 | |
| 10119 | p=MIP(sense=1) |
| 10120 | obj={} |
| 10121 | k=max(g.degree()) |
| 10122 | # Vizing's coloring uses Delta+1 colors |
| 10123 | if vizing: |
| 10124 | value_only=False |
| 10125 | k=k+1 |
| 10126 | |
| 10127 | for v in g.vertices(): |
| 10128 | for i in range(k): |
| 10129 | c={} |
| 10130 | # for any vertex v and for any color i, there is only one |
| 10131 | # edge of color i adjacent to v |
| 10132 | for e in g.edges_incident(v): |
| 10133 | c[(reorder_edge(e),i)]=1 |
| 10134 | obj[(reorder_edge(e),i)]=1 |
| 10135 | p.addconstraint(c,max=1) |
| 10136 | for e in g.edges(): |
| 10137 | c={} |
| 10138 | # an edge has at most one color |
| 10139 | for i in range(k): |
| 10140 | c[(reorder_edge(e),i)]=1 |
| 10141 | p.setbinary((reorder_edge(e),i)) |
| 10142 | p.addconstraint(c,max=1) |
| 10143 | # all the edges must have a color |
| 10144 | p.addconstraint(obj,max=g.size(),min=g.size()) |
| 10145 | p.setobj(obj) |
| 10146 | try: |
| 10147 | if value_only==True: |
| 10148 | p.solve(objective_only=True,log=log) |
| 10149 | else: |
| 10150 | tmp=p.solve(log=log) |
| 10151 | except: |
| 10152 | if value_only: |
| 10153 | return k+1 |
| 10154 | else: |
| 10155 | # if the coloring with Delta colors fails, tries Delta+1 |
| 10156 | return g.edge_coloring(vizing=True,hex_colors=hex_colors,log=log) |
| 10157 | if value_only: |
| 10158 | return k |
| 10159 | a=[[] for i in range(k)] |
| 10160 | # builds the color classes |
| 10161 | for i in range(k): |
| 10162 | a[i]=[] |
| 10163 | for i in tmp[1].items(): |
| 10164 | if i[1]==1: |
| 10165 | i=i[0] |
| 10166 | a[i[1]].append(i[0]) |
| 10167 | # if needed, builds a dictionary from the color classes adding colors |
| 10168 | if hex_colors: |
| 10169 | b={} |
| 10170 | r=rainbow(len(a)) |
| 10171 | for l in range(len(a)): |
| 10172 | b[r[l]]=a[l] |
| 10173 | return b |
| 10174 | |
| 10175 | else: |
| 10176 | return a |
| 10177 | |