Fixed database typo and removed unnecessary class identifier.
This commit is contained in:
parent
00ad49a143
commit
45fb349a7d
5098 changed files with 952558 additions and 85 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,125 @@
|
|||
from itertools import combinations
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
from networkx.algorithms.flow import boykov_kolmogorov
|
||||
from networkx.algorithms.flow import edmonds_karp
|
||||
from networkx.algorithms.flow import preflow_push
|
||||
from networkx.algorithms.flow import shortest_augmenting_path
|
||||
from networkx.algorithms.flow import dinitz
|
||||
|
||||
flow_funcs = [
|
||||
boykov_kolmogorov,
|
||||
dinitz,
|
||||
edmonds_karp,
|
||||
preflow_push,
|
||||
shortest_augmenting_path,
|
||||
]
|
||||
|
||||
|
||||
class TestGomoryHuTree:
|
||||
def minimum_edge_weight(self, T, u, v):
|
||||
path = nx.shortest_path(T, u, v, weight="weight")
|
||||
return min((T[u][v]["weight"], (u, v)) for (u, v) in zip(path, path[1:]))
|
||||
|
||||
def compute_cutset(self, G, T_orig, edge):
|
||||
T = T_orig.copy()
|
||||
T.remove_edge(*edge)
|
||||
U, V = list(nx.connected_components(T))
|
||||
cutset = set()
|
||||
for x, nbrs in ((n, G[n]) for n in U):
|
||||
cutset.update((x, y) for y in nbrs if y in V)
|
||||
return cutset
|
||||
|
||||
def test_default_flow_function_karate_club_graph(self):
|
||||
G = nx.karate_club_graph()
|
||||
nx.set_edge_attributes(G, 1, "capacity")
|
||||
T = nx.gomory_hu_tree(G)
|
||||
assert nx.is_tree(T)
|
||||
for u, v in combinations(G, 2):
|
||||
cut_value, edge = self.minimum_edge_weight(T, u, v)
|
||||
assert nx.minimum_cut_value(G, u, v) == cut_value
|
||||
|
||||
def test_karate_club_graph(self):
|
||||
G = nx.karate_club_graph()
|
||||
nx.set_edge_attributes(G, 1, "capacity")
|
||||
for flow_func in flow_funcs:
|
||||
T = nx.gomory_hu_tree(G, flow_func=flow_func)
|
||||
assert nx.is_tree(T)
|
||||
for u, v in combinations(G, 2):
|
||||
cut_value, edge = self.minimum_edge_weight(T, u, v)
|
||||
assert nx.minimum_cut_value(G, u, v) == cut_value
|
||||
|
||||
def test_davis_southern_women_graph(self):
|
||||
G = nx.davis_southern_women_graph()
|
||||
nx.set_edge_attributes(G, 1, "capacity")
|
||||
for flow_func in flow_funcs:
|
||||
T = nx.gomory_hu_tree(G, flow_func=flow_func)
|
||||
assert nx.is_tree(T)
|
||||
for u, v in combinations(G, 2):
|
||||
cut_value, edge = self.minimum_edge_weight(T, u, v)
|
||||
assert nx.minimum_cut_value(G, u, v) == cut_value
|
||||
|
||||
def test_florentine_families_graph(self):
|
||||
G = nx.florentine_families_graph()
|
||||
nx.set_edge_attributes(G, 1, "capacity")
|
||||
for flow_func in flow_funcs:
|
||||
T = nx.gomory_hu_tree(G, flow_func=flow_func)
|
||||
assert nx.is_tree(T)
|
||||
for u, v in combinations(G, 2):
|
||||
cut_value, edge = self.minimum_edge_weight(T, u, v)
|
||||
assert nx.minimum_cut_value(G, u, v) == cut_value
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_les_miserables_graph_cutset(self):
|
||||
G = nx.les_miserables_graph()
|
||||
nx.set_edge_attributes(G, 1, "capacity")
|
||||
for flow_func in flow_funcs:
|
||||
T = nx.gomory_hu_tree(G, flow_func=flow_func)
|
||||
assert nx.is_tree(T)
|
||||
for u, v in combinations(G, 2):
|
||||
cut_value, edge = self.minimum_edge_weight(T, u, v)
|
||||
assert nx.minimum_cut_value(G, u, v) == cut_value
|
||||
|
||||
def test_karate_club_graph_cutset(self):
|
||||
G = nx.karate_club_graph()
|
||||
nx.set_edge_attributes(G, 1, "capacity")
|
||||
T = nx.gomory_hu_tree(G)
|
||||
assert nx.is_tree(T)
|
||||
u, v = 0, 33
|
||||
cut_value, edge = self.minimum_edge_weight(T, u, v)
|
||||
cutset = self.compute_cutset(G, T, edge)
|
||||
assert cut_value == len(cutset)
|
||||
|
||||
def test_wikipedia_example(self):
|
||||
# Example from https://en.wikipedia.org/wiki/Gomory%E2%80%93Hu_tree
|
||||
G = nx.Graph()
|
||||
G.add_weighted_edges_from(
|
||||
(
|
||||
(0, 1, 1),
|
||||
(0, 2, 7),
|
||||
(1, 2, 1),
|
||||
(1, 3, 3),
|
||||
(1, 4, 2),
|
||||
(2, 4, 4),
|
||||
(3, 4, 1),
|
||||
(3, 5, 6),
|
||||
(4, 5, 2),
|
||||
)
|
||||
)
|
||||
for flow_func in flow_funcs:
|
||||
T = nx.gomory_hu_tree(G, capacity="weight", flow_func=flow_func)
|
||||
assert nx.is_tree(T)
|
||||
for u, v in combinations(G, 2):
|
||||
cut_value, edge = self.minimum_edge_weight(T, u, v)
|
||||
assert nx.minimum_cut_value(G, u, v, capacity="weight") == cut_value
|
||||
|
||||
def test_directed_raises(self):
|
||||
with pytest.raises(nx.NetworkXNotImplemented):
|
||||
G = nx.DiGraph()
|
||||
T = nx.gomory_hu_tree(G)
|
||||
|
||||
def test_empty_raises(self):
|
||||
with pytest.raises(nx.NetworkXError):
|
||||
G = nx.empty_graph()
|
||||
T = nx.gomory_hu_tree(G)
|
|
@ -0,0 +1,548 @@
|
|||
"""Maximum flow algorithms test suite.
|
||||
"""
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
from networkx.algorithms.flow import build_flow_dict, build_residual_network
|
||||
from networkx.algorithms.flow import boykov_kolmogorov
|
||||
from networkx.algorithms.flow import edmonds_karp
|
||||
from networkx.algorithms.flow import preflow_push
|
||||
from networkx.algorithms.flow import shortest_augmenting_path
|
||||
from networkx.algorithms.flow import dinitz
|
||||
|
||||
flow_funcs = [
|
||||
boykov_kolmogorov,
|
||||
dinitz,
|
||||
edmonds_karp,
|
||||
preflow_push,
|
||||
shortest_augmenting_path,
|
||||
]
|
||||
max_min_funcs = [nx.maximum_flow, nx.minimum_cut]
|
||||
flow_value_funcs = [nx.maximum_flow_value, nx.minimum_cut_value]
|
||||
interface_funcs = sum([max_min_funcs, flow_value_funcs], [])
|
||||
all_funcs = sum([flow_funcs, interface_funcs], [])
|
||||
|
||||
|
||||
def compute_cutset(G, partition):
|
||||
reachable, non_reachable = partition
|
||||
cutset = set()
|
||||
for u, nbrs in ((n, G[n]) for n in reachable):
|
||||
cutset.update((u, v) for v in nbrs if v in non_reachable)
|
||||
return cutset
|
||||
|
||||
|
||||
def validate_flows(G, s, t, flowDict, solnValue, capacity, flow_func):
|
||||
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
||||
assert set(G) == set(flowDict), errmsg
|
||||
for u in G:
|
||||
assert set(G[u]) == set(flowDict[u]), errmsg
|
||||
excess = {u: 0 for u in flowDict}
|
||||
for u in flowDict:
|
||||
for v, flow in flowDict[u].items():
|
||||
if capacity in G[u][v]:
|
||||
assert flow <= G[u][v][capacity]
|
||||
assert flow >= 0, errmsg
|
||||
excess[u] -= flow
|
||||
excess[v] += flow
|
||||
for u, exc in excess.items():
|
||||
if u == s:
|
||||
assert exc == -solnValue, errmsg
|
||||
elif u == t:
|
||||
assert exc == solnValue, errmsg
|
||||
else:
|
||||
assert exc == 0, errmsg
|
||||
|
||||
|
||||
def validate_cuts(G, s, t, solnValue, partition, capacity, flow_func):
|
||||
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
||||
assert all(n in G for n in partition[0]), errmsg
|
||||
assert all(n in G for n in partition[1]), errmsg
|
||||
cutset = compute_cutset(G, partition)
|
||||
assert all(G.has_edge(u, v) for (u, v) in cutset), errmsg
|
||||
assert solnValue == sum(G[u][v][capacity] for (u, v) in cutset), errmsg
|
||||
H = G.copy()
|
||||
H.remove_edges_from(cutset)
|
||||
if not G.is_directed():
|
||||
assert not nx.is_connected(H), errmsg
|
||||
else:
|
||||
assert not nx.is_strongly_connected(H), errmsg
|
||||
|
||||
|
||||
def compare_flows_and_cuts(G, s, t, solnFlows, solnValue, capacity="capacity"):
|
||||
for flow_func in flow_funcs:
|
||||
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
||||
R = flow_func(G, s, t, capacity)
|
||||
# Test both legacy and new implementations.
|
||||
flow_value = R.graph["flow_value"]
|
||||
flow_dict = build_flow_dict(G, R)
|
||||
assert flow_value == solnValue, errmsg
|
||||
validate_flows(G, s, t, flow_dict, solnValue, capacity, flow_func)
|
||||
# Minimum cut
|
||||
cut_value, partition = nx.minimum_cut(
|
||||
G, s, t, capacity=capacity, flow_func=flow_func
|
||||
)
|
||||
validate_cuts(G, s, t, solnValue, partition, capacity, flow_func)
|
||||
|
||||
|
||||
class TestMaxflowMinCutCommon:
|
||||
def test_graph1(self):
|
||||
# Trivial undirected graph
|
||||
G = nx.Graph()
|
||||
G.add_edge(1, 2, capacity=1.0)
|
||||
|
||||
solnFlows = {1: {2: 1.0}, 2: {1: 1.0}}
|
||||
|
||||
compare_flows_and_cuts(G, 1, 2, solnFlows, 1.0)
|
||||
|
||||
def test_graph2(self):
|
||||
# A more complex undirected graph
|
||||
# adapted from www.topcoder.com/tc?module=Statc&d1=tutorials&d2=maxFlow
|
||||
G = nx.Graph()
|
||||
G.add_edge("x", "a", capacity=3.0)
|
||||
G.add_edge("x", "b", capacity=1.0)
|
||||
G.add_edge("a", "c", capacity=3.0)
|
||||
G.add_edge("b", "c", capacity=5.0)
|
||||
G.add_edge("b", "d", capacity=4.0)
|
||||
G.add_edge("d", "e", capacity=2.0)
|
||||
G.add_edge("c", "y", capacity=2.0)
|
||||
G.add_edge("e", "y", capacity=3.0)
|
||||
|
||||
H = {
|
||||
"x": {"a": 3, "b": 1},
|
||||
"a": {"c": 3, "x": 3},
|
||||
"b": {"c": 1, "d": 2, "x": 1},
|
||||
"c": {"a": 3, "b": 1, "y": 2},
|
||||
"d": {"b": 2, "e": 2},
|
||||
"e": {"d": 2, "y": 2},
|
||||
"y": {"c": 2, "e": 2},
|
||||
}
|
||||
|
||||
compare_flows_and_cuts(G, "x", "y", H, 4.0)
|
||||
|
||||
def test_digraph1(self):
|
||||
# The classic directed graph example
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("a", "b", capacity=1000.0)
|
||||
G.add_edge("a", "c", capacity=1000.0)
|
||||
G.add_edge("b", "c", capacity=1.0)
|
||||
G.add_edge("b", "d", capacity=1000.0)
|
||||
G.add_edge("c", "d", capacity=1000.0)
|
||||
|
||||
H = {
|
||||
"a": {"b": 1000.0, "c": 1000.0},
|
||||
"b": {"c": 0, "d": 1000.0},
|
||||
"c": {"d": 1000.0},
|
||||
"d": {},
|
||||
}
|
||||
|
||||
compare_flows_and_cuts(G, "a", "d", H, 2000.0)
|
||||
|
||||
def test_digraph2(self):
|
||||
# An example in which some edges end up with zero flow.
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("s", "b", capacity=2)
|
||||
G.add_edge("s", "c", capacity=1)
|
||||
G.add_edge("c", "d", capacity=1)
|
||||
G.add_edge("d", "a", capacity=1)
|
||||
G.add_edge("b", "a", capacity=2)
|
||||
G.add_edge("a", "t", capacity=2)
|
||||
|
||||
H = {
|
||||
"s": {"b": 2, "c": 0},
|
||||
"c": {"d": 0},
|
||||
"d": {"a": 0},
|
||||
"b": {"a": 2},
|
||||
"a": {"t": 2},
|
||||
"t": {},
|
||||
}
|
||||
|
||||
compare_flows_and_cuts(G, "s", "t", H, 2)
|
||||
|
||||
def test_digraph3(self):
|
||||
# A directed graph example from Cormen et al.
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("s", "v1", capacity=16.0)
|
||||
G.add_edge("s", "v2", capacity=13.0)
|
||||
G.add_edge("v1", "v2", capacity=10.0)
|
||||
G.add_edge("v2", "v1", capacity=4.0)
|
||||
G.add_edge("v1", "v3", capacity=12.0)
|
||||
G.add_edge("v3", "v2", capacity=9.0)
|
||||
G.add_edge("v2", "v4", capacity=14.0)
|
||||
G.add_edge("v4", "v3", capacity=7.0)
|
||||
G.add_edge("v3", "t", capacity=20.0)
|
||||
G.add_edge("v4", "t", capacity=4.0)
|
||||
|
||||
H = {
|
||||
"s": {"v1": 12.0, "v2": 11.0},
|
||||
"v2": {"v1": 0, "v4": 11.0},
|
||||
"v1": {"v2": 0, "v3": 12.0},
|
||||
"v3": {"v2": 0, "t": 19.0},
|
||||
"v4": {"v3": 7.0, "t": 4.0},
|
||||
"t": {},
|
||||
}
|
||||
|
||||
compare_flows_and_cuts(G, "s", "t", H, 23.0)
|
||||
|
||||
def test_digraph4(self):
|
||||
# A more complex directed graph
|
||||
# from www.topcoder.com/tc?module=Statc&d1=tutorials&d2=maxFlow
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("x", "a", capacity=3.0)
|
||||
G.add_edge("x", "b", capacity=1.0)
|
||||
G.add_edge("a", "c", capacity=3.0)
|
||||
G.add_edge("b", "c", capacity=5.0)
|
||||
G.add_edge("b", "d", capacity=4.0)
|
||||
G.add_edge("d", "e", capacity=2.0)
|
||||
G.add_edge("c", "y", capacity=2.0)
|
||||
G.add_edge("e", "y", capacity=3.0)
|
||||
|
||||
H = {
|
||||
"x": {"a": 2.0, "b": 1.0},
|
||||
"a": {"c": 2.0},
|
||||
"b": {"c": 0, "d": 1.0},
|
||||
"c": {"y": 2.0},
|
||||
"d": {"e": 1.0},
|
||||
"e": {"y": 1.0},
|
||||
"y": {},
|
||||
}
|
||||
|
||||
compare_flows_and_cuts(G, "x", "y", H, 3.0)
|
||||
|
||||
def test_wikipedia_dinitz_example(self):
|
||||
# Nice example from https://en.wikipedia.org/wiki/Dinic's_algorithm
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("s", 1, capacity=10)
|
||||
G.add_edge("s", 2, capacity=10)
|
||||
G.add_edge(1, 3, capacity=4)
|
||||
G.add_edge(1, 4, capacity=8)
|
||||
G.add_edge(1, 2, capacity=2)
|
||||
G.add_edge(2, 4, capacity=9)
|
||||
G.add_edge(3, "t", capacity=10)
|
||||
G.add_edge(4, 3, capacity=6)
|
||||
G.add_edge(4, "t", capacity=10)
|
||||
|
||||
solnFlows = {
|
||||
1: {2: 0, 3: 4, 4: 6},
|
||||
2: {4: 9},
|
||||
3: {"t": 9},
|
||||
4: {3: 5, "t": 10},
|
||||
"s": {1: 10, 2: 9},
|
||||
"t": {},
|
||||
}
|
||||
|
||||
compare_flows_and_cuts(G, "s", "t", solnFlows, 19)
|
||||
|
||||
def test_optional_capacity(self):
|
||||
# Test optional capacity parameter.
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("x", "a", spam=3.0)
|
||||
G.add_edge("x", "b", spam=1.0)
|
||||
G.add_edge("a", "c", spam=3.0)
|
||||
G.add_edge("b", "c", spam=5.0)
|
||||
G.add_edge("b", "d", spam=4.0)
|
||||
G.add_edge("d", "e", spam=2.0)
|
||||
G.add_edge("c", "y", spam=2.0)
|
||||
G.add_edge("e", "y", spam=3.0)
|
||||
|
||||
solnFlows = {
|
||||
"x": {"a": 2.0, "b": 1.0},
|
||||
"a": {"c": 2.0},
|
||||
"b": {"c": 0, "d": 1.0},
|
||||
"c": {"y": 2.0},
|
||||
"d": {"e": 1.0},
|
||||
"e": {"y": 1.0},
|
||||
"y": {},
|
||||
}
|
||||
solnValue = 3.0
|
||||
s = "x"
|
||||
t = "y"
|
||||
|
||||
compare_flows_and_cuts(G, s, t, solnFlows, solnValue, capacity="spam")
|
||||
|
||||
def test_digraph_infcap_edges(self):
|
||||
# DiGraph with infinite capacity edges
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("s", "a")
|
||||
G.add_edge("s", "b", capacity=30)
|
||||
G.add_edge("a", "c", capacity=25)
|
||||
G.add_edge("b", "c", capacity=12)
|
||||
G.add_edge("a", "t", capacity=60)
|
||||
G.add_edge("c", "t")
|
||||
|
||||
H = {
|
||||
"s": {"a": 85, "b": 12},
|
||||
"a": {"c": 25, "t": 60},
|
||||
"b": {"c": 12},
|
||||
"c": {"t": 37},
|
||||
"t": {},
|
||||
}
|
||||
|
||||
compare_flows_and_cuts(G, "s", "t", H, 97)
|
||||
|
||||
# DiGraph with infinite capacity digon
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("s", "a", capacity=85)
|
||||
G.add_edge("s", "b", capacity=30)
|
||||
G.add_edge("a", "c")
|
||||
G.add_edge("c", "a")
|
||||
G.add_edge("b", "c", capacity=12)
|
||||
G.add_edge("a", "t", capacity=60)
|
||||
G.add_edge("c", "t", capacity=37)
|
||||
|
||||
H = {
|
||||
"s": {"a": 85, "b": 12},
|
||||
"a": {"c": 25, "t": 60},
|
||||
"c": {"a": 0, "t": 37},
|
||||
"b": {"c": 12},
|
||||
"t": {},
|
||||
}
|
||||
|
||||
compare_flows_and_cuts(G, "s", "t", H, 97)
|
||||
|
||||
def test_digraph_infcap_path(self):
|
||||
# Graph with infinite capacity (s, t)-path
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("s", "a")
|
||||
G.add_edge("s", "b", capacity=30)
|
||||
G.add_edge("a", "c")
|
||||
G.add_edge("b", "c", capacity=12)
|
||||
G.add_edge("a", "t", capacity=60)
|
||||
G.add_edge("c", "t")
|
||||
|
||||
for flow_func in all_funcs:
|
||||
pytest.raises(nx.NetworkXUnbounded, flow_func, G, "s", "t")
|
||||
|
||||
def test_graph_infcap_edges(self):
|
||||
# Undirected graph with infinite capacity edges
|
||||
G = nx.Graph()
|
||||
G.add_edge("s", "a")
|
||||
G.add_edge("s", "b", capacity=30)
|
||||
G.add_edge("a", "c", capacity=25)
|
||||
G.add_edge("b", "c", capacity=12)
|
||||
G.add_edge("a", "t", capacity=60)
|
||||
G.add_edge("c", "t")
|
||||
|
||||
H = {
|
||||
"s": {"a": 85, "b": 12},
|
||||
"a": {"c": 25, "s": 85, "t": 60},
|
||||
"b": {"c": 12, "s": 12},
|
||||
"c": {"a": 25, "b": 12, "t": 37},
|
||||
"t": {"a": 60, "c": 37},
|
||||
}
|
||||
|
||||
compare_flows_and_cuts(G, "s", "t", H, 97)
|
||||
|
||||
def test_digraph5(self):
|
||||
# From ticket #429 by mfrasca.
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("s", "a", capacity=2)
|
||||
G.add_edge("s", "b", capacity=2)
|
||||
G.add_edge("a", "b", capacity=5)
|
||||
G.add_edge("a", "t", capacity=1)
|
||||
G.add_edge("b", "a", capacity=1)
|
||||
G.add_edge("b", "t", capacity=3)
|
||||
flowSoln = {
|
||||
"a": {"b": 1, "t": 1},
|
||||
"b": {"a": 0, "t": 3},
|
||||
"s": {"a": 2, "b": 2},
|
||||
"t": {},
|
||||
}
|
||||
compare_flows_and_cuts(G, "s", "t", flowSoln, 4)
|
||||
|
||||
def test_disconnected(self):
|
||||
G = nx.Graph()
|
||||
G.add_weighted_edges_from([(0, 1, 1), (1, 2, 1), (2, 3, 1)], weight="capacity")
|
||||
G.remove_node(1)
|
||||
assert nx.maximum_flow_value(G, 0, 3) == 0
|
||||
flowSoln = {0: {}, 2: {3: 0}, 3: {2: 0}}
|
||||
compare_flows_and_cuts(G, 0, 3, flowSoln, 0)
|
||||
|
||||
def test_source_target_not_in_graph(self):
|
||||
G = nx.Graph()
|
||||
G.add_weighted_edges_from([(0, 1, 1), (1, 2, 1), (2, 3, 1)], weight="capacity")
|
||||
G.remove_node(0)
|
||||
for flow_func in all_funcs:
|
||||
pytest.raises(nx.NetworkXError, flow_func, G, 0, 3)
|
||||
G.add_weighted_edges_from([(0, 1, 1), (1, 2, 1), (2, 3, 1)], weight="capacity")
|
||||
G.remove_node(3)
|
||||
for flow_func in all_funcs:
|
||||
pytest.raises(nx.NetworkXError, flow_func, G, 0, 3)
|
||||
|
||||
def test_source_target_coincide(self):
|
||||
G = nx.Graph()
|
||||
G.add_node(0)
|
||||
for flow_func in all_funcs:
|
||||
pytest.raises(nx.NetworkXError, flow_func, G, 0, 0)
|
||||
|
||||
def test_multigraphs_raise(self):
|
||||
G = nx.MultiGraph()
|
||||
M = nx.MultiDiGraph()
|
||||
G.add_edges_from([(0, 1), (1, 0)], capacity=True)
|
||||
for flow_func in all_funcs:
|
||||
pytest.raises(nx.NetworkXError, flow_func, G, 0, 0)
|
||||
|
||||
|
||||
class TestMaxFlowMinCutInterface:
|
||||
def setup(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("x", "a", capacity=3.0)
|
||||
G.add_edge("x", "b", capacity=1.0)
|
||||
G.add_edge("a", "c", capacity=3.0)
|
||||
G.add_edge("b", "c", capacity=5.0)
|
||||
G.add_edge("b", "d", capacity=4.0)
|
||||
G.add_edge("d", "e", capacity=2.0)
|
||||
G.add_edge("c", "y", capacity=2.0)
|
||||
G.add_edge("e", "y", capacity=3.0)
|
||||
self.G = G
|
||||
H = nx.DiGraph()
|
||||
H.add_edge(0, 1, capacity=1.0)
|
||||
H.add_edge(1, 2, capacity=1.0)
|
||||
self.H = H
|
||||
|
||||
def test_flow_func_not_callable(self):
|
||||
elements = ["this_should_be_callable", 10, {1, 2, 3}]
|
||||
G = nx.Graph()
|
||||
G.add_weighted_edges_from([(0, 1, 1), (1, 2, 1), (2, 3, 1)], weight="capacity")
|
||||
for flow_func in interface_funcs:
|
||||
for element in elements:
|
||||
pytest.raises(nx.NetworkXError, flow_func, G, 0, 1, flow_func=element)
|
||||
pytest.raises(nx.NetworkXError, flow_func, G, 0, 1, flow_func=element)
|
||||
|
||||
def test_flow_func_parameters(self):
|
||||
G = self.G
|
||||
fv = 3.0
|
||||
for interface_func in interface_funcs:
|
||||
for flow_func in flow_funcs:
|
||||
errmsg = (
|
||||
f"Assertion failed in function: {flow_func.__name__} "
|
||||
f"in interface {interface_func.__name__}"
|
||||
)
|
||||
result = interface_func(G, "x", "y", flow_func=flow_func)
|
||||
if interface_func in max_min_funcs:
|
||||
result = result[0]
|
||||
assert fv == result, errmsg
|
||||
|
||||
def test_minimum_cut_no_cutoff(self):
|
||||
G = self.G
|
||||
for flow_func in flow_funcs:
|
||||
pytest.raises(
|
||||
nx.NetworkXError,
|
||||
nx.minimum_cut,
|
||||
G,
|
||||
"x",
|
||||
"y",
|
||||
flow_func=flow_func,
|
||||
cutoff=1.0,
|
||||
)
|
||||
pytest.raises(
|
||||
nx.NetworkXError,
|
||||
nx.minimum_cut_value,
|
||||
G,
|
||||
"x",
|
||||
"y",
|
||||
flow_func=flow_func,
|
||||
cutoff=1.0,
|
||||
)
|
||||
|
||||
def test_kwargs(self):
|
||||
G = self.H
|
||||
fv = 1.0
|
||||
to_test = (
|
||||
(shortest_augmenting_path, dict(two_phase=True)),
|
||||
(preflow_push, dict(global_relabel_freq=5)),
|
||||
)
|
||||
for interface_func in interface_funcs:
|
||||
for flow_func, kwargs in to_test:
|
||||
errmsg = (
|
||||
f"Assertion failed in function: {flow_func.__name__} "
|
||||
f"in interface {interface_func.__name__}"
|
||||
)
|
||||
result = interface_func(G, 0, 2, flow_func=flow_func, **kwargs)
|
||||
if interface_func in max_min_funcs:
|
||||
result = result[0]
|
||||
assert fv == result, errmsg
|
||||
|
||||
def test_kwargs_default_flow_func(self):
|
||||
G = self.H
|
||||
for interface_func in interface_funcs:
|
||||
pytest.raises(
|
||||
nx.NetworkXError, interface_func, G, 0, 1, global_relabel_freq=2
|
||||
)
|
||||
|
||||
def test_reusing_residual(self):
|
||||
G = self.G
|
||||
fv = 3.0
|
||||
s, t = "x", "y"
|
||||
R = build_residual_network(G, "capacity")
|
||||
for interface_func in interface_funcs:
|
||||
for flow_func in flow_funcs:
|
||||
errmsg = (
|
||||
f"Assertion failed in function: {flow_func.__name__} "
|
||||
f"in interface {interface_func.__name__}"
|
||||
)
|
||||
for i in range(3):
|
||||
result = interface_func(
|
||||
G, "x", "y", flow_func=flow_func, residual=R
|
||||
)
|
||||
if interface_func in max_min_funcs:
|
||||
result = result[0]
|
||||
assert fv == result, errmsg
|
||||
|
||||
|
||||
# Tests specific to one algorithm
|
||||
def test_preflow_push_global_relabel_freq():
|
||||
G = nx.DiGraph()
|
||||
G.add_edge(1, 2, capacity=1)
|
||||
R = preflow_push(G, 1, 2, global_relabel_freq=None)
|
||||
assert R.graph["flow_value"] == 1
|
||||
pytest.raises(nx.NetworkXError, preflow_push, G, 1, 2, global_relabel_freq=-1)
|
||||
|
||||
|
||||
def test_preflow_push_makes_enough_space():
|
||||
# From ticket #1542
|
||||
G = nx.DiGraph()
|
||||
nx.add_path(G, [0, 1, 3], capacity=1)
|
||||
nx.add_path(G, [1, 2, 3], capacity=1)
|
||||
R = preflow_push(G, 0, 3, value_only=False)
|
||||
assert R.graph["flow_value"] == 1
|
||||
|
||||
|
||||
def test_shortest_augmenting_path_two_phase():
|
||||
k = 5
|
||||
p = 1000
|
||||
G = nx.DiGraph()
|
||||
for i in range(k):
|
||||
G.add_edge("s", (i, 0), capacity=1)
|
||||
nx.add_path(G, ((i, j) for j in range(p)), capacity=1)
|
||||
G.add_edge((i, p - 1), "t", capacity=1)
|
||||
R = shortest_augmenting_path(G, "s", "t", two_phase=True)
|
||||
assert R.graph["flow_value"] == k
|
||||
R = shortest_augmenting_path(G, "s", "t", two_phase=False)
|
||||
assert R.graph["flow_value"] == k
|
||||
|
||||
|
||||
class TestCutoff:
|
||||
def test_cutoff(self):
|
||||
k = 5
|
||||
p = 1000
|
||||
G = nx.DiGraph()
|
||||
for i in range(k):
|
||||
G.add_edge("s", (i, 0), capacity=2)
|
||||
nx.add_path(G, ((i, j) for j in range(p)), capacity=2)
|
||||
G.add_edge((i, p - 1), "t", capacity=2)
|
||||
R = shortest_augmenting_path(G, "s", "t", two_phase=True, cutoff=k)
|
||||
assert k <= R.graph["flow_value"] <= (2 * k)
|
||||
R = shortest_augmenting_path(G, "s", "t", two_phase=False, cutoff=k)
|
||||
assert k <= R.graph["flow_value"] <= (2 * k)
|
||||
R = edmonds_karp(G, "s", "t", cutoff=k)
|
||||
assert k <= R.graph["flow_value"] <= (2 * k)
|
||||
|
||||
def test_complete_graph_cutoff(self):
|
||||
G = nx.complete_graph(5)
|
||||
nx.set_edge_attributes(G, {(u, v): 1 for u, v in G.edges()}, "capacity")
|
||||
for flow_func in [shortest_augmenting_path, edmonds_karp]:
|
||||
for cutoff in [3, 2, 1]:
|
||||
result = nx.maximum_flow_value(
|
||||
G, 0, 4, flow_func=flow_func, cutoff=cutoff
|
||||
)
|
||||
assert cutoff == result, f"cutoff error in {flow_func.__name__}"
|
|
@ -0,0 +1,146 @@
|
|||
"""Maximum flow algorithms test suite on large graphs.
|
||||
"""
|
||||
|
||||
import os
|
||||
import pytest
|
||||
|
||||
import networkx as nx
|
||||
from networkx.algorithms.flow import build_flow_dict, build_residual_network
|
||||
from networkx.algorithms.flow import boykov_kolmogorov
|
||||
from networkx.algorithms.flow import dinitz
|
||||
from networkx.algorithms.flow import edmonds_karp
|
||||
from networkx.algorithms.flow import preflow_push
|
||||
from networkx.algorithms.flow import shortest_augmenting_path
|
||||
from networkx.testing import almost_equal
|
||||
|
||||
flow_funcs = [
|
||||
boykov_kolmogorov,
|
||||
dinitz,
|
||||
edmonds_karp,
|
||||
preflow_push,
|
||||
shortest_augmenting_path,
|
||||
]
|
||||
|
||||
|
||||
def gen_pyramid(N):
|
||||
# This graph admits a flow of value 1 for which every arc is at
|
||||
# capacity (except the arcs incident to the sink which have
|
||||
# infinite capacity).
|
||||
G = nx.DiGraph()
|
||||
|
||||
for i in range(N - 1):
|
||||
cap = 1.0 / (i + 2)
|
||||
for j in range(i + 1):
|
||||
G.add_edge((i, j), (i + 1, j), capacity=cap)
|
||||
cap = 1.0 / (i + 1) - cap
|
||||
G.add_edge((i, j), (i + 1, j + 1), capacity=cap)
|
||||
cap = 1.0 / (i + 2) - cap
|
||||
|
||||
for j in range(N):
|
||||
G.add_edge((N - 1, j), "t")
|
||||
|
||||
return G
|
||||
|
||||
|
||||
def read_graph(name):
|
||||
dirname = os.path.dirname(__file__)
|
||||
path = os.path.join(dirname, name + ".gpickle.bz2")
|
||||
return nx.read_gpickle(path)
|
||||
|
||||
|
||||
def validate_flows(G, s, t, soln_value, R, flow_func):
|
||||
flow_value = R.graph["flow_value"]
|
||||
flow_dict = build_flow_dict(G, R)
|
||||
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
||||
assert soln_value == flow_value, errmsg
|
||||
assert set(G) == set(flow_dict), errmsg
|
||||
for u in G:
|
||||
assert set(G[u]) == set(flow_dict[u]), errmsg
|
||||
excess = {u: 0 for u in flow_dict}
|
||||
for u in flow_dict:
|
||||
for v, flow in flow_dict[u].items():
|
||||
assert flow <= G[u][v].get("capacity", float("inf")), errmsg
|
||||
assert flow >= 0, errmsg
|
||||
excess[u] -= flow
|
||||
excess[v] += flow
|
||||
for u, exc in excess.items():
|
||||
if u == s:
|
||||
assert exc == -soln_value, errmsg
|
||||
elif u == t:
|
||||
assert exc == soln_value, errmsg
|
||||
else:
|
||||
assert exc == 0, errmsg
|
||||
|
||||
|
||||
class TestMaxflowLargeGraph:
|
||||
def test_complete_graph(self):
|
||||
N = 50
|
||||
G = nx.complete_graph(N)
|
||||
nx.set_edge_attributes(G, 5, "capacity")
|
||||
R = build_residual_network(G, "capacity")
|
||||
kwargs = dict(residual=R)
|
||||
|
||||
for flow_func in flow_funcs:
|
||||
kwargs["flow_func"] = flow_func
|
||||
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
||||
flow_value = nx.maximum_flow_value(G, 1, 2, **kwargs)
|
||||
assert flow_value == 5 * (N - 1), errmsg
|
||||
|
||||
def test_pyramid(self):
|
||||
N = 10
|
||||
# N = 100 # this gives a graph with 5051 nodes
|
||||
G = gen_pyramid(N)
|
||||
R = build_residual_network(G, "capacity")
|
||||
kwargs = dict(residual=R)
|
||||
|
||||
for flow_func in flow_funcs:
|
||||
kwargs["flow_func"] = flow_func
|
||||
errmsg = f"Assertion failed in function: {flow_func.__name__}"
|
||||
flow_value = nx.maximum_flow_value(G, (0, 0), "t", **kwargs)
|
||||
assert almost_equal(flow_value, 1.0), errmsg
|
||||
|
||||
def test_gl1(self):
|
||||
G = read_graph("gl1")
|
||||
s = 1
|
||||
t = len(G)
|
||||
R = build_residual_network(G, "capacity")
|
||||
kwargs = dict(residual=R)
|
||||
|
||||
# do one flow_func to save time
|
||||
flow_func = flow_funcs[0]
|
||||
validate_flows(G, s, t, 156545, flow_func(G, s, t, **kwargs), flow_func)
|
||||
|
||||
# for flow_func in flow_funcs:
|
||||
# validate_flows(G, s, t, 156545, flow_func(G, s, t, **kwargs),
|
||||
# flow_func)
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_gw1(self):
|
||||
G = read_graph("gw1")
|
||||
s = 1
|
||||
t = len(G)
|
||||
R = build_residual_network(G, "capacity")
|
||||
kwargs = dict(residual=R)
|
||||
|
||||
for flow_func in flow_funcs:
|
||||
validate_flows(G, s, t, 1202018, flow_func(G, s, t, **kwargs), flow_func)
|
||||
|
||||
def test_wlm3(self):
|
||||
G = read_graph("wlm3")
|
||||
s = 1
|
||||
t = len(G)
|
||||
R = build_residual_network(G, "capacity")
|
||||
kwargs = dict(residual=R)
|
||||
|
||||
# do one flow_func to save time
|
||||
flow_func = flow_funcs[0]
|
||||
validate_flows(G, s, t, 11875108, flow_func(G, s, t, **kwargs), flow_func)
|
||||
|
||||
# for flow_func in flow_funcs:
|
||||
# validate_flows(G, s, t, 11875108, flow_func(G, s, t, **kwargs),
|
||||
# flow_func)
|
||||
|
||||
def test_preflow_push_global_relabel(self):
|
||||
G = read_graph("gw1")
|
||||
R = preflow_push(G, 1, len(G), global_relabel_freq=50)
|
||||
assert R.graph["flow_value"] == 1202018
|
|
@ -0,0 +1,467 @@
|
|||
import networkx as nx
|
||||
import pytest
|
||||
import os
|
||||
|
||||
|
||||
class TestMinCostFlow:
|
||||
def test_simple_digraph(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_node("a", demand=-5)
|
||||
G.add_node("d", demand=5)
|
||||
G.add_edge("a", "b", weight=3, capacity=4)
|
||||
G.add_edge("a", "c", weight=6, capacity=10)
|
||||
G.add_edge("b", "d", weight=1, capacity=9)
|
||||
G.add_edge("c", "d", weight=2, capacity=5)
|
||||
flowCost, H = nx.network_simplex(G)
|
||||
soln = {"a": {"b": 4, "c": 1}, "b": {"d": 4}, "c": {"d": 1}, "d": {}}
|
||||
assert flowCost == 24
|
||||
assert nx.min_cost_flow_cost(G) == 24
|
||||
assert H == soln
|
||||
assert nx.min_cost_flow(G) == soln
|
||||
assert nx.cost_of_flow(G, H) == 24
|
||||
|
||||
flowCost, H = nx.capacity_scaling(G)
|
||||
assert flowCost == 24
|
||||
assert nx.cost_of_flow(G, H) == 24
|
||||
assert H == soln
|
||||
|
||||
def test_negcycle_infcap(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_node("s", demand=-5)
|
||||
G.add_node("t", demand=5)
|
||||
G.add_edge("s", "a", weight=1, capacity=3)
|
||||
G.add_edge("a", "b", weight=3)
|
||||
G.add_edge("c", "a", weight=-6)
|
||||
G.add_edge("b", "d", weight=1)
|
||||
G.add_edge("d", "c", weight=-2)
|
||||
G.add_edge("d", "t", weight=1, capacity=3)
|
||||
pytest.raises(nx.NetworkXUnfeasible, nx.network_simplex, G)
|
||||
pytest.raises(nx.NetworkXUnbounded, nx.capacity_scaling, G)
|
||||
|
||||
def test_sum_demands_not_zero(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_node("s", demand=-5)
|
||||
G.add_node("t", demand=4)
|
||||
G.add_edge("s", "a", weight=1, capacity=3)
|
||||
G.add_edge("a", "b", weight=3)
|
||||
G.add_edge("a", "c", weight=-6)
|
||||
G.add_edge("b", "d", weight=1)
|
||||
G.add_edge("c", "d", weight=-2)
|
||||
G.add_edge("d", "t", weight=1, capacity=3)
|
||||
pytest.raises(nx.NetworkXUnfeasible, nx.network_simplex, G)
|
||||
pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
|
||||
|
||||
def test_no_flow_satisfying_demands(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_node("s", demand=-5)
|
||||
G.add_node("t", demand=5)
|
||||
G.add_edge("s", "a", weight=1, capacity=3)
|
||||
G.add_edge("a", "b", weight=3)
|
||||
G.add_edge("a", "c", weight=-6)
|
||||
G.add_edge("b", "d", weight=1)
|
||||
G.add_edge("c", "d", weight=-2)
|
||||
G.add_edge("d", "t", weight=1, capacity=3)
|
||||
pytest.raises(nx.NetworkXUnfeasible, nx.network_simplex, G)
|
||||
pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
|
||||
|
||||
def test_transshipment(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_node("a", demand=1)
|
||||
G.add_node("b", demand=-2)
|
||||
G.add_node("c", demand=-2)
|
||||
G.add_node("d", demand=3)
|
||||
G.add_node("e", demand=-4)
|
||||
G.add_node("f", demand=-4)
|
||||
G.add_node("g", demand=3)
|
||||
G.add_node("h", demand=2)
|
||||
G.add_node("r", demand=3)
|
||||
G.add_edge("a", "c", weight=3)
|
||||
G.add_edge("r", "a", weight=2)
|
||||
G.add_edge("b", "a", weight=9)
|
||||
G.add_edge("r", "c", weight=0)
|
||||
G.add_edge("b", "r", weight=-6)
|
||||
G.add_edge("c", "d", weight=5)
|
||||
G.add_edge("e", "r", weight=4)
|
||||
G.add_edge("e", "f", weight=3)
|
||||
G.add_edge("h", "b", weight=4)
|
||||
G.add_edge("f", "d", weight=7)
|
||||
G.add_edge("f", "h", weight=12)
|
||||
G.add_edge("g", "d", weight=12)
|
||||
G.add_edge("f", "g", weight=-1)
|
||||
G.add_edge("h", "g", weight=-10)
|
||||
flowCost, H = nx.network_simplex(G)
|
||||
soln = {
|
||||
"a": {"c": 0},
|
||||
"b": {"a": 0, "r": 2},
|
||||
"c": {"d": 3},
|
||||
"d": {},
|
||||
"e": {"r": 3, "f": 1},
|
||||
"f": {"d": 0, "g": 3, "h": 2},
|
||||
"g": {"d": 0},
|
||||
"h": {"b": 0, "g": 0},
|
||||
"r": {"a": 1, "c": 1},
|
||||
}
|
||||
assert flowCost == 41
|
||||
assert nx.min_cost_flow_cost(G) == 41
|
||||
assert H == soln
|
||||
assert nx.min_cost_flow(G) == soln
|
||||
assert nx.cost_of_flow(G, H) == 41
|
||||
|
||||
flowCost, H = nx.capacity_scaling(G)
|
||||
assert flowCost == 41
|
||||
assert nx.cost_of_flow(G, H) == 41
|
||||
assert H == soln
|
||||
|
||||
def test_max_flow_min_cost(self):
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("s", "a", bandwidth=6)
|
||||
G.add_edge("s", "c", bandwidth=10, cost=10)
|
||||
G.add_edge("a", "b", cost=6)
|
||||
G.add_edge("b", "d", bandwidth=8, cost=7)
|
||||
G.add_edge("c", "d", cost=10)
|
||||
G.add_edge("d", "t", bandwidth=5, cost=5)
|
||||
soln = {
|
||||
"s": {"a": 5, "c": 0},
|
||||
"a": {"b": 5},
|
||||
"b": {"d": 5},
|
||||
"c": {"d": 0},
|
||||
"d": {"t": 5},
|
||||
"t": {},
|
||||
}
|
||||
flow = nx.max_flow_min_cost(G, "s", "t", capacity="bandwidth", weight="cost")
|
||||
assert flow == soln
|
||||
assert nx.cost_of_flow(G, flow, weight="cost") == 90
|
||||
|
||||
G.add_edge("t", "s", cost=-100)
|
||||
flowCost, flow = nx.capacity_scaling(G, capacity="bandwidth", weight="cost")
|
||||
G.remove_edge("t", "s")
|
||||
assert flowCost == -410
|
||||
assert flow["t"]["s"] == 5
|
||||
del flow["t"]["s"]
|
||||
assert flow == soln
|
||||
assert nx.cost_of_flow(G, flow, weight="cost") == 90
|
||||
|
||||
def test_digraph1(self):
|
||||
# From Bradley, S. P., Hax, A. C. and Magnanti, T. L. Applied
|
||||
# Mathematical Programming. Addison-Wesley, 1977.
|
||||
G = nx.DiGraph()
|
||||
G.add_node(1, demand=-20)
|
||||
G.add_node(4, demand=5)
|
||||
G.add_node(5, demand=15)
|
||||
G.add_edges_from(
|
||||
[
|
||||
(1, 2, {"capacity": 15, "weight": 4}),
|
||||
(1, 3, {"capacity": 8, "weight": 4}),
|
||||
(2, 3, {"weight": 2}),
|
||||
(2, 4, {"capacity": 4, "weight": 2}),
|
||||
(2, 5, {"capacity": 10, "weight": 6}),
|
||||
(3, 4, {"capacity": 15, "weight": 1}),
|
||||
(3, 5, {"capacity": 5, "weight": 3}),
|
||||
(4, 5, {"weight": 2}),
|
||||
(5, 3, {"capacity": 4, "weight": 1}),
|
||||
]
|
||||
)
|
||||
flowCost, H = nx.network_simplex(G)
|
||||
soln = {
|
||||
1: {2: 12, 3: 8},
|
||||
2: {3: 8, 4: 4, 5: 0},
|
||||
3: {4: 11, 5: 5},
|
||||
4: {5: 10},
|
||||
5: {3: 0},
|
||||
}
|
||||
assert flowCost == 150
|
||||
assert nx.min_cost_flow_cost(G) == 150
|
||||
assert H == soln
|
||||
assert nx.min_cost_flow(G) == soln
|
||||
assert nx.cost_of_flow(G, H) == 150
|
||||
|
||||
flowCost, H = nx.capacity_scaling(G)
|
||||
assert flowCost == 150
|
||||
assert H == soln
|
||||
assert nx.cost_of_flow(G, H) == 150
|
||||
|
||||
def test_digraph2(self):
|
||||
# Example from ticket #430 from mfrasca. Original source:
|
||||
# http://www.cs.princeton.edu/courses/archive/spr03/cs226/lectures/mincost.4up.pdf, slide 11.
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("s", 1, capacity=12)
|
||||
G.add_edge("s", 2, capacity=6)
|
||||
G.add_edge("s", 3, capacity=14)
|
||||
G.add_edge(1, 2, capacity=11, weight=4)
|
||||
G.add_edge(2, 3, capacity=9, weight=6)
|
||||
G.add_edge(1, 4, capacity=5, weight=5)
|
||||
G.add_edge(1, 5, capacity=2, weight=12)
|
||||
G.add_edge(2, 5, capacity=4, weight=4)
|
||||
G.add_edge(2, 6, capacity=2, weight=6)
|
||||
G.add_edge(3, 6, capacity=31, weight=3)
|
||||
G.add_edge(4, 5, capacity=18, weight=4)
|
||||
G.add_edge(5, 6, capacity=9, weight=5)
|
||||
G.add_edge(4, "t", capacity=3)
|
||||
G.add_edge(5, "t", capacity=7)
|
||||
G.add_edge(6, "t", capacity=22)
|
||||
flow = nx.max_flow_min_cost(G, "s", "t")
|
||||
soln = {
|
||||
1: {2: 6, 4: 5, 5: 1},
|
||||
2: {3: 6, 5: 4, 6: 2},
|
||||
3: {6: 20},
|
||||
4: {5: 2, "t": 3},
|
||||
5: {6: 0, "t": 7},
|
||||
6: {"t": 22},
|
||||
"s": {1: 12, 2: 6, 3: 14},
|
||||
"t": {},
|
||||
}
|
||||
assert flow == soln
|
||||
|
||||
G.add_edge("t", "s", weight=-100)
|
||||
flowCost, flow = nx.capacity_scaling(G)
|
||||
G.remove_edge("t", "s")
|
||||
assert flow["t"]["s"] == 32
|
||||
assert flowCost == -3007
|
||||
del flow["t"]["s"]
|
||||
assert flow == soln
|
||||
assert nx.cost_of_flow(G, flow) == 193
|
||||
|
||||
def test_digraph3(self):
|
||||
"""Combinatorial Optimization: Algorithms and Complexity,
|
||||
Papadimitriou Steiglitz at page 140 has an example, 7.1, but that
|
||||
admits multiple solutions, so I alter it a bit. From ticket #430
|
||||
by mfrasca."""
|
||||
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("s", "a")
|
||||
G["s"]["a"].update({0: 2, 1: 4})
|
||||
G.add_edge("s", "b")
|
||||
G["s"]["b"].update({0: 2, 1: 1})
|
||||
G.add_edge("a", "b")
|
||||
G["a"]["b"].update({0: 5, 1: 2})
|
||||
G.add_edge("a", "t")
|
||||
G["a"]["t"].update({0: 1, 1: 5})
|
||||
G.add_edge("b", "a")
|
||||
G["b"]["a"].update({0: 1, 1: 3})
|
||||
G.add_edge("b", "t")
|
||||
G["b"]["t"].update({0: 3, 1: 2})
|
||||
|
||||
"PS.ex.7.1: testing main function"
|
||||
sol = nx.max_flow_min_cost(G, "s", "t", capacity=0, weight=1)
|
||||
flow = sum(v for v in sol["s"].values())
|
||||
assert 4 == flow
|
||||
assert 23 == nx.cost_of_flow(G, sol, weight=1)
|
||||
assert sol["s"] == {"a": 2, "b": 2}
|
||||
assert sol["a"] == {"b": 1, "t": 1}
|
||||
assert sol["b"] == {"a": 0, "t": 3}
|
||||
assert sol["t"] == {}
|
||||
|
||||
G.add_edge("t", "s")
|
||||
G["t"]["s"].update({1: -100})
|
||||
flowCost, sol = nx.capacity_scaling(G, capacity=0, weight=1)
|
||||
G.remove_edge("t", "s")
|
||||
flow = sum(v for v in sol["s"].values())
|
||||
assert 4 == flow
|
||||
assert sol["t"]["s"] == 4
|
||||
assert flowCost == -377
|
||||
del sol["t"]["s"]
|
||||
assert sol["s"] == {"a": 2, "b": 2}
|
||||
assert sol["a"] == {"b": 1, "t": 1}
|
||||
assert sol["b"] == {"a": 0, "t": 3}
|
||||
assert sol["t"] == {}
|
||||
assert nx.cost_of_flow(G, sol, weight=1) == 23
|
||||
|
||||
def test_zero_capacity_edges(self):
|
||||
"""Address issue raised in ticket #617 by arv."""
|
||||
G = nx.DiGraph()
|
||||
G.add_edges_from(
|
||||
[
|
||||
(1, 2, {"capacity": 1, "weight": 1}),
|
||||
(1, 5, {"capacity": 1, "weight": 1}),
|
||||
(2, 3, {"capacity": 0, "weight": 1}),
|
||||
(2, 5, {"capacity": 1, "weight": 1}),
|
||||
(5, 3, {"capacity": 2, "weight": 1}),
|
||||
(5, 4, {"capacity": 0, "weight": 1}),
|
||||
(3, 4, {"capacity": 2, "weight": 1}),
|
||||
]
|
||||
)
|
||||
G.nodes[1]["demand"] = -1
|
||||
G.nodes[2]["demand"] = -1
|
||||
G.nodes[4]["demand"] = 2
|
||||
|
||||
flowCost, H = nx.network_simplex(G)
|
||||
soln = {1: {2: 0, 5: 1}, 2: {3: 0, 5: 1}, 3: {4: 2}, 4: {}, 5: {3: 2, 4: 0}}
|
||||
assert flowCost == 6
|
||||
assert nx.min_cost_flow_cost(G) == 6
|
||||
assert H == soln
|
||||
assert nx.min_cost_flow(G) == soln
|
||||
assert nx.cost_of_flow(G, H) == 6
|
||||
|
||||
flowCost, H = nx.capacity_scaling(G)
|
||||
assert flowCost == 6
|
||||
assert H == soln
|
||||
assert nx.cost_of_flow(G, H) == 6
|
||||
|
||||
def test_digon(self):
|
||||
"""Check if digons are handled properly. Taken from ticket
|
||||
#618 by arv."""
|
||||
nodes = [(1, {}), (2, {"demand": -4}), (3, {"demand": 4})]
|
||||
edges = [
|
||||
(1, 2, {"capacity": 3, "weight": 600000}),
|
||||
(2, 1, {"capacity": 2, "weight": 0}),
|
||||
(2, 3, {"capacity": 5, "weight": 714285}),
|
||||
(3, 2, {"capacity": 2, "weight": 0}),
|
||||
]
|
||||
G = nx.DiGraph(edges)
|
||||
G.add_nodes_from(nodes)
|
||||
flowCost, H = nx.network_simplex(G)
|
||||
soln = {1: {2: 0}, 2: {1: 0, 3: 4}, 3: {2: 0}}
|
||||
assert flowCost == 2857140
|
||||
assert nx.min_cost_flow_cost(G) == 2857140
|
||||
assert H == soln
|
||||
assert nx.min_cost_flow(G) == soln
|
||||
assert nx.cost_of_flow(G, H) == 2857140
|
||||
|
||||
flowCost, H = nx.capacity_scaling(G)
|
||||
assert flowCost == 2857140
|
||||
assert H == soln
|
||||
assert nx.cost_of_flow(G, H) == 2857140
|
||||
|
||||
def test_deadend(self):
|
||||
"""Check if one-node cycles are handled properly. Taken from ticket
|
||||
#2906 from @sshraven."""
|
||||
G = nx.DiGraph()
|
||||
|
||||
G.add_nodes_from(range(5), demand=0)
|
||||
G.nodes[4]["demand"] = -13
|
||||
G.nodes[3]["demand"] = 13
|
||||
|
||||
G.add_edges_from([(0, 2), (0, 3), (2, 1)], capacity=20, weight=0.1)
|
||||
pytest.raises(nx.NetworkXUnfeasible, nx.min_cost_flow, G)
|
||||
|
||||
def test_infinite_capacity_neg_digon(self):
|
||||
"""An infinite capacity negative cost digon results in an unbounded
|
||||
instance."""
|
||||
nodes = [(1, {}), (2, {"demand": -4}), (3, {"demand": 4})]
|
||||
edges = [
|
||||
(1, 2, {"weight": -600}),
|
||||
(2, 1, {"weight": 0}),
|
||||
(2, 3, {"capacity": 5, "weight": 714285}),
|
||||
(3, 2, {"capacity": 2, "weight": 0}),
|
||||
]
|
||||
G = nx.DiGraph(edges)
|
||||
G.add_nodes_from(nodes)
|
||||
pytest.raises(nx.NetworkXUnbounded, nx.network_simplex, G)
|
||||
pytest.raises(nx.NetworkXUnbounded, nx.capacity_scaling, G)
|
||||
|
||||
def test_finite_capacity_neg_digon(self):
|
||||
"""The digon should receive the maximum amount of flow it can handle.
|
||||
Taken from ticket #749 by @chuongdo."""
|
||||
G = nx.DiGraph()
|
||||
G.add_edge("a", "b", capacity=1, weight=-1)
|
||||
G.add_edge("b", "a", capacity=1, weight=-1)
|
||||
min_cost = -2
|
||||
assert nx.min_cost_flow_cost(G) == min_cost
|
||||
|
||||
flowCost, H = nx.capacity_scaling(G)
|
||||
assert flowCost == -2
|
||||
assert H == {"a": {"b": 1}, "b": {"a": 1}}
|
||||
assert nx.cost_of_flow(G, H) == -2
|
||||
|
||||
def test_multidigraph(self):
|
||||
"""Multidigraphs are acceptable."""
|
||||
G = nx.MultiDiGraph()
|
||||
G.add_weighted_edges_from([(1, 2, 1), (2, 3, 2)], weight="capacity")
|
||||
flowCost, H = nx.network_simplex(G)
|
||||
assert flowCost == 0
|
||||
assert H == {1: {2: {0: 0}}, 2: {3: {0: 0}}, 3: {}}
|
||||
|
||||
flowCost, H = nx.capacity_scaling(G)
|
||||
assert flowCost == 0
|
||||
assert H == {1: {2: {0: 0}}, 2: {3: {0: 0}}, 3: {}}
|
||||
|
||||
def test_negative_selfloops(self):
|
||||
"""Negative selfloops should cause an exception if uncapacitated and
|
||||
always be saturated otherwise.
|
||||
"""
|
||||
G = nx.DiGraph()
|
||||
G.add_edge(1, 1, weight=-1)
|
||||
pytest.raises(nx.NetworkXUnbounded, nx.network_simplex, G)
|
||||
pytest.raises(nx.NetworkXUnbounded, nx.capacity_scaling, G)
|
||||
G[1][1]["capacity"] = 2
|
||||
flowCost, H = nx.network_simplex(G)
|
||||
assert flowCost == -2
|
||||
assert H == {1: {1: 2}}
|
||||
flowCost, H = nx.capacity_scaling(G)
|
||||
assert flowCost == -2
|
||||
assert H == {1: {1: 2}}
|
||||
|
||||
G = nx.MultiDiGraph()
|
||||
G.add_edge(1, 1, "x", weight=-1)
|
||||
G.add_edge(1, 1, "y", weight=1)
|
||||
pytest.raises(nx.NetworkXUnbounded, nx.network_simplex, G)
|
||||
pytest.raises(nx.NetworkXUnbounded, nx.capacity_scaling, G)
|
||||
G[1][1]["x"]["capacity"] = 2
|
||||
flowCost, H = nx.network_simplex(G)
|
||||
assert flowCost == -2
|
||||
assert H == {1: {1: {"x": 2, "y": 0}}}
|
||||
flowCost, H = nx.capacity_scaling(G)
|
||||
assert flowCost == -2
|
||||
assert H == {1: {1: {"x": 2, "y": 0}}}
|
||||
|
||||
def test_bone_shaped(self):
|
||||
# From #1283
|
||||
G = nx.DiGraph()
|
||||
G.add_node(0, demand=-4)
|
||||
G.add_node(1, demand=2)
|
||||
G.add_node(2, demand=2)
|
||||
G.add_node(3, demand=4)
|
||||
G.add_node(4, demand=-2)
|
||||
G.add_node(5, demand=-2)
|
||||
G.add_edge(0, 1, capacity=4)
|
||||
G.add_edge(0, 2, capacity=4)
|
||||
G.add_edge(4, 3, capacity=4)
|
||||
G.add_edge(5, 3, capacity=4)
|
||||
G.add_edge(0, 3, capacity=0)
|
||||
flowCost, H = nx.network_simplex(G)
|
||||
assert flowCost == 0
|
||||
assert H == {0: {1: 2, 2: 2, 3: 0}, 1: {}, 2: {}, 3: {}, 4: {3: 2}, 5: {3: 2}}
|
||||
flowCost, H = nx.capacity_scaling(G)
|
||||
assert flowCost == 0
|
||||
assert H == {0: {1: 2, 2: 2, 3: 0}, 1: {}, 2: {}, 3: {}, 4: {3: 2}, 5: {3: 2}}
|
||||
|
||||
def test_exceptions(self):
|
||||
G = nx.Graph()
|
||||
pytest.raises(nx.NetworkXNotImplemented, nx.network_simplex, G)
|
||||
pytest.raises(nx.NetworkXNotImplemented, nx.capacity_scaling, G)
|
||||
G = nx.MultiGraph()
|
||||
pytest.raises(nx.NetworkXNotImplemented, nx.network_simplex, G)
|
||||
pytest.raises(nx.NetworkXNotImplemented, nx.capacity_scaling, G)
|
||||
G = nx.DiGraph()
|
||||
pytest.raises(nx.NetworkXError, nx.network_simplex, G)
|
||||
pytest.raises(nx.NetworkXError, nx.capacity_scaling, G)
|
||||
G.add_node(0, demand=float("inf"))
|
||||
pytest.raises(nx.NetworkXError, nx.network_simplex, G)
|
||||
pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
|
||||
G.nodes[0]["demand"] = 0
|
||||
G.add_node(1, demand=0)
|
||||
G.add_edge(0, 1, weight=-float("inf"))
|
||||
pytest.raises(nx.NetworkXError, nx.network_simplex, G)
|
||||
pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
|
||||
G[0][1]["weight"] = 0
|
||||
G.add_edge(0, 0, weight=float("inf"))
|
||||
pytest.raises(nx.NetworkXError, nx.network_simplex, G)
|
||||
# pytest.raises(nx.NetworkXError, nx.capacity_scaling, G)
|
||||
G[0][0]["weight"] = 0
|
||||
G[0][1]["capacity"] = -1
|
||||
pytest.raises(nx.NetworkXUnfeasible, nx.network_simplex, G)
|
||||
# pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
|
||||
G[0][1]["capacity"] = 0
|
||||
G[0][0]["capacity"] = -1
|
||||
pytest.raises(nx.NetworkXUnfeasible, nx.network_simplex, G)
|
||||
# pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
|
||||
|
||||
def test_large(self):
|
||||
fname = os.path.join(os.path.dirname(__file__), "netgen-2.gpickle.bz2")
|
||||
G = nx.read_gpickle(fname)
|
||||
flowCost, flowDict = nx.network_simplex(G)
|
||||
assert 6749969302 == flowCost
|
||||
assert 6749969302 == nx.cost_of_flow(G, flowDict)
|
||||
flowCost, flowDict = nx.capacity_scaling(G)
|
||||
assert 6749969302 == flowCost
|
||||
assert 6749969302 == nx.cost_of_flow(G, flowDict)
|
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue