Ticket #7592: trac_7592.patch

File trac_7592.patch, 7.3 KB (added by ncohen, 11 years ago)
  • sage/graphs/graph.py

    # HG changeset patch
    # User Nathann Cohen <nathann.cohen@gmail.com>
    # Date 1260305403 -3600
    # Node ID 5792c7088b6f77b89369818f40a471527b1747ff
    # Parent  bae8353680492b942e0fe7b58db854055f94e6a0
    Graph.flow method
    
    diff -r bae835368049 -r 5792c7088b6f sage/graphs/graph.py
    a b  
    6565
    6666-  Anders Jonsson (2009-10-10): Counting of spanning trees and out-trees added.
    6767
     68-  Nathann Cohen (2009-09) : Cliquer, Connectivity, Flows
     69                             and everything that uses Linear Programming
     70                             and class numerical.MIP
     71
    6872
    6973Graph Format
    7074------------
     
    32543258            v = pv
    32553259        return B, C
    32563260
     3261    def flow(self,x,y,value_only=True,integer=False, use_edge_labels=True,vertex_bound=False):
     3262        r"""
     3263        Returns a maximum flow in the graph from ``x`` to ``y``
     3264        ( cf. http://en.wikipedia.org/wiki/Max_flow_problem )
     3265        represented by an optimal valuation of the edges.
     3266
     3267        As an optimization problem, is can be expressed this way :
     3268
     3269        .. MATH::
     3270            \mbox{Maximize : }&\sum_{e\in G.edges()} w_e b_e\\
     3271            \mbox{Such that : }&\forall v \in G, \sum_{(u,v)\in G.edges()} b_{(u,v)}\leq 1\\
     3272            &\forall x\in G, b_x\mbox{ is a binary variable}
     3273   
     3274       
     3275        INPUT:
     3276   
     3277        - ``x`` -- Source vertex
     3278
     3279        - ``y`` -- Sink vertex
     3280
     3281        - ``value_only`` (boolean)
     3282            - When set to ``True``, only the value of a maximal
     3283              flow is returned.
     3284            - When set to ``False``, is returned a pair whose first element
     3285              is the value of the maximum flow, and whose second value is
     3286              a flow graph ( a copy of the current graph, such that each edge
     3287              has the flow using it as a label, the edges without flow being
     3288              omitted ).
     3289
     3290        - ``integer`` (boolean)
     3291            - When set to ``True``, computes an optimal solution under the
     3292              constraint that the flow going through an edge has to be an
     3293              integer
     3294           
     3295        - ``use_edge_labels`` (boolean)
     3296
     3297            - When set to ``True``, computes a maximun flow
     3298              where each edge has a capacity defined by its label. ( if
     3299              an edge has no label, `1` is assumed )
     3300
     3301            - When set to ``False``, each edge has capacity `1`
     3302
     3303        - ``vertex_bound`` (boolean)
     3304
     3305            - When set to ``True``, sets the maximum flow leaving
     3306              a vertex different from `x` to `1` ( useful for vertex
     3307              connectivity parameters )
     3308                             
     3309              This parameter is set to ``False`` by default.
     3310   
     3311        EXAMPLES:
     3312
     3313        Two basic applications of the flow method for the ``PappusGraph`` and the
     3314        ``ButterflyGraph`` with parameter `2` ::
     3315       
     3316           sage: g=graphs.PappusGraph()
     3317           sage: g.flow(1,2) # optional - requires Glpk or COIN-OR/CBC
     3318           3.0
     3319
     3320        ::
     3321
     3322           sage: b=digraphs.ButterflyGraph(2)
     3323           sage: b.flow(('00',1),('00',2)) # optional - requires Glpk or COIN-OR/CBC
     3324           1.0
     3325
     3326        The flow method can be used to compute a matching in a bipartite graph
     3327        by linking a source `s` to all the vertices of the first set and linking
     3328        a sink `t` to all the vertices of the second set, then computing
     3329        a maximum `s-t` flow ::
     3330
     3331            sage: g = graphs.CompleteBipartiteGraph(4,4)
     3332            sage: g.add_edges([('s',i) for i in range(4)])
     3333            sage: g.add_edges([(4+i,'t') for i in range(4)])
     3334            sage: [cardinal, flow_graph] = g.flow('s','t',integer=True,value_only=False) # optional - requries GLPK or CBC
     3335            sage: flow_graph.delete_vertices(['s','t'])                                  # optional - requries GLPK or CBC
     3336            sage: flow_graph.edges(labels=None)                                          # optional - requries GLPK or CBC
     3337            [(0, 5), (1, 4), (2, 7), (3, 6)]
     3338
     3339       
     3340        """
     3341        from sage.numerical.mip import MixedIntegerLinearProgram
     3342        g=self
     3343        p=MixedIntegerLinearProgram(maximization=True)
     3344        flow=p.new_variable(dim=2)
     3345
     3346        if use_edge_labels:
     3347            capacity=lambda x: x if x!=None else 1
     3348        else:
     3349            capacity=lambda x: 1
     3350
     3351        # maximizes z, which is the flow leaving from x
     3352           
     3353        if g.is_directed():
     3354            # This function return the balance of flow at X
     3355            flow_sum=lambda X: sum([flow[X][v] for (u,v) in g.outgoing_edges([X],labels=None)])-sum([flow[u][X] for (u,v) in g.incoming_edges([X],labels=None)])
     3356
     3357            # Maximizes the flow leaving x
     3358            p.set_objective(flow_sum(x))
     3359
     3360            # Elsewhere, the flow is equal to 0-
     3361            [p.add_constraint(flow_sum(v),min=0,max=0) for v in g if v!=x and v!=y]
     3362
     3363            # Capacity constraints
     3364            [p.add_constraint(flow[u][v],max=capacity(w)) for (u,v,w) in g.edges()]
     3365
     3366            if vertex_bound:
     3367                [p.add_constraint(sum([flow[uu][vv] for (uu,vv) in g.outgoing_edges([v],labels=None)]),max=1) for v in g.vertices() if v!=x and v!=y]
     3368
     3369        else:
     3370            # This function return the balance of flow at X
     3371            flow_sum=lambda X:sum([flow[X][v]-flow[v][X] for v in g[X]])
     3372
     3373            # Maximizes the flow leaving x
     3374            p.set_objective(flow_sum(x))
     3375
     3376            # Elsewhere, the flow is equal to 0
     3377            [p.add_constraint(flow_sum(v),min=0,max=0) for v in g if v!=x and v!=y]
     3378
     3379            # Capacity constraints
     3380            [p.add_constraint(flow[u][v]+flow[v][u],max=capacity(w)) for (u,v,w) in g.edges()]
     3381           
     3382            if vertex_bound:
     3383                [p.add_constraint([flow[X][v] for X in g[v]],max=1) for v in g if v!=x and v!=y]
     3384           
     3385
     3386        if integer:
     3387            p.set_integer(flow)
     3388
     3389
     3390        if value_only:
     3391            return p.solve(objective_only=True)
     3392
     3393        obj=p.solve()
     3394        flow=p.get_values(flow)
     3395
     3396        flow_graph = g.copy()
     3397
     3398        if g.is_directed():
     3399            for (u,v) in g.edges(labels=None):
     3400                # We do not want to see both edges (u,v) and (v,u)
     3401                # with a positive flow
     3402                if g.has_edge(v,u):
     3403                    m=min(flow[u][v],flow[v][u])
     3404                    flow[u][v]-=m
     3405                    flow[v][u]-=m
     3406
     3407            for (u,v) in g.edge_iterator(labels=None):
     3408                if flow[u][v]>0:
     3409                    flow_graph.set_edge_label(u,v,flow[u][v])
     3410                else:
     3411                    flow_graph.delete_edge(u,v)
     3412
     3413        else:
     3414            for (u,v) in g.edges(labels=None):
     3415                m=min(flow[u][v],flow[v][u])
     3416                flow[u][v]-=m
     3417                flow[v][u]-=m
     3418
     3419            # We do not want to see both edges (u,v) and (v,u)
     3420            # with a positive flow
     3421            for (u,v) in g.edges(labels=None):
     3422                if flow[u][v]>0:
     3423                    flow_graph.set_edge_label(u,v,flow[u][v])
     3424                elif flow[v][u]>0:
     3425                    flow_graph.set_edge_label(v,u,flow[v][u])
     3426                else:
     3427                    flow_graph.delete_edge(v,u)
     3428
     3429        return [obj,flow_graph]
     3430
     3431
    32573432    ### Vertex handlers
    32583433
    32593434    def add_vertex(self, name=None):