Ticket #7289: trac_7289.patch

File trac_7289.patch, 6.5 KB (added by ncohen, 11 years ago)
  • sage/graphs/generic_graph.py

    # HG changeset patch
    # User Nathann Cohen <nathann.cohen@gmail.com>
    # Date 1270388381 -7200
    # Node ID 4d0b42b9beb43aa8c0d5f332949bf04d50625f93
    # Parent  db7143738e1dbcb0c5415884ebaeee4500da1520
    trac #7289 : Multiway cut for Graphs
    
    diff -r db7143738e1d -r 4d0b42b9beb4 sage/graphs/generic_graph.py
    a b  
    31973197            b=p.get_values(b)
    31983198            return [obj,[v for v in g if b[v]==1]]
    31993199
     3200    def multiway_cut(self, vertices, value_only = False, use_edge_labels = False, **kwds):
     3201        r"""
     3202        Returns a minimum edge multiway cut corresponding to the
     3203        given set of vertices
     3204        ( cf. http://www.d.kth.se/~viggo/wwwcompendium/node92.html )
     3205        represented by a list of edges.
     3206   
     3207        A multiway cut for a vertex set `S` in a graph or a digraph
     3208        `G` if a set `C` of edges such that any two vertices `u,v`
     3209        in `S` are disconnected when removing from `G` the edges from
     3210        `C`.
     3211
     3212        Such a cut is said to be minimum when its cardinality
     3213        (or weight) is minimum.
     3214
     3215        INPUT:
     3216
     3217        - ``vertices`` (iterable)-- the set of vertices
     3218   
     3219        - ``value_only`` (boolean)
     3220
     3221            - When set to ``True``, only the value of a minimum
     3222              multiway cut is returned.
     3223
     3224            - When set to ``False`` (default), the list of edges
     3225              is returned
     3226
     3227        - ``use_edge_labels`` (boolean)
     3228            - When set to ``True``, computes a weighted minimum cut
     3229              where each edge has a weight defined by its label. ( if
     3230              an edge has no label, `1` is assumed )
     3231
     3232            - when set to ``False`` (default), each edge has weight `1`.
     3233           
     3234        - ``**kwds`` -- arguments to be passed down to the ``solve`` 
     3235          function of ``MixedIntegerLinearProgram``. See the documentation 
     3236          of ``MixedIntegerLinearProgram.solve`` for more information.
     3237   
     3238        EXAMPLES:
     3239
     3240        Of course, a multiway cut between two vertices correspond
     3241        to a minimum edge cut ::
     3242
     3243            sage: g = graphs.PetersenGraph()
     3244            sage: g.edge_cut(0,3) == g.multiway_cut([0,3], value_only = True) # optional - requires GLPK, CBC or CPLEX
     3245            True
     3246
     3247        As Petersen's graph is `3`-regular, a minimum multiway cut
     3248        between three vertices contains at most `2\times 3` edges
     3249        (which could correspond to the neighborhood of 2
     3250        vertices)::
     3251
     3252            sage: g.multiway_cut([0,3,9], value_only = True) == 2*3          # optional - requires GLPK, CBC or CPLEX
     3253            True
     3254
     3255        In this case, though, the vertices are an independent set.
     3256        If we pick instead vertices `0,9,` and `7`, we can save `4`
     3257        edges in the multiway cut ::
     3258
     3259            sage: g.multiway_cut([0,7,9], value_only = True) == 2*3 - 1      # optional - requires GLPK, CBC or CPLEX
     3260            True
     3261
     3262        This example, though, does not work in the directed case anymore,
     3263        as it is not possible in Petersen's graph to mutualise edges ::
     3264       
     3265            sage: g = DiGraph(g)
     3266            sage: g.multiway_cut([0,7,9], value_only = True) == 3*3           # optional - requires GLPK, CBC or CPLEX
     3267            True
     3268
     3269        Of course, a multiway cut between the whole vertex set
     3270        contains all the edges of the graph::
     3271
     3272            sage: C = g.multiway_cut(g.vertices())                            # optional - requires GLPK, CBC or CPLEX
     3273            sage: set(C) == set(g.edges())                                    # optional - requires GLPK, CBC or CPLEX
     3274            True
     3275        """
     3276        from sage.numerical.mip import MixedIntegerLinearProgram
     3277        from itertools import combinations, chain
     3278
     3279        p = MixedIntegerLinearProgram(maximization = False)
     3280
     3281        # height[c][v] represents the height of vertex v for commodity c
     3282        height = p.new_variable(dim = 2)
     3283
     3284        # cut[e] represents whether e is in the cut
     3285        cut = p.new_variable()
     3286
     3287        # Reorder
     3288        R = lambda x,y : (x,y) if x<y else (y,x)
     3289
     3290        # Weight function
     3291        if use_edge_labels:
     3292            w = lambda l : l if l is not None else 1
     3293        else:
     3294            w = lambda l : 1
     3295
     3296        if self.is_directed():
     3297
     3298            p.set_objective(  sum([ w(l) * cut[u,v] for u,v,l in self.edge_iterator() ]) )
     3299
     3300            for s,t in chain( combinations(vertices,2), map(lambda (x,y) : (y,x), combinations(vertices,2))) :
     3301                # For each commodity, the source is at height 0
     3302                # and the destination is at height 1
     3303                p.add_constraint( height[(s,t)][s], min = 0, max = 0)
     3304                p.add_constraint( height[(s,t)][t], min = 1, max = 1)
     3305               
     3306                # given a commodity (s,t), the height of two adjacent vertices u,v
     3307                # can differ of at most the value of the edge between them
     3308                for u,v in self.edges(labels = False):
     3309                    p.add_constraint( height[(s,t)][u] - height[(s,t)][v] - cut[u,v], max = 0)
     3310
     3311        else:
     3312
     3313            p.set_objective(  sum([ w(l) * cut[R(u,v)] for u,v,l in self.edge_iterator() ]) )
     3314
     3315            for s,t in combinations(vertices,2):
     3316                # For each commodity, the source is at height 0
     3317                # and the destination is at height 1
     3318                p.add_constraint( height[(s,t)][s], min = 0, max = 0)
     3319                p.add_constraint( height[(s,t)][t], min = 1, max = 1)
     3320               
     3321                # given a commodity (s,t), the height of two adjacent vertices u,v
     3322                # can differ of at most the value of the edge between them
     3323                for u,v in self.edges(labels = False):
     3324                    p.add_constraint( height[(s,t)][u] - height[(s,t)][v] - cut[R(u,v)], max = 0)
     3325                    p.add_constraint( height[(s,t)][v] - height[(s,t)][u] - cut[R(u,v)], max = 0)
     3326
     3327
     3328        p.set_binary(cut)       
     3329        if value_only:
     3330            return p.solve(objective_only = True, **kwds)
     3331
     3332        p.solve(**kwds)
     3333
     3334        cut = p.get_values(cut)
     3335
     3336        if self.is_directed():
     3337            return filter(lambda (u,v,l) : cut[u,v] > .5, self.edge_iterator())
     3338
     3339        else:
     3340            return filter(lambda (u,v,l) : cut[R(u,v)] > .5, self.edge_iterator())
     3341
    32003342    def vertex_cover(self,algorithm="Cliquer",value_only=False,log=0):
    32013343        r"""
    32023344        Returns a minimum vertex cover of the graph