Ticket #9575: trac_9575.patch

File trac_9575.patch, 10.0 KB (added by ncohen, 11 years ago)
  • sage/graphs/graph_coloring.py

    # HG changeset patch
    # User Nathann Cohen <nathann.cohen@gmail.com>
    # Date 1279782482 -28800
    # Node ID be94a819ed703679bd9c3e2dfc89146792a93f3e
    # Parent  cb1ad1ee5bd452850fde04d47f9618dec4c3e997
    trac #9575 -- Grundy Coloring
    
    diff -r cb1ad1ee5bd4 -r be94a819ed70 sage/graphs/graph_coloring.py
    a b  
    302302       sage: vertex_coloring(g, value_only=True)
    303303       3
    304304    """
    305     from sage.numerical.mip import MixedIntegerLinearProgram
     305    from sage.numerical.mip import MixedIntegerLinearProgram, Sum
    306306    from sage.plot.colors import rainbow
    307307
    308308    # If k==None, tries to find an optimal coloring
     
    419419        p = MixedIntegerLinearProgram(maximization=True)
    420420        color = p.new_variable(dim=2)
    421421        # a vertex has exactly one color
    422         [p.add_constraint(sum([color[v][i] for i in xrange(k)]), min=1, max=1)
     422        [p.add_constraint(Sum([color[v][i] for i in xrange(k)]), min=1, max=1)
    423423             for v in g.vertices()]
    424424        # adjacent vertices have different colors
    425425        [p.add_constraint(color[u][i] + color[v][i], max=1)
     
    451451        else:
    452452            return classes
    453453
     454def grundy_coloring(g, k, value_only = True, solver = None, verbose = 0):
     455    r"""
     456    Computes the worst-case of a first-fit coloring with less than `k`
     457    colors.
     458
     459    Definition :
     460
     461    A first-fit coloring is obtained by sequentially coloring the
     462    vertices of a graph, assigning them the smallest color not already
     463    assigned to one of its neighbors. The result is clearly a proper
     464    coloring, which usually requires much more colors than an optimal
     465    vertex coloring of the graph, and heavily depends on the ordering
     466    of the vertices.
     467
     468    The number of colors required by the worst-case application of
     469    this algorithm on a graph `G` is called the Grundy number, written
     470    `\Gamma (G)`.
     471
     472    Equivalent formulation :
     473
     474    Equivalently, a Grundy coloring is a proper vertex coloring such
     475    that any vertex colored with `i` has, for every `j<i`, a neighbor
     476    colored with `j`. This can define a Linear Program, which is used
     477    here to compute the Grundy number of a graph.
     478
     479    .. NOTE:
     480
     481       This method computes a grundy coloring using at *MOST* `k`
     482       colors. If this method returns a value equal to `k`, it can not
     483       be assumed that `k` is equal to `\Gamma(G)`. Meanwhile, if it
     484       returns any value `k' < k`, this is a certificate that the
     485       Grundy number of the given graph is `k'`.
     486
     487       As `\Gamma(G)\leq \Delta(G)+1`, it can also be assumed that
     488       `\Gamma(G) = k` if ``grundy_coloring(g, k)`` returns `k` when
     489       `k = \Delta(G) +1`.
     490
     491    INPUT:
     492
     493    - ``k`` (integer) -- Maximum number of colors
     494
     495    - ``solver`` -- (default: ``None``) Specify a Linear Program (LP)
     496      solver to be used. If set to ``None``, the default one is used. For
     497      more information on LP solvers and which default solver is used, see
     498      the method
     499      :meth:`solve <sage.numerical.mip.MixedIntegerLinearProgram.solve>`
     500      of the class
     501      :class:`MixedIntegerLinearProgram <sage.numerical.mip.MixedIntegerLinearProgram>`.
     502
     503    - ``value_only`` -- boolean (default: ``True``). When set to
     504      ``True``, only the number of colors is returned. Otherwise, the
     505      pair ``(nb_colors, coloring)`` is returned, where ``coloring``
     506      is a dictionary associating its color (integer) to each vertex
     507      of the graph.
     508
     509    - ``verbose`` -- integer (default: ``0``). Sets the level of
     510      verbosity. Set to 0 by default, which means quiet.
     511
     512    ALGORITHM:
     513
     514    Integer Linear Program.
     515
     516    EXAMPLES:
     517
     518    The Grundy number of a `P_4` is equal to 3::
     519   
     520        sage: from sage.graphs.graph_coloring import grundy_coloring
     521        sage: g = graphs.PathGraph(4)
     522        sage: grundy_coloring(g, 4)
     523        3
     524
     525    The Grundy number of the PetersenGraph is equal to 4::
     526
     527        sage: g = graphs.PetersenGraph()
     528        sage: grundy_coloring(g, 5)
     529        4
     530
     531    It would have been sufficient to set the value of ``k`` to 4 in
     532    this case, as `4 = \Delta(G)+1`.
     533    """
     534    from sage.numerical.mip import MixedIntegerLinearProgram
     535    from sage.numerical.mip import MIPSolverException, Sum
     536
     537    p = MixedIntegerLinearProgram()
     538
     539    # List of colors
     540    classes = range(k)
     541
     542    # b[v][i] is set to 1 if and only if v is colored with i
     543    b = p.new_variable(dim=2)
     544
     545    # is_used[i] is set to 1 if and only if color [i] is used by some
     546    # vertex
     547    is_used = p.new_variable()
     548
     549    # Each vertex is in exactly one class
     550    for v in g:
     551        p.add_constraint(Sum( b[v][i] for i in classes ), max = 1, min = 1)
     552
     553    # Two adjacent vertices have different classes
     554    for u,v in g.edges(labels = None):
     555        for i in classes:
     556            p.add_constraint(b[v][i] + b[u][i], max = 1)
     557   
     558    # The following constraints ensure that if v is colored with i,
     559    # then it has a neighbor colored with j for every j<i
     560
     561    for i in range(k):
     562        for j in range(i):
     563            for v in g:
     564
     565                # If b[v][i] == 0, then the following constraint is
     566                # always satisfied, as a sum of binary variables is
     567                # always positive. If it is equal to 1, then at least
     568                # one of fthe other variables must be set to 1 too.
     569
     570                p.add_constraint( Sum( b[u][j] for u in g.neighbors(v) ) - b[v][i]  ,min = 0)
     571
     572    # is_used[i] can be set to 1 only if the color is used
     573    for i in classes:
     574        p.add_constraint( Sum( b[v][i] for v in g ) - is_used[i], min = 0)
     575         
     576    # Both variables are binary
     577    p.set_binary(b)
     578    p.set_binary(is_used)
     579
     580    # Trying to use as many colors as possible
     581    p.set_objective( Sum( is_used[i] for i in classes ) )
     582   
     583    try:
     584        obj = p.solve(solver = solver, log = verbose, objective_only = value_only)
     585        from sage.rings.integer import Integer
     586        obj = Integer(obj)
     587
     588    except MIPSolverException:
     589        raise ValueError("This graph can not be colored with k colors")
     590
     591    if value_only:
     592        return obj
     593
     594    # Building the dictionary associating its color to every vertex
     595
     596    b = p.get_values(b)
     597    coloring = {}
     598
     599    for v in g:
     600        for i in classes:
     601            if b[v][i] == 1:
     602                coloring[v] = i
     603                break
     604
     605    return obj, coloring
     606
     607
    454608def edge_coloring(g, value_only=False, vizing=False, hex_colors=False, log=0):
    455609    r"""
    456610    Properly colors the edges of a graph. See the URL
     
    529683    """
    530684    from sage.numerical.mip import MixedIntegerLinearProgram
    531685    from sage.plot.colors import rainbow
    532     from sage.numerical.mip import MIPSolverException
     686    from sage.numerical.mip import MIPSolverException, Sum
    533687
    534688    if g.is_clique():
    535689        if value_only:
     
    561715        k += 1
    562716    #  A vertex can not have two incident edges with the same color.
    563717    [p.add_constraint(
    564             sum([color[R(e)][i] for e in g.edges_incident(v, labels=False)]), max=1)
     718            Sum([color[R(e)][i] for e in g.edges_incident(v, labels=False)]), max=1)
    565719                for v in g.vertex_iterator()
    566720                    for i in xrange(k)]
    567721    # an edge must have a color
    568     [p.add_constraint(sum([color[R(e)][i] for i in xrange(k)]), max=1, min=1)
     722    [p.add_constraint(Sum([color[R(e)][i] for i in xrange(k)]), max=1, min=1)
    569723         for e in g.edge_iterator(labels=False)]
    570724    # anything is good as an objective value as long as it is satisfiable
    571725    e = g.edge_iterator(labels=False).next()
     
    715869      function of ``MixedIntegerLinearProgram``. See the documentation
    716870      of ``MixedIntegerLinearProgram.solve`` for more informations.
    717871
    718 
    719872    ALGORITHM:
    720873
    721874    Linear Programming
     
    769922    elif k==0:
    770923        k = (Integer(max(g.degree()))/2).ceil()
    771924
    772     from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException
     925    from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException, Sum
    773926    from sage.plot.colors import rainbow
    774927
    775928    p = MixedIntegerLinearProgram()
     
    787940
    788941    # Partition of the edges
    789942    for u,v in g.edges(labels=None):
    790         p.add_constraint(sum([c[i][E(u,v)] for i in range(k)]), max=1, min=1)
     943        p.add_constraint(Sum([c[i][E(u,v)] for i in range(k)]), max=1, min=1)
    791944
    792945    for i in range(k):
    793946
     
    798951
    799952        # Maximum degree 2
    800953        for u in g.vertices():
    801             p.add_constraint(sum([c[i][E(u,v)] for v in g.neighbors(u)]),max = 2)
     954            p.add_constraint(Sum([c[i][E(u,v)] for v in g.neighbors(u)]),max = 2)
    802955
    803956            # no cycles
    804             p.add_constraint(sum([r[i][(u,v)] for v in g.neighbors(u)]),max = MAD)
     957            p.add_constraint(Sum([r[i][(u,v)] for v in g.neighbors(u)]),max = MAD)
    805958
    806959
    807960    p.set_objective(None)
     
    9621115    elif k==0:
    9631116        k = max(g.degree())+2
    9641117   
    965     from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException
     1118    from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException, Sum
    9661119    from sage.plot.colors import rainbow
    9671120
    9681121    p = MixedIntegerLinearProgram()
     
    9791132
    9801133    # Partition of the edges
    9811134    for u,v in g.edges(labels=None):
    982         p.add_constraint(sum([c[i][E(u,v)] for i in range(k)]), max=1, min=1)
     1135        p.add_constraint(Sum([c[i][E(u,v)] for i in range(k)]), max=1, min=1)
    9831136
    9841137
    9851138    for i in range(k):
    9861139
    9871140        # Maximum degree 1
    9881141        for u in g.vertices():
    989             p.add_constraint(sum([c[i][E(u,v)] for v in g.neighbors(u)]),max = 1)
     1142            p.add_constraint(Sum([c[i][E(u,v)] for v in g.neighbors(u)]),max = 1)
    9901143
    9911144    for i,j in Subsets(range(k),2):
    9921145        # r is greater than c
    9931146        for u in g.vertices():
    994             p.add_constraint(sum([r[(i,j)][(u,v)] for v in g.neighbors(u)]),max = MAD)
     1147            p.add_constraint(Sum([r[(i,j)][(u,v)] for v in g.neighbors(u)]),max = MAD)
    9951148
    9961149        # r greater than c
    9971150        for u,v in g.edges(labels=None):