test_dag.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771
  1. from collections import deque
  2. from itertools import combinations, permutations
  3. import pytest
  4. import networkx as nx
  5. from networkx.utils import edges_equal, pairwise
  6. # Recipe from the itertools documentation.
  7. def _consume(iterator):
  8. "Consume the iterator entirely."
  9. # Feed the entire iterator into a zero-length deque.
  10. deque(iterator, maxlen=0)
  11. class TestDagLongestPath:
  12. """Unit tests computing the longest path in a directed acyclic graph."""
  13. def test_empty(self):
  14. G = nx.DiGraph()
  15. assert nx.dag_longest_path(G) == []
  16. def test_unweighted1(self):
  17. edges = [(1, 2), (2, 3), (2, 4), (3, 5), (5, 6), (3, 7)]
  18. G = nx.DiGraph(edges)
  19. assert nx.dag_longest_path(G) == [1, 2, 3, 5, 6]
  20. def test_unweighted2(self):
  21. edges = [(1, 2), (2, 3), (3, 4), (4, 5), (1, 3), (1, 5), (3, 5)]
  22. G = nx.DiGraph(edges)
  23. assert nx.dag_longest_path(G) == [1, 2, 3, 4, 5]
  24. def test_weighted(self):
  25. G = nx.DiGraph()
  26. edges = [(1, 2, -5), (2, 3, 1), (3, 4, 1), (4, 5, 0), (3, 5, 4), (1, 6, 2)]
  27. G.add_weighted_edges_from(edges)
  28. assert nx.dag_longest_path(G) == [2, 3, 5]
  29. def test_undirected_not_implemented(self):
  30. G = nx.Graph()
  31. pytest.raises(nx.NetworkXNotImplemented, nx.dag_longest_path, G)
  32. def test_unorderable_nodes(self):
  33. """Tests that computing the longest path does not depend on
  34. nodes being orderable.
  35. For more information, see issue #1989.
  36. """
  37. # Create the directed path graph on four nodes in a diamond shape,
  38. # with nodes represented as (unorderable) Python objects.
  39. nodes = [object() for n in range(4)]
  40. G = nx.DiGraph()
  41. G.add_edge(nodes[0], nodes[1])
  42. G.add_edge(nodes[0], nodes[2])
  43. G.add_edge(nodes[2], nodes[3])
  44. G.add_edge(nodes[1], nodes[3])
  45. # this will raise NotImplementedError when nodes need to be ordered
  46. nx.dag_longest_path(G)
  47. def test_multigraph_unweighted(self):
  48. edges = [(1, 2), (2, 3), (2, 3), (3, 4), (4, 5), (1, 3), (1, 5), (3, 5)]
  49. G = nx.MultiDiGraph(edges)
  50. assert nx.dag_longest_path(G) == [1, 2, 3, 4, 5]
  51. def test_multigraph_weighted(self):
  52. G = nx.MultiDiGraph()
  53. edges = [
  54. (1, 2, 2),
  55. (2, 3, 2),
  56. (1, 3, 1),
  57. (1, 3, 5),
  58. (1, 3, 2),
  59. ]
  60. G.add_weighted_edges_from(edges)
  61. assert nx.dag_longest_path(G) == [1, 3]
  62. def test_multigraph_weighted_default_weight(self):
  63. G = nx.MultiDiGraph([(1, 2), (2, 3)]) # Unweighted edges
  64. G.add_weighted_edges_from([(1, 3, 1), (1, 3, 5), (1, 3, 2)])
  65. # Default value for default weight is 1
  66. assert nx.dag_longest_path(G) == [1, 3]
  67. assert nx.dag_longest_path(G, default_weight=3) == [1, 2, 3]
  68. class TestDagLongestPathLength:
  69. """Unit tests for computing the length of a longest path in a
  70. directed acyclic graph.
  71. """
  72. def test_unweighted(self):
  73. edges = [(1, 2), (2, 3), (2, 4), (3, 5), (5, 6), (5, 7)]
  74. G = nx.DiGraph(edges)
  75. assert nx.dag_longest_path_length(G) == 4
  76. edges = [(1, 2), (2, 3), (3, 4), (4, 5), (1, 3), (1, 5), (3, 5)]
  77. G = nx.DiGraph(edges)
  78. assert nx.dag_longest_path_length(G) == 4
  79. # test degenerate graphs
  80. G = nx.DiGraph()
  81. G.add_node(1)
  82. assert nx.dag_longest_path_length(G) == 0
  83. def test_undirected_not_implemented(self):
  84. G = nx.Graph()
  85. pytest.raises(nx.NetworkXNotImplemented, nx.dag_longest_path_length, G)
  86. def test_weighted(self):
  87. edges = [(1, 2, -5), (2, 3, 1), (3, 4, 1), (4, 5, 0), (3, 5, 4), (1, 6, 2)]
  88. G = nx.DiGraph()
  89. G.add_weighted_edges_from(edges)
  90. assert nx.dag_longest_path_length(G) == 5
  91. def test_multigraph_unweighted(self):
  92. edges = [(1, 2), (2, 3), (2, 3), (3, 4), (4, 5), (1, 3), (1, 5), (3, 5)]
  93. G = nx.MultiDiGraph(edges)
  94. assert nx.dag_longest_path_length(G) == 4
  95. def test_multigraph_weighted(self):
  96. G = nx.MultiDiGraph()
  97. edges = [
  98. (1, 2, 2),
  99. (2, 3, 2),
  100. (1, 3, 1),
  101. (1, 3, 5),
  102. (1, 3, 2),
  103. ]
  104. G.add_weighted_edges_from(edges)
  105. assert nx.dag_longest_path_length(G) == 5
  106. class TestDAG:
  107. @classmethod
  108. def setup_class(cls):
  109. pass
  110. def test_topological_sort1(self):
  111. DG = nx.DiGraph([(1, 2), (1, 3), (2, 3)])
  112. for algorithm in [nx.topological_sort, nx.lexicographical_topological_sort]:
  113. assert tuple(algorithm(DG)) == (1, 2, 3)
  114. DG.add_edge(3, 2)
  115. for algorithm in [nx.topological_sort, nx.lexicographical_topological_sort]:
  116. pytest.raises(nx.NetworkXUnfeasible, _consume, algorithm(DG))
  117. DG.remove_edge(2, 3)
  118. for algorithm in [nx.topological_sort, nx.lexicographical_topological_sort]:
  119. assert tuple(algorithm(DG)) == (1, 3, 2)
  120. DG.remove_edge(3, 2)
  121. assert tuple(nx.topological_sort(DG)) in {(1, 2, 3), (1, 3, 2)}
  122. assert tuple(nx.lexicographical_topological_sort(DG)) == (1, 2, 3)
  123. def test_is_directed_acyclic_graph(self):
  124. G = nx.generators.complete_graph(2)
  125. assert not nx.is_directed_acyclic_graph(G)
  126. assert not nx.is_directed_acyclic_graph(G.to_directed())
  127. assert not nx.is_directed_acyclic_graph(nx.Graph([(3, 4), (4, 5)]))
  128. assert nx.is_directed_acyclic_graph(nx.DiGraph([(3, 4), (4, 5)]))
  129. def test_topological_sort2(self):
  130. DG = nx.DiGraph(
  131. {
  132. 1: [2],
  133. 2: [3],
  134. 3: [4],
  135. 4: [5],
  136. 5: [1],
  137. 11: [12],
  138. 12: [13],
  139. 13: [14],
  140. 14: [15],
  141. }
  142. )
  143. pytest.raises(nx.NetworkXUnfeasible, _consume, nx.topological_sort(DG))
  144. assert not nx.is_directed_acyclic_graph(DG)
  145. DG.remove_edge(1, 2)
  146. _consume(nx.topological_sort(DG))
  147. assert nx.is_directed_acyclic_graph(DG)
  148. def test_topological_sort3(self):
  149. DG = nx.DiGraph()
  150. DG.add_edges_from([(1, i) for i in range(2, 5)])
  151. DG.add_edges_from([(2, i) for i in range(5, 9)])
  152. DG.add_edges_from([(6, i) for i in range(9, 12)])
  153. DG.add_edges_from([(4, i) for i in range(12, 15)])
  154. def validate(order):
  155. assert isinstance(order, list)
  156. assert set(order) == set(DG)
  157. for u, v in combinations(order, 2):
  158. assert not nx.has_path(DG, v, u)
  159. validate(list(nx.topological_sort(DG)))
  160. DG.add_edge(14, 1)
  161. pytest.raises(nx.NetworkXUnfeasible, _consume, nx.topological_sort(DG))
  162. def test_topological_sort4(self):
  163. G = nx.Graph()
  164. G.add_edge(1, 2)
  165. # Only directed graphs can be topologically sorted.
  166. pytest.raises(nx.NetworkXError, _consume, nx.topological_sort(G))
  167. def test_topological_sort5(self):
  168. G = nx.DiGraph()
  169. G.add_edge(0, 1)
  170. assert list(nx.topological_sort(G)) == [0, 1]
  171. def test_topological_sort6(self):
  172. for algorithm in [nx.topological_sort, nx.lexicographical_topological_sort]:
  173. def runtime_error():
  174. DG = nx.DiGraph([(1, 2), (2, 3), (3, 4)])
  175. first = True
  176. for x in algorithm(DG):
  177. if first:
  178. first = False
  179. DG.add_edge(5 - x, 5)
  180. def unfeasible_error():
  181. DG = nx.DiGraph([(1, 2), (2, 3), (3, 4)])
  182. first = True
  183. for x in algorithm(DG):
  184. if first:
  185. first = False
  186. DG.remove_node(4)
  187. def runtime_error2():
  188. DG = nx.DiGraph([(1, 2), (2, 3), (3, 4)])
  189. first = True
  190. for x in algorithm(DG):
  191. if first:
  192. first = False
  193. DG.remove_node(2)
  194. pytest.raises(RuntimeError, runtime_error)
  195. pytest.raises(RuntimeError, runtime_error2)
  196. pytest.raises(nx.NetworkXUnfeasible, unfeasible_error)
  197. def test_all_topological_sorts_1(self):
  198. DG = nx.DiGraph([(1, 2), (2, 3), (3, 4), (4, 5)])
  199. assert list(nx.all_topological_sorts(DG)) == [[1, 2, 3, 4, 5]]
  200. def test_all_topological_sorts_2(self):
  201. DG = nx.DiGraph([(1, 3), (2, 1), (2, 4), (4, 3), (4, 5)])
  202. assert sorted(nx.all_topological_sorts(DG)) == [
  203. [2, 1, 4, 3, 5],
  204. [2, 1, 4, 5, 3],
  205. [2, 4, 1, 3, 5],
  206. [2, 4, 1, 5, 3],
  207. [2, 4, 5, 1, 3],
  208. ]
  209. def test_all_topological_sorts_3(self):
  210. def unfeasible():
  211. DG = nx.DiGraph([(1, 2), (2, 3), (3, 4), (4, 2), (4, 5)])
  212. # convert to list to execute generator
  213. list(nx.all_topological_sorts(DG))
  214. def not_implemented():
  215. G = nx.Graph([(1, 2), (2, 3)])
  216. # convert to list to execute generator
  217. list(nx.all_topological_sorts(G))
  218. def not_implemted_2():
  219. G = nx.MultiGraph([(1, 2), (1, 2), (2, 3)])
  220. list(nx.all_topological_sorts(G))
  221. pytest.raises(nx.NetworkXUnfeasible, unfeasible)
  222. pytest.raises(nx.NetworkXNotImplemented, not_implemented)
  223. pytest.raises(nx.NetworkXNotImplemented, not_implemted_2)
  224. def test_all_topological_sorts_4(self):
  225. DG = nx.DiGraph()
  226. for i in range(7):
  227. DG.add_node(i)
  228. assert sorted(map(list, permutations(DG.nodes))) == sorted(
  229. nx.all_topological_sorts(DG)
  230. )
  231. def test_all_topological_sorts_multigraph_1(self):
  232. DG = nx.MultiDiGraph([(1, 2), (1, 2), (2, 3), (3, 4), (3, 5), (3, 5), (3, 5)])
  233. assert sorted(nx.all_topological_sorts(DG)) == sorted(
  234. [[1, 2, 3, 4, 5], [1, 2, 3, 5, 4]]
  235. )
  236. def test_all_topological_sorts_multigraph_2(self):
  237. N = 9
  238. edges = []
  239. for i in range(1, N):
  240. edges.extend([(i, i + 1)] * i)
  241. DG = nx.MultiDiGraph(edges)
  242. assert list(nx.all_topological_sorts(DG)) == [list(range(1, N + 1))]
  243. def test_ancestors(self):
  244. G = nx.DiGraph()
  245. ancestors = nx.algorithms.dag.ancestors
  246. G.add_edges_from([(1, 2), (1, 3), (4, 2), (4, 3), (4, 5), (2, 6), (5, 6)])
  247. assert ancestors(G, 6) == {1, 2, 4, 5}
  248. assert ancestors(G, 3) == {1, 4}
  249. assert ancestors(G, 1) == set()
  250. pytest.raises(nx.NetworkXError, ancestors, G, 8)
  251. def test_descendants(self):
  252. G = nx.DiGraph()
  253. descendants = nx.algorithms.dag.descendants
  254. G.add_edges_from([(1, 2), (1, 3), (4, 2), (4, 3), (4, 5), (2, 6), (5, 6)])
  255. assert descendants(G, 1) == {2, 3, 6}
  256. assert descendants(G, 4) == {2, 3, 5, 6}
  257. assert descendants(G, 3) == set()
  258. pytest.raises(nx.NetworkXError, descendants, G, 8)
  259. def test_transitive_closure(self):
  260. G = nx.DiGraph([(1, 2), (2, 3), (3, 4)])
  261. solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
  262. assert edges_equal(nx.transitive_closure(G).edges(), solution)
  263. G = nx.DiGraph([(1, 2), (2, 3), (2, 4)])
  264. solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4)]
  265. assert edges_equal(nx.transitive_closure(G).edges(), solution)
  266. G = nx.DiGraph([(1, 2), (2, 3), (3, 1)])
  267. solution = [(1, 2), (2, 1), (2, 3), (3, 2), (1, 3), (3, 1)]
  268. soln = sorted(solution + [(n, n) for n in G])
  269. assert edges_equal(sorted(nx.transitive_closure(G).edges()), soln)
  270. G = nx.Graph([(1, 2), (2, 3), (3, 4)])
  271. solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
  272. assert edges_equal(sorted(nx.transitive_closure(G).edges()), solution)
  273. G = nx.MultiGraph([(1, 2), (2, 3), (3, 4)])
  274. solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
  275. assert edges_equal(sorted(nx.transitive_closure(G).edges()), solution)
  276. G = nx.MultiDiGraph([(1, 2), (2, 3), (3, 4)])
  277. solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
  278. assert edges_equal(sorted(nx.transitive_closure(G).edges()), solution)
  279. # test if edge data is copied
  280. G = nx.DiGraph([(1, 2, {"a": 3}), (2, 3, {"b": 0}), (3, 4)])
  281. H = nx.transitive_closure(G)
  282. for u, v in G.edges():
  283. assert G.get_edge_data(u, v) == H.get_edge_data(u, v)
  284. k = 10
  285. G = nx.DiGraph((i, i + 1, {"f": "b", "weight": i}) for i in range(k))
  286. H = nx.transitive_closure(G)
  287. for u, v in G.edges():
  288. assert G.get_edge_data(u, v) == H.get_edge_data(u, v)
  289. G = nx.Graph()
  290. with pytest.raises(nx.NetworkXError):
  291. nx.transitive_closure(G, reflexive="wrong input")
  292. def test_reflexive_transitive_closure(self):
  293. G = nx.DiGraph([(1, 2), (2, 3), (3, 4)])
  294. solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
  295. soln = sorted(solution + [(n, n) for n in G])
  296. assert edges_equal(nx.transitive_closure(G).edges(), solution)
  297. assert edges_equal(nx.transitive_closure(G, False).edges(), solution)
  298. assert edges_equal(nx.transitive_closure(G, True).edges(), soln)
  299. assert edges_equal(nx.transitive_closure(G, None).edges(), solution)
  300. G = nx.DiGraph([(1, 2), (2, 3), (2, 4)])
  301. solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4)]
  302. soln = sorted(solution + [(n, n) for n in G])
  303. assert edges_equal(nx.transitive_closure(G).edges(), solution)
  304. assert edges_equal(nx.transitive_closure(G, False).edges(), solution)
  305. assert edges_equal(nx.transitive_closure(G, True).edges(), soln)
  306. assert edges_equal(nx.transitive_closure(G, None).edges(), solution)
  307. G = nx.DiGraph([(1, 2), (2, 3), (3, 1)])
  308. solution = sorted([(1, 2), (2, 1), (2, 3), (3, 2), (1, 3), (3, 1)])
  309. soln = sorted(solution + [(n, n) for n in G])
  310. assert edges_equal(sorted(nx.transitive_closure(G).edges()), soln)
  311. assert edges_equal(sorted(nx.transitive_closure(G, False).edges()), soln)
  312. assert edges_equal(sorted(nx.transitive_closure(G, None).edges()), solution)
  313. assert edges_equal(sorted(nx.transitive_closure(G, True).edges()), soln)
  314. G = nx.Graph([(1, 2), (2, 3), (3, 4)])
  315. solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
  316. soln = sorted(solution + [(n, n) for n in G])
  317. assert edges_equal(nx.transitive_closure(G).edges(), solution)
  318. assert edges_equal(nx.transitive_closure(G, False).edges(), solution)
  319. assert edges_equal(nx.transitive_closure(G, True).edges(), soln)
  320. assert edges_equal(nx.transitive_closure(G, None).edges(), solution)
  321. G = nx.MultiGraph([(1, 2), (2, 3), (3, 4)])
  322. solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
  323. soln = sorted(solution + [(n, n) for n in G])
  324. assert edges_equal(nx.transitive_closure(G).edges(), solution)
  325. assert edges_equal(nx.transitive_closure(G, False).edges(), solution)
  326. assert edges_equal(nx.transitive_closure(G, True).edges(), soln)
  327. assert edges_equal(nx.transitive_closure(G, None).edges(), solution)
  328. G = nx.MultiDiGraph([(1, 2), (2, 3), (3, 4)])
  329. solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
  330. soln = sorted(solution + [(n, n) for n in G])
  331. assert edges_equal(nx.transitive_closure(G).edges(), solution)
  332. assert edges_equal(nx.transitive_closure(G, False).edges(), solution)
  333. assert edges_equal(nx.transitive_closure(G, True).edges(), soln)
  334. assert edges_equal(nx.transitive_closure(G, None).edges(), solution)
  335. def test_transitive_closure_dag(self):
  336. G = nx.DiGraph([(1, 2), (2, 3), (3, 4)])
  337. transitive_closure = nx.algorithms.dag.transitive_closure_dag
  338. solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
  339. assert edges_equal(transitive_closure(G).edges(), solution)
  340. G = nx.DiGraph([(1, 2), (2, 3), (2, 4)])
  341. solution = [(1, 2), (1, 3), (1, 4), (2, 3), (2, 4)]
  342. assert edges_equal(transitive_closure(G).edges(), solution)
  343. G = nx.Graph([(1, 2), (2, 3), (3, 4)])
  344. pytest.raises(nx.NetworkXNotImplemented, transitive_closure, G)
  345. # test if edge data is copied
  346. G = nx.DiGraph([(1, 2, {"a": 3}), (2, 3, {"b": 0}), (3, 4)])
  347. H = transitive_closure(G)
  348. for u, v in G.edges():
  349. assert G.get_edge_data(u, v) == H.get_edge_data(u, v)
  350. k = 10
  351. G = nx.DiGraph((i, i + 1, {"foo": "bar", "weight": i}) for i in range(k))
  352. H = transitive_closure(G)
  353. for u, v in G.edges():
  354. assert G.get_edge_data(u, v) == H.get_edge_data(u, v)
  355. def test_transitive_reduction(self):
  356. G = nx.DiGraph([(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)])
  357. transitive_reduction = nx.algorithms.dag.transitive_reduction
  358. solution = [(1, 2), (2, 3), (3, 4)]
  359. assert edges_equal(transitive_reduction(G).edges(), solution)
  360. G = nx.DiGraph([(1, 2), (1, 3), (1, 4), (2, 3), (2, 4)])
  361. transitive_reduction = nx.algorithms.dag.transitive_reduction
  362. solution = [(1, 2), (2, 3), (2, 4)]
  363. assert edges_equal(transitive_reduction(G).edges(), solution)
  364. G = nx.Graph([(1, 2), (2, 3), (3, 4)])
  365. pytest.raises(nx.NetworkXNotImplemented, transitive_reduction, G)
  366. def _check_antichains(self, solution, result):
  367. sol = [frozenset(a) for a in solution]
  368. res = [frozenset(a) for a in result]
  369. assert set(sol) == set(res)
  370. def test_antichains(self):
  371. antichains = nx.algorithms.dag.antichains
  372. G = nx.DiGraph([(1, 2), (2, 3), (3, 4)])
  373. solution = [[], [4], [3], [2], [1]]
  374. self._check_antichains(list(antichains(G)), solution)
  375. G = nx.DiGraph([(1, 2), (2, 3), (2, 4), (3, 5), (5, 6), (5, 7)])
  376. solution = [
  377. [],
  378. [4],
  379. [7],
  380. [7, 4],
  381. [6],
  382. [6, 4],
  383. [6, 7],
  384. [6, 7, 4],
  385. [5],
  386. [5, 4],
  387. [3],
  388. [3, 4],
  389. [2],
  390. [1],
  391. ]
  392. self._check_antichains(list(antichains(G)), solution)
  393. G = nx.DiGraph([(1, 2), (1, 3), (3, 4), (3, 5), (5, 6)])
  394. solution = [
  395. [],
  396. [6],
  397. [5],
  398. [4],
  399. [4, 6],
  400. [4, 5],
  401. [3],
  402. [2],
  403. [2, 6],
  404. [2, 5],
  405. [2, 4],
  406. [2, 4, 6],
  407. [2, 4, 5],
  408. [2, 3],
  409. [1],
  410. ]
  411. self._check_antichains(list(antichains(G)), solution)
  412. G = nx.DiGraph({0: [1, 2], 1: [4], 2: [3], 3: [4]})
  413. solution = [[], [4], [3], [2], [1], [1, 3], [1, 2], [0]]
  414. self._check_antichains(list(antichains(G)), solution)
  415. G = nx.DiGraph()
  416. self._check_antichains(list(antichains(G)), [[]])
  417. G = nx.DiGraph()
  418. G.add_nodes_from([0, 1, 2])
  419. solution = [[], [0], [1], [1, 0], [2], [2, 0], [2, 1], [2, 1, 0]]
  420. self._check_antichains(list(antichains(G)), solution)
  421. def f(x):
  422. return list(antichains(x))
  423. G = nx.Graph([(1, 2), (2, 3), (3, 4)])
  424. pytest.raises(nx.NetworkXNotImplemented, f, G)
  425. G = nx.DiGraph([(1, 2), (2, 3), (3, 1)])
  426. pytest.raises(nx.NetworkXUnfeasible, f, G)
  427. def test_lexicographical_topological_sort(self):
  428. G = nx.DiGraph([(1, 2), (2, 3), (1, 4), (1, 5), (2, 6)])
  429. assert list(nx.lexicographical_topological_sort(G)) == [1, 2, 3, 4, 5, 6]
  430. assert list(nx.lexicographical_topological_sort(G, key=lambda x: x)) == [
  431. 1,
  432. 2,
  433. 3,
  434. 4,
  435. 5,
  436. 6,
  437. ]
  438. assert list(nx.lexicographical_topological_sort(G, key=lambda x: -x)) == [
  439. 1,
  440. 5,
  441. 4,
  442. 2,
  443. 6,
  444. 3,
  445. ]
  446. def test_lexicographical_topological_sort2(self):
  447. """
  448. Check the case of two or more nodes with same key value.
  449. Want to avoid exception raised due to comparing nodes directly.
  450. See Issue #3493
  451. """
  452. class Test_Node:
  453. def __init__(self, n):
  454. self.label = n
  455. self.priority = 1
  456. def __repr__(self):
  457. return f"Node({self.label})"
  458. def sorting_key(node):
  459. return node.priority
  460. test_nodes = [Test_Node(n) for n in range(4)]
  461. G = nx.DiGraph()
  462. edges = [(0, 1), (0, 2), (0, 3), (2, 3)]
  463. G.add_edges_from((test_nodes[a], test_nodes[b]) for a, b in edges)
  464. sorting = list(nx.lexicographical_topological_sort(G, key=sorting_key))
  465. assert sorting == test_nodes
  466. def test_topological_generations():
  467. G = nx.DiGraph(
  468. {1: [2, 3], 2: [4, 5], 3: [7], 4: [], 5: [6, 7], 6: [], 7: []}
  469. ).reverse()
  470. # order within each generation is inconsequential
  471. generations = [sorted(gen) for gen in nx.topological_generations(G)]
  472. expected = [[4, 6, 7], [3, 5], [2], [1]]
  473. assert generations == expected
  474. MG = nx.MultiDiGraph(G.edges)
  475. MG.add_edge(2, 1)
  476. generations = [sorted(gen) for gen in nx.topological_generations(MG)]
  477. assert generations == expected
  478. def test_topological_generations_empty():
  479. G = nx.DiGraph()
  480. assert list(nx.topological_generations(G)) == []
  481. def test_topological_generations_cycle():
  482. G = nx.DiGraph([[2, 1], [3, 1], [1, 2]])
  483. with pytest.raises(nx.NetworkXUnfeasible):
  484. list(nx.topological_generations(G))
  485. def test_is_aperiodic_cycle():
  486. G = nx.DiGraph()
  487. nx.add_cycle(G, [1, 2, 3, 4])
  488. assert not nx.is_aperiodic(G)
  489. def test_is_aperiodic_cycle2():
  490. G = nx.DiGraph()
  491. nx.add_cycle(G, [1, 2, 3, 4])
  492. nx.add_cycle(G, [3, 4, 5, 6, 7])
  493. assert nx.is_aperiodic(G)
  494. def test_is_aperiodic_cycle3():
  495. G = nx.DiGraph()
  496. nx.add_cycle(G, [1, 2, 3, 4])
  497. nx.add_cycle(G, [3, 4, 5, 6])
  498. assert not nx.is_aperiodic(G)
  499. def test_is_aperiodic_cycle4():
  500. G = nx.DiGraph()
  501. nx.add_cycle(G, [1, 2, 3, 4])
  502. G.add_edge(1, 3)
  503. assert nx.is_aperiodic(G)
  504. def test_is_aperiodic_selfloop():
  505. G = nx.DiGraph()
  506. nx.add_cycle(G, [1, 2, 3, 4])
  507. G.add_edge(1, 1)
  508. assert nx.is_aperiodic(G)
  509. def test_is_aperiodic_raise():
  510. G = nx.Graph()
  511. pytest.raises(nx.NetworkXError, nx.is_aperiodic, G)
  512. def test_is_aperiodic_bipartite():
  513. # Bipartite graph
  514. G = nx.DiGraph(nx.davis_southern_women_graph())
  515. assert not nx.is_aperiodic(G)
  516. def test_is_aperiodic_rary_tree():
  517. G = nx.full_rary_tree(3, 27, create_using=nx.DiGraph())
  518. assert not nx.is_aperiodic(G)
  519. def test_is_aperiodic_disconnected():
  520. # disconnected graph
  521. G = nx.DiGraph()
  522. nx.add_cycle(G, [1, 2, 3, 4])
  523. nx.add_cycle(G, [5, 6, 7, 8])
  524. assert not nx.is_aperiodic(G)
  525. G.add_edge(1, 3)
  526. G.add_edge(5, 7)
  527. assert nx.is_aperiodic(G)
  528. def test_is_aperiodic_disconnected2():
  529. G = nx.DiGraph()
  530. nx.add_cycle(G, [0, 1, 2])
  531. G.add_edge(3, 3)
  532. assert not nx.is_aperiodic(G)
  533. class TestDagToBranching:
  534. """Unit tests for the :func:`networkx.dag_to_branching` function."""
  535. def test_single_root(self):
  536. """Tests that a directed acyclic graph with a single degree
  537. zero node produces an arborescence.
  538. """
  539. G = nx.DiGraph([(0, 1), (0, 2), (1, 3), (2, 3)])
  540. B = nx.dag_to_branching(G)
  541. expected = nx.DiGraph([(0, 1), (1, 3), (0, 2), (2, 4)])
  542. assert nx.is_arborescence(B)
  543. assert nx.is_isomorphic(B, expected)
  544. def test_multiple_roots(self):
  545. """Tests that a directed acyclic graph with multiple degree zero
  546. nodes creates an arborescence with multiple (weakly) connected
  547. components.
  548. """
  549. G = nx.DiGraph([(0, 1), (0, 2), (1, 3), (2, 3), (5, 2)])
  550. B = nx.dag_to_branching(G)
  551. expected = nx.DiGraph([(0, 1), (1, 3), (0, 2), (2, 4), (5, 6), (6, 7)])
  552. assert nx.is_branching(B)
  553. assert not nx.is_arborescence(B)
  554. assert nx.is_isomorphic(B, expected)
  555. # # Attributes are not copied by this function. If they were, this would
  556. # # be a good test to uncomment.
  557. # def test_copy_attributes(self):
  558. # """Tests that node attributes are copied in the branching."""
  559. # G = nx.DiGraph([(0, 1), (0, 2), (1, 3), (2, 3)])
  560. # for v in G:
  561. # G.node[v]['label'] = str(v)
  562. # B = nx.dag_to_branching(G)
  563. # # Determine the root node of the branching.
  564. # root = next(v for v, d in B.in_degree() if d == 0)
  565. # assert_equal(B.node[root]['label'], '0')
  566. # children = B[root]
  567. # # Get the left and right children, nodes 1 and 2, respectively.
  568. # left, right = sorted(children, key=lambda v: B.node[v]['label'])
  569. # assert_equal(B.node[left]['label'], '1')
  570. # assert_equal(B.node[right]['label'], '2')
  571. # # Get the left grandchild.
  572. # children = B[left]
  573. # assert_equal(len(children), 1)
  574. # left_grandchild = arbitrary_element(children)
  575. # assert_equal(B.node[left_grandchild]['label'], '3')
  576. # # Get the right grandchild.
  577. # children = B[right]
  578. # assert_equal(len(children), 1)
  579. # right_grandchild = arbitrary_element(children)
  580. # assert_equal(B.node[right_grandchild]['label'], '3')
  581. def test_already_arborescence(self):
  582. """Tests that a directed acyclic graph that is already an
  583. arborescence produces an isomorphic arborescence as output.
  584. """
  585. A = nx.balanced_tree(2, 2, create_using=nx.DiGraph())
  586. B = nx.dag_to_branching(A)
  587. assert nx.is_isomorphic(A, B)
  588. def test_already_branching(self):
  589. """Tests that a directed acyclic graph that is already a
  590. branching produces an isomorphic branching as output.
  591. """
  592. T1 = nx.balanced_tree(2, 2, create_using=nx.DiGraph())
  593. T2 = nx.balanced_tree(2, 2, create_using=nx.DiGraph())
  594. G = nx.disjoint_union(T1, T2)
  595. B = nx.dag_to_branching(G)
  596. assert nx.is_isomorphic(G, B)
  597. def test_not_acyclic(self):
  598. """Tests that a non-acyclic graph causes an exception."""
  599. with pytest.raises(nx.HasACycle):
  600. G = nx.DiGraph(pairwise("abc", cyclic=True))
  601. nx.dag_to_branching(G)
  602. def test_undirected(self):
  603. with pytest.raises(nx.NetworkXNotImplemented):
  604. nx.dag_to_branching(nx.Graph())
  605. def test_multigraph(self):
  606. with pytest.raises(nx.NetworkXNotImplemented):
  607. nx.dag_to_branching(nx.MultiGraph())
  608. def test_multidigraph(self):
  609. with pytest.raises(nx.NetworkXNotImplemented):
  610. nx.dag_to_branching(nx.MultiDiGraph())
  611. def test_ancestors_descendants_undirected():
  612. """Regression test to ensure anscestors and descendants work as expected on
  613. undirected graphs."""
  614. G = nx.path_graph(5)
  615. nx.ancestors(G, 2) == nx.descendants(G, 2) == {0, 1, 3, 4}
  616. def test_compute_v_structures_raise():
  617. G = nx.Graph()
  618. pytest.raises(nx.NetworkXNotImplemented, nx.compute_v_structures, G)
  619. def test_compute_v_structures():
  620. edges = [(0, 1), (0, 2), (3, 2)]
  621. G = nx.DiGraph(edges)
  622. v_structs = set(nx.compute_v_structures(G))
  623. assert len(v_structs) == 1
  624. assert (0, 2, 3) in v_structs
  625. edges = [("A", "B"), ("C", "B"), ("B", "D"), ("D", "E"), ("G", "E")]
  626. G = nx.DiGraph(edges)
  627. v_structs = set(nx.compute_v_structures(G))
  628. assert len(v_structs) == 2