test_dfs.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. import networkx as nx
  2. class TestDFS:
  3. @classmethod
  4. def setup_class(cls):
  5. # simple graph
  6. G = nx.Graph()
  7. G.add_edges_from([(0, 1), (1, 2), (1, 3), (2, 4), (3, 0), (0, 4)])
  8. cls.G = G
  9. # simple graph, disconnected
  10. D = nx.Graph()
  11. D.add_edges_from([(0, 1), (2, 3)])
  12. cls.D = D
  13. def test_preorder_nodes(self):
  14. assert list(nx.dfs_preorder_nodes(self.G, source=0)) == [0, 1, 2, 4, 3]
  15. assert list(nx.dfs_preorder_nodes(self.D)) == [0, 1, 2, 3]
  16. assert list(nx.dfs_preorder_nodes(self.D, source=2)) == [2, 3]
  17. def test_postorder_nodes(self):
  18. assert list(nx.dfs_postorder_nodes(self.G, source=0)) == [4, 2, 3, 1, 0]
  19. assert list(nx.dfs_postorder_nodes(self.D)) == [1, 0, 3, 2]
  20. assert list(nx.dfs_postorder_nodes(self.D, source=0)) == [1, 0]
  21. def test_successor(self):
  22. assert nx.dfs_successors(self.G, source=0) == {0: [1], 1: [2, 3], 2: [4]}
  23. assert nx.dfs_successors(self.G, source=1) == {0: [3, 4], 1: [0], 4: [2]}
  24. assert nx.dfs_successors(self.D) == {0: [1], 2: [3]}
  25. assert nx.dfs_successors(self.D, source=1) == {1: [0]}
  26. def test_predecessor(self):
  27. assert nx.dfs_predecessors(self.G, source=0) == {1: 0, 2: 1, 3: 1, 4: 2}
  28. assert nx.dfs_predecessors(self.D) == {1: 0, 3: 2}
  29. def test_dfs_tree(self):
  30. exp_nodes = sorted(self.G.nodes())
  31. exp_edges = [(0, 1), (1, 2), (1, 3), (2, 4)]
  32. # Search from first node
  33. T = nx.dfs_tree(self.G, source=0)
  34. assert sorted(T.nodes()) == exp_nodes
  35. assert sorted(T.edges()) == exp_edges
  36. # Check source=None
  37. T = nx.dfs_tree(self.G, source=None)
  38. assert sorted(T.nodes()) == exp_nodes
  39. assert sorted(T.edges()) == exp_edges
  40. # Check source=None is the default
  41. T = nx.dfs_tree(self.G)
  42. assert sorted(T.nodes()) == exp_nodes
  43. assert sorted(T.edges()) == exp_edges
  44. def test_dfs_edges(self):
  45. edges = nx.dfs_edges(self.G, source=0)
  46. assert list(edges) == [(0, 1), (1, 2), (2, 4), (1, 3)]
  47. edges = nx.dfs_edges(self.D)
  48. assert list(edges) == [(0, 1), (2, 3)]
  49. def test_dfs_labeled_edges(self):
  50. edges = list(nx.dfs_labeled_edges(self.G, source=0))
  51. forward = [(u, v) for (u, v, d) in edges if d == "forward"]
  52. assert forward == [(0, 0), (0, 1), (1, 2), (2, 4), (1, 3)]
  53. assert edges == [
  54. (0, 0, "forward"),
  55. (0, 1, "forward"),
  56. (1, 0, "nontree"),
  57. (1, 2, "forward"),
  58. (2, 1, "nontree"),
  59. (2, 4, "forward"),
  60. (4, 2, "nontree"),
  61. (4, 0, "nontree"),
  62. (2, 4, "reverse"),
  63. (1, 2, "reverse"),
  64. (1, 3, "forward"),
  65. (3, 1, "nontree"),
  66. (3, 0, "nontree"),
  67. (1, 3, "reverse"),
  68. (0, 1, "reverse"),
  69. (0, 3, "nontree"),
  70. (0, 4, "nontree"),
  71. (0, 0, "reverse"),
  72. ]
  73. def test_dfs_labeled_disconnected_edges(self):
  74. edges = list(nx.dfs_labeled_edges(self.D))
  75. forward = [(u, v) for (u, v, d) in edges if d == "forward"]
  76. assert forward == [(0, 0), (0, 1), (2, 2), (2, 3)]
  77. assert edges == [
  78. (0, 0, "forward"),
  79. (0, 1, "forward"),
  80. (1, 0, "nontree"),
  81. (0, 1, "reverse"),
  82. (0, 0, "reverse"),
  83. (2, 2, "forward"),
  84. (2, 3, "forward"),
  85. (3, 2, "nontree"),
  86. (2, 3, "reverse"),
  87. (2, 2, "reverse"),
  88. ]
  89. def test_dfs_tree_isolates(self):
  90. G = nx.Graph()
  91. G.add_node(1)
  92. G.add_node(2)
  93. T = nx.dfs_tree(G, source=1)
  94. assert sorted(T.nodes()) == [1]
  95. assert sorted(T.edges()) == []
  96. T = nx.dfs_tree(G, source=None)
  97. assert sorted(T.nodes()) == [1, 2]
  98. assert sorted(T.edges()) == []
  99. class TestDepthLimitedSearch:
  100. @classmethod
  101. def setup_class(cls):
  102. # a tree
  103. G = nx.Graph()
  104. nx.add_path(G, [0, 1, 2, 3, 4, 5, 6])
  105. nx.add_path(G, [2, 7, 8, 9, 10])
  106. cls.G = G
  107. # a disconnected graph
  108. D = nx.Graph()
  109. D.add_edges_from([(0, 1), (2, 3)])
  110. nx.add_path(D, [2, 7, 8, 9, 10])
  111. cls.D = D
  112. def test_dls_preorder_nodes(self):
  113. assert list(nx.dfs_preorder_nodes(self.G, source=0, depth_limit=2)) == [0, 1, 2]
  114. assert list(nx.dfs_preorder_nodes(self.D, source=1, depth_limit=2)) == ([1, 0])
  115. def test_dls_postorder_nodes(self):
  116. assert list(nx.dfs_postorder_nodes(self.G, source=3, depth_limit=3)) == [
  117. 1,
  118. 7,
  119. 2,
  120. 5,
  121. 4,
  122. 3,
  123. ]
  124. assert list(nx.dfs_postorder_nodes(self.D, source=2, depth_limit=2)) == (
  125. [3, 7, 2]
  126. )
  127. def test_dls_successor(self):
  128. result = nx.dfs_successors(self.G, source=4, depth_limit=3)
  129. assert {n: set(v) for n, v in result.items()} == {
  130. 2: {1, 7},
  131. 3: {2},
  132. 4: {3, 5},
  133. 5: {6},
  134. }
  135. result = nx.dfs_successors(self.D, source=7, depth_limit=2)
  136. assert {n: set(v) for n, v in result.items()} == {8: {9}, 2: {3}, 7: {8, 2}}
  137. def test_dls_predecessor(self):
  138. assert nx.dfs_predecessors(self.G, source=0, depth_limit=3) == {
  139. 1: 0,
  140. 2: 1,
  141. 3: 2,
  142. 7: 2,
  143. }
  144. assert nx.dfs_predecessors(self.D, source=2, depth_limit=3) == {
  145. 8: 7,
  146. 9: 8,
  147. 3: 2,
  148. 7: 2,
  149. }
  150. def test_dls_tree(self):
  151. T = nx.dfs_tree(self.G, source=3, depth_limit=1)
  152. assert sorted(T.edges()) == [(3, 2), (3, 4)]
  153. def test_dls_edges(self):
  154. edges = nx.dfs_edges(self.G, source=9, depth_limit=4)
  155. assert list(edges) == [(9, 8), (8, 7), (7, 2), (2, 1), (2, 3), (9, 10)]
  156. def test_dls_labeled_edges_depth_1(self):
  157. edges = list(nx.dfs_labeled_edges(self.G, source=5, depth_limit=1))
  158. forward = [(u, v) for (u, v, d) in edges if d == "forward"]
  159. assert forward == [(5, 5), (5, 4), (5, 6)]
  160. # Note: reverse-depth_limit edge types were not reported before gh-6240
  161. assert edges == [
  162. (5, 5, "forward"),
  163. (5, 4, "forward"),
  164. (5, 4, "reverse-depth_limit"),
  165. (5, 6, "forward"),
  166. (5, 6, "reverse-depth_limit"),
  167. (5, 5, "reverse"),
  168. ]
  169. def test_dls_labeled_edges_depth_2(self):
  170. edges = list(nx.dfs_labeled_edges(self.G, source=6, depth_limit=2))
  171. forward = [(u, v) for (u, v, d) in edges if d == "forward"]
  172. assert forward == [(6, 6), (6, 5), (5, 4)]
  173. assert edges == [
  174. (6, 6, "forward"),
  175. (6, 5, "forward"),
  176. (5, 4, "forward"),
  177. (5, 4, "reverse-depth_limit"),
  178. (5, 6, "nontree"),
  179. (6, 5, "reverse"),
  180. (6, 6, "reverse"),
  181. ]
  182. def test_dls_labeled_disconnected_edges(self):
  183. edges = list(nx.dfs_labeled_edges(self.D, depth_limit=1))
  184. assert edges == [
  185. (0, 0, "forward"),
  186. (0, 1, "forward"),
  187. (0, 1, "reverse-depth_limit"),
  188. (0, 0, "reverse"),
  189. (2, 2, "forward"),
  190. (2, 3, "forward"),
  191. (2, 3, "reverse-depth_limit"),
  192. (2, 7, "forward"),
  193. (2, 7, "reverse-depth_limit"),
  194. (2, 2, "reverse"),
  195. (8, 8, "forward"),
  196. (8, 7, "nontree"),
  197. (8, 9, "forward"),
  198. (8, 9, "reverse-depth_limit"),
  199. (8, 8, "reverse"),
  200. (10, 10, "forward"),
  201. (10, 9, "nontree"),
  202. (10, 10, "reverse"),
  203. ]
  204. # large depth_limit has no impact
  205. edges = list(nx.dfs_labeled_edges(self.D, depth_limit=19))
  206. assert edges == [
  207. (0, 0, "forward"),
  208. (0, 1, "forward"),
  209. (1, 0, "nontree"),
  210. (0, 1, "reverse"),
  211. (0, 0, "reverse"),
  212. (2, 2, "forward"),
  213. (2, 3, "forward"),
  214. (3, 2, "nontree"),
  215. (2, 3, "reverse"),
  216. (2, 7, "forward"),
  217. (7, 2, "nontree"),
  218. (7, 8, "forward"),
  219. (8, 7, "nontree"),
  220. (8, 9, "forward"),
  221. (9, 8, "nontree"),
  222. (9, 10, "forward"),
  223. (10, 9, "nontree"),
  224. (9, 10, "reverse"),
  225. (8, 9, "reverse"),
  226. (7, 8, "reverse"),
  227. (2, 7, "reverse"),
  228. (2, 2, "reverse"),
  229. ]