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 b  
    98389838            else:
    98399839                return C.values()
    98409840
     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   
    984110178    ### Centrality
    984210179   
    984310180    def centrality_betweenness(self, normalized=True):