diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 023439acea5..2b0387d210f 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -6312,7 +6312,7 @@ def is_prime(self): return D[0] == "Prime" and len(D[1]) == self.order() - def _gomory_hu_tree(self, vertices=None, method="FF"): + def _gomory_hu_tree(self, vertices, method="FF"): r""" Returns a Gomory-Hu tree associated to self. @@ -6345,101 +6345,65 @@ def _gomory_hu_tree(self, vertices=None, method="FF"): example is only present to have a doctest coverage of 100%. sage: g = graphs.PetersenGraph() - sage: t = g._gomory_hu_tree() + sage: t = g._gomory_hu_tree(frozenset(g.vertices())) """ self._scream_if_not_simple() - from sage.sets.set import Set - - # The default capacity of an arc is 1 - from sage.rings.real_mpfr import RR - capacity = lambda label: label if label in RR else 1 - - # Keeping the graph's embedding - pos = False # Small case, not really a problem ;-) - if self.order() == 1: - return self.copy() - - # This is a sign that this is the first call - # to this recursive function - if vertices is None: - # Now is the time to care about positions - pos = self.get_pos() - - # if the graph is not connected, returns the union - # of the Gomory-Hu tree of each component - if not self.is_connected(): - g = Graph() - for cc in self.connected_components_subgraphs(): - g = g.union(cc._gomory_hu_tree(method=method)) - g.set_pos(self.get_pos()) - return g - # All the vertices is this graph are the "real ones" - vertices = Set(self.vertices()) - - # There may be many vertices, though only one which is "real" if len(vertices) == 1: g = Graph() - g.add_vertex(vertices[0]) + g.add_vertices(vertices) return g - # Take any two vertices - u,v = vertices[0:2] + # Take any two vertices (u,v) + it = vertices.__iter__() + u,v = it.next(),it.next() - # Recovers the following values - # flow is the connectivity between u and v - # edges of a min cut - # sets1, sets2 are the two sides of the edge cut - flow,edges,[set1,set2] = self.edge_cut(u, v, use_edge_labels=True, vertices=True, method=method) + # Compute a uv min-edge-cut. + # + # The graph is split into U,V with u \in U and v\in V. + flow,edges,[U,V] = self.edge_cut(u, v, use_edge_labels=True, vertices=True, method=method) # One graph for each part of the previous one - g1,g2 = self.subgraph(set1), self.subgraph(set2) - - # Adding the fake vertex to each part - g1_v = Set(set2) - g2_v = Set(set1) - g1.add_vertex(g1_v) - g1.add_vertex(g2_v) - - # Each part of the graph had many edges going to the other part - # Now that we have a new fake vertex in each part - # we just say that the edges which were in the cut and going - # to the other side are now going to this fake vertex - - # We must preserve the labels. They sum. - - for e in edges: - x,y = e[0],e[1] - # Assumes x is in g1 - if x in g2: - x,y = y,x - # If the edge x-g1_v exists, adds to its label the capacity of arc xy - if g1.has_edge(x, g1_v): - g1.set_edge_label(x, g1_v, g1.edge_label(x, g1_v) + capacity(self.edge_label(x, y))) - else: - # Otherwise, creates it with the good label - g1.add_edge(x, g1_v, capacity(self.edge_label(x, y))) - # Same thing for g2 - if g2.has_edge(y, g2_v): - g2.set_edge_label(y, g2_v, g2.edge_label(y, g2_v) + capacity(self.edge_label(x, y))) - else: - g2.add_edge(y, g2_v, capacity(self.edge_label(x, y))) + gU,gV = self.subgraph(U), self.subgraph(V) - # Recursion for the two new graphs... The new "real" vertices are the intersection with - # with the previous set of "real" vertices - g1_tree = g1._gomory_hu_tree(vertices=(vertices & Set(g1.vertices())), method=method) - g2_tree = g2._gomory_hu_tree(vertices=(vertices & Set(g2.vertices())), method=method) + # A fake vertex fU (resp. fV) to represent U (resp. V) + fU = frozenset(U) + fV = frozenset(V) - # Union of the two partial trees ( it is disjoint, but - # disjoint_union does not preserve the name of the vertices ) - g = g1_tree.union(g2_tree) + # Each edge (uu,vv) with uu \in U and vv\in V yields: + # - an edge (uu,fV) in gU + # - an edge (vv,fU) in gV + # + # If the same edge is added several times their capacities add up. - # An edge to connect them, with the appropriate label - g.add_edge(next(g1_tree.vertex_iterator()), next(g2_tree.vertex_iterator()), flow) + from sage.rings.real_mpfr import RR + for uu,vv,capacity in edges: + capacity = capacity if capacity in RR else 1 + + # Assume uu is in gU + if uu in V: + uu,vv = vv,uu + + # Create the new edges if necessary + if not gU.has_edge(uu, fV): + gU.add_edge(uu, fV, 0) + if not gV.has_edge(vv, fU): + gV.add_edge(vv, fU, 0) + + # update the capacities + gU.set_edge_label(uu, fV, gU.edge_label(uu, fV) + capacity) + gV.set_edge_label(vv, fU, gV.edge_label(vv, fU) + capacity) + + # Recursion on each side + gU_tree = gU._gomory_hu_tree(vertices & frozenset(gU), method=method) + gV_tree = gV._gomory_hu_tree(vertices & frozenset(gV), method=method) - if pos: - g.set_pos(pos) + # Union of the two partial trees + g = gU_tree.union(gV_tree) + + # An edge to connect them, with the appropriate label + g.add_edge(u, v, flow) return g @@ -6448,15 +6412,17 @@ def gomory_hu_tree(self, method="FF"): Returns a Gomory-Hu tree of self. Given a tree `T` with labeled edges representing capacities, it is very - easy to determine the maximal flow between any pair of vertices : + easy to determine the maximum flow between any pair of vertices : it is the minimal label on the edges of the unique path between them. Given a graph `G`, a Gomory-Hu tree `T` of `G` is a tree - with the same set of vertices, and such that the maximal flow + with the same set of vertices, and such that the maximum flow between any two vertices is the same in `G` as in `T`. See the `Wikipedia article on Gomory-Hu tree `_. Note that, in general, a graph admits more than one Gomory-Hu tree. + See also 15.4 (Gomory-Hu trees) from [SchrijverCombOpt]_. + INPUT: - ``method`` -- There are currently two different @@ -6511,8 +6477,29 @@ def gomory_hu_tree(self, method="FF"): sage: g.edge_connectivity() == min(t.edge_labels()) True + + TESTS: + + :trac:`16475`:: + + sage: G = graphs.PetersenGraph() + sage: for u, v in [(0, 1), (0, 4), (0, 5), (1, 2), (1, 6), (3, 4), (5, 7), (5, 8)]: + ....: G.set_edge_label(u, v, 2) + sage: T = G.gomory_hu_tree() + sage: from itertools import combinations + sage: for u,v in combinations(G,2): + ....: assert T.flow(u,v,use_edge_labels=True) == G.flow(u,v,use_edge_labels=True) """ - return self._gomory_hu_tree(method=method) + if not self.is_connected(): + g = Graph() + for cc in self.connected_components_subgraphs(): + g = g.union(cc._gomory_hu_tree(frozenset(cc.vertices()),method=method)) + else: + g = self._gomory_hu_tree(frozenset(self.vertices()),method=method) + + if self.get_pos() is not None: + g.set_pos(dict(self.get_pos())) + return g def two_factor_petersen(self): r"""