Ticket #7476: trac_7476.patch

File trac_7476.patch, 8.3 KB (added by rlm, 11 years ago)

Revised version of Nathann's patch using the proper # optional syntax

  • sage/graphs/generic_graph.py

    # HG changeset patch
    # User Nathann Cohen <nathann.cohen@gmail.com>
    # Date 1267383537 -3600
    # Node ID c55667a5c456e5db2d1a36f9b2625c54cad7e532
    # Parent  e11f1f347ec9164698aba035bab4c42ba705d82d
    trac 7476 : Edge-disjoint spanning trees
    
    diff -r e11f1f347ec9 -r c55667a5c456 sage/graphs/generic_graph.py
    a b  
    30863086
    30873087            v = pv
    30883088        return B, C
     3089
     3090    def edge_disjoint_spanning_trees(self,k, root=None, **kwds):
     3091        r"""
     3092        Returns the desired number of edge-disjoint spanning
     3093        trees/arborescences.
     3094
     3095        INPUT:
     3096
     3097        - ``k`` (integer) -- the required number of edge-disjoint
     3098          spanning trees/arborescences
     3099
     3100        - ``root`` (vertex) -- root of the disjoint arborescences
     3101          when the graph is directed.
     3102          If set to ``None``, the first vertex in the graph is picked.
     3103
     3104        - ``**kwds`` -- arguments to be passed down to the ``solve``
     3105          function of ``MixedIntegerLinearProgram``. See the documentation
     3106          of ``MixedIntegerLinearProgram.solve`` for more informations.
     3107         
     3108        ALGORITHM:
     3109
     3110        Mixed Integer Linear Program. The formulation can be found
     3111        in [JVNC]_.
     3112
     3113        There are at least two possible rewritings of this method
     3114        which do not use Linear Programming:
     3115
     3116            * The algorithm presented in the paper entitled "A short
     3117              proof of the tree-packing theorem", by Thomas Kaiser
     3118              [KaisPacking]_.
     3119
     3120            * The implementation of a Matroid class and of the Matroid
     3121              Union Theorem (see section 42.3 of [SchrijverCombOpt]_),
     3122              applied to the cycle Matroid (see chapter 51 of
     3123              [SchrijverCombOpt]_).
     3124
     3125        EXAMPLES:
     3126
     3127        The Petersen Graph does have a spanning tree (it is connected)::
     3128
     3129            sage: g = graphs.PetersenGraph()
     3130            sage: [T] = g.edge_disjoint_spanning_trees(1)       # optional - GLPK, CBC
     3131            sage: T.is_tree()                                   # optional - GLPK, CBC
     3132            True
     3133
     3134        Though, it does not have 2 edge-disjoint trees (as it has less
     3135        than `2(|V|-1)` edges)::
     3136
     3137            sage: g.edge_disjoint_spanning_trees(2)              # optional - GLPK, CBC
     3138            Traceback (most recent call last):
     3139            ...
     3140            ValueError: This graph does not contain the required number of trees/arborescences !
     3141
     3142        By Edmond's theorem, a graph which is `k`-connected always has `k` edge-disjoint
     3143        arborescences, regardless of the root we pick::
     3144
     3145            sage: g = digraphs.RandomDirectedGNP(30,.3)
     3146            sage: k = Integer(g.edge_connectivity())                            # optional - GLPK, CBC
     3147            sage: arborescences = g.edge_disjoint_spanning_trees(k)             # optional - GLPK, CBC
     3148            sage: all([a.is_directed_acyclic() for a in arborescences])         # optional - GLPK, CBC
     3149            True
     3150            sage: all([a.is_connected() for a in arborescences])                # optional - GLPK, CBC
     3151            True           
     3152
     3153        In the undirected case, we can only ensure half of it::
     3154
     3155            sage: g = graphs.RandomGNP(30,.3)
     3156            sage: k = floor(Integer(g.edge_connectivity())/2)                   # optional - GLPK, CBC
     3157            sage: trees = g.edge_disjoint_spanning_trees(k)                     # optional - GLPK, CBC
     3158            sage: all([t.is_tree() for t in trees])                             # optional - GLPK, CBC
     3159            True
     3160
     3161        REFERENCES:
     3162
     3163        .. [JVNC] David Joyner, Minh Van Nguyen, and Nathann Cohen,
     3164          Algorithmic Graph Theory,
     3165          http://code.google.com/p/graph-theory-algorithms-book/
     3166
     3167        .. [KaisPacking] Thomas Kaiser
     3168          A short proof of the tree-packing theorem
     3169          http://arxiv.org/abs/0911.2809
     3170
     3171        .. [SchrijverCombOpt] Alexander Schrijver
     3172          Combinatorial optimization: polyhedra and efficiency
     3173          2003
     3174        """
     3175
     3176        from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException
     3177        p = MixedIntegerLinearProgram()
     3178        p.set_objective(None)
     3179
     3180        # The colors we can use
     3181        colors = range(0,k)
     3182
     3183        # edges[j][e] is equal to one if and only if edge e belongs to color j
     3184        edges = p.new_variable(dim=2)
     3185
     3186
     3187        # r_edges is a relaxed variable grater than edges. It is used to
     3188        # check the presence of cycles
     3189        r_edges = p.new_variable(dim=2)
     3190
     3191        epsilon = 1/(10*(Integer(self.order())))
     3192
     3193
     3194        if self.is_directed():
     3195            # Does nothing ot an edge.. Useful when out of "if self.directed"
     3196            S = lambda (x,y) : (x,y)
     3197
     3198            if root == None:
     3199                root = self.vertex_iterator().next()
     3200               
     3201           
     3202            # An edge belongs to at most arborescence
     3203            for e in self.edges(labels=False):
     3204                p.add_constraint(sum([edges[j][e] for j in colors]), max=1)
     3205
     3206
     3207            for j in colors:
     3208                # each color class has self.order()-1 edges
     3209                p.add_constraint(sum([edges[j][e] for e in self.edges(labels=None)]), min=self.order()-1)
     3210
     3211                # Each vertex different from the root has indegree equals to one
     3212                for v in self.vertices():
     3213                    if v is not root:
     3214                        p.add_constraint(sum([edges[j][e] for e in self.incoming_edges(v, labels=None)]), max=1, min=1)
     3215                    else:
     3216                        p.add_constraint(sum([edges[j][e] for e in self.incoming_edges(v, labels=None)]), max=0, min=0)
     3217
     3218                # r_edges is larger than edges
     3219                for u,v in self.edges(labels=None):
     3220                    if self.has_edge(v,u):
     3221                        if v<u:
     3222                            p.add_constraint(r_edges[j][(u,v)] + r_edges[j][(v, u)] - edges[j][(u,v)] - edges[j][(v,u)], min=0)
     3223                    else:
     3224                        p.add_constraint(r_edges[j][(u,v)] + r_edges[j][(v, u)] - edges[j][(u,v)], min=0)
     3225
     3226                from sage.graphs.digraph import DiGraph
     3227                D = DiGraph()
     3228                D.add_vertices(self.vertices())
     3229                D.set_pos(self.get_pos())
     3230                classes = [D.copy() for j in colors]
     3231               
     3232        else:
     3233
     3234            # Sort an edge
     3235            S = lambda (x,y) : (x,y) if x<y else (y,x)
     3236           
     3237            # An edge belongs to at most one arborescence
     3238            for e in self.edges(labels=False):
     3239                p.add_constraint(sum([edges[j][S(e)] for j in colors]), max=1)
     3240
     3241
     3242            for j in colors:
     3243                # each color class has self.order()-1 edges
     3244                p.add_constraint(sum([edges[j][S(e)] for e in self.edges(labels=None)]), min=self.order()-1)
     3245
     3246                # Each vertex is in the tree
     3247                for v in self.vertices():
     3248                    p.add_constraint(sum([edges[j][S(e)] for e in self.edges_incident(v, labels=None)]), min=1)
     3249
     3250                # r_edges is larger than edges
     3251                for u,v in self.edges(labels=None):
     3252                    p.add_constraint(r_edges[j][(u,v)] + r_edges[j][(v, u)] - edges[j][S((u,v))], min=0)
     3253
     3254                from sage.graphs.graph import Graph
     3255                D = Graph()
     3256                D.add_vertices(self.vertices())
     3257                D.set_pos(self.get_pos())
     3258                classes = [D.copy() for j in colors]
     3259
     3260        # no cycles
     3261        for j in colors:
     3262            for v in self:
     3263                p.add_constraint(sum(r_edges[j][(u,v)] for u in self.neighbors(v)), max=1-epsilon)
     3264
     3265        p.set_binary(edges)               
     3266
     3267        try:
     3268            p.solve(**kwds)
     3269
     3270        except MIPSolverException:
     3271            raise ValueError("This graph does not contain the required number of trees/arborescences !")
     3272
     3273        edges = p.get_values(edges)
     3274
     3275        for j,g in enumerate(classes):
     3276            for e in self.edges(labels=False):
     3277                if edges[j][S(e)] == 1:
     3278                    g.add_edge(e)
     3279
     3280        return classes
     3281           
    30893282               
    30903283    def edge_cut(self, s, t, value_only=True, use_edge_labels=False, vertices=False, solver=None, verbose=0):
    30913284        r"""