11"""Functions involving natural generalizations of the minimum cut problem."""
22
3+ import itertools
34import networkx as nx
45from networkx .utils import not_implemented_for
56
@@ -55,7 +56,11 @@ def minimum_multiway_cut(G, terminals, weight=None):
5556 Raises
5657 ------
5758 NetworkXNotImplemented
58- If G is directed.
59+ If G is directed or a mutigraph
60+ NetworkXError
61+ If G is empty or
62+ If G is not connected or
63+ If the number of terminals is smaller than 2.
5964
6065 References
6166 ----------
@@ -64,7 +69,53 @@ def minimum_multiway_cut(G, terminals, weight=None):
6469 .. [2] Dahlhaus, Elias, et al. *The complexity of multiway cuts*.
6570 Proceedings of the twenty-fourth annual ACM symposium on Theory of computing. 1992
6671 """
67- pass
72+ if not G :
73+ raise nx .NetworkXError ("Expected non-empty NetworkX graph!" )
74+ if not nx .is_connected (G ):
75+ raise nx .NetworkXError ("Graph not connected." )
76+ # convert to a set
77+ terminals = set (terminals )
78+ # only consider the terminals in G
79+ terminals = terminals & G .nodes ()
80+ # raise an error if less than two terminal have been provided
81+ if len (terminals & G .nodes ()) < 2 :
82+ raise nx .NetworkXError ("At least two terminals should be provided." )
83+
84+ # extract edges weight, and set edges weights with no attribute to 1
85+ edges_weights = G .edges (data = weight , default = 1 )
86+ # create a new Graph G2
87+ G2 = nx .Graph ()
88+ G2 .add_weighted_edges_from (edges_weights , weight = "capacity" )
89+
90+ # take a non-existing node of G to be the sink
91+ sink = next (u for u in range (len (G ) + 1 ) if u not in G )
92+ # add edges from the terminals to the sink node
93+ G2 .add_edges_from ([(u , sink ) for u in terminals ], capacity = float ("inf" ))
94+
95+ # compute the minimum weight cut for each terminal to all the others
96+ all_cuts = []
97+ for u in terminals :
98+ # remove the edge to the sink
99+ G2 .remove_edge (u , sink )
100+ # get the cut value and the 2 partitions of nodes
101+ value_cut , (p1 , p2 ) = nx .minimum_cut (G2 , u , sink )
102+ # get the edges crossing the cut
103+ edges_cut = set (nx .edge_boundary (G2 , p1 , p2 ))
104+ # add to the result
105+ all_cuts .append ((edges_cut , value_cut ))
106+ # re-add the edge to the sink
107+ G2 .add_edge (u , sink , capacity = float ("inf" ))
108+
109+ # discard the heaviest cut and take the union of the rest
110+ cutset = set (
111+ itertools .chain .from_iterable (
112+ el [0 ] for el in sorted (all_cuts , key = lambda x : x [1 ])[:- 1 ]
113+ )
114+ )
115+ # compute the cut value
116+ cut_value = sum (G2 .edges [u , v ]["capacity" ] for u , v in cutset )
117+
118+ return cut_value , cutset
68119
69120
70121@not_implemented_for ("directed" )
@@ -116,9 +167,11 @@ def minimum_k_cut(G, k, weight=None):
116167 Raises
117168 ------
118169 NetworkXNotImplemented
170+ If G is directed or a mutigraph
171+ NetworkXError
172+ If G is empty or
119173 If G is not connected or
120- If k is smaller than 1 or
121- If k is greater than the number of nodes of G.
174+ If k is smaller than 1 or greater than the number of nodes of G.
122175
123176 Notes
124177 -----
@@ -136,6 +189,8 @@ def minimum_k_cut(G, k, weight=None):
136189 .. [1] Vazirani, Vijay V. *Approximation algorithms*.
137190 Springer Science & Business Media, 2013.
138191 """
192+ if not G :
193+ raise nx .NetworkXError ("Expected non-empty NetworkX graph!" )
139194 if not nx .is_connected (G ):
140195 raise nx .NetworkXError ("Graph not connected." )
141196 if not 1 <= k <= len (G ):
@@ -164,7 +219,7 @@ def minimum_k_cut(G, k, weight=None):
164219 # re-add (u,v) to the tree
165220 T .add_edge (u , v )
166221
167- # compute the cut_value, each
222+ # compute the cut_value
168223 cut_value = sum (G2 .edges [u , v ]["capacity" ] for u , v in cutset )
169224
170225 return cut_value , cutset
0 commit comments