test_digraph.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. import pytest
  2. import networkx as nx
  3. from networkx.utils import nodes_equal
  4. from .test_graph import BaseAttrGraphTester, BaseGraphTester
  5. from .test_graph import TestEdgeSubgraph as _TestGraphEdgeSubgraph
  6. from .test_graph import TestGraph as _TestGraph
  7. class BaseDiGraphTester(BaseGraphTester):
  8. def test_has_successor(self):
  9. G = self.K3
  10. assert G.has_successor(0, 1)
  11. assert not G.has_successor(0, -1)
  12. def test_successors(self):
  13. G = self.K3
  14. assert sorted(G.successors(0)) == [1, 2]
  15. with pytest.raises(nx.NetworkXError):
  16. G.successors(-1)
  17. def test_has_predecessor(self):
  18. G = self.K3
  19. assert G.has_predecessor(0, 1)
  20. assert not G.has_predecessor(0, -1)
  21. def test_predecessors(self):
  22. G = self.K3
  23. assert sorted(G.predecessors(0)) == [1, 2]
  24. with pytest.raises(nx.NetworkXError):
  25. G.predecessors(-1)
  26. def test_edges(self):
  27. G = self.K3
  28. assert sorted(G.edges()) == [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
  29. assert sorted(G.edges(0)) == [(0, 1), (0, 2)]
  30. assert sorted(G.edges([0, 1])) == [(0, 1), (0, 2), (1, 0), (1, 2)]
  31. with pytest.raises(nx.NetworkXError):
  32. G.edges(-1)
  33. def test_out_edges(self):
  34. G = self.K3
  35. assert sorted(G.out_edges()) == [(0, 1), (0, 2), (1, 0), (1, 2), (2, 0), (2, 1)]
  36. assert sorted(G.out_edges(0)) == [(0, 1), (0, 2)]
  37. with pytest.raises(nx.NetworkXError):
  38. G.out_edges(-1)
  39. def test_out_edges_dir(self):
  40. G = self.P3
  41. assert sorted(G.out_edges()) == [(0, 1), (1, 2)]
  42. assert sorted(G.out_edges(0)) == [(0, 1)]
  43. assert sorted(G.out_edges(2)) == []
  44. def test_out_edges_data(self):
  45. G = nx.DiGraph([(0, 1, {"data": 0}), (1, 0, {})])
  46. assert sorted(G.out_edges(data=True)) == [(0, 1, {"data": 0}), (1, 0, {})]
  47. assert sorted(G.out_edges(0, data=True)) == [(0, 1, {"data": 0})]
  48. assert sorted(G.out_edges(data="data")) == [(0, 1, 0), (1, 0, None)]
  49. assert sorted(G.out_edges(0, data="data")) == [(0, 1, 0)]
  50. def test_in_edges_dir(self):
  51. G = self.P3
  52. assert sorted(G.in_edges()) == [(0, 1), (1, 2)]
  53. assert sorted(G.in_edges(0)) == []
  54. assert sorted(G.in_edges(2)) == [(1, 2)]
  55. def test_in_edges_data(self):
  56. G = nx.DiGraph([(0, 1, {"data": 0}), (1, 0, {})])
  57. assert sorted(G.in_edges(data=True)) == [(0, 1, {"data": 0}), (1, 0, {})]
  58. assert sorted(G.in_edges(1, data=True)) == [(0, 1, {"data": 0})]
  59. assert sorted(G.in_edges(data="data")) == [(0, 1, 0), (1, 0, None)]
  60. assert sorted(G.in_edges(1, data="data")) == [(0, 1, 0)]
  61. def test_degree(self):
  62. G = self.K3
  63. assert sorted(G.degree()) == [(0, 4), (1, 4), (2, 4)]
  64. assert dict(G.degree()) == {0: 4, 1: 4, 2: 4}
  65. assert G.degree(0) == 4
  66. assert list(G.degree(iter([0]))) == [(0, 4)] # run through iterator
  67. def test_in_degree(self):
  68. G = self.K3
  69. assert sorted(G.in_degree()) == [(0, 2), (1, 2), (2, 2)]
  70. assert dict(G.in_degree()) == {0: 2, 1: 2, 2: 2}
  71. assert G.in_degree(0) == 2
  72. assert list(G.in_degree(iter([0]))) == [(0, 2)] # run through iterator
  73. def test_out_degree(self):
  74. G = self.K3
  75. assert sorted(G.out_degree()) == [(0, 2), (1, 2), (2, 2)]
  76. assert dict(G.out_degree()) == {0: 2, 1: 2, 2: 2}
  77. assert G.out_degree(0) == 2
  78. assert list(G.out_degree(iter([0]))) == [(0, 2)]
  79. def test_size(self):
  80. G = self.K3
  81. assert G.size() == 6
  82. assert G.number_of_edges() == 6
  83. def test_to_undirected_reciprocal(self):
  84. G = self.Graph()
  85. G.add_edge(1, 2)
  86. assert G.to_undirected().has_edge(1, 2)
  87. assert not G.to_undirected(reciprocal=True).has_edge(1, 2)
  88. G.add_edge(2, 1)
  89. assert G.to_undirected(reciprocal=True).has_edge(1, 2)
  90. def test_reverse_copy(self):
  91. G = nx.DiGraph([(0, 1), (1, 2)])
  92. R = G.reverse()
  93. assert sorted(R.edges()) == [(1, 0), (2, 1)]
  94. R.remove_edge(1, 0)
  95. assert sorted(R.edges()) == [(2, 1)]
  96. assert sorted(G.edges()) == [(0, 1), (1, 2)]
  97. def test_reverse_nocopy(self):
  98. G = nx.DiGraph([(0, 1), (1, 2)])
  99. R = G.reverse(copy=False)
  100. assert sorted(R.edges()) == [(1, 0), (2, 1)]
  101. with pytest.raises(nx.NetworkXError):
  102. R.remove_edge(1, 0)
  103. def test_reverse_hashable(self):
  104. class Foo:
  105. pass
  106. x = Foo()
  107. y = Foo()
  108. G = nx.DiGraph()
  109. G.add_edge(x, y)
  110. assert nodes_equal(G.nodes(), G.reverse().nodes())
  111. assert [(y, x)] == list(G.reverse().edges())
  112. def test_di_cache_reset(self):
  113. G = self.K3.copy()
  114. old_succ = G.succ
  115. assert id(G.succ) == id(old_succ)
  116. old_adj = G.adj
  117. assert id(G.adj) == id(old_adj)
  118. G._succ = {}
  119. assert id(G.succ) != id(old_succ)
  120. assert id(G.adj) != id(old_adj)
  121. old_pred = G.pred
  122. assert id(G.pred) == id(old_pred)
  123. G._pred = {}
  124. assert id(G.pred) != id(old_pred)
  125. def test_di_attributes_cached(self):
  126. G = self.K3.copy()
  127. assert id(G.in_edges) == id(G.in_edges)
  128. assert id(G.out_edges) == id(G.out_edges)
  129. assert id(G.in_degree) == id(G.in_degree)
  130. assert id(G.out_degree) == id(G.out_degree)
  131. assert id(G.succ) == id(G.succ)
  132. assert id(G.pred) == id(G.pred)
  133. class BaseAttrDiGraphTester(BaseDiGraphTester, BaseAttrGraphTester):
  134. def test_edges_data(self):
  135. G = self.K3
  136. all_edges = [
  137. (0, 1, {}),
  138. (0, 2, {}),
  139. (1, 0, {}),
  140. (1, 2, {}),
  141. (2, 0, {}),
  142. (2, 1, {}),
  143. ]
  144. assert sorted(G.edges(data=True)) == all_edges
  145. assert sorted(G.edges(0, data=True)) == all_edges[:2]
  146. assert sorted(G.edges([0, 1], data=True)) == all_edges[:4]
  147. with pytest.raises(nx.NetworkXError):
  148. G.edges(-1, True)
  149. def test_in_degree_weighted(self):
  150. G = self.K3.copy()
  151. G.add_edge(0, 1, weight=0.3, other=1.2)
  152. assert sorted(G.in_degree(weight="weight")) == [(0, 2), (1, 1.3), (2, 2)]
  153. assert dict(G.in_degree(weight="weight")) == {0: 2, 1: 1.3, 2: 2}
  154. assert G.in_degree(1, weight="weight") == 1.3
  155. assert sorted(G.in_degree(weight="other")) == [(0, 2), (1, 2.2), (2, 2)]
  156. assert dict(G.in_degree(weight="other")) == {0: 2, 1: 2.2, 2: 2}
  157. assert G.in_degree(1, weight="other") == 2.2
  158. assert list(G.in_degree(iter([1]), weight="other")) == [(1, 2.2)]
  159. def test_out_degree_weighted(self):
  160. G = self.K3.copy()
  161. G.add_edge(0, 1, weight=0.3, other=1.2)
  162. assert sorted(G.out_degree(weight="weight")) == [(0, 1.3), (1, 2), (2, 2)]
  163. assert dict(G.out_degree(weight="weight")) == {0: 1.3, 1: 2, 2: 2}
  164. assert G.out_degree(0, weight="weight") == 1.3
  165. assert sorted(G.out_degree(weight="other")) == [(0, 2.2), (1, 2), (2, 2)]
  166. assert dict(G.out_degree(weight="other")) == {0: 2.2, 1: 2, 2: 2}
  167. assert G.out_degree(0, weight="other") == 2.2
  168. assert list(G.out_degree(iter([0]), weight="other")) == [(0, 2.2)]
  169. class TestDiGraph(BaseAttrDiGraphTester, _TestGraph):
  170. """Tests specific to dict-of-dict-of-dict digraph data structure"""
  171. def setup_method(self):
  172. self.Graph = nx.DiGraph
  173. # build dict-of-dict-of-dict K3
  174. ed1, ed2, ed3, ed4, ed5, ed6 = ({}, {}, {}, {}, {}, {})
  175. self.k3adj = {0: {1: ed1, 2: ed2}, 1: {0: ed3, 2: ed4}, 2: {0: ed5, 1: ed6}}
  176. self.k3edges = [(0, 1), (0, 2), (1, 2)]
  177. self.k3nodes = [0, 1, 2]
  178. self.K3 = self.Graph()
  179. self.K3._succ = self.k3adj # K3._adj is synced with K3._succ
  180. self.K3._pred = {0: {1: ed3, 2: ed5}, 1: {0: ed1, 2: ed6}, 2: {0: ed2, 1: ed4}}
  181. self.K3._node = {}
  182. self.K3._node[0] = {}
  183. self.K3._node[1] = {}
  184. self.K3._node[2] = {}
  185. ed1, ed2 = ({}, {})
  186. self.P3 = self.Graph()
  187. self.P3._succ = {0: {1: ed1}, 1: {2: ed2}, 2: {}}
  188. self.P3._pred = {0: {}, 1: {0: ed1}, 2: {1: ed2}}
  189. # P3._adj is synced with P3._succ
  190. self.P3._node = {}
  191. self.P3._node[0] = {}
  192. self.P3._node[1] = {}
  193. self.P3._node[2] = {}
  194. def test_data_input(self):
  195. G = self.Graph({1: [2], 2: [1]}, name="test")
  196. assert G.name == "test"
  197. assert sorted(G.adj.items()) == [(1, {2: {}}), (2, {1: {}})]
  198. assert sorted(G.succ.items()) == [(1, {2: {}}), (2, {1: {}})]
  199. assert sorted(G.pred.items()) == [(1, {2: {}}), (2, {1: {}})]
  200. def test_add_edge(self):
  201. G = self.Graph()
  202. G.add_edge(0, 1)
  203. assert G.adj == {0: {1: {}}, 1: {}}
  204. assert G.succ == {0: {1: {}}, 1: {}}
  205. assert G.pred == {0: {}, 1: {0: {}}}
  206. G = self.Graph()
  207. G.add_edge(*(0, 1))
  208. assert G.adj == {0: {1: {}}, 1: {}}
  209. assert G.succ == {0: {1: {}}, 1: {}}
  210. assert G.pred == {0: {}, 1: {0: {}}}
  211. with pytest.raises(ValueError, match="None cannot be a node"):
  212. G.add_edge(None, 3)
  213. def test_add_edges_from(self):
  214. G = self.Graph()
  215. G.add_edges_from([(0, 1), (0, 2, {"data": 3})], data=2)
  216. assert G.adj == {0: {1: {"data": 2}, 2: {"data": 3}}, 1: {}, 2: {}}
  217. assert G.succ == {0: {1: {"data": 2}, 2: {"data": 3}}, 1: {}, 2: {}}
  218. assert G.pred == {0: {}, 1: {0: {"data": 2}}, 2: {0: {"data": 3}}}
  219. with pytest.raises(nx.NetworkXError):
  220. G.add_edges_from([(0,)]) # too few in tuple
  221. with pytest.raises(nx.NetworkXError):
  222. G.add_edges_from([(0, 1, 2, 3)]) # too many in tuple
  223. with pytest.raises(TypeError):
  224. G.add_edges_from([0]) # not a tuple
  225. with pytest.raises(ValueError, match="None cannot be a node"):
  226. G.add_edges_from([(None, 3), (3, 2)])
  227. def test_remove_edge(self):
  228. G = self.K3.copy()
  229. G.remove_edge(0, 1)
  230. assert G.succ == {0: {2: {}}, 1: {0: {}, 2: {}}, 2: {0: {}, 1: {}}}
  231. assert G.pred == {0: {1: {}, 2: {}}, 1: {2: {}}, 2: {0: {}, 1: {}}}
  232. with pytest.raises(nx.NetworkXError):
  233. G.remove_edge(-1, 0)
  234. def test_remove_edges_from(self):
  235. G = self.K3.copy()
  236. G.remove_edges_from([(0, 1)])
  237. assert G.succ == {0: {2: {}}, 1: {0: {}, 2: {}}, 2: {0: {}, 1: {}}}
  238. assert G.pred == {0: {1: {}, 2: {}}, 1: {2: {}}, 2: {0: {}, 1: {}}}
  239. G.remove_edges_from([(0, 0)]) # silent fail
  240. def test_clear(self):
  241. G = self.K3
  242. G.graph["name"] = "K3"
  243. G.clear()
  244. assert list(G.nodes) == []
  245. assert G.succ == {}
  246. assert G.pred == {}
  247. assert G.graph == {}
  248. def test_clear_edges(self):
  249. G = self.K3
  250. G.graph["name"] = "K3"
  251. nodes = list(G.nodes)
  252. G.clear_edges()
  253. assert list(G.nodes) == nodes
  254. expected = {0: {}, 1: {}, 2: {}}
  255. assert G.succ == expected
  256. assert G.pred == expected
  257. assert list(G.edges) == []
  258. assert G.graph["name"] == "K3"
  259. class TestEdgeSubgraph(_TestGraphEdgeSubgraph):
  260. """Unit tests for the :meth:`DiGraph.edge_subgraph` method."""
  261. def setup_method(self):
  262. # Create a doubly-linked path graph on five nodes.
  263. G = nx.DiGraph(nx.path_graph(5))
  264. # Add some node, edge, and graph attributes.
  265. for i in range(5):
  266. G.nodes[i]["name"] = f"node{i}"
  267. G.edges[0, 1]["name"] = "edge01"
  268. G.edges[3, 4]["name"] = "edge34"
  269. G.graph["name"] = "graph"
  270. # Get the subgraph induced by the first and last edges.
  271. self.G = G
  272. self.H = G.edge_subgraph([(0, 1), (3, 4)])
  273. def test_pred_succ(self):
  274. """Test that nodes are added to predecessors and successors.
  275. For more information, see GitHub issue #2370.
  276. """
  277. G = nx.DiGraph()
  278. G.add_edge(0, 1)
  279. H = G.edge_subgraph([(0, 1)])
  280. assert list(H.predecessors(0)) == []
  281. assert list(H.successors(0)) == [1]
  282. assert list(H.predecessors(1)) == [0]
  283. assert list(H.successors(1)) == []