test_mincost.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. import bz2
  2. import os
  3. import pickle
  4. import pytest
  5. import networkx as nx
  6. class TestMinCostFlow:
  7. def test_simple_digraph(self):
  8. G = nx.DiGraph()
  9. G.add_node("a", demand=-5)
  10. G.add_node("d", demand=5)
  11. G.add_edge("a", "b", weight=3, capacity=4)
  12. G.add_edge("a", "c", weight=6, capacity=10)
  13. G.add_edge("b", "d", weight=1, capacity=9)
  14. G.add_edge("c", "d", weight=2, capacity=5)
  15. flowCost, H = nx.network_simplex(G)
  16. soln = {"a": {"b": 4, "c": 1}, "b": {"d": 4}, "c": {"d": 1}, "d": {}}
  17. assert flowCost == 24
  18. assert nx.min_cost_flow_cost(G) == 24
  19. assert H == soln
  20. assert nx.min_cost_flow(G) == soln
  21. assert nx.cost_of_flow(G, H) == 24
  22. flowCost, H = nx.capacity_scaling(G)
  23. assert flowCost == 24
  24. assert nx.cost_of_flow(G, H) == 24
  25. assert H == soln
  26. def test_negcycle_infcap(self):
  27. G = nx.DiGraph()
  28. G.add_node("s", demand=-5)
  29. G.add_node("t", demand=5)
  30. G.add_edge("s", "a", weight=1, capacity=3)
  31. G.add_edge("a", "b", weight=3)
  32. G.add_edge("c", "a", weight=-6)
  33. G.add_edge("b", "d", weight=1)
  34. G.add_edge("d", "c", weight=-2)
  35. G.add_edge("d", "t", weight=1, capacity=3)
  36. pytest.raises(nx.NetworkXUnfeasible, nx.network_simplex, G)
  37. pytest.raises(nx.NetworkXUnbounded, nx.capacity_scaling, G)
  38. def test_sum_demands_not_zero(self):
  39. G = nx.DiGraph()
  40. G.add_node("s", demand=-5)
  41. G.add_node("t", demand=4)
  42. G.add_edge("s", "a", weight=1, capacity=3)
  43. G.add_edge("a", "b", weight=3)
  44. G.add_edge("a", "c", weight=-6)
  45. G.add_edge("b", "d", weight=1)
  46. G.add_edge("c", "d", weight=-2)
  47. G.add_edge("d", "t", weight=1, capacity=3)
  48. pytest.raises(nx.NetworkXUnfeasible, nx.network_simplex, G)
  49. pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
  50. def test_no_flow_satisfying_demands(self):
  51. G = nx.DiGraph()
  52. G.add_node("s", demand=-5)
  53. G.add_node("t", demand=5)
  54. G.add_edge("s", "a", weight=1, capacity=3)
  55. G.add_edge("a", "b", weight=3)
  56. G.add_edge("a", "c", weight=-6)
  57. G.add_edge("b", "d", weight=1)
  58. G.add_edge("c", "d", weight=-2)
  59. G.add_edge("d", "t", weight=1, capacity=3)
  60. pytest.raises(nx.NetworkXUnfeasible, nx.network_simplex, G)
  61. pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
  62. def test_transshipment(self):
  63. G = nx.DiGraph()
  64. G.add_node("a", demand=1)
  65. G.add_node("b", demand=-2)
  66. G.add_node("c", demand=-2)
  67. G.add_node("d", demand=3)
  68. G.add_node("e", demand=-4)
  69. G.add_node("f", demand=-4)
  70. G.add_node("g", demand=3)
  71. G.add_node("h", demand=2)
  72. G.add_node("r", demand=3)
  73. G.add_edge("a", "c", weight=3)
  74. G.add_edge("r", "a", weight=2)
  75. G.add_edge("b", "a", weight=9)
  76. G.add_edge("r", "c", weight=0)
  77. G.add_edge("b", "r", weight=-6)
  78. G.add_edge("c", "d", weight=5)
  79. G.add_edge("e", "r", weight=4)
  80. G.add_edge("e", "f", weight=3)
  81. G.add_edge("h", "b", weight=4)
  82. G.add_edge("f", "d", weight=7)
  83. G.add_edge("f", "h", weight=12)
  84. G.add_edge("g", "d", weight=12)
  85. G.add_edge("f", "g", weight=-1)
  86. G.add_edge("h", "g", weight=-10)
  87. flowCost, H = nx.network_simplex(G)
  88. soln = {
  89. "a": {"c": 0},
  90. "b": {"a": 0, "r": 2},
  91. "c": {"d": 3},
  92. "d": {},
  93. "e": {"r": 3, "f": 1},
  94. "f": {"d": 0, "g": 3, "h": 2},
  95. "g": {"d": 0},
  96. "h": {"b": 0, "g": 0},
  97. "r": {"a": 1, "c": 1},
  98. }
  99. assert flowCost == 41
  100. assert nx.min_cost_flow_cost(G) == 41
  101. assert H == soln
  102. assert nx.min_cost_flow(G) == soln
  103. assert nx.cost_of_flow(G, H) == 41
  104. flowCost, H = nx.capacity_scaling(G)
  105. assert flowCost == 41
  106. assert nx.cost_of_flow(G, H) == 41
  107. assert H == soln
  108. def test_max_flow_min_cost(self):
  109. G = nx.DiGraph()
  110. G.add_edge("s", "a", bandwidth=6)
  111. G.add_edge("s", "c", bandwidth=10, cost=10)
  112. G.add_edge("a", "b", cost=6)
  113. G.add_edge("b", "d", bandwidth=8, cost=7)
  114. G.add_edge("c", "d", cost=10)
  115. G.add_edge("d", "t", bandwidth=5, cost=5)
  116. soln = {
  117. "s": {"a": 5, "c": 0},
  118. "a": {"b": 5},
  119. "b": {"d": 5},
  120. "c": {"d": 0},
  121. "d": {"t": 5},
  122. "t": {},
  123. }
  124. flow = nx.max_flow_min_cost(G, "s", "t", capacity="bandwidth", weight="cost")
  125. assert flow == soln
  126. assert nx.cost_of_flow(G, flow, weight="cost") == 90
  127. G.add_edge("t", "s", cost=-100)
  128. flowCost, flow = nx.capacity_scaling(G, capacity="bandwidth", weight="cost")
  129. G.remove_edge("t", "s")
  130. assert flowCost == -410
  131. assert flow["t"]["s"] == 5
  132. del flow["t"]["s"]
  133. assert flow == soln
  134. assert nx.cost_of_flow(G, flow, weight="cost") == 90
  135. def test_digraph1(self):
  136. # From Bradley, S. P., Hax, A. C. and Magnanti, T. L. Applied
  137. # Mathematical Programming. Addison-Wesley, 1977.
  138. G = nx.DiGraph()
  139. G.add_node(1, demand=-20)
  140. G.add_node(4, demand=5)
  141. G.add_node(5, demand=15)
  142. G.add_edges_from(
  143. [
  144. (1, 2, {"capacity": 15, "weight": 4}),
  145. (1, 3, {"capacity": 8, "weight": 4}),
  146. (2, 3, {"weight": 2}),
  147. (2, 4, {"capacity": 4, "weight": 2}),
  148. (2, 5, {"capacity": 10, "weight": 6}),
  149. (3, 4, {"capacity": 15, "weight": 1}),
  150. (3, 5, {"capacity": 5, "weight": 3}),
  151. (4, 5, {"weight": 2}),
  152. (5, 3, {"capacity": 4, "weight": 1}),
  153. ]
  154. )
  155. flowCost, H = nx.network_simplex(G)
  156. soln = {
  157. 1: {2: 12, 3: 8},
  158. 2: {3: 8, 4: 4, 5: 0},
  159. 3: {4: 11, 5: 5},
  160. 4: {5: 10},
  161. 5: {3: 0},
  162. }
  163. assert flowCost == 150
  164. assert nx.min_cost_flow_cost(G) == 150
  165. assert H == soln
  166. assert nx.min_cost_flow(G) == soln
  167. assert nx.cost_of_flow(G, H) == 150
  168. flowCost, H = nx.capacity_scaling(G)
  169. assert flowCost == 150
  170. assert H == soln
  171. assert nx.cost_of_flow(G, H) == 150
  172. def test_digraph2(self):
  173. # Example from ticket #430 from mfrasca. Original source:
  174. # http://www.cs.princeton.edu/courses/archive/spr03/cs226/lectures/mincost.4up.pdf, slide 11.
  175. G = nx.DiGraph()
  176. G.add_edge("s", 1, capacity=12)
  177. G.add_edge("s", 2, capacity=6)
  178. G.add_edge("s", 3, capacity=14)
  179. G.add_edge(1, 2, capacity=11, weight=4)
  180. G.add_edge(2, 3, capacity=9, weight=6)
  181. G.add_edge(1, 4, capacity=5, weight=5)
  182. G.add_edge(1, 5, capacity=2, weight=12)
  183. G.add_edge(2, 5, capacity=4, weight=4)
  184. G.add_edge(2, 6, capacity=2, weight=6)
  185. G.add_edge(3, 6, capacity=31, weight=3)
  186. G.add_edge(4, 5, capacity=18, weight=4)
  187. G.add_edge(5, 6, capacity=9, weight=5)
  188. G.add_edge(4, "t", capacity=3)
  189. G.add_edge(5, "t", capacity=7)
  190. G.add_edge(6, "t", capacity=22)
  191. flow = nx.max_flow_min_cost(G, "s", "t")
  192. soln = {
  193. 1: {2: 6, 4: 5, 5: 1},
  194. 2: {3: 6, 5: 4, 6: 2},
  195. 3: {6: 20},
  196. 4: {5: 2, "t": 3},
  197. 5: {6: 0, "t": 7},
  198. 6: {"t": 22},
  199. "s": {1: 12, 2: 6, 3: 14},
  200. "t": {},
  201. }
  202. assert flow == soln
  203. G.add_edge("t", "s", weight=-100)
  204. flowCost, flow = nx.capacity_scaling(G)
  205. G.remove_edge("t", "s")
  206. assert flow["t"]["s"] == 32
  207. assert flowCost == -3007
  208. del flow["t"]["s"]
  209. assert flow == soln
  210. assert nx.cost_of_flow(G, flow) == 193
  211. def test_digraph3(self):
  212. """Combinatorial Optimization: Algorithms and Complexity,
  213. Papadimitriou Steiglitz at page 140 has an example, 7.1, but that
  214. admits multiple solutions, so I alter it a bit. From ticket #430
  215. by mfrasca."""
  216. G = nx.DiGraph()
  217. G.add_edge("s", "a")
  218. G["s"]["a"].update({0: 2, 1: 4})
  219. G.add_edge("s", "b")
  220. G["s"]["b"].update({0: 2, 1: 1})
  221. G.add_edge("a", "b")
  222. G["a"]["b"].update({0: 5, 1: 2})
  223. G.add_edge("a", "t")
  224. G["a"]["t"].update({0: 1, 1: 5})
  225. G.add_edge("b", "a")
  226. G["b"]["a"].update({0: 1, 1: 3})
  227. G.add_edge("b", "t")
  228. G["b"]["t"].update({0: 3, 1: 2})
  229. "PS.ex.7.1: testing main function"
  230. sol = nx.max_flow_min_cost(G, "s", "t", capacity=0, weight=1)
  231. flow = sum(v for v in sol["s"].values())
  232. assert 4 == flow
  233. assert 23 == nx.cost_of_flow(G, sol, weight=1)
  234. assert sol["s"] == {"a": 2, "b": 2}
  235. assert sol["a"] == {"b": 1, "t": 1}
  236. assert sol["b"] == {"a": 0, "t": 3}
  237. assert sol["t"] == {}
  238. G.add_edge("t", "s")
  239. G["t"]["s"].update({1: -100})
  240. flowCost, sol = nx.capacity_scaling(G, capacity=0, weight=1)
  241. G.remove_edge("t", "s")
  242. flow = sum(v for v in sol["s"].values())
  243. assert 4 == flow
  244. assert sol["t"]["s"] == 4
  245. assert flowCost == -377
  246. del sol["t"]["s"]
  247. assert sol["s"] == {"a": 2, "b": 2}
  248. assert sol["a"] == {"b": 1, "t": 1}
  249. assert sol["b"] == {"a": 0, "t": 3}
  250. assert sol["t"] == {}
  251. assert nx.cost_of_flow(G, sol, weight=1) == 23
  252. def test_zero_capacity_edges(self):
  253. """Address issue raised in ticket #617 by arv."""
  254. G = nx.DiGraph()
  255. G.add_edges_from(
  256. [
  257. (1, 2, {"capacity": 1, "weight": 1}),
  258. (1, 5, {"capacity": 1, "weight": 1}),
  259. (2, 3, {"capacity": 0, "weight": 1}),
  260. (2, 5, {"capacity": 1, "weight": 1}),
  261. (5, 3, {"capacity": 2, "weight": 1}),
  262. (5, 4, {"capacity": 0, "weight": 1}),
  263. (3, 4, {"capacity": 2, "weight": 1}),
  264. ]
  265. )
  266. G.nodes[1]["demand"] = -1
  267. G.nodes[2]["demand"] = -1
  268. G.nodes[4]["demand"] = 2
  269. flowCost, H = nx.network_simplex(G)
  270. soln = {1: {2: 0, 5: 1}, 2: {3: 0, 5: 1}, 3: {4: 2}, 4: {}, 5: {3: 2, 4: 0}}
  271. assert flowCost == 6
  272. assert nx.min_cost_flow_cost(G) == 6
  273. assert H == soln
  274. assert nx.min_cost_flow(G) == soln
  275. assert nx.cost_of_flow(G, H) == 6
  276. flowCost, H = nx.capacity_scaling(G)
  277. assert flowCost == 6
  278. assert H == soln
  279. assert nx.cost_of_flow(G, H) == 6
  280. def test_digon(self):
  281. """Check if digons are handled properly. Taken from ticket
  282. #618 by arv."""
  283. nodes = [(1, {}), (2, {"demand": -4}), (3, {"demand": 4})]
  284. edges = [
  285. (1, 2, {"capacity": 3, "weight": 600000}),
  286. (2, 1, {"capacity": 2, "weight": 0}),
  287. (2, 3, {"capacity": 5, "weight": 714285}),
  288. (3, 2, {"capacity": 2, "weight": 0}),
  289. ]
  290. G = nx.DiGraph(edges)
  291. G.add_nodes_from(nodes)
  292. flowCost, H = nx.network_simplex(G)
  293. soln = {1: {2: 0}, 2: {1: 0, 3: 4}, 3: {2: 0}}
  294. assert flowCost == 2857140
  295. assert nx.min_cost_flow_cost(G) == 2857140
  296. assert H == soln
  297. assert nx.min_cost_flow(G) == soln
  298. assert nx.cost_of_flow(G, H) == 2857140
  299. flowCost, H = nx.capacity_scaling(G)
  300. assert flowCost == 2857140
  301. assert H == soln
  302. assert nx.cost_of_flow(G, H) == 2857140
  303. def test_deadend(self):
  304. """Check if one-node cycles are handled properly. Taken from ticket
  305. #2906 from @sshraven."""
  306. G = nx.DiGraph()
  307. G.add_nodes_from(range(5), demand=0)
  308. G.nodes[4]["demand"] = -13
  309. G.nodes[3]["demand"] = 13
  310. G.add_edges_from([(0, 2), (0, 3), (2, 1)], capacity=20, weight=0.1)
  311. pytest.raises(nx.NetworkXUnfeasible, nx.min_cost_flow, G)
  312. def test_infinite_capacity_neg_digon(self):
  313. """An infinite capacity negative cost digon results in an unbounded
  314. instance."""
  315. nodes = [(1, {}), (2, {"demand": -4}), (3, {"demand": 4})]
  316. edges = [
  317. (1, 2, {"weight": -600}),
  318. (2, 1, {"weight": 0}),
  319. (2, 3, {"capacity": 5, "weight": 714285}),
  320. (3, 2, {"capacity": 2, "weight": 0}),
  321. ]
  322. G = nx.DiGraph(edges)
  323. G.add_nodes_from(nodes)
  324. pytest.raises(nx.NetworkXUnbounded, nx.network_simplex, G)
  325. pytest.raises(nx.NetworkXUnbounded, nx.capacity_scaling, G)
  326. def test_finite_capacity_neg_digon(self):
  327. """The digon should receive the maximum amount of flow it can handle.
  328. Taken from ticket #749 by @chuongdo."""
  329. G = nx.DiGraph()
  330. G.add_edge("a", "b", capacity=1, weight=-1)
  331. G.add_edge("b", "a", capacity=1, weight=-1)
  332. min_cost = -2
  333. assert nx.min_cost_flow_cost(G) == min_cost
  334. flowCost, H = nx.capacity_scaling(G)
  335. assert flowCost == -2
  336. assert H == {"a": {"b": 1}, "b": {"a": 1}}
  337. assert nx.cost_of_flow(G, H) == -2
  338. def test_multidigraph(self):
  339. """Multidigraphs are acceptable."""
  340. G = nx.MultiDiGraph()
  341. G.add_weighted_edges_from([(1, 2, 1), (2, 3, 2)], weight="capacity")
  342. flowCost, H = nx.network_simplex(G)
  343. assert flowCost == 0
  344. assert H == {1: {2: {0: 0}}, 2: {3: {0: 0}}, 3: {}}
  345. flowCost, H = nx.capacity_scaling(G)
  346. assert flowCost == 0
  347. assert H == {1: {2: {0: 0}}, 2: {3: {0: 0}}, 3: {}}
  348. def test_negative_selfloops(self):
  349. """Negative selfloops should cause an exception if uncapacitated and
  350. always be saturated otherwise.
  351. """
  352. G = nx.DiGraph()
  353. G.add_edge(1, 1, weight=-1)
  354. pytest.raises(nx.NetworkXUnbounded, nx.network_simplex, G)
  355. pytest.raises(nx.NetworkXUnbounded, nx.capacity_scaling, G)
  356. G[1][1]["capacity"] = 2
  357. flowCost, H = nx.network_simplex(G)
  358. assert flowCost == -2
  359. assert H == {1: {1: 2}}
  360. flowCost, H = nx.capacity_scaling(G)
  361. assert flowCost == -2
  362. assert H == {1: {1: 2}}
  363. G = nx.MultiDiGraph()
  364. G.add_edge(1, 1, "x", weight=-1)
  365. G.add_edge(1, 1, "y", weight=1)
  366. pytest.raises(nx.NetworkXUnbounded, nx.network_simplex, G)
  367. pytest.raises(nx.NetworkXUnbounded, nx.capacity_scaling, G)
  368. G[1][1]["x"]["capacity"] = 2
  369. flowCost, H = nx.network_simplex(G)
  370. assert flowCost == -2
  371. assert H == {1: {1: {"x": 2, "y": 0}}}
  372. flowCost, H = nx.capacity_scaling(G)
  373. assert flowCost == -2
  374. assert H == {1: {1: {"x": 2, "y": 0}}}
  375. def test_bone_shaped(self):
  376. # From #1283
  377. G = nx.DiGraph()
  378. G.add_node(0, demand=-4)
  379. G.add_node(1, demand=2)
  380. G.add_node(2, demand=2)
  381. G.add_node(3, demand=4)
  382. G.add_node(4, demand=-2)
  383. G.add_node(5, demand=-2)
  384. G.add_edge(0, 1, capacity=4)
  385. G.add_edge(0, 2, capacity=4)
  386. G.add_edge(4, 3, capacity=4)
  387. G.add_edge(5, 3, capacity=4)
  388. G.add_edge(0, 3, capacity=0)
  389. flowCost, H = nx.network_simplex(G)
  390. assert flowCost == 0
  391. assert H == {0: {1: 2, 2: 2, 3: 0}, 1: {}, 2: {}, 3: {}, 4: {3: 2}, 5: {3: 2}}
  392. flowCost, H = nx.capacity_scaling(G)
  393. assert flowCost == 0
  394. assert H == {0: {1: 2, 2: 2, 3: 0}, 1: {}, 2: {}, 3: {}, 4: {3: 2}, 5: {3: 2}}
  395. def test_exceptions(self):
  396. G = nx.Graph()
  397. pytest.raises(nx.NetworkXNotImplemented, nx.network_simplex, G)
  398. pytest.raises(nx.NetworkXNotImplemented, nx.capacity_scaling, G)
  399. G = nx.MultiGraph()
  400. pytest.raises(nx.NetworkXNotImplemented, nx.network_simplex, G)
  401. pytest.raises(nx.NetworkXNotImplemented, nx.capacity_scaling, G)
  402. G = nx.DiGraph()
  403. pytest.raises(nx.NetworkXError, nx.network_simplex, G)
  404. # pytest.raises(nx.NetworkXError, nx.capacity_scaling, G)
  405. G.add_node(0, demand=float("inf"))
  406. pytest.raises(nx.NetworkXError, nx.network_simplex, G)
  407. pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
  408. G.nodes[0]["demand"] = 0
  409. G.add_node(1, demand=0)
  410. G.add_edge(0, 1, weight=-float("inf"))
  411. pytest.raises(nx.NetworkXError, nx.network_simplex, G)
  412. pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
  413. G[0][1]["weight"] = 0
  414. G.add_edge(0, 0, weight=float("inf"))
  415. pytest.raises(nx.NetworkXError, nx.network_simplex, G)
  416. # pytest.raises(nx.NetworkXError, nx.capacity_scaling, G)
  417. G[0][0]["weight"] = 0
  418. G[0][1]["capacity"] = -1
  419. pytest.raises(nx.NetworkXUnfeasible, nx.network_simplex, G)
  420. # pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
  421. G[0][1]["capacity"] = 0
  422. G[0][0]["capacity"] = -1
  423. pytest.raises(nx.NetworkXUnfeasible, nx.network_simplex, G)
  424. # pytest.raises(nx.NetworkXUnfeasible, nx.capacity_scaling, G)
  425. def test_large(self):
  426. fname = os.path.join(os.path.dirname(__file__), "netgen-2.gpickle.bz2")
  427. with bz2.BZ2File(fname, "rb") as f:
  428. G = pickle.load(f)
  429. flowCost, flowDict = nx.network_simplex(G)
  430. assert 6749969302 == flowCost
  431. assert 6749969302 == nx.cost_of_flow(G, flowDict)
  432. flowCost, flowDict = nx.capacity_scaling(G)
  433. assert 6749969302 == flowCost
  434. assert 6749969302 == nx.cost_of_flow(G, flowDict)