Ticket #7289: trac_7289-rebased.patch

File trac_7289-rebased.patch, 6.4 KB (added by rlm, 11 years ago)
  • sage/graphs/generic_graph.py

    # HG changeset patch
    # User Nathann Cohen <nathann.cohen@gmail.com>
    # Date 1270388381 -7200
    # Node ID f5960102b5f61491d3281fc2d8a733a0c749eca3
    # Parent  487b54a279b6135f044f3e0a1f3e873903e06426
    trac #7289 : Multiway cut for Graphs
    
    diff -r 487b54a279b6 -r f5960102b5f6 sage/graphs/generic_graph.py
    a b  
    33533353            return tuple(answer)
    33543354
    33553355
     3356    def multiway_cut(self, vertices, value_only = False, use_edge_labels = False, **kwds):
     3357        r"""
     3358        Returns a minimum edge multiway cut corresponding to the
     3359        given set of vertices
     3360        ( cf. http://www.d.kth.se/~viggo/wwwcompendium/node92.html )
     3361        represented by a list of edges.
     3362   
     3363        A multiway cut for a vertex set `S` in a graph or a digraph
     3364        `G` if a set `C` of edges such that any two vertices `u,v`
     3365        in `S` are disconnected when removing from `G` the edges from
     3366        `C`.
     3367
     3368        Such a cut is said to be minimum when its cardinality
     3369        (or weight) is minimum.
     3370
     3371        INPUT:
     3372
     3373        - ``vertices`` (iterable)-- the set of vertices
     3374   
     3375        - ``value_only`` (boolean)
     3376
     3377            - When set to ``True``, only the value of a minimum
     3378              multiway cut is returned.
     3379
     3380            - When set to ``False`` (default), the list of edges
     3381              is returned
     3382
     3383        - ``use_edge_labels`` (boolean)
     3384            - When set to ``True``, computes a weighted minimum cut
     3385              where each edge has a weight defined by its label. ( if
     3386              an edge has no label, `1` is assumed )
     3387
     3388            - when set to ``False`` (default), each edge has weight `1`.
     3389           
     3390        - ``**kwds`` -- arguments to be passed down to the ``solve`` 
     3391          function of ``MixedIntegerLinearProgram``. See the documentation 
     3392          of ``MixedIntegerLinearProgram.solve`` for more information.
     3393   
     3394        EXAMPLES:
     3395
     3396        Of course, a multiway cut between two vertices correspond
     3397        to a minimum edge cut ::
     3398
     3399            sage: g = graphs.PetersenGraph()
     3400            sage: g.edge_cut(0,3) == g.multiway_cut([0,3], value_only = True) # optional - requires GLPK, CBC or CPLEX
     3401            True
     3402
     3403        As Petersen's graph is `3`-regular, a minimum multiway cut
     3404        between three vertices contains at most `2\times 3` edges
     3405        (which could correspond to the neighborhood of 2
     3406        vertices)::
     3407
     3408            sage: g.multiway_cut([0,3,9], value_only = True) == 2*3          # optional - requires GLPK, CBC or CPLEX
     3409            True
     3410
     3411        In this case, though, the vertices are an independent set.
     3412        If we pick instead vertices `0,9,` and `7`, we can save `4`
     3413        edges in the multiway cut ::
     3414
     3415            sage: g.multiway_cut([0,7,9], value_only = True) == 2*3 - 1      # optional - requires GLPK, CBC or CPLEX
     3416            True
     3417
     3418        This example, though, does not work in the directed case anymore,
     3419        as it is not possible in Petersen's graph to mutualise edges ::
     3420       
     3421            sage: g = DiGraph(g)
     3422            sage: g.multiway_cut([0,7,9], value_only = True) == 3*3           # optional - requires GLPK, CBC or CPLEX
     3423            True
     3424
     3425        Of course, a multiway cut between the whole vertex set
     3426        contains all the edges of the graph::
     3427
     3428            sage: C = g.multiway_cut(g.vertices())                            # optional - requires GLPK, CBC or CPLEX
     3429            sage: set(C) == set(g.edges())                                    # optional - requires GLPK, CBC or CPLEX
     3430            True
     3431        """
     3432        from sage.numerical.mip import MixedIntegerLinearProgram
     3433        from itertools import combinations, chain
     3434
     3435        p = MixedIntegerLinearProgram(maximization = False)
     3436
     3437        # height[c][v] represents the height of vertex v for commodity c
     3438        height = p.new_variable(dim = 2)
     3439
     3440        # cut[e] represents whether e is in the cut
     3441        cut = p.new_variable()
     3442
     3443        # Reorder
     3444        R = lambda x,y : (x,y) if x<y else (y,x)
     3445
     3446        # Weight function
     3447        if use_edge_labels:
     3448            w = lambda l : l if l is not None else 1
     3449        else:
     3450            w = lambda l : 1
     3451
     3452        if self.is_directed():
     3453
     3454            p.set_objective(  sum([ w(l) * cut[u,v] for u,v,l in self.edge_iterator() ]) )
     3455
     3456            for s,t in chain( combinations(vertices,2), map(lambda (x,y) : (y,x), combinations(vertices,2))) :
     3457                # For each commodity, the source is at height 0
     3458                # and the destination is at height 1
     3459                p.add_constraint( height[(s,t)][s], min = 0, max = 0)
     3460                p.add_constraint( height[(s,t)][t], min = 1, max = 1)
     3461               
     3462                # given a commodity (s,t), the height of two adjacent vertices u,v
     3463                # can differ of at most the value of the edge between them
     3464                for u,v in self.edges(labels = False):
     3465                    p.add_constraint( height[(s,t)][u] - height[(s,t)][v] - cut[u,v], max = 0)
     3466
     3467        else:
     3468
     3469            p.set_objective(  sum([ w(l) * cut[R(u,v)] for u,v,l in self.edge_iterator() ]) )
     3470
     3471            for s,t in combinations(vertices,2):
     3472                # For each commodity, the source is at height 0
     3473                # and the destination is at height 1
     3474                p.add_constraint( height[(s,t)][s], min = 0, max = 0)
     3475                p.add_constraint( height[(s,t)][t], min = 1, max = 1)
     3476               
     3477                # given a commodity (s,t), the height of two adjacent vertices u,v
     3478                # can differ of at most the value of the edge between them
     3479                for u,v in self.edges(labels = False):
     3480                    p.add_constraint( height[(s,t)][u] - height[(s,t)][v] - cut[R(u,v)], max = 0)
     3481                    p.add_constraint( height[(s,t)][v] - height[(s,t)][u] - cut[R(u,v)], max = 0)
     3482
     3483
     3484        p.set_binary(cut)       
     3485        if value_only:
     3486            return p.solve(objective_only = True, **kwds)
     3487
     3488        p.solve(**kwds)
     3489
     3490        cut = p.get_values(cut)
     3491
     3492        if self.is_directed():
     3493            return filter(lambda (u,v,l) : cut[u,v] > .5, self.edge_iterator())
     3494
     3495        else:
     3496            return filter(lambda (u,v,l) : cut[R(u,v)] > .5, self.edge_iterator())
     3497
    33563498    def vertex_cover(self, algorithm="Cliquer", value_only=False, solver=None, verbose=0):
    33573499        r"""
    33583500        Returns a minimum vertex cover of self represented