test_graphviews.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. import pytest
  2. import networkx as nx
  3. from networkx.utils import edges_equal, nodes_equal
  4. # Note: SubGraph views are not tested here. They have their own testing file
  5. class TestReverseView:
  6. def setup_method(self):
  7. self.G = nx.path_graph(9, create_using=nx.DiGraph())
  8. self.rv = nx.reverse_view(self.G)
  9. def test_pickle(self):
  10. import pickle
  11. rv = self.rv
  12. prv = pickle.loads(pickle.dumps(rv, -1))
  13. assert rv._node == prv._node
  14. assert rv._adj == prv._adj
  15. assert rv.graph == prv.graph
  16. def test_contains(self):
  17. assert (2, 3) in self.G.edges
  18. assert (3, 2) not in self.G.edges
  19. assert (2, 3) not in self.rv.edges
  20. assert (3, 2) in self.rv.edges
  21. def test_iter(self):
  22. expected = sorted(tuple(reversed(e)) for e in self.G.edges)
  23. assert sorted(self.rv.edges) == expected
  24. def test_exceptions(self):
  25. G = nx.Graph()
  26. pytest.raises(nx.NetworkXNotImplemented, nx.reverse_view, G)
  27. def test_subclass(self):
  28. class MyGraph(nx.DiGraph):
  29. def my_method(self):
  30. return "me"
  31. def to_directed_class(self):
  32. return MyGraph()
  33. M = MyGraph()
  34. M.add_edge(1, 2)
  35. RM = nx.reverse_view(M)
  36. assert RM.__class__ == MyGraph
  37. RMC = RM.copy()
  38. assert RMC.__class__ == MyGraph
  39. assert RMC.has_edge(2, 1)
  40. assert RMC.my_method() == "me"
  41. class TestMultiReverseView:
  42. def setup_method(self):
  43. self.G = nx.path_graph(9, create_using=nx.MultiDiGraph())
  44. self.G.add_edge(4, 5)
  45. self.rv = nx.reverse_view(self.G)
  46. def test_pickle(self):
  47. import pickle
  48. rv = self.rv
  49. prv = pickle.loads(pickle.dumps(rv, -1))
  50. assert rv._node == prv._node
  51. assert rv._adj == prv._adj
  52. assert rv.graph == prv.graph
  53. def test_contains(self):
  54. assert (2, 3, 0) in self.G.edges
  55. assert (3, 2, 0) not in self.G.edges
  56. assert (2, 3, 0) not in self.rv.edges
  57. assert (3, 2, 0) in self.rv.edges
  58. assert (5, 4, 1) in self.rv.edges
  59. assert (4, 5, 1) not in self.rv.edges
  60. def test_iter(self):
  61. expected = sorted((v, u, k) for u, v, k in self.G.edges)
  62. assert sorted(self.rv.edges) == expected
  63. def test_exceptions(self):
  64. MG = nx.MultiGraph(self.G)
  65. pytest.raises(nx.NetworkXNotImplemented, nx.reverse_view, MG)
  66. def test_generic_multitype():
  67. nxg = nx.graphviews
  68. G = nx.DiGraph([(1, 2)])
  69. with pytest.raises(nx.NetworkXError):
  70. nxg.generic_graph_view(G, create_using=nx.MultiGraph)
  71. G = nx.MultiDiGraph([(1, 2)])
  72. with pytest.raises(nx.NetworkXError):
  73. nxg.generic_graph_view(G, create_using=nx.DiGraph)
  74. class TestToDirected:
  75. def setup_method(self):
  76. self.G = nx.path_graph(9)
  77. self.dv = nx.to_directed(self.G)
  78. self.MG = nx.path_graph(9, create_using=nx.MultiGraph())
  79. self.Mdv = nx.to_directed(self.MG)
  80. def test_directed(self):
  81. assert not self.G.is_directed()
  82. assert self.dv.is_directed()
  83. def test_already_directed(self):
  84. dd = nx.to_directed(self.dv)
  85. Mdd = nx.to_directed(self.Mdv)
  86. assert edges_equal(dd.edges, self.dv.edges, directed=True)
  87. assert edges_equal(Mdd.edges, self.Mdv.edges, directed=True)
  88. def test_pickle(self):
  89. import pickle
  90. dv = self.dv
  91. pdv = pickle.loads(pickle.dumps(dv, -1))
  92. assert dv._node == pdv._node
  93. assert dv._succ == pdv._succ
  94. assert dv._pred == pdv._pred
  95. assert dv.graph == pdv.graph
  96. def test_contains(self):
  97. assert (2, 3) in self.G.edges
  98. assert (3, 2) in self.G.edges
  99. assert (2, 3) in self.dv.edges
  100. assert (3, 2) in self.dv.edges
  101. def test_iter(self):
  102. revd = [tuple(reversed(e)) for e in self.G.edges]
  103. expected = sorted(list(self.G.edges) + revd)
  104. assert sorted(self.dv.edges) == expected
  105. class TestToUndirected:
  106. def setup_method(self):
  107. self.DG = nx.path_graph(9, create_using=nx.DiGraph())
  108. self.uv = nx.to_undirected(self.DG)
  109. self.MDG = nx.path_graph(9, create_using=nx.MultiDiGraph())
  110. self.Muv = nx.to_undirected(self.MDG)
  111. def test_directed(self):
  112. assert self.DG.is_directed()
  113. assert not self.uv.is_directed()
  114. def test_already_undirected(self):
  115. uu = nx.to_undirected(self.uv)
  116. Muu = nx.to_undirected(self.Muv)
  117. assert edges_equal(uu.edges, self.uv.edges)
  118. assert edges_equal(Muu.edges, self.Muv.edges)
  119. def test_pickle(self):
  120. import pickle
  121. uv = self.uv
  122. puv = pickle.loads(pickle.dumps(uv, -1))
  123. assert uv._node == puv._node
  124. assert uv._adj == puv._adj
  125. assert uv.graph == puv.graph
  126. assert hasattr(uv, "_graph")
  127. def test_contains(self):
  128. assert (2, 3) in self.DG.edges
  129. assert (3, 2) not in self.DG.edges
  130. assert (2, 3) in self.uv.edges
  131. assert (3, 2) in self.uv.edges
  132. def test_iter(self):
  133. expected = sorted(self.DG.edges)
  134. assert sorted(self.uv.edges) == expected
  135. class TestChainsOfViews:
  136. @classmethod
  137. def setup_class(cls):
  138. cls.G = nx.path_graph(9)
  139. cls.DG = nx.path_graph(9, create_using=nx.DiGraph())
  140. cls.MG = nx.path_graph(9, create_using=nx.MultiGraph())
  141. cls.MDG = nx.path_graph(9, create_using=nx.MultiDiGraph())
  142. cls.Gv = nx.to_undirected(cls.DG)
  143. cls.DGv = nx.to_directed(cls.G)
  144. cls.MGv = nx.to_undirected(cls.MDG)
  145. cls.MDGv = nx.to_directed(cls.MG)
  146. cls.Rv = cls.DG.reverse()
  147. cls.MRv = cls.MDG.reverse()
  148. cls.graphs = [
  149. cls.G,
  150. cls.DG,
  151. cls.MG,
  152. cls.MDG,
  153. cls.Gv,
  154. cls.DGv,
  155. cls.MGv,
  156. cls.MDGv,
  157. cls.Rv,
  158. cls.MRv,
  159. ]
  160. for G in cls.graphs:
  161. G.edges, G.nodes, G.degree
  162. def test_pickle(self):
  163. import pickle
  164. for G in self.graphs:
  165. H = pickle.loads(pickle.dumps(G, -1))
  166. assert edges_equal(H.edges, G.edges, directed=G.is_directed())
  167. assert nodes_equal(H.nodes, G.nodes)
  168. def test_subgraph_of_subgraph(self):
  169. SGv = nx.subgraph(self.G, range(3, 7))
  170. SDGv = nx.subgraph(self.DG, range(3, 7))
  171. SMGv = nx.subgraph(self.MG, range(3, 7))
  172. SMDGv = nx.subgraph(self.MDG, range(3, 7))
  173. for G in self.graphs + [SGv, SDGv, SMGv, SMDGv]:
  174. SG = nx.induced_subgraph(G, [4, 5, 6])
  175. assert list(SG) == [4, 5, 6]
  176. SSG = SG.subgraph([6, 7])
  177. assert list(SSG) == [6]
  178. # subgraph-subgraph chain is short-cut in base class method
  179. assert SSG._graph is G
  180. def test_restricted_induced_subgraph_chains(self):
  181. """Test subgraph chains that both restrict and show nodes/edges.
  182. A restricted_view subgraph should allow induced subgraphs using
  183. G.subgraph that automagically without a chain (meaning the result
  184. is a subgraph view of the original graph not a subgraph-of-subgraph.
  185. """
  186. hide_nodes = [3, 4, 5]
  187. hide_edges = [(6, 7)]
  188. RG = nx.restricted_view(self.G, hide_nodes, hide_edges)
  189. nodes = [4, 5, 6, 7, 8]
  190. SG = nx.induced_subgraph(RG, nodes)
  191. SSG = RG.subgraph(nodes)
  192. assert RG._graph is self.G
  193. assert SSG._graph is self.G
  194. assert SG._graph is RG
  195. assert edges_equal(SG.edges, SSG.edges)
  196. # should be same as morphing the graph
  197. CG = self.G.copy()
  198. CG.remove_nodes_from(hide_nodes)
  199. CG.remove_edges_from(hide_edges)
  200. assert edges_equal(CG.edges(nodes), SSG.edges)
  201. CG.remove_nodes_from([0, 1, 2, 3])
  202. assert edges_equal(CG.edges, SSG.edges)
  203. # switch order: subgraph first, then restricted view
  204. SSSG = self.G.subgraph(nodes)
  205. RSG = nx.restricted_view(SSSG, hide_nodes, hide_edges)
  206. assert RSG._graph is not self.G
  207. assert edges_equal(RSG.edges, CG.edges)
  208. def test_subgraph_copy(self):
  209. for origG in self.graphs:
  210. G = nx.Graph(origG)
  211. SG = G.subgraph([4, 5, 6])
  212. H = SG.copy()
  213. assert type(G) is type(H)
  214. def test_subgraph_todirected(self):
  215. SG = nx.induced_subgraph(self.G, [4, 5, 6])
  216. SSG = SG.to_directed()
  217. assert sorted(SSG) == [4, 5, 6]
  218. assert sorted(SSG.edges) == [(4, 5), (5, 4), (5, 6), (6, 5)]
  219. def test_subgraph_toundirected(self):
  220. SG = nx.induced_subgraph(self.G, [4, 5, 6])
  221. SSG = SG.to_undirected()
  222. assert list(SSG) == [4, 5, 6]
  223. assert sorted(SSG.edges) == [(4, 5), (5, 6)]
  224. def test_reverse_subgraph_toundirected(self):
  225. G = self.DG.reverse(copy=False)
  226. SG = G.subgraph([4, 5, 6])
  227. SSG = SG.to_undirected()
  228. assert list(SSG) == [4, 5, 6]
  229. assert sorted(SSG.edges) == [(4, 5), (5, 6)]
  230. def test_reverse_reverse_copy(self):
  231. G = self.DG.reverse(copy=False)
  232. H = G.reverse(copy=True)
  233. assert H.nodes == self.DG.nodes
  234. assert H.edges == self.DG.edges
  235. G = self.MDG.reverse(copy=False)
  236. H = G.reverse(copy=True)
  237. assert H.nodes == self.MDG.nodes
  238. assert H.edges == self.MDG.edges
  239. def test_subgraph_edgesubgraph_toundirected(self):
  240. G = self.G.copy()
  241. SG = G.subgraph([4, 5, 6])
  242. SSG = SG.edge_subgraph([(4, 5), (5, 4)])
  243. USSG = SSG.to_undirected()
  244. assert list(USSG) == [4, 5]
  245. assert sorted(USSG.edges) == [(4, 5)]
  246. def test_copy_subgraph(self):
  247. G = self.G.copy()
  248. SG = G.subgraph([4, 5, 6])
  249. CSG = SG.copy(as_view=True)
  250. DCSG = SG.copy(as_view=False)
  251. assert hasattr(CSG, "_graph") # is a view
  252. assert not hasattr(DCSG, "_graph") # not a view
  253. def test_copy_disubgraph(self):
  254. G = self.DG.copy()
  255. SG = G.subgraph([4, 5, 6])
  256. CSG = SG.copy(as_view=True)
  257. DCSG = SG.copy(as_view=False)
  258. assert hasattr(CSG, "_graph") # is a view
  259. assert not hasattr(DCSG, "_graph") # not a view
  260. def test_copy_multidisubgraph(self):
  261. G = self.MDG.copy()
  262. SG = G.subgraph([4, 5, 6])
  263. CSG = SG.copy(as_view=True)
  264. DCSG = SG.copy(as_view=False)
  265. assert hasattr(CSG, "_graph") # is a view
  266. assert not hasattr(DCSG, "_graph") # not a view
  267. def test_copy_multisubgraph(self):
  268. G = self.MG.copy()
  269. SG = G.subgraph([4, 5, 6])
  270. CSG = SG.copy(as_view=True)
  271. DCSG = SG.copy(as_view=False)
  272. assert hasattr(CSG, "_graph") # is a view
  273. assert not hasattr(DCSG, "_graph") # not a view
  274. def test_copy_of_view(self):
  275. G = nx.MultiGraph(self.MGv)
  276. assert G.__class__.__name__ == "MultiGraph"
  277. G = G.copy(as_view=True)
  278. assert G.__class__.__name__ == "MultiGraph"
  279. def test_subclass(self):
  280. class MyGraph(nx.DiGraph):
  281. def my_method(self):
  282. return "me"
  283. def to_directed_class(self):
  284. return MyGraph()
  285. for origG in self.graphs:
  286. G = MyGraph(origG)
  287. SG = G.subgraph([4, 5, 6])
  288. H = SG.copy()
  289. assert SG.my_method() == "me"
  290. assert H.my_method() == "me"
  291. assert 3 not in H or 3 in SG