Fixed database typo and removed unnecessary class identifier.

This commit is contained in:
Batuhan Berk Başoğlu 2020-10-14 10:10:37 -04:00
parent 00ad49a143
commit 45fb349a7d
5098 changed files with 952558 additions and 85 deletions

View file

@ -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)

View file

@ -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__}"

View file

@ -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

View file

@ -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)