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