test_euler.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. import collections
  2. import pytest
  3. import networkx as nx
  4. class TestIsEulerian:
  5. def test_is_eulerian(self):
  6. assert nx.is_eulerian(nx.complete_graph(5))
  7. assert nx.is_eulerian(nx.complete_graph(7))
  8. assert nx.is_eulerian(nx.hypercube_graph(4))
  9. assert nx.is_eulerian(nx.hypercube_graph(6))
  10. assert not nx.is_eulerian(nx.complete_graph(4))
  11. assert not nx.is_eulerian(nx.complete_graph(6))
  12. assert not nx.is_eulerian(nx.hypercube_graph(3))
  13. assert not nx.is_eulerian(nx.hypercube_graph(5))
  14. assert not nx.is_eulerian(nx.petersen_graph())
  15. assert not nx.is_eulerian(nx.path_graph(4))
  16. def test_is_eulerian2(self):
  17. # not connected
  18. G = nx.Graph()
  19. G.add_nodes_from([1, 2, 3])
  20. assert not nx.is_eulerian(G)
  21. # not strongly connected
  22. G = nx.DiGraph()
  23. G.add_nodes_from([1, 2, 3])
  24. assert not nx.is_eulerian(G)
  25. G = nx.MultiDiGraph()
  26. G.add_edge(1, 2)
  27. G.add_edge(2, 3)
  28. G.add_edge(2, 3)
  29. G.add_edge(3, 1)
  30. assert not nx.is_eulerian(G)
  31. class TestEulerianCircuit:
  32. def test_eulerian_circuit_cycle(self):
  33. G = nx.cycle_graph(4)
  34. edges = list(nx.eulerian_circuit(G, source=0))
  35. nodes = [u for u, v in edges]
  36. assert nodes == [0, 3, 2, 1]
  37. assert edges == [(0, 3), (3, 2), (2, 1), (1, 0)]
  38. edges = list(nx.eulerian_circuit(G, source=1))
  39. nodes = [u for u, v in edges]
  40. assert nodes == [1, 2, 3, 0]
  41. assert edges == [(1, 2), (2, 3), (3, 0), (0, 1)]
  42. G = nx.complete_graph(3)
  43. edges = list(nx.eulerian_circuit(G, source=0))
  44. nodes = [u for u, v in edges]
  45. assert nodes == [0, 2, 1]
  46. assert edges == [(0, 2), (2, 1), (1, 0)]
  47. edges = list(nx.eulerian_circuit(G, source=1))
  48. nodes = [u for u, v in edges]
  49. assert nodes == [1, 2, 0]
  50. assert edges == [(1, 2), (2, 0), (0, 1)]
  51. def test_eulerian_circuit_digraph(self):
  52. G = nx.DiGraph()
  53. nx.add_cycle(G, [0, 1, 2, 3])
  54. edges = list(nx.eulerian_circuit(G, source=0))
  55. nodes = [u for u, v in edges]
  56. assert nodes == [0, 1, 2, 3]
  57. assert edges == [(0, 1), (1, 2), (2, 3), (3, 0)]
  58. edges = list(nx.eulerian_circuit(G, source=1))
  59. nodes = [u for u, v in edges]
  60. assert nodes == [1, 2, 3, 0]
  61. assert edges == [(1, 2), (2, 3), (3, 0), (0, 1)]
  62. def test_multigraph(self):
  63. G = nx.MultiGraph()
  64. nx.add_cycle(G, [0, 1, 2, 3])
  65. G.add_edge(1, 2)
  66. G.add_edge(1, 2)
  67. edges = list(nx.eulerian_circuit(G, source=0))
  68. nodes = [u for u, v in edges]
  69. assert nodes == [0, 3, 2, 1, 2, 1]
  70. assert edges == [(0, 3), (3, 2), (2, 1), (1, 2), (2, 1), (1, 0)]
  71. def test_multigraph_with_keys(self):
  72. G = nx.MultiGraph()
  73. nx.add_cycle(G, [0, 1, 2, 3])
  74. G.add_edge(1, 2)
  75. G.add_edge(1, 2)
  76. edges = list(nx.eulerian_circuit(G, source=0, keys=True))
  77. nodes = [u for u, v, k in edges]
  78. assert nodes == [0, 3, 2, 1, 2, 1]
  79. assert edges[:2] == [(0, 3, 0), (3, 2, 0)]
  80. assert collections.Counter(edges[2:5]) == collections.Counter(
  81. [(2, 1, 0), (1, 2, 1), (2, 1, 2)]
  82. )
  83. assert edges[5:] == [(1, 0, 0)]
  84. def test_not_eulerian(self):
  85. with pytest.raises(nx.NetworkXError):
  86. f = list(nx.eulerian_circuit(nx.complete_graph(4)))
  87. class TestIsSemiEulerian:
  88. def test_is_semieulerian(self):
  89. # Test graphs with Eulerian paths but no cycles return True.
  90. assert nx.is_semieulerian(nx.path_graph(4))
  91. G = nx.path_graph(6, create_using=nx.DiGraph)
  92. assert nx.is_semieulerian(G)
  93. # Test graphs with Eulerian cycles return False.
  94. assert not nx.is_semieulerian(nx.complete_graph(5))
  95. assert not nx.is_semieulerian(nx.complete_graph(7))
  96. assert not nx.is_semieulerian(nx.hypercube_graph(4))
  97. assert not nx.is_semieulerian(nx.hypercube_graph(6))
  98. class TestHasEulerianPath:
  99. def test_has_eulerian_path_cyclic(self):
  100. # Test graphs with Eulerian cycles return True.
  101. assert nx.has_eulerian_path(nx.complete_graph(5))
  102. assert nx.has_eulerian_path(nx.complete_graph(7))
  103. assert nx.has_eulerian_path(nx.hypercube_graph(4))
  104. assert nx.has_eulerian_path(nx.hypercube_graph(6))
  105. def test_has_eulerian_path_non_cyclic(self):
  106. # Test graphs with Eulerian paths but no cycles return True.
  107. assert nx.has_eulerian_path(nx.path_graph(4))
  108. G = nx.path_graph(6, create_using=nx.DiGraph)
  109. assert nx.has_eulerian_path(G)
  110. def test_has_eulerian_path_directed_graph(self):
  111. # Test directed graphs and returns False
  112. G = nx.DiGraph()
  113. G.add_edges_from([(0, 1), (1, 2), (0, 2)])
  114. assert not nx.has_eulerian_path(G)
  115. # Test directed graphs without isolated node returns True
  116. G = nx.DiGraph()
  117. G.add_edges_from([(0, 1), (1, 2), (2, 0)])
  118. assert nx.has_eulerian_path(G)
  119. # Test directed graphs with isolated node returns False
  120. G.add_node(3)
  121. assert not nx.has_eulerian_path(G)
  122. @pytest.mark.parametrize("G", (nx.Graph(), nx.DiGraph()))
  123. def test_has_eulerian_path_not_weakly_connected(self, G):
  124. G.add_edges_from([(0, 1), (2, 3), (3, 2)])
  125. assert not nx.has_eulerian_path(G)
  126. @pytest.mark.parametrize("G", (nx.Graph(), nx.DiGraph()))
  127. def test_has_eulerian_path_unbalancedins_more_than_one(self, G):
  128. G.add_edges_from([(0, 1), (2, 3)])
  129. assert not nx.has_eulerian_path(G)
  130. class TestFindPathStart:
  131. def testfind_path_start(self):
  132. find_path_start = nx.algorithms.euler._find_path_start
  133. # Test digraphs return correct starting node.
  134. G = nx.path_graph(6, create_using=nx.DiGraph)
  135. assert find_path_start(G) == 0
  136. edges = [(0, 1), (1, 2), (2, 0), (4, 0)]
  137. assert find_path_start(nx.DiGraph(edges)) == 4
  138. # Test graph with no Eulerian path return None.
  139. edges = [(0, 1), (1, 2), (2, 3), (2, 4)]
  140. assert find_path_start(nx.DiGraph(edges)) is None
  141. class TestEulerianPath:
  142. def test_eulerian_path(self):
  143. x = [(4, 0), (0, 1), (1, 2), (2, 0)]
  144. for e1, e2 in zip(x, nx.eulerian_path(nx.DiGraph(x))):
  145. assert e1 == e2
  146. def test_eulerian_path_straight_link(self):
  147. G = nx.DiGraph()
  148. result = [(1, 2), (2, 3), (3, 4), (4, 5)]
  149. G.add_edges_from(result)
  150. assert result == list(nx.eulerian_path(G))
  151. assert result == list(nx.eulerian_path(G, source=1))
  152. with pytest.raises(nx.NetworkXError):
  153. list(nx.eulerian_path(G, source=3))
  154. with pytest.raises(nx.NetworkXError):
  155. list(nx.eulerian_path(G, source=4))
  156. with pytest.raises(nx.NetworkXError):
  157. list(nx.eulerian_path(G, source=5))
  158. def test_eulerian_path_multigraph(self):
  159. G = nx.MultiDiGraph()
  160. result = [(2, 1), (1, 2), (2, 1), (1, 2), (2, 3), (3, 4), (4, 3)]
  161. G.add_edges_from(result)
  162. assert result == list(nx.eulerian_path(G))
  163. assert result == list(nx.eulerian_path(G, source=2))
  164. with pytest.raises(nx.NetworkXError):
  165. list(nx.eulerian_path(G, source=3))
  166. with pytest.raises(nx.NetworkXError):
  167. list(nx.eulerian_path(G, source=4))
  168. def test_eulerian_path_eulerian_circuit(self):
  169. G = nx.DiGraph()
  170. result = [(1, 2), (2, 3), (3, 4), (4, 1)]
  171. result2 = [(2, 3), (3, 4), (4, 1), (1, 2)]
  172. result3 = [(3, 4), (4, 1), (1, 2), (2, 3)]
  173. G.add_edges_from(result)
  174. assert result == list(nx.eulerian_path(G))
  175. assert result == list(nx.eulerian_path(G, source=1))
  176. assert result2 == list(nx.eulerian_path(G, source=2))
  177. assert result3 == list(nx.eulerian_path(G, source=3))
  178. def test_eulerian_path_undirected(self):
  179. G = nx.Graph()
  180. result = [(1, 2), (2, 3), (3, 4), (4, 5)]
  181. result2 = [(5, 4), (4, 3), (3, 2), (2, 1)]
  182. G.add_edges_from(result)
  183. assert list(nx.eulerian_path(G)) in (result, result2)
  184. assert result == list(nx.eulerian_path(G, source=1))
  185. assert result2 == list(nx.eulerian_path(G, source=5))
  186. with pytest.raises(nx.NetworkXError):
  187. list(nx.eulerian_path(G, source=3))
  188. with pytest.raises(nx.NetworkXError):
  189. list(nx.eulerian_path(G, source=2))
  190. def test_eulerian_path_multigraph_undirected(self):
  191. G = nx.MultiGraph()
  192. result = [(2, 1), (1, 2), (2, 1), (1, 2), (2, 3), (3, 4)]
  193. G.add_edges_from(result)
  194. assert result == list(nx.eulerian_path(G))
  195. assert result == list(nx.eulerian_path(G, source=2))
  196. with pytest.raises(nx.NetworkXError):
  197. list(nx.eulerian_path(G, source=3))
  198. with pytest.raises(nx.NetworkXError):
  199. list(nx.eulerian_path(G, source=1))
  200. class TestEulerize:
  201. def test_disconnected(self):
  202. with pytest.raises(nx.NetworkXError):
  203. G = nx.from_edgelist([(0, 1), (2, 3)])
  204. nx.eulerize(G)
  205. def test_null_graph(self):
  206. with pytest.raises(nx.NetworkXPointlessConcept):
  207. nx.eulerize(nx.Graph())
  208. def test_null_multigraph(self):
  209. with pytest.raises(nx.NetworkXPointlessConcept):
  210. nx.eulerize(nx.MultiGraph())
  211. def test_on_empty_graph(self):
  212. with pytest.raises(nx.NetworkXError):
  213. nx.eulerize(nx.empty_graph(3))
  214. def test_on_eulerian(self):
  215. G = nx.cycle_graph(3)
  216. H = nx.eulerize(G)
  217. assert nx.is_isomorphic(G, H)
  218. def test_on_eulerian_multigraph(self):
  219. G = nx.MultiGraph(nx.cycle_graph(3))
  220. G.add_edge(0, 1)
  221. H = nx.eulerize(G)
  222. assert nx.is_eulerian(H)
  223. def test_on_complete_graph(self):
  224. G = nx.complete_graph(4)
  225. assert nx.is_eulerian(nx.eulerize(G))
  226. assert nx.is_eulerian(nx.eulerize(nx.MultiGraph(G)))
  227. def test_on_non_eulerian_graph(self):
  228. G = nx.cycle_graph(18)
  229. G.add_edge(0, 18)
  230. G.add_edge(18, 19)
  231. G.add_edge(17, 19)
  232. G.add_edge(4, 20)
  233. G.add_edge(20, 21)
  234. G.add_edge(21, 22)
  235. G.add_edge(22, 23)
  236. G.add_edge(23, 24)
  237. G.add_edge(24, 25)
  238. G.add_edge(25, 26)
  239. G.add_edge(26, 27)
  240. G.add_edge(27, 28)
  241. G.add_edge(28, 13)
  242. assert not nx.is_eulerian(G)
  243. G = nx.eulerize(G)
  244. assert nx.is_eulerian(G)
  245. assert nx.number_of_edges(G) == 39