Ticket #6679: coloring-symbolic.patch

File coloring-symbolic.patch, 14.0 KB (added by ncohen, 11 years ago)
  • sage/graphs/graph.py

    # HG changeset patch
    # User Nathann Cohen <nathann.cohen@gmail.com>
    # Date 1249571353 25200
    # Node ID 46ae38dff7b417bb89d5dccc09213de16b8461a6
    # Parent  f6e9a6b5aae0274dbd5dd6266b5ce13941248af1
    Functions vertex_coloring and edge_coloring in Graph with the Symbolics from the new MIP class
    
    diff -r f6e9a6b5aae0 -r 46ae38dff7b4 sage/graphs/graph.py
    a b  
    98419841            else:
    98429842                return C.values()
    98439843
     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   
    984410160    ### Centrality
    984510161   
    984610162    def centrality_betweenness(self, normalized=True):